複数の物理ホストの Debian9 上に Kubernetes (k8s) クラスタを Rancher で立ち上げる
[履歴] [最終更新] (2018/12/14 01:36:22)
1
作品
407
技術情報
最近の投稿
ここは
趣味の電子工作を楽しむ人のためのハードウェア情報共有サイト

技術情報や作品の投稿機能、リアルタイム遠隔操作 API をご利用いただけます。
新着作品

概要

Docker オーケストレーションツールの一つに Kubernetes (k8s) があります。k8s 実行環境の構築方法は複数ありますが、ここでは AWS や GCP 等のクラウドサービスを利用せず、Debian9 がインストールされた複数台の物理マシンがネットワーク内に存在する状況を考えます。これら複数の Debian9 上に k8s クラスタを立ち上げるためには Rancher が利用できます。k8s 以外の用途で利用しないことが分かっている場合は、Debian9 上で Rancher を利用するのではなく専用の RancherOS を利用することもできます。

ホストマシンの用意

ここでは実験のため VirtualBox で二つの Debian9 マシンを起動して、それらを同じ NAT ネットワーク 10.0.2.0/24 に所属させます。

  • debian1 10.0.2.4/24
  • debian2 10.0.2.5/24

Uploaded Image

Uploaded Image

各ホストマシンへの Rancher のインストール

簡単のため Docker イメージとして提供される Rancher を利用します。Docker エンジンをインストールします。

sudo apt-get update
sudo apt-get install \
     apt-transport-https \
     ca-certificates \
     curl \
     gnupg2 \
     software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"
sudo apt-get update
sudo apt-get install docker-ce

sudo なしで docker コマンドを利用できるように設定できます。

sudo groupadd docker
sudo usermod -aG docker $USER
exit # ログインしなおし
docker ps

以下のコマンドで Rancher を起動します。データを永続化するための -v、および後に Node 追加のために起動する rancher-agent コンテナへのポート割り当てを考慮した -p を指定します。

docker run -d --name=rancher --restart=unless-stopped \
  -v /host/rancher:/var/lib/rancher \
  -p 8080:80 \
  -p 8443:443 \
  rancher/rancher:stable

ブラウザからアクセスして admin の初期パスワードを設定してログインします https://127.0.0.1:8080

$ docker ps
CONTAINER ID  IMAGE                   COMMAND          CREATED         STATUS         PORTS                                     NAMES
cc89ae5eac4a  rancher/rancher:stable  "entrypoint.sh"  28 minutes ago  Up 28 minutes  0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp  rancher

Uploaded Image

Rancher Server URL にはホスト名など、ネットワーク内の他のマシンおよびローカルからアクセスできるものを指定します。更に必要な場合は nginx 等でリバースプロキシ設定を行います。X-Forwarded-Proto を指定することで、Rancher 側では http から https へのリダイレクトを行わなくなります。

Rancher で k8s クラスタを立ち上げる

ブラウザから「Add Cluster」→「CUSTOM」→「Cluster Name: mycluster」→「Next」として k8s クラスタの設定を追加します。

k8s クラスタに追加する Node を起動するための docker run コマンドを生成するためのオプションとして「etcd, Control, Worker」すべてにチェックを入れると以下のようなコマンドが表示されます。

sudo docker run -d --privileged --restart=unless-stopped --net=host -v /etc/kubernetes:/etc/kubernetes -v /var/run:/var/run rancher/rancher-agent:v2.1.1 --server https://10.0.2.4:8443 --token 9c5p642z52p7tllfjzc2hgfwkb5xmck6266vdlvs82lfc8crwbbsd6 --ca-checksum a42937938e0c38ba4e56845260c8105017205e886677fa0c6c5d020bf6a9ed93 --etcd --controlplane --worker

これを Rancher を起動したホストで実行すると、同じ Debian マシン上に Rancher サーバコンテナに加えて、Rancher-agent コンテナおよびその他必要なコンテナが起動した状態になります。

