不再逐台装Docker:我用Ansible Playbook批量统一服务器环境
前言
新机器少的时候,Docker 一台台装也还能接受;可一旦服务器数量上来,重复安装依赖、添加软件源、启动服务、设置开机自启,再去检查每台机器有没有漏步骤,很快就会变成一件又慢又容易出错的事。更麻烦的是,手工操作久了以后,同一批服务器还可能出现版本和配置不一致。
这次我把 Docker 的安装流程写进 Ansible Playbook,让控制节点通过 SSH 去执行:软件依赖、Docker 仓库、Docker 组件、服务启动和用户组配置都交给剧本统一完成。先在 Ubuntu 控制节点把 Ansible 安装好,再通过 Inventory 管理目标主机。
本地网络里验证成功以后,后半部分继续测试跨网络场景:把目标机的 SSH 端口通过公网 TCP 入口提供给 Ansible,再用同一份 Playbook 远程部署。
不过这篇配置里有两个地方必须先看清:Inventory 使用的是dbservers,Playbook 却写成docker_group;另外正文提到“剧本自动安装 cpolar”,但实际 Playbook 并没有这一任务。新版会保留代码不动,同时把这两个问题明确指出。
这种方式最适合重复性强、步骤固定的基础环境交付。以后再新增服务器,不需要重新翻安装笔记,也不用担心某一台漏掉软件源或开机自启;只要主机清单和变量准备好,就可以继续复用同一套剧本。
1 为什么这类任务适合交给Ansible
如果只是装一台服务器,手动执行命令并不复杂。
真正麻烦的是:
- 机器数量越来越多;
- 同样的步骤要重复执行;
- 某台机器漏了一条命令;
- 某台机器的软件源不同;
- Docker 服务忘了开机自启;
- 新同事接手以后又得重新理解整套安装流程。
Ansible 的价值就在这里:
把运维步骤从“靠人记住”变成“让剧本重复执行”。
它通过 SSH 管理目标主机,被控端不需要额外安装 Ansible Agent。
2 Ansible更适合做“标准化”,而不只是批量执行
Ansible 常见能力包括:
- 配置管理;
- 应用部署;
- 任务自动化;
- IT 编排;
- 批量执行;
- 文件分发;
- 服务管理;
- 用户与权限管理。
它和单纯 Shell 脚本最大的区别之一,是大量模块具备幂等特性。
也就是说:
目标已经符合要求时,再执行一次通常不会重复修改。
不过“Ansible 天然幂等”也不能理解成所有 shell、command、自定义脚本都自动幂等。
真正是否安全重复执行,还要看具体用了什么模块、任务怎么写。
3 先在Ubuntu控制节点安装Ansible
先更新系统:
sudoaptupdatesudoaptupgrade-y然后安装:
aptinstall-yansible这里通过 APT 安装的是:
当前软件源中提供的 Ansible 版本。
更准确地说,不应该简单写成“安装最新版”。
安装完成后查看版本:
ansible--version4 如果没有/etc/ansible目录,就手动创建
先查看:
ls/etc/ansible如果目录不存在,再创建:
mkdir-p/etc/ansible接下来准备 Inventory 和 Ansible 配置。
5 先创建一个本机Inventory做最小验证
写入:
cat>/etc/ansible/hosts<<EOF [local] localhost ansible_connection=local EOF这个配置定义:
[local]
并让 localhost 使用本地连接。
然后创建:
cat>/etc/ansible/ansible.cfg<<EOF [defaults] inventory = /etc/ansible/hosts host_key_checking = False remote_user = root stdout_callback = yaml EOF其中几个参数值得看一下:
inventory:默认主机清单;host_key_checking = False:关闭 SSH 主机指纹检查;remote_user = root:默认远程用户设为 root;stdout_callback = yaml:调整输出格式。
为了实验方便,关闭 Host Key Checking 可以减少第一次连接时的交互。
但正式环境中,这会降低对 SSH 主机身份的校验能力。
6 先用ping模块验证Ansible本身
执行:
ansible localhost-mping这里的ping不是普通 ICMP ping。
它验证的是:
Ansible 能不能正常执行模块并获得响应。
这一步成功以后,再继续配置其他目标主机。
7 准备目标主机Inventory
进入:
cd/etc/ansible然后配置:
[dbservers]192.168.42.146ansible_user=rootansible_port=22ansible_password=*****当前主机组叫:
dbservers
目标主机:
192.168.42.146
连接用户:
root
SSH 端口:
22
这里为了演示直接写了ansible_password。
长期环境不建议把真实密码明文留在 Inventory 文件里。
更合理的方式通常是:
- SSH Key;
- Ansible Vault;
- 受控凭据管理。
8 Playbook负责把Docker安装步骤固化下来
部署文件:
deploy_docker.yml
配置如下:
--- - name: Ansible一键部署Docker环境 hosts: docker_group become: true tasks: - name: 安装yum工具依赖 yum: name: yum-utils state: present - name: 添加Docker yum软件源 yum_repository: name: docker-ce description: Docker CE stable repo baseurl: https://download.docker.com/linux/centos/$releasever/$basearch/stable gpgcheck: yes gpgkey: https://download.docker.com/linux/centos/gpg - name: 安装Docker全套组件 yum: name: - docker-ce - docker-ce-cli - containerd.io state: present - name: 启动docker服务并设置开机自启 systemd: name: docker state: started enabled: yes - name: 将登录用户加入docker组,免sudo操作docker user: name: "{{ ansible_user }}" groups: docker append: true这个 Playbook 做了几件事:
- 安装
yum-utils; - 添加 Docker CE 软件源;
- 安装
docker-ce、docker-ce-cli、containerd.io; - 启动 Docker;
- 设置开机自启;
- 把登录用户加入 docker 组。
这就是把“手动安装 Docker”变成可重复执行的剧本。
9 这里有一个会直接影响执行的主机组错误
前面的 Inventory 定义的是:
[dbservers]
但 Playbook 写的是:
hosts: docker_group
两者并不一致。
如果 Inventory 里没有另外定义docker_group,执行时通常不会匹配到刚才那台dbservers主机。
所以真正执行前必须让:
Inventory主机组名称
和:
Playbook的hosts
一致。
代码块按照已有内容原样保留,不在这里直接修改。
10 执行Playbook
命令:
ansible-playbook -i hosts deploy_docker.yml执行时 Ansible 会按照 Inventory 找目标主机,再逐个运行 Playbook 里的任务。
如果输出里出现:
okchanged
说明任务已经被处理。
如果出现:
unreachablefailed
就要先看 SSH、权限、主机组和软件源。
11 在目标主机验证Docker
执行:
docker-vsystemctl statusdocker看到 Docker 版本号,并且服务状态为:
active (running)
就说明 Docker 已经正常启动。
到这里,本地网络内的自动部署链已经基本完成。
12 当前Playbook并没有自动安装cpolar
这里需要特别区分。
前面的 Docker Playbook 只包含:
- yum依赖;
- Docker软件源;
- Docker组件;
- Docker服务;
- docker用户组。
里面并没有 cpolar 安装任务。
因此更准确的描述应该是:
Docker由Ansible Playbook部署,cpolar在后续章节单独安装。
不能把两部分写成“一个Playbook全部自动完成”。
13 后半部分真正解决的是跨网络SSH
如果 Ansible 控制节点和目标主机在同一个局域网,可以直接使用:
192.168.42.146:22
但如果目标主机在另一处网络,控制节点不能直接访问内网 SSH,就需要先解决:
控制节点怎么连上目标机22端口。
这里使用 cpolar 建立 TCP 入口。
14 安装cpolar
执行:
sudocurlhttps://get.cpolar.sh|sh然后检查服务:
sudosystemctl status cpolar进入:
服务器IP:9200
查看 Web 管理界面:
15 给目标机22端口创建TCP入口
配置:
- 隧道名称:
ssh - 协议:
tcp - 本地地址:
22 - 端口类型:随机临时 TCP 端口
- 地区:
China Top
创建后查看在线隧道:
示例得到:
- 域名:
2.tcp.cpolar.top - 端口:
15197
这时从外部访问:
2.tcp.cpolar.top:15197
实际会转到目标主机:
22
16 Ansible只需要把Inventory改成远程SSH入口
把 Inventory 改成:
[dbservers]2.tcp.cpolar.topansible_user=rootansible_port=15197ansible_password=***这时候 Ansible 的目标已经不再是内网 IP。
而是:
公网TCP地址 + 公网端口
但 SSH 背后的目标仍然是原来的 Linux 主机。
17 再执行同一份Playbook
继续运行:
ansible-playbook-ihosts deploy_docker.yml然后到目标主机确认:
可以看到 Docker 已经安装。
这一步验证的是:
Ansible控制节点 → 公网TCP入口 → SSH → 目标主机 → Playbook
跨网络链路能够执行。
18 这并不是“把Docker容器直接暴露到公网”
这里容易产生一个概念混淆。
当前创建的是:
SSH 22端口的TCP入口。
目的只是让 Ansible 能远程连接目标主机。
它并没有自动把目标机上未来运行的每个 Docker 容器都发布出去。
如果以后某个容器需要远程访问,还需要针对那个容器实际监听的端口单独设计网络入口。
所以:
Ansible负责部署,SSH负责管理,cpolar负责网络入口。
三个角色不要混在一起。
19 把SSH映射到公网以后,安全比连通更重要
公开 SSH 入口以后,要特别注意:
- 不建议长期使用 root + 密码登录;
- 更推荐 SSH Key;
- 能关闭 root 远程登录就关闭;
- 能关闭密码登录就关闭;
- 密钥和密码不要写进公开文章;
- 固定地址不要随意公开;
- 有条件时限制允许访问的来源。
尤其当前 Inventory 直接使用:
ansible_user=root
和:
ansible_password=***
这更适合演示,不适合长期生产环境照搬。
20 随机TCP入口适合先验证
随机 TCP 地址适合做:
- 临时测试;
- 验证 Ansible 跨网络连接;
- 临时远程运维。
如果只是短时间部署一次,不一定需要固定地址。
21 长期运维再保留固定TCP地址
进入预留页面:
示例地区:
China VIP
固定 TCP 地址:
15.tcp.cpolar.top:13633
然后进入隧道列表:
将端口类型改为:
固定TCP端口
并绑定预留地址:
更新以后,SSH入口就不需要随着随机端口变化而不断修改 Inventory。
具体固定 TCP 套餐、地区和规则可能变化,应以实际账号页面为准。
22 固定地址解决的是Inventory稳定,不是SSH安全
固定 TCP 的真正价值是:
Ansible Inventory不需要频繁改地址和端口。
它并不会自动解决:
- root登录风险;
- 密码泄露;
- SSH暴力尝试;
- 主机身份验证;
- 权限控制。
所以固定地址配置完成以后,安全加固仍然要单独做。
23 这篇真正跑通的链路
同网环境:
Ansible → SSH → 目标主机 → Playbook → Docker
跨网环境:
Ansible → 公网TCP入口 → SSH → 目标主机 → Playbook → Docker
真正完成的是:
- Ansible安装;
- Inventory;
- 本机ping验证;
- Docker Playbook;
- Docker安装与服务验证;
- SSH随机TCP入口;
- 公网地址写入Inventory;
- 跨网络Playbook执行;
- 固定TCP地址。
需要特别注意:
dbservers和docker_group名称不一致;- cpolar没有写入Docker Playbook;
- 明文密码不适合长期使用;
- root SSH不适合直接照搬;
host_key_checking=False会降低SSH身份校验;- 公网SSH入口应该配合更严格认证。
总结
这次我觉得 Ansible 最有价值的地方,不是“少敲几条 Docker 命令”,而是把服务器交付流程固定成了一份可以反复执行的 Playbook。机器多以后,统一软件源、Docker组件、服务状态和权限,比每台机器靠人手操作可靠得多。
本地网络里跑通以后,同一份 Playbook 又继续通过远程 SSH 入口执行,说明 Ansible 真正依赖的是 SSH 可达性,而不是服务器必须和控制节点处在同一个局域网。
不过这套配置还不能直接照搬到生产环境:主机组名称要先统一,密码不应该明文保存,root远程登录和关闭Host Key Checking也都需要重新收紧。
如果后面继续完善,我更建议把SSH Key、Ansible Vault、变量文件和Role一起补上,让这份Playbook从“能批量装Docker”逐步变成真正可维护的服务器初始化方案。