Pic-a-harian: Lab 2—Buat thumbnail gambar

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.

31fa4f8a294d90df.pngS

Yang akan Anda pelajari

  • Cloud Run
  • Cloud Storage
  • Cloud Pub/Sub

2. Penyiapan dan Persyaratan

Penyiapan lingkungan mandiri

  1. 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.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • 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.
  1. 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:

bce75f34b2c53987.png

Hanya perlu waktu beberapa saat untuk penyediaan dan terhubung ke lingkungan. Jika sudah selesai, Anda akan melihat tampilan seperti ini:

f6ef2b5f13479f3a.png

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:

8e75c8099938e972.pngS

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.js
  • package.json menentukan dependensi library
  • Dockerfile 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:

3d145fe299dd8b3e.pngS

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:

b354b3a9a3631097.png

"Histori" Cloud Build akan menunjukkan build yang berhasil juga:

df00f198dd2bf6bf.png

Mengklik ID build untuk mendapatkan tampilan detail di "build artefak" Anda akan melihat bahwa image container telah diupload ke Cloud Registry (GCR):

a4577ce0744f73e2.png

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:

c0f28e7d6de0024.png

Saat membuka UI Konsol Cloud, Anda juga akan melihat bahwa layanan berhasil di-deploy:

9bfe48e3c8b597e5.pngS

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:

e8ab86dccb8d890.png

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:

42c025e2d7d6ca3a.pngS

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

Langkah Berikutnya