Bootstarp Excercise for CKA/CKAD

14 min read読了の目安(約13300字

目的

kubernetesの認定資格CKA/CKADを受験するにあたって、
試験準備の前提となるような基本スキルをハンズオンで習得しておく
(kubernetesドキュメントには書いていないものを中心に)
具体的には、nginxとbusyboxコンテナの性質と使い方を抑える。

想定読者

これからCKA/CKADの受験を検討しており、
k8sの概念を研修や書籍で学んだことがあるが、実践経験があまりない

留意事項

以下の環境は予告なく変更・削除することがあります

環境

kubernetes1.16.0(1 master, 2worker)
*Openstack上に構築したCentOS7.3で稼働

[centos@takano-1-1 ~]$ cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)
$ kubectl get nodes -o wide
NAME                   STATUS   ROLES    AGE    VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION          CONTAINER-RUNTIME
takano-1-1.novalocal   Ready    master   141d   v1.16.0   10.90.0.20    <none>        CentOS Linux 7 (Core)   3.10.0-514.el7.x86_64   docker://18.9.7
takano-1-2.novalocal   Ready    <none>   141d   v1.16.0   10.90.0.24    <none>        CentOS Linux 7 (Core)   3.10.0-514.el7.x86_64   docker://19.3.2
takano-1-3             Ready    <none>   140d   v1.16.0   10.90.0.23    <none>        CentOS Linux 7 (Core)   3.10.0-514.el7.x86_64   docker://19.3.2

(参考)CNIはflannelを利用

$ kubectl get po -n kube-system | grep flannel
kube-flannel-ds-amd64-4mwht                    1/1     Running   0          4m40s
kube-flannel-ds-amd64-98fgn                    1/1     Running   0          4m40s
kube-flannel-ds-amd64-tshbf                    1/1     Running   0          4m40s

接続方法

添付の鍵ファイルを用いてアクセス

ssh -i <秘密鍵のファイルパス> centos@10.90.0.20

コンテンツ

nginxPod

nginxは頻出のコンテナイメージ。
httpで80番ポートにアクセスすることでWelcome to nginx!
のページが表示される。
これをPodで建てて一通り疎通の確認ができることが第一歩。

はじめに環境のマスターノードに接続する(鍵は添付のファイル)

ssh -i <秘密鍵のファイルパス> centos@10.90.0.20

まずは、dokerでnginxイメージの基本仕様を確認しておく

$ docker run --rm -d -p 80:80 --name nginx-container nginx
b5812392d49f3976e8fceb7797f4330e678fb5b4973464a6504919972d36653f
[centos@village-1-1 ~]$ curl http://localhost:80
(長い)

長いので以下のようにすると便利(ステータスコード200も確認できる)

$ curl --head http://localhost
HTTP/1.1 200 OK
Server: nginx/1.17.8
Date: Thu, 13 Feb 2020 03:59:40 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 21 Jan 2020 13:36:08 GMT
Connection: keep-alive
ETag: "5e26fe48-264"
Accept-Ranges: bytes

コンテナはもう不要なので落とす

$ docker stop nginx-container
nginx-container

次に、kubernetesのnginx-podをたてる、、がその前に。
yamlを1から手で作成すると大変なため、kubectl runを使って基本系のyamlを生成する。また、様々な設定を付け足す場合にも基本系のyamlを修正して適用する。
さらに、後から履歴を見直して修正できるようにするためにも、一旦は設定反映しないために--dry-runオプションを投入し、-o yamlでyamlファイルを出力する。

$ kubectl run --restart=Never --image=nginx --port=80 nginx-pod --dry-run -o yaml > nginx-pod.yaml
$ cat nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx-pod
  name: nginx-pod
spec:
  containers:
  - image: nginx
    name: nginx-pod
    ports:
    - containerPort: 80
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Never
status: {}

ここで、実際にnginx-podをたてる

$ kubectl create -f nginx-pod.yaml
pod/nginx-pod created

nginx-podのIPアドレスを取得する
kubectlの-o wideオプションをつけることで取得できる

$ kubectl get po -o wide | grep nginx
nginx-pod               1/1     Running            0          2m45s   192.168.16.203    takano-1-3             <none>           <none>

