在实际运维工作中手动登录服务器执行重复性命令、部署应用、管理配置不仅效率低下而且极易出错。随着服务器规模从几台扩展到几十甚至上百台这种“人肉运维”模式将变得不可持续。自动化运维正是为了解决这一痛点而 Ansible 和 Docker 的组合则为我们提供了一套从基础设施编排到应用容器化部署的完整、高效且易于上手的解决方案。Ansible 以其无代理、基于 SSH 的架构成为配置管理和批量任务执行的利器Docker 则通过容器化技术实现了应用及其运行环境的标准化封装与快速交付。本文面向希望从零开始构建自动化运维能力的运维工程师和开发者。我们将不局限于简单的安装步骤而是通过一个完整的实战案例带你理解如何将 Ansible 与 Docker 结合实现从环境准备、批量主机管理到容器化应用部署的全流程。你将学会如何编写清晰的 Ansible Playbook如何构建和管理 Docker 镜像并最终实现一键式自动化部署。文章会详细解释每个关键配置和命令背后的逻辑并提供生产环境中常见的排错思路与最佳实践。1. 理解自动化运维的核心Ansible 与 Docker 的角色在深入动手之前必须厘清 Ansible 和 Docker 在自动化运维体系中各自承担的角色以及它们如何协同工作。混淆两者的职责范围是初学者常见的误区。1.1 Ansible基础设施与配置的“编排者”Ansible 的核心是配置管理和批量任务执行。它通过 SSH 协议连接到目标主机无需在目标主机安装客户端这是其与 SaltStack、Puppet 的重要区别并执行预定义的“任务”。这些任务被组织在 YAML 格式的 Playbook 中描述了你希望目标系统达到的“状态”。Ansible 典型工作场景软件安装与配置在 100 台服务器上统一安装 Nginx并推送定制化的配置文件。用户与权限管理批量创建系统用户、配置 SSH 密钥、管理 sudo 权限。服务管理启动、停止、重启系统服务并确保其开机自启。文件分发将本地的脚本、配置文件同步到多台服务器的指定位置。信息收集从多台主机收集系统信息如 IP、磁盘使用率、内存并汇总。Ansible 的哲学是“声明式”的你告诉它最终状态“Nginx 必须运行且监听 80 端口”它自己判断如何达到这个状态检查 Nginx 是否安装、配置文件是否正确、服务是否启动。1.2 Docker应用与环境的“封装者”Docker 的核心是应用容器化。它将应用程序及其所有依赖项库、环境变量、配置文件打包到一个标准化的单元中称为“镜像”。这个镜像可以在任何安装了 Docker 引擎的环境中运行形成一个隔离的“容器”。Docker 典型工作场景环境一致性解决“在我机器上能跑”的问题确保开发、测试、生产环境完全一致。快速部署与扩展镜像一旦构建完成可以在秒级启动容器实例非常适合微服务架构和弹性伸缩。资源隔离与高效利用容器共享主机操作系统内核比虚拟机更轻量启动更快资源利用率更高。简化依赖管理应用依赖被封装在镜像内宿主机只需提供 Docker 运行环境。1.3 协同工作模式Ansible Docker那么两者如何结合一个经典的自动化运维流水线是Ansible 管理 Docker 环境使用 Ansible Playbook 在全新的或已有的 Linux 服务器上自动化安装 Docker 引擎、配置镜像加速器、设置用户权限。Ansible 部署 Docker 应用使用 Ansible 的docker_container、docker_image等模块执行诸如拉取镜像、运行容器、配置容器网络、管理容器生命周期等操作。Docker 封装 Ansible 执行环境将 Ansible 及其所需的 Python 环境、依赖库打包成一个 Docker 镜像。这样你可以在任何有 Docker 的环境中包括 Windows/Mac 的开发者电脑快速获得一个一致、干净的 Ansible 控制节点无需担心本地 Python 环境冲突。这正是网络搜索材料中提到的“将 Ansible 打包为轻量化镜像”的思路。简单来说Ansible 负责“让机器准备好并运行 Docker 容器”而 Docker 负责“提供标准化、可移植的应用本身”。在下面的实战中我们将体验这种协同。2. 环境准备与基础安装我们将搭建一个经典的学习环境一台控制机Control Node运行 Ansible一台或多台被管理机Managed Nodes作为目标主机。为了简化你可以使用虚拟机如 VMware/VirtualBox创建两台 CentOS 7 或 Ubuntu 20.04 系统的机器。生产环境则可能是云上的多台 ECS 实例。2.1 控制节点Ansible安装控制节点需要安装 Ansible。Ansible 由 Python 编写因此需要 Python 环境。对于 CentOS/RHEL 系统# 1. 安装 EPEL 仓库Extra Packages for Enterprise Linux sudo yum install epel-release -y # 2. 安装 Ansible sudo yum install ansible -y # 3. 验证安装 ansible --version对于 Ubuntu/Debian 系统# 1. 更新软件包索引 sudo apt update # 2. 安装软件属性通用包并添加 PPA可选获取较新版本 sudo apt install software-properties-common -y sudo apt-add-repository --yes --update ppa:ansible/ansible # 3. 安装 Ansible sudo apt install ansible -y # 4. 验证安装 ansible --version安装成功后ansible --version会显示版本信息如ansible 2.9.27和配置路径。注意控制节点需要能够通过 SSH 密钥免密登录到所有被管理节点这是 Ansible 工作的基础。请确保已生成 SSH 密钥对ssh-keygen并将公钥分发到目标主机ssh-copy-id usertarget_host。2.2 被管理节点Docker 主机准备被管理节点需要安装 Docker 引擎。我们将使用 Ansible 来完成这件事但首先需要手动在一台主机上演示 Docker 安装以便理解过程。手动安装 Docker (以 CentOS 7 为例)# 1. 卸载旧版本如有 sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine # 2. 安装必要的依赖 sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # 3. 设置稳定的 Docker 仓库使用国内镜像源加速例如阿里云 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 4. 安装 Docker Engine 和 CLI sudo yum install -y docker-ce docker-ce-cli containerd.io # 5. 启动 Docker 并设置开机自启 sudo systemctl start docker sudo systemctl enable docker # 6. 验证安装 sudo docker run hello-world如果看到 “Hello from Docker!” 的输出说明 Docker 安装成功。对于 Ubuntu 系统步骤类似但包管理命令为apt仓库源也不同。配置 Docker 镜像加速器重要国内从 Docker Hub 拉取镜像速度很慢必须配置镜像加速器。# 创建或修改 Docker 守护进程配置文件 sudo tee /etc/docker/daemon.json -EOF { registry-mirrors: [https://your-mirror.mirror.aliyuncs.com] # 替换为你的加速器地址 } EOF # 重新加载配置并重启 Docker sudo systemctl daemon-reload sudo systemctl restart docker你可以从阿里云容器镜像服务、腾讯云等获取免费的镜像加速器地址。3. 使用 Ansible 自动化安装与管理 Docker现在我们将使用 Ansible 把上面手动安装 Docker 的步骤自动化并批量应用到多台主机上。3.1 配置 Ansible 清单 (Inventory)清单文件定义了 Ansible 要管理的主机。默认位置是/etc/ansible/hosts但我们通常在项目目录下创建自己的清单文件。mkdir ansible-docker-demo cd ansible-docker-demo vim hosts.ini在hosts.ini文件中写入以下内容[docker_servers] node1 ansible_host192.168.1.101 ansible_userroot node2 ansible_host192.168.1.102 ansible_userroot [docker_servers:vars] # 为组内所有主机设置变量 ansible_ssh_private_key_file/path/to/your/private_key # 或者使用密码不安全仅用于测试 # ansible_ssh_passyour_password # ansible_become_passyour_sudo_password请将 IP 地址、用户名和密钥路径替换为你的实际环境信息。ansible_user最好是具有 sudo 权限的用户。3.2 编写 Playbook 安装 Docker创建一个名为install_docker.yml的 Playbook 文件。--- - name: Install and configure Docker on target servers hosts: docker_servers # 指定在清单中的哪个主机组上执行 become: yes # 使用 sudo 权限执行任务 tasks: # 任务列表开始 - name: Remove old Docker packages (for CentOS/RHEL) yum: name: - docker - docker-client - docker-client-latest - docker-common - docker-latest - docker-latest-logrotate - docker-logrotate - docker-engine state: absent when: ansible_os_family RedHat # 条件判断仅 RedHat 系执行 - name: Install required system packages package: name: - yum-utils - device-mapper-persistent-data - lvm2 state: present when: ansible_os_family RedHat - name: Add Docker CE repository (CentOS/RHEL) yum_repository: name: docker-ce description: Docker CE Stable baseurl: https://mirrors.aliyun.com/docker-ce/linux/centos/$releasever/$basearch/stable gpgcheck: yes gpgkey: https://mirrors.aliyun.com/docker-ce/linux/centos/gpg enabled: yes when: ansible_os_family RedHat - name: Install Docker CE, CLI, and Containerd package: name: - docker-ce - docker-ce-cli - containerd.io state: present when: ansible_os_family RedHat # 对于 Debian/Ubuntu 系统的任务可以类似定义使用 apt 模块这里省略... - name: Start and enable Docker service systemd: name: docker state: started enabled: yes - name: Ensure Docker daemon configuration directory exists file: path: /etc/docker state: directory mode: 0755 - name: Configure Docker registry mirror copy: dest: /etc/docker/daemon.json content: | { registry-mirrors: [https://your-mirror.mirror.aliyuncs.com] } mode: 0644 notify: restart docker # 触发处理器 - name: Add current user to docker group (避免每次用sudo) user: name: {{ ansible_user }} groups: docker append: yes handlers: # 处理器由 notify 触发 - name: restart docker systemd: name: docker state: restarted这个 Playbook 做了以下几件事根据操作系统类型通过ansible_os_family变量判断执行对应的安装任务。使用阿里云镜像源安装 Docker CE。启动并设置 Docker 服务开机自启。配置 Docker 镜像加速器。将执行 Playbook 的用户加入docker组使其可以直接运行docker命令。如果镜像加速器配置被修改通过notify触发handler重启 Docker 服务使其生效。3.3 执行 Playbook 并验证在控制节点上运行以下命令来执行 Playbook# 使用 -i 指定清单文件 ansible-playbook -i hosts.ini install_docker.yml执行过程中Ansible 会输出每个任务的执行状态ok,changed,failed。如果一切顺利所有任务应为ok或changed。执行完成后验证 Docker 是否在被管理节点上成功安装并运行# 使用 Ansible 的 ad-hoc 命令在所有 docker_servers 主机上运行 docker version ansible -i hosts.ini docker_servers -m command -a docker version --format {{.Server.Version}}如果返回 Docker 服务器版本号如20.10.17则说明安装成功。4. 使用 Ansible 管理 Docker 容器与镜像安装好 Docker 环境后我们就可以使用 Ansible 来管理容器化应用了。Ansible 提供了docker_container和docker_image等模块使得管理 Docker 资源就像管理系统服务一样简单。4.1 拉取与管理 Docker 镜像创建一个 Playbook 文件manage_nginx.yml用于部署一个 Nginx 容器。--- - name: Manage Nginx Docker container hosts: docker_servers become: yes tasks: - name: Pull the latest Nginx image from registry docker_image: name: nginx tag: latest source: pull force_source: yes # 即使本地存在也强制拉取执行这个 Playbook 会从配置的镜像加速器拉取最新的 Nginx 镜像。ansible-playbook -i hosts.ini manage_nginx.yml4.2 运行与管理 Docker 容器在manage_nginx.yml的tasks中继续添加以下任务- name: Create a directory for custom HTML on host file: path: /home/{{ ansible_user }}/my-nginx-html state: directory mode: 0755 - name: Create a custom index.html file copy: dest: /home/{{ ansible_user }}/my-nginx-html/index.html content: | !DOCTYPE html html headtitleAnsible Docker Demo/title/head bodyh1Hello from Nginx deployed by Ansible!/h1/body /html mode: 0644 - name: Run Nginx container with custom config docker_container: name: my-nginx image: nginx:latest state: started # 状态可以是 present, started, stopped, absent, restarted restart_policy: unless-stopped # 重启策略 ports: - 8080:80 # 将宿主机的 8080 端口映射到容器的 80 端口 volumes: - /home/{{ ansible_user }}/my-nginx-html:/usr/share/nginx/html:ro # 挂载自定义页面只读 env: NGINX_HOST: demo.local networks: - name: bridge这个任务块完成了在宿主机上创建目录和自定义的 HTML 文件。使用docker_container模块启动一个名为my-nginx的容器。将宿主机的 8080 端口映射到容器的 80 端口。将宿主机上的 HTML 目录挂载到容器内替换默认页面。设置环境变量和网络。现在完整执行manage_nginx.ymlansible-playbook -i hosts.ini manage_nginx.yml执行成功后在浏览器访问http://被管理节点IP:8080你应该能看到 “Hello from Nginx deployed by Ansible!” 的页面。4.3 高级容器管理更新与回滚自动化运维的关键之一是变更管理。假设我们要更新 Nginx 版本或修改配置。更新容器镜像- name: Pull a specific version of Nginx docker_image: name: nginx tag: 1.21-alpine # 指定一个特定版本 source: pull - name: Stop and remove the old container docker_container: name: my-nginx state: absent - name: Recreate container with new image docker_container: name: my-nginx image: nginx:1.21-alpine state: started ports: - 8080:80 volumes: - /home/{{ ansible_user }}/my-nginx-html:/usr/share/nginx/html:ro通过先拉取新镜像再移除旧容器最后用新镜像创建新容器实现了应用的更新。Ansible 的幂等性确保了即使重复运行最终状态也是确定的。回滚操作回滚通常意味着使用之前的镜像版本重新创建容器。你需要有策略地管理镜像标签例如每次部署打上时间戳或版本号标签。在 Playbook 中你可以通过变量来控制使用的镜像标签。# 在 Playbook 顶部定义变量或通过外部变量文件、命令行传入 vars: nginx_image_tag: latest # 可以改为 previous-stable tasks: - name: Run container with variable tag docker_container: name: my-nginx image: nginx:{{ nginx_image_tag }} state: started # ... 其他参数要回滚只需重新执行 Playbook 并传入旧的标签值ansible-playbook -i hosts.ini manage_nginx.yml -e nginx_image_tagprevious-stable。5. 常见问题排查与最佳实践在实际操作中你可能会遇到各种问题。下面列出一些典型场景及其排查思路。5.1 Ansible 连接与权限问题问题现象可能原因检查与解决方式UNREACHABLE!1. 网络不通或防火墙阻断 SSH。2. 清单文件中主机 IP/用户名错误。3. SSH 服务未运行。1. 使用ping或ssh命令手动测试连通性。2. 检查hosts.ini文件中的ansible_host和ansible_user。3. 确认目标主机 SSH 服务状态systemctl status sshd。FAILED! {msg: Missing sudo password}1. 未设置become: yes。2. 设置了become但未提供密码或密码错误。3. 执行用户不在 sudoers 列表中。1. Playbook 或任务中需包含become: yes。2. 使用--ask-become-pass参数运行 Playbook 并提供密码或配置 SSH 密钥与 sudo 免密。3. 检查目标主机/etc/sudoers文件。Permission denied (publickey).SSH 公钥认证失败。1. 确认控制节点已执行ssh-copy-id将公钥分发到目标主机。2. 检查清单中ansible_ssh_private_key_file路径是否正确。3. 检查目标主机~/.ssh/authorized_keys文件权限是否为600。5.2 Docker 相关执行失败问题现象可能原因检查与解决方式Error response from daemon: pull access denied1. 镜像名称错误或不存在。2. 私有仓库需要认证。3. 镜像加速器配置错误导致拉取超时。1. 核对镜像名和标签。2. 使用docker_login模块先登录私有仓库。3. 检查/etc/docker/daemon.json中的registry-mirrors地址并执行systemctl restart docker。Error starting userland proxy: listen tcp 0.0.0.0:8080: bind: address already in use宿主机 8080 端口已被其他进程占用。1. 使用netstat -tlnp | grep :8080查找占用进程。2. 停止占用进程或修改 Playbook 中ports映射为其他端口如8081:80。docker_container任务成功但容器未运行1. 容器启动后立即退出如主进程失败。2. 资源限制导致启动失败。1. 查看容器日志docker logs my-nginx。2. 检查 Playbook 中command或env参数是否正确。3. 检查宿主机资源内存、磁盘。5.3 生产环境最佳实践清单使用角色Roles组织 Playbook当 Playbook 变得复杂时应使用 Ansible Roles 将任务、变量、文件、模板等按功能模块化。例如可以创建common、docker、nginx等角色。配置与代码分离将敏感信息如密码、密钥和可变参数如版本号、IP地址放入group_vars/、host_vars/目录或使用 Ansible Vault 加密。使用动态清单在云环境中主机 IP 可能变化。应使用动态清单脚本从云厂商 API 自动获取主机列表。镜像标签管理生产环境不要使用latest标签。使用具体的版本号或 Git Commit SHA以确保部署的一致性。健康检查与滚动更新在docker_container模块中配置healthcheck。对于多实例服务设计 Playbook 实现滚动更新避免服务中断。日志与监控配置 Docker 容器的日志驱动如json-file,syslog并统一收集到 ELK 或 Loki 等日志平台。为容器和应用添加监控指标。安全加固Ansible使用 SSH 密钥认证限制 sudo 权限定期更新 Ansible 版本。Docker以非 root 用户运行容器user:参数限制容器能力cap_drop使用只读根文件系统read_only: yes扫描镜像漏洞。将 Ansible 打包为 Docker 镜像正如网络资料所提可以创建一个包含 Ansible 和必要依赖的轻量级 Docker 镜像。这保证了控制环境的一致性特别适合 CI/CD 流水线。# Dockerfile.ansible FROM alpine:latest RUN apk add --no-cache python3 py3-pip openssh-client \ pip3 install --no-cache-dir ansible WORKDIR /workspace ENTRYPOINT [ansible-playbook]构建并运行docker build -t my-ansible-runner -f Dockerfile.ansible .然后docker run -v $(pwd):/workspace my-ansible-runner -i hosts.ini site.yml。从手动执行命令到编写声明式的 Playbook从单机部署到批量自动化Ansible 与 Docker 的组合极大地提升了运维的效率和可靠性。真正的挑战往往不在于工具本身的使用而在于如何设计清晰、可维护、安全的自动化流程。建议你从本文的小案例出发尝试将手头的重复性运维工作剧本化并逐步引入变量、角色、标签等高级功能来管理复杂性。下一步可以探索 Ansible Galaxy 复用社区角色或集成 Jenkins/GitLab CI 实现完整的 CI/CD 流水线。