以下をcloneさせていただき、環境構築していきます。
compose.ymlの設定
version: '3'
services:
  db:
    container_name: postgres
    image: postgres:15.2
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_HOST_AUTH_METHOD: trust
  backend:
    container_name: backend
    build: ./backend
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - ./backend:/myapp/backend
    depends_on:
      - db
  frontend:
    container_name: frontend
    image: "node:18.15.0-alpine"
    user: "node"
    working_dir: /myapp/frontend
    volumes:
      - ./frontend:/myapp/frontend
    command: "yarn dev"
    depends_on:
      - backend
  yaichi:
    container_name: yaichi
    image: mtsmfm/yaichi:1.8.1
    ports:
      - 80:3000
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
imageのバージョンだけ変更しています。
V3のリファレンスも見て、記述に誤りが無いか確認済。
※nodeの安定版が18.15.0なので、npmにてアップデートします(2023/3/23)
$ sudo npm install -g n
$ n -V
v9.0.1
$ n --stable
18.15.0
$ sudo n stable
  installing : node-v18.15.0
       mkdir : /usr/local/n/versions/node/18.15.0
       fetch : https://nodejs.org/dist/v18.15.0/node-v18.15.0-darwin-x64.tar.xz
     copying : node/18.15.0
   installed : v18.15.0 (with npm 9.5.0)
$ node -v
v18.15.0
用語の説明
version
docker-composeで使用するバージョンを定義しています。バージョンごとにComposeFileの書き方が異なります。
services
アプリケーションを動かすための要素です。以降、ネストして記述していきます。ここではdb backend frontend yaichi の4つをserviceとして定義しています。
db
DockerHubにアップされている既存のDockerImageを使います。Railsのようなウェブアプリケーションについては、ディレクトリを指定します。
container_name
指定したコンテナ名(postgres)が明示できます。
image
Dockerコンテナを起動するために必要なコンテナイメージを指定するために使用されます。postgres:15.2など、DockerHubを参照にコンテナイメージを指定します。
volumes
マウントする設定ファイルのパスを指定します。DockerEngineの管理下にvolumeを確保してそこにファイルを管理します。- ./tmp/db:/var/lib/postgresql/data
マウントについては以下を参考にさせていただきました↓
environment
パスワードなど、DBの環境変数設定を指定します。
POSTGRES_HOST_AUTH_METHODがtrustならパスワードを設定しなくてもpsqlに接続できます。
backend
バックエンドのContainerの内容を記載します。バックエンドのアプリは、DockerfileからImageをbuildするところから始まります。build: ./backend
command
コマンドを指定できます。Dockerfileよりも優先されます。
depends_on
Docker Compose の各サービスに対して設定できる項目です。 depends_onを使うとサービス間の依存関係を指定できます。この場合backendをdbに依存させています。
frontend
フロントエンドのContainerの内容を記載します。
user
デフォルトログインユーザ(node)を指定します。
working_dir
デフォルトの作業用ディレクトリを指定します/myapp/frontend
Serviceの設定項目はかなりたくさんあるようなので、今後、他にも使用していきたいと思います。
Dockerfileの設定(backend)
リファレンスにベストプラクティスの記載があったので目を通します。
FROM ruby:3.2.1
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir -p /myapp/backend
WORKDIR /myapp/backend
COPY Gemfile /myapp/backend/Gemfile
COPY Gemfile.lock /myapp/backend/Gemfile.lock
RUN bundle install
COPY . /myapp/backend
ENV ENTRYKIT_VERSION 0.4.0
RUN wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && mv entrykit /bin/entrykit \
  && chmod +x /bin/entrykit \
  && entrykit --symlink
ENTRYPOINT [ \
  "prehook", "ruby -v", "--", \
  "prehook", "/myapp/backend/prehook", "--"]
FROM
ベースイメージを指定します。
RUN
ベースイメージに対して追加で行う処理です。
apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
apt-getコマンドを使用し、quietモードupdateします。quietモードは進捗状況を表示しません。
同時に、パッケージをインストールしています。
WORKDIR
操作するディレクトリのパスを指定します。
COPY
コンテナに指定したフォルダやファイルをコピーします。
ENV
環境変数を設定します。
以下の記述でEntrykitを使用しています。Entrykitは、コンテナ内のプロセス起動時に便利な軽量initシステムです。Dockerの起動コマンドに Entrykitを使うと、起動時にテンプレートファイルを元に設定ファイルをレンダリングしてくれたり、メインプロセスの前にコマンドを実行できたりします。
ENV ENTRYKIT_VERSION 0.4.0
RUN wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && mv entrykit /bin/entrykit \
  && chmod +x /bin/entrykit \
  && entrykit --symlink
詳細は以下の記事を参考にしました。
ENTRYPOINT
コンテナ起動時に何のコマンドを実行するか定義します。
prehook を使用するとメインプロセス起動前にコマンドを複数実行できます。
ENTRYPOINT [ \
  "prehook", "ruby -v", "--", \
  "prehook", "/myapp/backend/prehook", "--"]
package.jsonの設定
※以下、詳細は別記事を書きます。
{
  "name": "frontend",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "build": "webpack --mode production",
    "dev": "webpack-dev-server",
    "test": "jest",
    "typecheck": "tsc -p . --noEmit"
  },
  "devDependencies": {
    "@types/jest": "^29.5.0",
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "css-loader": "^6.7.3",
    "html-webpack-plugin": "^4.5.2",
    "jest": "^29.5.0",
    "style-loader": "^3.3.2",
    "ts-jest": "^29.0.5",
    "ts-loader": "^9.4.2",
    "typescript": "^5.0.2",
    "webpack": "^4.46.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.13.1"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}
tsconfig.json
{
  "compilerOptions": {
    "target": "es2019",
    "module": "esnext",
    "moduleResolution": "node",
    "jsx": "react",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  }
}
webpack.config.js
const path = require("path");
const HTMLPlugin = require("html-webpack-plugin");
module.exports = {
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: {
          loader: "ts-loader",
          options: {
            transpileOnly: true,
          },
        },
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"]
      },
    ],
  },
  resolve: {
    extensions: [".js", ".ts", ".tsx", ".json", ".mjs", ".wasm"],
  },
  plugins: [
    new HTMLPlugin({
      template: path.join(__dirname, "src/index.html"),
    }),
  ],
  devServer: {
    host: '0.0.0.0',
    port: 3000,
    disableHostCheck: true,
  },
};
立ち上げ
Setup
$ docker-compose run frontend yarn
$ docker-compose run backend bin/rails db:create db:migrate
Start
$ docker-compose up -d
Open frontend
$ open http://localhost:80
Check backend API
$ curl -H 'Host: backend.localhost' http://localhost/greetings/hello
