40
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RUNTEQAdvent Calendar 2022

Day 10

【サンプル付き!】自分のDockerfileを振り返ってみた(Docker✖️Rails✖️React✖️MySQL)

Last updated at Posted at 2022-12-09

はじめに

おーせです!こんにちは!
実はプログラミングスクール(RUNTEQ)に通って5ヶ月が経ちました。現在PFを作成してます!
せっかくだから企業で使っているような技術を吸収しながらPFを開発したいと思い、付け焼き刃覚悟でDockerでの環境構築にチャレンジしました。
そして気づけば年末なので(?)、自分のDockerfileを振り返るという記事を書いてみます。

この記事で触れること

この記事では主にDockerfileの内容について触れていきます。
先月Dockerでの環境構築はできたものの、なんとなく立ち上がっている感が否めないので、この際振り返って確認することにしました。
ただ今回の記事では基本的なコマンドの説明は割愛しますので、ご了承ください。

サンプルについて

本記事で修正した環境構築前のDockerfileなどは以下のgithubで公開しております。
ご興味ある方は立ち上げてみたりして、遊んでみてください!(手順はREADMEに記載しております。)

使用技術について

以下の技術を念頭に環境構築をしました。
今回はバックエンドとフロントエンドそれぞれにDockerfileを作成しています。
 - Ruby(v2.7.4)
 - Rails APIモード(v6.1.7) 
 - React(v18)
 - MySQL@5.7

環境構築の方法について

以下の順序で作業しました。
① ホスト環境でReactアプリをセットアップ
② フロントエンド用のdockerfileを作成し、frontディレクトリに格納
③ バックエンド側のDockerfileやGemfile等必要なファイルを作成
④ バックエンド側の環境構築(コマンドの実行) 
⑤ docker-compose up -d --buildでビルドして立ち上げ。
これら手順はgithubのリポジトリでまとめておりますので、そちらをご参照いただけると嬉しいです。

バックエンドのDockerfileについて

まずはDockerfileを作成します。最初のものを下に載せます。

Dockerfile(api側)
FROM ruby:2.7.4
ENV TZ Asia/Tokyo

# 必要なパッケージをインストール
RUN apt-get update 
RUN apt-get install -y \
    build-essential \
    libpq-dev \
    nodejs \
    default-mysql-client \
    yarn

# 作業ディレクトリを指定
WORKDIR /myapp

# ローカルからコンテナにファイルをコピー
COPY Gemfile Gemfile.lock /myapp/
RUN bundle install
COPY . /myapp

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]

それぞれどういった意図で記載をしたか分割して説明していきます。まずはこちらから。

Dockerfile(api側抜粋)
FROM ruby:2.7.4
ENV TZ Asia/Tokyo

# 必要なパッケージをインストール
RUN apt-get update 
RUN apt-get install -y \
    build-essential \
    libpq-dev \
    nodejs \
    default-mysql-client \
    yarn

まず、ruby(v2.7.4)を使用して環境構築をするので、ベースとなるRuby2.7.4のDockerイメージを指定します。
2行目のENV TZ Asia/Tokyoでは、コンテナのタイムゾーンを東京に設定しています。(ENVコマンドではコンテナ内の環境変数を指定できます。)
参照させていただいた多くのDockerfileに記載されていたので記載しました。
 
次にRUNコマンドで必要なパッケージのインストールです。
apt-get updateでは、必要なインストールできるパッケージのリストを最新にするコマンドです。
その後、apt-get install -y [パッケージ名]とすることで必要なパッケージを最新でインストールできます。また、-yはインストール中に、YesかNoか複数回にわたって聞かれるのですが、それらに対して自動的に「yes」と返すためのオプションになります。
そして、今回導入したパッケージは次の5つです。必要性も含めて検証していきます。

build-essential

Ubuntu Linux/Debianにおいて、C/C++のビルド環境を整えるために必要なパッケージみたいです。
Ruby自体C言語で書かれていることを考慮すると、ビルドする際にはあった方が良いかと思われます。

libpg-dev

PostgreSQLをインストールする際に必要なパッケージです。
今回使用するのはMySQLでして、rails newする時点でデフォルトのDBをMySQLにすれば不要と判断して、今回は削除してみました。

nodejs

その名の通り、Node.jsを実行する環境を整えるパッケージです。
しかし、今回Rails apiモードでの開発であり、フロントはreactで別に開発するため、不要と判断して、こちらも削除します!

default-mysql-client

こちらはMySQLの環境関連のパッケージなので、削除せず据え置きとします。

yarn

こちらもnodejs同様で、yarnはバックエンド側では使用しないので不要と判断し、削除いたします!!

最後に、RUNを2行に分けて実行していますが、これも悪手だと思われます。
RUNコマンドはDockerの新たなイメージレイヤを追加するため、コンテナのボリュームが大きくなってしまいます。(RUNだけではありませんが...)
これらを踏まえて、上記コマンドについては以下のように改善できるかと思います!

上のDockerfile抜粋部分を改善したコードは以下のとおりです。

Dockerfile(改善案)
FROM ruby:2.7.4
ENV TZ Asia/Tokyo

# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y \
    build-essential \
    default-mysql-client 

次にこちらです。

Dockerfile(api側抜粋)
# 作業ディレクトリを指定
WORKDIR /myapp

# ローカルからコンテナにファイルをコピー
COPY Gemfile Gemfile.lock /myapp/
RUN bundle install
COPY . /myapp

