ChatGPT学习之旅 (9) 系统运维小助手
大家好,我是Edison。
上一篇我们写了一个单元测试助手的prompt,它帮我们写一些我们开发者不太愿意编写的单元测试代码,在我最近一个月的实践中我再也没有手写过单元测试,更多地只是在AI生成的代码基础上做微调。
这一篇,给大家分享下我在日常的系统开发和运维工作中的小任务是如何通过ChatGPT来解决的。可以毫不犹豫地说,它就是我的系统运维小助手。
场景1:辅助数据库运维脚本生成
在我们日常的运维工作中,有一部分工作需要通过DML语句在MongoDB中作查询和更新操作,鉴于团队对MongoDB的使用熟悉程度较低,原有的SQL知识储备不太够用,因此在生成MQL语句时往往存在较高的搜索和试错成本。因此,我们想要使用ChatGPT来帮助我们抹平这个gap,提高MQL生成的效率。
这里我们基于上一篇中提到的单元测试助手的提示词,稍作修改,形成我们的数据库运维脚本(DML/DDL)助手的提示词,如下所示,经过一些测试在GPT4-o模型下效果真的不错,建议收藏!
你是一位专业的数据库运维助理@Database Maintaince Master,我会输入相关的请求,你将根据`preferences`、`instruction`和`rules`来协助我高效生成数据库运维语句。 初始回复:“ **Hi I'm Database Maintaince Master,Created by Edison Zhou,V1.0,20230802** ### ⚙️ Preferences: - 🌍 db: <> else MongoDB ### 🤖 Menu: 请使用表格输出支持的`instructions`和对应名称,不需要解释具体含义,也不需要显示这句话: --- 请指出你的数据库产品名称,E.g.:/dbMongoDB。 回复1保持默认。 ” `preferences` /db:<数据库产品偏好,默认为MongoDB> `instructions` /tables:涉及到的数据库表的模型定义 用户输入一些涉及到的表结构如DDL或 JSON等,请按以下模版输出Table Structure相关信息: ## 📝Tables: 用表格输出:**Table Name**,只需输出TableName,无需再输出Structure信息; /req:即requirement,运维语句的具体需求描述,支持列表描述 结合之前的tables和用户指定的数据库产品,直接生成运维语句,比如SQL 或 MQL等,无需再用表格输出。 /fix: 补充完善的具体需求描述 结合之前的reqs命令给出的需求,用户会补充一些完善的需求点,请根据补充需求完善上一次给出的答案。 /help:输出支持的指令指引 `rules` - 请使用用户设置的偏好的数据库产品输出对应的数据库语句,比如MongoDB对应MQL - 请一步一步思考,不需要解释代码,如果有错误,用户会纠正你
对话示例
假设我们有一个基于.NET 6开发的Web API项目,其使用了MongoDB作为存储,定义了一个名为 TransferTasks 的集合(表),它会存储所有需要进行传输同步的任务,由CronJob触发同步,并更新其中的Transmission记录的状态,其结构如下(部分数据已脱敏):
{ "_id" : ObjectId("65828fe9012a189a9e301ac5"), "OrderNumber" : "90400150", "Transmissions" : [ { "_id" : ObjectId("65828fe9012a189a9e301abb"), "Type" : 1, "Content" : "{\"OrderNumber\":\"XX000150\",\"Part\":{\"Id\":null,\"ItemReference\":null,\"TransferContextId\":\"0000000110XXXX\"},\"Quantity\":100,\"ReleaseDate\":\"2022-09-23T03:25:13\",\"Status\":\"IN_WORK\",\"WorkCenters\":null,\"ErpProperties\":{\"EuropeanArticleNumber\":\"XXX5515070856\",\"RepairTime\":0}}", "Status" : -1, "Retries" : 5, "CreatedTime" : ISODate("2023-12-20T06:55:37.747+0000"), "FinishedTime" : null, "ErrorMessage" : "This task has beenexceed standard max retry counter!" }, { "_id" : ObjectId("65828ff2012a189a9e301adb"), "Type" : 2, "Content" : "{\"Status\":\"ON_HOLD\"}", "Status" : -1, "Retries" : 5, "CreatedTime" : ISODate("2023-12-20T06:55:46.156+0000"), "FinishedTime" : null, "ErrorMessage" : "This task has beenexceed standard max retry counter!" } ] }
我们需要对这个集合做一些查询 和 更新操作,需求如下:
-
找到所有同步失败且重试次数超过3次的Orders
-
更新单个Order的重试次数为0
-
批量更新所有同步失败Orders的重试次数为0
接下来,我们就通过几次会话,让GPT帮我们生成准确的MQL语句。
首先:告诉GPT你的表结构
/tables MongoDB集合名称:TransferTasks { "_id" : ObjectId("65828fe9012a189a9e301ac5"), "OrderNumber" : "90400150", "Transmissions" : [ { "_id" : ObjectId("65828fe9012a189a9e301abb"), "Type" : 1, "Content" : "{\"OrderNumber\":\"XX000150\",\"Part\":{\"Id\":null,\"ItemReference\":null,\"TransferContextId\":\"0000000110XXXX\"},\"Quantity\":100,\"ReleaseDate\":\"2022-09-23T03:25:13\",\"Status\":\"IN_WORK\",\"WorkCenters\":null,\"ErpProperties\":{\"EuropeanArticleNumber\":\"XXX5515070856\",\"RepairTime\":0}}", "Status" : -1, "Retries" : 5, "CreatedTime" : ISODate("2023-12-20T06:55:37.747+0000"), "FinishedTime" : null, "ErrorMessage" : "This task has beenexceed standard max retry counter!" }, { "_id" : ObjectId("65828ff2012a189a9e301adb"), "Type" : 2, "Content" : "{\"Status\":\"ON_HOLD\"}", "Status" : -1, "Retries" : 5, "CreatedTime" : ISODate("2023-12-20T06:55:46.156+0000"), "FinishedTime" : null, "ErrorMessage" : "This task has beenexceed standard max retry counter!" } ] }
需求1:找到所有同步失败且重试次数超过3次的Orders
/req (1)请帮我找到所有同步失败且重试次数大于3次的Type=1的记录; (2)根据OrderNumber降序排列; (3)返回数据只包括OrderNumber和Transmissions
通过清晰明确的需求描述,告诉GPT你的MQL需求,GPT回复如下:
db.TransferTasks.find( { "Transmissions": { $elemMatch: { "Type": 1, "Status": -1, "Retries": { $gt: 3} } } }, { "OrderNumber": 1, "Transmissions": 1 } ).sort({"OrderNumber": -1 });
将其复制到使用的MongoDB客户端工具,经测试,是准确可用的。不过,我不想要输出它自带的_id,我也不知道咋写,于是我给GPT反馈一下:
/fix 请帮我把_id也排除掉,不返回它
GPT的回答十分准确:
db.TransferTasks.find( { "Transmissions": { $elemMatch: { "Type": 1, "Status": -1, "Retries": { $gt: 3} } } }, { "_id": 0, "OrderNumber": 1, "Transmissions": 1 } ).sort({"OrderNumber": -1 });
但是,由于Transmissions是一个集合字段,我只想看每个Transmission的Type和Content字段,所以我们可以再跟GPT反馈一下:
/fix 请修改上面的查询,只返回Transmissons中的Type和Content字段
GPT的回答十分准确,在我们不太熟悉的查询语句格式方面游刃有余地帮我们增加了projection投影操作的设置:
db.TransferTasks.find( { "Transmissions": { $elemMatch: { "Type": 1, "Status": -1, "Retries": { $gt: 3} } } }, { "_id": 0, "OrderNumber": 1, "Transmissions.Type": 1, "Transmissions.Content":1 } ).sort( { "OrderNumber": -1 } )
需求2:找到单个Order同步失败且重试次数超过3次的Orders
有了上面的基础,我们再提一个需求给GPT,这个需求比较简单,我们可以整理为一句话直接告诉GPT:
/req 将OrderNumber=9060440的记录的Type=1的Transmission的重试次数更新为0
GPT的回答一如既往的稳定,经测试直接可用:
db.TransferTasks.updateMany( { "OrderNumber":"9060440", "Transmissions.Type": 1 }, { $set: {"Transmissions.$[elem].Retries": 0 } }, { arrayFilters: [{ "elem.Type":1 }] } );
需求3:批量更新所有同步失败Orders的重试次数为0
有时候我们需要做一个批量更新操作,它涉及到一个集合字段中的某个元素的某个字段的定位,我不太会写这种场景的MQL,还是交给GPT生成吧。
/req 更新所有Transmissions中同步失败(Status=-1)的记录的重试次数(Retries)为0
GPT的回答稳得一笔,我get到了updateMany的用法 以及 $[elem]的用法,太棒了:
db.TransferTasks.updateMany( { "Transmissions.Status": -1}, { $set: {"Transmissions.$[elem].Retries": 0 } }, { arrayFilters: [{ "elem.Status":-1 }] } );
举一反三:PostgreSQL数据库
使用这个prompt模版,不仅可以协助我们写MongoDB的运维语句,还可以是其他数据库产品,比如近期我们在开始使用postgresql数据库,完全可以复用这个模板。
下面是一个使用示例,供你参考:
# 告诉GPT切换到pgsql /db postgresql # 告诉GPT table的表结构 INSERT INTO public."ScrapTickets" ("Id", "Author", "ProductName", "ProductVersion", "Quantity", "Status", "CostCenter", "CoOrder", "WorkOrderName", "SourceOperationName", "DestinationOperationName", "DefectCode", "DefectCause", "ManufacturerId", "Comment", "CostType", "ParentTicketId", "CreatedTime", "UpdateUser", "UpdatedTime", "DestinationOperationSequence", "SourceOperationSequence", "Guid", "DefectDescription") VALUES(100000005, 'edison','XXX00369907', '006 / AB',6, 0, 'XXX102', '937XX80', '905XX625','PROXX_6', 'PRODASS_3','0322', 'AUT', 'ABR', 'TEST CASE', 0, NULL, '2024-07-29 14:47:51.319', NULL, NULL, 30, 80, '9176cb8e-d5c8-4fe4-bd72-22dca9f449e9'::uuid, 'assembly automatic'); INSERT INTO public."ScrapTicketRelatedFids" ("Fid", "ScrapTicketId") VALUES('SBX500066', 100000005); # 告诉GPT你的需求 /req (1) 请帮我查出所有CostCenter=XXX102的ScrapTickets (2) 同时查出关联的ScrapTicketRelatedFids表的数据 (3) 根据CreatedTime降序排序 # 补充完善你的需求 /fix 修改上面的查询,不显示UpdateUser和UpdatedTime
场景1总结:数据库运维助手
适用场景:对于自己不太熟悉的数据库产品如MongoDB或PgSQL,已有的SQL语句知识不足以支持你快速查询和搜索,然后又不太容易通过搜索引擎快速得到答案;
尬聊要点:首先你需要给够有效的表结构定义,例如MongoDB直接给JSON文档,PgSQL或MSSQL则直接给INSERT语句即可;其次简单的需求能一句话描述就一句话,不能的就分开罗列要点,避免凌乱;
最后,GPT可能是“一本正经的胡说八道”,因此必要的验证必不可少,建议在开发测试环境先验证一下!
场景2:CI场景中Dockerfile的编写
在我们日常的CI运维小任务中,为某个应用编写Dockerfile是一个常见的操作。然而,对于很多非IT编码背景的同事(例如OT工程师),则会踩很多的坑。因此,如果有一个标准的prompt模板,能够指导他们生成一个较为标准的Dockerfile,他们就能够集成CI流水线,完成Docker镜像的打包和推送。
这里我们仍然基于之前的prompt模板,稍微改了一改:
你是一位专业的DevOps运维助理,请帮我生成一个dockerfile文件,我的要求如下: - 我的应用是一个Python编写的Web应用 - 请基于registry.yourcompany.com/images/python3:ubi9-1-14作为基础镜像创建 - 请使用非root用户启动应用,建议添加一个1001的user - 请使用8080端口作为运行端口 - 请设置时区为Asia/Chongqing 请遵循以下原则: - 请添加必要的LABEL给每个dockerfile,例如:com.yourcompany.project,com.yourcompany.component, com.yourcompany.git, com.yourcompany.doc - 请一步一步思考,无需解释你的思路,但需要解释你生成的代码,便于用户阅读
这里我们没有做参数化处理,可以直接修改默认要求,例如:你的应用是一个ASP.NET 6编写的Web应用。
GPT的回答还是较为准确的,我们只需要在其中修改一些变量名 或 添加一些GPT不太好处理的环境变量 即可。
以下是一个符合您需求的Dockerfile文件: # 使用指定的基础镜像 FROM registry.yourcompany.com/images/python3:ubi9-1-14 # 设置必要的LABEL LABEL com.yourcompany.project="YourProjectName" LABEL com.yourcompany.component="YourComponentName" LABEL com.yourcompany.git="https://yourcompany.git/repo" LABEL com.yourcompany.doc="https://yourcompany.documentation.link" # 设置时区为Asia/Chongqing ENV TZ=Asia/Chongqing RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 创建非root用户并设置UID为1001 RUN useradd -u 1001 -m appuser # 复制应用文件到镜像 COPY . /app # 设置工作目录 WORKDIR /app # 安装应用依赖 RUN pip install --no-cache-dir -r requirements.txt # 切换到非root用户 USER appuser # 暴露应用运行端口 EXPOSE 8080 # 启动应用 CMD ["python", "app.py"] 解释: FROM registry.yourcompany.com/images/python3:ubi9-1-14: 使用指定的基础镜像。 LABEL: 添加项目、组件、Git仓库和文档链接的标签。 ENV TZ=Asia/Chongqing: 设置环境变量TZ为Asia/Chongqing。 RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone: 设置系统时区。 RUN useradd -u 1001 -m appuser: 创建UID为1001的非root用户appuser。 COPY . /app: 复制当前目录下的所有文件到镜像中的/app目录。 WORKDIR /app: 设置工作目录为/app。 RUN pip install --no-cache-dir -r requirements.txt: 安装Python依赖。 USER appuser: 切换到非root用户appuser。 EXPOSE 8080: 暴露8080端口。 CMD ["python", "app.py"]: 启动应用。
场景3:CD场景中Helm Values的编写
在我们日常的CD运维小任务中,为某个应用编写Helm Values(values.yml)也是一个常见的操作,其中涉及到一些重复性的工作,较为耗费时间。比如:我们需要将应用配置文件(例如:appsetting.json)以环境变量的方式写到values.yml中来覆盖默认的应用配置文件中的值,以便可以独立地在values.yml中维护而不需更改源代码仓库。
于是,我们还是基于之前的prompt模板,给这个场景也写了一个prompt让GPT帮我们根据我们的需求快速生成这些环境变量的设置,我们只需要微调和小改一下即可,节省了很多时间(取决于你的配置文件中的配置项个数)。
你是一位专业的DevOps运维助理,请根据我给出的json配置文件自动生成HelmValues中的environmentVariables内容。 你的初始回复:”Hi, I'm your DevOps master. Please input your config.json here.",然后无需输出其他内容,等待用户输入。 `sample` 下面是一个映射关系的示例: { "db": { "server":"192.168.1.100", "database":"TEST_DB", } } 上面json配置对应的environmentVariables为: environmentVariables: # Database Settings - name: db__server value:"HOST01.yourcompany.net" - name: db__database value: "YOUR_DB_NAME" `rules`: - 请基于我给你的values.yml示例作为参考,将其补充完整 - 请给所有带"***************"号的敏感信息配置为从sercet中引用某个key - 请在最后添加三个默认的网络配置的环境变量:HTTP_PROXY,HTTPS_PROXY 和 NO_PROXY,它们分别来自同一个configmap中的HTTP_PROXY、HTTPS_PROXY 和 NO_PROXY - 请一步一步思考,无需解释你的思路,但需要解释你生成的代码,便于用户阅读
GPT首先会回复你,并等待你给出你的json格式的配置文件:
Hi, I'm your DevOps master. Please input your config.json here.
然后我们给出一个示例的json配置文件给GPT:
{ "db": { "server": "192.168.1.100", "database": "QA_IT_Support", "username": "***************", "password": "***************" }, "msg": { "email_api": "https://your-service.your-company.com.cn/email-center/api/v1/email/send", "headers_secret": "***************", "headers_tenant": "***************", "from": "notification.itsystem.cn@your-comapny.com" }, "awx":{ "baseurl": "https://awx.your-comapny.com/api/v2/jobs/", "headers_authorization": "***************" } }
GPT就可以迅速把这个纯纯的体力劳动活给你做好一大半了,你只需要修改一下configmap 或 secret的key引用即可:
environmentVariables: # Database Settings - name: db__server value: "192.168.1.100" - name: db__database value: "QA_IT_Support" - name: db__username valueFrom: secretKeyRef: name: your-secret-name key: db_username - name: db__password valueFrom: secretKeyRef: name: your-secret-name key: db_password # Message Service Settings - name: msg__email_api value: "https://your-service.your-company.com.cn/email-center/api/v1/email/send" - name: msg__headers_secret valueFrom: secretKeyRef: name: your-secret-name key: msg_headers_secret - name: msg__headers_tenant valueFrom: secretKeyRef: name: your-secret-name key: msg_headers_tenant - name: msg__from value: "notification.itsystem.cn@your-comapny.com" # AWX Settings - name: awx__baseurl value: "https://awx.your-comapny.com/api/v2/jobs/" - name: awx__headers_authorization valueFrom: secretKeyRef: name: your-secret-name key: awx_headers_authorization # Proxy Settings - name: HTTP_PROXY valueFrom: configMapKeyRef: name: your-configmap-name key: HTTP_PROXY - name: HTTPS_PROXY valueFrom: configMapKeyRef: name: your-configmap-name key: HTTPS_PROXY - name: NO_PROXY valueFrom: configMapKeyRef: name: your-configmap-name key: NO_PROXY
场景2和3的总结:CICD运维小任务助手
适用场景:
-
对于自己很熟悉,但是纯重复性的体力劳动工作,比如Helm Values编写;
-
又或者是对于自己不熟悉,但是有迹可循的脚本编写工作,比如Dockerfile编写。
尬聊要点:
角色 和 需求 描述清晰到位,必要时要给出一个示例给GPT学习,例如在Helm Values中的映射关系实例。
小结
本篇,我们了解了如何基于ChatGPT中来帮助我们解决一些日常运维工作中的一些小任务,相信通过抛砖引玉,你已经了解了如何通过GPT来解决一些日常你不太容易记得住 且 重复性的体力劳动,解放你的部分生产力。
近期,我们也在使用GitHub Copilot作为代码的副驾驶来给我们的Dev开发过程中探索提效体验,后续有机会也希望和大家分享一下GitHub Copilot的使用体会。
本文工具
本文使用的工作:Azure OpenAI Studio
本文示例大模型版本:gpt-4o