Kubernetes (GKE) または Cloud Run のコンテナを監視し、その状況を LED で可視化します。
デモ参加者は QR コードからサイトにアクセスすることで負荷かけツールが起動、コンテナがスケールし始めます。
しばらくすると、増えていくコンテナに応じて LED パネルが光り始めます。
- Instance: GKE または Cloud Run 上で稼働するサンプルプログラム。自分自身の状態を一定周期で Firestore に保存
- Controller: LED 制御機能 + バックグラウンドで Instance の状態を LED 用のデータに整形 Firestore に書き込み
- K8s: Instance や Controller を Kubernetes 上にデプロイするためのマニフェスト
- Load-Gen: Hey を使って GKE や Cloud Run に対して負荷を生成
- Raspberry Pi: 毎秒 Firestore から LED 用のデータを読み込み、Teensy に送信
- Teensy: 受け取ったデータに基づき LED パネルを制御
- UI: GKE 用
- UI-CR: Cloud Run 用
-
有効な請求先アカウントとプロジェクトを作成
-
ローカル クライアントの認証
gcloud auth login
gcloud config set project <your-project-id>
- 利用サービスの有効化
gcloud services enable compute.googleapis.com firestore.googleapis.com \
container.googleapis.com run.googleapis.com artifactregistry.googleapis.com
- Firestore データベースの作成
gcloud app create --region "asia-northeast1"
gcloud firestore databases create --database "demo" --type "firestore-native" --location "asia-northeast1"
- Artifact Registry にリポジトリを作成
gcloud artifacts repositories create demo \
--repository-format "docker" --location "asia-northeast1" \
--description "Docker repository for demo apps"
- ローカル確認用に、認証情報を生成
gcloud auth application-default login
- アプリケーションをビルドし、ローカルで起動
docker build -t instance instance/
docker run --name instance -d --rm -u $(id -u):$(id -g) -p 8080:8080 \
-v "${HOME}/.config/gcloud:/gcp/config:ro" -e CLOUDSDK_CONFIG=/gcp/config \
-e GOOGLE_APPLICATION_CREDENTIALS=/gcp/config/application_default_credentials.json \
-e PROJECT_ID=$( gcloud config get-value project ) -e FIRESTORE_DATABASE=demo \
-e INSTANCE_COLLECTION=cr-instances -e HOSTNAME=instance instance
- API にアクセス、挙動を確認
time curl -iXGET localhost:8080/wait?s=5
curl -iXGET localhost:8080/status
docker logs -f instance
docker stop instance
- Firestore 上のデータを確認
Firestore コンソール にアクセスし、確認してみましょう。
- Artifact Registry への push
問題がなさそうであれば Artifact Registry へ docker push します。
repo="asia-northeast1-docker.pkg.dev/$( gcloud config get-value project )/demo"
docker tag instance "${repo}/instance:v0.7"
gcloud auth configure-docker asia-northeast1-docker.pkg.dev
docker push "${repo}/instance:v0.7"
- アプリケーションをビルドし、ローカルで起動
docker build -t controller controller/
docker run --name controller -d --rm -u $(id -u):$(id -g) -p 8000:8000 \
-v "${HOME}/.config/gcloud:/gcp/config:ro" -e CLOUDSDK_CONFIG=/gcp/config \
-e GOOGLE_APPLICATION_CREDENTIALS=/gcp/config/application_default_credentials.json \
-e PROJECT_ID=$( gcloud config get-value project ) -e FIRESTORE_DATABASE=demo \
-e INSTANCE_COLLECTION=cr-instances -e LED_COLLECTION=cr -e CONTROLLER_FOR="Cloud Run" \
-e GAMMA="20.0" controller
- Web にアクセス、挙動を確認
ログも確認し、問題がなければ停止します。
docker logs -f controller
docker stop controller
- Firestore 上のデータを確認
Firestore コンソール にアクセスし、確認してみましょう。
- Artifact Registry への push
問題がなさそうであれば Artifact Registry へ docker push します。
docker tag controller "${repo}/controller:v0.8"
docker push "${repo}/controller:v0.8"
- アプリケーション用のサービスアカウントを作成
gcloud iam service-accounts create demo-apis \
--display-name "SA for demo apis" \
--description "Service Account for demo APIs"
export project_id=$( gcloud config get-value project )
gcloud projects add-iam-policy-binding "${project_id}" \
--member "serviceAccount:demo-apis@${project_id}.iam.gserviceaccount.com" \
--role "roles/datastore.user"
- Instance サービスのデプロイ
割り当てを確認し、必要なら増やします。
gcloud run deploy demo-instance --platform "managed" --region "asia-northeast1" \
--image "${repo}/instance:v0.7" --cpu 1.0 --memory 512Mi --no-cpu-throttling \
--concurrency 1 --min-instances 0 --max-instances 4096 \
--ingress "internal-and-cloud-load-balancing" --allow-unauthenticated \
--set-env-vars "PROJECT_ID=${project_id},FIRESTORE_DATABASE=demo,INSTANCE_COLLECTION=cr-instances,LED_COLLECTION=cr" \
--service-account "demo-apis@${project_id}.iam.gserviceaccount.com"
- ロードバランサーの設置
VPC ネットワーク、プロキシ専用サブネットを作ります。
gcloud compute networks create demo-network --subnet-mode "custom"
gcloud compute networks subnets create demo-tokyo --network "demo-network" \
--range "10.1.2.0/24" --region "asia-northeast1"
gcloud compute networks subnets create proxy-only-subnet \
--purpose "REGIONAL_MANAGED_PROXY" --role "ACTIVE" \
--network "demo-network" --region "asia-northeast1" \
--range "10.129.0.0/23"
接続先の静的 IP アドレスを確保しつつ、
gcloud compute addresses create demo-instance-cr \
--region "asia-northeast1" --network-tier "STANDARD"
HTTP で接続可能なロードバランサを設置します。
gcloud compute network-endpoint-groups create neg-instance-cr \
--region "asia-northeast1" --network-endpoint-type "serverless" \
--cloud-run-service "demo-instance"
gcloud compute backend-services create demo-instance-cr --region "asia-northeast1" \
--load-balancing-scheme "EXTERNAL_MANAGED" --protocol "HTTP"
gcloud compute backend-services add-backend demo-instance-cr --region "asia-northeast1" \
--network-endpoint-group "neg-instance-cr" \
--network-endpoint-group-region "asia-northeast1"
gcloud compute url-maps create url-instance-cr --region "asia-northeast1" \
--default-service "demo-instance-cr"
gcloud compute target-http-proxies create proxy-instance-cr \
--region "asia-northeast1" --url-map "url-instance-cr"
gcloud compute forwarding-rules create demo-instance-cr \
--load-balancing-scheme "EXTERNAL_MANAGED" --network-tier "STANDARD" \
--region "asia-northeast1" --network "demo-network" \
--target-http-proxy-region "asia-northeast1" \
--address "demo-instance-cr" --ports "80" \
--target-http-proxy "proxy-instance-cr"
echo "http://$( gcloud compute addresses describe demo-instance-cr \
--region "asia-northeast1" --format json | jq -r .address )/"
- GKE クラスタの作成
GKE Autopilot の場合
export project_id=$( gcloud config get-value project )
gcloud container clusters create-auto demo-led --release-channel "stable" \
--network "demo-network" --subnetwork "demo-tokyo" --region "asia-northeast1"
GKE Standard の場合
export project_id=$( gcloud config get-value project )
gcloud container clusters create demo --release-channel "stable" \
--machine-type "e2-standard-4" --num-nodes 1 --min-nodes 1 --max-nodes 100 \
--enable-autoscaling --workload-pool="${project_id}.svc.id.goog" \
--network "demo-network" --subnetwork "demo-tokyo" --zone "asia-northeast1-c" \
--gateway-api "standard" --enable-image-streaming
- Workload Identity 経由でのアプリ用サービスアカウント利用を許可
gcloud iam service-accounts add-iam-policy-binding \
"demo-apis@${project_id}.iam.gserviceaccount.com" \
--member "serviceAccount:${project_id}.svc.id.goog[default/demo-apis]" \
--role roles/iam.workloadIdentityUser
- デプロイ
環境依存の設定値をファイルに書き出し、
cat << EOF >k8s/instance/setters.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: setters
data:
project-id: "${project_id}"
image-id: "asia-northeast1-docker.pkg.dev/${project_id}/demo/instance:v0.7"
k-service-account: "demo-apis"
g-service-account: "demo-apis@${project_id}.iam.gserviceaccount.com"
EOF
Kpt でレンダリングしたマニフェストを apply します。
kpt fn render k8s/instance/ -o unwrap | kubectl apply -f -
- LB & HPA の設置
kubectl apply -f k8s/instance-lb
ロードバランサが設定されるまで数分かかります。
しばらくしてから以下のコマンドで得られる URL にアクセスし、応答があることを確認します。
echo "http://$( kubectl get gateways.gateway.networking.k8s.io instance -o json \
| jq -r ".status.addresses[0].value" )/"
GKE と Cloud Run、それぞれの Controller を GKE 上にデプロイします。
環境依存の設定値をファイルに書き出し、
export project_id=$( gcloud config get-value project )
cat << EOF >k8s/controller/setters.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: setters
data:
project-id: "${project_id}"
image-id: "asia-northeast1-docker.pkg.dev/${project_id}/demo/controller:v0.8"
k-service-account: "demo-apis"
EOF
Kpt でレンダリングしたマニフェストを apply します。
kpt fn render k8s/controller/ -o unwrap | kubectl apply -f -
ロードバランサが設定されるまで数分かかります。
しばらくしてから以下のコマンドで得られる URL にアクセスしてみましょう。
echo "http://$( kubectl get services controller-cloudrun \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}' )/"
echo "http://$( kubectl get services controller-gke \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}' )/"
- アプリケーションの確認
docker build -t loadgen load-gen/
docker run --name loadgen -d --rm -p 9000:9000 \
-e PROJECT_ID=$( gcloud config get-value project ) -e PORT=9000 \
-e URL="http://$( gcloud compute addresses describe demo-instance-cr \
--region 'asia-northeast1' --format 'json' | jq -r '.address' )/" \
-e REQUEST=100 -e CONCURRENCY=50 -e DURATION=20 -e TIMEOUT=5 \
-e ENVIRONMENT='Cloud Run' -e K_REVISION=dev loadgen
Web にアクセスし、負荷をかけてみます。
問題がなければ停止します。
docker stop loadgen
- Artifact Registry への push
repo="asia-northeast1-docker.pkg.dev/$( gcloud config get-value project )/demo"
docker tag loadgen "${repo}/loadgen:v0.7"
docker push "${repo}/loadgen:v0.7"
- Load generator サービスのデプロイ
Cloud Run への負荷かけサービスをデプロイします。
export project_id=$( gcloud config get-value project )
gcloud run deploy demo-loadgen-cr --platform "managed" --region "asia-northeast1" \
--image "${repo}/loadgen:v0.7" --cpu 4.0 --memory 2Gi \
--concurrency 1 --min-instances 0 --max-instances 100 \
--set-env-vars "PROJECT_ID=${project_id},ENVIRONMENT=Cloud Run,URL=http://$( gcloud compute \
addresses describe demo-instance-cr --region 'asia-northeast1' --format 'json' \
| jq -r '.address' )/wait?s=5,REQUEST=1000,CONCURRENCY=700,DURATION=30,TIMEOUT=7" \
--allow-unauthenticated --timeout '10m'
GKE への負荷かけサービスもデプロイします。
gcloud run deploy demo-loadgen-gke --platform "managed" --region "asia-northeast1" \
--image "${repo}/loadgen:v0.7" --cpu 4.0 --memory 2Gi \
--concurrency 1 --min-instances 0 --max-instances 100 \
--set-env-vars "PROJECT_ID=${project_id},ENVIRONMENT=GKE,URL=http://$( kubectl get \
gateways.gateway.networking.k8s.io instance -o json \
| jq -r ".status.addresses[0].value" )/wait?s=5,REQUEST=1000,CONCURRENCY=700,DURATION=30,TIMEOUT=7" \
--allow-unauthenticated --timeout '10m'
https://console.firebase.google.com/ にアクセスし、Firebase プロジェクトを作成します。
- CLI のインストール・認証
npm install -g firebase-tools
firebase login --no-localhost
- プロジェクト ID を設定ファイルに保存
export project_id=$( gcloud config get-value project )
sed "s/project-id/${project_id}/" ui/.firebaserc.template > ui/.firebaserc
sed "s/project-id/${project_id}/" ui/firebase.json.template > ui/firebase.json
sed "s/project-id/${project_id}/" ui-cr/.firebaserc.template > ui-cr/.firebaserc
sed "s/project-id/${project_id}/" ui-cr/firebase.json.template > ui-cr/firebase.json
- UI for Cloud Run のビルドとデプロイ
Firebase Hosting に新規サイトを追加します。
firebase hosting:sites:create "${project_id}-led-cloudrun" --project "${project_id}"
Firebase コンソール プロジェクトの設定
から Web アプリ ui-cr
を追加し、
ui/src/app.js
の設定値を書き換え、ビルドします。
cd ui-cr
npm install
npm run build
firebase deploy
cd ..
- UI for GKE のビルドとデプロイ
Firebase Hosting に新規サイトを追加します。
firebase hosting:sites:create "${project_id}-led-gke" --project "${project_id}"
Firebase コンソール プロジェクトの設定
から Web アプリ ui
を追加し、
ui/src/app.js
の設定値を書き換え、ビルドします。
cd ui
npm install
npm run build
firebase deploy
cd ..
- Application Default Credentials (ADC) の再作成
gcloud auth application-default login
- Raspberry Pi への SSH・フォルダ作成
pi_user=google-cloud-japan
pi_host=192.168.1.1
ssh ${pi_user}@${pi_host} mkdir -p /home/${pi_user}/app \
/home/${pi_user}/.config/pip /home/${pi_user}/.config/gcloud
- ファイル転送
scp raspberry-pi/main.py ${pi_user}@${pi_host}:/home/${pi_user}/app/main.py
scp raspberry-pi/requirements.txt ${pi_user}@${pi_host}:/home/${pi_user}/app/requirements.txt
scp "${HOME}/.config/gcloud/application_default_credentials.json" \
${pi_user}@${pi_host}:/home/${pi_user}/.config/gcloud/creds.json
- プログラム実行(Cloud Run)
SSH で端末に入り
ssh ${pi_user}@${pi_host}
依存関係を解決し、プログラムを試験的に実行します。
cat << EOF >~/.config/pip/pip.conf
[global]
break-system-packages = true
EOF
cd app/
python3 -m venv .
source ./bin/activate
pip install -r requirements.txt
GOOGLE_APPLICATION_CREDENTIALS=$HOME/.config/gcloud/creds.json PROJECT_ID=xxxxx \
FIRESTORE_DB=demo LED_COLLECTION=cr python main.py
問題なければスタートアップスクリプトとして登録します。環境変数を設定して、
cat << EOF >led-envs
GOOGLE_APPLICATION_CREDENTIALS=$HOME/.config/gcloud/creds.json
PROJECT_ID=xxxxx
FIRESTORE_DB=demo
LED_COLLECTION=cr
EOF
sudo mkdir -p /etc/sysconfig
sudo mv led-envs /etc/sysconfig/
Systemd のサービスとして登録して、再起動します。
cat << EOF >led.service
[Unit]
Description=LED Auto Scaling Containers
[Service]
EnvironmentFile=/etc/sysconfig/led-envs
ExecStart=/home/google-cloud-japan/app/bin/python3 /home/google-cloud-japan/app/main.py
[Install]
WantedBy=multi-user.target
EOF
sudo mv led.service /etc/systemd/system/
sudo systemctl enable led
sudo reboot
再起動後、正常に動作しているかを確かめてみます。
sudo systemctl status led
journalctl -xfeu led
- プログラム実行(GKE)
GKE 用 Raspberry Pi にも同様にファイルを転送、依存解決し、プログラムを試験的に実行、
問題なければスタートアップスクリプトとして登録します。
環境変数の設定が Cloud Run と一部異なることに注意してください。
cat << EOF >/etc/sysconfig/led
GOOGLE_APPLICATION_CREDENTIALS=$HOME/.config/gcloud/creds.json
PROJECT_ID=xxxxx
FIRESTORE_DB=demo
LED_COLLECTION=gke
EOF
Arduino IDE を使い、Teensy 2 つにプログラム next-demo-basic.ino
を転送します。
Teensy は受け取ったデータを表示するだけなので、同じプログラムで問題ありません。
export project_id=$( gcloud config get-value project )
cat << EOF
Instance on Cloud Run > http://$( gcloud compute addresses describe demo-instance-cr \
--region "asia-northeast1" --format json | jq -r .address )/
Instance on GKE > http://$( kubectl get gateways.gateway.networking.k8s.io instance \
-o json | jq -r ".status.addresses[0].value" )/
Controllers on Cloud Run > http://$( kubectl get services controller-cloudrun \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}' )/
Controllers on GKE > http://$( kubectl get services controller-gke \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}' )/
Load-Gen for Cloud Run > $( gcloud run services describe demo-loadgen-cr \
--region "asia-northeast1" --format 'value(status.url)' )
Load-Gen for GKE > $( gcloud run services describe demo-loadgen-gke \
--region "asia-northeast1" --format 'value(status.url)' )
UI for Cloud Run > https://${project_id}-led-cloudrun.web.app
UI for GKE > https://${project_id}-led-gke.web.app
EOF