無料でdocker, travisCI, Herokuを使ってwebアプリの開発環境を作成してみた

あとで読む

最初に

Docker Composeを使用してwebアプリを開発する環境を作成していこうと思う。

Rubyが動作するDockerfileを作成する

FROM ruby:2.5
RUN apt-get update && apt-get install -y \
  build-essential \
  libpq-dev \
  nodejs \
  postgresql-client \
  yarn

WORKDIR /product-register
COPY Gemfile Gemfile.lock /product-register/
RUN bundle install

touch Gemfile Gemfile.lock を作成する。

Gemfileのみ更新する。

source 'https://rubygems.org'
gem 'rails', '~>5.2'

Docker Composeを使ってコンテナを実行する

上記のコンテナを実行する場合、 docker run -V ~/Desktop/Product_register:/product-register -p 3000:3000 -it 152898587708 bash このような長いコマンドになる。

毎回これを入力するのは大変なので YAML = YAML ain't markup language に記述して Docker compose からコンテナを実行する。

docker-compose.yml を記述する

version: '3'

services:
  web:
    build: .
    # portsは -pの役割
    ports:
      - '3000:3000'
    # volumesは -vの役割
    volumes:
      - '.:/product-register'
    # ttyは -itのtを意味してる
    tty: true
    # -iを意味してる
    stdin_open: true

docker composeのコマンド

# docker build . と同じ
docker-compose build

# docker run imageID と同じイメージがなければbuildも一緒にしてくれる。
docker-compose up

# docker psと同じ
docker-compose ps

# docker exec コンテナIDと同じ
docker-compose exec サービス

# buildしてrun
docker-compose up --build

# stopしてrm
docker-compose down

# コンテナ停止とそのイメージを削除する際に使用する。
docker-compose down --rmi local -v

# コンテナの停止
docker-compose stop

docker composeからコンテナを実行してみる。 -ddetach でコンテナを実行したままコンテナから出るという意味になる。なので docker-compose ps でバックグラウンドでコンテナが起動しているのが確認できると思う。

docker-compose up -d

これで実行したコンテナに入る。

docker-compose exec web bash

ls

# 実行結果
Dockerfile  Gemfile  Gemfile.lock  docker-compose.yml

railsの環境を構築する

dockerfileに RUN bundle install というコマンドが書かれているので --skip-bundle でバンドルインストールをスキップする。 docker-compose exec web bash でコンテナ内に入った後に下記のコマンドを実行する。

rails new . --force --database=postgresql --skip-bundle

そして exit でコンテナを出た後に再びビルドする。

--build オプションを付ける事でイメージがあっても強制的に再ビルドする。

docker-compose up --build -d

そして再びコンテナ内に入って rails s -b 0.0.0.0 でrailsサーバを起動する。

http://localhost:3000/ にアクセスするとデータベースの接続エラーが表示される画面が出力される。

rails new で作成された database.yml の設定ファイルに追加する。

default: &default
  adapter: postgresql
  encoding: unicode
  # 下記のパスワードまでを追加する
  # dbがdocker-compose.ymlのdbコンテナと一致してアクセスする。rubyのコンテナからpostgresのコンテナへ
  host: db
  user: postgres
  port: 5432
  password: <%= ENV.fetch("DATABASE_PASSWORD")%> 
  # For details on connection pooling, see Rails configuration guide
  # http://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

# ...この下にテスト・本番環境の設定がある。

そして docker-compose.yml にもdbコンテナの 設定を追加する。もしデータベースのコンテナが起動 docker-compose up 出来ない場合は環境変数の箇所にあるコメントアウトを外すと上手くいく。どうしてそうなるかはpostgresについて深掘りする必要がある。dbが起動するとタブが使用出来なくなるので別タブを開いてコンテナに入る docker-compose exec web bash そして rails g scaffold product name:string price:integer vender:string でアプリを作成する。そしてアプリの要求するデータベーステーブルを作成する rails db:migrate ここまで終了したら rails s -b 0.0.0.0 でサーバを起動して http://localhost:3000/products にアクセスするとアプリのページにいける。

version: '3'

volumes:
  db-data:

services:
  web:
    build: .
    # portsは -pの役割
    ports:
      - '3000:3000'
    # volumesは -vの役割
    volumes:
      - '.:/product-register'
    
    # 環境変数に設定される
    environment:
      - 'DATABASE_PASSWORD=postgres'

    # ttyは -itのtを意味してる
    tty: true
    # -iを意味してる
    stdin_open: true
    # 先にdbを作成して下さいと指定する。
    depends_on:
      - db
    # webからdbにアクセスできるようになる。
    links:
      - db

  db:
    image: postgres
    # environment:
    #   - 'POSTGRES_USER=postgres'
    #   - 'POSTGRES_PASSWORD=postgres'
    
    # コンテナが削除されてもデータは残るようにローカル側のフォルダに保存するように設定する。
    # 下記のようなフォルダパスはなくdocker側でデータが管理される。実際のローカル環境からはアクセス出来ない。
    volumes:
      - 'db-data:/var/lib/postgresql/data'

rails テスト

テスト機能が元々あり test/controllers/products_controller_test.rb ここにテストコードが書かれている。下記のコマンドでtestを実行できる。

rails test

TravisCIを使ってテスト環境を構築してみる

最初にGitHubで product-register というリポジトリを作成して、そこに作成したrailsアプリのコードをあげる。

Travis CI - Test and Deploy with Confidence

のページに行ってそこで GitHubでサインインした後に作成したリポジトリを連携する。

最近よく聞くGithub actionsも同じ類のCIツールだとは知らなかった。

product-register/.travis.yml を作成する。

sudo: required

services: docker

before_install:
  - docker-compose up --build -d

