Backstage でたわむれる on Google Cloud
Google Cloud Japan の RyuSA です 👍
最近 Backstage という IDP(Internal Developer Portal) が Platform Engineering の文脈で取り上げられることが増えてきています。Backstage はソフトウェアカタログやテンプレートなどをまとめ、開発者向けのポータルサイトを作り上げることができます。Backstage にはデフォルトで備え付けられている様々な機能がありますが、実際に運用するにはむしろその他のプラグインを多く利用することになります。
本記事では、Google Cloud を活用して Backstage を良い感じにデプロイしてみよう!ということを目標としています。紹介する内容は、Platform Engineering Kaigi 2024の会場でもデモとして公開していました。
Backstage プロジェクトの初期化
詳細は公式ドキュメントを参照してください。
今回は Node.js v20.x を利用して初期化します。Backstage は Prometheus や Istio などのように設定で振る舞いを変更するようなプロダクトではなく、自分で Backstage プロジェクトを初期化、実装、ビルドしデプロイする必要があります。まず npx
でプロジェクトを初期化し、ローカルで動作することを確認します。
$ npx @backstage/create-app@latest
? Enter a name for the app [required] backstage
Creating the app...
🥇 Successfully created backstage
$ cd backstage
$ yarn dev
ローカル開発時、デフォルトで3000(フロントエンド)と7007(バックエンド)のポートを利用します。ブラウザから3000番ポートへアクセスするとトップページが見れます。
Backstage を Google Cloud へデプロイする
Backstage を動かすために、今回は Backstage のドキュメントに記載されている構成(https://backstage.io/docs/deployment/k8s)を参考に次のような構成を組みます。
Cloud SQL for PostgreSQL をバックエンドデータベースとして利用し、コンテナイメージを Artifact Registry へ保存、GKE 上で Backstage そのものを動作させます。
# Artifact Registry を作成
$ gcloud artifacts repositories create backstage-ar --repository-format=docker --location=asia-northeast1
# Cloud SQL インスタンスを作成
$ gcloud sql instances create backstage-db \
--database-version=POSTGRES_13 \
--tier=db-f1-micro \
--network=default \
--no-assign-ip
$ gcloud sql users set-password postgres --instance=backstage-db --password=$POSTGRES_PASSWORD
# GKE Autopilot を作成
$ gcloud container clusters create-auto "backstage-gke" --region "asia-northeast1"
次にこのプロジェクトルートで以下のコマンドを実行してコンテナイメージをビルド、Artifact Registry へアップロードします。ただしビルドする前におまじないを app-config.production.yaml
へ追加しておいてください。
auth:
providers:
- guest: null
+ # これはProduction環境ではguest認証が無効となるので、強制的にguest認証を有効にする方法です。
+ # TODO 本番環境リリース前に変更する
+ guest:
+ dangerouslyAllowOutsideDevelopment: true
# プロジェクトのビルド
$ yarn build:all
# コンテナイメージのビルド
$ yarn build-image --tag asia-northeast1-docker.pkg.dev/$PROJECT_ID/backstage-ar/backstage:latest
# push
$ docker push asia-northeast1-docker.pkg.dev/$PROJECT_ID/backstage-ar/backstage:latest
最後に GKE へデータベースへの認証情報やコンテナイメージをデプロイして動作することを確認します。
# Cloud SQL への接続情報を Secret として作成
$ kubectl create secret generic backstage-secret \
--from-literal=POSTGRES_HOST=$(gcloud sql instances describe backstage-db --format='value(ipAddresses.ipAddress)') \
--from-literal=POSTGRES_USER=postgres \
--from-literal=POSTGRES_PASSWORD=$POSTGRES_PASSWORD
# Service Account(Kubernetes) を作成
$ kubectl create serviceaccount backstage
# マニフェストを作成してデプロイする
$ cat <<EOF > manifest.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backstage
spec:
replicas: 1
selector:
matchLabels:
app: backstage
template:
metadata:
labels:
app: backstage
spec:
serviceAccount: backstage
containers:
- image: asia-northeast1-docker.pkg.dev/$PROJECT_ID/backstage-ar/backstage:latest
name: backstage
ports:
- containerPort: 7007
name: http
envFrom:
- secretRef:
name: backstage-db
resources:
limits:
memory: 8Gi
cpu: 1500m
EOF
$ kubectl apply -f manifest.yaml
$ kubectl expose deployment backstage 7007:7007
# 動作検証のため、7007番ポートを開放してローカルから確認できるようにしておきます
$ kubectl port-forward service/backstage 7007:7007
ローカルのブラウザからアクセスできることを確認しましょう。Backstage のトップページが表示されれば成功です。
ソフトウェアカタログを作成する
Backstage はソフトウェアの一覧や依存関係を管理するソフトウェアカタログの機能を提供しています。そのソフトウェアカタログは Entity という概念で構成されています。この Entity はチームやソフトウェア、API など様々なものを記述し、それらに関係性を持たせたり Annotation をつけることで Backstage 上でいい感じに見たり検索したりできるようになります。
Entity の種類には組み込みで様々なものが定義されています。
- Component
- ソフトウェアのユニットやサービスを表現するもので、ひとつの Git リポジトリと大体一緒の概念として扱うことが多いです
- Resource
- Componentを維持するためのインフラストラクチャを表します、Cloud SQL インスタンスやGKE クラスタなど
- Template
- ソフトウェアテンプレートの作成手順を表します、本記事ではスキップ。Backstage Software Templates | Backstage
- Group, User
- Entity のオーナーシップの媒体を表す特別な Entity。外部の認証サービスと連携して動的に作成されることもあります。
たとえば、ひとつ前の節で GKE へデプロイした Backstage を表す Entity は次のような構成として表現できます。
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: MyBackstage
description: Hello Backstage
spec:
type: service
lifecycle: experiments
owner: guests
dependsOn:
- resource:default/backstage-db
- resource:default/backstage-gke
- resource:default/backstage-ar
---
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
name: backstage-db
description: Cloud SQL for PostgreSQL
links:
- url: https://console.cloud.google.com/sql/instances/backstage-db/overview
title: backstage-db | Google Cloud Console
spec:
type: database
owner: user:guest
---
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
name: backstage-gke
description: GKE Autopilot
links:
- url: https://console.cloud.google.com/kubernetes/clusters/details/asia-northeast1/backstage-gke/details
title: backstage-gke | Google Cloud Console
spec:
type: kubernetes-cluster
owner: guests
---
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
name: backstage-ar
description: Artifact Registry
spec:
type: container-registry
owner: guests
---
apiVersion: backstage.io/v1alpha1
kind: User
metadata:
name: guest
spec:
memberOf: [guests]
---
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
name: guests
spec:
type: team
children: []
この Entity を含む YAML ファイルを Backstage へ読み込ませることで、この Backstage が必要とする依存関係などを簡単に確認できるようになります。ローカルの環境で試してみます。
この YAML ファイルをローカルの catalog-info.yaml へ保存し、ローカル向けの設定ファイル app-config.local.yaml を作成してください。その後 Backstage を起動すると Backstage が catalog-info.yaml を読み込み、Component リストに MyBackstage が登録されるようになります。
catalog:
rules:
- allow: [Component, Resource, Location, User, Group]
locations:
- type: file
target: ../../catalog-info.yaml
Techdocs を使ってドキュメントをセットアップする
Backstage はマークダウンで記述されたドキュメントを Entity と紐つける Techdocs を提供しています。どこかにホスティングされているドキュメントを取得し、Backstage 上でドキュメントを読む/検索できます。これにより、他のチームメンバーや横断チームのような自チーム以外のメンバーがコンポーネントの情報へリーチしやすくなります。
まずはローカルで動作することを確認してみます。docs/index.md を作成して適当なマークダウンを記載して保存してください。
My Backstage
===
This is a sample techdocs.
次に catalog-info.yaml の MyBackstage に Techdocs 用の Annnotation を付与します。これにより MyBackstage コンポーネントの Techdocs 用の設定とドキュメントのありかを設定でき、Backstage がその情報を取得できるようになります。
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: MyBackstage
description: Hello Backstage haha
+ annotations:
+ backstage.io/techdocs-ref: dir:.
+ backstage.io/techdocs-entity: component:default/MyBackstage
spec:
...
最後に、内部のドキュメントビルダーが利用する mkdocs.yaml をプロジェクトルート上に作成して保存します。
site_name: My Backstage
nav:
- Home: index.md
plugins:
- techdocs-core
この状態で Backstage の MyBackstage のページへ移動すると、docs/index.md で作成したドキュメントがビルドされ HTML としてレンダリングされています。
Backstage の Techdocs は app-config.yaml の techdocs.builder
の値を読み取り、値がデフォルトの local
と指定されている場合ドキュメントページをアドホックにビルドし、マークダウンから HTML へ変換しています。この設定はアドホックにビルドされてしまいCPUリソースなどを多く消費してしまい、本番環境には推奨されていません。本番稼働させる際には techdocs.builder=external
として指定しアドホックビルドを抑制し、ドキュメントは事前にビルド&ホストしておくことが推奨されています。
コンポーネントを Kubernetes リソースと連携する
Backstage には Kubernetes と接続して「このコンポーネントがどのクラスター/名前空間/リソースで動いているか」といった情報を一緒に表示できるようにするプラグイン @backstage/plugin-kubernetes
が提供されています。これによりコンポーネントがどの Kubernetes クラスターのどの Deployment (あるいは Statefulset など)で表現されているのか、またはその接続名などを Backstage 上で確認できるようになります。 MyBackstage
は GKE 上で動かしている Deployment と Service に紐ついています。それぞれの Kubernetes リソースにラベリングをし、Backstage の Entity と紐つけましょう。
まずは依存関係をアップデートします。プロジェクトルート上で次の yarn コマンドでプロジェクトの依存関係を更新します。
# フロントエンドプロジェクトの更新
$ yarn --cwd packages/app add @backstage/plugin-kubernetes
# バックエンドプロジェクトの更新
$ yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend
まずはフロントエンドに Kubernetes リソースを確認できるようなタブを追加します。packages/app/src/components/catalog/EntityPage.tsx を開き、次のように変更して保存します。
...
+import { EntityKubernetesContent } from '@backstage/plugin-kubernetes';
...
const websiteEntityPage = (
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
{overviewContent}
</EntityLayout.Route>
<EntityLayout.Route path="/ci-cd" title="CI/CD">
{cicdContent}
</EntityLayout.Route>
+ <EntityLayout.Route path="/kubernetes" title="Kubernetes">
+ <EntityKubernetesContent refreshIntervalMs={30000} />
+ </EntityLayout.Route>
...
さらにバックエンドサーバーにも Kubernetes のプラグインをインストールします。packages/backend/src/index.ts を次のように修正してバックエンドシステムへプラグインを登録します。
...
+// kubernetes plugin
+backend.add(import('@backstage/plugin-kubernetes-backend/alpha'));
backend.start();
次にローカル環境の Backstage を GKE に接続します。app-config.local.yaml を以下のように修正します。
...
+ kubernetes:
+ serviceLocatorMethod:
+ type: 'multiTenant'
+ clusterLocatorMethods:
+ - type: 'gke'
+ projectId: PROJECT_ID
+ region: REGION
+ skipTLSVerify: true
+ authProvider: 'googleServiceAccount'
これで Backstage は PROJECT_ID プロジェクト内の REGION リージョンの GKE と接続できるようになりました。Backstage の Kubernetes Integration の機能では backstage.io/kubernetes-id
ラベルが付与されている Kubernetes リソースをピックアップします。そこで Deployment リソースにラベルを付与します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: backstage
+ labels:
+ backstage.io/kubernetes-id: mybackstage
spec:
replicas: 1
...
template:
metadata:
labels:
app: backstage
+ backstage.io/kubernetes-id: mybackstage
spec:
...
これにより GKE にデプロイされている状況を確認できるようになりました。ローカルで再度 Backstage を起動すると、Backstage は GKE へ接続して MyBackstage に紐つく Kubernetes リソースを調査してくれます。Backstage から MyBackstage コンポーネントのページを開くと、新たに Kubernetes タブが生えており、そこから GKE 内の情報を確認できるようになります。
ソフトウェアカタログを更新する
Backstage が追加の依存を持つようになったので catalog-info.yaml を更新しておきます。MyBackstage の依存関係を増やし、外部の catalog-info.yaml を Location 型で登録します。
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: MyBackstage
...
spec:
...
dependsOn:
- resource:default/backstage-db
- resource:default/backstage-gke
- resource:default/backstage-ar
+ - component:default/backstage-plugin-kubernetes
...
+---
+apiVersion: backstage.io/v1alpha1
+kind: Location
+metadata:
+ name: libraries
+spec:
+ type: url
+ targets:
+ - https://github.com/backstage/backstage/blob/v1.30.4/plugins/kubernetes/catalog-info.yaml
ローカルで起動している Backstage に反映されていれば完了です。このように Backstage では利用している依存関係(インフラストラクチャー、ライブラリ、API など)を相互に接続して一覧にできます。
Backstage を本番環境として Google Cloud へデプロイする
この節では Backstage を本番環境で動かすためのセットアップを進めます。今までローカルで検証してきた内容を、今度は GKE 上にデプロイされた環境で動かすための設定をしていきます。Backstage は組み込みのビルドの機構としてプロジェクト初期化時にプロジェクトルートに作成される app-config.production.yaml を利用して本番向けのコンテナイメージを作成します。この YAML ファイルを変更しつつ、本番環境向けの設定をします。
Workload Identity のセットアップをする
Backstage の Pod から Google Cloud の各種 API を利用するために、Service Account(Kubernetes) から Google Cloud の認証を通すための設定をしておきます。まずはプロジェクト内に Service Account(Google Cloud) を作成しいくつかの IAM Role を付与、さらにその Service Account(Google Cloud) と Service Account(Kubernetes) を Workload Identity 経由で紐つけます。
参考: GKE ワークロードから Google Cloud APIs に対する認証を行う | Google Cloud
# Service Account(Google Cloud)を作成する
$ gcloud iam service-accounts create backstage
# Service Account(Google Cloud)とプロジェクトのいくつかのロールとを紐つける
$ gcloud projects add-iam-policy-binding $PROJECT_ID \
--member "serviceAccount:backstage@$PROJECT_ID.iam.gserviceaccount.com" \
--role "roles/viewer"
$ gcloud projects add-iam-policy-binding $PROJECT_ID \
--member "serviceAccount:backstage@$PROJECT_ID.iam.gserviceaccount.com" \
--role "roles/container.viewer"
$ gcloud projects add-iam-policy-binding $PROJECT_ID \
--member "serviceAccount:backstage@$PROJECT_ID.iam.gserviceaccount.com" \
--role=roles/storage.objectViewer
# Service Account(Google Cloud)とService Account(Kubernetes)を紐つけ、Service Account(Kubernetes)からGoogle Cloudへアクセスできるようにする
$ gcloud iam service-accounts add-iam-policy-binding backstage@$PROJECT_ID.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:$PROJECT_ID.svc.id.goog[default/backstage]"
# Service Account(Kubernetes)に紐つけ情報を連携する
$ kubectl annotate serviceaccount backstage \
iam.gke.io/gcp-service-account=backstage@$PROJECT_ID.iam.gserviceaccount.com
これにより Backstage の Pod が利用する Service Account(Kubernetes) は Service Account(Google Cloud) と連携して Google Cloud 上のリソースへアクセスができるようになりました。
catalog-info.yaml
を利用する
リモートに存在する ソフトウェアカタログを作成するの節で作成した catalog-info.yaml と app-config.local.yaml はローカルで完結しており、GKE 上で動いている Backstage には反映されていません。コンテナイメージに本番用にカタログ情報を埋め込んでも良いですがそれでは柔軟にカタログを更新できず、新規プロジェクト作成やライブラリ利用への障壁が高くなってしまいます。
Backstageにはリモート上にホストされている catalog-info.yaml を読み取って動的に構造化する機能が提供されています。Integrations | Backstage Docs
今回は Cloud Storage 上にホストされている catalog-info.yaml を利用して Backstage に Entity を読み込ませます。まずホストするためのバケットを作成し、ローカルで作成した catalog-info.yaml を作成したバケットにアップロードします。
# GCSバケットを作成する
$ gsutil mb YOUR_BUTCKET_NAME
# ローカルのcatalog-info.yamlをGCSバケットにコピーする
$ gsutil cp ./catalog-info.yaml gs://YOUR_BUTCKET_NAME/
次に GKE 上の Backstage がリモートのソフトウェアカタログを読めるように app-config.production.yaml を修正してコンテナイメージを再ビルドし再デプロイします。
+integrations:
+ googleGcs: {}
catalog:
+ processingInterval:
+ minutes: 1
+ rules:
+ - allow: [Component, System, API, Resource, Location, Group, User]
- locations: []
+ locations:
+ - type: url
+ target: "https://storage.cloud.google.com/YOUR_BUTCKET_NAME/catalog-info.yaml"
# コンテナイメージをビルドしてArtifact Registryへプッシュ、GKEへデプロイする
$ yarn build:all
$ yarn build-image --tag asia-northeast1-docker.pkg.dev/$PROJECT_ID/backstage-ar/backstage:latest
$ docker push asia-northeast1-docker.pkg.dev/$PROJECT_ID/backstage-ar/backstage:latest
$ kubectl rollout restart deployment backstage
ロールアウト完了後、GKE 上の Backstage はバケットからソフトウェアカタログを読み取り Entity が見えるようになっていることを確認します。
最後にローカルの catalog-info.yaml を更新して依存関係を増やし、バケットへアップロードしておきます。数分すると GKE 上の Backstage で情報が更新されています。
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: MyBackstage
...
spec:
...
dependsOn:
- resource:default/backstage-db
- resource:default/backstage-gke
- resource:default/backstage-ar
- component:default/backstage-plugin-kubernetes
+ - resource:default/backstage-gcs
...
+---
+apiVersion: backstage.io/v1alpha1
+kind: Resource
+metadata:
+ name: backstage-gcs
+ description: Cloud Storage for Backstge
+spec:
+ type: s3-bucket
+ owner: user:guest
Techdocs のセットアップ
ローカル環境にて Techdocs を動作させることができたので、次は GKE へデプロイされている環境に Techdocs を設定します。ローカルで開発する場合はローカル上のドキュメントをアドホックにビルドしていましたが、実際の本番環境ではパフォーマンスなどの観点から事前にビルド&ホストしておくことが推奨されています。Backstage には Cloud Storage をドキュメントリモートストレージとしてドキュメントをホストするプラグインが実装されています。今回はその方針を利用するアーキテクチャを採用します。
まずは Cloud Storage のバケットを作成します。(すでに作成済みのバケットでも構いません)
$ gsutil mb YOUR_BUTCKET_NAME
次に techdocs-cli
を使いローカルのドキュメントを本番向けにビルドし、Cloud Storage のバケットへデプロイします。npx
から利用可能です。
# ドキュメントをビルド
$ npx @techdocs/cli generate
# Cloud Storageのバケットへ公開
$ npx @techdocs/cli publish \
--publisher-type googleGcs \
--storage-name YOUR_BUTCKET_NAME \
--entity default/Component/MyBackstage
これでビルド済みのドキュメントをビルド&デプロイできました。このドキュメントを GKE 上の Backstage から読めるように修正します。ローカルのプロジェクトの app-config.production.yaml を修正し、コンテナイメージを再ビルドしてデプロイします。
+ techdocs:
+ builder: 'external'
+ publisher:
+ type: 'googleGcs'
+ googleGcs:
+ bucketName: YOUR_BUTCKET_NAME
+ projectId: ${PROJECT_ID}
# コンテナイメージをビルドしてArtifact Registryへプッシュ、GKEへデプロイする
$ yarn build-image --tag asia-northeast1-docker.pkg.dev/$PROJECT_ID/backstage-ar/backstage:latest
$ docker push asia-northeast1-docker.pkg.dev/$PROJECT_ID/backstage-ar/backstage:latest
$ kubectl rollout restart deployment backstage
ロールアウトが完了するとコンポーネントのページからドキュメントを読むことができるようになります。
Google Cloud の Observability と統合する
本番環境で Backstage を稼働させる場合、やはり可観測性は欲しいですよね。Backstage は OpenTelemetry API を利用してメトリクスやトレース情報を公開できるように実装されています。これを Google Cloud の Observerbility と統合してみましょう。
各種依存関係を先に追加しておきます。
$ yarn --cwd packages/backend add \
@opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-prometheus \
@opentelemetry/sdk-trace-base \
@google-cloud/opentelemetry-cloud-trace-exporter
参考: Setup OpenTelemetry | Backstage
Managed Prometheus
Backstage はデフォルトでいくつかのカスタムメトリクスを提供しています。Managed Prometheus は Google Cloud プロジェクト上の様々な Prometheus 形式のメトリクスを収集して Cloud Monitoring の監視に統合できるサービスです。今回は Backstage のメトリクスを Managed Prometheus に取り込みます。
まずはローカル環境でプロジェクトを変更していきます。packages/backend/o11y/instrumentation.js を次の通りに作成します。
const { NodeSDK } = require('@opentelemetry/sdk-node');
const {
getNodeAutoInstrumentations,
} = require('@opentelemetry/auto-instrumentations-node');
const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus');
const prometheus = new PrometheusExporter();
const sdk = new NodeSDK({
metricReader: prometheus,
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
process.on('SIGTERM', () => {
sdk.shutdown()
.then(() => console.log('OpenTelemetrySDK gracefully terminated'))
.catch(err => console.log('Failed to terminate OpenTelemetrySDK gracefully', err));
});
この実装ではPrometheus形式のメトリクスを公開し、またSIGTERM
を受信するとSDKをGracefulにシャットダウンするようになっています。詳細はOpenTelemetrySDKのドキュメントを参照ください。Getting Started - Nodejs| OpenTelemetry
さらに Backstage 起動時にこのスクリプトを同時に起動させる必要があるため、--require
オプションでランタイムに読み込ませるようにします。packages/backend/package.json を次のように修正し、yarn workspace backend start
コマンドを変更します。
"scripts": {
- "start": "backstage-cli package start",
+ "start": "backstage-cli package start --require ./o11y/instrumentation.js",
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"build-image": "docker build ../.. -f Dockerfile --tag backstage"
},
この状態で Backstage を起動すると、:9464/metrics
で Prometheus 形式のメトリクスが公開されます。
$ curl localhost:9464/metrics
# HELP http_server_duration Measures the duration of inbound HTTP requests.
# UNIT http_server_duration ms
# TYPE http_server_duration histogram
http_server_duration_count{http_scheme="http",http_method="GET",net_host_name="localhost",http_flavor="1.1",http_status_code="200",net_host_port="7007",http_route="/"} 3
http_server_duration_sum{http_scheme="http",http_method="GET",net_host_name="localhost",http_flavor="1.1",http_status_code="200",net_host_port="7007",http_route="/"} 1350.739066
...
ローカルで動作の確認ができるようになりました。次にこれをビルドしてコンテナイメージへ組み込み、GKE へデプロイします。まずはコンテナイメージにテレメトリ収集スクリプトを追加し、起動コマンドを修正します。
RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \
yarn install --frozen-lockfile --production --network-timeout 300000
+# Copy instrumentation.js for observability
+COPY --chown=node:node packages/backend/o11y/instrumentation.js ./
+
# Then copy the rest of the backend bundle, along with any other files we might want.
COPY --chown=node:node packages/backend/dist/bundle.tar.gz app-config*.yaml ./
RUN tar xzf bundle.tar.gz && rm bundle.tar.gz
-CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"]
+CMD ["node", "--require", "./instrumentation.js", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"]
コンテナイメージをビルド&プッシュ後に GKE へデプロイすることでメトリクス収集ができるようになります。さらに Managed Prometheus がメトリクスを収集するための設定をします。GKE Autopilot 内にデフォルトでデプロイされている Collector に Backstage のメトリクスポートへクロールしてもらえるように、メトリクスのポートを Deployment に登録し、そのポートをクロールするような PodMonitoring リソースを作成します。
...
spec:
serviceAccount: backstage
containers:
- envFrom:
- secretRef:
name: backstage-db
image: asia-northeast1-docker.pkg.dev/PROJECT_ID/backstage-ar/backstage:latest
name: backstage
ports:
- containerPort: 7000
name: http
+ - containerPort: 9464
+ name: metrics
resources:
limits:
cpu: "2"
ephemeral-storage: 1Gi
memory: 8Gi
requests:
cpu: "2"
ephemeral-storage: 1Gi
memory: 8Gi
...
apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
name: mybackstage
spec:
selector:
matchLabels:
app: backstage
endpoints:
- port: metrics
path: /metrics
interval: 30s
それぞれを GKE へデプロイし、数分ほどすると Metrics Explorer からメトリクスを確認できるようになります。
Cloud Trace でトレース情報を収集する
Backstage はその特性上、本番運用を目標とする場合カスタムプラグインなどを実装することが多いです。その場合プラグイン内でのパフォーマンス調査などのためにトレーシングを入れたくなることがあるでしょう。packages/backend/o11y/instrumentation.js を少し修正し、Backstage 内のトレーシングを Cloud Trace へ連携できるようにします。
...
const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus');
+const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base");
+const { TraceExporter } = require("@google-cloud/opentelemetry-cloud-trace-exporter");
const prometheus = new PrometheusExporter();
+const trace = new TraceExporter(projectId=PROJECT_ID);
+const span = new BatchSpanProcessor(trace)
const sdk = new NodeSDK({
metricReader: prometheus,
+ traceExporter: trace,
instrumentations: [getNodeAutoInstrumentations()],
+ spanProcessors: [span],
});
console.log('OpenTelemetrySDK successfully started');
...
instrumentation.js を修正後、最後にコンテナイメージをビルドしてデプロイすると Backstage の様々なトレース情報を Cloud Trace から確認できます。たとえば Backstage にデフォルトで実装されているトレース TaskPipelineLoop
は次のように表示されます。
Google Cloud の認証情報を連携する
Backstageには認証の機構が組み込まれており、様々な認証形式を連携することができます。Authentication in Backstage | Backstage 特に Google Cloud と統合して使える認証方式として「Googleアカウントでログイン」「IAPでログイン」の2つが提供されています。
いずれも OAuth2.0 アプリケーションとして認証情報を構成し、前者はポップアップからの認証、後者はロードバランサーからの JWT での認証を行います。それぞれの構成方法を解説すると文量が倍になってしまうので簡略いたしますが、いずれも Google Cloud プロジェクトや Google Workspace と統合することができるため、普段の開発時の認証とシームレスに統合できます。
おわり
Backstage はよく Platform Engineering の文脈における IDP として紹介されることも多々あります。残念ながら本記事では Backstage の多くの機能を紹介できておらず、特にソフトウェアテンプレートは強力な機能のひとつですが紹介できていません。ソフトウェアテンプレートはプロジェクトフォーマットの共通化はもちろん、認知負荷の高い問題への知見や知識を共有することができる便利な機能です。多くの記事で紹介されておりますので、ぜひ確認してみてください。Backstage Software Templates | Backstage
そして、もし Google Cloud 上で Backstage の開発/運用を考えることがあれば本記事を参考いただければと思います。
今回は紹介できませんでしたが、たとえば「Google Workspace と連携して動的にユーザーを管理する」「Cloud SQL への接続を Cloud SQL Auth Proxy に切り替える」「Cloud Storage 上のドキュメントを Vertex AI Search に組み込んでセマンティック検索を実装する」など、Backstage on Google Cloud でできることはまだまだありますので、ぜひ試してみてください。
Discussion