$ docker ps | awk '{ print $2 }' | grep rancher | sort | uniq
rancher/coreos-etcd:v3.2.18
rancher/hyperkube:v1.11.3-rancher1
rancher/metrics-server-amd64
rancher/pause-amd64:3.1
rancher/rancher:stable

同様に別の Debian マシンで上記コマンドによって Rancher-agent を起動して Rancher サーバに接続することで、クラスタに Node を追加できます。以下の画像は debian1 10.0.2.4/24 と debian2 10.0.2.5/24 で Node 数が 2 の状態です。Node の追加が正常に完了するまでは Alert が表示された状態になります。

Uploaded Image

k8s クラスタで Docker コンテナを走らせる

k8s クラスタで Docker コンテナを走らせるためには、Rancher の WebUI および HTTP API に加えて、標準の kubectl コマンドも利用できます。kubectl は Rancher ではなく k8s 標準のコマンドです。クラスタを複数台のマシン間で組む必要がない場合は microk8sminikube を利用すると、ローカルマシン上に k8s クラスタを比較的簡単に構築でき、kubectl コマンドの動作確認等が行えます。以下では Rancher で起動した k8s クラスタを kubectl から利用するコマンドサンプルを記載します。

kubectl コマンドのインストール

kubectl は k8s クラスタのクライアントコマンドです。OS に応じて apt、yum、brew 等でインストールできます。Debian9 の場合は以下のようになります。

sudo apt-get update && sudo apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl

Web UI から取得したサーバへの接続情報を ~/.kube/config にコピーして接続確認します。kubectl クライアントと k8s クラスタのバージョンは Minor の一つ違いまで動作することが保証されています。例えば v1.2 クライアントは v1.1, v1.2, v1.3 クラスタに対して利用できます。

Uploaded Image

$ kubectl cluster-info
Kubernetes master is running at https://192.168.56.11:8443/k8s/clusters/c-zt2l5
KubeDNS is running at https://192.168.56.11:8443/k8s/clusters/c-zt2l5/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.2", GitCommit:"17c77c7898218073f14c8d573582e8d2313dc740", GitTreeState:"clean", BuildDate:"2018-10-24T06:54:59Z", GoVersion:"go1.10.4", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.3", GitCommit:"a4529464e4629c21224b3d52edfe0ea91b072862", GitTreeState:"clean", BuildDate:"2018-09-09T17:53:03Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}

また、Rancher の WebUI に実装されている簡易シェルからも kubectl コマンドが利用できます。

Uploaded Image

k8s クラスタについて

k8s クラスタは複数のコンポーネントで構成されます。コンポーネントはクラスタを構成するマシン上で実行されます。これらマシンを Node とよびます。k8s は実行するコンポーネントを etcd、Control Plane、Worker の三つのロールに分けて管理しており、上記の通り Node となる各マシンで rancher/rancher-agent を docker run するときの --etcd --controlplane --worker オプションで選択できるようになっています。"Node" という表現が重複しますが、コンポーネントを Master コンポーネントと Node コンポーネントで分けた場合において、etcd は Master コンポーネントに分類されます。

  • etcd → クラスタ内の分散 KVS です。
  • Control Plane → スケジューラや API サーバの機能を提供します。クラスタの状態はすべて etcd が管理するためステートレスなコンポーネントです。
  • Worker → ユーザアプリケーションを実行します。

Worker で実行するユーザアプリケーションは Pod という単位で管理されます。Pod は一つ以上の Docker コンテナおよび必要となるファイル等から成ります。Worker では複数の Pod を同時に実行できます。Worker には Kubelet というプロセスが常駐しており、コンテナの状態等を監視します。

nginx コンテナを起動して HTTP サービスを公開する例

クラスタ内のノードを確認

kubectl get nodes

NAME      STATUS   ROLES                      AGE   VERSION
debian1   Ready    controlplane,etcd,worker   6d    v1.11.3
debian2   Ready    controlplane,etcd,worker   5d    v1.11.3

クラスタ内に Pod のデプロイ設定を登録して Pod を起動 (kubectl run)

設定ファイルを基本とした kubectl create によるクラスタ操作の方法と、設定ファイルを用いない kubectl run による方法があります。状況に応じてメリットとデメリットを考慮しながら使い分けます

