🪢

pnpm + Turborepo MonorepoをDocker化する最小構成

最近、pnpmとTurborepoで構築したMonorepoをDocker上で動かす機会があり、つまずきも多かったので備忘録として残しておこうと思います

Monorepoの構成例

今回のプロジェクトは以下のようなディレクトリ構造になっています。apps/service-aのDockerfileがビルド対象です。

.
├── apps
│   ├── service-a         # 実際に動かすアプリ
│   ├── service-b         # 別のアプリ
│   └── service-c         # さらに別のアプリ
├── packages
│   ├── utils             # 共通ユーティリティ
│   ├── config            # 共通のeslintやtsconfigなど
│   └── types             # 型定義
├── turbo.json            
├── pnpm-workspace.yaml 
├── package.json          
└── pnpm-lock.yaml

Dockerfile

FROM node:22-bookworm-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable

# 依存関係のインストール
FROM base AS deps
WORKDIR /app
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY packages ./packages
COPY apps/service-a ./apps/service-a
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile

# ビルド
FROM base AS build
WORKDIR /app
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/apps/service-a/node_modules ./apps/service-a/node_modules
COPY --from=deps /app/packages ./packages
COPY apps/service-a ./apps/service-a
RUN pnpm run -r build

# 実行
FROM base AS runner
# curl(ヘルスチェック用)
RUN apt update && apt install -y curl \
    && apt clean && rm -rf /var/lib/apt/lists/*
COPY --from=build /app /app
EXPOSE 3000
WORKDIR /app/apps/service-a
CMD ["pnpm","start"]

詳しい解説

依存関係のインストールをしています

# 依存関係のインストール
FROM base AS deps
WORKDIR /app
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY packages ./packages
COPY apps/service-a ./apps/service-a
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile

COPY packages ./packagesとしているのは、packagesが増えた際に毎度Dockerfileを更新するのが面倒なためです。
RUN --mount=type=cache,id=pnpm,target=/pnpm/storeでキャッシュを効かせてビルドを高速化します。

つまづいた点

.tsbuildinfoの存在

TypeScriptをincrementalproject referencesでビルドしている場合、.tsbuildinfoファイルが生成されます。
これはキャッシュ情報であり、基本的には削除不要ですが、Dockerビルドの際にdistが存在しないのに「ビルド済み」となってしまい、distが存在しないのでpackagesがappsから参照できないということがありました。
.dockerignore.tsbuildinfoを指定しておきましょう。

tsconfig.jsonpathを指定すると、意図しないdistが生成されてしまう

モノレポ構成で tsconfig.jsonpaths を使ってパッケージを参照していたところ、アプリをビルドすると dist/apps/... の中にパッケージのソースファイルが展開されてしまうという現象にハマりました。
そのせいで、
CMD ["pnpm","start"]で指定しているdist/index.jsのパスがずれてしまい、アプリが起動できませんでした。

 "paths": {
      "@Application/types": [
        "../../packages/types/src/index"
      ],
      "@Application/types/*": [
        "../../packages/types/src/*"
      ]
    }

そこで、tsconfig.jsonreferencesを使用しました。

  "references": [
    {
      "path": "../../packages/types"
    }
]

採用情報

架電特化のAI電話SaaSを展開しているnocall.ai株式会社では、エンジニアを積極採用中です!
ジュニア層のエンジニアからリーダー職まで幅広く募集しています。

https://nocall.notion.site/1b1295fd2008809e87efe0a089ef5733?source=copy_link

https://nocall.notion.site/1b1295fd2008808eb10eec3e3a9aecb7?source=copy_link

興味をもっていただいた方はカジュアル面談でぜひお話ししましょう!

https://nocall.notion.site/?pvs=105

Xもフォローしてください!

https://x.com/1MoNo2Prod

nocall株式会社テックブログ

Discussion