こんにちは。KOUKIです。とある企業でWebエンジニアをしています。
最近、Kubenetes(以下k8s)を触る機会があったので、備忘を残したいと思います。
<目次>
とりあえずセットアップ
公式のセットアップ手順にしたがって、minikube上でk8sを動かす環境を作成しましょう!
前提条件として、Mac環境にdockerとvirtualbox, Homebrewがインストールされている程で記載します。
以下のコマンドを実行すると、minikube上にk8sクラスタを構築します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# minikube & kubectlのインストール brew install minikube brew install kubectl # minikube スタート minikube start --driver=virtualbox # ステータス確認 minikube status minikube type: Control Plane host: Running kubelet: Running apiserver: Running kubeconfig: Configured |
Dashboardの表示
1 2 |
# Dashboard表示 minikube dashboard |
minikube dashboardコマンドを実行すると、k8sのリソース情報が確認できます。

サンプルコードの実装
k8s上で動かす簡単なアプリケーションを作成します。
1 2 3 |
touch Dockerfile touch main.go go mod init k8s |
アプリは、Go言語で書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// main.go package main import ( "fmt" "log" "net/http" ) func main() { http.HandleFunc("/", helloHandler) http.HandleFunc("/error", errHandler) log.Fatal(http.ListenAndServe(":8080", nil)) } func helloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Hello from this Golang app!</h1><p>Try sending a request to /error and see what happens</p>") } func errHandler(w http.ResponseWriter, r *http.Request) { log.Fatal() } |
プログラムを実行すると以下のように表示されます。
1 |
go run main.go |

続いて、Dockerfileを書きましょう。golangのイメージは、DockerHubから確認できます。
1 2 3 4 5 6 7 8 9 10 11 |
# Dockerfile FROM golang:1.15-alpine WORKDIR /app COPY . . EXPOSE 8080 CMD ["go", "run", "main.go"] |
以下のコマンドで、Dockerイメージを作成しましょう。
1 2 |
# kub-first-appという名前のimageを作る docker build -t kub-first-app . |
k8s操作 ~コマンド編~
Deploymentオブジェクトの作成
Dockerイメージを作成したので、Deploymentオブジェクトを作成しましょう。
次のコマンドを実行してください。
1 2 |
$ kubectl create deployment first-app --image=kub-first-app deployment.apps/first-app created |
k8sの最小構成単位はPodになりますが、DeploymentはこのPodを管理し、Replica Setを作成して、Podのオートスケーリングを可能にします。
ステータスを確認します。
1 2 3 |
$ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE first-app 0/1 1 0 74s |
READYが、0/1になっていますね。起動に失敗しているようです。別のコマンドを実行して原因を探って見ましょう。
1 2 3 |
$ kubectl get pods NAME READY STATUS RESTARTS AGE first-app-85df859fb4-hm9sl 0/1 ErrImagePull 0 2m14s |
ErrImagePullになっていますね。ローカルで作成した「kub-first-app」をpullできていないようです。
実は、minikubeは、Virtualbox上で動作しているので、ローカルに作成したイメージを引っ張ってくることができません。
回避策は、Docker HubにイメージをPushして、そこからイメージを取得できるようにします。
私は、「kub-first-app」レポジトリを作成して、そこにイメージをPushしようと思います。

1 2 3 4 5 |
# Docker Hubにイメージをプッシュできるようにタグ付けする docker tag kub-first-app アカウント名(h)/kub-first-app # push docker push アカウント名(h)/kub-first-app |
Pushが完了したらDeploymentを作り直しましょう!
1 2 3 4 5 |
# deployment削除しておく $ kubectl delete deployment first-app deployment.apps "first-app" deleted $ kubectl create deployment first-app --image=アカウント名/kub-first-app |
今度の–imageには、DockerHubのレポジトリに格納しているイメージを指定しています。
状態を確認して見ましょう。
1 2 3 |
$ kubectl get pods NAME READY STATUS RESTARTS AGE first-app-8546f98857-kpgrx 1/1 Running 0 4m9s |
OKですね!
Serviceを作ろう
Deploymentを作成し、DashboardからIPアドレスを確認できるようになりました。