nginx-podのIPアドレスにアクセスしてみると、アクセスできる
このIPはClusterIPである

$ curl --head 192.168.16.203
HTTP/1.1 200 OK
Server: nginx/1.17.8
Date: Thu, 13 Feb 2020 04:20:05 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 21 Jan 2020 13:36:08 GMT
Connection: keep-alive
ETag: "5e26fe48-264"
Accept-Ranges: bytes

次にマスターノードではなく自端末からClusterIPにアクセスしてみる

$ curl --head 192.168.16.203
→クラスタ外なのでアクセスできない

最後に、nginx-podServiceとして公開し、外部の自端末からアクセスできるようにする(既存リソースを後から公開する問題も頻出)
まずは、基本系のyamlを作る。
※(補足)NodePortを指定することでクラスタ外からも、クラスタのホストOSへIPリーチャブルであればPodへ接続できる。

$ kubectl expose pod nginx-pod --port=80 --target-port=80 --type=NodePort --dry-run -o yaml > nginxpod-servise.yaml
$ cat nginxpod-servise.yaml
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    run: nginx-pod
  name: nginx-pod
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: nginx-pod
  type: NodePort
status:
  loadBalancer: {}

次に、基本系のyamlでは書き換えられない部分を手で書き換えて30001ポートを公開するようにする

$ vi nginxpod-servise.yaml
$ cat nginxpod-servise.yaml
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    run: nginx-pod
  name: nginx-pod
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30001 ★ここ。
  selector:
    run: nginx-pod
  type: NodePort
status:
  loadBalancer: {}

yamlを適用すると、狙ったとおりのPortで公開していそうなことがわかる

$ kubectl create -f nginxpod-servise.yaml
service/nginx-pod created
$ kubectl get svc -o wide | grep nginx
nginx-pod    NodePort    10.103.180.220   <none>        80:30001/TCP   2m43s   run=nginx-pod

自端末からアクセスする際はホストのIPを使うため、調べる。
アドレスを調べる場合は-o wideを付ける

$ kubectl get nodes -o wide
NAME                   STATUS   ROLES    AGE    VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION          CONTAINER-RUNTIME
takano-1-1.novalocal   Ready    master   141d   v1.16.0   10.90.0.20    <none>        CentOS Linux 7 (Core)   3.10.0-514.el7.x86_64   docker://18.9.7
takano-1-2.novalocal   Ready    <none>   141d   v1.16.0   10.90.0.24    <none>        CentOS Linux 7 (Core)   3.10.0-514.el7.x86_64   docker://19.3.2
takano-1-3             Ready    <none>   141d   v1.16.0   10.90.0.23    <none>        CentOS Linux 7 (Core)   3.10.0-514.el7.x86_64   docker://19.3.2

実際にアクセスしてみる。どのホストを狙ってもPodに転送されることがわかる

(base) cloudg:~ takano.s$ curl --head 10.90.0.20:30001
HTTP/1.1 200 OK
Server: nginx/1.17.8
Date: Thu, 13 Feb 2020 06:37:32 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 21 Jan 2020 13:36:08 GMT
Connection: keep-alive
ETag: "5e26fe48-264"
Accept-Ranges: bytes

(base) cloudg:~ takano.s$ curl --head 10.90.0.23:30001
HTTP/1.1 200 OK
Server: nginx/1.17.8
Date: Thu, 13 Feb 2020 06:37:35 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 21 Jan 2020 13:36:08 GMT
Connection: keep-alive
ETag: "5e26fe48-264"
Accept-Ranges: bytes

(base) cloudg:~ takano.s$ curl --head 10.90.0.24:30001
HTTP/1.1 200 OK
Server: nginx/1.17.8
Date: Thu, 13 Feb 2020 06:37:37 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 21 Jan 2020 13:36:08 GMT
Connection: keep-alive
ETag: "5e26fe48-264"
Accept-Ranges: bytes

busybox

busyboxコンテナもよく使われる。nginx Podに対する疎通など、Pod間の疎通確認で使われることが多い。
Podに対してexecコマンドで

