開発における CI/CD 関連の情報を Backstage ポータルに集約する
概要
Backstage は開発ポータルを構築するための OSS フレームワークです。github star は ~ 27 K で CNCF の incubating project となっています。
クラウドネイティブなアプリケーションの開発をより効率的に進めることを考えると、OSS ベースの自宅クラウドの構成 で見たように CI/CD のツールやコード管理のために様々なプロダクトを使いこなす必要があり、複数のドキュメントや web UI などを横断する手間が増えてきます。Backstage を使うと開発に必要不可欠なこれらの Git repo のコード、 CI/CD のインフラ、ドキュメントなどの情報を Backstage のポータルに集約することができ、プロジェクト全体を包括的に見通せるようになります。
Backstage の構築や基本的な使い方に関する記事は検索すると色々出てくるので、ここでは上記の目的に絞って CI/CD 関連の情報が集約できるか検証してみます。
Backstage の構築
Backstage は frontend と backend で構成されていますが、新しいものとそれ以前から使用されているものの 2 種類があります (古い方はドキュメントで old system や legacy system と表記されている)。
詳細は以下の記事あたりを参照。
新しい方が plugin を導入する際の手順が簡単のため基本的に新しい方を使えばいいのですが、今回使用する plugin のいくつかがまだ新しい方に対応していないようなので、今回は古い方の frontend, backend を使用します。
backstage の構築は https://backstage.io/docs/getting-started/ に沿って進めます。Prerequisites に記載のツール一式をインストールしたのち、古い方の frontend, backend を使用するためすこし古い create-app:0.5.1
を使って backstage を含むパッケージをインストールします。(記事を書いた当時の最新バージョンは 0.5.17)
$ npx @backstage/create-app@0.5.10
? Enter a name for the app [required] backstage
Creating the app...
Checking if the directory is available:
checking backstage ✔
Creating a temporary app directory:
Preparing files:
copying .dockerignore ✔
templating .eslintrc.js.hbs ✔
...
セットアップが完了すると backstage
ディレクトリ以下に backstage を動作するために必要なパッケージや設定ファイル等が作成されます。以降ではこの backstage ディレクトリをプロジェクトのルートディレクトリと呼びます。
Backstage の構成はセットアップ後に作成される app-config.yaml
に記載します。この記事では backstage のサーバーに backstage.ops.com
というドメイン名でアクセスできるようにするため、app-config.yaml 内で localhost となっている部分を書き換えておきます。
app:
baseUrl: http://backstage.ops.com:3000
backend:
baseUrl: http://backstage.ops.com:7007
cors:
origin: http://backstage.ops.com:3000
plugins を導入する
具体的なアプリケーションがないとどのような情報を集約すればよいかイメージが掴みづらいので、ここでは以前記事に書いた ローカル環境に簡易 CI/CD 環境を構築して試す tekton 編 のアプリケーションを例に考えてみます。この記事では k8s クラスタ上で稼働するアプリケーションを CI/CD で開発するために以下のようなツールを使用しました。
- アプリケーションのコードは Gitlab で管理
- コードからコンテナイメージをビルドするのに tekton を使用
- ビルドしたイメージは harbor で管理
- argocd を使って対象のクラスタにデプロイ
このフローにおいてアプリケーション開発に関連のあるツールは gitlab, tekton, harbor, argocd, k8s クラスタとなっているので、これらを backstage ポータル上に集約できればプロジェクト全体を包括的に管理するのが便利になります。幸いそれぞれのツールを backstage と連携するための plugin がすでに存在しているので、これらを導入してどのような情報が取得できるのか見ていきます。
Kubernetes plugin
kubernetes との連携は backstage のコア機能の 1 つとなっており、特定の k8s クラスタと通信して label に基づく k8s リソースを backstage のポータルから閲覧できるようにします。plugin の有効化や設定は基本的に ドキュメント に記載されていますが、設定すべき項目は下記のサイトも適切にまとめられているので両方を参考にしながら進めます。
以降では plugin のインストールは yarn で行い、プロジェクトのルートディレクトリで実行します。
まず k8s の frontend plugin をインストール。
yarn --cwd packages/app add @backstage/plugin-kubernetes
packages/app/src/components/catalog/EntityPage.tsx
に以下の部分を追加。
import { EntityKubernetesContent } from '@backstage/plugin-kubernetes';
// You can add the tab to any number of pages, the service page is shown as an
// example here
const serviceEntityPage = (
<EntityLayout>
{/* other tabs... */}
<EntityLayout.Route path="/kubernetes" title="Kubernetes">
<EntityKubernetesContent refreshIntervalMs={30000} />
</EntityLayout.Route>
</EntityLayout>
);
次に backend plugin をインストール。
yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend
packages/backend/src/plugins/kubernetes.ts
を新規作成。
import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { CatalogClient } from '@backstage/catalog-client';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const catalogApi = new CatalogClient({ discoveryApi: env.discovery });
const { router } = await KubernetesBuilder.createBuilder({
logger: env.logger,
config: env.config,
catalogApi,
discovery: env.discovery,
permissions: env.permissions,
}).build();
return router;
}
packages/backend/src/index.ts
に追加。
// ..
import kubernetes from './plugins/kubernetes';
async function main() {
// ...
const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes'));
// ...
apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv));
これで plugin の導入が完了したので、次に k8s クラスタ側の設定を進めます。
backstage を動かすサーバーとは別に k8s クラスタを用意し、backstage - k8s クラスタ間接続に使用する serviceaccount, role, rolebinding を作成。
apiVersion: v1
kind: ServiceAccount
metadata:
name: backstage-sa
namespace: backstage-sample
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: backstage-sample-clusterrole
rules:
- apiGroups: [""]
resources: ["pods", "services"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: backstage-sample-clusterrolebinding
subjects:
- kind: ServiceAccount
name: backstage-sa
namespace: backstage-sample
roleRef:
kind: ClusterRole
name: backstage-sample-clusterrole
apiGroup: rbac.authorization.k8s.io
この serviceaccount に token を設定。
apiVersion: v1
kind: Secret
metadata:
name: sa-secret
namespace: backstage-sample
annotations:
kubernetes.io/service-account.name: backstage-sa
type: kubernetes.io/service-account-token
以下のコマンドで作成した secret から token を取得できるのでメモ。
kubectl -n backstage-sample get secrets sa-secret -o yaml | yq -r ".data.token" | base64 -d
次に backstage の app-config.yaml
に kubernetes
を追加し、クラスタへの接続設定を記載します。
- url: k8s control plane の接続先 url を指定する。k8s クラスタの control plane 上で
kubectl cluster-info
を実行することで確認可能。大抵は k8s api-server のエンドポイントに一致。 - serviceAccountToken: 上記で確認した SA の token を指定。値を直接書いても良いが下記のように環境変数から読み込むことも可能。
その他の項目は configuration を参照。
kubernetes:
serviceLocatorMethod:
type: 'multiTenant'
clusterLocatorMethods:
- type: 'config'
clusters:
- url: https://192.168.3.131:6443
name: k8s
authProvider: 'serviceAccount'
skipTLSVerify: true
skipMetricsLookup: true
serviceAccountToken: ${k8S_TOKEN}
これで準備ができたので、試しに適当なリソースを作成して backstage から確認できるか見てみます。
backstage の entity を k8s リソースと関連付けるにはいくつかの方法がありますが、ここでは annotations の label selector を指定する方法にします。Backstage 側のリソースである Component
entity を作成し、annotations に backstage.io/kubernetes-label-selector: 'backstage-project=k8s-example'
を設定します。
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: k8s-example
annotations:
backstage.io/kubernetes-label-selector: 'backstage-project=k8s-example'
spec:
type: service
lifecycle: experimental
owner: guests
system: examples
上記を読み込むために app-config.yaml の catalog に以下を追加。
catalog:
locations:
- type: file
target: ../../examples/k8s.yml
この場合、k8s 側のリソースで backstage-project: k8s-example
の label を設定したリソースは上記の entity と関連付けられるようになります。動作確認のため、適当な deployments と svc を作成。
kind: Deployment
metadata:
name: nginx
namespace: backstage-sample
labels:
app: nginx
backstage-project: k8s-example
spec:
selector:
matchLabels:
app: nginx
backstage-project: k8s-example
replicas: 2
template:
metadata:
labels:
app: nginx
backstage-project: k8s-example
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: backstage-sample
labels:
app: nginx
backstage-project: k8s-example
spec:
selector:
app: nginx
backstage-project: backstage-example
ports:
- protocol: TCP
port: 80
targetPort: 80
プロジェクトのルートディレクトリで yarn dev
を実行し、backstage frontend と backend を起動。ブラウザから backstage.ops.com:3000
にアクセスし、作成した Component entity k8s-example
を確認すると kubernetes
タブが追加され、クラスタに展開した pod と service が確認できます。
ページ上部には権限不足により取得できないリソースに関するエラーメッセージが表示されます。今回は使用しないので問題ないですが、これらのリソースも取得したい場合は k8s 側の Role に権限を追加する必要があります。
There was a problem retrieving some Kubernetes resources for the entity: k8s-example. This could mean that the Error Reporting card is not completely accurate.
Errors:
Cluster: k8s
Error fetching Kubernetes resource: '/api/v1/configmaps', error: UNKNOWN_ERROR, status code: 403
Error fetching Kubernetes resource: '/api/v1/limitranges', error: UNKNOWN_ERROR, status code: 403
Error fetching Kubernetes resource: '/api/v1/resourcequotas', error: UNKNOWN_ERROR, status code: 403
Error fetching Kubernetes resource: '/apis/autoscaling/v2/horizontalpodautoscalers', error: UNKNOWN_ERROR, status code: 403
Error fetching Kubernetes resource: '/apis/batch/v1/jobs', error: UNKNOWN_ERROR, status code: 403
Error fetching Kubernetes resource: '/apis/batch/v1/cronjobs', error: UNKNOWN_ERROR, status code: 403
Error fetching Kubernetes resource: '/apis/networking.k8s.io/v1/ingresses', error: UNKNOWN_ERROR, status code: 403
Error fetching Kubernetes resource: '/apis/apps/v1/statefulsets', error: UNKNOWN_ERROR, status code: 403
Error fetching Kubernetes resource: '/apis/apps/v1/daemonsets', error: UNKNOWN_ERROR, status code: 403
backstage entity と特定の deployment や svc などを関連付けられるので、例えば frontend のアプリケーションを k8s 上で動かしている場合に pod が正常に稼働しているか等を確認できるようになります。特に同一クラスタ内に多数の pod が存在しているようなケースではプロジェクトに関連する pod をいちいち探すような手間が省けるといったメリットが考えられます。
Argocd plugin
ArgoCD との連携は上記の plugin が使用できます。このプラグインでは連携先の Argocd から指定した application の状態を取得し、health, sync, 更新履歴などを backstage ポータルから確認できるようになります。
frontend のインストール
yarn --cwd packages/app add @roadiehq/backstage-plugin-argo-cd
packages/app/src/components/catalog/EntityPage.tsx
に追加。
import {
EntityArgoCDOverviewCard,
isArgocdAvailable
} from '@roadiehq/backstage-plugin-argo-cd';
const overviewContent = (
<Grid container spacing={3} alignItems="stretch">
...
<EntitySwitch>
<EntitySwitch.Case if={e => Boolean(isArgocdAvailable(e))}>
<Grid item sm={4}>
<EntityArgoCDOverviewCard />
</Grid>
</EntitySwitch.Case>
</EntitySwitch>
...
</Grid>
);
次に backend のインストール
yarn --cwd packages/backend add @roadiehq/backstage-plugin-argo-cd-backend
packages/backend/src/plugins/argocd.ts
を作成。
import { createRouter } from '@roadiehq/backstage-plugin-argo-cd-backend';
import { PluginEnvironment } from '../types';
export default async function createPlugin({
logger,
config,
}: PluginEnvironment) {
return await createRouter({ logger, config });
}
packages/backend/src/index.ts
を更新。
import argocd from './plugins/argocd';
...
const argocdEnv = useHotMemoize(module, () => createEnv('argocd'));
...
apiRouter.use('/argocd', await argocd(argocdEnv));
対象の argocd の接続情報は app-config,yaml
に指定します。ここでは接続先の domain は argocd.ops.com
とします。
proxy:
endpoints:
'/argocd/api':
target: https://argocd.ops.com:32566/api/v1/
changeOrigin: true
secure: false
headers:
Cookie:
$env: ARGOCD_TOKEN
argocd:
username: <username>
password: <password>
appLocatorMethods:
- type: 'config'
instances:
- name: argoInstance1
url: https://argocd.ops.com:32566
token: ${ARGOCD_TOKEN}
次に argocd 側で検証に使用する application 等を作成しておきます。
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: backstage-sample-app
namespace: argocd
spec:
project: default
source:
repoURL: https://gitlab.ops.com/kube/backstage-example.git
targetRevision: HEAD
path: argocd
destination:
server: https://kubernetes.default.svc
namespace: backstage-sample
接続に使用する backstage
ユーザーと role の設定。
apiVersion: v1
data:
accounts.backstage: apiKey,login
accounts.backstage.enabled: "true"
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-cm
app.kubernetes.io/part-of: argocd
name: argocd-cm
namespace: argocd
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
labels:
app.kubernetes.io/name: argocd-rbac-cm
app.kubernetes.io/part-of: argocd
data:
policy.csv: |
p, role:test-role, applications, create, default/*, allow
p, role:test-role, applications, get, default/*, allow
p, role:test-role, applications, update, default/*, allow
p, role:test-role, applications, delete, default/*, allow
p, role:test-role, applications, sync, default/*, allow
p, role:test-role, applications, override, default/*, allow
g, backstage, role:test-role
policy.default: role:readonly
デプロイするマニフェストは self-managed gitlab に置いておくので証明書、認証方法なども設定。
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-tls-certs-cm
namespace: argocd
labels:
app.kubernetes.io/name: argocd-cm
app.kubernetes.io/part-of: argocd
data:
gitlab.ops.com: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
---
apiVersion: v1
kind: Secret
metadata:
name: gitlab-repo
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repository
stringData:
type: git
url: https://gitlab.ops.com/kube/backstage-example.git
password: <password>
username: <username>
検証用に gitlab の backstage-example.git
リポジトリにデプロイ用のマニフェストを配置します。
.
├── argocd
└── deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-from-gitlab
namespace: backstage-sample
labels:
app: nginx2
backstage-project: sample-project
backstage.io/kubernetes-id: k8s-example
spec:
selector:
matchLabels:
app: nginx2
backstage-project: sample-project
backstage.io/kubernetes-id: k8s-example
replicas: 2
template:
metadata:
labels:
app: nginx2
backstage-project: sample-project
backstage.io/kubernetes-id: k8s-example
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
上記のリソースを一通り作成したら、argocd 側で backstage ユーザーにパスワードを設定して token を作成。application の sync も実行しておきます。
argocd login argocd.ops.com:32566 --username admin --password <password> --insecure
argocd account update-password --account backstage --new-password backstage
argocd logout
argocd login argocd.ops.com:32566 --username backstage --password <password> --insecure
argocd account generate-token
argocd app sync backstage-sample-app
先程作成した component entity に 以下の label を追加します。
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: k8s-example
annotations:
backstage.io/kubernetes-label-selector: 'backstage-project=k8s-example'
+ argocd/app-name: backstage-sample-app
spec:
type: service
lifecycle: experimental
owner: guests
system: examples
argocd plugin も k8s plugin と同様にいくつかの方法で関連付けられる argocd リソースを指定できるようになっており、例えば上記のように argocd/app-name
を指定すると指定したリソース名に対応する argocd の Application
リソースが関連付けられるようになります。その他の指定方法については https://www.npmjs.com/package/@roadiehq/backstage-plugin-argo-cd を参照。
最後に、backstage → argocd の接続は先程作成した backstage
ユーザーの token を使用するため、backstage サーバー側で export ARGOCD_TOKEN=...
で環境変数に設定しておきます。
yarn dev
で backstage を起動して entity を開くと、overview page に指定した argocd Application の情報が表示されるようになります。
クリックすると詳細が application の spec に設定されている repo 等の情報が確認できます。
Link を押すと argocd ダッシュボードの対応する Application のページに飛びます。
こちらも k8s plugin と同様に entity に関連づいた argocd application の詳細や同期ステータスが一目でわかるようになります。また、実際のデプロイの状況やリソースに関しては argocd 側で管理できるので、デプロイが正常に動いていることの確認は backstage ポータルで行い、異常やエラーが発生した場合は argocd 側のプロジェクトに飛んで詳細に確認するといった使い方ができます。
Tekton plugin
tekton plugin を検索するといくつか見つかりますが上記のものを使用します。この plugin を使うと k8s クラスタ上の tekton PipelineRun
リソースの成功・失敗や実行時のログを backstage から確認できるようになります。
frontend のインストール
yarn --cwd packages/app add @janus-idp/backstage-plugin-tekton
EntityPage.tsx
に以下を追加。
import {
isTektonCIAvailable,
TektonCI,
} from '@janus-idp/backstage-plugin-tekton';
const cicdContent = (
<EntitySwitch>
{/* ... */}
<EntitySwitch.Case if={isTektonCIAvailable}>
<TektonCI />
</EntitySwitch.Case>
</EntitySwitch>
);
app-config.yml
の kubernetes に以下の CR を追加します。
kubernetes:
customResources:
- group: 'tekton.dev'
apiVersion: 'v1'
plural: 'pipelineruns'
- group: 'tekton.dev'
apiVersion: 'v1'
plural: 'taskruns'
ドキュメントを参照に tekton リソースにアクセスできるように k8s の role に権限を追加します。ここではk8s plugin 導入時に作成した backstage-sample-clusterrole
ロールに権限を追加します。
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: backstage-sample-clusterrole
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "services"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["tekton.dev"]
resources:
- pipelineruns
- taskruns
verbs:
- get
- list
次に tekton 側で pipeline リソースを追加します。ここでは ローカル環境に簡易 CI/CD 環境を構築して試す tekton 編 で使用したものを流用します。詳細は記事を参照。
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: backstage-ci-cd
namespace: backstage-sample
labels:
backstage.io/kubernetes-id: k8s-example
spec:
description: >-
This pipeline run the sequence of tasks as a pipeline.
1. Clone the application source from a git repository.
2. Build an image from Dockerfile and push it to a registry.
3. Deploy application by argocd sync.
params:
- name: repositoryUrl
type: string
description: The git repository URL to clone the source.
- name: branch
type: string
description: The branch of the repository.
- name: context
description: The path to the build context, used by Kaniko - within the workspace
default: "./"
- name: imageName
description: Image name
- name: imageTag
description: Image tag
default: "latest"
- name: argocdApplicationName
description: The name of Argocd application to be synced.
workspaces:
- name: shared-data
description: >-
This workspace contains the cloned repo files, so they can be read by the
next task.
- name: git-credentials
description: Name of the secret to log in to gitlab.
- name: harbor-credentials
description: Name of the secret to log in to harbor.
- name: argocd-credentials
description: Name of the secret to log in to argocd.
tasks:
- name: git-clone
taskRef:
name: git-clone
workspaces:
- name: output
workspace: shared-data
- name: basic-auth
workspace: git-credentials
params:
- name: url
value: $(params.repositoryUrl)
- name: revision
value: $(params.branch)
- name: sslVerify
value: "false"
- name: build
taskRef:
name: kaniko
runAfter:
- git-clone
workspaces:
- name: source
workspace: shared-data
- name: dockerconfig
workspace: harbor-credentials
params:
- name: IMAGE
value: $(params.imageName):$(params.imageTag)
- name: CONTEXT
value: $(params.context)
- name: deploy
taskRef:
name: argocd-sync
runAfter:
- git-clone
- build
workspaces:
- name: argocd-basic-auth
workspace: argocd-credentials
params:
- name: applicationName
value: $(params.argocdApplicationName)
tekton リソースと backstage entity を関連付けるには、entity 側の annotations に janus-idp.io/tekton: <BACKSTAGE_ENTITY_NAME>
を追加します。
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: k8s-example
annotations:
backstage.io/kubernetes-label-selector: 'backstage-project=k8s-example'
argocd/app-name: backstage-sample-app
+ janus-idp.io/tekton: k8s-example
spec:
type: service
lifecycle: experimental
owner: guests
system: examples
tekton 側では pipeline
リソースの labels に backstage.io/kubernetes-id: <BACKSTAGE_ENTITY_NAME>
を設定します。こちらは annotations ではなく labels に設定するので注意。他の指定方法は https://janus-idp.io/plugins/tekton/#for-administrators を参照。
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: backstage-ci-cd
namespace: backstage-sample
labels:
backstage.io/kubernetes-id: k8s-example
spec:
,,,
backstage を起動し、entity の CI/CD タブを確認すると tekton が追加されています。ここでは tekton パイプラインとパイプライン内の各タスクの実行結果が確認できます。
ステージをクリックすると各タスクのログが確認できます(pod log に対応)。字がやや小さいですがログをテキストファイルとしてダウンロードすることもできます。
これによって CI で行われる lint やテスト、コンテナイメージのビルド、コンテナレジストリへのアップロード、argocd のトリガーなど諸々の処理の結果を backstage ポータルから確認できます。
Harbor plugin
Harbor plugin では harbor に保存されているコンテナイメージの情報を backstage から確認できるようになります。harbor 用の plugin は backstage plugin list に container-registry が管理するもの が記載されていますが、現時点ではアーカイブとなっているようです (参考)。
アーカイブとなっているものの使用はできるのでこれを使ってもいいのですが、一部機能に不具合があるため代わりに上記のリポジトリから fork された @digitalist-open-cloud
所有の plugin を使用します。
frontend のインストール
yarn --cwd packages/app add @digitalist-open-cloud/backstage-plugin-harbor
EntityPage.tsx
に追加。
import {
HarborPage,
HarborWidget,
isHarborAvailable,
} from '@digitalist-open-cloud/backstage-plugin-harbor'
const serviceEntityPage = (
<EntityPageLayout>
// ...
<EntityLayout.Route path="/harbor" title="Harbor" if={isHarborAvailable}>
<HarborPage />
</EntityLayout.Route>
</EntityPageLayout>
)
以下の変更では backstage entity の overview に widget を追加します。お好みで記載。
const overviewContent = (
<Grid container spacing={6} alignItems="stretch">
// ...
<EntitySwitch>
<EntitySwitch.Case if={isHarborAvailable}>
<Grid item>
<HarborWidget />
</Grid>
</EntitySwitch.Case>
</EntitySwitch>
...
</Grid>
)
backend をインストール
yarn --cwd packages/backend add @digitalist-open-cloud/backstage-plugin-harbor-backend
packages/backend/src/plugins/harbor.ts
を新規作成。
import { createRouter } from '@digitalist-open-cloud/backstage-plugin-harbor-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin({
logger,
config,
}: PluginEnvironment): Promise<Router> {
return await createRouter({ logger, config });
}
index.ts
を更新。サイトでは harborusage
となっていますが、実際に試すと harborusage ではエラーとなり harbor だとうまくいきました。
import harbor from './plugins/harbor';
// ...
async function main() {
// ...
const harborEnv = useHotMemoize(module, () => createEnv('harbor'));
- apiRouter.use('/harbor', await harborusage(harborEnv));
+ apiRouter.use('/harbor', await harbor(harborEnv));
harbor への接続情報は app-config.yaml
に記載します。
harbor:
# This is the traditional way of configuring the Harbor plugin.
baseUrl: https://harbor.yourdomain.com
username: ${HARBOR_USERNAME}
password: ${HARBOR_PASSWORD}
backstage entity リソースの annotations に goharbor.io/repository-slug
を追加し、関連付けたい harbor イメージを <project>/<image_name>
の形式で追加します。ここで指定したイメージは上記の harbor ユーザーで参照できるように公開範囲や権限も設定する必要あり。
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: k8s-example
annotations:
backstage.io/kubernetes-label-selector: 'backstage-project=k8s-example'
argocd/app-name: backstage-sample-app
janus-idp.io/tekton: k8s-example
+ goharbor.io/repository-slug: k8s/backstage-front
spec:
type: service
lifecycle: experimental
owner: guests
system: examples
これで Backstage を輝度すると harbor タブが追加され、指定したイメージのタグ一覧やサイズ、脆弱性スキャンの結果が取得できます。ただ見た感じ harbor 側のイメージタグ数と一致していないので、ある程度新しいイメージタグのみ取得される等の制約があるのかもしれません。
Learn more
を押すと harbor webUI の対象イメージのページに飛びます。
こちらも argocd と同様にざっと確認したい際は backstage ポータルから確認し、詳細な情報を知りたい場合はそのまま Learn more から harbor の UI に飛んで確認するといった使い分けができます。
参考: 古いプライグインについて
アーカイブとなっている container-registry の harbor plugin ( @bestsellerit/backstage-plugin-harbor など) の方も試した結果タブが追加されることは確認できましたが、harbor 側で脆弱性スキャンを有効化していないと以下のエラーがログに表示されてイメージが取得できませんでした。
[1] 2024-07-28T08:30:30.025Z backstage error Cannot read properties of undefined (reading 'href') type=errorHandler stack=TypeError: Cannot read properties of undefined (reading 'href')
[1] at <anonymous> (/home/ubuntu/backstage/old/node_modules/@bestsellerit/backstage-plugin-harbor-backend/src/service/artifact.ts:18:73)
[1] at Array.map (<anonymous>)
[1] at getArtifacts (/home/ubuntu/backstage/old/node_modules/@bestsellerit/backstage-plugin-harbor-backend/src/service/artifact.ts:16:37)
[1] at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
[1] at <anonymous> (/home/ubuntu/backstage/old/node_modules/@bestsellerit/backstage-plugin-harbor-backend/src/service/router.ts:19:23)
イメージの取得処理は harbor api を実行してその取得結果を以下の HarborApiArtifact
に格納していますが、脆弱性スキャンの結果がない場合 addition_links.vulnerabilities.href
が返り値に含まれないため Cannot read properties of undefined (reading 'href
が発生するようです。
このエラーは harbor 側で trivy などの脆弱性スキャンを有効化することで対応できます。詳細は harbor Vulnerability Scanning を参照。
ただしこちらを対応しても harbor 側の脆弱性スキャンが正しく取得できない、harbor への link が正しく機能しないという問題があります。これは https://github.com/container-registry/backstage-plugin-harbor/issues/272 で報告されており、今回使った @digitalist-open-cloud/backstage-plugin-harbor
の方では修正されています。
TechDocs plugin
techdocs は backstage のコア機能の 1 つで、markdown で書かれた文章を mkdocs を使って backstage 上でビルド、レンダリングして閲覧できる機能となっています。アプリケーション開発においても仕様やアーキテクチャ、運用方針、引き継ぎ手順など様々な場面で文書化が必要となりますが、techdocs plugin を利用することでそういった文書を backstage ポータルに集約することができます。
techdocs を有効化するための plugin のインストールや設定は https://backstage.io/docs/features/techdocs/getting-started に記載されていますが、backstage のバージョンによっては構築時にセットアップされるため設定が不要な場合もあります。今回使用したバージョンでは構築時に既にセットアップ済みとなっていました。
backstage entity と techdocs の文書を関連付けるには entity の annotations に backstage.io/techdocs-ref:
を指定する必要があります。https://backstage.io/docs/features/techdocs/creating-and-publishing
techdocs が正しく動作することを検証するため、backstage のプロジェクトディレクトリに以下のようなファイルを作成します。
.
├── app-config.yml
├── docs
│ └── index.md
├── mkdocs.yml
└── k8s.yml
mkdocs.yml と docs/index.md は mkdocs でドキュメントを作成するために必要なファイルです。techdocs-core
plugin を指定する以外は通常の mkdocs でドキュメントを作成する際と同様に記述できます。
site_name: Backstage-frontend
nav:
- HomeDir: index.md
plugins:
- techdocs-core
## markdown sample
サンプル
k8s.yml は今まで使ってきた component entity と同じ内容にします。annotations に backstage.io/techdocs-ref: dir:.
で index.md が存在する docs のディレクトリパスを指定することで entity と techdocs を関連付けることができます。
techdocs の仕様上ファイルが存在するディレクトリより上のディレクトリを指定すると Relative path is not allowed to refer to a directory outside its parent Relative
のエラーが発生するので、k8s.yml
一時的に docs と同じディレクトリに配置します。
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: k8s-example
annotations:
backstage.io/kubernetes-label-selector: 'backstage-project=k8s-example'
argocd/app-name: backstage-sample-app
janus-idp.io/tekton: k8s-example
goharbor.io/repository-slug: k8s/backstage-front
+ backstage.io/techdocs-ref: dir:.
spec:
type: service
lifecycle: experimental
owner: guests
system: examples
k8s.yml の位置を変えたので app-config.yml も修正します。
catalog:
locations:
- - type: file
- target: ../../examples/k8s.yml
+ - type: file
+ target: ../../k8s.yml
これで yarn dev
で backstage を起動し、component entity の overview を見ると View techodocs
のリンクが選択できるようになります。
これをクリックすると index.md の内容を mkdocs でレンダリングしたページに飛びます(初回にページを開く際は markdown のビルドが行われるため少し時間がかかる)。また、docs タブからも同様の内容が確認できます。
生成されたドキュメントは内部的に docs に分類されているため、左側のナビゲーションの Docs
からも確認できます。こちらは backstage 内の docs リソース一覧が表示されるようです。
ドキュメント自体は mkdocs を使って作成できるので、アプリケーションの説明、アーキテクチャ、運用方針やなど色々な情報を自由に記載できます。
その他
上記ではまとめきれなかったものやメモなど。
他の plugin
本編では CI に tekton を使用している際の連携例について記載しましたが、plugin list では他にも様々なサービスに対応する plugin があります。役立ちそうなものを以下にまとめておきます。
- CI
- monitoring
monitoring 等の plugin もあるのでアプリケーションの監視状況などの情報も Backstage に集約することができそうです。
Authenticate provider
backstage はデフォルトでは認証が設定されていないため URL を知っていればユーザー認証無しで誰でもアクセスできますが、authentication provider を指定することで外部サービスと連携して認証を実装することができます。Github や Google など主要な provider はデフォルトで組み込まれているので plugin 等をインストールしなくしても使用できます。
OIDC の処理を実装することで上記のリストにない OIDC provider を認証に使うことも可能。
k8s での稼働
今までは backstage を起動する際は yarn dev
を実行してローカルで稼働させていましたが、本格的な運用を考える場合はやはり kubernetes でのデプロイが推奨されているようです。
At Spotify, we deploy software generally by:
Building a Docker image
Storing the Docker image on a container registry
Referencing the image in a Kubernetes Deployment YAML
Applying that Deployment to a Kubernetes cluster
This method is covered in Building a Docker image and Deploying with Kubernetes.
kubernetes でのデプロイは以下のドキュメントに記載があります。
これを読んでいくと、backstage イメージは以下の手順に沿ってビルドするように書かれています。
こちらでは以下の構成のビルド方法が記載されています。一見すると Host Build が推奨されてそう。
Type | Description | メリット |
---|---|---|
Host Build | ホスト側で backstage 関連をビルドしておき、コンテナ内に成果物を配置する方法 | ホスト側でキャッシュが聞くのでビルドが早い |
Multi-stage Build | docker のマルチステージビルドを利用してコンテナ内で backstage のビルドを行う方法 | 一部のケースで使う |
Separate Frontend | backend と frontend を分離する方法 | 一部のケースで使う |
その他 backstage をデプロイするための helm chart もあります。
ただ database や role 等は helm chart でセットアップできますが、肝心の backstage イメージ自体は上記の方法により自前でビルドする必要があります。
ポータルの theme を変える
backstage ポータルのデフォルトテーマは dark/light の 2 種類ですがカスタムテーマを作成することもできます。
また、他の人が作成した plugin を利用することもできます。
私は dracula テーマが好きなので backstage 用の dracula theme があるか探しましたが、以下のものが dracula
org 配下にありました。
現時点では公式のインストール方法については記載されていないようですが、backstage/plugins/dracula-theme に導入方法が記載されていたのでこちらを参考に導入します。
README では yarn --cwd packages/app add @fjudith/plugin-dracula-theme
で plugin をインストールできるとありますが、package が npm のレジストリにアップロードされていないようなのでローカルからインストールします。
まずリポジトリを clone
git clone https://github.com/dracula/backstage.git
dracula theme は plugins/dracula-theme
配下にあるので、元の backstage プロジェクトディレクトリの plugins 以下にコピー。
cd backstage
cp -r plugins/dracula-theme [project_dir]/plugins
元の project のルートディレクトリから local パッケージを yarn add する。
cd [project_dir]
yarn --cwd packages/app add ./plugins/dracula-theme/
あとは Theme installation と Update logo color に沿ってファイルを追加、編集する。
readme ではディレクトリが packages/app/src/components/
となっていますが、手元の環境では packages/app/src/components/Root
にありました。
また、以下の部分はそのままでは動かず以下のように修正する必要がありました。
- path: props => ({
- fill: props.ThemeId === 'dracula' ? '#F8F8F2' ; '#7df3e1'
- }),
+ path: props => ({
+ fill: props.themeId === 'dracula' ? '#F8F8F2' : '#7df3e1'
+ }),
- const themeId = appThemeApi.getActiveThemeId();
+ const appThemeApi = useApi(appThemeApiRef);
+ const themeId = appThemeApi.getActiveThemeId();
yarn dev
で backstage を起動すると以下のエラーが発生。
[0] Module parse failed: Unexpected token (12:19)
[0] You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
[0] |
[0] | // Change stop color: https://johndecember.com/html/spec/colorsvg.html
[0] > export const shapes: Record<string, string> = {
[0] | wave: `url("data:image/svg+xml,%3csvg xmlns='http://
上記エラーは import 内のコメントアウトされていた shapes を外し、もとの export const shapes となっていた箇所をコメントアウトすることで解消しました。
import {
createBaseThemeOptions,
createUnifiedTheme,
genPageTheme,
palettes,
+ shapes,
- // shapes,
} from '@backstage/theme';
import { alpha } from '@material-ui/core/styles';
- // Change stop color: https://johndecember.com/html/spec/colorsvg.html
- // export const shapes: Record<string, string> = {
- // wave: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='1368'
...
これで backstage 全体に dracula theme が適用されるようになりました。
theme は左下の settings > General > Appearance から切り替えられます。
まとめ
各 plugin を有効化した際に backstage ポータルでどのように見えるかをまとめておきます。
home page
Component entity の Overview
CI/CD タブの tekton pipeline 実行結果
API タブでは entity に関連づく API entity が表示される
OpenAPI 形式で記載した API 定義はレンダリングされて表示される
Dependency タブでは Component の依存関係が表示される
Docs タブ
K8s タブ
Harbor タブのイメージ一覧
System entity では Relation で他 entity との依存関係が一目でわかるようになっている
今回使った resource は以下に置いてあります。
おわりに
Backstage の plugin を利用して CI/CD 周りの情報を Backstage で集約できるか検証しましたが、試してみると割といい感じにまとめることができました。特に Argocd や Harbor は対応する webUI 側へのリンクも表示されるので、プロジェクト全体を俯瞰的に見渡す場合は Backstage ポータルから確認し、より詳細な情報は各ツールの webUI に飛んで確認するといったように使い分けることができます。
また、Backstage では主に React で plugin を書いて機能や使い勝手を拡張できるのが特徴の 1 つとなっていますが、既存の plugin ではそこまで踏み込まずとも適用できるようになっており、 React や frontend にそれほど馴染みがなくても活用できるのがメリットとなっています。
Discussion