「さぁ、アプリケーションにアクセスしよう!」という感じになるかもしれませんが、このままでは通信ができません。
アプリケーションは、Virtualboxのminikube上のクラスタで稼働しており、ローカルと通信することができないからです。
そこで、解決策としてServiceを作りましょう。
1 2 |
kubectl expose deployment first-app --type=LoadBalancer --port=8080 service/first-app exposed |
Serviceは、ClusterIP/NodePort/LoadBalancerの3つのタイプがありますが、ここではLoadBalancerを使用しています。
それぞれ違いがありますが、LoadBalancerは、L4レベルのロードバランサーを用意して、クラスタ内外に通信を可能にするサービスです。
以下のコマンドで状態を確認できます。
1 2 3 4 |
$ kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE first-app LoadBalancer 10.96.177.66 <pending> 8080:30884/TCP 3m54s kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d |
Serviceの稼働を確認したら以下のコマンドを使って、アプリケーションと通信してみましょう。
1 2 3 4 5 6 7 |
$ minikube service first-app |-----------|-----------|-------------|-----------------------------| | NAMESPACE | NAME | TARGET PORT | URL | |-----------|-----------|-------------|-----------------------------| | default | first-app | 8080 | http://192.168.99.100:30884 | |-----------|-----------|-------------|-----------------------------| 🎉 Opening service default/first-app in default browser... |
「http://192.168.99.100:30884/」にて、アプリケーションのURLが公開されているようですね。

Podの自動修復
次のコマンドを実行してください。
1 2 3 |
$ kubectl get pod NAME READY STATUS RESTARTS AGE first-app-8546f98857-kpgrx 1/1 Running 2 22h |
現在のステータスは、Running状態ですね。
それでは、「http://192.168.99.100:30884/error」にアクセスして、プログラムをクラッシュさせてみます。

Podのステータスを確認してみましょう。
1 2 3 |
$ kubectl get pod NAME READY STATUS RESTARTS AGE first-app-8546f98857-kpgrx 0/1 Error 2 22h |
エラーなりましたね。このまましばらく、ステータスを表示させてみます。
1 2 3 4 5 6 7 8 9 |
$ kubectl get pod NAME READY STATUS RESTARTS AGE first-app-8546f98857-kpgrx 0/1 Error 2 22h $ kubectl get pod NAME READY STATUS RESTARTS AGE first-app-8546f98857-kpgrx 0/1 CrashLoopBackOff 2 22h $ kubectl get pod NAME READY STATUS RESTARTS AGE first-app-8546f98857-kpgrx 1/1 Running 3 22h |
Error -> CrashLoopBackOff -> Runningになりましたね。
この様に、DeploymentはPodを監視しており、問題が発生した場合は自動で再起動してくれます。
オートスケール
k8sの利点の一つは、簡単にPodをオートスケールできることにあります。実際に体験してみましょう。
1 2 3 4 5 6 7 8 9 10 |
# Podを3つにスケール $ kubectl scale deployment/first-app --replicas=3 deployment.apps/first-app scaled # status確認 $ kubectl get pods NAME READY STATUS RESTARTS AGE first-app-8546f98857-ctzm2 1/1 Running 0 35s first-app-8546f98857-kpgrx 1/1 Running 4 23h first-app-8546f98857-kzdwh 1/1 Running 0 35s |
簡単ですね!
試しに、「http://192.168.99.100:30884/error」にアクセスして、Podを一つ機能不全にしましょう。