dockerコマンドで基本を確認

まずはdokcerで実行し基本的な設定を確認する

とりあえずdocker runしてみると、exit状態となる

$ docker run -d --name=busybox-container busybox

c8703325d737999668f8e9183d41940ab2bd3aa7f9db228e4f811e7f988e07be

$docker ps -a | grep busybox-container

c8703325d737    busybox         "sh"           28 seconds ago   Exited (0) 26 seconds ago            busybox-container

Docker inspsctを実行してみると,Entrypointが空でCMDがshしかないためであることがわかる

$docker inspect busybox-container | grep Entrypoint

​      "Entrypoint": null,

$docker inspect busybox-container | grep -A 2 Cmd

​      "Cmd": ["sh"],

$docker rm busybox-container

busybox-container

sleepコマンドをcmdに指定することで、起動しておく。


$docker run -d --name=busybox-container busybox sleep 3600

d0d00c23b25855d7fc7cc71b9e95d022facb651b6cc4f107afc0b3a2ac0534fe

$docker ps | grep busybox-container

d0d00c23b258    busybox        "sleep 3600"       4 seconds ago    Up 3 seconds              busybox-container

pingとかwgetは入っているが、curlが入っていない。nginxとの疎通を取る場合はwgetを使うことが多い


$docker exec busybox-container which curl

$docker exec busybox-container which wget

/bin/wget

$docker exec busybox-container which ping

/bin/ping

$docker stop busybox-container

busybox-container

$docker rm busybox-container

busybox-container

kubernetesで確認

kubectl runでPodを起動する。dockerと同様にexitとなっていることがわかる。

ただし、正常終了のためCompletedとなる


$kubectl run --restart=Never --image=busybox busybox-pod

pod/busybox-pod created

$kubectl get po

NAME     READY  STATUS   RESTARTS  AGE

busybox-pod  0/1   Completed  0     7s

PodでなくDeploymentで起動してしまった場合は自動でRestartが走りつづけるため注意

(10s->20s->40s->…最大5min)


$ kubectl run --image=busybox busybox-deployment

kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.

deployment.apps/busybox-deployment created

$kubectl get po

NAME                 READY  STATUS   RESTARTS  AGE

busybox-deployment-544b6cffc-8jbc9  0/1   Completed  1     12s

busybox-pod             0/1   Completed  0     58s

$kubectl get po

NAME                 READY  STATUS       RESTARTS  AGE

busybox-deployment-544b6cffc-8jbc9  0/1   CrashLoopBackOff  2     50s

busybox-pod             0/1   Completed     0     96s

$kubectl get po -w (抜粋)

busybox-deployment-544b6cffc-8jbc9  0/1   CrashLoopBackOff  3     79s

busybox-deployment-544b6cffc-8jbc9  0/1   CrashLoopBackOff  4     111s

busybox-deployment-544b6cffc-8jbc9  0/1   CrashLoopBackOff  5     3m37s

一度クリーンアップする


$kubectl delete po --all

pod "busybox-pod" deleted

$ kubectl delete deploy --all

deployment.extensions "busybox-deployment" deleted

sleepコマンドを引数に入れるとRunningとなる。

Execコマンドも実行できる


$kubectl run --restart=Never --image=busybox busybox-pod sleep 3600

pod/busybox-pod created

$kubectl get po

NAME     READY  STATUS  RESTARTS  AGE

busybox-pod  1/1   Running  0     7s

$kubectl exec busybox-pod env

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

HOSTNAME=busybox-pod

KUBERNETES_PORT_443_TCP_PORT=443

KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1

KUBERNETES_SERVICE_HOST=10.96.0.1

KUBERNETES_SERVICE_PORT=443

KUBERNETES_SERVICE_PORT_HTTPS=443

KUBERNETES_PORT=tcp://10.96.0.1:443

KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443

KUBERNETES_PORT_443_TCP_PROTO=tcp

HOME=/root

クリーンアップする


$kubectl delete po --all

pod "busybox-pod" deleted

上記sleepコマンドを入れたyamlを作成することもできる。

留意点として、下記argsはdockerのCMDに相当する

