Kubernetes the hard wayを自宅のPCでやってみる

古いPCが余っていて,こいつでKubernetesクラスタを作ろうと思って公式ガイドを久しぶりに見に行ったのだが,デプロイツールを使ってインストールする方法しか案内されていない. 昔俺がKubernetesをインストールしようとしたときは,まだkubeadmをここまで推していなくて,公式でも「etcdを用意しましょう」とか書いてあって,心を折りに来ていた気がするんだけど,そのガイドは既になくなっていた.

kubeadmでもいいんだけど,ガイドを見る限りそこまで手数も多くないしあんまり面白くなさそうだったので,

github.com

をやってみることにした.これだよこれ,この長ったらしい手順がやりたかったんだよ.なのでこの記事も長いよ.

最終的なスクリプトが見たい人はこちら

構成を決める

本家のthe hard wayではGCP上にマシンを用意してKubernetesクラスタを作っているが,今回はあくまで余ったマシンにKubernetesを入れたかった.そのためアレンジする必要がある.

  • 物理マシンは1台の上にvagrantで複数のVMを立ち上げる
  • VMのOSはArch Linuxにする
  • masterは1台,壊れたときやアップグレードのときは全部作り直せばええやん
  • nodeは2台,これはマシンのスペック的にこのくらいは渡せるかと
  • kubectlはLAN内の別マシンから叩くよ
  • CRIはdockerを利用
  • CNIはCalicoを利用

大雑把にはこのくらい. 物理マシンを複数台用意するほど余っていないので,vagrant仮想マシンで我慢する.Arch Linuxを選択したのは,完全に俺の趣味だ.だいたいのガイドでUbuntuを使っていたのだが,Ubuntuみたいに全部入りのOSが必要だとは全然感じなかったし,Archでも事足りる上に軽いと思ったのでArchにしている. ちなみに物理マシンのホストOSもArch Linuxだ.

nodeはスペックが許す限り用意したいけど,masterは1台で良い.これはプロダクション運用するわけではなくて,遊ぶために作っておくクラスタなので,壊れたらまた作れば良い.

vagrantで必要なマシンを用意する

本家01に該当: https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/01-prerequisites.md

vagrantを触ること自体がかなり久しぶりで,いろんなことを忘れていた.そもそもVagrantfileはrubyっぽい文法で書けることすら忘れていた. とりあえずArchLinuxのVMを作り,適当なスペックとprivate IPを付与する.このIPは今後随所で使うので,ここできっちり決めておく.

public networkはいらないけど,LAN内の別マシンからアクセスするためport forwardをしておく.Kubernetes API Serverは6443 で立ち上げる,このポートを開けておく.

ホストで編集したシェルスクリプトを各VM内で実行できるようにvolume mountもやっておく.

Vagrant.configure("2") do |config|
  config.vm.box = "archlinux/archlinux"
  config.vm.boot_timeout = 600

  config.vm.define "master" do |c|
    c.vm.hostname = "master"
    c.vm.network "private_network", ip: "10.240.0.10"
    c.vm.network "forwarded_port", guest: 6443, host: 6443
  end

  config.vm.define "node-0" do |c|
    c.vm.hostname = "node-0"
    c.vm.network "private_network", ip: "10.240.0.20"
  end

  config.vm.define "node-1" do |c|
    c.vm.hostname = "node-1"
    c.vm.network "private_network", ip: "10.240.0.21"
  end

  config.vm.synced_folder "./scripts", "/provisoning_scripts"

  config.vm.provider "virtualbox" do |vb|
    vb.gui = false
    vb.memory = "1024"
    vb.cpus = 1
  end

  config.vm.provision "shell", inline: <<-SHELL
pacman-key --populate archlinux
pacman -Sy --noconfirm
pacman -S docker --noconfirm
pacman -S git base-devel wget --noconfirm
pacman -S ebtables ethtool socat conntrack-tools ipset net-tools --noconfirm
systemctl enable docker
systemctl start docker
usermod -aG docker vagrant
swapoff -a
cat /etc/fstab
sudo sed -ri '/\\sswap\\s/s/^#?/#/' /etc/fstab
cat /etc/fstab
cat >/etc/hosts <<EOF
127.0.0.1       localhost
# KTHW Vagrant machines
10.240.0.10     master
10.240.0.20     node-0
10.240.0.21     node-1
EOF
  SHELL
end

provisionでは,VMのOSとして必要なものをセットアップしている.

CRIにはdockerを使うのでdockerを入れて,起動しておく.