上記の画像は、DashboardのPodの状態を示していますが、エラーになっているPodがあっても他のPodで処理を捌けるので、アプリケーションが落ちません。
Pod数を減らしたい場合も先ほどのコマンドでPod数を指定すばOKです。
1 2 3 |
# Podを1にする $ kubectl scale deployment/first-app --replicas=1 deployment.apps/first-app scaled |
イメージの更新
k8sでは、イメージの更新も簡単です。
挙動を確認するために、Goのアプリケーションを更新してみましょう。
1 2 3 4 5 6 7 8 |
// main.go func helloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` <h1>Hello from this Golang app!</h1> <p>This is new!</p> // Update <p>Try sending a request to /error and see what happens</p>`) } |
アプリケーションを編集できたら、次に以下のコマンドを実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# イメージをビルド $ docker build -t アカウント名/kub-first-app . # deployment情報確認 $ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE first-app 1/1 1 1 39h # Docker HubにイメージをPush $ docker push アカウント名/kub-first-app # イメージの更新 $ kubectl set image deployment/first-app kub-first-app=アカウント名/kub-first-app deployment.apps/first-app image updated $ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE first-app 1/1 1 1 39h |
「kub-first-app」の名前については、k8sのDashboardの「Pod」から確認できます。

一旦画面を表示してみましょう。
1 |
minikube service first-app |

変更が反映されてませんね。Tagを変更してみましょう。
1 2 3 4 5 6 7 8 |
# Tagを変更 $ docker build -t アカウント名/kub-first-app:2 . $ docker push アカウント名/kub-first-app:2 $ kubectl set image deployment/first-app kub-first-app=アカウント名/kub-first-app:2 # 実行結果を確認 $ kubectl rollout status deployment/first-app deployment "first-app" successfully rolled out |
今度はどうでしょうか。

変更が反映されました!どうやら同じタグの場合、k8sは新しいイメージをダウンロードしないようですね(前回ダウンロードしたイメージをそのまま使い回す)。

更新するときは、Tagの更新も忘れないようにしましょう。
ロールバック
イメージ更新やアプリケーションの不具合で、以前のバージョン(ロールバック)に戻したい時もあるかと思います。
例えば、存在しなイメージversion3を使って、アップデートしてみましょう。
1 2 3 4 5 6 7 |
# 存在しないイメージを使ってupdate $ kubectl set image deployment/first-app kub-first-app=アカウント名/kub-first-app:3 deployment.apps/first-app image updated # ステータスを確認 $ kubectl rollout status deployment/first-app Waiting for deployment "first-app" rollout to finish: 1 old replicas are pending termination... |
terminationになりましたね。Dashboardからもやばげな雰囲気が伝わってきます。