$kubectl run --restart=Never --image=busybox busybox-pod sleep 3600 --dry-run -o yaml

apiVersion: v1

kind: Pod

metadata:

 creationTimestamp: null

 labels:

  run: busybox-pod

 name: busybox-pod

spec:

 containers:

  - args:

  - sleep

  - "3600"

  image: busybox

  name: busybox-pod

  resources: {}

 dnsPolicy: ClusterFirst

 restartPolicy: Never

status: {}

以下のようなcommandはdockerのENTRYPOINTに相当する


$kubectl run --restart=Never --image=busybox busybox-pod --command sleep 3600 --dry-run -o yaml

apiVersion: v1

kind: Pod

metadata:

 creationTimestamp: null

 labels:

  run: busybox-pod

 name: busybox-pod

spec:

 containers:

  - command:

  - sleep

  - "3600"

  image: busybox

  name: busybox-pod

  resources: {}

 dnsPolicy: ClusterFirst

 restartPolicy: Never

status: {}

nginxpodとbusyboxpodを立てて疎通確認の準備をする。

IPアドレスを確認しておく


$kubectl run --restart=Never --image=nginx nginx-pod

pod/nginx-pod created

$kubectl run --restart=Never --image=busybox busybox-pod sleep 3600

pod/busybox-pod created

$kubectl get po -o wide

NAME     READY  STATUS  RESTARTS  AGE  IP      NODE       NOMINATED NODE  READINESS GATES

busybox-pod  1/1   Running  0     20s  10.1.0.157  docker-desktop  <none>      <none>

nginx-pod   1/1   Running  0     27s  10.1.0.156  docker-desktop  <none>      <none>

pingやwgetで疎通が確認できる


$kubectl exec busybox-pod ping 10.1.0.156

PING 10.1.0.156 (10.1.0.156): 56 data bytes

64 bytes from 10.1.0.156: seq=0 ttl=64 time=0.242 ms

64 bytes from 10.1.0.156: seq=1 ttl=64 time=0.160 ms

^C

$kubectl exec busybox-pod wget 10.1.0.156

Connecting to 10.1.0.156 (10.1.0.156:80)

saving to 'index.html'

index.html      100% |********************************|  612 0:00:00 ETA

'index.html' saved

IPアドレスがわかっていれば名前でアクセスすることも可能


$ kubectl exec busybox-pod nslookup 10-1-0-156.default.pod.cluster.local

Server:		10.96.0.10

Address:	10.96.0.10:53



Name:	10-1-0-156.default.pod.cluster.local

Address: 10.1.0.156



*** Can't find 10-1-0-156.default.pod.cluster.local: No answer



$kubectl exec busybox-pod ping 10-1-0-156.default.pod.cluster.local

PING 10-1-0-156.default.pod.cluster.local (10.1.0.156): 56 data bytes

64 bytes from 10.1.0.156: seq=0 ttl=64 time=0.128 ms

64 bytes from 10.1.0.156: seq=1 ttl=64 time=0.229 ms

^C

最後にexecする際の留意点を知っておく。

Secretのマウント時などにアクセス権を確認することがあるが、

ハイフン付きオプションを指定するとエラーとなる。

その際は--(ハイフン2つ)をコマンドの前につける。


$kubectl exec busybox-pod ls -l /etc

Error: unknown shorthand flag: 'l' in -l

(略)

$kubectl exec busybox-pod -- ls -l /etc

total 32

-rw-rw-r--  1 root   root      307 Dec 1 21:39 group

-rw-r--r--  1 root   root      12 Feb 16 01:01 hostname

-rw-r--r--  1 root   root      206 Feb 16 01:01 hosts

-rw-r--r--  1 root   root      127 Sep 17 20:51 localtime

lrwxrwxrwx  1 root   root      12 Feb 16 01:01 mtab -> /proc/mounts

drwxr-xr-x  6 root   root     4096 Dec 23 19:21 network

-rw-r--r--  1 root   root      340 Dec 1 21:39 passwd

-rw-r--r--  1 root   root      103 Feb 16 01:01 resolv.conf

-rw-------  1 root   root      136 Dec 23 19:21 shadow