自动化运维入门:Shell脚本、Ansible与CI/CD落地指南

📅 发布时间:2026/8/31 5:06:35
自动化运维入门:Shell脚本、Ansible与CI/CD落地指南 你第一次被拉去给服务器发版本大概就是这个场景先 ssh 登录用 scp 把包拷上去解压改配置重启服务再打开日志看有没有报错。如果只有一台机器这套动作还能忍。当成批机器出现或者版本号打错需要回滚时你就会意识到真正的问题不是命令记得不够多而是整个流程没有固化下来。这段时间网上很多教程都会用一个非常有吸引力的标题一口气学完 Ansible Shell CI/CD。作为不是“一口气”的过来人我更想给你一条能落地、不劝退的路径。很多人学自动化运维第一反应是去背 Ansible 模块或者背几个 Shell 脚本模板。但真正值得你花时间的是建立一套理解系统的手感手动操作是怎么发生的命令的背后在系统里动了什么哪些动作可以被标准化哪些动作需要保留人判断的空间。Shell、Ansible、CI/CD 不是三座孤立的山而是同一套自动化能力的三层楼梯。文章后面会按这个顺序展开先讲为什么自动化运维真正要解决的其实是“重复劳动的结构”再分别讲 Shell、Ansible、CI/CD 各自应该学到什么程度最后给出一条从零开始能每天推进的学习路线。1. 先想清楚自动化运维到底在解决什么重复劳动很多人把“自动化”理解成“把命令写成一键执行”。这个理解不算错但容易把人带偏。自动化运维真正解决的不是“帮你少敲几条命令”而是把那些高频、易错、流程固定的操作变成可以反复执行、结果可控、能被记录和审计的标准动作。要做到这一点先要能分辨日常工作中哪些重复值得自动化哪些重复只是表面雷同。1.1 日常运维里最常出现的三类重复劳动第一类是登录和传输的重复。每次发布都要 ssh 上去用 scp 或 rsync 传文件然后逐台机器检查结果。机器少的时候勉强能接受机器一多你花在“登录切换”上的精力远大于真正做变更的精力。第二类是配置和命令的重复。比如新机器上线你要装 nginx写配置调 firewalld开目录权限再启动服务。这一串操作有固定顺序有固定参数但如果你每次都手动敲很容易在某一台机器上漏掉一个步骤。漏掉之后问题不会立刻爆发而是等到某次业务发布时才暴露出来。第三类是发布和验证的重复。代码改完构建产物拿到手手动推到测试环境测完再推到生产。中间每一步都依赖人来记住“这次该用哪个版本”“该推到哪台机器”“验证命令是什么”。一旦换一个人执行或者半夜值班手忙脚乱流程就会走样。自动化不是把这些操作全部取消而是把它们从“靠人记、靠手敲、靠运气”变成“靠脚本、靠配置、靠流水线”。这也是为什么入门阶段不能只盯着一个工具学而是要同时理解 Shell、Ansible、CI/CD 各自在解决哪一层。1.2 Shell、Ansible、CI/CD 各负责哪一层可以用下面这张表快速建立一个坐标工具层核心作用典型动作适合解决的问题Shell单机操作的语言化写循环、判条件、处理文件、调用系统命令把一串手工命令固化成脚本Ansible多机编排的语言化写清单、写 Playbook、调用模块、保证幂等把同样的事快速推广到一批机器CI/CD流程触发的自动化代码提交后自动构建、测试、部署把发布过程变成可重复、可回滚的流水线这里有一个重要判断Shell 是一切的基础但只靠 Shell 不够。Ansible 能让你把“在一台机器上会做的事”复制到整个集群但如果你想每次代码合并后都自动跑一遍就需要 CI/CD 来触发。三者不是“学会一个就不用学另一个”而是越往后越靠近“流程控制”。所以不要被“一口气学完”这个说法吓到。你不是要一口气掌握所有细节而是要先把这条链路的整体轮廓摸清楚。2. 不要跳级Shell 是理解系统行为的“第一语言”我见过不少新手直接跳到 Ansible结果遇到问题完全不知道从哪里排查。原因很简单Ansible 在目标机器上执行的操作本质上还是通过 SSH 调用系统命令和模块。如果你不理解 Linux 里文件权限、进程、用户、服务这些基本概念就很难判断一个 Playbook 执行后到底是真是假地成功了。Shell 的价值不只是“会写循环”。它更像你认识 Linux 系统的那双眼睛。你会开始理解环境变量怎么传递退出码代表什么管道和重定向怎么组装为什么同一个命令在交互式终端里能跑写成脚本后却报错。2.1 第一个可复用脚本把“手动操作”翻译成“脚本逻辑”先不要急着写复杂函数。建议从你每天都在做的手动操作开始把它翻译成脚本。比如你经常要把一个压缩包部署到两台机器上手动操作是这样拷贝包到远程服务器。解压到指定目录。重启服务。检查服务状态。用 Shell 写一个最小版本大概长这样#!/bin/bash set -euo pipefail HOSTSweb01 web02 APP_DIR/opt/demo TARBALL./demo-1.2.0.tar.gz for host in $HOSTS; do echo deploying to ${host} scp $TARBALL ${host}:${APP_DIR}/ ssh $host cd ${APP_DIR} tar xzf demo-1.2.0.tar.gz systemctl restart demo ssh $host systemctl is-active demo done这个脚本并不复杂但它已经包含了几个关键点set -euo pipefail让脚本在遇到未定义变量或管道错误时停下来而不是继续跑出更离谱的结果。for host in $HOSTS这是 Shell 脚本里最常见的批量思想。最后的systemctl is-active demo不是发完命令就结束还要做一次验证。实际落地时脚本里还会加上日志、超时、失败重试等逻辑。但新手阶段先不要贪多先把“手动操作”翻译成“脚本逻辑”跑通一次再慢慢加固。2.2 脚本运行失败先不要瞎猜看退出码Shell 脚本最容易让人困惑的问题就是“明明命令单独执行没问题写成脚本就失败”。这时候不要瞎猜先看退出码。每条命令执行完都会返回一个数字0 表示成功非 0 表示失败。脚本里可以在命令后面立刻用$?看到它。systemctl restart demo if [ $? -ne 0 ]; then echo restart failed 2 exit 1 fi但更常见的问题是某个命令失败了脚本没有停下来后续命令基于错误状态继续执行。所以新手写脚本时建议先养成用set -e的习惯让脚本在遇到非 0 退出码时自动退出。这不是万能的很多命令即使“失败”也可能返回 0所以还要配合日志判断。另外进入自动化运维领域后你会频繁看到这类求助“Linux 用 shell 重命名文件怎么写”“shell 脚本 for 循环怎么遍历目录”。这些问题本身不难但底层都指向同一个能力你能不能在脑子里把“要做什么”转成“系统如何执行”。这个能力只能在不断写脚本、跑脚本、拆脚本的过程中获得。3. 用 Ansible 把“在一台机器上会做的事”推广到一批机器Shell 脚本解决单机流程Ansible 解决的是“一批机器”的问题。它的核心思路是你在一台控制机上定义要做什么SSH 连到目标机器去执行。目标机器不需要预装 agent控制机也不需要长时间保持连接执行完就断开。这让 Ansible 非常适合从几台到几百台服务器的运维场景。但 Ansible 真正的杀手级特性不是“免安装 agent”而是“幂等”。同样的 Playbook 跑一遍和跑十遍最终状态应该是一致的。这和你手动执行命令有本质区别手动操作时你可能会因为重复执行而把服务重启两次或者把配置重复追加到文件末尾Ansible 的模块会先检查当前状态只有状态不一致时才执行变更。3.1 从 ssh 循环升级到 Ansible ad-hoc如果你看过很多人写的批量部署脚本早期版本通常是这样的for host in $(cat hosts); do ssh $host yum install -y nginx done这样的循环当然能跑但问题很多一台机器失败怎么跳过怎么保证不会重复安装怎么统一处理不同系统的包管理器Ansible 把这个问题抽象成了更干净的写法。先安装 Ansible然后写好 inventory 文件[web] web01 ansible_host192.168.1.11 web02 ansible_host192.168.1.12接着可以直接用 ad-hoc 命令验证连通性ansible web -m ping然后把安装命令也改成模块化写法ansible web -m yum -a namenginx statepresent这里yum是针对 CentOS/RHEL 的模块如果你在 Ubuntu 上则用apt模块并把包名写成nginx。你不再需要关心“我到底是用 yum 还是 apt”Ansible 模块会尽量帮你屏蔽差异但前提是目标系统的发行版能被模块正确识别。3.2 一个最小 Playbook 怎么读怎么写ad-hoc 适合临时验证真正要沉淀和复用建议写 Playbook。下面是一个很常见的场景给 Web 服务器安装 nginx并复制一份站点配置。- hosts: web become: true tasks: - name: Install nginx ansible.builtin.apt: name: nginx state: present - name: Copy site config ansible.builtin.copy: src: ./default.conf dest: /etc/nginx/sites-available/default.conf mode: 0644 notify: restart nginx handlers: - name: restart nginx ansible.builtin.service: name: nginx state: restarted新手第一次看这个文件眼睛容易盯着 YAML 缩进不放。确实YAML 缩进错误是最高频的问题之一。但比缩进更重要的是理解 Playbook 的四个层级hosts告诉 Ansible 这个任务跑在哪些机器上。become是否需要提权通常对应 sudo。tasks真正要执行的动作。handlers被 notify 触发后执行的动作。copy模块会把本地./default.conf复制到目标机器指定位置mode: 0644控制权限。你可能在热搜里见过“Ansible 复制文件到所有节点并授权 777 权限”这种说法。这里要特别提醒777 意味着所有用户都有读写执行权限如果只在实验环境里自娱自乐问题不大一旦放进真实业务这就是一个非常危险的权限位。复制配置文件时建议按文件用途设置0644或0640可执行文件才考虑0755。不要因为别人写了 777 就照抄。notify: restart nginx的意思是只有当copy模块实际改变了文件内容时才会触发重启。如果配置没有变化Ansible 不会重启服务。这就是幂等带来的好处之一。3.3 为什么幂等性如此重要很多人第一次意识到幂等的重要性是在反复跑 Playbook 之后。假设你手动发布十次每次都会重复执行“重启服务”这个动作但 Ansible 会先看这个服务当前是否已经处于运行状态如果状态符合要求就什么都不做。幂等带来两个直接收益一是你可以放心地反复执行同一个 Playbook不用担心操作叠加产生副作用二是你可以把 Playbook 写进 CI/CD 流水线在每次代码提交后自动执行因为流水线不会因为“上次已经执行过”就报错也不会因为重复执行而把系统搞乱。但也要说清楚边界Ansible 的幂等并非魔法而是依赖模块的实现质量。比如shell和command模块只是原样执行命令默认不会做状态检查用它们写出来的任务往往不幂等。copy、file、service这些模块设计得比较好会先做状态判断。所以写 Playbook 时能使用专用模块就不要用shell硬扛。4. CI/CD 不是新工具而是把“验证-构建-发布”串成一条自动流水线如果你已经能写 Shell 脚本也能用 Ansible 管理一批机器那你的“自动化能力”已经超过不少人了。但这时候还会遇到一个新问题这些脚本和 Playbook 全靠手动触发一旦团队里有人忘记执行或者执行时机不对发布流程还是可能出问题。CI/CD 解决的就是这件事把“验证-构建-发布”这个过程变成一条由事件自动触发的流水线。CI 阶段负责持续集成简单说就是每次代码变更后自动做静态检查、单元测试、构建产物CD 阶段负责持续交付或持续部署把构建好的产物自动推到目标环境。4.1 一次合并引发的痛点不只是“手动发布太慢”假设一个小团队前端和后端在同一个仓库里开发。某天有人合并代码改动了一个配置项但没改测试用例。如果没有 CI合并后直到部署到测试环境问题可能都发现不了。如果有 CI代码一提交流水线就会自动拉起一个干净环境跑测试和构建一旦失败团队立刻收到通知。这就是 CI 的核心价值让“发现问题”的时间点尽量提前。错误越早被发现修复成本越低。你在学习阶段不需要搞很复杂的流水线但一定要建立“提交代码后必须经过自动检查”的直觉。CD 阶段则是把“发布”变成一次标准动作。以前发布可能是“登录服务器、备份、替换文件、重启、看日志”现在发布变成“触发流水线流水线里调用 Ansible Playbook 去完成这些动作”。你依然可以看日志但执行主体变成了工具而不是某个人的手工操作。4.2 一个最小可用的流水线示例这里以 GitLab CI 为例因为它的配置是 YAML和 Ansible Playbook 有相近的阅读体验适合入门。stages: - test - deploy test-job: stage: test script: - shellcheck deploy.sh - ansible-playbook --syntax-check playbook.yml deploy-job: stage: deploy script: - ansible-playbook -i prod playbook.yml only: - main这个例子非常朴素但它已经包含了一个最小流水线该有的部分stages定义阶段顺序先测试再部署。test-job在测试阶段执行两个检查shellcheck deploy.sh是 Shell 脚本静态检查工具能发现常见语法问题ansible-playbook --syntax-check只检查 Playbook 语法不真正执行。deploy-job在部署阶段调用 Ansible真正对目标机器执行 Playbook。only: - main只有在 main 分支触发时才执行部署。实际项目里你可能还要加入构建步骤比如前端打包npm run build后端编译mvn package。但作为入门先跑通“检查语法 → 部署到测试机”这个最小闭环比一开始就追求各种插件和缓存机制更重要。4.3 先有交付流程再谈平台选型很多新人纠结的问题是“Jenkins、GitLab CI、GitHub Actions 到底选哪个”。其实你只要理解流水线的基本概念换平台只是换语法。Jenkins 有 Pipeline 脚本GitLab CI 有.gitlab-ci.ymlGitHub Actions 有 workflow 文件。它们的底层逻辑都一样定义触发条件、定义阶段、定义每个阶段要执行的命令。所以建议选型时不要过度纠结。你在哪个平台管理代码就先学哪个平台的 CI。如果你只是自己练手GitLab 的免费版和 GitHub 都可以跑简单流水线。关键是先跑通一次“代码提交后自动执行 Playbook”的完整链路。这个链路一旦跑通你对 CI/CD 的理解就已经超过很多人了。另一个容易被忽略的点是“环境变量”。CI 流水线通常运行在一个干净环境中很多你终端里默认存在的环境变量在流水线里是不存在的。比如你本地有JAVA_HOME但流水线的 runner 不一定有。所以在流水线里执行命令时最好在脚本开头显式指定关键路径或者通过 CI 平台的环境变量配置传入。5. 从零开始的学习路线与实验环境建议网上很多教程会把 Ansible、Shell、CI/CD 拆成三门独立课程来教每门课都从头到尾讲一遍。但如果你是想入行运维或者已经在做运维相关工作但流程比较原始我更建议走一条“主线优先、细节按需”的路线。这条路线不追求一次学完而是每走一步都能看到成果。5.1 实验环境怎么搭最省心你不需要一开始就有一堆服务器。常见做法是准备一台 Linux 控制机再加一到两台目标机。可以用虚拟机也可以用云服务器。如果只有一台机器还可以用容器来模拟多节点但容器在 systemd、服务启动等方面的行为会和真实服务器有一些差异遇到问题时先要有这个意识。Ansible 本身建议装在控制机上目标机只需要能通过 SSH 访问即可。所以环境准备的核心其实是两件事一是让控制机能够免密 SSH 登录目标机二是确认目标机的用户名和权限能执行需要提权的操作。下面是一个常见的准备顺序安装一台 Linux 虚拟机建议用 CentOS Stream、Ubuntu Server 或 Debian 这类常见发行版。克隆快照或复制出第二台虚拟机用来当目标机。在控制机上生成 SSH 密钥ssh-keygen -t ed25519。把公钥复制到目标机ssh-copy-id usertarget_ip。安装 Ansible可以用包管理器也可以pip install ansible看你的环境习惯。写一个最小的hosts文件先执行ansible all -m ping确认连通。这一步看起来很基础但很多人会在 SSH 免密登录上卡住。免密不生效时先确认控制机用的是哪个用户密钥是否放在了~/.ssh/下目标机是否允许该用户密钥登录。不要一上来就改sshd_config更多时候是文件权限或用户写错了。5.2 学习顺序和阶段自检清单下面这条学习路线适合用两到四周推进而不是一天之内赶完阶段学习重点自检方法Linux 基础文件系统、用户权限、进程、服务、日志能独立完成用户创建、权限修改、服务重启、日志查询Shell 脚本变量、循环、判断、函数、退出码能把手动发布流程写成脚本并能用set -e避免失败后继续执行Ansibleinventory、模块、Playbook、handlers、幂等能写一个 Playbook 完成 nginx 安装和配置复制重复执行结果一致CI/CD流水线阶段、触发条件、环境变量能让代码提交后自动执行 Shell 检查或 Ansible Playbook工程化日志集中、权限收敛、失败重试、回滚能在 Playbook 里加日志输出和失败判断能设计简单回滚动作每个阶段的自检不是“我好像看懂了”而是要真正运行一遍。尤其是 Ansible 和 CI/CD如果你没有跑过完整流程很容易出现“照着文档写没问题一到自己环境就找不到文件”的情况。这里还想强调一点学习过程中不要只看“Linux 常用命令大全”这类材料。命令大全能帮你查漏补缺但建立不了操作系统的整体图景。更好的方式是带着任务学比如“给新机器装 nginx 并复制配置”当你为了完成这个任务去查systemctl、scp、chmod时记忆会更牢固。6. 入门之后最容易卡住的问题和一套排查思路不管学 Shell、Ansible 还是 CI/CD你都会发现一个规律真正让人烦躁的不是“不会写”而是“写完了跑不通又不知道从哪里查”。这里分享一套通用排查顺序遇到问题先沿着这条链路走一遍能解决大部分入门阶段的卡点。排查顺序可以固定为五步现象 → 输入 → 环境 → 参数 → 边界。不要一上来就怀疑工具坏了。6.1 Ansible 连不上远程主机时先查什么假设你执行ansible all -m ping报错先不要看 Ansible 文档里那些复杂调优参数按下面顺序来先用ssh userhost手动登录一次确认账号密码或密钥能通。在 Ansible 命令后面加-v看具体报错是“Permission denied”还是“Connection timed out”。“Permission denied”通常说明用户不对或密钥没有被目标机认可。“Connection timed out”则多半是网络不通、防火墙拦截或 SSH 端口不对。最后再检查 inventory 里的ansible_host、ansible_user、ansible_ssh_port是否写对了。很多人习惯不停换 Ansible 配置却忘了先用最基础的 ssh 命令验证链路。实际上 Ansible 只是替你封装了 SSH如果 SSH 本身不通Ansible 怎么调都白搭。6.2 Playbook 跑完但结果不对怎么一步步定位另一种常见情况是Playbook 显示成功但目标机器上的结果和预期不一样。这时候不要只盯着ok或changed的绿色输出要看更细的信息。先执行ansible-playbook your-playbook.yml --check --diff这条命令会告诉你如果真正执行会改动哪些文件。如果--check模式下没有发现变更但真实执行后却改了东西说明任务里可能用了shell或command这类对状态不敏感的模块。然后再回到目标机器上直接检查文件内容、属主、权限和服务状态。比如复制配置文件的任务登录到目标机后执行ls -l /etc/nginx/sites-available/default.conf cat /etc/nginx/sites-available/default.conf systemctl status nginx从工程经验看Playbook“结果不对”通常有三种原因一是源文件用错了路径导致复制了一模一样的旧文件二是权限位或属主不对服务没有权限读取三是配置文件复制过去了但没有触发服务 reload进程还在用旧配置。你在排查时按“文件是否存在 → 内容是否正确 → 权限是否合适 → 服务是否重新加载”的顺序走基本不会漏。6.3 Shell 脚本和 CI 流水线对“环境变量”的理解差异Shell 新手经常碰到一个诡异问题一个脚本在终端里运行正常放到 cron 里或者 CI 里就跑不了。大多数时候和 PATH 有关。终端登录时会加载.bashrc、.profile把 SDK 路径、工具路径加进 PATH。但 cron 和 CI runner 都是非交互式环境PATH 往往只有系统默认值比如/usr/bin:/bin。解决方法是让脚本不依赖环境里默认的 PATH而是在脚本开头显式声明需要使用的路径或者直接写命令的绝对路径#!/bin/bash export PATH/usr/local/bin:/usr/bin:/bin如果你在 CI 中执行ansible-playbook但 runner 的 PATH 里没有 Ansible 可执行文件也会遇到“command not found”。这时候要么在流水线配置里指定虚拟环境或安装步骤要么在调用命令前把路径加进去。这个问题处理过一次之后你以后写任何自动化脚本都会习惯性地先检查环境变量而不是复制别人的脚本就完事。7. 别急着“一口气学完”先把最小闭环跑通回到开头那个问题你是不是真的需要“一口气学完 Ansible Shell CI/CD”我觉得不需要也不现实。这三个东西不是同一个维度上的知识点一口气学完很容易变成“每个都会一点每个都用不起来”。我更建议你用“最小闭环”的思路来推进。所谓最小闭环就是一条从“手动操作”到“脚本化”再到“流水线化”的最短路径。比如选一个你工作中最常做的部署任务先手动做一遍再写一个 Shell 脚本再把脚本里的部分动作改成 Ansible Playbook最后把整个部署动作挂到 CI 流水线里。每完成一步你都能看到自动化的价值而不是停留在抽象概念里。一个可参考的最小闭环自检框架是你能否不登录目标机器只用一条 Ansible 命令或一个 Playbook 完成任务同一个任务连续执行两遍结果是否稳定如果任务失败你能否通过日志快速定位是哪一步出错你能否让代码提交后自动触发这个流程并在失败时通知到人这四个问题如果都能做到你就已经具备基本的自动化运维能力了。后面再学更复杂的主题时比如动态 inventory、角色拆分、Ansible Vault、多环境流水线都会更顺手。最后想提醒一句自动化不是把“人”从运维里拿掉而是把人的精力从重复劳动里释放出来投入到真正需要判断的事情上。工具能帮你稳定地做一百次同样的事但“这次变更是否需要上线”“某个参数是否要调整”“回滚时机怎么选择”这些仍然需要人来决策。所以入门时你可以从“全网最简单”的标题开始找资料但真正走下去时要依赖的不是标题而是你亲手跑通的那条流水线。当你第一次看到代码提交后测试自动通过Ansible 自动把新版本部署到服务器你会有一种“原来如此”的感觉。那一刻你会明白所谓自动化运维不是把命令背得更多而是把流程设计得让机器替你分担。这不只是一种技能也是一种更省心的工作方式。