Nginx配置代码化自动部署詹金斯/Github方案

项目背景:

各个环境Nginx配置变更不透明,团队内部和其他团队之间协作方式不够高效,因为采用项目迭代的方式进行配置代码化变更。

目录结构:

├──环境

│ ├──开发

│ │ ├──发展管理-mcrio.icbc.trade

│││└──dev-connex-mgt-MC Rio . conf

││dev-ops-am.icbc.com├──

│ │ │ └──开发-网站-ops.conf

││dev-web.icbc.com├──

│ │ │ └── Dev-website-client.conf

│ │ ├──管理发展工银贸易

│ │ │ └── Dev-mgt-mcrio.conf

││summit-dev.icbc.com└──

│ │ └──发展-网站-峰会. conf

│ ├──胖

││fat-mgt-mcrio.icbc.com├──

│ │ │ └── Fat-mgt-mcrio.conf

││fat-web.icbc.com├──

│ │ │ └──胖胖-网站-客户端. conf

││summit-fat.icbc.com└──

│ │ └──胖-网站-summit.conf

│ ├──镜报

│ ├──

│ │ ├── qa-mgt-mcrio.icbc.trade

│ │ │ └── QA-connex-mgt-mcrio.conf

││qa-mgt-mcrio.icbc.com├──

│ │ │ └── QA-mgt-mcrio.conf

││qa-ops-am.icbc.com├──

│ │ │ └── Qa-website-ops.conf

│ │ ├── qa-web.icbc.trade

│ │ │ └──连接-QA-网站-客户端. conf

││qa-web.icbc.com├──

│ │ │ └── Qa-website-client.conf

││qa-web2.icbc.com├──

│ │ │ └── Qa2-website-client.conf

││summit-qa.icbc.com└──

│ │ └── Qa-website-summit.conf

│ ├──坐

│ │ ├── sit-mgt-mcrio.icbc.trade

│││└──sit-connex-mgt-MC Rio . conf

││sit-mgt-mcrio.icbc.com├──

│ │ │ └── Sit-mgt-mcrio.conf

│ │ ├── sit-web.icbc.trade

│││└──connex-sit-website-client . conf

││sit-web.icbc.com├──

│ │ │ └── Sit-website-client.conf

││summit-sit.icbc.com└──

│ │ └── Sit-website-summit.conf

│ └──大学

│summit-uat.icbc.com├──

│ │ └── Uat-website-summit.conf

│ ├── uat-mgt-mcrio.icbc.trade

│ │ └── Uat-connex-mcrio.conf

│uat-mgt-mcrio.icbc.com├──

│ │ └── Uat-mgt-mcrio.conf

│uat-ops-am.icbc.com├──

│ │ └── Uat-website-ops.conf

│ ├── uat-web.icbc.trade

││└──connex-UAT-website-client . conf

│uat.icbc.com└──

│└──UAT-网站-客户端. conf

├──库存. ini

└──自述. md


Github工作流程:

名称:部署Nginx

开启:

推送:

分支:[关]

路径:

-"环境/**"

环境:

PKG _目录:/data/pkg/devops-nginx-config

COMMIT_ID: ${{ github.sha }}

工作:

部署:

运行:[自托管,ltp]

步骤:

-用途:actions/checkout@v3

使用:

提取深度:0

  • name:仅部署已更改的文件

运行:|

CHANGED _ FILES = (git diff-diff-filter = AM-name-only { { github . event . before } } { { github . sha } }--' environments/' \| grep ' \\。conf' || true)

回显"更改的文件:"

回显" $CHANGED_FILES "

if[-z " $ CHANGED _ FILES "];然后

回声"不。配置文件已更改。没有要部署的内容。

出口0

船方不负担装货费用

部署失败=假

SERVERS = (echo " CHANGED _ FILES " | awk-F '/' ' { print 2 "/ 3 } ' | sort-u)

对于$SERVERS中的目标;做

ENV = (echo " TARGET " | cut-d '/'-f1)

SERVER = (echo " TARGET " | cut-d '/'-F2)

host = (grep-a1 " \^\\\[env:$server\)" inventory . ini | tail-1 | tr-d '[:space:]')

回显""

echo " = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = "

回声"🚀正在处理:$SERVER "

echo " ENV: $ENV "

回显"主机:$HOST"

echo " = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = "

