Skip to content

要实现无感部署,即在更新应用程序时不影响用户访问接口,可以考虑使用以下方法:

1、使用负载均衡器:在部署应用程序时,通过负载均衡器将流量分发到多个实例上。这样,当更新一个实例时,其他实例仍然可以继续提供服务,从而保证用户访问接口的稳定性。

2、Blue-Green 部署:使用Blue-Green 部署策略,即同时部署两个版本的应用程序,一个作为主要版本(Blue),一个作为备用版本(Green)。当需要更新应用程序时,先将新版本部署为备用版本,然后逐渐将流量切换到新版本,最终将旧版本下线。这样可以保证用户在更新过程中不受影响。

3、Canary 部署:使用Canary 部署策略,即逐步将新版本应用程序部署到生产环境中的一小部分节点,然后逐渐扩大部署范围。这样可以在更新过程中逐步验证新版本的稳定性,避免一次性影响所有用户。

通过以上方法,您可以实现无感部署,确保在更新应用程序时不影响用户访问接口。同时,建议在部署过程中进行充分的测试和监控,以确保更新过程的稳定性和可用性。

环境

  • 服务器:CentOS 7.9.2009
  • docker-compose版本:3.9
  • docker版本:20.10.12
  • 根目录在/docker/mes

docker-compose.yml

version: "3.9"
services:
  fcat-mes-main-blue:
    image: adoptopenjdk/openjdk8-openj9:alpine-slim
    container_name: fcat-mes-main-blue
    restart: always
    ports:
      - "16219:11001"
    volumes:
      - ./fcat-mes-main-blue/app:/app
      - /etc/localtime:/etc/localtime
    environment:
      - TZ=Asia/Shanghai
    command: [
          'java',
          '-Xmx600m',
          '-jar',
          '/app/fcat-mes-main.jar'
         ]
    deploy:
      resources:
        limits:
          memory: 800M
  fcat-mes-main-green:
    image: adoptopenjdk/openjdk8-openj9:alpine-slim
    container_name: fcat-mes-main-green
    restart: always
    ports:
      - "16220:11001"
    volumes:
      - ./fcat-mes-main-green/app:/app
      - /etc/localtime:/etc/localtime
    environment:
      - TZ=Asia/Shanghai
    command: [
          'java',
          '-Xmx600m',
          '-jar',
          '/app/fcat-mes-main.jar'
         ]
    deploy:
      resources:
        limits:
          memory: 800M
  fcat-nginx:
    image: nginx:1.21.0
    container_name: fcat-nginx
    restart: always
    privileged: true
    environment:
      - TZ=Asia/Shanghai 
    ports:
      - 16221:16221
    volumes:
      - /etc/localtime:/etc/localtime
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/log:/var/log/nginx
      - ./nginx/web:/web
      - ./logs:/logs
    deploy:
      resources:
        limits:
          memory: 1024M
version: "3.9"
services:
  fcat-mes-main-blue:
    image: adoptopenjdk/openjdk8-openj9:alpine-slim
    container_name: fcat-mes-main-blue
    restart: always
    ports:
      - "16219:11001"
    volumes:
      - ./fcat-mes-main-blue/app:/app
      - /etc/localtime:/etc/localtime
    environment:
      - TZ=Asia/Shanghai
    command: [
          'java',
          '-Xmx600m',
          '-jar',
          '/app/fcat-mes-main.jar'
         ]
    deploy:
      resources:
        limits:
          memory: 800M
  fcat-mes-main-green:
    image: adoptopenjdk/openjdk8-openj9:alpine-slim
    container_name: fcat-mes-main-green
    restart: always
    ports:
      - "16220:11001"
    volumes:
      - ./fcat-mes-main-green/app:/app
      - /etc/localtime:/etc/localtime
    environment:
      - TZ=Asia/Shanghai
    command: [
          'java',
          '-Xmx600m',
          '-jar',
          '/app/fcat-mes-main.jar'
         ]
    deploy:
      resources:
        limits:
          memory: 800M
  fcat-nginx:
    image: nginx:1.21.0
    container_name: fcat-nginx
    restart: always
    privileged: true
    environment:
      - TZ=Asia/Shanghai 
    ports:
      - 16221:16221
    volumes:
      - /etc/localtime:/etc/localtime
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/log:/var/log/nginx
      - ./nginx/web:/web
      - ./logs:/logs
    deploy:
      resources:
        limits:
          memory: 1024M

