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

1. Genel Bakış

Bu kod laboratuvarında, önceki laboratuvarı geliştirecek ve bir küçük resim hizmeti ekleyeceksiniz. Küçük resim hizmeti, büyük resimler çeken ve bunlardan küçük resimler oluşturan bir web kapsayıcısıdır.

Resim Cloud Storage'a yüklenirken, Cloud Pub/Sub aracılığıyla bir Cloud Run web container'ına bildirim gönderilir. Bu bildirim, görüntüleri yeniden boyutlandırıp Cloud Storage'daki başka bir pakete kaydeder.

31fa4f8a294d90df.png

Neler öğreneceksiniz?

  • Cloud Run
  • Cloud Storage
  • Cloud Pub/Sub

2. Kurulum ve Gereksinimler

Kendi hızınızda ortam kurulumu

  1. Google Cloud Console'da oturum açıp 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 dizesidir ve bunu istediğiniz zaman güncelleyebilirsiniz.
  • Proje Kimliği, tüm Google Cloud projelerinde benzersiz olmalıdır ve değiştirilemez (belirlendikten sonra değiştirilemez). Cloud Console, otomatik olarak benzersiz bir dize oluşturur. bunun ne olduğunu umursamıyorsunuz. Çoğu codelab'de, Proje Kimliğine referans vermeniz gerekir (ve bu kimlik genellikle PROJECT_ID olarak tanımlanır). Beğenmezseniz başka bir rastgele kod oluşturun ya da kendi proje kimliğinizi deneyip mevcut olup olmadığına bakın. Sıcaklık "soğudu" takip etmeniz gerekir.
  • Bazı API'lerin kullandığı üçüncü bir değer, yani Proje Numarası daha vardır. Bu değerlerin üçü 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 çalıştırmanın maliyeti, yüksek değildir. Bu eğitim dışında faturalandırmayla karşılaşmamak için kaynakları kapatmak istiyorsanız tüm "temizleme" işlemlerini uygulayın buradaki talimatları uygulayın. Yeni Google Cloud kullanıcıları, 300 ABD doları değerindeki ücretsiz denemeden yararlanabilir.

Cloud Shell'i başlatma

Google Cloud dizüstü bilgisayarınızdan uzaktan çalıştırılabilse de 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 yer alan Cloud Shell simgesini tıklayın:

bce75f34b2c53987.png

Ortamı sağlamak ve bağlamak yalnızca birkaç dakika sürer. Tamamlandığında şuna benzer bir sonuç görmeniz gerekir:

f6ef2b5f13479f3a.png

İhtiyacınız olan tüm geliştirme araçlarını bu sanal makinede bulabilirsiniz. 5 GB boyutunda kalıcı bir ana dizin sunar ve Google Cloud üzerinde çalışarak ağ performansını ve kimlik doğrulamasını büyük ölçüde iyileştirir. Bu laboratuvardaki tüm çalışmalarınızı yalnızca bir tarayıcıyla yapabilirsiniz.

3. API'leri etkinleştir

Bu laboratuvarda, container görüntüleri derlemek için Cloud Build, container'ı dağıtmak için 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öreceksiniz:

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

4. Başka bir paket oluştur

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

Cloud Shell'in içinde benzersiz paket adı için bir değişken ayarlayın. Cloud Shell'de GOOGLE_CLOUD_PROJECT, benzersiz proje kimliğinize zaten ayarlanmış. Bunu paket adına ekleyebilirsiniz. Ardından tek tip düzeyde erişimle, Avrupa'da herkese açık çok bölgeli 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

Sonuç olarak, yeni bir herkese açık paketiniz olur:

8e75c8099938e972.png

5. Kodu klonlama

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ünün içinde 3 dosya var:

  • 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 inceleyin

Kodu keşfetmek 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ı elde etmek için düzenleyiciyi özel 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'da görüntü dosyalarını okumak ve kaydetmek için kullanılır. Resim meta verilerini güncellemek için Firestore. Express, bir JavaScript / Düğüm web çerçevesidir. Gövde ayrıştırıcı modülü, gelen istekleri kolayca ayrıştırmak için kullanılır. Bluebird, verilen görevleri yerine getirmek için kullanılıyor. Imagemagick, resimleri değiştirmek için kullanılan bir kitaplıktır.

Dockerfile

Dockerfile, uygulamanın 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ü Düğüm 14'tür ve imagemagick kitaplığı, görüntü işleme için kullanılır. Orijinal dosyaları ve küçük resim dosyalarını saklamak için bazı geçici dizinler oluşturulur. Ardından, kodumuzun gerektirdiği NPM modülleri, kod npm start ile başlatılmadan önce yüklenir.

index.js

Bu programın işlevini daha iyi anlayabilmemiz için kodu parçalar halinde 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());

