- (主にサーバーサイドの)アプリをDockerを使って環境構築して
- terraformでインフラの構成をコード化して
- (Kubernetesなどのコンテナオーケストレーションツールを用いて)なるべく開発環境との差異が無いままコンテナデプロイをして
- CircleCIでテストからデプロイまで自動化
といった事がサーバーサイドエンジニアとしては求められている、いや最早知らないようではやっていくのは難しい、といったご意見が結構な頻度で見られたりします。
(そういったご意見の是非はまた別の機会に預けるとして)だとしたら初心者のサーバーサイド志望のエンジニアさん達の為には、これらの
- Docker
- CircleCI
- Terraform
- コンテナデプロイ(今回はECS)
といった要素をチュートリアル形式で学んでいくのが手っ取り早いのでは無いか(そして自分の理解の整理にもなる)と思い立ちました! どうでしょうか?
つまりこの記事で**現時点でナウいサーバーサイドの技術が一通り触れちゃう!**ということです!
目次
目次 |
---|
全体像を把握しよう |
Dockerを用いてRailsの環境を構築 |
CircleCIでCIの設定 |
Terraformを使うための準備 |
前半はここで終わり |
##この記事を読むにあたっての前提
- gitとgithubを使った事がある
- Dockerをなんとなく理解している
- Railsも触ったことある(触ったことなくてもできるかも)
- AWSのコンソールでEC2やVPCを立てた事がある
もちろん、それ以外の方も是非是非挑戦してみてください。理解なんて後から帳尻合わせればいいんです。
##できるようになること
railsアプリをDockerで構築できて、pushしたタイミングでCircleCIが走って、予めTerraformで構築しておいたAWS環境(ECS)に自動デプロイする事ができるようになる!
##注意してほしい事
- DBはsqliteです。RDSとつないでません。
- Webサーバもpumaで兼用してます。nginxなどのWebサーバーとつないでません
=>これらはまた次回やろうと思います!
##自分の環境
- Ruby 2.6.3
- Rails 6.0.1
- Docker 19.03.4
- Git 2.14.1
- Terraform 0.12.8
##全体像を把握しよう
目次 |
---|
全体像を把握しよう←今ココ |
Dockerを用いてRailsの環境を構築 |
CircleCIでCIの設定 |
Terraformを使うための準備 |
前半はここで終わり |
様々なツールやサービスを繋げていく事は、全体像の把握がとても大事になってきます。
まず大きい流れを確認しましょう。時系列順に列挙すると以下となります。
###アプリが自動でコンテナデプロイされるまでの流れ
(0.TerraformでAWS上のリソースを定義)
1.RailsアプリをGithubにpush
2.CircleCIでCI開始
3.CIでDockerイメージをビルド
4.DockerイメージをECRへpush
5.ECSのTaskDefinitionを更新
6.CIでmigration
7.アプリがデプロイされる!
画像に表すと、以下となります。
ざっくり各ツールの役割も解説すると
####terraform
インフラストラクチャ定義ツール。
クラウド上のリソースを定義ファイルの状態になるように生成・操作してくれる。
画面上でポチポチやってたインフラの操作をコードにできる。
####rails
デプロイしたいアプリそのもの。
####github
バージョン管理ツール。
アプリとCircleCIをつなぐ為のハブみたいな役割も担っている。
####CircleCI
ビルド/テスト/デプロイなどについて自動実行できるサービス。
これを用いて色々面倒くさい事を自動化しよう。
####Docker
アプリを動かす為の環境が一式詰まった仮想化プラットホーム。
ここでrailsが動く事はだいたい担保されてるので、Railsアプリが入ったDockerコンテナをそのまま本番環境にのっけたい
####ECR
コンテナイメージをprivateな環境に格納しておけるサービス。
Railsアプリの環境を入れたDockerコンテナイメージをここにぶっ込みたい。
####ECS
dockerコンテナを通して処理をしたりサービスを立ち上げたりと行った挙動を、EC2上で容易に行うためのAWSのサービス。EC2上での操作は全部AWSでやってくれるので、デプロイが簡単(のはずだがそうでもない)。
となります!非常に盛りだくさんですね。
はい、いつも通り順番に解説していきますのでご安心ください。
ではまずはDockerを用いて、Railsアプリを動かせる環境を作っていきましょう!
##Dockerを用いてRailsの環境を構築
目次 |
---|
全体像を把握しよう |
Dockerを用いてRailsの環境を構築←今ココ |
CircleCIでCIの設定 |
Terraformを使うための準備 |
前半はここで終わり |
とりあえずローカル環境で一度作ります。
terraformとrailsアプリを一つのディレクトリにまとめちゃいたいので、 terraform_ecs_deploy
というディレクトリを作っちゃいましょう(githubのリポジトリは別にするので注意してください!)
$ mkdir terraform_ecs_deploy
$ cd terraform_ecs_deploy
$ rails new terraform_ecs_app --skip-javascript # webpackerは今回いらないです
$ cd terraform_ecs_app
$ rails db:migrate
さて、このタイミングでRailsアプリをDocker化します。
Dockerfile
を作り、公式のRubyのイメージを取得し、以下のように書きましょう。
(今回簡単なDockerfileしか用意していないのはご了承ください。)
# 公式のイメージから取得
FROM ruby:2.6.3
# Dockerfile内部で使える変数として定義
ARG RAILS_ENV
ARG RAILS_MASTER_KEY
# コンテナ内のルートとする変数を/appと定義
ENV APP_ROOT /app
# 環境変数化
ENV RAILS_ENV ${RAILS_ENV}
ENV RAILS_MASTER_KEY ${RAILS_MASTER_KEY}
# コンテナ内のルートとする。
WORKDIR $APP_ROOT
# ローカルのGemfile, Gemfile.lockをコンテナ内のルートへコピー
ADD Gemfile $APP_ROOT
ADD Gemfile.lock $APP_ROOT
# bundle install実行。
# (バージョンのエラーが出る為、一応bundler 2.0.2を指定)
RUN \
gem install bundler:2.0.2 && \
bundle install && \
rm -rf ~/.gem
# バンドルインストールが終わってから他のファイルもコンテナ内へコピー
ADD . $APP_ROOT
# 本番環境の場合プロダクション
RUN if ["${RAILS_ENV}" = "production"]; then bundle exec rails assets:precompile; else export RAILS_ENV=development; fi
# ポート3000番を公開
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
では、このDockerfile
を元にDockerコンテナをbuild
し、rails
を立ち上げてみます。
$ docker build -t terraform_ecs_app:latest . # -tでタグを指定
$ docker run -it -p 3000:3000 terraform_ecs_app:latest # -it
docker run
時のオプションは以下の通りです。
-i, --interactive・・・コンテナのSTDINにアタッチする
-t, --tty・・・疑似ターミナルを割り当てる
このようになっていたらOKです。
http://localhost:3000
にアクセスするとこのようにおなじみの画面になっているはずです。
※注意! docker run -it -p 3000:3000 terraform_ecs_app:latestが通らない場合
もしここで
docker: you are not authorized to perform this operation: server returned 401.
See 'docker run --help'.
このようなエラーが出る場合は、ターミナルで以下のコマンドを打ち、環境変数を設定してください。
export DOCKER_CONTENT_TRUST=0
これにより、Dockerイメージが改ざんされていないか、という整合性判定をスキップでき、上のコマンドが通るようになるはずです。
Railsの詳細設定
さて、今回Railsの話が本題では無いので、機能的には簡素なもので良いです。なので、
- 表示用のトップ画面
-
ALB(Application Load Balancer)
のHealthCheck
用に、jsonでstatus: ok
が帰ってくるURL
の二つだけ実装しましょう。(もちろんALBとヘルスチェックについても後述します。)
$ rails g controller top index
$ rails g controller health_check
topコントローラ
はアクションとビューが自動生成されているはずなので、health_checkコントローラ
だけ変更します。
class HealthCheckController < ApplicationController
# ALBにステータスokを返す為のアクション
def index
render json: '{ "status": "ok" }'
end
end
ルーティングも変更します。
Rails.application.routes.draw do
root to: 'top#index'
resources :health_check, only: [:index]
end
それぞれ以下のように表示されればOKです。
http://localhost:3000/health_check
また、本番環境で tmp/pids
とtmp/sockets
が作成されない事がある為、念のため.gitignore
に以下の記述を追加しましょう。
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
# Ignore bundler config.
/.bundle
# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
/db/*.sqlite3-*
# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
# ===============追加================
!/tmp/pids
!/tmp/sockets
# =============ここまで===============
# Ignore uploaded files in development.
/storage/*
!/storage/.keep
/public/assets
.byebug_history
# Ignore master key for decrypting credentials and more.
/config/master.key
最低限の機能が実装できたので、githubにpushしましょう。
###githubにrailsアプリをpush
今回はgitとgihubの解説は省略します。
カレントディレクトリがrailsアプリなのを確認してから、以下のコマンドを打ってください。
(自分は今回リポジトリ名は terraform_ecs_app
としています。)
$ git init
$ git commit -m "initial commit"
$ git remote add origin リポジトリのURL
$ git push origin master
githubのリポジトリが以下のようになっていたらOKです。
それではいよいよCircleCIのCI設定をおこなっていきましょう!
##CircleCIでCIの設定
目次 |
---|
全体像を把握しよう |
Dockerを用いてRailsの環境を構築 |
CircleCIでCIの設定←今ココ |
Terraformを使うための準備 |
前半はここで終わり |
CircleCIとは、Saas型のCI/CDサービスであり、CI/CDでやることの基本であるビルドとテストとデプロイの3点が自動化可能なサービスです。
今回はこちらを使ってtestとデプロイ(ECRへのpush)の自動化を行なっていきたいと思います。
以下、CircleCIならではの特徴や料金についてはこちらをご参照ください。
いまさらだけどCircleCIに入門したので分かりやすくまとめてみた
###CircleCIの導入
↓こちらのURLの右上あたりにあるLogin
をクリックし、Log in with Github
を押してログインしましょう。
すると、自動的にgithubと紐づくはずなので、CIの設定画面まで行きましょう。
CircleCI
こちらの画像のように、Add Projects
を押すと、自分のgithubのリポジトリが出てくるので、
その中から先ほど作成したterraform_ecs_app
を選択します。Set Up Project
を押しましょう。
すると、プロジェクトの設定画面になると思います。
Operating System
はLinux
Language
もRuby
のままで大丈夫です。
そして、そのすぐ下に英語でチュートリアルが書いてあります。和訳すると
.circleci
という名前のフォルダーを作成し、config.yml
ファイルを追加します(ファイルパスが.circleci/config.yml
になるように)。config.yml
にsample.yml
の内容を入力します(以下を参照)sample.yml
を更新して、プロジェクトの構成を反映します。- この変更を
GitHub
にプッシュします。- 構築を始めましょう!これにより、
CircleCI
でプロジェクトが起動し、Webhookが作業の更新をリッスンします。
とのことです。実際にそのすぐ下に、あらかじめCircleCIが用意してくれてたrails用の設定ファイルsample.yml
があります。
指示通り、これをコピペして、ローカル環境で.circleci
というフォルダを作り、その中にconfig.yml
という名前のファイルを作り、そこに貼り付ければOKです。
ただし、これだとrubyのバージョンが違うのと、テストフレームワークがrspec
になっているので、今回はrailsデフォルトのテストフレームワークであるminitest
に変えた以下のものにしてください。
# Ruby CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
#
version: 2
# jobsの中にタスクを定義。一番下のworkflowのjobsのなかで定義したタスクを使う。
jobs:
# buildという名前のタスク定義
build:
docker:
# specify the version you desire here
- image: circleci/ruby:2.6.3-node-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/postgres:9.4
working_directory: ~/repo
# 実際の処理内容
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile.lock" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
# runのたびに実行
- run:
name: install dependencies
command: |
gem install bundler -v 2.0.2
bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
# Database setup
- run: bundle exec rake db:create
- run: bundle exec rake db:schema:load
# run tests
- run:
name: run tests
command: |
DISABLE_SPRING=true bundle exec rails test
# collect reports
- store_test_results:
path: /tmp/test-results
- store_artifacts:
path: /tmp/test-results
destination: test-results
ここまでできたら、先ほどのCircleCIの設定画面に戻り、5の右側にあるStartbuilding
を押してください。CircleCIが動き出します。
こうすることによって次回以降はpushされたタイミングでCircleCIがJobし始めます!
(ちなみにroutes.rbを変えたせいでtestが失敗してしまうので、該当のtestをコメントアウトするか、正しい記述(pathをroot_pathに変えます
)に直してからpushとbuildをしてください)
成功したらcircleCIのJOBSのタブの表示がこのようになります。(一番上の緑色がそうです。)
さて、ここまできたらようやくTerraform
に入ります!!
##Terraformを使うための準備
目次 |
---|
全体像を把握しよう |
Dockerを用いてRailsの環境を構築 |
CircleCIでCIの設定 |
Terraformを使うための準備←今ココ |
前半はここで終わり |
###Terraformのインストール
Terraform公式
Terraformを用いることで、今まで手続き的だったインフラの工程をコード化することできます。
homebrew
でもインストールできますが、Terraform
のバージョンマネージャであるtfenv
を使うのがおすすめです。バージョンアップに追従しやすいです。
まずはtfenv
自体をインストールしましょう。
$ brew install tfenv
$ tfenv --version
tfenv 1.0.1
list-remote
コマンドでインストール可能なTerraformのバージョンを確認できます。
ここでは、0.12.8
をインストールしましょう。
$ tfenv list-remote
.
.
0.12.8
$ tfenv install 0.12.8
$ terraform -v
Terraform v0.12.8
###TerraformでAWSを扱うためのIAMユーザの設定
terraformでAWSを扱うにはIAMユーザのACCESS KEY、SECRET KEY(とDEFAULT REGION)が必要です。
ここだけはterraformの管理下におけないので、AWSのコンソールでIAMユーザを作成しし、アクセスキーとシークレットキーを発行してください。
発行できたら、ターミナルで以下のようにアクセスキーとシークレットキーを環境変数に設定してください。
$ export AWS_ACCESS_KEY_ID=AKIxxxxxxxxx
$ export AWS_SECRET_ACCESS_KEY=wJalxxxxxxxxxxxxxxxx
$ export AWS_DEFAULT_REGION=ap-northeast-1
(PCの電源落としたり、ターミナル落としたりしたら消えてしまうのでもう一度入力し直してください。)
今回自分はAdministratorAccess ポリシーをアタッチしたIAMユーザのアクセスキーを用いていますが、こちら相当に強力な権限なので扱いには注意してください。
(間違ってもGitHubなどで公開してはダメです!)
AdministratorAccessポリシー以外では、権限不足で Terraform の実行が失敗することがあるので、その場合はエラーメッセージを参考に、必要な権限を付与しましょう。
これでTerraformを扱う準備ができました!
terraform用のgithubのリポジトリの作成
アプリ開発やCIとインフラ構成は別の話なので、リポジトリを分けるのがセオリーです。
今回はterraform_ecsという名前でgithubのリポジトリを作成します。
秘匿情報を扱うので、プライベートリポジトリにすることに注意してください。
さて、terraformのディレクトリ構造に関する考えは色々あるのですが
https://dev.classmethod.jp/devops/directory-layout-bestpractice-in-terraform/
https://qiita.com/anfangd/items/1b84f69fa2a4f8a29fbc
https://future-architect.github.io/articles/20190903/
今回は簡単のため環境だけディレクトリを分けて、その中に全てのtfファイルを突っ込む方式でいこうと思います。
terraform_ecs_deploy
├──terraform_ecs_app
└──terraform_ecs
├──prod
└──stg
また、今回はステージング環境の構築は行いません!ほぼ同じことをやるだけなので、一度productionで構築できたらすぐできちゃうと思います。
では早速、terraform用のディレクトリを作成しましょう。
$ cd .. # railsアプリにいた場合
$ pwd
/Users/matsumotokazuki/Desktop/terraform_ecs_deploy # terraform_ecs_deployにいることを確認。
$ mkdir terraform_ecs
$ mkdir terraform_ecs/prod
$ cd terraform_ecs/prod
このようなディレクトリ構成になっていれば大丈夫です。
terraform_ecs_deploy
├──terraform_ecs_app
└──terraform_ecs
└──prod # カレントディレクトリ
ここで、githubにpushする前に、terraformのインフラ構成ファイルを表すterraform.tfstateや秘匿情報を表すterraform.tfvarsなどをgitの管理下から除外します。(こちらも後述します)
$touch .gitignore
/.terraform/*
/terraform.tfvars
/terraform.tfstate
それではpushしましょう。
$ cd .. # terraform_ecsディレクトリに移動
$ git init
$ git add -A
$ git commit -m "initial commit"
$ git remote add origin リポジトリ名
$ git push origin master
前半はここで終わり
目次 |
---|
全体像を把握しよう |
Dockerを用いてRailsの環境を構築 |
CircleCIでCIの設定 |
Terraformを使うための準備 |
前半はここで終わり←今ココ |
諸々の準備が終わった時点で一旦区切ります!
後半からいよいよTerraformを使ってインフラを構築していきますので、気長にお待ちください。
後半はコチラ(にする予定)