Kubernetes The Hard Way をやって知ったこと
巷ではケーサンショー、もとい k3s が盛り上がっていることとは存じますが、初級修行僧の身として今更ながら Kubernetes The Hard Way をやりました。知らなかったところと調べたことをメモしておきます。
- 実施した commit
- bf2850974e19c118d04fdc0809ce2ae8a0026a27
- 注記
- 気になった点や調べたこと、感想はこの色で書いてます
01~03
真顔でコピペ。
04 Provisioning a CA and Generating TLS Certificates
- 各コンポーネントがそれぞれに (主に API Server との) 通信を行う際に使用する認証を生成している。
- Kubernetes admin user
- Node (Kubelet)
- kube-controller-manager
- kube-proxy
- kube-scheduler
- kube-apiserver
- Service Account
- GCPの Service Account ではない
- Kubernetes では Node の Kubelet から生成される API リクエストの認証を行うのに Node Authorizer という認証方式を使う
- Using Node Authorization - Kubernetes
- Kubelet が Node Authorizer に認証されるためには
system:nodes:NodeName
の形式で自身を識別させる認証情報を持つ必要がある
- 各コンポーネントの中で中心的にリクエストを受け付ける API Server の Certificate には、そのIPアドレス (
kubernetes-the-hard-way
という名前で取得した外部 IP アドレス) を含めることで remote client との間で認証が可能になる - Kubernetes Controller Manager は鍵ペアを使用して Service Account tokens を生成/認証する
- 参考の要約
- Service Account は クラスタ外部で管理される通常のユーザとは異なる Kubernetes 管理下のユーザであり Pod が kube-apiserver と通信を行うための認証に用いられる
- Service Account の情報は Secret として Pod 起動時にマウントされている (
/var/run/secrets/kubernetes.io/serviceaccount
配下)
05 Generating Kubernetes Configuration Files for Authentication
- 04章で作成した認証情報を使って Kubernetes clients (各コンポーネント) が kube-apiserver を認識し認証されるための kubeconfig を生成する
- API Server の IP アドレスは冗長性のため通常ロードバランサに付与して負荷分散するのがベター
- Kubelet の kubeconfig を生成する際には、 Kubelet が動作する Node の名前
system:nodes:NodeName
にマッチする Certificate を使う必要がある。これにより Node Authorizer による認証が行われる
06 Generating the Data Encryption Config and Key
- Kubernetes は cluster state, application config, secrets といったデータを暗号化する仕組みを持つ
- 暗号鍵と Secret を暗号化するための
kind: EncryptionConfig
の manifest を生成し Controller Node にばら撒く
07 Bootstrapping the etcd Cluster
- Kubernetes のコンポーネントはステートレスであり、 ステートに関する情報は etcd に格納される
- etcd サーバはインスタンスの内部IPアドレス (03章で作成した kubernetes-the-hard-way VPC の kubernetes subnet 範囲内のIPアドレス) を使ってクライアントからのリクエストを受け付けたり、 etcd クラスタ内の他の peer とコミュニケーションを行う
- etcd は API Version を指定しないと v2 で起動する
etcdctl -h
で表示される実行可能なサブコマンド一覧も v2 と v3 で異なる- 今回のように認証情報を渡して起動すると、それなしではクラスタに関する情報を閲覧することができない
controller-2:~$ sudo ETCDCTL_API=3 etcdctl member list --endpoints=https://127.0.0.1:2379 --cacert=/etc/etcd/ca.pem --cert=/etc/etcd/kubernetes.pem --key=/etc/etcd/kubernetes-key.pem 3a57933972cb5131, started, controller-2, https://10.240.0.12:2380, https://10.240.0.12:2379 f98dc20bce6225a0, started, controller-0, https://10.240.0.10:2380, https://10.240.0.10:2379 ffed16798470cab5, started, controller-1, https://10.240.0.11:2380, https://10.240.0.11:2379 controller-2:~$ sudo ETCDCTL_API=3 etcdctl member list Error: context deadline exceeded
08 Bootstrapping the Kubernetes Control Plane
- kube-apiserver, kube-controller-manager, kube-scheduler を Systemd で Service 化して起動する
- ちなみに kubeadm は上記コンポーネントを Pod として動かしているらしい。他には GKE とかはどうだろうか。
- kube-scheduler には
kind: KubeSchedulerConfiguration
の manifest を引数に与える - オプションより
--cluster-cidr
は kube-controller-manager で指定--service-cluster-ip-range
は kube-apiserver, kube-controller-manager の両方で指定--leader-elect=true
は kube-controller-manager でのみ指定- Kubernetes完全ガイドの第19章を見ると kube-scheduler も leader 制をとることができるという
- /healthz エンドポイントはデフォルトでは認証不要になっている
kind: ClusterRole
の manifest を作製してよく使う一般的な Kubelet API を定義し、kind: ClusterRoleBinding
manifest でkubernetes
ユーザに紐づけを行う
09 Bootstrapping the Kubernetes Worker Nodes
- Worker に必要なコンポーネントをインストールする
- runc
- 低レイヤコンテナランタイム
- 高レイヤコンテナランタイムである containerd から OCI 経由で命令を受け取りコンテナを作成する
- gVisor
- runc と同じく低レイヤコンテナランタイム
- 比較的セキュアなランタイム
- container networking plugins
- CNI プラグイン
- コンテナのネットワークインターフェースに関する仕様とライブラリから成る
- containerd
- 高レイヤコンテナランタイム
- Kubelet から CRI という Unix Socket 経由の gRPC API で命令を受けコンテナ作成に必要な環境を整備し、低レイヤランタイムに命令を送る
- ランタイムに関しては非常に分かりやすいこちらの資料を参照した 今話題のいろいろなコンテナランタイムを比較してみた [Docker Meetup Tokyo #26発表レポート]
- kubelet
- kube-proxy
- runc
- cni の設定では type として
bridge
を指定し、03章で定義したpod-cidr
を記述しているtype: bridge
は cni にデフォルトで備わる プラグインの一つ 。type は他にflannel
等サードパーティのプラグイン名を指定するtype: loopback
が必要な理由は Kubernetes CNI loopback by によると以下の通り
The loopback CNI is automatically used by Kubernetes to do low-level networking (like for the Kubelet to talk to the containers local to the node) and Kubernetes will fail if it's not installed or running,
- containerd の設定では低レイヤコンテナランタイムである runc 及び runsc (gVisor) を指定
- kubelet の設定では
kind: KubeletConfiguration
を作成、その中でclusterDNS
、podCIDR
、認証情報などを指定 - kubelet の unit ファイルを作成。
--container-runtime
で指定可能な値はdocker, remote, rkt(deprecated)
であり、 containerd を使う場合にはremote
を設定の後、--container-runtime-endpoint
で使用する Unix Socket を指定する
- kube-proxy の設定では
kind: KubeProxyConfiguration
を作成、その中で動作モードをiptables
と指定。
10 Configuring kubectl for Remote Access
- 前半で作製した admin ユーザで kubectl を実行できるようにする設定する
- ロードバランスされている API Server のIPアドレスと admin ユーザの認証情報を使用する
- これを全て手で設定することを思うと
gcloud container clusters get-credentials
は偉大である
11 Provisioning Pod Network Routes
- Pod が Node にスケジューリングされると Pod は Node の Pod Cidr からIPアドレスを受け取って設定するが、09章で見たように Pod Cidr は Node 毎に異なるため、通常であれば別 Node にある Pod とは通信することができない
- そこで今回はそれを GCP のルートを用いて Pod Cidr を Node の Internal IP アドレスに解決する経路を生成する
- この lab では敢えて原始的な方法を用いているが Calico や Flannel 等の CNI プラグインを用いる方法が一般的ですよね
12 Deploying the DNS Cluster Add-on
- サービスディスカバリのための DNS として CoreDNS を Add-on 的にデプロイする
- 作成されている Service に対して名前解決ができることを確認する
- が、やけに遅い
$ time 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 kubectl exec -ti $POD_NAME -- nslookup kubernetes 0.09s user 0.03s system 0% cpu 45.347 total
- Service に対して問い合わせる場合と Endpoint に問い合わせる場合で差があるようだ。 Service に問い合わせをする場合、実際の DNS 応答は Endpoint のIPアドレスから返ってくるため、そこらへんが nslookup の機嫌を損ねているのではと推測。
## dns の Service と Endpoint を確認しておく $ kubectl -n kube-system get svc kube-dns NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.32.0.10 <none> 53/UDP,53/TCP 12d $ kubectl -n kube-system describe ep kube-dns (snip) Subsets: Addresses: 10.200.0.9,10.200.2.7 NotReadyAddresses: <none> Ports: Name Port Protocol ---- ---- -------- dns 53 UDP dns-tcp 53 TCP $ kubectl exec -it $POD_NAME /bin/sh ## 結果が得られることに変わりはないが Service を介した場合だけやけに遅い / # time nslookup kubernetes 10.32.0.10 1> /dev/null real 0m 10.01s user 0m 0.00s sys 0m 0.00s / # time nslookup kubernetes 10.200.0.9 1> /dev/null real 0m 0.00s user 0m 0.00s sys 0m 0.00s / # time nslookup kubernetes 10.200.2.7 1> /dev/null real 0m 0.00s user 0m 0.00s sys 0m 0.00s
12 Smoke Test
crictl
コマンドを使用して CRI の Unix Socket 経由で実際に動いているコンテナの一覧を取得可能
root@worker-0:~# sudo crictl -r unix:///var/run/containerd/containerd.sock ps CONTAINER ID IMAGE CREATED STATE NAME ATTEMPT POD ID c67595b3b33c2 8c811b4aec35f 32 minutes ago Running busybox 24 a58da8259ee26 a15e2d786eb05 367cdc8433a45 About an hour ago Running coredns 0 c3a0d3c772526 66f1bbe5ed396 881bd08c0b082 5 hours ago Running nginx 2 e8a5bbdb3733a
まとめ
Hard とは名前だけで優しさに溢れた素晴らしい教材でした。またやろう。