Nginx应用与运维——Nginx集群负载与配置管理(二)

Nginx集群负载与配置管理

2、Nginx集群配置管理

当用Nginx服务器作为负载均衡应用时,经常会因业务调整或被代理服务器的变化需要对Nginx的配置进行修改,对于Nginx集群,若修改其中一台Nginx的配置,还需要对集群内Nginx的配置进行同步修改。在实际的网络架构中,为降低因部分应用负载策略频繁变更带来的影响建立了多组Nginx集群,此时,这些Nginx集群就面临Nginx配置的修改、同步、回滚等配置管理问题。为更灵活地应对Nginx的配置变更,需要有一套可便捷操作的、规范化的管理工具进行Nginx集群的配置管理。本节将通过对现有的开源软件Jenkins、GitLab和Ansible进行组合,快速搭建一套Web化的Nginx集群配置管理框架。该管理框架通过Jenkins的Web化管理界面实现权限管理、前端配置、发布记录等功能,并结合GitLab的版本控制功能对每次变更进行归档,可随时实现配置回滚。通过对Ansible剧本的调用,自动化地实现Nginx集群的配置修改、加载、灰度发布等操作。

2.1、Nginx集群配置管理规划

Nginx的配置是以文件形式存在的,配置指令会在启动时一次性加载并生效,采用这种方式除upstream的配置可动态变更(商业版本支持API变更,开源版本依赖第三方模块动态修改)外,其他配置的修改均需要重启或热加载Nginx进程才可生效。为实现便捷的Nginx配置变更管理,需要从以下几个方面进行规划。

2.1.1、配置目录结构

Nginx默认所有配置文件均存放在其安装目录的conf目录下,为防止配置文件不方便阅读和管理,可以按照虚拟主机(具有独立主机名或网络端口)进行拆分,每个虚拟主机一个配置文件,并存放在统一的目录下。对功能固定、全局的配置指令以固定文件的形式存放在配置文件目录的根目录下。所有的配置文件都以nginx.conf为统一入口,并使用配置指令include按需引入。Nginx的目录结构规划样例如下。

bash 复制代码
conf/
  ├── conf.d
  │   ├── mysql_apps.ream
  │   ├── www.nginxbar.com.conf
  │   └── www.nginxbar.org.conf
  ├── fastcgi.conf
  ├── fastcgi_params
  ├── fscgi.conf
  ├── gzip.conf
  ├── mime.types
  ├── nginx.conf
  ├── proxy.conf
  ├── scgi_params
  ├── ssl
  │   ├── www_nginxbar_org.csr
  │   ├── www_nginxbar_org.key
  │   └── www_nginxbar_org.pem
  └── uwsgi_params
  • Nginx默认配置文件目录结构说明可参见3.1.1节的相关内容。
  • conf.d为自建目录,是存放虚拟主机配置文件的目录。
  • mysql_apps.ream是自定义应用apps的MySQL集群代理配置文件。
  • www.nginxbar.com.conf是域名www.nginxbar.com对应的虚拟主机配置文件。
  • www.nginxbar.org.conf是域名www.nginxbar.org对应的虚拟主机配置文件。
  • fscgi.conf是自定义FastCGI代理配置文件,配置文件样例可参见5.3.3节。
  • gzip.conf是自定义响应数据gzip压缩配置指令文件,配置文件样例可参见4.3.5节。
  • proxy.conf是自定义HTTP代理配置指令文件,配置文件样例可参见6.1.3节。
  • ssl是自建目录,用于存放虚拟主机的SSL证书文件。

nginx.conf配置样例如下:

bash 复制代码
# 选择加载动态模块
load_module "modules/ngx_http_geoip_module.so";
load_module "modules/ngx_http_image_filter_module.so";
load_module "modules/ngx_http_xslt_filter_module.so";

# 工作进程及事件配置
worker_processes auto;                  # 启动与CPU核数一致的工作进程
worker_priority -5;                     # 工作进程在linux系统中的优先级为-5

events {
    worker_connections  65535;              # 每个工作进程的最大连接数
    multi_accept on;                        # 每个工作进程每次都可以接收多个连接
}

