Kubernetes the hard way (GCP版)の続きです。
前回の記事はこちら
Chap9. Bootstrapping the Kubernetes Worker Nodes(ワーカーノードのブートストラップ)
このチャプターでは、ユーザーが作成したコンテナが動作するWorker Nodeのブートストラップを行います。
Worker NodeのコンポーネントであるKubelet・kube-proxyとコンテナランタイムのインストール・設定を行い、サービスとして起動します。
これからの作業は、Worker Node用の3つのインスタンスで同じ作業を行います。
パッケージのインストール
必要なパッケージのインストールを行います。
...@worker-0:~$ { > sudo apt-get update > sudo apt-get -y install socat conntrack ipset > } Hit:1 http://us-west1.gce.archive.ubuntu.com/ubuntu bionic InRelease Get:2 http://us-west1.gce.archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB] ...
Swapの無効化
ユーザーが作成したコンテナが動作するWorker Nodeでは、スワップの無効化が推奨されています。
It is recommended that swap be disabled to ensure Kubernetes can provide proper resource allocation and quality of service.
そのため、各Nodeでスワップが有効化されていないかを確認します。
(スワップは無効になっていたことを確認したので、次の手順に進みます。)
...@worker-0:~$ sudo swapon --show
Worker Node用のバイナリーのダウンロードとインストール
Worker Node用のコンポーネントなどを含んだバイナリーをダウンロードします。
...@worker-0:~$ wget -q --show-progress --https-only --timestamping \ > https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.15.0/crictl-v1.15.0-linux-amd64.tar.gz \ > https://github.com/opencontainers/runc/releases/download/v1.0.0-rc8/runc.amd64 \ > https://github.com/containernetworking/plugins/releases/download/v0.8.2/cni-plugins-linux-amd64-v0.8.2.tgz \ > https://github.com/containerd/containerd/releases/download/v1.2.9/containerd-1.2.9.linux-amd64.tar.gz \ > https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubectl \ > https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kube-proxy \ > https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubelet crictl-v1.15.0-linux-amd64.tar.gz 100%[=================================================================================================>] 11.50M 18.5MB/s in 0.6s runc.amd64 100%[=================================================================================================>] 9.79M 15.8MB/s in 0.6s cni-plugins-linux-amd64-v0.8.2.tgz 100%[=================================================================================================>] 34.96M 29.8MB/s in 1.2s containerd-1.2.9.linux-amd64.tar.gz 100%[=================================================================================================>] 32.96M 30.5MB/s in 1.1s kubectl 100%[=================================================================================================>] 40.99M 170MB/s in 0.2s kube-proxy 100%[=================================================================================================>] 35.27M 117MB/s in 0.3s kubelet 100%[=================================================================================================>] 114.10M 202MB/s in 0.6s
次に、バイナリーのインストール用のディレクトリを作成します。
...@worker-0:~$ sudo mkdir -p \ > /etc/cni/net.d \ > /opt/cni/bin \ > /var/lib/kubelet \ > /var/lib/kube-proxy \ > /var/lib/kubernetes \ > /var/run/kubernetes
ディレクトリを作成した上で、ダウンロードしたバイナリーをインストールします。
Kubernetesのコンポーネントに加えてcrictl
もダウンロードしていますが、これはCRI互換のコンテナランタイムに対して利用可能なCLIツールのようです。
...@worker-0:~$ { > mkdir containerd > tar -xvf crictl-v1.15.0-linux-amd64.tar.gz > tar -xvf containerd-1.2.9.linux-amd64.tar.gz -C containerd > sudo tar -xvf cni-plugins-linux-amd64-v0.8.2.tgz -C /opt/cni/bin/ > sudo mv runc.amd64 runc > chmod +x crictl kubectl kube-proxy kubelet runc > sudo mv crictl kubectl kube-proxy kubelet runc /usr/local/bin/ > sudo mv containerd/bin/* /bin/ > } crictl bin/ bin/containerd-shim-runc-v1 bin/ctr bin/containerd bin/containerd-stress bin/containerd-shim ./ ./flannel ./ptp ./host-local ./firewall ./portmap ./tuning ./vlan ./host-device ./bandwidth ./sbr ./static ./dhcp ./ipvlan ./macvlan ./loopback ./bridge
CNIネットワーキングの設定
次にWorker Nodeに関連するネットワーク周りの設定を行います。
最初に、Node(VM)で確保されているpodに割り当てるためのCIDRブロック(IPアドレスレンジ)を確認します。
...@worker-0:~$POD_CIDR=$(curl -s -H "Metadata-Flavor: Google" \ > http://metadata.google.internal/computeMetadata/v1/instance/attributes/pod-cidr) ...@worker-0:~$ echo $POD_CIDR 10.200.0.0/24
事前に定義しておいたCIDRブロック(IPアドレスレンジ)を基に、下記のネットワーク設定ファイルを作成します。
10-bridge.conf
: Bridgeのネットワークの設定99-loopback.conf
: Loopbackネットワークの設定
...@worker-0:~$ cat <<EOF | sudo tee /etc/cni/net.d/10-bridge.conf > { > "cniVersion": "0.3.1", > "name": "bridge", > "type": "bridge", > "bridge": "cnio0", > "isGateway": true, > "ipMasq": true, > "ipam": { > "type": "host-local", > "ranges": [ > [{"subnet": "${POD_CIDR}"}] > ], > "routes": [{"dst": "0.0.0.0/0"}] > } > } > EOF { "cniVersion": "0.3.1", "name": "bridge", "type": "bridge", "bridge": "cnio0", "isGateway": true, "ipMasq": true, "ipam": { "type": "host-local", "ranges": [ [{"subnet": "10.200.0.0/24"}] ], "routes": [{"dst": "0.0.0.0/0"}] } } ...@worker-0:~$ cat <<EOF | sudo tee /etc/cni/net.d/99-loopback.conf > { > "cniVersion": "0.3.1", > "name": "lo", > "type": "loopback" > } > EOF { "cniVersion": "0.3.1", "name": "lo", "type": "loopback" }
10-bridge.conf
では、IPAM (IPアドレス管理) の設定項目でranges
(IPアドレスの範囲) に対して事前に定義した値 ($POD_CIDR
) を設定しています。
containerdの設定
続いて、コンテナランタイムの設定を行います。
最初に、containerdの設定ファイル(config.toml
)を作成します。
今回作成するクラスタでは、High-levelのランタイムとしてcontainerd
・Low-levelのランタイムとしてrunc
を利用します。
そのため、containerdの設定ファイル内の項目(runtime_engine
)ではrunc
を指定しています。
High-level / Low-levelのコンテナランタイムの概念は事前に頭に入れておけば、この部分の設定内容も混乱しないと思います。
...@worker-0:~$ sudo mkdir -p /etc/containerd/ ...@worker-0:~$ cat << EOF | sudo tee /etc/containerd/config.toml > [plugins] > [plugins.cri.containerd] > snapshotter = "overlayfs" > [plugins.cri.containerd.default_runtime] > runtime_type = "io.containerd.runtime.v1.linux" > runtime_engine = "/usr/local/bin/runc" > runtime_root = "" > EOF [plugins] [plugins.cri.containerd] snapshotter = "overlayfs" [plugins.cri.containerd.default_runtime] runtime_type = "io.containerd.runtime.v1.linux" runtime_engine = "/usr/local/bin/runc" runtime_root = ""
別のLow-levelであるkata-container
と連携する場合も、この項目で利用するLow-levelランタイムを指定しています。
次に、containerd.service
のsystemdのユニットファイルを作成します。
...@worker-0:~$ cat <<EOF | sudo tee /etc/systemd/system/containerd.service > [Unit] > Description=containerd container runtime > Documentation=https://containerd.io > After=network.target > > [Service] > ExecStartPre=/sbin/modprobe overlay > ExecStart=/bin/containerd > Restart=always > RestartSec=5 > Delegate=yes > KillMode=process > OOMScoreAdjust=-999 > LimitNOFILE=1048576 > LimitNPROC=infinity > LimitCORE=infinity > > [Install] > WantedBy=multi-user.target > EOF [Unit] Description=containerd container runtime Documentation=https://containerd.io After=network.target [Service] ExecStartPre=/sbin/modprobe overlay ExecStart=/bin/containerd Restart=always RestartSec=5 Delegate=yes KillMode=process OOMScoreAdjust=-999 LimitNOFILE=1048576 LimitNPROC=infinity LimitCORE=infinity [Install] WantedBy=multi-user.target
これでcontainerdの設定が完了となります。
Kubeletの設定
次にkubeletの設定を行います。
最初にkubelet用の秘密鍵・証明書ファイル、kubeconfigファイルを/var/lib/kubelet/
に移動します。
...@worker-0:~$ { > sudo mv ${HOSTNAME}-key.pem ${HOSTNAME}.pem /var/lib/kubelet/ > sudo mv ${HOSTNAME}.kubeconfig /var/lib/kubelet/kubeconfig > sudo mv ca.pem /var/lib/kubernetes/ > }
次に、kubeletの設定ファイルを作成します。
...@worker-0:~$ cat <<EOF | sudo tee /var/lib/kubelet/kubelet-config.yaml > kind: KubeletConfiguration > apiVersion: kubelet.config.k8s.io/v1beta1 > authentication: > anonymous: > enabled: false > webhook: > enabled: true > x509: > clientCAFile: "/var/lib/kubernetes/ca.pem" > authorization: > mode: Webhook > clusterDomain: "cluster.local" > clusterDNS: > - "10.32.0.10" > podCIDR: "${POD_CIDR}" > resolvConf: "/run/systemd/resolve/resolv.conf" > runtimeRequestTimeout: "15m" > tlsCertFile: "/var/lib/kubelet/${HOSTNAME}.pem" > tlsPrivateKeyFile: "/var/lib/kubelet/${HOSTNAME}-key.pem" > EOF kind: KubeletConfiguration apiVersion: kubelet.config.k8s.io/v1beta1 authentication: anonymous: enabled: false webhook: enabled: true x509: clientCAFile: "/var/lib/kubernetes/ca.pem" authorization: mode: Webhook clusterDomain: "cluster.local" clusterDNS: - "10.32.0.10" podCIDR: "10.200.0.0/24" resolvConf: "/run/systemd/resolve/resolv.conf" runtimeRequestTimeout: "15m" tlsCertFile: "/var/lib/kubelet/worker-0.pem" tlsPrivateKeyFile: "/var/lib/kubelet/worker-0-key.pem"
設定ファイル内で確認すべき点としては下記の部分です。
1. Authentication Mode
Authentication ModeとしてWebhook
(webhook-token-authentication) を指定します。
これにより、Kubeletはトークンを受け取った上でTokenReview
APIをcallし、API Serverに対して認証処理をDeligate(委任)します。
また、Authentication Modeの一つであるanonymousを無効化しています。
この設定が有効になっていると、Kubeletに対するリクエストを (Authenticationのフェーズで) 全て許可して、Kubeletに対するAPI Requestをanonymous
として扱います。
基本的にはAPI Requestはユーザー(もしくはService Account)と紐づけられますが、この場合は下記のユーザーとグループに紐づけられます。
- user:
system:anonymous
- group:
system:unauthenticated
公式のドキュメントでも、anonymousの設定は非推奨となっています。
加えて、クライアント側のCA証明書ファイル(clientCAFile
)としてユーザー側で作成したca.pem
を指定しています。
これにより、kubelet側でX509クライアント証明書を用いた認証が可能になります。
(kubelet側では、webhookとX509クライアント証明書認証の両方の方式を利用することができると認識していますが、この部分はまだ整理できていないので調査中です。)
2. Authorization Mode
Authorization ModeとしてWebhook
を指定します。
先ほどのAuthenticationのフェーズで認証された後にユーザー情報が付与されて、それを基に認可(Authorization)を行うという流れになっています。
... and user information is added to its context. This gives future steps (such as authorization and admission controllers) the ability to access the previously established identity of the user.
Authorization ModeとしてWebhook
を指定することで、API Requestに対する認可処理をKubernetesのAPI ServerにDeligate(委任)します。
KubeletはSubjectAccessReview
APIをcallして、API Serverに対して認可処理を依頼します。
3. resolvConf
kubeletではWorker Nodeではなく外部のDNSリゾルバを参照するために/run/systemd/resolve/resolv.conf
を指定しています。
これにより、KubeletはControl Plane内のDNSを参照することになります。
次に、kubelet.service
のsystemdのユニットファイルを作成します。
ユニットファイルの中では、先ほど作成した設定ファイル (kubelet-config.yaml
) を参照しています。
...@worker-0:~$ cat <<EOF | sudo tee /etc/systemd/system/kubelet.service > [Unit] > Description=Kubernetes Kubelet > Documentation=https://github.com/kubernetes/kubernetes > After=containerd.service > Requires=containerd.service > > [Service] > ExecStart=/usr/local/bin/kubelet \\ > --config=/var/lib/kubelet/kubelet-config.yaml \\ > --container-runtime=remote \\ > --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock \\ > --image-pull-progress-deadline=2m \\ > --kubeconfig=/var/lib/kubelet/kubeconfig \\ > --network-plugin=cni \\ > --register-node=true \\ > --v=2 > Restart=on-failure > RestartSec=5 > > [Install] > WantedBy=multi-user.target > EOF [Unit] Description=Kubernetes Kubelet Documentation=https://github.com/kubernetes/kubernetes After=containerd.service Requires=containerd.service [Service] ExecStart=/usr/local/bin/kubelet \ --config=/var/lib/kubelet/kubelet-config.yaml \ --container-runtime=remote \ --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock \ --image-pull-progress-deadline=2m \ --kubeconfig=/var/lib/kubelet/kubeconfig \ --network-plugin=cni \ --register-node=true \ --v=2 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target
kube-proxyの設定
最後に、kube-proxyの設定を行います。
kubeletと同様に、設定ファイルを作成した上でsystemdのユニットファイルを作成します。
...@worker-0:~$ sudo mv kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig ...@worker-0:~$ cat <<EOF | sudo tee /var/lib/kube-proxy/kube-proxy-config.yaml > kind: KubeProxyConfiguration > apiVersion: kubeproxy.config.k8s.io/v1alpha1 > clientConnection: > kubeconfig: "/var/lib/kube-proxy/kubeconfig" > mode: "iptables" > clusterCIDR: "10.200.0.0/16" > EOF kind: KubeProxyConfiguration apiVersion: kubeproxy.config.k8s.io/v1alpha1 clientConnection: kubeconfig: "/var/lib/kube-proxy/kubeconfig" mode: "iptables" clusterCIDR: "10.200.0.0/16" ...@worker-0:~$ cat <<EOF | sudo tee /etc/systemd/system/kube-proxy.service > [Unit] > Description=Kubernetes Kube Proxy > Documentation=https://github.com/kubernetes/kubernetes > > [Service] > ExecStart=/usr/local/bin/kube-proxy \\ > --config=/var/lib/kube-proxy/kube-proxy-config.yaml > Restart=on-failure > RestartSec=5 > > [Install] > WantedBy=multi-user.target > EOF [Unit] Description=Kubernetes Kube Proxy Documentation=https://github.com/kubernetes/kubernetes [Service] ExecStart=/usr/local/bin/kube-proxy \ --config=/var/lib/kube-proxy/kube-proxy-config.yaml Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target
設定ファイル内で確認すべき点としては下記の部分です。
1. kube-proxyのmode
kube-proxyはWorker Node内で動作して、クラスタ内部または外部からPodにアクセスできるようにネットワーク周りを管理しています。
下記のモードから任意の実装を選択することが可能です。
iptables
(デフォルト)ipvs
userspace
Worker Node用のサービス起動
各コンポーネントの設定を行なった上で、作成したサービスを起動します。
{ sudo systemctl daemon-reload sudo systemctl enable containerd kubelet kube-proxy sudo systemctl start containerd kubelet kube-proxy }
検証1
一通り設定が完了したら、検証を行います。
Control Planeにsshでログインして、Adminユーザー用のkubeconfigを指定した上でkubectl get nodes
コマンドを実行します。
~/w/k/h/04 ❯❯❯ gcloud compute ssh controller-0 \ --command "kubectl get nodes --kubeconfig admin.kubeconfig" Enter passphrase for key '/Users/nishikawatakushi/.ssh/google_compute_engine': NAME STATUS ROLES AGE VERSION worker-0 Ready <none> 22s v1.15.3 worker-1 Ready <none> 21s v1.15.3 worker-2 Ready <none> 19s v1.15.3
(+α) swap(スワップ)とは
メモリ不足時に、物理メモリ上のデータをHDDなどに書き出すことをスワップと言います。
OSのメモリ管理機能として提供されており、Worker Nodeの実態であるVMでもメモリのスワップは発生し得ます。
しかし、Kubernetesでは一部のpodがメモリを消費することで、他のpodのメモリがswapして速度が低下するため、swap自体を無効化することが推奨されています。
Kubernetesのv1.8から、swapが有効になっている場合に、kubeletが起動しないように設定されているようです。
今回は、Chap9の内容をまとめました。
Chap8でまとめたControl Planeの設定と関連する部分もあるので、各コンポーネント同士の設定を紐づけて理解しておく必要があると感じました。