Docker ComposeからMinikube + Komposeに移行してみよう

# はじめに

Kubernetesがコンテナオーケストレーションツールのデファクトになったといわれて久しい[^1]ですが、今年の[AWS re:Invent](https://reinvent.awsevents.com/)ではAmazon EKS[^2]の発表もあり、これからKubernetesに取り組もうという人もいるのではないかと思います。

[^1]: [Container orchestration: Moving from fleet to Kubernetes | CoreOS](https://coreos.com/blog/migrating-from-fleet-to-kubernetes.html)
[^2]: Amazon Elastic Container Service for Kubernetes. https://aws.amazon.com/eks/

かくいう私もその一人で、最近この辺りの技術に入門して色々と試していたので、今日はそのまとめを記します。

## 更新履歴

- 2020-05-09 Compose on Kubernetesについて追記
- 2017-12-12 初稿投稿

# 用語解説

## [Minikube](https://github.com/kubernetes/minikube)

Kubernetesクラスタをローカルマシン上で構築できるようにしてくれるものです。

Kubernetesクラスタを自前で一から構築するのは非常に大変そう(やったことはありません)なのですが、これを使うと簡単に試すことができます。

CIなどでも活用できそうです。

## [Kompose](http://kompose.io/)

Docker Composeの設定ファイルをKubernetes向けに変換するツールです。  
また、変換によって生成されたリソース群を、docker-composeライクなインタフェースで制御することもできます。

# この記事で行うこと

1. PostgreSQLをバックエンドとするシンプルなRailsのWebアプリケーションを用意します。
2. このWebアプリケーションをDocker化して、**Docker Compose**を使った開発環境を構築します。
3. **Kompose**を使ってDocker Composeの設定をKubernetes向けに変換し、**Minikube**上で動かします。

以降では、DockerやDocker Composeについては、ある程度知っているという前提で書きます。

使用するソースコードや設定ファイルは https://github.com/progrhyme/compose-to-minikube にまとめましたので、適宜ご参照ください。

## 2020-05-09追記: Compose on Kubernetesについて

2018年12月、Docker社が[Compose on Kubernetes](https://github.com/docker/compose-on-kubernetes)というツールを発表しました[^3]。

[^3]: [Simplifying Kubernetes with Docker Compose and Friends - Docker Blog](https://www.docker.com/blog/simplifying-kubernetes-with-docker-compose-and-friends/)

これを使うと、設定をKubernetes向けに変換することなく、次のようなコマンドで直接Kubernetesクラスタにデプロイすることが可能です。

```sh
docker stack deploy --namespace my-app --compose-file /path/to/docker-compose.yml mystack
```

ただし、利用にはDocker DesktopまたはDocker Enterpriseが必要とのことです。
利用できるのであれば、こちらの方が便利なケースもありそうです。

# 必要なもの

| ツール | 筆者環境のバージョン | 公式インストールガイド |
|-------|-------------------|---------------------|
| Docker | 17.05.0-ce | https://docs.docker.com/engine/installation/ |
| Docker Compose | 1.17.0 | https://docs.docker.com/compose/install/ |
| kubectl | v1.8 | https://kubernetes.io/docs/tasks/tools/install-kubectl/ |
| Minikube | v0.24.1 | https://github.com/kubernetes/minikube#installation |
| Kompose | 1.5.0 | http://kompose.io/installation/ |

全てのツールがWindows, Mac OS, Linuxの各プラットフォームに対応しているので、いずれかの環境であれば動かせると思います。

※ここでは、個々の細かいインストール手順は省略します。

筆者はUbuntu 16.04で作業しました。

# Railsアプリケーションの作成

PostgreSQLを使うアプリケーションを次のように作成します。

```sh
rails new my_app --database=postgresql
```

後の作業でDB接続先を環境変数から渡すようにするので、 `config/database.yml` を次のように編集しておきます。

```YAML:config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  url: <%= ENV['DATABASE_URL'] %>

development:
  <<: *default

test:
  <<: *default

production:
  <<: *default
```

動作確認をするため、適当なModelをControllerと共に生成しておきます。

```sh
bundle exec rails g scaffold person name:string age:integer
```

これで、 `/people` や `/people/new` などのエンドポイントによって、DBのCRUDが試せるようになりました。

# アプリケーションをDocker化する

上のRailsアプリケーションをDockerで動かすため、下のようなDockerfileを書きました。

```Dockerfile:Dockerfile
FROM ruby:2.4.2

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

RUN mkdir /app
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . ./

CMD bundle exec rails server -b 0.0.0.0 -p ${PORT:-3000}
```

下のコマンドで、Dockerイメージのビルドを試すことができます。

```sh
docker build -t progrhyme/compose2minikube:v1 .
```

# Docker Composeによる開発環境構築

上で用意したRailsアプリケーションをDBと連携して動作させるため、Docker Composeによってローカルマシン上でクラスタを構築します。

下のような設定ファイルを `docker-compose.yml` として作成しました。

```YAML:docker-compose.yml
version: "3"

services:
  db:
    image: postgres:9.5
    environment:
      - POSTGRES_USER=${MY_APP_DATABASE_USER}
      - POSTGRES_PASSWORD=${MY_APP_DATABASE_PASSWORD}
      - POSTGRES_DB=${MY_APP_DATABASE_NAME}
  rails:
    build:
      context: .
    ports:
      - "3000:${PORT:-3000}"
    environment:
      - DATABASE_URL=postgres://${MY_APP_DATABASE_USER}:${MY_APP_DATABASE_PASSWORD}@db:5432/${MY_APP_DATABASE_NAME}
    depends_on:
      - db
```

DockerHubで提供されている[PostgreSQLのイメージ](https://hub.docker.com/_/postgres/)では、上のような環境変数によって初期データベースユーザ名、パスワード、データベース名を設定することができます。

`${MY_APP_DATABASE_USER}`, ... といった変数は下のように環境変数で設定しました。

```sh
export MY_APP_DATABASE_USER=myapp
export MY_APP_DATABASE_PASSWORD=password
export MY_APP_DATABASE_NAME=my_app_development
```

docker-compose.ymlでは、上の設定のように環境変数を直接YAMLに記述して、比較的シンプルな設定ファイルを作ることができます。

また、上の設定中の `DATABASE_URL` で記しているように、 `rails` のコンテナ内からはservice `db` に対してホスト名 `db` でアクセスすることができます。

## 動作確認

上で構築したクラスタを動かしてみます。
下のコマンドでdbとrailsのコンテナを起動できます。

```sh
docker-compose up
```

http://0.0.0.0:3000 でRailsサーバが稼働しますが、DBマイグレーションを実施していないため、アクセスするとエラーになってしまいます。
マイグレーションは以下のように実行できます:

```sh
docker-compose exec rails rake db:migrate
```

ここで `rails` はdocker-composeから見たserviceの識別名です。

再度アクセスすると、Railsの画面を確認できるでしょう。

http://0.0.0.0:3000/people にアクセスすると、scaffoldで生成したRails開発者にお馴染みのコントローラ画面が表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/168843/213be7ee-79ac-d265-35ad-051d15a1b5c5.png)

# KomposeによってMinikube上でアプリケーションを動かす

以下では、先の手順で作成したDocker ComposeのクラスタをMinikube上で動かしていきます。

## 下準備

### Minikubeを立ち上げておく

まず、予めMinikubeを起動しておきましょう。
※必要なツール類のインストールは済んでいるものとします。

```sh
minikube start
```

初回はコンテナイメージのpullなどでかなり時間が掛かると思います。
無事に起動したら、下のようなコマンドで状態を確認することができます。

```sh
minikube status
minikube dashboard # ブラウザでダッシュボードを開く
```

`minikube start`によって、`kubectl`のコンテキストにMinikubeがセットされ、`kubectl`コマンドでクラスタに対する操作が可能になります。

### ローカルでDockerレジストリを動かす

Kubernetesにはコンテナイメージをビルドする機能はありません。
従って、サンプルアプリケーションのカスタムイメージをどこかのレジストリにpushし、そこからpullするように設定する必要があります。

サンプルなのでDockerHubに置いても良いのですが、ここでは[公式のDocker Registryイメージ](https://hub.docker.com/_/registry/)を利用して、ローカルでレジストリサーバを動かすことにしました。

以下のように、Dockerで簡単にレジストリサーバを動かすことができます:

```sh
docker run -d -p 5000:5000 \
  -v ~/.dockerregistry:/var/lib/registry \
  --restart always \
  --name registry \
  registry:2
```

ここでは、 ローカルマシンの `~/.dockerregistry` ディレクトリを永続化用のボリュームとしてマウントしています。

このレジストリに、上でビルドしたDockerイメージをpushしておきます。

```sh
docker tag progrhyme/compose2minikube:v1 localhost:5000/progrhyme/compose2minikube:v1
docker push localhost:5000/progrhyme/compose2minikube:v1
```

## Docker Compose設定を変換する

上の手順で作った `docker-compose.yml` を `kompose convert` で変換してみます。

```sh
$ cd <docker-compose.ymlがあるディレクトリ>
$ kompose convert
INFO Kubernetes file "db-service.yaml" created    
INFO Kubernetes file "rails-service.yaml" created 
INFO Kubernetes file "db-deployment.yaml" created 
INFO Kubernetes file "rails-deployment.yaml" created 
```

上手く設定を変換することができました。

ただし、 `kompose up` してローカルマシンからサービスにアクセス可能にするために、2点ほど修正する必要がありました。

1. `docker-compose.yml` で、コンテナイメージとしてローカルレジストリにpushしたイメージを指定するようにしておく
2. `rails-service.yaml` で、Serviceのtypeを変更

それぞれの差分としては以下のようになります:

```YAML
# docker-compose.yml
       :
       - POSTGRES_DB=${MY_APP_DATABASE_NAME}
    rails:
-     build:
-       context: .
+     image: localhost:5000/progrhyme/compose2minikube:v1
      ports:
        - "3000:${PORT:-3000}"
      environment:
       :

# rails-service.yaml
     :
     targetPort: 3000
    selector:
      io.kompose.service: rails
+   type: NodePort
  status:
    loadBalancer: {}
```

2について、KubernetesのServiceのtypeはデフォルトでは`ClusterIP`となっており、クラスタ内からしかアクセスできません。
クラスタ外からアクセス可能にするには、`NodePort`や`LoadBalancer`にする必要があります。

## 動作確認

`kompose convert`で生成した設定ファイル内のリソースには `io.kompose.service` といったキーのlabelやselectorが付加されています。
Komposeはこれらのlabel等を使ってリソースを管理しているのでしょう。

`kompose up`すると、リソース群をKubernetesクラスタ上に作成します。

```sh
$ kompose up
INFO We are going to create Kubernetes Deployments, Services and PersistentVolumeClaims for your Dockerized application. If you need differe
nt kind of resources, use the 'kompose convert' and 'kubectl create -f' commands instead. 

INFO Deploying application in "default" namespace 
INFO Successfully created Service: db             
INFO Successfully created Service: rails          
INFO Successfully created Deployment: db          
INFO Successfully created Deployment: rails       

Your application has been deployed to Kubernetes. You can run 'kubectl get deployment,svc,pods,pvc' for details.
```

rails, dbのDeploymentとServiceが作成されました。

`kubectl`コマンドでPodの状態を確認してみましょう。

```sh
$ kubectl get pods
NAME                     READY     STATUS        RESTARTS   AGE
db-65644f9968-4zx5w      1/1       Running       0          5s
rails-6896549fc6-45mmw   1/1       Running       0          5s
```

新しく作ったdbコンテナは空っぽなので、またDBマイグレーションが必要です。
`kubectl`コマンドを使ってrailsコンテナでコマンドを実行します。

```sh
kubectl exec -it rails-6896549fc6-45mmw rake db:migrate
```

さて、実は先ほど編集した `rails-service.yaml` の変更はクラスタに反映されていません。
反映するためには、 `kubectl apply` コマンドを実行します。

```sh
$ kubectl apply -f rails-service.yaml
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
service "rails" configured
```

警告は出ますが、変更は適用されました。

`kubectl`コマンドと`minikube`コマンドで確認してみます。

```sh
$ kubectl get services
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
db           ClusterIP   None            <none>        55555/TCP        6m
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP          1d
rails        NodePort    10.96.140.120   <none>        3000:31308/TCP   6m

$ minikube service list
|-------------|----------------------|------------------------|
|  NAMESPACE  |         NAME         |          URL           |
|-------------|----------------------|------------------------|
| default     | db                   | No node port           |
| default     | kubernetes           | No node port           |
| default     | rails                | http://127.0.0.1:31308 |
| kube-system | kube-dns             | No node port           |
| kube-system | kubernetes-dashboard | http://127.0.0.1:30000 |
|-------------|----------------------|------------------------|
```

ブラウザで http://127.0.0.1:31308 にアクセスすると、Railsが動いているのを確認できます。
または、 `minikube service rails` コマンドによって、該当のURLをブラウザで開くことができます。

# 所感

Kubernetesの設定はDocker Composeより複雑で、必要な事前知識も多いのですが、Komposeによってそれらをスキップして動作する設定を作れるのは便利だと思います。

MinikubeやKomposeによって、Kubernetesの開発環境もだいぶ便利になってきていると感じました。

本番でKubernetesを使うなら、開発環境はこんな感じで構築すると良さそうです。

# 今後やりたいこと

上で紹介した `docker-compose.yml` について、開発時はプロジェクトのディレクトリをマウントしておくと便利だと思いますが、Kubernetesでのやり方をまだ調べきれていないので、その辺りも試しておきたいなと思っています。

# むすびに

思いのほか色々な要素を詰め込んだエントリになってしまったので、説明が足りなかったり、わかりにくくなっている部分があるかもしれません。
また、試したばかりのことも多いです。

何か誤りなどありましたら、コメントなどでお知らせ下さい。

# 参考

- https://kubernetes.io/ ... Kubernetes公式サイト
  - [Running Kubernetes Locally via Minikube | Kubernetes](https://kubernetes.io/docs/getting-started-guides/minikube/) ... MinikubeのGetting Startedガイド
- http://kompose.io/ ... Kompose公式サイト
- https://docs.docker.com/ ... Dockerドキュメンテーション
  - https://docs.docker.com/compose/ ... Docker Compose
- [From Docker Compose to Minikube – Skillshare Writings – Medium](https://medium.com/skillshare-team/from-docker-compose-to-minikube-d94cbe97acda)
  - [Skillshare](https://www.skillshare.com/)でプロダクション/QA環境をKubernetesに移行したことに続いて、開発環境をDocker ComposeからMinikubeに移行した事例。

# 脚注