ビビリフクロウの足跡

とあるインフラSEの勉強&備忘ブログ

Kubernetes the Hard Way on OpenStack

Kubernetesの構築をスクラッチで実践できるチュートリアルとしてKubernetes the Hard Wayがあります。来年のはじめくらいにCKAを取得したいと考えているので、本チュートリアルを自宅のOpenStack上で実施してみました。その際の実施手順と少しばかりの解説を残しておきたいと思います。

前提

OpenStackには次のコンポーネントがインストールされているとします。

  • Keystone
  • Glance
  • Nova
  • Neutron
  • Neutron LBaaSv2
  • Designate

次のインスタンスを用意し、HA構成のKubernetesを構成します。 ホスト名でFloatingIPに名前解決ができるようにDesignateを構成しておきます。

  • Masterサーバ x3

    • OS: CentOS 7 Minimal Install
    • vCPU: 2
    • Mem: 4GB
    • ホスト名: hardway-master-[0:2].bbrfkr.mydns.jp
    • IP: 10.0.0.[10:12]
    • FloatingIP: 192.168.0.[10:12]
  • Workerサーバ x3

    • OS: CentOS 7 Minimal Install
    • vCPU: 2
    • Mem: 4GB
    • ホスト名: hardway-worker-[0:2].bbrfkr.mydns.jp
    • IP: 10.0.0.[20:22]
    • FloatingIP: 192.168.0.[20:22]

また、FloatingIPに対して到達可能なネットワーク上に作業用端末を用意しておきます。作業用端末ではあらかじめ以下のコマンドが叩けるように準備しておきます。私の場合はWindows 10にWSLのUbuntuをインストールして、その上から叩けるようにしました。

  • cfssl
  • cfssljson
  • kubectl
  • openstack
  • neutron

Masterサーバのkube-apiserverに対するエンドポイントとしてNeutron LBaaSv2にてロードバランサを切っておきます。また、VIP portに対するセキュリティグループも適切に設定しておきましょう。

  • Neutron LBaaSv2 LB
    • 名前: kubernetes-the-hard-way
    • Internal VIP: 10.0.0.100
    • FloatingIP: 192.168.0.100
    • リスナーポート: 6443

手順

以降、Hard Wayな手順を示して行きます。作業対象は()付きでmasters、workers、bastion(作業用端末の意)で示していきます。

OS設定

  • (masters, workers) OSパッケージの更新とSELinuxの無効化をしておきます。これはKubernetesのインストール要件になります。
sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config
sudo yum update -y
sudo reboot

CAおよび各種証明書の作成

ここではkube-apiserverとhttps通信するために必要な各種証明書を作成していきます。

  • (bastion) CAの署名における設定とCA証明書および鍵を作成します。
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
  • (bastion) Kubernetesクラスタのadminユーザに対するクライアント証明書および鍵を作成します。Kubernetesのx509証明書認証においてはCN(Common Name)に指定した値がクラスタのユーザ名、O(Organization)に指定した値がグループ名として扱われます。system:mastersグループはデフォルトでKubernetesクラスタのフルコントロール権限を持つグループです。
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
  • (bastion) kubeletがkube-apiserverと通信するときのクライアント証明書および鍵を作成します。ここでは厳密にはNode Authorizationというkube-apiserverの認可機能を使うために、CNにsystem:node:<インスタンス名>、Oにsystem:nodesという値を指定するのが肝となります。
for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; 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=$(openstack server show -f json ${instance} | \
              jq -r .addresses | \
              awk -F= '{ print $2 }' | \
              sed 's/,//g' | \
              awk '{ print $2 }')

INTERNAL_IP=$(openstack server show -f json ${instance} | \
              jq -r .addresses | \
              awk -F= '{ print $2 }' | \
              sed 's/,//g' | \
              awk '{ print $1 }')

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=${instance},${EXTERNAL_IP},${INTERNAL_IP} \
  -profile=kubernetes \
  ${instance}-csr.json | cfssljson -bare ${instance}
