Kubernetesで始める 実践プラットフォームエンジニアリング 3

OSSの開発者ポータル「Backstage」で「ゴールデンパス」テンプレートを作ってみよう

第3回の今回は、OSSの開発者ポータル「Backstage」を利用して、Go言語によるシンプルなWebサーバーの「ゴールデンパス」テンプレートを作成する方法を解説します。

松田 元輝 (まつだ げんき)

6:30

はじめに

前回はGateway APIを利用して最小限のプラットフォームを構築しました。今回はプラットフォームで「ゴールデンパス」を提供します。

ゴールデンパスとは、Webアプリケーションを構築、スキャン、テスト、デプロイ、オブザーバビリティなど、開発者がプロジェクトの立ち上げから運用までの一連のプロセスを迅速かつ効率的に実行できるようにするためのベストプラクティスやツール、ワークフローのセットです。ゴールデンパスの重要な要素として「ゴールデンパステンプレート」(ソースコードと各種ワークフローのテンプレート)があります。開発者はテンプレートからプロジェクトを作成することで、必要な構成があらかじめ用意された状態でプロジェクトの開発を迅速に開始できます。

また、今回はゴールデンパステンプレートを管理するツールとして「Backstage」を利用します。BackstageはSpotifyが開発したオープンソースの開発者ポータル(IDP: Internal Developer Portal)で、組織内のツールやサービスを統合し、開発者が効率的に作業できる環境を提供します。

Backstageの機能の1つである「Software Templates」はソースコードのテンプレートを管理し、テンプレートから新しいプロジェクトを作成できる機能です。本記事では、Software Templates機能を利用して、Go言語で書かれたシンプルなWebサーバーのゴールデンパステンプレートを作成する方法を解説します。

構築する構成の説明

今回構築するのは、図1の赤い点線で囲った部分です。Namespace backstageにBackstageをデプロイし、GitHubと連携させます。GitHubはゴールデンパステンプレートの保管、および各プロダクトのコード保管場所として利用します。

Backstageの初期化

Backstageの設定やカスタマイズはTypescriptで行います。そのため、まずはBackstageのGetting Startedに従ってBackstageアプリケーションを初期化します。

BackstageはNode.js上で動作するため、事前にNode.jsをインストールしておく必要があります。今回はNode.js v24.12.0を使用して検証しています。

以下のコマンドを実行して、Backstageアプリケーションの雛形を作成します。

npx @backstage/create-app@0.7.8

以下のようにBackstageアプリケーションの名前(ディレクトリ名)が聞かれるため、任意の名前を入力します。

今回は、backstage-appと入力しました。

ローカルで実行

BackstageをKubernetesにデプロイする前に、ローカルで動作確認を行うケースは多いと思うので、その方法を紹介します。初期化したBackstageアプリのルートディレクトリでyarn startを実行するだけで動作確認ができます。

cd backstage-app/
yarn start

ブラウザでhttp://localhost:3000にアクセスすると、Backstageの初期画面が表示されます。

Kubernetesへのデプロイ

BackstageをKubernetesにデプロイするための手順を説明します。

Backstageの設定更新

まず、Backstageの設定ファイル(app-config.production.yaml)を更新します。今回は検証環境ということもありHTTPで運用します。HTTPで運用する場合、upgrade-insecure-requestsを無効化する必要があります。また、ゲストユーザーログインを許可するための設定を追加します。

backend:
+ csp:
+   upgrade-insecure-requests: false

auth:
  providers:
-   guest: {}
+   guest:
+     userEntityRef: user:default/guest
+     ownershipEntityRefs: [group:default/guests]
+     dangerouslyAllowOutsideDevelopment: true

上記は今回の検証用に特別に入れた設定で、セキュリティ上のリスクがあります。本番環境向けにはHTTPSでの運用、およびゲストユーザーでのログインを許可しない設定を推奨します。

コンテナイメージのビルド

コンテナをビルドする前に、バックエンドパッケージをビルドしておきます。

yarn install --immutable
yarn tsc
yarn build:backend

コンテナイメージをプッシュするため、コンテナレジストリにログインします。今回はBackstageを含むプラットフォームのコンテナイメージの保管に、GitHubが提供するコンテナレジストリであるGitHub Container Registryを利用します。

まず、GitHubのPersonal Access Tokenを発行します。必要な権限はwrite:packagesです。