このような場合は、以下のコマンドで前回のイメージに戻すことが可能です。
1 2 3 4 5 6 |
# ロールバック $ kubectl rollout undo deployment/first-app deployment.apps/first-app rolled back $ kubectl rollout status deployment/first-app deployment "first-app" successfully rolled out |
履歴も確認できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$ kubectl rollout history deployment/first-app deployment.apps/first-app REVISION CHANGE-CAUSE 1 <none> 2 <none> 4 <none> 5 <none> # リビジョンを指定 $ kubectl rollout history deployment/first-app --revision=4 deployment.apps/first-app with revision #4 Pod Template: Labels: app=first-app pod-template-hash=79b9bbf6c4 Containers: kub-first-app: Image: accountname/kub-first-app:3 Port: <none> Host Port: <none> Environment: <none> Mounts: <none> Volumes: <none> |
Clean Up
ここまで作成したServiceとDeploymentは、以下のコマンドで削除することができます。
1 2 |
kubectl delete service first-app kubectl delete deployment first-app |
k8s操作 ~コンフィグファイル編~
Deploymentファイルの作成
先ほどまではkubectlコマンドでDeploymentなどを作成してましたが、本来はコンフィグファイルを作成して、その情報からDeploymentを起動した方が便利です。
詳しくは、公式サイトを参考にしてください。
1 |
touch deployment.yaml |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#deployment-v1-apps apiVersion: apps/v1 kind: Deployment metadata: name: second-app-deployment spec: replicas: 3 selector: matchLabels: app: second-app tier: backend template: metadata: labels: app: second-app tier: backend spec: containers: - name: second-golang image: アカウント名/kub-first-app:2 |
コマンドで作成したDeploymentの設定情報をyamlファイルに書き出しました。
これを以下のコマンドで読み込みます。
1 2 3 4 5 6 7 8 |
$ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE second-app-deployment 3/3 3 3 108s $ kubectl get pods NAME READY STATUS RESTARTS AGE second-app-deployment-b4c59965-hj6gm 1/1 Running 0 73s second-app-deployment-b4c59965-nv7kx 1/1 Running 0 73s second-app-deployment-b4c59965-v9bwm 1/1 Running 0 73s |
Serviceファイルの作成
Serviceのコンフィグファイルも作成できます。公式サイトは、こちらです。
1 |
touch service.yaml |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#service-v1-core apiVersion: v1 kind: Service metadata: name: backend spec: selector: app: second-app ports: - protocol: 'TCP' port: 80 targetPort: 8080 type: LoadBalancer |
コンフィグファイルが定義できたら以下のコマンドでServiceを立ち上げます。
1 2 3 4 5 6 7 |
$ kubectl apply -f service.yaml service/backend created $ kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE backend LoadBalancer 10.102.138.203 <pending> 80:32405/TCP 49s kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d23h |
無事に立ち上がってますね。次のコマンドで、アプリケーションにアクセスしてみましょう。
1 2 |
# backendは、Serviceのmetadataに設定した名前 $ minikube service backend |

コンフィグファイルを設定すれば、k8sの起動がかなり楽になりますね。
オートスケールやイメージの更新
コンフィグファイルで起動したPodの数の増減をするには、コンフィグファイルを更新して、applyすればOKです。
例えば…
1 2 3 4 5 6 7 8 9 |
# deployment.yaml # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#deployment-v1-apps apiVersion: apps/v1 kind: Deployment metadata: name: second-app-deployment spec: replicas: 1 <<<<<<<<<<< 3 -> 1に変更 ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 現在の状況を確認 $ kubectl get pods NAME READY STATUS RESTARTS AGE second-app-deployment-b4c59965-hj6gm 1/1 Running 1 22h second-app-deployment-b4c59965-nv7kx 1/1 Running 1 22h second-app-deployment-b4c59965-v9bwm 1/1 Running 1 22h # 新しくapply $ kubectl apply -f=deployment.yaml deployment.apps/second-app-deployment configured # 状態確認 $ kubectl get pods NAME READY STATUS RESTARTS AGE second-app-deployment-b4c59965-hj6gm 1/1 Running 1 22h |
簡単ですね。イメージの更新もコンフィグファイルを変更するだけです。
1 2 3 4 |
spec: containers: - name: second-golang image: アカウント名/kub-first-app <<<<<< v2 -> v1に変更 |
1 2 |
$ kubectl apply -f=deployment.yaml deployment.apps/second-app-deployment configured |

削除
削除は、以下のようにします。
1 2 3 4 5 6 7 8 |
$ kubectl delete -f=deployment.yaml deployment.apps "second-app-deployment" deleted $ kubectl delete -f=service.yaml service "backend" deleted # 以下でもOK $ kubectl delete -f=deployment.yaml -f=service.yaml |
マルチプルコンフィグファイル
応用的な使い方になりますが、「—」のセパレートで分ければ同じファイルの中にServiceとDeploymentのコンフィグ情報を記載することができます。
1 |
touch master-deployment.yaml |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
apiVersion: v1 kind: Service metadata: name: backend spec: selector: app: second-app ports: - protocol: 'TCP' port: 80 targetPort: 8080 type: LoadBalancer --- apiVersion: apps/v1 kind: Deployment metadata: name: second-app-deployment spec: replicas: 3 selector: matchLabels: app: second-app tier: backend template: metadata: labels: app: second-app tier: backend spec: containers: - name: second-golang image: アカウント名/kub-first-app:2 |
1 2 3 |
$ kubectl apply -f=master-deployment.yaml service/backend created deployment.apps/second-app-deployment created |
アプリケーションにアクセスします。
1 |
$ minikube srvice backend |

