【作業ログ】Kubernetes チュートリアル【基本編】
はじめに
業務で Kubernetes を扱うに当たり、チュートリアルを実施しました。一緒に「なんもわからん」から「完全に理解した!」を経て「チョットデキル」を目指しましょう!
目次
環境
Kubernetes チュートリアルを実施した環境のバージョンは次のとおり。
$ sw_vers
ProductName: macOS
ProductVersion: 15.6
BuildVersion: 24G84
$ docker --version
Docker version 28.3.2, build 578ccf6
$ kubectl version
Client Version: v1.34.0
Kustomize Version: v5.7.1
$ minikube version
minikube version: v1.36.0
commit: f8f52f5de11fc6ad8244afac475e1d0f96841df1
Kubernetes チュートリアル
kubectl のインストール
Kubernetes クラスターを CLI から操作する kubectl をインストールしよう。OS 毎に手順が異なりる。こちら からインストール手順を確認できる。
筆者の環境は macOS であり、homebrew 経由でインストールした。
$ brew install kubectl
Hello Minikube
本チュートリアルではまず最初に、minikube を使用して Kubernetes クラスターの構築・追加・削除を体験する。
minikube とは何か
はじめに minikube について補足しておこう。minikube とは、ローカルで簡単に Kubernetes クラスターを構築するためのツールである。まさに学習用にもってこいというわけだ。
詳細は minikube セクションで深掘りを行っている。
minikube のインストール
まずは minikube のインストール を行う。
minikube のインストールサイトでは「OS × Architecture × Installer type」を選択すると、自動的にインストールコマンドを提示してくれる。大変便利だ。筆者の環境は macOS であり、パッケージマネージャーで管理するために homebrew 経由でのインストールを選択した。
$ brew install minikube
minikube クラスターの作成
minikube クラスターを作成してみよう。
$ minikube start
😄 minikube v1.36.0 on Darwin 15.6.1 (arm64)
✨ Automatically selected the docker driver. Other choices: parallels, ssh
📌 Using Docker Desktop driver with root privileges
👍 Starting "minikube" primary control-plane node in "minikube" cluster
🚜 Pulling base image v0.0.47 ...
🔥 Creating docker container (CPUs=2, Memory=2865MB) ...
🐳 Preparing Kubernetes v1.33.1 on Docker 28.1.1 ...
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
🔗 Configuring bridge CNI (Container Networking Interface) ...
🔎 Verifying Kubernetes components...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟 Enabled addons: storage-provisioner, default-storageclass
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
さまざまなことが行われている。
- Docker driver の選択
- Docker Desktop driver を root 権限で使用
- minikube クラスターの作成
- プライマリのコントロールプレーン・ノード
miniube
を起動
- プライマリのコントロールプレーン・ノード
- ベースイメージの pull
- Kuberntes の準備
- 証明書とキーの作成
- コントロールプレーンの起動
- RBAC ルールの設定
- Kubernetes コンポーネントの検証
- アドオンの有効化
- kubectrl のデフォルト設定
- minikube クラスターを使用
- default 名前空間を使用
まず、minikube の driver として Docker が自動的に選択されたようだ。Docker Desktop も利用している。他の選択肢として parallels
と ssh
が提示されている。minikube にはさまざまな driver が用意されているようだ[1]。driver の深掘りは minikube Drivers セクションにて行う。
minikube のクラスターを作成し、Kubernetes 起動の準備を行っている。用語が多数登場するが、詳細な理解は後回しにする。
最後に、kubectrl のデフォルト設定を行なったメッセージが出力された。kubectrl コマンドのアクセスがデフォルトでは minikube クラスターに接続されるようだ。チュートリアル中は便利だが、そんなことをして良いのか。kubectrl には接続先を切り替えるプロフィール機能のようなものがあるらしい。ローカル環境が汚されることを嫌う筆者としては、この設定がいつ解除されるのかが気になった場面であった。
ダッシュボードを開く
minikube のダッシュボードを開いてみよう。
$ minikube dashboard
🔌 Enabling dashboard ...
▪ Using image docker.io/kubernetesui/dashboard:v2.7.0
▪ Using image docker.io/kubernetesui/metrics-scraper:v1.0.8
💡 Some dashboard features require the metrics-server addon. To enable all features please run:
minikube addons enable metrics-server
🤔 Verifying dashboard health ...
🚀 Launching proxy ...
🤔 Verifying proxy health ...
🎉 Opening http://127.0.0.1:55851/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...
ブラウザで次のような画面が開いた。
サイドメニューの大項目は次のとおりだ。
- Workloads
- Service
- Config and Storage
- Cluster
- Custom Resource Definitions
- Setting
- About
Kuberntes コンポーネントなどの状態を GUI から操作できるようになっている。かなり便利そうだ。
Deployment の作成
- Pod
- 1つ以上のコンテナのグループ
- 所属するコンテナは、コンテナの管理やネットワーキングの目的でまとめられる
- Docker イメージをもとにコンテナを実行する
- 1つ以上のコンテナのグループ
- Deployment
- Pod の作成やスケールを管理するための方法
- Pod の状態を確認し、Pod のコンテナが停止した場合には再起動を行う
Deployment 経由で Pod を管理し、Pod 経由でコンテナが作成されるようだ。実際に Deployment を作成してみよう。
$ kubectl create deployment hello-node --image=registry.k8s.io/e2e-test-images/agnhost:2.39 -- /agnhost netexec --http-port=8080
deployment.apps/hello-node created
Deployment の作成には kubectl create deployment
コマンドを使用する。コマンドは次の形式になっていそうだ。
$ kubectl create depoyment ${name} --image=${registry}/${image_name}:${version} -- ${image_arguments}
イメージには registry.k8s.io/e2e-test-images/agnhost
を指定している。イメージをホスティングしているレジストリに自身がない。イメージとしては Docker Hub にある opsdockerimage/e2e-test-images-agnhost
であり、それを registry.k8s.io
レジストリでホスティングしている、であっているだろうか?調べてもあまり情報が出てこない。
Deployment の状態を確認してみよう。無事に作成されていそうだ。
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
hello-node 1/1 1 1 1m
Pod はどうだろう?こちらも起動中になっている。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-node-c74958b5d-qt9p8 1/1 Running 0 1m
Pod のログを見てみよう。
$ kubectl logs hello-node-c74958b5d-qt9p8
I0830 11:48:37.432974 1 log.go:195] Started HTTP server on port 8080
I0830 11:48:37.433201 1 log.go:195] Started UDP server on port 8081
クラスターイベントを確認できるらしいが、特にイベントはなかったようだ。
$ kubectl get events
No resources found in default namespace.
Kubernetes の設定内容を確認することもできた。
$ kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://127.0.0.1:6443
name: docker-desktop
- cluster:
certificate-authority: /Users/user/.minikube/ca.crt
extensions:
- extension:
last-update: Sat, 30 Aug 2025 19:53:24 JST
provider: minikube.sigs.k8s.io
version: v1.36.0
name: cluster_info
server: https://127.0.0.1:55471
name: minikube
contexts:
- context:
cluster: docker-desktop
user: docker-desktop
name: docker-desktop
- context:
cluster: minikube
extensions:
- extension:
last-update: Sat, 30 Aug 2025 19:53:24 JST
provider: minikube.sigs.k8s.io
version: v1.36.0
name: context_info
namespace: default
user: minikube
name: minikube
current-context: minikube
kind: Config
users:
- name: docker-desktop
user:
client-certificate-data: DATA+OMITTED
client-key-data: DATA+OMITTED
- name: minikube
user:
client-certificate: /Users/user/.minikube/profiles/minikube/client.crt
client-key: /Users/user/.minikube/profiles/minikube/client.key
Service の作成
- Service
- ネットワークアプリケーションを公開する方法
- クラスター内で1つ以上の Pod として実行されている
- Pod の公開
- 通常、Pod は Kubernetes クラスター内部のIPアドレスからのみアクセスできる
- Pod を Service として公開することにより、外部ネットワークからアクセスできる
Service が何者か理解できていないが、とりあえずやってみよう。次のコマンドで Pod を Service として公開できるようだ。
$ kubectl expose deployment hello-node --type=LoadBalancer --port=8080
service/hello-node exposed
type
に LoadBalancer
を指定している。どうやらロードバランサーとしての役割を与えたようだ。ポートは 8080
だ。
Service を確認してみよう。すでに kubernetes
というサービスが ClousterIP
type で作成されていたようだ。hello-node
も Service として存在することがわかる。
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-node LoadBalancer 10.104.117.17 <pending> 8080:31626/TCP 114s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 155m
minikube では Service に対し、そのアプリケーションと応答が表示されるブラウザーページを開くことができるらしい。
$ minikube service hello-node
|-----------|------------|-------------|---------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|------------|-------------|---------------------------|
| default | hello-node | 8080 | http://192.168.49.2:31626 |
|-----------|------------|-------------|---------------------------|
🏃 Starting tunnel for service hello-node.
|-----------|------------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|------------|-------------|------------------------|
| default | hello-node | | http://127.0.0.1:56803 |
|-----------|------------|-------------|------------------------|
🎉 Opening service default/hello-node in default browser...
❗ Because you are using a Docker driver on darwin, the terminal needs to be open to run it.
ブラウザにはこのような文言が表示されている。Pod のコンテナプロセスに直接アクセスし、その応答が表示されているのだろうか?
先ほど確認したとき、Service は2つあった。kubernetes
Service も minikube 経由でアクセスできるだろうか?
minikube service kubernetes
|-----------|------------|-------------|--------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|------------|-------------|--------------|
| default | kubernetes | | No node port |
|-----------|------------|-------------|--------------|
😿 service default/kubernetes has no node port
❗ Services [default/kubernetes] have type "ClusterIP" not meant to be exposed, however for local development minikube allows you to access this !
🏃 Starting tunnel for service kubernetes.
|-----------|------------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|------------|-------------|------------------------|
| default | kubernetes | | http://127.0.0.1:56827 |
|-----------|------------|-------------|------------------------|
🎉 Opening service default/kubernetes in default browser...
❗ Because you are using a Docker driver on darwin, the terminal needs to be open to run it.
なんと、できた。これが minikube 経由で Kubernetes クラスターを作成したからアクセスできるのか、minikube 以外の Kubernetes クラスターにも汎用的にアクセスできるのか、判断が難しいところだ。
アドオンの有効化
minikube のアドオンについて解説されている。内容は割愛する。
クリーンアップ
Kubernetes クラスターに作成したリソースを削除しよう。
$ kubectl delete service hello-node
service "hello-node" deleted from default namespace
$ kubectl delete deployment hello-node
deployment.apps "hello-node" deleted from default namespace
minikube クラスターも停止・削除しよう。
$ minikube stop
✋ Stopping node "minikube" ...
🛑 Powering off "minikube" via SSH ...
🛑 1 node stopped.
$ minikube delete
🔥 Deleting "minikube" in docker ...
🔥 Deleting container "minikube" ...
🔥 Removing /Users/user/.minikube/machines/minikube ...
💀 Removed all traces of the "minikube" cluster.
kubernetes
Service の存在が気になったが、こちらも削除されているようだ。
$ kubectl get services
E0830 22:41:22.490500 53751 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E0830 22:41:22.491092 53751 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E0830 22:41:22.492187 53751 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E0830 22:41:22.492602 53751 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E0830 22:41:22.493755 53751 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
The connection to the server localhost:8080 was refused - did you specify the right host or port?
これで Kuberntes クラスターの構築・追加・削除を一通り体験できた。
Kubernetes の基本を学ぶ
Kubernetes ではどんなことができるのか?
- モダンな Web サービス
- ユーザー:アプリケーションが24時間365日利用可能であることを期待する
- 開発者:アプリケーションを1日数回デプロイすることを期待する
- コンテナ化
- アプリケーションをダウンタイムなしでリリース、アップデート可能にする
- Kubernetes
- コンテナ化されたアプリケーションをいつでもどこでも実行できる
- コンテナ化されたアプリケーションが機能するために必要なリソースとツールを見つけやすくなる
- プロダクションレディなオープンソースプラットフォームである
Kubernetes の基本モジュール
- Kubernetes クラスターの作成
- アプリケーションデプロイ
- デプロイしたアプリケーションの探求
- アプリケーションの公開
- アプリケーションのスケールアップ
- アプリケーションのアップデート
クラスターの作成
〜Minikube を使ったクラスターの作成〜
Kubernetes クラスター
- Kubernetes
- コンピューターのクラスターをまとめる
- より効率的な方法で、クラスター全体のアプリケーションコンテナの配布とスケジューリングを自動化する
- Kubernetes クラスター
- コントロールプレーンとノードの2種類のリソースから構成される
- コントロールプレーン
- クラスターを管理するもの
- クラスターのすべての動作をまとめる
- アプリケーションのスケジューリング
- 望ましい状態の維持
- 新しい更新のロールアウト
- ノード
- アプリケーションを動かすワーカーとして機能するVMか物理マシン
- 内部に Kubelet やコンテナ操作ツール(containerd や CRI-O)などが存在する
- Kubelet
- ノードを管理し、コントロールプレーンと通信するエージェント
いまいち理解きれていない。Kubelet の実態はノードに包含されているプロセスであると推測しているが、果たしてチュートリアルでコンポーネントの内部構成まで理解する必要があるだろうか?と感じずにはいられない。おそらく重要な要素だからチュートリアル初期で解説されていうのだと思っているが、論理的なコンポーネントと物理的なプロセスが同時に紹介されているように見え、少々混乱してしまっている。
クラスターダイアグラム
クラスターダイアグラム[2]
- Kubernetes へアプリケーションをデプロイ
- コントロールプレーンにアプリケーションコンテナを起動するように指示する
- コントロールプレーンはコンテナをクラスタのノードで実行するようにスケジュールする
- ノードは Kubernetes API を通してコントロールプレーンと通信する
- Kubernetes クラスターのデプロイ先
- 物理マシンと仮想マシンのどちらへもデプロイできる
このタイミングで minikube を使用して Kubernetes クラスターを作成しておこう。
$ minikube start
アプリケーションのデプロイ
〜Deployment を作成するために kubectl を使う〜
Kubernetes の Deployment
- Deployment
- アプリケーションの作成・更新する責務を負う
- Deployment 作成後の動き
- コントロールプレーン
- Deployment に含まれるアプリケーションインスタンスを、クラスターの個々のノードで実行するようにスケジュールする
- Deployment コントローラー
- アプリケーションインスタンスを監視する
- アプリケーションインスタンスを保持するノードが停止・削除された場合、クラスター内の別のノードのインスタンスと置き換える
- コントロールプレーン
Deployment が何者かわかった。ノードの状態を監視し、ノードの数を維持するコンポーネントだ。
Deployment の監視と復旧によるセルフヒーリングの仕組みが、Kuberntes が提供するオケースとレーションの強みとして紹介されている。障害発生時に人間が調査・復旧するよりも、システムにより自律的に復旧してくれることはかなりありがたい。
Deployment コントローラーは、ノードが停止・削除された時にクラスター内の別のノードのインスタンスと置換するようだ。これはつまり、システムを冗長化するためのスタンバイレプリカ(バックアップ)のようなノードが常に起動してるということだろうか。たしかに、障害発生時にすでに起動しているノードと置き換えるだけだと高速に復旧可能だ。しかし、そのバックアップのノードは障害時以外は常に遊ばせていることになり、余計なリソースを消費してしまう。コンテナの軽量性を活かしている無視できるコストとして計上しているのだろうか。実際、障害時に早期復旧できるメリットに比べれば、バックアップのノードを遊ばせているコストは十分に許容できるように感じる。
Deployment コントローラーのノードの監視・置換の仕組みについて、どこかで理解しておく必要がありそうだ。
Kubernetes 上にはじめてのアプリケーションをデプロイする
- kubectl
- Kubernetes API を使用してクラスターと対話するツール
- Deployment の作成
- アプリケーションのコンテナイメージと実行するレプリカの数を指定する
- それらの情報は Deployment を更新することで後から変更できる
Kubernetes Custer[3]
図にいくつかの情報が追加されている。Deployment はコントロールプレーンの内部に作成されるようだ。ノード内には、コンテナ化されたアプリケーションとノードのプロセスが存在することがわかる。
kubectl の基本
-
kubectrl
コマンド- 一般的な書式は
kubectl action resource
- action:
create
,describe
,delete
など - resource:
node
,deployment
など
- action:
- 一般的な書式は
-
kubectl get nodes
- クラスター内のノードを表示
クラスターの作成 セクションで minikube を使用して Kubernetes クラスターの作成を行っていた。現在のノードを確認してみよう。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 59s v1.33.1
minikube
というノードが Ready
ステータスで存在している。ロールは control-plane
となっている。
ノードのロールが control-plane
とはどのような状況だろう?現時点の理解では、中央にコントロールプレーンが存在し、その周囲にノードが存在していた。つまりコントロールプレーンとノードは別物であり、ノードの一覧表示の結果にコントロールプレーンは含まれないはずだ。
ありがちな構成は「コントロールプレーン自体もノードとして作成される」ではないだろうか。コントロールプレーン自体も1ノードとして管理・実行してしまおうという考えだ。そうであれば、ノードの役割にはコントロールプレーンとアプリケーションの実行の2つが存在し、現在はアプリケーションを実行するノードは存在しないことになる。
ノード のドキュメントを確認すると、どうやら上記の理解で合っていそうだ。
アプリケーションをデプロイする
- Kuberntes にアプリケーションをデプロイ
kubectl create deployment
- Docker Hub 外でホストされているイメージは、リポジトリの完全 URL である必要がある
いよいよアプリケーションをデプロイしてみよう。イメージには kuberntes-bootcamp
を使用するようだ。
$ kubectl create deployment kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1
deployment.apps/kubernetes-bootcamp created
Deployment の作成には、コンテナイメージとレプリカ数が必要だった。コマンドではレプリカ数を指定していないようだが、省略可能なのだろうか。省略した場合は1となるか、もしくは minikube によりデフォルト値が設定されていそうな気配を感じる。
- Kubernetes へのアプリケーションのデプロイで行われること
- アプリケーションのインスタンスを実行可能なノードの探索
-
kubectl get nodes
で確認した通り、現在のノードは1つしかない
-
- アプリケーションをそのノードで実行するようにスケジュール
- 必要であればインスタンスを新しいノードで再スケジュールするような設定を追加
- アプリケーションのインスタンスを実行可能なノードの探索
-
kubectl get deployments
- Deployment の一覧を確認
Deployment を確認してみよう。確かに作成されている。
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1/1 1 1 2m49s
ここでもう一度ノードを確認してみよう。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 9m32s v1.33.1
なんと、前回確認した時と同じく1つだ。ノードにはコントロールプレーンとアプリケーションの実行の2つのロールがあるのではないかと推察した。しかし、目の前には1つしかない。これは1つのノードが両者の役割を兼ね備えており、1つのノードの中に Deployment とコンテナ化されたアプリケーションの2つが同席しているということか。
そして気づいたことがある。やはり「起動しているノードは1つしかない」のだ。アプリケーションのデプロイ セクションで学んだ通り、Kubernetes の特徴の一つはセルフヒーリングの仕組みだった。Deployment がノードを監視し、ノードに異常があれば他のノードのインタンスと置換するのだ。しかし、目の前にはノードは1つしかなく、バックアップとなるノードは起動されていない。
LLM に聞いたら、minikube の学習用環境であるから1ノード構成になっているとのことだった。そしてセルフヒーリングによるノード置換の仕組みの理解が合っているかどうか、少し怪しいとも感じた。TODO 案件として先に進もう。
アプリケーションを見る
-
Pod との通信
- プライベートに隔離されたネットワーク上で動作する
- デフォルトでは、ネットワーク外からは見えず、ネットワーク内(Kubernetes クラスター内)からは見える
-
kubectl
では API エンドポイントを通じてやりとりする
-
kubectl proxy
- ホスト(ターミナル)と Kubernetes クラスターを接続する
- 通信をクラスター全体のプライベートネットワークに転送するプロキシを作成する
- プロキシエンドポイント
- Kubernetes によりホストされている全てのAPIを確認することができる
- たとえば API を通じてバージョンを確認することができる
curl http://localhost:8001/version
$ kubectl proxy
Starting to serve on 127.0.0.1:8001
$ curl http://localhost:8001/version
{
"major": "1",
"minor": "33",
"emulationMajor": "1",
"emulationMinor": "33",
"minCompatibilityMajor": "1",
"minCompatibilityMinor": "32",
"gitVersion": "v1.33.1",
"gitCommit": "8adc0f041b8e7ad1d30e29cc59c6ae7a15e19828",
"gitTreeState": "clean",
"buildDate": "2025-05-15T08:19:08Z",
"goVersion": "go1.24.2",
"compiler": "gc",
"platform": "linux/arm64"
}
すごそう!便利だ!
コマンドでは何も接続先を指定しなかった。指定しない場合は、現在の端末のローカルで起動している Kubernetes クラスターに自動的に接続するのだろうか?
kubectl proxy --help
の説明では、リモートの Kubernetes API サーバーとも接続できるようだった。
ローカルホストと Kubernetes API サーバーの間にプロキシサーバーまたはアプリケーションレベルのゲートウェイを作成します。
また、指定された HTTP パスを介して静的コンテンツを提供することもできます。
すべての受信データは1つのポートから入力され、静的コンテンツのパスと一致するパスを除き、リモート Kubernetes API サーバーのポートに転送されます。
しかし、コマンドの書式やオプションを見ても、リモートサーバーのエンドポイントを指定する方法がない気がする。うーん、どうやるんだろう。こちらも TODO 案件としよう。
Usage:
kubectl proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix] [options]
- Kubernetes API サーバー
- Pod 名に基づいて、各 Pod 用のエンドポイントを自動的に作成する
- プロキシからもアクセスできるようにする
次のコマンドを実行し、Kubernetes API 経由で Kubernetes クラスターにリクエストしてみよう。
$ POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
$ echo Name of the Pod: $POD_NAME
Name of the Pod: kubernetes-bootcamp-9bc58d867-sw9z4
$ curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME:8080/proxy/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-9bc58d867-sw9z4 | v=1
ふむ、何が何だかわからない。
まずは Pod 名を取得するコマンドから見てみよう。-o go-template
オプションでアウトプットの形式を指定しているようだ。いきなり Go Templates が登場しており、--template '...'
でテンプレートを指定している。items
変数が kubectl により与えられるようで、そのメターデータ名を改行付きで繰り返し表示している。つまり items[].metadata.name
の値を出力している。
そして真にわからないのはエンドポイントの方だ。構造は /api/v1/namespaces/{namespace}/pods/{name}:{port}/{path}
のパスになっていそうだ。パスの中に :
が存在しているが、有効な URL となっているのか...初めて知った。ある Pod に対し {port}
と {path}
で API リクエストを投げている。
Executing commands on the container
Pod が実行中であれば、コンテナ上で直接コマンドを実行できる。そのためには、kubectrl exec
コマンドを使用し、Pod の名前をパラメータとして指定しよう。
次のコマンドで Pod のコンテナの環境変数を一覧表示してみよう。
$ kubectl exec "$POD_NAME" -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=kubernetes-bootcamp-658f6cbd58-8vzm9
KUBERNETES_PORT_443_TCP_PROTO=tcp
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
NPM_CONFIG_LOGLEVEL=info
NODE_VERSION=6.3.1
HOME=/root
Pod のコンテナで bash セッションを開始することもできる。
$ kubectl exec -ti "$POD_NAME" -- bash
root@kubernetes-bootcamp-658f6cbd58-8vzm9:/#
これで、NodeJS アプリケーションを実行しているコンテナにコンソールを開くことができた。アプリのソースコードは server.js
ファイルにある。確認してみよう。
root@kubernetes-bootcamp-658f6cbd58-8vzm9:/# cat server.js
var http = require('http');
var requests=0;
var podname= process.env.HOSTNAME;
var startTime;
var host;
var handleRequest = function(request, response) {
response.setHeader('Content-Type', 'text/plain');
response.writeHead(200);
response.write("Hello Kubernetes bootcamp! | Running on: ");
response.write(host);
response.end(" | v=1\n");
console.log("Running On:" ,host, "| Total Requests:", ++requests,"| App Uptime:", (new Date() - startTime)/1000 , "seconds", "| Log Time:",new Date());
}
var www = http.createServer(handleRequest);
www.listen(8080,function () {
startTime = new Date();;
host = process.env.HOSTNAME;
console.log ("Kubernetes Bootcamp App Started At:",startTime, "| Running On: " ,host, "\n" );
});
curl
コマンドを使用して、コンテナ内からアプリケーションが稼働していることを確認しよう。
root@kubernetes-bootcamp-658f6cbd58-8vzm9:/# curl http://localhost:8080
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-658f6cbd58-8vzm9 | v=1
exit
でコンテナ接続を閉じることができる。
root@kubernetes-bootcamp-658f6cbd58-8vzm9:/# exit
exit
アプリケーションの探索
〜Pod とノードについて〜
Kubernetes Pod
- Pod
- 次の2つを表す Kubernetes の抽象概念
- 1つ以上のアプリケーションコンテナ( Docker など)のグループ
- それらのコンテナの共有リソース
- 以下のものが含まれる
- 共有ストレージ(ボリューム)
- ネットワーキング(クラスターに固有の IP アドレス)
- 各コンテナをどう動かすかの情報
- コンテナのイメージバージョンや使用するポートなど
- アプリケーション固有の「論理ホスト」をモデル化する
- 比較的密接に結合されたさまざまなアプリケーションコンテナを含むことができる
- 例えば、Node.js アプリケーションのコンテナと DB コンテナの両方を含めることができる
- 次の2つを表す Kubernetes の抽象概念
- Pod 内の各コンテナ
- IP アドレスとポートスペースを共有する
- 同じ場所に配置される
- 同じスケジュールに入れられる
- 同じノード上の共有コンテキストで実行される
- Pod の扱い
- Deployment を作成すると、その Deployment は Pod を作成する(コンテナを直接作成するのではなく)
- 各 Pod は、スケジュールされているノードに関連付けられる
- Pod は終了もしくは削除されるまでそのノードに残る
- ノードに障害が発生した場合、Pod はそのままクラスター内の他のノードにスケジュールされる
- Deployment を作成すると、その Deployment は Pod を作成する(コンテナを直接作成するのではなく)
Pod について少しわかったような気がする。
まずはノードと Pod の関係性について。どちらも似たようなものでは?と感じていたが、ノードの内側に Pod が存在するようだ。そしてノードに障害が発生すれば、Pod は他の利用可能なノードに移動される。
では結局 Pod とは何か?それがわかるようでわからない...。もう少し詳しく Pod の内容を参照してみよう。どうやら「論理的なホスト」が本質なようだ。つまり、Pod とは1台のサーバーのようなものだ。その中に Web サーバープログラムが動いていたり、DB サーバープログラムが動いていたりする。両者は localhost
とポート番号を通じて通信できるし、サーバー内のファイルにも共有してアクセスできるだろう。あくまで仮想的ではあるが、複数のコンテナをグループ化し、まるで1台のサーバーで動かしているかのように扱うことができる単位が Pod だ。
Pod の概要
Pod[4]
ノード
- ノード
- Kubernetes ではワーカーマシンである
- クラスターによっては仮想マシン、物理マシンのどちらであってもかまわない
- 各ノードはマスターによって管理される
- ノードと Pod
- ノードは複数の Pod を持つことができる
- ノードで動作するもの
- Kubelet
- Kubernetes マスターとノード間の通信を担当するプロセス
- マシン上で実行されている Pod とコンテナを管理する
- コンテナランタイム
- レジストリからコンテナイメージを取得し、コンテナを解凍し、アプリケーションを実行する
- Docker のようなもの
- Kubelet
ノードとは、実際に計算リソースを提供するワーカーマシンだ。物理マシンかもしれないし、仮想マシンかもしれない。AWS であれば EC2 1台に該当すると理解している。
ノードの概要
ノード[5]
kubectlを使ったトラブルシューティング
- kubectl get
- リソースの一覧を表示
- kubectl describe
- 単一リソースに関する詳細情報を表示
- kubectl logs
- 単一 Pod 上の単一コンテナ内のログを表示
- kubectl exec
- 単一 Pod 上の単一コンテナ内でコマンドを実行
アプリケーションの公開
〜Service を使ったアプリケーションの公開〜
Kubernetes Service の概要
- Pod 運用の課題
- Pod の寿命
- Pod にはライフサイクルがある
- ワーカーのノードが停止すると、そのノードで実行されている Pod も失われる
- そうなると、ReplicaSet は新しい Pod を作成してクラスターを目的の状態に保とうとする
- Pod への通信
- 例えば、フロントエンドシステムはバックエンドのレプリカを気にしたり、Pod が失われて再作成されたとしても配慮すべきではない
- しかし、各 Pod は同じノード上の Pod であっても一意のIPアドレスを持っている
- 新しい Pod が再作成されても、フロントエンドからバックエンドのレプリカへ通信できるようにする仕組みが必要である
- Pod の寿命
- Service の概要
- Pod の論理セットと、それらにアクセスするためのポリシーを定義する抽象概念
- 依存 Pod 間を疎結合にする
- Service の特徴
- YAML もしくは JSON により定義される
- Service が対象とする Pod のセットは、通常 LabelSelector によって決定される
- Service の役割
- 各 Pod には固有の IP アドレスがあるが、通常それらの IP はクラスターの外部に公開されない
- Serviceに よって Pod の IP アドレスを公開し、アプリケーションはトラフィックを受信できるようになる
- Serivce の公開方法
- ServiceSpec で
type
を指定する-
ClusterIP
(既定値)- クラスター内の内部 IP で Service を公開する
- この型では、Service はクラスター内からのみ到達可能になる
-
NodePort
- NAT を使用して、クラスター内の選択された各ノードの同じポートに Service を公開する
-
<NodeIP>:<NodePort>
を使用して、クラスターの外部から Service にアクセスできるようにする - これは
ClusterIP
のスーパーセットである
-
LoadBalancer
- 現在のクラウドに外部ロードバランサを作成し、Service に固定の外部 IP を割り当てる
- これは
NodePort
のスーパーセットである
-
ExternalName
- 仕様の
externalName
で指定した名前の CNAME レコードを返すことにより、任意の名前を使って Service を公開する - プロキシは使用されない
- このタイプは v1.7 以上の
kube-dns
を必要とする
- 仕様の
-
- 様々な種類の Service に関する詳細情報
- ServiceSpec で
-
selector
を定義しない Service-
selector
を指定せずに作成した Service- 対応する Endpoints オブジェクトは作成されない
- ユーザーは手動で Service を特定のエンドポイントにマッピングできる
-
type:ExternalName
を厳密に使用している Service
-
理解できていない部分が多々ある。以下の前提があると理解している。
- Pod に割り当てられた IP アドレスは、Pod のみでは到達不可能である
- クラスター外部からのアクセスも、クラスター内部からのアクセスもどちらも不可能である
- そんなこと...あるのか?なんのための IP アドレスなのか?
- Service を通して、Pod への通信を到達可能にする
- Pod への通信方法 = Serivce の公開方法
- 「Service の」公開方法という表現が気になるが...公開されるのは「Pod の」IP アドレスではないのか?
ServiceSpec なる定義ファイルの実例を覗きたいところだ。
現時点の不明点は次のとおりだ。
- Service が対象とする Pod のセットは、通常 LabelSelector によって決定される
- LabelSelector とは何か?
-
selector
を定義しない Service-
selector
とは何か?
-
ボリュームが大きい概念であると感じた。いったん次に進もう。
Service とラベル
Service[6]
- Service が行うこと
- 一連の Pod にトラフィックをルーティングする
- アプリケーションに影響を与えることなく、Kubernetes で Pod が死んだり複製したりすることを可能にする
- 依存 Pod 間の検出とルーティングを処理する
- アプリケーションのフロントエンドとバックエンドコンポーネントなど
- 一連の Pod にトラフィックをルーティングする
- ラベルとセレクタ
- Kubernetes 内のオブジェクトに対する論理操作を可能にするグループ化のプリミティブ
- Service は、ラベルとセレクタを使用して一連の Pod を照合する
- ラベル
- オブジェクトに付けられた key/value のペア
- オブジェクトアタッチした後でもいつでも変更可能である
- さまざまな方法で使用できる
- 開発、テスト、および本番用のオブジェクトを指定する
- バージョンタグを埋め込む
- タグを使用してオブジェクトを分類する
ラベルとセレクタ[7]
ラベルはわかる。ユーザーが自由に指定可能なただのタグだ。AWS でもリソースに key/value 形式で タグ を指定することができる。1つの AWS アカウントに複数の環境のリソースを作成している場合、タグに環境名を指定しておくと検索に便利だろう。
セレクタとは何か?ラベルセレクター を参照してみよう。
ラベルセレクター を介して、クライアントとユーザーはオブジェクトのセットを指定できます。ラベルセレクターはKubernetesにおいてコアなグルーピング機能となります。
Kubernetes APIは現在2タイプのセレクターをサポートしています。
それは等価ベース(equality-based) と集合ベース(set-based) です。
単一のラベルセレクターは、コンマ区切りの複数の要件(requirements) で構成されています。
複数の要件がある場合、コンマセパレーターは論理積 AND(&&)オペレーターと同様にふるまい、全ての要件を満たす必要があります。
セレクタとはつまり、SQL クエリのようなものだ。要件として項目と条件を指定し、Kubernetes オブジェクトをフィルタリングする。複数の要件を指定することも可能だ。
セレクタの要件の項目には何を指定できるのだろう?ラベルを指定することはできるだろうが、Pod 名など Kubernetes などで事前定義された項目も指定できるのだろうか。もしラベルのみの場合、ラベルは非常に重要な意味を持ち、ラベル設計を行う必要がありそうだ。
Step 1: Creating a new Service
まずはアプリケーションが実行されていることを確認しよう。既存の Pod を探してみる。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-658f6cbd58-7r68q 1/1 Running 0 18h
次に、クラスターから現在のサービスを表示してみよう。
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 22h
なんか...すでに Service がありますな。Service を作成した覚えはないが、Minikube により自動的に作成されたのだろうか。type
はデフォルト値の ClusterIP
であり、クラスター内部からアクセス可能な Service であることが伺える。
前のステップで作成した Deployment を確認しておこう。
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1/1 1 1 18h
この Deployment を外部トラフィックに公開してみよう。--type=NodePort
オプションを指定する。
$ kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
service/kubernetes-bootcamp exposed
ここでは、Service は固有のクラスター IP、内部ポート、および外部 IP(ノードの IP)を受け取っている。
ここで、kubectl create service
ではなく kubectl expose deployment/${name}
で Deployment を外部公開していることに注目しよう。あくまでも Service 自体を作成するのではなく、Deployment を公開することに主眼が置かれていそうだ。
どのような Service が作成されたか確認しよう。
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 22h
kubernetes-bootcamp NodePort 10.110.53.216 <none> 8080:32217/TCP 33s
kubernetes-bootcamp
Service が作成作成されている。type
は指定通りに NodePort
であり、ポートも 8080:32217/TCP
とコマンド通りに作成されていそうだ。
Service の詳細を確認してみよう。
$ kubectl describe services/kubernetes-bootcamp
Name: kubernetes-bootcamp
Namespace: default
Labels: app=kubernetes-bootcamp
Annotations: <none>
Selector: app=kubernetes-bootcamp
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.102.221.48
IPs: 10.102.221.48
Port: <unset> 8080/TCP
TargetPort: 8080/TCP
NodePort: <unset> 32587/TCP
Endpoints: 10.244.0.4:8080
Session Affinity: None
External Traffic Policy: Cluster
Internal Traffic Policy: Cluster
Events: <none>
describe
コマンドで Service を指定するには、service/
プレフィックスを指定する必要がある。
Deployment の外部に開かれたポートは、type: NodePort
サービスの場合は NodePort
から確認することができる。今回の場合は 32587
だ。
何も指定しなくても、ラベルとセレタクタに app=kubernetes-bootcamp
が指定されている。アプリケーション名なのだろうが、いつどのように指定されたのだろう?時間がある人は次のアコーディオンを開いてみてほしい。
他のオブジェクトのラベルとセレクタも確認してみよう
まず最初に実行したコマンドは minikube
だった。
$ minikube start
このコマンドで Kuberenetes クラスターを作成した。同時にノードと Service (アプリケーションのものではない)も作成されている。Deployment と Pod はまだ作成されていない。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 22s v1.34.0
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 26s
$ kubectl get deployments
No resources found in default namespace.
$ kubectl get pods
No resources found in default namespace.
次に Deployment を作成した。
$ kubectl create deployment kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1
このコマンドにより Deployment が作成され、ノードの中に Pod が作成された。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 4m32s v1.34.0
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4m32s
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 0/1 1 0 8s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-658f6cbd58-8vzm9 0/1 ContainerCreating 0 11s
そして最後に、Service を作成した。
$ kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
kubernetes-bootcamp
Service が作成されている。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 4m32s v1.34.0
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 13m
kubernetes-bootcamp NodePort 10.102.221.48 <none> 8080:32587/TCP 3s
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 0/1 1 0 8s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-658f6cbd58-8vzm9 0/1 ContainerCreating 0 11s
ここで改めて、それぞれのオブジェクトのラベルとセレクタを確認してみよう。まずはノードからだ。
$ kubectl describe nodes/minikube
Name: minikube
Roles: control-plane
Labels: beta.kubernetes.io/arch=arm64
beta.kubernetes.io/os=linux
kubernetes.io/arch=arm64
kubernetes.io/hostname=minikube
kubernetes.io/os=linux
minikube.k8s.io/commit=65318f4cfff9c12cc87ec9eb8f4cdd57b25047f3
minikube.k8s.io/name=minikube
minikube.k8s.io/primary=true
minikube.k8s.io/updated_at=2025_09_14T16_59_28_0700
minikube.k8s.io/version=v1.37.0
node-role.kubernetes.io/control-plane=
node.kubernetes.io/exclude-from-external-load-balancers=
...
ノードには様々なラベルが付与されている。項目名の形式は ${domain}/${name}
になっているようだ。os
や name
、primary
などの情報が付与されている。
次に Deployments と Pod を見てみよう。
$ kubectl describe deployments/kubernetes-bootcamp
Name: kubernetes-bootcamp
Namespace: default
CreationTimestamp: Sun, 14 Sep 2025 17:12:07 +0900
Labels: app=kubernetes-bootcamp
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=kubernetes-bootcamp
...
$ kubectl describe pods/kubernetes-bootcamp-658f6cbd58-8vzm9
Name: kubernetes-bootcamp-658f6cbd58-8vzm9
Namespace: default
Priority: 0
Service Account: default
Node: minikube/192.168.49.2
Start Time: Sun, 14 Sep 2025 17:12:07 +0900
Labels: app=kubernetes-bootcamp
pod-template-hash=658f6cbd58
...
Deployment にはラベルとセレクタの両方に app=kubernetes-bootcamp
が付与されている。このアプリケーション名が Deployment 名から来ているのかコンテナイメージ名から来ているのかは判断に迷うが、おそらく前者だろう。
Pod にはラベルのみが存在し、同じく app=kubernetes-bootcamp
が付与されている。Pod は Deployment から作成されるのだから、当然だろう。
最後に Service をみてみよう。
$ kubectl describe services/kubernetes-bootcamp
Name: kubernetes-bootcamp
Namespace: default
Labels: app=kubernetes-bootcamp
Annotations: <none>
Selector: app=kubernetes-bootcamp
...
Service にもラベルとセレクタの両方に app=kubernetes-bootcamp
が付与されていた。Service は Deployment を expose して作成されたので、当然だろう。
かくして、Deployment を起点としてアプリケーション名のラベルとセレクタが付与されていることがわかった。
Deployment の外部に公開されたポートを利用して、実際に外部からアクセスしてみよう。
$ export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')"
$ echo "NODE_PORT=$NODE_PORT"
NODE_PORT=32587
$ curl http://"$(minikube ip):$NODE_PORT"
これでサーバーから応答が返ってくるはず。Service が公開されていることを確認できるはずなのだが...。筆者の場合はどちらのコマンドもエラーとなってしまった。
$ curl http://"$(minikube ip):$NODE_PORT"
^C # 応答なしでキャンセル
$ curl 127.0.0.1:51082
curl: (7) Failed to connect to 127.0.0.1 port 51082 after 0 ms: Couldn't connect to server
なぜなのかはわらない。雰囲気は掴めたので先に進もう。
Step 2: Using labels
Deployment は Pod のラベルを自動的に作成している。まずは次のコマンドで Deployment のラベルを確認してみよう。
$ kubectl describe deployment
Name: kubernetes-bootcamp
Namespace: default
CreationTimestamp: Sun, 14 Sep 2025 17:12:07 +0900
Labels: app=kubernetes-bootcamp
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=kubernetes-bootcamp
ラベルは app=kubernetes-bootcamp
だ。このラベルを使用して、Pod のリストを表示してみよう。
$ kubectl get pods -l app=kubernetes-bootcamp
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-658f6cbd58-8vzm9 1/1 Running 0 36m
Service に対しても同様にラベルからリストを表示することができる。
$ kubectl get services -l app=kubernetes-bootcamp
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes-bootcamp NodePort 10.102.221.48 <none> 8080:32587/TCP 36m
もちろんラベルを新たに作成して付与することもできる。Pod に新しいラベルを付けてみよう。
$ export POD_NAME="$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')"
$ echo "Name of the Pod: $POD_NAME"
Name of the Pod: kubernetes-bootcamp-658f6cbd58-8vzm9
$ kubectl label pods "$POD_NAME" version=v1
pod/kubernetes-bootcamp-658f6cbd58-8vzm9 labeled
本当にラベルが付与されただろうか?確認してみよう。
$ kubectl describe pods "$POD_NAME"
Name: kubernetes-bootcamp-658f6cbd58-8vzm9
Namespace: default
Priority: 0
Service Account: default
Node: minikube/192.168.49.2
Start Time: Sun, 14 Sep 2025 17:12:07 +0900
Labels: app=kubernetes-bootcamp
pod-template-hash=658f6cbd58
version=v1
...
version=v1
が付与されている!
そしてさらに、このラベルを使用して Pod のリストを表示することも可能だ。
$ kubectl get pods -l version=v1
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-658f6cbd58-8vzm9 1/1 Running 0 40m
Step 3: Deleting a service
ラベルを指定して Service を削除することもできる。
$ kubectl delete service -l app=kubernetes-bootcamp
service "kubernetes-bootcamp" deleted from default namespace
これにより Service は削除された。そのルートがもう公開されていないことを確認するには、以前に公開されていたIPとポートをにリクエストを送ることで確認できる。
$ curl http://"$(minikube ip):$NODE_PORT"
しかし、Pod 内からはまだ到達可能である。次のコマンドでアプリがまだ実行中であることを確認できる。
$ kubectl exec -ti $POD_NAME -- curl http://localhost:8080
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-658f6cbd58-8vzm9 | v=1
アプリケーションがまだ稼働していることがわかった。これは Deployment がアプリケーションを管理しているためだ。アプリケーションをシャットダウンするには、Deployment を削除する必要がある。
アプリケーションのスケーリング
〜アプリケーションの複数インスタンスを実行〜
アプリケーションのスケーリング
- スケーリング
- Deployment のレプリカの数を変更することによって実現可能である
スケーリングの概要
スケール前[8]
スケール後[9]
- Deployment のスケールアウト
- 新しい Pod が作成され、使用可能なリソースを持つノードにスケジュールされる
- Deployment のスケーリング
- Kubernetes は Pod のオートスケーリングもサポートしている
- スケーリングを
0
に設定することで、指定された Deployment のすべての Pod を終了させることもできる
- アプリケーションのインスタンスのトラフィック分散
- Service のロードバランサを利用することができる
- Service は、エンドポイントを使用して実行中の Pod を継続的に監視し、トラフィックが使用可能な Pod にのみ送信されるようにする
- ローリングアップデート
- アプリケーションの複数のインスタンスが実行されている場合、ダウンタイムなしでアプリケーションをアップデートできる
Scaling a Deployment
現在の Deployment の構成を見てみよう。
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1/1 1 1 86m
それぞれの項目は次のことを表している。
-
NAME
- クラスター内の Deployment の名前を一覧表示する
-
READY
- 現在のレプリカ数と希望するレプリカ数の比率を表示する
-
UP-TO-DATE
- 望ましい状態を達成するために更新されたレプリカの数を表示する
-
AVAILABLE
- アプリケーションのレプリカがユーザーにどれだけ利用可能かを表示する
-
AGE
- アプリケーションが実行されている期間を表示する
Deployment によって作成された ReplicaSet を確認してみよう。
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
kubernetes-bootcamp-658f6cbd58 1 1 1 89m
それぞれの項目を見てみよう。
-
DESIRED
- アプリケーションの望ましいレプリカ数を表示する
- Deployment の作成時に定義する
-
kubectl create deployment
コマンドの--replicas
パラメータを使用する
-
-
CURRENT
- 現在実行中のレプリカ数を表示する
次に、Deployment を4つのレプリカにスケールしてみよう。
$ kubectl scale deployments/kubernetes-bootcamp --replicas=4
deployment.apps/kubernetes-bootcamp scaled
Deployment の構成はどうなっただろうか?確認してみよう。
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 4/4 4 4 93m
望ましいレプリカの数が4つに変更され、実際に実行中のレプリカ数も4つになっている。スケール成功だ。
Pod の数はどうなっただろう?
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kubernetes-bootcamp-658f6cbd58-8vzm9 1/1 Running 0 95m 10.244.0.4 minikube <none> <none>
kubernetes-bootcamp-658f6cbd58-mg88n 1/1 Running 0 2m7s 10.244.0.6 minikube <none> <none>
kubernetes-bootcamp-658f6cbd58-nnf7r 1/1 Running 0 2m7s 10.244.0.5 minikube <none> <none>
kubernetes-bootcamp-658f6cbd58-tswns 1/1 Running 0 2m7s 10.244.0.7 minikube <none> <none>
IP アドレスが異なる4つの Pod が存在することを確認できる。
scale
コマンドによるレプリカセットの変更は、Deployment イベントログに登録されている。内容を確認してみよう。
$ kubectl describe deployments/kubernetes-bootcamp
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 5m44s deployment-controller Scaled up replica set kubernetes-bootcamp-658f6cbd58 from 1 to 4
Deployment にイベントが追加されていることが確認できた。
Load Balancing
Service がトラフィックを負荷分散していることを確認しよう。まずは公開されているIPとポートを探してみよう。
$ kubectl describe services/kubernetes-bootcamp
Name: kubernetes-bootcamp
Namespace: default
Labels: app=kubernetes-bootcamp
Annotations: <none>
Selector: app=kubernetes-bootcamp
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.99.92.84
IPs: 10.99.92.84
Port: <unset> 8080/TCP
TargetPort: 8080/TCP
NodePort: <unset> 30599/TCP
Endpoints: 10.244.0.4:8080,10.244.0.5:8080,10.244.0.7:8080 + 1 more...
Session Affinity: None
External Traffic Policy: Cluster
Internal Traffic Policy: Cluster
Events: <none>
NodePort
は 30599
だ。
次のコマンドで、公開されている IP アドレスとポートに対し、リクエストしてみよう。
$ export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')"
$ echo NODE_PORT=$NODE_PORT
NODE_PORT=30599
$ curl http://"$(minikube ip):$NODE_PORT"
リクエストを何回か送ってみよう。リクエストごとに異なる Pod にアクセスしており、トラフィックの負荷分散が機能していることを示している。
$ curl http://127.0.0.1:52751
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-658f6cbd58-8vzm9 | v=1
$ curl http://127.0.0.1:52751
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-658f6cbd58-mg88n | v=1
$ curl http://127.0.0.1:52751
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-658f6cbd58-nnf7r | v=1
Scale Down
Deployment を2つのレプリカにスケールダウンしてみよう。
$ kubectl scale deployments/kubernetes-bootcamp --replicas=2
deployment.apps/kubernetes-bootcamp scaled
Deployment への変更が適用されたか確認してみよう。
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 2/2 2 2 109m
Deployment のレプリカ数が2に減少した。
Pod の数も確認してみよう。
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kubernetes-bootcamp-658f6cbd58-8vzm9 1/1 Running 0 109m 10.244.0.4 minikube <none> <none>
kubernetes-bootcamp-658f6cbd58-tswns 1/1 Running 0 16m 10.244.0.7 minikube <none> <none>
Pod の数も2つだ。これにより、2つの Pod が終了したことを確認できた。
アプリケーションのアップデート
〜ローリングアップデートの実行〜
アプリケーションのアップデート
- それぞれの期待
- ユーザーはアプリケーションが常に利用可能であることを期待している
- 開発者はアプリケーションの新しいバージョンを1日に数回デプロイすることを期待している
- ローリングアップデート
- Pod インスタンスを新しいインスタンスで段階的にアップデートする
- ダウンタイムなしで Deployment をアップデートできる
- 新しい Pod は、利用可能なリソースを持つノードにスケジュールされる
- アップデート中の Pod
- デフォルトでは、アップデート中に使用できなくなる可能性がある Pod の最大数は
1
であり、アップデート中に作成できる新しいPodの最大数も1
である - どちらのオプションも、Pod の数または全体数に対する割合(%)のいずれかに設定できる
- デフォルトでは、アップデート中に使用できなくなる可能性がある Pod の最大数は
- アップデートのバージョン管理
- Deployments のアップデートはバージョン管理されている
- Deployment におけるアップデートは以前の stable バージョンに戻すことができる
ローリングアップデートの概要
アップデート前[10]
アップデート中[11]
アップデート後[12]
- アップデート中のトラフィックの負荷分散
- Deployment がパブリックに公開されている場合、Service はアップデート中に利用可能な Pod にのみトラフィックを負荷分散する
- 利用可能な Pod はアプリケーションのユーザーが利用できるインスタンスである
- ローリングアップデートで可能な操作
- コンテナイメージのアップデートを介した、ある環境から別の環境へのアプリケーションの昇格
- 以前のバージョンへのロールバック
- ダウンタイムなしでのアプリケーションのCI/CD
Update the version of the app
アプリケーションの現在のイメージバージョンを確認しよう。kubectrl describe pods
コマンドを実行し、Image
フィールドを探そう。
$ kubectl describe pods
Name: kubernetes-bootcamp-658f6cbd58-8vzm9
...
Containers:
kubernetes-bootcamp:
Container ID: docker://e63b83cc8eb1386c702fb89463f8e958664ffeb0e33fd34d43f11a0774592b17
Image: gcr.io/google-samples/kubernetes-bootcamp:v1
...
Name: kubernetes-bootcamp-658f6cbd58-tswns
...
Containers:
kubernetes-bootcamp:
Container ID: docker://c295fe898c766d5c196f631005ee1a8d364a4e6c5a6baf5369ed60d89760624e
Image: gcr.io/google-samples/kubernetes-bootcamp:v1
...
アプリケーションの現在のイメージ名は gcr.io/google-samples/kubernetes-bootcamp
で、イメージバージョンは v1
だ。
アプリケーションのイメージをバージョン2に更新してみよう。kubectl set image
コマンドを実行し、Deployment に新しいイメージバージョンを紐付けよう。
$ kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=docker.io/jocatalin/kubernetes-bootcamp:v2
deployment.apps/kubernetes-bootcamp image updated
コマンドにより、Deployment にアプリケーションの異なるイメージを使用するよう通知し、ローリングアップデートが開始された。新しい Pod のステータスを確認し、終了する古い Pod を表示してみよう。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-57cc954bb9-q4xkr 1/1 Running 0 20s
kubernetes-bootcamp-57cc954bb9-tk6lf 1/1 Running 0 16s
kubernetes-bootcamp-658f6cbd58-8vzm9 1/1 Terminating 0 129m
kubernetes-bootcamp-658f6cbd58-tswns 1/1 Terminating 0 36m
しばらく待つと、新しい Pod のみが残った。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-57cc954bb9-q4xkr 1/1 Running 0 89s
kubernetes-bootcamp-57cc954bb9-tk6lf 1/1 Running 0 85s
Verify an update
実際にアプリケーションが稼働しているかを確認しよう。
$ export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')"
$ echo "NODE_PORT=$NODE_PORT"
NODE_PORT=30599
$ curl http://"$(minikube ip):$NODE_PORT"
実際にリクエストを送ると、v=2
となり Pod が最新バージョンで実行されていることわかる。
$ curl http://127.0.0.1:53022
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-57cc954bb9-tk6lf | v=2
複数回リクエストを送ってみよう。2つあるすべての Pod が最新バージョンで実行されていることがわかる。
$ curl http://127.0.0.1:53022
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-57cc954bb9-tk6lf | v=2
$ curl http://127.0.0.1:53022
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-57cc954bb9-q4xkr | v=2
Deployment のアップデートを確認することができる。
$ kubectl rollout status deployments/kubernetes-bootcamp
deployment "kubernetes-bootcamp" successfully rolled out
Pod の一覧を表示し、Image
フィールドに最新のイメージバージョンが割り当てられていることを確認しよう。
$ kubectl describe pods
Name: kubernetes-bootcamp-57cc954bb9-q4xkr
...
Containers:
kubernetes-bootcamp:
Container ID: docker://1ca88e3151ca7c372024105324e5d2fcc084324f4e9d96748903573499e07f70
Image: docker.io/jocatalin/kubernetes-bootcamp:v2
Name: kubernetes-bootcamp-57cc954bb9-tk6lf
...
Containers:
kubernetes-bootcamp:
Container ID: docker://58006756a6610d674c2be930e7c14078792aad775308f3eeaff88e7c440fb735
Image: docker.io/jocatalin/kubernetes-bootcamp:v2
...
Roll back an update
もう一度 Deployment のアップデートを実行し、v10
のタグが付いたイメージをデプロイしてみよう。
$ kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=gcr.io/google-samples/kubernetes-bootcamp:v10
deployment.apps/kubernetes-bootcamp image updated
Deployment の状態を確認してみよう。
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 2/2 1 2 140m
ここで UP-TO-DATE
の数が 1
であることに注意しよう。UP-TO-DATE
には「望ましい状態を達成するために更新されたレプリカの数」が表示されるのだった。現在は望ましいレプリカ数には 2
を指定しているのだから、UP-TO-DATE
にも 2 が表示され、2つのレプリカが更新されるはずではなかっただろうか?
Pod の状態を確認してみよう。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-57cc954bb9-q4xkr 1/1 Running 0 15m
kubernetes-bootcamp-57cc954bb9-tk6lf 1/1 Running 0 14m
kubernetes-bootcamp-677ff875c4-5dq7n 0/1 ImagePullBackOff 0 4m3s
一部の Pod のステータスが ImagePullBackOff
であることに注意しよう。いったいどういうことだろう?
問題をさらに詳しく知るために、Pod の詳細を確認してみよう。
$ kubectl describe pods
Name: kubernetes-bootcamp-57cc954bb9-q4xkr
...
Name: kubernetes-bootcamp-57cc954bb9-tk6lf
...
Name: kubernetes-bootcamp-677ff875c4-5dq7n
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 38s default-scheduler Successfully assigned default/kubernetes-bootcamp-677ff875c4-5dq7n to minikube
Normal Pulling 23s (x2 over 38s) kubelet Pulling image "gcr.io/google-samples/kubernetes-bootcamp:v10"
Warning Failed 21s (x2 over 37s) kubelet Failed to pull image "gcr.io/google-samples/kubernetes-bootcamp:v10": Error response from daemon: manifest for gcr.io/google-samples/kubernetes-bootcamp:v10 not found: manifest unknown: Failed to fetch "v10"
Warning Failed 21s (x2 over 37s) kubelet Error: ErrImagePull
Normal BackOff 7s (x2 over 36s) kubelet Back-off pulling image "gcr.io/google-samples/kubernetes-bootcamp:v10"
Warning Failed 7s (x2 over 36s) kubelet Error: ImagePullBackOff
ステータスが ImagePullBackOff
になっていた Pod の Events
を確認してみよう。以下のメッセージが表示されている。
Failed to pull image "gcr.io/google-samples/kubernetes-bootcamp:v10": Error response from daemon: manifest for gcr.io/google-samples/kubernetes-bootcamp:v10 not found: manifest unknown: Failed to fetch "v10"
つまり、v10
のイメージバージョンがリポジトリに存在しなかったのだ。
Deployment は新しいイメージをデプロイしようとした。新しい Pod を作成し、イメージを pull し、Pod をローリングアップデートで順番に更新しようとした。しかし、指定されたイメージは存在しなかった。そのため、デプロイに失敗した。新しく作成された Pod はステータス ImagePullBackOff
のまま残り、更新対象の Pod が引き続き残っているのだ。更新対象の Pod を残すことによりアプリケーションは継続的に稼働している。
Deployment アップデートはバージョン管理されているのだった。Deployment を最後に動作したバージョンに戻そう。
$ kubectl rollout undo deployments/kubernetes-bootcamp
deployment.apps/kubernetes-bootcamp rolled back
kubectl rollout undo
コマンドは、Deployment を以前の既知の状態(v2
のイメージバージョン)に戻す。Deployment アップデートはバージョン管理されており、以前の既知の状態にいつでも戻すことができる。
もう一度 Pod の状態を確認してみよう。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-57cc954bb9-q4xkr 1/1 Running 0 26m
kubernetes-bootcamp-57cc954bb9-tk6lf 1/1 Running 0 26m
ステータスが ImagePullBackOff
だった Pod は削除されている。また、すでに稼働していた2つの Pod が引き続き残っている。
実行中の Pod にデプロイされたイメージを確認してみよう。
$ kubectl describe pods
Name: kubernetes-bootcamp-57cc954bb9-q4xkr
...
Containers:
kubernetes-bootcamp:
Container ID: docker://1ca88e3151ca7c372024105324e5d2fcc084324f4e9d96748903573499e07f70
Image: docker.io/jocatalin/kubernetes-bootcamp:v2
Name: kubernetes-bootcamp-57cc954bb9-tk6lf
...
Containers:
kubernetes-bootcamp:
Container ID: docker://58006756a6610d674c2be930e7c14078792aad775308f3eeaff88e7c440fb735
Image: docker.io/jocatalin/kubernetes-bootcamp:v2
どちらの Pod も v2
のイメージバージョンを使用している。
Deployment は再びアプリケーションの安定版 (v2
) を使用している。ロールバックは成功した。
深掘り
minikube
- minikube
- macOS、Linux、Windows 上にローカルの Kubernetes クラスターを迅速にセットアップする
- アプリケーション開発者や新しい Kubernetes ユーザーを支援するツールであり、その行為に誇りを持っている
- ハイライト
- 最新の Kubernetes リリースと過去6つのマイナーバージョンをサポート
- クロスプラットフォーム(Linux, macOS, Windows)
- VM、コンテナ、ベアメタルとしてデプロイ
- 複数のコンテナランタイム(CRI-O, containerd, docker)
- 超高速イメージロードとビルド のためのダイレクト API エンドポイント
- ロードバランサー、ファイルシステムマウント、FeatureGates、ネットワークポリシー などの高度な機能
- 簡単にインストールできる Kubernetes アプリケーション用のアドオン
- 一般的な CI環境 をサポート
minikube は Kuberntes 学習者向けの高速な Kubernetes クラスターのセットアップツールだ。まさに本チュートリアルのように、ローカルで簡単に Kubernetes クラスターを構築して触ってみたいケースにぴったりだ。
Kubernetes クラスターのデプロイ形式として、VM、コンテナ、ベアメタルの3つをサポートしている。ベアメタルとは、コンピューターのハードディスクに直接インストールする作業のことだ[13]。仮想環境へのデプロイだけでなく、従来のソフトウェアのようにコンピューターに直接インストールする形式もサポートしているということだ。
単に Kubernetes クラスターを構築するだけでなく、本番運用で必要になるロードバランサーやネットワークポリシーまでサポートされている。性能面をカバーする機能であるローダーバラランサーならまだしも、セキュリティ面の機能であるネットワークポリシーまで提供している点には驚いた。
minikube Drivers
- Docker Driver
- 既存の Docker 環境に Kubernetes をインストールする
- Linux では仮想化を有効にする必要はない
- 特別な機能
- クロスプラットフォーム(Linux、macOS、Windows)
- Linuxで実行する場合、ハイパーバイザーは不要
- Windows 10 での WSL2 の実験的サポート
他にも次のことが言及されていたが、内容は割愛する。
- 既知の Issues
-
トラブルシューティング
- DockerコンテナタイプがLinuxであることを確認
- ログ付きで実行
- AppArmor を使用した Linux への MySql デプロイ
おわりに
いかがでしたでしょうか。Kubernetes チュートリアルを実施し、概要が掴めたのではないかと感じます。Kubernetes の基本的な4つのオブジェクトを操作してきました。それぞれどのようなものであるか、肌感を掴むことができたのではないでしょうか。
- ノード
- Service
- Deployment
- Pod
Kubernetes チュートリアルにはまだ次の項目も残っています。かなりボリューミーですね。時間を見つけて実施したいところです。
- ステートレスアプリケーション
- 設定
- セキュリティ
- ステートレスアプリケーション
- ステートフルアプリケーション
- Cluster Management (EN)
- Service
以上となりますが、お役に立てましたら幸いです!
Discussion