🕸️

Docker × Kubernetes をはじめる

2025/01/20に公開

はじめに

DockerやKubernetesについて知らないことが多かったので、これを機にハンズオンの題材として勉強し直してみました。この記事では、

  • DockerやKubernetesとは何かを学びたい方
  • Dockerを使った仮想環境の構築をしたい方
  • Kubernetesのマニフェストを使ってみたい方

を対象として、DockerとKubernetesの基本概念を交えたハンズオンを行います。
※ 1時間半程度を想定しています。

本ハンズオンの流れ

  • Dockerを使った仮想環境の構築をして、Webサーバを立てる。
  • Kubernetes上でWordpressを動かす。

Docker

Dockerとは、コンテナ型の仮想環境を作成、配布、実行するためのオープンソースのプラットフォームです。「コンテナ型の仮想環境」とはどういうことか、仮想環境とコンテナについて説明します。

仮想環境とは

仮想環境とは、コンピュータの中に作った疑似的な環境のことを指します。ここで言う環境は、開発を行ったりテストを行ったり、プログラムを動かしたりするのに必要なコンピュータやソフトウェアなどが揃っているものとイメージしてください。この仮想環境を構築することを仮想化と言います。仮想化のアーキテクチャには、以下の3種類があります。

ホスト型

物理サーバのホスト上に仮想ハードウェアを入れ、その上でゲストOSを動かす方法です。
サービス例: VMware Workstation、VMWare Fusion

ハイパーバイザー型

ハードウェア上にハイパーバイザーと呼ばれる仮想化ソフトウェアを用意し、その上でゲストOSを動かす方式です(ホストOSは使いません)。
サービス例: Xen、Linux KVM

コンテナ型

アプリケーションレベルでの仮想化をひとつのホストOS上で行う方式です。物理サーバーのホスト上で、仮想サーバーとしての機能を持つコンテナを構築します。単一のOS上に独立したコンテナを構築するため、ゲストOSは存在しません。
サービス例: Docker、LXC、OpenVZ

コンテナとは

Dockerにおけるコンテナは、アプリケーションを動かすために必要な機能がまとまって入っている箱というイメージです。異なる環境において、コンテナを使うことで同一の仮想環境を作成でき、環境の違いによって発生する問題を少なくすることができます。

Dockerでは複数のコンテナを立てることができるため、ローカルと隔離した環境をいくつも作ることが可能です。つまり、自分の環境を汚さずに様々な開発ができるようになります。


Dockerを使うメリット

Dockerを利用して仮想環境を立てることで、以下のようなメリットがあります。

再現性のある環境構築が容易

まず、Dockerイメージを使うことで簡単に環境構築ができます。イメージとは、Dockerコンテナを作成するための、読み取り専用のテンプレートです。このイメージを共有することによって、誰でも簡単に同じ環境(Dockerコンテナ)を用意することができます。

次に、Dockerfileにイメージ構築手順をファイル化できます。Dockerfileとはイメージを作成するための手順が記されたテキストファイルです。イメージの設計図と説明されることもあります。仮想環境を構築する手順をテキストで管理できます。
Dockerを使った環境構築の流れ
Dockerを使った環境構築の流れ

軽量で高速

従来の仮想化と異なり、仮想サーバの立ち上げが不要なのでアプリケーションの起動が早いというメリットがあります。これにより開発作業をスムーズに行うことができます。

拡張性に優れている

Dockerでは、同じアプリケーションの複数のインスタンスを簡単にスケールすることができます。これにより、多くのユーザーが同時にアプリケーションを利用できるようになります。
例) Webサーバへのアクセス数が増えた際に、複数コンテナを起動することで、アクセスを1つのコンテナではなく分散させることができます。


ローカルにWebサーバを立てる

ここからは、NginxでWebサーバを立てながら、Dockerの基本コマンドを紹介していきます。

イメージの作成

まず、任意のフォルダの中に Dockerfile という名前のファイルを作ります。これは、先ほどの説明にも出てきたように、イメージの構築手順を示したファイルです。
次に、このファイルの中身を以下のように編集します。

Dockerfile
FROM nginx

そしたら、Dockerfileをもとにイメージを作成します。

docker build -t test-image:test-tag .

-t test-image:test-tagは、作成したイメージに test-image という名前と test-tag というタグをつけています。最後のピリオド.では、Dockerfileの場所をカレントディレクトリに指定しています。
実行すると以下のような出力が出ます。

[+] Building 8.0s (6/6) FINISHED
 => [internal] load build definition from dockerfile
 => => transferring dockerfile: 47B
 => [internal] load metadata for docker.io/library/nginx:latest
 => [auth] library/nginx:pull token for registry-1.docker.io
...
 => exporting to image
 => => exporting layers
 => => writing image sha256:9bea9...
 => => naming to docker.io/library/test-image:test-tag

Dockerfileの FROM で指定したNginxのイメージがローカルにない場合は、Docker Hubからプルしてローカル上にイメージを構築します。

docker image でイメージが作成されていることを確認できます。

PS> docker images
REPOSITORY                  TAG         IMAGE ID       CREATED         SIZE
test-image                  test-tag    9bea9f2796e2   6 months ago    192MB

コンテナの起動

作成したイメージからコンテナを起動してみます。

docker run -d -p 8080:80 --name test-container test-image:test-tag

-d(--detach)はコンテナをバックグラウンドで実行させるオプションです。
-p 8080:80(--publish)では、コンテナの80番ポートがホストの8080番ポートにバインドされます。
--name test-containerは起動するコンテナにtest-containerという名前を付けます。この名前はdockerコマンドによるコンテナの操作に使われます。
最後のtest-image:test-tagは、コンテナのもとになるイメージです。

docker ps でコンテナが起動しているか確認します。-aオプションを付けると、停止しているコンテナも表示されます。

PS> docker ps
CONTAINER ID   IMAGE                 COMMAND                   CREATED          STATUS          PORTS                  NAMES
5ccf9ab34b7b   test-image:test-tag   "/docker-entrypoint.…"   23 seconds ago   Up 23 seconds   0.0.0.0:8080->80/tcp   test-container

コンテナの起動が確認できたら、http://localhost:8080 を開きます。そうすると、以下のような画面が表示されます。
Nginxサーバ起動画面

コンテナの削除

コンテナの作成から起動までの一連の流れができたので、コンテナの削除も行います。
docker stopでコンテナを停止します。

docker stop test-container

docker ps -aでコンテナが停止していることを確認します。

PS> docker ps -a
CONTAINER ID   IMAGE                    COMMAND                   CREATED          STATUS                     PORTS     NAMES
5ccf9ab34b7b   test-image:test-tag      "/docker-entrypoint.…"   35 minutes ago   Exited (0) 2 minutes ago             test-container

コンテナの停止が確認できたら、コンテナを削除してみます。
docker rm <CONTAINER ID or NAME>で、指定したコンテナを削除することができます。
今回はNAMEを指定して削除します。

docker rm test-container

docker ps -aでコンテナが削除されていることを確認できます。

Kubernetes

Kubernetesは、コンテナを管理するためのオープンソースプラットフォームです。Kubernetesを使えるようになると、Dockerとコンテナでは不可能だったことが簡単に実現できるようになります。以下にその例をあげます。

  • コンテナの起動管理
  • 複数のコンテナを効率的にデプロイ・管理
  • リソースの消費量に基づいてスケーリングを管理
  • コンテナが障害等で停止した場合、自動で再起動して復旧
  • ログ収集が容易

ただ、コンテナ化(コンテナ自体を作成すること)はできないので、Dockerと併用するのが一般的です。

Kubernetesの構成と用語

Kubernetesの構成
Kubernetesの基本用語 - Qiitaより引用

上図がKubernetesの基本的な構成図になります。分かりやすさのため、最小単位の用語から説明していきます。下の方に行くにつれて、その用語がカバーする範囲は広がっていきます。

Container

Dockerコンテナを指し、1つのアプリケーションの立ち上げが可能です。

Pod

コンテナの集合体です。ポッドは通常ノードの中に存在し、最低1つのコンテナを保有しています。Kubernetesにデプロイする場合はポッド単位で行います。

Node

ノードには、NodeとMaster Nodeがあります。
Nodeは、コンテナ化されたアプリケーションなどを実行するホストで、workerノードと呼ばれています。各Nodeには2つのコンポーネントがあり、実行中のポッドを維持したり、Kubernetesランタイム環境を提供したりします。

  • kubelet: 各workerノードで実行するコンテナ、ボリューム、ポッドネットワークなどの設定を管理
  • kube-proxy: クラスター内の各workerノードで動作しているネットワークプロキシ(他workerノードとの連携)

