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と通信します。

Dockerとは

コンテナの実行環境です。ハイパーバイザー型はホストOSにさらにOSを積み重ねますが、DockerはDockerエンジンというコンテナ実行環境上にコンテナを配備し、動作させます。
OSを積み重ねるハイパーバイザー型よりも無駄がないため、高速で動作します。
しかし制限もあり、コンテナ上で起動するサービスがカーネルパラメータなどを用いることができなくなります。

環境構築

EC2を立ち上げたり、DockerやDocker-composeをインストールする方法は以下ページを参考にしました。
AWS EC2インスタンスにdockerとdocker-composeをインストールして簡単なWEBサービスを立ち上げる方法
Docker-composeの1.23.0のバージョンをインストールするために、参照したページでは1.21.0となっていますが、

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にしてインストールしています。

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が多少変わっていますが、基本的なところは同一です。
Amazon RDS for MySQLインスタンス作成手順

ツールのインストール

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

mysql

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

sudo yum install mysql-devel

nodejs

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

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のバージョンを適用するかなど設定できます。

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のコンパイラをインストールします。


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/
以下のコマンドでインストールができます。
バージョンをつけなければ基本的に最新のバージョンがインストールされます。


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

bundler

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


gem install bundler

AWSのセキュリティグループの設定

EC2のセキュリティグループの設定

今回はリバースプロキシなどを設定せず、直接Railsサーバーにアクセスするため、カスタムTCPルールでポート3000、アクセス元を無制限に設定します。

RDSのセキュリティグループの設定

EC2を立てたサブネットからRDSにアクセスできるようにRDSのセキュリティグループを設定します。
ソースをEC2を立てたサブネットIDに設定します。

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

実行

ソースの取得

gitからソースを取得します。

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

env_file.envの設定

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

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

REDISのホストとポートは固定で上記の値を設定します。(後ほどのDockerBuild時にこのように設定します。)
RDSのエンドポイントはコンソールから確認できます。

DB、テーブルの作成

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

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

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

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

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

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を起動することができます。

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

Dockerの起動処理

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

docker-compose.yml

コメントを削除すると以下のように記載しています。

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

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

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実行用環境が立ち上がり、処理が終了すると廃棄されます。

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

まとめ

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

docker-compose -f docker-compose_sample_rails.yml run

最後に

こちらを置いておきますね。