Günlük resim: Laboratuvar 2: Resimlerin küçük resimlerini oluşturun

1. Genel Bakış

Bu kod laboratuvarında, önceki laboratuvarda öğrendiklerinizi kullanarak bir küçük resim hizmeti ekleyeceksiniz. Küçük resim hizmeti, büyük resimleri alıp bunlardan küçük resimler oluşturan bir web kapsayıcısıdır.

Resim Cloud Storage'a yüklendiğinde Cloud Pub/Sub aracılığıyla bir Cloud Run web kapsayıcısına bildirim gönderilir. Bu kapsayıcı, resimleri yeniden boyutlandırır ve Cloud Storage'daki başka bir pakete geri kaydeder.

31fa4f8a294d90df.png

Neler öğreneceksiniz?

  • Cloud Run
  • Cloud Storage
  • Cloud Pub/Sub

2. Kurulum ve Gereksinimler

Yönlendirmesiz ortam kurulumu

  1. Google Cloud Console'da oturum açın ve yeni bir proje oluşturun veya mevcut bir projeyi yeniden kullanın. Gmail veya Google Workspace hesabınız yoksa hesap oluşturmanız gerekir.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • Proje adı, bu projenin katılımcıları için görünen addır. Google API'leri tarafından kullanılmayan bir karakter dizisidir ve istediğiniz zaman güncelleyebilirsiniz.
  • Proje kimliği, tüm Google Cloud projelerinde benzersiz olmalı ve sabittir (ayarlandıktan sonra değiştirilemez). Cloud Console, benzersiz bir dizeyi otomatik olarak oluşturur. Genellikle bu dizenin ne olduğuyla ilgilenmezsiniz. Çoğu codelab'de proje kimliğine (genellikle PROJECT_ID olarak tanımlanır) başvurmanız gerekir. Bu nedenle, beğenmezseniz başka bir rastgele kimlik oluşturabilir veya kendi kimliğinizi deneyip kullanılabilir olup olmadığını görebilirsiniz. Proje oluşturulduktan sonra bu değer "dondurulur".
  • Bazı API'lerin kullandığı üçüncü bir değer olan Proje Numarası da vardır. Bu üç değer hakkında daha fazla bilgiyi belgelerde bulabilirsiniz.
  1. Ardından, Cloud kaynaklarını/API'lerini kullanmak için Cloud Console'da faturalandırmayı etkinleştirmeniz gerekir. Bu codelab'i tamamlamak neredeyse hiç maliyetli değildir. Bu eğitimin ötesinde faturalandırma ücreti alınmaması için kaynakları kapatmak üzere codelab'in sonunda bulunan "temizleme" talimatlarını uygulayın. Google Cloud'un yeni kullanıcıları 300 ABD doları değerinde ücretsiz deneme programından yararlanabilir.

Cloud Shell'i başlatma

Google Cloud, dizüstü bilgisayarınızdan uzaktan çalıştırılabilir. Ancak bu codelab'de, Cloud'da çalışan bir komut satırı ortamı olan Google Cloud Shell'i kullanacaksınız.

GCP Console'da sağ üstteki araç çubuğunda Cloud Shell simgesini tıklayın:

bce75f34b2c53987.png

Ortamın temel hazırlığı ve bağlanması yalnızca birkaç dakikanızı alır. İşlem tamamlandığında aşağıdakine benzer bir sonuç görürsünüz:

f6ef2b5f13479f3a.png

Bu sanal makine, ihtiyaç duyacağınız tüm geliştirme araçlarını içerir. 5 GB boyutunda kalıcı bir ana dizin sunar ve Google Cloud üzerinde çalışır. Bu sayede ağ performansı ve kimlik doğrulama önemli ölçüde güçlenir. Bu laboratuvardaki çalışmalarınızın tamamını yalnızca bir tarayıcı kullanarak yapabilirsiniz.

3. API'leri etkinleştir

Bu laboratuvarda, container görüntüleri oluşturmak için Cloud Build'e, container'ı dağıtmak için ise Cloud Run'a ihtiyacınız olacak.

Cloud Shell'den her iki API'yi de etkinleştirin:

gcloud services enable cloudbuild.googleapis.com \
  run.googleapis.com

İşlemin başarıyla tamamlandığını görmeniz gerekir:

Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.

4. Başka bir paket oluşturma

Yüklenen resimlerin küçük resimlerini başka bir pakette saklarsınız. İkinci paketi oluşturmak için gsutil simgesini kullanalım.

