-
Notifications
You must be signed in to change notification settings - Fork 61.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Shave a minute off of Docker image build (#54478)
- Loading branch information
Showing
6 changed files
with
86 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,162 +1,131 @@ | ||
# This Dockerfile is used solely for production deployments to Moda | ||
# For staging deployments, see src/deployments/staging/Dockerfile | ||
# For building this file locally, see src/deployments/production/README.md | ||
# Environment variables are set in the Moda configuration: | ||
# config/moda/configuration/*/env.yaml | ||
|
||
# -------------------------------------------------------------------------------- | ||
# BASE IMAGE | ||
# -------------------------------------------------------------------------------- | ||
# --------------------------------------------------------------- | ||
# BASE STAGE: Install linux dependencies and set up the node user | ||
# --------------------------------------------------------------- | ||
# To update the sha: | ||
# https://github.com/github/gh-base-image/pkgs/container/gh-base-image%2Fgh-base-noble | ||
FROM ghcr.io/github/gh-base-image/gh-base-noble:20250131-172559-g0fd5a2edc AS base | ||
|
||
# Install curl for Node install and determining the early access branch | ||
# Install git for cloning docs-early-access & translations repos | ||
# Install curl for determining the early access branch | ||
RUN apt-get -qq update && apt-get -qq install --no-install-recommends git curl | ||
|
||
# Install Node.js latest LTS | ||
# https://github.com/nodejs/release#release-schedule | ||
# Ubuntu's apt-get install nodejs is _very_ outdated | ||
RUN curl -sL https://deb.nodesource.com/setup_22.x | bash - | ||
RUN apt-get install -y nodejs | ||
RUN node --version | ||
|
||
# This directory is owned by the node user | ||
RUN useradd -ms /bin/bash node | ||
ARG APP_HOME=/home/node/app | ||
RUN mkdir -p $APP_HOME && chown -R node:node $APP_HOME | ||
# Must run as root | ||
RUN apt-get -qq update && apt-get -qq install --no-install-recommends curl git \ | ||
&& curl -sL https://deb.nodesource.com/setup_22.x | bash - \ | ||
&& apt-get install -y nodejs \ | ||
&& node --version | ||
|
||
# Create the node user and home directory | ||
ARG APP_HOME="/home/node/app" # Define in base so all child stages inherit it | ||
RUN useradd -ms /bin/bash node \ | ||
&& mkdir -p $APP_HOME && chown -R node:node $APP_HOME | ||
|
||
# ----------------------------------------------------------------- | ||
# CLONES STAGE: Clone docs-internal, early-access, and translations | ||
# ----------------------------------------------------------------- | ||
FROM base AS clones | ||
USER node:node | ||
WORKDIR $APP_HOME | ||
|
||
# Switch to root to ensure we have permissions to copy, chmod, and install | ||
USER root | ||
|
||
# Copy in build scripts | ||
COPY src/deployments/production/build-scripts/*.sh ./build-scripts/ | ||
|
||
# Make scripts executable | ||
RUN chmod +x build-scripts/*.sh | ||
|
||
# We need to copy over content that will be merged with early-access | ||
COPY content ./content | ||
COPY assets ./assets | ||
COPY data ./data | ||
COPY --chown=node:node content content/ | ||
COPY --chown=node:node assets assets/ | ||
COPY --chown=node:node data data/ | ||
|
||
# Copy in build scripts and make them executable | ||
COPY --chown=node:node --chmod=+x \ | ||
src/deployments/production/build-scripts/*.sh build-scripts/ | ||
|
||
# Use the mounted --secret to: | ||
# - 1. Fetch the docs-internal repo | ||
# - 2. Fetch the docs-early-access repo & override docs-internal with early access content | ||
# - 3. Fetch each translations repo to the repo/translations directory | ||
# We use --mount-type=secret to avoid the secret being copied into the image layers for security | ||
# The secret passed via --secret can only be used in this RUN command | ||
RUN --mount=type=secret,id=DOCS_BOT_PAT_READPUBLICKEY \ | ||
RUN --mount=type=secret,id=DOCS_BOT_PAT_READPUBLICKEY,mode=0444 \ | ||
# We don't cache because Docker can't know if we need to fetch new content from remote repos | ||
echo "Don't cache this step by printing date: $(date)" && \ | ||
. ./build-scripts/fetch-repos.sh | ||
|
||
# Give node user access to the copied content since we cloned as root | ||
RUN chown -R node:node $APP_HOME/content | ||
RUN chown -R node:node $APP_HOME/assets | ||
RUN chown -R node:node $APP_HOME/data | ||
# Give node user access to translations repos | ||
RUN chown -R node:node $APP_HOME/translations | ||
|
||
# Change back to node to make sure we don't run anything as the root user | ||
USER node | ||
|
||
# --------------- | ||
# ALL DEPS Image | ||
# --------------- | ||
FROM base AS all_deps | ||
|
||
ARG APP_HOME=/home/node/app | ||
USER node | ||
# ----------------------------------------- | ||
# DEPENDENCIES STAGE: Install node packages | ||
# ----------------------------------------- | ||
FROM base AS dependencies | ||
USER node:node | ||
WORKDIR $APP_HOME | ||
|
||
# Copy what is needed to run npm ci | ||
COPY --chown=node:node package.json package-lock.json ./ | ||
|
||
RUN npm ci --no-optional --registry https://registry.npmjs.org/ | ||
RUN npm ci --omit=optional --registry https://registry.npmjs.org/ | ||
|
||
# --------------- | ||
# BUILDER Image | ||
# --------------- | ||
FROM all_deps AS builder | ||
|
||
ARG APP_HOME=/home/node/app | ||
USER node | ||
# ----------------------------------------- | ||
# BUILD STAGE: Prepare for production stage | ||
# ----------------------------------------- | ||
FROM base AS build | ||
USER node:node | ||
WORKDIR $APP_HOME | ||
|
||
# Copy what is needed to: | ||
# 1. Build the app | ||
# 2. run warmup-remotejson script | ||
# 3. run precompute-pageinfo script | ||
# Dependencies | ||
COPY --chown=node:node --from=all_deps $APP_HOME/node_modules $APP_HOME/node_modules | ||
# Content with merged early-access content | ||
COPY --chown=node:node --from=base $APP_HOME/data ./data | ||
COPY --chown=node:node --from=base $APP_HOME/assets ./assets | ||
COPY --chown=node:node --from=base $APP_HOME/content ./content | ||
# Source code | ||
COPY --chown=node:node --from=all_deps $APP_HOME/package.json ./ | ||
COPY src ./src | ||
COPY next.config.js ./ | ||
COPY tsconfig.json ./ | ||
|
||
# 1. Build | ||
RUN npm run build | ||
|
||
# 2. Warm up the remotejson cache | ||
RUN npm run warmup-remotejson | ||
|
||
# 3. Precompute the pageinfo cache | ||
RUN npm run precompute-pageinfo -- --max-versions 2 | ||
|
||
# Prune deps for prod image | ||
RUN npm prune --production | ||
|
||
# -------------------------------------------------------------------------------- | ||
# PRODUCTION IMAGE | ||
# -------------------------------------------------------------------------------- | ||
COPY --chown=node:node src src/ | ||
COPY --chown=node:node package.json ./ | ||
COPY --chown=node:node next.config.js ./ | ||
COPY --chown=node:node tsconfig.json ./ | ||
|
||
# From the clones stage | ||
COPY --chown=node:node --from=clones $APP_HOME/data data/ | ||
COPY --chown=node:node --from=clones $APP_HOME/assets assets/ | ||
COPY --chown=node:node --from=clones $APP_HOME/content content/ | ||
COPY --chown=node:node --from=clones $APP_HOME/translations translations/ | ||
|
||
# From the dependencies stage | ||
COPY --chown=node:node --from=dependencies $APP_HOME/node_modules node_modules/ | ||
|
||
# Generate build files | ||
RUN npm run build \ | ||
&& npm run warmup-remotejson \ | ||
&& npm run precompute-pageinfo -- --max-versions 2 \ | ||
&& npm prune --production | ||
|
||
# ------------------------------------------------- | ||
# PRODUCTION STAGE: What will run on the containers | ||
# ------------------------------------------------- | ||
FROM base AS production | ||
|
||
ARG APP_HOME=/home/node/app | ||
USER node | ||
USER node:node | ||
WORKDIR $APP_HOME | ||
|
||
# Copy the content with merged early-access content | ||
COPY --chown=node:node --from=base $APP_HOME/data ./data | ||
COPY --chown=node:node --from=base $APP_HOME/assets ./assets | ||
COPY --chown=node:node --from=base $APP_HOME/content ./content | ||
|
||
# Include cloned translations | ||
COPY --chown=node:node --from=base $APP_HOME/translations ./translations | ||
|
||
# Copy prod dependencies | ||
COPY --chown=node:node --from=builder $APP_HOME/package.json ./ | ||
COPY --chown=node:node --from=builder $APP_HOME/node_modules $APP_HOME/node_modules | ||
|
||
# Copy built artifacts needed at runtime for the server | ||
COPY --chown=node:node --from=builder $APP_HOME/.next $APP_HOME/.next | ||
# Source code | ||
COPY --chown=node:node src src/ | ||
COPY --chown=node:node package.json ./ | ||
COPY --chown=node:node next.config.js ./ | ||
COPY --chown=node:node tsconfig.json ./ | ||
|
||
# Copy cache files generated during build scripts | ||
COPY --chown=node:node --from=builder $APP_HOME/.remotejson-cache ./.remotejson-cache | ||
COPY --chown=node:node --from=builder $APP_HOME/.pageinfo-cache.json.br* ./.pageinfo-cache.json.br | ||
# From clones stage | ||
COPY --chown=node:node --from=clones $APP_HOME/data data/ | ||
COPY --chown=node:node --from=clones $APP_HOME/assets assets/ | ||
COPY --chown=node:node --from=clones $APP_HOME/content content/ | ||
COPY --chown=node:node --from=clones $APP_HOME/translations translations/ | ||
|
||
# Copy only what's needed to run the server | ||
COPY --chown=node:node --from=builder $APP_HOME/src ./src | ||
COPY --chown=node:node --from=builder $APP_HOME/.remotejson-cache ./.remotejson-cache | ||
COPY --chown=node:node --from=builder $APP_HOME/.pageinfo-cache.json.br* ./.pageinfo-cache.json.br | ||
COPY --chown=node:node --from=builder $APP_HOME/next.config.js ./ | ||
COPY --chown=node:node --from=builder $APP_HOME/tsconfig.json ./ | ||
# From dependencies stage (*modified in build stage) | ||
COPY --chown=node:node --from=build $APP_HOME/node_modules node_modules/ | ||
|
||
# - - - | ||
# Environment variables are set in the Moda | ||
# configuration: config/moda/configuration/*/env.yaml | ||
# - - - | ||
# From build stage | ||
COPY --chown=node:node --from=build $APP_HOME/.next .next/ | ||
COPY --chown=node:node --from=build $APP_HOME/.remotejson-cache ./ | ||
COPY --chown=node:node --from=build $APP_HOME/.pageinfo-cache.json.br* ./ | ||
|
||
# This makes it possible to set `--build-arg BUILD_SHA=abc123` | ||
# and it then becomes available as an environment variable in the docker run. | ||
ARG BUILD_SHA | ||
ENV BUILD_SHA=$BUILD_SHA | ||
|
||
# Entrypoint to start the server | ||
# Note: Currently we have to use tsx because we have a mix of `.ts` and `.js` files with multiple import patterns | ||
# Note: Currently we have to use tsx because | ||
# we have a mix of `.ts` and `.js` files with multiple import patterns | ||
CMD ["node_modules/.bin/tsx", "src/frame/server.ts"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.