今回のポストでは、家でWi-Fi設定とデスクトップを利用してKubernetes Clusterを構築する予定です。毎回トイプロジェクトを行う際にアプリケーションサーバーをどうするか悩んでいましたが、また、追加でRedis、ElasticSearch、MySQL、Jenkinsなどの複数のリソースを便利に使用するためにKubernetesを構築して管理およびデプロイする予定です。
私は次のように設定しました。
- 家に余っているデスクトップをCitrix Xen Hypervisorで仮想化
- VMを5つ作成した後にKubernetes Clusterを構築(Control Plane 1、Worker 3)
ネットワーク設定
現在家ではLGU+ルーターを使用しており、DHCP範囲が自動で192.168.219.0/24に設定されています。ルーター設定ページでも確認しましたが、これを変更できないようです。とりあえずRFCのプライベートIPレンジを見て、大まかに次のように分けました。
参考. RFC 1918 Private IPv4 address
- 24-bit block / 10.0.0.0–10.255.255.255 / 16777216
- 20-bit block / 172.16.0.0–172.31.255.255 / 1048576
- 16-bit block / 192.168.0.0–192.168.255.255 / 65536
設定したIP
- 既存の家で使用する必要があるIP(無線接続など)192.168.219.1(gateway) ~ 192.168.219.99
- Xen Server 192.168.219.100
- VM IP 192.168.219.150–192.168.219.199
- DNS & Util Server 192.168.219.200
- NFS 192.168.219.210
- Kubernetes IP Range 10.0.0.0/16
SSH
SSH接続はSSHジャンプで簡単に行えます。一般的なSSHクライアントはこれをサポートしているため、DMZノードを通じて非常に簡単に内側のVMまでSSH接続を行うことができました。Mac用にはTerminusで設定しておきました。
CentOS/RHEL Kubernetes Cluster構築
XenServerのVMで作成したCentOSにKubernetesクラスタを構築しました。Xen Centerのコンソール画面でも可能ですが不便なため、SSHクライアントでSSH接続を構成して使用する方が便利です。
おおよそ次のようなスペックで構築しました。
- Kubeadm
- CNI : Calico
- Ingress Controller : Nginx
ノードのスペックは次のように設定しました。vCPUの計算方法を検索すると、デスクトップのコアを通じてvCPUを計算できます。
- マスターノード : 2 vCPU, 6G RAM
- ワーカーノード : 4 vCPU, 6G RAM
kubeadmでノードをジョインする
通常、マスターノードでkubeadm initを実行すると、最後にノードをジョインするコマンドが表示されますが、これを忘れてしまった場合は、以下のように再度実行できます。
# TOKENリストの確認
$ kubeadm token list
# ない場合はTOKENを生成
$ kubeadm token create
# HASHの確認
$ openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
# kubeadmノードジョイン
$ kubeadm join {APISERVER}:{PORT} --token {TOKEN} --discovery-token-ca-cert-hash sha256:{HASH}
最終的に次のような状態になります。
$ kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-master Ready master 25h v1.18.8
k8s-node-01 Ready <none> 25h v1.18.8
k8s-node-02 NotReady <none> 20s v1.18.8
k8s-node-03 NotReady <none> 7s v1.18.8
$ kubectl get all -n kube-system
NAME READY STATUS RESTARTS AGE
pod/calico-kube-controllers-75d555c48-gzcsk 1/1 Running 1 15h
pod/calico-node-9c9fk 1/1 Running 0 14h
pod/calico-node-mcnds 1/1 Running 1 15h
pod/coredns-66bff467f8-c76mz 1/1 Running 1 15h
pod/coredns-66bff467f8-nrdgl 1/1 Running 1 15h
pod/etcd-k8s-master 1/1 Running 1 15h
pod/kube-apiserver-k8s-master 1/1 Running 1 15h
pod/kube-controller-manager-k8s-master 1/1 Running 1 15h
pod/kube-proxy-f9tz8 1/1 Running 0 14h
pod/kube-proxy-ls2v2 1/1 Running 1 15h
pod/kube-scheduler-k8s-master 1/1 Running 1 15h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 15h
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/calico-node 2 2 2 2 2 beta.kubernetes.io/os=linux 15h
daemonset.apps/kube-proxy 2 2 2 2 2 kubernetes.io/os=linux 15h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/calico-kube-controllers 1/1 1 1 15h
deployment.apps/coredns 2/2 2 2 15h
NAME DESIRED CURRENT READY AGE
replicaset.apps/calico-kube-controllers-75d555c48 1 1 1 15h
replicaset.apps/coredns-66bff467f8 2 2 2 15h
$ kubectl get all -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-admission-create-qx642 0/1 Completed 0 3h26m
pod/ingress-nginx-admission-patch-b8h8m 0/1 Completed 0 3h26m
pod/ingress-nginx-controller-9d6f6c9d7-dshnk 1/1 Running 0 3h26m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller NodePort 10.103.85.174 <none> 80:32360/TCP,443:31208/TCP 3h26m
service/ingress-nginx-controller-admission ClusterIP 10.97.157.206 <none> 443/TCP 3h26m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 3h26m
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-9d6f6c9d7 1 1 1 3h26m
NAME COMPLETIONS DURATION AGE
job.batch/ingress-nginx-admission-create 1/1 12s 3h26m
job.batch/ingress-nginx-admission-patch 1/1 15s 3h26m
Jenkins構築
まず、Jenkinsのためにネームスペースを作成し、Deployment、Service、PV、PVCを設定しました。
$ k -n jenkins get all
NAME READY STATUS RESTARTS AGE
pod/jenkins-deployment-5754bd847d-v48z8 1/1 Running 0 113m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/jenkins-service ClusterIP 10.99.68.224 <none> 8080/TCP,50000/TCP 116m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/jenkins-deployment 1/1 1 1 113m
NAME DESIRED CURRENT READY AGE
replicaset.apps/jenkins-deployment-5754bd847d 1 1 1 113m
$ k -n jenkins get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
jenkins-volume 5Gi RWX Retain Bound jenkins/pvc-jenkins manual 117m
$ k -n jenkins get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-jenkins Bound jenkins-volume 5Gi RWX manual 117m
# 또한 jenkins를 Proxy 하기 위해서 Ingress를 만들고 서비스에 붙여 놓았습니다.
$ k -n jenkins get ing
NAME CLASS HOSTS ADDRESS PORTS AGE
jenkins-ingress <none> jenkins.****.** 192.168.219.151 80 117m`
その後、Route53でIngress ControllerのノードへのIPにAレコードを設定して接続しました。
Jenkins接続成功
イングレスにTLS設定(letsencrypt使用)
defaultにcertbot-autoを利用してTLSシークレットを作成し、各ネームスペースにコピー、更新するスクリプトを作成してcrontabに登録しました。
# シークレットスペック
SECRET=tls-secret
KEY_PATH=/etc/letsencrypt/archive/****.**/privkey1.pem
CERT_PATH=/etc/letsencrypt/archive/****.**/cert1.pem
# 更新する必要があるネームスペース
ARGOCD=argocd
JENKINS=jenkins
# 証明書再発行
certbot-auto renew
# 既存のシークレット削除
kubectl delete secret $SECRET
kubectl -n $ARGOCD delete secret $SECRET
kubectl -n $JENKINS delete secret $SECRET
# defaultにシークレット作成
kubectl create secret tls $SECRET --key $KEY_PATH --cert $CERT_PATH
# Argocdにシークレットコピー
kubectl get secret $SECRET --namespace=default -oyaml | grep -v namespace | kubectl apply --namespace=$ARGOCD -f -
# Jenkinsにシークレットコピー
kubectl get secret $SECRET --namespace=default -oyaml | grep -v namespace | kubectl apply --namespace=$JENKINS -f -
DNSサーバー構築と接続
dnsmasqを利用してDNSサーバーを構築しました。これにより、クラスタ内ではドメインを通じて内部通信が可能となりました。また、別途必要な場合に活用することもできると思います。
- 1次:DNSサーバーの /etc/hosts
- 2次:8.8.8.8(Google DNS)
### DNS
$ yum install -y dnsmasq
$ vi /etc/hosts
192.168.219.150 k8s-master
192.168.219.151 k8s-node-01 jenkins.****.** argocd.****.**
192.168.219.152 k8s-node-02
192.168.219.153 k8s-node-03
$ vi /etc/dnsmasq.conf
...
# 2次ネームサーバーとして使用するGoogle DNSを設定
resolv-file=/etc/resolv.dnsmasq
各ノードではネームサーバーを変更するだけで動作します。
$ vi /etc/resolve.conf
nameserver 192.168.219.200
NFSサーバー構築と接続
ストレージ容量が大きいCentOS7のVMを1台作成し、nfs-serverを構築して/storageパスにすべてマウントしました。権限設定はno_all_squash、no_root_squashにしました。これにより、KubernetesのPersistenceVolumeをhostPathまたはnfsで使用できるようになりました。
結論
家庭用ルーターと余っているデスクトップ1台を仮想化してKubernetesクラスターを作成しました。シングルノードクラスターとは異なり、マルチノードクラスターで経験できることが確実にあるため、Kubernetesを学びたい方には考慮すべきオプションだと思います。また、EKS、GKEのようにマスターノードがベンダーネイティブな場合には試せないプラクティスも試すことができます。