マニュフェスト(YAML)を作らず、kubectl コマンドで Pod や Deployment、Service を作成する方法のメモ(kubectl run/kubectl create/kubectl scale/kubectl expose)

k8s の検証を行う際にマニュフェスト(YAML)を作らずに Pod や　Deployment、Service を作る方法を知っておくと良さそうだと思ったのでメモしておく。（あとは CKD/CKAD 取得を目指す場合にも知っておくと良さそう）

具体的には以下のコマンドについて説明する

+ kubectl run
+ kubectl create
+ kubectl scale
+ kubectl expose

# 環境

+ AWS 環境を利用(EKS)
+ k8s は v1.14.9
+ `k` を `kubectl` の alias として指定(``)

# 参考記事

[kubectl run/create/expose のススメ](https://qiita.com/sourjp/items/f0c8c8b4a2a494a80908)

# Pod をサクッと作りたい（kubectl run コマンド)

`kubectl run` コマンドにより可能。

作成できるリソースとして[kubectl run](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#run)の `generator` のページを見ると以下がリンクされている。

[Generators](https://kubernetes.io/docs/reference/kubectl/conventions/#generators)

ここに `Pod` はないが、`generator` のデフォルトが `run-pod/v1` であり、以下のように指定することで作成出来る

```bash
$kubectl run nginx --image=nginx --generator=run-pod/v1
pod/nginx created

$ k get pods
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          5s
```

なお、デフォルトなので設定しなくても良いのではと思ったが、指定しないと以下のようになり、Pod ではなく、Deployment が作成される模様

```bash
$kubectl run nginx --image=nginx

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/nginx created
```

`kubectl run --help` を見ると `Create and run a particular image, possibly replicated` と書いてあるので、なるべく Pod 単独では起動させないようしたいという感じな気がする。
後述するが `kubectl create` で Deploymnet 及び Pod などの作成は可能であるが、指定出来るオプションが少なかったので検証という意味では `kubectl run`を覚えておいたほうが良い気がする。

また、いつも `--generator=run-pod/v1` オプションの `run-pod/v1` を忘れてしまっていたのだが、`--restart=Never` オプションを付与すれば Pod のマニュフェストファイル作成となるのでこちらの方が楽(こちらは `--help` に記載がある）

## 作った Pod の設定を変更したい

`kubectl edit` すればできる（本当はファイルを作ってや立ったほうが良い)

例えば以下で Pod の設定を変更できる

```bash
$k edit pod nginx
```

## YAML ファイルを作りたい

`--dry-run -o yaml` を指定する事で作ることができる
先程の例でやってみる

```bash
$kubectl run nginx --image=nginx --generator=run-pod/v1 --dry-run -o yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
```

こんな感じで出来る。
なのでこれをリダイレクトさせてファイルを作ればいい。

```bash
# テンプレートを作成
$kubectl run nginx --image=nginx --generator=run-pod/v1 --dry-run -o yaml > nginx.yaml

# 編集
$vi nginx.yaml

# apply
$ kubectl apply -f nginx.yaml
pod/nginx created

$ k get pods
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          9s
```

## コンテナで任意のコマンドを実行したい

「Pod　から nslookup を実行したい」などのようなワンライナーの確認をしたい時は以下のようにする。 

```bash
$kubectl run -i busybox --image=busybox --generator=run-pod/v1 --rm --restart=Never --command -- nslookup google.co.jp
If you don't see a command prompt, try pressing enter.
Server:         10.100.0.10
Address:        10.100.0.10:53

Non-authoritative answer:
Name:   google.co.jp
Address: 2404:6800:4004:81d::2003

*** Can't find google.co.jp: No answer

pod "busybox" deleted
```

関係するオプションなどは以下

+ `-i` オプションを指定してフォアグラウンドで実行
+ `--rm` オプションを指定してコンテナが終了したら Pod も削除する
+ `--restart=Never` オプションを指定して失敗した場合でもリスタートさせないようにする(デフォルトは Always　になっている)
+ `--command -- <cmd> <arg1> ... <argN>` という書式のオプションでコマンド及び引数を指定

こんな感じで指定することも良くする

```bash
kubectl run sleep-pod --image=busybox:1.28 -- /bin/sh -c "sleep 4800"
```

## コンテナ内で shell を起動したい

コンテナでワンライナーではない確認をしたい場合に shell を起動したいということがある。
上記については以下のようにすれば良い

```bash
$kubectl run -i --tty busybox --image=busybox --generator=run-pod/v1 --rm -- sh
If you don't see a command prompt, try pressing enter.
/ # exit
Session ended, resume using 'kubectl attach busybox -c busybox -i -t' command when the pod is running
pod "busybox" deleted
```

なお、[kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/)　のコマンドの通りだと shell の起動はできるがメッセージが出たり、あとで deployment の削除も必要で面倒。

```bash
# kubectl Cheat Sheet の通りのコマンド.exit コマンドで shell は終了している
$ kubectl run -i --tty busybox --image=busybox -- sh
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.
If you don't see a command prompt, try pressing enter.
/ # ls
bin   dev   etc   home  proc  root  sys   tmp   usr   var
/ # exit
Session ended, resume using 'kubectl attach busybox-c8f8564d4-thjfg -c busybox -i -t' command when the pod is running

# deployment が残っているので削除が必要
$ k get deployments
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
busybox   1/1     1            1           80s
 
$k delete
 deployments busybox
deployment.extensions "busybox" deleted
```

最初に示したコマンドと比較すると 最初のコマンドでは `--generator` オプションを指定する事で Pod の起動であることを指定して deployment の作成を防ぎ、`--rm` オプションを指定することで shell が終了したタイミングで Pod も削除するようにしている。

## その他例が見たい

`kubectl run --help` コマンドでいくつかサンプルが見えたのでこれを見ておくと良いだろう。
指定可能なオプションの確認もこちらの参照が有効。
[kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/)にもいくつかのサンプルあり。

少しだけ抜粋

```bash
kubectl run -i --tty busybox --image=busybox -- sh  # Run pod as interactive shell
kubectl run nginx --image=nginx --restart=Never -n 
mynamespace                                         # Run pod nginx in a specific namespace
kubectl run nginx --image=nginx --restart=Never     # Run pod nginx and write its spec into a file called pod.yaml
--dry-run -o yaml > pod.yaml
```

# Pod 以外のリソースをサクッと作りたい(kubectl create )

`kubectl create` コマンドを使う。
具体的に作れるリソース郡は以下に記載あり。

[Generators](https://kubernetes.io/docs/reference/kubectl/conventions/#generators)

```
  clusterrole         Create a ClusterRole.
  clusterrolebinding  Create a ClusterRoleBinding for a particular ClusterRole.
  configmap           Create a configmap from a local file, directory or literal value.
  cronjob             Create a cronjob with the specified name.
  deployment          Create a deployment with the specified name.
  job                 Create a job with the specified name.
  namespace           Create a namespace with the specified name.
  poddisruptionbudget Create a pod disruption budget with the specified name.
  priorityclass       Create a priorityclass with the specified name.
  quota               Create a quota with the specified name.
  role                Create a role with single rule.
  rolebinding         Create a RoleBinding for a particular Role or ClusterRole.
  secret              Create a secret using specified subcommand.
  service             Create a service using specified subcommand.
  serviceaccount      Create a service account with the specified name.
  ```

なお、`service` については後述する `kubectl expose` コマンドを使うほうが指定出来るオプションも多い。

試しに deployment を作ってみる。

```bash
$kubectl create deployment nginx --image=nginx

$k get deployments
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           6s
```

こんな感じで作れる。

`kubectl run`コマンドと同様にテンプレートの雛形を作る事も可能。
その時は同じように `--dry-run -o yaml` オプションを付与する。

```bash
$k create deployment nginx --dry-run --image nginx -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        resources: {}
status: {}
```

あとはこれをリダイレクトさせればファイルとして作成出来る。

なお、`--help` オプションで指定可能な詳細が分かるが、[kubectl run](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#run)コマンドと比較すると指定可能なオプションが全然ない。

```bash
$k create deployment --help
Create a deployment with the specified name.

Aliases:
deployment, deploy

Examples:
  # Create a new deployment named my-dep that runs the busybox image.
  kubectl create deployment my-dep --image=busybox

Options:
      --allow-missing-template-keys=true: If true, ignore any errors in
templates when a field or map key is missing in the template. Only applies to
golang and jsonpath output formats.
      --dry-run=false: If true, only print the object that would be sent,
without sending it.
      --generator='': The name of the API generator to use.
      --image=[]: Image name to run.
  -o, --output='': Output format. One of:
json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file.
      --save-config=false: If true, the configuration of current object will be
saved in its annotation. Otherwise, the annotation will be unchanged. This flag
is useful when you want to perform kubectl apply on this object in the future.
      --template='': Template string or path to template file to use when
-o=go-template, -o=go-template-file. The template format is golang templates
[http://golang.org/pkg/text/template/#pkg-overview].
      --validate=true: If true, use a schema to validate the input before
sending it

Usage:
  kubectl create deployment NAME --image=image [--dry-run] [options]

Use "kubectl options" for a list of global command-line options (applies to all
commands).
```

細かい指定をする場合の検証(env や limit など)の場合には `kubectl run` を使うほうが良さそう。

ServiceAccount/Role/RoleBinding の検証時に `kubectl create` コマンドが役にたった。
以下が検証時のページ

[ServiceAccount を作成し、Pod から `kubectl get pods` をやってみた時のメモ
](https://qiita.com/toshihirock/items/d72b3fd1359ec0867fb7)


# ReplicaSet のスケールイン・スケールアウトをさくっと確認したい(kubectl scale コマンド)

[Scaling Resources](https://kubernetes.io/docs/reference/kubectl/cheatsheet/#scaling-resources)

ReplicaSet の数を変えてスケールイン・スケールアウトの際のテストをする時には `kubectl scale` コマンドが便利。
実際にやってみる

```bash
# deploymnet を作って ReplicaSet を作る
$ kubectl create deployment nginx --image=nginx
deployment.apps/nginx created

# デフォルトでは Pod 数は1 
$k get deployments
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           31s

# kubectl scale コマンドで数を変える
$k scale deployment  nginx --replicas=5
deployment.extensions/nginx scaled

# Pod 数が5になっている
$ k get deployments
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   5/5     5            5           2m53s

# 同じようにスケールインさせる
$k scale deployment  nginx --replicas=1
deployment.extensions/nginx scaled

$k get deployments
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           3m51s

```

# Service を作って Pod へのアクセスをサクッと確認したい(kubectl expose コマンド)

Service を使ってアプリケーションを公開できるが、`kubectl expose` コマンドを使うことでこちらも可能。


`--type` オプションを使うことでいくつかのサービスが作成できる。

[Serviceを使ったアプリケーションの公開](https://kubernetes.io/ja/docs/tutorials/kubernetes-basics/expose/expose-intro/)

+ ClusterIP->クラスター内の内部 IP で Service を公開する。Service はクラスター内からのみ到達可能
+ NodePort-> NAT を使用して、クラスター内の選択された各ノードの同じポートに Service を公開する。<NodeIP>:<NodePort>を使用してクラスターの外部から Service にアクセスできるようにする
+ LoadBalancer->現在のクラウドに外部ロードバランサを作成し(サポートされている場合)、Serviceに固定の外部IPを割り当てる

今回は上記3種類で確認する。

## ClusterIP

以下の記事を参考にやってみる

[ Amazon EKS クラスターで実行されている Kubernetes Services を公開するにはどうすればよいですか?](https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-kubernetes-services-cluster/)

ClusterIP のため、確認は同じクラスターの Pod からアクセスしてみる。

```bash
# Pod を作成
$kubectl run nginx  --replicas=2 --labels='app=nginx' --image=nginx --port=80
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/nginx created

# Pod が Running であることと IP を確認
$kubectl get pods -l 'app=nginx' -o wide | awk {'print $1" " $3 " " $6'} | column -t

NAME                    STATUS   IP
nginx-56db997f77-vvbjp  Running  192.168.69.238
nginx-56db997f77-wm5ql  Running  192.168.42.52

# kubectl expose コマンドで ClusterIP のサービスを作成。deployment の nginx を指定してこちらを公開するようにしている 
$kubectl expose deployment nginx  --type=ClusterIP  --name=nginx-service
service/nginx-service exposed

# サービスの情報を確認。アクセスするための IP は Pod の IP とは異なる
$ k get service nginx-service
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
nginx-service   ClusterIP   10.100.223.112   <none>        80/TCP    13s

$k describe service nginx-service
Name:              nginx-service
Namespace:         default
Labels:            app=nginx
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
IP:                10.100.223.112
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         192.168.42.52:80,192.168.69.238:80
Session Affinity:  None
Events:            <none>

# 以下でアクセス出来てレスポンスが返却される
$kubectl run -i testpod --image=centos:6 --generator=run-pod/v1 --restart=Never --rm --command -- curl -s http://nginx-service

# <service-name>.<namespace>.svc.cluster.local によって名前解決出来る。IP の IP アドレスが返却される
$kubectl run -i testpod --image=centos:6 --generator=run-pod/v1 --restart=Never --rm --command -- dig +short nginx-service.default.svc.cluster.local
10.100.223.112
pod "testpod" deleted
```

## NodePort

以下の記事を参考にやってみる

[Serviceを利用したクラスター内のアプリケーションへのアクセス](https://kubernetes.io/ja/docs/tasks/access-application-cluster/service-access-application-cluster/)

[ Amazon EKS クラスターで実行されている Kubernetes Services を公開するにはどうすればよいですか?](https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-kubernetes-services-cluster/)

NodePort を使ってアプリケーションを外部からアクセス出来ることを確認する。

```bash
# hello world アプリをデプロイする。これによって deployment が作成される
$kubectl run hello-world --replicas=2 --labels="run=load-balancer-example" --image=gcr.io/google-samples/node-hello:1.0  --port=8080

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/hello-world created

$k get deployments
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
hello-world   2/2     2            2           26s

# NodePort のサービスを kubectl expose コマンドで作成する
$kubectl expose deployment hello-world --type=NodePort --name=example-service
service/example-service exposed

# service の情報を確認。以下の場合、NodePort では TCP 32425 ポートでアクセスする
$ kubectl describe services example-service
Name:                     example-service
Namespace:                default
Labels:                   run=load-balancer-example
Annotations:              <none>
Selector:                 run=load-balancer-example
Type:                     NodePort
IP:                       10.100.88.21
Port:                     <unset>  8080/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  32425/TCP
Endpoints:                192.168.57.115:8080,192.168.71.173:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

# ノードの PublicIP を確認する
$kubectl get nodes -o wide |  awk {'print $1" " $2 " " $7'} | column -t
NAME                                               STATUS  EXTERNAL-IP
ip-192-168-48-33.ap-northeast-1.compute.internal   Ready   52.192.84.154
ip-192-168-74-169.ap-northeast-1.compute.internal  Ready   54.250.97.77

# <PublicIP>:<NodePort>でアクセスしてみる
$curl http://52.192.84.154:32425
Hello Kubernetes!
```

なお、`curl` を実行するクライアントから Node のポートまでの接続は可能である必要があるので注意(上記例であればクライアントから TCP 32425 番ポートでの接続が可能である前提)
例えば AWS であればセキュリティグループのインバウンドルールが許可されている必要がある。

## LoadBalancer

以下の記事を参考にやってみる

[クラスター内のアプリケーションにアクセスするために外部IPアドレスを公開する](https://kubernetes.io/ja/docs/tutorials/stateless-application/expose-external-ip-address/)

[ Amazon EKS クラスターで実行されている Kubernetes Services を公開するにはどうすればよいですか?](https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-kubernetes-services-cluster/)

LodBalancer を使う場合、外部ロードバランサが使われるが、今回の環境は AWS 環境のため、ELB が作成され、こちらを経由してのアクセスの確認をしてみる。

```bash
# hello world アプリをデプロイ
$kubectl run hello-world --replicas=2 --labels="run=load-balancer-example" --image=gcr.io/google-samples/node-hello:1.0  --port=8080

# 外部公開のために LoadBalancer タイプのサービスを作成
$kubectl expose deployment hello-world --type=LoadBalancer --name=my-service

# サービスの情報を確認
$kubectl get services my-service
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP                                                                   PORT(S)          AGE
my-service   LoadBalancer   10.100.79.155   a8731078a9e2111ea80d80aafc37fdac-244
533548.ap-northeast-1.elb.amazonaws.com   8080:31474/TCP   38s

$kubectl describe services my-service
Name:                     my-service
Namespace:                default
Labels:                   run=load-balancer-example
Annotations:              <none>
Selector:                 run=load-balancer-example
Type:                     LoadBalancer
IP:                       10.100.79.155
LoadBalancer Ingress:     a8731078a9e2111ea80d80aafc37fdac-244533548.ap-northeast-1.elb.amazonaws.com
Port:                     <unset>  8080/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  31474/TCP
Endpoints:                192.168.42.52:8080,192.168.81.79:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type    Reason                Age   From                Message
  ----    ------                ----  ----                -------
  Normal  EnsuringLoadBalancer  88s   service-controller  Ensuring load balancer
  Normal  EnsuredLoadBalancer   84s   service-controller  Ensured load balancer


# <LoadBalancerIngress>:<Port> でアクセス
$curl http://a8731078a9e2111ea80d80aafc37fdac-244533548.ap-northeast-1.elb.amazonaws.com:8080
Hello Kubernetes!
```

OK.
上記例の場合、AWS 環境のため ELB が作成され、ELB を経由してアクセスしている。