done
  • (bastion) kube-controller-managerがkube-apiserverと通信するための証明書および鍵を作成します。
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
  • (bastion) kube-proxyがkube-apiserverと通信するための証明書および鍵を作成します。
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
  • (bastion) kube-schedulerがkube-apiserverと通信するための証明書および鍵を作成します。
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
  • (bastion) kube-apiserver用のサーバ証明書および鍵を作成します。この証明書で許可されたCNやSAN(Subject Alternative Name)でアクセスしなければkube-apiservertとhttps通信できないので、登録するSAN(cfssl gencertコマンドの-hostnameオプションのところ)に注意します。
KUBERNETES_PUBLIC_ADDRESS=$(neutron floatingip-list | grep \
                          $(neutron lbaas-loadbalancer-show kubernetes-the-hard-way | \
                            grep vip_port_id | \
                            awk '{ print $4 }') | \
                          awk '{ print $8 }')
KUBERNETES_INTERNAL_ADDRESS=$(neutron lbaas-loadbalancer-show kubernetes-the-hard-way | \
                              grep vip_address | \
                              awk '{ print $4 }')
KUBERNETES_PUBLIC_DNS=kubernetes-the-hard-way.bbrfkr.mydns.jp

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

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
MASTER_INTERNAL_IPS=${MASTER_INTERNAL_IPS}$(openstack server show -f json ${instance} | \
              jq -r .addresses | \
              awk -F= '{ print $2 }' | \
              sed 's/,//g' | \
              awk '{ print $1 }'),
done

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=10.32.0.1,${MASTER_INTERNAL_IPS}${KUBERNETES_PUBLIC_ADDRESS},${KUBERNETES_INTERNAL_ADDRESS},${KUBERNETES_PUBLIC_DNS},127.0.0.1,kubernetes.default \
  -profile=kubernetes \
  kubernetes-csr.json | cfssljson -bare kubernetes
  • (bastion) ServiceAccountのトークン発行用キーペアを作成します。
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
  • (bastion) 証明書群をMasterサーバ、Workerサーバに配布します。
for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; do
  scp ca.pem ${instance}-key.pem ${instance}.pem centos@${instance}.bbrfkr.mydns.jp:~/
done

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
  scp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
    service-account-key.pem service-account.pem centos@${instance}.bbrfkr.mydns.jp:~/
done

kubeconfig群の作成

ここではkube-apiserverと通信するために必要な認証ファイルであるkubeconfigファイル群を作成していきます。

  • (bastion) kubelet用のkubeconfigを作成します。
KUBERNETES_PUBLIC_ADDRESS=$(neutron floatingip-list | grep \
                          $(neutron lbaas-loadbalancer-show kubernetes-the-hard-way | \
                            grep vip_port_id | \
                            awk '{ print $4 }') | \
                          awk '{ print $8 }')

for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; 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
  • (bastion) kube-proxy用のkubeconfigを作成します。
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
  • (bastion) kube-controller-manager用のkubeconfigを作成します。
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
  • (bastion) kube-scheduler用のkubeconfigを作成します。
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
  • (bastion) adminユーザ用のkubeconfigを作成します。
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
  • (bastion) kubeconfig群をMasterサーバ、Workerサーバに配布します。
for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; do
  scp ${instance}.kubeconfig kube-proxy.kubeconfig centos@${instance}.bbrfkr.mydns.jp:~/
done

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
  scp admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig centos@${instance}.bbrfkr.mydns.jp:~/
done

データ暗号化用設定ファイルの作成

Kubernetesではetcdにクラスタ情報が格納されますが、ここではその格納データを暗号化するための設定を実施します。

  • (bastion) クラスタデータ暗号化用設定ファイルを作成し、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

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
  scp encryption-config.yaml centos@${instance}.bbrfkr.mydns.jp:~/
done

etcdクラスタの構築

ここではMasterサーバ上でetcdのHAクラスタを構築します。

  • (masters) etcdバイナリのダウンロードと適切な場所への配置を行います。
