Kubernetes v1.16.0
がリリースされて1週間ほど経ちましたが、手元の環境でインストールしてみました。折角なので記事として残してみようと思います。
構成
パブリッククラウド上で簡単にKubernetesを利用できる昨今ではありますが、それだとあまり面白くないので、家のPC上に仮想マシンを建てて作りたいと思います。
クラスタ構成
今回は折角なのでMulti-Master構成で構築します。複数ノードにしても物理ホストが1台では意味ないよねとか言う天使の囁きは無視します。
Masterノード: 3台
Workerノード: 3台
今回のレベル感であればSingle-Master構成やMaster/Worker同居の完全1台構成に変更することも難しくはないので、補足的に随所に記載しておきます (v1.15.3
の時の実績からなので、v1.16.0
で手順が変わっていたらすみません)。
ソフトウェア構成
今回はCNIにflannelを利用します。Workerが複数あるのにサービスの外部公開がNodePortでは残念過ぎるので、MetalLBを利用してLoadBalancerを利用できるようにします。
本来であればMasterノードはHAProxyなどを利用してロードバランスされるべきですが、今回は(面倒だったので)keepaliveでAct-Stb-Stb状態で構築します。
また本当はDynamic Volume Provisioningを利用できるようにGlusterFSも導入しようと思ったのですが、時間の都合により割愛します。その内、別記事で記載するかもしれません。
Module | Version |
---|---|
CentOS | 7.6 |
Docker | 19.03.2 |
kubelet | 1.16.0 |
kubeadm | 1.16.0 |
kubectl | 1.16.0 |
keepalive | 1.3.5 |
flannel | v0.11.0 |
MetalLB | 0.8.1 |
サイジング
作って動けば良いレベルなので、ホスト側のリソース量と相談して決めました。物理ホスト側は5〜6年くらい前に作った自作PCなので、そんなに高いスペックは必要ありません。ちなみに公式の推奨スペックはこちらです。
- Master/Worker共通
- CPU: 2コア
- メモリ: 4GB
- ディスク: 64GB
ちなみにv1.15.3
ですがMaster/Worker同居でもこのスペックでそこそこ動きます。
ネットワーク構成
IPアドレス構成は以下の通りです。ホスト名の連番がバラバラなのは我が家の他のクラスタとの兼ね合いなので気にしないでください。k8s-master02
を最初に構築するMasterとして、k8s-master*
でMaserクラスタを構築します。k8s-worker*
がWorkerノード用のVMです。
flannelのIPはCNIによって要件が変わるので、他のCNIを利用する場合はドキュメントを参照して変更してください。
役割 | IPアドレス |
---|---|
k8s-master02 | 192.168.2.15 |
k8s-master03 | 192.168.2.16 |
k8s-master04 | 192.168.2.17 |
MasterノードのクラスタIP | 192.168.2.18 |
k8s-worker04 | 192.168.2.24 |
k8s-worker05 | 192.168.2.25 |
k8s-worker06 | 192.168.2.26 |
flannelのIP | 10.244.0.0/16 |
ClusterIP | 10.96.0.0/12 |
LoadBalancerIP | 192.168.2.64/27 |
インストール
CentOSのインストール (k8s-master02
)
まずは最初のMasterノードとなるk8s-master02
のCentOSをminimalインストールします。他のノードはk8s-master02
をクローンして作成するので、この時点では構築不要です。インストール時にネットワーク設定とユーザ作成を行っています。ユーザはrltnm7
というユーザにしました。
OSの設定 (k8s-master02
)
既にSSH出来るはずなので、SSHで外部から繋いで設定しています。(面倒だったので)しばらくはroot
ユーザで設定しています。ユーザ設定などは各自必要に応じて設定してください。
firewallを無効化します。真面目にやる時はちゃんと設定しましょう。
systemctl stop firewalld
systemctl disable firewalld
selinuxも無効化します。
setenforce 0
sed -i "s/SELINUX=enforcing/SELINUX=disabled/" /etc/selinux/config
swapを無効化します。これは必須です。やらないと設定時のpreflightで失敗します。
swapoff -a
sed -i "s/\/dev\/mapper\/centos-swap/#\/dev\/mapper\/centos-swap/" /etc/fstab
hostsの追加をします。ちゃんとDNSがある環境であれば不要な設定かと思います。
cat << EOF >> /etc/hosts
192.168.2.15 k8s-master02
192.168.2.16 k8s-master03
192.168.2.17 k8s-master04
192.168.2.24 k8s-worker04
192.168.2.25 k8s-worker05
192.168.2.26 k8s-worker06
EOF
yumのアップデートもしておきます。OSのイメージが古いので300以上updateが走りました。
yum update -y
yum upgrade -y
Dockerのインストール (k8s-master02
)
DockerのCommunity Editionを使います。yum install docker
するとEnterprise Edtionがインストールされるので気をつけてください。エディションの違いはこちらを参照してください。
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce
rltnm7
ユーザをdocker
グループに追加します。これをしないとrltnm7
ユーザがdocker
コマンドを実行する際に毎回sudo
をしなければなりません。Kubernetesを利用する上で必須の設定ではありませんが、トラブルシュートする際に設定されていると便利です。
usermod -g docker rltnm7
こちらの設定も必須ではありませんが、ローカル環境にPrivate Registryを構築し、かつセキュリティ設定が面倒でやらない場合に設定する必要がある項目です。このRegistryはHTTPSではなくHTTPでアクセスしてねっていう設定です。Docker Hubしか使わないのであれば不要です。
cat << EOF > /etc/sysconfig/docker
INSECURE_REGISTRY='--insecure-registry registry:5000'
EOF
最後にDockerを起動します。
systemctl start docker
systemctl enable docker
Kubernetesのインストール (k8s-master02
)
インストール前にネットワーク設定をしておきます。
cat << EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system
ついにKubernetesをインストールします。Kubernetes用のyumレポジトリを追加して、インストールします。あえてバージョン指定しましたが、最新版を入れるのであれば指定は不要です。
cat << EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
yum install -y kubelet-1.16.0-0.x86_64 kubeadm-1.16.0-0.x86_64 kubectl-1.16.0-0.x86_64
最後にkubeletの自動起動設定を入れて、シャットダウンします。すぐに落としてしまうので起動はしていませんが、systemctl start kubelet
して動作確認してみても良いかもしれませんね。Kubenetes自体はまだ機能していないので何が見れるか分かりませんが。。。
systemctl enable kubelet
shutdown -h now
VMのクローン (k8s-master02
以外全て)
ここまで構築したVMをクローンします。Master用に2つ、Worker用に3つクローンします。Master用はもう少しだけ共通の設定をしますが、本当に少しなのでこの時点でクローンしてしまいます。クローン手順は省略します。VirtualBoxであれば右クリックでクローン選ぶだけです。
もちろんSingle-Masterにする場合はMasterのクローンは不要ですし、1台構成にするのであればWorkerのクローンも不要です。
各VMの設定 (k8s-master02
以外全て)
クローンしたVMのOS設定をしていきます。最初はIP競合を起こしてしまうので、各VMを1台ずつ起動し、以下の設定をして落としていきます。ホスト名/IPアドレスは対象マシンごとに変更してください。
sed -i "s/k8s-master02/k8s-master03/" /etc/hostname
sed -i "s/192.168.2.15/192.168.2.16/" /etc/sysconfig/network-scripts/ifcfg-enp0s3
shutdown -h now
Keepaliveの設定 (全てのMasterノード)
Masterノードに仮想IPを設定します。kubectl
などが接続するIPアドレスになります。priorityは優先順位が高い順に変更してください。私はk8s-master02, 03, 04
の順に100, 90, 80
としました。
Single-Master/1台構成の場合は不要な設定です。
yum install -y keepalived
mv /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.org
cat <<EOF > /etc/keepalived/keepalived.conf
global_defs {
}
vrrp_instance KUBE_APISERVER {
state MASTER
interface enp0s3
virtual_router_id 51
priority 100
advert_int 1
virtual_ipaddress {
192.168.2.18/24
}
}
EOF
systemctl start keepalived
systemctl enable keepalived
最初のMasterの設定 (k8s-master02
)
ようやくMasterノードの構築に入ります。kubeadm init
コマンドを実行するだけの簡単なお仕事です。
--apiserver-advertise-address
および--control-plane-endpoint
に対してはkeepaliveで設定したクラスタIPを渡します。--pod-network-cidr
にはflannelのIPを渡します。こちらはCNIによって異なるので利用するCNIのドキュメントを参照してください。あるいは不要な場合もあるかもしれません。--service-cidr
はClusterIPのIPアドレス範囲になります。デフォルトで10.96.0.0/12
になるので、省略しても大丈夫なはずです(試したことないですが)。変えたい場合は設定してください。
またSingle-Masterや1台構成の場合は--control-plane-endpoint
の引数が不要になります。
kubeadm init --apiserver-advertise-address 192.168.2.18 --control-plane-endpoint 192.168.2.18 --pod-network-cidr 10.244.0.0/16 --service-cidr 10.96.0.0/12
最初にpreflightでシステム要件の確認をされます。何か足りなければここでエラーが出てインストールが失敗します。
kubeadm init
の結果にkubeadm join
コマンドが2つ(--control-plane-endpoint
を指定していない場合は1つ)記載されているのでメモしておいてください。
Master間の鍵交換
Kubernetesの鍵情報を送りつけるために、まずはSSHの鍵を作成し、公開鍵を送ります。これはk8s-master02
でのみ実行します。
ssh-keygen -t rsa -b 4096 -C "admin@example.com"
USER=rltnm7
scp .ssh/id_rsa.pub "${USER}"@192.168.2.16:
scp .ssh/id_rsa.pub "${USER}"@192.168.2.17:
受け取り手側で公開鍵を受け入れます。これはk8s-master03
とk8s-master04
の一般ユーザrltnm7
で実行します。全部root
でやり取りでも良かったのですが、何となくroot
でダイレクトにSSHするのは気が引けたので。。。
mkdir .ssh
chmod 700 .ssh
cd .ssh
cat ~/id_dsa.pub >> authorized_keys
chmod 600 authorized_keys
sudo systemctl restart sshd
k8s-master02
に戻ってSCPでファイルを送付します。一般ユーザだとファイルにアクセス権がないのでroot
で実行します。実行する前にSSHでパスワードなしでアクセスできるか試しておいた方が幸せになれると思います。SSHの鍵交換は最悪やらなくても、下記SCPの際に(9*台数)回パスワード入力すれば大丈夫です笑
USER=rltnm7
CONTROL_PLANE_IPS="192.168.2.16 192.168.2.17"
for host in ${CONTROL_PLANE_IPS}; do
scp /etc/kubernetes/pki/ca.crt "${USER}"@$host:
scp /etc/kubernetes/pki/ca.key "${USER}"@$host:
scp /etc/kubernetes/pki/sa.key "${USER}"@$host:
scp /etc/kubernetes/pki/sa.pub "${USER}"@$host:
scp /etc/kubernetes/pki/front-proxy-ca.crt "${USER}"@$host:
scp /etc/kubernetes/pki/front-proxy-ca.key "${USER}"@$host:
scp /etc/kubernetes/pki/etcd/ca.crt "${USER}"@$host:etcd-ca.crt
scp /etc/kubernetes/pki/etcd/ca.key "${USER}"@$host:etcd-ca.key
scp /etc/kubernetes/admin.conf "${USER}"@$host:
done
kubectl
の設定
kubectl
を利用できるように設定します。ここからはroot
ユーザではなく、一般ユーザで設定を行います。今回は既に作成済みのrltnm7
を利用します。
この時点ではk8s-master02
でしか出来ませんが、後で他のMasterでもやっておいた方が良いと思います。
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
さて、kubectl
コマンドが利用できるようになったので、試しにいくつか実行してみましょう。
kubectl get nodes
k8s-master02
がNotReady
状態になっていると思います。
kubectl get pod --namespace kube-system
CoreDNSがPending
状態で、その他のPodはRunning
になっていると思います。まだなっていなければ少し待ってからもう一度確認してみてください。CoreDNSはCNIを展開しない限りは永遠にPending
のままなので先に進んでください。
CNIの設定 (k8s-master02
)
クラスタ上へflannelを展開します。github上のmanifestファイルを直接指定しています。
注意点としてKubernetes v1.16.0
からDaemonSetのAPIがapps/v1beta1からapps/v1へ変更になっているので、1.15
以前に使えていたmanifestファイルは使えません。
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/2140ac876ef134e0ed5af15c65e414cf26827915/Documentation/kube-flannel.yml
参考までにv1.15.3
の場合は、以下のコミットハッシュのファイルで展開していました。(最初にこちらを実行して失敗したのはナイショ)
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/62e44c867a2846fefb68bd5f178daf4da3095ccb/Documentation/kube-flannel.yml
環境に依るとも思いますが、5〜10分くらい待ってからもう一度kubectl get pod --namespace kube-system
を実行すると全てのPodがRunning
になっていると思います。たまにRestart
してたりしますが、その内収束するので焦らずお待ち下さい。
待っている間は折角なのでv1.16.0
からの新機能kubectl get node -n kube-system -w --output-watch-events
なんか使って、Podの作成状況なんか眺めてみると面白いかもしれませんね。
最後にkubectl get nodes
を実行してk8s-master02
がReady
になっていればOKです。
さて、ここまで来ればk8s-master02
は完成です。
残りのMasterの設定 (k8s-master03
, k8s-master04
)
先程SCPした鍵を所定の場所に移動します。こちらはroot
で実行するか、sudo
でやってください。
Single-Master/1台構成の場合はこの説は飛ばしてしまって大丈夫です。
USER=rltnm7
mkdir -p /etc/kubernetes/pki/etcd
mv /home/${USER}/ca.crt /etc/kubernetes/pki/
mv /home/${USER}/ca.key /etc/kubernetes/pki/
mv /home/${USER}/sa.pub /etc/kubernetes/pki/
mv /home/${USER}/sa.key /etc/kubernetes/pki/
mv /home/${USER}/front-proxy-ca.crt /etc/kubernetes/pki/
mv /home/${USER}/front-proxy-ca.key /etc/kubernetes/pki/
mv /home/${USER}/etcd-ca.crt /etc/kubernetes/pki/etcd/ca.crt
mv /home/${USER}/etcd-ca.key /etc/kubernetes/pki/etcd/ca.key
mv /home/${USER}/admin.conf /etc/kubernetes/admin.conf
最後に、k8s-master02
にて構成したクラスタに参加します。kubeadm init
した際にメモしたkubeadm join
コマンドを実行します。Masterでは--control-plane
引数が付いている方を実行します。付いていない方を実行してしまうと、ただのWorkerになってしまうのでご注意ください。
sudo kubeadm join 192.168.2.18:6443 --token lpm579.cxre2a1xzucdp547 --discovery-token-ca-cert-hash sha256:6c2d80e1b4d26f12c48ba83b8a7b9d502a0e3877dc0cd29130b562925588e1be --control-plane
k8s-master03
、k8s-master04
でそれぞれ完了したら、kubectl get nodes
を実行し、全てのNodeがReady
になればMulti-Master構成は完成です。前述の通り、各Masterでkubectl
の設定をしておくと良いと思います。続いてWorkerの設定を行います。
Workerの設定 (全てのWorker)
1台構成の場合はこの節も飛ばして大丈夫です。代わりにMaster/Worker同居1台構成の場合を実施してください。
まずはIPv4の転送設定を行います。
sudo sh -c "echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.d/k8s.conf"
sudo sysctl --system
次にk8s-master02
にて構成したクラスタに参加します。kubeadm init
した際にメモしたkubeadm join
コマンドを実行します。Workerでは--control-plane
引数が付いていない方を実行します。
sudo kubeadm join 192.168.2.18:6443 --token lpm579.cxre2a1xzucdp547 --discovery-token-ca-cert-hash sha256:6c2d80e1b4d26f12c48ba83b8a7b9d502a0e3877dc0cd29130b562925588e1be
kubectl get nodes
を実行し、対象のノードがReady
になったらOKです。全てのWorkerノードの追加が完了したらKubernetesクラスタの完成です。
Master/Worker同居1台構成の場合
Master上では通常、ユーザが作成したコンテナは実行できないので、Master/Workerを同居させる場合には追加の設定が必要になります。ちなみに同居させることは、あまり推奨できる構成ではないので開発や検証程度にしておいた方が良いでしょう。
MasterとWorkerを分けた構成にする場合は不要な設定です。
まずはIPv4の転送設定を行います。
sudo sh -c "echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.d/k8s.conf"
sudo sysctl --system
次にMasterノードでもコンテナを実行できるようにします。
kubectl taint node k8s-master02 node-role.kubernetes.io/master:NoSchedule-
これでMaster/Worker同居のクラスタが完成しました。
MetalLBの導入
さて、ここまでで無事にKubernetesクラスタは完成し、Kubernetesとして最低限利用できるようにはなりました。しかしまだLoadBalancer
サービスは利用できません。そこでクラスタ上にMetalLBをデプロイしてLoadBalancer
サービスを利用できるようにします。
まずはデプロイします。
kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.8.1/manifests/metallb.yaml
MetalLBにはL2モードとBGPモードががありますが、今回はL2モードで実行させます。違いはドキュメントを参照してください。addresses
に設定されているIPアドレスがLoadBalancer
のExternalIP
として設定されるIPアドレス範囲になります。
cat << EOF > metallb-l2.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: config
namespace: metallb-system
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.2.64/27
EOF
kubectl apply -f metallb-l2.yaml
その他
その他、IngressやIstio、Dynamic Provisioningなどで遊びたいところではありますが、今回の構築範囲としてはここまでとし、またいつか別記事にしたいと思います。
動作確認
だいぶ長くなっていますが、作って終わりも面白くないので、少し遊んでみたいと思います。
コンテナのデプロイ
とても雑にtype: LoadBalancer
のサービスとreplica: 3
でnginx
のDeployment
を展開したいと思います。ただのnginx
だと面白くないのでimage: stenote/nginx-hostname:latest
として、ホスト名が出るようにします。
cat << EOF > nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: stenote/nginx-hostname:latest
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: LoadBalancer
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
EOF
kubectl apply -f nginx.yaml
kubectl get pod -o wide -w --output-watch-events
全てRunning
になったのを確認したら、以下を実行します。IPはkubectl get svc
を実行して、nginx
のExternal-IP
を指定してください。
for i in `seq 0 100`; do curl http://192.168.2.64; done
3台に分散されてホスト名 (= Pod名) が表示されると思います。
最後に掃除しておきます。
kubectl delete -f nginx.yaml
rm -f nginx.yaml
Multi-Masterの動作を見る
特に記載できる内容もないのですが、、、
通常時はk8s-master02をAct機として動作していますが、k8s-master02をシャットダウンしても、k8s-master03をAct機として動作するのが分かると思います。
ただし今回はkeepaliveでのIP付け替えという雑なやり方しかしていないので、特に復旧時の切り替えには比較的長めの接続断が発生してしまいます。きれいな切り替えがやりたい場合はHAProxyを利用するなど、ロードバランス機能を持たせるべきでしょう。
感想
やはり自分で構築するとKubernetesへの理解が深まりますね。パブリッククラウドでは手軽に利用できる反面、上っ面だけでも使えてしまうので、こう言った自分の手を動かす機会も積極的に持ちたいですね。
Ephemeral Containers
とかも遊びたかったのですが、気力が尽きたので終わります。メモリ24GBとか普通に邪魔なので、このクラスタは消して1台構成で作り直して遊んでみたいと思います。
Kubernetesインストール系の記事も最近はだいぶ多くなっているので、今更需要があるのかも分かりませんが、どなたかの一助となれば幸いです。今後も色々Kubernetesだけでなく、色々な記事を書いてみようと思います。