今日も爽快な打鍵音を

ITエンジニアのブログ

Kuberentesクラスタ 1.10 をkubeadmで構築

Kubernetes 1.10 の新機能

KubernetesはDockerのオーケストレーションツールとして使われており、Dockerコンテナの作成から、コンテナ監視管理、レプリケーション機能など多くの機能を提供している。3/26に1.10がリリースされ、新たにStorage機能やDNS機能が強化された。

Storage関連

Container Storage Interface(CSI)がBeta版に移行したらしく、CSIは新しいVolumeプラグインということ。またPersistentVolumeの更新もあり、Podで使用されているPersistentVolumeとPersistemtClaimの削除を防止する。これにより正しい順序でストレージが破棄されるようになるとのこと。

ネットワーク

CoreDNSをDNSプロバイダとして使用可能に、ただしBeta版。CoreDNSはバイナリで動作し、Configfileを記述するだけで簡単に機能する。

Kubernetesのサポートについて

1.10.x のSupportは Decenmber 2018まで。 各バージョンともに10か月間のサポートがある。1.8.x は2018/6月にサポートが終了する。

kubeadmによる Kubernetesクラスタの構築

Kubernetesをスクラッチで,1から構築する手順は公式にあるが、クラスタ間の通信のTLS対応や各コンポーネントの各種設定など、初心者にはそこそこ厳しい。Kuberetes公式Tutorialもscratchは難しいからやめとけと言わんばかりにMinikubeやkubeadmを勧めている。まずはkubeadmによる自動構築を試してみる。 今回の手順はKubernetes公式の手順を参考に躓いたポイントをまとめる。

Using kubeadm to Create a Cluster

VMの作成

多くのOSで導入可能。今回はCentOSを選択。

kubeadm Support OS

導入環境

# 推奨
OS CentOS7 CentOS or Ubuntu
CPU 4Core 2Core以上
メモリ 4GB 2GB以上

kubeadmのインストール

前提事項

Installing kubeadm

各設定をユニークに
  • Unique hostname, MAC address, and product_uuid for every node.
    • VMでKuberentesクラスタを構築するときは注意
      MAC addressの確認 ip link or ifconfig -a
      product_uuidの確認 sudo cat /sys/class/dmi/id/product_uuid
ホスト名、IPアドレスを固定化
  • ホスト名設定: hostnamectl set-hostname k8s-master
  • IP固定化: vi /etc/sysconfig/network-scripts/ifcfg-ens* 等で
SELinuxを無効
# vi /etc/selinux/config
...
SELINUX=disabled
...
Firewalldを無効

Kubernetesは多くのポートを使うので、ひとまず無効に