sudo yum -y install wget
wget "https://github.com/coreos/etcd/releases/download/v3.3.9/etcd-v3.3.9-linux-amd64.tar.gz"

tar -xvf etcd-v3.3.9-linux-amd64.tar.gz
sudo mv etcd-v3.3.9-linux-amd64/etcd* /usr/local/bin/
  • (masters) etcd用のsystemd unitファイルを作成し、サービスを有効化します。
sudo mkdir -p /etc/etcd /var/lib/etcd
sudo cp ca.pem kubernetes-key.pem kubernetes.pem /etc/etcd/

INTERNAL_IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
ETCD_NAME=$(hostname -s)
MASTER_0_HOSTNAME=hardway-master-0
MASTER_0_INTERNAL_IP=10.0.0.10
MASTER_1_HOSTNAME=hardway-master-1
MASTER_1_INTERNAL_IP=10.0.0.11
MASTER_2_HOSTNAME=hardway-master-2
MASTER_2_INTERNAL_IP=10.0.0.12
cat <<EOF | sudo tee /etc/systemd/system/etcd.service
[Unit]
Description=etcd
Documentation=https://github.com/coreos

[Service]
ExecStart=/usr/local/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_0_HOSTNAME}=https://${MASTER_0_INTERNAL_IP}:2380,${MASTER_1_HOSTNAME}=https://${MASTER_1_INTERNAL_IP}:2380,${MASTER_2_HOSTNAME}=https://${MASTER_2_INTERNAL_IP}:2380 \\
  --initial-cluster-state new \\
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable etcd
sudo systemctl start etcd
  • (masters) etcdの正常動作確認をするには以下のコマンドを実行します。
sudo su - -lc \
"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"

Masterサーバの構築

ここではMasterサーバの構築を実施します。

sudo mkdir -p /etc/kubernetes/config
  • (masters) Masterサーバ用コンポーネントのバイナリをダウンロードし、適切な場所に配置します。
wget \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-apiserver" \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-controller-manager" \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-scheduler" \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/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/local/bin/
  • (masters) kube-apiserver用の証明書群の配置とsystemd unitファイルを作成します。ここでKubernetesのServiceリソースで利用するIP帯10.32.0.0/24を指定します。
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/