export GHCR_PUSH_TOKEN=xxxxxx  # GitHub Personal Access Token
docker login ghcr.io -u GH_USERNAME --password $GHCR_PUSH_TOKEN

コンテナイメージをビルドし、プッシュします。IMGの部分は適宜書き換えてください。

export IMG=ghcr.io/GH_USERNAME/REPOSITORY:TAG
docker image build . -f packages/backend/Dockerfile -t $IMG
docker push $IMG

PostgreSQLのデプロイ

BackstageのデータベースとしてPostgreSQLを使用するため、Kubernetesにデプロイします。Backstage用のNamespaceを作成して、その中にPostgreSQLをデプロイします。

kubectl create namespace backstage
kubectl apply -n backstage -f https://raw.githubusercontent.com/Hitachi/oss-assets/main/article/thinkit-smallplatform/03-backstage-software-template/deploy/postgres.yaml

Image Pull Secretを作成

GitHubのPersonal Access Tokenを発行し、プライベートリポジトリからイメージをプルできるようにImage Pull Secretを作成します。必要な権限はread:packagesです。

export GHCR_PULL_TOKEN=xxxxxx  # GitHub Personal Access Token
export GH_USERNAME=xxxxxx      # GitHubのユーザー名
kubectl create secret docker-registry regcred \
  --docker-server=ghcr.io \
  --docker-username=$GH_USERNAME \
  --docker-password=$GHCR_PULL_TOKEN

Backstageのデプロイ

Backstage用のSecretを作成します。Secretに書かれた環境変数がBackstageのコンテナに渡されるようにDeploymentを設定します。今はPostgreSQLの接続情報のみを記載していますが、後ほどGitHub連携のための環境変数も追加します。

kubectl apply -n backstage -f- <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: backstage-secret
type: Opaque
stringData:
  # PostgreSQL接続情報
  POSTGRES_HOST: "postgres"
  POSTGRES_PORT: "5432"
  POSTGRES_USER: "postgres"
  POSTGRES_PASSWORD: "postgres"
  POSTGRES_DB: "backstage"
EOF

Backstageをデプロイ

kubectl apply -n backstage -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backstage
  namespace: backstage
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backstage
  template:
    metadata:
      labels:
        app: backstage
    spec:
      containers:
        - name: backstage
          image: ${IMG}
          imagePullPolicy: Always
          ports:
            - name: http
              containerPort: 7007
          envFrom:
            - secretRef:
                name: backstage-secret
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "200m"
      imagePullSecrets:
        - name: regcred
---
apiVersion: v1
kind: Service
metadata:
  name: backstage
  namespace: backstage
spec:
  selector:
    app: backstage
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 7007
  type: ClusterIP
EOF

Backstageの公開

Gateway APIを使ってBackstageを公開します。Gateway APIのセットアップ方法と解説は第2回を参照してください。

Backstageを公開するために、Gatewayを編集してHTTPRouteを作成します。第2回で作成した共有GatewayにBackstage用のリスナーを追加します。なお、公開設定のホスト名部分はご自身の環境に合わせて変更してください。

# shared-gateway/shared-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shared-gateway
  namespace: shared-gateway
spec:
  gatewayClassName: envoy-gateway
  listeners:
    # Keycloak用リスナー
    - name: keycloak
      protocol: HTTP
      port: 80
      hostname: keycloak.172.32.4.127.nip.io
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              kubernetes.io/metadata.name: keycloak
        kinds:
          - kind: HTTPRoute
+   # backstage用リスナー
+   - name: backstage
+     protocol: HTTP
+     port: 80
+     hostname: "backstage.172.32.4.127.nip.io"
+     allowedRoutes:
+       namespaces:
+         from: Selector
+         selector:
+           matchLabels:
+             kubernetes.io/metadata.name: backstage
+       kinds:
+         - kind: HTTPRoute
    # echo API用リスナー (テナント)
    - name: echo
      protocol: HTTP
      port: 80
      hostname: "echo.172.32.4.127.nip.io"
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              kubernetes.io/metadata.name: echo
        kinds:
          - kind: HTTPRoute

Backstage用のHTTPRouteを作成します。

# backstage/deploy/httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: backstage-route
spec:
  hostnames:
  - backstage.172.32.4.127.nip.io
  parentRefs:
    - name: shared-gateway
      namespace: shared-gateway
      sectionName: backstage
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: backstage
          port: 8080
          kind: Service
          weight: 1

