From d0840647dd1e15eeda89419be34252fc3324059d Mon Sep 17 00:00:00 2001 From: Aaron Elkiss Date: Wed, 22 May 2024 10:15:39 -0400 Subject: [PATCH] Use health checks & multi-stage builds in docker compose * add base/dev/prod stage rather than Dockerfile vs Dockerfile.prod * use docker compose health checks instead of wait-for --- .github/workflows/build.yml | 1 - .github/workflows/tests.yml | 8 ++- Dockerfile | 26 +++++++-- Dockerfile.prod | 22 -------- README.md | 2 +- bin/setup/setup_dev.sh | 5 +- bin/setup/setup_test.sh | 5 +- bin/setup/wait-for | 104 ------------------------------------ docker-compose.yml | 85 +++++++++++++++++++---------- 9 files changed, 86 insertions(+), 172 deletions(-) delete mode 100644 Dockerfile.prod delete mode 100755 bin/setup/wait-for diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6a3c8158..66babb76 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,6 @@ jobs: uses: hathitrust/github_actions/build@v1.4.0 with: image: ghcr.io/hathitrust/holdings-client-unstable - dockerfile: Dockerfile.prod img_tag: ${{ inputs.img_tag }} tag: ${{ inputs.ref }} push_latest: ${{ inputs.push_latest}} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a5029437..ea9e2d13 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,12 +16,10 @@ jobs: run: ./bin/setup/setup_test.sh - name: Run standardrb - run: docker compose run --rm dev bundle exec standardrb + run: docker compose run --rm test bundle exec standardrb - name: Run tests - run: docker compose run --rm -e MONGOID_ENV=test dev bin/setup/wait-for mariadb:3306 pushgateway:9091 redis:6379 -- bundle exec rspec + run: docker compose run --rm test - name: Report to Coveralls - uses: coverallsapp/github-action@1.1.3 - with: - github-token: ${{ secrets.github_token }} + uses: coverallsapp/github-action@v2 diff --git a/Dockerfile b/Dockerfile index 600ccfc1..b4794b10 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,28 @@ -FROM ruby:3.3 -ARG UNAME=holdings -ARG UID=1000 -ARG GID=1000 +FROM ruby:3.3 AS base RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends \ - nodejs netcat-openbsd rclone less entr uchardet + nodejs rclone uchardet WORKDIR /usr/src/app ENV BUNDLE_PATH /gems ENV RUBYLIB /usr/src/app/lib RUN gem install bundler + +FROM base AS dev +RUN apt-get install -yqq --no-install-recommends less entr + +FROM base AS prod +LABEL org.opencontainers.image.source https://github.com/hathitrust/holdings-backend + +ARG UNAME=holdings +ARG UID=1000 +ARG GID=1000 + +RUN groupadd -g $GID -o $UNAME +RUN useradd -m -d /usr/src/app -u $UID -g $GID -o -s /bin/bash $UNAME +RUN mkdir -p /gems && chown $UID:$GID /gems +USER $UNAME + +COPY --chown=$UID:$GID Gemfile* /usr/src/app/ +RUN bundle install +COPY --chown=$UID:$GID . /usr/src/app diff --git a/Dockerfile.prod b/Dockerfile.prod deleted file mode 100644 index 8f188f7c..00000000 --- a/Dockerfile.prod +++ /dev/null @@ -1,22 +0,0 @@ -FROM ruby:3.3 -LABEL org.opencontainers.image.source https://github.com/hathitrust/holdings-backend - -ARG UNAME=holdings -ARG UID=1000 -ARG GID=1000 - -RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends \ - nodejs rclone uchardet - -RUN gem install bundler -RUN groupadd -g $GID -o $UNAME -RUN useradd -m -d /usr/src/app -u $UID -g $GID -o -s /bin/bash $UNAME -RUN mkdir -p /gems && chown $UID:$GID /gems -USER $UNAME - -COPY --chown=$UID:$GID Gemfile* /usr/src/app/ -WORKDIR /usr/src/app -ENV BUNDLE_PATH /gems -ENV RUBYLIB /usr/src/app/lib -RUN bundle install -COPY --chown=$UID:$GID . /usr/src/app diff --git a/README.md b/README.md index b870da71..a1a73894 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ bash bin/setup/setup_dev.sh ## Running the tests -`docker-compose run --rm dev bundle exec rspec` +`docker-compose run --rm test` ## Clearing out/resetting the data For resetting everything (cleaning up containers & their persistent volumes): diff --git a/bin/setup/setup_dev.sh b/bin/setup/setup_dev.sh index 8844a8d6..d02dd7aa 100755 --- a/bin/setup/setup_dev.sh +++ b/bin/setup/setup_dev.sh @@ -2,8 +2,7 @@ $(dirname ${BASH_SOURCE[0]})/setup_test.sh -docker compose up -d mongo_dev pushgateway redis -docker compose run --rm dev bin/setup/wait-for mongo_dev:27017 -- echo "mongo is ready" +docker compose up --wait mongo_dev docker compose exec -T mongo_dev bash /tmp/bin/setup/rs_initiate.sh mongo_dev -docker compose run --rm -e MONGOID_ENV=development dev bundle exec ruby lib/tasks/build_database.rb +docker compose run --rm dev bundle exec ruby lib/tasks/build_database.rb diff --git a/bin/setup/setup_test.sh b/bin/setup/setup_test.sh index 0b0887f5..0d8cf4e7 100755 --- a/bin/setup/setup_test.sh +++ b/bin/setup/setup_test.sh @@ -2,7 +2,6 @@ docker compose build docker compose run --rm dev bundle install -docker compose up -d mongo_test mariadb pushgateway redis -docker compose run --rm -e MONGOID_ENV=test dev bin/setup/wait-for mongo_test:27017 -- echo "mongo is ready" +docker compose up --wait mongo_test docker compose exec -T mongo_test bash /tmp/bin/setup/rs_initiate.sh mongo_test -docker compose run --rm -e MONGOID_ENV=test dev bundle exec ruby lib/tasks/build_database.rb +docker compose run --rm -e test bundle exec ruby lib/tasks/build_database.rb diff --git a/bin/setup/wait-for b/bin/setup/wait-for deleted file mode 100755 index 134bf448..00000000 --- a/bin/setup/wait-for +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/sh - -# From https://github.com/eficode/wait-for - -# The MIT License (MIT) -# -# Copyright (c) 2017 Eficode Oy -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -TIMEOUT=15 -QUIET=0 - -echoerr() { - if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi -} - -usage() { - exitcode="$1" - cat << USAGE >&2 -Usage: - $cmdname host:port [-t timeout] [-- command args] - -q | --quiet Do not output any status messages - -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout - -- COMMAND ARGS Execute command with args after the test finishes -USAGE - exit "$exitcode" -} - -wait_for() { - command="$*" - for i in `seq $TIMEOUT` ; do - nc -z "$HOST" "$PORT" > /dev/null 2>&1 - - result=$? - if [ $result -eq 0 ] ; then - if [ -n "$command" ] ; then - exec $command - fi - exit 0 - fi - sleep 1 - done - echo "Operation timed out" >&2 - exit 1 -} - -while [ $# -gt 0 ] -do - case "$1" in - *:* ) - HOST=$(printf "%s\n" "$1"| cut -d : -f 1) - PORT=$(printf "%s\n" "$1"| cut -d : -f 2) - shift 1 - ;; - -q | --quiet) - QUIET=1 - shift 1 - ;; - -t) - TIMEOUT="$2" - if [ "$TIMEOUT" = "" ]; then break; fi - shift 2 - ;; - --timeout=*) - TIMEOUT="${1#*=}" - shift 1 - ;; - --) - shift - break - ;; - --help) - usage 0 - ;; - *) - echoerr "Unknown argument: $1" - usage 1 - ;; - esac -done - -if [ "$HOST" = "" -o "$PORT" = "" ]; then - echoerr "Error: you need to provide a host and port to test." - usage 2 -fi - -wait_for "$@" diff --git a/docker-compose.yml b/docker-compose.yml index 2902cc7f..190d001d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,37 +1,56 @@ -version: '3' +--- + +x-condition-healthy: &healthy + condition: service_healthy + +x-healthcheck-defaults: &healthcheck-defaults + interval: 5s + timeout: 10s + start_period: 10s + retries: 5 + +x-holdings-container: &holdings-container-defaults + build: + context: . + target: dev + volumes: + - .:/usr/src/app + - gem_cache:/gems + command: bundle exec rspec + environment: &holdings-default-environment + MYSQL_CONNECTION_STRING: "mysql2://ht_repository:ht_repository@mariadb/ht_repository" + PUSHGATEWAY: http://pushgateway:9091 + MONGOID_ENV: development + depends_on: + mariadb: *healthy + redis: *healthy + mongo_dev: *healthy services: - dev: - build: . - volumes: - - .:/usr/src/app - - gem_cache:/gems - environment: - MYSQL_CONNECTION_STRING: "mysql2://ht_repository:ht_repository@mariadb/ht_repository" - PUSHGATEWAY: http://pushgateway:9091 + test: + <<: *holdings-container-defaults + command: bundle exec rspec + environment: + <<: *holdings-default-environment + MONGOID_ENV: test + depends_on: + mongo_test: *healthy + mariadb: *healthy + redis: *healthy - phctl: - build: . - volumes: - - .:/usr/src/app - - gem_cache:/gems - environment: - MYSQL_CONNECTION_STRING: "mysql2://ht_repository:ht_repository@mariadb/ht_repository" - PUSHGATEWAY: http://pushgateway:9091 + dev: *holdings-container-defaults + + phctl: + <<: *holdings-container-defaults entrypoint: bundle exec ruby bin/phctl.rb processor: - build: . + <<: *holdings-container-defaults restart: always volumes: - - .:/usr/src/app - - gem_cache:/gems - ./example/datasets:/tmp/datasets command: bundle exec sidekiq -c 1 -r ./lib/sidekiq_jobs.rb - environment: - MYSQL_CONNECTION_STRING: "mysql2://ht_repository:ht_repository@mariadb/ht_repository" - PUSHGATEWAY: http://pushgateway:9091 mongo_dev: image: mongo:6.0.2 @@ -39,12 +58,16 @@ services: volumes: - data_db:/data/db - ./bin:/tmp/bin + healthcheck: &mongo-healthcheck + <<: *healthcheck-defaults + test: [ "CMD", "mongosh", "--quiet", "--eval", 'db.runCommand("ping").ok'] mongo_test: image: mongo:6.0.2 command: --replSet rs0 --bind_ip localhost,mongo_test volumes: - ./bin:/tmp/bin + healthcheck: *mongo-healthcheck mariadb: image: mariadb @@ -52,6 +75,9 @@ services: MYSQL_RANDOM_ROOT_PASSWORD: 1 volumes: - ./sql:/docker-entrypoint-initdb.d/ + healthcheck: + <<: *healthcheck-defaults + test: [ "CMD", "healthcheck.sh", "--su-mysql", "--connect", "--innodb_initialized" ] pushgateway: image: prom/pushgateway @@ -59,16 +85,16 @@ services: - --web.enable-admin-api ports: - 9091:9091 + healthcheck: + <<: *healthcheck-defaults + test: [ "CMD", "wget", "--quiet", "--tries=1", "-O", "/dev/null", "pushgateway:9091/-/healthy" ] sidekiq_web: - build: . + <<: *holdings-container-defaults restart: always - volumes: - - .:/usr/src/app - - gem_cache:/gems command: bundle exec puma bin/sidekiq_web.ru depends_on: - - redis + redis: *healthy ports: - 9292:9292 environment: @@ -77,6 +103,9 @@ services: redis: image: redis restart: always + healthcheck: + <<: *healthcheck-defaults + test: ["CMD", "redis-cli","ping"] volumes: gem_cache: