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

「Backstage」でユーザーからのフィードバックを収集する仕組みを作ってみよう

第4回の今回は、「Backstage」にKeycloak認証とentity-feedbackプラグインを導入し、ユーザーからのフィードバックを収集する仕組みを構築する方法について解説します

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

6:30

はじめに

本連載も4回目となり、コンテナオーケストレーション、APIゲートウェイ、認証認可、ゴールデンパステンプレートとプラットフォームの機能を拡充してきました。新しい機能を追加していくことも重要ですが、これまで追加した機能を振り返り、改善にいかすことも大切です。

そこで今回は、これまで作った機能でユーザー(プロダクトチーム)がどのような体験をしているのか、ユーザーは満足しているのかを把握するため、ユーザーからのフィードバックを収集する仕組みを追加していきます。ユーザーからのフィードバックをプラットフォームに反映させるプロセスは「プラットフォームエンジニアリングの成熟度モデル」でも重要な要素の1つとして挙げられています。

今回、フィードバックの仕組みに利用するのは、Backstageの「entity-feedback」プラグインです。これにより、ユーザーは各機能に対するフィードバックをプラットフォームチームへ伝えられるようになります。

また、entity-feedbackプラグインでフィードバックを行えるのは認証済みユーザーのみです。そのため、まずはBackstageをKeycloakに連携させ、ユーザー認証を行えるようにしていきます。

なお、今回利用したソースコードはこちらに格納しています。

Backstageのセットアップ

第3回で構築したBackstageはバージョンの関係でKeycloakとの連携が少し複雑になってしまうため、最新版のBackstageを新たにセットアップして、そこにKeycloak連携とフィードバック機能の追加を行っていきます。 詳しい説明は第3回の解説を参照してください。

まずは、以下のコマンドを実行して、Backstageの初期セットアップを行います。

npx @backstage/create-app@0.8.2

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

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

Keycloak連携

BackstageをKeycloakに連携させるための手順を説明します。 今回、ユーザーのログインだけでなく、ユーザーの管理もKeycloakで行いたいので、以下の2つの設定を行います。

  • Keycloakでユーザーがログインできるようにする設定
  • KeycloakのユーザーとグループをBackstageに同期する設定

ここでは簡単な手順を示します。 詳しい情報が必要な方は、以下のドキュメントを参照してください。

フロントエンドコード修正

フロントエンドにKeycloakでログインするための設定を追加していきます。 なお、Keycloakのユーザーとグループを同期する設定はフロントエンド側に機能を持たないため、フロントエンドで設定する項目はありません。

Keycloakでのログインページの設定を追加します。

# packages/app/src/App.tsx
 import { createApp } from '@backstage/frontend-defaults';
 import catalogPlugin from '@backstage/plugin-catalog/alpha';
 import { navModule } from './modules/nav';
+import {
+  OpenIdConnectApi,
+  ProfileInfoApi,
+  BackstageIdentityApi,
+  SessionApi,
+} from '@backstage/core-plugin-api';
+import { OAuth2 } from '@backstage/core-app-api';
+import { SignInPageBlueprint } from '@backstage/plugin-app-react';
+import { SignInPage } from '@backstage/core-components';
+import {
+  createApiRef,
+  createFrontendModule,
+  configApiRef,
+  discoveryApiRef,
+  oauthRequestApiRef,
+  ApiBlueprint,
+} from '@backstage/frontend-plugin-api';
+
+const keycloakAuthApiRef = createApiRef<
+  OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
+>().with({
+  id: 'auth.keycloak',
+});
+
+const keycloakAuthApi = ApiBlueprint.make({
+  name: 'keycloak',
+  params: defineParams =>
+    defineParams({
+      api: keycloakAuthApiRef,
+      deps: {
+        discoveryApi: discoveryApiRef,
+        oauthRequestApi: oauthRequestApiRef,
+        configApi: configApiRef,
+      },
+      factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
+        OAuth2.create({
+          configApi,
+          discoveryApi,
+          oauthRequestApi,
+          environment: configApi.getOptionalString('auth.environment'),
+          provider: {
+            id: 'oidc',
+            title: 'Keycloak',
+            icon: () => null,
+          },
+          defaultScopes: ['openid', 'profile', 'email'],
+          popupOptions: {
+            size: {
+              width: 900,
+              height: 600,
+            },
+          },
+        }),
+    }),
+});
+
+const signInPage = SignInPageBlueprint.make({
+  params: {
+    loader: async () => props =>
+      (
+        <SignInPage
+          {...props}
+          provider={{
+            id: 'keycloak-auth-provider',
+            title: 'Keycloak',
+            message: 'Sign In using Keycloak',
+            apiRef: keycloakAuthApiRef,
+          }}
+        />
+      ),
+  },
+});

 export default createApp({
-  features: [catalogPlugin, navModule],
+  features: [
+    catalogPlugin,
+    navModule,
+    createFrontendModule({
+      pluginId: 'app',
+      extensions: [keycloakAuthApi, signInPage],
+    }),
+  ],
 });

バックエンドコード修正

Backstageのバックエンドに以下のプラグインを追加します。

  • @backstage/plugin-auth-backend-module-oidc-provider: OIDC認証の設定を行うプラグイン
  • @backstage-community/plugin-catalog-backend-module-keycloak: KeycloakのユーザーとグループをBackstageに同期するためのプラグイン
yarn workspace backend add @backstage/plugin-auth-backend-module-oidc-provider @backstage-community/plugin-catalog-backend-module-keycloak

Backstageのバックエンドコードに、上記2つのプラグインを初期化する設定を追加します。

# packages/backend/src/index.ts
+backend.add(import('@backstage/plugin-auth-backend-module-oidc-provider'));
+backend.add(
+  import('@backstage-community/plugin-catalog-backend-module-keycloak'),
+);

backend.start();

設定ファイルの書き換え

Backstageの設定ファイル(app-config.production.yaml)にKeycloakとの連携設定を追加します。${AUTH_OIDC_METADATA_URL}のような変数は、KubernetesのSecretから環境変数として渡される想定です。

# 認証認可設定
auth:
+ environment: production
+ session:
+   secret: ${AUTH_SESSION_SECRET}
  providers:
    guest:
      userEntityRef: user:default/guest
      ownershipEntityRefs: [group:default/guests]
      dangerouslyAllowOutsideDevelopment: true
+   oidc:
+     production:
+       metadataUrl: ${AUTH_OIDC_METADATA_URL}
+       clientId: ${AUTH_OIDC_CLIENT_ID}
+       clientSecret: ${AUTH_OIDC_CLIENT_SECRET}
+       prompt: auto
+       signIn:
+         resolvers:
+           - resolver: emailMatchingUserEntityProfileEmail

catalog:
+ providers:
+   # Keycloakのユーザーとグループを同期する設定
+   keycloakOrg:
+     default:
+       baseUrl: ${CATALOG_KEYCLOAK_BASE_URL}
+       loginRealm: ${CATALOG_KEYCLOAK_LOGIN_REALM}
+       realm: ${CATALOG_KEYCLOAK_REALM}
+       clientId: ${CATALOG_KEYCLOAK_CLIENT_ID}
+       clientSecret: ${CATALOG_KEYCLOAK_CLIENT_SECRET}
+       schedule:
+         frequency: { seconds: 30 } # テスト用に短く設定
+         timeout: { minutes: 3 }

Keycloakの設定

ここでは、プロダクトチームの開発者を管理するためのRealm platformを作成します。 そして、Realm platformに以下のクライアントを作成します。

クライアントID用途
backstage-auth認証認可
backstage-sync-user-groupユーザーとグループの同期

Realm platform作成

KeycloakでRealmを作成します。

Realm nameにplatformを入力し、Createをクリックします。

backstage-authクライアント作成