また,kubeletはswapがonの状態だと起動できないので sed でfstabをいじってswapを無効にしている.最初からswapが無効なVMならやらなくていいんだけど,公式のArch Linuxはswapがonになっていた.なお,sedをこのprovisionに書く場合は,\エスケープしてやる必要があるので,手動でコマンド実行するときに比べて\が一つ増えることに注意.

/etc/hosts には各VMのprivate IPを書いておく.これは後半で必要になる.

ここまで書いたらvagrant upしてvagrant ssh masterでmasterに入っておく.

なお,今回は証明書の生成等の作業をすべてmaster上で行い,必要なものをnodeに配置する.この作業はぶっちゃけどこでやっても良くて,生成されたものを正しく各VMに配れれば良い.

クライアントツールのインストール

本家02に該当: https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/02-client-tools.md

ひたすら必要なツールをwgetでとってきて配置する.

# 以下はmasterで行う
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
sudo mv cfssl cfssljson /usr/bin/

cfssl version
cfssljson --version

wget https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kubectl
chmod +x kubectl
sudo mv kubectl /usr/bin/

kubectl version --client

証明書の作成

本家04に該当: https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/04-certificate-authority.md

03は既にvagrant upしてあるので必要ない,というわけで飛ばす.

CAを作る

# 以下はmasterで行う
cat > ca-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "8760h"
    },
    "profiles": {
      "kubernetes": {
        "usages": ["signing", "key encipherment", "server auth", "client auth"],
        "expiry": "8760h"
      }
    }
  }
}
EOF