nginx/conf.d/main.conf

server {
    listen       16221;
    listen  [::]:16221;
    charset utf-8,gbk;
    client_max_body_size 10m;  # 设置最大上传文件大小为10MB
 
    location /dev-api/ {
		proxy_set_header Host $http_host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header REMOTE-HOST $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass   http://mes_main_service/;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

upstream  mes_main_service{
    server fcat-mes-main-blue:11001;
    server fcat-mes-main-green:11001 weight=100;
}
server {
    listen       16221;
    listen  [::]:16221;
    charset utf-8,gbk;
    client_max_body_size 10m;  # 设置最大上传文件大小为10MB
 
    location /dev-api/ {
		proxy_set_header Host $http_host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header REMOTE-HOST $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass   http://mes_main_service/;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

upstream  mes_main_service{
    server fcat-mes-main-blue:11001;
    server fcat-mes-main-green:11001 weight=100;
}

nginx/conf.d/blue.conf_bak

server {
    listen       16221;
    listen  [::]:16221;
    charset utf-8,gbk;
    client_max_body_size 10m;  # 设置最大上传文件大小为10MB
 
    location /dev-api/ {
		proxy_set_header Host $http_host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header REMOTE-HOST $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass   http://mes_main_service/;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

upstream  mes_main_service{
    server fcat-mes-main-blue:11001 weight=100;
    server fcat-mes-main-green:11001 ;
}
server {
    listen       16221;
    listen  [::]:16221;
    charset utf-8,gbk;
    client_max_body_size 10m;  # 设置最大上传文件大小为10MB
 
    location /dev-api/ {
		proxy_set_header Host $http_host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header REMOTE-HOST $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass   http://mes_main_service/;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

upstream  mes_main_service{
    server fcat-mes-main-blue:11001 weight=100;
    server fcat-mes-main-green:11001 ;
}

nginx/conf.d/green.conf_bak

server {
    listen       16221;
    listen  [::]:16221;
    charset utf-8,gbk;
    client_max_body_size 10m;  # 设置最大上传文件大小为10MB
 
    location /dev-api/ {
		proxy_set_header Host $http_host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header REMOTE-HOST $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass   http://mes_main_service/;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

upstream  mes_main_service{
    server fcat-mes-main-blue:11001;
    server fcat-mes-main-green:11001 weight=100;
}
server {
    listen       16221;
    listen  [::]:16221;
    charset utf-8,gbk;
    client_max_body_size 10m;  # 设置最大上传文件大小为10MB
 
    location /dev-api/ {
		proxy_set_header Host $http_host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header REMOTE-HOST $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass   http://mes_main_service/;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

upstream  mes_main_service{
    server fcat-mes-main-blue:11001;
    server fcat-mes-main-green:11001 weight=100;
}

运行服务

shell
docker-compose up -d
docker-compose up -d
curl http://192.168.9.126:16219/test/version
{
"version": "v1"
}

curl http://192.168.9.126:16220/test/version
{
"version": "v2"
}

curl http://192.168.9.126:16221/dev-api/test/version
{
"version": "v2"
}
curl http://192.168.9.126:16219/test/version
{
"version": "v1"
}

curl http://192.168.9.126:16220/test/version
{
"version": "v2"
}

curl http://192.168.9.126:16221/dev-api/test/version
{
"version": "v2"
}

bluegreen.txt

green
green

deploy.sh

#!/bin/bash
# 记录当前使用蓝色版本还是绿色版本
filename="bluegreen.txt"
# 读取当前使用版本
content=$(cat "$filename")
# 蓝色版本
blue_app_name="fcat-mes-main-blue"
# 绿色版本
green_app_name="fcat-mes-main-green"
# jar应用名称
jar_name="fcat-mes-main"
# 指定要匹配的字符串
search_string="Application Start Success"

echo "当前部署版本:$content" 
case $content in
  "blue")
	rm -rf nginx/conf.d/main.conf
	cp  nginx/conf.d/green.conf_bak  nginx/conf.d/main.conf 
	rm -rf  $green_app_name/app/$jar_name.jar_bak  
	cp  $green_app_name/app/$jar_name.jar $green_app_name/app/$jar_name.jar_bak  
	rm -rf $green_app_name/app/$jar_name.jar  
	cp $jar_name.jar  $green_app_name/app/
	echo "" > output.log
	docker-compose restart $green_app_name   
	timeout 60  docker-compose logs -f --tail=0 $green_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done > output.log 2>&1
	if grep -q "$search_string" output.log; then
		echo "$search_string"
		docker-compose  exec fcat-nginx nginx -s reload
		echo "green" > bluegreen.txt
	else
		echo "Error detected in logs. Rolling back..."
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/main.conf
		cp nginx/conf.d/blue.conf_bak  nginx/conf.d/main.conf
		rm -rf $green_app_name/app/$jar_name.jar
		cp  $green_app_name/app/$jar_name.jar_bak $green_app_name/app/$jar_name.jar  
		docker-compose restart $green_app_name
		
	fi
  ;;
  "green")
	rm -rf nginx/conf.d/main.conf
	cp nginx/conf.d/blue.conf_bak  nginx/conf.d/main.conf
	rm -rf $blue_app_name/app/$jar_name.jar_bak  
	cp  $blue_app_name/app/$jar_name.jar $blue_app_name/app/$jar_name.jar_bak  
	rm -rf $blue_app_name/app/$jar_name.jar   
	cp $jar_name.jar  $blue_app_name/app/
	echo "" > output.log
	docker-compose restart $blue_app_name  
	timeout 60  docker-compose logs -f  --tail=0 $blue_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			echo "Found the search string. Exiting..."
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done  > output.log 2>&1
	if grep -q "$search_string" output.log; then
		echo "$search_string"
		docker-compose  exec fcat-nginx nginx -s reload
		echo "blue" > bluegreen.txt
	else
		echo "Error detected in logs. Rolling back..."
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/main.conf
		cp nginx/conf.d/green.conf_bak  nginx/conf.d/main.conf
		rm -rf $blue_app_name/app/$jar_name.jar
		cp  $blue_app_name/app/$jar_name.jar_bak $blue_app_name/app/$jar_name.jar  
		docker-compose restart $blue_app_name
	fi 
  ;;
  "*")
      echo "bluegreen.txt 内容不对"
  ;;
esac
#!/bin/bash
# 记录当前使用蓝色版本还是绿色版本
filename="bluegreen.txt"
# 读取当前使用版本
content=$(cat "$filename")
# 蓝色版本
blue_app_name="fcat-mes-main-blue"
# 绿色版本
green_app_name="fcat-mes-main-green"
# jar应用名称
jar_name="fcat-mes-main"
# 指定要匹配的字符串
search_string="Application Start Success"

echo "当前部署版本:$content" 
case $content in
  "blue")
	rm -rf nginx/conf.d/main.conf
	cp  nginx/conf.d/green.conf_bak  nginx/conf.d/main.conf 
	rm -rf  $green_app_name/app/$jar_name.jar_bak  
	cp  $green_app_name/app/$jar_name.jar $green_app_name/app/$jar_name.jar_bak  
	rm -rf $green_app_name/app/$jar_name.jar  
	cp $jar_name.jar  $green_app_name/app/
	echo "" > output.log
	docker-compose restart $green_app_name   
	timeout 60  docker-compose logs -f --tail=0 $green_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done > output.log 2>&1
	if grep -q "$search_string" output.log; then
		echo "$search_string"
		docker-compose  exec fcat-nginx nginx -s reload
		echo "green" > bluegreen.txt
	else
		echo "Error detected in logs. Rolling back..."
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/main.conf
		cp nginx/conf.d/blue.conf_bak  nginx/conf.d/main.conf
		rm -rf $green_app_name/app/$jar_name.jar
		cp  $green_app_name/app/$jar_name.jar_bak $green_app_name/app/$jar_name.jar  
		docker-compose restart $green_app_name
		
	fi
  ;;
  "green")
	rm -rf nginx/conf.d/main.conf
	cp nginx/conf.d/blue.conf_bak  nginx/conf.d/main.conf
	rm -rf $blue_app_name/app/$jar_name.jar_bak  
	cp  $blue_app_name/app/$jar_name.jar $blue_app_name/app/$jar_name.jar_bak  
	rm -rf $blue_app_name/app/$jar_name.jar   
	cp $jar_name.jar  $blue_app_name/app/
	echo "" > output.log
	docker-compose restart $blue_app_name  
	timeout 60  docker-compose logs -f  --tail=0 $blue_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			echo "Found the search string. Exiting..."
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done  > output.log 2>&1
	if grep -q "$search_string" output.log; then
		echo "$search_string"
		docker-compose  exec fcat-nginx nginx -s reload
		echo "blue" > bluegreen.txt
	else
		echo "Error detected in logs. Rolling back..."
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/main.conf
		cp nginx/conf.d/green.conf_bak  nginx/conf.d/main.conf
		rm -rf $blue_app_name/app/$jar_name.jar
		cp  $blue_app_name/app/$jar_name.jar_bak $blue_app_name/app/$jar_name.jar  
		docker-compose restart $blue_app_name
	fi 
  ;;
  "*")
      echo "bluegreen.txt 内容不对"
  ;;
esac

deploy-ifms.sh

shell
#!/bin/bash
cd /docker/mes/

# 记录当前使用蓝色版本还是绿色版本
filename="ifms-bluegreen.txt"
# 读取当前使用版本
content=$(cat "$filename")
# 蓝色版本
blue_app_name="ifms-admin-blue"
# 绿色版本
green_app_name="ifms-admin-green"
# jar应用名称
jar_name="ifms-admin"
# 指定要匹配的字符串
search_string="Application Start Success"
error_string="Application Start Failed"
# 抓取的日志记录文件
log_file="ifms-output.log"
# nginx配置文件,不需要.conf后缀
nginx_file="mes"

wait_minutes=120
# 公司的ID
companyId=1

echo "当前部署版本:$content" 

# 参数:json_data='{"companyId": "11", "status": "1", "msg": "中文哈哈"}'
send_mail(){
	local json_data=$1
	echo "$json_data"
	# HTTP 接口地址和参数
	url="http://www.tsuantone.top:16020/system/mail/deploy1" 
	# 发送 POST 请求,指定 Content-Type 为 application/json
	response=$(curl -s -X POST -H "Content-Type: application/json" -d "$json_data" "$url")
	# 输出响应结果
	echo "Response: $response"
}

escape_string() {
  local string="$1"
  escaped_string=$(echo "$string" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g; s/\t/\\t/g')
  echo "$escaped_string"
}

case $content in
  "blue")
	rm -rf nginx/conf.d/$nginx_file.conf
	cp  nginx/conf.d/$nginx_file-green.conf_bak  nginx/conf.d/$nginx_file.conf 
	rm -rf  $green_app_name/app/$jar_name.jar_bak  
	cp  $green_app_name/app/$jar_name.jar $green_app_name/app/$jar_name.jar_bak  
	rm -rf $green_app_name/app/$jar_name.jar  
	cp $jar_name.jar  $green_app_name/app/
	echo "" > $log_file
	cd /docker/mes/
	docker-compose restart $green_app_name   
	timeout $wait_minutes  docker-compose logs -f --tail=0 $green_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done > $log_file 2>&1
	content_log=$(cat $log_file)
	# 去掉shell终端的颜色显示
	content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
	#content_log=$(escape_string "$original_string")
	echo "$content_log"
	if grep -q "$search_string" $log_file; then
		echo "$search_string"
		json_data=$(jq -n --arg company_id "$companyId" --arg status "1" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
		send_mail "$json_data" 
		cd /docker/mes/
		docker-compose  restart fcat-nginx
		echo "green" > $filename
	else
		echo "$error_string"
		json_data=$(jq -n --arg company_id "$companyId" --arg status "0" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
		send_mail "$json_data" 
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/$nginx_file.conf
		cp nginx/conf.d/$nginx_file-blue.conf_bak  nginx/conf.d/$nginx_file.conf
		rm -rf $green_app_name/app/$jar_name.jar
		cp  $green_app_name/app/$jar_name.jar_bak $green_app_name/app/$jar_name.jar  
		docker-compose restart $green_app_name
		
	fi
  ;;
  "green")
	rm -rf nginx/conf.d/$nginx_file.conf
	cp nginx/conf.d/$nginx_file-blue.conf_bak  nginx/conf.d/$nginx_file.conf
	rm -rf $blue_app_name/app/$jar_name.jar_bak  
	cp  $blue_app_name/app/$jar_name.jar $blue_app_name/app/$jar_name.jar_bak  
	rm -rf $blue_app_name/app/$jar_name.jar   
	cp $jar_name.jar  $blue_app_name/app/
	echo "" > $log_file
	docker-compose restart $blue_app_name  
	timeout $wait_minutes  docker-compose logs -f  --tail=0 $blue_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done  > $log_file 2>&1
	content_log=$(cat $log_file)
	# 去掉shell终端的颜色显示
	content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
	#content_log=$(escape_string "$original_string")
	echo "$content_log"
	if grep -q "$search_string" $log_file; then
		echo "$search_string"
		json_data=$(jq -n --arg company_id "$companyId" --arg status "1" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
		send_mail "$json_data" 
		cd /docker/mes/
		docker-compose  restart fcat-nginx
		echo "blue" > $filename
	else
		echo "$error_string"
		json_data=$(jq -n --arg company_id "$companyId" --arg status "0" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
		send_mail "$json_data"
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/$nginx_file.conf
		cp nginx/conf.d/$nginx_file-green.conf_bak  nginx/conf.d/$nginx_file.conf
		rm -rf $blue_app_name/app/$jar_name.jar
		cp  $blue_app_name/app/$jar_name.jar_bak $blue_app_name/app/$jar_name.jar  
		docker-compose restart $blue_app_name
	fi 
  ;;
  "*")
      echo "bluegreen.txt 内容不对"
  ;;
esac
#!/bin/bash
cd /docker/mes/

# 记录当前使用蓝色版本还是绿色版本
filename="ifms-bluegreen.txt"
# 读取当前使用版本
content=$(cat "$filename")
# 蓝色版本
blue_app_name="ifms-admin-blue"
# 绿色版本
green_app_name="ifms-admin-green"
# jar应用名称
jar_name="ifms-admin"
# 指定要匹配的字符串
search_string="Application Start Success"
error_string="Application Start Failed"
# 抓取的日志记录文件
log_file="ifms-output.log"
# nginx配置文件,不需要.conf后缀
nginx_file="mes"

wait_minutes=120
# 公司的ID
companyId=1

echo "当前部署版本:$content" 

# 参数:json_data='{"companyId": "11", "status": "1", "msg": "中文哈哈"}'
send_mail(){
	local json_data=$1
	echo "$json_data"
	# HTTP 接口地址和参数
	url="http://www.tsuantone.top:16020/system/mail/deploy1" 
	# 发送 POST 请求,指定 Content-Type 为 application/json
	response=$(curl -s -X POST -H "Content-Type: application/json" -d "$json_data" "$url")
	# 输出响应结果
	echo "Response: $response"
}

escape_string() {
  local string="$1"
  escaped_string=$(echo "$string" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g; s/\t/\\t/g')
  echo "$escaped_string"
}

case $content in
  "blue")
	rm -rf nginx/conf.d/$nginx_file.conf
	cp  nginx/conf.d/$nginx_file-green.conf_bak  nginx/conf.d/$nginx_file.conf 
	rm -rf  $green_app_name/app/$jar_name.jar_bak  
	cp  $green_app_name/app/$jar_name.jar $green_app_name/app/$jar_name.jar_bak  
	rm -rf $green_app_name/app/$jar_name.jar  
	cp $jar_name.jar  $green_app_name/app/
	echo "" > $log_file
	cd /docker/mes/
	docker-compose restart $green_app_name   
	timeout $wait_minutes  docker-compose logs -f --tail=0 $green_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done > $log_file 2>&1
	content_log=$(cat $log_file)
	# 去掉shell终端的颜色显示
	content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
	#content_log=$(escape_string "$original_string")
	echo "$content_log"
	if grep -q "$search_string" $log_file; then
		echo "$search_string"
		json_data=$(jq -n --arg company_id "$companyId" --arg status "1" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
		send_mail "$json_data" 
		cd /docker/mes/
		docker-compose  restart fcat-nginx
		echo "green" > $filename
	else
		echo "$error_string"
		json_data=$(jq -n --arg company_id "$companyId" --arg status "0" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
		send_mail "$json_data" 
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/$nginx_file.conf
		cp nginx/conf.d/$nginx_file-blue.conf_bak  nginx/conf.d/$nginx_file.conf
		rm -rf $green_app_name/app/$jar_name.jar
		cp  $green_app_name/app/$jar_name.jar_bak $green_app_name/app/$jar_name.jar  
		docker-compose restart $green_app_name
		
	fi
  ;;
  "green")
	rm -rf nginx/conf.d/$nginx_file.conf
	cp nginx/conf.d/$nginx_file-blue.conf_bak  nginx/conf.d/$nginx_file.conf
	rm -rf $blue_app_name/app/$jar_name.jar_bak  
	cp  $blue_app_name/app/$jar_name.jar $blue_app_name/app/$jar_name.jar_bak  
	rm -rf $blue_app_name/app/$jar_name.jar   
	cp $jar_name.jar  $blue_app_name/app/
	echo "" > $log_file
	docker-compose restart $blue_app_name  
	timeout $wait_minutes  docker-compose logs -f  --tail=0 $blue_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done  > $log_file 2>&1
	content_log=$(cat $log_file)
	# 去掉shell终端的颜色显示
	content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
	#content_log=$(escape_string "$original_string")
	echo "$content_log"
	if grep -q "$search_string" $log_file; then
		echo "$search_string"
		json_data=$(jq -n --arg company_id "$companyId" --arg status "1" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
		send_mail "$json_data" 
		cd /docker/mes/
		docker-compose  restart fcat-nginx
		echo "blue" > $filename
	else
		echo "$error_string"
		json_data=$(jq -n --arg company_id "$companyId" --arg status "0" --arg msg "$content_log" '{companyId: $company_id, status: $status, msg: $msg}')
		send_mail "$json_data"
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/$nginx_file.conf
		cp nginx/conf.d/$nginx_file-green.conf_bak  nginx/conf.d/$nginx_file.conf
		rm -rf $blue_app_name/app/$jar_name.jar
		cp  $blue_app_name/app/$jar_name.jar_bak $blue_app_name/app/$jar_name.jar  
		docker-compose restart $blue_app_name
	fi 
  ;;
  "*")
      echo "bluegreen.txt 内容不对"
  ;;
esac

deploy-fcat.sh

shell
#!/bin/bash

# 记录当前使用蓝色版本还是绿色版本
filename="ruoyi-admin-bluegreen.txt"
# 读取当前使用版本
content=$(cat "$filename")
# 蓝色版本
blue_app_name="ruoyi-admin-blue"
# 绿色版本
green_app_name="ruoyi-admin-green"
# jar应用名称
jar_name="ruoyi-admin"
jar_path="/root/ruoyi-vue/ruoyi-admin/target/"
# 指定要匹配的字符串
search_string="Application Start Success"
error_string="Application Start Failed"
# 抓取的日志记录文件
log_file="ruoyi-admin-output.log"
# nginx配置文件,不需要.conf后缀
nginx_file="ruoyi"
# 检测启动日志等待时间
wait_minutes=120
# 切换nginx的蓝绿版本,需要留时间处理请求中的接口返回
wait_inteface_deal_minutes=30
# 名称
name="FCAT"

echo "当前部署版本:$content" 

# 参数:json_data='{"name": "FCAT", "status": "1", "msg": "中文哈哈"}'
send_mail(){
	local json_data=$1
	echo "$json_data"
	# HTTP 接口地址和参数
	url="https://localhost/prod-api/system/mail/deploy" 
	# 发送 POST 请求,指定 Content-Type 为 application/json
	response=$(curl -s -X POST -H "Content-Type: application/json" -d "$json_data" -k "$url")
	# 输出响应结果
	echo "Response: $response"
}

escape_string() {
  local string="$1"
  escaped_string=$(echo "$string" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g; s/\t/\\t/g')
  echo "$escaped_string"
}

case $content in
  "blue")
	rm -rf nginx/conf.d/$nginx_file.conf
	cp  nginx/conf.d/$nginx_file-green.conf_bak  nginx/conf.d/$nginx_file.conf 
	rm -rf  $green_app_name/jar/$jar_name.jar_bak  
	cp  $green_app_name/jar/$jar_name.jar $green_app_name/jar/$jar_name.jar_bak  
	rm -rf $green_app_name/jar/$jar_name.jar  
	cp $jar_path$jar_name.jar  $green_app_name/jar/
	echo "" > $log_file
	docker-compose restart $green_app_name   
	timeout $wait_minutes  docker-compose logs -f --tail=0 $green_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done > $log_file 2>&1
	content_log=$(cat $log_file)
	# 去掉shell终端的颜色显示
	content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
	#content_log=$(escape_string "$original_string")
	echo "$content_log"
	if grep -q "$search_string" $log_file; then
		echo "$search_string"
		docker-compose  exec fcat-nginx nginx -s reload
		sleep $wait_inteface_deal_minutes
		json_data=$(jq -n --arg name "$name" --arg status "1" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
		send_mail "$json_data" 
		echo "green" > $filename
		docker-compose stop $blue_app_name
	else
		echo "$error_string"
		json_data=$(jq -n --arg name "$name" --arg status "0" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
		send_mail "$json_data" 
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/$nginx_file.conf
		cp nginx/conf.d/$nginx_file-blue.conf_bak  nginx/conf.d/$nginx_file.conf
		rm -rf $green_app_name/jar/$jar_name.jar
		cp  $green_app_name/jar/$jar_name.jar_bak $green_app_name/jar/$jar_name.jar  
		docker-compose restart $green_app_name
		
	fi
  ;;
  "green")
	rm -rf nginx/conf.d/$nginx_file.conf
	cp nginx/conf.d/$nginx_file-blue.conf_bak  nginx/conf.d/$nginx_file.conf
	rm -rf $blue_app_name/jar/$jar_name.jar_bak  
	cp  $blue_app_name/jar/$jar_name.jar $blue_app_name/jar/$jar_name.jar_bak  
	rm -rf $blue_app_name/jar/$jar_name.jar   
	cp $jar_path$jar_name.jar  $blue_app_name/jar/
	echo "" > $log_file
	docker-compose restart $blue_app_name  
	timeout $wait_minutes  docker-compose logs -f  --tail=0 $blue_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done  > $log_file 2>&1
	content_log=$(cat $log_file)
	# 去掉shell终端的颜色显示
	content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
	#content_log=$(escape_string "$original_string")
	echo "$content_log"
	if grep -q "$search_string" $log_file; then
		echo "$search_string"
		docker-compose  exec fcat-nginx nginx -s reload
		sleep $wait_inteface_deal_minutes
		json_data=$(jq -n --arg name "$name" --arg status "1" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
		send_mail "$json_data" 
		echo "blue" > $filename
		docker-compose stop $green_app_name
	else
		echo "$error_string"
		json_data=$(jq -n --arg name "$name" --arg status "0" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
		send_mail "$json_data"
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/$nginx_file.conf
		cp nginx/conf.d/$nginx_file-green.conf_bak  nginx/conf.d/$nginx_file.conf
		rm -rf $blue_app_name/jar/$jar_name.jar
		cp  $blue_app_name/jar/$jar_name.jar_bak $blue_app_name/jar/$jar_name.jar  
		docker-compose restart $blue_app_name
	fi 
  ;;
  "*")
      echo "$filename内容不对"
  ;;
esac
#!/bin/bash

# 记录当前使用蓝色版本还是绿色版本
filename="ruoyi-admin-bluegreen.txt"
# 读取当前使用版本
content=$(cat "$filename")
# 蓝色版本
blue_app_name="ruoyi-admin-blue"
# 绿色版本
green_app_name="ruoyi-admin-green"
# jar应用名称
jar_name="ruoyi-admin"
jar_path="/root/ruoyi-vue/ruoyi-admin/target/"
# 指定要匹配的字符串
search_string="Application Start Success"
error_string="Application Start Failed"
# 抓取的日志记录文件
log_file="ruoyi-admin-output.log"
# nginx配置文件,不需要.conf后缀
nginx_file="ruoyi"
# 检测启动日志等待时间
wait_minutes=120
# 切换nginx的蓝绿版本,需要留时间处理请求中的接口返回
wait_inteface_deal_minutes=30
# 名称
name="FCAT"

echo "当前部署版本:$content" 

# 参数:json_data='{"name": "FCAT", "status": "1", "msg": "中文哈哈"}'
send_mail(){
	local json_data=$1
	echo "$json_data"
	# HTTP 接口地址和参数
	url="https://localhost/prod-api/system/mail/deploy" 
	# 发送 POST 请求,指定 Content-Type 为 application/json
	response=$(curl -s -X POST -H "Content-Type: application/json" -d "$json_data" -k "$url")
	# 输出响应结果
	echo "Response: $response"
}

escape_string() {
  local string="$1"
  escaped_string=$(echo "$string" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g; s/\t/\\t/g')
  echo "$escaped_string"
}

case $content in
  "blue")
	rm -rf nginx/conf.d/$nginx_file.conf
	cp  nginx/conf.d/$nginx_file-green.conf_bak  nginx/conf.d/$nginx_file.conf 
	rm -rf  $green_app_name/jar/$jar_name.jar_bak  
	cp  $green_app_name/jar/$jar_name.jar $green_app_name/jar/$jar_name.jar_bak  
	rm -rf $green_app_name/jar/$jar_name.jar  
	cp $jar_path$jar_name.jar  $green_app_name/jar/
	echo "" > $log_file
	docker-compose restart $green_app_name   
	timeout $wait_minutes  docker-compose logs -f --tail=0 $green_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done > $log_file 2>&1
	content_log=$(cat $log_file)
	# 去掉shell终端的颜色显示
	content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
	#content_log=$(escape_string "$original_string")
	echo "$content_log"
	if grep -q "$search_string" $log_file; then
		echo "$search_string"
		docker-compose  exec fcat-nginx nginx -s reload
		sleep $wait_inteface_deal_minutes
		json_data=$(jq -n --arg name "$name" --arg status "1" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
		send_mail "$json_data" 
		echo "green" > $filename
		docker-compose stop $blue_app_name
	else
		echo "$error_string"
		json_data=$(jq -n --arg name "$name" --arg status "0" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
		send_mail "$json_data" 
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/$nginx_file.conf
		cp nginx/conf.d/$nginx_file-blue.conf_bak  nginx/conf.d/$nginx_file.conf
		rm -rf $green_app_name/jar/$jar_name.jar
		cp  $green_app_name/jar/$jar_name.jar_bak $green_app_name/jar/$jar_name.jar  
		docker-compose restart $green_app_name
		
	fi
  ;;
  "green")
	rm -rf nginx/conf.d/$nginx_file.conf
	cp nginx/conf.d/$nginx_file-blue.conf_bak  nginx/conf.d/$nginx_file.conf
	rm -rf $blue_app_name/jar/$jar_name.jar_bak  
	cp  $blue_app_name/jar/$jar_name.jar $blue_app_name/jar/$jar_name.jar_bak  
	rm -rf $blue_app_name/jar/$jar_name.jar   
	cp $jar_path$jar_name.jar  $blue_app_name/jar/
	echo "" > $log_file
	docker-compose restart $blue_app_name  
	timeout $wait_minutes  docker-compose logs -f  --tail=0 $blue_app_name  | while IFS= read -r line; do
	    echo "$line"
		if echo "$line" | grep -q "$search_string"; then
			pkill -P $$  # 杀死timeout进程
			break
		fi
	done  > $log_file 2>&1
	content_log=$(cat $log_file)
	# 去掉shell终端的颜色显示
	content_log=$(echo "$content_log" | sed -r "s/\x1B\[[0-9;]*[mGK]//g")
	#content_log=$(escape_string "$original_string")
	echo "$content_log"
	if grep -q "$search_string" $log_file; then
		echo "$search_string"
		docker-compose  exec fcat-nginx nginx -s reload
		sleep $wait_inteface_deal_minutes
		json_data=$(jq -n --arg name "$name" --arg status "1" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
		send_mail "$json_data" 
		echo "blue" > $filename
		docker-compose stop $green_app_name
	else
		echo "$error_string"
		json_data=$(jq -n --arg name "$name" --arg status "0" --arg msg "$content_log" '{name: $name, status: $status, msg: $msg}')
		send_mail "$json_data"
		# 在这里编写回滚操作的代码
		rm -rf nginx/conf.d/$nginx_file.conf
		cp nginx/conf.d/$nginx_file-green.conf_bak  nginx/conf.d/$nginx_file.conf
		rm -rf $blue_app_name/jar/$jar_name.jar
		cp  $blue_app_name/jar/$jar_name.jar_bak $blue_app_name/jar/$jar_name.jar  
		docker-compose restart $blue_app_name
	fi 
  ;;
  "*")
      echo "$filename内容不对"
  ;;
esac

上传jar包到服务器

根目录在/docker/mes/fcat-mes-main.jar

shell
sh deploy.sh
sh deploy.sh
curl http://192.168.9.126:16219/test/version
{
"version": "v3"
}

curl http://192.168.9.126:16220/test/version
{
"version": "v2"
}

curl http://192.168.9.126:16221/dev-api/test/version
{
"version": "v3"
}
curl http://192.168.9.126:16219/test/version
{
"version": "v3"
}

curl http://192.168.9.126:16220/test/version
{
"version": "v2"
}

curl http://192.168.9.126:16221/dev-api/test/version
{
"version": "v3"
}