Multi Kubernetes Cluster間でレプリケーションするOperator作った
はじめに
普段からOperatorの実装や、コードリーディングが好きで四六時中考えたりしています。
そこで、最近気になっていたMulti Kubernetes Cluster間でレプリケーションするOperatorを実装しようと思い立ち、今回実装に踏み切りました。
なお、現在一応レプリケーションできるようになっていますが、まだ完成ではなく最低限の実装が完了した状態です。最後にロードマップも記載しますので、お付き合いください。
コードの場所
動作例
上記githubリポジトリのREADMEに動画を貼っています。そちらをご確認ください。
機能紹介
- 以下の作業を自動化します。
- PrimaryクラスタとSecondaryクラスタの検出
- clusterdetectorリソースがOperatorにより自動作成され、クラスタの検出を行います。
- 以下のkuberndtes Resourceを作成する。
- ConfigMap
- Deployment
- Service
- Ingress
- IngressでSSLを有効にすると、以下のリソースが自動的に作成されます。
- Secret1: IngressのSSL終端に必要なCA証明書、サーバー証明書、サーバー証明書の秘密鍵が含まれる
- Secret2: Ingressへのアクセスに必要なクライアント証明書と秘密鍵が含まれる
- IngressでSSLを有効にすると、以下のリソースが自動的に作成されます。
- レプリケーション用namespace自動作成/削除
レプリケーション
PrimaryクラスタとSecondaryクラスタの検出
Operatorデプロイと同時に以下のようにclusterdetectorリソースが自動作成されます。
この時、いずれかのSecondaryクラスタと通信ができない場合、Status列がUnknownとなり、REASON列に理由が出力されます。clusterdetectorリソースを用い、レプリケーションを行います。
kubectl -n resource-replicator-system get clusterdetectors.replicate.jnytnai0613.github.io -owide --show-labels
NAME CONTEXT CLUSTER USER CLUSTERSTATUS REASON AGE LABELS
v1252-cluster.kubernetes-admin3 secondary2 v1252-cluster kubernetes-admin3 Running 37h app.kubernetes.io/role=secondary
v1262-cluster.kubernetes-admin2 secondary1 v1262-cluster kubernetes-admin2 Running 37h app.kubernetes.io/role=secondary
v1270-cluster.kubernetes-admin1 primary v1270-cluster kubernetes-admin1 Running 37h app.kubernetes.io/role=primary
replicatorリソースの作成
以下のyamlファイルをapplyし、replicatorリソースを作成することで、レプリケーションを行うことが可能です。また、レプリケーション対象のnamespaceはreplicationNamespaceフィールドに、レプリケーション先のSecondaryクラスタはtargetClusterフィールドに記載します。
なお、今回はレプリケーションにServer-Side Applyを採用しています。Server-Side Applyを行うに当たって、以下のreplicatorリソースの定義にApplyconfigurationを埋め込み、通常の、Deploymentなどの定義を行う際と同様の書き方で定義できるようにしています。
また、replicatorリソースは様々なnamespaceを管理する関係上Clusterワイドなリソースとしています。
apiVersion: replicate.jnytnai0613.github.io/v1
kind: Replicator
metadata:
labels:
app.kubernetes.io/name: replicator
app.kubernetes.io/instance: replicator-sample
app.kubernetes.io/part-of: resource-replicator
app.kubernetes.io/managed-by: kustomize
app.kubernetes.io/created-by: resource-replicator
name: replicator-sample
spec:
targetCluster:
- v1252-cluster.kubernetes-admin3
- v1262-cluster.kubernetes-admin2
replicationNamespace: "test-ns"
deploymentName: nginx
deploymentSpec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 30%
maxUnavailable: 30%
template:
spec:
initContainers:
- name: init
image: alpine
command:
- sh
- -c
- |
cat << EOT > /tmp/run-nginx.sh
apt-get update
apt-get install inotify-tools -y
nginx
EOT
chmod 500 /tmp/run-nginx.sh
cat << EOT > /tmp/auto-reload-nginx.sh
oldcksum=\`cksum /etc/nginx/conf.d/default.conf\`
inotifywait -e modify,move,create,delete -mr --timefmt '%Y/%m/%d %H:%M:%S' --format '%T' /etc/nginx/conf.d/ | \
while read date time; do
newcksum=\`cksum /etc/nginx/conf.d/default.conf\`
if [ ${newcksum} != ${oldcksum} ]; then
echo "At \${time} on \${date}, config file update detected."
oldcksum=\${newcksum}
service nginx restart
fi
done
EOT
chmod 500 /tmp/auto-reload-nginx.sh
volumeMounts:
- name: nginx-reload
mountPath: "/tmp/"
containers:
- name: nginx
image: nginx:latest
command:
- bash
- -c
- "/tmp/run-nginx.sh && /tmp/auto-reload-nginx.sh"
volumeMounts:
- name: conf
mountPath: "/etc/nginx/conf.d/"
- name: index
mountPath: "/usr/share/nginx/html/"
- name: nginx-reload
mountPath: "/tmp/"
volumes:
- name: conf
configMap:
name: "nginx"
items:
- key: "default.conf"
path: "default.conf"
- name: index
configMap:
name: "nginx"
items:
- key: "mod-index.html"
path: "mod-index.html"
- name: nginx-reload
emptyDir: {}
configMapName: nginx
configMapData:
default.conf: |
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/share/nginx/html;
index index.html index.htm mod-index.html;
server_name localhost;
}
mod-index.html: |
<!DOCTYPE html>
<html>
<head>
<title>Yeahhhhhhh!! Welcome to nginx!!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Yeahhhhhhh!! Welcome to nginx!!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
serviceName: nginx
##############################################################################
## Selector is automatically assigned by the controller and is not required.
##############################################################################
serviceSpec:
type: ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 80
ingressName: nginx
ingressSpec:
rules:
- host: nginx.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80
ingressSecureEnabled: true
上記yamlをデプロイすると、以下のようにPrimaryクラスタとSecondaryクラスタにリソースが作成され、レプリケーション完了とします。また、Primaryクラスタにリソース作成完了してから、Secondaryクラスタにレプリケーションされます。
$ kubectl -n test-ns get all,cm,secret,ing
NAME READY STATUS RESTARTS AGE
pod/nginx-b76b5ccdf-57fzc 1/1 Running 0 11h
pod/nginx-b76b5ccdf-g57d6 1/1 Running 0 11h
pod/nginx-b76b5ccdf-jj5j5 1/1 Running 0 11h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx ClusterIP 10.108.190.77 <none> 80/TCP 11h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 3/3 3 3 11h
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-b76b5ccdf 3 3 3 11h
NAME DATA AGE
configmap/kube-root-ca.crt 1 11h
configmap/nginx 2 11h
NAME TYPE DATA AGE
secret/ca-secret Opaque 3 11h
secret/cli-secret Opaque 2 11h
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/nginx nginx nginx.example.com 192.168.9.1 80, 443 11h
今後のロードマップ
- CLIでコンテキストの読み取り/削除、レプリケーション先リソースの追加を行う。
- 「replicatorリソースの作成」で説明した以外のリソースのレプリケーションを許可する。その場合、ユーザーはCLIで任意のリソースを追加することができる。
- 常時レプリケーションによるアウトプレイスアップグレードツールとして昇華させる。
- ベストエフォート: OpenTelemetryによる分散トレーシングをサポートする。
さいごに
前述した通り、まだまだ実装は終わりではありません。
最終的には、アウトプレイスアップグレードツールとしての昇華が目標ですので、ロードマップを消化しつつ、
楽しんで実装していきたいと思います。
Discussion