Cloud Shell'de benzersiz paket adı için bir değişken ayarlayın. Cloud Shell'de GOOGLE_CLOUD_PROJECT, benzersiz proje kimliğinize ayarlanmıştır. Bunu paket adına ekleyebilirsiniz. Ardından, Avrupa'da tek tip düzeyde erişime sahip herkese açık çok bölgeli bir paket oluşturun:

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

Sonunda yeni bir herkese açık paketin olması gerekir:

8e75c8099938e972.png

5. Kodu klonlayın

Kodu klonlayın ve hizmeti içeren dizine gidin:

git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
cd serverless-photosharing-workshop/services/thumbnails/nodejs

Hizmet için aşağıdaki dosya düzenine sahip olursunuz:

services
 |
 ├── thumbnails
      |
      ├── nodejs
           |
           ├── Dockerfile
           ├── index.js
           ├── package.json

thumbnails/nodejs klasöründe 3 dosya bulunur:

  • index.js, Node.js kodunu içerir.
  • package.json, kitaplık bağımlılıklarını tanımlar.
  • Dockerfile, container görüntüsünü tanımlar.

6. Kodu keşfetme

Kodu incelemek için Cloud Shell penceresinin üst kısmındaki Open Editor düğmesini tıklayarak yerleşik metin düzenleyiciyi kullanabilirsiniz:

3d145fe299dd8b3e.png

Daha fazla ekran alanı için düzenleyiciyi ayrı bir tarayıcı penceresinde de açabilirsiniz.

Bağımlılıklar

package.json dosyası, gerekli kitaplık bağımlılıklarını tanımlar:

{
  "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 kitaplığı, Cloud Storage'daki resim dosyalarını okumak ve kaydetmek için kullanılır. Resim meta verilerini güncellemek için Firestore. Express, bir JavaScript / Node web çerçevesidir. Body-parser modülü, gelen istekleri kolayca ayrıştırmak için kullanılır. Bluebird, sözleri işlemek için, Imagemagick ise görüntüleri işlemek için kullanılan bir kitaplıktır.

Dockerfile

Dockerfile, uygulama için container görüntüsünü tanımlar:

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" ]

Temel görüntü Node 14'tür ve görüntü işleme için ImageMagick kitaplığı kullanılır. Orijinal ve küçük resim dosyalarını tutmak için bazı geçici dizinler oluşturulur. Ardından, kodumuzun ihtiyaç duyduğu NPM modülleri, kodu npm start ile başlatmadan önce yüklenir.

index.js

Bu programın ne yaptığını daha iyi anlamak için kodu parça parça inceleyelim.

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());

Öncelikle gerekli bağımlılıkları istiyoruz ve Express web uygulamamızı oluşturuyoruz. Ayrıca, gelen istekler aslında uygulamamıza bir POST isteği aracılığıyla gönderilen JSON yükleri olduğundan JSON gövde ayrıştırıcısını kullanmak istediğimizi belirtiyoruz.

app.post('/', async (req, res) => {
    try {
        // ...
    } catch (err) {
        console.log(`Error: creating the thumbnail: ${err}`);
        console.error(err);
        res.status(500).send(err);
    }
});

Bu gelen yükleri / temel URL'sinde alıyoruz ve Google Cloud web konsolundaki Stackdriver Logging arayüzünden görülebilecek günlükleri inceleyerek kodumuzda bir şeyin neden başarısız olabileceği hakkında daha iyi bilgi sahibi olmak için kodumuzu bazı hata mantığı işleme ile sarmalıyoruz.

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 platformunda Pub/Sub mesajları, şu biçimde JSON yükleri olarak HTTP POST istekleri aracılığıyla gönderilir:

{
  "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"
}

Ancak bu JSON belgesinde gerçekten ilginç olan şey, yalnızca bir dize olan ancak gerçek yükü Base64'e kodlayan message.data özelliğinde yer alanlardır. Bu nedenle, yukarıdaki kodumuz bu özelliğin Base64 kodlu içeriğini çözüyor. Bu data özelliği, kodu çözüldüğünde Cloud Storage etkinlik ayrıntılarını temsil eden başka bir JSON belgesi içerir. Bu belge, diğer meta verilerin yanı sıra dosya adını ve paket adını belirtir.

{
  "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="
}

