🐳

3年間Dockerと向き合ってきた拙者によるDocker環境構築決定版 with Devcontainer (その2)

に公開

前回は環境のディレクトリ構造のみを示しました。
今回はファイルの中身についてみていこうと思います。

compose.yml

# 開発環境名として設定
name: ${PROJECT_NAME}-env
services:
  # バックエンドコンテナ定義
  back:
    build:
      # Dockerfile保存ディレクトリ
      context: ./${SERVICE_A}
      dockerfile: Dockerfile
    # 明示的に生成するイメージ名を指定
    image: ${PROJECT_NAME}-${SERVICE_A}-image
    # 明示的に生成するコンテナ名を指定
    container_name: ${PROJECT_NAME}-${SERVICE_A}-container
    # 明示的に生成するホスト名を指定
    hostname: ${PROJECT_NAME}-${SERVICE_A}-host
    # TTYを有効化
    tty: true
    # 所属するdocker networkの指定
    networks:
      default: null
    # コンテナ内ストレージの永続化
    volumes:
      # dockerシステムへの保存
      - type: volume
        source: template-a
        target: /home/work
      # ホストと共有するファイルの保存
      - type: bind
        source: ./${SERVICE_A}/share
        target: /bak
  db:
    # データベースコンテナ定義
    build:
      context: ./${SERVICE_B}
      dockerfile: Dockerfile
    image: ${PROJECT_NAME}-${SERVICE_B}-image
    container_name: ${PROJECT_NAME}-${SERVICE_B}-container
    hostname: ${PROJECT_NAME}-${SERVICE_B}-host
    tty: true
    networks:
      default: null
    volumes:
      - type: volume
        source: template-b
        target: /home/work
      - type: bind
        source: ./${SERVICE_B}/share
        target: /bak
  front:
    # フロントエンドコンテナ定義
    build:
      context: ./${SERVICE_C}
      dockerfile: Dockerfile
    image: ${PROJECT_NAME}-${SERVICE_C}-image
    container_name: ${PROJECT_NAME}-${SERVICE_C}-container
    hostname: ${PROJECT_NAME}-${SERVICE_C}-host
    tty: true
    networks:
      default: null
    volumes:
      - type: volume
        source: template-c
        target: /home/work
      - type: bind
        source: ./${SERVICE_C}/share
        target: /bak
    # コンテナ内ポートの公開設定
    ports:
      - mode: ingress
        target: 80
        published: "80"
        protocol: tcp
# docker networkの定義
networks:
  default:
    # 明示的にネットワーク名を指定
    name: ${PROJECT_NAME}-net
# docker volumeの定義
volumes:
  template-a:
    # 明示的にボリューム名を指定
    name: ${PROJECT_NAME}-${SERVICE_A}-vol
  template-b:
    name: ${PROJECT_NAME}-${SERVICE_B}-vol
  template-c:
    name: ${PROJECT_NAME}-${SERVICE_C}-vol
  • 各ディレクティブについてはコメントに説明を記載しております。

構造の意図について

  • 紹介するcompose.ymlは私の中での最適解として挙げております。
  • なぜこの構造が最適であるかの根拠について解説していきたいと思います。

1. .envファイルを用いた柔軟性

  • ${VAR}はLinuxのユーザーであればなじみのある表記ですが、これは変数を代入する際の表記となっております。この変数は基本的には環境変数から持ってくるのですが、環境変数で定義されていない場合、compose.ymlと同階層の.envファイルの内容を参照するようになっています。基本的には環境変数で設定するのではなく、.envファイルで定義して変数を構築することで

    1. docker composeの環境名を個別のユーザーごとに設定できる
    2. 各コンテナのディレクトリ名などを柔軟に設定できる
      etc...

    などのメリットが考えられます。
    また今回は登場していない、APIサーバー、メールサーバー、キャッシュサーバー、といったサーバーを導入した際に同一の構成をとりながらコンテナを追加できるというメリットも上げられます。
    因みに、.envファイルのサンプルはこのようになってます。

# .env
PROJECT_NAME=docker-sample
SERVICE_A=back
SERVICE_B=db
SERVICE_C=front

またcompose.ymlが正常に機能するかどうか確認する際は以下のコマンドにて確認することが可能です。

docker compose config

2. dockerが生成する各要素の名称の固定化

  • dockerは便利なもので、実行環境をとりあえず欲しい場合にdocker runなどのコマンドで環境構築ができます。しかし、実際の開発においては継続開発が必要で、その開発環境自体の管理も必須の要件になってきます。そうなった場合、後から必要になるコンテナ等も出てくると思います。それを想定して作成するイメージやボリュームには名称を付与しています。

3. インフラソースとアプリケーションソースの分離

  • 今回volumesに2つ指定している意図としては、「インフラ部」と「フロント部」を分けてソース管理したいという狙いがあるためです。こうすることでインフラのメンテナ時はインフラのみに集中でき、実際のアプリケーション開発時はアプリ開発に集中できます。

それぞれのボリュームの用途

  1. type: volume
  • ここでアプリケーションソースを基本的に管理します。初回コンテナ立ち上げ時にgit clone等でリポジトリを取得して開発することを想定しています。
  1. type: bind
  • 開発中にホスト側からコンテナ側にファイルを渡す、もしくは、コンテナ側で作成したファイルをホストにもってくるときに使う用にbindのvolumeも用意しています。

予告

いかがでしょうか? 今回はcompose.ymlと.envについて解説しました。次回は各コンテナのディレクトリの中身について紹介したいと思います。

Discussion