kubectl run mydeployment --image=nginx
kubectl get deployments
kubectl describe deployments

NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
mydeployment   1         1         1            1           3m

自動的にデプロイ設定に応じた Pod も作成されます。

kubectl get pods
kubectl describe pods

NAME                            READY   STATUS    RESTARTS   AGE
mydeployment-65b9f8795d-r4hrs   1/1     Running   0          4m

デプロイにサービスポートを設定

kubectl expose deployment/mydeployment --type="NodePort" --port 80
kubectl get services
kubectl describe services

NAME           TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes     ClusterIP   10.43.0.1      <none>        443/TCP        10d  ←k8s作成時から存在
mydeployment   NodePort    10.43.89.123   <none>        80:31685/TCP   6s  ←今回新規作成

nginx のサービスを利用してみます。

kubectl cluster-info 
curl 192.168.56.11:31685

NAT の外部 IP は以下のコマンドで取得できます。

kubectl get services/mydeployment -o go-template='{{(index .spec.ports 0).nodePort}}'

削除

サービス

kubectl delete services mydeployment

デプロイ (Pod も合わせて削除されます)

kubectl delete deployments mydeployment
kubectl get pods

NAME                            READY   STATUS        RESTARTS   AGE
mydeployment-65b9f8795d-r4hrs   0/1     Terminating   0          12m

コマンドサンプル

Pod 内のログを確認

Pod 名の確認

kubectl get pods -o wide

NAME                            READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE
mydeployment-65b9f8795d-8r4pl   1/1     Running   0          16m   10.42.0.8    debian   <none>

tailf のように監視できます。

kubectl logs mydeployment-65b9f8795d-8r4pl -f

すべて削除

Pod 削除が完了するまで待つ

kubectl delete services,deployments,pods --all

cron ジョブを設定している場合

kubectl delete services,deployments,pods,cronjobs --all

Terminate されたことの確認を待たない

kubectl delete services,deployments,pods,cronjobs --all --grace-period 0 --force

Pod 一覧の取得

echo "kubectl get pods -o go-template --template '[[range .items]][[.metadata.name]][[\"\\n\"]][[end]]'" | tr '[' '{' | tr ']' '}'

mydeployment-65b9f8795d-vb72j
mydeployment2-6dcc9fb68-9gbbs

kubectl get pods -o name

pod/mydeployment-65b9f8795d-vb72j
pod/mydeployment2-6dcc9fb68-9gbbs

起動済みの Pod に対してコマンドを発行

-c を指定しない場合は Pod 内のコンテナが一つ選択されます。

kubectl exec -it mydeployment-65b9f8795d-dpt96 /bin/bash
kubectl exec mydeployment-65b9f8795d-dpt96 -- ls /
kubectl exec mydeployment-65b9f8795d-dpt96 -- /bin/bash -c "ls && ls"

クラスタ内への HTTP プロキシを一時的に立てる

kubectl proxy --port 8001
curl http://localhost:8001/

kubectl run のオプション

環境変数の設定

kubectl run mydeployment --image=nginx --env="MYENV1=xxx" --env="MYENV2=yyy"
kubectl exec mydeployment-6c6b59798f-2bqp4 -- /bin/bash -c 'echo $MYENV1'

イメージのコマンドを上書き

kubectl run mydeployment --image=nginx --restart=Never -it --command -- /bin/bash -c "ls /"
kubectl run mydeployment --image=nginx --restart=Never -it --command -- /bin/bash

--restart=Never を付与すると Pod のみ作成されます。デプロイ設定は作成されません。

kubectl run mydeployment --image=nginx --restart=Never
kubectl get pods
kubectl get deployments

cron ジョブ

kubectl run mydeployment --image=nginx --restart=Never --schedule="0/2 * * * *" --command -- /bin/bash -c "date"
kubectl get cronjobs
kubectl delete cronjobs --all

NAME           SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
mydeployment   0/2 * * * *   False     0        <none>          20s

デプロイ設定は作成されません。

$ kubectl get deployments
No resources found.

