From dc065cc68250f022f3b62df721e8d7213c0183f6 Mon Sep 17 00:00:00 2001 From: Marius Scurtescu Date: Thu, 7 Mar 2024 21:05:23 -0800 Subject: [PATCH] multi stage Dockerfile and Taskfile (#102) * multi-stage Dockerfile * add build arguments for git * add taskfile * switch back to copying source into build stage and clean up labels * get image name from .env * remove top level ARGs * update DOCKER-README with Taskfile details * mention light and safe image * add a backup task * add healthcheck * add logs-folder task * add warnings to backup task * allow port overriding in docker compose --- .env.example | 15 +++++++ .gitignore | 1 + DOCKER-README.md | 74 +++++++++++++++++++++++++++++++- Dockerfile | 39 +++++++++++------ Taskfile.yaml | 105 +++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 15 ++++--- 6 files changed, 230 insertions(+), 19 deletions(-) create mode 100644 .env.example create mode 100644 Taskfile.yaml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..bd2f870 --- /dev/null +++ b/.env.example @@ -0,0 +1,15 @@ +# Use a custom docker image name +# Default: quilibrium +QUILIBRIUM_IMAGE_NAME= + +# Use a custom P2P port. +# Default: 8336 +QUILIBRIUM_P2P_PORT= + +# Use a custom gRPC port. +# Default: 8337 +QUILIBRIUM_GRPC_PORT= + +# Use a custom REST port. +# Default: 8338 +QUILIBRIUM_REST_PORT= diff --git a/.gitignore b/.gitignore index e1d3bba..5255caa 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ ceremony-client .config* .DS_Store *.mprof +.env diff --git a/DOCKER-README.md b/DOCKER-README.md index 3a198c9..92034cd 100644 --- a/DOCKER-README.md +++ b/DOCKER-README.md @@ -43,13 +43,51 @@ net.core.wmem_max = 600000000 ## Build +The only requirements are `git` (to checkout the repository) and docker (to build the image and run the container). +Golang does not have to be installed, the docker image build process uses a build stage that provides the +correct Go environment and compiles the node down to one command. + In the repository root folder, where the [Dockerfile](Dockerfile) file is, build the docker image: ```shell -docker build --build-arg GIT_COMMIT=$(git log -1 --format=%h) -t quilibrium -t quilibrium:1.2.15 . +docker build --build-arg GIT_COMMIT=$(git log -1 --format=%h) -t quilibrium -t quilibrium:1.4.2 . ``` -Use latest version instead of `1.2.15`. +Use latest version instead of `1.4.2`. +The image that is built is light and safe. It is based on Alpine Linux with the Quilibrium node binary, not the +source code, nor the Go development environment. The image also has the `grpcurl` tool that can be used to +query the gRPC interface. + +### Task + +You can also use the [Task](https://taskfile.dev/) tool, it a simple build tool that takes care of extracting +parameters, building the image and running the container. The tasks are all defined in [Taskfile.yaml](Taskfile.yaml). + +You can optionally create an `.env` file, in the same repository root folder to override specific parameters. Right now +only one optional env var is supported and that is `QUILIBRIUM_IMAGE_NAME`, if you want to change the default +image name from `quilibrium` to something else. If you are pushing your images to Github then you have to follow the +Github naming convention and use a name like `ghcr.io/mscurtescu/ceremonyclient`. + +Bellow there are example interaction with `Task`. + +The node version is extracted from [node/main.go](node/main.go). This version string is used to tag the image. The git +repo, branch and commit are read throught the `git` command and depend on the current state of your working +directory (one what branch and at what commit you are). These last three values are used to label the image. + +List tasks: +```shell +task -l +``` + +Show what parameters, like image name, version etc, will be used: +```shell +task status +``` + +Build the image (aka run the `build` task): +```shell +task build +``` ## Run @@ -70,6 +108,38 @@ docker compose up -d A `.config/` subfolder will be created under the current folder, this is mapped inside the container. Make sure you backup `config.yml` and `keys.yml`. +### Task + +Similarly to building the image you can also use `Task`. + +Start the container through docker compose: +```shell +task up +``` + +Show the logs through docker compose: +```shell +task logs +``` + +Drop into a shell inside the running container: +```shell +task shell +``` + +Stop the running container(s): +```shell +task down +``` + +Backup the critical configuration: +```shell +task backup +``` + +The above command will create a `backup.tar.gz` archive in the current folder, you still have to copy this +file from the server into a safe location. The command adds the `config.yml` and `keys.yml` files from +the `.config/` subfolder to the archive, with the ownership of the current user. ### Resource management To ensure that your client performs optimally within a specific resource configuration, you can specify diff --git a/Dockerfile b/Dockerfile index 01768f7..0c72367 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,38 @@ -FROM golang:1.20.14-alpine3.19 +FROM golang:1.20.14-alpine3.19 as build -ARG GIT_COMMIT=unspecified +ENV GOEXPERIMENT=arenas + +WORKDIR /opt/ceremonyclient + +COPY . . + +WORKDIR /opt/ceremonyclient/node + +RUN go install ./... +RUN go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest + +FROM alpine:3.19 + +ARG GIT_REPO +ARG GIT_BRANCH +ARG GIT_COMMIT + +ENV GOEXPERIMENT=arenas LABEL org.opencontainers.image.title="Quilibrium Network Node" LABEL org.opencontainers.image.description="Quilibrium is a decentralized alternative to platform as a service providers." LABEL org.opencontainers.image.vendor=Quilibrium LABEL org.opencontainers.image.url=https://quilibrium.com/ LABEL org.opencontainers.image.documentation=https://quilibrium.com/docs +LABEL org.opencontainers.image.source=$GIT_REPO +LABEL org.opencontainers.image.ref.name=$GIT_BRANCH LABEL org.opencontainers.image.revision=$GIT_COMMIT -ENV GOEXPERIMENT=arenas +COPY --from=build /go/bin/node /usr/local/bin +COPY --from=build /go/bin/grpcurl /usr/local/bin +COPY --from=build /opt/ceremonyclient/node/ceremony.json /root +COPY --from=build /opt/ceremonyclient/node/retroactive_peers.json /root -WORKDIR /opt/ceremonyclient +WORKDIR /root -COPY . . - -WORKDIR /opt/ceremonyclient/node - -RUN go mod download && go mod verify -RUN go build ./... - -ENTRYPOINT ["go", "run", "./..."] +ENTRYPOINT ["node"] diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 0000000..7dd9c36 --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,105 @@ +# https://taskfile.dev + +version: '3' + +dotenv: + - '.env' + +vars: + VERSION: + sh: cat node/main.go | grep "Quilibrium Node - v" | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' + PROJECT_NAME: quilibrium + SERVICE_NAME: node + GIT_REPO: + sh: git config --get remote.origin.url + GIT_BRANCH: + sh: git rev-parse --abbrev-ref HEAD + GIT_COMMIT: + sh: git log -1 --format=%h + +tasks: + status: + desc: Display configuration info. + cmds: + - echo -n "Image name:" && echo " ${QUILIBRIUM_IMAGE_NAME:-quilibrium}" + - echo -n "Version :" && echo " {{.VERSION}}" + - echo -n "Repo :" && echo " {{.GIT_REPO}}" + - echo -n "Branch :" && echo " {{.GIT_BRANCH}}" + - echo -n "Commit :" && echo " {{.GIT_COMMIT}}" + silent: true + + build: + desc: Build the Quilibrium docker image, unless it is already built. + cmds: + - | + docker build \ + --build-arg GIT_REPO={{.GIT_REPO}} \ + --build-arg GIT_BRANCH={{.GIT_BRANCH}} \ + --build-arg GIT_COMMIT={{.GIT_COMMIT}} \ + -t ${QUILIBRIUM_IMAGE_NAME:-quilibrium}:{{.VERSION}} \ + -t ${QUILIBRIUM_IMAGE_NAME:-quilibrium}:latest \ + . + status: + - | + docker image inspect \ + ${QUILIBRIUM_IMAGE_NAME:-quilibrium}:{{.VERSION}} \ + >/dev/null 2>/dev/null + + up: + desc: Run a new Quilibrium container, through docker compose. + cmds: + - docker compose up -d + + down: + desc: Take down the Quilibrium container, through docker compose. + cmds: + - docker compose down + + shell: + desc: Drop into a shell inside the running container. + cmds: + - docker compose exec -it {{.SERVICE_NAME}} sh + + logs: + desc: Print the logs of the running Quilibrium container. + cmds: + - docker compose logs + + logs-folder: + desc: Show where Docker stores the logs for the Quilibrium node. You need root permissions to access the folder. + cmds: + - "docker container inspect {{.PROJECT_NAME}}-{{.SERVICE_NAME}}-1 | grep LogPath | cut -d : -f 2 | cut -d '\"' -f 2 | xargs dirname" + + backup: + desc: Create a backup file with the critical configuration files. + prompt: You will be prompted for root access. Make sure you verify the generated backup file. Continue? + sources: + - '.config/config.yml' + - '.config/keys.yml' + outputs: + - 'backup.tar.gz' + cmds: + - | + export TMP_DIR=$(mktemp -d) + export TASK_DIR=$(pwd) + sudo cp .config/config.yml $TMP_DIR + sudo cp .config/keys.yml $TMP_DIR + sudo chown $(whoami):$(id -gn) $TMP_DIR/* + cd $TMP_DIR + tar -czf $TASK_DIR/backup.tar.gz * + cd $TASK_DIR + sudo rm -rf $TMP_DIR + echo "Backup saved to: backup.tar.gz" + echo "Do not assume you have a backup unless you verify it!!!" + silent: true + + github:login: + desc: Login to GitHub container registry. + cmds: + - echo $GITHUB_TOKEN | docker login ghcr.io -u $GITHUB_USERNAME --password-stdin + + push: + desc: Push Quilibrium docker image to the container registry. + cmds: + - docker push ${QUILIBRIUM_IMAGE_NAME:-quilibrium}:{{.VERSION}} + - docker push ${QUILIBRIUM_IMAGE_NAME:-quilibrium}:latest diff --git a/docker-compose.yml b/docker-compose.yml index 5b92b1d..2288762 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,8 +7,7 @@ name: quilibrium services: node: - build: ./ - image: quilibrium + image: ${QUILIBRIUM_IMAGE_NAME:-quilibrium} restart: unless-stopped deploy: resources: @@ -22,9 +21,15 @@ services: - DEFAULT_LISTEN_REST_MULTIADDR=/ip4/0.0.0.0/tcp/8338 - DEFAULT_STATS_MULTIADDR=/dns/stats.quilibrium.com/tcp/443 ports: - - '8336:8336/udp' # p2p - - '127.0.0.1:8337:8337/tcp' # gRPC - - '127.0.0.1:8338:8338/tcp' # REST + - '${QUILIBRIUM_P2P_PORT:-8336}:8336/udp' # p2p + - '127.0.0.1:${QUILIBRIUM_GRPC_PORT:-8337}:8337/tcp' # gRPC + - '127.0.0.1:${QUILIBRIUM_REST_PORT:-8338}:8338/tcp' # REST + healthcheck: + test: ["CMD", "node", "--peer-id"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 1m volumes: - ./.config:/opt/ceremonyclient/node/.config logging: