Docker × Kubernetes をはじめる
はじめに
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では、同じアプリケーションの複数のインスタンスを簡単にスケールすることができます。これにより、多くのユーザーが同時にアプリケーションを利用できるようになります。
例) Webサーバへのアクセス数が増えた際に、複数コンテナを起動することで、アクセスを1つのコンテナではなく分散させることができます。
ローカルにWebサーバを立てる
ここからは、NginxでWebサーバを立てながら、Dockerの基本コマンドを紹介していきます。
イメージの作成
まず、任意のフォルダの中に 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 を開きます。そうすると、以下のような画面が表示されます。
コンテナの削除
コンテナの作成から起動までの一連の流れができたので、コンテナの削除も行います。
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の基本的な構成図になります。分かりやすさのため、最小単位の用語から説明していきます。下の方に行くにつれて、その用語がカバーする範囲は広がっていきます。
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の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はこの内容を維持するように動いてくれます。マニフェストファイルの一例を以下に示します。
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は必須です。
これらのapiVersion
、kind
、metadata
は各リソースで共通の部分になります。
spec
にはリソースの構造を記述します。詳細については実際のマニフェストファイルを見ていきます。
MySQLを含むDeploymentを作成する
任意のフォルダに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で名前解決が可能になります。
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を作ります。
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のマニフェストは以下の通りです。
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/ にアクセスしてみます。以下のような画面になれば成功です。
因みに、Podを再起動するとデータが消えます。試しにPodのMySQLを削除してみます。
kubectl delete pod -l role=database
永続化を行わないとデータベースに接続できなくなります。
リソースの削除
最後に、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