درباره این codelab
1. نمای کلی
در این آزمایشگاه کد، شما بر روی آزمایشگاه قبلی ساخته شده و یک سرویس بند انگشتی اضافه می کنید. سرویس بندانگشتی یک محفظه وب است که تصاویر بزرگ می گیرد و تصاویر کوچک را از آنها ایجاد می کند.
همانطور که تصویر در Cloud Storage آپلود می شود، یک اعلان از طریق Cloud Pub/Sub به یک محفظه وب Cloud Run ارسال می شود، که سپس اندازه تصاویر را تغییر می دهد و آنها را در سطل دیگری در Cloud Storage ذخیره می کند.
چیزی که یاد خواهید گرفت
- Cloud Run
- فضای ذخیره سازی ابری
- Cloud Pub/Sub
2. راه اندازی و الزامات
تنظیم محیط خود به خود
- به Google Cloud Console وارد شوید و یک پروژه جدید ایجاد کنید یا از یک موجود استفاده مجدد کنید. اگر قبلاً یک حساب Gmail یا Google Workspace ندارید، باید یک حساب ایجاد کنید .
- نام پروژه نام نمایشی برای شرکت کنندگان این پروژه است. این یک رشته کاراکتری است که توسط API های Google استفاده نمی شود و می توانید هر زمان که بخواهید آن را به روز کنید.
- شناسه پروژه باید در تمام پروژههای Google Cloud منحصربهفرد باشد و تغییرناپذیر باشد (پس از تنظیم نمیتوان آن را تغییر داد). Cloud Console به طور خودکار یک رشته منحصر به فرد تولید می کند. معمولاً برای شما مهم نیست که چیست. در اکثر کدها، باید به شناسه پروژه ارجاع دهید (و معمولاً به عنوان
PROJECT_ID
شناخته میشود)، بنابراین اگر آن را دوست ندارید، یک نمونه تصادفی دیگر ایجاد کنید، یا میتوانید شناسه پروژه را امتحان کنید و ببینید در دسترس است. سپس پس از ایجاد پروژه "یخ زده" می شود. - یک مقدار سوم وجود دارد، یک شماره پروژه که برخی از API ها از آن استفاده می کنند. در مورد هر سه این مقادیر در مستندات بیشتر بیاموزید.
- در مرحله بعد، برای استفاده از منابع Cloud/APIها، باید صورتحساب را در کنسول Cloud فعال کنید . اجرا کردن از طریق این کد لبه نباید هزینه زیادی داشته باشد، اگر اصلاً باشد. برای اینکه منابع را خاموش کنید تا بیش از این آموزش متحمل صورتحساب نشوید، دستورالعملهای «پاکسازی» را که در انتهای Codelab یافت میشود دنبال کنید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان 300 دلاری هستند.
Cloud Shell را راه اندازی کنید
در حالی که Google Cloud را می توان از راه دور از لپ تاپ شما کار کرد، در این کد لبه از Google Cloud Shell استفاده خواهید کرد، یک محیط خط فرمان که در Cloud اجرا می شود.
از کنسول GCP روی نماد Cloud Shell در نوار ابزار بالا سمت راست کلیک کنید:
تهیه و اتصال به محیط فقط چند لحظه طول می کشد. وقتی تمام شد، باید چیزی شبیه به این را ببینید:
این ماشین مجازی با تمام ابزارهای توسعه که شما نیاز دارید بارگذاری شده است. این یک فهرست اصلی 5 گیگابایتی دائمی را ارائه می دهد و در Google Cloud اجرا می شود و عملکرد و احراز هویت شبکه را تا حد زیادی افزایش می دهد. تمام کارهای شما در این آزمایشگاه به سادگی با یک مرورگر قابل انجام است.
3. API ها را فعال کنید
در این آزمایشگاه، برای ساخت تصاویر کانتینر به Cloud Build و برای استقرار کانتینر به Cloud Run نیاز دارید.
هر دو API را از Cloud Shell فعال کنید:
gcloud services enable cloudbuild.googleapis.com \ run.googleapis.com
برای اتمام موفقیت آمیز باید عملیات را مشاهده کنید:
Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.
4. یک سطل دیگر ایجاد کنید
شما تصاویر کوچک تصاویر آپلود شده را در سطل دیگری ذخیره خواهید کرد. بیایید از gsutil
برای ایجاد سطل دوم استفاده کنیم.
در داخل Cloud Shell، یک متغیر برای نام سطل یکتا تنظیم کنید. Cloud Shell قبلاً GOOGLE_CLOUD_PROJECT
را روی شناسه پروژه منحصر به فرد شما تنظیم کرده است. می توانید آن را به نام سطل اضافه کنید. سپس، یک سطل عمومی چند منطقه ای در اروپا با دسترسی سطح یکنواخت ایجاد کنید:
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
در پایان، شما باید یک سطل عمومی جدید داشته باشید:
5. کد را کلون کنید
کد را کلون کنید و به دایرکتوری حاوی سرویس بروید:
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop cd serverless-photosharing-workshop/services/thumbnails/nodejs
شما طرح بندی فایل زیر را برای سرویس خواهید داشت:
services | ├── thumbnails | ├── nodejs | ├── Dockerfile ├── index.js ├── package.json
در داخل پوشه thumbnails/nodejs
، 3 فایل دارید:
-
index.js
حاوی کد Node.js است -
package.json
وابستگی های کتابخانه را تعریف می کند -
Dockerfile
تصویر ظرف را تعریف می کند
6. کد را کاوش کنید
برای کشف کد، میتوانید از ویرایشگر متن داخلی با کلیک بر روی دکمه Open Editor
در بالای پنجره Cloud Shell استفاده کنید:
همچنین میتوانید ویرایشگر را در یک پنجره مرورگر اختصاصی باز کنید تا صفحه نمایش املاک بیشتری داشته باشید.
وابستگی ها
فایل package.json
وابستگی های کتابخانه مورد نیاز را تعریف می کند:
{
"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"
}
}
کتابخانه Cloud Storage برای خواندن و ذخیره فایل های تصویری در Cloud Storage استفاده می شود. Firestore برای به روز رسانی متادیتای تصویر. Express یک چارچوب وب جاوا اسکریپت / Node است. ماژول body-parser برای تجزیه درخواست های دریافتی به راحتی استفاده می شود. Bluebird برای مدیریت وعده ها استفاده می شود و Imagemagick یک کتابخانه برای دستکاری تصاویر است.
Dockerfile
Dockerfile
تصویر ظرف را برای برنامه تعریف می کند:
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" ]
تصویر پایه Node 14 است و کتابخانه imagemagick برای دستکاری تصویر استفاده می شود. برخی دایرکتوریهای موقت برای نگهداری فایلهای تصویر اصلی و بندانگشتی ایجاد میشوند. سپس ماژول های NPM مورد نیاز کد ما قبل از شروع کد با npm start
نصب می شوند.
index.js
بیایید کد را به صورت تکه تکه بررسی کنیم تا بهتر بفهمیم این برنامه چه کاری انجام می دهد.
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());
ما ابتدا به وابستگیهای مورد نیاز نیاز داریم و برنامه وب Express خود را ایجاد میکنیم، و همچنین نشان میدهیم که میخواهیم از تجزیهکننده بدن JSON استفاده کنیم، زیرا درخواستهای دریافتی در واقع فقط بارهای JSON هستند که از طریق یک درخواست POST به برنامه ما ارسال میشوند.
app.post('/', async (req, res) => {
try {
// ...
} catch (err) {
console.log(`Error: creating the thumbnail: ${err}`);
console.error(err);
res.status(500).send(err);
}
});
ما آن بارهای دریافتی را در URL پایه / دریافت میکنیم، و کد خود را با برخی مدیریت منطق خطا میپیچانیم تا با نگاه کردن به گزارشهایی که از Stackdriver Logging قابل مشاهده است، اطلاعات بهتری در مورد اینکه چرا ممکن است چیزی در کد ما خراب است داشته باشیم. رابط در کنسول وب 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}`);
در پلتفرم Cloud Run، پیامهای Pub/Sub از طریق درخواستهای HTTP POST، بهعنوان بارهای JSON از فرم ارسال میشوند:
{
"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"
}
اما آنچه در این سند JSON واقعاً جالب است، در واقع چیزی است که در ویژگی message.data
وجود دارد، که فقط یک رشته است اما بار واقعی را در Base 64 رمزگذاری می کند. به همین دلیل است که کد ما در بالا محتوای Base 64 این ویژگی را رمزگشایی می کند. . این ویژگی data
پس از رمزگشایی حاوی سند JSON دیگری است که جزئیات رویداد Cloud Storage را نشان میدهد، که در میان سایر ابردادهها، نام فایل و نام سطل را نشان میدهد.
{
"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="
}
ما به نام تصویر و سطل علاقه مند هستیم، زیرا کد ما قرار است آن تصویر را از سطل برای پردازش تصویر کوچک آن واکشی کند:
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}`);
ما در حال بازیابی نام سطل ذخیره خروجی از یک متغیر محیطی هستیم.
ما سطل مبدا را داریم که ایجاد فایل آن سرویس Cloud Run ما را راهاندازی کرد و سطل مقصد را داریم که تصویر حاصل را در آن ذخیره میکنیم. ما از path
API داخلی برای انجام مدیریت فایل های محلی استفاده می کنیم، زیرا کتابخانه imagemagick تصویر کوچک را به صورت محلی در دایرکتوری موقت /tmp
ایجاد می کند. برای دانلود فایل تصویری آپلود شده await
تماس ناهمزمان هستیم.
const resizeCrop = Promise.promisify(im.crop);
await resizeCrop({
srcPath: originalFile,
dstPath: thumbFile,
width: 400,
height: 400
});
console.log(`Created local thumbnail in ${thumbFile}`);
ماژول imagemagick خیلی async
نیست / await
دوستانه نیست، بنابراین ما آن را در یک وعده جاوا اسکریپت (ارائه شده توسط ماژول Bluebird) جمع بندی می کنیم. سپس تابع تغییر اندازه / برش ناهمزمان را که با پارامترهای فایل های مبدا و مقصد و همچنین ابعاد تصویر کوچکی که می خواهیم ایجاد کنیم، فراخوانی می کنیم.
await thumbBucket.upload(thumbFile);
console.log(`Uploaded thumbnail to Cloud Storage bucket ${process.env.BUCKET_THUMBNAILS}`);
هنگامی که فایل تصویر کوچک در Cloud Storage آپلود شد، ما همچنین ابرداده ها را در Cloud Firestore به روز می کنیم تا یک پرچم بولین اضافه کنیم که نشان می دهد تصویر کوچک این تصویر واقعاً ایجاد شده است:
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`);
هنگامی که درخواست ما تمام شد، به درخواست HTTP POST پاسخ می دهیم که فایل به درستی پردازش شده است.
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Started thumbnail generator on port ${PORT}`);
});
در پایان فایل منبع، دستورالعملهایی داریم که Express واقعاً برنامه وب ما را روی پورت پیشفرض 8080 راهاندازی کند.
7. به صورت محلی تست کنید
کد را به صورت محلی تست کنید تا مطمئن شوید قبل از استقرار در فضای ابری کار می کند.
در داخل پوشه thumbnails/nodejs
، وابستگیهای npm را نصب کرده و سرور را راهاندازی کنید:
npm install; npm start
اگر همه چیز خوب پیش رفت، باید سرور را روی پورت 8080 راه اندازی کند:
Started thumbnail generator on port 8080
برای خروج از CTRL-C
استفاده کنید.
8. تصویر ظرف را بسازید و منتشر کنید
Cloud Run کانتینرها را اجرا می کند اما ابتدا باید تصویر کانتینر را بسازید (تعریف شده در Dockerfile
). از Google Cloud Build می توان برای ساخت تصاویر کانتینر و سپس میزبانی در Google Container Registry استفاده کرد.
در داخل پوشه thumbnails/nodejs
که Dockerfile
در آن قرار دارد، دستور زیر را برای ساخت تصویر کانتینر صادر کنید:
gcloud builds submit --tag gcr.io/$GOOGLE_CLOUD_PROJECT/thumbnail-service
پس از یک یا دو دقیقه، ساخت باید با موفقیت انجام شود:
بخش "تاریخچه" ساخت ابر باید ساخت موفق را نیز نشان دهد:
با کلیک بر روی شناسه ساخت برای مشاهده جزئیات، در تب "build artifacts" باید مشاهده کنید که تصویر ظرف در Cloud Registry (GCR) بارگذاری شده است:
در صورت تمایل، میتوانید دوباره بررسی کنید که تصویر کانتینر به صورت محلی در Cloud Shell اجرا شود:
docker run -p 8080:8080 gcr.io/$GOOGLE_CLOUD_PROJECT/thumbnail-service
باید سرور را روی پورت 8080 در کانتینر راه اندازی کند:
Started thumbnail generator on port 8080
برای خروج از CTRL-C
استفاده کنید.
9. در Cloud Run مستقر شوید
قبل از استقرار در Cloud Run، منطقه Cloud Run را روی یکی از مناطق پشتیبانی شده و پلتفرم برای managed
تنظیم کنید:
gcloud config set run/region europe-west1 gcloud config set run/platform managed
می توانید بررسی کنید که پیکربندی تنظیم شده است:
gcloud config list ... [run] platform = managed region = europe-west1
دستور زیر را برای استقرار تصویر کانتینر در 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
به پرچم --no-allow-unauthenticated
توجه کنید. این باعث میشود سرویس Cloud Run یک سرویس داخلی باشد که فقط توسط حسابهای سرویس خاص راهاندازی میشود.
اگر استقرار موفقیت آمیز باشد، باید خروجی زیر را ببینید:
اگر به رابط کاربری کنسول ابری بروید، همچنین باید ببینید که سرویس با موفقیت مستقر شده است:
10. رویدادهای Cloud Storage به Cloud Run از طریق Pub/Sub
این سرویس آماده است، اما همچنان باید رویدادهای Cloud Storage را در سرویس Cloud Run جدید ایجاد کنید. Cloud Storage میتواند رویدادهای ایجاد فایل را از طریق Cloud Pub/Sub ارسال کند، اما چند مرحله برای این کار وجود دارد.
یک موضوع Pub/Sub به عنوان خط لوله ارتباطی ایجاد کنید:
TOPIC_NAME=cloudstorage-cloudrun-topic gcloud pubsub topics create $TOPIC_NAME
وقتی فایلها در سطل ذخیره میشوند، اعلانهای Pub/Sub ایجاد کنید:
BUCKET_PICTURES=uploaded-pictures-$GOOGLE_CLOUD_PROJECT gsutil notification create -t $TOPIC_NAME -f json gs://$BUCKET_PICTURES
یک حساب سرویس برای اشتراک Pub/Sub ایجاد کنید که بعداً ایجاد خواهیم کرد:
SERVICE_ACCOUNT=$TOPIC_NAME-sa gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name "Cloud Run Pub/Sub Invoker"
به حساب سرویس اجازه فراخوانی یک سرویس 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
اگر حساب سرویس Pub/Sub را در تاریخ 8 آوریل 2021 یا قبل از آن فعال کردهاید، نقش iam.serviceAccountTokenCreator
را به حساب سرویس 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
ممکن است چند دقیقه طول بکشد تا تغییرات IAM منتشر شود.
در نهایت، یک اشتراک Pub/Sub با حساب سرویس ایجاد کنید:
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
می توانید بررسی کنید که یک اشتراک ایجاد شده است. در کنسول به Pub/Sub بروید، موضوع gcs-events
را انتخاب کنید و در پایین، باید اشتراک را ببینید:
11. سرویس را تست کنید
برای آزمایش اینکه آیا راهاندازی کار میکند، یک عکس جدید در سطل uploaded-pictures
آپلود کنید و در سطل thumbnails
بررسی کنید که عکسهای تغییر اندازه جدید مطابق انتظار ظاهر شوند.
همچنین میتوانید گزارشها را دوباره بررسی کنید تا پیامهای گزارشگیری ظاهر شوند، زیرا مراحل مختلف سرویس Cloud Run در حال انجام است:
12. تمیز کردن (اختیاری)
اگر قصد ندارید با دیگر آزمایشگاههای این سری ادامه دهید، میتوانید منابع را پاکسازی کنید تا در هزینهها صرفهجویی کنید و در کل شهروند ابری خوبی باشید. می توانید منابع را به صورت جداگانه به صورت زیر پاک کنید.
سطل را حذف کنید:
gsutil rb gs://$BUCKET_THUMBNAILS
سرویس را حذف کنید:
gcloud run services delete $SERVICE_NAME -q
موضوع Pub/Sub را حذف کنید:
gcloud pubsub topics delete $TOPIC_NAME
یا می توانید کل پروژه را حذف کنید:
gcloud projects delete $GOOGLE_CLOUD_PROJECT
13. تبریک می گویم!
اکنون همه چیز سر جای خود است:
- یک اعلان در فضای ذخیرهسازی ابری ایجاد کرد که هنگام آپلود یک عکس جدید، پیامهای Pub/Sub را در مورد موضوعی ارسال میکند.
- اتصالات و حساب های مورد نیاز IAM را تعریف کرد (برخلاف توابع ابری که در آن همه خودکار است، در اینجا به صورت دستی پیکربندی می شود).
- اشتراکی ایجاد کردیم تا سرویس Cloud Run ما پیامهای Pub/Sub را دریافت کند.
- هر زمان که عکس جدیدی در سطل آپلود می شود، به لطف سرویس جدید Cloud Run اندازه عکس تغییر می کند.
آنچه را پوشش داده ایم
- Cloud Run
- فضای ذخیره سازی ابری
- Cloud Pub/Sub