# TCP/UDP代理配置
stream {
    # 配置TCP/UDP代理的日志格式模板,模板名为tcp
    log_format  tcp  '$remote_addr - $connection - [$time_local] $server_addr:$server_port - $protocol'
                      '- $status - $upstream_addr - $bytes_received - $bytes_sent - $session_time '
                      '- $proxy_protocol_addr:$proxy_protocol_port ';

    # 配置TCP/UDP代理的错误日志输出位置,错误级别为error
    error_log logs/tcp_error.log error;

    # 引入conf.d目录下所有后缀名为ream的配置文件
    include conf.d/*.ream;
}

# HTTP配置
http {
    include       mime.types;               # 引入MIME类型映射表文件

    # 配置HTTP的错误日志输出位置,错误级别为error
    error_log logs/error.log error;

    # 配置HTTP的日志格式,模板名为main
    log_format  main  '$remote_addr - $connection - $remote_user [$time_local] "$request" - $upstream_addr '
                      '$status  - $body_bytes_sent - $request_time - "$http_referer" '
                      '"$http_user_agent" - "$http_x_forwarded_for" - ';

    # 配置全局访问日志输出位置,并以模板main的日志格式输出
    access_log  logs/access.log  main;

    charset  utf-8;                         # 字符编码为utf-8
    variables_hash_max_size 2048;           # 变量哈希表最大值为2048B
    variables_hash_bucket_size 128;         # 变量哈希桶最大值为128B
    server_names_hash_bucket_size 256;      # 服务主机名哈希桶大小为256B
    client_header_buffer_size 32k;          # 请求头缓冲区大小为32KB
    large_client_header_buffers 4 128k;     # 最大缓存为4个128KB
    client_max_body_size 20m;               # 允许客户端请求的最大单个文件字节数为20MB
    sendfile on;                            # 开启零复制机制
    tcp_nopush on;                          # 启用在零复制时数据包最小传输的限制机制
    tcp_nodelay on;                         # 当处于保持连接状态时以最快的方式发送数据包
    keepalive_timeout  60;                  # 保持连接超时时间为60s
    client_header_timeout 10;               # 读取客户请求头的超时时间是10s
    client_body_timeout 10;                 # 请求体接收超时时间为10s
    server_tokens on;                       # 不显示Nginx版本信息
    include gzip.conf;                      # HTTP gzip的配置文件
    include proxy.conf;                     # HTTP代理配置
    include conf.d/*.conf;                  # 引入HTTP虚拟主机配置
}

nginx.conf中编辑在全局区域的配置指令均可按照Nginx配置指令规范在server、location指令域中被同名的配置指令覆盖。

2.1.2、配置归档

Nginx作为负载均衡应用时,是业务应用的入口,Nginx服务器的可用性决定了其所负责的所有被代理业务的可用性。所以Nginx进行配置变更时要及时做好归档和版本控制,因为Nginx配置是以文件方式存在的,所以可以将每次修改的文件以Git标签的方式在Git仓库中进行存档和版本控制。

2.1.3、配置变更

可以使用对应的配置模板进行Nginx配置内容的修改、配置部分的标准化及通用性约定,以便进行自动化操作。开源软件Ansible提供了自定义模板的功能,使用户可以按照预期设计更加严谨、规范地配置变更。Ansible支持批量操作,可以快速完成多台Nginx服务器配置文件的同步和加载。

2.1.4、配置发布

Ansible虽然提供了命令行的操作能力,但是用户权限、操作日志及快速回滚等操作仍不够便捷。Jenkins是一款Web化的持续集成发布工具,被广泛应用于业务应用的发布,拥有超过1000个插件,用户无须额外开发就可快速完成代码从代码仓库到运行部署的整个流程,同时还支持用户权限、操作日志及快速回滚等操作。

根据上述4个方面的规划,通过Jenkins与GitLab及Ansible的配合使用,无须复杂编程就可以快速搭建一套Web化的Nginx配置管理系统。应用架构如图所示。

  • Jenkins通过GitLab获取Git仓库中的Nginx文件。
  • Ansible根据Jenkins Web界面输入的参数与对应配置模板生成配置文件,更新本地的Nginx配置文件。
  • Ansible将更新后的配置文件同步到Nginx集群的所有Nginx服务器,并对Nginx进程执行reload操作,以加载更新后的配置。
  • Jenkins将更新后的Nginx配置文件以Git标签的方式进行归档。
  • 用户可以通过Jenkins获取对应Git仓库的所有Git标签,并根据需求选择对应的Git标签代码执行回滚操作。

2.2、配置归档工具GitLab

GitLab是使用Ruby语言编写的Git仓库管理工具,以Git作为代码管理工具,并提供了Web管理、WIKI及Issue等功能。GitLab是按照MIT许可证分发的开源软件,已被很多知名公司使用,目前由GitLabInc.开发维护。GitLab可以搭建在私有服务器上,被授权的用户可以创建自己的代码仓库,并可授权给多人协作进行维护。GitLab拥有与GitHub类似的功能,可以通过Web浏览器浏览代码、管理缺陷和注释。通过GitLab管理Nginx配置文件可以从Web浏览器中非常方便地浏览到提交过的历史变更,也可以利用Git相关命令实现Nginx配置的快速回滚操作。GitLab同样支持以Docker方式部署,官方在Docker Hub中也提供了可直接使用的镜像,通过编写相应的docker-compose脚本,可以快速搭建GitLab服务器,部署过程如下。

  • (1)初始化系统环境
    主机的操作系统为CentOS 7.6,初始化Docker环境如下:

    bash 复制代码
    yum install -y yum-utils                        # 安装yum工具
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo                              # 安装Docker安装源
    yum install -y docker-ce docker-compose # 安装Docker和docker-compose
    systemctl enable docker                 # 将Docker注册为自启动服务
    systemctl start docker
  • (2)编写docker-compose脚本
    创建docker-compose脚本,保存为gitlab.yaml。

    bash 复制代码
    gitlab:
        image: 'gitlab/gitlab-ce:latest'
        restart: always
        hostname: 'gitlab'
        container_name: gitlab
    #   environment:
    #     GITLAB_OMNIBUS_CONFIG: |
          #external_url 'https://gitlab.example.com'
          #Add any other gitlab.rb configuration here, each on its own line
        ports:
            - '8080:80'
            - '8443:443'
  • (3)持久化GitLab数据
    GitLab需要持久化的有3个部分的内容,分别是GitLab的配置、GitLab的代码仓库和GitLab日志。

    bash 复制代码
    # 运行GitLab容器
    docker -f gitlab.yaml up -d
    
    # 创建挂载目录并复制原容器内的文件
    mkdir -p /opt/data/apps/gitlab
    docker cp gitlab:/etc/gitlab /opt/data/apps/gitlab/config
    docker cp gitlab:/var/opt/gitlab /opt/data/apps/gitlab/data
    docker cp gitlab:/var/log/gitlab /opt/data/apps/gitlab/logs
    chown -R 998:998 /opt/data/apps/gitlab/logs
    # 添加挂载卷配置
    echo "
        volumes:
            - '/opt/data/apps/gitlab/config:/etc/gitlab'
            - '/opt/data/apps/gitlab/logs:/var/log/gitlab'
            - '/opt/data/apps/gitlab/data:/var/opt/gitlab'
    " >>gitlab.yaml
    docker stop gitlab
    docker rm gitlab
    docker-compose -f gitlab.yaml up -d

GitLab运行后可通过http://IP:8080访问登录。如果GitLab仅作为代码仓库应用,只需默认配置即可使用。

2.3、配置修改工具Ansible

Ansible是一款自动化的运维工具,是基于Python开发的。Ansible提供了一种自动化执行框架,其可以按照用户设计的剧本自动化执行相关操作。Ansible是基于模块工作的,其可以实现使用各种模块,并按照设计的剧本,批量对多个目标执行相同的操作。Ansible集合了众多运维工具的优点,配置更加简单方便。

2.3.1、Ansible剧本(playbook)

Ansible剧本是Ansible可以自动化执行一系列动作的执行方式。其通过YAML格式文件描述任务步骤,Ansible按照指定的步骤有序执行,并支持同步和异步,非常适合完成各种复杂的部署工作。Ansible剧本的相关术语如下。

  • 任务(Tasks),由YAML描述的一系列操作步骤。
  • 变量(Variables),被剧本引用,可以使剧本的设计更加灵活,并根据变量的值执行不同的步骤。
  • 模板(Templates),可根据变量的值,动态生成目标文件的预置文件,Ansible使用Jinja2模板语法。
  • 处理器(Handlers),当剧本任务条件满足时,触发执行的任务步骤。
  • 角色(Roles),描述某一特定任务的集合,其由以上术语的YAML描述文件组成。
  • 主机(Hosts),被剧本操作的目标主机IP或组名称,主机组名称由外部的hosts文件定义。

Ansible中每个剧本只有一个主入口文件,并且只有一个主线任务,主线任务可根据不同条件选用不同的角色,每个角色由任务、变量、模板、处理器的YAML描述文件组成。Ansible及剧本目录结构如下:

bash 复制代码
.
├── ansible.cfg                 # ansible配置文件
├── hosts                       # 目标主机资源文件
└── roles                       # 剧本目录,可自定义
    ├── nginx                       # 剧本角色名称及角色任务文件目录
    │   ├── defaults                # 角色默认变量目录
    │   │   └── main.yaml           # 默认变量自动加载的文件
    │   ├── files                   # 文件存放目录
    │   ├── handlers                # 处理器任务文件目录
    │   │   └── main.yaml           # 默认处理器任务自动加载的文件
    │   ├── tasks                   # 角色任务文件存放目录
    │   │   └── main.yaml           # 当前角色默认的任务入口文件
    │   └── templates               # 任务模板存放目录
    └── nginx.yaml                  # 剧本入口文件
2.3.2、基础语法
  • (1)步骤描述
    任务由多个步骤组成,每个步骤由步骤命名、任务模块、动作组成,配置样例如下:

    bash 复制代码
    - name: reload Nginx Service                    # 步骤名
      systemd: "name=nginx state=reloaded enabled=yes"  # 任务模块为systemd,动作是对系统服务
                                                            # Nginx执行reload操作
  • (2)执行顺序
    Ansible剧本中的步骤是自上而下执行的,在默认情况下,每个步骤的执行结果返回值如果不为0,就会报错,剧本任务也终止,也可以通过忽略错误指令继续运行,配置样例如下:

    bash 复制代码
    - name: Create conf.d
      shell: mkdir -p /etc/nginx/conf.d # 任务模块是shell,动作是执行mkdir命令
      ignore_errors: True               # 如果当前动作执行出错,忽略错误继续执行
  • (3)变量赋值
    Ansible剧本中变量赋值有静态和动态两种方式,一种是在defaults目录的main.yaml文件中静态地直接赋值,这通常被用作默认变量的赋值,配置样例如下:

    bash 复制代码
    confdir: "/etc/nginx"                           # 定义变量confdir的值为/etc/nginx

    另一种是在当前执行的过程中动态地进行变量赋值,被赋值的变量可以在当前剧本执行过程中被引用,配置样例如下所示:

    bash 复制代码
    # 方法一:将执行结果动态赋值给变量
      - name: Test Nginx Config
        shell: nginx -c {{ confdir }}/nginx.conf -t     # 任务模块是shell,动作是Nginx
                                                            # 执行-t参数命令
        register: test_result                           # 将执行结果赋值给变量test_result
    # 方法二:根据其他变量的值进行动态的变量赋值
      - name: check set_fact output 
        set_fact: output="{{ work }}/output"            # 为变量output赋值
        when: test_result                               # 当变量test_result为真时
  • (4)条件判断
    在Ansible剧本中可以根据变量的值判断是否执行当前步骤,配置样例如下:

    bash 复制代码
    - debug: msg="{{ test_result.stderr_lines }}"   # 任务模块debug,输出变量test_result.
                                                            # stderr_lines的内容
      when: not test_result.stderr_lines == ""  # 当test_result变量的输出结果不为空,
                                                            # 执行当前步骤
  • (5)外部引入
    多个步骤可以编写在一个YAML文件中,并通过指令include_tasks被其他任务引入,结合条件判断,它可以使主线任务因变量不同而存在多个不同分支,配置样例如下:

    bash 复制代码
    - name: "check system type"
      include_tasks: linux.yaml                 # 引入外部任务步骤
      when: ansible_os_family != "Windows"              # 当目标操作系统非windows时,执行当前
                                                            # 任务
2.3.3、剧本执行

Ansible剧本编写结束后,使用ansible-playbook命令调用剧本入口文件执行相应的剧本,即可自动完成预设的任务。

bash 复制代码
ansible-playbook -i /etc/ansible/hosts /etc/ansible/roles/nginx.yaml --extra-vars 'hosts=192.168.2.145'

2.4、配置发布工具Jenkins

Jenkins是基于Java开发的一个开源的持续集成项目,其提供了一个可扩展的可对代码持续集成、发布(代码编译、打包、部署)及交付的Web化操作平台。Jenkins拥有超过1000个插件,使其支持包括SVN、Git等多种版本的管理工具(SCM)的代码管理,也可以快速实现Java、Node.js、.Net等语言项目的编译构建,并支持包括Docker在内的多种形式的部署交付。通过Jenkins的Web化管理界面,依赖各种强大的插件功能,可以使Nginx的配置变更管理变得更加便捷和安全。

使用Jenkins官方提供的Docker镜像,可以很方便地搭建Jenkins工作环境,搭建过程如下:

  • (1)初始化系统环境

    主机的操作系统为CentOS 7.6,初始化Docker环境如下:

    bash 复制代码
    yum install -y yum-utils                        # 安装yum工具
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo                               # 安装Docker安装源
    yum install -y docker-ce docker-compose # 安装Docker和docker-compose
    systemctl enable docker                 # 将Docker注册为自启动服务
    systemctl start docker
  • (2)编写docker-compose脚本

    将脚本保存为jenkinsci.yaml

    bash 复制代码
    jenkinsci:
        image: 'jenkinsci/blueocean'
        restart: always
        hostname: 'jenkinsci'
        container_name: jenkinsci
        environment:
            - PATH=/opt/apps/apache-maven-3.5.3/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
            - JAVA_OPTS="-Duser.timezone=Asia/Shanghai"
            - JENKINS_SLAVE_AGENT_PORT=50000
        ports:
            - '8086:8080'
            - '50000:50000'
  • (3)数据持久化

    Jenkins需要持久化的是Jenkins的运行目录,该目录包含其运行的所有配置文件,具体如下:

    bash 复制代码
    docker cp jenkinsci:/var/jenkins_home /opt/data/apps/jenkinsci/
    chown -R 1000:1000 /opt/data/apps/jenkinsci/jenkins_home
    echo "
        volumes:
            - '/opt/data/apps/jenkinsci/jenkins_home:/var/jenkins_home'
            - '/opt/data/apps/jenkinsci/apps:/opt/apps'
    " >>jenkinsci.yaml
    
    docker stop jenkinsci
    docker rm jenkinsci
    docker-compose -f jenkinsci.yaml up -d
  • (4)初始化配置及插件

    Jenkins启动后,在浏览器中访问http://IP:8086即可进入初始化安装界面,使用初始化密码登录,选择安装推荐插件即可。

    通过以下指令获取初始化密码:

    bash 复制代码
    docker exec -it jenkinsci2 cat /var/jenkins_home/secrets/initialAdminPassword

    Jenkins是以任务(Job)为管理单元的,常用的任务类型有自由风格、Maven项目、文件夹和流水线(pipeline)四种,本样例中仅使用自由风格任务类型。自由风格及流水线任务按照工作流程被划分为多个阶段,Jenkins负责维护和管理任务在每个阶段的执行,并通过工作流的状态,按照任务的设定推动任务工作流的完成。自由风格任务的6个阶段配置如表所示。

2.5、Nginx配置管理实例

根据部署规划,如果对Nginx集群配置实现管理,需要在GitLab、Jenkins上完成相关的配置及编写Ansible剧本。本节将通过对配置文件nginx.conf举例GitLab、Jenkins及Ansible的配置,以实现Nginx配置管理的操作。

2.5.1、GitLab配置

首先为Nginx配置创建用户及Nginx项目,操作步骤如下。

  • 1)创建发布用户gitlab_nginx: Admin Area→Users→NewUser用户名gitlab_nginx。
  • 2)创建项目组nginx: GitLab登录后创建项目组(Group)nginx,可视级别(Visibility Level)为Private。
  • 3)添加组用户:将用户gitlab_nginx添加到项目组nginx中,权限为Developer。
  • 4)创建Nginx配置项目homebox:按照Nginx集群名称创建Gitlab项目,nginx组项目→New project,命名为home-box。
  • 5)初始化:进入Nginx配置文件目录,将配置文件初始化到GitLab仓库中。初始化命令如下:
bash 复制代码
git init
git remote add origin http://IP:8080/nginx/homebox.git
git add .
git commit -m "Initial commit"
git push -u origin master
2.5.2、Ansible剧本

根据Nginx配置目录的规划,定义Ansible剧本目录结构如下:

bash 复制代码
.
├── ansible.cfg
├── hosts
└── roles
    ├── nginx
    │   ├── defaults
    │   │   └── main.yaml
    │   ├── files
    │   │   ├── gzip.conf
    │   │   ├── fscgi.conf
    │   │   └── proxy.conf
    │   ├── handlers
    │   │   └── main.yaml
    │   ├── tasks
    │   │   ├── config_nginx.yaml
    │   │   ├── config_server.yaml
    │   │   ├── config_status.yaml
    │   │   ├── deploy.yaml
    │   │   ├── install.yaml
    │   │   ├── rollback.yaml
    │   │   └── main.yaml
    │   └── templates
    │       ├── nginx.conf
    │       ├── server.conf
    │       └── status.conf
    └── nginx.yaml
  • defaults目录中的main.yaml是自定义默认变量值的描述文件,文件内容如下:

    bash 复制代码
    self_services: nginx
    exclude: ".git"
    rsync_opts:
        - "--exclude={{ exclude }}"
    process_events: >
        worker_processes auto;
        worker_rlimit_nofile 65535;
        worker_priority -5;
    modules: ""
    server: ""
    confdir: "/etc/nginx"
    env_packages:
        - pcre-devel
        - zlib-devel
        - openssl-devel
        - libxml2-devel
        - libxslt-devel
        - gd-devel
        - GeoIP-devel
        - jemalloc-devel
        - libatomic_ops-devel
        - luajit
        - luajit-devel
        - perl-devel
        - perl-ExtUtils-Embed
  • gzip.conf、fscgi.conf、proxy.conf这3个文件是全局的配置文件,放在files目录中仅作Nginx初始化安装时使用。

  • handlers的main.yaml是处理器任务描述文件,文件内容如下:

    bash 复制代码
    # 重启Nginx服务任务
    - name: Restart Nginx services
      service:
        name: "{{ self_services }}"
        state: restarted
    
    # 启动Nginx服务任务
    - name: Start Nginx services
      service:
        name: "{{ self_services }}"
        state: started
  • tasks目录中的main.yaml是当前角色的默认入口文件,文件内容如下:

    bash 复制代码
    ---
        # 当变量deploy的值为deploy时执行deploy.yaml的任务步骤
        - name: "Starting deploy for nginx"
          include_tasks: deploy.yaml
          when: deploy == "deploy"
    
        # 当变量deploy的值为rollback时执行rollback.yaml的任务步骤
        - name: "Starting rollback for nginx"
          include_tasks: rollback.yaml
          when: deploy == "rollback"
  • tasks目录中的deploy.yaml是修改配置的任务分支描述文件,文件内容如下:

    bash 复制代码
    ---
            # 检查目标服务器是否存在配置文件,并将检查结果赋值给变量has_nginx
        - name: "check nginx service"
          stat: path={{ confdir }}/nginx.conf
          register: has_nginx
    
            # 如果目标服务器不存在Nginx服务器则调用分支任务install进行安装
        - name: "Starting install nginx "
          include_tasks: install.yaml
          when: not has_nginx.stat.exists
    
            # 如果当前任务为配置nginx.conf,则调用config_nginx任务配置nginx.conf文件
        - name: "Starting config nginx.conf "
          include_tasks: config_nginx.yaml
          when: not jobname == "" and jobname == "nginx.conf"
    
            # 如果当前任务为配置status.conf,则调用config_status任务配置status.conf文件
        - name: "Starting config website status for nginx"
          include_tasks: config_status.yaml
          when: not jobname == "" and jobname == "status.conf"
    
            # 如果当前任务为配置server.conf,则调用config_server任务配置server.conf文件
        - name: "Starting config website server for nginx"
          include_tasks: config_server.yaml
          when: not jobname == "" and jobname == "server.conf"
    
            # 初始化rsync模块的ssh免登录key
        - name: add authorized_keys
          authorized_key:
              user: "{{ ansible_user_id }}"
              key: "{{ lookup('file', '/home/jenkins/.ssh/id_rsa.pub') }}"
              state: present
              exclusive: no
    
            # 使用rsync模块将Nginx配置文件同步到目标机器
        - name: check rsync_opts rsync dir
          synchronize:
              src: "{{ work }}/"
              dest: "{{ confdir }}"
              delete: yes
              copy_links: yes
              private_key: "/home/jenkins/.ssh/id_rsa"
              rsync_opts: "{{ rsync_opts }}"
            register: rsync_result
    
            # 输出rsync的执行详情
        - debug: msg="{{ rsync_result.stdout_lines }}"
    
            # 使用Nginx的测试参数测试配置文件是否存在语法错误
        - name: Test Nginx Config
          shell: nginx -c {{ confdir }}/nginx.conf -t -q
          ignore_errors: True
          register: test_result
    
            # 如果执行检测失败,则停止当前任务,并输出检测结果
        - fail: msg="{{ test_result.stderr_lines }}"
          when: test_result.failed
    
            # 热加载Nginx进程
        - name: reload Nginx Service
          systemd: "name=nginx state=reloaded enabled=yes"
  • tasks目录中的rollback.yaml是回滚配置的任务分支描述文件,文件内容如下:

    bash 复制代码
    ---
        - name: check rsync_opts rsync dir
          synchronize:
              src: "{{ work }}/"
              dest: "{{ confdir }}"
              delete: yes
              copy_links: yes
              private_key: "/home/jenkins/.ssh/id_rsa"
              rsync_opts: "{{ rsync_opts }}"
            register: rsync_result
    
        - debug: msg=" {{ rsync_result.stdout_lines }} "
    
        - name: "Test Nginx Config"
          shell: nginx -c {{ confdir }}/nginx.conf -t -q
          ignore_errors: True
          register: test_result
    
        - fail: msg="{{ test_result.stderr_lines }}"
          when: test_result.failed
    
        - name: reload Nginx Service
          systemd: "name=nginx state=reloaded enabled=yes"
          register: test_result
  • tasks目录中的config_nginx.yaml是Nginx配置文件nginx.conf的任务分支描述文件,文件内容如下:

    bash 复制代码
    ---
            # 通过模板文件与外部输入变量生成新的nginx.conf文件,替换Jenkins的工作目录中的
            # nginx.conf
        - name: "Starting init nginx.conf "
          template: src=nginx.conf dest={{ work }}/nginx.conf
          delegate_to: localhost
    
            # 因外部参数中的单、双引号及变量符号被转义,此处则重新替换回原符号
        - name: "Starting format nginx.conf "
          shell: sed -i 's/%24/$/g' {{ work }}/nginx.conf && sed -i 's/%9c/\"/g' {{ work }}/nginx.conf && sed -i "s/%98/\'/g" {{ work }}/nginx.conf && python /etc/ansible/bin/nginxfmt.py {{ work }}/nginx.conf
          delegate_to: localhost
  • tasks目录中的config_server.yaml是Nginx配置文件中配置各虚拟主机的任务分支描述文件,文件内容如下:

    bash 复制代码
    ---
            # 通过模板文件与外部输入变量生成新的虚拟主机文件,替换Jenkins的工作目录中虚拟主机
            # 文件并在conf.d目录下保存
        - name: "Starting init {{ jobname }} "
          template: src=server.conf dest={{ workdir }}/conf.d/{{ jobname }}.conf
          delegate_to: localhost
    
            # 因外部参数中的单、双引号及变量符号被转义,此处则重新替换回原符号
        - name: "Starting format nginx.conf "
          shell: sed -i 's/%24/$/g' {{ work }}/nginx.conf && sed -i 's/%9c/\"/g' {{ work }}/nginx.conf && sed -i "s/%98/\'/g" {{ work }}/nginx.conf && python /etc/ansible/bin/nginxfmt.py {{ work }}/nginx.conf
          delegate_to: localhost
  • tasks目录中的config_staus.yaml是Nginx配置文件中统一状态监控的虚拟主机任务描述文件,文件内容如下:

    bash 复制代码
    ---
            # 通过模板文件与外部输入变量生成新的状态监控虚拟主机文件,替换Jenkins的工作目录中
            # 的conf.d目录下保存
        - name: "Starting init status.conf "
          template: src=status.conf dest={{ workdir }}/conf.d/status.conf
          delegate_to: localhost
    
          # 因外部参数中的单、双引号及变量符号被转义,此处则重新替换回原符号
        - name: "Starting format nginx.conf "
          shell: sed -i 's/%24/$/g' {{ work }}/nginx.conf && sed -i 's/%9c/\"/g' {{ work }}/nginx.conf && sed -i "s/%98/\'/g" {{ work }}/nginx.conf && python /etc/ansible/bin/nginxfmt.py {{ work }}/nginx.conf
          delegate_to: localhost
  • tasks目录中的install.yaml是Nginx的部署任务描述文件,文件内容如下:

    bash 复制代码
        # 添加Nginx yum安装源
        - name: add repo
          yum_repository:
              name: nginx
              description: nginx repo
              baseurl: http://nginx.org/packages/centos/7/$basearch/
              gpgcheck: no
              enabled: 1
        # 安装环境依赖包
        - name: install centos packages
          yum:
              name: "{{ env_packages }}"
              disable_gpg_check: yes
              state: present
        # yum方式安装Nginx,并触发处理器Start Nginx services任务
        - name: install nginx
          yum:
              name: nginx
              state: latest
          notify: Start Nginx services
  • templates目录中的nginx.conf为配置文件nginx.conf的模板文件,文件内容如下:

    bash 复制代码
    {{ modules }}
    {{ process_events }}
    stream {
        {{ stream }}
        include conf.d/*.ream;
    }
    http {
        {{ http }}
    
        {% if gzip != "false" %}
        include gzip.conf;                      # HTTP gzip的配置文件
        {% endif %}
    
        {% if fscgi != "false" %}
        include fscgi.conf;                     # FastCGI代理的配置文件
        {% endif %}
    
        {% if proxy != "false" %}
        include proxy.conf;     # HTTP代理配置
        {% endif %}
    
        include conf.d/*.conf;
    }
  • templates目录中的server.conf为配置文件中虚拟服务器的模板文件,文件内容如下:

    bash 复制代码
    {{ global }}
    upstream {
    {{ upstream }}
    }
    server{
    {{ server }}
    }
  • templates目录中的status.conf为配置文件中用于状态监控的虚拟主机模板文件,文件内容如下:

    bash 复制代码
    {{ global }}
    server{
    {{ server }}
    }
  • roles目录中的nginx.yaml为主剧本文件,该剧本文件调用了角色Nginx,使用外部变量、应用角色Nginx中的任务描述文件完成Nginx的配置修改、同步及加载动作,文件内容如下:

    bash 复制代码
    ---
    # 变量hosts由外部输入,设定操作的目标主机
    - hosts:
            - "{{ hosts }}"
        max_fail_percentage: 30 # 当有30%的操作目标任务执行出错时,则终止整个剧本的执行
        serial: "{{ serial }}"  # 该模块可以设定操作目标数量实现灰度发布的效果,当设定为
                                            # 30%且操作目标为3台时,则表示一次仅操作一个目标
        roles:
            - nginx                     # 调用Nginx角色
2.5.3、Jenkins配置

根据GitLab及Ansible剧本的设置,Jenkins需要创建具有如下操作内容的任务:

  • 通过Web页面设定Nginx的配置内容。
  • 使用账号gitlab_nginx从GitLab中获取Nginx的配置文件。
  • 调用Ansible剧本实现Nginx配置文件中的修改、同步及加载。
  • 实现修改文件的归档。
  • 实现修改内容的快速回滚。
  • 对操作者设定访问的权限。
  • 对发布的历史可以查看。
  • 可以满足多个Nginx集群的配置管理。

按照上述需求的设定,可以将不同的Nginx集群以文件夹类型任务进行创建,每个Nginx集群文件夹中包括nginx.conf、status.conf全局配置的自由风格任务,每个虚拟主机则按照虚拟主机名称创建自由风格任务分列在该集群文件夹下。任务层级结构如下:

bash 复制代码
homebox                 # Nginx集群名称,任务类型为文件夹
    nginx.conf              # nginx.conf任务,任务类型为自由风格
    status.conf             # status.conf任务,任务类型为自由风格
    www.nginxbar.org        # 虚拟主机任务,任务类型为自由风格

该任务层级设计,可以使操作者清晰地知道所操作的Nginx集群,同时还可以结合Jenkins的权限功能进行细粒度的权限控制。任务配置nginx.conf的创建步骤首先是在全局配置阶段通过参数化构建插件实现Web化变量的输入,通过参数化配置,设计部署与回滚操作选项。当选择回滚时,通过Git参数插件提供Git标签(tag)筛选功能列出可用的Git标签,选择后执行回滚操作。同时还要按照之前的规划在此阶段将nginx.conf文件内容分割成多个不同的变量,并定义为构建参数,让发布者在点击参数化构建后,可以通过Web界面进行选择和修改。在构建操作配置阶段,编写shell脚本对所有输入的变量进行判断、修整后通过ansible-playbook命令传递给Nginx剧本,完成Nginx配置的修改、同步及加载操作。若在构建后动作配置阶段,则通过Git Publisher插件将当前的修改标记Git标签进行归档。详细配置过程如下。

  • (1)全局配置

    • 定义时间戳变量格式Change date pattern for the BUILD_TIMESTAMP(build time-stamp)variable为yyyyMMdd

    • 选项参数deploy,选项(Choices)为deploy和rollback,用以定义构建脚本中的变量,进行控制是更新配置文件还是回滚以前的配置操作。

    • Git参数(Git Parameter)tag,参数类型为tag,过滤(Tag Filter)配置为nginx.conf-deploy-*,排序(SortMode)选择DESCENDING,默认值为Default Value。该参数可以获取当前任务Git仓库的分支及tag列表,这里获取过滤被标记为部署成功的Git标签,用以实现代码回滚。

    • 文本参数modules,加载动态模块,参数值如下:

      bash 复制代码
      # 选择加载动态模块
      load_module "modules/ngx_http_geoip_module.so";
      load_module "modules/ngx_http_image_filter_module.so";
      load_module "modules/ngx_http_xslt_filter_module.so";
    • 文本参数process_events,工作进程及事件配置,参数值如下:

      bash 复制代码
      # 工作进程及事件配置,定义文本参数process_events
      worker_processes auto;          # 启动与CPU核数一致的工作进程
      worker_priority -5;                     # 工作进程在Linux系统中的优先级为-5
      
      events {
          worker_connections  65535;              # 每个工作进程的最大连接数
          multi_accept on;                        # 每个工作进程每次都可以接受多个连接
      }
    • 文本参数stream,加载TCP/UDP代理配置,参数值如下:

      bash 复制代码
      # 配置TCP/UDP代理的日志格式模板,模板名为tcp
      log_format  tcp  '$remote_addr - $connection - [$time_local] $server_addr:$server_port - $protocol'
                          '- $status - $upstream_addr - $bytes_received - $bytes_sent - $session_time '
                          '- $proxy_protocol_addr:$proxy_protocol_port ';
      
      # 配置TCP/UDP代理的错误日志输出位置,错误级别为error
      error_log /var/log/nginx/tcp_error.log error;
    • 文本参数http,加载HTTP配置,参数值如下:

      bash 复制代码
      include       mime.types;               # 引入MIME类型映射表文件
      
      # 配置HTTP的错误日志输出位置,错误级别为error
      error_log /var/log/nginx/error.log error;
      
      # 配置HTTP的日志格式,模板名为main
      log_format  main  '$remote_addr - $connection - $remote_user [$time_local] "$request" - $upstream_addr '
                       '$status  - $body_bytes_sent - $request_time - "$http_referer" '
                       '"$http_user_agent" - "$http_x_forwarded_for" - ';
      
      # 配置全局访问日志输出位置,并使用模板main的日志格式输出
      access_log  /var/log/nginx/access.log  main;
      
      charset  utf-8;                         # 字符编码为utf-8
      variables_hash_max_size 2048;           # 变量哈希表最大值为2048字节
      variables_hash_bucket_size 128; # 变量哈希桶最大值为128字节
      server_names_hash_bucket_size 256;      # 服务主机名哈希桶大小为256字节
      client_header_buffer_size 32k;          # 请求头缓冲区的大小为32KB
      large_client_header_buffers 4 128k;     # 最大缓存为4个128KB
      client_max_body_size 20m;               # 允许客户端请求的最大单个文件字节数为20MB
      sendfile on;                            # 开启零复制机制
      tcp_nopush on;                          # 启用在零复制时数据包最小传输的限制机制
      tcp_nodelay on;                         # 当处于保持连接状态时,以最快方式发送数据包
      keepalive_timeout  60;                  # 保持连接超时时间为60s
      client_header_timeout 10;               # 读取客户请求头的超时时间是10s
      client_body_timeout 10;         # 请求体接收超时时间为10s
      server_tokens on;                       # 不显示Nginx版本信息
    • 布尔值参数proxy,该设置默认为选中,用以选择是否加载代理相关指令配置。

    • 布尔值参数gzip,该设置默认为不选中,用以选择是否加载gzip相关指令配置。

    • 布尔值参数fscgi,该设置默认为不选中,用以选择是否加载FastCGI相关指令配置。

  • (2)代码仓库配置

    • 添加GitLab的地址、账户及密码。
    • 构建分支(Branches to build),填写${tag}, Git参数定义的变量。
  • (3)构建环境配置

    • 选择构建前先删除之前的构建目录(Delete workspacebefore build starts)。
    • 选择设置Jenkins用户变量(Set jenkins user build variables)。
  • (4)构建操作配置
    编写构建脚本。

    bash 复制代码
    #!/bin/bash
    set -x
    
    # 初始化变量
    jobname=${JOB_NAME}
    jobnum=${BUILD_TIMESTAMP}-${BUILD_NUMBER}
    OLD_IFS="$IFS" ;IFS="/" ;arr=($jobname) ;IFS="$OLD_IFS"
    cluster=${arr[1]}
    name=${arr[2]}
    
    # 部署时执行的操作
    if [ "$deploy" == "deploy" ];then
    
        rm -rf *.default
    
        # 对变量中的单引号、双引号及变量符号进行转义
        stream=${stream//$/%24}
        stream=${stream//\'/%98}
        stream=${stream//\"/%9c}
    
        http=${http//$/%24}
        http=${http//\'/%98}
        http=${http//\"/%9c}
    
        # 生成当前配置变量
        jobvars="process_events='$process_events' modules='$modules' stream= '$stream' http='$http' proxy='$proxy' gzip='$gzip' fscgi='$fscgi'"
    
    fi
    
    # 回滚时执行的操作
    if [ "$deploy" == "rollback" ];then
        OLD_IFS="$IFS" ;IFS="-" ;arr=($tag) ;IFS="$OLD_IFS"
        jobnum=${arr[${#arr[@]}-2]}-${arr[${#arr[@]}-1]}
        jobvars=""
    fi
    
    # 生成版本信息
    echo  "#$cluster-$name-$jobnum $deploy by ${BUILD_USER}"  >version.txt
    
    # 生成任务变量
    vars="hosts=$cluster jobname=$name  work=${WORKSPACE} serial=30% deploy= '$deploy' $jobvars "
    
    # 执行Ansible剧本
    ansible-playbook -i /etc/ansible/hosts /etc/ansible/roles/nginx.yaml --extra-vars "$vars "
    if [ $? -ne 0 ];then exit 1; fi
    
    # 执行部署操作成功时,对变更的配置文件进行归档
    if [ "$deploy" == "deploy" ];then
        git add .
        git commit -m "#$cluster-$name-$jobnum deploy by ${BUILD_USER}"
    fi
    • 添加修改构建名(Update build name),选择从文件名中读取(Read from file),文件名填写为version.txt。
  • (5)构建后动作配置

    • 使用Git Publisher插件,将修改成功的代码提交到Gitlab中,并打标签(tag)为当前构建的时间戳和编号。
    • 选择构建成功后,再打标签(Push Only If BuildSucceeds)。
    • 选择合并结果(Merge Results)。
    • 标签名(Tag to push),填写${JOB_NAME}-${deploy}-${BUILD_TIMESTAMP}-${BUILD_NUMBER}。
    • 选择创建新标签(Create new tag)。

配置文件staus.conf及server.conf的Jenkins任务创建过程 仅与nginx.conf在构建的参数配置和shell脚本上略有变化,此处就不一一详细举例了。Jenkins拥有诸多功能强大的插件,使其可以完成各种部署及发布的操作需求。例如,可以通过jQuery插件对Jenkins的操作界面进行自定义修改,增加根据选择项动态实现参数选项的显示和隐藏,或者增加自定义按钮实现配置预览等功能,此处就不再进行深入探讨了。结合GitLab、Ansible及Jenkins等开源软件,用户可以根据实际需求,不断优化并打造符合自身需求的Nginx配置管理工具。

相关推荐
硅基手札1 小时前
【Linux内核专栏 11】驱动框架:platform / char / misc / 设备树
linux·运维·服务器
cpolar技术支持1 小时前
外网测试机的异常传不回内网?自托管 Sentry,用 cpolar 打通错误上报链路
python·nginx·docker·cpolar·sentry
DongQiShanRen2 小时前
裁决台账双向互校(上):名册与实物的第一道对账
java·linux·运维·数据库·人工智能·自然语言处理·数据挖掘
归秋1422 小时前
2026企业AI办公工具选型指南:从场景匹配到平台评估
大数据·运维·人工智能
well06123 小时前
Linux 文件相关底层知识
linux·运维·服务器
Lsetea3 小时前
OpenSSL verify报CA certificate key too weak:auth_level与弱密钥链排查
运维·https·ssl证书·openssl·证书链
熊猫钓鱼>_>3 小时前
从闲置平板到家里的“控制大脑“:鸿蒙智慧中控面板完整实战
运维·人工智能·华为·自动化·电脑·ai编程·harmonyos
DP DPharness4 小时前
浏览器自动化插件时,三个可核对的判断维度
运维·自动化
姜鱼问生4 小时前
数据库备份 + 灾难恢复演练:从 dump 恢复全库
运维·数据库