script: 
  - docker-compose exec --env 'RAILS_ENV=test' web rails db:create
  - docker-compose exec --env 'RAILS_ENV=test' web rails db:migrate
  - docker-compose exec --env 'RAILS_ENV=test' web rails test

そして、サーバ上でPostgresが起動出来る用に docker-compose.yml のdbに環境変数 'POSTGRES_HOST_AUTH_METHOD=trust' を追加する。

version: '3'

volumes:
  db-data:

services:
  web:
    build: .
    # portsは -pの役割
    ports:
      - '3000:3000'
    # volumesは -vの役割
    volumes:
      - '.:/product-register'
    
    # 環境変数に設定される
    environment:
      - 'DATABASE_PASSWORD=postgres'

    # ttyは -itのtを意味してる
    tty: true
    # -iを意味してる
    stdin_open: true
    depends_on:
      - db
    links:
      - db

  db:
    image: postgres
    environment:
      - 'POSTGRES_HOST_AUTH_METHOD=trust'
    #   - 'POSTGRES_USER=postgres'
    #   - 'POSTGRES_PASSWORD=postgres'
    volumes:
      - 'db-data:/var/lib/postgresql/data'

そして GitHubに変更をpushすると travisCIにデプロイされテストが実行される。問題がなければ下記のように Done. Your build exited with 0 と表示される。

https://user-images.githubusercontent.com/23703281/126106189-d22223be-2611-4aa3-8774-f892d13e7c98.png

Herokuに本番環境をあげる

herokuのアカウントを作成する。(クレジットカードがいらない。)

ログインしたら

create new app > Create App 作成したら Resources > Heroku Postgres 選択して追加する。

次にrailsにある database.yml の productionの設定を下記のように変更する。

... テスト環境用の設定

production:
  url: <%= ENV['DATABASE_URL'] %>
#
# production:
#   <<: *default
#   database: product-register_production
#   username: product-register
#   password: <%= ENV['PRODUCT-REGISTER_DATABASE_PASSWORD'] %>

Dockerfile.prod とすることで本番環境で使用するDockerfileを作成することが出来る。

FROM ruby:2.5
RUN apt-get update && apt-get install -y \
  build-essential \
  libpq-dev \
  nodejs \
  postgresql-client \
  yarn

WORKDIR /product-register
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPy . .
CMD ["rails", "s"]

そして Herokuに rails特有の環境変数を追加する。 master.key にある値を https://dashboard.heroku.com/apps/アプリ名/settings > Config Vars に SECRET_KEY_BASE と入力した valueの項目に貼り付けて addをクリックする。

上記の DATABASE_URL は Herokuでpostgresを追加した際に Config Varsに追加されるので、それをHerokuにデプロイされたrails側で読み込めるようになっている。そして docker-composeに記述された環境変数は herokuの方で読み込まれる Dockerfile.prod には記述されてないので 'DATABASE_PASSWORD=postgres'

HerokuとGitHub レポジトリを連携する

https://dashboard.heroku.com/apps/アプリ名/deploy/github > Deploy から GitHub Connectを選択する。そうするとGitHubのアカウントが出るのでそこから連携したいレポジトリを選択する。そして wait for CI to pass deploy にチェックを入れる。そして Enable Automatic Deploys をクリックする。

次に .travis.yml に下記の用コードを追加する。

sudo: required

services: docker

before_install:
  - docker-compose up --build -d
  - docker login -u "$HEROKU_USERNAME" -p "$HEROKU_API_KEY" registry.heroku.com

script: 
  - docker-compose exec --env 'RAILS_ENV=test' web rails db:create
  - docker-compose exec --env 'RAILS_ENV=test' web rails db:migrate
  - docker-compose exec --env 'RAILS_ENV=test' web rails test

deploy:
  provider: script
  script: 
    docker build -t registry.heroku.com/$HEROKU_APP_NAME/web -f Dockerfile.prod .;
    docker push registry.heroku.com/$HEROKU_APP_NAME/web;
    heroku run --app $HEROKU_APP_NAME rails db:migrate;
  on:
    # masterにマージされた時だけデプロイする。
    branch: master

そして、travisCIで環境変数を設定する。 https://travis-ci.com/github/GitHubユーザ名/アプリ名/settings > Environment Variables に HEROKU_USERNAME=_HEROKU_API_KEY=heroku authorization tokenHEROKU_APP_NAME=herokuでcreateしたapp名 を入力する。

heroku authorization tokenhttps://dashboard.heroku.com/account/applications で作成することが出来る。

最後に config/routes.rbroot 'products#index' を追加することで herokuがアプリをデプロイしたページにアクセスした際に productsのページがルートページとして表示される。

これで GitHubにコードをpushするとtravisCIでテスト環境(docker-composeでビルドされるrailsのコンテナとpostgresのコンテナ)が走ってエラーがなければheroku(dockerfile.prod railsのみの環境 postgresはherokuのサイトから追加したのでコンテナとして必要ない)に本番環境にビルドされる。

今回の環境での開発フロー

実際の開発フローは Githubで featureブランチを切ってpushするとtravisCIのみが走る、その後プルリクエストを作成してコードがマージされる際にherokuにデプロイされる。

GitHubでの操作は下記の記事にまとめてある。

すぐ業務に就けるようにGitHubで一人チーム開発を体験してみた。

参照

とても分かりやすくおすすめの講座です。 米国AI開発者がゼロから教えるDocker講座

DockerComposeで生成したコンテナ・イメージ・ボリューム・ネットワークを一括削除 | TOMILOG

記事に関するコメント等は

🕊:Twitter 📺:Youtube 📸:Instagram 👨🏻‍💻:Github 😥:Stackoverflow

でも受け付けています。どこかにはいます。