Tue, Apr 17, 2018

Kubernetes 1.10をスクラッチから全手動で構築

Kubernetes 1.10をスクラッチから全手動で構築

Oracle Linux 7.4.0のVMでKubernetes1.10.0のクラスタをスクラッチから全手動で作った。 参考にしたのは主に以下。

構成

  • マシン: Windows 10 Homeのラップトップの上のVMware PlayerのVM
    • CPU: 2コア
    • メモリ: 4GB
    • NIF: NATのを一つ
  • OS: Oracle Linux 7.4.0
    • Minimalインストール
    • IPアドレス: 192.168.171.200、静的割り当て
    • ホスト名: k8s-master (hostsで解決)
  • Docker: Oracle Container Runtime for Docker (docker-engine) 17.06.2
  • Kubernetes: バージョン1.10.0
    • 単一ノード
    • 全コンポーネント(kubelet、kube-proxy、kube-apiserver、kube-controller-manager、kube-scheduler、etcd)をsystemdで起動 (i.e. 非コンテナ)
    • コンポーネント間通信とkubectlの通信をTLSで暗号化
      • TLS 1.2
      • セキュアなCipher Suites
    • コンポーネント間通信とkubectlの通信の認証はx509クライアント証明書
    • TLS Bootstrapping
      • Bootstrap token使用
      • CSR自動承認
    • Certificate Rotation有効
    • etcd 3.1.12
    • flannel 0.10.0
    • CoreDNS 1.1.1
    • SERVICE_CLUSTER_IP_RANGE (Serviceに割り当てるIPの範囲) は10.0.0.0/16
      • kube-apiserverのIPはこの範囲の最初のIP(i.e. 10.0.0.1)になる。
      • ホストネットワークや、CLUSTER_CIDRと範囲が被らないようにする必要がある。
    • CLUSTER_CIDR (Podに割り当てるIPの範囲) は10.244.0.0/16
      • flannelの要件に合わせている。
      • ホストネットワークや、SERVICE_CLUSTER_IP_RANGEと範囲が被らないようにする必要がある。
    • Proxyモードはiptables。
      • ipvsのほうが速いけど、flannelとかがサポートしているかよくわからないので。


kubeletの動作条件にあるので、swapをoffにする。 Oracle Linuxにログインして、/etc/fstabのswapの行を削除して、以下のコマンドを実行。

# swapoff -a


SELinuxはちゃんと設定すればKubernetes動かせるはずだけど、面倒なのでとりあえず無効にする。

/etc/selinux/configを編集して、SELINUXpermissiveにして、以下のコマンドを実行。

# setenforce 0


ファイアウォールもちゃんと設定すればいいんだけど面倒なのでとりあえず無効にする。

# systemctl stop firewalld
# systemctl disable firewalld


これで準備完了。

クラスタ構築手順

