はじめに
世はエンジニア戦国時代。Docker くらい一般常識。Docker 使えないなんてエンジニアを名乗れない。そんな時代です。(ほんとか?)
この記事を書き始めた時の僕の Docker 戦闘力は「Docker 公式チュートリアルを眺めただけ」です。 なので逆に言えば Docker 公式チュートリアルをやっただけの方にも理解できるかと思います。
(ちなみに Kubernetes 戦闘力は「なんで Kubernetes を k8s って言うのかだけ知ってる」です。)
この記事はそんな僕が「Docker/Kubernetes ちょっと分かる」になるまでにやったことを後から追えるようにズラっと書き連ねたものになります。
使用するのは僕の大好きな言語 Elixir とその Web フレームワーク Phoenix です。が、この記事でどの言語を用いているかは重要ではありません。 (記事内で Elixir のコードはほぼ触らないですし)
また、Rails がわかる方は以下の記事で Rails と Phoenix のコマンドを対応させて説明しているのでチラ見すると Phoenix で何をしようとしているか理解できるかと思います。 Rails 経験者に贈る Phoenix 入門
何か訂正や補足、アドバイスなどありましたら、是非是非コメントかTwitterまでお願いします!🙇♂️
この記事で扱う内容
- Web アプリケーションを扱える環境をDockerfileで作成する
- docker-composeを使って Web アプリケーション(+DB)を動かす
- 作成した image をdockerhubに上げる
- Kubernetes(minikube)を使って Web アプリケーション(+DB)を動かす
Dockerfile の作成
では早速 Dockerfile を作成していきます
Dockerfile ではこれから作成するコンテナで何をしようとしているかを定義します。
以下の公式リファレンスが参考になります。 Dockerfile リファレンス
FROM elixir:1.10.2
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash
RUN apt-get install -y nodejs
RUN npm install npm@latest -g
RUN mix local.hex --force
RUN mix archive.install hex phx_new 1.4.12 --force
RUN mix local.rebar --force
WORKDIR /app
この Dockerfile が何をしようとしているか
初心者なりに一行ずつ説明してみます。
FROM elixir:1.10.2
親イメージを選択します。 イメージって何?という方は以下の公式チュートリアルの説明がわかりやすいです Part 1:概要説明とセットアップ | コンテナの概要を説明
この親イメージは Elixir 公式の image です。
こういった公式で出ているイメージなどから自分の目的に即した環境が作れるように Dockerfile
を記述していって、カスタムしていく訳です。
(今回だと自分の目的=Phoenix を動かせる環境となります)
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash
RUN apt-get install -y nodejs
RUN npm install npm@latest -g
nodejs が必要なのでインストールしています。
ちなみにはじめはこの部分を以下のように記述していたのですが、(nodejs に npm は同梱のはず)
こうするとその後 bash: npm: command not found
が出てしまいます。
以下のページを参考に上のコードに落ち着きました。 Docker で php コンテナとかに npm をインストールするときのメモ
RUN apt-get update \
&& apt-get install -y nodejs
RUN mix local.hex --force
RUN mix archive.install hex phx_new 1.4.12 --force
RUN mix local.rebar --force
hex という Elixir のパッケージ管理ツールをインストールします。
(Ruby でいう rubygems)
ここで --force
がついてるのは以下のエラーが出るためです
Shall I install Hex? (if running non-interactively, use "mix local.hex --force") [Yn] ** (Mix) Could not find an SCM for dependency :phx_new from Mix.Local.Installer.MixProject
途中で yes と答えなければいけない部分があるのですが、それを --force
をつけることで無視してインストールできます。
postgres はどうすんの?
はい、先ほどの Dockerfile では Elixir(Phoenix)の環境しか整っていません。 postgres のコンテナも作らなければいけないです。
しかし
- postgres のコンテナと Phoenix のコンテナ間の通信はどうするの?
- コンテナ間通信を頑張って設定したとしても毎回それを設定するの?
- 毎回 postgres のコンテナ、Phoenix のコンテナを両方立てるのめんどくせえ
という問題たちが出てきます。 これらを解決してくれるのがdocker-composeです
※ちなみに docker-compose
を使わないコンテナ間通信は以下のページを参考にすればできそうです。
https://kitsune.blog/docker-network#i
「いやいや同じコンテナに DB も突っ込めばええやん!」について
そうなるかもですが、コンテナを分けることにはちゃんと理由があります。
この後出てくる docker-compose
と Kubernetes
ではアクセス分散のために複数のコンテナで Web サーバーを動かすことができます。
同じコンテナに DB も一緒に入れてしまうと、この際に DB もたくさんできてしまい、どのコンテナに接続されるかで DB の中身が変わってしまうと言う事態が起こります。 これを防ぐために DB と Web でコンテナを分けて Web のコンテナを増やしても同じ DB を参照するように設定すべきな訳です
docker-compose を使用する
docker-compose を使用するために docker-compose.yml
を作成します。
docker-compose.yml
には docker のコンテナ達やネットワークについてあるべき姿を記述します。
すると docker-compose がそれを元に良しなに設定してくれるわけです。 以下のように作成します。
version: "3" #docker-composeのバージョン指定
services: #ここより下でserviceを定義
web:
build: . #使用するDockerfileの場所
ports: #portをバインド
- "4000:4000"
volumes: #hostの./を/appとして共有
- .:/app
command: mix phx.server #サーバー起動のためのコマンド
depends_on:
- db #webの開始前にdbを起動
db:
image: postgres #使用するimage
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_HOST=db
以下の公式リファレンスがすごく参考になります。 Compose ファイル・リファレンス
docker-compose.yml に定義した command
や ports
は CMD
や EXPOSE
として Dockerfile で定義することもできます。
これで Docker で Elixir/Phoenix の環境を使用する準備ができました。
※volumes に関してはファイルを共有できるという面と、コンテナの外にファイルを安全に置いておけるという面もあります。詳しくは Kubernetes の章で出てきます。
適当なサンプルアプリを作ってみる
テストもかねてサンプルアプリを作ってみます。(アプリ名は dododo にしました)
$ docker-compose run web mix phx.new . --app dododo
Creating network "docker-elixir_default" with the default driver
Creating docker-elixir_db_1 ... done
The directory /app already exists. Are you sure you want to continue? [Yn] y
* creating config/config.exs
* creating config/dev.exs
* creating config/prod.exs
* creating config/prod.secret.exs
* creating config/test.exs
* creating lib/dododo/application.ex
* creating lib/dododo.ex
* creating lib/dododo_web/channels/user_socket.ex
* creating lib/dododo_web/views/error_helpers.ex
* creating lib/dododo_web/views/error_view.ex
* creating lib/dododo_web/endpoint.ex
* creating lib/dododo_web/router.ex
* creating lib/dododo_web.ex
* creating mix.exs
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating test/support/channel_case.ex
* creating test/support/conn_case.ex
* creating test/test_helper.exs
* creating test/dododo_web/views/error_view_test.exs
* creating lib/dododo/repo.ex
* creating priv/repo/migrations/.formatter.exs
* creating priv/repo/seeds.exs
* creating test/support/data_case.ex
* creating lib/dododo_web/controllers/page_controller.ex
* creating lib/dododo_web/templates/layout/app.html.eex
* creating lib/dododo_web/templates/page/index.html.eex
* creating lib/dododo_web/views/layout_view.ex
* creating lib/dododo_web/views/page_view.ex
* creating test/dododo_web/controllers/page_controller_test.exs
* creating test/dododo_web/views/layout_view_test.exs
* creating test/dododo_web/views/page_view_test.exs
* creating lib/dododo_web/gettext.ex
* creating priv/gettext/en/LC_MESSAGES/errors.po
* creating priv/gettext/errors.pot
* creating assets/webpack.config.js
* creating assets/.babelrc
* creating assets/js/app.js
* creating assets/js/socket.js
* creating assets/package.json
* creating assets/css/app.css
* creating assets/static/favicon.ico
* creating assets/css/phoenix.css
* creating assets/static/images/phoenix.png
* creating assets/static/robots.txt
Fetch and install dependencies? [Yn] y
* running mix deps.get
* running cd assets && npm install && node node_modules/webpack/bin/webpack.js --mode development
* running mix deps.compile
We are almost there! The following steps are missing:
$ cd app
Then configure your database in config/dev.exs and run:
$ mix ecto.create
Start your Phoenix app with:
$ mix phx.server
You can also run your app inside IEx (Interactive Elixir) as:
$ iex -S mix phx.server
また、しっかりホストとのファイル共有もできていることがわかります。
$ ls
Dockerfile _build config docker-compose.yml mix.exs priv
README.md assets deps lib mix.lock test
config/dev.exs の微修正
config/dev.exs
は dev 環境の設定ファイルです。
データベースのホスト名を db に変更しておきます。
# Configure your database
config :dododo, Dododo.Repo,
username: "postgres",
password: "postgres",
database: "dododo_dev",
hostname: "db", #fix
show_sensitive_data_on_connection_error: true,
pool_size: 10
DB の作成
$ docker-compose run web mix ecto.create
Starting docker-elixir_db_1 ... done
(省略)
Generated dododo app
The database for Dododo.Repo has been created
うまく作成できました。 これで DB との連携もうまくいっている事がわかります。
サンプルアプリを立ち上げてみる
$ docker-compose up
以下のように表示されれば成功です。
dockerhub にあげる
image を確認して tag をつける
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker-elixir_web latest a9ff6e7b157f 29 minutes ago 1.37GB
<none> <none> 507e3f91e80f 55 minutes ago 1.28GB
dododo_web latest d7724891c88c 4 hours ago 1.27GB
elixir 1.10.2 d6641893fb96 12 hours ago 1.23GB
postgres latest 73119b8892f9 2 days ago 314MB
$ docker tag a9ff6e7b157f sanposhiho/phoenix:latest
dockerhub にログイン
$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: sanposhiho
Password:
Login Succeeded
以下のリンクから適当に Create Repository
します。
https://hub.docker.com/repository/create
作った Repository に push します。
$ docker push sanposhiho/phoenix
dockerhub にあげると何ができるのか
元々 Dockerfile から image を毎回ビルドしていましたが、dockerhub にあげる事でその必要がなくなります。
すなわちdocker-compose.yml さえあれば先ほどの環境が作成できるということになります。
docker-compose.yml を修正
Dockerfile を使用しない形に docker-compose.yml を修正します。
version: "3"
services:
web:
image: sanposhiho/phoenix #先ほど作成したimage
ports:
- '4000:4000'
volumes:
- .:/app
command: mix phx.server
depends_on:
- db
db:
image: postgres
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_HOST=db
変更箇所は web の image の部分です。 Dockerfile を参照していたのを先ほど作成した image を指定しました。
これにより sanposhiho/phoenix をローカルで削除してから docker-compose up
しても
$ docker-compose up
Creating network "docker-elixir_default" with the default driver
Pulling db (postgres:)...
latest: Pulling from library/postgres
68ced04f60ab: Pull complete
59f4081d08e6: Pull complete
74fc17f00df0: Pull complete
8e5e30d57895: Pull complete
a1fd179b16c6: Pull complete
7496d9eb4150: Pull complete
0328931819fd: Pull complete
8acde85a664a: Pull complete
38e831e7d2d3: Pull complete
582b4ba3b134: Pull complete
cbf69ccc1db5: Pull complete
1e1f3255b2e0: Pull complete
c1c0cedd64ec: Pull complete
6adde56874ed: Pull complete
Digest: sha256:110d3325db02daa6e1541fdd37725fcbecb7d51411229d922562f208c51d35cc
Status: Downloaded newer image for postgres:latest
Pulling web (sanposhiho/phoenix:)...
latest: Pulling from sanposhiho/phoenix
50e431f79093: Already exists
dd8c6d374ea5: Already exists
c85513200d84: Already exists
55769680e827: Already exists
f5e195d50b88: Already exists
f7e2598a9cb7: Already exists
9ba52fdf113f: Already exists
896d0883eede: Already exists
019ae449ef4b: Already exists
a653e3c2dbc7: Pull complete
1b5116636524: Pull complete
6a7182c301e9: Pull complete
ff51ec8f406c: Pull complete
4c53f0b7d33e: Pull complete
79b95deb3b15: Pull complete
4e0c0135d3e7: Pull complete
Digest: sha256:ab7dbe3a514597f3e390f90de76de6465defb90103f58c3f08e34db97d890ae7
Status: Downloaded newer image for sanposhiho/phoenix:latest
Creating docker-elixir_db_1 ... done
Creating docker-elixir_web_1 ... done
このように sanposhiho/phoenix がなくても勝手に dockerhub から取ってきてくれます。
Kubernetes をやっていく
以下の記事を参考に先ほどの環境を Kubernetes(minikube)でも動かしてみます。 Docker Compose から Minikube + Kompose に移行してみよう
Kompose と言うのは docker-compose.yml
を Kubernetes 向けの設定ファイルに変換してくれる便利なやつです。
そもそも Kubernetes って?
色々見ましたが以下の記事の前半部分の説明がとても分かり易かったです 数時間で完全理解!わりとゴツい Kubernetes ハンズオン!!
Kompose を使う前に色々修正
Dockerfile
FROM elixir:1.10.2
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash
RUN apt-get install -y nodejs
RUN npm install npm@latest -g
RUN mix local.hex --force
RUN mix archive.install hex phx_new 1.4.12 --force
RUN mix local.rebar --force
RUN apt-get install ca-certificates #追加
COPY . /app #追加
WORKDIR /app
これを先ほどの手順で dockerhub に上げます (僕は sanposhiho/phoenixfork8s として上げました。)
docker-compose.yml
version: "3"
services:
web:
image: sanposhiho/phoenix_for_k8s #変更
ports:
- "4000:4000"
command: mix phx.server
depends_on:
- db
db:
image: postgres
ports:
- "5432:5432" #追加
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_HOST=db
追加/変更の他に volume の部分が削除されています。
kompose で変換
$ kompose convert
INFO Kubernetes file "db-service.yaml" created
INFO Kubernetes file "web-service.yaml" created
INFO Kubernetes file "db-deployment.yaml" created
INFO Kubernetes file "web-deployment.yaml" created
幾つかのファイルが作成されました。
生成されたファイルを微修正
web-service.yaml
に以下を追記します。
apiVersion: v1
kind: Service
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 ()
creationTimestamp: null
labels:
io.kompose.service: web
name: web
spec:
ports:
- name: "4000"
port: 4000
targetPort: 4000
selector:
io.kompose.service: web
type: NodePort #追加
status:
loadBalancer: {}
これにより外の世界からアクセス可能になります。
生成されたファイルを見ていく
Kompose が生成してくれたファイルを見ていきます。 以下の公式ドキュメントが理解の上で役立つと思います。 Kubernetes | Kubernetes オブジェクトを理解する
Kompose によって大きく分けて「Deployment」と「Service」の 2 つが作成されています。
Deployment とは
Deployment に関しては以下の公式ドキュメントがわかりやすいです。 Kubernetes | Deployment
以下の記事も(少し古いですが)とても参考になりました。 Kubernetes: Deployment の仕組み
deployment は pod(Kubernetes の管理する最小単位)を管理します。 (正確には pod を管理する ReplicaSet を作成します。)
実際に作成された web-deployment.yaml
を見てみます。
apiVersion: apps/v1 #どのバージョンのKubernetesAPIを利用するか
kind: Deployment #何についての定義ファイルか
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 ()
creationTimestamp: null
labels:
io.kompose.service: web
name: web #deploymentの名前
spec:
replicas: 1 #replicaの数
selector:
matchLabels:
io.kompose.service: web #podのラベル定義
strategy: {}
template: #deploymentが管理するpodの定義
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 ()
creationTimestamp: null
labels:
io.kompose.service: web
spec:
containers:
- args:
- mix
- phx.server
image: sanposhiho/phoenix_for_k8s
imagePullPolicy: ""
name: web
ports:
- containerPort: 4000
resources: {}
restartPolicy: Always
serviceAccountName: ""
volumes: null
status: {}
web-deployment.yaml
では spec.template
で指定された pod を常に 1 つ維持するようにしています。
Service とは
以下の公式ドキュメントが参考になります。 Kubernetes | Service
Pod はそれぞれが IP アドレスを持っています。例えば今回のように DB の Pod と Web サーバーの Pod に別れている場合、Web サーバーが DB の Pod にアクセスするには DB の Pod の IP アドレスが必要になります。
そこで Service
は pod たちをセットで管理し(「DB の Pod」「サーバーの Pod」と言う風に管理)、そのセットに対してのアクセスが可能になります。
例え Pod が動的に入れ替わったりしても一貫した方法でのアクセスが可能になります。
(Service
無しだと、何かの障害で 1 つの Pod が死んで、Deployment が代わりの Pod に入れ替えた時には IP アドレスが変わってしまうのでアクセスができなくなってしまいます)
実際に作成された web-service.yaml
をみてみます。
apiVersion: v1
kind: Service
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 ()
creationTimestamp: null
labels:
io.kompose.service: web
name: web
spec:
ports: #管理するportに関して
- name: "4000"
port: 4000
targetPort: 4000
selector: #管理するPodの指定
io.kompose.service: web
type: NodePort
status:
loadBalancer: {}
先ほど追加した type: NodePort
は指定しなかった場合デフォルト値として ClusterIP
に指定されます。
ClusterIP: クラスター内部の IP で Service を公開する。このタイプでは Service はクラスター内部からのみ疎通性があります。
これではクラスターの外部からのアクセスができなかったため NodePort
に変更しました
NodePort: 各 Node の IP にて、静的なポート(NodePort)上で Service を公開します。その NodePort の Service が転送する先の ClusterIP Service が自動的に作成されます。
<NodeIP>:<NodePort>
にアクセスすることによって NodePort Service にアクセスできるようになります。
minikube を立ち上げておく
$ minikube start
ダッシュボードを開いておく
$ minikube dashboard
ダッシュボードを使えば以下のように Pod などの状態をブラウザから確認できます。
立ち上げ!
ついに Kubernetes 上で立ち上げてみます。
$ kubectl apply -f db-deployment.yaml
$ kubectl apply -f web-deployment.yaml
$ kubectl apply -f db-service.yaml
$ kubectl apply -f web-service.yaml
これによってファイルに定義されたもの達が立ち上がります。
kensei-mba:docker-elixir nakatakensei$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/db-5fbcf655cd-2k7lw 1/1 Running 0 159m
pod/web-87795996-r6rcf 1/1 Running 0 159m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/db ClusterIP 10.111.98.119 <none> 5432/TCP 159m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19h
service/web NodePort 10.107.156.58 <none> 4000:30249/TCP 159m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/db 1/1 1 1 159m
deployment.apps/web 1/1 1 1 159m
NAME DESIRED CURRENT READY AGE
replicaset.apps/db-5fbcf655cd 1 1 1 159m
replicaset.apps/web-87795996 1 1 1 159m
DB を作成する
kubectl exec -it web-87795996-r6rcf mix ecto.create
kubectl exec -it <Pod NAME> <command>
で任意のコードを Pod に対して実行させることができます。
また、このコードが通る=Service が機能して DB に繋いでくれていることを意味します。
ちゃんと立ち上がっているか確認
$ minikube service list
|----------------------|---------------------------|--------------|---------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|----------------------|---------------------------|--------------|---------------------------|
| default | db | No node port |
| default | kubernetes | No node port |
| default | web | | http://192.168.64.2:32566 |
| kube-system | kube-dns | No node port |
| kubernetes-dashboard | dashboard-metrics-scraper | No node port |
| kubernetes-dashboard | kubernetes-dashboard | No node port |
|----------------------|---------------------------|--------------|---------------------------|
web の URL にアクセスします このように Phoenix の Top 画面が表示されれば成功です!
これでも動いてはいますが…
現状の設定では DB の Pod 内のみに DB のデータが存在します。 なので DB の Pod が死んだ時に全てのデータが死んでしまいます。
1 回実験してみましょう
ダッシュボードから作成されているKubernetes 以外のService, Pod, deployment を全て削除してください。
以下のようになれば合っています。
Web アプリケーションを DB を使うアプリケーションに作り直す
Phoenix にも Rails と同様に便利な generator の機能が搭載されています。
ローカルで generator を使用します。
$ mix phx.gen.html Blog Post posts title:string content:string
* creating lib/dododo_web/controllers/post_controller.ex
* creating lib/dododo_web/templates/post/edit.html.eex
* creating lib/dododo_web/templates/post/form.html.eex
* creating lib/dododo_web/templates/post/index.html.eex
* creating lib/dododo_web/templates/post/new.html.eex
* creating lib/dododo_web/templates/post/show.html.eex
* creating lib/dododo_web/views/post_view.ex
* creating test/dododo_web/controllers/post_controller_test.exs
* creating lib/dododo/blog/post.ex
* creating priv/repo/migrations/20200308110013_create_posts.exs
* creating lib/dododo/blog.ex
* injecting lib/dododo/blog.ex
* creating test/dododo/blog_test.exs
* injecting test/dododo/blog_test.exs
Add the resource to your browser scope in lib/dododo_web/router.ex:
resources "/posts", PostController
Remember to update your repository by running migrations:
$ mix ecto.migrate
書かれているように router.ex
にルーティングを追加しておきます。
defmodule DododoWeb.Router do
use DododoWeb, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/", DododoWeb do
pipe_through :browser
get "/", PageController, :index
resources "/posts", PostController #追加
end
# Other scopes may use custom stacks.
# scope "/api", DododoWeb do
# pipe_through :api
# end
end
migration します
$ mix ecto.migrate
11:23:37.327 [info] == Running 20200308110013 Dododo.Repo.Migrations.CreatePosts.change/0 forward
11:23:37.335 [info] create table posts
11:23:37.392 [info] == Migrated 20200308110013 in 0.0s
これで /posts
にアクセスすると以下のようなアプリが作成できています
(画像は New Post から新たな post を作成した後です)
この変更を dockerhub の image に反映させます。 先ほど説明した手順とほとんど同じなのでコマンドだけ載せておきます。
$ docker build .
$ docker images #image idを取得
$ docker tag <image id> sanposhiho/phoenix_for_k8s
$ docker push sanposhiho/phoenix_for_k8s
minikube 環境で変更後のアプリケーションを動かす
こちらもほぼ手順が変わらないのでコマンドだけ載せておきます。
$ kubectl apply -f db-deployment.yaml
$ kubectl apply -f web-deployment.yaml
$ kubectl apply -f db-service.yaml
$ kubectl apply -f web-service.yaml
$ kubectl get pods #pod nameの確認
$ kubectl exec -it <Pod NAME> mix ecto.create
$ kubectl exec -it <Pod NAME> mix ecto.migrate
先ほどと違うのは最後に $ kubectl exec -it <Pod NAME> mix ecto.migrate
が追加されていることです。これによって posts テーブルが DB の Pod 内に作成されます。
画像使い回しですが、以下のページが/posts から確認できれば成功です。
DB の Pod を削除してみる
ダッシュボードから DB の Pod を削除します。
Deployment によってすぐに新しい DB 用の Pod が作られます。(さすが)
さて、先ほどのページを開き直してみるとどうなっているでしょうか
訳のわからんエラーが出ています。 「何回か DB にアクセスしようとしたけど、無理でしたー」というエラーです。
無事に(?)DB が Pod が死んだことで消えてしまったことがわかりました。
ちなみに以下のコマンドで DB を作り直して Posts テーブルを再作成すると先ほどの「ほげほげ」のデータは残っていませんが、ページが正常に表示されます。 (作り直された DB の Pod に新しく出来た DB だから当たり前ですね)
$ kubectl get pods #pod nameの確認
$ kubectl exec -it <Pod NAME> mix ecto.create
$ kubectl exec -it <Pod NAME> mix ecto.migrate
volume を設定して DB の揮発を防ぐ
長々実験しましたが、この DB の揮発(=永続の逆。Pod が死ぬと DB も一緒に消えてしまうと言う意)を防ぐには volume を設定する必要があります。
volume の設定方法ですが 2 つ存在しました。 (どっちがいいのかは分からないです…どなたか教えてください。)
db-development.yaml
の volumes のみを弄るPersistentVolumeClaim
を利用する
1. db-development.yaml
の volumes のみを弄る
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 ()
creationTimestamp: null
labels:
io.kompose.service: db
name: db
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: db
strategy: {}
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 ()
creationTimestamp: null
labels:
io.kompose.service: db
spec:
containers:
- env:
- name: POSTGRES_HOST
value: db
- name: POSTGRES_PASSWORD
value: postgres
- name: POSTGRES_USER
value: postgres
image: postgres
imagePullPolicy: ""
name: db
ports:
- containerPort: 5432
volumeMounts: #追加
- mountPath: "/var/lib/postgresql/data"
name: pgdata
resources: {}
restartPolicy: Always
serviceAccountName: ""
volumes: #追加
- name: pgdata
hostPath:
path: /Users/nakatakensei/docker-elixir/ #postgresのdataをhostのどこに置いておくか
2. PersistentVolumeClaim
を利用する
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pv-claim
spec:
storageClassName: standard
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 ()
creationTimestamp: null
labels:
io.kompose.service: db
name: db
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: db
strategy: {}
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 ()
creationTimestamp: null
labels:
io.kompose.service: db
spec:
containers:
- env:
- name: POSTGRES_HOST
value: db
- name: POSTGRES_PASSWORD
value: postgres
- name: POSTGRES_USER
value: postgres
image: postgres
imagePullPolicy: ""
name: db
ports:
- containerPort: 5432
volumeMounts:
- mountPath: "/var/lib/postgresql/data"
name: pgdata
resources: {}
restartPolicy: Always
serviceAccountName: ""
volumes:
- name: pgdata
persistentVolumeClaim: #1とはここが違う
claimName: pv-claim
先ほどの手順で実験してもらうとどちらの方法を用いても DB 揮発しちゃう問題が解決したことがわかります。 (スクショを撮っても分かり難かったため、ここまで実際に手を動かして進めていただいた方は自分で実験してみてください)
終わりに
最終的なファイル達は以下のリポジトリに上がっています。 https://github.com/sanposhiho/docker-elixir-phoenix
すごく長い記事になってしまいました。 しかし、個人的に Docker→Kubernetes と一緒の流れで学べるようなチュートリアルが無かったため記事を分けずにこのように進めました。
どなたかの役に立てば幸いです。
参考
記事内であげなかったけどチラチラ見て参考にさせていただいたサイトです。
Docker で Ruby on Rails の開発をしよう kubernetes クラスタで Rails アプリを公開するチュートリアル Kubernetes の永続化 [PersistentVolume/PersistentVolumeClaim]