Kubernetesは公式ドキュメントを読んでも理解できず、発音(クバネティス)が覚えづらい上に仕組みが複雑なこともあり、長いあいだ敬遠していました。
そんな中ある時、Docker Desktopの設定画面の「Kubernetesを有効化する」ボタンに気がつき、「もしかして簡単に使えるかも」と思い立ちました。
そこでKubernetesを学びつつ、以前作成したDockerで構築していたWordPress+MySQL環境を、Docker Desktop内のKubernetesクラスタへ移行してみました。
【参考文献】
例: Persistent Volumeを使用したWordpressとMySQLをデプロイする
Compose on Kubrenetes を使ってみる

作業環境
- Windows 11
- Docker Desktopインストール
- WSL2 + Ubuntu
- Visual Studio Code + Remote – Containers 拡張
環境の詳細は、こちらの記事を参照してください。
KubernetesでのWordPress+MySQL 環境作成手順
以下は、Docker Compose で立ち上げている WordPress+MySQL 環境を Kubernetes マニフェストに置き換え、Docker Desktop の Kubernetes クラスタ上で起動するまでの手順です。
Docker Desktopで Kubernetes を有効化
- Docker Desktopの設定画面を開く
画面右上の歯車アイコン(Settings)をクリックします。 - Kubernetes タブを選択
左サイドバーの「Kubernetes」をクリックします。 - 「Enable Kubernetes」にチェック
「Enable Kubernetes(Kubernetes を有効化)」のチェックボックスをオンにします。 - Kubernetes の起動状態を確認
Docker Desktop のステータスバーに「Kubernetes is running」または「Kubernetes: Running」と表示されるまで待ちます - クラスタプロビジョニング方式
デフォルトの kubeadmのままにしておく。
(kindは、マルチノード構成を試したい場合やKubernetesのバージョン切り替えをしたい場合、高速に立ち上げたい場合に利用する様です。)

WSL2(Ubuntu)にkubectlをインストール
sudo apt update、sudo apt install -y kubectlでは、エラーになったので、curlを使用してLinuxへkubectlのバイナリをインストールする手順を使用してインストール。
#最新の安定版リリース番号を取得&バイナリ取得
curl -LO "https://dl.k8s.io/release/$(\
curl -L -s https://dl.k8s.io/release/stable.txt\
)/bin/linux/amd64/kubectl"
#実行権限を付与
chmod +x kubectl
#実行ファイルを PATH の通る場所へ移動
mv kubectl /usr/local/bin/
#バージョン確認
kubectl version --client
【参考】
外側の curl -LO
curl:指定した URL からデータを取得するコマンド
-L:サーバーが返すリダイレクト(HTTP 3xx)を自動で追跡する
-O:取得したファイルをリモートのファイル名(ここでは kubectl)で保存する
"https://dl.k8s.io/release/$(…)/bin/linux/amd64/kubectl"
この文字列がダウンロード先の URL になります。
$( … ) の中身を実行した結果(Kubernetes の最新安定版リリース番号)で置き換えてから curl が動きます。
内側の curl -L -s https://dl.k8s.io/release/stable.txt
-L:同じくリダイレクトを追跡
-s:進捗やエラーメッセージを表示しないサイレントモード
例)このファイルの中身が v1.28.2 なら、内側のコマンドは v1.28.2 を出力します。
結果として生成される実際の URLが、最新安定版が v1.28.2 なら出力は以下になります。
https://dl.k8s.io/release/v1.28.2/bin/linux/amd64/kubectl
これを curl -LO でダウンロードし、実行ファイル kubectl として保存します。
マニフェスト(YAML)を作成
PVC → Deployment → Service の順で2つのリソース(mysql.yaml、wordpress.yaml)ファイルを作成する。
MySQL 用マニフェスト(mysql.yaml)
# mysql.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
replicas: 1
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "root_password"
- name: MYSQL_DATABASE
value: "wordpress_db"
- name: MYSQL_USER
value: "wordpress_user"
- name: MYSQL_PASSWORD
value: "wordpress_password"
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumes:
- name: mysql-data
persistentVolumeClaim:
claimName: mysql-pvc
---
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
targetPort: 3306
selector:
app: mysql
WordPress 用マニフェスト(wordpress.yaml)
# wordpress.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wordpress-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
spec:
selector:
matchLabels:
app: wordpress
replicas: 1
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: wordpress:latest
ports:
- containerPort: 80
env:
- name: WORDPRESS_DB_HOST
value: "mysql"
- name: WORDPRESS_DB_USER
value: "wordpress_user"
- name: WORDPRESS_DB_PASSWORD
value: "wordpress_password"
- name: WORDPRESS_DB_NAME
value: "wordpress_db"
volumeMounts:
- name: wordpress-data
mountPath: /home/users/2/main.jp-7945459b0a332a6e/web/docker
volumes:
- name: wordpress-data
persistentVolumeClaim:
claimName: wordpress-pvc
---
apiVersion: v1
kind: Service
metadata:
name: wordpress
spec:
# ClusterIP のままにしておき、後述の port-forward で外部公開
type: ClusterIP
ports:
- port: 80
targetPort: 80
selector:
app: wordpress
マニフェストファイルのPVC → Deployment → Service の順番の意味
Kubernetes のリソースを作成するときに「PVC → Deployment → Service」という順番で考えるのは、それぞれの役割と依存関係を踏まえた論理的なフローになります。
- PersistentVolumeClaim(PVC)
役割:Pod(コンテナ)が使う永続ストレージの要求を定義する。
なぜ先に?
Deployment で作成される Pod が起動時にマウントするボリュームを、あらかじめ確保/バインドしておく必要がある。
PVC が Bound(ストレージ確保済み)にならないと、Pod はエラーで起動できない場合がある。 - Deployment
役割:Pod のレプリカ管理(リビジョン管理/ローリングアップデート含む)を行う。
なぜ真ん中?
Deployment のテンプレートで「この PVC をこのマウントパスに使う」と指定するため、PVC が先に存在している必要がある。
Pod が起動するときに、マウント先のボリューム(PVC)が利用可能であることを前提とする。 - Service
役割:Pod 群に対する安定したネットワークアクセスポイント(ClusterIP/NodePort/LoadBalancer)を提供する。
なぜ最後?
Service はラベルセレクターで Pod(Deployment が作成したもの)にトラフィックを振り分ける。Pod がまだ存在しないと、Service に紐づくエンドポイントが空のままになるため、適切に機能しない。
Kubernetes 上でのリソース配置とネットワーク
マニフェストファイル(mysql.yaml、wordpress.yaml)により作成されるリソース配置とネットワーク構成図は、下記の通りです。

