Skip to content

Commit eed068d

Browse files
authored
Shave a minute off of Docker image build (#54478)
1 parent ae3589c commit eed068d

File tree

6 files changed

+86
-172
lines changed

6 files changed

+86
-172
lines changed

Diff for: .dockerignore

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,4 @@ node_modules/
88
tests/
99
# Folder is cloned during the preview + prod workflows, the assets are merged into other locations for use before the build
1010
docs-early-access/
11-
# During the preview deploy untrusted user code may be cloned into this directory
12-
user-code/
11+
README.md

Diff for: .gitignore

-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ broken_links.md
2727
# still have these files on their disk.
2828
lib/redirects/.redirects-cache*.json
2929

30-
# During the preview deploy untrusted user code may be cloned into this directory
31-
# We ignore it from git to keep things deterministic
32-
user-code/
3330

3431
# Logs from scripts
3532
*/logs/

Diff for: Dockerfile

+83-114
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,131 @@
11
# This Dockerfile is used solely for production deployments to Moda
2-
# For staging deployments, see src/deployments/staging/Dockerfile
32
# For building this file locally, see src/deployments/production/README.md
3+
# Environment variables are set in the Moda configuration:
4+
# config/moda/configuration/*/env.yaml
45

5-
# --------------------------------------------------------------------------------
6-
# BASE IMAGE
7-
# --------------------------------------------------------------------------------
6+
# ---------------------------------------------------------------
7+
# BASE STAGE: Install linux dependencies and set up the node user
8+
# ---------------------------------------------------------------
89
# To update the sha:
910
# https://github.com/github/gh-base-image/pkgs/container/gh-base-image%2Fgh-base-noble
1011
FROM ghcr.io/github/gh-base-image/gh-base-noble:20250131-172559-g0fd5a2edc AS base
1112

13+
# Install curl for Node install and determining the early access branch
1214
# Install git for cloning docs-early-access & translations repos
13-
# Install curl for determining the early access branch
14-
RUN apt-get -qq update && apt-get -qq install --no-install-recommends git curl
15-
1615
# Install Node.js latest LTS
1716
# https://github.com/nodejs/release#release-schedule
1817
# Ubuntu's apt-get install nodejs is _very_ outdated
19-
RUN curl -sL https://deb.nodesource.com/setup_22.x | bash -
20-
RUN apt-get install -y nodejs
21-
RUN node --version
22-
23-
# This directory is owned by the node user
24-
RUN useradd -ms /bin/bash node
25-
ARG APP_HOME=/home/node/app
26-
RUN mkdir -p $APP_HOME && chown -R node:node $APP_HOME
18+
# Must run as root
19+
RUN apt-get -qq update && apt-get -qq install --no-install-recommends curl git \
20+
&& curl -sL https://deb.nodesource.com/setup_22.x | bash - \
21+
&& apt-get install -y nodejs \
22+
&& node --version
23+
24+
# Create the node user and home directory
25+
ARG APP_HOME="/home/node/app" # Define in base so all child stages inherit it
26+
RUN useradd -ms /bin/bash node \
27+
&& mkdir -p $APP_HOME && chown -R node:node $APP_HOME
28+
29+
# -----------------------------------------------------------------
30+
# CLONES STAGE: Clone docs-internal, early-access, and translations
31+
# -----------------------------------------------------------------
32+
FROM base AS clones
33+
USER node:node
2734
WORKDIR $APP_HOME
2835

29-
# Switch to root to ensure we have permissions to copy, chmod, and install
30-
USER root
31-
32-
# Copy in build scripts
33-
COPY src/deployments/production/build-scripts/*.sh ./build-scripts/
34-
35-
# Make scripts executable
36-
RUN chmod +x build-scripts/*.sh
37-
3836
# We need to copy over content that will be merged with early-access
39-
COPY content ./content
40-
COPY assets ./assets
41-
COPY data ./data
37+
COPY --chown=node:node content content/
38+
COPY --chown=node:node assets assets/
39+
COPY --chown=node:node data data/
40+
41+
# Copy in build scripts and make them executable
42+
COPY --chown=node:node --chmod=+x \
43+
src/deployments/production/build-scripts/*.sh build-scripts/
4244

4345
# Use the mounted --secret to:
4446
# - 1. Fetch the docs-internal repo
4547
# - 2. Fetch the docs-early-access repo & override docs-internal with early access content
4648
# - 3. Fetch each translations repo to the repo/translations directory
4749
# We use --mount-type=secret to avoid the secret being copied into the image layers for security
4850
# The secret passed via --secret can only be used in this RUN command
49-
RUN --mount=type=secret,id=DOCS_BOT_PAT_READPUBLICKEY \
51+
RUN --mount=type=secret,id=DOCS_BOT_PAT_READPUBLICKEY,mode=0444 \
5052
# We don't cache because Docker can't know if we need to fetch new content from remote repos
5153
echo "Don't cache this step by printing date: $(date)" && \
5254
. ./build-scripts/fetch-repos.sh
5355

54-
# Give node user access to the copied content since we cloned as root
55-
RUN chown -R node:node $APP_HOME/content
56-
RUN chown -R node:node $APP_HOME/assets
57-
RUN chown -R node:node $APP_HOME/data
58-
# Give node user access to translations repos
59-
RUN chown -R node:node $APP_HOME/translations
60-
61-
# Change back to node to make sure we don't run anything as the root user
62-
USER node
63-
64-
# ---------------
65-
# ALL DEPS Image
66-
# ---------------
67-
FROM base AS all_deps
68-
69-
ARG APP_HOME=/home/node/app
70-
USER node
56+
# -----------------------------------------
57+
# DEPENDENCIES STAGE: Install node packages
58+
# -----------------------------------------
59+
FROM base AS dependencies
60+
USER node:node
7161
WORKDIR $APP_HOME
7262

7363
# Copy what is needed to run npm ci
7464
COPY --chown=node:node package.json package-lock.json ./
7565

76-
RUN npm ci --no-optional --registry https://registry.npmjs.org/
66+
RUN npm ci --omit=optional --registry https://registry.npmjs.org/
7767

78-
# ---------------
79-
# BUILDER Image
80-
# ---------------
81-
FROM all_deps AS builder
82-
83-
ARG APP_HOME=/home/node/app
84-
USER node
68+
# -----------------------------------------
69+
# BUILD STAGE: Prepare for production stage
70+
# -----------------------------------------
71+
FROM base AS build
72+
USER node:node
8573
WORKDIR $APP_HOME
8674

87-
# Copy what is needed to:
88-
# 1. Build the app
89-
# 2. run warmup-remotejson script
90-
# 3. run precompute-pageinfo script
91-
# Dependencies
92-
COPY --chown=node:node --from=all_deps $APP_HOME/node_modules $APP_HOME/node_modules
93-
# Content with merged early-access content
94-
COPY --chown=node:node --from=base $APP_HOME/data ./data
95-
COPY --chown=node:node --from=base $APP_HOME/assets ./assets
96-
COPY --chown=node:node --from=base $APP_HOME/content ./content
9775
# Source code
98-
COPY --chown=node:node --from=all_deps $APP_HOME/package.json ./
99-
COPY src ./src
100-
COPY next.config.js ./
101-
COPY tsconfig.json ./
102-
103-
# 1. Build
104-
RUN npm run build
105-
106-
# 2. Warm up the remotejson cache
107-
RUN npm run warmup-remotejson
108-
109-
# 3. Precompute the pageinfo cache
110-
RUN npm run precompute-pageinfo -- --max-versions 2
111-
112-
# Prune deps for prod image
113-
RUN npm prune --production
114-
115-
# --------------------------------------------------------------------------------
116-
# PRODUCTION IMAGE
117-
# --------------------------------------------------------------------------------
76+
COPY --chown=node:node src src/
77+
COPY --chown=node:node package.json ./
78+
COPY --chown=node:node next.config.js ./
79+
COPY --chown=node:node tsconfig.json ./
80+
81+
# From the clones stage
82+
COPY --chown=node:node --from=clones $APP_HOME/data data/
83+
COPY --chown=node:node --from=clones $APP_HOME/assets assets/
84+
COPY --chown=node:node --from=clones $APP_HOME/content content/
85+
COPY --chown=node:node --from=clones $APP_HOME/translations translations/
86+
87+
# From the dependencies stage
88+
COPY --chown=node:node --from=dependencies $APP_HOME/node_modules node_modules/
89+
90+
# Generate build files
91+
RUN npm run build \
92+
&& npm run warmup-remotejson \
93+
&& npm run precompute-pageinfo -- --max-versions 2 \
94+
&& npm prune --production
95+
96+
# -------------------------------------------------
97+
# PRODUCTION STAGE: What will run on the containers
98+
# -------------------------------------------------
11899
FROM base AS production
119-
120-
ARG APP_HOME=/home/node/app
121-
USER node
100+
USER node:node
122101
WORKDIR $APP_HOME
123102

124-
# Copy the content with merged early-access content
125-
COPY --chown=node:node --from=base $APP_HOME/data ./data
126-
COPY --chown=node:node --from=base $APP_HOME/assets ./assets
127-
COPY --chown=node:node --from=base $APP_HOME/content ./content
128-
129-
# Include cloned translations
130-
COPY --chown=node:node --from=base $APP_HOME/translations ./translations
131-
132-
# Copy prod dependencies
133-
COPY --chown=node:node --from=builder $APP_HOME/package.json ./
134-
COPY --chown=node:node --from=builder $APP_HOME/node_modules $APP_HOME/node_modules
135-
136-
# Copy built artifacts needed at runtime for the server
137-
COPY --chown=node:node --from=builder $APP_HOME/.next $APP_HOME/.next
103+
# Source code
104+
COPY --chown=node:node src src/
105+
COPY --chown=node:node package.json ./
106+
COPY --chown=node:node next.config.js ./
107+
COPY --chown=node:node tsconfig.json ./
138108

139-
# Copy cache files generated during build scripts
140-
COPY --chown=node:node --from=builder $APP_HOME/.remotejson-cache ./.remotejson-cache
141-
COPY --chown=node:node --from=builder $APP_HOME/.pageinfo-cache.json.br* ./.pageinfo-cache.json.br
109+
# From clones stage
110+
COPY --chown=node:node --from=clones $APP_HOME/data data/
111+
COPY --chown=node:node --from=clones $APP_HOME/assets assets/
112+
COPY --chown=node:node --from=clones $APP_HOME/content content/
113+
COPY --chown=node:node --from=clones $APP_HOME/translations translations/
142114

143-
# Copy only what's needed to run the server
144-
COPY --chown=node:node --from=builder $APP_HOME/src ./src
145-
COPY --chown=node:node --from=builder $APP_HOME/.remotejson-cache ./.remotejson-cache
146-
COPY --chown=node:node --from=builder $APP_HOME/.pageinfo-cache.json.br* ./.pageinfo-cache.json.br
147-
COPY --chown=node:node --from=builder $APP_HOME/next.config.js ./
148-
COPY --chown=node:node --from=builder $APP_HOME/tsconfig.json ./
115+
# From dependencies stage (*modified in build stage)
116+
COPY --chown=node:node --from=build $APP_HOME/node_modules node_modules/
149117

150-
# - - -
151-
# Environment variables are set in the Moda
152-
# configuration: config/moda/configuration/*/env.yaml
153-
# - - -
118+
# From build stage
119+
COPY --chown=node:node --from=build $APP_HOME/.next .next/
120+
COPY --chown=node:node --from=build $APP_HOME/.remotejson-cache ./
121+
COPY --chown=node:node --from=build $APP_HOME/.pageinfo-cache.json.br* ./
154122

155123
# This makes it possible to set `--build-arg BUILD_SHA=abc123`
156124
# and it then becomes available as an environment variable in the docker run.
157125
ARG BUILD_SHA
158126
ENV BUILD_SHA=$BUILD_SHA
159127

160128
# Entrypoint to start the server
161-
# Note: Currently we have to use tsx because we have a mix of `.ts` and `.js` files with multiple import patterns
129+
# Note: Currently we have to use tsx because
130+
# we have a mix of `.ts` and `.js` files with multiple import patterns
162131
CMD ["node_modules/.bin/tsx", "src/frame/server.ts"]

Diff for: contributing/deployments.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
Staging and production deployments are automated by a deployer service created and maintained by @github/docs-engineering.
44

5-
### Preview deployments
5+
### Review deployments
66

7-
When a pull request contains only content changes, it can be previewed without a deployment. Code changes will require a deployment. GitHub Staff can deploy such a PR to a staging environment.
7+
TBD
88

99
### Production deployments
1010

Diff for: package-lock.json

-45
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)