INTERNAL_IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
MASTER_0_INTERNAL_IP=10.0.0.10
MASTER_1_INTERNAL_IP=10.0.0.11
MASTER_2_INTERNAL_IP=10.0.0.12

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=${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 \\
  --bind-address=0.0.0.0 \\
  --client-ca-file=/var/lib/kubernetes/ca.pem \\
  --enable-admission-plugins=Initializers,NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
  --enable-swagger-ui=true \\
  --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://${MASTER_0_INTERNAL_IP}:2379,https://${MASTER_1_INTERNAL_IP}:2379,https://${MASTER_2_INTERNAL_IP}:2379 \\
  --event-ttl=1h \\
  --experimental-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
EOF
  • (masters) kube-controller-manager用のkubeconfigの配置とsystemd unitファイルを作成します。--cluster-cidrオプションにはPodが利用するIP帯172.17.0.0/16を指定しておきます。
sudo mv 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/local/bin/kube-controller-manager \\
  --address=0.0.0.0 \\
  --cluster-cidr=172.17.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 \\
  --allocate-node-cidrs=true \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (masters) kube-scheduler用の設定ファイルおよびkubeconfigの配置とsystemd unitファイルを作成します。
sudo mv 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 <<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
  • (masters) 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
  • (masters(hardway-master-0のみ)) kube-apiserverがWorkerサーバのkubeletに接続可能にするため、RBACの設定を入れておきます。
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サーバの構築

ここではWorkerサーバを構築し、Kubernetesクラスタに参加させます。

  • (workers) OS依存パッケージのインストールを行います。
sudo yum -y install socat conntrack ipset
  • (workers) Workerサーバ用コンポーネントのバイナリをダウンロードし、適切な場所に配置します。
sudo yum -y install wget
wget \
  https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.13.0/crictl-v1.13.0-linux-amd64.tar.gz \
    https://github.com/containernetworking/plugins/releases/download/v0.6.0/cni-plugins-amd64-v0.6.0.tgz \
  https://github.com/opencontainers/runc/releases/download/v1.0.0-rc5/runc.amd64 \
  https://github.com/containerd/containerd/releases/download/v1.2.0-rc.0/containerd-1.2.0-rc.0.linux-amd64.tar.gz \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kubectl \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-proxy \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kubelet

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

sudo mv runc.amd64 runc
chmod +x kubectl kube-proxy kubelet runc
sudo mv kubectl kube-proxy kubelet runc /usr/local/bin/
sudo tar -xvf crictl-v1.13.0-linux-amd64.tar.gz -C /usr/local/bin/
sudo tar -xvf cni-plugins-amd64-v0.6.0.tgz -C /opt/cni/bin/
sudo tar -xvf containerd-1.2.0-rc.0.linux-amd64.tar.gz
sudo mv bin/* /bin
sudo rmdir bin
  • (workers) CNI(Container Network Interface)のloopbackインタフェースに対する設定を実施します。
cat <<EOF | sudo tee /etc/cni/net.d/99-loopback.conf
{
    "cniVersion": "0.3.1",
    "type": "loopback"
}
EOF
  • (workers) コンテナランタイム containerdの設定ファイルとsystemd unitファイルを配置します。本家Kubernetes the Hard WayではgVisorにも対応した設定を行っていますが、CentOS 7のカーネルがデフォルトでは古く、gVisorに対応していないため、省いています。
sudo mkdir -p /etc/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

cat <<EOF | sudo tee /etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target

[Service]
ExecStartPre=/sbin/modprobe overlay
ExecStart=/bin/containerd
Restart=always
RestartSec=5
Delegate=yes
KillMode=process
OOMScoreAdjust=-999
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity

[Install]
WantedBy=multi-user.target
EOF
  • (workers) kubelet用の証明書群およびkubeconfigを配置し、設定ファイルとsystemd unitファイルを作成します。
WORKER_HOSTNAME=$(hostname -s)
sudo mv ${WORKER_HOSTNAME}-key.pem ${WORKER_HOSTNAME}.pem /var/lib/kubelet/
sudo mv ${WORKER_HOSTNAME}.kubeconfig /var/lib/kubelet/kubeconfig
sudo mv ca.pem /var/lib/kubernetes/

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"
resolvConf: "/etc/resolv.conf"
runtimeRequestTimeout: "15m"
tlsCertFile: "/var/lib/kubelet/${WORKER_HOSTNAME}.pem"
tlsPrivateKeyFile: "/var/lib/kubelet/${WORKER_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/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 \\
  --hostname-override=${WORKER_HOSTNAME}
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (workers) kube-proxy用のkubeconfigを配置し、設定ファイルとsystemd unitファイルを作成します。
sudo mv 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: "172.17.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
  • (workers) Workerサーバのサービスを有効化します。同時にKubernetesクラスタへWorkerサーバ登録が行われます。
sudo systemctl daemon-reload
sudo systemctl enable containerd kubelet kube-proxy
sudo systemctl start containerd kubelet kube-proxy

CNI flannelとサービスディスカバリ corednsの導入

ここでは、作業用端末のkubectlを作成したKubernetesクラスタに接続できるよう設定し、CNIとサービスディスカバリを導入して最低限、Kubernetesが機能するようにします。

kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://kubernetes-the-hard-way.bbrfkr.mydns.jp: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
  • (bastion) CNI flannelをデプロイします。
wget https://raw.githubusercontent.com/coreos/flannel/bc79dd1505b0c8681ece4de4c0d86c5cd2643275/Documentation/kube-flannel.yml
sed -i 's@10.244.0.0/16@172.17.0.0/16@g' kube-flannel.yml
kubectl apply -f kube-flannel.yml
  • (bastion) サービスディスカバリ corednsをデプロイします。
kubectl apply -f https://storage.googleapis.com/kubernetes-the-hard-way/coredns.yaml