マニフェスト適用と動作確認
#リソース適用
kubectl apply -f mysql.yaml
kubectl apply -f wordpress.yaml
#PVC の状態確認
kubectl get pvc
#Deployment/Service の確認
kubectl get deploy,svc
#Pod の稼働状況
kubectl get pods
アクセスと接続テスト
#WordPress のポート転送
#Kubernetes クラスター内で実行されているwordpress Serviceのポートを、ローカルマシンのポートに転送する。
kubectl port-forward svc/wordpress 8080:80
#ブラウザで http://localhost:8080 にアクセスしwordpressセットアップ画面を確認する。
#MySQL クライアント接続確認
#Kubernetes クラスター内で一時的な MySQL クライアントの Pod を起動し、Pod から指定された MySQL データベースへの接続を確認
kubectl run mysql-client --rm -it --image=mysql:5.7 \
--env="MYSQL_PWD=wordpress_password" -- \
mysql -h mysql -u wordpress_user wordpress_db
#SQL (Structured Query Language) コマンドを入力して確認
mysql>
SHOW DATABASES; → wordpress_db の存在
SHOW TABLES; → 初期テーブル確認
SELECT COUNT(*) FROM wp_posts; → クエリ実行
ubuntuターミナル操作
# ubuntuターミナル操作画面
#作業ディレクトリの作成と移動
~$ mkdir k8s
~$ cd k8s/
#mysql.yamlの作成
~/k8s$ nano mysql.yaml
~/k8s$ cat mysql.yaml
# mysql.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
replicas: 1
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "root_password"
- name: MYSQL_DATABASE
value: "wordpress_db"
- name: MYSQL_USER
value: "wordpress_user"
- name: MYSQL_PASSWORD
value: "wordpress_password"
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumes:
- name: mysql-data
persistentVolumeClaim:
claimName: mysql-pvc
---
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
targetPort: 3306
selector:
app: mysql
#wordpress.yamlの作成
~/k8s$ nano wordpress.yaml
~/k8s$ cat wordpress.yaml
# wordpress.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wordpress-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
spec:
selector:
matchLabels:
app: wordpress
replicas: 1
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: wordpress:latest
ports:
- containerPort: 80
env:
- name: WORDPRESS_DB_HOST
value: "mysql"
- name: WORDPRESS_DB_USER
value: "wordpress_user"
- name: WORDPRESS_DB_PASSWORD
value: "wordpress_password"
- name: WORDPRESS_DB_NAME
value: "wordpress_db"
volumeMounts:
- name: wordpress-data
mountPath: /home/users/2/main.jp-7945459b0a332a6e/web/docker
volumes:
- name: wordpress-data
persistentVolumeClaim:
claimName: wordpress-pvc
---
apiVersion: v1
kind: Service
metadata:
name: wordpress
spec:
# ClusterIP のままにしておき、後述の port-forward で外部公開
type: ClusterIP
ports:
- port: 80
targetPort: 80
selector:
app: wordpress
#マニフェストの適用
# MySQLマニフェストを適用
~/k8s$ kubectl apply -f mysql.yaml
persistentvolumeclaim/mysql-pvc created
deployment.apps/mysql created
service/mysql created
# wordpressマニフェストを適用
~/k8s$ kubectl apply -f wordpress.yaml
persistentvolumeclaim/wordpress-pvc created
deployment.apps/wordpress created
service/wordpress created
#マニュフェストが反映されたか確認
#PersistentVolumeClaim (PVC) の一覧表示
~/k8s$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-pvc Bound pvc-4e1b46cf-3a07-4e31-9832-385876a80de6 1Gi RWO hostpath <unset> 30s
wordpress-pvc Bound pvc-69cd9c1f-a879-4855-939b-7ee37d533e30 1Gi RWO hostpath <unset> 18s
#Deployment (デプロイメント)とService (サービス)の情報を表示
#Deployment は、アプリケーションの安定稼働、スムーズな更新、そして緊急時の復旧を自動で行ってくれる、Kubernetesのリソース。
~/k8s$ kubectl get deploy,svc
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/mysql 1/1 1 1 48s
deployment.apps/wordpress 1/1 1 1 35s
#Service は、Kubernetes においてアプリケーション(Pod)への安定したアクセスを提供するための仕組み
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7h32m
service/mysql ClusterIP 10.109.213.60 <none> 3306/TCP 48s
service/wordpress ClusterIP 10.98.213.161 <none> 80/TCP 35s
#Kubernetes (クバネティス) のクラスターに存在するPod (ポッド) の一覧を表示
~/k8s$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-7f5bf656c6-wppvm 1/1 Running 0 2m44s
wordpress-75dbd9cbcf-pws2b 1/1 Running 0 2m31s
#MySQL への接続確認
#Kubernetes クラスター内で一時的な MySQL クライアントの Pod を起動し、Pod から指定された MySQL データベースに接続確認
~/k8s$ kubectl run mysql-client --rm -it --image=mysql:5.7 --env="MYSQL_PWD=wordpress_password" -- \
mysql -h mysql -u wordpress_user wordpress_db
If you don't see a command prompt, try pressing enter.
#データベース一覧の確認
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| wordpress_db |
+--------------------+
2 rows in set (0.00 sec)
#テーブル一覧の確認
mysql> USE wordpress_db;
Database changed
mysql> SHOW TABLES;
+------------------------+
| Tables_in_wordpress_db |
+------------------------+
| wp_commentmeta |
| wp_comments |
| wp_links |
| wp_options |
| wp_postmeta |
| wp_posts |
| wp_term_relationships |
| wp_term_taxonomy |
| wp_termmeta |
| wp_terms |
| wp_usermeta |
| wp_users |
+------------------------+
12 rows in set (0.00 sec)
#投稿データの確認
mysql> SELECT COUNT(*) FROM wp_posts;
+----------+
| COUNT(*) |
+----------+
| 5 |
+----------+
1 row in set (0.00 sec)
mysql> \q
Bye
Session ended, resume using 'kubectl attach mysql-client -c mysql-client -i -t' command when the pod is running
pod "mysql-client" deleted
#WordPress へのポート転送
~/k8s$ kubectl port-forward svc/wordpress 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
#起動コンテナ確認
~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e4158ed9223e 93230cd54060 "/docker-entrypoint.…" About an hour ago Up About an hour k8s_nginx_nginx-748459795-dhq52_wordpress-stack_b81f57c6-21b4-4499-bcc0-122b0b1899c5_2
1f9263b25340 4445b2668d41 "docker-entrypoint.s…" About an hour ago Up About an hour k8s_mysql_mysql-7565c86569-tmrr8_wordpress-stack_e76d33dd-e4d9-4472-8037-fbf8bf2bc4a8_0
a8e359c64b24 4bc6bc963e6d "docker-entrypoint.s…" About an hour ago Up About an hour k8s_mysql_mysql-7f5bf656c6-wppvm_default_4f0c0a62-d80a-4507-912b-082e23eab832_0
987a8ef3edfa 9ca181730570 "docker-entrypoint.s…" About an hour ago Up About an hour k8s_wordpress_wordpress-75dbd9cbcf-pws2b_default_042c771e-35b8-4c34-bad2-ed77ae01ed4a_0
c55eaf487f26 9ca181730570 "docker-entrypoint.s…" About an hour ago Up About an hour k8s_wordpress_wordpress-6598b88574-5dwqq_wordpress-stack_024a3a07-58f6-4db8-b027-9335a0ccb618_0
1933e351c31c 9ca181730570 "docker-entrypoint.s…" About an hour ago Up About an hour k8s_wordpress_wordpress-6598b88574-7rljm_wordpress-stack_7956699b-041c-4213-ad8e-6fa5a8316999_0
#ブラウザで http://localhost:8080 を開き、WordPress画面を確認する。