$ kubectl get pods
NAME                            READY   STATUS      RESTARTS   AGE
mydeployment-1542640920-rhphc   0/1     Completed   0          8s

$ kubectl logs mydeployment-1542640920-rhphc
Mon Nov 19 15:22:14 UTC 2018

kubectl create で設定する YAML/JSON を kubectl run で設定

例えば --cap-add ALL --privileged--name xxx-h yyy に相当する設定は以下のようになります--env で指定した環境変数の値は無視されるため JSON 内に記載します。以下では特権モードにした状態でホストのデバイスファイルをマウントしています。

kubectl run mydeployment --image=nginx --overrides '{
  "spec": {
    "template": {
      "spec": {
        "hostname": "yyy",
        "containers": [
          {
            "name": "xxx",
            "image": "nginx",
            "env": [
              {"name":"MYENV", "value":"zzz"}
            ],
            "securityContext": {
              "capabilities": {
                "add": ["ALL"]
              },
              "privileged": true
            },
            "volumeMounts": [
              {
                "mountPath": "/dev/fuse",
                "name": "devfuse"
              }
            ]
          }
        ],
        "volumes": [
          {
            "name": "devfuse",
            "hostPath": {
              "path": "/dev/fuse"
            }
          }
        ]
      }
    }
  }
}'

kubectl exec -it `kubectl get pods -l run=mydeployment -o name | cut -d/ -f2` -- /bin/bash -c 'hostname'
yyy

kubectl exec -it `kubectl get pods -l run=mydeployment -o name | cut -d/ -f2` -- /bin/bash -c 'ls -l /dev/fuse'
crw-rw-rw- 1 root root 10, 229 Sep  7 08:28 /dev/fuse

kubectl exec -it `kubectl get pods -l run=mydeployment -o name | cut -d/ -f2` -- /bin/bash -c 'echo $MYENV'
zzz

kubectl describe pods -l run=mydeployment | grep -A1 Containers:
Containers:
  xxx:

docker の ENTRYPOINTcommand で変更できます。CMD は args で指定できます。