おおむね、k8sコンポーネント間の通信の暗号化に使う鍵と証明書の生成、各コンポーネント用kubeconfigの生成、etcdのデプロイ、k8sコンポーネントのデプロイ、fannelデプロイ、CoreDNSデプロイ、という流れ。 ついでに最後にWeave Scopeをデプロイしてみる。

  1. Bridge netfilterとIP forwardingを設定

    まず、Bridge netfilterモジュールをロードする。

    # modprobe br_netfilter
    # echo "br_netfilter" > /etc/modules-load.d/br_netfilter.conf
    

    Bridge netfilterとIP forwardingを有効化する。

    # cat > /etc/sysctl.d/kubernetes.conf << EOF
    net.bridge.bridge-nf-call-iptables = 1
    net.bridge.bridge-nf-call-ip6tables = 1
    net.ipv4.ip_forward = 1
    EOF
    # sysctl -p /etc/sysctl.d/kubernetes.conf
    


    設定確認。

    # lsmod |grep br_netfilter
    # sysctl -a | grep -E "net.bridge.bridge-nf-call-|net.ipv4.ip_forward"
    
  2. x509証明書生成

    1. opensslの設定作成

      # mkdir -p /etc/kubernetes/pki
      # HOSTNAME=k8s-master
      # K8S_SERVICE_IP=10.0.0.1
      # MASTER_IP=192.168.171.200
      # cat > /etc/kubernetes/pki/openssl.cnf << EOF
      [ req ]
      distinguished_name = req_distinguished_name
      [req_distinguished_name]
      [ v3_ca ]
      basicConstraints = critical, CA:TRUE
      keyUsage = critical, digitalSignature, keyEncipherment, keyCertSign
      [ v3_req_client ]
      basicConstraints = CA:FALSE
      keyUsage = critical, digitalSignature, keyEncipherment
      extendedKeyUsage = clientAuth
      [ v3_req_apiserver ]
      basicConstraints = CA:FALSE
      keyUsage = critical, digitalSignature, keyEncipherment
      extendedKeyUsage = serverAuth
      subjectAltName = @alt_names_cluster
      [ v3_req_etcd ]
      basicConstraints = CA:FALSE
      keyUsage = critical, digitalSignature, keyEncipherment
      extendedKeyUsage = serverAuth
      subjectAltName = @alt_names_etcd
      [ alt_names_cluster ]
      DNS.1 = kubernetes
      DNS.2 = kubernetes.default
      DNS.3 = kubernetes.default.svc
      DNS.4 = kubernetes.default.svc.cluster.local
      DNS.5 = ${HOSTNAME}
      IP.1 = ${MASTER_IP}
      IP.2 = ${K8S_SERVICE_IP}
      [ alt_names_etcd ]
      DNS.1 = ${HOSTNAME}
      IP.1 = ${MASTER_IP}
      EOF
      
    2. Kubernetes CA証明書生成

      以降で生成する証明書に署名するための証明書。 後述のTLS Bootstrappingでの証明書生成にも使う。

      # groupadd -r kubernetes
      # adduser -r -g kubernetes -M -s /sbin/nologin kubernetes
      # CA_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/ca.key
      # chown kubernetes:kubernetes /etc/kubernetes/pki/ca.key
      # chmod 0600 /etc/kubernetes/pki/ca.key
      # openssl req -x509 -new -sha256 -nodes -key /etc/kubernetes/pki/ca.key -days $CA_DAYS -out /etc/kubernetes/pki/ca.crt -subj "/CN=kubernetes-ca"  -extensions v3_ca -config /etc/kubernetes/pki/openssl.cnf
      
    3. kube-apiserver証明書生成

      kube-apiserverのサーバ証明書。

      # APISERVER_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/kube-apiserver.key
      # chown kubernetes:kubernetes /etc/kubernetes/pki/kube-apiserver.key
      # chmod 0600 /etc/kubernetes/pki/kube-apiserver.key
      # openssl req -new -sha256 -key /etc/kubernetes/pki/kube-apiserver.key -subj "/CN=kube-apiserver" | openssl x509 -req -sha256 -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out /etc/kubernetes/pki/kube-apiserver.crt -days $APISERVER_DAYS -extensions v3_req_apiserver -extfile /etc/kubernetes/pki/openssl.cnf
      
    4. kube-apiserver-kubelet証明書生成

      kube-apiserverがkubeletのAPIにアクセスするときのクライアント証明書。

      # APISERVER_KUBELET_CLIENT_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/apiserver-kubelet-client.key
      # chown kubernetes:kubernetes /etc/kubernetes/pki/apiserver-kubelet-client.key
      # chmod 0600 /etc/kubernetes/pki/apiserver-kubelet-client.key
      # openssl req -new -key /etc/kubernetes/pki/apiserver-kubelet-client.key -subj "/CN=kube-apiserver-kubelet-client/O=system:masters" | openssl x509 -req -sha256 -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out /etc/kubernetes/pki/apiserver-kubelet-client.crt -days $APISERVER_KUBELET_CLIENT_DAYS -extensions v3_req_client -extfile /etc/kubernetes/pki/openssl.cnf
      
    5. adminクライアント証明書生成

      kubectlがkube-apiserverのAPIにアクセスするときのクライアント証明書。

      # groupadd -r kube-admin
      # adduser -r -g kube-admin -M -s /sbin/nologin kube-admin
      # ADMIN_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/admin.key
      # chown kube-admin:kube-admin /etc/kubernetes/pki/admin.key
      # chmod 0600 /etc/kubernetes/pki/admin.key
      # openssl req -new -key /etc/kubernetes/pki/admin.key -subj "/CN=kubernetes-admin/O=system:masters" | openssl x509 -req -sha256 -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out /etc/kubernetes/pki/admin.crt -days $ADMIN_DAYS -extensions v3_req_client -extfile /etc/kubernetes/pki/openssl.cnf
      
    6. kube-controller-managerのクライアント証明書生成

      kube-controller-managerがkube-apiserverに接続するときのクライアント証明書。 この証明書に対応する秘密鍵と公開鍵はそれぞれ、kube-controller-managerがService Accountトークンに署名するとき、kube-apiserverがトークンの署名を確認するときにも使う。

      # CONTROLLER_MANAGER_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/kube-controller-manager.key
      # openssl ec -in /etc/kubernetes/pki/kube-controller-manager.key -outform PEM -pubout -out /etc/kubernetes/pki/kube-controller-manager.pub
      # chown kubernetes:kubernetes /etc/kubernetes/pki/kube-controller-manager.key
      # chmod 0600 /etc/kubernetes/pki/kube-controller-manager.key
      # openssl req -new -sha256 -key /etc/kubernetes/pki/kube-controller-manager.key -subj "/CN=system:kube-controller-manager" | openssl x509 -req -sha256 -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out /etc/kubernetes/pki/kube-controller-manager.crt -days $CONTROLLER_MANAGER_DAYS -extensions v3_req_client -extfile /etc/kubernetes/pki/openssl.cnf
      
    7. kube-schedulerクライアント証明書生成

      kube-schedulerがkube-apiserverにリクエストするときに使うクライアント証明書。

      # SCHEDULER_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/kube-scheduler.key
      # chown kubernetes:kubernetes /etc/kubernetes/pki/kube-scheduler.key
      # chmod 0600 /etc/kubernetes/pki/kube-scheduler.key
      # openssl req -new -sha256 -key /etc/kubernetes/pki/kube-scheduler.key -subj "/CN=system:kube-scheduler" | openssl x509 -req -sha256 -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out /etc/kubernetes/pki/kube-scheduler.crt -days $SCHEDULER_DAYS -extensions v3_req_client -extfile /etc/kubernetes/pki/openssl.cnf
      
    8. kube-proxyクライアント証明書生成

      kube-proxyがkube-apiserverにリクエストするときに使うクライアント証明書。

      # PROXY_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/kube-proxy.key
      # chown kubernetes:kubernetes /etc/kubernetes/pki/kube-proxy.key
      # chmod 0600 /etc/kubernetes/pki/kube-proxy.key
      # openssl req -new -sha256 -key /etc/kubernetes/pki/kube-proxy.key -subj "/CN=system:kube-proxy" | openssl x509 -req -sha256 -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out /etc/kubernetes/pki/kube-proxy.crt -days $PROXY_DAYS -extensions v3_req_client -extfile /etc/kubernetes/pki/openssl.cnf
      
    9. front proxy CA証明書生成

      front proxyの証明書に署名するのにつかう証明書。 front proxyはAPI Aggregationのためのもの。 API Aggregationは、kube-apiserverを変更することなく、別途作られたExtension API ServerでKubernetesのAPIを拡張できるようにする機能。 API Aggregationは現時点ではkube-apiserverの一機能として実装されていて、将来的にはkubernetes-aggregatorという別のコンポーネントで実現される。

      API AggregationしないならこのCA証明書と次のクライアント証明書はいらないはず。 今回はしないけど、とりあえず作って設定したおく。

      # FRONT_PROXY_CA_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/front-proxy-ca.key
      # chown kubernetes:kubernetes /etc/kubernetes/pki/front-proxy-ca.key
      # chmod 0600 /etc/kubernetes/pki/front-proxy-ca.key
      # openssl req -x509 -new -sha256 -nodes -key /etc/kubernetes/pki/front-proxy-ca.key -days $FRONT_PROXY_CA_DAYS -out /etc/kubernetes/pki/front-proxy-ca.crt -subj "/CN=front-proxy-ca" -extensions v3_ca -config /etc/kubernetes/pki/openssl.cnf
      
    10. front proxyクライアント証明書

      Extension API ServerのAPIへのリクエストは、いったんkube-apiserverが受け取ってExtension API Serverに転送される。(多分。) この転送の暗号化と認証にTLSが使われていて、ここではそのクライアント証明書を生成する。

      # FRONT_PROXY_CLIENT_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/front-proxy-client.key
      # chown kubernetes:kubernetes /etc/kubernetes/pki/front-proxy-client.key
      # chmod 0600 /etc/kubernetes/pki/front-proxy-client.key
      # openssl req -new -sha256 -key /etc/kubernetes/pki/front-proxy-client.key -subj "/CN=front-proxy-client" | openssl x509 -req -sha256 -CA /etc/kubernetes/pki/front-proxy-ca.crt -CAkey /etc/kubernetes/pki/front-proxy-ca.key -CAcreateserial -out /etc/kubernetes/pki/front-proxy-client.crt -days $FRONT_PROXY_CLIENT_DAYS -extensions v3_req_client -extfile /etc/kubernetes/pki/openssl.cnf
      
    11. etcd CA証明書

      以降で生成するetcdの証明書に署名するための証明書。

      # groupadd -r etcd
      # adduser -r -g etcd -M -s /sbin/nologin etcd
      # ETCD_CA_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/etcd-ca.key
      # chown etcd:etcd /etc/kubernetes/pki/etcd-ca.key
      # chmod 0600 /etc/kubernetes/pki/etcd-ca.key
      # openssl req -x509 -new -sha256 -nodes -key /etc/kubernetes/pki/etcd-ca.key -days $ETCD_CA_DAYS -out /etc/kubernetes/pki/etcd-ca.crt -subj "/CN=etcd-ca" -extensions v3_ca -config /etc/kubernetes/pki/openssl.cnf
      
    12. etcd証明書

      etcdのサーバ証明書。

      # ETCD_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/etcd.key
      # chown etcd:etcd /etc/kubernetes/pki/etcd.key
      # chmod 0600 /etc/kubernetes/pki/etcd.key
      # openssl req -new -sha256 -key /etc/kubernetes/pki/etcd.key -subj "/CN=etcd" | openssl x509 -req -sha256 -CA /etc/kubernetes/pki/etcd-ca.crt -CAkey /etc/kubernetes/pki/etcd-ca.key -CAcreateserial -out /etc/kubernetes/pki/etcd.crt -days $ETCD_DAYS -extensions v3_req_etcd -extfile /etc/kubernetes/pki/openssl.cnf
      
    13. etcdクライアント証明書

      etcdのクライアント証明書。 kube-apiserverだけがetcdと話すので、kube-apiserverだけが使う。

      # ETCD_CLIENT_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/etcd-client.key
      # chown kubernetes:kubernetes /etc/kubernetes/pki/etcd-client.key
      # chmod 0600 /etc/kubernetes/pki/etcd-client.key
      # openssl req -new -sha256 -key /etc/kubernetes/pki/etcd-client.key -subj "/CN=kube-apiserver" | openssl x509 -req -sha256 -CA /etc/kubernetes/pki/etcd-ca.crt -CAkey /etc/kubernetes/pki/etcd-ca.key -CAcreateserial -out /etc/kubernetes/pki/etcd-client.crt -days $ETCD_CLIENT_DAYS -extensions v3_req_client -extfile /etc/kubernetes/pki/openssl.cnf
      
    14. etcd peer証明書

      etcdサーバが冗長構成のとき、サーバ間の通信の暗号化に使う証明書。 マスタが一つなら要らないはずだけど、今回とりあえず作って設定しておく。

      # ETCD_PEER_DAYS=5475
      # openssl ecparam -name secp521r1 -genkey -noout -out /etc/kubernetes/pki/etcd-peer.key
      # chown etcd:etcd /etc/kubernetes/pki/etcd-peer.key
      # chmod 0600 /etc/kubernetes/pki/etcd-peer.key
      # openssl req -new -sha256 -key /etc/kubernetes/pki/etcd-peer.key -subj "/CN=etcd-peer" | openssl x509 -req -sha256 -CA /etc/kubernetes/pki/etcd-ca.crt -CAkey /etc/kubernetes/pki/etcd-ca.key -CAcreateserial -out /etc/kubernetes/pki/etcd-peer.crt -days $ETCD_PEER_DAYS -extensions v3_req_etcd -extfile /etc/kubernetes/pki/openssl.cnf
      
    15. 確認

      以上で生成した証明書の内容を確認する。

      # for i in /etc/kubernetes/pki/*crt; do
        echo $i:;
        openssl x509 -subject -issuer -noout -in $i;
        echo;
      done
      
  3. Kubernetesバイナリインストール

    公式ドキュメントによると、Docker、kubelet、kube-proxyはコンテナ外で動かして、etcd、kube-apiserver、kube-controller-manager、kube-schedulerはコンテナで動かすのが推奨されている。 けど、とりあえずは簡単に全部コンテナ外でやる。

    (Oracle Linux用には、各コンポのコンテナイメージ詰め合わせがOracle Container Services for use with Kubernetesという名前で配布されているけど、現時点で1.9までしかないので使わない。)

    バイナリは以下URLからダウンロードできる。

    最後のhyperkubeは、各種Kubernetesバイナリのごった煮。 ファイル名によって動作が変わる。 簡単のためこれを使うけど、個別のバイナリ使ったほうがメモリ使用量などで有利そう。

    hyperkubeとkubeadmのバイナリを/usr/bin/において、以下のコマンドを実行。

    # ln -s /usr/bin/hyperkube /usr/bin/kube-apiserver
    # ln -s /usr/bin/hyperkube /usr/bin/kube-controller-manager
    # ln -s /usr/bin/hyperkube /usr/bin/kube-scheduler
    # ln -s /usr/bin/hyperkube /usr/bin/kube-proxy
    # ln -s /usr/bin/hyperkube /usr/bin/kubelet
    # ln -s /usr/bin/hyperkube /usr/bin/kubectl
    # chmod +x /usr/bin/kube*
    # mkdir -p /var/lib/{kubelet,kube-proxy}
    
  4. kubeconfigファイル生成

    kubectlとマスタコンポーネントがkube-apiserverと話すときに使うkubeconfigファイルを生成する。

    1. kube-controller-managerのkubeconfig

      # MASTER_IP=192.168.171.200
      # KUBERNETES_PUBLIC_ADDRESS=$MASTER_IP
      # CLUSTER_NAME="k8s"
      # KCONFIG=/etc/kubernetes/kube-controller-manager.kubeconfig
      # KUSER="system:kube-controller-manager"
      # kubectl config set-cluster ${CLUSTER_NAME} --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 --kubeconfig=${KCONFIG}
      # kubectl config set-credentials ${KUSER} --client-certificate=/etc/kubernetes/pki/kube-controller-manager.crt --client-key=/etc/kubernetes/pki/kube-controller-manager.key --embed-certs=true --kubeconfig=${KCONFIG}
      # kubectl config set-context ${KUSER}@${CLUSTER_NAME} --cluster=${CLUSTER_NAME} --user=${KUSER} --kubeconfig=${KCONFIG}
      # kubectl config use-context ${KUSER}@${CLUSTER_NAME} --kubeconfig=${KCONFIG}
      # chown kubernetes:kubernetes ${KCONFIG}
      # chmod 0600 ${KCONFIG}
      

      設定確認。

      # kubectl config view --kubeconfig=${KCONFIG}
      
    2. kube-schedulerのkubeconfig

      # MASTER_IP=192.168.171.200
      # KUBERNETES_PUBLIC_ADDRESS=$MASTER_IP
      # CLUSTER_NAME="k8s"
      # KCONFIG=/etc/kubernetes/kube-scheduler.kubeconfig
      # KUSER="system:kube-scheduler"
      # kubectl config set-cluster ${CLUSTER_NAME} --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 --kubeconfig=${KCONFIG}
      # kubectl config set-credentials ${KUSER} --client-certificate=/etc/kubernetes/pki/kube-scheduler.crt --client-key=/etc/kubernetes/pki/kube-scheduler.key --embed-certs=true --kubeconfig=${KCONFIG}
      # kubectl config set-context ${KUSER}@${CLUSTER_NAME} --cluster=${CLUSTER_NAME} --user=${KUSER} --kubeconfig=${KCONFIG}
      # kubectl config use-context ${KUSER}@${CLUSTER_NAME} --kubeconfig=${KCONFIG}
      # chown kubernetes:kubernetes ${KCONFIG}
      # chmod 0600 ${KCONFIG}
      

      設定確認。

      # kubectl config view --kubeconfig=${KCONFIG}
      
    3. adminのkubeconfig

      kubectl用。

      # MASTER_IP=192.168.171.200
      # KUBERNETES_PUBLIC_ADDRESS=$MASTER_IP
      # CLUSTER_NAME="k8s"
      # KCONFIG=/etc/kubernetes/admin.kubeconfig
      # KUSER="kubernetes-admin"
      # kubectl config set-cluster ${CLUSTER_NAME} --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 --kubeconfig=${KCONFIG}
      # kubectl config set-credentials ${KUSER} --client-certificate=/etc/kubernetes/pki/admin.crt --client-key=/etc/kubernetes/pki/admin.key --embed-certs=true --kubeconfig=${KCONFIG}
      # kubectl config set-context ${KUSER}@${CLUSTER_NAME} --cluster=${CLUSTER_NAME} --user=${KUSER} --kubeconfig=${KCONFIG}
      # kubectl config use-context ${KUSER}@${CLUSTER_NAME} --kubeconfig=${KCONFIG}
      # chown kube-admin:kube-admin ${KCONFIG}
      # chmod 0600 ${KCONFIG}
      # ln -s ${KCONFIG} ~/.kube/config
      

      設定確認。

      # kubectl config view --kubeconfig=${KCONFIG}
      
  5. etcdデプロイ

    https://github.com/coreos/etcd/releases/download/v3.1.12/etcd-v3.1.12-linux-amd64.tar.gz からアーカイブをダウンロードして、中のetcdとetcdctlを/usr/bin/にいれて、以下のコマンドを実行。

    # chown root:root /usr/bin/etcd*
    # chmod 0755 /usr/bin/etcd*
    # mkdir -p /var/lib/etcd
    # chown etcd:etcd /var/lib/etcd
    

    で、systemdのユニットファイルを書いてサービス化。

    (参考: Kubernetesドキュメントetcdドキュメント)

    # MASTER_IP=192.168.171.200
    # ETCD_MEMBER_NAME=etcd1
    # CLUSTER_NAME="k8s"
    # ETCD_TOKEN=$(openssl rand -hex 5)
    # ETCD_CLUSTER_TOKEN=$CLUSTER_NAME-$ETCD_TOKEN
    # cat > /etc/systemd/system/etcd.service << EOF
    [Unit]
    Description=etcd
    Documentation=https://coreos.com/etcd/docs/latest/
    After=network.target
    
    
    [Service]
    Type=notify
    NotifyAccess=all
    User=etcd
    Group=etcd
    ExecStart=/usr/bin/etcd \\
      --name ${ETCD_MEMBER_NAME} \\
      --listen-client-urls https://${MASTER_IP}:2379 \\
      --advertise-client-urls https://${MASTER_IP}:2379 \\
      --data-dir=/var/lib/etcd \\
      --cert-file=/etc/kubernetes/pki/etcd.crt \\
      --key-file=/etc/kubernetes/pki/etcd.key \\
      --peer-cert-file=/etc/kubernetes/pki/etcd-peer.crt \\
      --peer-key-file=/etc/kubernetes/pki/etcd-peer.key \\
      --trusted-ca-file=/etc/kubernetes/pki/etcd-ca.crt \\
      --peer-trusted-ca-file=/etc/kubernetes/pki/etcd-ca.crt \\
      --peer-client-cert-auth \\
      --client-cert-auth \\
      --initial-advertise-peer-urls https://${MASTER_IP}:2380 \\
      --listen-peer-urls https://${MASTER_IP}:2380 \\
      --initial-cluster-token ${ETCD_CLUSTER_TOKEN} \\
      --initial-cluster ${ETCD_MEMBER_NAME}=https://${MASTER_IP}:2380 \\
      --initial-cluster-state new
    Restart=always
    RestartSec=10s
    
    
    [Install]
    WantedBy=multi-user.target
    EOF
    # systemctl daemon-reload
    # systemctl enable etcd
    # systemctl start etcd
    

    確認。

    # systemctl status etcd -l
    # MASTER_IP=192.168.171.200
    # etcdctl --endpoints https://${MASTER_IP}:2379 --ca-file=/etc/kubernetes/pki/etcd-ca.crt --cert-file=/etc/kubernetes/pki/etcd-client.crt --key-file=/etc/kubernetes/pki/etcd-client.key cluster-health
    # etcdctl --endpoints https://${MASTER_IP}:2379 --ca-file=/etc/kubernetes/pki/etcd-ca.crt --cert-file=/etc/kubernetes/pki/etcd-client.crt --key-file=/etc/kubernetes/pki/etcd-client.key member list
    
  6. マスタコンポーネントデプロイ。

    1. kube-apiserver

      systemdのユニットファイルを書いてサービス化。

      • d
      # mkdir -p /var/log/kubernetes
      # chown kubernetes:kubernetes /var/log/kubernetes
      # chmod 0700 /var/log/kubernetes
      # MASTER_IP=192.168.171.200
      # SERVICE_CLUSTER_IP_RANGE="10.0.0.0/16"
      # SECRET_ENC_KEY=$(echo -n 'your_32_bytes_secure_private_key' | base64)
      # cat > /etc/kubernetes/encryption.conf << EOF
      kind: EncryptionConfig
      apiVersion: v1
      resources:
        - resources:
          - secrets
          providers:
          - aescbc:
              keys:
              - name: key1
                secret: ${SECRET_ENC_KEY}
          - identity: {}
      EOF
      # cat > /etc/kubernetes/audit-policy.conf << EOF
      apiVersion: audit.k8s.io/v1beta1
      kind: Policy
      # Don't generate audit events for all requests in RequestReceived stage.
      omitStages:
        - "RequestReceived"
      rules:
        # Log pod changes at RequestResponse level
        - level: RequestResponse
          resources:
          - group: ""
            # Resource "pods" doesn't match requests to any subresource of pods,
            # which is consistent with the RBAC policy.
            resources: ["pods"]
        # Log "pods/log", "pods/status" at Metadata level
        - level: Metadata
          resources:
          - group: ""
            resources: ["pods/log", "pods/status"]
      
      
        # Don't log requests to a configmap called "controller-leader"
        - level: None
          resources:
          - group: ""
            resources: ["configmaps"]
            resourceNames: ["controller-leader"]
      
      
        # Don't log watch requests by the "system:kube-proxy" on endpoints or services
        - level: None
          users: ["system:kube-proxy"]
          verbs: ["watch"]
          resources:
          - group: "" # core API group
            resources: ["endpoints", "services"]
      
      
        # Don't log authenticated requests to certain non-resource URL paths.
        - level: None
          userGroups: ["system:authenticated"]
          nonResourceURLs:
          - "/api*" # Wildcard matching.
          - "/version"
      
      
        # Log the request body of configmap changes in kube-system.
        - level: Request
          resources:
          - group: "" # core API group
            resources: ["configmaps"]
          # This rule only applies to resources in the "kube-system" namespace.
          # The empty string "" can be used to select non-namespaced resources.
          namespaces: ["kube-system"]
      
      
        # Log configmap and secret changes in all other namespaces at the Metadata level.
        - level: Metadata
          resources:
          - group: "" # core API group
            resources: ["secrets", "configmaps"]
      
      
        # Log all other resources in core and extensions at the Request level.
        - level: Request
          resources:
          - group: "" # core API group
          - group: "extensions" # Version of group should NOT be included.
      
      
        # A catch-all rule to log all other requests at the Metadata level.
        - level: Metadata
          # Long-running requests like watches that fall under this rule will not
          # generate an audit event in RequestReceived.
          omitStages:
            - "RequestReceived"
      EOF
      # cat > /etc/systemd/system/kube-apiserver.service << EOF
      [Unit]
      Description=Kubernetes API Server
      Documentation=https://github.com/kubernetes/kubernetes
      After=network.target
      
      
      [Service]
      User=kubernetes
      Group=kubernetes
      ExecStart=/usr/bin/kube-apiserver \\
        --feature-gates=RotateKubeletServerCertificate=true \\
        --apiserver-count=1 \\
        --allow-privileged=true \\
        --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction,DenyEscalatingExec,StorageObjectInUseProtection \\
        --authorization-mode=Node,RBAC \\
        --bind-address=0.0.0.0 \\
        --advertise-address=${MASTER_IP} \\
        --client-ca-file=/etc/kubernetes/pki/ca.crt \\
        --etcd-cafile=/etc/kubernetes/pki/etcd-ca.crt \\
        --etcd-certfile=/etc/kubernetes/pki/etcd-client.crt \\
        --etcd-keyfile=/etc/kubernetes/pki/etcd-client.key \\
        --etcd-servers=https://${MASTER_IP}:2379 \\
        --service-account-key-file=/etc/kubernetes/pki/kube-controller-manager.pub \\
        --service-cluster-ip-range=${SERVICE_CLUSTER_IP_RANGE} \\
        --tls-cert-file=/etc/kubernetes/pki/kube-apiserver.crt \\
        --tls-private-key-file=/etc/kubernetes/pki/kube-apiserver.key \\
        --kubelet-certificate-authority=/etc/kubernetes/pki/ca.crt \\
        --enable-bootstrap-token-auth=true \\
        --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt \\
        --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key \\
        --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname \\
        --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt \\
        --requestheader-username-headers=X-Remote-User \\
        --requestheader-group-headers=X-Remote-Group \\
        --requestheader-allowed-names=front-proxy-client \\
        --requestheader-extra-headers-prefix=X-Remote-Extra- \\
        --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt \\
        --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key \\
        --experimental-encryption-provider-config=/etc/kubernetes/encryption.conf \\
        --v=2 \\
        --tls-min-version=VersionTLS12 \\
        --tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 \\
        --anonymous-auth=false \\
        --audit-log-format=json \\
        --audit-log-maxage=30 \\
        --audit-log-maxbackup=3 \\
        --audit-log-maxsize=100 \\
        --audit-log-path=/var/log/kubernetes/kube-audit.log \\
        --audit-policy-file=/etc/kubernetes/audit-policy.conf
      Restart=always
      RestartSec=10s
      
      
      [Install]
      WantedBy=multi-user.target
      EOF
      # systemctl daemon-reload
      # systemctl enable kube-apiserver
      # systemctl start kube-apiserver
      

      --allow-privilegedはflannelなどに必要。

      --enable-admission-pluginsには公式推奨のプラグインに加えて、後述のTLS BootstrappingのためのNodeRestrictionを指定。 また、--allow-privilegedの効果を軽減するため、DenyEscalatingExecも追加で指定。 また、使われているPersistent VolumeやPersistent Volume Claimが誤って消されることを防ぐStorageObjectInUseProtectionも追加で指定。 因みに、プラグインを指定する順番はKubernetes 1.10からは気にしなくてよくなった。

      --authorization-modeにはRBACを指定するのが標準。 後述のTLS Bootstrappingをするなら、Nodeも要る。

      --experimental-encryption-provider-configSecretを暗号化するための設定。 暗号化のキーをローテーションすることもできるけど、それはやってない。

      --tls-min-version--tls-cipher-suitesOpenSSLクックブックGoのtlsパッケージドキュメントを参考に設定。 RSA鍵交換はNG、RC4と3DESもNG、AESの鍵長は128ビット以上、SHA1はNG。

      また、(–tls-min-versionをVersionTLS12にする場合?)TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256が必須で、CBCモードがNG。(参照: https://github.com/golang/go/blob/release-branch.go1.9/src/net/http/h2_bundle.go)

      --anonymous-auth=falseはセキュリティのため設定。

      --requestheader-*--proxy-client-*は上記API Aggregationのための設定。

      --audit-*は監査ログ設定。 100MB3面のログを30日間保持する。 ログポリシーは公式のサンプルそのまま。

      --feature-gatesでRotateKubeletServerCertificateを有効にして、kubeletのサーバ証明書を自動更新するようにしている。 因みに、クライアント証明書を自動更新するRotateKubeletClientCertificateはデフォルトで有効。 これらがCertificate Rotationと呼ばれる機能。

      --feature-gatesは全Kubernetesコンポーネントで同じ値を指定するのがよさそう。

      確認。

      # systemctl status kube-apiserver -l
      # journalctl -u kube-apiserver
      
    2. kube-controller-manager

      systemdのユニットファイルを書いてサービス化。

      # CLUSTER_CIDR="10.244.0.0/16"
      # SERVICE_CLUSTER_IP_RANGE="10.0.0.0/16"
      # CLUSTER_NAME="k8s"
      # cat > /etc/systemd/system/kube-controller-manager.service << EOF
      [Unit]
      Description=Kubernetes Controller Manager
      Documentation=https://github.com/kubernetes/kubernetes
      After=network.target
      
      
      [Service]
      User=kubernetes
      Group=kubernetes
      ExecStart=/usr/bin/kube-controller-manager \\
        --feature-gates=RotateKubeletServerCertificate=true \\
        --kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
        --bind-address=0.0.0.0 \\
        --controllers=*,bootstrapsigner,tokencleaner \\
        --service-account-private-key-file=/etc/kubernetes/pki/kube-controller-manager.key \\
        --allocate-node-cidrs=true \\
        --cluster-cidr=${CLUSTER_CIDR} \\
        --node-cidr-mask-size=24 \\
        --cluster-name=${CLUSTER_NAME} \\
        --service-cluster-ip-range=${SERVICE_CLUSTER_IP_RANGE} \\
        --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt \\
        --cluster-signing-key-file=/etc/kubernetes/pki/ca.key \\
        --root-ca-file=/etc/kubernetes/pki/ca.crt \\
        --use-service-account-credentials=true \\
        --v=2 \\
        --experimental-cluster-signing-duration=8760h0m0s
      Restart=always
      RestartSec=10s
      
      
      [Install]
      WantedBy=multi-user.target
      EOF
      # systemctl daemon-reload
      # systemctl enable kube-controller-manager
      # systemctl start kube-controller-manager
      

      期限の切れたBootstrap token(後述)を消すためにtokencleanerを有効にしている。

      bootstrapsignerは後述のcluster-infoにBootstrap tokenで署名するためのコントローラ。

      csrapprovingというコントローラがデフォルトで有効になっていて、後述のTLS BootstrapppingやCertificate Rotationの時に作られるCSRを自動で承認する。

      --cluster-cidrで指定するネットワークは、後述のネットワークプロバイダの設定と合っている必要がある。 --allocate-node-cidrs--cluster-cidrの前提。

      --node-cidr-mask-sizeは、ノードに使うネットワークのサイズを指定するオプションで、--cluster-cidrで指定したネットワークの一部になるようにする。 --cluster-cidr/16を指定した場合、半分の/24にするのが普通。 つまり256ノードまで作れて、それぞれ254個のPodをホストできるような構成。

      --experimental-cluster-signing-durationは、Certificate Rotationのための設定で、自動発行する証明書の期限を1年に指定している。

      --use-service-account-credentialsをつけると、各コントローラが別々のService Accountで動く

      --secure-port--tls-*は、ヘルスチェックAPIをHTTPSにするだけで意味が無いし、設定するとkubectl get componentstatusesでエラーが出るようになるので、設定しないほうがいい。

      確認。

      # systemctl status kube-controller-manager -l
      
    3. kube-scheduler

      systemdのユニットファイルを書いてサービス化。

      # cat > /etc/systemd/system/kube-scheduler.service << EOF
      [Unit]
      Description=Kubernetes Scheduler
      Documentation=https://github.com/kubernetes/kubernetes
      After=network.target
      
      
      [Service]
      User=kubernetes
      Group=kubernetes
      ExecStart=/usr/bin/kube-scheduler \\
        --feature-gates=RotateKubeletServerCertificate=true \\
        --kubeconfig=/etc/kubernetes/kube-scheduler.kubeconfig \\
        --address=0.0.0.0 \\
        --v=2
      Restart=always
      RestartSec=10s
      
      
      [Install]
      WantedBy=multi-user.target
      EOF
      # systemctl daemon-reload
      # systemctl enable kube-scheduler
      # systemctl start kube-scheduler
      

      確認。

      # systemctl status kube-scheduler -l
      
    4. マスタコンポーネント状態確認

      # kubectl version
      # kubectl get componentstatuses
      
  7. TLS Bootstrappingの設定

    TLS Bootstrappingは、Kubernetesクラスタのコンポーネント間の通信がTLSで暗号化されている環境で、ノードが新たにクラスタに参加するとき、自動的にセキュアにCSRを処理する仕組み。

    TLS Bootstrappingでは、kubeletは起動時にBootstrap kubeconfigを読んで、kubeletとノード用のCSRを生成し、それらがkube-controller-managerに承認されると、kubelet用のクライアント証明書と秘密鍵を生成する。 その証明書と鍵を使ってkubeconfigを生成し、以降のクラスタへの接続に使う。

    Bootstrap時の認証にはBootstrap TokensToken authentication fileを使うことが推奨されていて、今回は前者を使う。

    (後者についてはこの記事に詳しい。)

    1. Bootstrap TokenのSecret生成

      以下のように生成できる。

      # TOKEN_PUB=$(openssl rand -hex 3)
      # TOKEN_SECRET=$(openssl rand -hex 8)
      # BOOTSTRAP_TOKEN="${TOKEN_PUB}.${TOKEN_SECRET}"
      # kubectl -n kube-system create secret generic bootstrap-token-${TOKEN_PUB} --type 'bootstrap.kubernetes.io/token' --from-literal description="cluster bootstrap token" --from-literal token-id=${TOKEN_PUB} --from-literal token-secret=${TOKEN_SECRET} --from-literal usage-bootstrap-authentication=true --from-literal usage-bootstrap-signing=true --from-literal auth-extra-groups=system:bootstrappers:worker,system:bootstrappers:ingress
      

      けど、kubeadmでも生成出来てこっちのほうが楽なので、それで。

      # BOOTSTRAP_TOKEN=$(kubeadm token create --kubeconfig /etc/kubernetes/admin.kubeconfig)
      

      BOOTSTRAP_TOKENの値はあとで使う。

      expirationは指定できなくて、1日で期限切れになっちゃうけど、クラスタにノードを追加するときに有効であればいいのでまあいい。

      確認。

      # TOKEN_PUB=$(echo $BOOTSTRAP_TOKEN | sed -e s/\\..*//)
      # kubectl -n kube-system get secret/bootstrap-token-${TOKEN_PUB} -o yaml
      
    2. Bootstrap kubeconfig作成

      Bootstrap時はkubelet-bootstrapというユーザでkube-apiserverに接続する。 kubelet-bootstrapsystem:node-bootstrapperロールを持ってsystem:bootstrappersに属しているユーザとして認証される必要がある。

      # mkdir -p /etc/kubernetes/manifests
      # MASTER_IP=192.168.171.200
      # KUBERNETES_PUBLIC_ADDRESS=$MASTER_IP
      # CLUSTER_NAME="k8s"
      # KCONFIG="/etc/kubernetes/bootstrap.kubeconfig"
      # KUSER="kubelet-bootstrap"
      # kubectl config set-cluster ${CLUSTER_NAME} --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 --kubeconfig=${KCONFIG}
      # kubectl config set-context ${KUSER}@${CLUSTER_NAME} --cluster=${CLUSTER_NAME} --user=${KUSER} --kubeconfig=${KCONFIG}
      # kubectl config use-context ${KUSER}@${CLUSTER_NAME} --kubeconfig=${KCONFIG}
      # chown kubernetes:kubernetes ${KCONFIG}
      # chmod 0600 ${KCONFIG}
      

      確認。

      # kubectl config view --kubeconfig=${KCONFIG}
      
    3. CA証明書とbootstrap kubeconfigをConfigMap(cluster-info)で公開

      kubeletはこのConfigMapを見てクラスタに参加する。

      # kubectl -n kube-public create configmap cluster-info --from-file /etc/kubernetes/pki/ca.crt --from-file /etc/kubernetes/bootstrap.kubeconfig
      

      anonymousユーザにcluster-infoへのアクセスを許可する。

      # kubectl -n kube-public create role system:bootstrap-signer-clusterinfo --verb get --resource configmaps
      # kubectl -n kube-public create rolebinding kubeadm:bootstrap-signer-clusterinfo --role system:bootstrap-signer-clusterinfo --user system:anonymous
      

      system:bootstrappersグループにsystem:node-bootstrapperロールを紐づける。

      # kubectl create clusterrolebinding kubeadm:kubelet-bootstrap --clusterrole system:node-bootstrapper --group system:bootstrappers
      
    4. bootstrap.kubeconfigにトークンを追記

      # kubectl config set-credentials kubelet-bootstrap --token=${BOOTSTRAP_TOKEN} --kubeconfig=/etc/kubernetes/bootstrap.kubeconfig
      
  8. Docker、CNI、kubeletインストール

    1. Docker

      https://docs.docker.com/install/linux/docker-ce/centos/#set-up-the-repository に従ってDocker CEをインストール。 ストレージドライバにはoverlay2をつかうので、device-mapper-persistent-dataとlvm2は入れない。

      # yum install -y yum-utils
      # yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
      # yum install -y docker-ce
      

      18.03.0-ceが入った。

      が、よくみたらDocker CEはOracle Linuxをサポートしていないので、Docker CEはアンインストールして、代わりにOracle Container Runtime for Docker (aka docker-engine)を入れる。

      /etc/yum.repos.d/public-yum-ol7.repool7_addonsenabledを1にして、以下のコマンドでdocker-engineをインストール。

      # yum install -y docker-engine
      

      docker-engine 17.06.2が入った。

      /etc/sysconfig/dockerに以下を追記して、 Dockerがオープンできる最大ファイル数を増やす。

      DOCKER_NOFILE=1000000
      

      Kubernetes環境ではiptablesはkube-proxyが操作するので、Dockerには操作させないようにするため、/etc/sysconfig/dockerOPTIONS--iptables=falseを追加。 (これをすると、--icc=falseは設定できなくなる(不要になる)。)

      また、PodのallowPrivilegeEscalationをfalseにできない問題に対処するため、/etc/sysconfig/dockerOPTIONSから--selinux-enabledを消す。

      で、起動。

      # systemctl daemon-reload
      # systemctl enable docker
      # systemctl start docker
      

      確認。

      # cat /proc/$(pidof dockerd)/environ
      # systemctl status docker -l
      # docker version
      
    2. CNI

      # mkdir -p /etc/cni/net.d /opt/cni/bin/
      # cd /tmp
      # curl -OL https://github.com/containernetworking/cni/releases/download/v0.6.0/cni-amd64-v0.6.0.tgz
      # curl -OL https://github.com/containernetworking/plugins/releases/download/v0.7.1/cni-plugins-amd64-v0.7.1.tgz
      # cd /opt/cni/bin
      # tar zxf /tmp/cni-amd64-v0.6.0.tgz
      # tar zxf /tmp/cni-plugins-amd64-v0.7.1.tgz
      # chmod +x /opt/cni/bin/*
      # cat >/etc/cni/net.d/99-loopback.conf <<EOF
      {
        "type": "loopback"
      }
      EOF
      
    3. kubelet

      前提コマンド(conntrack)インストール。

      # yum -y install conntrack-tools
      

      systemdのユニットファイルを書いてサービス化。

      # DNS_SERVER_IP=10.0.0.10
      # PAUSE_IMAGE=k8s.gcr.io/pause-amd64:3.1
      # DNS_DOMAIN="cluster.local"
      # cat > /etc/systemd/system/kubelet.service << EOF
      [Unit]
      Description=Kubernetes Kubelet
      Documentation=https://github.com/kubernetes/kubernetes
      After=docker.service
      Requires=docker.service
      
      
      [Service]
      User=root
      Group=root
      ExecStart=/usr/bin/kubelet \\
        --feature-gates=RotateKubeletServerCertificate=true \\
        --address=0.0.0.0 \\
        --bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig \\
        --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\
        --pod-manifest-path=/etc/kubernetes/manifests \\
        --network-plugin=cni \\
        --cni-conf-dir=/etc/cni/net.d \\
        --cni-bin-dir=/opt/cni/bin \\
        --cluster-dns=${DNS_SERVER_IP} \\
        --cluster-domain=${DNS_DOMAIN} \\
        --authorization-mode=Webhook \\
        --client-ca-file=/etc/kubernetes/pki/ca.crt \\
        --cert-dir=/etc/kubernetes/pki \\
        --rotate-certificates=true \\
        --v=2 \\
        --cgroup-driver=cgroupfs \\
        --pod-infra-container-image=${PAUSE_IMAGE} \\
        --tls-min-version=VersionTLS12 \\
        --tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 \\
        --allow-privileged=true \\
        --anonymous-auth=false
      Restart=always
      RestartSec=10s
      
      
      [Install]
      WantedBy=multi-user.target
      EOF
      # systemctl daemon-reload
      # systemctl enable kubelet
      # systemctl start kubelet
      

      (実際は、systemctl start kubeletするまえに、後述のNode CSR自動承認設定をすべし。)

      --allow-privilegedはflannelなどに必要。

      --pod-infra-container-imageではpauseコンテナイメージを指定する。 このコンテナはPod毎に起動され、Podネットワークの名前空間を保持するために使われるらしい。 今回使ったk8s.gcr.io/pause-amd64:3.1はKubernetesチームが配布しているものだけど、Oracleが配布しているものもあり、そちらを使うには、Oracle Linux 7.4のダウンロード媒体リストに含まれるOracle Container Services for use with Kubernetes 1.1.9.1に入っているpause-amd64.tarをdocker loadしておいて、そのイメージ名を--pod-infra-container-imageに渡せばいい。

      --bootstrap-kubeconfigで指定したkubeconfigでTLS Bootstrapして、--cert-dirで指定したディレクトリに証明書と鍵を生成して、--kubeconfigで指定したパスに以降使うkubeconfigを生成する。 この証明書を自動更新(i.e. Certificate Rotation)するオプションが--rotate-certificates

      --pod-manifest-pathで指定したディレクトリはkubeletに定期的にスキャンされ、そこに置いたKubernetesマニフェスト(ドットで始まるもの以外)が読まれる。 (参照: Static Pods)

      --pod-cidrは指定しない。 これはkube-controller-managerに渡した--cluster-cidr--node-cidr-mask-sizeから計算されるので。

      --anonymous-auth=falseセキュリティのために推奨されたオプション

      --authorization-mode=Webhookセキュリティのために推奨されたオプションで、認可処理をkube-apiserverに移譲する設定。

      本当はKubelet Configファイルを使ったほうがいいみたいなので、いずれそれに対応する。 (対応した: 「Kubernetes 1.10のkubeletの起動オプションをKubelet ConfigファイルとPodSecurityPolicyで置き換える」)

      起動確認。

      # systemctl status kubelet -l
      
    4. Node CSR手動承認

      TLS Bootstrappingで生成されたCSRを手動で承認する。

      CSRは以下のコマンドで見れる。

      # kubectl get csr
      NAME                                                   AGE       REQUESTOR                 CONDITION
      csr-cf9hm                                              24m       system:node:k8s-master  Pending
      node-csr-Vcw_4HioW1CI96eDH29RMKPrOchEN133053wm6DCXUk   24m       system:bootstrap:itacbw   Pending
      

      node-csr-…がクライアント証明書のためのCSRで、csr-…がサーバ証明書の。 これらを承認する。

      # kubectl certificate approve node-csr-Vcw_4HioW1CI96eDH29RMKPrOchEN133053wm6DCXUk
      # kubectl certificate approve csr-cf9hm
      

      (因みに否認するときはkubectl certificate deny)

      これでクラスタにノードが追加されたはず。 確認。

      # kubectl get node
      NAME         STATUS    ROLES     AGE       VERSION
      k8s-master   Ready     <none>    36s       v1.10.0
      
    5. Node CSR自動承認設定

      前節でやった手動承認はcsrapprovingが自動でやってくれる。

      新規ノード参加時のCSRを承認するClusterRoleとしてsystem:certificates.k8s.io:certificatesigningrequests:nodeclientが自動生成されているので、これをsystem:bootstrappersグループにバインドしてやると、自動承認が有効になる。

      • s
      # cat <<EOF | kubectl create -f -
      kind: ClusterRoleBinding
      apiVersion: rbac.authorization.k8s.io/v1
      metadata:
        name: auto-approve-csrs-for-group
      subjects:
      - kind: Group
        name: system:bootstrappers
        apiGroup: rbac.authorization.k8s.io
      roleRef:
        kind: ClusterRole
        name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
        apiGroup: rbac.authorization.k8s.io
      EOF
      

      また、kubeletのクライアント証明書を自動更新(i.e. RotateKubeletClientCertificate)するときのCSRを承認するClusterRoleとしてsystem:certificates.k8s.io:certificatesigningrequests:selfnodeclientが自動生成されていて、これをノード毎のユーザにバインドしてやると、自動承認が有効になる。

      # HOSTNAME=k8s-master
      # cat <<EOF | kubectl create -f -
      kind: ClusterRoleBinding
      apiVersion: rbac.authorization.k8s.io/v1
      metadata:
        name: ${HOSTNAME}-node-client-cert-renewal
      subjects:
      - kind: User
        name: system:node:${HOSTNAME}
        apiGroup: rbac.authorization.k8s.io
      roleRef:
        kind: ClusterRole
        name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
        apiGroup: rbac.authorization.k8s.io
      EOF
      

      kubeletのサーバ証明書を自動更新(i.e. RotateKubeletServerCertificate)するときのCSRを承認するClusterRoleは現時点で自動生成されないので、自分で作ってノード毎のユーザにバインドして、自動承認を有効にする。

      # cat <<EOF | kubectl create -f -
      kind: ClusterRole
      apiVersion: rbac.authorization.k8s.io/v1
      metadata:
        name: approve-node-server-renewal-csr
      rules:
      - apiGroups: ["certificates.k8s.io"]
        resources: ["certificatesigningrequests/selfnodeserver"]
        verbs: ["create"]
      EOF
      # HOSTNAME=k8s-master
      # cat <<EOF | kubectl create -f -
      kind: ClusterRoleBinding
      apiVersion: rbac.authorization.k8s.io/v1
      metadata:
        name: ${HOSTNAME}-server-client-cert-renewal
      subjects:
      - kind: User
        name: system:node:${HOSTNAME}
        apiGroup: rbac.authorization.k8s.io
      roleRef:
        kind: ClusterRole
        name: approve-node-server-renewal-csr
        apiGroup: rbac.authorization.k8s.io
      EOF
      
  9. kube-proxy、オーバレイネットワーク、DNSのデプロイ

    1. kube-proxy

      kube-proxyのkubeconfigを作成。

      # MASTER_IP=192.168.171.200
      # KUBERNETES_PUBLIC_ADDRESS=$MASTER_IP
      # CLUSTER_NAME="k8s"
      # KCONFIG="/etc/kubernetes/kube-proxy.kubeconfig"
      # KUSER="system:kube-proxy"
      # kubectl config set-cluster ${CLUSTER_NAME} --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 --kubeconfig=${KCONFIG}
      # kubectl config set-credentials ${KUSER} --client-certificate=/etc/kubernetes/pki/kube-proxy.crt --client-key=/etc/kubernetes/pki/kube-proxy.key --embed-certs=true --kubeconfig=${KCONFIG}
      # kubectl config set-context ${KUSER}@${CLUSTER_NAME} --cluster=${CLUSTER_NAME} --user=${KUSER} --kubeconfig=${KCONFIG}
      # kubectl config use-context ${KUSER}@${CLUSTER_NAME} --kubeconfig=${KCONFIG}
      # chown kubernetes:kubernetes ${KCONFIG}
      # chmod 0600 ${KCONFIG}
      

      確認。

      # kubectl config view --kubeconfig=${KCONFIG}
      

      Service Accountのkube-proxyにsystem:node-proxierというClusterRoleを付ける。

      # kubectl create clusterrolebinding kubeadm:node-proxier --clusterrole system:node-proxier --serviceaccount kube-system:kube-proxy
      

      systemdのユニットファイルを書いてサービス化。

      # CLUSTER_CIDR="10.244.0.0/16"
      # cat > /etc/systemd/system/kube-proxy.service << EOF
      [Unit]
      Description=Kubernetes Kube Proxy
      Documentation=https://github.com/kubernetes/kubernetes
      After=network.target
      
      
      [Service]
      User=root
      Group=root
      ExecStart=/usr/bin/kube-proxy \\
        --feature-gates=RotateKubeletServerCertificate=true \\
        --bind-address 0.0.0.0 \\
        --cluster-cidr=${CLUSTER_CIDR} \\
        --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig \\
        --v=2
      Restart=always
      RestartSec=10s
      
      
      [Install]
      WantedBy=multi-user.target
      EOF
      # systemctl daemon-reload
      # systemctl enable kube-proxy
      # systemctl start kube-proxy
      

      確認。

      # systemctl status kube-proxy -l
      
    2. ネットワークプロバイダ (flannel)

      flannelのドキュメントを参考に。

      flannelをデプロイするには、kube-apiserverとkube-controller-managerの起動オプションに--allow-privilegedを付ける必要がある。

      また、公式が配布しているKubernetesマニフェストを使う場合、kube-controller-managerの起動オプションの--cluster-cidr10.244.0.0/16を指定する必要がある。

      デプロイ自体は以下のコマンドを実行するだけ。

      # kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
      

      このKubernetesマニフェストでは、quay.ioからquay.io/coreos/flannel:v0.10.0-amd64というコンテナイメージがpullされる。

      Oracleもflannelのコンテナイメージを配布していて、そちらを使うには、Oracle Linux 7.4のダウンロード媒体リストに含まれるOracle Container Services for use with Kubernetes 1.1.9.1に入っているflannel.tarをdocker loadしておいて、そのイメージを使うようにマニフェストを書きかえればいい。

      起動確認。

      # kubectl -n kube-system get po
      NAME                    READY     STATUS    RESTARTS   AGE
      kube-flannel-ds-gkcqd   1/1       Running   0          1m
      

      flannelはNetwork Policyをサポートしていないので、CalicoWeave Netあたりにすればよかったかも。 (やった: 「Kubernetes 1.10のクラスタにWeave Netをデプロイする」)

    3. CoreDNS

      Kubernetes 1.10からは、サービスディスカバリに(kube-dnsの代わりに)CoreDNSを使うのが標準になった。

      以下を参考にCoreDNSをデプロイする:

      # cd /tmp
      # curl -LO https://raw.githubusercontent.com/coredns/deployment/master/kubernetes/coredns.yaml.sed
      # curl -LO https://raw.githubusercontent.com/coredns/deployment/master/kubernetes/deploy.sh
      # chmod +x deploy.sh
      # DNS_SERVER_IP="10.0.0.10"
      # SERVICE_CLUSTER_IP_RANGE="10.0.0.0/16"
      # DNS_DOMAIN="cluster.local"
      # ./deploy.sh -r $SERVICE_CLUSTER_IP_RANGE -i $DNS_SERVER_IP -d $DNS_DOMAIN > coredns.yaml
      # kubectl apply -f coredns.yaml
      

      このKubernetesマニフェストではDocker Hubからcoredns/coredns:1.1.1というイメージがpullされる。

      起動確認。

      # kubectl -n kube-system get pods -o wide | grep coredns
      coredns-8459d9f654-b585f   1/1       Running   0          48s       10.244.0.3        k8s-master
      coredns-8459d9f654-x7drc   1/1       Running   0          48s       10.244.0.2        k8s-master
      

      起動確認時にCoreDNSのIPアドレスを確認して、動作確認。

      # dig @10.244.0.3 kubernetes.default.svc.cluster.local +noall +answer
      
      
      ; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> @10.244.0.3 kubernetes.default.svc.cluster.local +noall +answer
      ; (1 server found)
      ;; global options: +cmd
      kubernetes.default.svc.cluster.local. 5 IN A    10.0.0.1
      
  10. Kubernetesアプリデプロイ

    前節まででKubernetesクラスタの構築は完了。 試しにKubernetesアプリをひとつデプロイしてみる。

    1. Weave Scope

      ドキュメントを参考に。

      # cd /tmp
      # curl -sSL -o scope.yaml https://cloud.weave.works/k8s/scope.yaml?k8s-service-type=NodePort
      # kubectl apply -f scope.yaml
      

      このKubernetesマニフェストではDocker Hubからweaveworks/scope:1.8.0というイメージがpullされる。

      kubectl -n weave get svc/weave-scope-appでポート調べて、http://k8s-master:<ポート>/をブラウザ開くとWeave ScopeのGUIが見れる。