首页 / 资讯中心 / 文章详情

AI辅助运维脚本实战:用Codex高效生成Python自动化脚本

AI辅助运维脚本实战:用Codex高效生成Python自动化脚本 ★ FEATURED ARTICLE
1. 运维脚本这件事为什么值得用 AI 重做一遍干了七八年运维我越来越觉得这个岗位的核心矛盾不在于会不会写脚本而在于值不值得为这件事写脚本。一台机器上改个配置手动敲两行命令就完事了但如果是三十台机器要批量改同一个参数还得考虑回滚、日志、异常处理这时候写脚本的收益才真正显现出来。问题在于很多运维场景恰好卡在中间地带——写脚本吧前后得花一两个小时不写吧手动操作又容易出错。这种时候用 AI 辅助生成脚本草稿再人工审校补全效率提升非常明显。Codex 这类代码生成模型进入我的工作流之后最大的变化不是AI 替我写代码而是AI 帮我把脑子里模糊的想法快速变成可运行的骨架。我只需要用自然语言描述清楚需求它就能给出一个结构基本正确的 Python 脚本我再在此基础上调整参数、补充异常处理、加上日志输出。整个过程从从零开始写变成了改一份草稿心智负担小了很多。这篇文章适合两类人看一类是有一定运维经验、但 Python 写得不算熟练的同行你们可以借助 AI 快速把运维思路落地成脚本另一类是 Python 基础还不错、但没怎么接触过运维场景的开发者你们可以从文中看到运维脚本和普通业务代码在关注点上的差异。我会围绕 Codex 辅助写运维脚本这个核心场景把提示词设计、脚本结构、常见坑点、排查技巧都讲清楚尽量做到看完就能上手。2. 用 Codex 写运维脚本的整体思路拆解2.1 为什么选 Codex 而不是自己硬写先说一个我自己的真实感受运维脚本和业务代码最大的区别在于运维脚本往往是一次性的、场景高度特化的。比如把 /etc/nginx/conf.d 下所有配置文件里的 worker_connections 从 1024 改成 2048改之前备份改完 reload失败就回滚——这种脚本写一次可能就用这一回下次场景变了又得重写。如果每次都从零手写时间成本太高但如果用 AI 生成骨架我只需要花几分钟审校和调整性价比就出来了。Codex 在这类任务上的优势主要体现在三个方面。第一它对常见运维操作的代码模式非常熟悉比如文件读写、subprocess 调用、异常捕获、日志记录这些它都能给出比较规范的写法。第二它能理解自然语言里的条件分支比如如果备份失败就不要继续修改它会自动加上相应的判断逻辑。第三它生成的代码结构通常比较清晰函数拆分合理方便我后续修改。当然Codex 不是万能的。它生成的脚本往往缺少对具体环境的适配比如路径可能写死、命令可能不兼容你的系统版本、异常处理可能过于笼统。这些问题都需要人工审校。我的经验是把 Codex 当成一个写代码很快但不太懂你环境的初级工程师它出的东西你得 review但 review 的成本远低于从零写。2.2 运维脚本的三个核心关注点在让 Codex 生成脚本之前我自己会先想清楚三件事这三件事也决定了提示词该怎么写。第一是幂等性。运维脚本最怕的就是重复执行出问题。比如一个创建用户的脚本如果用户已存在是报错退出还是跳过一个修改配置的脚本如果配置已经是目标值是重新写一遍还是不动这些逻辑必须在提示词里说清楚否则 Codex 默认生成的代码很可能不具备幂等性。第二是可回滚。任何修改类操作都要考虑失败之后怎么恢复。备份原文件、记录操作日志、失败时自动还原这些都是运维脚本的标配。我在提示词里通常会明确要求修改前备份失败时回滚Codex 会据此生成相应的 try-except 结构和备份逻辑。第三是可观测。脚本跑完了我得知道它干了什么、成功了没有、哪里出了问题。所以日志输出是必须的而且日志要包含时间戳、操作对象、执行结果。Codex 生成的代码默认可能只有简单的 print我会在提示词里要求用 logging 模块并指定日志格式。2.3 提示词设计的核心原则用 Codex 写运维脚本提示词的质量直接决定生成代码的可用性。我总结了几条原则实测下来很管用。原则一说清楚环境。是 CentOS 还是 UbuntuPython 是 3.6 还是 3.10有没有装第三方库这些信息会影响 Codex 生成的代码。比如 Python 3.6 不支持 f-string 的某些用法CentOS 7 默认的 Python 是 2.7这些它都会考虑进去。原则二说清楚输入输出。脚本接收什么参数是命令行参数还是配置文件输出是什么是打印到终端还是写日志文件把这些说清楚Codex 生成的代码接口会更符合你的预期。原则三说清楚边界条件。文件不存在怎么办权限不够怎么办命令执行超时怎么办这些边界条件如果不说Codex 可能只处理正常路径异常路径就漏了。原则四给一个例子。如果你能给出输入输出的示例Codex 生成的代码会更贴近你的需求。比如输入是 /etc/nginx/nginx.conf输出是把 worker_processes 改成 auto 之后的文件内容这样它就知道该怎么处理。3. 核心细节解析与实操要点3.1 提示词模板从模糊需求到可执行脚本我常用的提示词模板大概长这样你可以直接拿去改你是一名资深运维工程师请用 Python 写一个脚本完成以下任务 【任务描述】 批量修改指定目录下所有 .conf 文件中的 worker_connections 参数 从原值改为 2048。 【环境信息】 - 操作系统CentOS 7 - Python 版本3.8 - 可用第三方库无仅用标准库 【具体要求】 1. 修改前先备份原文件到 /tmp/backup/ 目录保留原文件名加时间戳 2. 如果文件中没有 worker_connections 参数跳过该文件并记录日志 3. 如果修改过程中出现异常自动从备份恢复 4. 使用 logging 模块输出日志格式包含时间、级别、消息 5. 脚本接收一个命令行参数指定要处理的目录 【输出要求】 - 给出完整可运行的代码 - 关键步骤加中文注释 - 说明脚本的使用方法这个模板的关键在于把任务描述环境信息具体要求输出要求分开写Codex 对结构化提示词的理解明显更好。我试过把同样的需求写成一大段话生成的代码质量明显不如分点写。3.2 生成代码的审校要点Codex 生成的代码不能直接用这是我踩过几次坑之后的深刻教训。审校的时候我重点看这几个地方。第一路径处理。Codex 经常把路径写死比如直接写/etc/nginx/conf.d但实际环境可能不一样。我会把所有硬编码路径改成变量或命令行参数。第二异常处理粒度。Codex 默认的 except 往往太宽比如except Exception as e一把抓这样会掩盖具体问题。我会根据实际情况细化比如文件操作捕获 IOErrorsubprocess 捕获 CalledProcessError。第三命令兼容性。如果脚本里调用了系统命令Codex 可能用的是某个发行版特有的写法。比如systemctl reload nginx在 CentOS 7 上没问题但在某些容器环境里可能没有 systemd。这些需要根据实际环境调整。第四日志级别。Codex 生成的日志可能全是 INFO但实际运维中正常操作用 INFO异常用 ERROR调试信息用 DEBUG级别要区分开否则日志文件很快就爆了。第五编码问题。处理中文文件时Codex 可能没指定 encoding导致在某些系统上报 UnicodeDecodeError。我会统一加上encodingutf-8。3.3 一个完整的脚本示例与逐段解析下面这个脚本是我用 Codex 生成之后修改过的功能是批量修改配置文件中的某个参数带备份和回滚。我把它拆开讲你能看到哪些是 Codex 生成的哪些是我补的。#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import re import sys import shutil import logging import argparse from datetime import datetime from pathlib import Path # 配置日志 logging.basicConfig( levellogging.INFO, format%(asctime)s [%(levelname)s] %(message)s, datefmt%Y-%m-%d %H:%M:%S ) logger logging.getLogger(__name__) def backup_file(file_path, backup_dir): 备份文件到指定目录保留原文件名加时间戳 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) backup_name f{Path(file_path).name}.{timestamp}.bak backup_path os.path.join(backup_dir, backup_name) shutil.copy2(file_path, backup_path) logger.info(f已备份 {file_path} 到 {backup_path}) return backup_path def restore_file(backup_path, original_path): 从备份恢复文件 shutil.copy2(backup_path, original_path) logger.warning(f已从 {backup_path} 恢复 {original_path}) def modify_config(file_path, param_name, new_value): 修改配置文件中的指定参数 with open(file_path, r, encodingutf-8) as f: content f.read() # 匹配参数行支持行首可能有空格的情况 pattern re.compile( rf^(\s*{re.escape(param_name)}\s)(\S)(.*)$, re.MULTILINE ) match pattern.search(content) if not match: logger.info(f{file_path} 中未找到 {param_name}跳过) return False old_value match.group(2) if old_value str(new_value): logger.info(f{file_path} 中 {param_name} 已是 {new_value}无需修改) return False new_content pattern.sub( rf\g1{new_value}\g3, content ) with open(file_path, w, encodingutf-8) as f: f.write(new_content) logger.info(f{file_path} 中 {param_name} 从 {old_value} 改为 {new_value}) return True def process_directory(target_dir, param_name, new_value, backup_dir): 处理目录下所有 .conf 文件 os.makedirs(backup_dir, exist_okTrue) conf_files list(Path(target_dir).glob(*.conf)) if not conf_files: logger.warning(f{target_dir} 下没有找到 .conf 文件) return success_count 0 skip_count 0 fail_count 0 for conf_file in conf_files: backup_path None try: backup_path backup_file(str(conf_file), backup_dir) modified modify_config(str(conf_file), param_name, new_value) if modified: success_count 1 else: skip_count 1 except Exception as e: logger.error(f处理 {conf_file} 时出错{e}) if backup_path and os.path.exists(backup_path): restore_file(backup_path, str(conf_file)) fail_count 1 logger.info( f处理完成成功 {success_count}跳过 {skip_count}失败 {fail_count} ) def main(): parser argparse.ArgumentParser(description批量修改配置文件参数) parser.add_argument(directory, help要处理的目录) parser.add_argument(--param, defaultworker_connections, help参数名) parser.add_argument(--value, default2048, help新值) parser.add_argument(--backup-dir, default/tmp/backup, help备份目录) args parser.parse_args() if not os.path.isdir(args.directory): logger.error(f{args.directory} 不是有效目录) sys.exit(1) process_directory(args.directory, args.param, args.value, args.backup_dir) if __name__ __main__: main()这段代码里backup_file、restore_file、modify_config这三个函数的骨架是 Codex 生成的我做了几处修改。第一modify_config里我加了值已经是目标值就跳过的判断这是幂等性的要求Codex 默认没加。第二正则匹配我改成了支持行首空格的形式因为实际配置文件里参数前面可能有缩进。第三process_directory里的计数逻辑是我补的方便最后汇总。main函数用了 argparse这是 Codex 建议的我觉得比 sys.argv 更规范就保留了。日志配置那块是我自己加的Codex 默认用的是 print。3.4 参数计算与选择过程脚本里有一个参数值得单独说备份目录的命名。我一开始用的是固定目录/tmp/backup后来发现多次执行会覆盖之前的备份出问题想找回更早的版本就找不到了。改成带时间戳的文件名之后每次备份都是独立的虽然占空间但安全。时间戳的格式我选的是%Y%m%d_%H%M%S精确到秒。为什么不精确到毫秒因为运维脚本执行频率不高秒级足够区分而且文件名更短更好读。如果你有高频执行的场景可以改成毫秒。另一个参数是正则里的re.MULTILINE。这个标志让^和$匹配每一行的开头和结尾而不是整个字符串的开头和结尾。配置文件是多行的不加这个标志^只会匹配文件第一行的开头后面的行就匹配不到了。这个细节 Codex 有时候会漏需要自己检查。4. 实操过程与核心环节实现4.1 环境准备Python 与依赖在开始之前先把环境弄干净。我用的是 CentOS 7系统自带的 Python 是 2.7所以需要单独装 Python 3。安装过程不复杂但有几个坑要注意。# 安装依赖 yum install -y gcc make zlib-devel bzip2-devel openssl-devel \ ncurses-devel sqlite-devel readline-devel tk-devel libffi-devel # 下载 Python 3.8 源码 cd /usr/local/src wget https://www.python.org/ftp/python/3.8.18/Python-3.8.18.tgz tar -xzf Python-3.8.18.tgz cd Python-3.8.18 # 编译安装 ./configure --prefix/usr/local/python3 --enable-optimizations make -j$(nproc) make altinstall这里用make altinstall而不是make install是为了避免覆盖系统的 python 命令。装完之后/usr/local/python3/bin/python3.8就是我们的解释器。建议把它加到 PATH 里或者做个软链接。ln -s /usr/local/python3/bin/python3.8 /usr/local/bin/python3验证一下python3 --version # 应该输出 Python 3.8.18注意不要动系统自带的 Python 2.7很多系统工具依赖它。用 altinstall 和软链接的方式两个版本可以共存。4.2 脚本编写与调试的完整流程环境准备好之后就可以开始写脚本了。我的流程一般是这样的。第一步用 Codex 生成初版。把前面说的提示词模板填好发给 Codex拿到一份初版代码。这一步不要纠结细节先有个能跑的骨架。第二步本地跑一遍。在测试目录里造几个假的 .conf 文件跑一遍脚本看输出是否符合预期。我一般会造三种文件正常的、没有目标参数的、格式奇怪的。这样能覆盖大部分情况。第三步根据报错调整。第一次跑大概率会报错常见的是路径问题、编码问题、正则匹配问题。根据报错信息逐个修。第四步加日志和异常处理。初版跑通之后把日志补全异常处理细化。这一步是让脚本从能用变成敢用的关键。第五步在测试环境验证。找一台测试机用真实的配置文件跑一遍确认备份、修改、回滚都正常。第六步上生产。上生产之前先手动备份一次然后跑脚本跑完检查日志和文件内容。4.3 一个真实场景的完整操作记录我拿一个真实场景走一遍。需求是把/etc/nginx/conf.d/下所有 .conf 文件里的worker_connections从 1024 改成 2048。先看原始文件$ grep worker_connections /etc/nginx/conf.d/*.conf /etc/nginx/conf.d/site1.conf: worker_connections 1024; /etc/nginx/conf.d/site2.conf: worker_connections 1024; /etc/nginx/conf.d/site3.conf: worker_connections 512;注意 site3 的值是 512不是 1024。如果脚本只匹配 1024site3 就会被漏掉。所以脚本里不能写死原值要匹配任意值。这也是我在提示词里强调从原值改为 2048而不是从 1024 改为 2048的原因。跑脚本python3 modify_conf.py /etc/nginx/conf.d/ --param worker_connections --value 2048输出2024-01-15 10:23:01 [INFO] 已备份 /etc/nginx/conf.d/site1.conf 到 /tmp/backup/site1.conf.20240115_102301.bak 2024-01-15 10:23:01 [INFO] /etc/nginx/conf.d/site1.conf 中 worker_connections 从 1024 改为 2048 2024-01-15 10:23:01 [INFO] 已备份 /etc/nginx/conf.d/site2.conf 到 /tmp/backup/site2.conf.20240115_102301.bak 2024-01-15 10:23:01 [INFO] /etc/nginx/conf.d/site2.conf 中 worker_connections 从 1024 改为 2048 2024-01-15 10:23:01 [INFO] 已备份 /etc/nginx/conf.d/site3.conf 到 /tmp/backup/site3.conf.20240115_102301.bak 2024-01-15 10:23:01 [INFO] /etc/nginx/conf.d/site3.conf 中 worker_connections 从 512 改为 2048 2024-01-15 10:23:01 [INFO] 处理完成成功 3跳过 0失败 0验证$ grep worker_connections /etc/nginx/conf.d/*.conf /etc/nginx/conf.d/site1.conf: worker_connections 2048; /etc/nginx/conf.d/site2.conf: worker_connections 2048; /etc/nginx/conf.d/site3.conf: worker_connections 2048;然后 reload nginxnginx -t systemctl reload nginxnginx -t是检查配置语法通过了再 reload。这一步很重要如果配置有问题reload 会失败但不会影响正在运行的服务。如果直接 restart配置有问题就可能导致服务起不来。4.4 回滚操作的实际演练回滚是运维脚本的保命功能必须实际演练过才敢用。我的演练方法是故意制造一个错误看脚本能不能正确回滚。比如把modify_config里的写入逻辑改错让它写入非法内容然后跑脚本。预期是备份成功修改失败脚本捕获异常从备份恢复原文件内容不变。# 故意制造错误 with open(file_path, w, encodingutf-8) as f: f.write(this is invalid content) # 模拟写入错误 raise RuntimeError(模拟写入失败)跑完之后检查原文件$ cat /etc/nginx/conf.d/site1.conf # 应该还是原来的内容没有被破坏如果原文件被破坏了说明回滚逻辑有问题需要检查restore_file的调用时机和路径。提示回滚演练一定要在测试环境做不要拿生产环境试。我见过有人直接在生产上试回滚结果备份路径写错原文件被覆盖只能从更早的备份恢复损失了几个小时的配置变更。5. 常见问题与排查技巧实录5.1 Codex 生成代码的典型问题速查表问题现象可能原因解决方法脚本报 UnicodeDecodeError文件编码不是 utf-8open 时指定 encoding或用 errorsignore正则匹配不到目标行没加 re.MULTILINE加上 re.MULTILINE 标志备份文件被覆盖备份文件名没加时间戳文件名加 datetime 时间戳重复执行报错脚本不幂等加值已是目标值则跳过判断日志文件过大日志级别全是 INFO区分 DEBUG/INFO/WARNING/ERROR命令执行失败没提示没检查 subprocess 返回值用 checkTrue 或检查 returncode路径写死导致换环境失败硬编码路径改成命令行参数或配置文件中文乱码没指定编码统一用 utf-85.2 我踩过的三个坑坑一正则里的特殊字符没转义。有一次参数名里带点号比如nginx.conf我直接写进正则结果点号匹配了任意字符把不该改的行也改了。后来用re.escape()包一下就好了。Codex 生成的代码有时候会漏这个需要自己检查。坑二备份目录权限不够。脚本用 root 跑没问题但用普通用户跑的时候往/tmp/backup写文件可能没权限。后来我把备份目录改成脚本所在目录下的backup/权限问题就没了。或者用os.makedirs(backup_dir, exist_okTrue)确保目录存在再检查写权限。坑三subprocess 调用命令时 shell 注入。如果命令里拼接了用户输入的参数直接shellTrue会有注入风险。Codex 生成的代码有时候图省事用shellTrue我会改成列表形式传参比如subprocess.run([nginx, -t], checkTrue)这样更安全。5.3 让 Codex 生成代码更靠谱的几个技巧技巧一分步生成。不要一次性让 Codex 生成整个脚本而是分函数生成。比如先让它写备份文件的函数再写修改配置的函数最后写主流程。这样每个函数的逻辑更清晰出问题也容易定位。技巧二让它解释代码。生成之后我会追问一句请解释这段代码的执行流程和潜在问题。Codex 的解释往往能帮我发现一些自己没注意到的细节比如某个异常没处理、某个边界没考虑。技巧三给它看报错信息。脚本跑报错的时候把完整的报错信息贴给 Codex让它分析原因并给出修改方案。这比自己去查文档快得多。技巧四要求它写测试用例。我会让 Codex 顺便生成几个测试用例比如测试文件不存在的情况测试参数已存在的情况。虽然这些测试不一定全对但能帮我快速验证脚本的健壮性。技巧五用注释引导。如果我对某个函数的实现有想法会先写一段注释描述逻辑再让 Codex 补全代码。这样生成的代码更贴近我的预期。5.4 运维脚本的安全底线用 AI 写运维脚本有几个安全底线必须守住。第一任何修改类操作必须先备份。这是铁律没有例外。备份路径要明确备份文件要可追溯。第二脚本要有 dry-run 模式。正式执行之前先用 dry-run 跑一遍看看会改哪些文件、改成什么。实现方式很简单加一个--dry-run参数为真时只打印不写入。第三危险操作要二次确认。比如删除文件、重启服务脚本里应该加一个确认步骤或者要求显式传入--yes参数。第四日志要留痕。谁在什么时候执行了什么操作改了什么文件结果如何都要记录。出了问题能追溯。第五不要在脚本里硬编码密码。如果脚本需要连接数据库或远程主机密码应该从环境变量或配置文件读取不要写在代码里。6. 从脚本到工作流把 AI 辅助运维用起来6.1 把常用脚本沉淀成模板库用 Codex 写脚本写多了之后你会发现很多逻辑是重复的备份、日志、参数解析、异常处理。这些可以沉淀成模板下次写新脚本的时候直接套。我自己的模板库大概有这几个文件批量修改模板遍历目录、匹配内容、备份、修改、回滚服务管理模板检查服务状态、启动/停止/重启、等待就绪日志分析模板读取日志、正则提取、统计汇总、输出报告远程执行模板通过 SSH 在多个主机上执行命令、收集结果每个模板都是一个可运行的脚本骨架用的时候把业务逻辑填进去就行。这样即使不用 Codex写脚本的速度也快很多。6.2 提示词的版本管理提示词也是代码值得管理起来。我会把常用的提示词存成文件按场景分类比如prompt_file_modify.txt、prompt_service_manage.txt。每次用的时候复制出来改改比重新写快。提示词也要迭代。同一个需求第一次生成的代码有问题我会把问题反馈加到提示词里下次生成就更准。比如第一次忘了要求幂等第二次就在提示词里加上脚本必须幂等重复执行不报错。6.3 人工审校不可省略最后强调一点AI 生成的运维脚本人工审校这一步绝对不能省。我见过有人直接把 Codex 生成的脚本扔到生产上跑结果因为一个路径写错把整个目录的文件都改了。这种事故的代价远高于审校花的那几分钟。审校的时候我重点看这几个地方路径对不对、权限够不够、异常处理全不全、回滚逻辑对不对、日志够不够详细。这五项检查完基本就稳了。6.4 一个延伸思路把脚本变成定时任务脚本写好了下一步往往是让它定时跑。用 crontab 就行但有几个细节要注意。# 每天凌晨 2 点执行 0 2 * * * /usr/local/bin/python3 /opt/scripts/modify_conf.py /etc/nginx/conf.d/ /var/log/modify_conf.log 21第一用绝对路径crontab 的环境变量和登录 shell 不一样相对路径会找不到。第二把输出重定向到日志文件方便排查。第三脚本里要有锁机制防止上一次没跑完下一次又启动。可以用文件锁import fcntl lock_file /tmp/modify_conf.lock with open(lock_file, w) as f: try: fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) except BlockingIOError: logger.error(上一次执行还没结束退出) sys.exit(1) # 执行主逻辑这个锁的逻辑 Codex 也能生成但需要你在提示词里明确要求防止并发执行。我在实际使用中的体会是Codex 这类工具最大的价值不是替代你写代码而是把你从从零开始的困境里拉出来让你把精力集中在真正需要判断的地方——环境适配、异常处理、安全边界。脚本的骨架交给它灵魂还是得自己注入。踩过几次坑之后我现在用 Codex 写运维脚本的流程已经很顺了描述需求、生成初版、审校修改、测试验证、上生产。整个过程从以前的一两个小时压缩到二三十分钟而且因为有了模板和提示词积累越来越快。如果你还没试过建议从一个简单的文件修改脚本开始跑通一遍你就能感受到这种工作方式的差异了。
阅读完成 · 觉得有帮助?
咨询建站