😺

【作業ログ】Kubernetes チュートリアル【基本編】

に公開

はじめに

業務で Kubernetes を扱うに当たり、チュートリアルを実施しました。一緒に「なんもわからん」から「完全に理解した!」を経て「チョットデキル」を目指しましょう!

https://kubernetes.io/ja/docs/tutorials/

目次

環境

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 クラスターを構築するためのツールである。まさに学習用にもってこいというわけだ。

https://minikube.sigs.k8s.io/docs/

詳細は 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 も利用している。他の選択肢として parallelsssh が提示されている。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 イメージをもとにコンテナを実行する
  • 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

typeLoadBalancer を指定している。どうやらロードバランサーとしての役割を与えたようだ。ポートは 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 など
  • 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 コンテナの両方を含めることができる
  • Pod 内の各コンテナ
    • IP アドレスとポートスペースを共有する
    • 同じ場所に配置される
    • 同じスケジュールに入れられる
    • 同じノード上の共有コンテキストで実行される
  • Pod の扱い
    • Deployment を作成すると、その Deployment は Pod を作成する(コンテナを直接作成するのではなく)
      • 各 Pod は、スケジュールされているノードに関連付けられる
      • Pod は終了もしくは削除されるまでそのノードに残る
      • ノードに障害が発生した場合、Pod はそのままクラスター内の他のノードにスケジュールされる

Pod について少しわかったような気がする。

まずはノードと Pod の関係性について。どちらも似たようなものでは?と感じていたが、ノードの内側に Pod が存在するようだ。そしてノードに障害が発生すれば、Pod は他の利用可能なノードに移動される。

では結局 Pod とは何か?それがわかるようでわからない...。もう少し詳しく Pod の内容を参照してみよう。どうやら「論理的なホスト」が本質なようだ。つまり、Pod とは1台のサーバーのようなものだ。その中に Web サーバープログラムが動いていたり、DB サーバープログラムが動いていたりする。両者は localhost とポート番号を通じて通信できるし、サーバー内のファイルにも共有してアクセスできるだろう。あくまで仮想的ではあるが、複数のコンテナをグループ化し、まるで1台のサーバーで動かしているかのように扱うことができる単位が Pod だ。

Pod の概要


Pod[4]

ノード

  • ノード
    • Kubernetes ではワーカーマシンである
    • クラスターによっては仮想マシン、物理マシンのどちらであってもかまわない
    • 各ノードはマスターによって管理される
  • ノードと Pod
    • ノードは複数の Pod を持つことができる
  • ノードで動作するもの
    • Kubelet
      • Kubernetes マスターとノード間の通信を担当するプロセス
      • マシン上で実行されている Pod とコンテナを管理する
    • コンテナランタイム
      • レジストリからコンテナイメージを取得し、コンテナを解凍し、アプリケーションを実行する
      • Docker のようなもの

ノードとは、実際に計算リソースを提供するワーカーマシンだ。物理マシンかもしれないし、仮想マシンかもしれない。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 が再作成されても、フロントエンドからバックエンドのレプリカへ通信できるようにする仕組みが必要である
  • 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 に関する詳細情報
  • 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 間の検出とルーティングを処理する
      • アプリケーションのフロントエンドとバックエンドコンポーネントなど
  • ラベルとセレクタ
    • 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} になっているようだ。osnameprimary などの情報が付与されている。

次に 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>

NodePort30599 だ。

次のコマンドで、公開されている 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 の数または全体数に対する割合(%)のいずれかに設定できる
  • アップデートのバージョン管理
    • 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

  • Docker Driver
    • 既存の Docker 環境に Kubernetes をインストールする
    • Linux では仮想化を有効にする必要はない
  • 特別な機能
    • クロスプラットフォーム(Linux、macOS、Windows)
    • Linuxで実行する場合、ハイパーバイザーは不要
    • Windows 10 での WSL2 の実験的サポート

他にも次のことが言及されていたが、内容は割愛する。

おわりに

いかがでしたでしょうか。Kubernetes チュートリアルを実施し、概要が掴めたのではないかと感じます。Kubernetes の基本的な4つのオブジェクトを操作してきました。それぞれどのようなものであるか、肌感を掴むことができたのではないでしょうか。

  • ノード
  • Service
  • Deployment
  • Pod

Kubernetes チュートリアルにはまだ次の項目も残っています。かなりボリューミーですね。時間を見つけて実施したいところです。

  • ステートレスアプリケーション
  • 設定
  • セキュリティ
  • ステートレスアプリケーション
  • ステートフルアプリケーション
  • Cluster Management (EN)
  • Service

以上となりますが、お役に立てましたら幸いです!

脚注
  1. Drivers | minikube より参照 ↩︎

  2. Minikubeを使ったクラスターの作成 | Kubernetes より引用 ↩︎

  3. Deploymentを作成するためにkubectlを使う | Kubernetes より引用 ↩︎

  4. Podとノードについて | Kubernetes より引用 ↩︎

  5. Podとノードについて | Kubernetes より引用 ↩︎

  6. Serviceを使ったアプリケーションの公開 | Kubernetes より引用 ↩︎

  7. Serviceを使ったアプリケーションの公開 | Kubernetes より引用 ↩︎

  8. アプリケーションの複数インスタンスを実行 | Kubernetes より引用 ↩︎

  9. アプリケーションの複数インスタンスを実行 | Kubernetes より引用 ↩︎

  10. ローリングアップデートの実行 | Kubernetes より引用 ↩︎

  11. ローリングアップデートの実行 | Kubernetes より引用 ↩︎

  12. ローリングアップデートの実行 | Kubernetes より引用 ↩︎

  13. What is bare-metal provisioning? – TechTarget Definition より引用 ↩︎

Discussion