始めに
Kubernetesへの理解を深めるために役に立つ物としてKubernetes The Hard Wayというチュートリアルがあります1。自動化ツール無しでkubernetesクラスターを構築することでKubernetesの構造を学べます。この記事はkubeadmを使って と上記のチュートリアルと同じクラスターを立ち上げて見ることでThe Hard Wayの理解を深める事を目的とします。
基本的にはThe Hard Wayの流れに沿って記述します。なるべく元の設定に寄せますが不必要だと思う所は修正を加えた後に注意書きでお知らせします。もとのチュートリアルと同じくGoogle Cloud Platform上で3つのmasterと3つのworkerを持つクラスターを構築します。作業環境はGoogle Cloud
のCloud Shell
を使います。
自分もただKubernetesについて勉強している途中ですので間違ってる所や無駄な部分が多々あると思います。お気づきの点などございましたら、ご指摘いただけると幸いです。
Prerequisites
gcloud
のcommand-line toolを使う基本的な設定を行います。違いはありません。
(
gcloud config set compute/region us-west1
gcloud config set compute/zone us-west1-c
)
Installing the Client Tools
要りません。
Provisioning Compute Resources
ネットワークのセッティングとVMインスタンスを立ち上げます。
Networking
ファイアウォール規則がちょっと違います。
(
gcloud compute networks create kubernetes-the-easy-way --subnet-mode custom
gcloud compute networks subnets create kubernetes \
--network kubernetes-the-easy-way \
--range 10.240.0.0/24
gcloud compute firewall-rules create kubernetes-the-easy-way-allow-internal \
--allow tcp,udp,icmp,ipip \
--network kubernetes-the-easy-way \
--source-ranges 10.240.0.0/24,10.200.0.0/16
gcloud compute firewall-rules create kubernetes-the-easy-way-allow-external \
--allow tcp:22,tcp:6443,icmp\
--network kubernetes-the-easy-way \
--source-ranges 0.0.0.0/0
gcloud compute addresses create kubernetes-the-easy-way \
--region $(gcloud config get-value compute/region)
)
内部用のファイアウォール規則(kubernetes-the-easy-way-allow-internal)の許可項目にIPIPプロトコールを追加します。
この記事ではチュートリアルと違ってCNIとしてCalicoを使います。よって内部のファイアウォールでIPIPプロトコールを許可する必要があります2。
gcloud compute firewall-rules list --filter="network:kubernetes-the-easy-way"
を通して以下のような設定を確認できます。
$ gcloud compute firewall-rules list --filter="network:kubernetes-the-easy-way"
NAME NETWORK DIRECTION PRIORITY ALLOW DENY DISABLED
kubernetes-the-easy-way-allow-external kubernetes-the-easy-way INGRESS 1000 tcp:22,tcp:6443,icmp False
kubernetes-the-easy-way-allow-internal kubernetes-the-easy-way INGRESS 1000 tcp,udp,icmp False
また、gcloud compute addresses list --filter="name=('kubernetes-the-easy-way')"
を通してパブリックIPアドレスを確認できます。
$ gcloud compute addresses list --filter="name=('kubernetes-the-easy-way')"
NAME ADDRESS/RANGE TYPE PURPOSE NETWORK REGION SUBNET STATUS
kubernetes-the-easy-way XXX.XXX.XXX.XXX EXTERNAL us-west1 RESERVED
Compute Instances
インスタンスを立ち上げます。違いはありません。
Kubernetes Controllers
for i in 0 1 2; do
gcloud compute instances create controller-${i} \
--async \
--boot-disk-size 200GB \
--can-ip-forward \
--image-family ubuntu-2004-lts \
--image-project ubuntu-os-cloud \
--machine-type e2-standard-2 \
--private-network-ip 10.240.0.1${i} \
--scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
--subnet kubernetes \
--tags kubernetes-the-easy-way,controller
done
Kubernetes Workers
for i in 0 1 2; do
gcloud compute instances create worker-${i} \
--async \
--boot-disk-size 200GB \
--can-ip-forward \
--image-family ubuntu-2004-lts \
--image-project ubuntu-os-cloud \
--machine-type e2-standard-2 \
--metadata pod-cidr=10.200.${i}.0/24 \
--private-network-ip 10.240.0.2${i} \
--scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
--subnet kubernetes \
--tags kubernetes-the-easy-way,worker
done
gcloud compute instances list --filter="tags.items=kubernetes-the-easy-way"
を打つと
$ gcloud compute instances list --filter="tags.items=kubernetes-the-easy-way"
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
controller-0 us-west1-c e2-standard-2 10.240.0.10 XXX.XXX.XXX.XXX RUNNING
controller-1 us-west1-c e2-standard-2 10.240.0.11 XXX.XXX.XXX.XXX RUNNING
controller-2 us-west1-c e2-standard-2 10.240.0.12 XXX.XXX.XXX.XXX RUNNING
worker-0 us-west1-c e2-standard-2 10.240.0.20 XXX.XXX.XXX.XXX RUNNING
worker-1 us-west1-c e2-standard-2 10.240.0.21 XXX.XXX.XXX.XXX RUNNING
worker-2 us-west1-c e2-standard-2 10.240.0.22 XXX.XXX.XXX.XXX RUNNING
六つのインスタンスが立ち上がっている事が確認できます。
Provisioning the CA and Generating TLS Certificates
要りません。
Generating Kubernetes Configuration Files for Authentication
要りません。
Generating the Data Encryption Config and Key
要りません。
#※kubernetesのインストール※
元のチュートリアルではないセクションですが、kubeadm
を使う場合、マスターノードとワーカーノードで同じ環境を使います。よって各VMに接続して3以下の過程を六つのインスタンスで行います。
ルート権限を仮定しています。
(
# Swapをオフにする。
swapoff -a
# sed to comment the swap partition in /etc/fstab
sed -i.bak -r 's/(.+ swap .+)/#\1/' /etc/fstab
# iptablesがブリッジを通過するトラフィックを処理できるようにする
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
# iptablesをレガシーバージョンに切り替える。
apt-get update && apt-get install -y iptables arptables ebtables
update-alternatives --set iptables /usr/sbin/iptables-legacy
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
update-alternatives --set arptables /usr/sbin/arptables-legacy
update-alternatives --set ebtables /usr/sbin/ebtables-legacy
# Dockerをインストールする
apt-get install -y \
apt-transport-https ca-certificates curl software-properties-common gnupg2
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
apt-get update && apt-get install -y \
containerd.io=1.2.13-2 \
docker-ce=5:19.03.11~3-0~ubuntu-$(lsb_release -cs) \
docker-ce-cli=5:19.03.11~3-0~ubuntu-$(lsb_release -cs)
apt-mark hold containerd.io docker-ce docker-ce-cli
# Dockerデーモンをセットアップ
cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
mkdir -p /etc/systemd/system/docker.service.d
systemctl daemon-reload
systemctl restart docker
systemctl enable docker
# kubernetesをインストールする
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update && apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl
systemctl daemon-reload
systemctl restart kubelet
systemctl enable kubelet
)
kubeadm version -o short
とkubelet --version
でインストールが正しく行われたか確認でみます。
$ kubeadm version -o short
v1.22.1
$ kubelet --version
Kubernetes v1.22.1
Bootstrapping the etcd Cluster & Bootstrapping the Kubernetes Control Plane
controller-0
,controller-1
,controller-2
を用いて積層コントロールプレーンを構成します。
kubeadmの積層HAクラスターはデフォルトで積層etcdトポロジーのクラスターを生成します。別途にetcdクラスターを構築する必要はありません。
Provision a Network Load Balancer
ターゲットプールではなくバックエンドサービスを使用したネットワークロードバランサを使用します。
Enable HTTPS Health Checks
kubernetesは基本的にヘルスチェック用のHTTPSエンドポイントを持っています4。即ち、新しいロードバランサを使えばHTTPSヘルスチェックでNGINXなしに直接繋げます5。
(
KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-easy-way \
--region $(gcloud config get-value compute/region) \
--format 'value(address)')
# リージョンヘルスチェックを作成
gcloud compute health-checks create https kubernetes \
--region=$(gcloud config get-value compute/region) \
--description="Kubernetes Health Check" \
--host "kubernetes.default.svc.cluster.local" \
--request-path "/readyz" \
--port=6443
# ネットワーク負荷分散のソースIP
gcloud compute firewall-rules create kubernetes-the-easy-way-allow-health-check \
--network kubernetes-the-easy-way \
--source-ranges=35.191.0.0/16,209.85.152.0/22,209.85.204.0/22 \
--allow tcp
)
Provision a Network Load Balancer
非マネージドインスタンスグループとバックエンドサービスを作りネットワークロードバランサに繋げます。まだインスタンスグループにcontrollerインスタンスは追加しないでください。
(
KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-easy-way \
--region $(gcloud config get-value compute/region) \
--format 'value(address)')
# 非マネージドインスタンスグループの作成
gcloud compute instance-groups unmanaged create instance-group-easy-way \
--zone $(gcloud config get-value compute/zone)
# リージョンバックエンドサービスを作成
gcloud compute backend-services create kubernetes-network-lb-backend-service \
--region $(gcloud config get-value compute/region) \
--health-checks kubernetes \
--health-checks-region $(gcloud config get-value compute/region) \
--protocol TCP
# インスタンスグループをバックエンドとして追加
gcloud compute backend-services add-backend kubernetes-network-lb-backend-service \
--instance-group instance-group-easy-way \
--instance-group-zone $(gcloud config get-value compute/zone) \
--region $(gcloud config get-value compute/region)
# 転送ルールを作成
gcloud compute forwarding-rules create kubernetes-forwarding-rule \
--address ${KUBERNETES_PUBLIC_ADDRESS} \
--ports 6443 \
--region $(gcloud config get-value compute/region) \
--backend-service kubernetes-network-lb-backend-service
)
Provision the Kubernetes Control Plane
kubeadmを使ってコントロールプレーンを構成していきます。最初のcontrollerと他のcontrollerではンスタンスグループにインスタンスを追加する順番が逆なことにご注意ください。もし、順番を間違えばcontroller同士の通信ができなくなりtimeoutが発生します。
# インスタンスグループにcontroller-0を追加
gcloud compute instance-groups unmanaged add-instances instance-group-easy-way \
--instances controller-0 \
--zone $(gcloud config get-value compute/zone)
# controller-0で実行
(
REGION=$(curl -s -H "Metadata-Flavor: Google" \
http://metadata.google.internal/computeMetadata/v1/project/attributes/google-compute-default-region)
KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute addresses describe kubernetes-the-easy-way \
--region $REGION \
--format 'value(address)')
kubeadm init --pod-network-cidr=10.200.0.0/16 --service-cidr=10.32.0.0/24 --control-plane-endpoint "${KUBERNETES_PUBLIC_ADDRESS}:6443" --upload-certs
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
# CNIとしてCalicoを指定
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
)
出力されるkubeadmのテキストを記録しておきます。上の方に書かれているjoin
はcontrollerで下のjoin
はworkerでクラスターに参加させる際に使います。
You can now join any number of the control-plane node running the following command on each as root:
kubeadm join XXX.XXX.XXX.XXX:6443 --token XXXXXX.xxxxxxxxxxxxxxxx \
--discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
--control-plane --certificate-key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join XXX.XXX.XXX.XXX:6443 --token XXXXXX.xxxxxxxxxxxxxxxx \
--discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
このトークンはデフォルトで2時間で消滅します。joinコマンドは"kubeadm token create --print-join-command"で、コントロールプレーンの為のcertificate-keyは"kubeadm init phase upload-certs --upload-certs"で再発行できます。
上記のコマンドを使ってcontroller-1とcontroller-2をjoinさせていきます。
必ず各インスタンスでkubernetesを立ち上げた後にVMインスタンスをインスタンスグループに加えてください。
もし先にインスタンスグループに加えてしまえば、ロードバランサが常にトラフィックをリクエストを送る側のVMに返してしまうため、クラスターに参加できなくなります6。
# controller-1で実行
(
kubeadm join XXX.XXX.XXX.XXX:6443 --token XXXXXX.xxxxxxxxxxxxxxxx \
--discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
--control-plane --certificate-key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
)
# インスタンスグループにcontroller-1を追加
gcloud compute instance-groups unmanaged add-instances instance-group-easy-way \
--instances controller-1 \
--zone $(gcloud config get-value compute/zone)
# controller-2で実行
(
kubeadm join XXX.XXX.XXX.XXX:6443 --token XXXXXX.xxxxxxxxxxxxxxxx \
--discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
--control-plane --certificate-key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
)
# インスタンスグループにcontroller-2を追加
gcloud compute instance-groups unmanaged add-instances instance-group-easy-way \
--instances controller-2 \
--zone $(gcloud config get-value compute/zone)
各インスタンスでkubectl get nodes
を通して積層コントロールプレーンが構築されている事が確認できます。
# controllerで実行
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
controller-0 Ready control-plane,master 10m v1.22.2
controller-1 Ready control-plane,master 3m30s v1.22.2
controller-2 Ready control-plane,master 101s v1.22.2
各インスタンスで以下のコマンドを通して積層etcdクラスターが正常に動作している事が確認できます。
# controllerで実行
$ kubeadm config images list --kubernetes-version v1.22.2
k8s.gcr.io/kube-apiserver:v1.22.2
k8s.gcr.io/kube-controller-manager:v1.22.2
k8s.gcr.io/kube-scheduler:v1.22.2
k8s.gcr.io/kube-proxy:v1.22.2
k8s.gcr.io/pause:3.5
k8s.gcr.io/etcd:3.5.0-0
k8s.gcr.io/coredns/coredns:v1.8.4
$ docker run --rm -it \
--net host \
-v /etc/kubernetes:/etc/kubernetes k8s.gcr.io/etcd:3.5.0-0 etcdctl \
--cert /etc/kubernetes/pki/etcd/peer.crt \
--key /etc/kubernetes/pki/etcd/peer.key \
--cacert /etc/kubernetes/pki/etcd/ca.crt \
--endpoints https://10.240.0.10:2379 endpoint health --cluster
https://10.240.0.10:2379 is healthy: successfully committed proposal: took = 18.440766ms
https://10.240.0.11:2379 is healthy: successfully committed proposal: took = 21.317217ms
https://10.240.0.12:2379 is healthy: successfully committed proposal: took = 22.050581ms
Bootstrapping the Kubernetes Worker Nodes
前のパートでもう環境設定は終わっています。各workerインスタンスでkubeadm joinコマンドを実行するだけでクラスターに加わります。
# 各々のworkerで実行
kubeadm join XXX.XXX.XXX.XXX:6443 --token XXXXXX.xxxxxxxxxxxxxxxx \
--discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# controllerで実行
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
controller-0 Ready control-plane,master 25m v1.22.2
controller-1 Ready control-plane,master 18m v1.22.2
controller-2 Ready control-plane,master 16m v1.22.2
worker-0 Ready <none> 2m v1.22.2
worker-1 Ready <none> 2m7s v1.22.2
worker-2 Ready <none> 117s v1.22.2
Configuring kubectl for Remote Access
Master Nodeでkubectl config view --raw
を実行した後、出力されるテキストを~/.kube/config
に上書きすることでkubectlでのリモートアクセスが可能になります。もともとグローバルIPでの証明書なので問題なく作動します。
Provisioning Pod Network Routes
Calicoを導入しているので必要ありません。
Deploying the DNS Cluster Add-on
もう作動しています。
# controllerで実行
$ kubectl get pods -l k8s-app=kube-dns -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-78fcd69978-kvcjq 1/1 Running 0 49m
coredns-78fcd69978-nvhkf 1/1 Running 0 49m
$ kubectl run busybox --image=busybox:1.28 --command -- sleep 360
$ kubectl get pods -l run=busybox
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 3s
$ POD_NAME=$(kubectl get pods -l run=busybox -o jsonpath="{.items[0].metadata.name}")
$ kubectl exec -ti $POD_NAME -- nslookup kubernetes
Server: 10.32.0.10
Address 1: 10.32.0.10 kube-dns.kube-system.svc.cluster.local
Name: kubernetes
Address 1: 10.32.0.1 kubernetes.default.svc.cluster.local
Smoke Test
テストなので割愛します。参考までにData Encryptionセッションでは証明書を使用していますが、kubeadmが自動生成する証明書は/etc/kubernetes/pki
に位置します7。
Cleaning Up
# インスタンスの削除
gcloud -q compute instances delete \
controller-0 controller-1 controller-2 \
worker-0 worker-1 worker-2 \
--zone $(gcloud config get-value compute/zone)
# ロードバランサの削除
gcloud -q compute forwarding-rules delete kubernetes-forwarding-rule \
--region $(gcloud config get-value compute/region)
gcloud -q compute backend-services remove-backend kubernetes-network-lb-backend-service \
--instance-group instance-group-easy-way \
--instance-group-zone $(gcloud config get-value compute/zone) \
--region $(gcloud config get-value compute/region)
gcloud -q compute backend-services delete kubernetes-network-lb-backend-service \
--region $(gcloud config get-value compute/region)
# インスタンスグループの削除
gcloud -q compute instance-groups unmanaged delete instance-group-easy-way \
--zone $(gcloud config get-value compute/zone)
# ヘルスチェックの削除
gcloud -q compute health-checks delete kubernetes \
--region=$(gcloud config get-value compute/region)
# ファイアウォール規則の削除
gcloud -q compute firewall-rules delete \
kubernetes-the-easy-way-allow-internal \
kubernetes-the-easy-way-allow-external \
kubernetes-the-easy-way-allow-health-check
# ネットワークの削除
gcloud -q compute networks subnets delete kubernetes
gcloud -q compute networks delete kubernetes-the-easy-way
# ip addressesの解放
gcloud -q compute addresses delete kubernetes-the-easy-way \
--region $(gcloud config get-value compute/region)
-
Kubernetes The Hard Way, https://github.com/kelseyhightower/kubernetes-the-hard-way ↩
-
Self-managed Kubernetes in Google Compute Engine (GCE), https://docs.projectcalico.org/getting-started/kubernetes/self-managed-public-cloud/gce ↩
-
Linux VM への SSH 接続, https://cloud.google.com/compute/docs/instances/ssh?hl=ja ↩
-
Kubernetes API health endpoints, https://kubernetes.io/docs/reference/using-api/health-checks/ ↩
-
Google Cloud Load Balancing ガイド - ヘルスチェックの概要, https://cloud.google.com/load-balancing/docs/health-check-concepts?hl=ja ↩
-
Google Cloud Load Balancing ガイド - 負荷分散された VM からのリクエストの送信, https://cloud.google.com/load-balancing/docs/internal/setting-up-internal#test-from-backend-vms ↩
-
Kubernetesドキュメント ベストプラクティス - PKI証明書とその要件, https://kubernetes.io/ja/docs/setup/best-practices/certificates/ ↩