#初めに
「kubernetes the hard way を PC でやってみる」の10回目、「Bootstrapping the Kubernetes Control Plane」についてです。( 目次 )
kubernetes の中心的な機能を果たす control plane のセットアップです。
バイナリ入手はどれかの control plane で実施して、コピーすればよいですが、
それ以降については ※3台の control plane ノードで実施します※
やることは以下の通りです。
- kubernetes バイナリの入手
- API サーバーの構成
- Controller-manager の構成
- Scheduler の構成
- Controller サービスの起動
- HTTP Health Check
- kubelet 用の RBAC 設定
- The Kubernetes Frontend Load Balancer
図で言うと赤枠の部分です。
ただし、外部ロードバランサーを用意していない関係で、
HTTP Health Check 並びに The Kubernetes Frontend Load Balancer は割愛しています。
※ kubernetes the hard way では google cloud の Network load balancer を構成していますが
オンプレではできないので割愛しています。(他のロードバランサーでやってみようか、とは思いましたがあきらめ…)
また、末尾につまづいたエラーとその調査・対応方法も記載しておきます。
kubernetes バイナリの入手
kubernetes の最新版は git hub 上で確認することができます。
以下の URL アクセス後、右側の列の Releases 欄に最新版が記載されています。
https://github.com/kubernetes/kubernetes
2020/07 時点では、 v1.18.6 でした。
Releases 欄の バージョン部分をクリックすると リリースページに遷移します。 そのページ下部にダウンロードできるリンクがあります。
https://github.com/kubernetes/kubernetes/releases/download/v1.18.6/kubernetes.tar.gz
kubernetes the hard way では、各コンポーネントのバイナリを個別に取得する手順になっていますが、
最近は kubernetes.tar.gz となっており、その中のスクリプトを実行してバイナリを取得する構成のようです。
私が実施した際は、 v1.16.9 で、 URL としては以下の通りでした。今でも なぜ 1.16 でやったのか理解に苦しみます。
https://github.com/kubernetes/kubernetes/releases/download/v1.16.9/kubernetes.tar.gz
これを wget でダウンロードします。
# wget -q --show-progress --https-only --timestamping https://github.com/kubernetes/kubernetes/releases/download/v1.16.9/kubernetes.tar.gz
kubernetes.tar.gz 100%[================================================================================================================================>] 445.60K 214KB/s in 2.1s
ダウンロード完了後、 展開し、 kubernetes/cluster まで移動します。
# tar zxf kubernetes.tar.gz
# ls
ca etcd kubernetes kubernetes-client-linux-amd64.tar.gz kubernetes.tar.gz
# cd kubernetes/
# ls
client cluster docs hack LICENSES README.md server version
# cd cluster/
# ls
addons clientbin.sh gce get-kube-local.sh images kubeadm.sh kube-down.sh kubernetes-anywhere kube-util.sh OWNERS README.md skeleton test-network.sh update-storage-objects.sh
BUILD common.sh get-kube-binaries.sh get-kube.sh juju kubectl.sh kubemark kube-up.sh log-dump pre-existing restore-from-backup.sh test-e2e.sh test-smoke.sh validate-cluster.sh
そこで、get-kube-binaries.sh
を実行します。途中で、 ダウンロードするかどうか聞かれるので y を押します。
# ./get-kube-binaries.sh
Kubernetes release: v1.16.9
Server: linux/amd64 (to override, set KUBERNETES_SERVER_ARCH)
Client: linux/amd64 (autodetected) (to override, set KUBERNETES_CLIENT_OS and/or KUBERNETES_CLIENT_ARCH)
Will download kubernetes-server-linux-amd64.tar.gz from https://dl.k8s.io/v1.16.9
Will download and extract kubernetes-client-linux-amd64.tar.gz from https://dl.k8s.io/v1.16.9
Is this ok? [Y]/n
y
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 161 100 161 0 0 473 0 --:--:-- --:--:-- --:--:-- 473
100 352M 100 352M 0 0 59.2M 0 0:00:05 0:00:05 --:--:-- 78.4M
md5sum(kubernetes-server-linux-amd64.tar.gz)=f606d0f63fd751f857c66e33454cfcbf
sha1sum(kubernetes-server-linux-amd64.tar.gz)=8b3b190f84463dfba117cf49a82c5a58eeb6e67b
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 161 100 161 0 0 1025 0 --:--:-- --:--:-- --:--:-- 1025
100 12.3M 100 12.3M 0 0 9774k 0 0:00:01 0:00:01 --:--:-- 29.9M
md5sum(kubernetes-client-linux-amd64.tar.gz)=e9c4ca5fdb50619d35531b859aadc2bd
sha1sum(kubernetes-client-linux-amd64.tar.gz)=cd0cd90c99a461ae3ff12a569b902e58653a1e9b
Extracting /data/assam/k8s/kubernetes/client/kubernetes-client-linux-amd64.tar.gz into /data/assam/k8s/kubernetes/platforms/linux/amd64
Add '/data/assam/k8s/kubernetes/client/bin' to your PATH to use newly-installed binaries.
メッセージでは kubernetes/client/bin に PATH を通すという指示になっていますが、
kubernetes/client/bin 以下には kubectl がダウンロードされているだけです。
サーバー用のコンポーネントは kubernetes/server 以下にダウンロードされています。
それをさらに展開します。展開した先の kubernetes/server/bin 以下に各種コンポーネントがあります。
# cd ../server
# ls
README kubernetes-manifests.tar.gz kubernetes-server-linux-amd64.tar.gz
# tar zxf kubernetes-server-linux-amd64.tar.gz
# ls
README kubernetes kubernetes-manifests.tar.gz kubernetes-server-linux-amd64.tar.gz
# cd kubernetes/server/bin
# ls
apiextensions-apiserver kube-apiserver.tar kube-proxy kube-scheduler.docker_tag kubelet
hyperkube kube-controller-manager kube-proxy.docker_tag kube-scheduler.tar mounter
kube-apiserver kube-controller-manager.docker_tag kube-proxy.tar kubeadm
kube-apiserver.docker_tag kube-controller-manager.tar kube-scheduler kubectl
上記の各コンポーネントのうち、以下のものを 各 control plane ノードの /usr/local/bin 以下にコピーします。
(方法はお任せで。あと念のため実行権限も確認してください。)
- kube-apiserver
- kube-controller-manager
- kube-scheduler
- kubectl
#API サーバーの構成
kubernetes の中心ともいえる API サーバーを構成します。
まず、ディレクトリを作成し、その中に 各種秘密鍵や証明書をコピーします。
証明書は第6回の最後で各ノードの作業ユーザーのホームディレクトリに配置したと思います。
# mkdir -p /var/lib/kubernetes/
# cp ca.pem ca-key.pem kubernetes.pem kubernetes-key.pem service-account.pem service-account-key.pem encryption-config.yaml /var/lib/kubernetes/
# ls -l /var/lib/kubernetes/
total 28
-rw------- 1 root root 1675 May 9 05:34 ca-key.pem
-rw-r--r-- 1 root root 1318 May 9 05:34 ca.pem
-rw-r--r-- 1 root root 240 May 9 05:34 encryption-config.yaml
-rw------- 1 root root 1679 May 9 05:34 kubernetes-key.pem
-rw-r--r-- 1 root root 1728 May 9 05:34 kubernetes.pem
-rw------- 1 root root 1679 May 9 05:34 service-account-key.pem
-rw-r--r-- 1 root root 1440 May 9 05:34 service-account.pem
次に、 API サーバーを systemd 配下のサービスとして稼働させるために、
ユニットファイルを作成します。
長いので、 kubernetes the hard way からまずテキストファイル等にコピー&ペーストして実施すればよいです。
違いは以下のポイントです
- ここでは root ユーザーで作業しているので、 sudo は不要(と言いつつ後半面倒になって使っています)
- IP アドレスが異なる
kubernetes the hard way と合わせた名前、IP 等にしていれば単純にコピー&ペーストで
対応可能なのですが、最初に何も考えずにホスト名、IP 等を決めてしまったツケが出ていますね。。
(ただ、そのおかげで注意深く見ながらやることになり、結果として理解が深まったかもしれません)
例によって、cat コマンドでファイルを作るため、事前に IP アドレス等を変数に入れています。
kubernetes the hard way では Google cloud のコマンドで IP を取得していますが、
こちらはオンプレPC ですので、以下のように ip コマンドで取得します。
tmux で3台同時に操作する前提でコマンドを用いて IP を変数に格納していますが、
1台ずつ実施する場合は INTERNAL_IP=192.168.199.200
等で全く問題ありません。
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:01:97:f0 brd ff:ff:ff:ff:ff:ff
inet 192.168.199.200/24 brd 192.168.199.255 scope global enp1s0
valid_lft forever preferred_lft forever
# ip addr | grep 192.168 | awk '{ print $2 }' | cut -d / -f 1
192.168.199.200
# INTERNAL_IP=$( ip addr | grep 192.168 | awk '{ print $2 }' | cut -d / -f 1 )
# echo ${INTERNAL_IP}
192.168.199.200
変数の設定ができれば、 いよいよ API Server のユニットファイルを作成します。
繰り返しになりますが、ここから先は
INTERNAL_IP などに注意しながら Control plane のノード3台それぞれで作成します。
# cat <<EOF | tee /etc/systemd/system/kube-apiserver.service
> [Unit]
> Description=Kubernetes API Server
> Documentation=https://github.com/kubernetes/kubernetes
>
> [Service]
> ExecStart=/usr/local/bin/kube-apiserver \\
> --advertise-address=${INTERNAL_IP} \\
> --allow-privileged=true \\
> --apiserver-count=3 \\
> --audit-log-maxage=30 \\
> --audit-log-maxbackup=3 \\
> --audit-log-maxsize=100 \\
> --audit-log-path=/var/log/audit.log \\
> --authorization-mode=Node,RBAC \\
> --enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
> --etcd-cafile=/var/lib/kubernetes/ca.pem \\
> --etcd-certfile=/var/lib/kubernetes/kubernetes.pem \\
> --etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \\
> --etcd-servers=https://192.168.199.200:2379,https://192.168.199.201:2379,https://192.168.199.202:2379 \\
> --event-ttl=1h \\
> --encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
> --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
> --kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \\
> --kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \\
> --kubelet-https=true \\
> --runtime-config=api/all \\
> --service-account-key-file=/var/lib/kubernetes/service-account.pem \\
[Service]
ExecStart=/usr/local/bin/kube-apiserver \
--advertise-address=192.168.199.200 \
--allow-privileged=true \
--apiserver-count=3 \
--audit-log-maxage=30 \
--audit-log-maxbackup=3 \
--audit-log-maxsize=100 \
--audit-log-path=/var/log/audit.log \
--authorization-mode=Node,RBAC \
--bind-address=0.0.0.0 \
--client-ca-file=/var/lib/kubernetes/ca.pem \
--enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \
--etcd-cafile=/var/lib/kubernetes/ca.pem \
--etcd-certfile=/var/lib/kubernetes/kubernetes.pem \
--etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \
--etcd-servers=https://192.168.199.200:2379,https://192.168.199.201:2379,https://192.168.199.202:2379 \
--event-ttl=1h \
--encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \
--kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \
--kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \
--kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \
--kubelet-https=true \
--runtime-config=api/all \
--service-account-key-file=/var/lib/kubernetes/service-account.pem \
--service-cluster-ip-range=10.32.0.0/24 \
--service-node-port-range=30000-32767 \
--tls-cert-file=/var/lib/kubernetes/kubernetes.pem \
--tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem \
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
#
よく見ると、各種証明書の指定や、 encryption-config.yaml の指定、 クラスターIP レンジやポートレンジの指定が行われていることがわかります。
--service-cluster-ip-range=10.32.0.0
を指定しているので、 kubernetes 上でサービスを作成すると 10.32.0.0 のNW からアドレスが払い出されます。
また、 NodePort を作成する際のポートは 30000-32767 になります。
API サーバーの各種オプションについては、 Command Line tools reference の kube-apiserver をご覧ください。
API サーバーの準備はここまでです。 systemd のユニットファイルを作成しているので、
systemctl daemon-reload
の実行と systemctl list-unit-files | grep xxx
で確認したりする必要がありますが、
他にも同様にユニットファイルを作成するため、最後にまとめて実施します。
#Controller-manager の構成
まず、第7回で作成・配布した kubeconfig ファイルをコピーします。
# cp kube-controller-manager.kubeconfig /var/lib/kubernetes/
API サーバー同様、ユニットファイルを作成します。
--cluster-cidr=10.200.0.0/16
を指定していますね。
# cat <<EOF | sudo tee /etc/systemd/system/kube-controller-manager.service
> [Unit]
> Description=Kubernetes Controller Manager
> Documentation=https://github.com/kubernetes/kubernetes
>
> [Service]
> ExecStart=/usr/local/bin/kube-controller-manager \\
> --address=0.0.0.0 \\
> --cluster-cidr=10.200.0.0/16 \\
> --cluster-name=kubernetes \\
> --cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \\
> --cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \\
> --kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
> --leader-elect=true \\
> --root-ca-file=/var/lib/kubernetes/ca.pem \\
> --service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \\
> --service-cluster-ip-range=10.32.0.0/24 \\
> --use-service-account-credentials=true \\
> --v=2
> Restart=on-failure
> RestartSec=5
>
> [Install]
> WantedBy=multi-user.target
> EOF
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-controller-manager \
--address=0.0.0.0 \
--cluster-cidr=10.200.0.0/16 \
--cluster-name=kubernetes \
--cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \
--cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \
--kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \
--leader-elect=true \
--root-ca-file=/var/lib/kubernetes/ca.pem \
--service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \
--service-cluster-ip-range=10.32.0.0/24 \
--use-service-account-credentials=true \
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
#
オプションについては API サーバー同様に
Command Line tools reference の kube-controller-manager をご覧ください。
#Scheduler の構成
Scheduler についても同様に対応します。
# cp kube-scheduler.kubeconfig /var/lib/kubernetes/
同様に、と言いつつ scheduler に関しては yaml の設定ファイルが必要なようです。
この辺りはあまり丁寧に確認していないのですが、
kube-scheduler を systemd 配下のサービスではなく、コンテナで動かす場合
この yaml ファイルにコンテナ関連の設定を行い、
static pod として各ノード上で稼働させるということをやるようです。
ただ、今回はあくまで systemd 配下で稼働させるため、
内容としては kubeconfig ファイルの指定と、 Leader Election を行うかどうかの設定ぐらいのようです。
(※ static pod とは kubelet に直接管理される pod で、
乱暴に言うと、あらかじめ指定したディレクトリ内に Pod 用 yaml ファイルを置くと kubelet が起動、管理してくれるというものです)
# cat <<EOF | sudo tee /etc/kubernetes/config/kube-scheduler.yaml
> apiVersion: kubescheduler.config.k8s.io/v1alpha1
> kind: KubeSchedulerConfiguration
> clientConnection:
> kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig"
> leaderElection:
> leaderElect: true
> EOF
apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
clientConnection:
kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig"
leaderElection:
leaderElect: true
#
次にユニットファイルを作成します。
# cat <<EOF | sudo tee /etc/systemd/system/kube-scheduler.service
> [Unit]
> Description=Kubernetes Scheduler
> Documentation=https://github.com/kubernetes/kubernetes
>
> [Service]
> ExecStart=/usr/local/bin/kube-scheduler \\
> --config=/etc/kubernetes/config/kube-scheduler.yaml \\
> --v=2
> Restart=on-failure
> RestartSec=5
>
> [Install]
> WantedBy=multi-user.target
> EOF
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-scheduler \
--config=/etc/kubernetes/config/kube-scheduler.yaml \
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
#
オプションについては API サーバー同様に
Command Line tools reference の kube-scheduler をご覧ください。
#Controller サービスの起動
ここまで作成してきた ユニットファイルを用いて、 systemd 配下で動かす kubernetes の各種サービスを起動します。
まずは daemon-reload です。
# systemctl daemon-reload
次に、ユニットファイルに間違いがないか簡易的に確認します。
(※3列表示ですが、前から ユニット・ファイル、STATE、VENDOR PRESET の順です。 VENDOR PRESET は一言でいえばデフォルトです。)
STATE欄が bad となっていればユニットファイル見直しです。
# systemctl list-unit-files | grep kube
kube-apiserver.service disabled enabled
kube-controller-manager.service disabled enabled
kube-scheduler.service disabled enabled
次に、先に自動起動設定をしておきます。
# systemctl enable kube-apiserver kube-controller-manager kube-scheduler
Created symlink /etc/systemd/system/multi-user.target.wants/kube-apiserver.service → /etc/systemd/system/kube-apiserver.service.
Created symlink /etc/systemd/system/multi-user.target.wants/kube-controller-manager.service → /etc/systemd/system/kube-controller-manager.service.
Created symlink /etc/systemd/system/multi-user.target.wants/kube-scheduler.service → /etc/systemd/system/kube-scheduler.service.
# systemctl list-unit-files | grep kube
kube-apiserver.service enabled enabled
kube-controller-manager.service enabled enabled
kube-scheduler.service enabled enabled
そして、いよいよ起動です。
##API サーバー起動
systemctl コマンドで起動し、 systemctl status でステータスを見ています。 loaded , active になっているので起動しています。
なお、ログ末尾に healthz check failed となっていますが、現時点では気にする必要はありません。
まだクラスターが構成されていないためのようです。
# systemctl start kube-apiserver
# echo $?
0
# systemctl status kube-apiserver
● kube-apiserver.service - Kubernetes API Server
Loaded: loaded (/etc/systemd/system/kube-apiserver.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2020-05-09 05:46:22 UTC; 11s ago
Docs: https://github.com/kubernetes/kubernetes
Main PID: 2625 (kube-apiserver)
Tasks: 11 (limit: 2282)
Memory: 273.2M
CGroup: /system.slice/kube-apiserver.service
mq2625 /usr/local/bin/kube-apiserver --advertise-address=192.168.199.200 --allow-privileged=true --apiserver-count=3 --audit-log-maxage=30 --audit-log-maxbackup=3 --audit-log-maxsize=100 --audit-log-path=/var/log>
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/start-apiextensions-informers ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/start-apiextensions-controllers ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/crd-informer-synced ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/bootstrap-controller ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [-]poststarthook/rbac/bootstrap-roles failed: reason withheld
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/scheduling/bootstrap-system-priority-classes ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/ca-registration ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/start-kube-apiserver-admission-initializer ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/start-kube-aggregator-informers ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/apiservice-registration-controller ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/apiservice-status-available-controller ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/kube-apiserver-autoregistration ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]autoregister-completion ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: [+]poststarthook/apiservice-openapi-controller ok
May 09 05:46:33 k8smaster0 kube-apiserver[2625]: healthz check failed
なお、ログをすべて確認する場合は、journalctl -u kube-apiserver
でご確認ください。
一度ぐらいは少し時間をかけて わからないながらも見ておいてもよいと思います。
単体レベルで確認したい場合は以下のようなコマンドで確認するようです。(admin.kubeconfig がある場所で実行)
# kubectl get --raw /healthz/poststarthook/rbac/bootstrap-roles --kubeconfig admin.kubeconfig
ok
##kube-controller-manager 起動
# systemctl start kube-controller-manager
# echo $?
0
# systemctl status kube-controller-manager
kube-controller-manager.service - Kubernetes Controller Manager
Loaded: loaded (/etc/systemd/system/kube-controller-manager.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2020-05-09 05:57:23 UTC; 7s ago
Docs: https://github.com/kubernetes/kubernetes
Main PID: 2654 (kube-controller)
Tasks: 8 (limit: 2282)
Memory: 21.5M
CGroup: /system.slice/kube-controller-manager.service
mq2654 /usr/local/bin/kube-controller-manager --address=0.0.0.0 --cluster-cidr=10.200.0.0/16 --cluster-name=kubernetes --cluster-signing-cert-file=/var/lib/kubernetes/ca.pem --cluster-signing-key-file=/var/lib/ku>
May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.727766 2654 controllermanager.go:519] Starting "cronjob"
May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.727830 2654 attach_detach_controller.go:334] Starting attach detach controller
May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.727906 2654 shared_informer.go:197] Waiting for caches to sync for attach detach
May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.967158 2654 controllermanager.go:534] Started "cronjob"
May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.968030 2654 controllermanager.go:519] Starting "csrsigning"
May 09 05:57:30 k8smaster0 kube-controller-manager[2654]: I0509 05:57:30.967999 2654 cronjob_controller.go:96] Starting CronJob Manager
May 09 05:57:31 k8smaster0 kube-controller-manager[2654]: I0509 05:57:31.118212 2654 controllermanager.go:534] Started "csrsigning"
May 09 05:57:31 k8smaster0 kube-controller-manager[2654]: I0509 05:57:31.118276 2654 controllermanager.go:519] Starting "clusterrole-aggregation"
May 09 05:57:31 k8smaster0 kube-controller-manager[2654]: I0509 05:57:31.118359 2654 certificate_controller.go:113] Starting certificate controller
May 09 05:57:31 k8smaster0 kube-controller-manager[2654]: I0509 05:57:31.118374 2654 shared_informer.go:197] Waiting for caches to sync for certificate
Starting "cronjob" などとなっていますね。 controller-manager は cronjob で Reconciliation loop を回しているんでしょうね。
##kube-scheduler 起動
# systemctl start kube-scheduler
# echo $?
0
# systemctl status kube-scheduler
● kube-scheduler.service - Kubernetes Scheduler
Loaded: loaded (/etc/systemd/system/kube-scheduler.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2020-05-09 05:59:55 UTC; 10s ago
Docs: https://github.com/kubernetes/kubernetes
Main PID: 2689 (kube-scheduler)
Tasks: 10 (limit: 2282)
Memory: 8.7M
CGroup: /system.slice/kube-scheduler.service
mq2689 /usr/local/bin/kube-scheduler --config=/etc/kubernetes/config/kube-scheduler.yaml --v=2
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.432942 2689 server.go:148] Version: v1.16.9
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.433059 2689 defaults.go:91] TaintNodesByCondition is enabled, PodToleratesNodeTaints predicate is mandatory
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.433080 2689 server.go:167] Starting Kubernetes Scheduler version v1.16.9
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.447008 2689 factory.go:294] Creating scheduler from algorithm provider 'DefaultProvider'
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.447053 2689 factory.go:382] Creating scheduler with fit predicates 'map[CheckNodeUnschedulable:{} CheckVolumeBinding:{} GeneralPredicates:{} MatchInterPodAffi>
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: W0509 05:59:57.450788 2689 authorization.go:47] Authorization is disabled
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: W0509 05:59:57.451153 2689 authentication.go:79] Authentication is disabled
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.451387 2689 deprecated_insecure_serving.go:51] Serving healthz insecurely on 0.0.0.0:10251
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.453170 2689 secure_serving.go:123] Serving securely on 0.0.0.0:10259
May 09 05:59:57 k8smaster0 kube-scheduler[2689]: I0509 05:59:57.553817 2689 leaderelection.go:241] attempting to acquire leader lease kube-system/kube-scheduler...
ログの末尾で、 Leader Election が開始されているのがわかります。
(ただし、このログの時点では他のノードの scheduler が起動していないのでうまく Leader にはなれませんが…)
##起動結果確認
各コンポーネントが問題ないことを確認します。
すると、下記のように表示されるはずです。
# kubectl get componentstatuses --kubeconfig admin.kubeconfig
# kubectl get componentstatus
NAME STATUS MESSAGE ERROR
controller-manager Healthy ok
scheduler Healthy ok
etcd-0 Healthy {"health":"true"}
etcd-1 Healthy {"health":"true"}
etcd-2 Healthy {"health":"true"}
実際には、 v1.16 では 下記のようになります。
NAME AGE
scheduler <unknown>
controller-manager <unknown>
etcd-0 <unknown>
etcd-2 <unknown>
etcd-1 <unknown>
これは v1.16 のバグで、 v1.17 では修正されています。
もう少し詳しくは末尾のエラーの説明部分に記載します。
#HTTP Health Check
HTTP Health Check ですが、 kubernetes the hard way では google cloud のロードバランサーからの
Health Check 用途で nginx を入れています。
今回オンプレPC でロードバランサーを入れていないため、あまり実施する意味がありません。
そのため今回は割愛いたします。
#kubelet 用の RBAC 設定
API サーバーから各ノードの kubelet にアクセスするための RBAC 権限設定を行います。
また、ここでの作業は control plane のどれか1ノードで実施すればOKです。
これは kubernetes の世界の中の設定なので etcd 内に格納され、どの control plane ノードからも参照できます。
system:kube-apiserver-to-kubelet という ClusterRole 内で
記載の resource に対する全種類の操作を許可しています。
なお、下記のやり方は 標準入力から直接 kubectl apply に内容を渡して設定していますが、
これは個人的には危ないやり方だと思います。
きちんと一度テキストファイルに出力してから apply したほうが良いと思います。
# cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
> apiVersion: rbac.authorization.k8s.io/v1beta1
> kind: ClusterRole
> metadata:
> annotations:
> rbac.authorization.kubernetes.io/autoupdate: "true"
> labels:
> kubernetes.io/bootstrapping: rbac-defaults
> name: system:kube-apiserver-to-kubelet
> rules:
> - apiGroups:
> - ""
> resources:
> - nodes/proxy
> - nodes/stats
> - nodes/log
> - nodes/spec
> - nodes/metrics
> verbs:
> - "*"
> EOF
clusterrole.rbac.authorization.k8s.io/system:kube-apiserver-to-kubelet created
#
次に ClusterRole を ユーザーに結び付ける ClusterRoleBinding を作成します。
roleRef 欄で 上記 ClusterRole を指定しています。
また、 subjects 欄で kubernetes というユーザーを指定しています。
# cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
> apiVersion: rbac.authorization.k8s.io/v1beta1
> kind: ClusterRoleBinding
> metadata:
> name: system:kube-apiserver
> namespace: ""
> roleRef:
> apiGroup: rbac.authorization.k8s.io
> kind: ClusterRole
> name: system:kube-apiserver-to-kubelet
> subjects:
> - apiGroup: rbac.authorization.k8s.io
> kind: User
> name: kubernetes
> EOF
clusterrolebinding.rbac.authorization.k8s.io/system:kube-apiserver created
一応作成されたことを見ておきましょう。
root@k8smaster0:/data/assam/k8s/ca# kubectl --kubeconfig admin.kubeconfig get ClusterRole
NAME AGE
admin 30m
cluster-admin 30m
edit 30m
system:aggregate-to-admin 30m
system:aggregate-to-edit 30m
system:aggregate-to-view 30m
system:auth-delegator 30m
system:basic-user 30m
system:certificates.k8s.io:certificatesigningrequests:nodeclient 30m
system:certificates.k8s.io:certificatesigningrequests:selfnodeclient 30m
system:controller:attachdetach-controller 30m
system:controller:certificate-controller 30m
system:controller:clusterrole-aggregation-controller 30m
system:controller:cronjob-controller 30m
system:controller:daemon-set-controller 30m
system:controller:deployment-controller 30m
system:controller:disruption-controller 30m
system:controller:endpoint-controller 30m
system:controller:expand-controller 30m
system:controller:generic-garbage-collector 30m
system:controller:horizontal-pod-autoscaler 30m
system:controller:job-controller 30m
system:controller:namespace-controller 30m
system:controller:node-controller 30m
system:controller:persistent-volume-binder 30m
system:controller:pod-garbage-collector 30m
system:controller:pv-protection-controller 30m
system:controller:pvc-protection-controller 30m
system:controller:replicaset-controller 30m
system:controller:replication-controller 30m
system:controller:resourcequota-controller 30m
system:controller:route-controller 30m
system:controller:service-account-controller 30m
system:controller:service-controller 30m
system:controller:statefulset-controller 30m
system:controller:ttl-controller 30m
system:csi-external-attacher 30m
system:csi-external-provisioner 30m
system:discovery 30m
system:heapster 30m
system:kube-aggregator 30m
system:kube-apiserver-to-kubelet 3m7s
system:kube-controller-manager 30m
system:kube-dns 30m
system:kube-scheduler 30m
system:kubelet-api-admin 30m
system:node 30m
system:node-bootstrapper 30m
system:node-problem-detector 30m
system:node-proxier 30m
system:persistent-volume-provisioner 30m
system:public-info-viewer 30m
system:volume-scheduler 30m
view 30m
root@k8smaster0:/data/assam/k8s/ca#
1つだけ 3m7s のものがありますね。 簡単に色が変えられればよいんですが…
また、詳細も見ておきます。
# kubectl --kubeconfig admin.kubeconfig get ClusterRole system:kube-apiserver-to-kubelet -o wide
NAME AGE
system:kube-apiserver-to-kubelet 4m9s
yaml 形式でも出力させてみましょう。 入力したもの以外にもあれこれついていますが、
内容は同じです。
# kubectl --kubeconfig admin.kubeconfig get ClusterRole system:kube-apiserver-to-kubelet -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"rbac.authorization.k8s.io/v1beta1","kind":"ClusterRole","metadata":{"annotations":{"rbac.authorization.kubernetes.io/autoupdate":"true"},"labels":{"kubernetes.io/bootstrapping":"rbac-defaults"},"name":"system:kube-apiserver-to-kubelet"},"rules":[{"apiGroups":[""],"resources":["nodes/proxy","nodes/stats","nodes/log","nodes/spec","nodes/metrics"],"verbs":["*"]}]}
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: "2020-05-09T06:14:16Z"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:kube-apiserver-to-kubelet
resourceVersion: "10679"
selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/system%3Akube-apiserver-to-kubelet
uid: 1b15b2f1-b090-4e4f-98a0-0ee833a49b5e
rules:
- apiGroups:
- ""
resources:
- nodes/proxy
- nodes/stats
- nodes/log
- nodes/spec
- nodes/metrics
verbs:
- '*'
同様に、 ClusterRoleBinding も観ておきましょう。
# kubectl --kubeconfig admin.kubeconfig get ClusterRoleBindings system:kube-apiserver
NAME AGE
system:kube-apiserver 4m45s
# kubectl --kubeconfig admin.kubeconfig get ClusterRoleBindings system:kube-apiserver -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"rbac.authorization.k8s.io/v1beta1","kind":"ClusterRoleBinding","metadata":{"annotations":{},"name":"system:kube-apiserver"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"system:kube-apiserver-to-kubelet"},"subjects":[{"apiGroup":"rbac.authorization.k8s.io","kind":"User","name":"kubernetes"}]}
creationTimestamp: "2020-05-09T06:14:47Z"
name: system:kube-apiserver
resourceVersion: "10721"
selfLink: /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/system%3Akube-apiserver
uid: 6f67f771-41b3-4e1d-a356-6e58251dc0e6
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:kube-apiserver-to-kubelet
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: kubernetes
#The Kubernetes Frontend Load Balancer
ここは Google cloud での ロードバランサー設定のため、
今回は利用しません。
そのため、割愛します。
ロードバランサーからアクセスできるように設定できればかっこいいですが…
(一応、外部のサーバーに nginx のロードバランサーを置いてみようかとトライしましたが
SSL で受けて SSL で後ろに流す、ということができないようであきらめました。。。)
#エラー並びに調査・対応方法
さて、前回から恒例になりつつあるエラー対応です。
kubernetes the hard way をやっている人は多数いると思いますが、
みんなエラー無く対応できているんでしょうか。。。??
##1. component status が unknown
本文中でも若干触れましたが、 kubectl get componentstatus
が unknown になる、という事象です。
# kubectl get componentstatuses --kubeconfig admin.kubeconfig
NAME AGE
scheduler <unknown>
controller-manager <unknown>
etcd-0 <unknown>
etcd-2 <unknown>
etcd-1 <unknown>
unknown と言われてもこちらもわかりません、としか言いようがありません。
こういう時はもう少し詳しく聞いてみましょう。
--v=レベル
を指定すると、詳細ログが出ます (下記は、 --v=6
で実施していますが、 4ぐらいからどんどん上げて確認していきました。 なお、 0-4: debug レベル、 5-10: trace レベルだそうです。 9-10 はかなり詳細が出ます。一度ぐらいは眺めてみると理解が深まってよいです。 あ、 curl 使っているのね、とか。)
root@k8smaster01:~# kubectl --v=6 get componentstatuses --kubeconfig admin.kubeconfig
I0504 06:15:06.831312 4248 loader.go:375] Config loaded from file: admin.kubeconfig
I0504 06:15:06.960862 4248 round_trippers.go:443] GET https://127.0.0.1:6443/api/v1/componentstatuses?limit=500 200 OK in 122 milliseconds
I0504 06:15:06.963204 4248 table_printer.go:44] Unable to decode server response into a Table. Falling back to hardcoded types: attempt to decode non-Table object into a v1beta1.Table
I0504 06:15:06.963309 4248 table_printer.go:44] Unable to decode server response into a Table. Falling back to hardcoded types: attempt to decode non-Table object into a v1beta1.Table
I0504 06:15:06.963362 4248 table_printer.go:44] Unable to decode server response into a Table. Falling back to hardcoded types: attempt to decode non-Table object into a v1beta1.Table
NAME AGE
scheduler <unknown>
controller-manager <unknown>
etcd-0 <unknown>
ログの 2行目 GET https:.... となっているところをよく見ると、以下のように出力されています。
GET https://127.0.0.1:6443/api/v1/componentstatuses?limit=500 200 OK in 122 milliseconds
200 OK となっています。
また、--v=8
から見られる Reponse Body を見ると(見づらいですが)
Type:healthy とか、 message:ok などのログが見られます。(上記ログでは表示されていません)
また、 table_printer.go というところには、 "Unable to decode server response into a Table..." という記載もあります。
要は、正常なレスポンスが返ってきているが、テーブル形式に変換できていないのではないか、とあたりがつきます。
そこで、形式を変えて表示させます。
丁寧にみると問題なさそうなことが見て取れますね。
# kubectl get componentstatuses --kubeconfig admin.kubeconfig -o yaml
apiVersion: v1
items:
- apiVersion: v1
conditions:
- message: ok
status: "True"
type: Healthy
kind: ComponentStatus
metadata:
creationTimestamp: null
name: scheduler
selfLink: /api/v1/componentstatuses/scheduler
- apiVersion: v1
conditions:
- message: ok
status: "True"
type: Healthy
kind: ComponentStatus
metadata:
creationTimestamp: null
name: controller-manager
selfLink: /api/v1/componentstatuses/controller-manager
- apiVersion: v1
conditions:
- message: '{"health":"true"}'
status: "True"
type: Healthy
kind: ComponentStatus
metadata:
creationTimestamp: null
name: etcd-0
selfLink: /api/v1/componentstatuses/etcd-0
kind: List
metadata:
resourceVersion: ""
selfLink: ""
その後、改めて Web で調べてみると、下記のような Issue がありました。
kubectl get componentstatus
fails to write as table #83024
https://github.com/kubernetes/kubernetes/issues/83024
v1.17 で修正済みのようです。
##2. kube-apiserver 起動時エラー(その1)
systemctl start kube-apiserver
コマンドで API サーバーを起動した際に
ログに以下のようなエラーが出力されました。
# journalctl -u kube-apiserver
(略)
May 03 07:11:39 k8smaster01 kube-apiserver[3133]: Error: error while parsing encryption provider configuration file "/var/lib/kubernetes/encryption-config.yaml": could not obtain secret for named key key1: illegal base64 data
はい、これはもう encryption-config.yaml が間違っている以外の解はなさそうですね。
この時確認すると、ファイルが以下のようになっていました。。。
# cat encryption-config.yaml
kind: EncryptionConfig
apiVersion: v1
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: ${ENCRYPTION_KEY}
- identity: {}
コピペするときに変数のことを考えなかったようですね。。。
encryption-config.yaml 作成は 第8回で記載しています。
head -c 32 /dev/urandom | base64
の出力文字列を該当の変数部分に修正入力しました。
##3. kube-controller-manager の Leader election 失敗
kube-controller-manager の起動時のエラーです。
正確には、起動はするがログにエラーが出ている、という状態です。
# journalctl -u kube-controller-manager
(略)
May 03 07:46:49 k8smaster01 kube-controller-manager[3622]: E0503 07:46:49.285704 3622 leaderelection.go:330] error retrieving resource lock kube-system/kube-controller-manager: rpc error: code = Unavailable desc = etcdserve
May 03 07:46:51 k8smaster01 kube-controller-manager[3622]: I0503 07:46:51.727629 3622 leaderelection.go:287] failed to renew lease kube-system/kube-controller-manager: failed to tryAcquireOrRenew context deadline exceeded
May 03 07:46:51 k8smaster01 kube-controller-manager[3622]: F0503 07:46:51.727722 3622 controllermanager.go:279] leaderelection lost
May 03 07:46:51 k8smaster01 systemd[1]: kube-controller-manager.service: Main process exited, code=exited, status=255/EXCEPTION
May 03 07:46:51 k8smaster01 systemd[1]: kube-controller-manager.service: Failed with result 'exit-code'.
May 03 07:46:56 k8smaster01 systemd[1]: kube-controller-manager.service: Service RestartSec=5s expired, scheduling restart.
May 03 07:46:56 k8smaster01 systemd[1]: kube-controller-manager.service: Scheduled restart job, restart counter is at 1.
May 03 07:46:56 k8smaster01 systemd[1]: Stopped Kubernetes Controller Manager.
May 03 07:46:56 k8smaster01 systemd[1]: Started Kubernetes Controller Manager.
以下のようなことが見受けられます。
- ログ1行目: etcdserve が Unavailable である
- ログ2行目: failed to tryAcquireOrRenew context deadline exceeded
- ログ3行目: leaderelection lost
- ログ4行目: Exit Code 255 で異常終了
etcd からの応答が返ってこずに、 Leader Election が失敗し、 kube-controller-manager 自体が異常終了しているようです。
etcd 自体は稼働していたのですが、 第9回で少しふれたとおり、 CPU リソースが wait 100% になっており
体感的にも以上に遅く、Disk I/O によるタイムアウトだと判断しました。
対応としては、 一旦 etcd をシングル構成に格下げしたのですが、最終的には SSD を購入して再度最初からやり直しました。
etcd の Disk I/O は重要です。
※ etcd を 1台構成に変更する方法概要
- etcd 停止
- /var/lib/etcd/member ディレクトリ削除 (状態を保持しているようです)
- etcd.services ファイル修正 (#2, #3 エントリーの削除)
- etcd サービスの無効化( #2, #3 のみ )
- etcd 起動(#1 のみ)
※ kube-apiserver.service ファイルから、 etcd #2, #3 のエントリーを削除
なお、 control plane の各種コンポーネントのログについては
一度ぐらいは丁寧に見ておくことをお勧めいたします。
いろんな設定がデフォルトでされているんだな、ということがよくわかります。
今回はここまでとして、次回は Bootstrapping the Kubernetes Worker Nodes の部分を実施します。
← 9.etcd
↑ 目次
→ 11.Worker Nodes