環境
- 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
:hostparameter 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:socketparameter). - On Windows, connect using a shared-memory connection, if enabled, or TCP.
- On Unix, connect to the default local socket path. (To set a custom socket
- 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にて行う。
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_PASSWORD、MYSQL_USER、MYSQL_PASSWORDの3つの環境変数を設定する。
./.envにはこれらを含む接続に必要な環境変数を設定する。パスワードはpwgenなどによって適当に生成する。
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コンテナ用のDockerfile、docker-compose.ymlを設定する。
COMPOSE_PROJECT_NAME=sample_project
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"]
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コンテナのサーバーの起動を確認した後、設定したユーザーに権限を適用する。
# !/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コマンドでコンテナを操作できる。
# !/bin/bash
source ./docker/.env
export COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME}
docker-compose exec rails_app bash
最後にrailsコンテナにアタッチした後、bundle exec rails db:createとすればデータベースが生成される。ここで正しく生成されれば接続が確立されている。