Kubernetes チートシート

# この記事について
Kubernetes(k8sと呼びます)に関するコマンドを集めたチートシートです。
実際に簡単な構成のk8sクラスタをいじりながら、k8sの基本コマンドである `kubectl` の使い方を中心にまとめていきます。

# 環境
k8sを動かす環境としては、

- ローカルではminikube
- AWS EKS

などがありますが、簡単のためにminikubeを用いたローカル環境を使うことにします。
EKSクラスタでも権限さえ調整すれば基本的には同じ使い方ができます。

# 準備
minikubeを以下のURLを参考にしながらインストールします。
(https://kubernetes.io/docs/tasks/tools/install-minikube/)

まずは、minikubeを起動し、クラスタのIPを確認してみましょう。

```
$ minikube start
...
$ minikube ip
192.168.99.101
```

## 設定の確認
K8sの基本コマンドである `kubectl` を使って、情報をみてみましょう。

### 設定全体をみる
minikubeやEKSだと、これらの設定は自動で作られますが、中身を理解しておくとうまくいかない時に対処がしやすいと思うので説明していきます。

設定ファイルは `~/.kube/config` にありますが、 `kubectl config view` で見ることもできます。
`kubectl config view` の結果から、minikubeに関する情報を抜き出しました。

```yaml:
- cluster:
    certificate-authority: /Users/zawawahoge/.minikube/ca.crt
    server: https://192.168.99.101:8443
  name: minikube
contexts:
- context:
    cluster: minikube
    user: minikube
  name: minikube
current-context: minikube
```

構造としては、`minikube` という名前の`cluster`があり、それを参照している`minikube` という名前の`context` があるというものになっています。
まず、`cluster` にクラスタIPと認証キーを登録しておき、それを`context` が参照しています。
先ほどのクラスタIPと同じIPが設定ファイルにも書き込まれています。

続いて、kubectlが接続する先のクラスタ情報を記述したコンテクストについて `kubectl` を使って確認しましょう。
コンテクストとは、k8sは、複数のクラスタとの接続を簡単にするために用意された、クラスタやプロジェクトの接続先の情報のことです。

詳細には、

- name(コンテクストそのものの名前)
- cluster（どのクラスタ設定を使うか）
- user(どういうユーザとしてクラスタに接続するか)
- authinfo
- namespace(名前空間）

から構成されています。

### 現在設定されているコンテクストの一覧をみる
`kubectl config get-contexts` が使えます。

```bash:
$ kubectl config get-contexts
CURRENT   NAME       CLUSTER    AUTHINFO   NAMESPACE
*         minikube   minikube   minikube
```

minikubeのデフォルト値が少し味気ないですが、EKSクラスタなどの場合はもっと複雑な名前になります。

### デフォルトのコンテクスト名を確認
```bash:
$ kubectl config current-context
minikube
```

----
コンテクストは、kubectlでクラスタに接続しようとしているクライアント側にある情報ですので、書き換えてもリモートには反映されません。
異なるクラスタor名前空間に接続したい場合は、新しくコンテクストを作るか、既存のコンテクストを編集する必要があります。

### 新しくコンテクストを作る

```bash:
$ kubectl config set-context my-context
Context "my-context" created.

$ kubectl config get-contexts my-context
CURRENT   NAME         CLUSTER   AUTHINFO   NAMESPACE
          my-context
```

まっさらなコンテクストが作られました。

### コンテクストを編集する

```bash:
$ kubectl config set-context my-context --namespace new-namespace --cluster minikube
Context "my-context" modified.

$ kubectl config get-contexts my-context
CURRENT   NAME         CLUSTER    AUTHINFO   NAMESPACE
          my-context   minikube              new-namespace
```

### デフォルトのコンテクストを変更する

```bash:
$ kubectl config use-context my-context
Switched to context "my-context".
```

### デフォルトのコンテクストの名前空間を変更する
実用上、接続しているクラスタは同じだが、名前空間を変えたい場合があるかもしれません。
その時は、カレントコンテクストの名前空間を書き換えればいけます。

```bash:
# 変更前のコンテクストの詳細
$ kubectl config get-contexts $(kubectl config current-context)
CURRENT   NAME         CLUSTER    AUTHINFO   NAMESPACE
*         my-context   minikube              new-namespace

$ kubectl config set-context --current --namespace new-new-namespace
Context "my-context" modified.

# 変更後のコンテクストの詳細
$ kubectl config get-contexts $(kubectl config current-context)
CURRENT   NAME         CLUSTER    AUTHINFO   NAMESPACE
*         my-context   minikube              new-new-namespace
```

### コンテクストの設定詳細

```bash:
$ kubectl config view

# デフォルトのコンテクストの設定詳細
$ kubectl config view --minify
```

# 実際にクラスタを動かしてみる
前座が長くなってしまいましたが、実際にminikubeのクラスタを動かしてみましょう。
`nginx` のイメージから作られたコンテナが３つあるような、かなり単純なクラスタをデプロイします。

```yaml:nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
```

デフォルトコンテクストをminikubeに戻してクラスタにアクセスできるように準備します。

```bash:
kubectl config use-context minikube
Switched to context "minikube".
```

### クラスタをデプロイする

```bash:
$ kubectl apply -f nginx-deploy.yaml
```

### クラスタに関する情報の一覧をみる

k8sでは、

- Deployment
- ReplicaSet
- Pod
- Service

の４つを組み合わせてクラスタを作っていくことになります。
`kubectl get` を使うと、クラスタの情報を取得できます。`all` とすると、上の4つ全てについて出力されます。


```bash:
$ kubectl get all
NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-56db997f77-6rvxq   1/1     Running   0          8m27s
pod/nginx-56db997f77-9w55k   1/1     Running   0          8m27s
pod/nginx-56db997f77-ptw9c   1/1     Running   0          8m27s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   13d

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   3/3     3            3           8m27s

NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-56db997f77   3         3         3       8m27s
```

例えば、Deploymentだけをみたい時は、`all` の代わりに`deploy`を渡します。
`-o wide` のオプションをつけると出力される情報が増えます。

```bash:
$ kubectl get deploy
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   3/3     3            3           11m

$ kubectl get deploy -o wide
NAME    READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES   SELECTOR
nginx   3/3     3            3           11m   nginx        nginx    app=nginx
```

### 詳細な設定を見る
`-o yaml` や `-o json`とすると、さらに詳細な情報がみられます。先ほどapplyしたyamlファイルはかなり省略して書いていましたが、省略されたデフォルトの値がコマンドで確認できます。
`kubectl get deploy -o yaml` の結果が以下のようになります。

```yaml:
apiVersion: v1
items:
- apiVersion: extensions/v1beta1
  kind: Deployment
  metadata:
    annotations:
      deployment.kubernetes.io/revision: "1"
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"nginx","namespace":"default"},"spec":{"replicas":3,"selector":{"matchLabels":{"app":"nginx"}},"template":{"metadata":{"labels":{"app":"nginx"}},"spec":{"containers":[{"image":"nginx","name":"nginx","ports":[{"containerPort":80}]}]}}}}
    creationTimestamp: "2019-05-11T17:51:32Z"
    generation: 1
    name: nginx
    namespace: default
    resourceVersion: "211356"
    selfLink: /apis/extensions/v1beta1/namespaces/default/deployments/nginx
    uid: 693dc4b4-7415-11e9-9b1f-0800273e0575
  spec:
    progressDeadlineSeconds: 600
    replicas: 3
    revisionHistoryLimit: 10
    selector:
      matchLabels:
        app: nginx
    strategy:
      rollingUpdate:
        maxSurge: 25%
        maxUnavailable: 25%
      type: RollingUpdate
    template:
      metadata:
        creationTimestamp: null
        labels:
          app: nginx
      spec:
        containers:
        - image: nginx
          imagePullPolicy: Always
          name: nginx
          ports:
          - containerPort: 80
            protocol: TCP
          resources: {}
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
        dnsPolicy: ClusterFirst
        restartPolicy: Always
        schedulerName: default-scheduler
        securityContext: {}
        terminationGracePeriodSeconds: 30
  status:
    availableReplicas: 3
    conditions:
    - lastTransitionTime: "2019-05-11T17:54:24Z"
      lastUpdateTime: "2019-05-11T17:54:24Z"
      message: Deployment has minimum availability.
      reason: MinimumReplicasAvailable
      status: "True"
      type: Available
    - lastTransitionTime: "2019-05-11T17:51:32Z"
      lastUpdateTime: "2019-05-11T17:54:24Z"
      message: ReplicaSet "nginx-56db997f77" has successfully progressed.
      reason: NewReplicaSetAvailable
      status: "True"
      type: Progressing
    observedGeneration: 1
    readyReplicas: 3
    replicas: 3
    updatedReplicas: 3
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""
```

かなり詳細な情報が得られました。

### Pod一覧をみる
`-o wide` をオプションとしてつけると、表示される情報が増えます。

```bash:
$ kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
nginx-56db997f77-6rvxq   1/1     Running   0          22m   172.17.0.10   minikube   <none>           <none>
nginx-56db997f77-9w55k   1/1     Running   0          22m   172.17.0.9    minikube   <none>           <none>
nginx-56db997f77-ptw9c   1/1     Running   0          22m   172.17.0.8    minikube   <none>           <none>
```

### Pod名のリストを求める
awkを使うと、Pod名のリストを簡単に抜き出せます。２行目以降について一列目を表示します。

```bash:
# Pod名のリストを表示
$ kubectl get pods | awk 'NR>1{print $1}'
nginx-56db997f77-6rvxq
nginx-56db997f77-9w55k
nginx-56db997f77-ptw9c

# PodのIPのリストを表示
$ kubectl get pods -o wide | awk 'NR>1{print $6}'
172.17.0.9
172.17.0.8
172.17.0.4
```

### 特定のラベルを持つPodのリストを求める
`-l` オプションでラベルを指定すると、特定のラベルを持つPODのみを抜き出せます。

```bash:
$ kubectl get pods -l app=nginx
NAME                     READY   STATUS    RESTARTS   AGE
nginx-56db997f77-6rvxq   1/1     Running   2          36h
nginx-56db997f77-9w55k   1/1     Running   2          36h
nginx-56db997f77-ptw9c   1/1     Running   2          36h
```

### 

## クラスタ内のコンテナにアクセスする
3つのPodはそれぞれクラスタにおけるプライベートIPを持ちます。
k8sの特徴として、同一クラスタ内のPodはこのプライベートIPを用いて互いに通信できる、というものがあります。
実際に確かめてみましょう。

### クラスタ内にアクセスするための一時的なPodを立てる
busyboxでwgetするためだけのPodを立てて、`172.17.0.10` のPodにリクエストを送信すると、無事`index.html` が返ってきました。

```:
$ kubectl run -i -t busybox --image=busybox --restart=Never
/ # wget 172.17.0.10:80
Connecting to 172.17.0.10:80 (172.17.0.10:80)
index.html           100% |****************************************************************************************|   612  0:00:00 ETA

/ # ip route
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 scope link  src 172.17.0.11
# このPodのプライベートIPは172.17.0.11
```

### コンテナに入ってみる
docker execと同じ使い方ができる `kubectl exec` を使うことで、Podのなかのコンテナにアクセスすることができます。

```bash:
$ kubectl exec -it nginx-56db997f77-6rvxq bash
root@nginx-56db997f77-6rvxq:/# ls
bin  boot  dev	etc  home  lib	lib64  media  mnt  opt	proc  root  run  sbin  srv  sys  tmp  usr  var
root@nginx-56db997f77-6rvxq:/#
```

ただ、nginxのログは標準出力に出されていたようなので、`kubectl logs` で確認します。

### Pod内のコンテナが出力したログを確認する

```bash:
$ kubectl logs nginx-56db997f77-6rvxq
172.17.0.11 - - [11/May/2019:18:28:39 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
```

先ほどhttpリクエストを送った送信元のbusyboxコンテナのIPが`172.17.0.11` でしたので、ログがちゃんと保存されていることがわかります。

## Service
### LoadBalancerで外部からの通信をPodにルーティングする
３つのnginxのPodが正常に動いていることは確認できましたが、このままではクラスタの外からは通信ができません。
外部からの通信を、指定した方法でPodにルーティングしてくれる**Load balancer**というTypeのServiceがあります。

```yaml:nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-lb
spec:
  selector:
    app: nginx
  ports:
  - port: 80
  type: LoadBalancer
```

`apply -f` すると、新しくServiceが作られます。
このServiceをDeploymentと別々に追加してちゃんとLoadBalancerとDeploymentが組み合わさるのか心配になると思います。
実は、`selector` がDeploymentのものと一致しているおかげで、ServiceがDeploymentを見つけられるようになってます。

```bash:
$ kubectl apply -f nginx-service.yaml
service/nginx-lb created

$ kubectl get svc
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP        13d
nginx-lb     LoadBalancer   10.99.193.167   <pending>     80:32328/TCP   10s
```

minikubeだとこのServiceがPending状態で進まないので、アクセスができません。
Version 0.31.0からminikubeに`minikube tunnel` という機能が追加され、これを使うとアクセスできるようになりました。
参考資料：https://qiita.com/inajob/items/4025a1d1aa83721c453d

```
$ minikube tunnel
Status:
	machine: minikube
	pid: 45884
	route: 10.96.0.0/12 -> 192.168.99.101
	minikube: Running
	services: [nginx-lb]
    errors:
		minikube: no errors
		router: no errors
		loadbalancer emulator: no errors
```

再び、`get` してみると、`EXTERNAL-IP` がちゃんと表示されています。

```bash:
$ kubectl get svc
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1      <none>         443/TCP        13d
nginx-lb     LoadBalancer   10.109.96.98   10.109.96.98   80:32425/TCP   10m
```

```
$ curl 10.109.96.98
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
　　...（略）
```

外部からアクセスできました。
実際のAWSでの運用では、EKSサービスで利用可能なLoadBalancerを使わなければなりませんので、ご注意ください。