Raspberry PiでKubernetes The Hard Way(v1.21.0)をやる(失敗)
はじめに
最近、Raspberry Piを買ってKubeadmを使ってKubernetesのクラスタを構築してみたので今度は、「Kubernetes The Hard Way」をやってみようと思います。
Kubernetes The Hard Way自体は、VirutalBoxを使ってやったみたことはあったのですが、その当時はほぼ手順をなぞっただけで終わってしまったので今回はきちんと理解を整理しながらやってみようかと思います。
....と思ったのですが、最終的には失敗してしまいました。
クラスターのネットワークの設定にflannelを使ったのですが、Podのネットワークにサブネットが割当されず。
クラスター側のサブネットとかぶってないかとか諸々確認したのですが、うまく行かず。一旦は断念しました。
とはいえせっかくまとめたメモとかを、そのまま消してしまうのももったいないので、最後に雑にまとめて投稿だけしようと思います。
ちょっと別の理由で環境を壊してしまった都合上、問題の分析なども行えていない(本当に、どういうログが出てて困ったのかすらも載せてない)中途半端なブログになっているので、もし何かを期待してみられる方がいるのであればおそらくこのブログは期待に添えるものではありません。
やってみる
環境構築
クライアントマシン
Kubernetesのクラスタはラズパイ上に構築しますが、それぞれのラズパイにsshしたり必要なリソースを作成したりするためにThinkPadのPCを用います。
環境は以下のとおりです。
$ uname -srvmpio Linux 5.4.0-72-generic #80-Ubuntu SMP Mon Apr 12 17:35:00 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a LSB Version: core-11.1.0ubuntu2-noarch:security-11.1.0ubuntu2-noarch Distributor ID: Ubuntu Description: Ubuntu 20.04.2 LTS Release: 20.04 Codename: focal
Raspberry Piともろもろ
ラズベリーパイ含め今回は以下のものを使って物理的な環境を構築します。
もともと家にあったのもありますが、PC以外で全部で3万円強ぐらい だったと思います。
Aamazonのリンクを貼っているものは実際に自分が購入したものです。
名前 | 説明 | 個数 |
---|---|---|
Raspberry Pi 3 Model B+ | 1GのRAMを持つラズパイです。秋葉原でリフレッシュ品を2つ適当に購入しました。こちらはサブNodeとして使おうと思います。 | 2 |
Raspberry Pi 4 ModelB 4GB | 4GのRAMを持つラズパイです。Amazonで購入しました。こちらはMasterのNodeとして使おうと思います。 | 1 |
Micro SD 32G | ラズパイのディスクとして使用します。Aamazonで購入しました。 | 3 |
ルータ親機 | ラズパイをWiFiに接続したかったので、Aamazonで購入しました。 | 1 |
ロジテック スイッチングハブ 5ポート | ラズパイでネットワークを構築するためにAamazonで買いました。 | 1 |
AUKEY USB充電器 50W/10A ACアダプター 5ポート | ラズパイと諸々の電源として使います。 | 1 |
Raspberry Pi 4 ケース | 4とありますが、3でも使えます。4段構成で、1つの段は電源を置くために使います。 | |
Micro-USBケーブル | Raspberry Pi 3、スイッチングパブ、ルータ親機の電源コードとして使います。配線が邪魔にならないように短めのものを使います。 | 4 |
USB type C | Raspberry Pi 4の電源コードとして使います。配線が邪魔にならないように短めのものを使います | 1 |
LANケーブル | ラズパイとハブ、ハブとルータ親機をそれぞれつなぎます。 | 4 |
HDMIケーブル | ディスプレイとラズパイをつなぎます。 | 1 |
Micro HDMI変換コネクタ | Raspberry Pi 4を画面につなぐために使いました | 1 |
ThinkPad T490 | もともと持ってたPCです。Micro SDにOSのイメージを焼いたり、sshしてラズパイに諸々インストールしたりするのに使います。 | 1 |
ディスプレイ | もともと持っていたものを使います | 1 |
配線する
詳細に説明はしませんが、最終的には以下のように配線します。
ラズパイ⇨電源、スイッチングハブ スイッチングハブ⇨電源、ルータ親機 ルータ親機⇨電源 ThinkPad⇨スイッチングハブ
また、ルータ親機に関しては説明書を読みつつ自宅の無線LANに接続できるようにしておきます。
画像では電源もすでに入ってますが、この段階では大本の電源のケーブルを抜いておき、まだ電源を入れないようにしておきます。
Micro SDカードをラズパイに入れてから電源を指します。
Micro SDにUbuntuをインストールする
配線ができたら次にMicro SDにOSのイメージを焼いていきます。
今回はOSはUbuntuを使いたいと思います。Raspberry Pi 3には「Ubuntu Server 18.04.5 Arm64」のイメージ、Raspberry Pi 4には「Ubuntu Server 20.04.2 Arm64」を利用します。
それぞれ以下のページからダウンロードしてきます。
ダウンロードしてきたイメージをSDカードに焼きます。Micro SDの場合は/dev/mmcblk0
にイメージを焼けば良さそうです。
以下のコマンドをそれぞれのSDカードをPCの組み込みSDカードドライブに差し込んでから実行します。
$ xzcat ubuntu-18.04.5-preinstalled-server-arm64+raspi3.img.xz | sudo dd bs=4M of=/dev/mmcblk0 0+318958 レコード入力 0+318958 レコード出力 2653289472 bytes (2.7 GB, 2.5 GiB) copied, 29.1715 s, 91.0 MB/s
Raspberry Pi 4に利用するSDカードにはubuntu-20.04.2-preinstalled-server-arm64+raspi.img.xz
という名前のイメージがダウンロードされると思うので、同じコマンドを実行してください。
3枚のMicro SDカードにイメージを焼き終わったら、ラズパイに差し込んで電源を入れます。
また、ディスプレイと
電源を入れるとUbuntuのインストールがはじまります。
しばらく待つと初期ユーザネーム/パスワードを聞かれるので、ubuntu/ubuntu
と入力してください。
初回のパスワード変更では任意のものを入力してください。
同様の作業をすべのSDカードで行ってください「Ubuntu20+Raspberry Pi 4」でも同様のセットアップを行ってください。
Ubuntuが起動したら、この後、諸々わかりやすくするためにホスト名を変更します。
それぞれのラズパイで/etc/hostname
を編集してホスト名を以下のように変更し、reboot
コマンドで再起動します。
ホスト名 | ラズパイ |
---|---|
leader-01 | Raspberry Pi 4 |
worker-01 | Raspberry Pi 3 |
worker-02 | Raspberry Pi 3 |
今後の作業はsshして行なうため、hostname -I
コマンド等を使って、それぞれのIPも調べておきます。
僕の環境では以下のような対応付になりました。
ホスト名 | ip |
---|---|
leader-01 | 192.168.13.5 |
worker-01 | 192.168.13.2 |
worker-02 | 192.168.13.3 |
これで、基本のところのセットアップは完了です。
クライアントツールのインストール
ThinkPadの方でクライアントツールをインストールしておきます。
インストールするのは以下のツールです。
kubectlのインストール
まずはkubectlをインストールします。
以下のコマンドを実行してください。
$ wget https://storage.googleapis.com/kubernetes-release/release/v1.21.0/bin/linux/amd64/kubectl $ chmod +x kubectl $ sudo mv kubectl /usr/local/bin/
これで、インストールはOKです。
ちなみに諸事上で、僕の環境ではkubectlはv1.20.5
のものを使います。
$ kubectl version --client Client Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.5", GitCommit:"6b1d87acf3c8253c123756b9e61dac642678305f", GitTreeState:"clean", BuildDate:"2021-03-18T01:10:43Z", GoVersion:"go1.15.8", Compiler:"gc", Platform:"linux/amd64"}
ドキュメントによるとkubectlは2つのバージョンがサポートされるようです。
cfssl、cfssljsonのインストール
cfsslとcfljsonはPKI(公開鍵暗号基盤)を構築するために利用します。
$ wget -q --show-progress --https-only --timestamping \ https://storage.googleapis.com/kubernetes-the-hard-way/cfssl/1.4.1/linux/cfssl \ https://storage.googleapis.com/kubernetes-the-hard-way/cfssl/1.4.1/linux/cfssljson $ chmod +x cfssl cfssljson
正しくインストールができていれば以下のコマンドでそれぞれバージョンが表示されるはずです。
$ cfssl version Version: 1.4.1 Runtime: go1.12.12 $ cfssljson --version Version: 1.4.1 Runtime: go1.12.12
これでクライアントツールのインストールは完了です。
CAのプロビジョニングとTLS証明書の発行
etcd、kube-apiserver、kubelet、kube-proxyのコンポーネント間の通信で利用されるTSL証明書の作成などを行います。
CAのプロビジョンを行なうために、CAの設定ファイルを作成します。
cat > ca-config.json <<EOF { "signing": { "default": { "expiry": "8760h" }, "profiles": { "kubernetes": { "usages": ["signing", "key encipherment", "server auth", "client auth"], "expiry": "8760h" } } } } EOF
次に、CAのための証明書署名リクエストのJsonを作成します。
cat > ca-csr.json <<EOF { "CN": "Kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "Kubernetes", "OU": "CA", "ST": "Oregon" } ] } EOF
そして、CSの証明書と秘密鍵を作成します。
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca
すると以下のファイルが作成されているのを確認します。
- ca-key.pem
- ca.pem
クライアントとサーバの証明書発行
Kubernetesの各コンポーネントとクライアント(adminユーザ)のための証明書を発行します。
Admin クライアントの証明書の発行
Adminクライアントのための証明書署名リクエストのJsonを作成します。
cat > admin-csr.json <<EOF { "CN": "admin", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:masters", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF
cfssl gencert
コマンドを用いて、Adminクライアントの秘密鍵と証明書を発行します。
$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ admin-csr.json | cfssljson -bare admin
その際に前に使っているCAの証明書および秘密鍵を利用します。
実行すると以下のファイルが作成されているのを確認します。
- admin-key.pem
- admin.pem
kubeletの証明書の発行
Kubernetesはkubeletからのリクエストの認可を行なうためにNode Authorizerと呼ばれるような特別な認可モードを利用します(詳細はこちら)。
werkerノードがNode Authriozerのリクエストを発行するための証明書を作成します。
今回はwerkerノードが2つなので以下のコマンドで証明書を発行します。
$ cat > worker-01-csr.json <<EOF { "CN": "system:node:worker-01", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:nodes", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=worker-01,192.168.13.2 \ -profile=kubernetes \ worker-01-csr.json | cfssljson -bare worker-01
ここで、Node Authriozerでリクエストを送るためには、system:node:worker-01
のworker-01
の部分はノードのホスト名にする必要があります。
同様のことをworker-02
でも行います。worker-01
の部分を単純置換してcfssl genecert
コマンドの-hostname
オプションに渡すIPは 192.168.13.3
になります(手元の環境でそれぞれのIPは確認してください)。
以下の4つのファイルができると思います。
- worker-01-key.pem
- worker-01.pem
- worker-02-key.pem
- worker-02.pem
Controller Managerの証明書の発行
kube-controller-managerにも証明書を発行します。
$ cat > kube-controller-manager-csr.json <<EOF { "CN": "system:kube-controller-manager", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:kube-controller-manager", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager
kube-proxyの証明書の発行
今までと同様の方法でkube-proxyにも証明書を発行します。
$ cat > kube-proxy-csr.json <<EOF { "CN": "system:kube-proxy", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:node-proxier", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ kube-proxy-csr.json | cfssljson -bare kube-proxy
以下のファイルが作成されます。
- kube-proxy-key.pem
- kube-proxy.pem
Scheduler Clientの証明書の発行
Schedulerの証明書を発行します。
$ cat > kube-scheduler-csr.json <<EOF { "CN": "system:kube-scheduler", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:kube-scheduler", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ kube-scheduler-csr.json | cfssljson -bare kube-scheduler
以下のファイルが作成されます。
- kube-scheduler-key.pem
- kube-scheduler.pem
Kubernetes APIサーバの証明書の発行
APIサーバの証明書を発行します。 APIサーバはleader-01にデプロイするため、IPなどは、そちらのものを利用します。
$ cat > kubernetes-csr.json <<EOF { "CN": "kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "Kubernetes", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ export KUBERNETES_PUBLIC_ADDRESS=192.168.13.5 $ export KUBERNETES_HOSTNAMES=kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster,kubernetes.svc.cluster.local $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=10.32.0.1,10.240.0.10,10.240.0.11,10.240.0.12,${KUBERNETES_PUBLIC_ADDRESS},127.0.0.1,${KUBERNETES_HOSTNAMES} \ -profile=kubernetes \ kubernetes-csr.json | cfssljson -bare kubernetes
次のファイルが作成されると思います。
- kubernetes-key.pem
- kubernetes.pem
Service Accountの証明書を発行します。
Service Accountの証明書を発行します。
$ cat > service-account-csr.json <<EOF { "CN": "service-accounts", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "Kubernetes", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ service-account-csr.json | cfssljson -bare service-account }
作成した証明書のNodeへの配置
作成した証明書を各ノードに配置しておきます。
$ scp ca.pem ca-key.pem kubernetes.pem kubernetes-key.pem ubuntu@192.168.13.5:~/ $ scp ca.pem ca-key.pem worker-01-key.pem worker-01.pem ubuntu@192.168.13.2:~/ $ scp ca.pem ca-key.pem worker-02-key.pem worker-02.pem ubuntu@192.168.13.3:~/
認証のためのkubeconfigsの作成
KubernetesクライアントがKubernetes API Serverを発見し、認証を行なうためのkubeconfigsファイルを作成する必要があります。
kubeconfigはkubelet
、kube-proxy
、scheduler
、adminユーザ
、controller manager
用に作成します。
Kubeletのkubeconfigファイルを作成する
以下のコマンドでそれぞれのworker Nodeで動くkubeletのためのconfigファイルを作成します。
kubeletが正しく認可されるためにはkubeconfigファイルの作成にはNodeのホスト名を利用する必要があります。
また、KUBERNETES_PUBLIC_ADDRESS
にはAPIサーバが動くノードのIPをセットしておきます(今回は前の工程ですでにexportしているのでそのまま利用します)。
$ for instance in worker-01 worker-02 ; do kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \ --kubeconfig=${instance}.kubeconfig kubectl config set-credentials system:node:${instance} \ --client-certificate=${instance}.pem \ --client-key=${instance}-key.pem \ --embed-certs=true \ --kubeconfig=${instance}.kubeconfig kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:node:${instance} \ --kubeconfig=${instance}.kubeconfig kubectl config use-context default --kubeconfig=${instance}.kubeconfig done
以下のファイルが作成されたと思います。
- worker-01.kubeconfig
- worker-02.kubeconfig
kube-proxyのkubeconfigファイルを作成する
次はkube-proxyのファイルを作成します。
以下のコマンドを実行します。
$ kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://192.168.13.5:6443 \ --kubeconfig=kube-proxy.kubeconfig $ kubectl config set-credentials system:kube-proxy \ --client-certificate=kube-proxy.pem \ --client-key=kube-proxy-key.pem \ --embed-certs=true \ --kubeconfig=kube-proxy.kubeconfig $ kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:kube-proxy \ --kubeconfig=kube-proxy.kubeconfig $ kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
以下のファイルが作成されたと思います。
- kube-proxy.kubeconfig
kube-controller-managerのkubeconfigファイルを作成する
次はkube-controller-managerのファイルを作成します。
以下のコマンドを実行します。
$ kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://127.0.0.1:6443 \ --kubeconfig=kube-controller-manager.kubeconfig $ kubectl config set-credentials system:kube-controller-manager \ --client-certificate=kube-controller-manager.pem \ --client-key=kube-controller-manager-key.pem \ --embed-certs=true \ --kubeconfig=kube-controller-manager.kubeconfig $ kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:kube-controller-manager \ --kubeconfig=kube-controller-manager.kubeconfig $ kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig
以下のファイルが作成されたと思います。
- kube-controller-manager.kubeconfig
kube-scheduler.kubeconfigのkubeconfigファイルを作成する
次はkube-schedulerのファイルを作成します。
以下のコマンドを実行します。
$ kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://192.168.13.5:6443 \ --kubeconfig=kube-scheduler.kubeconfig $ kubectl config set-credentials system:kube-scheduler \ --client-certificate=kube-scheduler.pem \ --client-key=kube-scheduler-key.pem \ --embed-certs=true \ --kubeconfig=kube-scheduler.kubeconfig $ kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:kube-scheduler \ --kubeconfig=kube-scheduler.kubeconfig $ kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig
以下のファイルが作成されました。
- kube-scheduler.kubeconfig
adminのkubeconfigファイルを作成する
次はadminのファイルを作成します。
以下のコマンドを実行します。
$ kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://127.0.0.1:6443 \ --kubeconfig=admin.kubeconfig $ kubectl config set-credentials admin \ --client-certificate=admin.pem \ --client-key=admin-key.pem \ --embed-certs=true \ --kubeconfig=admin.kubeconfig $ kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=admin \ --kubeconfig=admin.kubeconfig $ kubectl config use-context default --kubeconfig=admin.kubeconfig
作成したkubeconfigのNodeへの配置
以下のコマンドを実行して、Nodeへkubeconfigへ配置します。
$ scp admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig ubuntu@192.168.13.5:~/ $ scp worker-01.kubeconfig kube-proxy.kubeconfig ubuntu@192.168.13.2:~/ $ scp worker-02.kubeconfig kube-proxy.kubeconfig ubuntu@192.168.13.3:~/
データ暗号化の設定と鍵を作成する
Kubernetesはクラスターの状態、アプリケーションコンフィグ、Sercretとなどの様々なデータをストアしています。
Kubernetesはクラスターのデータを利用されていないときに暗号化して保持する機能を持っています。
ここでは、暗号化の設定と鍵を作成します。
まずは暗号化するための鍵を作成します。
$ ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
次に設定ファイルを作成ます。
$ cat > encryption-config.yaml <<EOF kind: EncryptionConfig apiVersion: v1 resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: ${ENCRYPTION_KEY} - identity: {} EOF
作ったencryption-config.yaml
をleaderのNodeに配置します。
$ scp encryption-config.yaml ubuntu@192.168.13.5:~/
Bootstrapping the etcdの起動
諸々の設定ファルの作成とその配置ができたので、コンポーネントを起動していきます。
まずは、etcdからです。
etcdは分散型のkey-valueストアでKubernetesのすべてのクラスターの情報の保存場所として利用されています。
通常はクラスターを組むと思いますが、今回はleaderのNodeは1つのためひとつだけ起動します。
leaderのNodeにsshします。
$ ssh ubuntu@192.168.13.5
ログインに成功したらセットアップを行っていきます。
まずは、etcdのバイナリをダウンロードします。
$ wget -q --show-progress --https-only --timestamping \ "https://github.com/etcd-io/etcd/releases/download/v3.4.15/etcd-v3.4.15-linux-arm64.tar.gz"
cpuアーキテクチャはarm64を選択するように注意しましょう。
ダウンロードしたバイナリを回答して、/usr/local/bin
に配置します。
$ tar -xvf etcd-v3.4.15-linux-arm64.tar.gz $ sudo mv etcd-v3.4.15-linux-arm64/etcd* /usr/local/bin/
etcdサーバのセットアップを行います。
$ sudo mkdir -p /etc/etcd /var/lib/etcd $ sudo chmod 700 /var/lib/etcd $ sudo cp ca.pem kubernetes-key.pem kubernetes.pem /etc/etcd/
systemdで管理するためにユニットファイルを作成します。
以下のコマンドを実行します。
cat <<EOF | sudo tee /etc/systemd/system/etcd.service [Unit] Description=etcd Documentation=https://github.com/coreos [Service] Type=notify ExecStart=/usr/local/bin/etcd \\ --name leader-01 \\ --cert-file=/etc/etcd/kubernetes.pem \\ --key-file=/etc/etcd/kubernetes-key.pem \\ --peer-cert-file=/etc/etcd/kubernetes.pem \\ --peer-key-file=/etc/etcd/kubernetes-key.pem \\ --trusted-ca-file=/etc/etcd/ca.pem \\ --peer-trusted-ca-file=/etc/etcd/ca.pem \\ --peer-client-cert-auth \\ --client-cert-auth \\ --initial-advertise-peer-urls https://192.168.13.5:2380 \\ --listen-peer-urls https://192.168.13.5:2380 \\ --listen-client-urls https://192.168.13.5:2379,https://127.0.0.1:2379 \\ --advertise-client-urls https://192.168.13.5:2379 \\ --initial-cluster-token etcd-cluster-0 \\ --initial-cluster leader-01=https://192.168.13.5:2380 \\ --initial-cluster-state new \\ --data-dir=/var/lib/etcd Restart=on-failure RestartSec=5 Environment="ETCD_UNSUPPORTED_ARCH=arm64" [Install] WantedBy=multi-user.target EOF
systemdでetcdを起動します。
$ sudo systemctl daemon-reload $ sudo systemctl enable etcd $ sudo systemctl start etcd
起動に成功していれば以下のコマンドでリストが取得できると思います。
$ 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 6ceeb31f89f1fb0d, started, leader-01, https://192.168.13.5:2380, https://192.168.13.5:2379, false
Kubernetes Control Planeの起動
Control Planeを起動しいきます。
Control Planeとは
- kube-apiserver
- KubernetesのAPIを外部に提供するコンポーネントです。Control Planeでのフロントエンドの役割を持っており、クライアントからのアクセスもコンポーネント間の通信も一度このAPI Serverrを経由するようになっています。
- kube-scheduler
- Podに対してNodeが割り当てられているか監視し、当てられていなかった場合はNodeの割当を行います。
- kube-controller-manager
コントロールプレーンの各コンポーネントのより詳細の情報に関してはこちらを確認ください。
バイナリダウンロードと配置
それではインストールしていきます。
etcd同様この作業もleaderのNodeで行います。
まずは、kubernetesの設定を配置するディレクトリを作成します。
$ sudo mkdir -p /etc/kubernetes/config
次にそれぞれのバイナリソースを取得します。
Control Planeとkubectlもインストールします。
wget -q --show-progress --https-only --timestamping \ "https://storage.googleapis.com/kubernetes-release/release/v1.21.0/bin/linux/arm64/kube-apiserver" \ "https://storage.googleapis.com/kubernetes-release/release/v1.21.0/bin/linux/arm64/kube-controller-manager" \ "https://storage.googleapis.com/kubernetes-release/release/v1.21.0/bin/linux/arm64/kube-scheduler" \ "https://storage.googleapis.com/kubernetes-release/release/v1.21.0/bin/linux/arm64/kubectl"
ダウンロードしたバイナリに実行権限を与え、適当なところに配置します。
$ chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl $ sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/
Kubernetes API Serverの設定を行なう
API Serverを設定していきます。
まずは証明書を配置するディレクトリを作成して、証明書をいどうさせます。
$ sudo mkdir -p /var/lib/kubernetes/ $ sudo mv ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \ service-account-key.pem service-account.pem \ encryption-config.yaml /var/lib/kubernetes/
API Serverをsystemdで起動したいので、ユニットファイルを作成します。
cat <<EOF | sudo 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=192.168.13.5 \\ --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://127.0.0.1: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 \\ --runtime-config='api/all=true' \\ --service-account-key-file=/var/lib/kubernetes/service-account.pem \\ --service-account-signing-key-file=/var/lib/kubernetes/service-account-key.pem \\ --service-account-issuer=https://192.168.13.5:6443 \\ --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 EOF
Kubernetes Controller Managerの設定を行なう
Controller Managerの設定を行っていきます。
kube-controller-manager.kubeconfig
を/var/lib/kubernetes
に配置します。
sudo mv kube-controller-manager.kubeconfig /var/lib/kubernetes/
Controller Managerのユニットファイルを作成します。
$ 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 \\ --bind-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
Kubernetes Schedulerの設定を行なう
Schedulerの設定を行っていきます。
kube-scheduler.kubeconfig
を/var/lib/kubernetes
に配置します。
$ sudo mv kube-scheduler.kubeconfig /var/lib/kubernetes/
次に、kube-scheduler.yaml
を作成します。
$ sudo mkdir -p /etc/kubernetes/config/ $ cat <<EOF | sudo tee /etc/kubernetes/config/kube-scheduler.yaml apiVersion: kubescheduler.config.k8s.io/v1beta1 kind: KubeSchedulerConfiguration clientConnection: kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig" leaderElection: leaderElect: true EOF
systemdのユニットファイルを作成します。
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
各Control Planeの起動
各コンポーネントのユニットファイルは作成したので、読み込んで起動します。
$ sudo systemctl daemon-reload $ sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler $ sudo systemctl start kube-apiserver kube-controller-manager kube-scheduler
起動が正常にできているか以下のコマンドで確認します。
$ kubectl cluster-info --kubeconfig admin.kubeconfig Kubernetes control plane is running at https://127.0.0.1:6443
Kubernetes control plane is running
というのが出ていればOKっぽいです。
KubeletへのRBACの設定を行なう
API Serverが各Workerで動くKubeletへのアクセスをするためのRBACの設定を行います。
API Serverはメトリクスの収集、ログの取得、Pod内でのコマンド実行などのためにkubeletにアクセスします。
以下のコマンドを実行して、system:kube-apiserver-to-kubelet
ClusterRoleを作成します。
$ cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f - apiVersion: rbac.authorization.k8s.io/v1 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
API ServerはKubuletにkubernetes
ユーザとして認証します。なので、先程作ったsystem:kube-apiserver-to-kubelet ClusterRole
をkubernetes
ユーザにバインドします。
cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f - apiVersion: rbac.authorization.k8s.io/v1 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
Worker Nodeの起動
2つのworker Nodeを起動します。
Worker Nodeでは以下のようなソフトウェアが起動します。
- runc
- OCI Specificationに沿って、コンテナの生成、実行を行なうCLIツール
- container networking plugins
- containerd
- 業界標準のコンテナランタイム
- kubelet
- 各ノードで動作する主要なNodeエージェント。API Serverにホスト名等を使ってNodeを登録する。PodSpecのセットを取得し、PodSpecに記載されているコンテナが正常に動作している状態を保証する
- kube-proxy
- 各ノードで動作するネットワークプロキシ。Nodeのネットワークルールをメンテナンスする。
Worker Nodeをプロビジョニングする
以下の操作は各Worker Nodeで実行します。なお、説明ではworker-01でセットアップを行い02のセットアップは省略しますが基本的に同じようなセットアップを行います(別途、別の操作が必要な場合は補足します)。
最初にworker-01にアクセスします。
$ ssh ubuntu@192.168.13.2
必要な依存を取得してきます。
$ sudo apt-get update $ sudo apt-get -y install socat conntrack ipset
socat
はkubectl port-forward
コマンドのサポートを行なうために必要なようです。
デフォルトではkubeletはswapが有効化されていると起動しません。
以下のコマンドを実行した際に出力がなにもなければswapは有効化されていません。
$ sudo swapon --show
もし、有効化されていた場合は以下のコマンドで、無効にしてください。
$ sudo swapoff -a
無効化が確認できたら、必要なバイナリをインストールします。
まずはバイナリをダウンロードします。
$ wget -q --show-progress --https-only --timestamping \ https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.21.0/crictl-v1.21.0-linux-arm64.tar.gz \ https://github.com/containernetworking/plugins/releases/download/v0.9.1/cni-plugins-linux-arm64-v0.9.1.tgz \ https://storage.googleapis.com/kubernetes-release/release/v1.21.0/bin/linux/arm64/kubectl \ https://storage.googleapis.com/kubernetes-release/release/v1.21.0/bin/linux/arm64/kube-proxy \ https://storage.googleapis.com/kubernetes-release/release/v1.21.0/bin/linux/arm64/kubelet
次に必要なディレクトリを作成しておきます。
$ sudo mkdir -p \ /etc/cni/net.d \ /opt/cni/bin \ /var/lib/kubelet \ /var/lib/kube-proxy \ /var/lib/kubernetes \ /var/run/kubernetes
バイナリを各ディレクトリに移動 or 解凍します。
$ tar -xvf crictl-v1.21.0-linux-arm64.tar.gz $ sudo tar -xvf cni-plugins-linux-arm64-v0.9.1.tgz -C /opt/cni/bin/ $ chmod +x crictl kubectl kube-proxy kubelet $ sudo mv crictl kubectl kube-proxy kubelet /usr/local/bin/
containerdとruncに関してはarm64
アーキテクチャでのインストールにはひと工夫が必要です。
まず、containerdですが、このIssueによるとdownload.docker.com
で提供されているものがあるみたいだったのでそちらを利用させていただこうと思います。
以下のコマンドでインストールします。
$ wget https://download.docker.com/linux/ubuntu/dists/focal/pool/test/arm64/containerd.io_1.4.3-2_arm64.deb $ sudo apt install ./containerd.io_1.4.3-2_arm64.deb $ containerd -v containerd containerd.io 1.4.3 269548fa27e0089a8b8278fc4fc781d7f65a939b
続いて、runc
ですが、こちらは公式での提供が内容だったので(少なくとも僕が調べる限りでは)自分でビルドしようと思います。
ビルドは1番スペックがよいmaster-01で行って、実行ファイルをcspでそれぞれのworkerに配布しようと思います。
$ ssh ubuntu@192.168.13.5
まず、ビルドにはgo が必要なようなので、goのインストールを行います。
$ wget https://golang.org/dl/go1.16.3.linux-arm64.tar.gz $ sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.16.3.linux-arm64.tar.gz $ export PATH=$PATH:/usr/local/go/bin $ go version go version go1.16.3 linux/arm6
次にビルドに必要な依存をとってきます。
$ sudo apt install build-essential $ sudo apt install pkg-config $ sudo apt install -y libseccomp-dev
最後にソースコードを取得して、ビルドを行います。
$ git clone https://github.com/opencontainers/runc $ cd runc $ make $ sudo make install
ビルドが完了したらできた実行ファイルをそれぞれのworkerに配布します。
$ scp /usr/local/sbin/runc ubuntu@192.168.13.2:~/ $ scp /usr/local/sbin/runc ubuntu@192.168.13.3:~/
それぞれのworkerで適当なディレクトリにruncを移動します。
$ sudo mv runc /usr/local/bin/ $ runc -v runc version 1.0.0-rc93+dev commit: 2965ffc7e327dc3dc33a9b308ba8396e60e5bb58 spec: 1.0.2-dev go: go1.16.3 libseccomp: 2.4.3
(versionの指定をちゃんとしてなかったので、devがインストールされてしまってますね...一旦ここでは先に進みます)
CNIの設定を行なう
bridgeネットワークの設定ファイルを作成します。
$ cat <<EOF | sudo tee /etc/cni/net.d/10-bridge.conf { "cniVersion": "0.4.0", "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"}] } } EOF
loopbackのネットワーク設定を記述します。
$ cat <<EOF | sudo tee /etc/cni/net.d/99-loopback.conf { "cniVersion": "0.4.0", "name": "lo", "type": "loopback" } EOF
kubeletの設定を行なう
kubeletのための設定や証明書を必要なディレクトリに配置します。
$ 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-config.yaml
を作成します。
$ 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: "10.200.0.0/24" resolvConf: "/run/systemd/resolve/resolv.conf" runtimeRequestTimeout: "15m" tlsCertFile: "/var/lib/kubelet/${HOSTNAME}.pem" tlsPrivateKeyFile: "/var/lib/kubelet/${HOSTNAME}-key.pem" EOF
kubuletのユニットファイルを作成します。
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
containerdの設定を行なう
containerdの設定ファイルを作成します。
以下のコマンドを実行します。
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
Kubernetes Proxyの設定を行なう
kube-proxyの設定を行います。
まずは、必要な設定ファイルを適当なディレクトリに配置します。
$ sudo mv kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig
次にkube-proxy-config.yaml
を作成します。
$ 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
例のごとく、ユニットファイルを作成します。
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
各コンポーネントの起動
設定が完了したので、最後にコンポーネントを起動します。
以下のコマンドを実行します。
$ sudo systemctl daemon-reload $ sudo systemctl enable containerd kubelet kube-proxy $ sudo systemctl start containerd kubelet kube-proxy
正常に起動が完了すると、leader-01からNodeが認識されているのが確認できます。
$ kubectl get nodes --kubeconfig admin.kubeconfig NAME STATUS ROLES AGE VERSION worker-01 Ready <none> 8m2s v1.21.0 worker-02 Ready <none> 8m59s v1.21.0
リモートからkubectlでアクセスできるようにする
kubectlの設定を行って毎回--kubeconfig
等でファイルを指定しなくても良いようにします。
ThinkPadのクライアントPCで以下のコマンドを実行します。
$ kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://192.168.13.5:6443 $ kubectl config set-credentials admin \ --client-certificate=admin.pem \ --client-key=admin-key.pem $ kubectl config set-context kubernetes-the-hard-way \ --cluster=kubernetes-the-hard-way \ --user=admin $ kubectl config use-context kubernetes-the-hard-way
kubectl get node
でNodeの一覧を取得できるようになってます。
$ kubectl version Client Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.5", GitCommit:"6b1d87acf3c8253c123756b9e61dac642678305f", GitTreeState:"clean", BuildDate:"2021-03-18T01:10:43Z", GoVersion:"go1.15.8", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.0", GitCommit:"cb303e613a121a29364f75cc67d3d580833a7479", GitTreeState:"clean", BuildDate:"2021-04-08T16:25:06Z", GoVersion:"go1.16.1", Compiler:"gc", Platform:"linux/arm64"} $ kubectl get node NAME STATUS ROLES AGE VERSION worker-01 Ready <none> 16m v1.21.0 worker-02 Ready <none> 17m v1.21.0
Podネットワークルールのプロビジョニングを行なう
Pod間の通信を行なうためにflannelをクラスタにインストールします。
flannelはkubernetesのlayer 3でのネットワーク設定を行ってくれます。
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
このあと失敗した
ここまでは、いろいろ苦難しつつもできたのですが、前述の通りflannelがうまく動かず失敗しました。
また、時間が立ってから再度やってみようと思います。