Skip to content

Commit

Permalink
The reinforced checking states logic
Browse files Browse the repository at this point in the history
The checking states logic is now based on scores.
Add checking states scripts (See the README)
Add the checking states logic one more before Up the Containers.
  • Loading branch information
patternhelloworld committed Sep 17, 2023
1 parent 986668a commit 96be344
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 198 deletions.
101 changes: 62 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ For all echo messages or properties .env, the following terms indicate...
- DOWN : ```docker-compose down```
- RESTART : ```docker build & docker-compose down & docker-compose up ```
- ex) NGINX_RESTART on .env means docker build & down & up for NGINX

- safe : set a new state(=blue or green) without stopping or causing errors on your web App.
## How to Start with a Node Sample (Local).

A Node.js sample project (https://github.com/hagopj13/node-express-boilerplate) that has been receiving a lot of stars, comes with an MIT License and serves as an example for demonstrating how to use Docker-Blue-Green-Runner.
Expand Down Expand Up @@ -155,9 +155,21 @@ CONSUL_RESTART=false
# The value must be json or yaml type, which is injected into docker-compose-app-${app_env}.yml
DOCKER_COMPOSE_ENVIRONMENT={"MONGODB_URL":"mongodb://host.docker.internal:27017/node-boilerplate","NODE_ENV":"development"}
```

## Check states
```shell
bash check-current-states.sh

# an output sample below
# [DEBUG] ! Setting which (Blue OR Green) to deploy the App as... (Final Check) : blue_score : 80, green_score : 0, state : blue, new_state : green, state_for_emergency : blue, new_upstream : https://laravel_crud_boilerplate-green:8080.
# The higher the score a state receives, the more likely it is to be the currently running state. So the updated App should be deployed as the non-occupied state(=new_state).
# For the emergency script, there is another safer priority added over the results of scores. So, the 'state_for_emergency' is basically the same as the 'state' but can differ.
```

## Emergency
- Nginx (like when Nginx is NOT booted OR 502 error...)
```shell
# Automatically set the safe state & down and up Nginx
bash emergency-nginx-down-and-up.sh
# In case you need to manually set the Nginx to point to 'blue' or 'green'
bash emergency-nginx-down-and-up.sh blue
Expand Down Expand Up @@ -205,50 +217,60 @@ bash ./rollback.sh

## Structure
```shell
# [A] Get mandatory variables
check_necessary_commands
cache_global_vars
# The 'cache_all_states' in 'cache_global_vars' function decides which state should be deployed. If this is called later at a point in this script, states could differ.
local initially_cached_old_state=${state}
check_env_integrity

echo "[NOTICE] We will now deploy '${project_name}' in a way of 'Blue-Green'"

# [B] Set mandatory files
# These are all about passing variables from the .env to the docker-compose-${project_name}-local.yml
initiate_docker_compose
apply_env_service_name_onto_app_yaml
apply_ports_onto_nginx_yaml
apply_docker_compose_environment_onto_app_yaml

# Refer to .env.*.real
if [[ ${app_env} == 'real' ]]; then
apply_docker_compose_volumes_onto_app_real_yaml
fi
# [A] Get mandatory variables
check_necessary_commands
cache_global_vars
# The 'cache_all_states' in 'cache_global_vars' function decides which state should be deployed. If this is called later at a point in this script, states could differ.
local initially_cached_old_state=${state}
check_env_integrity

echo "[NOTICE] Finally, !! Deploy the App as !! ${new_state} !!, we will now deploy '${project_name}' in a way of 'Blue-Green'"

# [B] Set mandatory files
# These are all about passing variables from the .env to the docker-compose-${project_name}-local.yml
initiate_docker_compose
apply_env_service_name_onto_app_yaml
apply_ports_onto_nginx_yaml
apply_docker_compose_environment_onto_app_yaml

# Refer to .env.*.real
if [[ ${app_env} == 'real' ]]; then
apply_docker_compose_volumes_onto_app_real_yaml
fi

apply_docker_compose_volumes_onto_app_nginx_yaml
apply_docker_compose_volumes_onto_app_nginx_yaml

create_nginx_ctmpl
create_nginx_ctmpl

if [[ ${skip_building_app_image} != 'true' ]]; then
backup_app_to_previous_images
backup_nginx_to_previous_images
fi
if [[ ${skip_building_app_image} != 'true' ]]; then
backup_app_to_previous_images
fi
backup_nginx_to_previous_images

if [[ ${app_env} == 'local' ]]; then
give_host_group_id_full_permissions
fi
if [[ ${app_env} == 'local' ]]; then

if [[ ${docker_layer_corruption_recovery} == 'true' ]]; then
terminate_whole_system
fi
give_host_group_id_full_permissions
#else

# [B] Build Docker images for the App, Nginx, Consul
if [[ ${skip_building_app_image} != 'true' ]]; then
load_app_docker_image
load_consul_docker_image
load_nginx_docker_image
fi
# create_host_folders_if_not_exists
fi

if [[ ${docker_layer_corruption_recovery} == 'true' ]]; then
terminate_whole_system
fi

# [B] Build Docker images for the App, Nginx, Consul
if [[ ${skip_building_app_image} != 'true' ]]; then
load_app_docker_image
fi
load_consul_docker_image
load_nginx_docker_image

local cached_new_state=${new_state}
cache_all_states
if [[ ${cached_new_state} != "${new_state}" ]]; then
(echo "[ERROR] Just checked all states shortly after the Docker Images had been done built. The state the App was supposed to be deployed as has been changed. (Original : ${cached_new_state}, New : ${new_state}). For the safety, we exit..." && exit 1)
fi

# Run 'docker-compose up' for 'App', 'Consul (Service Mesh)' and 'Nginx' and
# Check if the App is properly working from the inside of the App's container using 'wait-for-it.sh ( https://github.com/vishnubob/wait-for-it )' and conducting a health check with settings defined on .env.
Expand Down Expand Up @@ -302,6 +324,7 @@ fi
echo "[NOTICE] Delete <none>:<none> images."
docker rmi $(docker images -f "dangling=true" -q) || echo "[NOTICE] Any images in use will not be deleted."

echo "[NOTICE] APP_URL : ${app_url}"
```

## Test
Expand Down
11 changes: 11 additions & 0 deletions check-current-states.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
sed -i -e "s/\r$//g" $(basename $0)
set -e

git config apply.whitespace nowarn
git config core.filemode false

source ./util.sh

cache_global_vars
cache_all_states
2 changes: 1 addition & 1 deletion emergency-nginx-down-and-up.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ nginx_down_and_up(){

}

echo "[NOTICE] Finally, !! to ${state_a}, we will now deploy '${project_name}' in a way of 'Blue-Green'"
echo "[NOTICE] Finally, !! Deploy the App as !! ${state_a} !!, we will now deploy '${project_name}' in a way of 'Blue-Green'"
nginx_down_and_up
./activate.sh ${state_a} ${state_b} ${state_upstream} ${consul_key_value_store}
45 changes: 9 additions & 36 deletions run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -313,41 +313,6 @@ load_all_containers(){

}

check_availability_out_of_container(){

echo "[NOTICE] Check the http status code from the outside of the container." >&2
sleep 1

for retry_count in {1..8}
do
status=$(curl ${app_url}/${app_health_check_path} -o /dev/null -k -Isw '%{http_code}' --connect-timeout 10)
available_status_cnt=$(echo ${status} | egrep -i '^2[0-9]+|3[0-9]+$' | wc -l)

if [[ ${available_status_cnt} -lt 1 ]]; then

echo "Bad HTTP response in the ${new_state} app: ${status}" >&2

if [[ ${retry_count} -eq 7 ]]
then
echo "[ERROR] Health Check Failed. (If you are not accessing an external domain (=closed network setting environment), you need to check if APP_URL is the value retrieved by ifconfig on the Ubuntu host. Access to the ip output by the WIN ipconfig command may fail. Or you need to check the network firewall." >&2
echo "false"
return
fi

else
echo "[NOTICE] Success. (Status (2xx, 3xx) : ${status})" >&2
break
fi

echo "[NOTICE] Retry once every 3 seconds for a total of 8 times..." >&2
sleep 3
done

echo 'true'
return

}

backup_to_new_images(){

echo "[NOTICE] docker tag latest new"
Expand All @@ -366,7 +331,7 @@ _main() {
local initially_cached_old_state=${state}
check_env_integrity

echo "[NOTICE] Finally, !! to ${new_state}, we will now deploy '${project_name}' in a way of 'Blue-Green'"
echo "[NOTICE] Finally, !! Deploy the App as !! ${new_state} !!, we will now deploy '${project_name}' in a way of 'Blue-Green'"

# [B] Set mandatory files
# These are all about passing variables from the .env to the docker-compose-${project_name}-local.yml
Expand Down Expand Up @@ -408,6 +373,12 @@ _main() {
load_consul_docker_image
load_nginx_docker_image

local cached_new_state=${new_state}
cache_all_states
if [[ ${cached_new_state} != "${new_state}" ]]; then
(echo "[ERROR] Just checked all states shortly after the Docker Images had been done built. The state the App was supposed to be deployed as has been changed. (Original : ${cached_new_state}, New : ${new_state}). For the safety, we exit..." && exit 1)
fi

# [C] Docker-compose up the App, Nginx, Consul & * Internal Integrity Check for the App
load_all_containers

Expand Down Expand Up @@ -445,6 +416,8 @@ _main() {

echo "[NOTICE] Delete <none>:<none> images."
docker rmi $(docker images -f "dangling=true" -q) || echo "[NOTICE] Any images in use will not be deleted."

echo "[NOTICE] APP_URL : ${app_url}"
}

_main
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
sed -i -e "s/\r$//g" $(basename $0)
set -eu

echo "[TEST][NOTICE] Check DB is connected (refer to Samples on README)."

cd ../../

sudo chmod a+x *.sh
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
sed -i -e "s/\r$//g" $(basename $0)
set -eu

echo "[TEST][NOTICE] Check DB is connected (refer to Samples on README)."

cd ../../

sudo chmod a+x *.sh
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/bash
sed -i -e "s/\r$//g" $(basename $0)
set -eu

cd ../../

sudo chmod a+x *.sh

echo "[NOTICE] Substituting CRLF with LF to prevent possible CRLF errors..."
bash prevent-crlf.sh
git config apply.whitespace nowarn
git config core.filemode false

container=$(docker ps --format '{{.Names}}' | grep "spring-sample-h-auth-[bg]")
if [ -z "$container" ]; then
echo "[NOTICE] There is NO spring-sample-h-auth container, now we will build it."
cp -f .env.java.real .env
sudo bash run.sh
else
echo "[NOTICE] $container exists."
fi

sleep 3
source ./util.sh

cache_global_vars

consul_pointing=$(docker exec ${project_name}-nginx curl ${consul_key_value_store}?raw 2>/dev/null || echo "failed")
the_opposite_of_consul_pointing=''
if [[ ${consul_pointing} == 'blue' ]]; then
the_opposite_of_consul_pointing='green'
else
the_opposite_of_consul_pointing='blue'
fi

echo "[TEST][DEBUG] the_opposite_of_consul_pointing : ${the_opposite_of_consul_pointing}"

echo "[TEST][NOTICE] To make a Nginx error, get consul_pointing to the wrong(=NOT running) container"
bash emergency-nginx-down-and-up.sh ${the_opposite_of_consul_pointing} || echo ""
#echo "[TEST][NOTICE] Run 'emergency-nginx-down-and-up.sh'"
#bash emergency-nginx-down-and-up.sh

echo "[TEST][NOTICE] Run check_availability_out_of_container"
cache_global_vars
re=$(check_availability_out_of_container | tail -n 1);

if [[ ${re} != 'true' ]]; then
echo "[TEST][NOTICE] : FAILURE"
else
echo "[TEST][NOTICE] : SUCCESS"
fi
Loading

0 comments on commit 96be344

Please sign in to comment.