#はじめに
つい最近DockerとCICD(Travis CI)を学んだので、実践がてらローカルのRailsアプリをDocker環境に移行し、CICDパイプラインを構築してみました。いろいろな方の知恵をお借りしてなんとか構築できたので、自分の備忘録として記録します。CICDツールには、最近登場したGitHubActionsを使用しています。
※まだまだ初学者ですので、間違い等あればご指導ご鞭撻のほどよろしくお願いいたします。
#環境
Mac OS Monterey12.1
Ruby2.5.3
Rails5.2.5
MySQL(latest)
#1.Dockerfileの作成
**Dockerfile**
FROM ruby:2.5.3
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
node.js \
mysql-client \
yarn
WORKDIR /workdir
COPY Gemfile Gemfile.lock /workdir/
RUN bundle install
**docker-compose.yml**
version: '3'
volumes:
mysqldb-data:
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- '.:/workdir'
environment:
- 'DATABASE_PASSWORD=mysqlpass'
- 'DB_HOST=db'
tty: true
stdin_open: true
depends_on:
- db
links:
- db
networks:
- app-net
db:
container_name: mysql
image: mysql
command: --default-authentication-plugin=mysql_native_password
volumes:
- 'mysqldb-data:/var/lib/mysql'
environment:
- 'MYSQL_ROOT_PASSWORD=mysqlpass'
- 'MYSQL_DATABASE=collections_development'
- 'MYSQL_PASSWORD=mysqlpass'
ports:
- '3306:3306'
networks:
- app-net
networks:
app-net:
driver: bridge
**database.yml**
default: &default
adapter: mysql2
encoding: utf8
host: db #⬅︎追加
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password: <%= ENV.fetch("DATABASE_PASSWORD") %> #⬅︎追加
socket: /tmp/mysql.sock
docker-compose.yml
でDBはMySQLのlatest imageを使用するので、command
の部分を忘れないようにします。また、networks
にてwebコンテナ
からdbコンテナ
にアクセスできるようにしています。
このままだと後からGitHubActionsでエラーが発生して修正しますが、とりあえず次に進みます。
#2.Rspecの作成
Rspecの作成については、以下の記事を参考にさせていただきました。
試しにコンテナに入ってテストを実行してみます。
$ docker-compose up --build -d
$ docker-compose exec web bash
$ bundle exec rspec
テストが問題なく通りました。
次はCICDパイプラインを構築していきます。
#3.GitHub Actionsの作成(test実行まで)
GitHubのActionsのタブで確認し、.github/workflows/
ディレクトリを作成し、その中にymlファイルを作成します。
$ mkdir -p .github/workflows
$ touch .github/workflows/rails_ci.yml
続いて、ymlファイルを作成していきます。
**.github/workflows/rails_ci.yml**
name: Ruby
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
Rspec_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up docker
shell: bash
env:
RAILS_ENV: test
run: |
docker-compose up --build -d
docker-compose exec -T web rails db:create
docker-compose exec -T web rails db:migrate
- name: RUN Rspec
shell: bash
env:
RAILS_ENV: test
run: |
docker-compose exec -T web bundle exec rspec
docker-compose exec
の後に-T
オプションを付けないと以下のエラーが発生します。
the input device is not a TTY
ここから最初にハマった箇所です。CIの中で無事にコンテナをbuildできましたが、DBにつながりません。
Mysql2::Error::ConnectionError: Can't connect to MySQL server on 'db' (111 "Connection refused")
いろいろ調べて試しましたがうまくいかず、最終的にたどり着いた答えが、
「MySQLが起動していないのではないか?」
恐らくMySQLが起動する前にdb:createしてエラーが出ているのではないかと思い調べると、
「wait-for-it 」という起動を確認できるライブラリがあるようなので、それを利用してみる。
**docker-compose.yml修正**
version: '3'
volumes:
mysqldb-data:
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- '.:/workdir'
environment:
- 'DATABASE_PASSWORD=mysqlpass'
- 'DB_HOST=db'
tty: true
stdin_open: true
depends_on:
- db
links:
- db
networks:
- app-net
wait: #⬅︎追加
image: willwill/wait-for-it:latest
db:
container_name: mysql
image: mysql
command: --default-authentication-plugin=mysql_native_password
volumes:
- 'mysqldb-data:/var/lib/mysql'
environment:
- 'MYSQL_ROOT_PASSWORD=mysqlpass'
- 'MYSQL_DATABASE=collections_development'
- 'MYSQL_PASSWORD=mysqlpass'
ports:
- '3306:3306'
networks:
- app-net
networks:
app-net:
driver: bridge
**.github/workflows/rails_ci.yml修正**
name: Ruby
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
Rspec_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up docker
shell: bash
env:
RAILS_ENV: test
run: |
docker-compose up --build -d
docker-compose -f docker-compose.yml run wait db:3306 -- echo "Database is up" #⬅︎追加
docker-compose exec -T web rails db:create
docker-compose exec -T web rails db:migrate
- name: RUN Rspec
shell: bash
env:
RAILS_ENV: test
run: |
docker-compose exec -T web bundle exec rspec
結果DBをcreateでき、テストが無事に実行されました。感激!
ローカル環境ではすぐにDBが立ち上がるのかdepends_on
を記載するのみでうまくいきましたが、CI環境上ではうまくいかないみたいですね。
wait-for-it.sh: waiting 15 seconds for db:3306
wait-for-it.sh: timeout occurred after waiting 15 seconds for db:3306
Database is up
Database 'web_development' already exists
Created database 'web_test'
次はherokuへのデプロイです。
#4.GitHub Actionsの作成(herokuへのデプロイ)
rails_ci.yml
にデプロイ部分を追記します。
**.github/workflows/rails_ci.yml デプロイ**
#続き
Deploy:
needs: Rspec_test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: login to heroku
run:
docker login -u ${{secrets.HEROKU_USERNAME}} -p ${{secrets.HEROKU_API_KEY}} registry.heroku.com
- name: deploy to heroku
run: |
docker build -t registry.heroku.com/${{secrets.HEROKU_APP}}/web -f Dockerfile.prod .
docker push registry.heroku.com/${{secrets.HEROKU_APP}}/web
heroku run -app ${{secrets.HEROKU_APP}} rails db:migrate
また、本番環境ではheroku側のDBを使用したいので、Dockerfile.prod
を作成し、それをbuildすることにします。
**Dockerfile.prod**
FROM ruby:2.5.3
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
node.js \
mysql-client \
yarn
WORKDIR /workdir
COPY Gemfile Gemfile.lock /workdir/
RUN bundle install
COPY . . #⬅︎追加
CMD ["rails", "s"] #⬅︎追加
続いて、各サービスの設定です。
###●herokuの設定
新しくAPPをcreateします。
本番環境では、DBにheroku側のHeroku Postgres
を使用します。
以下の通り準備します。
database.yml
のproduction
を以下の通りとします。
コメントアウトされているので外し、他のproduction
をコメントアウトする。
production:
url: <%= ENV['DATABASE_URL'] %>
そうするとherokuのConfig Vars
に記載されているDATABASE_URL
の環境変数が使われ、herokuのPostgresが使用できます。
また、KEY
のところにrailsのmaster.key
の値を設定します。
Deploy
でGitHubと連携します。
###●GitHubの環境変数設定
GitHubのActions secrets
に環境変数を設定していきます。
※余計な変数も設定されていますが気にしないでください。
herokuのAPI_KEYの取得の仕方は以下の通りです。
$ brew tap heroku/brew && brew install heroku
$ heroku authorizations:create
表示されるToken
のところです。
準備が整いましたので、リモートにpushしてCIを走らせます。
$ git add .
$ git commit -m '***'
$ git push origin main
やったー!!デプロイ成功!!herokuでアプリも起動できました!!!
長い道のりでしたが、やっと完成しました。
お知恵をお貸しいただいたみなさん、ありがとうございます!
記載していない途中出会ったエラーは別途まとめる予定です。
#参考
・Rails+RSpecで気軽に始めるテスト
・GitHub ActionsでRails+Rspec+docker-compose環境のci構築
・他のDockerコンテナからコンテナ内のMySQLに接続する
・GitHub Actionsのワークフロー構文
・Docker ComposeでDBコンテナが準備状態になるまで待つ
・docker-composeで「the input device is not a TTY」
・udemy 米国AI開発者がゼロから教えるDocker講座