蓝绿部署技术方案

news2024/11/15 2:15:58

文章目录

  • ngx_lua介绍
    • Nginx
    • lua
    • ngx_lua模块的原理:
    • ngx_lua 模块执行顺序与阶段
    • ngx_lua应用场景
  • JWT
  • nginx镜像构造
  • lua-redis
  • 蓝绿部署
    • 特性
    • 注意:
    • 蓝绿部署架构图
    • nginx配置
    • 服务脚本
    • 部署
    • 使用
    • 职责分工

ngx_lua介绍

Nginx

Nginx是Web服务器、HTTP反向代理和TCP代理服务器。
特点:

  • 性能非常高
  • 资源占用CPU、内存非常节省
  • 内存池设计非常稳定
  • 高度模块化易于扩展

我们常常拿Nginx与Apache做比较,其实它们各有各的适用场景。Nginx相对于Apache的优势在于处理高并发很好的解决了C10K问题,这就靠Nginx的epoll网络I/O模型。nginx的epoll机制比apache的select机制更适用于高并发的场景。

lua

Lua 是一个功能强大、快速、轻量的可嵌入式脚本语言由标准的 ANSI C 实现由于拥有一组精简的强大特性以及容易使用的 C API这使得它可以很容易嵌入或扩展到其他语言中使用。
特点:

  • 适合嵌入
  • 支持协程coroutine
  • 用同步的语义来实现异步的调用

lua在脚本语言里速度上有很大的优势加上Nginx两者结合在高并发负载的情况下仍然可以游刃有余。

ngx_lua模块的原理:

  1. 每个worker(工作进程)创建一个Lua VM,worker内所有协程共享VM;
  2. 将Nginx I/O原语封装后注入 Lua VM,允许Lua代码直接访问;
  3. 每个外部请求都由一个Lua协程处理,协程之间数据隔离;
  4. Lua代码调用I/O操作等异步接口时,会挂起当前协程(并保护上下文数据),而不阻塞worker;
  5. I/O等异步操作完成时还原相关协程上下文数据,并继续运行;

ngx_lua 模块执行顺序与阶段

ngx_lua属于nginx的一部分,它的执行指令都包含在nginx的11个步骤之中了,相应的处理阶段可以做插入式处理,即可插拔式架构,不过ngx_lua并不是所有阶段都会运行的;另外指令可以在http、server、server if、location、location if几个范围进行配置

img_1.png

ngx_lua应用场景

对于Nginx粘合Lua来开发应用可以说是一把锋利的瑞士军刀,可以帮我们很容易的解决很多问题,基于Nginx+Lua的常用架构模式中一些常见实践和场景:

  • WEB应用防火墙(waf)
  • 限流
  • 降级
  • 服务质量监控
  • 灰度/蓝绿发布

JWT

请参考:JWT校验

nginx镜像构造

请参考:nginx整合lua、jwt、cjson、redis、mysql模块镜像构建

lua-redis

请参考:nginx中lua-redis使用

蓝绿部署

蓝绿部署上线以后,支持任意时刻生产上线、随时随地切换分支。实现一键部署、分支一键切换,秒级生效。
nginx整合lua、jwt、cjson、redis、mysql等模块,通过开发lua/shell脚本,实现维护共享内存、流量拦截、蓝绿切换、白名单维护。实现蓝绿部署。

特性

  • 蓝绿部署:
    蓝绿两部分,随时切换。细粒度的蓝绿,每个后端及每个前端服务都可以单独蓝绿切换
  • 用户切流:
    根据userId/userName/clientIp实现用户分流
  • 动态配置lua多进程全局共享内存
    • 动态upstream:动态的实现蓝绿环境切换
    • 动态用户配置:动态实现userId/userName/clientIp更新,热加载

宗旨:在最少组件依赖、最简单架构设计的情况下满足业务需求。因为依赖的组件越多,架构越复杂,越不容易把控,同时出问题的概率越大

注意:

  1. 部署蓝绿的基本前提:首先需要对nginx相当熟悉,对nginx的代理转发、跨域等相当的了解
  2. 在请求进行代理转发的时候最好显示的指定request_uri
    在以下两种情况需要显示指定/重写request_uri
    • 请求request_uri与转发的request_uri不一致的情况下,必须显示的指定request_uri
    • 请求的站点为"/"的情况下,必须显示的指定request_uri,或者重写request_uri
  3. 在用到nginx多进程全局共享内存【ngx_shared_dict】时,遇到【锁竞争】的问题。原因:在高并发场景下,不同的worker进程可能会竞争访问同一个共享字典条目,当许多worker试图同时访问该条目时,可能会导致锁竞争,从而降低性能