İlk olarak, gerekli bağımlılıkları gerekli kılıp 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 de 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üklemeleri / temel URL'den alıyoruz ve Google Cloud web konsolundaki Stackdriver Logging arayüzünde görülebilecek günlüklere bakarak kodumuzdaki bir şeyin neden başarısız olabileceğine dair daha iyi bilgi edinmek amacıyla kodumuzu hata mantığı işlemeyle 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çimdeki JSON yükleri şeklinde 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 asıl ilginç olan, yalnızca bir dize olan ancak gerçek yükü Base 64'e kodlayan message.data özelliğinin içinde yer almasıdır. Bu nedenle, yukarıdaki kodumuz bu özelliğin Base 64 içeriğinin kodunu çözmektedir. Kodu çözüldüğünde bu data özelliği, 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ı da 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 kullanımı için bu resmi paketten getireceğinden resim ve grup 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şturulması Cloud Run hizmetimizi tetikleyen kaynak paketimiz ve üretilen 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şlemeyi gerçekleştirmek için path yerleşik API'sini kullanıyoruz. Yüklenen resim dosyasını indirmek üzere eşzamansız bir çağrı için 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 modülü pek de async / await uyumlu değil. Bu nedenle, söz konusu modülü bir JavaScript taahhüdü (Bluebird modülü tarafından sağlanır) kapsamında tamamladık. Ardından, kaynak ve hedef dosyaların parametreleri ve 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 bittikten sonra, dosyanın düzgün bir ş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ı gerçekten 8080 varsayılan bağlantı noktasında başlatmasını sağlama talimatlarını bulabilirsiniz.

7. Yerel olarak test et

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

thumbnails/nodejs klasörünün içinde, npm bağımlılarını yükleyin ve sunucuyu başlatın:

npm install; npm start

Her şey yolundaysa sunucu, 8080 numaralı bağlantı noktasından başlatılmalıdır:

Started thumbnail generator on port 8080

Çıkmak için CTRL-C tuşunu kullanın.

8. Container görüntüsünü derleyip yayınlayın

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

Dockerfile klasörünün bulunduğu thumbnails/nodejs klasöründe kapsayıcı görüntüsünü oluşturmak için aşağıdaki komutu verin:

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

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

b354b3a9a3631097.png

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

df00f198dd2bf6bf.png

"Derleme yapıları" bölümünde ayrıntılar görünümünü almak için derleme kimliğini tıklama sekmesinde container görüntüsünün Cloud Registry'ye (GCR) yüklendiğini göreceksiniz:

a4577ce0744f73e2.png

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

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

Sunucu, kapsayıcıdaki bağlantı noktası 8080'den başlatılır:

Started thumbnail generator on port 8080

Çıkmak için CTRL-C tuşunu kullanın.

9. Cloud Run'a dağıt

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

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

Yapılandırmanın ayarlanıp ayarlanmadığını kontrol edebilirsiniz:

gcloud config list

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

Container görüntüsünü Cloud Run'da dağıtmak için aşağıdaki komutu çalıştırı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 dahili bir hizmet haline getirir.

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

c0f28e7d6de0024.png

Cloud Console kullanıcı arayüzüne gittiğinizde hizmetin başarıyla dağıtıldığını da görürsünüz:

9bfe48e3c8b597e5.png

10. Cloud Storage etkinliklerini Pub/Sub üzerinden Cloud Run'a taşıma

Hizmet hazır ancak yeni oluşturulan Cloud Run hizmetine Cloud Storage etkinlikleri oluşturmanız gerekiyor. Cloud Storage, dosya oluşturma etkinliklerini Cloud Pub/Sub üzerinden gönderebilir. Ancak bu işlevi gerçekleştirmenin birkaç adımı vardır.

İletişim ardışık düzeni 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şturun:

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

Pub/Sub aboneliği için daha sonra oluşturacağımız 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 bir Cloud Run hizmetini ç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 öncesinde 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

Bir aboneliğin oluşturulup oluşturulmadığını kontrol edebilirsiniz. Konsolda Pub/Sub'a gidin, gcs-events konusunu seçin ve en altta aboneliği göreceksiniz:

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, yeniden boyutlandırılan yeni resimlerin beklendiği gibi görünüp görünmediğini kontrol edin.

Ayrıca, Cloud Run hizmetinin çeşitli adımları izleyeceğinden günlük kaydı mesajlarının görüntülendiğini görmek için günlükleri bir kez daha kontrol edebilirsiniz:

42c025e2d7d6ca3a.png

12. Temizleme (İsteğe bağlı)

Serideki diğer laboratuvarlarla devam etmeyi düşünmüyorsanız maliyet tasarrufu yapmak ve genel olarak iyi bir bulut vatandaşı olmak için kaynakları temizleyebilirsiniz. Kaynakları aşağıda açıklandığı şekilde tek tek temizleyebilirsiniz.

Paketi silin:

gsutil rb gs://$BUCKET_THUMBNAILS

Hizmeti silin:

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 yerli yerinde:

  • Cloud Storage'da, yeni bir resim yüklendiğinde bir konuda Pub/Sub mesajları gönderen bir bildirim oluşturuldu.
  • Gerekli IAM bağlamalarını ve hesapları tanımlayın (tamamen otomatik olduğu 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şturuldu.
  • Pakete her yeni resim yüklendiğinde, yeni Cloud Run hizmeti sayesinde resim yeniden boyutlandırılır.

İşlediklerimiz

  • Cloud Run
  • Cloud Storage
  • Cloud Pub/Sub

Sonraki Adımlar