From 4748740386b33f11aec39353e8a0015e17f746bd Mon Sep 17 00:00:00 2001 From: Mrugesh Mohapatra <1884376+raisedadead@users.noreply.github.com> Date: Fri, 25 Feb 2022 00:20:24 +0530 Subject: [PATCH] feat(docker): create configs for dockerized setup (#44753) --- client.Dockerfile | 40 -------------------------- docker-compose.api.yml | 8 ------ docker-compose.client.yml | 8 ------ docker/api/Dockerfile | 35 +++++++++++++++++++++++ docker/web/Dockerfile | 28 ++++++++++++++++++ docker/web/serve.json | 60 +++++++++++++++++++++++++++++++++++++++ server.Dockerfile | 26 ----------------- 7 files changed, 123 insertions(+), 82 deletions(-) delete mode 100644 client.Dockerfile delete mode 100644 docker-compose.api.yml delete mode 100644 docker-compose.client.yml create mode 100644 docker/api/Dockerfile create mode 100644 docker/web/Dockerfile create mode 100644 docker/web/serve.json delete mode 100644 server.Dockerfile diff --git a/client.Dockerfile b/client.Dockerfile deleted file mode 100644 index a4eeb93b0ac..00000000000 --- a/client.Dockerfile +++ /dev/null @@ -1,40 +0,0 @@ -FROM node:16-buster AS builder - -# this is a bit clunky, perhaps there's a more concise way of passing in build -# arguments -ARG FREECODECAMP_NODE_ENV -ARG HOME_LOCATION -ARG API_LOCATION -ARG FORUM_LOCATION -ARG NEWS_LOCATION -ARG CLIENT_LOCALE -ARG CURRICULUM_LOCALE -ARG ALGOLIA_APP_ID -ARG ALGOLIA_API_KEY -ARG STRIPE_PUBLIC_KEY -ARG PAYPAL_CLIENT_ID -ARG DEPLOYMENT_ENV -ARG SHOW_UPCOMING_CHANGES - -# node images create a non-root user that we can use -USER node -WORKDIR /home/node/build -COPY --chown=node:node . . -RUN npm ci -# we don't need to separately run create:config, since it gets called as part of -# build:client -RUN npm run build:client - -WORKDIR /home/node/config -RUN git clone https://github.com/freeCodeCamp/client-config.git client - -FROM node:16-alpine -RUN npm i -g serve -USER node -WORKDIR /home/node -COPY --from=builder /home/node/build/client/public/ client/public -COPY --from=builder /home/node/config/client/serve.json client -COPY --from=builder /home/node/config/client/www/ client - -ENTRYPOINT ["serve", "-c", "../serve.json", "client/public"] -CMD ["-l", "8000"] diff --git a/docker-compose.api.yml b/docker-compose.api.yml deleted file mode 100644 index 7fe731e24ca..00000000000 --- a/docker-compose.api.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: '3.7' -services: - api: - env_file: .env - image: fcc_api - restart: unless-stopped - ports: - - '${API_PORT:-3000}:${API_PORT:-3000}' diff --git a/docker-compose.client.yml b/docker-compose.client.yml deleted file mode 100644 index 5e65e3ff170..00000000000 --- a/docker-compose.client.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: '3.7' -services: - client: - image: fcc_client - restart: unless-stopped - ports: - - '${CLIENT_PORT:-8000}:${CLIENT_PORT:-8000}' - command: ['-l', '${CLIENT_PORT:-8000}'] diff --git a/docker/api/Dockerfile b/docker/api/Dockerfile new file mode 100644 index 00000000000..e0df353254b --- /dev/null +++ b/docker/api/Dockerfile @@ -0,0 +1,35 @@ +FROM node:16-buster AS builder +# Install doppler CLI +RUN (curl -Ls --tlsv1.2 --proto "=https" --retry 3 https://cli.doppler.com/install.sh) | sh -s -- --verify-signature +# node images create a non-root user that we can use +USER node +WORKDIR /home/node/build +COPY --chown=node:node . . +# Pass `DOPPLER_TOKEN` at build time to create an encrypted snapshot for high-availability +ARG DOPPLER_TOKEN +RUN \ + doppler secrets download doppler.encrypted.json &&\ + npm ci --no-progress --ignore-scripts &&\ + doppler run --fallback=doppler.encrypted.json --command="npm run create:config" &&\ + doppler run --fallback=doppler.encrypted.json --command="npm run build:curriculum" &&\ + doppler run --fallback=doppler.encrypted.json --command="npm run build:server" + +FROM node:16-alpine as depends +USER node +WORKDIR /home/node/depends +COPY --chown=node:node . . +RUN npm ci --production --workspace=api-server --include-workspace-root --no-progress --ignore-scripts + +FROM node:16-alpine +RUN npm i -g pm2@4 +USER node +WORKDIR /home/node/api +COPY --from=builder --chown=node:node /home/node/build/api-server/lib/ api-server/lib/ +COPY --from=builder --chown=node:node /home/node/build/utils/ utils/ +COPY --from=builder --chown=node:node /home/node/build/config/ config/ +COPY --from=depends --chown=node:node /home/node/depends/api-server/node_modules/ api-server/node_modules/ +COPY --from=depends --chown=node:node /home/node/depends/node_modules/ node_modules/ +WORKDIR /home/node/api/api-server +CMD ["pm2-runtime", "./lib/production-start.js"] + +# TODO: don't copy mocks/fixtures diff --git a/docker/web/Dockerfile b/docker/web/Dockerfile new file mode 100644 index 00000000000..3ddefff7845 --- /dev/null +++ b/docker/web/Dockerfile @@ -0,0 +1,28 @@ +FROM node:16-buster AS builder +# Install doppler CLI +RUN (curl -Ls --tlsv1.2 --proto "=https" --retry 3 https://cli.doppler.com/install.sh) | sh -s -- --verify-signature +# node images create a non-root user that we can use +USER node +WORKDIR /home/node/build +COPY --chown=node:node . . +# Pass `DOPPLER_TOKEN` at build time to create an encrypted snapshot for high-availability +ARG DOPPLER_TOKEN +RUN \ + doppler secrets download doppler.encrypted.json &&\ + # Install and donot ignore the scripts for sharp + npm ci --no-progress --ignore-scripts=false &&\ + doppler run --fallback=doppler.encrypted.json --command="npm run create:config" &&\ + doppler run --fallback=doppler.encrypted.json --command="npm run build:curriculum" &&\ + doppler run --fallback=doppler.encrypted.json --command="npm run build:client" + +# Use a lightweight image for the serving the files +FROM node:16-alpine +RUN npm i -g serve@13 + +USER node +WORKDIR /home/node +COPY --from=builder /home/node/build/client/public/ client/public +COPY --from=builder /home/node/build/docker/web/serve.json client/serve.json + +EXPOSE 8000 +CMD ["serve", "--config", "client/serve.json", "--cors", "--no-clipboard", "--no-port-switching", "-p", "8000", "client/public"] diff --git a/docker/web/serve.json b/docker/web/serve.json new file mode 100644 index 00000000000..fb5c32a25cd --- /dev/null +++ b/docker/web/serve.json @@ -0,0 +1,60 @@ +{ + "directoryListing": false, + "trailingSlash": false, + "rewrites": [ + { + "source": "/certification/**", + "destination": "/certification/index.html" + } + ], + "redirects": [ + { + "source": "/challenges/:superblock?/:block?/:challenge?", + "destination": "/learn/:superblock?/:block?/:challenge?" + }, + { + "source": "/learn/apis-and-microservices/apis-and-microservices-projects", + "destination": "/learn/back-end-development-and-apis/back-end-development-and-apis-projects" + }, + { + "source": "/learn/apis-and-microservices/apis-and-microservices-projects/:challenge", + "destination": "/learn/back-end-development-and-apis/back-end-development-and-apis-projects/:challenge" + }, + { + "source": "/learn/apis-and-microservices/:block?/:challenge?", + "destination": "/learn/back-end-development-and-apis/:block?/:challenge?" + }, + { + "source": "/certification/:username/apis-and-microservices", + "destination": "/certification/:username/back-end-development-and-apis" + }, + { + "source": "/learn/front-end-libraries/front-end-libraries-projects", + "destination": "/learn/front-end-development-libraries/front-end-development-libraries-projects" + }, + { + "source": "/learn/front-end-libraries/front-end-libraries-projects/:challenge", + "destination": "/learn/front-end-development-libraries/front-end-development-libraries-projects/:challenge" + }, + { + "source": "/learn/front-end-libraries/:block?/:challenge?", + "destination": "/learn/front-end-development-libraries/:block?/:challenge?" + }, + { + "source": "/certification/:username/front-end-libraries", + "destination": "/certification/:username/front-end-development-libraries" + }, + { + "source": "/learn/javascript-algorithms-and-data-structures/es6/explore-differences-between-the-var-and-let-keywords", + "destination": "/learn/javascript-algorithms-and-data-structures/basic-javascript/explore-differences-between-the-var-and-let-keywords" + }, + { + "source": "/learn/javascript-algorithms-and-data-structures/es6/declare-a-read-only-variable-with-the-const-keyword", + "destination": "/learn/javascript-algorithms-and-data-structures/basic-javascript/declare-a-read-only-variable-with-the-const-keyword" + }, + { + "source": "/learn/responsive-web-design/applied-visual-design/adjust-the-size-of-a-header-versus-a-paragraph-tag", + "destination": "/learn/responsive-web-design/applied-visual-design/adjust-the-size-of-a-heading-element-versus-a-paragraph-element" + } + ] +} diff --git a/server.Dockerfile b/server.Dockerfile deleted file mode 100644 index 7c9d5d87baf..00000000000 --- a/server.Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM node:16-alpine as builder -USER node -WORKDIR /home/node/build -COPY --chown=node:node . . - -RUN npm ci --no-progress -RUN npm run build:curriculum -RUN npm run build:server - -FROM node:16-alpine -USER node -WORKDIR /home/node/api -# get and install deps -COPY --from=builder --chown=node:node /home/node/build/package.json /home/node/build/package-lock.json ./ -COPY --from=builder --chown=node:node /home/node/build/api-server/package.json api-server/ -RUN npm ci --production -w=api-server --include-workspace-root --no-progress --ignore-scripts \ - && npm cache clean --force -COPY --from=builder --chown=node:node /home/node/build/api-server/lib/ api-server/lib/ -COPY --from=builder --chown=node:node /home/node/build/utils/ utils/ -COPY --from=builder --chown=node:node /home/node/build/config/ config/ - -WORKDIR /home/node/api/api-server - -CMD ["npm", "start"] - -# TODO: don't copy mocks/fixtures