DockerコンテナをEC2で起動と公開

Ruby on RailsアプリケーションをDockerでコンテナ化し、RDSと連携してEC2で公開しました。
環境
EC2　Amazon Linux 2 AMI (HVM), SSD Volume Type
DB　 RDS MySQL5.7.25
Docker version 18.06.1-ce
Docker-compose version 1.23.0i
Ruby 2.6.1
Nodejs 11.14.0
ソースは以下を利用しています。
https://github.com/tky-k/work/tree/master/docker_sample

##構成図
AWSにEC2をたて、RDSと通信します。
![スクリーンショット 2019-04-26 21.37.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388313/c7fd625b-a395-b004-a2d0-effe919bc604.png)

##Dockerとは
コンテナの実行環境です。ハイパーバイザー型はホストOSにさらにOSを積み重ねますが、DockerはDockerエンジンというコンテナ実行環境上にコンテナを配備し、動作させます。
OSを積み重ねるハイパーバイザー型よりも無駄がないため、高速で動作します。
しかし制限もあり、コンテナ上で起動するサービスがカーネルパラメータなどを用いることができなくなります。
![スクリーンショット 2019-04-26 21.39.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388313/b19d6feb-f8e5-4275-8790-2cb0c217b412.png)


##環境構築

EC2を立ち上げたり、DockerやDocker-composeをインストールする方法は以下ページを参考にしました。
<a href=https://qiita.com/y-do/items/e127211b32296d65803a>AWS EC2インスタンスにdockerとdocker-composeをインストールして簡単なWEBサービスを立ち上げる方法<a/>
Docker-composeの1.23.0のバージョンをインストールするために、参照したページでは1.21.0となっていますが、

```shell
sudo curl -L https://github.com/docker/compose/releases/download/1.21.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
```

バージョンを1.23.0にしてインストールしています。

```shell
sudo curl -L https://github.com/docker/compose/releases/download/1.23.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
```

RDSの立ち上げ方法は以下ページを参考にしました。2019年4月現在、UIが多少変わっていますが、基本的なところは同一です。
<a href=https://qiita.com/na0AaooQ/items/7c69a88c80f1efb4cad3>Amazon RDS for MySQLインスタンス作成手順<a/>

###ツールのインストール
ソースの取得、RubyやRailsのインストールにgitが必要なのでインストールします。
コンテナで配備されるのでEC2にRails環境は必要ないのですが、MySQLにDatabaseやテーブルの作成をEC2から行うのでインストールします。
（その後の開発をCIツールなどで継続していく、ということになるとRails環境が必要になってくると思います。）

####mysql
クライアントが必要になるのでインストールします。

```shell
sudo yum install mysql-devel
```

####nodejs
RailsでDBの作成に必要なのでインストールします。

```shell
curl -sL https://rpm.nodesource.com/setup_11.x | sudo bash -
sudo yum -y install nodejs
```

####git
ソースの取得やrbenvのためにインストールします。

```
sudo yum install git
```

####rbenv
rbenvはrubyのバージョン設定やインストールを行ってくれます。
複数のRubyをインストールしている場合、どのRubyのバージョンを適用するかなど設定できます。

```shell
git clone git://github.com/sstephenson/rbenv.git ~/.rbenv
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
sudo sh .rbenv/plugins/ruby-build/install.sh
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
```

####Ruby
rbenvを利用すればバージョンを指定してインストールできます。
rbenvでインストールするときはRubyをダウンロードしてその場でコンパイルします。
RubyはCで書かれているため、Cのコンパイラをインストールします。

```shell

sudo yum install gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel
rbenv install 2.6.1
・・・インストールログ・・・
rbenv rehash
rbenv global 2.6.1
ruby -v
ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-linux]
```

####gem
Rubyのサードパーティのライブラリです。
様々なライブラリが公開されています。
詳細はRuby公式ページでもわかりやすく書かれています。
https://www.ruby-lang.org/ja/libraries/
以下のコマンドでインストールができます。
バージョンをつけなければ基本的に最新のバージョンがインストールされます。

```shell

gem install ライブラリ:バージョン名 
```

####bundler
様々なgemの依存関係を解決してくれるgemです。
Railsもgemなのですが、1つずつgemをインストールすることは大変なので、bundlerを使います。

```shell

gem install bundler
```

###AWSのセキュリティグループの設定
####EC2のセキュリティグループの設定
今回はリバースプロキシなどを設定せず、直接Railsサーバーにアクセスするため、カスタムTCPルールでポート3000、アクセス元を無制限に設定します。
![スクリーンショット 2019-04-26 21.54.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388313/3b1c527f-57ad-304d-6563-468485e3b148.png)

####RDSのセキュリティグループの設定
EC2を立てたサブネットからRDSにアクセスできるようにRDSのセキュリティグループを設定します。
ソースをEC2を立てたサブネットIDに設定します。
![スクリーンショット 2019-04-26 22.07.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388313/37f4c850-da78-af49-3405-865a82de3ee7.png)

ここまでで環境整備は完了です。

###実行
####ソースの取得
gitからソースを取得します。

```shell
git clone https://github.com/tky-k/work
```

####env_file.envの設定
env_file.envはDockerのビルドやRailsアプリケーション内で使用する環境変数を設定するファイルです。
値が設定されていないので、以下のように値を設定します。