GatewayとHTTPRouteをKubernetesに適用します。

kubectl apply -n shared-gateway -f shared-gateway/shared-gateway.yaml
kubectl apply -n backstage -f backstage/deploy/httproute.yaml

また、共有Gatewayにはデフォルトで全ての通信を拒否するAuthPolicyが設定されているため、全ての通信を許可するAuthPolicyで上書きします。

# backstage/deploy/authpolicy.yaml
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
  name: backstage-authpolicy-allow-all
  namespace: backstage
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: backstage-route
  defaults:
    rules:
      authentication:
        "anonymous":
          anonymous: {}

上記のAuthPolicyをKubernetesに適用します。

kubectl apply -n backstage -f backstage/deploy/authpolicy.yaml

Backstageにアクセス

ブラウザでhttp://backstage.172.32.4.127.nip.ioにアクセスします。

現在、ゲストユーザーしか設定していないため、ゲストユーザーでログインします。

Software Templates機能の設定

Software Templatesはソースコードのテンプレートを管理し、テンプレートから新しいプロジェクトを作成するBackstageの機能です。今回は私が作成してGitHubで公開しているゴールデンパステンプレートを例に、ゴールデンパステンプレートの構成とBackstageでの管理方法を紹介します。

私が作成したゴールデンパステンプレートは開発者が新しいプロジェクトを開始し、本番環境へのデプロイするまでのフローを支援することを目的としたもので、Go言語で書かれたシンプルなWebサーバーのテンプレートです。

このゴールデンパステンプレートは以下を含んでいます。

  • IDE(Visual Studio Code)の設定ファイル
  • OpenAPI仕様からエンドポイントのインターフェースを自動生成するスクリプト
  • Hello Worldが実装されたソースコード
  • CI(テスト、ビルド、スキャン)
  • 本番環境へのデプロイに必要なKubernetesのマニフェスト
  • ビルド方法からデプロイまでの手順を記したドキュメント

逆に、以下のような内容は今回作成するゴールデンパスには含まれません。

  • DevContainerなどの開発環境を容易に立ち上げる手段
  • GitOpsなどのCDワークフロー
  • オブザーバビリティ

テンプレートの準備

今回は私が作成したテンプレートを使って説明します。本番環境では自作テンプレートを使うことになりますが、動作確認が目的であれば私が作成したものを指定するのが手軽です。

テンプレートは、テンプレート定義ファイルtemplate.yamlとスケルトンソースコード(テンプレートから新しいプロジェクト作成時にコピーされるソースコード)を格納するskeleton/ディレクトリで構成されています。

template.yaml       # テンプレートの定義ファイル
skeleton/           # スケルトンソースコード
  ├── go.mod
  ├── go.sum
  ├── main.go
  ├── README.md
  └── ...

テンプレート定義ファイル

テンプレート定義ファイル(template.yaml)はYAML形式で記述されます。Kubernetesのマニフェストに似た形式ですが、Kubernetesにデプロイするのではなく、Backstageに読み込ませて使用します。

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: go-echo-template
  title: Go echo template
  description: GoのechoによるREST APIテンプレート
spec:
  owner: backstageadmin
  type: service
  parameters:
    # パラメーターの定義
    ...
  steps:
    # ワークフローの定義
    ...

テンプレート定義は、主に以下の2つの要素で構成されています。

  • パラメーター定義: ワークフローで使用するパラメーターを定義
  • ワークフロー: テンプレートからのリポジトリ生成プロセスを定義(ソースコードのコピー、Gitリポジトリへのプッシュなど)

パラメーター定義(spec.parameters)はJSON Schemaで記述します。すべてのパラメーターを紹介すると長くなるため、一部のみ抜粋して紹介します。

parameters:
    ...
    - title: Choose a location
      required:
        - repoUrl
      properties:
        repoUrl:
          title: Repository Location
          type: string
          ui:field: RepoUrlPicker
          ui:options:
            allowedHosts:
              - github.com

ui:field: RepoUrlPickerはリポジトリを指定するためのUIコンポーネントで、上記により以下のようなパラメーター入力画面になります。