docker ps に k8s_nginx_nginx-…_wordpress-stack_… というコンテナが見える理由
マニュフェスト(mysql.yaml/wordpress.yaml)ではなく、「Compose on Kubernetes」機能(あるいは docker stack deploy)が自動的に作成した Ingress 用の nginx プロキシコンテナ。
nginx プロキシコンテナ(Nginx Ingress Controller)
Nginx をベースとした Ingress Controllerで、外部からのリクエストを受け、Ingress リソースで定義されたルールに従ってバックエンド Service へ転送します。
動作イメージ
- 外部ロードバランサ(クラウド LB/MetalLB など)から NodePort/HostPort 経由で nginx Ingress Controller Pod へトラフィックが到達
- Controller が API サーバーから Ingress リソースを watch
- Nginx の設定ファイル(nginx.conf)を自動生成し、nginx プロセスをリロード
- リクエストを定義されたバックエンド Service(ClusterIP)へルーティング
まとめ
以上で、以前作成したDockerで構築していたWordPress+MySQL環境の、Docker Desktop内のKubernetesクラスタへ移行が完了しました。
Kubernetes(クバネティス)のコンテナオーケストレーション機能には触れられず、Kubernetesの理解には程遠いですが、Kubernetes(クバネティス)を理解していくのための手掛かりはつかめそうな気がしています。
参考ドキュメント
例: Persistent Volumeを使用したWordpressとMySQLをデプロイする
主な用語と概念
| 用語 | 説明 |
|---|---|
| Pod | コンテナの最小実行単位。 複数のコンテナを同じネットワーク名前空間で束ねる。 |
| Node | ワーカーマシン(VM/物理サーバー)。 Pod を実際に動かすホスト。 |
| Control Plane | クラスター全体を管理する中枢。 API サーバー、スケジューラー等から構成。 |
| Deployment | Pod のレプリカ数やアップデート方法を宣言的に管理するオブジェクト。 |
| Service | Pod の集合に対する安定したアクセス方法 (ロードバランサー/ClusterIP など)。 |
| ConfigMap / Secret | アプリ設定値や機密情報を Pod に注入するための仕組み。 |
| Namespace | 同じクラスター内でリソースを論理的に分割するための仕切り。 |
| Volume | Pod 内のコンテナ間で共有するストレージ領域。 |
アーキテクチャの全体像
- ユーザー操作
└kubectl apply -f deployment.yamlなどでマニフェストを投入 - API サーバー
└ リクエストを受け付け、etcd に状態を永続化 - Scheduler
└ 新規 Pod を適切な Node に振り分け - Controller Manager
└ 宣言された状態と実際の状態を常に比較し、差分を修正 - kubelet(各 Node)
└ kubelet が API サーバーから Pod 定義を受け取り、コンテナランタイム(Docker/containerd)で起動 - kube-proxy
└ Service の仮想 IP を実現し、ネットワーク通信を各 Pod にルーティング


