株式会社チュートリアルでWebエンジニアを担当しています有賀です。
この記事は『Tutorial Advent Calendar 2020』の13日目です。
はじめに
チュートリアルでは以前からCI/CDのサービスとしてCloud Buildを使用していたのですが、Production用GCPの権限の問題もあり、例えテストが落ちていても一部のメンバー以外すぐに気づけない問題がありました。そのため、Cloud Buildに加えて、Github ActionsをCIツールとして新たに採用することになりました。そこで、普段業務で中々触ることの少ないCI/CDパイプラインの構築にGithub Actionsを使ってチャレンジしてみようと思います!
前提知識
- Rails開発に関する入門レベルの知識
- CI/CDに関する入門レベルの知識
- yamlファイルに関する入門レベルの知識
- コンテナに関する入門レベルの知識
各種環境、バージョン
- MacOS Catalina 10.15.4
- Ruby 2.6.6
- Rails 6.0.3
- Docker 19.03.8
- docker-compose 1.25.5
- MySQL 5.7
概要
Github Actionsとは
Githubが提供するCI/CDサービスであり、Circle CIやTravis CIなどその他のCI/CDツールと同様にリポジトリと連携しながら、リポジトリへのプッシュやマージ、プルリクエストなどをトリガーにして、アプリのビルドやテスト、デプロイといった様々な処理を実行できるようになっています。
Github自身が開発、提供するCI/CDツールということで、他のサービスには実現できないUI、UXや便利な機能の実現も十分に期待されるため、機能の拡充に伴ってシェアを伸ばしていくのではないかと個人的に期待しています。詳しく知りたい方は以下のドキュメントを参照ください。
参考
まずはアプリの環境構築
テスト用のアプリの作成には、docker、docker-composeを活用していきます。今回はCI/CDに関する紹介ということでDockerfileやコマンドの解説は行いませんので、気になる方は「Docker rails 環境構築」とググってみれば先人の方の記事がたくさんヒットするので参考にしてみてください。
1.各種ファイルの作成
任意のディレクトリとそのディレクトリ内にDockerfile、docker-compose.yml、Gemfile、Gemfile.lock、entrypoint.shを作成します。
$ mkdir cicd-test
$ cd cicd-test
Dockerfile
FROM ruby:2.6
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
&& apt-get update -qq \
&& apt-get install -y nodejs yarn \
&& mkdir /cicd-test
WORKDIR /cicd-test
COPY Gemfile /cicd-test/Gemfile
COPY Gemfile.lock /cicd-test/Gemfile.lock
RUN bundle install
COPY . /cicd-test
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
docker-compose.yml
version: '3'
services:
web:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/cicd-test
- /cicd-test/tmp
ports:
- "3000:3000"
depends_on:
- db
stdin_open: true
tty: true
db:
image: mysql:5.7
environment:
MYSQL_USERNAME: dbuser
MYSQL_ROOT_PASSWORD: dbpass
ports:
- '3306:3306'
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data:
driver: local
Gemfile
source 'https://rubygems.org'
gem 'rails', '~>6'
Gemfile.lock
空ファイルで作成する
entrypoint.sh
#!/bin/bash
set -e
# Remove a potentially pre-existing server.pid for Rails.
rm -f /cicd-test/tmp/pids/server.pid
# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
2.Railsアプリの作成、起動
まずは、アプリの雛形を作成します。
docker-compose run --rm --no-deps web rails new . --force --database=mysql --webpacker
その後、イメージをビルドします。
docker-compose build
database.ymlを編集して、データベースの設定まで完了させます。
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: <%= ENV.fetch("MYSQL_USERNAME", "dbuser") %>
password: <%= ENV.fetch("MYSQL_ROOT_PASSWORD", "dbpass") %>
host: <%= ENV.fetch("MYSQL_HOST", "db") %>
development:
<<: *default
database: cicd_test_development
test:
<<: *default
database: cicd_test_test
production:
<<: *default
database: cicd_test_production
username: cicd_test
password: <%= ENV['CICD_TEST_DATABASE_PASSWORD'] %>
以下のコマンドでDBを作成します。
docker-compose run --rm web rails db:create
以下のコマンドでアプリを起動後、http://localhost:3000にアクセスすることでおなじみのあの画面が表示されることを確認できます!
docker-compose up -d
3.ローカルでテストを実行
まずはscaffoldで簡単なCRUD機能とテストコードを自動生成します。
docker-compose exec web rails g scaffold User name:string age:integer
マイグレーションファイルなどが作成されるので、以下のコマンドでその変更を反映させます。
docker-compose exec web rails db:migrate
自動生成されたテストコードを実行して、パスするか確認します。
docker-compose exec web rails test
テストケースが全て通ることが確認できたらここまでの準備はOKです!
4.Github Actionsの設定、実行
まずは設定から
まずは設定ファイルを作成します。公式ドキュメントによると設定ファイルは.github/workflows配下に置きます。
mkdir .github && mkdir .github/workflows && touch .github/workflows/.rails-test.yml
name: RailsTest
on: [push]
jobs:
build:
runs-on: ubuntu-latest
services:
db:
image: mysql:5.7
env:
MYSQL_USERNAME: dbuser
MYSQL_ROOT_PASSWORD: dbpass
container:
image: ruby:2.6
env:
RAILS_ENV: test
MYSQL_HOST: db
MYSQL_USERNAME: dbuser
MYSQL_ROOT_PASSWORD: dbpass
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v1
with:
path: vendor/bundle
key: bundle-${{ hashFiles('**/Gemfile.lock') }}
- uses: actions/cache@v1
with:
path: node_modules
key: yarn-${{ hashFiles('**/yarn.lock') }}
- name: Setup yarn and node
run: |
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
apt-get update -qq
apt-get install -y nodejs yarn
yarn install
- name: Setup bundles
run: bundle install
- name: Setup Database
run: |
bundle exec rails db:create
bundle exec rails db:migrate
- name: Run tests
run: |
bundle exec rails test
設定ファイルの説明
まずはnameプロパティでワークフロー名を指定します。ワークフローとは1つ以上のジョブで構成された実行する処理とその処理を実行する条件を定義したもので、このワークフロー内で様々な処理を指定していくことで、自動でGitHub上でプロジェクトをビルド、テスト、デプロイなどをすることができます。
name: RailsTest # ワークフロー名を指定
次にワークフローをトリガーするためのイベントについて紹介します。このイベントを設定することで誰かがリポジトリにpushした時、issueやプルリクエストが作成された時などに予め登録しておいた手順で処理を実行することができます。今回はコミットをリポジトリにpushしたタイミングで、自動ビルド、テストが走るようにします。
on: [push] # リポジトリへpushしたタイミングで、ワークフローを実行するよう指定
次にjobsと呼ばれる実行する一連の処理群について設定していきます。基本的にjobsの各ジョブは並列で実行されますが、特定の順序で実行することも可能です。
jobs: # ここで実行するジョブを指定する
build: # 「build」という名前のジョブを定義
次に処理を実行する環境を指定します。ここではubuntuの最新環境を指定しておきます。
runs-on: ubuntu-latest # コンテナはLinuxベースのOS内で実行するように指定
続いて、servicesを使って、各ステップとは独立して動作するコンテナを指定することができます。テスト用のWebサーバーやデータベースが必要になる時に指定します。ここではdbというサービス名でテスト用のデータベースの実行コンテナを指定しています。
services:
db: # サービス名を指定
image: mysql:5.7 # 使用するコンテナイメージの指定
env: # 環境変数の設定
MYSQL_USERNAME: dbuser
MYSQL_ROOT_PASSWORD: dbpass
次に一連のbuildジョブが実行されるメインのイメージを指定していきます。ここではrubyのイメージを指定しています。
container:
image: ruby:2.6 # jobが実行されるDockerHubイメージを指定
env: # 環境変数の設定
RAILS_ENV: test
MYSQL_HOST: db
MYSQL_USERNAME: dbuser
MYSQL_ROOT_PASSWORD: dbpass
ここからはstepsを使用してテスト実行までの一連の処理を書いていきます。GitHubの提供しているuses: actions/checkout@v2とuses: actions/cache@v1を使用して、ソースコードをチェックアウトするアクションと依存関係をキャッシュするアクションを指定することでソースコードをチェックアウトし、ワークフロー内で頻繁に使われる依存関係をキャッシュして処理にかかる時間を大幅に短縮することができます。lockファイルから算出されるハッシュ値をcacheの検索keyに指定することで、依存関係に変更がなければ保存したcacheを使用することができます。
steps:
- uses: actions/checkout@v2 # ソースコードをチェックアウトするアクション
- uses: actions/cache@v1 # 依存関係をキャッシュするアクション
with:
path: vendor/bundle # キャッシュあるいはリストアをするファイルパス
key: bundle-${{ hashFiles('**/Gemfile.lock') }} # cacheの検索キー
- uses: actions/cache@v1
with:
path: node_modules # キャッシュあるいはリストアをするファイルパス
key: yarn-${{ hashFiles('**/yarn.lock') }} # cacheの検索キー
Railsの動作にnode.jsとyarnが必須のためインストールしていきます。
- name: Setup yarn and node
run: |
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
apt-get update -qq
apt-get install -y nodejs yarn
yarn install
bundle installでgemをインストールしていきます。
- name: Setup bundles
run: bundle install
DBの作成とマイグレーションもテストに必要なため、行っていきます。
- name: Setup Database
run: |
bundle exec rails db:create
bundle exec rails db:migrate
最後にテストが実行されます。
- name: Run tests
run: |
bundle exec rails test
いよいよ実行してみる
適当にhtmlファイルを編集して、リポジトリにpushしてみます。Actionsタブから確認できる実行履歴で以下の画面のような緑のチェックマークが確認できたら成功です。もし失敗していたら、実行履歴からエラーログを確認できるので適宜修正が必要になります。
終わりに
今回はGithub Actionsを題材にrailsアプリのpushをトリガーにして自動でビルド、テストするまでの流れを紹介してきました。今回使用したGithub Actionsの設定ファイルやDockerfileなどはテスト用のもので、そのままでは本番運用に耐えうるものではないかと思いますが、記事をよんだ方がとりあえずGithub Actionsを体験したり、今後カスタマイズしていくベースになれば幸いです。
次の私の記事ではGithub Actions CD編を予定しているのですが、どのような構成にするかはまだ未定です…(早く決めないとやばい)