Skip to content

Commit 8b3a335

Browse files
Merge pull request #1 from patternhelloworld/feat/isomorphic-safe-deployment
Feat/isomorphic safe deployment
2 parents 33c30f3 + 4e6af52 commit 8b3a335

20 files changed

+510
-65
lines changed

.docker/binary/.gitkeep

Whitespace-only changes.

.env.example

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,19 @@ NGINX_LOGROTATE_FILE_SIZE=1M
8787

8888
SHARED_VOLUME_GROUP_ID=1351
8989
SHARED_VOLUME_GROUP_NAME=shared-volume-group
90-
UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID=
90+
UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID=
91+
92+
ONLY_BUILDING_APP_IMAGE_FOR_PRODUCTION=false
93+
94+
REMOTE_DEPLOYMENT_RUNNER_PATH=
95+
# ex. ["10.0.0.1","10.0.0.2"]
96+
REMOTE_DEPLOYMENT_IP_ADDRESS_LIST=
97+
REMOTE_DEPLOYMENT_PORT_NUMBER_LIST=
98+
# ex. /home/deployer/.ssh/id_rsa
99+
REMOTE_DEPLOYMENT_SSH_PRIVATE_KEY_LOCAL_PATH_WITH_FILE=
100+
# ex. deployer
101+
REMOTE_DEPLOYMENT_SSH_USER=
102+
# ex. rollback, stop, go
103+
REMOTE_DEPLOYMENT_FAILURE_STRATEGY=
104+
105+
WITH_SUDO=true

.env.example.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,15 @@
7979

8080
SHARED_VOLUME_GROUP_ID=1351
8181
SHARED_VOLUME_GROUP_NAME=shared-volume-group
82-
UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID=
82+
UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID=
83+
84+
ONLY_BUILDING_APP_IMAGE_FOR_PRODUCTION=false
85+
86+
REMOTE_DEPLOYMENT_RUNNER_PATH=
87+
REMOTE_DEPLOYMENT_IP_ADDRESS_LIST=
88+
REMOTE_DEPLOYMENT_PORT_NUMBER_LIST=
89+
REMOTE_DEPLOYMENT_SSH_PRIVATE_KEY_LOCAL_PATH_WITH_FILE=
90+
REMOTE_DEPLOYMENT_SSH_USER=
91+
REMOTE_DEPLOYMENT_FAILURE_STRATEGY=
92+
93+
WITH_SUDO=true

.env.example.java.commercial.ssl.sample

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,15 @@ NGINX_LOGROTATE_FILE_SIZE=1M
8181

8282
SHARED_VOLUME_GROUP_ID=1351
8383
SHARED_VOLUME_GROUP_NAME=shared-volume-group
84-
UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID=
84+
UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID=
85+
86+
ONLY_BUILDING_APP_IMAGE_FOR_PRODUCTION=false
87+
88+
REMOTE_DEPLOYMENT_RUNNER_PATH=
89+
REMOTE_DEPLOYMENT_IP_ADDRESS_LIST=
90+
REMOTE_DEPLOYMENT_PORT_NUMBER_LIST=
91+
REMOTE_DEPLOYMENT_SSH_PRIVATE_KEY_LOCAL_PATH_WITH_FILE=
92+
REMOTE_DEPLOYMENT_SSH_USER=
93+
REMOTE_DEPLOYMENT_FAILURE_STRATEGY=
94+
95+
WITH_SUDO=true

.env.example.node

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,15 @@ NGINX_LOGROTATE_FILE_SIZE=1M
7474