WOKRDIRコマンドは、コンテナ内での作業ディレクトリを作り、指定する(今回はmyapp/と指定)コマンドになっています。
ここの部分RUN mkdir /myappを上に挟むことも考えたのですが、WORKDIRはディレクトリがなければ作ってくれるみたいなので、不要と判断しました。

その後、COPYコマンドでローカルにあるGemfileとGemfile.lockをコンテナ内のmyappディレクトリに追加します。
このCOPYコマンドもGemfileとGemfile.lockで行を分けているケースがありますが、COPYコマンドもコンテナにレイヤーを追加するようです。
そのため、1行にまとめて極力コンテナのボリュームを軽減することにしました。

RUN bundle installの行では、コンテナ内でbundle installを実行し、最後のCOPYコマンドでローカルのカレントディレクトリ内の全ファイルをコンテナに追加しています。
ここは据え置きでいいかな、と思います。

最後にこちらの行です。

Dockerfile(api側抜粋)
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]

こちらは、ローカルにあるentrypoint.shをコンテナのディレクトリにコピーして実行しています。
上4行によって、コンテナ内のアプリを3000番ポートに(EXPOSE)公開している状態になります。

最後のCMDコマンドは指定したコマンドを実行するコマンドになります。
今回は同一のDockerfile内でENTRYPOINTと一緒に使っているため、ENTRYPOINTで指定したコマンドの引数をCMDでは設定していることになります。
つまり、 ENTRYPOINT ["entrypoint.sh"]の実行をする際の引数として、CMDにより["rails", "server", "-b", "0.0.0.0"]を渡しているということです。
最終的にはentrypoint.shに記載されているexec "$@"に渡されて実行されていることになります。
 
["rails", "server", "-b", "0.0.0.0"]の内容も少しだけ説明します。
rails serverはrailsアプリを立ち上げるコマンドですが、--bind=0.0.0.0により、同一ネットワーク内のIPアドレス全てからのアクセスを可能にしています。
このオプションにより、コンテナの外からでもアプリにアクセスすることができるようになっています。
この部分については特段変更なしで大丈夫と思われます。

改善後のDockerfile

あまり変わってはいませんが、以下のようになりました。

Dockerfile(api側改善後)
FROM ruby:2.7.4
ENV TZ Asia/Tokyo

# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y \
    build-essential \
    default-mysql-client

# 作業ディレクトリを指定
WORKDIR /myapp

# ローカルからコンテナにファイルをコピー
COPY Gemfile Gemfile.lock /myapp/
RUN bundle install
COPY . /myapp

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]

フロント側のDockerfileについて

こちらは下のような記載になります。
環境構築自体はホスト環境で行い、その後Dockerfileをフロントのディレクトリ内に置いてビルドする、という手順を踏みました。

Dockerfile(front)
FROM node:19.0.0-alpine
WORKDIR /user/src/app

こちらはNode.jsのイメージ(軽量と言われるalpineを使用)をベースとして作り、コンテナ内にappディレクトリを作成します。
Node.jsのバージョンは導入したいreactのバージョンとの互換性を調べて、問題ないバージョンにしてください。
ここまででDockerfileの振り返りは以上となります!
最後にdocker-compose.ymlについても簡単に触れてみます。

docker-compose.ymlについて

docker-composeとは複数のコンテナを同時操作するために役立つコマンドです。
docker-compose.ymlは操作方法の元となる設計書だと私は理解しています。
作成したdocker-compose.ymlは下のようになっております。

docker-compose.yml
version: '3'

volumes:
  mysql_data:
services:  
  api: 
    build: 
      context: ./api/ 
      dockerfile: Dockerfile
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    image: rails:dev
    volumes:
      - './api:/myapp' 
    ports:
      - '3000:3000'
    tty: true
    stdin_open: true
    depends_on: 
      - db
  db:
    platform: linux/x86_64 
    image: mysql:5.7
    volumes:
      - 'mysql_data:/var/lib/mysql'
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - '3306:3306'
  front:
    build:
      context: ./front/
      dockerfile: Dockerfile
    volumes:
      - './front:/user/src/app'
    environment:
      - WDS_SOCKET_PORT=0
    command: sh -c "yarn start"
    ports:
      - '8000:3000'
    tty: true
    stdin_open: true

1行目のversion: '3'ではdocker-composenのバージョンを指定しています。
2022年時点で3が最新なのでこのように記載しています。
docker-composeで使用するコマンドの説明を簡単にできればと思います。

以下、コマンドの説明をします。
(私なりに調べ噛み砕いたつもりですが、間違ってたらご指摘お願いします。)

コマンド 説明
build 指定したDockerfileをビルドする
command 実行するコマンドを指定し、実行する
image 実行するコンテナのイメージを指定する
volumes ホストとコンテナ内をバインドマウントし、共有可能にする
environment コンテナ内の環境変数を設定して管理する
ports コンテナ内で公開されているポートとホストのポートを接続する
tty コンテナ内で作業する場合の出力結果をきれいに表示する
stdin_open コンテナ内での標準入出力を可能にする
depends_on 指定したサービスとの依存関係を設定する

それぞれのコマンドの役割に応じて、Dockerfileと齟齬がないようにコマンドの内容を指定します。
docker-compose.ymlの内容はアプリ作成を進める中で追加したものも記載しています。

終わりに

以上で、自分のDockerfileを振り返る記事を終了します。
結局修正したのはapi側のDockerfileの冒頭だけでしたが、記事作成を通して曖昧に勉強してきた知識を少しは整理できたかな、と思います。
これからDockerライフを始める皆さんに少しでも参考になれば嬉しいです。
不備ありましたら、確認し随時更新しますのでご指摘お願いします。では!

40
20
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
40
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?