[Rails6] Capistrano+docker-composeを使用してデプロイ を自動化する。

docker-composeを使用してnginxとRailsコンテナを用意して、AWSにデプロイをしています。現在はSSH接続して、いちいち起動しています。
なので、Capistranoを使用して、自動デプロイを行えるようにしていきたいと思います。

##docker-compose.production.yml
```
version: '3.7'
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile.production
    volumes:
      - .:/date_course # カレントディレクトリを/date_courseにバインドマウントしている
      - public-data:/date_course/public
      - tmp-data:/date_course/tmp
      - log-data:/date_course/log
    ports:
      - "3000:3000"
    command: bash -c "bundle exec puma -C config/puma.rb -e production"
  nginx:
    build: 
      context: containers/nginx
    volumes:
      - public-data:/date_course/public
      - tmp-data:/date_course/tmp
    ports:
      - 80:80
    depends_on:
      - web
volumes:
  public-data:
  tmp-data:
  log-data:
```

##Dockerfile.production
```
FROM ruby:2.7.2 

RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - \
    && 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 --no-install-recommends && rm -rf /var/lib/apt/lists/*

#WORKDIRは指定するディレクトリがもし存在しない場合は作成してくれる。そのためRUN mkdir /date_courseを省略
WORKDIR /date_course

COPY Gemfile /date_course/Gemfile
COPY Gemfile.lock /date_course/Gemfile.lock

RUN gem install bundler
RUN bundle install

COPY . /date_course

RUN mkdir -p tmp/sockets
RUN mkdir -p tmp/pids
```

データベースはAWSのサービスであるRDSのmysqlを実装しています。
ここからはCapistranoの設定を行います。


###Gem追加
```Gemfile
  gem 'capistrano', '~> 3.15'
```

Gemの追加はこれだけになります。
直接Railsやnginxを動く際はGemをさらに追加する必要がありますが、
このアプリケーションではdocker-composeを動かせばいいのでこれだけで充分です。

```
$ bundle install
$ bundle exec cap install
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
create Capfile
Capified
```

###Capfile設定
特になにもいじりません。
なにも編集しない理由は
Dockerを動かせばrailsもnginxも動くからです。

###config/deploy/production.rb
```
server '××.××.×××.×××', user: 'ec2-user', roles: %w{web}
```

serverのElasticIPを設定します。
ElasticIPをAWSで関連づけすることに注意してください。
そうしなければIPアドレスが変わってしまうので

###config/deploy.rb
```
# config valid for current version and patch releases of Capistrano
lock "~> 3.16.0"

set :application, "date-course" #アプリケーション名
set :repo_url, "https://github.com/daisuke-harada/date-course.git" # githubのurl
set :deploy_to, "/home/ec2-user/date-course" # applicationをdeployする場所
set :docker_compose_path, "docker-compose -f docker-compose.production.yml"
set :RAILS_ENV, "RAILS_ENV=production"

namespace :deploy do
  application = fetch :application
  docker_compose_path = fetch :docker_compose_path
  rails_env = fetch :RAILS_ENV
  rails_compile = "#{docker_compose_path} run web rails assets:precompile #{rails_env}"
  db_drop = "#{docker_compose_path} exec -T web rails db:drop #{rails_env} DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
  db_migrate = "#{docker_compose_path} exec -T web rails db:migrate #{rails_env}"
  db_create = "#{docker_compose_path} exec -T web rails db:create #{rails_env}"
  db_seed = "#{docker_compose_path} exec -T web rails db:seed #{rails_env}"

  task :application_update do
    on roles(:web) do # config/deploy/production.rbのroles: %w{web}を指定している。
      execute "cd #{application};" "git pull origin master"
    end
  end

  task :build do
    on roles(:web) do
      execute "cd #{application};" "#{docker_compose_path} build"
    end
  end

  task :application_compile do
    on roles(:web) do
      execute "cd #{application};" "#{rails_compile}"
    end
  end

  task :up_containers do
    on roles(:web) do
      execute "cd #{application};" "#{docker_compose_path} up -d"
    end
  end

  task :database_create do
    on roles(:web) do
      execute "cd #{application};" "#{db_create}"
    end
  end

  task :database_set_up do
    on roles(:web) do
      execute "cd #{application};" "#{db_create};" "#{db_migrate};" "#{db_seed};"
    end
  end

  task :database_reset do
    on roles(:web) do
      execute "cd #{application};" "#{db_drop};" "#{db_create};" "#{db_migrate};" "#{db_seed};"
    end
  end

  task :down_containers do
    on roles(:web) do
      execute "cd #{application};" "#{docker_compose_path} down"
    end
  end

  task :destroy_images do
    on roles(:web) do
      execute "cd #{application};" "docker rmi -f `docker images -q`"
    end
  end

  task :application_set_up do
    on roles(:web) do
      execute "cd #{application};" "git pull origin master;" "#{docker_compose_path} down;" "docker rmi -f `docker images -q`"
      execute "cd #{application};" "#{docker_compose_path} build;" "#{rails_compile};" "#{docker_compose_path} up -d"
    end
  end
end
```

実行する際は、例えばapplication_set_upのtaskを実行する際は、

```
bundle exec cap production deploy:application_set_up
```
で実行します。bundle exec cap production の後に名前スペース名:task名で実行できます。

さらにconfig/deploy.rbないのコードから気になる部分を解説します。

```
set :docker_compose_path, "docker-compose -f docker-compose.production.yml"
set :RAILS_ENV, "RAILS_ENV=production"
```

setで指定したものはfetchで展開できます。

```
docker_compose_path = fetch :docker_compose_path
rails_env = fetch :RAILS_ENV
```

taskを解説します。

```
task :application_update do
  on roles(:web) do # config/deploy/production.rbのroles: %w{web}を指定している。
    execute "cd #{application};" "git pull origin master"
  end
end
```
on roles(:web） do ...endは先ほどconfig/deploy/production.rbで指定したserver '××.××.×××.×××', user: 'ec2-user', roles: %w{web}のサーバーを指定しています。
executeは実行するコマンドを指定しています。
まとめるとこのtaskはIPアドレス'××.××.×××.×××'のサーバー内でAWSuserのec2-userの実行権限を持ってcdコマンドを実行しアプリケーション内に移動します。その後、git pull origin masterを実行します。

###まとめ
Docker、docker-composeを実装していたため、capistaranoの設定が少なく済みました。
次回はGitHubActionsを使用しての自動デプロイを実装していきたいと思います。