本記事では、external-secretsのPushSecret機能を使用して、Kubernetesクラスター間でシークレットを共有する方法を解説します。マルチクラスター環境での実運用において、シークレットの同期は重要な課題の一つです。クラスター間でシークレットを安全に共有し、自動的に同期する仕組みを構築することで、運用の効率化とセキュリティの向上を図ることができます。
実現したい構成
以下のような構成を実現し、ソースクラスターのシークレットをターゲットクラスターに自動的に同期します:
前提条件
本記事の手順を実行するには、以下のツールが必要です:
- Docker
- k3d
- kubectl
- Helm
- kubectx(オプション: コンテキスト切り替えを簡単にするため)
- kubectl-view-secret (オプション: シークレットの内容確認のため)
環境構築
k3dで次の2つクラスターを構築します。
- ソースクラスター:
k3d-source-cluster
- ターゲットクラスター:
k3d-target-cluster
構築手順は本記事の本質ではないものの、長くなってしまったので折りたたんであります↓
構築手順を見る
共有ネットワークの作成
まず、クラスター間で通信できるように共有ネットワークを作成します。このネットワークは、後ほど作成する2つのKubernetesクラスター間の通信を可能にします。
docker network create shared-net --subnet 172.28.0.0/16 --gateway 172.28.0.1
このコマンドで作成されるネットワークは以下の特徴を持ちます:
- サブネット: 172.28.0.0/16
- ゲートウェイ: 172.28.0.1
- ネットワーク名: shared-net
ソースクラスターの構築
ソースクラスターは、シークレットを提供する側のクラスターです。以下の設定で構築します。
source-cluster.yaml
を作成し、以下の内容を記述します:
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: source-cluster
servers: 1
agents: 1
image: docker.io/rancher/k3s:v1.30.0-k3s1
kubeAPI:
host: 0.0.0.0
hostIP: 127.0.0.1
hostPort: "6443"
ports:
- port: 8080:80
nodeFilters:
- loadbalancer
registries:
create:
name: registry.localhost
host: 127.0.0.1
hostPort: "15000"
network: shared-net
options:
k3d:
wait: true
kubeconfig:
updateDefaultKubeconfig: true
switchCurrentContext: true
k3s:
extraArgs:
# CIDR範囲の重複回避のため、クラスターごとに異なるCIDRを指定
- arg: "--cluster-cidr=10.42.0.0/16"
nodeFilters:
- server:*
- arg: "--service-cidr=10.43.0.0/16"
nodeFilters:
- server:*
この設定ファイルの重要なポイントを解説します:
-
ネットワーク設定
- KubeAPI: ポート6443でホストにバインド
- ロードバランサー: ポート8080:80をマッピング
- network: 先ほど作成した共有ネットワークを使用
-
レジストリ設定
- ローカルレジストリを作成(名前: registry.localhost)
- ポート15000でホストにバインド
-
CIDR設定
- Cluster CIDR: 10.42.0.0/16
- Service CIDR: 10.43.0.0/16
(これらはクラスター間でのネットワークの重複を避けるために重要)
クラスターを作成するには、以下のコマンドを実行します:
k3d cluster create --config source-cluster.yaml
ターゲットクラスターの構築
ターゲットクラスターは、シークレットを受け取る側のクラスターです。ソースクラスターとは異なる設定で構築します。
target-cluster.yaml
を作成し、以下の内容を記述します:
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: target-cluster
servers: 1
agents: 1
image: docker.io/rancher/k3s:v1.30.0-k3s1
kubeAPI:
host: 0.0.0.0
hostIP: 127.0.0.1
hostPort: "6444" # ソースクラスターと異なるポートを使用
ports:
- port: 8081:80 # ソースクラスターと異なるポートを使用
nodeFilters:
- loadbalancer
registries:
use:
- registry.localhost # ソースクラスターのレジストリを使用
network: shared-net
options:
k3d:
wait: true
kubeconfig:
updateDefaultKubeconfig: true
switchCurrentContext: true
k3s:
extraArgs:
# CIDR範囲の重複回避のため、ソースクラスターと異なるCIDRを指定
- arg: "--cluster-cidr=10.44.0.0/16"
nodeFilters:
- server:*
- arg: "--service-cidr=10.45.0.0/16"
nodeFilters:
- server:*
この設定ファイルのポイントを解説します:
-
ソースクラスターとの違い
- KubeAPIポート: 6444(ソースクラスターは6443)
- ロードバランサーポート: 8081:80(ソースクラスターは8080:80)
- CIDR範囲: 10.44.0.0/16と10.45.0.0/16(ソースクラスターとは異なる範囲)
-
レジストリ設定
- ソースクラスターで作成したレジストリを使用
-
use
セクションで既存のレジストリを指定
-
ネットワーク設定
- ソースクラスターと同じ共有ネットワークを使用
- これによりクラスター間の通信が可能に
クラスターを作成:
k3d cluster create --config target-cluster.yaml
クラスター間通信の検証
コンテキストの確認
まず、両方のクラスターが正しく作成されたことを確認します:
kubectl config get-contexts
出力例:
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
k3d-source-cluster k3d-source-cluster admin@k3d-source-cluster
* k3d-target-cluster k3d-target-cluster admin@k3d-target-cluster
コンテキストの切り替え方法:
# ソースクラスターに切り替え
kubectl config use-context k3d-source-cluster
# ターゲットクラスターに切り替え
kubectl config use-context k3d-target-cluster
クラスター間の疎通確認
クラスター間の通信を確認するため、デバッグ用のPodを作成します。
1. デバッグ用Pod定義の作成
debug-pod.yaml
を作成:
apiVersion: v1
kind: Pod
metadata:
name: debug-pod
spec:
containers:
- name: debug
image: curlimages/curl
command: ["sleep", "infinity"]
volumeMounts:
- name: auth
mountPath: /tmp/auth
readOnly: true
volumes:
- name: auth
secret:
secretName: debug-auth
このPodの特徴:
- 軽量なcurlイメージを使用
- 無限スリープで実行し続ける
- 認証情報をボリュームとしてマウント
2. 認証情報の準備
debug-auth-secret.yaml
を作成:
apiVersion: v1
kind: Secret
metadata:
name: debug-auth
type: Opaque
data:
client-certificate-data: ... # kubeconfigのtarget-clusterのBase64エンコードされたclient-certificate-data
client-key-data: ... # kubeconfigのtarget-clusterのBase64エンコードされたclient-key-data
認証情報の取得方法:
# kubeconfigの内容を表示
kubectl config view --raw
Base64エンコードされた認証情報をコピーして、debug-auth-secret.yamlに貼り付けます。
3. デバッグ環境のセットアップ
# ソースクラスターに切り替え
kubectl config use-context k3d-source-cluster
# 認証情報とデバッグPodを作成
kubectl apply -f debug-auth-secret.yaml
kubectl apply -f debug-pod.yaml
# Podの状態を確認
kubectl get pod debug-pod
4. 疎通確認の実行
kubectl exec debug-pod -- curl -k \
--cert /tmp/auth/client-certificate-data \
--key /tmp/auth/client-key-data \
https://k3d-target-cluster-server-0:6443/api/v1/namespaces
このコマンドで成功すると、JSONフォーマットでnamespaceのリストが返ってきます。これは以下を意味します:
- ターゲットクラスターのAPIサーバーに到達可能
- 認証が正常に機能している
- クラスター間の基本的な通信が確立されている
external-secretsのセットアップ
クラスターの準備が確認できたら、いよいよexternal-secretsのセットアップを行います。
ソースクラスターへのインストール
# ソースクラスターのコンテキストに切り替え
kubectl config use-context k3d-source-cluster
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets \
external-secrets/external-secrets \
-n external-secrets \
--create-namespace
ターゲットクラスターの認証情報設定
ソースクラスターからターゲットクラスターへのアクセスに必要な認証情報を設定します。
認証情報の取得
まず、ターゲットクラスターのクライアント証明書情報を取得します:
kubectl config view --raw
# 以下の情報をメモ
# - client-certificate-data
# - client-key-data
# - certificate-authority-data
認証情報の設定
target-cluster-credentials.yaml
を作成:
apiVersion: v1
kind: Secret
metadata:
name: target-cluster-credentials
namespace: default
type: Opaque
data:
client-certificate-data: ... # kubeconfigから取得したBase64エンコード済みデータ
client-key-data: ... # kubeconfigから取得したBase64エンコード済みデータ
認証情報をソースクラスターに適用:
# ソースクラスターのコンテキストに切り替え
kubectl config use-context k3d-source-cluster
# 認証情報の適用
kubectl apply -f target-cluster-credentials.yaml
# 作成されたSecretの確認
kubectl get secret target-cluster-credentials -o yaml
SecretStoreの設定
SecretStoreは、external-secretsがシークレットを保存・取得するためのバックエンドストアを定義します。
SecretStore定義の作成
secret-store.yaml
を作成:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: target-cluster
namespace: default
spec:
provider:
kubernetes:
remoteNamespace: default # ターゲットクラスターのnamespace
server:
url: https://k3d-target-cluster-server-0:6443 # k3d内部のホスト名を使用
# CA証明書はBase64エンコードされた値を直接指定
caBundle: ... # kubeconfigから取得したcertificate-authority-data
auth:
cert:
clientCert:
name: target-cluster-credentials
key: client-certificate-data
clientKey:
name: target-cluster-credentials
key: client-key-data
SecretStoreの適用と確認
# SecretStoreの適用
kubectl apply -f secret-store.yaml
# 状態の確認
kubectl describe secretstore target-cluster
# 期待される出力に含まれる内容
Status:
Conditions:
Last Transition Time: ...
Message: SecretStore validated
Reason: Valid
Status: True
Type: Ready
PushSecretの設定と動作確認
PushSecretの作成
PushSecretは、ソースクラスターのシークレットをターゲットクラスターに自動的にプッシュするための設定です。
サンプルシークレットの作成(ソースクラスター)
# サンプルシークレットの作成
kubectl create secret generic my-secret \
--from-literal=username=admin \
--from-literal=password=supersecret
# 作成されたシークレットの確認
kubectl get secret my-secret -o yaml
PushSecret定義の作成
push-secret.yaml
を作成:
apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
name: pushsecret-example
namespace: default
spec:
# 同期時にプロバイダーの既存のシークレットを上書き
updatePolicy: Replace
# PushSecret削除時にプロバイダーのシークレットも削除
deletionPolicy: Delete
# 再同期の間隔
refreshInterval: 10s
# シークレットをプッシュするSecretStore
secretStoreRefs:
- name: target-cluster
kind: SecretStore
# 同期対象のSecret
selector:
secret:
name: my-secret # ソースクラスターでのSecretのname
data:
- match:
secretKey: username # ソースクラスターでのSecretのkey
remoteRef:
remoteKey: my-secret-copy # ターゲットクラスターでのSecretのname
property: username-copy # ターゲットクラスターでのSecretのkey
- match:
secretKey: password # ソースクラスターでのSecretのkey
remoteRef:
remoteKey: my-secret-copy # ターゲットクラスターでのSecretのname
property: password-copy # ターゲットクラスターでのSecretのkey
PushSecretの適用
kubectl apply -f push-secret.yaml
# 状態の確認
kubectl describe pushsecret pushsecret-example
動作確認
ターゲットクラスターでのシークレット確認
# ターゲットクラスターに切り替え
kubectl config use-context k3d-target-cluster
# シークレットの確認
kubectl describe secret my-secret-copy
# 期待される出力
Name: my-secret-copy
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password-copy: 11 bytes
username-copy: 5 bytes
シークレットの内容確認
シークレットの値を確認するには、以下のようなコマンドを使用できます:
# kubectl-view-secretプラグインを使用する場合
kubectl-view-secret my-secret-copy --all
# または、直接Base64デコードする場合
kubectl get secret my-secret-copy -o jsonpath='{.data.username-copy}' | base64 -d
kubectl get secret my-secret-copy -o jsonpath='{.data.password-copy}' | base64 -d
期待される出力:
password-copy='supersecret'
username-copy='admin'
自動同期の確認
ソースクラスターでシークレットを更新し、ターゲットクラスターに反映されることを確認:
# ソースクラスターに切り替え
kubectl config use-context k3d-source-cluster
# シークレットの更新
kubectl create secret generic my-secret \
--from-literal=username=newadmin \
--from-literal=password=newsecret \
--dry-run=client -o yaml | kubectl apply -f -
# ターゲットクラスターに切り替えて確認
kubectl config use-context k3d-target-cluster
kubectl-view-secret my-secret-copy --all
クリーンアップ
PushSecretの削除
# ソースクラスターに切り替え
kubectl config use-context k3d-source-cluster
# PushSecretの削除
kubectl delete pushsecret pushsecret-example
# ターゲットクラスターでシークレットが削除されたことを確認
kubectl config use-context k3d-target-cluster
kubectl get secret my-secret-copy
# "Error from server (NotFound): secrets "my-secret-copy" not found" と表示されれば成功
クラスターの削除
# クラスターの削除
k3d cluster delete --config source-cluster.yaml
k3d cluster delete --config target-cluster.yaml
# 共有ネットワークの削除
docker network rm shared-net
まとめ
本記事では、external-secretsのPushSecret機能を使用して、Kubernetesクラスター間でシークレットを共有する方法を詳しく解説しました。実運用では、この機能を活用することで、マルチクラスター環境でのシークレット管理を効率化できると思います!