1. Ringkasan
Di codelab ini, Anda akan mengembangkan lab sebelumnya dan menambahkan layanan thumbnail. Layanan thumbnail adalah penampung web yang mengambil gambar besar dan membuat thumbnail darinya.
Saat gambar diupload ke Cloud Storage, notifikasi akan dikirim melalui Cloud Pub/Sub ke container web Cloud Run, yang kemudian mengubah ukuran gambar dan menyimpannya kembali di bucket lain di Cloud Storage.
Yang akan Anda pelajari
- Cloud Run
- Cloud Storage
- Cloud Pub/Sub
2. Penyiapan dan Persyaratan
Penyiapan lingkungan mandiri
- Login ke Google Cloud Console dan buat project baru atau gunakan kembali project yang sudah ada. Jika belum memiliki akun Gmail atau Google Workspace, Anda harus membuatnya.
- Project name adalah nama tampilan untuk peserta project ini. String ini adalah string karakter yang tidak digunakan oleh Google API, dan Anda dapat memperbaruinya kapan saja.
- Project ID harus unik di semua project Google Cloud dan tidak dapat diubah (tidak dapat diubah setelah ditetapkan). Cloud Console otomatis menghasilkan string unik; biasanya Anda tidak peduli dengan kata-katanya. Pada sebagian besar codelab, Anda harus mereferensikan Project ID (dan biasanya diidentifikasi sebagai
PROJECT_ID
). Jadi, jika Anda tidak menyukainya, buat ID acak lain, atau, Anda dapat mencoba sendiri dan melihat apakah tersedia. Kemudian file akan "dibekukan" setelah project dibuat. - Ada nilai ketiga, Nomor Project yang digunakan oleh beberapa API. Pelajari lebih lanjut ketiga nilai ini di dokumentasi.
- Selanjutnya, Anda harus mengaktifkan penagihan di Cloud Console untuk menggunakan API/resource Cloud. Menjalankan operasi dalam codelab ini seharusnya tidak memerlukan banyak biaya, bahkan mungkin tidak sama sekali. Untuk menonaktifkan resource agar tidak menimbulkan penagihan di luar tutorial ini, ikuti petunjuk "pembersihan" yang ada di akhir codelab. Pengguna baru Google Cloud memenuhi syarat untuk mengikuti program Uji Coba Gratis senilai $300 USD.
Mulai Cloud Shell
Meskipun Google Cloud dapat dioperasikan dari jarak jauh menggunakan laptop Anda, dalam codelab ini, Anda akan menggunakan Google Cloud Shell, lingkungan command line yang berjalan di Cloud.
Dari GCP Console, klik ikon Cloud Shell di toolbar kanan atas:
Hanya perlu waktu beberapa saat untuk penyediaan dan terhubung ke lingkungan. Jika sudah selesai, Anda akan melihat tampilan seperti ini:
Mesin virtual ini berisi semua alat pengembangan yang Anda perlukan. Layanan ini menawarkan direktori beranda tetap sebesar 5 GB dan beroperasi di Google Cloud, sehingga sangat meningkatkan performa dan autentikasi jaringan. Semua pekerjaan Anda di lab ini dapat dilakukan hanya dengan browser.
3. Mengaktifkan API
Di lab ini, Anda akan memerlukan Cloud Build untuk membangun image container dan Cloud Run untuk men-deploy container.
Aktifkan kedua API dari Cloud Shell:
gcloud services enable cloudbuild.googleapis.com \ run.googleapis.com
Anda akan melihat operasi berhasil diselesaikan:
Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.
4. Buat bucket lain
Anda akan menyimpan thumbnail dari gambar yang diupload di bucket lain. Mari kita gunakan gsutil
untuk membuat bucket kedua.
Di dalam Cloud Shell, tetapkan variabel untuk nama bucket yang unik. Cloud Shell sudah memiliki GOOGLE_CLOUD_PROJECT
yang ditetapkan ke project ID unik Anda. Anda dapat menambahkannya ke nama bucket. Kemudian, buat bucket multi-region publik di Eropa dengan akses tingkat yang seragam:
BUCKET_THUMBNAILS=thumbnails-$GOOGLE_CLOUD_PROJECT gsutil mb -l EU gs://$BUCKET_THUMBNAILS gsutil uniformbucketlevelaccess set on gs://$BUCKET_THUMBNAILS gsutil iam ch allUsers:objectViewer gs://$BUCKET_THUMBNAILS
Pada akhirnya, Anda akan memiliki bucket publik baru:
5. Meng-clone kode
Clone kode, lalu buka direktori yang berisi layanan:
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop cd serverless-photosharing-workshop/services/thumbnails/nodejs
Anda akan memiliki tata letak file berikut untuk layanan:
services | ├── thumbnails | ├── nodejs | ├── Dockerfile ├── index.js ├── package.json
Di dalam folder thumbnails/nodejs
, Anda memiliki 3 file:
index.js
berisi kode Node.jspackage.json
menentukan dependensi libraryDockerfile
menentukan image container
6. Mempelajari kode
Untuk mempelajari kode ini, Anda dapat menggunakan editor teks bawaan, dengan mengklik tombol Open Editor
di bagian atas jendela Cloud Shell:
Anda juga dapat membuka editor di jendela browser khusus, untuk ruang layar yang lebih luas.
Dependensi
File package.json
menentukan dependensi library yang diperlukan:
{
"name": "thumbnail_service",
"version": "0.0.1",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"bluebird": "^3.7.2",
"express": "^4.17.1",
"imagemagick": "^0.1.3",
"@google-cloud/firestore": "^4.9.9",
"@google-cloud/storage": "^5.8.3"
}
}
Library Cloud Storage digunakan untuk membaca dan menyimpan file gambar dalam Cloud Storage. Firestore untuk memperbarui metadata gambar. Express adalah framework web JavaScript / Node. Modul body-parser digunakan untuk mengurai permintaan yang masuk dengan mudah. Bluebird digunakan untuk menangani promise, dan Imagemagick adalah library untuk memanipulasi gambar.
Dockerfile
Dockerfile
menentukan image container untuk aplikasi:
FROM node:14-slim
# installing Imagemagick
RUN set -ex; \
apt-get -y update; \
apt-get -y install imagemagick; \
rm -rf /var/lib/apt/lists/*; \
mkdir /tmp/original; \
mkdir /tmp/thumbnail;
WORKDIR /picadaily/services/thumbnails
COPY package*.json ./
RUN npm install --production
COPY . .
CMD [ "npm", "start" ]
Image dasarnya adalah Node 14 dan library imagemagick digunakan untuk manipulasi gambar. Beberapa direktori sementara dibuat untuk menyimpan file gambar asli dan thumbnail. Kemudian, modul NPM yang diperlukan oleh kode kami akan diinstal sebelum memulai kode dengan npm start
.
index.js
Mari kita pelajari kode tersebut secara bertahap, sehingga kita dapat lebih memahami apa yang dilakukan program ini.
const express = require('express');
const imageMagick = require('imagemagick');
const Promise = require("bluebird");
const path = require('path');
const {Storage} = require('@google-cloud/storage');
const Firestore = require('@google-cloud/firestore');
const app = express();
app.use(express.json());
Pertama-tama, kita memerlukan dependensi yang diperlukan, lalu membuat aplikasi web Express, serta menunjukkan bahwa kita ingin menggunakan parser isi JSON, karena permintaan yang masuk sebenarnya hanyalah payload JSON yang dikirim melalui permintaan POST ke aplikasi kita.
app.post('/', async (req, res) => {
try {
// ...
} catch (err) {
console.log(`Error: creating the thumbnail: ${err}`);
console.error(err);
res.status(500).send(err);
}
});
Kita menerima payload masuk tersebut di URL dasar /, dan kita menggabungkan kode kita dengan beberapa penanganan logika error, untuk mendapatkan informasi yang lebih baik tentang alasan kegagalan dalam kode kita dengan melihat log yang akan terlihat dari antarmuka Stackdriver Logging di konsol web Google Cloud.
const pubSubMessage = req.body;
console.log(`PubSub message: ${JSON.stringify(pubSubMessage)}`);
const fileEvent = JSON.parse(Buffer.from(pubSubMessage.message.data, 'base64').toString().trim());
console.log(`Received thumbnail request for file ${fileEvent.name} from bucket ${fileEvent.bucket}`);
Pada platform Cloud Run, pesan Pub/Sub dikirim melalui permintaan POST HTTP, sebagai payload JSON dalam bentuk:
{
"message": {
"attributes": {
"bucketId": "uploaded-pictures",
"eventTime": "2020-02-27T09:22:43.255225Z",
"eventType": "OBJECT_FINALIZE",
"notificationConfig": "projects/_/buckets/uploaded-pictures/notificationConfigs/28",
"objectGeneration": "1582795363255481",
"objectId": "IMG_20200213_181159.jpg",
"payloadFormat": "JSON_API_V1"
},
"data": "ewogICJraW5kIjogInN0b3JhZ2Ujb2JqZWN...FQUU9Igp9Cg==",
"messageId": "1014308302773399",
"message_id": "1014308302773399",
"publishTime": "2020-02-27T09:22:43.973Z",
"publish_time": "2020-02-27T09:22:43.973Z"
},
"subscription": "projects/serverless-picadaily/subscriptions/gcs-events-subscription"
}
Namun, yang benar-benar menarik dalam dokumen JSON ini adalah sebenarnya apa yang terdapat dalam atribut message.data
, yang hanya merupakan string, tetapi mengenkode payload sebenarnya ke Base 64. Itulah sebabnya kode di atas mendekode konten Base 64 dari atribut ini. Setelah didekode, atribut data
tersebut akan berisi dokumen JSON lain yang mewakili detail peristiwa Cloud Storage, yang di antara metadata lainnya, menunjukkan nama file dan nama bucket.
{
"kind": "storage#object",
"id": "uploaded-pictures/IMG_20200213_181159.jpg/1582795363255481",
"selfLink": "https://www.googleapis.com/storage/v1/b/uploaded-pictures/o/IMG_20200213_181159.jpg",
"name": "IMG_20200213_181159.jpg",
"bucket": "uploaded-pictures",
"generation": "1582795363255481",
"metageneration": "1",
"contentType": "image/jpeg",
"timeCreated": "2020-02-27T09:22:43.255Z",
"updated": "2020-02-27T09:22:43.255Z",
"storageClass": "STANDARD",
"timeStorageClassUpdated": "2020-02-27T09:22:43.255Z",
"size": "4944335",
"md5Hash": "QzBIoPJBV2EvqB1EVk1riw==",
"mediaLink": "https://www.googleapis.com/download/storage/v1/b/uploaded-pictures/o/IMG_20200213_181159.jpg?generation=1582795363255481&alt=media",
"crc32c": "hQ3uHg==",
"etag": "CLmJhJu08ecCEAE="
}
Kita memerlukan nama gambar dan bucket, karena kode kita akan mengambil gambar tersebut dari bucket untuk perlakuan thumbnail-nya:
const bucket = storage.bucket(fileEvent.bucket);
const thumbBucket = storage.bucket(process.env.BUCKET_THUMBNAILS);
const originalFile = path.resolve('/tmp/original', fileEvent.name);
const thumbFile = path.resolve('/tmp/thumbnail', fileEvent.name);
await bucket.file(fileEvent.name).download({
destination: originalFile
});
console.log(`Downloaded picture into ${originalFile}`);
Kita mengambil nama bucket penyimpanan output dari variabel lingkungan.
Kita memiliki bucket origin yang pembuatan filenya memicu layanan Cloud Run, dan bucket tujuan tempat kita akan menyimpan image yang dihasilkan. Kita menggunakan API bawaan path
untuk melakukan penanganan file lokal, karena library imagemagick akan membuat thumbnail secara lokal di direktori sementara /tmp
. Kita await
untuk panggilan asinkron guna mendownload file gambar yang diupload.
const resizeCrop = Promise.promisify(im.crop);
await resizeCrop({
srcPath: originalFile,
dstPath: thumbFile,
width: 400,
height: 400
});
console.log(`Created local thumbnail in ${thumbFile}`);
Modul imagemagick tidak terlalu ramah async
/ await
, sehingga kami menggabungkannya dalam promise JavaScript (disediakan oleh modul Bluebird). Kemudian, kita memanggil fungsi perubahan ukuran / Pemangkasan asinkron yang kita buat dengan parameter untuk file sumber dan tujuan, serta dimensi thumbnail yang ingin dibuat.
await thumbBucket.upload(thumbFile);
console.log(`Uploaded thumbnail to Cloud Storage bucket ${process.env.BUCKET_THUMBNAILS}`);
Setelah file thumbnail diupload ke Cloud Storage, kami juga akan memperbarui metadata di Cloud Firestore untuk menambahkan flag boolean yang menunjukkan bahwa thumbnail untuk gambar ini telah dihasilkan:
const pictureStore = new Firestore().collection('pictures');
const doc = pictureStore.doc(fileEvent.name);
await doc.set({
thumbnail: true
}, {merge: true});
console.log(`Updated Firestore about thumbnail creation for ${fileEvent.name}`);
res.status(204).send(`${fileEvent.name} processed`);
Setelah permintaan selesai, kami akan membalas permintaan HTTP POST bahwa file tersebut telah diproses dengan benar.
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Started thumbnail generator on port ${PORT}`);
});
Di akhir file sumber, kita memiliki petunjuk agar Express benar-benar memulai aplikasi web pada port default 8080.
7. Menguji secara lokal
Uji kode secara lokal untuk memastikan kode berfungsi sebelum di-deploy ke cloud.
Di dalam folder thumbnails/nodejs
, instal dependensi npm dan mulai server:
npm install; npm start
Jika semuanya berjalan dengan baik, server akan dimulai pada port 8080:
Started thumbnail generator on port 8080
Gunakan CTRL-C
untuk keluar.
8. Membuat dan memublikasikan image container
Cloud Run menjalankan container, tetapi Anda harus membangun image container terlebih dahulu (ditentukan di Dockerfile
). Google Cloud Build dapat digunakan untuk membangun image container, lalu menghosting ke Google Container Registry.
Di dalam folder thumbnails/nodejs
tempat Dockerfile
berada, berikan perintah berikut untuk membangun image container:
gcloud builds submit --tag gcr.io/$GOOGLE_CLOUD_PROJECT/thumbnail-service
Setelah satu atau dua menit, build akan berhasil:
"Histori" Cloud Build akan menunjukkan build yang berhasil juga:
Mengklik ID build untuk mendapatkan tampilan detail di "build artefak" Anda akan melihat bahwa image container telah diupload ke Cloud Registry (GCR):
Jika ingin, Anda dapat memeriksa kembali apakah image container berjalan secara lokal di Cloud Shell:
docker run -p 8080:8080 gcr.io/$GOOGLE_CLOUD_PROJECT/thumbnail-service
Tindakan ini harus memulai server di port 8080 dalam container:
Started thumbnail generator on port 8080
Gunakan CTRL-C
untuk keluar.
9. Men-deploy ke Cloud Run
Sebelum men-deploy ke Cloud Run, tetapkan region Cloud Run ke salah satu region dan platform yang didukung ke managed
:
gcloud config set run/region europe-west1 gcloud config set run/platform managed
Anda dapat memeriksa apakah konfigurasi telah disetel:
gcloud config list ... [run] platform = managed region = europe-west1
Jalankan perintah berikut untuk men-deploy image container di Cloud Run:
SERVICE_NAME=thumbnail-service gcloud run deploy $SERVICE_NAME \ --image gcr.io/$GOOGLE_CLOUD_PROJECT/thumbnail-service \ --no-allow-unauthenticated \ --update-env-vars BUCKET_THUMBNAILS=$BUCKET_THUMBNAILS
Perhatikan flag --no-allow-unauthenticated
. Tindakan ini menjadikan layanan Cloud Run sebagai layanan internal yang hanya akan dipicu oleh akun layanan tertentu.
Jika deployment berhasil, Anda akan melihat output berikut:
Saat membuka UI Konsol Cloud, Anda juga akan melihat bahwa layanan berhasil di-deploy:
10. Peristiwa Cloud Storage ke Cloud Run melalui Pub/Sub
Layanan sudah siap, tetapi Anda masih perlu membuat peristiwa Cloud Storage ke layanan Cloud Run yang baru dibuat. Cloud Storage dapat mengirim peristiwa pembuatan file melalui Cloud Pub/Sub, tetapi ada beberapa langkah yang membuatnya berfungsi.
Buat topik Pub/Sub sebagai pipeline komunikasi:
TOPIC_NAME=cloudstorage-cloudrun-topic gcloud pubsub topics create $TOPIC_NAME
Buat notifikasi Pub/Sub saat file disimpan di bucket:
BUCKET_PICTURES=uploaded-pictures-$GOOGLE_CLOUD_PROJECT gsutil notification create -t $TOPIC_NAME -f json gs://$BUCKET_PICTURES
Buat akun layanan untuk langganan Pub/Sub yang akan kita buat nanti:
SERVICE_ACCOUNT=$TOPIC_NAME-sa gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name "Cloud Run Pub/Sub Invoker"
Berikan izin pada akun layanan untuk memanggil layanan Cloud Run:
SERVICE_NAME=thumbnail-service gcloud run services add-iam-policy-binding $SERVICE_NAME \ --member=serviceAccount:$SERVICE_ACCOUNT@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \ --role=roles/run.invoker
Jika Anda mengaktifkan akun layanan Pub/Sub pada atau sebelum 8 April 2021, berikan peran iam.serviceAccountTokenCreator
ke akun layanan Pub/Sub:
PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format='value(projectNumber)') gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \ --member=serviceAccount:service-$PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com \ --role=roles/iam.serviceAccountTokenCreator
Diperlukan waktu beberapa menit agar perubahan IAM diterapkan.
Terakhir, buat langganan Pub/Sub dengan akun layanan:
SERVICE_URL=$(gcloud run services describe $SERVICE_NAME --format 'value(status.url)') gcloud pubsub subscriptions create $TOPIC_NAME-subscription --topic $TOPIC_NAME \ --push-endpoint=$SERVICE_URL \ --push-auth-service-account=$SERVICE_ACCOUNT@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
Anda dapat memeriksa apakah langganan sudah dibuat. Buka Pub/Sub di konsol, pilih topik gcs-events
dan di bagian bawah, Anda akan melihat langganan:
11. Menguji layanan
Untuk menguji apakah penyiapan berhasil, upload gambar baru ke bucket uploaded-pictures
dan periksa di bucket thumbnails
bahwa gambar baru yang diubah ukurannya muncul seperti yang diharapkan.
Anda juga dapat memeriksa kembali log untuk melihat pesan logging muncul, saat berbagai langkah layanan Cloud Run sedang dijalankan:
12. Pembersihan (Opsional)
Jika tidak ingin melanjutkan lab lain dalam seri ini, Anda dapat menghapus resource untuk menghemat biaya dan menjadi cloud citizen yang baik secara keseluruhan. Anda dapat membersihkan resource satu per satu seperti berikut.
Hapus bucket:
gsutil rb gs://$BUCKET_THUMBNAILS
Hapus layanan:
gcloud run services delete $SERVICE_NAME -q
Menghapus topik Pub/Sub
gcloud pubsub topics delete $TOPIC_NAME
Atau, Anda dapat menghapus seluruh project:
gcloud projects delete $GOOGLE_CLOUD_PROJECT
13. Selamat!
Semuanya kini tersedia:
- Membuat notifikasi di Cloud Storage yang mengirimkan pesan Pub/Sub tentang suatu topik, saat gambar baru diupload.
- Menentukan binding dan akun IAM yang diperlukan (tidak seperti Cloud Functions yang semuanya otomatis, dikonfigurasi secara manual di sini).
- Membuat langganan agar layanan Cloud Run kami menerima pesan Pub/Sub.
- Setiap kali gambar baru diupload ke bucket, gambar tersebut akan diubah ukurannya berkat layanan Cloud Run yang baru.
Yang telah kita bahas
- Cloud Run
- Cloud Storage
- Cloud Pub/Sub