systemctl stop firewalld
systemctl disable firewalld
OS swapを無効
  • Swap disabled. You MUST disable swap in order for the kubelet to work properly
    • OS設定のswapが有効な場合`kubelet'の動作に影響があるため無効に

swapの無効化(CentOS7)

/etc/fstabを編集 ※ 修正ミスするとOSが立ち上がらくなることがあるので注意

...
...
/dev/mapper/cl-swap     swap                    swap    defaults        0 0
kubernetesリポジトリを追加

kubernetes用のリポジトリを追加し、kubelet', 'kubeadm', 'kubectlをインストールする。

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF

yum repolistで追加を確認。

[root@k8s-master ~]# yum repolist
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: ftp.tsukuba.wide.ad.jp
 * extras: ftp.tsukuba.wide.ad.jp
 * updates: ftp.tsukuba.wide.ad.jp
repo id                                                repo name                                               status
base/7/x86_64                                          CentOS-7 - Base                                         9,591
extras/7/x86_64                                        CentOS-7 - Extras                                         448
kubernetes/x86_64                                      Kubernetes                                                202
updates/7/x86_64                                       CentOS-7 - Updates                                      2,416
repolist: 12,657

kubelet kubeadm kubectlをインストールし、サービス登録。

yum install -y kubelet kubeadm kubectl
systemctl enable kubelet && systemctl start kubelet
ルーティング設定

RHEL系だと標準ではKubernetesの仮想ネットワークからルーティングされないようなので、有効にする。

cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system
cgroup設定

kubeletでは同じcgoup driverを使う必要があるため、設定内容を確認する

[root@k8s-master ~]# docker info | grep -i cgroup
  WARNING: You're not using the default seccomp profile
Cgroup Driver: systemd

[root@k8s-master ~]# cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf | grep -i cgroup-driver
Environment="KUBELET_CGROUP_ARGS=--cgroup-driver=systemd"

今回はどちらもsystemdのため問題がないが、もし異なる場合は下記を実行

sed -i "s/cgroup-driver=systemd/cgroup-driver=cgroupfs/g" /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

systemctl daemon-reload
systemctl restart kubelet
kubeadm によるKuberetesの構築

kubeadmを使う準備が整った。kubeadmでkubernetesを構築する。 kubeadm init を実行するだけで、クラスタが構築されるが、さまざまなオプションが用意されている。

kubeadm init [FLAG]

FLAG 用途
--apiserver-advertise-address string k8sAPIサーバのlisten IPアドレス. stringにipアドレスを記述
--apiserver-bind-port int32 APIサーバのポートを指定。デフォルトは6443
--cert-dir string k8s用の証明書保存ディレクトリ デフォルトは/etc/kubernetes/pki
--config string kubeadm用のConfigファイルを指定
--ignore-preflight-errors strings Warning, Errorを無視してインストール
--kubernetes-version string 任意のバージョンのKubernetesをインストール デフォルトはstable-1.10
--pod-network-cidr string podのサブネットを変更。stringに任意のサブネットを記述
--service-cidr string サービスのVIPのサブネットを変更 デフォルトは10.96.0.0/12
--service-dns-domain string サービスのドメインを変更 デフォルトはcluster.local

その他オプションに CoreDNSの有効設定がある。

Auditing=true|false (ALPHA - default=false)
CoreDNS=true|false (BETA - default=false)
DynamicKubeletConfig=true|false (ALPHA - default=false)
SelfHosting=true|false (ALPHA - default=false)
StoreCertsInSecrets=true|false (ALPHA - default=false)

この中で--apiserver-advertise-address, --kubernetes-version, --service-cidr, --pod-network-cidrあたりは環境に応じて変更したほうがよさそう。Service-cidrに関してはk8s内部のIP帯と基幹ネットワークのPrivateIPが重複することもあり、k8sの対外通信自体はNAT処理が入るが、使用されるIP帯には注意が必要だろう。

kubeadmによる構築を実行
kubeadm init --apiserver-advertise-address '192.168.0.221' --kubernetes-version 1.10.2 --service-cidr '10.0.0.0/24' --pod-network-cidr '172.16.0.0/24'

kubeadm initでエラーがでたらkubeadm resetを実行し、再度kubeadm initを実行。

[root@k8s-master ~]# kubeadm init --apiserver-advertise-address '192.168.0.221' --kubernetes-version 1.10.2 --service-cidr '10.0.0.0/24' --pod-network-cidr '172.16.0.0/24'
[init] Using Kubernetes version: v1.10.2
[init] Using Authorization modes: [Node RBAC]
[preflight] Running pre-flight checks.
        [WARNING Hostname]: hostname "k8s-master" could not be reached
        [WARNING Hostname]: hostname "k8s-master" lookup k8s-master on 192.168.0.1:53: no such host
        [WARNING FileExisting-crictl]: crictl not found in system path
Suggestion: go get github.com/kubernetes-incubator/cri-tools/cmd/crictl
[preflight] Starting the kubelet service
[certificates] Generated ca certificate and key.
[certificates] Generated apiserver certificate and key.
[certificates] apiserver serving cert is signed for DNS names [k8s-master kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.0.0.1 192.168.0.221]
[certificates] Generated apiserver-kubelet-client certificate and key.
[certificates] Generated etcd/ca certificate and key.
[certificates] Generated etcd/server certificate and key.
[certificates] etcd/server serving cert is signed for DNS names [localhost] and IPs [127.0.0.1]
[certificates] Generated etcd/peer certificate and key.
[certificates] etcd/peer serving cert is signed for DNS names [k8s-master] and IPs [192.168.0.221]
[certificates] Generated etcd/healthcheck-client certificate and key.
[certificates] Generated apiserver-etcd-client certificate and key.
[certificates] Generated sa key and public key.
[certificates] Generated front-proxy-ca certificate and key.
[certificates] Generated front-proxy-client certificate and key.
[certificates] Valid certificates and keys now exist in "/etc/kubernetes/pki"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/admin.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/kubelet.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/controller-manager.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/scheduler.conf"
[controlplane] Wrote Static Pod manifest for component kube-apiserver to "/etc/kubernetes/manifests/kube-apiserver.yaml"
[controlplane] Wrote Static Pod manifest for component kube-controller-manager to "/etc/kubernetes/manifests/kube-controller-manager.yaml"
[controlplane] Wrote Static Pod manifest for component kube-scheduler to "/etc/kubernetes/manifests/kube-scheduler.yaml"
[etcd] Wrote Static Pod manifest for a local etcd instance to "/etc/kubernetes/manifests/etcd.yaml"
[init] Waiting for the kubelet to boot up the control plane as Static Pods from directory "/etc/kubernetes/manifests".
[init] This might take a minute or longer if the control plane images have to be pulled.
[apiclient] All control plane components are healthy after 18.501018 seconds
[uploadconfig]?Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[markmaster] Will mark node k8s-master as master by adding a label and a taint
[markmaster] Master k8s-master tainted and labelled with key/value: node-role.kubernetes.io/master=""
[bootstraptoken] Using token: 3ugeo9.90nstqyd48q4esa7
[bootstraptoken] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: kube-dns
[addons] Applied essential addon: kube-proxy

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

  kubeadm join 192.168.0.221:6443 --token 3ugeo9.90nstqyd48q4esa7 --discovery-token-ca-cert-hash sha256:43d8fe3e155ab1c2226bd12c5e9e72aacfe178c3a5566b874f1c4b5fc26757a4

Your Kubernetes master has initialized successfully! と表示されていれば完了。 途中にWarningメッセージが出ている。ホスト名解決ができていないのかホスト名関連のエラー。気には留めておく。

[WARNING Hostname]: hostname "k8s-master" could not be reached
[WARNING Hostname]: hostname "k8s-master" lookup k8s-master on 192.168.0.1:53: no such host
[WARNING FileExisting-crictl]: crictl not found in system path

最後の一行のkubeadm join ~~だが、これはクラスタノード(minion node)を組む際に使うもので、kubeadm導入済みの環境でコピペすればクラスタへ参加する。とても簡単。

kubectlによる接続

kubectlでは鍵による接続設定が必要。kubeadmが自動生成してくれているので、これをkubectlが読み取れる位置に変更する。

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

別の環境からkubectlを実行する場合もこのadmin.confをコピーすれば接続が完了する。

Podネットワークの構築(CNI)

k8sコンポーネントの起動確認kubectl get pods --all-namespacesをするとkube-dnsが動作せず、他のコンポーネントもループしている。

[root@k8s-master ~]# kubectl get pods --all-namespaces
NAMESPACE     NAME                                 READY     STATUS    RESTARTS   AGE
kube-system   etcd-k8s-master                      1/1       Running   0          4s
kube-system   kube-apiserver-k8s-master            1/1       Running   0          4s
kube-system   kube-controller-manager-k8s-master   1/1       Running   0          4s
kube-system   kube-dns-86f4d74b45-7vqqq            0/3       Pending   0          8m
kube-system   kube-proxy-kzpgb                     1/1       Running   0          8m
kube-system   kube-scheduler-k8s-master            1/1       Running   0          4s

これはk8sのContainer Network Interfaceが動作していないためなので、CNIをインストールを行う。

多くのサードパーティによるCNIが用意されている。

  • Calico
  • Canal
  • Flannel
  • Kube-router
  • Romana
  • Weave Net

今回はflannelを使う。 kubeadmでflannelを使う場合はオプションに--pod-network-cidr=10.244.0.0/16を指定しないと行けないらしい。

変えられそうなので調べたらkube-flannel.ymlの中に記述されており、書き換えれば良い。

wget https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml

65行目 "Network": "10.244.0.0/16",172.16.0.0/24

     63   net-conf.json: |
     64     {
     65       "Network": "10.244.0.0/16",
     66       "Backend": {
     67         "Type": "vxlan"
     68       }
     69     }

flannelをインストール

kubectl apply -f kube-flannel.yml

k8sコンポーネントの起動状況を確認 kubectl get pods --all-namespaces

[root@k8s-master ~]# kubectl get pods --all-namespaces
NAMESPACE     NAME                                 READY     STATUS    RESTARTS   AGE
kube-system   etcd-k8s-master                      1/1       Running   0          37s
kube-system   kube-apiserver-k8s-master            1/1       Running   0          37s
kube-system   kube-controller-manager-k8s-master   1/1       Running   0          37s
kube-system   kube-dns-86f4d74b45-7vqqq            3/3       Running   0          31m
kube-system   kube-flannel-ds-fpskt                1/1       Running   0          46s
kube-system   kube-proxy-kzpgb                     1/1       Running   0          31m
kube-system   kube-scheduler-k8s-master            1/1       Running   0          37s

kube-dnsが起動している。よさそう。

Podの作成

試しにPodを作成し、動作検証する。 kubeadmでは標準ではMasterノードではPodがデプロイされない設定になっているので、これを解除

[root@k8s-master ~]# kubectl taint nodes --all node-role.kubernetes.io/master-
node "k8s-master" untainted

k8sはPodとServiceをデプロイするがマニフェストファイルyamlで管理する。 テストとしてnginxをデプロイする。List記法を使い、DeploymentServiceをまとめて書いた。

nginx-test.yaml

apiVersion: v1
kind: List
items:
- apiVersion: extensions/v1beta1
  kind: Deployment
  metadata:
    name: nginx
  spec:
    replicas: 2
    template:
      metadata:
        labels:
          run: nginx
      spec:
        containers:
        - name: nginx
          image: nginx:1.13
          ports:
          - containerPort: 80
- apiVersion: v1
  kind: Service
  metadata:
    name: nginx-nodeport
  spec:
    type: NodePort
    ports:
    - port: 80
      protocol: TCP
      targetPort: 80
    selector:
      run: nginx

Pod, Serviceをデプロイ

[root@k8s-master ~]# kubectl create -f nginx-test.yaml
deployment.extensions "nginx" created
service "nginx-nodeport" created

kubectl get allでpodとserviceを確認

[root@k8s-master ~]# kubectl get all
NAME                       READY     STATUS    RESTARTS   AGE
pod/nginx-bbc784cf-qqkbk   1/1       Running   0          1m
pod/nginx-bbc784cf-wm8n4   1/1       Running   0          1m

NAME                     TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
service/kubernetes       ClusterIP   10.0.0.1     <none>        443/TCP        43m
service/nginx-nodeport   NodePort    10.0.0.30    <none>        80:30062/TCP   1m

NAME                    DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   2         2         2            2           1m

NAME                             DESIRED   CURRENT   READY     AGE
replicaset.apps/nginx-bbc784cf   2         2         2         1m

よさそう。 kubeadm initで指定したservice CIDR10.0.0.0/24になっている。 ついでにコンテナIPも見てみる。

kubectl describeで Pod,コンテナの状態を確認

[root@k8s-master ~]# kubectl describe pod/nginx-bbc784cf-qqkbk
Name:           nginx-bbc784cf-qqkbk
Namespace:      default
Node:           k8s-master/192.168.0.221
Start Time:     Sat, 28 Apr 2018 13:33:24 -0400
Labels:         pod-template-hash=66734079
                run=nginx
Annotations:    <none>
Status:         Running
IP:             172.16.0.3
Controlled By:  ReplicaSet/nginx-bbc784cf
Containers:
  nginx:
    Container ID:   docker://5be8d67afbfe477c92519e215eeccc1b13163480ce5f072ad95d3c1d2ce6f140
    Image:          nginx:1.13
    Image ID:       docker-pullable://docker.io/nginx@sha256:80e2f223b2a53cfcf3fd491521e5fb9b4004d42dfc391c76011bcdd9565643df
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sat, 28 Apr 2018 13:33:36 -0400
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-rs2wt (ro)
Conditions:
  Type           Status
  Initialized    True
  Ready          True
  PodScheduled   True
Volumes:
  default-token-rs2wt:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-rs2wt
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason                 Age   From                 Message
  ----    ------                 ----  ----                 -------
  Normal  Scheduled              3m    default-scheduler    Successfully assigned nginx-bbc784cf-qqkbk to k8s-master
  Normal  SuccessfulMountVolume  3m    kubelet, k8s-master  MountVolume.SetUp succeeded for volume "default-token-rs2wt"
  Normal  Pulling                3m    kubelet, k8s-master  pulling image "nginx:1.13"
  Normal  Pulled                 2m    kubelet, k8s-master  Successfully pulled image "nginx:1.13"
  Normal  Created                2m    kubelet, k8s-master  Created container
  Normal  Started                2m    kubelet, k8s-master  Started container

こちらもPodのIPが172.16.0.3になっていることを確認。

サービスのバインドポートがservice/nginx-nodeport NodePort 10.0.0.30 <none> 80:30062/TCPのため、クライアント環境からk8s向けに192.168.0.221:30062でアクセス

f:id:mbook-x86:20180429024114p:plain

無事 nginx が表示された。

とりあえずkubeadmを用いてPodが立つところまでできた。

以上。