cat > ca-csr.json <<EOF
{
  "CN": "Kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "CA",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert -initca ca-csr.json | cfssljson -bare ca

これで

  • ca-key.pem
  • ca.pem

ができるはず.

管理ユーザのClient Certificateを作る

管理ユーザとしてAdminを作る.

# 以下はmasterで行う
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 \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  admin-csr.json | cfssljson -bare admin
  • admin-key.pem
  • admin.pem

ができるはず.

kubeletのClient Certificateを作る

# 以下はmasterで行う
for instance in node-0 node-1; do
cat > ${instance}-csr.json <<EOF
{
  "CN": "system:node:${instance}",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:nodes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

EXTERNAL_IP=10.240.0.10
cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=${instance},${EXTERNAL_IP} \
  -profile=kubernetes \
  ${instance}-csr.json | cfssljson -bare ${instance}
done

今回はmasterが1台なので,EXTERNAL_IPをmasterのIPにしているが,もしmasterを複数台用意して,LBでバランシングするような場合は,ここにはLBのIPを指定する.

  • node-0-key.pem
  • node-0.pem
  • node-1-key.pem
  • node-1.pem

ができるはず.

Controller ManagerのClient Certificateを作る

# 以下はmasterで行う
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-controller-manager-key.pem
  • kube-controller-manager.pem

ができるはず.

Kube ProxyのClient Certificateを作る

# 以下はmasterで行う
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 Certificateを作る

# 以下はmasterで行う
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 ServerのCertificateを作る

# 以下はmasterで行う
KUBERNETES_EXTERNAL_ADDRESS=192.168.0.7
KUBERNETES_HOSTNAMES=kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster,kubernetes.svc.cluster.local

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

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_EXTERNAL_ADDRESS},127.0.0.1,${KUBERNETES_HOSTNAMES} \
  -profile=kubernetes \
  kubernetes-csr.json | cfssljson -bare kubernetes

ここでのIP指定は結構重要.

10.32.0.1 は,ここではまだ登場しないが後半ででてくる,ClusterIPのIP rangeで指定するIPだ. 10.240.0.10はmaster自身のVMのIPであり, 10.240.0.1110.240.0.12 はnodeのVMのIPだ. そして, KUBERNETES_EXTERNAL_ADDRESS で指定しているのが,物理マシンのLAN内でのIPになっている.

これはServer Certificateなので,ここで指定するIPでしかリクエストを受け付けない.このクラスタvagrantで立ち上げたVMをport forwardして使うので,LAN内から見たKubernetes API ServerのIPは物理マシンのLAN内のIPということになる.それが192.168.0.7 なのだが,ここでこのIPをhostnameから外すとLAN内の別マシンからはアクセスできなくなってしまう.その状態であっても,もちろんmasterのVM内からはアクセスできる.なぜならmasterのVM内から見れば自身のIPは127.0.0.1になるから.

これで

  • kubernetes-key.pem
  • kubernetes.pem

ができるはず.

Service AccountのKey Pairを作る

# これはmasterで行う
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
  • service-account-key.pem
  • service-account.pem

ができるはず.

作成した証明書をVMに配る

masterに配る

といっても,今回はこれらはmasterで作成しているので,別に配る必要はないんだけど,一応手順としては残しておく.

# これはホストOSでやる

for instance in master; do
    vagrant scp ca.pem ${instance}:~/
    vagrant scp ca-key.pem ${instance}:~/
    vagrant scp kubernetes.pem ${instance}:~/
    vagrant scp kubernetes-key.pem ${instance}:~/
    vagrant scp service-account.pem ${instance}:~/
    vagrant scp service-account-key.pem ${instance}:~/
done

nodeに配る

# これはホストOSでやる

for instance in node-0 node-1; do
  vagrant scp ca.pem ${instance}:~/
  vagrant scp ${instance}-key.pem ${instance}:~/
  vagrant scp ${instance}.pem ${instance}:~/
done

Configの作成

本家05に該当: https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/05-kubernetes-configuration-files.md

kubeletのconfigを作成

# これはmasterで行う
KUBERNETES_PUBLIC_ADDRESS=10.240.0.10


for instance in node-0 node-1; 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

前述の通り,KUBERNETES_PUBLIC_ADDRESSはnodekから見たAPIサーバのIPなので,LBを使う場合はLBのIPを書く.今回はmasterが1台なので,masterのIPを指定している.

  • node-0.kubeconfig
  • node-1.kubeconfig

ができるはず.

kube-proxyのconfigを作成

# masterで行う
KUBERNETES_PUBLIC_ADDRESS=10.240.0.10

kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://${KUBERNETES_PUBLIC_ADDRESS}: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のconfigを作成

# masterで行う

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のconfigを作成

# masterで行う
kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://127.0.0.1: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のconfigを作成

# masterで行う


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
  • admin.kubeconfig

ができるはず.

configを配布

masterに配布

masterで生成しているので特に配布する必要はないんだけど.

# ホストOSで行う

for instance in master; do
    vagrant scp admin.kubeconfig ${instance}:~/
    vagrant scp kube-controller-manager.kubeconfig ${instance}:~/
    vagrant scp kube-scheduler.kubeconfig ${instance}:~/
done

nodeに配布

# ホストOSで行う

for instance in node-0 node-1; do
    vagrant scp ${instance}.kubeconfig ${instance}:~/
    vagrant scp kube-proxy.kubeconfig ${instance}:~/
done

データ暗号化用の鍵を作成

本家06に該当: https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/06-data-encryption-keys.md

Secret等を保存するときに暗号化するんだけど,そのときに使う鍵を生成する.

鍵の作成

# masterで行う
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

ができるはず.

鍵の配布

これはmasterにのみ配れば良い.つまり今回は特に配布する必要はないんだけど…….

# ホストOSで行う
for instance in master; do
    vagrant scp encryption-config.yaml ${instance}:~/
done

etcdのセットアップ

本家07に該当: https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/07-bootstrapping-etcd.md

etcdは本来3台以上のクラスタを構成する.そのため,masterが3台ある場合は,以降の操作を3台それぞれのmasterで行う必要がある.が,今回はmasterが1台だけなので,etcdも1台しか起動しない.

etcdのインストール

# masterで行う
wget -q --show-progress --https-only --timestamping \
    "https://github.com/etcd-io/etcd/releases/download/v3.4.10/etcd-v3.4.10-linux-amd64.tar.gz"
tar -xvf etcd-v3.4.10-linux-amd64.tar.gz
sudo mv etcd-v3.4.10-linux-amd64/etcd* /usr/bin/

etcdの設定

# masterで行う
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/

INTERNAL_IP=$(ip addr show eth1 | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')

ETCD_NAME=master

cat <<EOF | sudo tee /etc/systemd/system/etcd.service
[Unit]
Description=etcd
Documentation=https://github.com/coreos
[Service]
Type=notify
ExecStart=/usr/bin/etcd \\
  --name ${ETCD_NAME} \\
  --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://${INTERNAL_IP}:2380 \\
  --listen-peer-urls https://${INTERNAL_IP}:2380 \\
  --listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379 \\
  --advertise-client-urls https://${INTERNAL_IP}:2379 \\
  --initial-cluster-token etcd-cluster-0 \\
  --initial-cluster master=https://10.240.0.10:2380 \\
  --initial-cluster-state new \\
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF

INTERNAL_IPip addrでとっているけど,別にifconfig等でとっても構わない.

ETCD_NAMEは,複数台のmasterがある場合は,

  • master-0
  • master-1
  • master-2

のように名前が分散するようにしてやる必要がある.

また,--initial-cluster には,クラスタに所属するetcdのすべてのnameとアドレスを書く必要があるので,クラスタ化する際にはそれらをすべて列挙しておこう.

etcdを起動

# masterで行う
sudo systemctl daemon-reload
sudo systemctl enable etcd
sudo systemctl start etcd

etcdの確認

# masterで行う
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

startedになっていれば問題ない.複数台でクラスタ化した場合は,クラスタ内のノードがすべて表示されていること.

f98dc20bce6225a0, started, master, https://10.240.0.10:2380, https://10.240.0.10:2379, false

Controle Planeをセットアップ

本家08に該当: https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/08-bootstrapping-kubernetes-controllers.md

この章の操作は,masterとなるVMすべてで同じことを行う必要がある.

必要なものをインストール

# masterで行う
sudo mkdir -p /etc/kubernetes/config

wget -q --show-progress --https-only --timestamping \
    "https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kube-apiserver" \
    "https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kube-controller-manager" \
    "https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kube-scheduler" \
    "https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kubectl"

chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl
sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/bin/

API Serverの設定

sudo mkdir -p /var/lib/kubernetes/

sudo cp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
    service-account-key.pem service-account.pem \
    encryption-config.yaml /var/lib/kubernetes/

INTERNAL_IP=$(ip addr show eth1 | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')

cat <<EOF | sudo tee /etc/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/bin/kube-apiserver \\
  --advertise-address=${INTERNAL_IP} \\
  --allow-privileged=true \\
  --apiserver-count=1 \\
  --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://10.240.0.10: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=true' \\
  --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
EOF

--etcd-servers は,クラスタ化しているのであればetcdすべてのノードのアドレスを列挙すること.

--service-cluster-ip-rangeが,API Server Certificateのときに出てきたClusterIPのIP rangeだ.ここは揃えておく必要がある.

Controller Managerの設定

# masterで行う
sudo cp kube-controller-manager.kubeconfig /var/lib/kubernetes/

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/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

--cluster-cidrは,クラスタ内のPodに割り振られるIPのrange.この段階では割と好きに決めて良い.

--service-cluster-ip-rangeAPI Serverと同じ.

Schedulerの設定

# masterで行う
sudo cp kube-scheduler.kubeconfig /var/lib/kubernetes/

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

cat <<EOA | sudo tee /etc/systemd/system/kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/bin/kube-scheduler \\
  --config=/etc/kubernetes/config/kube-scheduler.yaml \\
  --v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOA

ControlerのServiceを起動

# masterで行う
sudo systemctl daemon-reload
sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler
sudo systemctl start kube-apiserver kube-controller-manager kube-scheduler

HTTP HealthCheckの有効化のためにnginxを設定

これはLBを用意している場合には,LBでやれば良いだけの話となる.

# masterで行う
sudo pacman -Sy --noconfirm
sudo pacman -S nginx --noconfirm

sudo mkdir -p /etc/nginx/conf.d /etc/nginx/sites-available /etc/nginx/sites-enabled

cat > nginx.conf <<EOF
user http;
worker_processes auto;
worker_cpu_affinity auto;
events {
    multi_accept on;
    worker_connections 1024;
}
http {
    charset utf-8;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    server_tokens off;
    log_not_found off;
    types_hash_max_size 4096;
    client_max_body_size 16M;
    # MIME
    include mime.types;
    default_type application/octet-stream;
    # logging
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log warn;
    # load configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}
EOF

sudo mv nginx.conf /etc/nginx/nginx.conf

cat > kubernetes.default.svc.cluster.local <<EOF
server {
  listen      80;
  server_name kubernetes.default.svc.cluster.local;
  location /healthz {
    proxy_pass                    https://127.0.0.1:6443/healthz;
    proxy_ssl_trusted_certificate /var/lib/kubernetes/ca.pem;
  }
}
EOF

sudo mv kubernetes.default.svc.cluster.local /etc/nginx/sites-available/kubernetes.default.svc.cluster.local
sudo ln -s /etc/nginx/sites-available/kubernetes.default.svc.cluster.local /etc/nginx/sites-enabled/

sudo systemctl restart nginx
sudo systemctl enable nginx

確認

これもLBでやればよい.

# masterで行う

$ kubectl get componentstatuses --kubeconfig admin.kubeconfig
NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-0               Healthy   {"health":"true"}

$ curl -H "Host: kubernetes.default.svc.cluster.local" -i http://127.0.0.1/healthz
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 18 Aug 2020 13:20:55 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Connection: keep-alive
Cache-Control: no-cache, private
X-Content-Type-Options: nosniff

ok

$ curl --cacert ca.pem https://10.240.0.10:6443/version
{
  "major": "1",
  "minor": "18",
  "gitVersion": "v1.18.6",
  "gitCommit": "dff82dc0de47299ab66c83c626e08b245ab19037",
  "gitTreeState": "clean",
  "buildDate": "2020-07-15T16:51:04Z",
  "goVersion": "go1.13.9",
  "compiler": "gc",
  "platform": "linux/amd64"
}

こんな感じの出力になれば良い.

KubeletにアクセスするためのRBACを設定

ここでは,Kubernetes API ServerがKubelet APIにアクセスするためのRBACを設定しておく.

# masterで行う
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

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

Workerをセットアップ

本家09に該当: https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/09-bootstrapping-kubernetes-workers.md

本家はこの章でCNIのセットアップを行っているが,今回はCalicoを入れたいのでCNIのセットアップを次々章に飛ばす.その関係で手順が本家と少し異なる.

必要なツールのインストール

本家ではcontainerdを使っているが,ここはdockerを使いたかったので書き換えている.

# workerで実行
wget -q --show-progress --https-only --timestamping \
    https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.18.0/crictl-v1.18.0-linux-amd64.tar.gz \
    https://github.com/opencontainers/runc/releases/download/v1.0.0-rc91/runc.amd64 \
    https://github.com/containernetworking/plugins/releases/download/v0.8.6/cni-plugins-linux-amd64-v0.8.6.tgz \
    https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kubectl \
    https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kube-proxy \
    https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kubelet

sudo mkdir -p \
    /etc/cni/net.d \
    /opt/cni/bin \
    /var/lib/kubelet \
    /var/lib/kube-proxy \
    /var/lib/kubernetes \
    /var/run/kubernetes

tar -xvf crictl-v1.18.0-linux-amd64.tar.gz
sudo tar -xvf cni-plugins-linux-amd64-v0.8.6.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/bin/

なおdocker自体はVagrantのprovisioningでインストールしてしまっているので,ここでは特に何もしない.

kubeletをセットアップ

# workerで実行
sudo cp ${HOSTNAME}-key.pem ${HOSTNAME}.pem /var/lib/kubelet/
sudo cp ${HOSTNAME}.kubeconfig /var/lib/kubelet/kubeconfig
sudo cp ca.pem /var/lib/kubernetes/

INTERNAL_IP=$(ip addr show eth1 | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')

case "$INTERNAL_IP" in
10.240.0.20)
    POD_CIDR="10.200.0.0/24"
    ;;
10.240.0.21)
    POD_CIDR="10.200.1.0/24"
    ;;
*)
    echo "invalid ip"
    ;;
esac

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

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/bin/kubelet \\
  --config=/var/lib/kubelet/kubelet-config.yaml \\
  --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

PodCIDRは好きに割り振っていいのだが,masterのkube-controller-managerの起動時に

  --cluster-cidr=10.200.0.0/16

という指定をしているので,この範囲に収まるようにしておく.

clusterDNSとして 10.32.0.10 を指定しているが,これは後々corednsを入れる段階で,corednsのServiceのClusterIPに該当する.

github.com

kube-dnsとして入れるServiceのIPを記述するので,別のものや別のIPでServiceを作る場合は,ちゃんとここも書き換えておくこと.

kube-proxyのセットアップ

# workerで実行

sudo cp kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig

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/bin/kube-proxy \\
  --config=/var/lib/kube-proxy/kube-proxy-config.yaml
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF

ここで指定するclusterCIDRも,kube-controller-managerの

  --cluster-cidr=10.200.0.0/16

と揃える必要がある.

WorkerのServiceを起動

# workerで実行

sudo systemctl daemon-reload
sudo systemctl enable kubelet kube-proxy
sudo systemctl start kubelet kube-proxy

本来であればこの後に確認ができるはずなのだが,CNIを飛ばしているので確認はCNIのセットアップ後になる.

リモートアクセス用のkubeconfigを準備

本家10に該当: https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/10-configuring-kubectl.md

Adminのkubeconfigを用意

# masterで実行

KUBERNETES_PUBLIC_ADDRESS=10.240.0.10

kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://${KUBERNETES_PUBLIC_ADDRESS}: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

確認

# masterで実行

kubectl get componentstatuses

echo ''

kubectl get nodes

ただ,CNIを入れてないのでこの段階ではkubectl get nodes は失敗するはずである.

Calicoをセットアップ

本家の該当項目なし.なお,11に関しては,GCPではないのでやる必要がないため飛ばす.

CalicoをKubernetesのnodeに入れるにしても,いくつか選択肢がある.楽なのはKubernetes API datastoreを使ってしまう方法だが,せっかくmaster用にetcdを入れてあるので,etcdを使うことにする.

docs.projectcalico.org

Calicoのインストール

$ curl https://docs.projectcalico.org/manifests/calico-etcd.yaml -o calico.yaml

としてcalico.yaml をダウンロードする.

そしてこのマニフェストを手動で編集する必要がある.

まず,etcdへ接続できるようにするために,Secretの

  • etcd-key
  • etcd-cert
  • etcd-ca

にそれぞれ,

  • cat kubernetes-key.pem | base64 -w 0
  • cat kubernetes.pem | base64 -w 0
  • cat ca.pem | base64 -w 0

の値を貼り付ける.

次に,ConfigMapの

etcd_endpoints: "https://10.240.0.10:2379"

を設定する.ここはmasterで稼働しているetcdのエンドポイントを指定する.

同じくConfigMapの

etcd_ca: "/calico-secrets/etcd-ca" # "ca.pem"
etcd_cert: "/calico-secrets/etcd-cert" # "kubernetes.pem"
etcd_key: "/calico-secrets/etcd-key" # "kubernetes-key.pem"

コメントアウトしておく.

最後にDaemonSetの環境変数にPod network CIDRのIPレンジを書いておく.

 - name: CALICO_IPV4POOL_CIDR
   value: "10.200.0.0/16"  # Pod network cidr

これでできあがったcalico.yamlkubectl apply -f してやる.

確認

# ホストOSで実行する

vagrant ssh master \
    --command "kubectl get nodes --kubeconfig admin.kubeconfig"

で,

NAME     STATUS   ROLES    AGE   VERSION
node-0   Ready    <none>   17d   v1.18.6
node-1   Ready    <none>   17d   v1.18.6
Connection to 127.0.0.1 closed.

こんな出力になれば問題ない.

ようやくこれでkubectl get nodesが通るようになった.

DNS add-onを入れる

本家12に該当: https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/12-dns-addon.md

つまりkube-dnsやcorednsと呼ばれるものを入れる.

corednsのインストール

# masterで行う

kubectl apply -f https://storage.googleapis.com/kubernetes-the-hard-way/coredns-1.7.0.yaml

kubectl get pods -l k8s-app=kube-dns -n kube-system

これでcorednsのPodが起動してくるはず.

busyboxをデプロイ

確認のためにbusyboxを動かす.

# masterで行う


kubectl run busybox --image=busybox:1.28 --command -- sleep 3600

kubectl get pods -l run=busybox

確認

# masterで行う

$ POD_NAME=$(kubectl get pods -l run=busybox -o jsonpath="{.items[0].metadata.name}")
$ kubectl exec -ti $POD_NAME -- nslookup kubernete
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

となれば問題ない.

残り

13のSmoke Test14のCleaning Upはやっていない.

一応ちょいちょいPodをデプロイしたりSecretを作ってみたりして問題ないし,消すつもりはなかったのでCleanupは不要.

というわけでここまででできあがりということになる.

[番外編]一般ユーザを追加

Adminのユーザは作成して,system:mastersを与えたが,それ以外のユーザも作ってみたいと思う.

だいたいAdminと同じ手順になる.

ユーザのクライアント証明書を作る

cat > akira-csr.json <<EOF
{
  "CN": "akira",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:authenticated",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  akira-csr.json | cfssljson -bare akira

なお,このユーザは管理者ではないので,system:mastersは付与せずsystem:authenticatedのみを付与する.こうすることで,このユーザは認証だけができる状態になり,あと必要な権限はRBACで付与していく形になる.

ユーザのkubeconfigを作る

KUBERNETES_PUBLIC_ADDRESS=192.168.0.7

kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443
    --kubeconfig=akira.kubeconfig

kubectl config set-credentials akira \
    --client-certificate=akira.pem \
    --client-key=akira-key.pem
    --kubeconfig=akira.kubeconfig

kubectl config set-context user \
    --cluster=kubernetes-the-hard-way \
    --user=akira \
    --kubeconfig=akira.kubeconfig

kubectl config use-context user --kubeconfig=akira.kubeconfig

あとは,これで~/.kube/configakira用の設定ができるので,これを任意のホストに持ち出して入れておく.

ユーザに権限を付与する

このままの状態だと,system:authenticatedしか権限がないので,なにかやろうと思うとエラーになる.

$ kubectl get nodes
Error from server (Forbidden): nodes is forbidden: User "akira" cannot list resource "nodes" in API group "" at the cluster scope

というわけで,適切な権限を付与してやる.

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: akira-masters-all
subjects:
- kind: User
  name: akira
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: view
  apiGroup: rbac.authorization.k8s.io

で,これをkubectl apply -f してやれば,akiraは閲覧だけができるようになる. なお,ここの操作自体はadminでやってやらないといけない.

トラブルシューティング

swapが生きてた

これは冒頭にも書いたが,kubeletはswapがonの状態だと起動できない.そのため,Wrokerのセットアップ時で失敗して,Vagrantfileにてswapを無効化してprovisionし直した.

なお,試すだけなら起動後にswapoff -aとしてもいいんだけど,これは再起動後に再びswapが復活してしまうため,起動後に毎回やる必要がある.

/etc/hosts への書き込みを忘れた

これを忘れるとcorednsインストール後の確認で,

$ kubectl exec -ti $POD_NAME -- nslookup kubernetes

とやったところでnode-0を解決できずにエラーが出た.まぁそうなるわな…….

CNIがbridgeだとFirewallをoffにする必要がある

本家では,CNIにbridgeを使っている

しかし,このとおりにセットアップしたところ,corednsを入れたときにcorednsのpodがエラーになって落ちてしまう.その時のログ.

[INFO] plugin/ready: Still waiting on: "kubernetes"
[INFO] plugin/ready: Still waiting on: "kubernetes"
[INFO] plugin/ready: Still waiting on: "kubernetes"
I0802 16:11:15.741083       1 trace.go:116] Trace[637979947]: "Reflector ListAndWatch" name:pkg/mod/k8s.io/client-go@v0.18.3/tools/cache/reflector.go:125 (started: 2020-08-02 16:10:45.740199663 +0000 UTC m=+134.639398318) (total time: 30.000839979s):
Trace[637979947]: [30.000839979s] [30.000839979s] END
E0802 16:11:15.741126       1 reflector.go:178] pkg/mod/k8s.io/client-go@v0.18.3/tools/cache/reflector.go:125: Failed to list *v1.Endpoints: Get "https://10.32.0.1:443/api/v1/endpoints?limit=500&resourceVersion=0": dial tcp 10.32.0.1:443: i/o timeout
I0802 16:11:18.430763       1 trace.go:116] Trace[443632888]: "Reflector ListAndWatch" name:pkg/mod/k8s.io/client-go@v0.18.3/tools/cache/reflector.go:125 (started: 2020-08-02 16:10:48.429966802 +0000 UTC m=+137.329165455) (total time: 30.000752415s):
Trace[443632888]: [30.000752415s] [30.000752415s] END
E0802 16:11:18.430809       1 reflector.go:178] pkg/mod/k8s.io/client-go@v0.18.3/tools/cache/reflector.go:125: Failed to list *v1.Service: Get "https://10.32.0.1:443/api/v1/services?limit=500&resourceVersion=0": dial tcp 10.32.0.1:443: i/o timeout
[INFO] plugin/ready: Still waiting on: "kubernetes"
I0802 16:11:18.644349       1 trace.go:116] Trace[1496193015]: "Reflector ListAndWatch" name:pkg/mod/k8s.io/client-go@v0.18.3/tools/cache/reflector.go:125 (started: 2020-08-02 16:10:48.64359993 +0000 UTC m=+137.542798583) (total time: 30.000705162s):
Trace[1496193015]: [30.000705162s] [30.000705162s] END
E0802 16:11:18.644484       1 reflector.go:178] pkg/mod/k8s.io/client-go@v0.18.3/tools/cache/reflector.go:125: Failed to list *v1.Namespace: Get "https://10.32.0.1:443/api/v1/namespaces?limit=500&resourceVersion=0": dial tcp 10.32.0.1:443: i/o timeout
[INFO] plugin/ready: Still waiting on: "kubernetes"
[INFO] plugin/ready: Still waiting on: "kubernetes"