```shell
RAILS_DATABASE_PASSWORD="RDSを作成したときに設定したパスワード"
RAILS_DATABASE_HOST=RDSのエンドポイント
RAILS_DATABASE_USER="RDSを作成したときに作成したユーザー"
REDIS_HOST=rails-redis
REDIS_PORT=6379
```

REDISのホストとポートは固定で上記の値を設定します。（後ほどのDockerBuild時にこのように設定します。）
RDSのエンドポイントはコンソールから確認できます。
![RDSエンドポイント.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388313/5af2df74-075e-2950-1be0-2ca44e08f0ef.png)


####DB、テーブルの作成
Railsを使ってDBの作成、テーブルの作成を行います。
環境変数を設定します。env_file.envに設定した値を同じものを設定します。

```shell
export RAILS_DATABASE_PASSWORD="RDSを作成したときに設定したパスワード"
export RAILS_DATABASE_HOST=RDSのエンドポイント
export RAILS_DATABASE_USER="RDSを作成したときに作成したユーザー"
export REDIS_HOST=rails-redis
export REDIS_PORT=6379
```

bundleのインストールを行います。

```
cd work/
bundle install
```

もしうまくインストール出来ない場合は以下コマンドを実行してみてください

```shell
bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"
```

DB,テーブルを作成します。

```shell
cd gitからソースを取得したディレクトリ/work/docker_sample/docker_sample_rails/
bundle exec rails db:create
bundle exec rails db:migrate
```

ここまでで準備は完了です。

##Dockerでのコンテナ配備、起動
取得したソースに含まれているDocker-compose.ymlを利用してDocker-composeを実行すればRailsを起動することができます。

```shell
cd gitからソースを取得したディレクトリ/work/docker_sample/
docker-compose up -d --build
・・・ログ（中略）・・・
Creating rails-redis ... done
Creating rails-rails ... done
```

doneと出力されればコンテナの起動完了です。
ブラウザでEC2にアクセスすればRailsが起動していることを確認できます。
http://EC2に割り当てたElasticIP:3000

![スクリーンショット 2019-04-27 6.12.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388313/ac90a1e8-359b-63bd-d8f6-4540fe1aeba6.png)


##Dockerの起動処理
docker-composeを実行することで、docker-compose.ymlに記載した内容を元に、コンテナの取得やビルドが行われます。

####docker-compose.yml
コメントを削除すると以下のように記載しています。

```shell
version: "3"

services:
  local-redis:
    image: redis
    container_name:
      rails-redis
  
  local-rails:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - local-redis
    env_file:
      - env_file.env
    container_name:
      rails-rails
    command:
      bundle exec rails s -b 0.0.0.0
```

services
起動するコンテナです。ここに記載されたコンテナを包括してコントロールできます。
image
コンテナのイメージです。ローカルにイメージがあればローカルのイメージを、なければデフォルトではDockerhubからイメージを取得します。
container_name
コンテナに名前をつけます。
build
Dockerfileを使ってビルドを行います。
depends_on
依存関係をもたせることができます。記載したコンテナの起動処理が優先されます。
コンテナの起動処理は優先されるのですが、コンテナ内でプロセスが立ち上がるのを待つことはできません。
env_file
環境変数を記載したファイルを読み込みます。
command
コンテナを起動するときにパラメーターを与えます。

####Dockerfile
コメントを削除すると以下の記載になっています。

```shell
FROM ruby:2.6.1
RUN curl -sL https://deb.nodesource.com/setup_11.x | bash - && apt-get install -y nodejs

COPY docker_sample_rails /var/docker_sample_rails
WORKDIR /var/docker_sample_rails
RUN gem update bundler
RUN bundle install
```

FROM
コンテナを作成するベースイメージを指定します。これから作成するコンテナの枠組みみたいなものです。
RUN
ベースイメージ上で実行するコマンドです。注意点はRUNは毎回異なる環境で実行されている（ようなものである）
ことです。
RUNは毎回RUN実行用環境が立ち上がり、処理が終了すると廃棄されます。
![スクリーンショット 2019-04-27 6.27.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388313/41bcc742-e975-79d2-8d9c-5b74d0532dde.png)

COPY
コンテナ内に指定したファイルをコピーします。ディレクトリを指定すればディレクトリの配下にあるファイルを一式コピーできます。
WORKDIR
作業ディレクトリを変更します。

###まとめ
Dockerもdocker-composeも単純に起動したり、アプリケーションを配備するだけなら非常に簡単に実行できました。
実際に今回の構成でAWSに配備して実行する程度なら10数時間の試行錯誤で行えました。（ほとんどRailsの環境変数周りに躓いてしまってたのですが。。。）
起動、停止なら非常に簡単だと思いますし、コンテナで環境を使い捨てることができるので学習にはもってこいだと実感しました。（OSイメージを起動して、アタッチすれば様々な設定を試すことができますし、環境を破壊してしまった場合は削除してしまえばいいので）
せっかくAWS使ってるので、次はFargateやELBを使ってみようかとおもいます。
今回作成したコンテナはDockerhubにpushもしてみました。
ビルドなしで起動もできます。

```shell
docker-compose -f docker-compose_sample_rails.yml run
```

###最後に
こちらを置いておきますね。
![30750012.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388313/3eac6332-0e91-26cd-1d54-aac40456b128.jpeg)