Master Nodeは、Kubernetesを管理する以下のコンポーネントで構成されています。また、Master NodeはControl Planeとも呼ばれています。

  • Kube-apiserver:Control Planeノードを動かすためのAPIサーバー
  • etcd:データストアとしての役割を果たす
  • Kube~scheduler:ポッドを実行するノード(workerノード)を選択する
  • Kube~controller-manager: APIサーバーによる宣言による実行を管理する
  • cloud-controller-manager: Kubernetesと任意のクラウド環境との連携を行う

Cluster

PodやNodeのようなKubernetesのリソースを管理する集合体です。


Kubernetes上でWordpressを動かす

ここからは、実際にKubernetes環境を構築していきます。構成としては、WordpressとDBに使用するMySQLです。どちらも公式のDockerイメージを使います。

Kubernetesの有効化

まず、Kubernetesが使えるように、Docker Desktopから有効化します。
Docker DesktopのSettingからKubernetesタブを選択して、その中のEnable Kubernetesを有効にします。そして、Apply & restartを押すとKubernetesクラスターが起動します。このとき、必要なイメージのダウンロードに数分程度かかります。

Kubernetesの有効化

これにより、KubernetesのCLIコマンドが使えるようになりました。kubectl get nodeを実行すると、Kubernetesクラスターの状態を確認できます。

PS> kubectl get node
NAME             STATUS   ROLES           AGE    VERSION
docker-desktop   Ready    control-plane   117s   v1.30.5

Kubernetesマニフェスト

Kubernetesでは、YAMLやJSONで書かれたマニフェストファイルを用いてリソースを作成し、環境の構築を行います。このマニフェストファイルにはあるべき姿を宣言的に記述し、Kubernetesはこの内容を維持するように動いてくれます。マニフェストファイルの一例を以下に示します。

example.yml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest

apiVersionには使用するAPIのバージョンを指定します。これがあることでKubernetesのバージョンが上がっても互換性を維持することができます。APIのバージョンを調べるにはkubectl api-resourcesコマンドを使います。特定のリソースのみを調べるには以下のようにします。

PowerShellの場合

kubectl api-resources | Select-String pods

Linuxの場合

kubectl api-resources | grep pods

kindではリソースの種類を指定します。リソースにはPodやDeployment、Serviceなどがあります。
metadataではリソースに付与するメタデータを指定します。nameは必須です。
これらのapiVersionkindmetadataは各リソースで共通の部分になります。
specにはリソースの構造を記述します。詳細については実際のマニフェストファイルを見ていきます。

MySQLを含むDeploymentを作成する

任意のフォルダにmysql-deployment.ymlファイルを作り、以下の内容を入力します。

mysql-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
      role: database
  template:
    metadata:
      name: mysql
      labels:
        app: wordpress
        role: database
    spec:
      containers:
      - name: mysql
        image: mysql
        env:
          - name: MYSQL_ROOT_PASSWORD
            value: password
          - name: MYSQL_DATABASE
            value: wordpress
          - name: MYSQL_USER
            value: wordpress_user
          - name: MYSQL_PASSWORD
            value: wordpress_pass
        ports:
          - containerPort: 3306

specの内容について説明します。
replicasには、Podの複製を何個作るかを指定します。複数作ることで負荷を分散できます。
templateには、Podの構成を記述します。
metadata以下にはPodのmetadataを記述し、labelには任意のkeyとvalueを設定できます。
spec.containersにはPodに含むイメージを指定します。Docker Hub以外からプルする場合はフルパスで書く必要があります。envにはコンテナの環境変数を指定します。

kubectl createでマニフェストファイルからリソースを作成します。

kubectl create -f mysql-deployment.yml

deploymentとpodができたかを確認します。

PS> kubectl get deployment
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
mysql   1/1     1            1           9m35s

PS> kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
mysql-76b8f456f6-n6tf2   1/1     Running   0          13m

Serviceを作成する

先ほど立てたMySQLをこの後立てるWordpressから使えるようにするには、Serviceを作る必要があります。これにより、クラスタ内のDNSで名前解決が可能になります。

mysql-service.yml
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
    targetPort: 3306
  selector:
    app: wordpress
    role: database

spec.portsでは、外部から見えるポート番号(port)とPod内のポート番号(targetPort)を指定します。今回はMySQL標準のポートだけで行います。
spec.selectorでは、labelを使ってどのPodを対象とするかを指定します(先ほど作成したMySQLのPodのmetadataを参照しています)。

Deploymentのときと同様にリソースを作成して、

kubectl create -f mysql-service.yml

Serviceができているかを確認します。

PS> kubectl get service
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
mysql        ClusterIP   10.107.220.68   <none>        3306/TCP   8s

Wordpress本体を立てる

基本的にはMySQLと同じでDeploymentとServiceを作ります。

wordpress-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
      role: web
  template:
    metadata:
      labels:
        app: wordpress
        role: web
    spec:
      containers:
        - image: wordpress
          name: wordpress
          env:
            - name: WORDPRESS_DB_HOST
              value: mysql:3306
            - name: WORDPRESS_DB_USER
              value: wordpress_user
            - name: WORDPRESS_DB_PASSWORD
              value: wordpress_pass

こちらも同様にリソースを作成し、

kubectl create -f wordpress-deployment.yml

deploymentとpodができたかを確認します。

PS> kubectl get deployment
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
mysql       1/1     1            1           4h30m
wordpress   1/1     1            1           117s

PS> kubectl get pod
NAME                         READY   STATUS    RESTARTS   AGE
mysql-76b8f456f6-n6tf2       1/1     Running   0          4h31m
wordpress-57b5674fd4-9fzlb   1/1     Running   0          2m57s

Serviceのマニフェストは以下の通りです。

wordpress-service.yml
apiVersion: v1
kind: Service
metadata:
  name: wordpress
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: wordpress
    role: web

Serviceができたかを確認します。

PS> kubectl get service
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
mysql        ClusterIP   10.107.220.68    <none>        3306/TCP   21m
wordpress    ClusterIP   10.101.222.148   <none>        80/TCP     43s

Wordpressに接続する

Pod一覧からWordpress本体が動いているPodの名前をコピーしておきます(この場合はwordpress-57b5674fd4-9fzlb)。次に、kubectl port-forwardでローカルの8080番ポートからPodの80番ポートにアクセスできるようにします。

kubectl port-forward wordpress-57b5674fd4-9fzlb 8080:80

そして、http://localhost:8080/ にアクセスしてみます。以下のような画面になれば成功です。
Wordpressの起動成功画面

因みに、Podを再起動するとデータが消えます。試しにPodのMySQLを削除してみます。

kubectl delete pod -l role=database

DBとの接続が不可な状態

永続化を行わないとデータベースに接続できなくなります。

リソースの削除

最後に、kubectl deleteで今回作ったリソースの削除を行います。

kubectl delete deployment,service wordpress mysql

まとめ

今回は、DockerとKubernetesの基本概念を交えながら、仮想環境の構築を行いました。このハンズオンで触れていない内容も沢山あるので、色々な記事を読んでみると面白いかもしれないです。

この記事を読んで、少しでもDockerとKubernetesへの理解が深まれば幸いです。

参考文献

このハンズオンを作るにあたって、大変参考にした記事やサイトです。

仮想化技術

仮想マシンのアーキテクチャの種類とその特徴まとめ - Qiita
各仮想化技術の違いをざっと理解していくぅ - Qiita
仮想環境とは? VMwareとは? 仕組みや種類、概念を学ぼう - bcblog.sios.jp

コンテナ

【図解】コレ1枚で分かるコンテナ型仮想化とDocker - blogs.itmedia.co.jp

Docker

Docker初学者がやるべきこと3選 - Qiita
What is Docker? - Docker Docs
docker - Docker Docs

Kubernetes

Cluster Architecture - Kubernetes
Kubernetesの基本用語 - Qiita
”Kubernetes” の基本を徹底解説 - Qiita
数時間で完全理解!わりとゴツいKubernetesハンズオン!! - Qiita

How to Set Up a Kubernetes Cluster on Docker Desktop - Docker
How Kubernetes works under the hood with Docker Desktop - Docker
Deploy on Kubernetes with Docker Desktop - Docker Docs

Kubernetes入門ハンズオン-WordPressを立ててみよう - Qiita
Kubernetesの apiVersion に何を書けばいいか - Qiita
grep的なPowerShellコマンドSelect-String - Qiita
kubectl create - Kubernetes
kubectl delete - Kubernetes

Discussion