これに関してはufwでfirewallをoffにしろという情報がある.

確かにnode側のjournalctlで見ると

Aug 03 16:33:02 node-1 kernel: audit: type=1104 audit(1596472382.759:1538): pid=8075 uid=0 auid=1000 ses=3 msg='op=PAM:setcred grantors=pam_unix,pam_permit,pam_env acct="root" exe="/usr/bin/sudo" hostname=? add>
Aug 03 16:33:03 node-1 kubelet[346]: I0803 16:33:03.080747     346 prober.go:124] Readiness probe for "coredns-5677dc4cdb-4ljsh_kube-system(9c653514-284a-4328-8eef-0a0d1292dbb6):coredns" failed (failure): HTTP >
Aug 03 16:33:04 node-1 kernel: [UFW BLOCK] IN=cnio0 OUT=eth1 PHYSIN=veth9dbf349c MAC=fa:1b:40:ae:a5:e1:8e:25:1e:0a:a8:66:08:00 SRC=10.200.1.5 DST=10.240.0.10 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=16729 DF PROTO=T>
Aug 03 16:33:05 node-1 kernel: [UFW BLOCK] IN=cnio0 OUT=eth1 PHYSIN=veth9dbf349c MAC=fa:1b:40:ae:a5:e1:8e:25:1e:0a:a8:66:08:00 SRC=10.200.1.5 DST=10.240.0.10 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=16730 DF PROTO=T>
Aug 03 16:33:13 node-1 kubelet[346]: I0803 16:33:13.080574     346 prober.go:124] Readiness probe for "coredns-5677dc4cdb-4ljsh_kube-system(9c653514-284a-4328-8eef-0a0d1292dbb6):coredns" failed (failure): HTTP >
Aug 03 16:33:23 node-1 kubelet[346]: I0803 16:33:23.080864     346 prober.go:124] Readiness probe for "coredns-5677dc4cdb-4ljsh_kube-system(9c653514-284a-4328-8eef-0a0d1292dbb6):coredns" failed (failure): HTTP >
Aug 03 16:33:26 node-1 kernel: [UFW BLOCK] IN=cnio0 OUT=eth1 PHYSIN=veth9dbf349c MAC=fa:1b:40:ae:a5:e1:8e:25:1e:0a:a8:66:08:00 SRC=10.200.1.5 DST=10.240.0.10 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=8532 DF PROTO=TC>
Aug 03 16:33:33 node-1 kubelet[346]: I0803 16:33:33.080790     346 prober.go:124] Readiness probe for "coredns-5677dc4cdb-4ljsh_kube-system(9c653514-284a-4328-8eef-0a0d1292dbb6):coredns" failed (failure): HTTP >