ワークフローでは、以下の実装がされています。

  1. GitHubからスケルトンコードを取得してパラメーターを埋め込む
  2. GitHubリポジトリにコードをプッシュする
  3. Backstageのソフトウェアカタログにコンポーネントを登録する
  4. BackstageのソフトウェアカタログにAPIを登録する

「ソフトウェアカタログ」はBackstageの機能の1つで、組織内のソフトウェアコンポーネントを管理するためのカタログです。ソフトウェアカタログにコンポーネントを登録することで、ユーザーがBackstageの画面からコンポーネントの情報を検索したり、ソースコードにアクセスしたりできるようになります。また、APIを登録することで、APIドキュメントを統合して管理できます。

ワークフローは、spec.stepsに定義します。

steps:
    # スケルトンコードを取得して、パラメータを埋め込む
    - id: fetchBase
      name: Fetch Base
      action: fetch:template
      input:
        url: ./skeleton
        values:
          name: ${{ parameters.name }}
          image: ${{ parameters.image }}
          destination: ${{ parameters.repoUrl | parseRepoUrl }}
          k8sResource: ${{ parameters.k8sResource }}
          domain: ${{ parameters.domain }}
          keycloakRealm: ${{ parameters.keycloakRealm }}

    # リポジトリにコードをプッシュする
    - id: publish
      name: Publish
      action: publish:github
      input:
        description: This is ${{ parameters.name }}
        repoUrl: ${{ parameters.repoUrl }}
        defaultBranch: 'main'

    # ソフトウェアカタログにコンポーネントを登録する
    - id: registerComponent
      name: Register Component
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
        catalogInfoPath: '/catalog-info.yaml'

    # ソフトウェアカタログにAPIを登録する
    - id: registerApi
      name: Register API
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
        catalogInfoPath: '/catalog-api.yaml'

ユーザーがSoftware Templatesの画面でリポジトリ作成を実行すると、上記ワークフローの各ステップが順番に実行され、テンプレートから新しいリポジトリが作成されます。

・スケルトンソースコード
skeleton/ディレクトリには、Go言語で書かれたシンプルなWebサーバーのソースコードが含まれています。リポジトリごとに変更される部分は ${{values.<パラメーター名>}} の形式でプレースホルダーとして記述されています。プレースホルダーはテンプレート定義のワークフローのfetch:templateアクションで定義された値に置き換えられます。

例えば、go.modファイルは以下のようになります。

module github.com/${{values.destination.owner + "/" + values.destination.repo}}
go 1.25.1

GitHub Actionsのワークフロー内にある ${{ xxx }} のような表現は、そのままではBackstageがプレースホルダーと誤認して置き換えてしまいます。これを避けるには ${{ '${{ xxx }}' }} のようにエスケープして記載するとリポジトリ作成後に ${{ xxx }} が残ります。以下はGitHub Actionsのワークフローの例です。

- name: Build and push Docker image
      id: build
      uses: docker/build-push-action@v5
      with:
        context: .
        platforms: linux/amd64,linux/arm64
        push: true
        tags: ${{ '${{ steps.meta.outputs.tags }}' }}       # エスケープ
        labels: ${{ '${{ steps.meta.outputs.labels }}' }}   # エスケープ
        cache-from: type=gha,scope=build
        cache-to: type=gha,mode=max,scope=build

Backstageの設定更新

Backstageのapp-config.production.yamlに以下の設定を追加します。

catalog:
  locations:
+   - type: url
+     target: https://github.com/makihh/software-templates/blob/main/templates/go-echo/template.yaml
+     rules:
+       - allow: [Template]

+integrations:
+ github:
+   - host: github.com
+     token: ${GITHUB_TOKEN}

catalog.locationsにはテンプレート定義ファイルのURLを指定します。これによりBackstageがテンプレート定義ファイルを読み込み、Software Templatesの画面にテンプレートを表示します。また、integrations.githubはGitHubの認証情報を追加し、テンプレートのワークフローでGitHubリポジトリからコードを取得・プッシュできるようにします。

GitHubの設定

GitHubとBackstageを連携するための設定を行います。

・GitHub Personal Access Token作成
BackstageをGitHubに連携するため、Personal Access Token(classic)を作成します。詳細はGitHubのドキュメントを参照してください。

必要な権限は以下の通りです。

  • repo(全て)
  • workflow
  • read:org
  • read:user
  • user:email

今回はPersonal Access Tokenを使いましたが、本番環境ではGitHub Appsの使用を推奨します。

