🌴

【kamal2】EC2にRails8アプリをデプロイしてみる (DockerHub不使用)

に公開

ゴール

Rails8で採用されたkamal2を使用して、EC2に簡単にデプロイできるようにしていきます。
標準ではdockerhubを使用しますが、今回はdockerのpush先にAWS ECRを使用します。

前提

・rails newにて、kamalがインストール済みであること
・EC2にSSHログインができていること
・AWS CLIがインストール済みでPush先のAWS環境で利用可能であること
・direnvが使用可能であること

Kamalについて

Rails8ではkamal2を使用して、EC2に簡単にデプロイが可能になりました。
設定が完了することで下記のコマンド一つでデプロイが可能になります。

kamal setup

色々割愛していますがkamal setupの流れはざっっっっっくり以下です。

標準のデプロイ方法では、dockerリポジトリにdockerhubが採用されますが
今回はECRを使用していきます。
※dockerhubアカウント等不要

やってみよう

deploy.yml

各種ファイルを修正していきましょう。
まずはdeploy.ymlからです。kamalでのデプロイ設定を記述するファイルです。

初期状態から修正した箇所に下記のような感じでコメントをしていきます。
例) 「# ■修正: XXXXXXXXXXXXX」

# Name of your application. Used to uniquely configure containers.
service: oden

# Name of the container image.
image: cask-tokyo-ec2 # ■修正: AWS ECRリポジトリ名を設定

# Deploy to these servers.
servers:
  web:
    - 13.114.146.208 # ■修正: ssh configでの接続先名を設定
  # job:
  #   hosts:
  #     - 192.168.0.1
  #   cmd: bin/jobs

# Enable SSL auto certification via Let's Encrypt and allow for multiple apps on a single web server.
# Remove this section when using multiple web servers and ensure you terminate SSL at your load balancer.
#
# Note: If using Cloudflare, set encryption mode in SSL/TLS setting to "Full" to enable CF-to-app encryption.
# ■修正: proxyは不要なのでコメントアウト
# proxy:
#   ssl: true
#   host: app.example.com

# Credentials for your image host.
registry:
  # Specify the registry server, if you're not using Docker Hub
  # server: registry.digitalocean.com / ghcr.io / ...
  server: xxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com # ■修正: ECRリポジトリURIを設定
  username: AWS                                          # ■修正: ECRを使用する設定
  # Always use an access token rather than real password when possible.
  password:
    - <%= %x(aws ecr get-login-password --region ap-northeast-1) %> # ■修正: AWS CLIで取得

# Inject ENV variables into containers (secrets come from .kamal/secrets).
env:
  secret:
    - RAILS_MASTER_KEY
  clear:
    # Run the Solid Queue Supervisor inside the web server's Puma process to do jobs.
    # When you start using multiple servers, you should split out job processing to a dedicated machine.
    SOLID_QUEUE_IN_PUMA: true

    # Set number of processes dedicated to Solid Queue (default: 1)
    # JOB_CONCURRENCY: 3

    # Set number of cores available to the application on each server (default: 1).
    # WEB_CONCURRENCY: 2

    # Match this to any external database server to configure Active Record correctly
    # Use oden-db for a db accessory server on same machine via local kamal docker network.
    # DB_HOST: 192.168.0.2

    # Log everything from Rails
    # RAILS_LOG_LEVEL: debug

# Aliases are triggered with "bin/kamal <alias>". You can overwrite arguments on invocation:
# "bin/kamal logs -r job" will tail logs from the first server in the job section.
aliases:
  console: app exec --interactive --reuse "bin/rails console"
  shell: app exec --interactive --reuse "bash"
  logs: app logs -f
  dbc: app exec --interactive --reuse "bin/rails dbconsole"


# Use a persistent storage volume for sqlite database files and local Active Storage files.
# Recommended to change this to a mounted volume path that is backed up off server.
volumes:
  - "oden_storage:/rails/storage"


# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
# hitting 404 on in-flight requests. Combines all files from new and old
# version inside the asset_path.
asset_path: /rails/public/assets

# Configure the image builder.
builder:
  arch: amd64

  # # Build image via remote server (useful for faster amd64 builds on arm64 computers)
  # remote: ssh://docker@docker-builder-server
  #
  # # Pass arguments and secrets to the Docker build process
  # args:
  #   RUBY_VERSION: ruby-3.2.5
  # secrets:
  #   - GITHUB_TOKEN
  #   - RAILS_MASTER_KEY

# Use a different ssh user than root
ssh:
  user: ec2-user                        # ■修正: SSHログインユーザー名を設定
  keys: [ "~/.ssh/cask-tokyo-ec2.pem" ] # ■修正: SSH接続用pemファイル指定

# Use accessory services (secrets come from .kamal/secrets).
# accessories:
#   db:
#     image: mysql:8.0
#     host: 192.168.0.2
#     # Change to 3306 to expose port to the world instead of just local network.
#     port: "127.0.0.1:3306:3306"
#     env:
#       clear:
#         MYSQL_ROOT_HOST: '%'
#       secret:
#         - MYSQL_ROOT_PASSWORD
#     files:
#       - config/mysql/production.cnf:/etc/mysql/my.cnf
#       - db/production.sql:/docker-entrypoint-initdb.d/setup.sql
#     directories:
#       - data:/var/lib/mysql
#   redis:
#     image: redis:7.0
#     host: 192.168.0.2
#     port: 6379
#     directories:
#       - data:/data

.kamal/secrets

kamalデプロイ時の秘密情報です。
でも、git管理対象でコミットログに残るからあまり機密情報のハードコーディングはしたくない。

環境変数は基本的に.envrc(direnv)を使用する。

# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,
# and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either
# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.

# Example of extracting secrets from 1password (or another compatible pw manager)
# SECRETS=$(kamal secrets fetch --adapter 1password --account your-account --from Vault/Item KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY)
# KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD ${SECRETS})
# RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY ${SECRETS})

# Use a GITHUB_TOKEN if private repositories are needed for the image
# GITHUB_TOKEN=$(gh config get -h github.com oauth_token)

# Improve security by using a password manager. Never check config/master.key into git!
RAILS_MASTER_KEY=$(cat config/master.key)

.envrc

export AWS_PROFILE=''         # AWS CLI 使用するAWS環境のAWS CLI profile名

AWS_PROFILE: aws configure --profileで設定したプロフィール名

以上

上記設定が完了したら、下記を行いましょう!

kamal setup

https://自分のドメイン/upへアクセスしたらちゃんと緑画面出ました!

詰まったところ

最初何回kamal setupをしても全然反映されなくて、master.keyがあってないエラー出てました。

それもそのはず、kamal setupに反映される内容はあくまでgit commitされているものだけ。
ちょちょっと手元をいじってコミットしていない奴らはsetup時に反映されないのでご注意を!!

Discussion