First you need to look into a process summary of Docker-Blue-Green-Runner at this link,
https://github.com/patternhelloworld/docker-blue-green-runner
The following explains why I use Docker-Blue-Green-Runner…
- No Unpredictable Errors in Reverse Proxy and Deployment
If any error occurs in the app or router, deployment is halted to prevent any impact on the existing deployment
-
For example, Traefik offers powerful dynamic configuration and service discovery; however, certain errors, such as a failure to detect containers (due to issues like unrecognized certificates), can lead to frustrating 404 errors that are hard to trace through logs alone.
Manipulates NGINX configuration files directly to ensure container accessibility. It also tests configuration files by launching a test NGINX Docker instance, and if an NGINX config update via Consul-Template fails, Contingency Plan provided is activated to ensure connectivity to your containers.
- From Scratch
Docker-Blue-Green-Runner’s run.sh script is designed to simplify deployment: "With your .env, project, and a single Dockerfile, simply run 'bash run.sh'." This script covers the entire process from Dockerfile build to server deployment from scratch.
This means you can easily migrate to another server with just the files mentioned above.
In contrast, Traefik requires the creation and gradual adjustment of various configuration files, which can introduce the types of errors mentioned above.
- Focus on zero-downtime deployment on a single machine.
While Kubernetes excels in multi-machine environments with the support of Layer 7 (L7) technologies (I would definitely use Kubernetes in that case), this approach is ideal for scenarios where only one or two machines are available.
However, for deployments involving more machines, traditional Layer 4 (L4) load-balancer using servers could be utilized.
Now, let’s dive into how to use it…
First, clone the project
git clone https://github.com/patternhelloworld/docker-blue-green-runner
Step 1: Create an .env file at the root of the project.
# Leave as it is
HOST_IP=host.docker.internal
# It is recommended to enter your formal URL or IP, such as https://test.com, for the 'check_availability_out_of_container' test in the 'run.sh' script.
# Both https://your-app.com:443 and https://localhost:443 are valid
# Docker-Blue-Runner recognizes if your App requires SSL in the Nginx router if this starts with 'https'.
# This URL is used for the "External Integrity Check" process.
APP_URL=https://localhost:443
# APP_URL=http://localhost:<--!host-port-number!-->
# PROJECT_PORT=<--!common-port-number!--> OR
# PROJECT_PORT=[<--!host-port-number!-->,<--!internal-project-port-number!-->]
PROJECT_PORT=[443,8360]
# In case USE_COMMERCIAL_SSL is 'false', the Runner generates self-signed SSL certificates. However, you should set any name for ``COMMERCIAL_SSL_NAME``.
# In case it is 'true', locate your commercial SSLs in the folder docker-blue-green-runner/.docker/ssl. See the comments in the .env above.
USE_COMMERCIAL_SSL=true
# Your domain name is recommended. The files 'your-app.com.key', 'your-app.com.crt', and 'your-app.com.chained.crt' should be in place.
COMMERCIAL_SSL_NAME=your-app.com
DOCKER_LAYER_CORRUPTION_RECOVERY=false
NGINX_RESTART=false
CONSUL_RESTART=false
# The method of acquiring Docker images:
# build (Used in developer's local environment or during Jenkins builds when a new image needs to be built, so this module is typically used)
# registry (Used on deployment servers where images are fetched from a repository, so this module is used)
# If you choose the "build" method, you don't need to input the values below since Dockerfile is used (no image is fetched from the Docker registry).
GIT_IMAGE_LOAD_FROM=build
GIT_IMAGE_LOAD_FROM_HOST=xxx
GIT_IMAGE_LOAD_FROM_PATHNAME=xxx
GIT_TOKEN_IMAGE_LOAD_FROM_USERNAME=xxx
GIT_TOKEN_IMAGE_LOAD_FROM_PASSWORD=xxx
GIT_IMAGE_VERSION=1.0.0
PROJECT_NAME=your-app
## [IMPORTANT] Ensure it matches 'PROJECT_ROOT_IN_CONTAINER' below.
PROJECT_LOCATION=/app
PROJECT_PORT=[443,8360]
# Example (8093,8094,11000...)
ADDITIONAL_PORTS=
CONSUL_KEY_VALUE_STORE=http://consul:8500/v1/kv/deploy/your-app
# If you locate your project on ../ (upper folder)
HOST_ROOT_LOCATION=/var/projects/your-app
# If you locate your project's Dockerfile ../ (upper folder)
DOCKER_FILE_LOCATION=/var/projects/your-app
# This is for integrating health checkers such as "https://www.baeldung.com/spring-boot-actuators"
# This path is used for both internal and external health checks.
# Note: Do not include a leading slash ("/") at the start of the path.
# Example: "api/v1/health" (correct), "/api/v1/health" (incorrect)
APP_HEALTH_CHECK_PATH=api/v1/health
# The BAD & GOOD conditions are checked using an "AND" condition.
# To ignore the "BAD_APP_HEALTH_CHECK_PATTERN", set it to a non-existing value (e.g., "###lsdladc").
BAD_APP_HEALTH_CHECK_PATTERN=DOWN
# Pattern required for a successful health check.
GOOD_APP_HEALTH_CHECK_PATTERN=UP
# The following trick is just for skipping the check.
# APP_HEALTH_CHECK_PATH=login
# BAD_APP_HEALTH_CHECK_PATTERN=xxxxxxx
# GOOD_APP_HEALTH_CHECK_PATTERN=Head
# This is for environment variables for docker-compose-app-${app_env}.
DOCKER_COMPOSE_ENVIRONMENT={"TZ":"Asia/Seoul"}
# [IMPORTANT] You can pass any variable to Step 2 of your Dockerfile using DOCKER_BUILD_ARGS, e.g., DOCKER_BUILD_ARGS={"PROJECT_ROOT_IN_CONTAINER":"/app"}."
DOCKER_BUILD_ARGS={"PROJECT_ROOT_IN_CONTAINER":"/app"}
# For SSL, the host folder is recommended to be './.docker/ssl' to be synchronized with 'docker-compose-nginx-original.yml'.
# [IMPORTANT] Run mkdir -p /var/projects/files/your-app/logs on your host machine
DOCKER_COMPOSE_SELECTIVE_VOLUMES=["/var/projects/your-app/.docker/nginx/app.conf.ctmpl:/etc/nginx-template/app.conf.ctmpl","/var/projects/files/your-app/logs:/var/log/nginx"]
# [IMPORTANT] Run mkdir -p /var/projects/files/nginx/logs on your host machine
DOCKER_COMPOSE_NGINX_SELECTIVE_VOLUMES=["/var/projects/files/nginx/logs:/var/log/nginx"]
DOCKER_COMPOSE_HOST_VOLUME_CHECK=false
NGINX_CLIENT_MAX_BODY_SIZE=50M
USE_MY_OWN_APP_YML=false
SKIP_BUILDING_APP_IMAGE=false
# Docker-Swarm(stack) is currently a beta version. Use 'compose'.
ORCHESTRATION_TYPE=compose
ONLY_BUILDING_APP_IMAGE=false
DOCKER_BUILD_MEMORY_USAGE=1G
USE_NGINX_RESTRICTED_LOCATION=false
# ex. /docs/api-app.html
NGINX_RESTRICTED_LOCATION=xxx
# If you set this to 'true', you won't need to configure SSL for your app. For instance, in a Spring Boot project, you won't have to create a ".jks" file. However, in rare situations, such as when it's crucial to secure all communication lines with SSL or when converting HTTPS to HTTP causes 'curl' errors, you might need to set it to 'false'.If you set this to 'true', you don't need to set SSL on your App like for example, for a Spring Boot project, you won't need to create the ".jks" file. However, in rare cases, such as ensuring all communication lines are SSL-protected, or when HTTPS to HTTP causes 'curl' errors, you might need to set it to 'false'.
# 1) true : [Request]--> https (external network) -->Nginx--> http (internal network) --> App
# 2) false :[Request]--> https (external network) -->Nginx--> httpS (internal network) --> App
# !!! [IMPORTANT] As your App container below is Http, this should be set to 'true'.
REDIRECT_HTTPS_TO_HTTP=true
NGINX_LOGROTATE_FILE_NUMBER=7
NGINX_LOGROTATE_FILE_SIZE=1M
# You can change the values below. These settings for security related to ``set-safe-permissions.sh`` at the root of Docker-Blue-Green-Runner.
SHARED_VOLUME_GROUP_ID=1559
SHARED_VOLUME_GROUP_NAME=mba-shared-volume-group
UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID=1000,1001
USE_MY_OWN_NGINX_ORIGIN=false
Step 2. Write your Dockerfile at the root of your project
- React sample (SPA)
The ARG below is passed from Step 1. In SPA case, since Next.js isn’t being used, you may need to set up an additional Nginx server in your app. Spring Boot, Next.js, and NestJS each have their own servers, so no additional setup is needed for them.
FROM node:18.20.4-slim AS build
ARG PROJECT_ROOT_IN_CONTAINER
RUN mkdir -p $PROJECT_ROOT_IN_CONTAINER
COPY .. $PROJECT_ROOT_IN_CONTAINER
WORKDIR $PROJECT_ROOT_IN_CONTAINER
RUN export NODE_OPTIONS="--max-old-space-size=2048"
RUN whereis npm && alias npm='node --max_old_space_size=2048 /usr/local/bin/npm'
RUN export NODE_OPTIONS="--max-old-space-size=2048"
RUN if [ -d $PROJECT_ROOT_IN_CONTAINER/node_modules ]; then echo "[NOTICE] The node_modules folder exists. Skipping 'npm install'... "; else npm install --legacy-peer-deps; fi
RUN npm cache clean --force
RUN npm run build:prod
FROM nginx:stable
RUN apt-get update -qqy && apt-get -qqy --force-yes install curl runit wget unzip vim && \
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
ARG PROJECT_ROOT_IN_CONTAINER
COPY --chown=nginx --from=build $PROJECT_ROOT_IN_CONTAINER/dist/ $PROJECT_ROOT_IN_CONTAINER
USER root
WORKDIR $PROJECT_ROOT_IN_CONTAINER
COPY ./.docker/entrypoint.sh /entrypoint.sh
RUN chmod a+x /entrypoint.sh
ENTRYPOINT bash /entrypoint.sh
entrypoint.sh & app.conf.ctmpl… these are not not necessary this is just my App’s setting.
entrypoint.sh, app.conf.ctmpl.. these are just for my App settings.
#!/bin/bash
cat /etc/nginx-template/app.conf.ctmpl > /etc/nginx/conf.d/default.conf
/usr/sbin/nginx -t && exec /usr/sbin/nginx -g "daemon off;"
server {
listen 8360; # [IMPORTANT] PROJECT_PORT in .env Step1.
server_name localhost;
# Root directory
root /app;
index index.html
# Access and Error logs
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Gzip compression for performance improvement
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types
application/javascript
application/json
application/xml
text/css
text/plain;
# Cache settings for static files for better performance
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
location / {
try_files $uri $uri/ /index.html?$query_string;
}
}
Step 3: Deploy daily with a simple git pull && bash run.sh command
For more details, please refer to https://github.com/patternhelloworld/docker-blue-green-runner?tab=readme-ov-file#security.
Thank you for taking the time to read this!
Top comments (0)