背景
在Nginx集群有一定的规模时,比较让人头疼的问题有2个,一是如何在不reload nginx的情况下,动态更新后端rs,减少nginx reload的性能损耗,也能更好的对接到内部的部署平台;二是如何标准化的修改nginx配置并下发,统一对配置进行管理。
目标
目标一:可以在不Reload Nginx的情况下,动态更新upstream后端rs的信息
目标二:可以通过Jenkins对Nginx的配置进行更新、下发、删除文件、证书更新和回滚操作
功能预览
修改配置并上传git
配置下发
规划说明
整体架构
简要流程:
1.Nginx配置管理
- 管理员通过jenkins配置的job修改nginx配置并以tag方式提交到gitlab
- 管理员通过jenkins的配置下发job,下发指定tag的配置到选定的nginx集群主机上
2.Nginx动态负载均衡,使用开源upsync模块配置
- 管理员通过调用etcd的接口维护(增加、删除、修改)指定upstream下的rs
- Nginx集群会动态的更新指定upstream下的rs,进行负载调度
环境规划
这里规划的系统配置和角色都是实验环境,生产环境按照实际情况拆分,升配。
IP地址 | 主机名 | 系统配置 | 服务角色 |
10.0.1.222 | ops-node-01 | CentOS 7.9.2009 X86_64 2c*2g | Nginx-Pord-L7集群 |
10.0.1.223 | ops-node-02 | CentOS 7.9.2009 X86_64 2c*2g | Nginx-Pord-L7集群 |
10.0.1.224 | ops-node-03 | CentOS 7.9.2009 X86_64 2c*2g | Nginx-Pord-L4集群 |
10.0.1.225 | ops-node-04 | CentOS 7.9.2009 X86_64 4c*8g | etcd/gitlab/jenkins/ansible和业务应用 |
数据存储目录:
- ETCD集群12379端口实例数据存储目录:/root/docker/etcd-vloume-12379
- ETCD集群22379端口实例数据存储目录:/root/docker/etcd-vloume-22379
- ETCD集群32379端口实例数据存储目录:/root/docker/etcd-vloume-32379
- Gitlab数据存储目录:/root/docker/gitlab-volume
- Jenkins数据存储目录:/root/docker/jenkins-volume
- Nginx数据存储目录:/root/docker/nginx-volume
注意:为了便于管理,建议将Jenkins、Gitlab、Etcd独立出来,只做运维内部使用,和线上区别开。
Gitlab规划
Gitlab用于存储Nginx的配置。规划一个Nginx分组,在Nginx分组中维护两个仓库,分别是Nginx-Prod-L4(四层负载均衡配置)和Nginx-Prod-L7(七层负载均衡配置)
实际生产中会有很多的配置仓库,建议按照环境进行区分。这里的仓库命名规范:工具名称-环境-用途
Nginx规划
Nginx用于反向代理,使用新浪开源的upsync模块,结合etcd进行动态负载均衡。规划两套集群,分别是Nginx-Prod-L4(四层负载均衡集群)和Nginx-Prod-L7(七层负载均衡集群),规范标准化配置。
Nginx-Prod-L7配置规划如下:
# 存放证书的目录
├── certs
│ ├── zhushiyang.net.key
│ └── zhushiyang.net.pem
# 存放upstream从etcd拉取rs的信息,进行持久化的目录
├── dump_upstreams
│ ├── blog_cluster_upsync.conf
│ └── web_cluster_upsync.conf
├── fastcgi.conf
├── fastcgi.conf.default
├── fastcgi_params
├── fastcgi_params.default
├── koi-utf
├── koi-win
├── mime.types
├── mime.types.default
# nginx主配置文件,已经优化
├── nginx.conf
├── nginx.conf.default
├── proxy.conf
├── scgi_params
├── scgi_params.default
# 存放upstream配置的目录
├── upstreams
│ ├── blog_cluster_upsync.conf
│ └── web_cluster_upsync.conf
├── uwsgi_params
├── uwsgi_params.default
# 存放虚拟主机配置的目录
├── vhosts
│ ├── blog.zhushiyang.net.conf
│ └── www.zhushiyang.net.conf
└── win-utf
Nginx-Prod-L4配置规划如下:
├── fastcgi.conf
├── fastcgi.conf.default
├── fastcgi_params
├── fastcgi_params.default
├── koi-utf
├── koi-win
├── mime.types
├── mime.types.default
# nginx主配置文件,已经优化
├── nginx.conf
├── nginx.conf.default
├── proxy.conf
├── scgi_params
├── scgi_params.default
# 存放upstream配置的目录
├── streams
│ └── etcd.zhushiyang.net.conf
├── uwsgi_params
├── uwsgi_params.default
└── win-utf
- 开启四层代理:--with-stream
Jenkins规划
Jenkins只用作Nginx集群的配置更新和下发。规划两个目录,分别是Nginx-Prod-L4(四层负载均衡Folder)和Nginx-Prod-L7(七层负载均衡Folder)。Folder命名规范:工具名称-环境-用途
Nginx-Prod-L7的Job布局:
- configure_deploy:下发配置文件
- nginx.conf:更新nginx.conf文件到gitlab
- proxy.conf:更新proxy.conf文件到gitlab
- upstreams:维护upstream,每个upstream是一个job,命名格式是upstream名称,不需要带.conf后缀,会把配置推送到gitlab
- vhosts:维护虚拟主机,每个虚拟主机是一个job,命名格式是域名,不需要带.conf后缀,会把配置推送到gitlab
Nginx-Prod-L4的Job布局:
- configure_deploy:下发配置文件
- nginx.conf:更新nginx.conf文件到gitlab
- streams:维护stream,每个stream是一个job,命名格式是域名,不需要带.conf后缀,会把配置推送到gitlab
Etcd规划
Etcd集群用于存储Nginx的upstream下的RS信息。在一台机器上启动三个不同端口的etcd容器,组成etcd集群(生产中要分成3台机器组建集群),使用Nginx-Prod-L4代理Etcd集群。管理员调用etcd接口动态配置Nginx-Prod-L7集群的upstream。
Ansible规划
Ansible用于下发配置、检验和重启Nginx操作。不需要太复杂,需要在jenkins容器中进行安装。主机的资产文件从宿主机进行挂载。
环境搭建
注意:所有的应用均使用docker搭建。
注意:所有的应用均使用docker搭建。
注意:所有的应用均使用docker搭建。
系统初始化
按照下面的步骤正常初始化系统即可,生产环境要按照组件类别进行系统调优。
# 1.关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
# 2.关闭selinux
setenforce 0
sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
# 3.关闭postfix
systemctl stop postfix.service
systemctl disable postfix.service
# 4.下载epel源并安装基础依赖包
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum install -y wget vim lsof unzip net-tools ntpdate bash-completion lrzsz jq git mlocate tree
# 5.设置时间同步
/usr/sbin/ntpdate time1.aliyun.com
echo '*/1 * * * * /usr/sbin/ntpdate time1.aliyun.com > /dev/null 2>&1' > /var/spool/cron/root
chmod 600 /var/spool/cron/root
systemctl restart crond
# 6.设置hosts
cat >> /etc/hosts <<EOF
10.0.1.222 ops-node-01
10.0.1.223 ops-node-02
10.0.1.224 ops-node-03
10.0.1.225 ops-node-04
EOF
# 7.设置UTF-8
echo "LANG=\"en_US.UTF-8\"">/etc/locale.conf
source /etc/locale.conf
# 8.设置ulimit
cat > /etc/security/limits.conf << EOF
* soft nofile 1024000
* hard nofile 1024000
* soft nproc 1024000
* hard nproc 1024000
EOF
cat > /etc/security/limits.d/20-nproc.conf << EOF
* soft nproc 409600
root soft nproc unlimited
EOF
# 9.设置终端超时,别名和PS变量
vim /etc/profile
export TMOUT=0
alias grep='grep --color=auto'
alias ll='ls -l --color=auto --time-style=long-iso'
PS1='\[\e[0;33m\]\u\[\e[0m\]@\[\e[0;32m\]\h\[\e[0m\]:\[\e[0;34m\]\w\[\e[0m\]\$ '
source /etc/profile
# 10.按照环境规划设置主机名
# 在10.0.1.222上执行
hostnamectl set-hostname ops-node-01
# 在10.0.1.223上执行
hostnamectl set-hostname ops-node-02
# 在10.0.1.224上执行
hostnamectl set-hostname ops-node-03
# 在10.0.1.225上执行
hostnamectl set-hostname ops-node-04
安装docker
在所有机器上安装docker,配置阿里云镜像加速,并加入开机启动。没有限制docker版本,也没有自定义太多的docker配置,可以按照实际情况定制。
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
yum install -y docker-ce
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
systemctl enable --now docker
# 配置镜像加速
cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors":["https://q2gr04ke.mirror.aliyuncs.com"]
}
EOF
systemctl restart docker
安装etcd
在ops-node-04,10.0.1.225节点上安装etcd集群(使用的是不同端口的etcd实例模拟的集群)。
下面的启动命令在官方文档(Run etcd clusters inside containers | etcd)中有示例,做了一些变量和参数的调整(参数对集群进行了一些优化),如果你对etcd不太了解,可以看一下官方文档(etcd),或者看一下《云原生分布式存储基石 etcd深入解析》这本书。
# 初始化环境变量,在每个节点上都要执行(这里是一台服务器模拟集群)
# 设置三个ETCD的节点名称(这里是一台服务器模拟集群)
ETCD_NAME_1=ops-etcd-01
ETCD_NAME_2=ops-etcd-02
ETCD_NAME_3=ops-etcd-03
# 设置三个ETCD的节点IP地址(这里是一台服务器模拟集群)
ETCD_NODE_IP_1=10.0.1.225
ETCD_NODE_IP_2=10.0.1.225
ETCD_NODE_IP_3=10.0.1.225
# 设置ETCD的节点唯一ID
ETCD_CLUSTER_TOKEN=etcd-cluster
# 设置ETCD初始化集群状态
ETCD_CLUSTER_STATE=new
# 设置ETCD集群CLUSTER的地址
ETCD_CLUSTER=${ETCD_NAME_1}=http://${ETCD_NODE_IP_1}:12380,${ETCD_NAME_2}=http://${ETCD_NODE_IP_2}:22380,${ETCD_NAME_3}=http://${ETCD_NODE_IP_3}:32380
# 设置ETCD镜像仓库
ETCD_REGISTRY=quay.io/coreos/etcd
# 设置ETCD镜像版本
ETCD_VERSION=v3.4.20
# 初始化第一个节点
THIS_NAME=${ETCD_NAME_1}
THIS_IP=${ETCD_NODE_IP_1}
# 设置ETCD数据存储目录
ETCD_DATA_DIR=/root/docker/etcd-vloume-12379/
docker run -d --restart=always --network=host --hostname ${THIS_NAME} --name ${THIS_NAME} \
-v ${ETCD_DATA_DIR}:/etcd-data \
-v /etc/localtime:/etc/localtime:ro \
${ETCD_REGISTRY}:${ETCD_VERSION} \
/usr/local/bin/etcd \
--name ${THIS_NAME} \
--data-dir=/etcd-data \
--initial-advertise-peer-urls http://${THIS_IP}:12380 \
--listen-peer-urls http://0.0.0.0:12380 \
--advertise-client-urls http://${THIS_IP}:12379 \
--listen-client-urls http://0.0.0.0:12379 \
--initial-cluster ${ETCD_CLUSTER} \
--initial-cluster-state ${ETCD_CLUSTER_STATE} \
--initial-cluster-token ${ETCD_CLUSTER_TOKEN} \
--heartbeat-interval=100 \
--election-timeout=500 \
--auto-compaction-mode=periodic \
--auto-compaction-retention=1 \
--quota-backend-bytes=8589934592 \
--snapshot-count=5000 \
--enable-v2
# 初始化第二个节点
THIS_NAME=${ETCD_NAME_2}
THIS_IP=${ETCD_NODE_IP_2}
# 设置ETCD数据存储目录
ETCD_DATA_DIR=/root/docker/etcd-vloume-22379/
docker run -d --restart=always --network=host --hostname ${THIS_NAME} --name ${THIS_NAME} \
-v ${ETCD_DATA_DIR}:/etcd-data \
-v /etc/localtime:/etc/localtime:ro \
${ETCD_REGISTRY}:${ETCD_VERSION} \
/usr/local/bin/etcd \
--name ${THIS_NAME} \
--data-dir=/etcd-data \
--initial-advertise-peer-urls http://${THIS_IP}:22380 \
--listen-peer-urls http://0.0.0.0:22380 \
--advertise-client-urls http://${THIS_IP}:22379 \
--listen-client-urls http://0.0.0.0:22379 \
--initial-cluster ${ETCD_CLUSTER} \
--initial-cluster-state ${ETCD_CLUSTER_STATE} \
--initial-cluster-token ${ETCD_CLUSTER_TOKEN} \
--heartbeat-interval=100 \
--election-timeout=500 \
--auto-compaction-mode=periodic \
--auto-compaction-retention=1 \
--quota-backend-bytes=8589934592 \
--snapshot-count=5000 \
--enable-v2
# 初始化第三个节点
THIS_NAME=${ETCD_NAME_3}
THIS_IP=${ETCD_NODE_IP_3}
# 设置ETCD数据存储目录
ETCD_DATA_DIR=/root/docker/etcd-vloume-32379/
docker run -d --restart=always --network=host --hostname ${THIS_NAME} --name ${THIS_NAME} \
-v ${ETCD_DATA_DIR}:/etcd-data \
-v /etc/localtime:/etc/localtime:ro \
${ETCD_REGISTRY}:${ETCD_VERSION} \
/usr/local/bin/etcd \
--name ${THIS_NAME} \
--data-dir=/etcd-data \
--initial-advertise-peer-urls http://${THIS_IP}:32380 \
--listen-peer-urls http://0.0.0.0:32380 \
--advertise-client-urls http://${THIS_IP}:32379 \
--listen-client-urls http://0.0.0.0:32379 \
--initial-cluster ${ETCD_CLUSTER} \
--initial-cluster-state ${ETCD_CLUSTER_STATE} \
--initial-cluster-token ${ETCD_CLUSTER_TOKEN} \
--heartbeat-interval=100 \
--election-timeout=500 \
--auto-compaction-mode=periodic \
--auto-compaction-retention=1 \
--quota-backend-bytes=8589934592 \
--snapshot-count=5000 \
--enable-v2
安装etcd客户端,主要为了使用etcdctl客户端工具
yum install -y etcd
检查etcd集群状态
ENDPOINTS=http://10.0.1.225:12379,http://10.0.1.225:22379,http://10.0.1.225:32379
ETCDCTL_API=3 etcdctl --endpoints=${ENDPOINTS} -w table endpoint status
# 集群状态正常
+-------------------------+------------------+---------+---------+-----------+-----------+------------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+-------------------------+------------------+---------+---------+-----------+-----------+------------+
| http://10.0.1.225:12379 | f0507c477f30cfa3 | 3.4.20 | 20 kB | true | 2 | 9 |
| http://10.0.1.225:22379 | 8fb5ef3dcf4f436c | 3.4.20 | 20 kB | false | 2 | 9 |
| http://10.0.1.225:32379 | c025315846602833 | 3.4.20 | 20 kB | false | 2 | 9 |
+-------------------------+------------------+---------+---------+-----------+-----------+------------+
安装gitlab
在ops-node-04,10.0.1.225节点上安装gitlab。在安装gitlab时遇到了几个小问题:
- 安装后用户头像无法显示,影响体验,有强迫症的人很是抓狂(替换显示头像的url)
- 安装后需要查看gitlab的初始密码,略麻烦(生产环境还是留给系统自动初始化比较好)(采用预配置参数解决)
- 创建项目后,项目的url并不是域名或者ip地址,而是默认的主机名,导致无法克隆项目(采用预配置参数解决)
- 默认启动了监控的相关组件,测试的时候也基本用不到,还占用内存(采用预配置参数解决)。
预配置参数规则:只要是gitlab.rb文件中的配置都可以追加到容器的GITLAB_OMNIBUS_CONFIG变量中。
处理了上面几个问题后,启动的命令如下
# 为了降低内存占用,关闭了所有监控组件(建议测试的时候关闭);
# 设置root密码为root1234
# 替换gravatar源,解决用户头像无法显示问题
docker run -d --restart=always --hostname ops-gitlab --name ops-gitlab \
-e GITLAB_OMNIBUS_CONFIG="external_url 'http://10.0.1.225/'; \
gitlab_rails['gitlab_shell_ssh_port'] = 22; \
grafana['enable'] = false; \
prometheus['enable'] = false; \
node_exporter['enable'] = false; \
redis_exporter['enable'] = false; \
postgres_exporter['enable'] = false; \
gitlab_exporter['enable'] = false; \
alertmanager['enable'] = false; \
gitlab_rails['gravatar_plain_url'] = 'http://sdn.geekzu.org/avatar/%{hash}?s=%{size}&d=identicon';\
gitlab_rails['gravatar_ssl_url'] = 'https://sdn.geekzu.org/avatar/%{hash}?s=%{size}&d=identicon'" \
-e GITLAB_ROOT_PASSWORD="root1234" \
-p 443:443 -p 80:80 -p 2222:22 \
-v /root/docker/gitlab-volume/config:/etc/gitlab \
-v /root/docker/gitlab-volume/logs:/var/log/gitlab \
-v /root/docker/gitlab-volume/data:/var/opt/gitlab \
-v /etc/localtime:/etc/localtime:ro gitlab/gitlab-ee:latest
账号:root,密码:root1234
访问http://10.0.1.225,输入账号密码,登录后进行中文设置:点击【右上角头像】-【Preferences】-【Localization】-【Chinese, Simplified - 简体中文 (95% translated)】-保存
安装jenkins
在ops-node-04,10.0.1.225节点上安装jenkins。容器启动的jenkins一般在填写初始化密码后,安装插件环节会报【离线】的错误,大部分的解决方案都是修改updates/default.json中www.google.com地址以此来跳过健康检查,或者将hudson.model.UpdateCenter.xml中的域名替换http,在或者将域名替换为国内三方源,但是尝试过所有的方案后,发现该问题并没有得到解决,这是为什么呢?
jenkins使用updates/default.json文件中的www.google.com检测主机能否联网,如果检测正常,就会进入到安装插件页面,反之就会报离线错误。而且在修改配置文件重启jenkins后,jenkins还会从指定的url重新拉取updates/default.json文件导致修改无效。所以,在容器中给www.google.com设置hosts可以解决该问题,给www.google.com解析的IP地址,发送http请求时必须可以返回2xx或者3xx,因为安全性要求,内网的主机不一定都能联网,也可以使用私有地址。
# 配置Ansible资产文件
mkdir -p /root/ansible
cat > /root/ansible/hosts <<EOF
[nginx_prod_l7]
10.0.1.222
10.0.1.223
[nginx_prod_l4]
10.0.1.224
EOF
# 获取www.baidu.com的IP地址,用来给www.google.com设置hosts
ALIVE_IP=$(ping -c 1 www.baidu.com|grep PING|awk -F '[()]' '{print $2}')
docker run -d -u root --add-host www.google.com:${ALIVE_IP} --restart=always --hostname ops-jenkins --name ops-jenkins \
-e JAVA_OPTS="-Xms1024m -Xmx2048m -XX:MaxNewSize=512m" \
-p 8080:8080 -p 50000:50000 \
-v /root/docker/jenkins-volume:/var/jenkins_home \
-v /root/ansible/hosts:/root/hosts \
-v /etc/localtime:/etc/localtime:ro jenkins/jenkins:2.346.3-alpine-jdk8
# 安装基础工具
docker exec -it ops-jenkins /bin/bash
sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && apk add --no-cache rsync git ansible
# 生成公私钥
docker exec ops-jenkins ssh-keygen -t rsa
# 配置SSH免密,因为Ansible在容器中,所以进入到容器中执行,Ansible执行需要
docker exec -it /bin/bash
# 执行如下命令,输入yes回车,输入root密码
ssh-copy-id root@10.0.1.222
ssh-copy-id root@10.0.1.223
ssh-copy-id root@10.0.1.224
查看jenkins密码
docker exec ops-jenkins cat /var/jenkins_home/secrets/initialAdminPassword
# 密码如下
f52367de922d43a79850b82973447b23
访问http://10.0.1.225:8080,输入jenkins密码
接下来安装推荐的插件,如果有安装失败的插件,重试即可。安装完成后配置jenkins管理员密码(账号:admin,密码:admin123)
进入【系统管理】-【插件管理】-【可选插件】,安装以下使用到的插件(如果没有搜索到说明已经安装了):
- 活跃选择参数插件:Active Choices
- 修改构建名称和描述插件:Build Name and Description Setter
- 构建的用户变量插件:build user vars
- 扩展选择参数插件:Extended Choice Parameter
- Git参数插件:Git Parameter
- GitLab插件:GitLab
- 汉化插件:Localization: Chinese (Simplified)
- 持久化参数插件:Persistent Parameter
- Job修改历史插件:Job Configuration History
- 构建后操作,根据构建结果执行shell或批处理插件:Post build task
对接Gitlab
进入【系统管理】-【系统配置】-【Git plugin】,配置如下图所示
- Global Config user.name Value:root
- Global Config user.email Value:root@zhushiyang.net
添加ops-gitlab凭据,
- 范围:全局 (Jenkins, nodes, items, all child items, etc)
- 用户名:root(gitlab root用户)
- 密码:root1234(gitlab root用户密码)
- ID:ops-gitlab
- 描述:ops-gitlab
至此Jenkins安装基础环境完成。
安装openresty
在ops-node-01、ops-node-02和ops-node-03上分别构建镜像。使用openresty官方的Dockerfile构建镜像为模板进行简单调整,添加alpine的国内源,编译upsync模块和check_module模块,然后使用构建的镜像启动。
先在ops-node-01上执行
mkdir /root/openresty && cd /root/openresty
# 如果git clone失败,直接到github上下载
git clone https://github.com/msaf1980/nginx_upstream_check_module
git clone https://github.com/weibocom/nginx-upsync-module
# 手动下载上传到/root/openresty目录下解压
unzip nginx_upstream_check_module-master.zip
unzip nginx-upsync-module-master.zip
mv nginx_upstream_check_module-master nginx_upstream_check_module
mv nginx-upsync-module-master nginx-upsync-module
Dockerfile文件的第50-51行添加了nginx模块,85行替换了安装源,145行打check_module补丁。创建Dockerfile,执行vim Dockerfile
写入下面的内容
# Dockerfile - alpine
# https://github.com/openresty/docker-openresty
ARG RESTY_IMAGE_BASE="alpine"
ARG RESTY_IMAGE_TAG="3.12"
FROM ${RESTY_IMAGE_BASE}:${RESTY_IMAGE_TAG}
LABEL maintainer="Evan Wies <evan@neomantra.net>"
# Docker Build Arguments
ARG RESTY_IMAGE_BASE="alpine"
ARG RESTY_IMAGE_TAG="3.12"
ARG RESTY_VERSION="1.17.8.2"
ARG RESTY_OPENSSL_VERSION="1.1.1g"
ARG RESTY_OPENSSL_PATCH_VERSION="1.1.1f"
ARG RESTY_OPENSSL_URL_BASE="https://www.openssl.org/source"
ARG RESTY_PCRE_VERSION="8.44"
ARG RESTY_J="1"
ARG RESTY_CONFIG_OPTIONS="\
--with-compat \
--with-file-aio \
--with-http_addition_module \
--with-http_auth_request_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_geoip_module=dynamic \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_image_filter_module=dynamic \
--with-http_mp4_module \
--with-http_random_index_module \
--with-http_realip_module \
--with-http_secure_link_module \
--with-http_slice_module \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_v2_module \
--with-http_xslt_module=dynamic \
--with-ipv6 \
--with-mail \
--with-mail_ssl_module \
--with-md5-asm \
--with-pcre-jit \
--with-sha1-asm \
--with-stream \
--with-stream_ssl_module \
--with-threads \
--add-module=/root/nginx_upstream_check_module \
--add-module=/root/nginx-upsync-module \
"
ARG RESTY_CONFIG_OPTIONS_MORE=""
ARG RESTY_LUAJIT_OPTIONS="--with-luajit-xcflags='-DLUAJIT_NUMMODE=2 -DLUAJIT_ENABLE_LUA52COMPAT'"
ARG RESTY_ADD_PACKAGE_BUILDDEPS=""
ARG RESTY_ADD_PACKAGE_RUNDEPS=""
ARG RESTY_EVAL_PRE_CONFIGURE=""
ARG RESTY_EVAL_POST_MAKE=""
# These are not intended to be user-specified
ARG _RESTY_CONFIG_DEPS="--with-pcre \
--with-cc-opt='-DNGX_LUA_ABORT_AT_PANIC -I/usr/local/openresty/pcre/include -I/usr/local/openresty/openssl/include' \
--with-ld-opt='-L/usr/local/openresty/pcre/lib -L/usr/local/openresty/openssl/lib -Wl,-rpath,/usr/local/openresty/pcre/lib:/usr/local/openresty/openssl/lib' \
"
LABEL resty_image_base="${RESTY_IMAGE_BASE}"
LABEL resty_image_tag="${RESTY_IMAGE_TAG}"
LABEL resty_version="${RESTY_VERSION}"
LABEL resty_openssl_version="${RESTY_OPENSSL_VERSION}"
LABEL resty_openssl_patch_version="${RESTY_OPENSSL_PATCH_VERSION}"
LABEL resty_openssl_url_base="${RESTY_OPENSSL_URL_BASE}"
LABEL resty_pcre_version="${RESTY_PCRE_VERSION}"
LABEL resty_config_options="${RESTY_CONFIG_OPTIONS}"
LABEL resty_config_options_more="${RESTY_CONFIG_OPTIONS_MORE}"
LABEL resty_config_deps="${_RESTY_CONFIG_DEPS}"
LABEL resty_add_package_builddeps="${RESTY_ADD_PACKAGE_BUILDDEPS}"
LABEL resty_add_package_rundeps="${RESTY_ADD_PACKAGE_RUNDEPS}"
LABEL resty_eval_pre_configure="${RESTY_EVAL_PRE_CONFIGURE}"
LABEL resty_eval_post_make="${RESTY_EVAL_POST_MAKE}"
COPY ./nginx_upstream_check_module/ /root/nginx_upstream_check_module
COPY ./nginx-upsync-module/ /root/nginx-upsync-module
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories \
&& apk add --no-cache --virtual .build-deps \
build-base \
coreutils \
curl \
gd-dev \
geoip-dev \
libxslt-dev \
linux-headers \
make \
perl-dev \
readline-dev \
zlib-dev \
${RESTY_ADD_PACKAGE_BUILDDEPS} \
&& apk add --no-cache \
gd \
geoip \
libgcc \
libxslt \
zlib \
${RESTY_ADD_PACKAGE_RUNDEPS} \
&& cd /tmp \
&& if [ -n "${RESTY_EVAL_PRE_CONFIGURE}" ]; then eval $(echo ${RESTY_EVAL_PRE_CONFIGURE}); fi \
&& cd /tmp \
&& curl -fSL "${RESTY_OPENSSL_URL_BASE}/openssl-${RESTY_OPENSSL_VERSION}.tar.gz" -o openssl-${RESTY_OPENSSL_VERSION}.tar.gz \
&& tar xzf openssl-${RESTY_OPENSSL_VERSION}.tar.gz \
&& cd openssl-${RESTY_OPENSSL_VERSION} \
&& if [ $(echo ${RESTY_OPENSSL_VERSION} | cut -c 1-5) = "1.1.1" ] ; then \
echo 'patching OpenSSL 1.1.1 for OpenResty' \
&& curl -s https://raw.githubusercontent.com/openresty/openresty/master/patches/openssl-${RESTY_OPENSSL_PATCH_VERSION}-sess_set_get_cb_yield.patch | patch -p1 ; \
fi \
&& if [ $(echo ${RESTY_OPENSSL_VERSION} | cut -c 1-5) = "1.1.0" ] ; then \
echo 'patching OpenSSL 1.1.0 for OpenResty' \
&& curl -s https://raw.githubusercontent.com/openresty/openresty/ed328977028c3ec3033bc25873ee360056e247cd/patches/openssl-1.1.0j-parallel_build_fix.patch | patch -p1 \
&& curl -s https://raw.githubusercontent.com/openresty/openresty/master/patches/openssl-${RESTY_OPENSSL_PATCH_VERSION}-sess_set_get_cb_yield.patch | patch -p1 ; \
fi \
&& ./config \
no-threads shared zlib -g \
enable-ssl3 enable-ssl3-method \
--prefix=/usr/local/openresty/openssl \
--libdir=lib \
-Wl,-rpath,/usr/local/openresty/openssl/lib \
&& make -j${RESTY_J} \
&& make -j${RESTY_J} install_sw \
&& cd /tmp \
&& curl -fSL https://nchc.dl.sourceforge.net/project/pcre/pcre/8.44/pcre-${RESTY_PCRE_VERSION}.tar.gz -o pcre-${RESTY_PCRE_VERSION}.tar.gz \
&& tar xzf pcre-${RESTY_PCRE_VERSION}.tar.gz \
&& cd /tmp/pcre-${RESTY_PCRE_VERSION} \
&& ./configure \
--prefix=/usr/local/openresty/pcre \
--disable-cpp \
--enable-jit \
--enable-utf \
--enable-unicode-properties \
&& make -j${RESTY_J} \
&& make -j${RESTY_J} install \
&& cd /tmp \
&& curl -fSL https://openresty.org/download/openresty-${RESTY_VERSION}.tar.gz -o openresty-${RESTY_VERSION}.tar.gz \
&& tar xzf openresty-${RESTY_VERSION}.tar.gz \
&& cd /tmp/openresty-${RESTY_VERSION}/bundle/nginx-1.17.8 \
&& patch -p1 < /root/nginx_upstream_check_module/check_1.16.1+.patch \
&& cd /tmp/openresty-${RESTY_VERSION} \
&& eval ./configure -j${RESTY_J} ${_RESTY_CONFIG_DEPS} ${RESTY_CONFIG_OPTIONS} ${RESTY_CONFIG_OPTIONS_MORE} ${RESTY_LUAJIT_OPTIONS} \
&& make -j${RESTY_J} \
&& make -j${RESTY_J} install \
&& cd /tmp \
&& if [ -n "${RESTY_EVAL_POST_MAKE}" ]; then eval $(echo ${RESTY_EVAL_POST_MAKE}); fi \
&& rm -rf \
openssl-${RESTY_OPENSSL_VERSION}.tar.gz openssl-${RESTY_OPENSSL_VERSION} \
pcre-${RESTY_PCRE_VERSION}.tar.gz pcre-${RESTY_PCRE_VERSION} \
openresty-${RESTY_VERSION}.tar.gz openresty-${RESTY_VERSION} \
&& apk del .build-deps \
&& mkdir -p /var/run/openresty \
&& ln -sf /dev/stdout /usr/local/openresty/nginx/logs/access.log \
&& ln -sf /dev/stderr /usr/local/openresty/nginx/logs/error.log \
&& rm -rf /root/ngx_healthcheck_module
# Add additional binaries into PATH for convenience
ENV PATH=$PATH:/usr/local/openresty/luajit/bin:/usr/local/openresty/nginx/sbin:/usr/local/openresty/bin
# Copy nginx configuration files
CMD ["/usr/local/openresty/bin/openresty", "-g", "daemon off;"]
# Use SIGQUIT instead of default SIGTERM to cleanly drain requests
# See https://github.com/openresty/docker-openresty/blob/master/README.md#tips--pitfalls
STOPSIGNAL SIGQUIT
构建镜像,生产环境需要把镜像上传到阿里云或者内部的镜像仓库中,一次构建即可。现在的环境中可以在ops-node-01节点导出镜像并拷贝到ops-node-02和ops-node-03两个节点。
docker build -t openresty:1.17.8.2-alpine-upsync_check .
# 导出镜像拷贝到ops-node-02和ops-node-03两个节点
docker save -o openresty.tar openresty:1.17.8.2-alpine-upsync_check
scp openresty.tar.gz root@10.0.1.223:/root
scp openresty.tar.gz root@10.0.1.224:/root
# 在ops-node-02和ops-node-03两个节点执行
docker load -i /root/openresty.tar
现在ops-node-01、ops-node-02和ops-node-03三个节点都生成了openresty:1.17.8.2-alpine-upsync_check镜像。
下载示例配置文件到三个节点的/root/docker/nginx-volume/conf目录下,示例文件地址:https://github.com/zhushiyang/nginx_config.git,说明见Nginx规划部分。这里的配置只是初始化的,接下来会通过Jenkins统一进行管理。
# ops-node-01、ops-node-02和ops-node-03三个节点都执行
mkdir -p /root/docker/nginx-volume/conf
cd /root/docker/nginx-volume/conf
git clone https://github.com/zhushiyang/nginx_config.git
mv nginx_config/* ./
rm -rf nginx_config
# 文件web_cluster.conf不是本次的重点,删除掉
rm -f upstreams/web_cluster.conf
在ops-node-01、ops-node-02和ops-node-03三个节点启动openresty
docker run -d --restart=always --hostname ops-openresty --name ops-openresty --network=host \
-v /root/docker/nginx-volume/conf:/usr/local/openresty/nginx/conf \
-v /root/docker/nginx-volume/logs:/var/log/nginx \
-v /etc/localtime:/etc/localtime:ro openresty:1.17.8.2-alpine-upsync_check
启动nginx容器后,查看容器日志汇报下面的错误,可暂时先忽略,是因为etcd的地址不对导致的
2022/08/26 15:48:25 [error] 10#10: recv() failed (111: Connection refused)
2022/08/26 15:48:25 [error] 10#10: upsync_recv: recv error with upstream: "web_cluster_upsync"
2022/08/26 15:48:25 [error] 7#7: recv() failed (111: Connection refused)
2022/08/26 15:48:25 [error] 7#7: upsync_recv: recv error with upstream: "web_cluster_upsync"
2022/08/26 15:48:25 [error] 8#8: recv() failed (111: Connection refused)
2022/08/26 15:48:25 [error] 8#8: upsync_recv: recv error with upstream: "web_cluster_upsync"
2022/08/26 15:48:25 [error] 9#9: recv() failed (111: Connection refused)
2022/08/26 15:48:25 [error] 9#9: upsync_recv: recv error with upstream: "web_cluster_upsync"
2022/08/26 15:48:25 [error] 8#8: send() failed (111: Connection refused)
2022/08/26 15:48:26 [error] 7#7: recv() failed (111: Connection refused)
2022/08/26 15:48:26 [error] 7#7: upsync_recv: recv error with upstream: "web_cluster_upsync"
2022/08/26 15:48:26 [error] 10#10: recv() failed (111: Connection refused)
2022/08/26 15:48:26 [error] 10#10: upsync_recv: recv error with upstream: "web_cluster_upsync"
启动验证,curl访问后,返回nginx的状态监控数据说明是正常的
curl http://10.0.1.222/stub_status
curl http://10.0.1.223/stub_status
curl http://10.0.1.224/stub_status
环境配置
配置gitlab
创建Nginx分组
在Nginx分组下创建Nginx-Prod-L4仓库(不要勾选【使用自述文件初始化仓库】)
在Nginx分组下创建Nginx-Prod-L7仓库(不要勾选【使用自述文件初始化仓库】)
最后的仓库布局如下
将Nginx的示例文件上传到两个仓库中,因为示例配置已经在ops-node-01的/root/docker/nginx-volume/conf目录下存在,所以在ops-node-01上执行即可
mkdir /root/nginx/ && cd /root/nginx/
# 如果你修改的gitlab的密码这里将root1234替换即可
git clone http://root:root1234@10.0.1.225/nginx/nginx-prod-l7.git
git clone http://root:root1234@10.0.1.225/nginx/nginx-prod-l4.git
cp -rf /root/docker/nginx-volume/conf/* nginx-prod-l4
cp -rf /root/docker/nginx-volume/conf/* nginx-prod-l7
# 设置gitlab的邮箱和用户
git config --global user.email "root@zhushiyang.net"
git config --global user.name "root"
cd /root/nginx/nginx-prod-l4
git add --all
git commit -m '初始化Nginx配置'
git push -u origin master
cd /root/nginx/nginx-prod-l7
git add --all
git commit -m '初始化Nginx配置'
git push -u origin master
配置jenkins
注意:jenkins的所有的job配置文件全部已经打包好,下载文件后放到任意位置并加压到/root/docker/jenkins-volume/jobs目录下,然后重启jenkins即可,但是强烈建议手动配置一遍,增加下印象,在ops-node-04上执行。
git clone https://github.com/zhushiyang/nginx-prod-l7.git
cp -rf nginx-prod-l7 /root/docker/jenkins-volume/jobs/
docker restart jenkins
下面是手动配置的过程,建议按照自定义配置走一遍,以此来熟悉每个配置的作用。
创建Folder,名称是Nginx-Prod-L7
下面是手动配置的过程,建议按照自定义配置走一遍,以此来熟悉每个配置的作用。
创建Folder,名称是Nginx-Prod-L7
创建Folder,名称是Nginx-Prod-L4
Jenkins布局如下
进入到Nginx-Prod-L7目录下,创建如下格式的job:
- configure_deploy:下发配置文件(job类型【自由风格的软件项目】)
- nginx.conf:更新nginx.conf文件到gitlab(job类型【自由风格的软件项目】)
- proxy.conf:更新proxy.conf文件到gitlab(job类型【自由风格的软件项目】)
- upstreams:维护upstream,每个upstream是一个job,命名格式是upstream名称,不需要带.conf后缀,会把配置推送到gitlab(job类型【文件夹】)
- vhosts:维护虚拟主机,每个虚拟主机是一个job,命名格式是域名,不需要带.conf后缀,会把配置推送到gitlab(job类型【文件夹】)
job是按照nginx的配置文件来划分的,原则是经常修改的放到jenkins上进行管理,不修改的无需配置。
Nginx-Prod-L7-nginx.conf配置
任务名称:nginx.conf
任务类型:构建自由风格的软件项目
描述:Nginx主配置文件
丢弃旧的构建:这里构建历史保留10个,可以按照实际情况修改,多点,少点都没关系。
参数化构建:试过文本参数,不过不理想,在编辑配置提交后,每次在输入框里显示都是默认配置,而我希望的是在修改配置后,可以在上一次的基础上继续进行修改。jenkins没有从workspace中读取文件显示在文本框里面的插件,后来发现【Persistent Text Parameter】插件可以满足要求,原理是从上一次构建记录中读取对应的参数到当前构建中。比如,我设置的APP_NAME参数的默认值是1,上一次我把APP_NAME的值改为了2,那么在新的构建中,APP_NAME的值就是2
参数类型:Persistent Text Parameter
Name:NGINX_PROCESS_EVENT_CONFIG
Default Value:
# nginx work进程用户
user nobody;
# 根据cpu的核数自动生成work进程树
worker_processes auto;
# 启用多核CPU;
worker_cpu_affinity auto;
# work进程打开最大文件数
worker_rlimit_nofile 65536;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#error_log "pipe:rollback logs/error_log interval=1d baknum=7 maxsize=2G";
pid /var/run/nginx.pid;
# event context
events {
# 默认开启,解决惊群问题
accept_mutex on;
# 使用epoll
use epoll;
# 每个进程最大连接数(最大连接=连接数x进程数)
worker_connections 20480;
}
Description:Nginx进程和事件模型配置
在添加一个Persistent Text Parameter
Name:NGINX_HTTP_CONFIG
Default Value:
# 七层负载均衡
http {
# 隐藏nginx版本号
server_tokens off;
# 文件扩展名与文件类型映射表
include mime.types;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
#access_log "pipe:rollback logs/access_log interval=1d baknum=7 maxsize=2G" main;
# 调整hash表,解决绑定域名过多问题
server_names_hash_max_size 4096;
server_names_hash_bucket_size 1024;
variables_hash_max_size 4096;
variables_hash_bucket_size 2048;
# 默认文件类型
default_type text/html;
# 设置编码格式utf-8
charset utf-8;
# 当请求头部中有下划线时,认为请求有效
underscores_in_headers on;
# 防止网络阻塞
tcp_nopush on;
# 输出缓冲区
tcp_nodelay on;
# 输出拆包大小
output_buffers 4 32k;
postpone_output 1460;
# 缓冲区代理缓冲用户端请求的最大字节数
client_header_buffer_size 128k;
large_client_header_buffers 4 256k;
# 客户端发送header超时
client_body_timeout 30;
# 发送到客户端超时
send_timeout 30;
# 注意:如果图片显示不正常把这个改成off。
sendfile on;
# keepalive_timeout 60;
keepalive_timeout 60;
# 开启gzip压缩输出
gzip on;
# 最小压缩文件大小
gzip_min_length 1k;
# 处理请求压缩的缓冲区数量和大小
gzip_buffers 4 16k;
# 设置健康检查共享内存
check_shm_size 20M;
# 压缩类型,默认就已经包含textml,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn
gzip_types text/plain application/x-javascript text/css application/xml;
# 压缩比率,1代表压缩比最小处理速度最快,9压缩比最大但处理速度最慢(传输快但比较消耗cpu)
gzip_comp_level 9;
# 压缩通过代理的所有文件
gzip_proxied any;
# vary header支持
gzip_vary on;
include proxy.conf;
# server配置
server {
listen 80 backlog=65535;
# listen 8080 backlog=65535;
# listen 443 ssl backlog=65535;
# ssl_certificate ./ssl/xxx/xxx.crt;
# ssl_certificate_key ./ssl/xxx/xxx.key;
root /dev/null;
server_name _;
# nginx状态信息
location = /stub_status {
stub_status;
}
# upstream健康检查信息
location = /check_status {
check_status;
access_log off;
error_log off;
# allow SOME.IP.ADD.RESS;
# deny all;
}
# upsync show
location /upstream_list {
upstream_show;
}
}
# 虚拟主机配置目录
include vhosts/*.conf;
# upstreams配置目录
include upstreams/*.conf;
}
Description:Nginx的http配置
源码管理:Git
Repository URL:http://10.0.1.225/nginx/nginx-prod-l7.git
Credentials:选择ops-gitlab
高级里面的Name:nginx-prod-l7(因为配置修改后,需要把修改推送到master分支,同时创建tag,jenkins的Git publish插件就是通过Git插件中的Name进行关联的)
Branches to build:*/master
构建环境:
Delete workspace before starts:在构建前清理workspace
Add timestamps to the Console Output:构建日志中显示时间戳
Set jenkins user build variables:设置jenkins的用户变量,在构建时可以通过${BUILD_USER}记录当前的构建用户
构建:执行 shell
脚本的内容比较简单,生产环境可以优化,添加报警功能等内容,简化版本的脚本如下
# 加锁,防止多人同时修改
if [ ! -f /tmp/${JOB_BASE_NAME}.lock ];then
echo "> ==========> 进行加锁 <=========="
echo "" > /tmp/${JOB_BASE_NAME}.lock
else
echo "==========> 不能多人同时修改该文件,请稍后修改 <=========="
exit 1
fi
echo "==========> 正在生成配置文件 <=========="
echo -e "${NGINX_PROCESS_EVENT_CONFIG}\n${NGINX_HTTP_CONFIG}" > ${JOB_BASE_NAME}
echo "==========> 正在校验文件变化 <=========="
# 时间戳,格式:年月日时分-202208100510
DATE_TIME=$(date +%Y%m%d%H%M)
# git status结果为0,说明有修改,其它结果说明没有修改
CHECK_RELT=$(git status ${JOB_BASE_NAME}|grep ${JOB_BASE_NAME}>/dev/null ;echo $?)
if [ ${CHECK_RELT} = "0" ];then
git add --all
git commit -m "Update ${JOB_BASE_NAME} version to ${JOB_BASE_NAME}-${DATE_TIME}-${BUILD_NUMBER} by ${BUILD_USER}"
else
echo "==========> 文件内容无变化 <=========="
exit 1
fi
构建后操作:Git Publisher
勾选:Push Only If Build Succeeds,只有成功时才提交tag
勾选:Merge Results,合并结果
Add Tag:
- Tag to push:${JOB_BASE_NAME}-${BUILD_USER}-${BUILD_NUMBER},设置tag格式job名称-构建用户-构建顺序,如:nginx.conf-admin-1
- 勾选:Create new tag,每次构建都创建新的tag,便于配置的更新和回滚
- Target remote name:nginx-prod-l7,关联指定Name为nginx-prod-l7的配置
Add Branches:
- Branch to push:master,文件更新后同步更新的分支
- Target remote name:nginx-prod-l7,关联指定Name为nginx-prod-l7的配置
构建后操作:Post build task,主要使用Script,执行删除锁文件
Script内容如下:
echo "==========> 删除锁文件 <=========="
rm -f /tmp/${JOB_BASE_NAME}.lock
运行该job,先按照默认参数执行,默认的参数删除了示例配置中的stream配置
然后再构建一次,在NGINX_PROCESS_EVENT_CONFIG的输入框的第一行,添加一行注释# 再次构建
查看Nginx-Prod-L7的git仓库,发现nginx.conf配置已经更新
生成的tag
然后再点击一次构建(不要执行,这一次只是为了看下参数的内容,由于我一开始调试了一次,所以我的构建数是3,你们的应该是2),上次的修改记录保存下来了
第3次构建的日志保存在/root/docker/jenkins-volume/jobs/Nginx-Prod-L7/jobs/nginx.conf/builds/3/build.xml文件中,jenkins的Persistent Text Parameter参数就是读取该文件相同变量的值。
Nginx-Pord-L7-proxy.conf配置
proxy.conf定义的是全局配置反向代理配置文件,偶尔会有调整。新建jenkins的job,因为proxy.conf和nginx.conf的配置大同小异,所以从nginx.conf进行复制
要修改的配置如下
描述:修改为Nginx的proxy.conf文件
参数:删除所有的Persistent Text Parameter,然后再添加Persistent Text Parameter,设置Name为NGINX_PROXY_CONFIG,内容如下
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE_ADDR $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header RealIP $remote_addr;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_set_header host-name $hostname;
proxy_pass_header Server;
client_max_body_size 30m;
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
proxy_temp_file_write_size 1024m;
proxy_buffer_size 32k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_ignore_client_abort on;
#失败不重试
proxy_next_upstream_tries 1;
Description:修改为Nginx的代理配置
构建:执行shell修改内容如下
if [ ! -f /tmp/${JOB_BASE_NAME}.lock ];then
echo "> ==========> 进行加锁 <=========="
echo "" > /tmp/${JOB_BASE_NAME}.lock
else
echo "==========> 不能多人同时修改该文件,请稍后修改 <=========="
exit 1
fi
echo "==========> 正在生成配置文件 <=========="
echo -e "${NGINX_PROXY_CONFIG}" > ${JOB_BASE_NAME}
echo "==========> 正在校验文件变化 <=========="
# 时间戳,格式:年月日时分-202208100510
DATE_TIME=$(date +%Y%m%d%H%M)
# git status结果为0,说明有修改,其它结果说明没有修改
CHECK_RELT=$(git status ${JOB_BASE_NAME}|grep ${JOB_BASE_NAME}>/dev/null ;echo $?)
if [ ${CHECK_RELT} = "0" ];then
git add --all
git commit -m "Update ${JOB_BASE_NAME} version to ${JOB_BASE_NAME}-${DATE_TIME}-${BUILD_NUMBER} by ${BUILD_USER}"
else
echo "==========> 文件内容无变化 <=========="
exit 1
fi
其它的配置无需修改了,直接保存即可。点击构建,在输入框的第一行添加内容【测试proxy.conf构建】
构建成功后,查看Nginx-Pord-L7仓库的proxy.conf文件内容
生成的tag
Nginx-Prod-L7-upstreams配置
这里的配置用来管理nginx的upstream,每个去掉.conf后缀的upstream文件名作为job名称。
在Nginx-Prod-L7目录下,创建Job,类型是文件夹,名称是upstreams。构建后,最终会在upstreams目录下生成${JOB_BASE_NAME}.conf文件。
因为job的配置和其他配置大致相同,可以使用jenkins的job复制功能。jenkins的job无法跨文件夹进行复制,所以可以先把proxy.conf的job移动到upstreams文件夹下,用于复制需要。
选择移动到upstreams的目录
移动完成后,切换到upstreams目录,可以看到proxy.conf文件已经移动过来,现在新建job,名称是web_cluster_upsync,复制源是proxy.conf
要修改的内容如下
描述:修改为Nginx的upstream配置文件
构建参数:将Persistent Text Parameter的Name修改为NGINX_UPSTREAM_CONFIG,默认值如下(在使用时去掉下面的注释)
# ${JOB_BASE_NAME}对的值就是job的名称,这里是web_cluster_upsync
upstream ${JOB_BASE_NAME} {
# upsync在首次配置时,必须包含一个server,不然nginx会没有server的错误
server 127.0.0.1:80 weight=1 max_fails=2 fail_timeout=10;
# 这里是upsync的具体配置
# etcd的api:10.0.1.224:2379/v2/keys/upstreams/${JOB_BASE_NAME}
# 超时时间6分钟:upsync_timeout=6m
# 同步间隔500ms:upsync_interval=500ms
# 使用的外部存储是etcd:upsync_type=etcd
# 每次nginx启动时,是否会重新从etcd拉取数据,这里关闭:strong_dependency=off
upsync 10.0.1.224:2379/v2/keys/upstreams/${JOB_BASE_NAME} upsync_timeout=6m upsync_interval=500ms upsync_type=etcd strong_dependency=off;
# 用来持久化upstream的文件路径,当etcd集群异常时,会从本地文件中持久化的内容,继续提供服务
upsync_dump_path /usr/local/openresty/nginx/conf/dump_upstreams/${JOB_BASE_NAME}.conf;
# http_check健康检查配置
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_keepalive_requests 1;
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}
构建:执行shell的内容如下,内容比较简单,替换了变量,对upstreams目录是否存在进行校验
if [ ! -f /tmp/${JOB_BASE_NAME}.lock ];then
echo "> ==========> 进行加锁 <=========="
echo "" > /tmp/${JOB_BASE_NAME}.lock
else
echo "==========> 不能多人同时修改该文件,请稍后修改 <=========="
exit 1
fi
echo "==========> 正在生成配置文件 <=========="
[ ! -d ./upstreams ] && mkdir ./upstreams
echo "${NGINX_UPSTREAM_CONFIG}" > ./upstreams/${JOB_BASE_NAME}.conf
echo "==========> 正在校验文件变化 <=========="
# 时间戳,格式:年月日时分-202208100510
DATE_TIME=$(date +%Y%m%d%H%M)
# git status结果为0,说明有修改,其它结果说明没有修改
CHECK_RELT=$(git status ./upstreams/${JOB_BASE_NAME}.conf|grep ${JOB_BASE_NAME}.conf>/dev/null ;echo $?)
if [ ${CHECK_RELT} = "0" ];then
git add --all
git commit -m "Update ${JOB_BASE_NAME} version to ${JOB_BASE_NAME}-${DATE_TIME}-${BUILD_NUMBER} by ${BUILD_USER}"
else
echo "==========> 文件内容无变化 <=========="
exit 1
fi
其他内容不变,保存即可。点击构建,在输入框的第一行添加内容【测试upstream构建】
构建成功后在git仓库Nginx-Prod-L7的upstreams目录下查看生成的web_cluster_upsync.conf配置文件,模板的变量也已经替换为期望的值
然后,把proxy.conf在移动回去
Nginx-Prod-L7-vhosts配置
这里的配置用来管理nginx的虚拟主机配置,每个去掉.conf后缀的vhosts文件名作为job名称。
在Nginx-Prod-L7目录下,创建Job,类型是文件夹,名称是vhosts。构建后,最终会在vhosts目录下生成${JOB_BASE_NAME}.conf文件。
因为job的配置和其他配置大致相同,可以使用jenkins的job复制功能。jenkins的job无法跨文件夹进行复制,所以可以先把proxy.conf的job移动到vhosts文件夹下,用于复制需要。
移动完成后,切换到vhosts目录,可以看到proxy.conf文件已经移动过来,现在新建job,名称是www.zhushiyang.net,复制源是proxy.conf
修改的内容如下
描述:修改为Nginx的vhosts文件
构建参数:将Persistent Text Parameter的Name修改为NGINX_VHOSTS_CONFIG,默认值如下(在使用时去掉下面的注释)
#server {
# listen 80;
# server_name ${JOB_BASE_NAME};
# return 301 https://$server_name$request_uri;
#}
server{
listen 80 ;
listen 443 ssl;
server_name ${JOB_BASE_NAME};
access_log /var/log/nginx/${JOB_BASE_NAME}.access.log main;
ssl_certificate ./certs/zhushiyang.net.pem;
ssl_certificate_key ./certs/zhushiyang.net.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_protocols TLSv1.1 TLSv1.2;
add_header Strict-Transport-Security "max-age=63072000" always;
# www主程序
location / {
# 支持upsync
proxy_pass http://web_cluster_upsync;
}
}
构建:执行shell,修改为如下内容
if [ ! -f /tmp/${JOB_BASE_NAME}.lock ];then
echo "> ==========> 进行加锁 <=========="
echo "" > /tmp/${JOB_BASE_NAME}.lock
else
echo "==========> 不能多人同时修改该文件,请稍后修改 <=========="
exit 1
fi
echo "==========> 正在生成配置文件 <=========="
[ ! -d ./vhosts ] && mkdir ./upstreams
echo "${NGINX_VHOSTS_CONFIG}" > ./vhosts/${JOB_BASE_NAME}.conf
echo "==========> 正在校验文件变化 <=========="
# 时间戳,格式:年月日时分-202208100510
DATE_TIME=$(date +%Y%m%d%H%M)
# git status结果为0,说明有修改,其它结果说明没有修改
CHECK_RELT=$(git status ./vhosts/${JOB_BASE_NAME}.conf|grep ${JOB_BASE_NAME}.conf>/dev/null ;echo $?)
if [ ${CHECK_RELT} = "0" ];then
git add --all
git commit -m "Update ${JOB_BASE_NAME} version to ${JOB_BASE_NAME}-${DATE_TIME}-${BUILD_NUMBER} by ${BUILD_USER}"
else
echo "==========> 文件内容无变化 <=========="
exit 1
fi
其他内容不变,保存即可。点击构建,在输入框的第一行,添加【# 测试vhosts构建】
构建成功后,在git仓库Nginx-Prod-L7的vhosts目录下查看www.zhushiyang.net.conf文件内容
然后,把proxy.conf在移动回去
Nginx-Prod-L7-configure_deploy配置
前面配的jenkins任务都是本地修改配置后推送到gitlab,并没有将nginx的配置文件分发到nginx的主机上,接下来要配置的job就是实现配置分发功能。
在Nginx-Pord-L7文件夹下新建名称为configure_deploy的自由风格软件项目。
描述:Nginx配置文件下发
丢弃旧的构建:设置为10
参数:Git 参数
名称:NGINX_GIT_TAGS
参数:Extended Choice Parameter
Name:NGINX_HOSTS
选择Basic Parameter Types
- Parameter Type:Check Boxes
- Number of Visible items:5
- Delimiter:,
- 点击Choose Source for Value
Git:
Repository URL:http://10.0.1.225/nginx/nginx-prod-l7.git
Credentials:选择添加的ops-gitlab
构建环境:
构建:执行shell,写入的内容如下
echo "==========> 正在下发配置文件 <=========="
ansible -i /root/hosts ${NGINX_HOSTS} -m synchronize -a 'src=./ dest=/root/docker/nginx-volume/conf delete=yes copy_links=yes rsync_opts=--exclude=.git'
CHECK_RELT=$(ansible -i /root/hosts ${NGINX_HOSTS} -m shell -a "docker exec ops-openresty nginx -t" > /dev/null;echo $?)
if [ ${CHECK_RELT} != "0" ];then
echo "Nginx check configuration failed!"
exit 1
else
ansible -i /root/hosts ${NGINX_HOSTS} -m shell -a "docker exec ops-openresty nginx -s reload"
fi
保存后,点击构建,选择一个tag和要更新nginx配置的主机
点击开始构建,查看构建日志,出现文件同步且nginx reload成功说明配置下发成功。
配置etcd
Etcd集群使用nginx的四层负载对外,在ops-node-03(10.0.1.224)上创建四层负载,监听端口是2379
mkdir /root/docker/nginx-volume/conf/streams
cat > /root/docker/nginx-volume/conf/streams/etcd.zhushiyang.net.conf <<EOF
server {
listen 2379;
proxy_pass etcd_cluster;
}
upstream etcd_cluster {
server 10.0.1.225:12379;
server 10.0.1.225:22379;
server 10.0.1.225:32379;
}
EOF
docker exec ops-openresty nginx -t
docker exec ops-openresty nginx -s reload
验证通过Nginx代理etcd集群情况,有返回值说明正常
ETCDCTL_API=3 etcdctl --endpoints=http://10.0.1.224:2379 -w table endpoint status
+------------------------+------------------+---------+---------+-----------+-----------+------------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+------------------------+------------------+---------+---------+-----------+-----------+------------+
| http://10.0.1.224:2379 | 8fb5ef3dcf4f436c | 3.4.20 | 20 kB | false | 23 | 44 |
+------------------------+------------------+---------+---------+-----------+-----------+------------+
向ETCD集群中添加upstream名称是web_cluster_upsync的rs,rs分别是10.0.1.225:9091,10.0.1.225:9092,10.0.1.225:9093
curl -X PUT -d value="{\"weight\":1, \"max_fails\":2, \"fail_timeout\":10}" http://10.0.1.224:2379/v2/keys/upstreams/web_cluster_upsync/10.0.1.225:9091
curl -X PUT -d value="{\"weight\":1, \"max_fails\":2, \"fail_timeout\":10}" http://10.0.1.224:2379/v2/keys/upstreams/web_cluster_upsync/10.0.1.225:9092
curl -X PUT -d value="{\"weight\":1, \"max_fails\":2, \"fail_timeout\":10}" http://10.0.1.224:2379/v2/keys/upstreams/web_cluster_upsync/10.0.1.225:9093
在你的电脑上配置hosts,windows系统的hosts文件是C:\Windows\System32\drivers\etc\hosts,添加如下内容
10.0.1.223 www.zhushiyang.net
在浏览器中访问www.zhushiyang.net,你会发现报502错误
查看http健康检查页面,http://10.0.1.223/check_status,发现三个rs健康检查没有通过,因为我们还没有在10.0.1.225上面启动这三个rs
现在,在ops-node-04(10.0.1.225)上,启动三个nginx,作为域名www.zhushiyang.net的后端应用
docker run --name nginx-rs-9091 -p 9091:80 -d --restart=always nginx
docker run --name nginx-rs-9092 -p 9092:80 -d --restart=always nginx
docker run --name nginx-rs-9093 -p 9093:80 -d --restart=always nginx
# 自定义nginx-rs-9091页面
docker exec -it nginx-rs-9091 /bin/bash
echo "nginx-rs-9091" > /usr/share/nginx/html/index.html
# 自定义nginx-rs-9092页面
docker exec -it nginx-rs-9092 /bin/bash
echo "nginx-rs-9092" > /usr/share/nginx/html/index.html
# 自定义nginx-rs-9093页面
docker exec -it nginx-rs-9093 /bin/bash
echo "nginx-rs-9093" > /usr/share/nginx/html/index.html
再次查看健康检查页面,发现三个rs健康检查都已经通过
在浏览器中访问www.zhushiyang.net,现在是正常的响应了。
然后将10.0.1.225:9091的rs从etcd中删除,在查看健康检查页面并访问www.zhushiyang.net查看负载情况
curl -X DELETE -d value="{\"weight\":1, \"max_fails\":2, \"fail_timeout\":10}" http://10.0.1.224:2379/v2/keys/upstreams/web_cluster_upsync/10.0.1.225:9091
至此,Nginx动态负载均衡和配置管理基本完成。
注意:Nginx-Pord-L4的示例这里就不在列举了,根据实际情况配置即可。
后期维护
动态负载均衡更新rs流程:在新的rs上启动服务-将rs添加到etcd集群中的指定upstream上
配置管理:
- 新增upstream:在jenkins的upstreams目录下,复制现有的upstream,如果配置模板无变动,只需修改job名称,然后构建提交到git,在下发配置。
- 新增虚机主机:在jenkins的vhosts目录下,复制现有的vhosts,修改job名称,修改配置,然后构建提交到git,在下发配置。
- 更新upstream:找到要更新的upstream的job名称,构建后下发配置。
- 更新虚拟主机:找到要更新的vhosts的job名称,构建后下发配置。
证书更新:
- 使用jenkins的文件参数来实现,配置很容易
文件删除:
- 使用活跃选择参数来实现,显示文件列表,通过参数传递实现文件删除
注意:
- 生产中,下发nginx配置,不要一把梭,先灰度在生产,且生产要分批进行,并通过劫持和日志观察,判断更新的配置是否生效。
- jenkins的参数的输入框不确定可以支持多少字节,如果nginx的虚拟主机配置几千行,能否正常生成需要验证。
etcd接口:
# 增加和修改rs
curl -X PUT -d value="{\"weight\":1, \"max_fails\":2, \"fail_timeout\":10}" http://etcd_ip:etcd_port/v2/keys/upstreams/upstream名称/rs_ip:rs_port
# 删除rs
curl -X DELETE -d value="{\"weight\":1, \"max_fails\":2, \"fail_timeout\":10}" http://etcd_ip:etcd_port/v2/keys/upstreams/upstream名称/rs_ip:rs_port
数据备份与监控:
- Gitlab备份与监控
- Jenkins备份备份与监控
- Etcd备份备份与监控
监控必须告警,否则无任何意义,这些不是本次的主要内容,不做更多介绍。