LoginSignup
1
1

More than 1 year has passed since last update.

【Rails x MySQL x Docker】まとめ

Last updated at Posted at 2022-02-06

環境

  • M1 Mac
  • lima(arch: x86_64)
  • MySQL:8.0
  • Ruby:3.0.3
  • Rails:6.1.4.4

留意点

MySQLにはunixソケットまたはtcpで接続できるが、ソケットだとディレクトリの権限でエラーになったりして手間なので、tcp接続についてのみ説明する。

mysql2

RubyでMySQLに接続するためにはmysql2を使用する。このgemはRails newコマンド時に-d mysqlを付与することでもインストールできる。

mysql2で接続を行うにはいくつかのパラメータを設定する必要がある。

The underlying MySQL client library uses the :host parameter to determine the
type of connection to make, with special interpretation you should be aware of:

  • An empty value or "localhost" will attempt a local connection:
    • On Unix, connect to the default local socket path. (To set a custom socket path, use the :socket parameter).
    • On Windows, connect using a shared-memory connection, if enabled, or TCP.
  • A value of "." on Windows specifies a named-pipe connection.
  • An IPv4 or IPv6 address will result in a TCP connection.
  • Any other value will be looked up as a hostname for a TCP connection.

tcpで接続を行うにはhostパラメーターにMySQLのホスト名を指定する必要がある。mysql2はhostが正しく設定されていない場合はソケット通信を試みる。

Railsの場合、これらのパラメーターの指定はconfig/database.ymlにて行う。

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV["MYSQL_USER"] %>
  password: <%= ENV["MYSQL_PASSWORD"] %>
  host: <%= ENV["MYSQL_HOST"] %>

development:
  <<: *default
  database: <%= ENV["MYSQL_DEV_DATABASE"] %>

test:
  <<: *default
  database: <%= ENV["MYSQL_TEST_DATABASE"] %>

production:
  <<: *default
  database: <%= ENV["MYSQL_PRD_DATABASE"] %>

MySQLはクライアントサーバシステムなので、ユーザーとそのパスワードを設定する必要がある。YAMLのエイリアスとインジェクトを使用して各環境にdefaultを差し込んでいる。また接続に必要な情報はすべて環境変数から読み込んでいる。

Docker環境の構築

今回は以下のような構成にする。

.
├── config
│   └── database.yml
├── docker
│   ├── docker-compose.yml
│   ├── .env
│   ├── mysql_db
│   │   └── conf.d
│   │       └── my.cnf
│   └── rails_app
│       └── Dockerfile.dev
├── .env
├── Gemfile

MySQL Dockerコンテナには起動及び接続用のユーザー設定を行うためMYSQL_ROOT_PASSWORDMYSQL_USERMYSQL_PASSWORDの3つの環境変数を設定する。

./.envにはこれらを含む接続に必要な環境変数を設定する。パスワードはpwgenなどによって適当に生成する。

./.env
MYSQL_ROOT_PASSWORD=Thaithi3xez7
MYSQL_USER=rails-app
MYSQL_PASSWORD=aix1Eequei8d
MYSQL_DEV_DATABASE=dev-db
MYSQL_TEST_DATABASE=test-db
MYSQL_PRD_DATABASE=prd-dev

Dockerコンテナにこの環境変数を読み込む。次にRailsコンテナ用のDockerfiledocker-compose.ymlを設定する。

docker/.env
COMPOSE_PROJECT_NAME=sample_project
docker/rails_app/Dockerfile.dev
ARG NODE_VERSION=14
ARG RUBY_VERSION=3.0.3

FROM node:${NODE_VERSION} as node
FROM ruby:${RUBY_VERSION}

# Install node and yarn
# see https://hub.docker.com/_/node
COPY --from=node /opt/yarn-* /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/

RUN ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
    && ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm

ENV PATH $PATH:\
/usr/local/bin/yarn:\
/usr/local/bin/node

RUN apt-get update -qq

# set envrionment variables
ENV RAILS_ENV="development" \
    NODE_ENV="development" \
    LANG="C.UTF-8" \
    TZ="Asia/Tokyo"

# Install gems
WORKDIR /rails_app

COPY Gemfile \
    Gemfile.lock \
    /rails_app/

RUN gem update bundler \
    && bundle config set path .cache/bundle
RUN --mount=type=cache,uid=1000,target=/rails_app/.cache/bundle \
    bundle install \
    && mkdir vendor \
    && cp -ra .cache/bundle vendor/bundle \
    && bundle config set path vendor/bundle

# Install npm packages
COPY package.json \
    yarn.lock \
    /rails_app/
RUN --mount=type=cache,uid=1000,target=/rails_app/.cache/node_modules \
    yarn install --modules-folder .cache/node_modules \
    && cp -ra .cache/node_modules node_modules

# Copy codes
COPY . /rails_app/

# Precompile assets
# RUN --mount=type=cache,uid=1000,target=/rails_app/tmp/cache/assets \
#     bundle exec rails assets:precompile

CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
docker/docker-compose.yml
version: "3.9"
services: 
  mysql_db:
    image: mysql:8.0
    container_name: dev-db
    hostname: &mysql_host mysql_host
    env_file:
      - ../.env
    volumes:
      - ./mysql_db/conf.d:/etc/mysql/conf.d
      - db_data:/var/lib/mysql

  rails_app:
    build:  
      context: ../
      dockerfile: ./docker/rails_app/Dockerfile.dev
    container_name: dev-server
    env_file:
      - ../.env
    environment:
      MYSQL_HOST: *mysql_host
    stdin_open: true
    tty: true
    ports:
      - 3000:3000
    command: bundle exec puma -C config/puma.rb
    volumes:
      - ..:/rails_app:cached
    depends_on: 
      - mysql_db

volumes:
  db_data:

docker/.envにてプロジェクト名を設定している。docker-compose.ymlのディレクトリに.envを設置することで環境変数を適用できる。COMPOSE_PROJECT_NAMEでプロジェクト名を指定している。

Dockerfileではmulti-stage build及びBuildkitによるbuildの高速化を図っている。詳細は以下の記事を参照。

肝心のdocker/docker-compose.ymlではenv_filesによって./.envを読み込んでいる。またdocker-composeではサービス名及びコンテナ名(この場合、サービス名はmysql_db。コンテナ名はdev-db)がホスト名になるが、hostnameによって明示的にホスト名を指定している。さらにエイリアスでMYSQL_HOSTを設定することで整合をとっている。

これをdocker-compsoe -f docker/docker-compose.yml up -dで起動、docker logsでMySQLコンテナのサーバーの起動を確認した後、設定したユーザーに権限を適用する。

./grant_mysql_user.sh
#!/bin/bash

source ./.env
source ./docker/.env

query=`cat << EOF
GRANT ALL PRIVILEGES ON *.* TO '${MYSQL_USER}'@'%'; 
FLUSH PRIVILEGES;
EOF
`

export COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME}
docker-compose exec mysql_db mysql -u root -p${MYSQL_ROOT_PASSWORD} -e"${query}"

COMPOSE_PROJECT_NAMEを設定することでdocker-compose.ymlのディレクトリ外でもdocker-composeコマンドでコンテナを操作できる。

./attach_rails_container.sh
#!/bin/bash

source ./docker/.env
export COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME}

docker-compose exec rails_app bash

最後にrailsコンテナにアタッチした後、bundle exec rails db:createとすればデータベースが生成される。ここで正しく生成されれば接続が確立されている。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1