蓝绿部署架构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EpgBiUWp-1681813483252)(img.png)]
蓝绿分组互相切换,例如:蓝->a,绿->b;切换为 蓝->b,绿->a

nginx配置

  • nginx.conf
user root;
#基本优化
worker_processes 8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 1000000;
worker_rlimit_nofile 120000;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 120000;
    use epoll;
}

http {

	  log_format main escape=json '$remote_addr - $remote_user [$time_local] '
                                                 '"<$host> $request" $status $bytes_sent '
                                                 '"$http_referer" "$http_user_agent" '
                                                 '"$request_time $upstream_response_time $pipe" '
                                                 '"$gzip_ratio" || "$request_body" '
                                                 '"token:$http_authorization"';

    lua_package_path "/usr/local/lua_core/lib/lua/?.lua;/etc/nginx/conf.d/lua/?.lua;;";
    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   30;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;
    gzip on;
    gzip_min_length  10k;
    gzip_buffers 4 16k;
    gzip_comp_level 3;
    gzip_types  text/xml text/javascript application/javascript text/css text/plain text/json application/json;
    keepalive_requests 8192;

    #lua_shared_dict upstreams 1m;
    #lua_shared_dict user_ids 5m;
    #lua_shared_dict user_names 5m;
    #lua_shared_dict client_ips 5m;

    #初始化全局变量
    init_by_lua_file /etc/nginx/conf.d/lua/lua-init-redis-cmd.lua;
    #init_by_lua_block {
    #  upstreams = {}
    #  user_ids = {}
    #  user_names = {}
    #  client_ips = {}
    #}

    include /etc/nginx/conf.d/*.conf;

    #全局配置上传大小
    client_max_body_size 200m;
    client_body_buffer_size 1024k;

    deny 192.148.0.200;

}

  • dynamic_shared_dict.conf

server {
  listen       8001;
  #server_name  localhost;
  allow 192.168.0.12;
  deny all;

  location = /_lua_shared_dict_init { # 单个worker生效,无用
    content_by_lua_file /etc/nginx/conf.d/lua/lua-init-redis.lua;
  }

  location = /_lua_shared_dict_print {
    default_type 'text/plain';
    content_by_lua_file /etc/nginx/conf.d/lua/print-lua-shared.lua;
  }

  location = /_switch_upstream {
    content_by_lua_file /etc/nginx/conf.d/lua/switch-upstream-redis-cmd.lua;
  }

  location = /_get_upstream {
    content_by_lua_file /etc/nginx/conf.d/lua/get-upstream-redis-cmd.lua;
  }

  location = /_update_white_list {
    content_by_lua_file /etc/nginx/conf.d/lua/update-white-list-redis-cmd.lua;
  }

  location = /_get_white_list {
    content_by_lua_file /etc/nginx/conf.d/lua/get-white-list-redis-cmd.lua;
  }

}


  • frontend.conf

server {
  listen       9002;
  #server_name  localhost;

  #set_by_lua_file $frontend /etc/nginx/conf.d/lua/lua-set-frontend.lua;
  #set_by_lua_file $backend_upstream /etc/nginx/conf.d/lua/lua-set-backend.lua;

  #root /etc/nginx/html/$frontend;

  #验证前端切流
  #location / {
  #  index  index.html index.htm;
  #}

  #验证后端切流
  location / {
    default_type 'text/html';
    set_by_lua_file $backend_upstream /etc/nginx/conf.d/lua/lua-set-backend.lua;
    #proxy_next_upstream off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://$backend_upstream; #重启下游服务
  }

  #   在以下两种情况需要显示指定/重写request_uri
  #- 请求request_uri与转发的request_uri不一致的情况下,必须显示的指定request_uri
  #- 请求的站点为"/"的情况下,必须显示的指定request_uri,或者重写request_uri
  location /test1/test/aa {
    default_type 'text/html';
    set_by_lua_file $backend_upstream /etc/nginx/conf.d/lua/lua-set-backend.lua;
    set_by_lua_block $replace_uri {
      ngx.log(ngx.WARN,ngx.var.request_uri)
      return string.gsub(ngx.var.request_uri, "^/test1/test/aa", "/test/aa",1)
    }
    #proxy_next_upstream off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    #proxy_pass http://b_backend; #重启下游服务
    #proxy_pass http://b_backend/test/aa;
    proxy_pass http://${backend_upstream}${replace_uri};
  }

  location / {
    default_type 'text/html';
    set_by_lua_file $backend_upstream /etc/nginx/conf.d/lua/lua-set-backend.lua;
    set_by_lua_block $replace_uri {
      ngx.log(ngx.WARN,ngx.var.request_uri)
      return string.gsub(ngx.var.request_uri, "^/", "/",1)
    }
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://${backend_upstream}${replace_uri};
  }

}

  • pass.conf
#dynamic_shared_dict.conf
upstream a_backend01 {
  server 192.168.0.23:8081;
}

upstream b_backend01 {
  server 192.168.0.23:8082;
}


upstream a_backend02 {
  server 192.168.0.23:8081;
}

upstream b_backend02 {
  server 192.168.0.23:8082;
}
  • test.conf

server {
  listen       9001;
  server_name  localhost;
  allow 172.17.0.1;
  deny all;

  location /redis {
    default_type 'text/plain';
    content_by_lua_file /etc/nginx/conf.d/lua/test-redis.lua;
  }
}


服务脚本

  • lua脚本
    请参考:ngx-lua蓝绿部署lua脚本

  • 部署脚本blue-green-deploy.sh

#!/bin/bash
set -e
HOST_PORT="127.0.0.1:8001"
CONTAINER="ngx_lua"

function shared_dict_print() {
  curl http://${HOST_PORT}/_lua_shared_dict_print
}

function share_dict_init() {
  curl http://${HOST_PORT}/_lua_shared_dict_init
}

function switch_upstream() {
  #backend=a_backend
  curl http://${HOST_PORT}/_switch_upstream?$1

  docker exec -it ${CONTAINER} nginx -s reload
}

function switch_upstream_advance() {
  PRO=$1
  GREEN=`curl http://${HOST_PORT}/_get_upstream?${PRO} | grep green | awk -F '=' '{print $2}'`
  curl http://${HOST_PORT}/_switch_upstream?${PRO}=${GREEN}

  /bin/bash -c "docker exec  ${CONTAINER} nginx -s reload"
}

function get_upstream() {
  #all/backend/taskcenter/frontend
  curl http://${HOST_PORT}/_get_upstream?$1
}

function update_white_list() {
  curl http://${HOST_PORT}/_update_white_list \
    -X "POST" \
    --data ${1}  \
    --compressed

  /bin/bash -c "docker exec  ${CONTAINER} nginx -s reload"
}

function get_white_list() {
   #all/userids/usernames/clientips
  curl http://${HOST_PORT}/_get_white_list?$1
}

function ngx_reload() {
  /bin/bash -c "docker exec  ${CONTAINER} nginx -s reload"
}

function printUsage(){
        echo -e "Usage: [shared_dict_print] [share_dict_init] [switch_upstream]  [get_upstream] [update_white_list] [get_white_list]"
}

function main() {
  case "$1" in
      (shared_dict_print)
          shared_dict_print
      ;;
      (share_dict_init)
          share_dict_init
      ;;
      (switch_upstream)
          switch_upstream_advance  $2
      ;;
      (get_upstream)
          get_upstream  $2
      ;;
      (update_white_list)
          update_white_list  $2
      ;;
      (get_white_list)
          get_white_list  $2
      ;;
      (ngx_reload)
          ngx_reload
      ;;
      (*)
          printUsage
          exit 1;
      ;;
  esac
}

main $@


  • nginx日志滚动脚本
#!/bin/bash
# rotatelog.sh
# 0 0 * * * /bin/bash /var/log/nginx/rotatelog.sh
BASE=/var/log/nginx
DATE=$(TZ='Asia/Chongqing' date "+%Y%m%d")
mv ${BASE}/access.log ${BASE}/access.${DATE}.log
mv ${BASE}/error.log ${BASE}/error.${DATE}.log

/bin/bash -c "docker container kill ngx_lua -s USR1"

find ${BASE} -mtime +30 -name "*access*" | xargs rm -f
find ${BASE} -mtime +10 -name "*error*" | xargs rm -f

exit 0

部署

  • 启动redis
docker run -itd \
-v /data/redis:/data \
--name redis \
-p 6379:6379 \
--privileged=true \
redis   --appendonly yes --requirepass  "xxx"

docker exec -it redis redis-cli -h 127.0.0.1 -p 6379 --pass "xxx"

默认有16 db [0~15]
查看当前db的keys数量: dbsize
切换db: select n
查看当前db数量: CONFIG GET databases


#redis 用法:
    https://www.cnblogs.com/ysocean/p/9080940.html#_label1
    https://segmentfault.com/a/1190000007207616/
#openresty ngx_lua共享内存
    https://blog.csdn.net/weixin_43931625/article/details/125829576
    https://www.w3cschool.cn/openresty1 (包括openresty各种组件(redis等))

  • 启动nginx
docker run --rm --name=ngx_lua -it  \
-p 8001:8001 \
-p 9001:9001 \
-p 9002:9002 \
-v /etc/nginx/nginx.conf:/etc/nginx/nginx.conf:rw \
-v /etc/nginx/conf.d:/etc/nginx/conf.d:rw \
-v /etc/nginx/html:/etc/nginx/html:rw \
--privileged=true \
ponylee/centos7-nginx:latest

使用

配置白名单(ip或者usernames),验证

  • ip白名单验证
    查看ip白名单
    非ip白名单访问验证
    添加ip白名单 – 管理员在后台操作
    ip白名单访问验证
    删除ip白名单 – 管理员在后台操作
    非ip白名单访问验证

  • username白名单验证
    查看username白名单
    非username白名单访问验证
    添加username白名单 – 管理员在后台操作
    username白名单访问验证
    删除username白名单 – 管理员在后台操作
    非username白名单访问验证

    curl http://127.0.0.1:8001/_update_white_list \
      -X "GET" \
      --data '{"update":{"clientips":["192.168.0.15"]}}' \
      --compressed
    
    curl http://127.0.0.1:8001/_update_white_list \
      -X "GET" \
      --data '{"update":{"usernames":["zhangsan"]}}' \
      --compressed
    
  • 蓝绿切换

    switch_upstream backend01

职责分工

  • 后台管理人员职责
    • 维护白名单
  • 开发人员需要做的
    • 部署预发分支(green分支),如有异常重新部署,白名单成员需求验证
    • 蓝绿切换,蓝绿分组互相切换(例如:蓝->a,绿->b;切换为 蓝->b,绿->a)
      如有异常,蓝绿再次切换
      正常,结束

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/431708.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

apache+tomcat实现动静分离和负载均衡

文章目录 ApacheTomcat整合环境通过JK实现动静分离编译mod_jk.so创建测试页面配置jk模块启动apache和tomcat测试。 ApacheTomcat负载均衡配置测试页配置mod_jk文件配置worker.properties测试 ApacheTomcat整合 Tomcat作为一个Servlet容器&#xff0c;可以用于运行Java Web应用…

Unity之c#专题篇——【不动如山核心章】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

连接VPN后无法上网 Windows Route 轻松解决

连接VPN后无法上网 Windows Route 轻松解决 引言文档添加路由 引言 很多时候&#xff0c;我们公司的 VPN 为了不占用公司的外网带宽和安全起见&#xff0c;都会禁止访问外网。我们的电脑连接 VPN 后&#xff0c;所有的网络数据包都会走 VPN&#xff0c;从而导致我们无法访问互…

ES集群配置和分词器(九)

一直在坑自己家人&#xff0c;对&#xff0c;说的就是你&#xff0c;大A. 上一章简单介绍了SpringBoot整合 EasyES (八), 如果没有看过,请观看上一章 一. Windows 集群部署 一.一 服务复制 将 es 复制3份 一.二 配置文件配置 每个服务节点的 config/eslsticsearch.yml 配置…

AANet: CTPA图像中肺动脉栓塞检测的动脉感知网络

AANet: Artery-Aware Network for Pulmonary Embolism Detection in CTPA Images 摘要 肺栓塞(PE)是危及生命的疾病&#xff0c;计算机断层肺血管造影(CTPA)是临床上最好的诊断技术 然而&#xff0c;在CTPA图像中&#xff0c;PE通常表现为血液动脉明亮区域中的黑点&#xff0…

UE4/5多人游戏详解(一、基础理论与局域网内部的连接)

目录 多人游戏理论简单讲解 点对点&#xff1a; 于是&#xff0c;服务端和客户端的概念出现了&#xff1a; 局域网连接&#xff1a; 从第三人称c项目开始 创建项目&#xff1a; 以一个客户端作为监听服务器 3个客户端&#xff0c;在场景后方使用服务器&#xff1a; 局…

leetcode:同构字符串(详解)

前言&#xff1a;内容包括&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读 题目&#xff1a; 给定两个字符串 s 和 t &#xff0c;判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t &#xff0c;那么这两个字符串是同构…

数字营销(三)如何确定合适的流量渠道?

一、为什么要确定付费客户特征&#xff1f; 首先营销策略是什么&#xff1f;营销策略&#xff0c;即战略&#xff0c;就是为实现主要流量侧的长期目标&#xff0c;所采取的具体打法和步骤。 比如一个网站的成功离不开获取网站流量和用户信任的建立。除了谷歌 SEO 获取流量&am…

【JavaEE初阶】多线程(一)

摄影分享&#xff01; 文章目录 认识线程&#xff08;Thread&#xff09;概念执行多线程编程创建线程的写法1.继承Thread&#xff0c;重写run2.实现Runnable接口3.使用匿名内部类&#xff0c;继承Thread4.使用匿名内部类&#xff0c;实现Runable5.使用Lambda表达式 Thread用法…

【ARMv8/v9 MMU 页表配置 01 】

文章目录 1.1 MMU1.1.1 虚拟地址位宽配置1.1.2 页面大小(grandule size)配置1.1.3 AArch64 页表项描述符格式1.1.4 内存属性配置 1.1 MMU 1.1.1 虚拟地址位宽配置 64 位虚拟地址中&#xff0c;并不是所有位都用上&#xff0c;除了高 16 位用于区分内核空间和用户空间的虚拟地…

SpringCloud:ElasticSearch之数据聚合

聚合&#xff08;aggregations&#xff09; 可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f;这些手机的平均价格、最高价格、最低价格&#xff1f;这些手机每月的销售情况如何&#xff1f; 实现这些统计功能的比数据…

数组题目总结 -- 双指针

目录① 快慢指针&#xff1a;一. 删除有序数组中的重复项1. 思路和代码I. 博主的做法&#xff1a;II. 东哥的做法&#xff1a;2. 总结二. 删除排序链表中的重复元素&#xff08;扩展&#xff09;1. 思路和代码I. 博主的做法&#xff1a;II. 东哥的做法&#xff1a;2. 总结三. 移…

硬件语言Verilog HDL牛客刷题day08 综合部分

1. Johnson Counter 1.题目&#xff1a; 请用Verilog实现4位约翰逊计数器&#xff08;扭环形计数器&#xff09;&#xff0c;计数器的循环状态如下。 电路的接口如下图所示 2.解题思路 2.1 一个简单的状态机的配置。 2.2 注意 起始状态 是 0000 就行 3.解题代码 timescale …

动态内存管理——C语言【进阶】(下)

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a;进阶C语言&#xff0c;本专栏主要讲解数据存储&#xff0c;进阶指针&#xff0c;动态内存管理&a…

前端面试题 - 计算机网络与浏览器相关

系列文章目录 vue常见面试题总结 htmlcss 面试题总结附答案 初级前端面试题总结&#xff08;html, css, js, ajax&#xff0c;http&#xff09; js基础面试题整理(包含ES5&#xff0c;ES6) 文章目录 系列文章目录一、网络协议相关1. 从浏览器地址栏输入url到显示页面的步骤…

蓝桥杯欲伸手CTF?有多远爬多远

注意&#xff1a;网络安全类比赛 或者说 CTF 参赛不会需要任何费用 只有国赛/省赛有可能会收取一定运维费用 其他比赛都不会收费 望周知。 先来看个特离谱的事情 早上起床看到几位师傅的朋友圈一脸懵&#xff0c;再仔细一看&#xff0c;好嘛。。。。。。 先看看探姬的回复 接下…

人工智能大模型多场景应用原理解析

前言 在上篇文章《人工智能大模型之ChatGPT原理解析》中分享了一些大模型之ChatGPT的核心原理后&#xff0c;收到大量读者的反馈&#xff0c;诸如:在了解了核心原理后想进一步了解未来的发展趋势(比如生成式人工智能和元宇宙能擦出什么样的火花&#xff1f;)&#xff0c;大模型…

伪命题之MYSQL分库分表

看到使用分库分表来解决性能问题的时候心里总是不能太理解。 如果同事发生大量请求的时候&#xff0c;损害性能的是硬盘的随机读。那么分库分表也没有对性能的瓶颈进行“分治”啊。 应该的做法是使用一块新的硬盘来创建分库。但是基本的文章都没有提到这点。而且基本上也不会有…

价值导向型研发管理数字化建设方案——易趋亮相CCTI中国研发管理峰会

4月15日-16日&#xff0c;由光环国际举办的2023 CCTI中国研发管理峰会在北京中关村国家自主创新示范区会议中心成功举办。 &#xff08;现场签到处&#xff09; 此次峰会邀请了20余位来自腾讯、华为、网易、阿里云等知名企业的研发管理领域专家&#xff0c;带来最新前沿知识和内…

【网络安全知识体系】外网渗透测试 | 14个小实验

写在前面&#xff1a;视频地址 成功上岸360&#xff01;0基础网络安全 入行 到 入yu、漏洞挖掘-外网渗透测试流程目录 一、导读&#xff1a; 二、汇总&#xff1a; 三、知识导图 四、面试常见问题 五、渗透测试流程 1、简述&#xff1a; 2、寻找测试目标 3、信息收集 …