・Secretの更新
GitHubのPersonal Access TokenをBackstage用のSecretに追加します。

export GITHUB_TOKEN="xxxxxx"  # GitHub Personal Access Token

kubectl apply -n backstage -f- <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: backstage-secret
type: Opaque
stringData:
  # PostgreSQL接続情報
  POSTGRES_HOST: "postgres"
  POSTGRES_PORT: "5432"
  POSTGRES_USER: "postgres"
  POSTGRES_PASSWORD: "postgres"
  POSTGRES_DB: "backstage"
  # GitHub連携 (追加)
  GITHUB_TOKEN: "${GITHUB_TOKEN}"
EOF

動作確認

  1. コンテナイメージをビルドしなおして、Podを再起動します。

    docker image build . -f packages/backend/Dockerfile -t $IMG
    docker push $IMG
    kubectl rollout restart deployment backstage -n backstage
  2. Backstageにアクセスして、KindをTemplateでフィルタします。先ほど追加した「Go echo template」テンプレートが表示されるので、選択します。

  3. テンプレートの詳細画面で「LAUNCH TEMPLATE」ボタンをクリックして、リポジトリ作成を開始します。

  4. 各種パラメーターを入力します。

  5. 確認画面で「CREATE」ボタンをクリックします。

  6. ワークフローが実行され、テンプレートからリポジトリが作成されます。

  7. 作成したコンポーネントがコンポーネント一覧に表示されるので、選択します。

  8. コンポーネントとして登録されます。「VIEW SOURCE」をクリックすると、作成されたGitHubリポジトリに遷移します。

  9. スケルトンコードにはデプロイ手順が記載されており、開発者がアプリケーションをKubernetesに容易にデプロイできます。

また、ワークフローはAPIドキュメントをBackstageに登録するように構成されており、OpenAPI形式のドキュメントが統合されます。これにより、APIの利用者は開発者ポータルから最新のAPI仕様を確認できます。

利用したソフトウェアのバージョン

コンポーネントバージョン
create-app0.7.8
Node.jsv24.12.0
Backstage1.47.0
PostgreSQL17.0

まとめ

Backstageを導入し、Software Templates機能を利用してGo言語でのシンプルなWebサーバーのゴールデンパステンプレートを作成しました。これにより、開発者はテンプレートから新しいプロジェクトを迅速に開始でき、開発プロセスの効率化が期待できます。ゴールデンパスの整備により、プラットフォームエンジニアリングの成熟度モデルのインターフェース レベル2の要素の1つが満たされました。

ただし、本実装ではCDが含まれず手動でデプロイする必要があります。実運用ではGitOpsやオブザーバビリティなどの要素も含めて、より実用的なゴールデンパスを提供することが望ましいです。

また、Backstage導入前後での開発プロセスの変化を下表にまとめます。変わっていない箇所は今後の連載の中で改善していきます。

大プロセス小プロセスプラットフォーム導入前第2回第3回のゴールデンパステンプレート導入後
セキュリティ要件定義認証要件の設計手動Keycloakで標準化Keycloakで標準化
認可要件の設計手動Authorinoで部分自動化Authorinoで部分自動化
OpenAPI仕様作成OpenAPIの設計1から手書き1から手書きテンプレートから開発開始
セキュリティスキーマの定義1から手書きKeycloak定義Keycloak定義
API仕様の公開メール送付などメール送付などBackstageで公開
実装APIの実装手動実装手動実装OpenAPI仕様からエンドポイントのインターフェースを自動生成 
認証・認可の実装コード実装Authorinoで自動化Authorinoで自動化
ルーティング設定手動設定Gateway APIで宣言管理Gateway APIで宣言管理
Kubernetesマニフェスト実装1から手書きテンプレートから開発
デプロイと運用デプロイ手動Kubernetesで自動化Kubernetesで自動化
監視・可観測性の導入ログ出力と手動確認ログ出力と手動確認ログ出力と手動確認
ポリシー準拠チェック手動レビュー手動レビュー手動レビュー
認可ポリシーの連携手動実装手動実装手動実装

次回は、ユーザーからのフィードバックを収集できるよう、Backstageにフィードバック収集機能を追加していきます。

この記事をシェアしてください

人気記事トップ10

人気記事ランキングをもっと見る

企画広告も役立つ情報バッチリ! Sponsored