7575
SHARED_VOLUME_GROUP_ID=1351
7676
SHARED_VOLUME_GROUP_NAME=shared-volume-group
77-
UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID=
77+
UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID=
78+
79+
ONLY_BUILDING_APP_IMAGE_FOR_PRODUCTION=false
80+
81+
REMOTE_DEPLOYMENT_RUNNER_PATH=
82+
REMOTE_DEPLOYMENT_IP_ADDRESS_LIST=
83+
REMOTE_DEPLOYMENT_PORT_NUMBER_LIST=
84+
REMOTE_DEPLOYMENT_SSH_PRIVATE_KEY_LOCAL_PATH_WITH_FILE=
85+
REMOTE_DEPLOYMENT_SSH_USER=
86+
REMOTE_DEPLOYMENT_FAILURE_STRATEGY=
87+
88+
WITH_SUDO=true

.env.example.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,15 @@
8080

8181
SHARED_VOLUME_GROUP_ID=1351
8282
SHARED_VOLUME_GROUP_NAME=laravel-shared-volume-group
83-
UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID=1000
83+
UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID=1000
84+
85+
ONLY_BUILDING_APP_IMAGE_FOR_PRODUCTION=false
86+
87+
REMOTE_DEPLOYMENT_RUNNER_PATH=
88+
REMOTE_DEPLOYMENT_IP_ADDRESS_LIST=
89+
REMOTE_DEPLOYMENT_PORT_NUMBER_LIST=
90+
REMOTE_DEPLOYMENT_SSH_PRIVATE_KEY_LOCAL_PATH_WITH_FILE=
91+
REMOTE_DEPLOYMENT_SSH_USER=
92+
REMOTE_DEPLOYMENT_FAILURE_STRATEGY=
93+
94+
WITH_SUDO=true

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
!/.docker/ssl/.gitkeep
44
/.docker/nginx/custom-files/*
55
!/.docker/nginx/custom-files/.gitkeep
6+
/.docker/binary/*
7+
!/.docker/binary/.gitkeep
68

79
/shared/app-error-logs/*
810
!/shared/app-error-logs/.gitkeep

README.md

Lines changed: 75 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Docker-Blue-Green-Runner
22

3-
> A Simple and Safe Blue-Green Deployment Starting from Your Source Code—Not from Your Prebuilt Docker Image
3+
> An Isomorphic Blue-Green Deployment Starting from Your Source Code—Not from Your Prebuilt Docker Image
44
55
> [NOTE] To upgrade your app from v5 to v6, update your .env file with the following settings and proceed:
66
```.dotenv
@@ -50,39 +50,44 @@
5050
## Features
5151

5252
1. **Achieve zero-downtime deployment using just your ``.env`` and ``Dockerfile``**
53-
- 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.
53+
- 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'." If you prefer not to use `sudo`, see [WITH_SUDO](#with_sudo), set it in your `.env`, and run `apply-security.sh` first. This script covers the entire process from Dockerfile build to server deployment from scratch.
5454
- This means you can easily migrate to another server with just the files mentioned above.
5555
- In contrast, Traefik requires the creation and gradual adjustment of various configuration files, which requires your App's docker binary running.
5656

5757

58-
2. **No unpredictable errors in reverse proxy and deployment : Implement safety measures to handle errors caused by your app or Nginx**
59-
- If any error occurs in the app or router, ``deployment is halted`` to prevent any impact on the existing deployment
60-
- Internal Integrity Check:
61-
- Step 1: Use wait-for-it.sh (https://github.com/vishnubob/wait-for-it)
62-
- Step 2: Perform a health check with customized settings defined in your .env file
63-
- Nginx Router Test Container
64-
- External Integrity Check
65-
- Rollback Procedures
66-
- Additional Know-hows on Docker: Tips and best practices for optimizing your Docker workflow and deployment processes
67-
- 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.
68-
- https://stackoverflow.com/questions/76660749/traefik-404-page-not-found-when-use-https
69-
- https://community.traefik.io/t/getting-bad-gateway-404-page-when-supposed-to-route-to-container-port-8443/20398
70-
- Manipulates NGINX configuration files directly to ensure container accessibility.
58+
2. **[Beta] Isomorphic local-and-remote runner**
59+
- The same `run.sh` and `.env` drive deployments locally and on remote servers over SSH.
60+
- Remote servers receive the image binary and execute the same pipeline with `GIT_IMAGE_LOAD_FROM=file` (see [Production > GIT_IMAGE_LOAD_FROM=file](#1-git_image_load_fromfile-strategy-without-docker-registry)).
61+
- Behavior stays consistent across environments; only the image source differs (build/registry/file).
7162

63+
3. **No unpredictable errors in reverse proxy and deployment : Implement safety measures to handle errors caused by your app or Nginx**
64+
- If any error occurs in the app or router, ``deployment is halted`` to prevent any impact on the existing deployment
65+
- Internal Integrity Check:
66+
- Step 1: Use wait-for-it.sh (https://github.com/vishnubob/wait-for-it)
67+
- Step 2: Perform a health check with customized settings defined in your .env file
68+
- Nginx Router Test Container
69+
- External Integrity Check
70+
- Rollback Procedures
71+
- Additional Know-hows on Docker: Tips and best practices for optimizing your Docker workflow and deployment processes
72+
- 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.
73+
- https://stackoverflow.com/questions/76660749/traefik-404-page-not-found-when-use-https
74+
- https://community.traefik.io/t/getting-bad-gateway-404-page-when-supposed-to-route-to-container-port-8443/20398
75+
- Manipulates NGINX configuration files directly to ensure container accessibility.
7276

73-
3. **Track Blue-Green status and the Git SHA of your running container for easy monitoring.**
74-
- Blue-Green deployment decision algorithm: scoring-based approach
75-
- Run the command bash ``check-current-status.sh`` (similar to ``git status``) to view all relevant details
76-
-
77-
-
78-
![img7.png](documents/images/img7.png)
7977

78+
4. **Track Blue-Green status and the Git SHA of your running container for easy monitoring.**
79+
- Blue-Green deployment decision algorithm: scoring-based approach
80+
- Run `bash check-current-states.sh` locally and `bash check-remote-current-states.sh` to fan out the same check to all configured remotes
81+
-
82+
-
83+
![img7.png](documents/images/img7.png)
8084

81-
4. **Security**
85+
86+
5. **Security**
8287
- Refer to the [Security](#Security) section
8388

8489

85-
5. **Production Deployment**
90+
6. **Production Deployment**
8691
- Refer to the [Production Deployment](#production-deployment) section
8792

8893
## Process Summary
@@ -255,6 +260,14 @@ sudo bash run.sh
255260

256261
### Information on Environment Variables
257262

263+
#### ``WITH_SUDO``
264+
```dotenv
265+
WITH_SUDO=true
266+
```
267+
- When `true`, the runner executes privileged operations with `sudo` where needed.
268+
- When `false`, `sudo` is not used. After installing Docker-Blue-Green-Runner, follow the steps in the [Security](#security) section and then run the root-level `apply-security.sh` to set secure permissions. Also, grant appropriate host permissions for Docker, Nginx, and related resources to the user running the runner.
269+
- For security, it is recommended to keep this `false` where possible and rely on proper host permissions and ACLs instead of broad sudo usage.
270+
258271
#### ``APP_URL``
259272
- ```shell
260273
APP_URL=http://localhost:<--!host-port-number!-->
@@ -458,9 +471,42 @@ bash check-source-integrity.sh
458471
459472
## Production Deployment
460473
- Up to this point, your app has been running in a Docker container through the ``bash.run.sh`` command, enabling continuous Blue-Green deployments. However, you may want to deploy the built Docker image independently to another environment, and you likely wouldn't want to leave unnecessary source code, except for the Docker images and configuration files, on the production server.
461-
- It is recommended to automate this process using Jenkins.
474+
- The key environment variable enabling this is the ``GIT_IMAGE_LOAD_FROM``. Up to this point, ``GIT_IMAGE_LOAD_FROM`` has been set to ``build``.
475+
476+
### 1. ``GIT_IMAGE_LOAD_FROM=file`` strategy (Beta. without Docker Registry)
477+
- With Load Balancer
478+
```mermaid
479+
graph TD;
480+
A[Load Balancer] --->|Distribute Traffic| B[Server 1]
481+
A --->|Distribute Traffic| C[Server 2]
482+
A --->|Distribute Traffic| D[Server 3]
483+
E[Build Server] -->|Send Docker Image Binary| B[Server 1]
484+
E -->|Send Docker Image Binary| C[Server 2]
485+
E -->|Send Docker Image Binary| D[Server 3]
486+
F[Git] -->|Github Action or Jenkins| E[Build Server]
487+
```
488+
- Set the Load Balancer to use "Round-Robin"
489+
- What is "Round-Robin"?
490+
- Round-robin is a load-balancing method that distributes incoming requests evenly across all available servers in a sequential order. For example, the first request goes to Server 1, the second request to Server 2, the third request to Server 3, and then it cycles back to Server 1. This ensures a balanced distribution of traffic across the servers.
491+
- Your Github Action or Jenkins scripts just send the source codes to the Build Server or run ``git pull`` on the server.
492+
- Set the 'Docker-Blue-Green-Runner' on the Build Server, and run ``run.sh`` with ``ONLY_BUILDING_APP_IMAGE_FOR_PRODUCTION`` set to ``true``.
493+
- The ``ONLY_BUILDING_APP_IMAGE_FOR_PRODUCTION=true`` creates the Docker binary file to ``./.docker/binary``
494+
- If ``REMOTE_DEPLOYMENT_RUNNER_PATH``, ``REMOTE_DEPLOYMENT_IP_ADDRESS_LIST``, ``REMOTE_DEPLOYMENT_PORT_NUMBER_LIST``, and ``REMOTE_DEPLOYMENT_SSH_PRIVATE_KEY_LOCAL_PATH_WITH_FILE`` are set:
495+
- The Runner automatically copies the binary to each server at ``${REMOTE_DEPLOYMENT_RUNNER_PATH}/.docker/binary``.
496+
- If ``REMOTE_DEPLOYMENT_FAILURE_STRATEGY`` is set (``stop`` | ``rollback`` | ``go``), the Runner connects via SSH and executes ``sudo bash run.sh`` on each server after pre-checks (``GIT_IMAGE_LOAD_FROM=file`` and sudo). Failures follow the configured strategy.
497+
- If ``REMOTE_DEPLOYMENT_FAILURE_STRATEGY`` is NOT set, you can perform the steps manually:
498+
- Set ``GIT_IMAGE_LOAD_FROM=file`` on each server.
499+
- Copy the binary to each server's ``${REMOTE_DEPLOYMENT_RUNNER_PATH}/.docker/binary``.
500+
- Run ``run.sh`` on ``Server 1``; if any issues are found, run ``rollback.sh``.
501+
- If no problems are detected, run ``run.sh`` on both ``Server 2`` and ``Server 3``.
462502
463-
### Upload Image (CI/CD Server -> Git)
503+
- Tip: For smooth permission and volume access, include the UID of the ``REMOTE_DEPLOYMENT_SSH_USER`` in ``UIDS_BELONGING_TO_SHARED_VOLUME_GROUP_ID`` (in your `.env`).
504+
505+
- CI tip: If you set ``REMOTE_DEPLOYMENT_SSH_PRIVATE_KEY_LOCAL_PATH_WITH_FILE`` and ``REMOTE_DEPLOYMENT_SSH_USER`` to match your GitHub Actions credentials, and your workflow triggers ``sudo bash run.sh`` on the build server, then with ``REMOTE_DEPLOYMENT_FAILURE_STRATEGY`` configured, the Runner can perform end-to-end distribution and remote execution in a single run.
506+
507+
508+
### 2. ``GIT_IMAGE_LOAD_FROM=registry`` strategy (with Docker Registry)
509+
#### Upload Image (CI/CD Server -> Git)
464510
- If you run the ``push-to-git.sh`` command, it pushes the container image currently running on the test server to the ``Git Container Registry`` at the specified address.
465511
```shell
466512
GIT_IMAGE_LOAD_FROM_HOST=mysite.com:5050
@@ -477,7 +523,7 @@ bash check-source-integrity.sh
477523
- Solution
478524
- Place your CA's root certificate (.crt file) in /usr/local/share/ca-certificates/ and run sudo update-ca-certificates.
479525

480-
### Download Image (Git -> Production Server)
526+
#### Download Image (Git -> Production Server)
481527
- Your production server should have docker-blue-green-runner, .env, and run ``run.sh`` to deploy the built images above.
482528
- The only difference is to set ``GIT_IMAGE_LOAD_FROM=registry`` instead of ``GIT_IMAGE_LOAD_FROM=build``.
483529
- When ``GIT_IMAGE_LOAD_FROM`` is set to ``build``, docker-blue-green-runner ``builds your Dockerfile``. However, when it is set to ``registry``, the runner ``downloads your images from the Git Container registry``.
@@ -490,8 +536,7 @@ bash check-source-integrity.sh
490536
GIT_TOKEN_IMAGE_LOAD_FROM_PASSWORD=bar
491537
GIT_IMAGE_VERSION=1.0.0
492538
```
493-
494-
### With Load Balancer
539+
- With Load Balancer
495540
```mermaid
496541
graph TD;
497542
A[Load Balancer] --->|Distribute Traffic| B[Server 1]
@@ -500,12 +545,12 @@ graph TD;
500545
E[Git] -->|Download Image| B[Server 1]
501546
E -->|Download Image| C[Server 2]
502547
E -->|Download Image| D[Server 3]
503-
F[CI/CD Server] -->|Upload Image| E[Git]
548+
F[Build Server] -->|Upload Image| E[Git]
504549
```
505550
- Set the Load Balancer to use "Round-Robin"
506551
- What is "Round-Robin"?
507552
- Round-robin is a load-balancing method that distributes incoming requests evenly across all available servers in a sequential order. For example, the first request goes to Server 1, the second request to Server 2, the third request to Server 3, and then it cycles back to Server 1. This ensures a balanced distribution of traffic across the servers.
508-
- Set the 'Docker-Blue-Green-Runner' on each server.
553+
- Set the 'Docker-Blue-Green-Runner' on each server with ``GIT_IMAGE_LOAD_FROM`` set to ``registry``.
509554
- Run ``run.sh`` on ``Server 1``
510555
- Check the logs on ``Server 1``, if any issues are found, run the command ``rollback.sh``.
511556
- If no problems are detected, run the command ``run.sh`` on both ``Server 2`` and ``Server 3``.

apply-security.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/bin/bash
22
set -eu
33

4+
# [NOTICE] This script should be run with "sudo".
5+
46
source use-common.sh
57

68
check_bash_version
@@ -73,4 +75,15 @@ if [[ "$(uname)" != "Darwin" ]]; then
7375
set_safe_filemode_on_volumes
7476
else
7577
echo "[NOTICE] Skipping chown command on Darwin (macOS) platform. See the README."
78+
fi
79+
80+
# Add host users to shared volume group (Linux only)
81+
if [[ "$(uname)" != "Darwin" ]]; then
82+
echo "[NOTICE] Adding host users to shared volume group (gid=${shared_volume_group_id}, name=${shared_volume_group_name})"
83+
add_host_users_to_shared_volume_group_re=$(add_host_users_to_host_group_with_sudo ${shared_volume_group_id} ${shared_volume_group_name} ${uids_belonging_to_shared_volume_group_id} | tail -n 1) || echo "[WARNING] Running 'add_host_users_to_shared_volume_group' failed."
84+
if [[ ${add_host_users_to_shared_volume_group_re} = 'false' ]]; then
85+
echo "[WARNING] Running 'add_host_users_to_host_group_with_sudo'(SHARED) failed."
86+
fi
87+
else
88+
echo "[NOTICE] Skipping 'add_host_users_to_host_group_with_sudo' on Darwin (macOS)."
7689
fi

check-remote-current-states.sh

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/bin/bash
2+
set -eu
3+
4+
source ./use-common.sh
5+
6+
# Read required env values directly to avoid heavy checks
7+
runner_path=$(get_value_from_env "REMOTE_DEPLOYMENT_RUNNER_PATH")
8+
ip_list=$(get_value_from_env "REMOTE_DEPLOYMENT_IP_ADDRESS_LIST")
9+
port_list=$(get_value_from_env "REMOTE_DEPLOYMENT_PORT_NUMBER_LIST")
10+
key_file=$(get_value_from_env "REMOTE_DEPLOYMENT_SSH_PRIVATE_KEY_LOCAL_PATH_WITH_FILE")
11+
ssh_user=$(get_value_from_env "REMOTE_DEPLOYMENT_SSH_USER")
12+
13+
if [[ -z "${runner_path}" ]]; then
14+
echo "[ERROR] REMOTE_DEPLOYMENT_RUNNER_PATH is empty." && exit 1
15+
fi
16+
17+
if [[ -z "${ip_list}" || -z "${port_list}" || -z "${key_file}" ]]; then
18+
echo "[ERROR] One or more required REMOTE_DEPLOYMENT_* variables are empty. (IP, PORT, KEY)" && exit 1
19+
fi
20+
21+
check_yq_installed
22+
23+
ip_len=$(echo ${ip_list} | bin/yq eval 'length')
24+
port_len=$(echo ${port_list} | bin/yq eval 'length')
25+
26+
if [[ ${ip_len} -eq 0 || ${port_len} -eq 0 ]]; then
27+
echo "[ERROR] IP/PORT list is empty. (ip_len=${ip_len}, port_len=${port_len})" && exit 1
28+
fi
29+
30+
if [[ ${ip_len} -ne ${port_len} ]]; then
31+
echo "[ERROR] REMOTE_DEPLOYMENT_* list lengths mismatch. (ip=${ip_len}, port=${port_len})" && exit 1
32+
fi
33+
34+
echo "[NOTICE] Running 'check-current-states.sh' on ${ip_len} remote host(s)..."
35+
36+
for ((i=1; i<=${ip_len}; i++))
37+
do
38+
ip_item=$(echo ${ip_list} | bin/yq -r '.['$((i-1))']' | xargs)
39+
port_item=$(echo ${port_list} | bin/yq -r '.['$((i-1))']' | xargs)
40+
41+
if [[ -z "${ip_item}" || -z "${port_item}" ]]; then
42+
echo "[ERROR] Invalid remote at index $((i-1)). (ip='${ip_item}', port='${port_item}')" >&2
43+
continue
44+
fi
45+
46+
user_part="${ssh_user:-root}"
47+
remote_host="${ip_item}"
48+
if [[ "${ip_item}" != *@* ]]; then
49+
remote_host="${user_part}@${ip_item}"
50+
fi
51+
52+
echo "[NOTICE] (${i}/${ip_len}) ${remote_host}:${port_item} - connectivity check"
53+
if ! ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=8 -p "${port_item}" -i "${key_file}" "${remote_host}" "echo yes" >/dev/null 2>&1; then
54+
echo "[ERROR] SSH connection failed: ${remote_host}:${port_item}" >&2
55+
continue
56+
fi
57+
58+
echo "[NOTICE] (${i}/${ip_len}) ${remote_host}:${port_item} - running 'bash check-current-states.sh'"
59+
ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=30 -p "${port_item}" -i "${key_file}" "${remote_host}" \
60+
"set -eu; cd '${runner_path}' && bash check-current-states.sh" \
61+
| sed -e "s/^/[${ip_item}] /" || echo "[ERROR] Command failed on ${remote_host}" >&2
62+
done
63+
64+
echo "[NOTICE] Remote 'check-current-states.sh' calls completed."
65+
66+

0 commit comments

Comments
 (0)