ブロックされていることがわかる.手っ取り早くsudo ufw disableすると無事疎通した.

が,このあと結局CNIはCalicoを入れてしまったし,Calicoを入れたらこの手順は不要になった.

CNIをflannelにするかCalicoにするか

ネット上ではflannelに関する情報が多いが,リリースがあまり頻繁ではないのと,CalicoのNetwork Policyを使ってみたかったこともあり,Calicoを入れた.

所感

出来上がったスクリプトたちがこれ.

github.com

トラブルシューティングにあるように,結構トラブルに遭遇しているので,どれも一発で全部が上手く行ったわけではない.結構作り直している. そして最初からこの形だったわけではなく,最初はCNIはbridgeのままだったし(本家のthe hard wayはこれを使ってた),Container Runtimeもcontainerdだった. そのため,上記のスクリプトには古いCNIの手順だったりContainer Runtimeの手順が少し残っている. _09-02-configure-cni.sh とかね.

ただトラブって何度も作り直しただけあって,だいぶ手慣れてきてしまった.VMごと作り直しを何回もやったので

  • 証明書は作るだけ,作り直したら利用している箇所は再生成する必要があるが
  • 例えばworkerの手順でミスってVMごと潰してしまっても,masterは何も変更する必要はない
  • CNIとかCRIはすべてworkerだけの問題なので,ここを変更するのにmasterをいじる必要がない
  • masterのetcdは大事,ここが吹き飛ぶと構成情報がみんな吹き飛ぶ