以下の設定で認証認可用のクライアントを作成する手順を示します。URL(http://backstage.172.32.4.127.nip.io)部分は、ご自身の環境に合わせて変更してください。

  • Client ID: backstage-auth
  • Client authenticationを有効化
  • Standard flowにチェック
  • Valid redirect URIs: http://backstage.172.32.4.127.nip.io/api/auth/oidc/handler/frame
  • Web origins: http://backstage.172.32.4.127.nip.io

クライアントを作成します。

Client IDにbackstage-authを入力し、Nextをクリックします。

Client authenticationを有効にします。続いて、Standard flowにチェックが入っていることを確認し、Nextをクリックします。なお、Standard flowはデフォルトでチェックされています。

Valid redirect URIsにhttp://backstage.172.32.4.127.nip.io/api/auth/oidc/handler/frame、Web originsにhttp://backstage.172.32.4.127.nip.ioを設定してSaveをクリックし、クライアントを作成します。

クライアントを作成したら、クライアントシークレットをメモしておきます。

backstage-sync-user-groupクライアント作成

以下の設定でユーザーとグループの同期用のクライアントを作成する手順を示します。

  • Client ID: backstage-sync-user-group
  • Client authenticationを有効にする
  • Service accounts rolesにチェックを入れる
  • サービスアカウントロールにquery-groupsquery-usersview-usersを追加する

クライアント作成画面で、Client IDにbackstage-sync-user-groupを入力し、Nextをクリックします。

Client authenticationを有効にし、Service accounts rolesにチェックを入れます。また、Standard flowは使用しないため、チェックを外しておきます。

Login settingsは特に入力せず、Saveをクリックしてクライアントを作成します。

次に、サービスアカウントロールを設定します。

query-groupsquery-usersview-usersにチェックを入れて、Assignをクリックします。realm-managementで検索すると見つけやすいです。

クライアントシークレットをメモしておきます。

Backstage用のSecret作成

Backstageの設定ファイル(app-config.production.yaml)で参照されている環境変数をKubernetesのSecretに設定します。

GitHub Personal access tokens作成

Keycloak連携とは直接関係ないのですが、フィードバックプラグインで評価の対象とするソフトウェアテンプレートで利用するGitHubのPersonal access tokens(classic)を作成します。詳細はGitHubのドキュメントを参照してください。

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

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

発行したトークンは、BackstageのSecretに設定するためにメモしておきます。なお、今回は簡略化のためPersonal access tokensを使いましたが、本番環境ではGitHub Appsの使用を推奨します。

セッションシークレットの生成

Backstageのセッション管理で使用されるセッションクッキーの署名と、検証のためのランダムな文字列を生成します。

export AUTH_SESSION_SECRET=$(openssl rand -hex 32)

シークレット作成

Backstage用のSecretを作成します。 環境変数はそれぞれの環境に合わせて設定してください。

export BACKSTAGE_URL="http://backstage.172.32.4.127.nip.io"
export KC_BASE_URL="http://keycloak.172.32.4.127.nip.io"
export KC_REALM="platform"
export KC_OIDC_URL="${KC_BASE_URL}/realms/${KC_REALM}/.well-known/openid-configuration"
export KC_AUTH_CLIENT_SECRET="<backstage-authクライアントのシークレット>"
export KC_SYNC_CLIENT_SECRET="<backstage-sync-user-groupクライアントのシークレット>"
export GITHUB_TOKEN="<GitHub連携用のトークン>"

kubectl apply -n backstage -f- <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: backstage-secret
type: Opaque
stringData:
  # BackstageのURL
  BACKSTAGE_URL: "${BACKSTAGE_URL}"
  # PostgreSQL接続情報
  POSTGRES_HOST: "postgres"
  POSTGRES_PORT: "5432"
  POSTGRES_USER: "postgres"
  POSTGRES_PASSWORD: "postgres"
  POSTGRES_DB: "backstage"
  # GitHub連携
  GITHUB_TOKEN: "${GITHUB_TOKEN}"
  # Keycloak接続情報
  AUTH_SESSION_SECRET: "${AUTH_SESSION_SECRET}"
  AUTH_OIDC_METADATA_URL: "${KC_OIDC_URL}"
  AUTH_OIDC_CLIENT_ID: "backstage-auth"
  AUTH_OIDC_CLIENT_SECRET: "${KC_AUTH_CLIENT_SECRET}"
  # Catalog Keycloak情報
  CATALOG_KEYCLOAK_BASE_URL: "${KC_BASE_URL}"
  CATALOG_KEYCLOAK_LOGIN_REALM: "${KC_REALM}"
  CATALOG_KEYCLOAK_REALM: "${KC_REALM}"
  CATALOG_KEYCLOAK_CLIENT_ID: "backstage-sync-user-group"
  CATALOG_KEYCLOAK_CLIENT_SECRET: "${KC_SYNC_CLIENT_SECRET}"
EOF

以下のように、BackstageのPodから環境変数が渡されます。 具体的なマニフェストはbackstage.yamlを参照してください。

spec:
  containers:
    - name: backstage
      envFrom:
        - secretRef:
            name: backstage-secret

Keycloakでユーザーとグループの作成

Keycloakの管理コンソールで、適当なユーザーとグループを作成します。

グループ作成

KeycloakのRealm platformでグループを作成します。
 

今回は、BackstageAdminというグループを作成しました。

ユーザー作成

ユーザーを作成します。

今回、Keycloakでメール送信の設定をしていないため、メールアドレスを検証済みとします。Email verifiedにチェックを入れておいてください。ユーザー名、メールアドレス、姓名を埋めたら、先ほど作ったグループにユーザーを所属させます。

ユーザーを作成したら、パスワードを設定します。

今回は自分で設定して自分でログインするため、パスワードを後から再設定させるTemporaryのチェックを外します。

動作確認

コンテナイメージをビルドして、コンテナレジストリにプッシュします。GH_USERNAMEREPOSITORYTAGの部分は、ご自身の環境に合わせて変更してください。

export IMG=ghcr.io/GH_USERNAME/REPOSITORY:TAG

yarn build-image --tag $IMG
docker push $IMG

次に、Kustomizeの設定ファイルkustomization.yamlを作成します。GH_USERNAMEREPOSITORYTAGの部分は、先ほどビルドしたイメージの名前とタグに置き換えてください。また、backstage.172.32.4.127.nip.ioもご自身の環境に合わせて変更してください。

resources:
  - github.com/Hitachi/oss-assets/article/thinkit-smallplatform/04-backstage-feedback/deploy

images:
  - name: ghcr.io/makihh/backstage:latest
    newName: ghcr.io/GH_USERNAME/REPOSITORY
    newTag: TAG

patches:
  - target:
      kind: HTTPRoute
      name: backstage-route
    patch: |-
      - op: replace
        path: /spec/hostnames/0
        value: backstage.172.32.4.127.nip.io

kustomization.yamlがあるディレクトリで以下のコマンドを実行して、Backstageをデプロイします。

kubectl apply -k .

http://backstage.172.32.4.127.nip.ioにアクセスし、Keycloakでログインします。

また、ユーザーおよびグループがKeycloakからBackstageに同期されていることも確認できます。

このグループに対して、Permission Frameworkを使ってアクセス制御をかけられますが、今回は割愛します。

フィードバック機能の実装

ユーザーからのフィードバックを集めて継続的な改善に活かすため、entity-feedbackプラグインを導入していきます。entity-feedbackプラグインはフロントエンドとバックエンドの両方にプラグインがあるため、両方に追加していきます。

フロントエンド

Entity Feedback PluginのREADMEに従い、フロントエンド側の設定を行います。

まずはフロントエンド用のプラグイン@backstage-community/plugin-entity-feedbackをBackstageに追加します。

yarn --cwd packages/app add @backstage-community/plugin-entity-feedback

次に、app-config.yamlapp.extensionsにプラグインの設定を追加します。

# app-config.yaml
app:
  extensions:
+    # フィードバック画面の設定
+    - 'entity-card:entity-feedback/ratings-buttons':
+        config:
+          variant: 'starred' # 今回は5段階の星評価を使用。
+          title: '評価をお願いします'
+          requireResponse: true # 低評価で追加のフィードバックを入力する項目を表示
+          dialogTitle: '理由をお聞かせください?' # 追加フィードバックのダイアログタイトル
+          dialogResponses: # 追加フィードバックの選択肢
+            - id: 'documentation'
+              label: 'ドキュメントが不十分/わかりにくい/不正確/不足している/古い'
+            - id: 'usability'
+              label: 'ユーザビリティが悪い/使いにくい'
+    # フィードバックの集計画面の設定
+    - 'entity-card:entity-feedback/ratings-table':
+        config:
+          variant: 'starred'
+          title: '評価一覧'
+          allEntities: true

バックエンド

Entity Feedback Backendのドキュメントに従い、バックエンド側の設定を行います。
バックエンド用のプラグイン@backstage-community/plugin-entity-feedback-backendをBackstageに追加します。

yarn --cwd packages/backend add @backstage-community/plugin-entity-feedback-backend

バックエンドコードにフィードバックプラグインを初期化する設定を追加します。

# packages/backend/src/index.ts
backend.add(import('@backstage/plugin-auth-backend-module-oidc-provider'));
backend.add(
  import('@backstage-community/plugin-catalog-backend-module-keycloak'),
);
+backend.add(import('@backstage-community/plugin-entity-feedback-backend'));

backend.start();

動作確認

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

yarn build-image --tag $IMG
docker push $IMG

kubectl rollout restart deployment backstage -n backstage

各カタログページに5段階評価のUIが表示されるようになります。

評価1または2を選択すると、低評価の理由を詳しく聞くためのフィードバックフォームが表示されます。試しに内容を入力してみます。

5段階評価の集計結果は、グループ画面で確認できます。

詳細なフィードバックは、各カタログページのFeedbackタブで確認できます。

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

コンポーネントバージョン
Node.jsv24.15.0
Backstage1.50.0
@backstage/plugin-auth-backend-module-oidc-provider0.4.16
@backstage-community/plugin-catalog-backend-module-keycloak3.19.3
@backstage-community/plugin-entity-feedback0.19.0
@backstage-community/plugin-entity-feedback-backend0.21.0
Keycloak26.3.2

まとめ

Backstageにフィードバック機能を追加することで、ユーザーからのフィードバックを収集し、プラットフォームの継続的な改善に活かすことができるようになりました。 

プラットフォームエンジニアリングの成熟度モデル」に照らし合わせると、フィードバックチャネルが標準化された状態と言えるため、「計測」のレベル2の一要素を満たすことができました。

人気記事トップ10

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

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