クリーンアップは、以下のコマンドです。
1 |
kubectl delete -f=master-deployment.yaml |
livenessProbeで自動復旧
Podがエラーで落ちると通常は再起動しないと回復できませんが、k8sはそれを自動修復するlivenessProbeという機能を持っています。
deployment.yamlに以下の設定を追加しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#deployment-v1-apps apiVersion: apps/v1 kind: Deployment metadata: name: second-app-deployment spec: replicas: 1 selector: matchLabels: app: second-app tier: backend template: metadata: labels: app: second-app tier: backend spec: containers: - name: second-golang image: アカウント名/kub-first-app:2 # 追加 livenessProbe: httpGet: path: / port: 8080 periodSeconds: 10 initialDelaySeconds: 5 |
上記のlivenessProbe以下が、追加した設定です。また、状態を確認するためにPodのreplicasを1にしています。
以下のコマンドでリソースを作成しましょう。
1 |
kubectl apply -f=deployment.yaml -f=service.yaml |
リソースが作成できたらアプリケーションにアクセスします。
1 |
$ minikube service backend |
「http://XXXXX/error」にアクセスするとアプリケーションが落ちる仕様なっています。

そのあと、ルートにアクセスするとアプリケーションに接続できることがわかります。

livenessProbeが設定されていない場合は、アプリケーションには接続できない状態になります。まぁ、しばらくするとmasterが修復してくれますが。。。
imagePullPolicy ~常にイメージ更新~
先の「イメージの更新」の節で説明しましたが、tag情報を変更していない場合は、k8sは既存(キャッシュされたイメージ)をそのまま使いまわします。
しかし、imagePullPolicyを設定すると「イメージタグが変更していなくてもDocker imageをpullする」挙動になります。
imagePullPolicyの対象となるのは、imageに指定されたイメージ名です。
1 2 3 4 5 6 7 8 9 10 11 |
... containers: - name: second-golang image: アカウント名/kub-first-app:2 <--- このタグをロックする imagePullPolicy: Always <--- 追加 livenessProbe: httpGet: path: / port: 8080 periodSeconds: 10 initialDelaySeconds: 5 |
挙動を確認してみましょう。
main.goファイルを変更します。
1 2 3 4 5 6 7 |
func helloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` <h1>OK Update!!!</h1> // 追加 <h1>Hello from this Golang app!</h1> <p>This is new!</p> <p>Try sending a request to /error and see what happens</p>`) } |
terminal上で以下のコマンドを打ちます。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 再ビルド docker build -t アカウント名/kub-first-app:2 . # Docker HubにPush docker push アカウント名/kub-first-app:2 # 変更を反映 kubectl apply -f=deployment.yaml deployment.apps/second-app-deployment configured ## アプリケーションにアクセス minikube service backend |

タグを変更しなくても更新されました!
最後にclean upしておきましょう。
1 2 3 4 5 |
$ kubectl delete -f=deployment.yaml deployment.apps "second-app-deployment" deleted $ kubectl delete -f=service.yaml service "backend" deleted |
おわりに
k8sは、覚えるべき用語も多いので最初はかなり難しく感じると思います。
しかし、バージョンアップのしやすさ、強固な障害対応、ロードバランサによる処理の振り分けなど、なかなか面白い機能が詰まっています。
ガンガン覚えていきたいですね。
それでは、また!
次回
次回は、Volumeを学びます。
最近のコメント