Skip to content

Commit

Permalink
chore(docker-build): simplify the docker build process and reduce doc…
Browse files Browse the repository at this point in the history
…ker image size (#9447)

* Dockerfile basic created. Improvements added to reduce build time and size (down from 795MB to 445MB, depending on systemtap). Readme reduced, removing the old process used to build the image.

* basic-env file using a RethinkDB database name that is clearly dedicated to the building proces.

* Readme improved to run all three components

* Unused dockerfiles removed. Docker entrypoint renamed. Docker Readme adapted

* Legacy build kept in both dockerfile and env file. Readme adapted to use the new basic image. Build GH workflow adapted to use the new basic.dockerfile.
  • Loading branch information
rafaelromcar-parabol authored Feb 29, 2024
1 parent 052acd1 commit 5e356c2
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 295 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
env:
PARABOL_DOCKERFILE: ./docker/parabol-ubi/docker-build/dockerfiles/pipeline.dockerfile
PARABOL_DOCKERFILE: ./docker/parabol-ubi/docker-build/dockerfiles/basic.dockerfile
PARABOL_BUILD_ENV_PATH: docker/parabol-ubi/docker-build/environments/pipeline
jobs:
build:
Expand Down Expand Up @@ -114,7 +114,6 @@ jobs:
context: .
build-args: |
"_NODE_VERSION=${{ env.NODE_VERSION }}"
"_SECURITY_ENABLED=true"
push: true
tags: |
"${{ secrets.GCP_AR_PARABOL_DEV }}:${{github.sha}}"
Expand Down
82 changes: 22 additions & 60 deletions docker/parabol-ubi/docker-build/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
# docker-image-parabol

This repo was created to build a **secure** Parabol base image that is **agnostic to configuration and can be used anywhere**. Once an image is built, it can be pushed to any repository.
This repo was created to build a Parabol base image that is **agnostic to configuration and can be used anywhere**. Once an image is built, it can be pushed to any repository.

There are two possible ways to build the application:

- **Standard build:** duild using local files, using the same Dockerfile and process used in our Docker Build pipeline.
- **Build from git:** build using a simplified process that downloads the source code and builds from scratch.

The processes are different and the details of it can be checked in both dockerfiles.

## Standard build

### Requirements
## Requirements

Required:

Expand All @@ -21,37 +12,35 @@ Required:

Recommended:

- jq installed.
- [jq](https://jqlang.github.io/jq/) installed. It is used to get the version of the application.

### Variables
## Variables

| Name | Description | Possible values | Recommended value |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------- |
| `postgresql_tag` | PostgreSQL version from the [Docker image](https://hub.docker.com/_/postgres) | `Any tag` | `15.4` |
| `rethinkdb_tag` | RethinkDB version from the [Docker image](https://hub.docker.com/_/rethinkdb) | `Any tag` | `2.4.2` |
| `redis_tag` | Redis version from the [Docker image](https://hub.docker.com/_/redis) | `Any tag` | `7.0-alpine` |
| `_BUILD_ENV_PATH` | File `.env` used by the application during the build process | `Relative path from the root level of the repository` | `docker/parabol-ubi/docker-build/environments/pipeline` |
| `_BUILD_ENV_PATH` | File `.env` used by the application during the build process | `Relative path from the root level of the repository` | `docker/parabol-ubi/docker-build/environments/basic-env` |
| `_NODE_VERSION` | Node version, used by Docker to use the Docker image node:\_NODE_VERSION as base image to build | `Same as in root package.json` | |
| `_DOCKERFILE` | Dockerfile used to build the image | `Relative path from the root level of the repository` | `./docker/parabol-ubi/docker-build/dockerfiles/pipeline.dockerfile` |
| `_SECURITY_ENABLED` | Enable or disable security configurations. It will add some MBs to the final image, but it will produce a secured image | `true/false` | `true` |
| `_DOCKERFILE` | Dockerfile used to build the image | `Relative path from the root level of the repository` | `./docker/parabol-ubi/docker-build/dockerfiles/basic.dockerfile` |
| `_DOCKER_REPOSITORY` | The destination repository | `String` | `parabol` |
| `_DOCKER_TAG` | Tag for the produced image | `String` | |

Example of variables:

```commandLine
export postgresql_tag=15.4-alpine; \
export postgresql_tag=15.4; \
export rethinkdb_tag=2.4.2; \
export redis_tag=7.0-alpine; \
export _BUILD_ENV_PATH=docker/parabol-ubi/docker-build/environments/pipeline; \
export _BUILD_ENV_PATH=docker/parabol-ubi/docker-build/environments/basic-env; \
export _NODE_VERSION=$(jq -r -j '.engines.node|ltrimstr("^")' package.json); \
export _DOCKERFILE=./docker/parabol-ubi/docker-build/dockerfiles/pipeline.dockerfile; \
export _SECURITY_ENABLED=true; \
export _DOCKERFILE=./docker/parabol-ubi/docker-build/dockerfiles/basic.dockerfile; \
export _DOCKER_REPOSITORY=parabol; \
export _DOCKER_TAG=test-image
```

### Building the image
## Building the image

The application must be already built locally using the command `yarn build --no-deps` mode.

Expand Down Expand Up @@ -90,7 +79,7 @@ yarn build --no-deps
- **Build the docker image:**

```commandLine
docker build -t $_DOCKER_REPOSITORY:$_DOCKER_TAG -f $_DOCKERFILE --build-arg _NODE_VERSION=$_NODE_VERSION --build-arg _SECURITY_ENABLED=$_SECURITY_ENABLED .
docker build -t $_DOCKER_REPOSITORY:$_DOCKER_TAG -f $_DOCKERFILE --build-arg _NODE_VERSION=$_NODE_VERSION .
```

> Some build tips
Expand Down Expand Up @@ -119,57 +108,30 @@ It will produce a Docker image tagged as `${_DOCKER_REPOSITORY}:${_DOCKER_TAG}`.
docker images $_DOCKER_REPOSITORY:$_DOCKER_TAG
```

## Build from git

This version of the Dockerfile downloads the application during the docker build process and differs in other
## Run the application using a docker image

Modify the version export below e.g. update vX.X.X and run the export command and the docker command. The command below will create a temp postgres container (this allows pgtype files to be generated) and then build the docker image with a temp .env file.
_Assumes redis, rethinkdb, and postgres already running to have operational stack._

- Change `environments/buildenv` connection string names form container names to localhost for local image build.
- Use `_PARABOL_GIT_REF` to select the reference in Parabol's Git repository. It can be any tag or branch, but it is recommended to use released tags as `v6.69.0`. By default it buils a local image using only `parabol` as repository.
- Use `_DOCKER_REPOSITORY` to build the image for a remote repository (ex: `gcr.io/parabol-proving-ground/parabol`)
- Use `_DOCKER_TAG` to define the tag for the new image.
The commands below will start a Parabol container on the target tag specified in \_DOCKER_TAG export. It will volume mount a .env in your current working directory to the container, so you can pass in any .env in your current working directory.

```commandLine
export postgresql_tag=15.4-alpine; \
export rethinkdb_tag=2.4.2; \
export redis_tag=7.0-alpine; \
export _BUILD_ENV_PATH=environments/local-buildenv \
export _NODE_VERSION=20.11.0 \
export _DOCKER_REPOSITORY=parabol \
export _PARABOL_GIT_REF=vX.X.X \
export _DOCKER_TAG=vX.X.X
```
For a more detailed how-to deploy Parabol, please go to the section [docker-host-st](https://github.com/ParabolInc/parabol/tree/master/docker/parabol-ubi/docker-host-st)

Now you can build the image
- Run the PreDeploy script

```commandLine
docker run --name temp-postgres --network=host -e POSTGRES_PASSWORD=temppassword -e POSTGRES_USER=tempuser -e POSTGRES_DB=tempdb -d -p 5432:5432 postgres:${postgresql_tag} && \
docker run --name temp-rethinkdb --network=host -d -p 28015:28015 -p 29015:29015 -p 8080:8080 rethinkdb:${rethinkdb_tag} && \
docker run --name temp-redis --network=host -d -p 6379:6379 redis:${redis_tag} && \
docker build --no-cache --network=host -t ${_DOCKER_REPOSITORY}:${_DOCKER_TAG} -f ./dockerfiles/parabol.dockerfile --build-arg _PARABOL_GIT_REF=${_PARABOL_GIT_REF} --build-arg _NODE_VERSION=$_NODE_VERSION --build-arg _BUILD_ENV_PATH=${_BUILD_ENV_PATH} . && \
docker stop temp-postgres temp-rethinkdb temp-redis && docker rm temp-postgres temp-rethinkdb temp-redis -f || docker stop temp-postgres temp-rethinkdb temp-redis && docker rm temp-postgres temp-rethinkdb temp-redis -f
```

If `_DOCKER_REPOSITORY` wasn't local and you want to push the image, you can run then:
export _DOCKER_REPOSITORY=parabol; \
export _DOCKER_TAG=vX.X.X
```commandLine
docker push ${_DOCKER_REPOSITORY}:${_DOCKER_TAG}
docker run --name=parabol-predeploy --network=host -v $(pwd)/.env:/home/node/parabol/.env ${_DOCKER_REPOSITORY}:${_DOCKER_TAG} /bin/bash -c "node dist/preDeploy.js"
```

## Run the application using a docker image

_Assumes redis, rethinkdb, and postgres already running to have operational stack._

The commands below will start a Parabol container on the target tag specified in \_DOCKER_TAG export. It will volume mount a .env in your current working directory to the container, so you can pass in any .env in your current working directory.

- Start GraphQL

```commandLine
export _DOCKER_REPOSITORY=parabol; \
export _DOCKER_TAG=vX.X.X
docker run --name=parabolgraphql --network=host -v $(pwd)/.env:/home/node/parabol/.env ${_DOCKER_REPOSITORY}:${_DOCKER_TAG} /bin/bash -c "yarn predeploy && NODE_ENV=production && node ./dist/gqlExecutor.js" || docker container rm parabolgraphql -f
docker run --name=parabol-gql-executor --network=host -v $(pwd)/.env:/home/node/parabol/.env ${_DOCKER_REPOSITORY}:${_DOCKER_TAG} /bin/bash -c "node ./dist/gqlExecutor.js" || docker container rm parabol-gql-executor -f
```

- Start Web Server
Expand All @@ -178,7 +140,7 @@ docker run --name=parabolgraphql --network=host -v $(pwd)/.env:/home/node/parabo
export _DOCKER_REPOSITORY=parabol; \
export _DOCKER_TAG=vX.X.X
docker run --name=parabol --network=host -v $(pwd)/.env:/home/node/parabol/.env -p 3000:3000 ${_DOCKER_REPOSITORY}:${_DOCKER_TAG} /bin/bash -c "yarn predeploy && NODE_ENV=production && node ./dist/web.js" || docker container rm parabol -f
docker run --name=parabol-web-server --network=host -v $(pwd)/.env:/home/node/parabol/.env -p 3000:3000 ${_DOCKER_REPOSITORY}:${_DOCKER_TAG} /bin/bash -c "node ./dist/web.js" || docker container rm parabol-web-server -f
```

To stop the container, just open another terminal and enter `docker container stop parabol`
To stop the container, just open another terminal and enter `docker container stop parabol-COMPONENT`
26 changes: 26 additions & 0 deletions docker/parabol-ubi/docker-build/dockerfiles/basic.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
ARG _NODE_VERSION=${_NODE_VERSION}
FROM node:${_NODE_VERSION}-bookworm-slim as base

ENV NPM_CONFIG_PREFIX=/home/node/.npm-global
ENV PORT=3000

COPY --chown=node --chmod=755 docker/parabol-ubi/docker-build/entrypoints/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
COPY --chown=node docker/parabol-ubi/docker-build/tools/ip-to-server_id ${HOME}/tools/ip-to-server_id

# Required for pushToCDN to work with FILE_STORE_PROVIDER set to 'local'
RUN mkdir -p ${HOME}/parabol/self-hosted && \
chown node:node ${HOME}/parabol/self-hosted

COPY --chown=node .env.example ${HOME}/parabol/.env.example

# The application requires a yarn.lock file on the root folder to identify it
COPY --chown=node yarn.lock ${HOME}/parabol/yarn.lock
COPY --chown=node build ${HOME}/parabol/build
COPY --chown=node dist ${HOME}/parabol/dist

WORKDIR ${HOME}/parabol/

USER node
EXPOSE ${PORT}

ENTRYPOINT ["docker-entrypoint.sh"]
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# DO NOT DELETE. Legacy docker file for versions still in use. Delete only when all Parabol instances are using the newest docker image.
ARG _NODE_VERSION=${_NODE_VERSION}
#base build for dev deps
FROM node:${_NODE_VERSION} as base

ARG _PARABOL_GIT_REF=${_PARABOL_GIT_REF}
ARG _BUILD_ENV_PATH=environments/buildenv
ARG _BUILD_ENV_PATH=environments/legacy-build
ENV NPM_CONFIG_PREFIX=/home/node/.npm-global

WORKDIR /home/node
Expand Down Expand Up @@ -45,7 +46,7 @@ COPY --from=base /usr/local/lib/node_modules /usr/local/lib/node_modules
COPY --from=base /opt /opt
COPY --from=base /home/node/parabol/ ${HOME}/parabol
RUN rm -rf ${HOME}/parabol/.env
COPY entrypoints/buildenv /usr/local/bin/docker-entrypoint.sh
COPY entrypoints/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
COPY security /security

COPY ./tools/ip-to-server_id /home/node/tools/ip-to-server_id
Expand Down
117 changes: 0 additions & 117 deletions docker/parabol-ubi/docker-build/dockerfiles/pipeline.dockerfile

This file was deleted.

Loading

0 comments on commit 5e356c2

Please sign in to comment.