とかがわかるようになってくる.

PCは起動しっぱなしにするの?

これはNo. 最初からPCをつけっぱなしにするつもりはなかったのだが,Kubernetesはnodeもmasterも一度すべて停止しても,再起動時にはちゃんと復元してくれる.

これはetcdが構成情報をディスクに保存しておいてくれるおかげだが.

なので,PCを起動している間だけKubernetesクラスタも稼働している.

Ingress等への外部からのアクセスはどうする?

そもそもLAN外へ公開するつもりはなかったので,完全に外部へは公開していない.ただし,LAN内からのアクセスは行っている. これにはingress-nginx等でIngressを作り,NodePortに公開した後,NodePortの範囲をVagrant側でforwarded_portしている. MetalLBとかでIPを振ってもいいのだが,そのIPにLAN -> Host OS -> Vagrant VMというようにアクセスしたくて,これは今の構成ではちょっと面倒くさかったのでやっていない.

どうせなら,Vagrantの段階でPrivateIPではなくPublicIPを振ってしまったほうがよかったのかもしれない…….そうしたらport forwardから開放され,LAN内から自由にアクセスできたかも…….

参考

本家.

github.com

Vagrantでやっている人の例.

qiita.com

github.com

Calicoを入れている人の例.

https://hakengineer.xyz/2019/06/28/post-1932/