|
この文書は自動機械翻訳技術を使用して翻訳されています。 正確な翻訳を提供するように努めておりますが、翻訳された内容の完全性、正確性、信頼性については一切保証いたしません。 相違がある場合は、元の英語版 英語 が優先され、正式なテキストとなります。 |
Kubernetes向け開発
Rancher Desktop – SUSEアプリケーションコレクション – Tilt
開発環境からデプロイメントへ – インナーループ、監視、GitOps
1.概要: なぜこのガイドが必要なのか?
このガイドは、Kubernetes開発の*完全なバリューチェーン*をカバーしており、初期環境のセットアップから継続的デプロイメントまでを含みます。目標: 開発者が人気のあるツール、ローカルクラスタ(Rancher Desktop)、および信頼できるイメージ(SUSEアプリケーションコレクション)を利用して、すぐに生産性を発揮できるようにすること。
このガイドは二部構成になっています: まず、数分で実行できる*ハンズオンデモ*があります。次に、*さらに進む*セクションでは、ワークフローを深める準備ができたときのために、より広範なエコシステム(Dev Containers、Testcontainers、mirrord、Helm、セキュリティ、GitOps)をカバーします。
1.1 クラウドネイティブ開発の二つのループ
| インナーループ | アウターループ | |
|---|---|---|
手段 |
開発者の迅速な日常サイクル: コードを書く、ビルドする、ローカルにデプロイする、テストする、デバッグする、反復する |
自動化されたポストコミットサイクル:CI/CD、統合テスト、セキュリティスキャン、ステージング/本番デプロイメント |
目的 |
数秒でのフィードバック |
品質と再現性 |
[ツール] |
Tilt、mirrord |
Argo CD、GitHub Actions、Tekton |
スコープ |
開発者ワークステーション + ローカルクラスタ |
CI/CDパイプライン + リモートクラスタ |
重要な原則 – 内部ループは可能な限り速くなければなりません。コードビルドデプロイテストサイクルで節約された毎秒は、日々の変更の数によって倍増します。良い内部ループは、1回のイテレーションで5-10分から5-10秒に短縮されることを意味します。
2.全体アーキテクチャ
2.2 スタックレイヤー
| 層 | ツール | Role |
|---|---|---|
IDE |
VS Code + 拡張機能 |
エディタ、デバッグ、統合ターミナル |
ローカルクラスタ |
Rancher Desktop (k3s) |
ローカルKubernetes + コンテナランタイム |
画像ファイル |
SUSEアプリケーションコレクション |
ベースイメージ、言語、ミドルウェア、ツール |
内部ループ |
Tilt |
自動ビルド、自動デプロイ、ホットリロード |
認証 |
Keycloak |
OAuth2 / OpenID Connectアイデンティティプロバイダー |
監視 |
Prometheus + Grafana |
アプリケーションメトリクス、リアルタイムダッシュボード |
パッケージング |
Helm / Kustomize |
K8sマニフェストテンプレート化 |
セキュリティ |
Trivy / Cosign |
脆弱性スキャン、イメージ署名 |
GitOps |
Argo CD |
Gitからの宣言的デプロイメント |
3.Rancher Desktopの設定
3.1 Rancher Desktopとは?
Rancher Desktopは、ローカルKubernetesクラスター(k3s)と*コンテナランタイム*(dockerdまたはcontainerd)を自動管理されるVM内で提供するオープンソースのデスクトップアプリケーションです。Docker Desktopをインストールする必要はありません。
3.2 インストールと設定
-
*ダウンロード*は rancherdesktop.ioから行います。
-
ランタイムを選択: *dockerd (moby)*を選択します(containerdではありません)。これは今後のために重要です。
-
Kubernetesを有効にする(デフォルトで有効)。
-
確認:
docker info # shows "Server Version: ..." kubectl get nodes # shows "Ready" -
PATH – macOSでは、
~/.rd/bin/`が$PATH`に含まれていることを確認してください(インストーラーによって自動的に追加されます):which docker kubectl helm # should point to ~/.rd/bin/
*Rancher DesktopはDocker Desktopではありません。*Docker Desktopは必要ありません。Rancher Desktopは独自のDockerデーモン(moby/dockerd)を提供します。両方をインストールすると、ソケットの競合が発生する可能性があります。Docker Desktopを無効にしてください。
3.3 本番環境でcontainerdが動作しているのに、なぜdockerd(moby)が必要ですか?
本番環境では、Kubernetesは*containerd*をランタイムとして使用します。ここが重要なポイントです:*Rancher Desktopもmobyモードで同様です。*k3sクラスターは、設定に関係なく常にcontainerdで実行されます。"`dockerd (moby)`"を選択してもcontainerdは置き換わりません。Dockerのデーモンが追加され、両者は同じイメージストアを共有します。
これにより、私たちは両方の利点を得ることができます:本番環境と同じランタイムに加え、Dockerの開発者向けツールも利用できます。
Rancher Desktop VM (moby mode): +---------------------------------------------+ | | | dockerd (moby) k3s (containerd) | | +-- image store <----> image store --+ | | (shared) (same!) | | | | | docker build -> image appears in both | | NO PUSH NEEDED! | +---------------------------------------------+ Rancher Desktop VM (containerd mode): +---------------------------------------------+ | | | nerdctl (containerd) k3s (containerd) | | +-- image store image store --+ | | (separate!) (separate!) | | | | | nerdctl build -> push -> pull -> k3s | | 3 steps instead of 1 = slower | +---------------------------------------------+
Dockerによって構築されたイメージは、同じストアを共有しているため、k3sに*即座に表示されます*。レジストリは不要、プッシュもプルもありません。これが内部ループを非常に速くする理由です。
*妥協なしのパリティ。*あなたのアプリは、どちらの場合でもcontainerd上で動作します。唯一の違いは、イメージを構築するために使用されるCLIです:
docker(mobyモード)対`nerdctl`(containerdモード)。ランタイムでは、k3sは同様に動作します。
これが、デモK8sデプロイメントが`imagePullPolicy: IfNotPresent`(または`Never`)を使用する理由でもあります:私たちはk3sに"`use the local image, do not look in a registry`"を指示します。
4.SUSEアプリケーションコレクション
4.1 SUSEアプリケーションコレクションとは何ですか?
*SUSEアプリケーションコレクション*は、コンテナイメージとHelmチャートの形で提供されるアプリケーションのコレクションであり、SUSEによって構築、パッケージ化、強化、維持されており、SLSA L3グレードのビルドと運用を円滑に保つために必要なすべてのメタデータが含まれています。Kubernetes上でアプリケーションを構築するための信頼できるソースです。
レジストリは`dp.apps.rancher.io`です。次のものが見つかります:
-
ベースイメージ(BCI) – SUSE Linux Enterprise Base Container Images:最小限で安全な基盤。
-
言語イメージ – Node.js、Go、Rust、Java、Ruby、Clojure… 完全なツールチェーン付き。
-
ミドルウェア – PostgreSQL、Redis、Kafka、MariaDB、Nats、NGINX、Apache ActiveMQ、Apache Apisix、Apache Tomcat…
-
ツール – Helm、Trivy、Cosign、kubectl、ArgoCD、Prometheus、Grafana…
単一のコンテナの形で、または関連する場合は、デプロイ用のhelmチャートを持つフル機能のアプリケーションとして利用可能です。
Rancher Desktopの*SUSEアプリケーションコレクション拡張*は、UIに専用のタブを追加します。カタログをブラウズし、値を設定し、ワンクリックでインストールします – Helmの複雑さは隠されています。
4.2 なぜアプリケーションコレクションが公共レジストリより優れているのか?
| 公共レジストリ | SUSEアプリケーションコレクション | |
|---|---|---|
保守 |
コミュニティ、変動 |
SUSE、エンタープライズSLA |
ベースOS |
Alpine、Debian、Ubuntu… |
SLE BCI(SUSE Linux Enterprise) |
セキュリティパッチ |
メンテナが望むとき |
SUSEによる継続的なCVE追跡 |
署名 |
オプション |
Cosignが組み込まれています |
サプライチェーン |
[可変] |
SBOM、来歴、証明、SLSA L3 |
4.3 認証
アプリケーションコレクションレジストリへの認証は、Rancher DesktopのSUSEアプリケーションコレクション拡張機能によって自動的に構成されます。
動作確認:
docker pull dp.apps.rancher.io/containers/bci-base:latest
認証が構成されていない場合、手動で追加してください:
# Log in to the registry (SUSE Customer Center credentials)
docker login dp.apps.rancher.io
# Verify
docker pull dp.apps.rancher.io/containers/bci-base:latest
Kubernetes(helm install、ポッド) – 画像がすでにプルされていない場合は、プルシークレットが必要です。Rancher Desktopは、これを拡張機能を通じて自動的に処理します。問題がある場合:
kubectl create secret docker-registry application-collection \
--docker-server=dp.apps.rancher.io \
--docker-username=<USERNAME> \
--docker-password=<PASSWORD>
次に、Helmの値に`imagePullSecrets: [{name: application-collection}]`を追加してください。
5.Tiltのインストール
*Tilt*は、コード変更から再デプロイまで、内部ループのすべてのステップを自動化するオープンソースツールです。ファイルを監視し、画像を再構築し、クラスターを更新し、リアルタイムダッシュボードにすべてを表示します。 tilt.devを参照してください。
5.2 Linux(SUSEおよびその他)
curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
スクリプトは、アーキテクチャを検出し、バイナリを`$PATH`(/.local/bin、/usr/local/bin、または`/bin`)に配置します。確認:
tilt version
5.3 Windows
PowerShellで:
iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.ps1'))
Scoopがインストールされている場合、スクリプトは自動的にそれを使用します。そうでない場合は、インストールディレクトリを`$PATH`に追加する必要があります。確認:
tilt version
5.4 Tilt + Rancher Desktop
Tiltは*ホスト上*で実行され(コンテナ内ではなく)、Rancher DesktopによってインストールされたCLIを直接使用します: docker, kubectl, helm。ランタイムがdockerdのとき、Tilt v0.25.1以降でRancher Desktopを自動的に検出します。それにより、ローカルでビルドされたイメージがクラスター内で直接利用可能であることを認識し、*プッシュをスキップ*します。
Tiltがクラスターを自動的に検出しない場合は、Tiltfileの先頭にこの行を追加してください:
allow_k8s_contexts('rancher-desktop')
6.デモ:監視機能付きメッセージウォール
このセクションでは、完全なデモを説明します。内部ループのワークフローを示します: Node.js "`message wall`"アプリケーションがPostgreSQLに接続され、認証にはKeycloakを使用し、Prometheusで計測され、Grafanaで視覚化されます。すべてはSUSE Application Collectionからインストールされます。
完全なソースコードはGitHubで入手可能です: fxHouard/Rancher-Developer-Access-Demo。
6.1 プロジェクト構造
Rancher-Developer-Access-Demo/ +-- src/ | +-- server.js Application (API + UI + Prometheus metrics) +-- k8s/ | +-- appco/ | | +-- deployment.yaml Pod spec with Prometheus annotations | | +-- service.yaml ClusterIP service | | +-- keycloak.yaml Keycloak Deployment + Service (Application Collection image) | +-- shared/ | +-- grafana-dashboard.yaml 8-panel dashboard (auto-provisioned via sidecar) | +-- keycloak-realm.json Realm config (demo user + OAuth client) +-- scripts/ | +-- setup-keycloak-realm.sh Keycloak realm import via Admin REST API +-- values_yaml/ | +-- postgresql.yaml Helm values for PostgreSQL | +-- prometheus.yaml Helm values for Prometheus | +-- grafana.yaml Helm values for Grafana +-- Dockerfile Container image (Application Collection base) +-- Tiltfile Inner loop config (build, deploy, sync, monitoring) +-- package.json
6.2 package.json
{
"name": "message-wall",
"version": "1.0.0",
"description": "SUSE Rancher Developer Access + Tilt: Demo",
"main": "src/server.js",
"scripts": {
"start": "node src/server.js"
},
"dependencies": {
"pg": "^8.13.0",
"prom-client": "^15.1.0"
}
}
依存関係は2つのみ: PostgreSQL用の`pg`とPrometheusメトリクスを公開するための`prom-client`。
6.3 アプリケーション (src/server.js)
アプリケーションは、ビルトインのWeb UIを持つインタラクティブなメッセージウォールです。監視のためにPrometheusメトリクスを公開します。ここが重要な部分です – 完全なファイルはリポジトリにあります。
設定とPrometheusメトリクス:
const http = require('http');
const { Client } = require('pg');
const promClient = require('prom-client');
const PORT = 3000;
// Change this color, save, see it update!
const ACCENT_COLOR = "#747dcd";
// --- Prometheus metrics ---
// collectDefaultMetrics() automatically exposes Node.js
// metrics: CPU, heap memory, event loop lag, GC...
promClient.collectDefaultMetrics();
// Custom metrics -- prefixed "app_" for easy discovery
const httpDuration = new promClient.Histogram({
name: 'app_http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'path', 'status'],
buckets: [0.005, 0.01, 0.05, 0.1, 0.5, 1],
});
const messagesPosted = new promClient.Counter({
name: 'app_messages_posted_total',
help: 'Total number of messages posted',
});
const messagesDeleted = new promClient.Counter({
name: 'app_messages_deleted_total',
help: 'Total number of bulk deletes',
});
const messagesCurrent = new promClient.Gauge({
name: 'app_messages_count',
help: 'Current number of messages in the database',
});
なぜ`app_`プレフィックスが必要ですか? – Prometheusは数百のメトリクス(Node.js、k8s、システムなど)を収集します。`app_`プレフィックスを使用すると、アプリケーションのメトリクスを瞬時に見つけることができます:Grafanaに`app_`と入力すると、自動補完が残りを処理します。
Prometheusメトリクスタイプ:
| タイプ | 使用方法 | デモ例 |
|---|---|---|
カウンタ |
値は常に増加します |
|
ゲージ |
値は増減します |
|
ヒストグラム |
値の分布(レイテンシ) |
|
APIルート:
アプリケーションは6つのルートを公開しています:`GET /`はHTMLページを提供し、`GET /api/messages`は最新の50件のメッセージをリストし、`POST /api/messages`はメッセージを作成します(280文字制限)、`DELETE /api/messages`はすべてのメッセージを削除し、`GET /health`はK8sプローブ(ライブネス+準備完了)として機能し、`GET /metrics`はPrometheusメトリクスをテキスト形式で公開します。
エンドポイント:/metrics
if (req.method === 'GET' && req.url === '/metrics') {
const metrics = await promClient.register.metrics();
res.writeHead(200, { 'Content-Type': promClient.register.contentType });
res.end(metrics);
// Do not record /metrics in the histogram (noise)
return;
}
これはPrometheusが定期的にスクレイピングするエンドポイントです。すべてのメトリクスはOpenMetricsテキスト形式で返されます。`/metrics`自体はヒストグラムによって測定されないことに注意してください – それはノイズになります。
測定ミドルウェア:
すべてのHTTPリクエストは自動的にタイミングされます:
const end = httpDuration.startTimer();
// ... request processing ...
end({ method: req.method, path: routePath, status: statusCode });
ヒストグラムは、期間、メソッド、パス、および戻りコードを記録します。Grafanaは、ルートごとにパーセンタイル(p50、p95、p99)を計算できます。
HTMLページ:
アプリケーションは、Node.jsから直接インタラクティブなメッセージウォールを提供します(`server.js`内のインラインHTML)。UIには、入力フィールド、ポッド名とアップタイムを表示する情報バー、メッセージが変更されていない場合はタイムスタンプのみが更新されるスマートディフを使用した3秒ごとの自動ポーリングが含まれています(ちらつきなし)。
live_updateデモ – 9行目の`ACCENT_COLOR`定数を変更し、保存します。約2秒後、メッセージを失うことなく壁の色が変わります。これがTiltのlive_updateの実行です。
6.4 Dockerfile
FROM dp.apps.rancher.io/containers/nodejs:24-dev
WORKDIR /app
COPY package.json ./
RUN npm install --no-package-lock
COPY . .
EXPOSE 3000
CMD ["node", "src/server.js"]
本番環境では、最終イメージ(npmやビルドツールなしの最小限の`nodejs:24`イメージ)から`npm install`ステップ(npmを使用した開発イメージ)を分離するためにマルチステージビルドを使用します。ここでは、デモ用のシンプルなDockerfileを保持しています。さらに進む: 最小限のイメージを参照してください。
6.5 Kubernetesマニフェスト
k8s/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: message-wall
spec:
replicas: 1
selector:
matchLabels:
app: message-wall
template:
metadata:
labels:
app: message-wall
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "3000"
prometheus.io/path: "/metrics"
spec:
containers:
- name: message-wall
image: message-wall # Tilt replaces with local image
ports:
- containerPort: 3000
env:
- name: DB_HOST
value: "demo-db-postgresql"
- name: DB_PORT
value: "5432"
- name: DB_USER
value: "demo"
- name: DB_PASSWORD
value: "demo"
- name: DB_NAME
value: "demo"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
Prometheusアノテーション –
template.metadata.annotations`の下にある3つのアノテーションはPrometheusに次のように指示します: "`このポッドをポート3000で、パス/metrics``をスクレイプします。"Prometheusサーバーは、Kubernetesの自動検出のためにデフォルトで構成されており、アノテーションされたポッドを自動的に検出します。追加のPrometheus構成は必要ありません。
インデントに注意してください – アノテーションは`template.metadata`(ポッドテンプレート)の下に配置する必要があり、Deploymentの`metadata`や`spec`レベルの下には配置しないでください。これは一般的な間違いです: アノテーションが間違ったレベルにあると、Prometheusはポッドを見つけることができません。
k8s/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: message-wall
spec:
selector:
app: message-wall
ports:
- port: 3000
targetPort: 3000
6.6 PostgreSQL、Prometheus、Grafanaのインストール
これら3つはすべてインフラストラクチャサービスです – Rancher Desktopを介して一度インストールし、すべての`tilt up`にインストールしないでください。これらは開発セッション間で持続します。
リポジトリには、各サービスのための`values_yaml/`にHelm値ファイルが含まれています。Rancher Desktopアプリケーションのコレクションタブで、各チャートを検索し、YAMLモードに切り替えて、対応するファイルを貼り付けます。
values_yaml/postgresql.yaml:
auth:
database: demo
postgresPassword: demo
postgresUsername: demo
username: demo
global:
imagePullSecrets:
- application-collection
values_yaml/prometheus.yaml:
alertmanager:
service:
type: NodePort
global:
imagePullSecrets:
- application-collection
values_yaml/grafana.yaml:
adminPassword: admin
global:
imagePullSecrets:
- application-collection
sidecar:
dashboards:
enabled: true
datasources:
enabled: true
imagePullSecrets– 各値ファイルは`application-collection`シークレットを参照しているため、ポッドは`dp.apps.rancher.io`からイメージを取得できます。このシークレットはRancher Desktop拡張機能によって自動的に作成されます。
Grafanaサイドカー – `sidecar.dashboards.enabled`および`sidecar.datasources.enabled`の設定は重要です。彼らはGrafanaと一緒に小さなコンテナを起動し、特定のラベルを持つKubernetes ConfigMapを監視し、その内容を自動的にGrafanaに読み込みます。データソースを手動で設定したり、ダッシュボードをインポートしたりする必要はありません。
| サイドカー | 監視されたラベル | 説明 |
|---|---|---|
|
|
ダッシュボードJSONファイルを自動的に読み込みます |
|
|
データソースを自動的に設定します |
サイドカーがアクティブであることを確認してください:
kubectl get pods -l app.kubernetes.io/name=grafana \
-o jsonpath='{.items[0].spec.containers[*].name}'
# Expected: grafana grafana-sc-dashboard grafana-sc-datasources
`grafana`しか見えない場合は、Rancher Desktop UIに戻り、`sidecar.dashboards.enabled`と`sidecar.datasources.enabled`の両方が`true`であることを確認してください。その後、*アップグレードする*を実行してください。
PostgreSQLのHelmチャートは、自動的にユーザー、データベース、および`<release-name>-postgresql`という名前のサービスを作成します。Tiltfileは`app.kubernetes.io/name=postgresql`ラベルを介してこのサービスを自動検出します – リリース名を覚えておく必要はありません。
6.7 Keycloak: Helmチャートを使わない認証
KeycloakはMessage WallのためにOAuth2 / OpenID Connect認証を提供します。ユーザーはログインし、アプリはメッセージを投稿または削除する前に彼らのアイデンティティトークンを確認します。
なぜHelmインストールがないのですか? – PostgreSQL、Prometheus、Grafanaとは異なり、SUSE Application CollectionにはKeycloakのHelmチャートはありません。コンテナイメージとしてのみ利用可能です (dp.apps.rancher.io/containers/keycloak)。これは現実的なシナリオです:すべてのアプリケーションがHelmチャートを提供するわけではなく、開発者は生のKubernetesマニフェストをデプロイする方法を知っておく必要があります。
k8s/appco/keycloak.yaml(簡略化):
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak-appco
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
volumes:
- name: realm-config
configMap:
name: keycloak-realm
containers:
- name: keycloak
image: dp.apps.rancher.io/containers/keycloak:26.5.4
args: ["start-dev", "--health-enabled=true", "--import-realm"]
volumeMounts:
- name: realm-config
mountPath: /opt/keycloak/data/import
env:
- name: KC_DB
value: postgres
- name: KC_DB_URL
value: jdbc:postgresql://PLACEHOLDER_PG_SVC:5432/keycloak
# ... KC_DB_USERNAME, KC_DB_PASSWORD, KEYCLOAK_ADMIN, etc.
readinessProbe:
httpGet:
path: /health/ready
port: 9000
initialDelaySeconds: 30
imagePullSecrets:
- name: application-collection
重要なポイント:
start-dev+--import-realm– Keycloakは開発モード(HTTP、証明書なし)で起動し、`/opt/keycloak/data/import`に見つかったJSONレルムファイルを自動的にインポートします。レルムConfigMap – レルムJSONを含むConfigMap (
keycloak-realm) がボリュームとしてマウントされます。このConfigMapは`k8s/shared/keycloak-realm.json`からTiltfileによって作成されます。
PLACEHOLDER_PG_SVC– Tiltfileはデプロイ時にラベルセレクターを介して発見された実際のPostgreSQLサービス名でこれを置き換えます。別のデータベース – Keycloakは同じPostgreSQLインスタンス内で専用の`keycloak`データベースを使用します。Tiltfileは存在しない場合、自動的に作成します。
レルムセットアップスクリプト (scripts/setup-keycloak-realm.sh):
Tiltfileは、フォールバックとしてAdmin REST APIを介してセットアップスクリプトも実行します。このスクリプトは冪等なスクリプトです:レルムがすでに存在するかどうかを確認し、管理者トークンを取得し、必要に応じてレルムを作成します。これは、ConfigMapのインポートが認識されない場合(例:ConfigMapが作成される前にKeycloakがすでに実行されていた場合)を処理します。
6.8 Grafanaダッシュボード(自動プロビジョニングされたConfigMap)
GrafanaダッシュボードはKubernetes ConfigMapで定義されています。前のステップで有効にされたサイドカーのおかげで、自動的に読み込まれます – 手動インポートはゼロです。
k8s/grafana-dashboard.yaml(構造 – 完全なファイルはリポジトリにあります):
apiVersion: v1
kind: ConfigMap
metadata:
name: message-wall-dashboard
labels:
grafana_dashboard: "1" # the sidecar detects this label
data:
message-wall.json: |
{
"uid": "message-wall",
"title": "Message Wall",
"panels": [ ... ]
}
uid: "prometheus"– 各パネルは`"uid": "prometheus"`によってデータソースを参照します。このuidはTiltfileによって生成されたデータソースConfigMapに宣言されたものと*正確に一致する必要があります*(次のセクションを参照)。JSONが`${DS_PROMETHEUS}`(Grafana UIインポート構文)を使用している場合、サイドカーはその変数を*解決しません*。ハードコーディングされたuidを使用する必要があります。
ダッシュボードには8つのパネルがあります:
| パネル | タイプ | メトリック | 表示内容 |
|---|---|---|---|
データベース内のメッセージ |
統計 |
|
しきい値のあるゲージ 緑 < 50 < 黄 < 100 < 赤 |
投稿されたメッセージ |
統計 |
|
投稿されたメッセージの合計カウンター |
一括削除 |
統計 |
|
一括削除の合計カウンター |
リクエスト / 秒 |
時系列 |
|
HTTPルートごとのスループット |
投稿 / 分 |
時系列 |
|
発行率 |
レスポンスタイム(p95) |
時系列 |
|
ルートごとの95パーセンタイルのレイテンシ |
メモリ使用量 |
時系列 |
|
RSS + Node.jsヒープ |
イベントループの遅延(p99) |
時系列 |
|
イベントループの健康 |
なぜ`rate(…[5m])
でなく[1m]`なのですか? – `rate()`関数はウィンドウ内に少なくとも2つのデータポイントが必要です。Prometheusが60秒ごとにスクレイプする場合、1分のウィンドウには1つのポイントしかなく、何も返しません。経験則:`rate()`ウィンドウを少なくとも*スクレイプ間隔の2倍*に設定してください。5分は安全なデフォルトです。
宣言的プロビジョニング – ダッシュボードはGitにあり、コードと共にバージョン管理されています。Grafanaで変更を加えた場合(パネルの追加、クエリの変更)、エクスポートしてConfigMapを更新し、次回の再デプロイ時に変更が失われないようにしてください。これは、監視に適用された*Infrastructure as Code*アプローチです。
6.9 完全なTiltfile
# Demo Tiltfile
load('ext://restart_process', 'docker_build_with_restart')
allow_k8s_contexts('rancher-desktop')
# --- Helpers ---------------------------------------------------------
def find_service(label_selector, required=False, name='Service'):
# Discover a Kubernetes service by label selector.
#
# Helm charts installed via Rancher Desktop get random release
# names (e.g. postgresql-1772033328). Searching by label is
# robust regardless of the release name.
svc = str(local(
"kubectl get svc -l " + label_selector +
" -o jsonpath='{.items[0].metadata.name}'",
quiet=True,
)).strip()
if required and svc == '':
fail(name + ' not found. Install it via Rancher Desktop ' +
'(Application Collection).')
return svc
# --- Service discovery -----------------------------------------------
pg_svc = find_service(
'app.kubernetes.io/name=postgresql',
required=True,
name='PostgreSQL',
)
grafana_svc = find_service('app.kubernetes.io/name=grafana')
prometheus_svc = find_service(
'app.kubernetes.io/name=prometheus,app.kubernetes.io/component=server',
)
# --- Application -----------------------------------------------------
docker_build_with_restart(
'message-wall',
'.',
entrypoint=['node', 'src/server.js'],
only=['src/', 'package.json'],
live_update=[
sync('./src', '/app/src'),
run('cd /app && npm install --no-package-lock',
trigger=['package.json']),
],
)
deployment = str(read_file('k8s/appco/deployment.yaml')).replace(
'PLACEHOLDER_PG_SVC', pg_svc)
service = str(read_file('k8s/appco/service.yaml'))
k8s_yaml([blob(deployment), blob(service), 'k8s/shared/grafana-dashboard.yaml'])
k8s_resource(
'message-wall-appco',
port_forwards='3000:3000',
labels=['app'],
)
# --- Keycloak (no Helm chart — deployed as raw K8s manifest) ---------
# Ensure keycloak DB exists in PostgreSQL
pg_pod = str(local(
"kubectl get pods -l app.kubernetes.io/name=postgresql "
"-o jsonpath='{.items[0].metadata.name}'", quiet=True)).strip()
local("kubectl exec " + pg_pod +
" -- env PGPASSWORD=demo psql -U demo -tc "
"\"SELECT 1 FROM pg_database WHERE datname='keycloak'\""
" | grep -q 1 || kubectl exec " + pg_pod +
" -- env PGPASSWORD=demo psql -U demo -c 'CREATE DATABASE keycloak'",
quiet=True)
# Realm ConfigMap (auto-imports realm with demo user + OAuth client)
local('kubectl create configmap keycloak-realm '
'--from-file=message-wall.json=k8s/shared/keycloak-realm.json '
'--dry-run=client -o yaml | kubectl apply -f -', quiet=True)
# Deploy Keycloak using the Application Collection container image
keycloak_yaml = str(read_file('k8s/appco/keycloak.yaml')).replace(
'PLACEHOLDER_PG_SVC', pg_svc)
k8s_yaml(blob(keycloak_yaml))
k8s_resource('keycloak-appco', port_forwards='8080:8080', labels=['app'])
# Realm setup via Admin REST API (idempotent fallback)
local_resource(
'keycloak-realm-setup',
cmd='bash scripts/setup-keycloak-realm.sh http://localhost:8080 '
'k8s/shared/keycloak-realm.json',
labels=['app'],
resource_deps=['keycloak-appco'],
)
# --- Monitoring (optional) -------------------------------------------
if grafana_svc:
local_resource(
'grafana',
serve_cmd='kubectl port-forward svc/' + grafana_svc + ' 3001:80',
labels=['monitoring'],
allow_parallel=True,
links=['http://localhost:3001',
'http://localhost:3001/d/message-wall/'],
)
if prometheus_svc:
local_resource(
'prometheus',
serve_cmd='kubectl port-forward svc/' + prometheus_svc
+ ' 9090:80',
labels=['monitoring'],
allow_parallel=True,
links=['http://localhost:9090'],
)
datasource_cm = """apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-datasource-prometheus
labels:
grafana_datasource: "1"
data:
prometheus.yaml: |
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
uid: prometheus
url: http://{svc}:80
access: proxy
isDefault: true
editable: false
""".format(svc=prometheus_svc)
k8s_yaml(blob(datasource_cm))
k8s_resource(
objects=['message-wall-dashboard:configmap',
'grafana-datasource-prometheus:configmap'],
new_name='grafana-config',
labels=['monitoring'],
links=['http://localhost:3001/d/message-wall/'],
)
Tiltfileの重要なポイント:
find_service()`ヘルパー – Rancher Desktop UIを介してインストールされたサービスはランダムなリリース名(例:`grafana-1772033328)を持っています。これらの名前をハードコーディングする代わりに、ヘルパーはHelmチャートによって設定されたKubernetesラベルを介してそれらを発見します。これは堅牢なパターンです:Tiltfileはリリース名に関係なく機能します。
docker_build_with_restart – `restart_process`拡張は、解釈型言語特有の問題を解決します。`live_update`がファイルをコンテナに同期すると、Node.jsはそれを認識しません – コードはすでにメモリにロードされています。`docker_build_with_restart`はエントリポイントをラップして、各同期後にプロセスを自動的に再起動します。コンパイル言語(Go、Rust)では、`docker_build`にコンパイルステップを含む`run()`標準的なアプローチが一般的です。
ConfigMapとしてのPrometheusデータソース – Grafana UIでPrometheusを設定するのではなく(手動設定は再デプロイ時に失われます)、Tiltfileは`grafana_datasource: "1"`ラベルを持つConfigMapを生成します。Grafanaサイドカーはそれを検出し、自動的に接続をプロビジョニングします。PrometheusのURLは動的に注入されます:http://<detected-service-name>:80。
links – 各`local_resource`と`k8s_resource`はTiltダッシュボードにクリック可能なリンクを宣言できます。Tilt UIでGrafana、Prometheus、および特定のダッシュボードのURLを直接確認できます。
objects + new_name – ConfigMapはワークロード(Deployment、StatefulSetなど)ではないため、Tiltはデフォルトでそれらを"`uncategorized`"の下に配置します。objects`ディレクティブは、それらを明示的な名前(`grafana-config)と`monitoring`ラベルの下にグループ化します。
Keycloakのデプロイメント – SUSE Application CollectionにはKeycloak用のHelmチャートがないため、Tiltfileはそれを生のK8sマニフェストとしてデプロイします。最初にPostgreSQLに`keycloak`データベースが存在することを確認し、realm JSONを含むConfigMapを作成し、Keycloak Deploymentをデプロイします(`PLACEHOLDER_PG_SVC`を検出されたサービス名に置き換え)、そしてAdmin REST APIを介してセットアップスクリプトを冪等的なフォールバックとして実行します。
条件性 – モニタリングブロックは条件付きです(if grafana_svc / if prometheus_svc)。PrometheusとGrafanaがインストールされていなくても、Tiltfileは機能します – アプリとPostgreSQLのみが必要です。監視はオプトインのボーナスです。
6.10 デモの実行
# 1. Clone the repo
git clone https://github.com/fxHouard/Rancher-Developer-Access-Demo.git
cd Rancher-Developer-Access-Demo
# 2. Install services via Rancher Desktop (one time only):
#
# PostgreSQL:
# Application Collection tab -> search PostgreSQL -> Install
# Switch to YAML mode, paste values_yaml/postgresql.yaml, Install
#
# Prometheus:
# Application Collection tab -> search Prometheus -> Install
# Switch to YAML mode, paste values_yaml/prometheus.yaml, Install
#
# Grafana:
# Application Collection tab -> search Grafana -> Install
# Switch to YAML mode, paste values_yaml/grafana.yaml, Install
# 3. Start the inner loop:
tilt up
# 4. Press Space to open the Tilt dashboard in your browser
# 5. From the Tilt dashboard, click the links to:
# -> http://localhost:3000 -- The Message Wall app
# -> http://localhost:8080 -- Keycloak (admin / admin)
# -> http://localhost:9090 -- Prometheus (check targets)
# -> http://localhost:3001 -- Grafana (admin / admin)
# 6. In Grafana: go to Dashboards -> "Message Wall" is already there
# (or click the direct link in Tilt)
# 7. Post messages on localhost:3000 and watch the metrics
# update in real time in Grafana
# 8. Change ACCENT_COLOR in src/server.js (line 9)
# -> save -> ~2 sec -> the color changes
6.11 背後で何が起こるか
1. `tilt up` on the host: +-- Auto-detects PostgreSQL (label app.kubernetes.io/name=postgresql) +-- Auto-detects Prometheus and Grafana (labels app.kubernetes.io/name=...) +-- Creates keycloak DB in PostgreSQL if needed +-- docker build message-wall -> image in dockerd local store | +-- k3s sees the image because same store -> imagePullPolicy: IfNotPresent +-- kubectl apply deployment + service -> k3s creates the app pod | +-- Pod connects to detected PG service (K8s internal DNS) | +-- Prometheus scrapes the pod (prometheus.io/* annotations) +-- Deploys Keycloak (Application Collection image, raw K8s manifest) | +-- Imports realm via ConfigMap volume mount | +-- Runs setup script via Admin REST API (idempotent fallback) +-- kubectl apply ConfigMaps (datasource + dashboard) | +-- Grafana sidecars detect and load them +-- Port-forward 3000 -> localhost:3000 (app) +-- Port-forward 8080 -> Keycloak +-- Port-forward 3001 -> Grafana +-- Port-forward 9090 -> Prometheus 2. Dev modifies src/server.js: +-- Tilt (on host) detects the change (file watcher) +-- live_update syncs ./src -> /app/src in the pod (kubectl cp) +-- restart_process relaunches `node src/server.js` in the container +-- ~2 seconds later: change visible on localhost:3000 3. Observability loop (continuous): +-- Prometheus scrapes localhost:3000/metrics every 15-60s +-- Grafana queries Prometheus to display dashboards +-- Dev sees the impact of their changes in real time
6.12 ポートフォワーディングの説明
Tiltは`port_forwards`を介してポートフォワーディングを自動的に管理します k8s_resource()。これは`kubectl port-forward`と同等ですが、Tiltのライフサイクルに統合されています(ポッドが再作成された場合は自動的に再起動されます)。
完全なポートフォワーディングチェーン:
Browser (localhost:3000)
-> Tilt port-forward
-> K8s Service (ClusterIP)
-> Your app pod (:3000)
-> Connects to PostgreSQL Service (:5432)
-> PostgreSQL pod
ポッド内のアプリはKubernetes内部DNSを使用してPostgreSQLにアクセスします:
postgresql://user:pass@demo-db-postgresql.default.svc.cluster.local:5432/mydb
ローカルマシンから(例えばSQLクライアント用):kubectl port-forward svc/demo-db-postgresql 5432:5432。
7.完全なワークフロー
| # | ステップ | [ツール] | 詳細 |
|---|---|---|---|
1 |
コードを書く |
VS Code + 拡張機能 |
エディタ、自動補完、リンティング、Git |
2 |
反復(内部ループ) |
Tilt |
自動ビルド + ライブ同期 + ダッシュボード |
3 |
認証 |
Keycloak |
OAuth2ログイン、Tiltによって自動プロビジョニングされたレルム |
4 |
観察する |
Prometheus + Grafana |
リアルタイムメトリクス、自動プロビジョニングされたダッシュボード |
5 |
コミット + プッシュ |
Git |
GitOpsの真実の源 |
6 |
ビルド + スキャン(外ループ) |
CIパイプライン + Trivy + Cosign |
脆弱性 + 画像署名 |
7 |
デプロイ |
Argo CD |
自動同期Git → クラスター |
8 |
プログレッシブデプロイメント |
Argo Rollouts |
カナリア / ブルーグリーン分析付き |
8.さらに進む
上記のデモは、内ループの必須事項をカバーしています。このセクションでは、プロジェクトが成長するにつれてワークフローを補完する追加のツールとプラクティスを紹介します。
8.1 開発コンテナ:再現可能な環境
原則:あなたの開発環境は*コード*で定義されています(.devcontainer/)。プロジェクトを開くすべての開発者は、同じツール、同じバージョン、同じVS Code拡張機能を持つまったく同じ環境を得ます。
Dev Containerは開発コンテナであり、コーディング専用です。Docker、kubectl、またはHelmは必要ありません。Tiltはホスト上で実行されます。
.devcontainer/Dockerfile:
FROM dp.apps.rancher.io/containers/nodejs:24-dev
# System tools (available in the SLE_BCI repo)
# gawk: required by VS Code Server (check-requirements.sh)
RUN zypper --non-interactive install -y git openssh make gawk \
&& zypper clean -a
.devcontainer/devcontainer.json:
{
"name": "Message Wall - Node.js",
"build": {
"dockerfile": "Dockerfile"
},
"customizations": {
"vscode": {
"extensions": [
"ms-kubernetes-tools.vscode-kubernetes-tools"
],
"settings": {
"vs-kubernetes.disable-linters": true
}
}
},
"postCreateCommand": "if [ -f package.json ]; then npm install --no-package-lock; fi"
}
開発コンテナ内に`docker`、
kubectl、helm、または`tilt`はありません。Tiltはホスト上で実行されます。Kubernetes拡張機能はK8sマニフェスト編集のためのIntelliSenseを提供します。`kubeconfig not found`の警告は予期されるもので無害です – クラスターエクスプローラーはコンテナ内では動作しませんが、自動補完は機能します。
VS CodeがDev Containerに接続されているとき、すべての統合ターミナルは*コンテナ内*で開きます。しかし、Tiltはホスト上(docker、kubectl、helmがある場所)で実行する必要があります。別のターミナル(Terminal.app、iTerm2、Warp、Windows Terminal…)または同じフォルダーで開かれた2つ目のVS Codeウィンドウを使用してください*なし* “Reopen in Container”。
8.2 リソース要求と制限
デモでは、簡潔さのためにデプロイメントに`resources`ブロックがありません。本番環境(または共有クラスター)では、常にそれらを定義する必要があります:
resources:
requests:
cpu: 100m # 0.1 vCPU -- scheduler reserves this
memory: 128Mi # 128 MB -- guaranteed minimum
limits:
cpu: 500m # 0.5 vCPU -- ceiling, throttled beyond
memory: 256Mi # 256 MB -- OOMKill beyond
`requests`または`limits`がないと、ポッドはすべてのノードリソースを消費し、他のワークロードに影響を与える可能性があります – *“noisy neighbor”*の問題です。`requests`はスケジューラー(インテリジェント配置)用で、`limits`はノードを保護します(CPU制限、メモリが超過した場合のOOMKill)。
8.3 Testcontainers:統合テスト
*概念:*Testcontainersは、統合テストで一時的なDockerコンテナを立ち上げるライブラリです。SQLクエリをテストするためにPostgreSQLが必要ですか?Testcontainersは1つを起動し、テストを実行し、完了したらそれを破棄します。
なぜ重要なのか:
-
*再現性:*各テストは新しいデータベースを開始します – テスト間の汚染はありません。
-
モックなし: 実際のデータベースに対してテストを行い、逸脱する可能性のあるモックは使用しません。
-
CIフレンドリー: GitHub Actions、GitLab CIなどで動作します(Dockerが必要です)。
TestcontainersはホストのDockerデーモンを使用します。Rancher Desktop(dockerd)を使用すると、直接動作します - 特別な設定は必要ありません。
設定:
# macOS / Linux -- already configured if ~/.rd/bin is in PATH
export DOCKER_HOST=unix://$HOME/.rd/run/docker.sock
例(Node.js):
const { GenericContainer } = require('testcontainers');
const { Client } = require('pg');
describe('Database integration', () => {
let container, client;
beforeAll(async () => {
container = await new GenericContainer('dp.apps.rancher.io/containers/postgresql:17')
.withExposedPorts(5432)
.withEnvironment({ POSTGRES_USER: 'test', POSTGRES_PASSWORD: 'test', POSTGRES_DB: 'test' })
.start();
client = new Client({
host: container.getHost(),
port: container.getMappedPort(5432),
user: 'test', password: 'test', database: 'test',
});
await client.connect();
});
afterAll(async () => {
await client.end();
await container.stop();
});
test('should insert and retrieve data', async () => {
await client.query('CREATE TABLE test (id SERIAL, name TEXT)');
await client.query("INSERT INTO test (name) VALUES ('hello')");
const result = await client.query('SELECT * FROM test');
expect(result.rows).toHaveLength(1);
});
});
TestcontainersとTilt+Helmの比較:
| Testcontainers | Tilt + Helm | |
|---|---|---|
ライフサイクル |
一時的(1回のテスト実行) |
永続的(開発セッションの期間) |
Data |
毎回新鮮 |
永続的(削除されない限り) |
使用方法 |
CIでの統合テスト |
日々のローカル開発 |
8.4 mirrord: リモートクラスターでのローカルデバッグ
mirrordを使用すると、ローカルプロセスを実行しながら、リモートKubernetesクラスター内のポッドのネットワークとファイルシステムに接続できます。コードはローカルで実行されますが、“sees” クラスター環境に接続します。
*依存関係がすべて揃ったローカルクラスタがある場合*は、mirrordは必要ありません。アプリには1から5のマイクロサービスがあります。
アプリに20以上のマイクロサービスがある場合(すべてをローカルで実行することは不可能です)、ローカルでは利用できない管理サービスが必要な場合、または実際のクラスターのコンテキストでローカルデバッグ(VS Codeでのブレークポイント)を行いたい場合に、mirrordは興味深くなります。
勝利の組み合わせ ― 日々の内ループ(ローカルクラスタ)にはTilt、ステージングでの偶発的なデバッグにはmirrordを使用します。2つのツールは補完的です。
8.5 HelmとKustomize
*Helm*はKubernetesマニフェストをテンプレート化し、それを"`charts`"として配布します。これはKubernetes用のパッケージマネージャー(npm、zypperなど)に相当します。`values.yaml`は環境(開発、ステージング、本番)ごとにデプロイメントをカスタマイズします。Helmはデプロイされたバージョンを追跡し、ロールバックを可能にします。
*Kustomize*は基本のYAMLマニフェストに*パッチを重ねる*ことで機能します。テンプレート言語はありません:宣言的な変換を適用します。kubectlにネイティブに統合されています(kubectl apply -k)。
HelmまたはKustomize? – 2つは相互排他的ではありません。一般的なアプローチ:外部チャート(データベース、監視)にはHelmを使用し、環境ごとに自分のマニフェストをカスタマイズするためにKustomizeを使用します。
8.6 セキュリティ:TrivyとCosign
Trivy(アプリケーションコレクションで利用可能)は、コンテナイメージ、Kubernetesファイル、依存関係、IaCコードをスキャンして、既知の脆弱性(CVE)、誤設定、公開された秘密を検出します。
trivy image dp.apps.rancher.io/containers/nodejs:24
trivy k8s --report summary cluster
Cosign(アプリケーションコレクションで利用可能)は、コンテナイメージに暗号的に署名することを可能にします。これは*サプライチェーンセキュリティ*の柱です。
cosign sign --key cosign.key myregistry/myapp:v1.0
cosign verify --key cosign.pub myregistry/myapp:v1.0
8.7 GitOps:Argo CDとArgo Rollouts
*GitOps*は、*Gitがインフラストラクチャとアプリケーションの望ましい状態の唯一の真実のソースである*という原則です。デプロイメントツールはGitリポジトリを監視し、クラスターの状態をGitで宣言された状態と自動的に調整します。
Argo CD(アプリケーションコレクションで利用可能)は、Kubernetes用の宣言的な継続的デプロイメントツールです:Gitでの宣言的状態、ドリフト検出による自動同期、マルチクラスターサポート、可視化のためのWeb UIを備えています。
Argo Rollouts(アプリケーションコレクションにも含まれます)は、Kubernetesに*ブルーグリーン*および*カナリア*デプロイメント戦略を追加し、デプロイメントを昇格またはロールバックするかを決定するための自動メトリック分析を行います。
8.8 Kubernetesの"`12 Factor App`"
-
環境による設定: ConfigMapsとSecretsを使用し、イメージ内に設定をハードコーディングしないでください。
-
ステートレスプロセス: アプリの各インスタンスは同一で置き換え可能でなければなりません。
-
ポートバインディング: アプリはポートを公開し、Kubernetesはサービスを介してルーティングを処理します。
-
標準出力へのログ: ログファイルに書き込まないでください。Kubernetes / Fluent Bitに標準出力を収集させてください。
-
ヘルスチェック: 常にライブネスとレディネスのプローブを実装してください。
-
メトリクス:
/metricsPrometheusエンドポイントを公開してください。監視は贅沢ではなく、標準です。
9.用語集
| 期間 | 定義 |
|---|---|
インナーループ |
迅速なローカル開発サイクル:コード、ビルド、デプロイ、テスト |
アウターループ |
自動化されたコミット後のサイクル:CI/CD、テスト、デプロイメント |
ティルト |
live_updateとウェブダッシュボードを備えたインナーループツール |
Tiltfile |
Starlark(Pythonに似たDSL)でのTilt設定ファイル |
live_update |
Tiltの機能:再ビルドなしで、コンテナ内にファイルをsyncします。 |
restart_process |
Tiltの拡張機能:live_update後にアプリケーションのプロセスを再起動します。 |
開発用コンテナ |
コンテナ内の開発環境を定義するためのオープン仕様 |
mirrord |
ローカルプロセスをリモートK8sクラスターコンテキストに接続します |
Testcontainers |
テストにおけるコンテナ化された依存関係のライブラリ |
Helm |
Kubernetesのパッケージマネージャー(チャート) |
Kustomize |
K8s マニフェストのカスタマイズ(パッチ/オーバーレイ) |
Trivy |
画像、コード、IaCの脆弱性スキャナー |
Cosign |
コンテナイメージの署名/検証ツール |
Argo CD |
KubernetesのためのGitOps継続的デプロイメントツール |
Argo Rollouts |
Kubernetesのカナリアおよびブルーグリーンデプロイメント |
GitOps |
パラダイム:Git = クラスター状態の唯一の信頼できる情報源 |
Keycloak |
オープンソース アイデンティティおよびアクセス管理(OAuth2 / OpenID Connect) |
Prometheus |
HTTPスクレイピングを通じてメトリクスを収集する監視システム |
Grafana |
メトリクス可視化プラットフォーム(ダッシュボード) |
prom-client |
Prometheusメトリクスを公開するためのNode.jsライブラリ |
Sidecar |
補完的なタスクを実行するポッド内の補助コンテナ |
ConfigMap |
設定を保存するためのK8sリソース(ここでは: ダッシュボード、データソース) |
k3s |
Rancher Desktopによって使用される軽量Kubernetesディストリビューション |
BCI |
SUSEのベースコンテナイメージ: アプリケーションコレクションイメージの基盤 |
OCI |
オープンコンテナイニシアティブ: コンテナイメージの標準 |
imagePullPolicy |
K8sポリシー: |
10.さらなる情報
-
Rancher Desktop: rancherdesktop.io
-
SUSE アプリケーションコレクション: apps.rancher.io/
-
Tilt: tilt.dev – そしてブログ記事 Tilt + Rancher Desktop
-
mirrord: mirrord.dev
-
Testcontainers: testcontainers.com
-
Dev Containers Spec: containers.dev
-
Argo CD: argoproj.github.io/cd
-
ヘルム: helm.sh
-
Cosign: docs.sigstore.dev/cosign
-
キークローク: keycloak.org
-
プロメテウス: prometheus.io
-
グラファナ: grafana.com
-
prom-client (Node.js): github.com/siimon/prom-client
楽しいKubernetes開発を!