Kodumuz, küçük resim işlemi için bu resmi paketten alacağından resim ve paket adlarıyla ilgileniyoruz:

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}`);

Çıkış depolama paketinin adını bir ortam değişkeninden alıyoruz.

Dosya oluşturma işlemi Cloud Run hizmetimizi tetikleyen kaynak paketimiz ve sonuçta elde edilen görüntüyü depolayacağımız hedef paketimiz var. Imagemagick kitaplığı, küçük resmi yerel olarak /tmp geçici dizininde oluşturacağından yerel dosya işleme için path yerleşik API'yi kullanıyoruz. Yüklenen resim dosyasını indirmek için await bir eşzamansız çağrı yapıyoruz.

const resizeCrop = Promise.promisify(im.crop);
await resizeCrop({
        srcPath: originalFile,
        dstPath: thumbFile,
        width: 400,
        height: 400         
});
console.log(`Created local thumbnail in ${thumbFile}`);

Imagemagick modülü çok async / await dostu olmadığından, Bluebird modülü tarafından sağlanan bir JavaScript sözüyle sarmalıyoruz. Ardından, kaynak ve hedef dosyaların parametreleriyle birlikte oluşturmak istediğimiz küçük resmin boyutlarını kullanarak oluşturduğumuz eşzamansız yeniden boyutlandırma / kırpma işlevini çağırıyoruz.

await thumbBucket.upload(thumbFile);
console.log(`Uploaded thumbnail to Cloud Storage bucket ${process.env.BUCKET_THUMBNAILS}`);

Küçük resim dosyası Cloud Storage'a yüklendikten sonra, Cloud Firestore'daki meta verileri de güncelleyerek bu resmin küçük resminin gerçekten oluşturulduğunu belirten bir boole işareti ekleriz:

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`);

İsteğimiz tamamlandıktan sonra, dosyanın düzgün şekilde işlendiğini belirten HTTP POST isteğine yanıt veririz.

const PORT = process.env.PORT || 8080;

app.listen(PORT, () => {
    console.log(`Started thumbnail generator on port ${PORT}`);
});

Kaynak dosyamızın sonunda, Express'in web uygulamamızı 8080 numaralı varsayılan bağlantı noktasında başlatması için talimatlar yer alıyor.

7. Yerel olarak test etme

Buluta dağıtmadan önce çalıştığından emin olmak için kodu yerel olarak test edin.

thumbnails/nodejs klasöründe npm bağımlılıklarını yükleyin ve sunucuyu başlatın:

npm install; npm start

Her şey yolunda gittiyse sunucu 8080 numaralı bağlantı noktasında başlatılır:

Started thumbnail generator on port 8080

Çıkmak için CTRL-C öğesini kullanın.

8. Container görüntüsünü oluşturma ve yayınlama

Cloud Run, container'ları çalıştırır ancak önce container görüntüsünü (Dockerfile içinde tanımlanır) oluşturmanız gerekir. Container görüntülerini oluşturmak ve ardından Google Container Registry'de barındırmak için Google Cloud Build kullanılabilir.

thumbnails/nodejs klasöründe (Dockerfile konumunda) aşağıdaki komutu vererek kapsayıcı görüntüsünü oluşturun:

gcloud builds submit --tag gcr.io/$GOOGLE_CLOUD_PROJECT/thumbnail-service

Bir veya iki dakika sonra derleme başarılı olur:

b354b3a9a3631097.png

Cloud Build "geçmiş" bölümünde de başarılı derleme gösterilmelidir:

df00f198dd2bf6bf.png

Ayrıntılar görünümünü almak için derleme kimliğini tıkladığınızda, "derleme yapıları" sekmesinde container görüntüsünün Cloud Registry'ye (GCR) yüklendiğini görürsünüz:

a4577ce0744f73e2.png

İsterseniz container görüntüsünün Cloud Shell'de yerel olarak çalıştığını tekrar kontrol edebilirsiniz:

docker run -p 8080:8080 gcr.io/$GOOGLE_CLOUD_PROJECT/thumbnail-service

Bu komut, kapsayıcıda 8080 numaralı bağlantı noktasında sunucuyu başlatmalıdır:

Started thumbnail generator on port 8080

Çıkmak için CTRL-C öğesini kullanın.

9. Cloud Run'a dağıt

Cloud Run'a dağıtmadan önce Cloud Run bölgesini desteklenen bölgelerden biri olarak ve platformu managed olarak ayarlayın:

gcloud config set run/region europe-west1
gcloud config set run/platform managed

Yapılandırmanın ayarlandığını şu şekilde kontrol edebilirsiniz:

gcloud config list

...
[run]
platform = managed
region = europe-west1