if[-z " $ HOST "];然后

echo "❌错误:在inventory.ini中找不到[ENV:SERVER]的主机"

回显"跳过此服务器..."

继续

船方不负担装货费用

server _ files = (echo " changed _ files " | grep " ^environments/env/server/ " | | true)

if[-z " $ SERVER _ FILES "];然后

回显"⚠️没有要为server部署的文件,跳过..."

继续

船方不负担装货费用

STAGING _ DIR = "/tmp/nginx-STAGING-$ { { env。COMMIT_ID }} "

BACKUP _ DIR = "/data/pkg/devo PS-nginx-config/ ENV/ SERVER/$ { { ENV。COMMIT_ID }} "

ssh root @ HOST " mkdir-p STAGING _ DIR $ BACKUP _ DIR "

#第一步:上传到临时目录

回声"📤正在上传到暂存中..."

对于$SERVER_FILES中的文件;做

文件名=(基本名称" 文件")

scp " FILE " root @ HOST: STAGING _ DIR/ FILENAME

回显"↳$文件名"

完成的

#第二步:备份当前配置并验证

回声"📦正在备份当前配置..."

BACKUP_OK=true

对于$SERVER_FILES中的文件;做

文件名=(基本名称" 文件")

if[[" $ FILENAME " = = " nginx . conf "]];然后

DEST="/etc/nginx/nginx.conf "

其他

DEST = "/etc/nginx/conf . d/$ FILENAME "

船方不负担装货费用

记录文件是否为新增(服务器上不存在)

如果ssh root @ HOST"test-f DEST";然后

如果!ssh root @ HOST " CP DEST 备份目录/文件名";然后

回显"❌备份失败:$FILENAME"

BACKUP_OK=false

其他

echo"↳备份:$FILENAME"

船方不负担装货费用

其他

新文件,写标记以便回滚时知道要删除

ssh root@HOST "echo '新文件' \> BACKUP_DIR/$FILENAME.new_flag "

echo"↳新文件(不需要备份):$FILENAME"

船方不负担装货费用

完成的

备份失败则跳过该服务器

if[" $ BACKUP _ OK " = false];然后

回显"❌备份失败,跳过server以避免数据丢失"

ssh root @ HOST " RM-RF STAGING _ DIR "

部署失败=真

继续

船方不负担装货费用

#第三步:替换为新配置

回声"📤正在部署新配置..."

对于$SERVER_FILES中的文件;做

文件名=(基本名称" 文件")

if[[" $ FILENAME " = = " nginx . conf "]];然后

ssh root @ HOST " CP STAGING _ DIR/$ FILENAME/etc/nginx/nginx . conf "

其他

ssh root @ HOST " CP STAGING _ DIR/ FILENAME/etc/nginx/conf . d/ FILENAME "

船方不负担装货费用

回显"↳$文件名"

完成的

#第四步:检测语法

回声"🔍正在测试nginx配置..."

如果ssh root @ $ HOST " nginx-t " 2 > & 1;然后

回显"✅语法检查通过"

ssh root@$HOST "nginx -s reload "

回显"✅ Nginx重装成功"

其他

echo"❌语法检查失败!正在回滚..."

回滚失败=假

对于$SERVER_FILES中的文件;做

文件名=(基本名称" 文件")

if[[" $ FILENAME " = = " nginx . conf "]];然后

DEST="/etc/nginx/nginx.conf "

其他

DEST = "/etc/nginx/conf . d/$ FILENAME "

船方不负担装货费用

有备份则恢复,新增文件则删除

如果ssh root @ HOST " test-f BACKUP _ DIR/$ filename . new _ flag ";然后

ssh root @ host " RM-f dest " & & echo "↳已删除(新文件):$ filename " | | roll back _ failed = true

elif ssh root @ HOST " test-f BACKUP _ DIR/$ FILENAME ";然后

ssh root @ host " CP backup _ dir/ filename dest " & & echo "↳恢复:$ filename " | | roll back _ failed = true

船方不负担装货费用

完成的

验证回滚后一款反向代理网页服务器配置是否正常

如果ssh root @ $ HOST " nginx-t " 2 > & 1;然后

回显"✅回滚完成,nginx配置有效"

其他

echo"⚠️警告:回滚已完成,但nginx -t仍然失败!"

回显"备份目录保留在:$BACKUP_DIR"

回显"请手动检查服务器$HOST!"

船方不负担装货费用

if[" $ roll back _ FAILED " = true];然后

echo "⚠️警告:一些文件无法在$HOST上回滚!"

回显"备份目录:$BACKUP_DIR"

船方不负担装货费用

ssh root @ HOST " RM-RF STAGING _ DIR "

部署失败=真

继续

船方不负担装货费用

清理临时目录

ssh root @ HOST " RM-RF STAGING _ DIR "

清理旧备份,保留最近10次

ssh root@HOST "ls -dt {{ env。PKG _目录} }/ ENV/ SERVER/*/2 >/dev/null | tail-n+11 | xargs RM-RF 2 >/dev/null | | true "

回显"完成:$SERVER"

完成的

if[" $ DEPLOY _ FAILED " = true];然后

回声"❌一些服务器部署失败,检查上面的日志。"

1号出口

船方不负担装货费用


詹金斯·皮普林斯:

管道{

任何代理

环境{

ANSIBLE_KEY = '/data/runner.key '

INVENTORY _ FILE = " $ { WORKSPACE }/INVENTORY . ini "

GIT _ REPO _ URL = " GIT @ github . com:ICBC/$ { JOB _ BASE _ NAME }。饭桶"

}

参数{

选择(

名称:'部署环境',

选择:['dev ',' fat ',' sit ',' qa ',' uat ',' mirror'],

'描述: '选择要部署的环境'

)

选择(

名称:'部署模式',

选项:['仅更改','完全同步'],

'描述: '部署模式:仅已更改=仅部署有变更的文件(默认),full_sync=全量同步'

)

字符串(

名称:'基本提交',

默认值: '',

描述:'[仅仅已更改\模式有效]对比基准提交(留空则自动使用HEAD ~ 1)'

)

}

阶段{

阶段('拉最新'){

步骤{

结帐([

$class: 'GitSCM ',

分支机构:[[name:'非盈利']],

扩展:[

$class: 'CleanCheckout'\], \[$class: 'CloneOption ',深度:0,浅度:false

],

用户远程配置:[[

url: env。GIT报告URL

]]

])

脚本{

环境。GIT_AUTHOR = sh(脚本:' git log -1 - pretty=format:%an ',returnStdout: true)。修剪()

环境。GIT_MSG = sh(脚本:' git log -1 - pretty=format:%s ',returnStdout: true)。修剪()

环境。GIT_COMMIT = sh(脚本:' git rev-parse HEAD ',returnStdout: true)。修剪()

currentBuild.description = "环境:{params。部署环境} \|模式:{params。部署模式} |分支:nonprod | {env。GIT_AUTHOR}: {env。GIT_MSG} "

}

}

}

stage('检测文件'){

步骤{

脚本{

def changedFiles = ' '

if (params。DEPLOY_MODE == 'changed_only') {

def baseCommit = params。BASE_COMMIT?。修剪()

如果(!baseCommit) {

baseCommit = sh(脚本:' git rev-parse HEAD~1 ',returnStdout: true)。修剪()

}

def headCommit = sh(脚本:' git rev-parse HEAD ',returnStdout: true)。修剪()

回声"📌 对比范围:{baseCommit} → {headCommit} "

changedFiles = sh(

脚本:""

git diff-diff-filter = AM-name-only { base commit } { head commit } \

  • "environments/${params。部署环境}//\

| grep -i '\\。conf\$' || true

""",

returns out:true

).修剪()

如果(!changedFiles) {

回声"ℹ️ [{params。DEPLOY_ENV}\]在{baseCommit.take(8)}..${headCommit.take(8)}范围内没有变更的。会议文件。"

回声"💡 如需部署全部文件,请选择完全同步模式;或在基本提交中指定更早的基准提交,"

currentBuild.result = ' NOT _ BUILT '

返回

}

}否则{

changedFiles = sh(

脚本:" find environments/${params。DEPLOY_ENV}/ -name '*。conf"| sort | | true",

returns out:true

).修剪()

}

如果(!changedFiles) {

error("❌环境[{params。DEPLOY_ENV}\]下未找到任何。会议文件,请检查environments/{params。部署_环境}/目录。")

}

回声"📄 待部署文件列表:\n${changedFiles} "

def servers = changed files . split(' \ n ')。收集{ line-->

line.trim()。拆分('/')[2]

}.唯一()

回声"🖥️涉及服务器:${servers.join(',')} "

环境。CHANGED_FILES = changedFiles

环境。TARGET_SERVERS = servers.join(',')

}

}

}

登台('部署'){

何时{

表达式{ env。已更改_文件?。trim() }

}

步骤{

脚本{

def文件= env。CHANGED_FILES.split('\n ')

定义服务器=环境。TARGET_SERVERS.split(',')

servers . each { server-->

def serverIP = sh(

脚本:""

awk '/^\\[{params.DEPLOY _ ENV }: { server } \ \]/{ found = 1;下一个} \

发现& & /^[0-9]/{print;退出} \

找到& & /^\\[/{exit}' $ { inventory _ file }

""",

returns out:true

).修剪()

如果(!serverIP) {

error("❌库存. ini中未找到[{params。DEPLOY_ENV}:{server}],请检查inventory.ini,")

}

回声"🚀 开始部署到:{server} ({serverIP})"

def server files = files . find all { it . contains("/$ { server }/)}

server files . each { conf file-->

def fileName = confFile.split('/')。最后一个()

def remote path = "/etc/nginx/conf . d/$ { fileName } "

def backup path = "/etc/nginx/conf . d/$ { fileName }。bak "

嘘"""

ansible all -i '${serverIP},' \

-u根私钥${ ansi ble _ KEY } \

-m shell \

-a ' test-f { remote path } \& \& CP { remote path } $ { backup path } | | true '

"""

嘘"""

ansible all -i '${serverIP},' \

-u根私钥${ ansi ble _ KEY } \

-m副本\

-a ' src = { WORKSPACE }/ { conf file } dest = $ { remote path } mode = 0644 '

"""

}

def testResult = sh(

脚本:""

ansible all -i '${serverIP},' \

-u根私钥${ ansi ble _ KEY } \

-m shell \

-a 'nginx -t 2>&1 '

""",

returnStatus: true

)

if (testResult!= 0) {

回声"❌ nginx -t校验失败,回滚{server}({serverIP})上的配置..."

server files . each { conf file-->

def fileName = confFile.split('/')。最后一个()

def remote path = "/etc/nginx/conf . d/$ { fileName } "

def backup path = "/etc/nginx/conf . d/$ { fileName }。bak "

嘘"""

ansible all -i '${serverIP},' \

-u根私钥${ ansi ble _ KEY } \

-m shell \

-a ' test-f { backup path } \& \& mv { backup path } { remote path } \| \| RM-f { remote path } '

"""

}

error("❌ ${server} nginx配置校验失败,已回滚,请检查配置文件。")

}

嘘"""

ansible all -i '${serverIP},' \

-u根私钥${ ansi ble _ KEY } \

-m shell \

-一个"nginx -s重新加载"

"""

echo "✅ {server} ({serverIP})部署完成"

}

}

}

}

}

帖子{

成功{

wrap([$class: 'BuildUser']) {

脚本{

嘘"""

python3 /data/notify_lark.py \

" {env。JOB _ NAME }" { env .BUILD _ NUMBER }"$ { env .BUILD_URL}" \

" {env。GIT_AUTHOR}" "{env。GIT_COMMIT}" \

" {env。GIT_MSG}" "{env。BUILD _ USER }"$ { params。DEPLOY_ENV}" "nonprod " "成功"

"""

}

}

}

失败{

wrap([$class: 'BuildUser']) {

脚本{

嘘"""

python3 /data/notify_lark.py \

" {env。JOB _ NAME }" { env .BUILD _ NUMBER }"$ { env .BUILD_URL}" \

" {env。GIT _作者?:' unknown'}" "{env。GIT_COMMIT?:'未知' }" \

" {env。GIT_MSG?:' unknown'}" "{env。BUILD_USER?:' unknown'}" "${params。DEPLOY_ENV}" "nonprod " "失败"

"""

}

}

}

未建造{

回声"⏭️ 无需部署(没有变更的文件)"

}

总是{

脚本{

echo " = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = "

echo "Nginx配置部署完成"

回声"环境:${params。部署环境} "

回声"模式:${params。部署模式} "

回声"状态:${currentBuild.currentResult} "

回声"分支:非盈利"

echo " = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = "

}

}

}

}

复制代码
目录结构:
## 快速开始

### 1. 修改配置文件

```bash
vim environments/dev/dev-web.icbc.com/Dev-website-client.conf
```

### 2. 提交并推送

```bash
git add .
git commit -m "update dev-web nginx config"
git push origin nonprod
```

### 3. 自动部署

推送后 GitHub Actions 自动执行:
1. 检测变更的 `.conf` 文件
2. 从 `inventory.ini` 读取目标主机 IP
3. 上传配置到临时目录
4. 备份当前配置
5. 应用新配置并执行 `nginx -t`
6. 测试通过后 `nginx -s reload`
7. 测试失败自动回滚

## 主机映射 (inventory.ini)

`inventory.ini` 定义了每个域名目录对应的服务器 IP 地址。CI/CD 流程通过此文件确定配置文件应该部署到哪台服务器。

### 格式说明

```ini
[环境:域名目录]
服务器IP
```

- **环境**: 必须与 `environments/` 下的目录名一致 (dev/fat/sit/qa/uat)
- **域名目录**: 必须与 `environments/{env}/` 下的目录名完全一致
- **服务器IP**: 目标 Nginx 服务器的 IP 地址,下一行紧跟 `[env:domain]`

### 示例

```ini
# ========== DEV ==========
[dev:mgt-dev.icbc.com]
10.16.137.5

[dev:dev-mgt-mcrio.icbc.com]
10.16.137.5

[dev:dev-web.icbc.com]
10.16.76.31

[dev:dev-ops-am.icbc.com]
10.16.76.31

[dev:summit-dev.icbc.com]
10.16.76.31

# ========== QA ==========
[qa:qa-mgt-mcrio.icbc.com]
10.16.60.103

[qa:qa-web.icbc.com]
10.16.76.31
```


### 添加新域名

1. 创建目录: `environments/{env}/{domain}/`
2. 添加配置文件: `{Name}.conf`
3. **在 `inventory.ini` 中添加映射** (必须):

```ini
[{env}:{domain}]
{server_ip}
```

> 如果 `inventory.ini` 中缺少对应配置,部署时会报错 `No host found for [env:domain]` 并跳过该域名。

## 回滚操作

在 GitHub Actions 页面手动触发 `Rollback Nginx Config` 工作流:

| 参数 | 说明 | 示例 |
|------|------|------|
| env | 环境名称 | dev / fat / sit / qa / uat |
| domain | 域名目录 | dev-web.icbc.com |
| commit_id | 回滚到的版本 (可选) | 留空使用最近备份 |

备份存储位置: `/data/pkg/devops-nginx-config/{env}/{domain}/{commit_id}/`

## 配置文件部署规则

| 文件名 | 部署位置 |
|--------|----------|
| `nginx.conf` | `/etc/nginx/nginx.conf` |
| `*.conf` (其他) | `/etc/nginx/conf.d/` |

## 注意事项

- 只有 `.conf` 文件的变更会触发部署
- 删除的文件不会触发部署操作
- 每次部署前会自动备份当前配置
- `nginx -t` 失败会自动回滚到部署前状态
复制代码
Github actions:

詹金斯·皮普林斯:

相关推荐
开发者如是说2 小时前
可能是最好用的多语言管理工具
android·前端·后端
是上好佳佳佳呀2 小时前
【前端(六)】HTML5 新特性笔记总结
前端·笔记·html5
李白的天不白2 小时前
ai编程工具
github
北城笑笑2 小时前
FPGA 与 市场主流芯片分类详解:SoC/CPU/GPU/DPU 等芯片核心特性与工程应用
前端·单片机·fpga开发·fpga
A923A2 小时前
【从零开始学 React | 第四章】useEffect和自定义Hook
前端·react.js·fetch·useeffect
ZC跨境爬虫3 小时前
批量爬取小说章节并优化排版(附完整可运行脚本)
前端·爬虫·python·自动化
ZC跨境爬虫3 小时前
海南大学交友平台登录页开发实战day4(解决python传输并读取登录信息的问题)
开发语言·前端·python·flask·html
来一颗砂糖橘3 小时前
pnpm:现代前端开发的高效包管理器
前端·pnpm
前端摸鱼匠3 小时前
Vue 3 的defineProps编译器宏:详解<script setup>中defineProps的使用
前端·javascript·vue.js·前端框架·ecmascript