Hey there, fellow cloud enthusiasts! 👋
I recently tackled a pretty interesting Challenge Lab on Google Cloud titled "Set Up an App Dev Environment on Google Cloud"—and it was the perfect mix of practical hands-on learning and real-world cloud engineering. Whether you're prepping for certification or just trying to boost your GCP skills, this one's worth diving into.
In this post, I’ll break down what the lab was about, how I approached it, and some takeaways that might help you breeze through it, too.
Scenario: Welcome to Jooli Inc
You’ve just landed a junior cloud engineer role at Jooli Inc.. Not bad, right?
Your first big task is to help the newly formed Memories team set up their environment for a photo storage and thumbnail generation app. Your responsibilities include creating storage and Pub/Sub infrastructure, deploying a Cloud Run function, and cleaning up old user access.
The kicker? There are no step-by-step instructions. Just you, your GCP know-how, and a list of tasks.
Let’s get into it.
Task 1: Create a Bucket for Photo Storage
This is the starting point, creating a Cloud Storage bucket where uploaded images would live.
Steps:
- Navigate to Cloud Storage > Buckets.
- Click Create bucket, give it the exact name Bucket Name.
- Select the correct region (as specified).
You can also create a Cloud Storage bucket using the gsutil command (a part of gcloud CLI).
gcloud storage buckets create gs://Bucket-Name --location=REGION
Make sure to replace Bucket-Name with your desired name and REGION with the appropriate region (e.g., us-central1).
Task 2: Create a Pub/Sub Topic
This topic will be used by the Cloud Run function to publish messages when thumbnails are created.
Steps:
- Head to Pub/Sub > Topics.
- Click Create Topic, name it exactly Topic Name.
- No bells and whistles—just a straight-up topic setup.
To create a Pub/Sub topic, use the following gcloud command:
gcloud pubsub topics create Topic-Name
Replace Topic-Name with the desired name for the Pub/Sub topic.
Task 3: Deploy a Thumbnail Generator with Cloud Run (2nd Gen)
You’ll deploy a Cloud Run (2nd gen) function in Node.js 22 that watches for new images in the bucket, generates 64x64 thumbnails using Sharp, and sends a message to the Pub/Sub topic.
Key Notes:
- Set the trigger to Cloud Storage (Object Finalized).
- Use Cloud Functions Framework with Node.js 22.
- Make sure the entry point matches your function name.
The main logic in index.js
const functions = require('@google-cloud/functions-framework');
const { Storage } = require('@google-cloud/storage');
const { PubSub } = require('@google-cloud/pubsub');
const sharp = require('sharp');
functions.cloudEvent('', async cloudEvent => {
const event = cloudEvent.data;
console.log(`Event: ${JSON.stringify(event)}`);
console.log(`Hello ${event.bucket}`);
const fileName = event.name;
const bucketName = event.bucket;
const size = "64x64";
const bucket = new Storage().bucket(bucketName);
const topicName = "";
const pubsub = new PubSub();
if (fileName.search("64x64_thumbnail") === -1) {
// doesn't have a thumbnail, get the filename extension
const filename_split = fileName.split('.');
const filename_ext = filename_split[filename_split.length - 1].toLowerCase();
const filename_without_ext = fileName.substring(0, fileName.length - filename_ext.length - 1); // fix sub string to remove the dot
if (filename_ext === 'png' || filename_ext === 'jpg' || filename_ext === 'jpeg') {
// only support png and jpg at this point
console.log(`Processing Original: gs://${bucketName}/${fileName}`);
const gcsObject = bucket.file(fileName);
const newFilename = `${filename_without_ext}_64x64_thumbnail.${filename_ext}`;
const gcsNewObject = bucket.file(newFilename);
try {
const [buffer] = await gcsObject.download();
const resizedBuffer = await sharp(buffer)
.resize(64, 64, {
fit: 'inside',
withoutEnlargement: true,
})
.toFormat(filename_ext)
.toBuffer();
await gcsNewObject.save(resizedBuffer, {
metadata: {
contentType: `image/${filename_ext}`,
},
});
console.log(`Success: ${fileName} → ${newFilename}`);
await pubsub
.topic(topicName)
.publishMessage({ data: Buffer.from(newFilename) });
console.log(`Message published to ${topicName}`);
} catch (err) {
console.error(`Error: ${err}`);
}
} else {
console.log(`gs://${bucketName}/${fileName} is not an image I can handle`);
}
} else {
console.log(`gs://${bucketName}/${fileName} already has a thumbnail`);
}
});
And the package.json:
{
"name": "thumbnails",
"version": "1.0.0",
"description": "Create Thumbnail of uploaded image",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0",
"@google-cloud/pubsub": "^2.0.0",
"@google-cloud/storage": "^6.11.0",
"sharp": "^0.32.1"
},
"devDependencies": {},
"engines": {
"node": ">=4.3.2"
}
}
For the Cloud Run Function (2nd Generation) that handles the thumbnail creation, I used gcloud commands to deploy the function. Here’s the full flow:
Steps:
Set up the Cloud Function: Ensure that you have the necessary code files (index.js and package.json) for the function ready in a directory.
Enable necessary APIs: Before deploying the function, ensure the required APIs are enabled
Deploy the Cloud Run Function: Navigate to the directory containing your
index.js
andpackage.json
files and run the following:
gcloud run deploy Cloud-Run-Function-Name \
--image=gcr.io/cloud-builders/npm \
--platform=managed \
--region=REGION \
--allow-unauthenticated \
--trigger-bucket=gs://Bucket-Name \
--memory=256Mi \
--cpu=1 \
Replace:
Cloud-Run-Function-Name with your desired Cloud Run function name.
REGION with the region where the function should be deployed (e.g., us-central1).
Bucket-Name with the name of the bucket you created earlier.
Note: The --trigger-bucket flag connects the function to your bucket, so it runs when a new object is uploaded.
Task 4: Remove the Previous Cloud Engineer’s Access
To remove the previous engineer’s access, I used the gcloud IAM commands.
Steps:
- List IAM members: You can check the current IAM roles with:
gcloud projects get-iam-policy PROJECT_ID
- Remove the previous engineer’s role: If the previous engineer has the Viewer role, you can remove them with:
gcloud projects remove-iam-policy-binding PROJECT_ID \
--member='user:USERNAME' \
--role='roles/viewer'
Replace PROJECT_ID with your project ID, and USERNAME with the email address of the previous engineer.
Final Thoughts
This lab did a great job simulating a real-world task flow:
- Creating infrastructure
- Automating image processing
- Managing permissions
It helped solidify my understanding of how Cloud Storage, Pub/Sub, and Cloud Run can work together to build scalable, event-driven systems.
If you're prepping for the Associate Cloud Engineer certs like me, labs like these are gold.
What are your thoughts?