Aşağıdaki komutu çalıştırarak kapsayıcı görüntüsünü Cloud Run'a dağıtın:

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 işaretine dikkat edin. Bu işlem, Cloud Run hizmetini yalnızca belirli hizmet hesapları tarafından tetiklenecek bir dahili hizmet haline getirir.

Dağıtım başarılı olursa aşağıdaki çıkışı görürsünüz:

c0f28e7d6de0024.png

Bulut konsolu kullanıcı arayüzüne giderseniz hizmetin başarıyla dağıtıldığını da görürsünüz:

9bfe48e3c8b597e5.png

10. Pub/Sub aracılığıyla Cloud Storage etkinliklerinden Cloud Run'a

Hizmet hazır ancak yeni oluşturulan Cloud Run hizmetinde Cloud Storage etkinlikleri oluşturmanız gerekiyor. Cloud Storage, dosya oluşturma etkinliklerini Cloud Pub/Sub üzerinden gönderebilir ancak bu işlevin çalışması için birkaç adım gerekir.

İletişim hattı olarak bir Pub/Sub konusu oluşturun:

TOPIC_NAME=cloudstorage-cloudrun-topic
gcloud pubsub topics create $TOPIC_NAME

Dosyalar pakette depolandığında Pub/Sub bildirimleri oluşturma:

BUCKET_PICTURES=uploaded-pictures-$GOOGLE_CLOUD_PROJECT
gsutil notification create -t $TOPIC_NAME -f json gs://$BUCKET_PICTURES

Daha sonra oluşturacağımız Pub/Sub aboneliği için bir hizmet hesabı oluşturun:

SERVICE_ACCOUNT=$TOPIC_NAME-sa
gcloud iam service-accounts create $SERVICE_ACCOUNT \
     --display-name "Cloud Run Pub/Sub Invoker"

Hizmet hesabına Cloud Run hizmeti çağırma izni verin:

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 hizmet hesabını 8 Nisan 2021'de veya daha önce etkinleştirdiyseniz Pub/Sub hizmet hesabına iam.serviceAccountTokenCreator rolünü verin:

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 değişikliklerinin uygulanması birkaç dakika sürebilir.

Son olarak, hizmet hesabıyla bir Pub/Sub aboneliği oluşturun:

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

Aboneliğin oluşturulup oluşturulmadığını kontrol edebilirsiniz. Konsolda Pub/Sub'a gidin, gcs-events konusunu seçin. Alt kısımda aboneliği görürsünüz:

e8ab86dccb8d890.png

11. Hizmeti test etme

Kurulumun çalışıp çalışmadığını test etmek için uploaded-pictures paketine yeni bir resim yükleyin ve thumbnails paketinde yeni boyutlandırılmış resimlerin beklendiği gibi göründüğünü kontrol edin.

Cloud Run hizmetinin çeşitli adımları uygulanırken günlük mesajlarının göründüğünü görmek için günlükleri de tekrar kontrol edebilirsiniz:

42c025e2d7d6ca3a.png

12. Temizleme (isteğe bağlı)

Serideki diğer laboratuvarlara devam etmeyi düşünmüyorsanız maliyetleri düşürmek ve genel olarak iyi bir bulut kullanıcısı olmak için kaynakları temizleyebilirsiniz. Kaynakları tek tek aşağıdaki şekilde temizleyebilirsiniz.

Paketi silme:

gsutil rb gs://$BUCKET_THUMBNAILS

Hizmeti silme:

gcloud run services delete $SERVICE_NAME -q

Pub/Sub konusunu silin:

gcloud pubsub topics delete $TOPIC_NAME

Alternatif olarak, projenin tamamını silebilirsiniz:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

13. Tebrikler!

Artık her şey hazır:

  • Yeni bir resim yüklendiğinde bir konudaki Pub/Sub mesajlarını gönderen bir Cloud Storage bildirimi oluşturuldu.
  • Gerekli IAM bağlamalarını ve hesaplarını tanımladı (her şeyin otomatikleştirildiği Cloud Functions'ın aksine, burada manuel olarak yapılandırılır).
  • Cloud Run hizmetimizin Pub/Sub mesajlarını alması için bir abonelik oluşturduk.
  • Pakete yeni bir resim yüklendiğinde, yeni Cloud Run hizmeti sayesinde resim yeniden boyutlandırılır.

İşlediğimiz konular

  • Cloud Run
  • Cloud Storage
  • Cloud Pub/Sub

Sonraki Adımlar