kubectl run mydeployment --image=nginx --overrides '{
  "spec": {
    "template": {
      "spec": {
        ...
        "containers": [
          {
            ...
            "command": ["/bin/bash", "-c"],
            "args": ["echo 123 && echo 123"]
          }
        ],

既定では imagePullPolicyIfNotPresent になっているため、:latest タグを指定している場合等を除いて、既に同名のイメージが Node 上に存在する場合は pull されませんAlways を指定することでこれを回避できます。

"image": "nginx",
"imagePullPolicy": "Always",
...

特定のデバイスファイルを隠したい場合

ホスト側のデバイスファイルを利用したい場合があります。docker の --device オプションに相当する機能が k8s で利用できない場合--privileged全デバイスファイルの利用を許可するしかありません。デバイスファイルのうち一部を隠したい場合は、以下のように設定できます。hostPath で FileOrCreate を指定しています。

kubectl run mydeployment --image=nginx --overrides '{
  "spec": {
    "template": {
      "spec": {
        "hostname": "yyy",
        "containers": [
          {
            "name": "xxx",
            "image": "nginx",
            "env": [
              {"name":"MYENV", "value":"zzz"}
            ],
            "securityContext": {
              "capabilities": {
                "add": ["ALL"]
              },
              "privileged": true
            },
            "volumeMounts": [
              {
                "mountPath": "/dev/fuse",
                "name": "devfuse"
              },
              {
                "mountPath": "/dev/port",
                "name": "devport"
              }
            ]
          }
        ],
        "volumes": [
          {
            "name": "devfuse",
            "hostPath": {
              "path": "/dev/fuse"
            }
          },
          {
            "name": "devport",
            "hostPath": {
              "path": "/dev/shm/mydevport_mydeployment",
              "type": "FileOrCreate"
            }
          }
        ]
      }
    }
  }
}'

ラベルの利用

Pod とサービスを作成します。

kubectl run mydeployment --image=nginx
kubectl expose deployment/mydeployment --type="NodePort" --port 80

-lkey=value 形式のラベルに合致する Pod およびサービスを選択できます。

kubectl get deployments -l run=mydeployment
kubectl get pods -l run=mydeployment
kubectl get services -l run=mydeployment

追加のラベルを付与するためには以下のようにします。

kubectl label pod mydeployment-65b9f8795d-f67s7 mykey=myvalue
kubectl get pods -l mykey=myvalue

ラベルを指定した削除も行えます。

kubectl delete service -l run=mydeployment
kubectl delete deployments -l run=mydeployment

Pod 内のコンテナへのファイル転送および取得

Pod を作成します。

kubectl run mydeployment --image=nginx

ファイルを転送します。-c でコンテナを指定できます。

echo 'hello' > /tmp/hello.txt
kubectl cp /tmp/hello.txt mydeployment-65b9f8795d-z7b9d:/tmp/
kubectl exec mydeployment-65b9f8795d-z7b9d -- cat /tmp/hello.txt

ディレクトリも転送できます。

mkdir /tmp/mydir
echo 'hello' > /tmp/mydir/hello1.txt
echo 'hello' > /tmp/mydir/hello2.txt
kubectl cp /tmp/mydir mydeployment-65b9f8795d-z7b9d:/tmp/
kubectl exec mydeployment-65b9f8795d-z7b9d -- ls /tmp/mydir/

取得は以下のようになります。

mkdir /tmp/mydir2
kubectl cp mydeployment-65b9f8795d-z7b9d:/tmp/mydir /tmp/mydir2

一時的なポート転送

Pod を作成します。

kubectl run mydeployment --image=nginx

Pod 内の 80 番を localhost の 8080 で Listen するようにポート転送します。

kubectl port-forward mydeployment-65b9f8795d-d7n66 8080:80
curl localhost:8080

デプロイ設定に対しても実行できます。

kubectl port-forward deployment/mydeployment 8080:80

CPU/メモリ 使用量の調査

Node マシン

kubectl top node
kubectl top node debian1

Pod 毎

kubectl top pod -l run=mydeployment

コンテナ間通信

Pod 内のコンテナはネットワークを共有するため、お互いのサービスに localhost:xxxx でアクセスできます。異なる Pod 内のコンテナとの通信については、Rancher の場合、各 Pod に割り当てられた Rancher managed IP を指定して通信できます。既定では managed ネットワーク 10.42.0.0/16 から各 Pod に ip が割り当てられます。

kubectl exec -it mydeployment-65b9f8795d-5qwtf -- /bin/bash
apt update
apt install iproute2
ip addr

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
3: eth0@if181: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 0a:a4:0d:10:28:38 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.42.0.84/32 scope global eth0
       valid_lft forever preferred_lft forever

また別の方法として kubectl expose でサービスを作成し、サービスに ClusterIP を割り当てることで、背後の Pod にアクセスをプロキシして通信することもできます。Rancher の場合は既定では 10.43.0.0/16 が指定できます。

kubectl expose deployment/mydeployment --cluster-ip 10.43.0.123 --port=8080 --container-port=80
kubectl get services
curl 10.43.0.123:8080

NAME           TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
kubernetes     ClusterIP   10.43.0.1     <none>        443/TCP    24m
mydeployment   ClusterIP   10.43.0.123   <none>        8080/TCP   23m

NodePort で利用するポート番号を指定

以下のコマンドでテンプレートを作成できます。

kubectl expose deployment/mydeployment --type="NodePort" --port 80 --cluster-ip 10.43.0.101 --port=22,80,5900 --dry-run -o yaml

出力された YAML を編集して nodePort の設定を追加できます。

...
spec:
  ...
  ports:
  - name: ssh
    port: 22
    protocol: TCP
    targetPort: 22
    nodePort: 30022
  ...

expose ではなく create を利用してサービスを作成します。

kubectl create -f /path/to/my.yaml

YAML 設定ファイルに関するコマンド

新規作成

kubectl create -f nginx.yaml
kubectl create -f configs/

再作成

kubectl replace -f nginx.yaml

削除

kubectl delete -f nginx.yaml -f redis.yaml
関連ページ