这是一篇关于 Ansible 的速成课程,你可以用作小项目的模板,或者帮你深入了解这个神奇的工具。阅读了本指南之后,你将对自动化服务器配置、部署等有足够的了解。

Ansible 是什么,为什么你该了解?

Ansible 简单的说是一个配置管理系统configuration management system。你只需要可以使用 ssh 访问你的服务器或设备就行。它也不同于其他工具,因为它使用推送的方式,而不是像 puppet 或 chef 那样使用拉取的方式。你可以将代码部署到任意数量的服务器上,配置网络设备或在基础架构中自动执行任何操作。

前置要求

假设你使用 Mac 或 Linux 作为你的工作站,Ubuntu Trusty 作为你的服务器,并有一些安装软件包的经验。此外,你的计算机上将需要以下软件。所以,如果你还没有它们,请先安装:

  • Virtualbox
  • Vagrant
  • Mac 用户:Homebrew

情景

我们将模拟 2 个连接到 MySQL 数据库的 Web 应用程序服务器。Web 应用程序使用 Rails 5 和 Puma。

准备

Vagrantfile

为这个项目创建一个文件夹,并将下面的内容保存到名为 Vagrantfile 的文件。

  1. VMs = [
  2. [ "web1", "10.1.1.11"],
  3. [ "web2", "10.1.1.12"],
  4. [ "dbserver", "10.1.1.21"],
  5. ]
  6. Vagrant.configure(2) do |config|
  7. VMs.each { |vm|
  8. config.vm.define vm[0] do |box|
  9. box.vm.box = "ubuntu/trusty64"
  10. box.vm.network "private_network", ip: vm[1]
  11. box.vm.hostname = vm[0]
  12. box.vm.provider "virtualbox" do |vb|
  13. vb.memory = "512"
  14. end
  15. end
  16. }
  17. end

配置你的虚拟网络

我们希望我们的虚拟机能互相交互,但不要让流量流出到真实的网络,所以我们将在 Virtualbox 中创建一个仅主机(HOST-Only)的网络适配器。

  1. 打开 Virtualbox
  2. 转到 Preferences
  3. 转到 Network
  4. 单击 Host-Only
  5. 单击添加网络
  6. 单击 Adapter
  7. 将 IPv4 设置为 10.1.1.1,IPv4 网络掩码:255.255.255.0
  8. 单击 “OK”

测试虚拟机及虚拟网络

在终端中,在存放 Vagrantfile 的项目目录中,输入下面的命令:

  1. vagrant up

它会创建你的虚拟机,因此会花费一会时间。输入下面的命令并验证输出内容以检查是否已经工作:

  1. $ vagrant status
  2. Current machine states:
  3. web1 running (virtualbox)
  4. web2 running (virtualbox)
  5. master running (virtualbox)
  6. This environment represents multiple VMs. The VMs are all listed
  7. above with their current state. For more information about a specific
  8. VM, run `vagrant status NAME`.

现在使用 vagrant 的用户名和密码 ,按 Vagrantfile 中的 IP 登录其中一台虚拟机,这将验证虚拟机并将它们的密钥添加到你的已知主机(known_hosts)文件中。

  1. ssh vagrant@10.1.1.11 # password is `vagrant`
  2. ssh vagrant@10.1.1.12
  3. ssh vagrant@10.1.1.21

恭喜你!现在你已经有可以实验的服务器了。下面的剩下的部分!

安装 Ansible

对于 Mac 用户:

  1. $ brew install ansible

对于 Ubuntu 用户:

  1. $ sudo apt install ansible

确保你使用了ansible 最近的版本 2.1 或者更高的版本:

  1. $ ansible --version
  2. ansible 2.1.1.0

清单

Ansible 使用清单文件来了解要使用的服务器,以及如何将它们分组以并行执行任务。让我们为这个项目创建我们的清单文件 inventory,并将它放在与 Vagrantfile 相同的文件夹中:

  1. [all:children]
  2. webs
  3. db
  4. [all:vars]
  5. ansible_user=vagrant
  6. ansible_ssh_pass=vagrant
  7. [webs]
  8. web1 ansible_host=10.1.1.11
  9. web2 ansible_host=10.1.1.12
  10. [db]
  11. dbserver ansible_host=10.1.1.21
  • [all:children] 定义一个组的组(all
  • [all:vars] 定义属于组 all 的变量
  • [webs] 定义一个组,就像 [db] 一样
  • 文件的其余部分只是主机的声明,带有它们的名称和 IP
  • 空行表示声明结束

现在我们有了一个清单,我们可以从命令行开始使用 ansible,指定一个主机或一个组来执行命令。以下是检查与服务器的连接的命令示例:

  1. $ ansible -i inventory all -m ping
  • -i 指定清单文件
  • all 指定要操作的服务器或服务器组
  • -m' 指定一个 ansible 模块,在这种情况下为ping`

下面是命令输出:

  1. dbserver | SUCCESS => {
  2. "changed": false,
  3. "ping": "pong"
  4. }
  5. web1 | SUCCESS => {
  6. "changed": false,
  7. "ping": "pong"
  8. }
  9. web2 | SUCCESS => {
  10. "changed": false,
  11. "ping": "pong"
  12. }

服务器以不同的顺序响应,这只取决于谁先响应,但是这个没有关系,因为 ansible 独立保持每台服务器的状态。

你也可以使用另外一个选项来运行任何命令:

  • -a <command>
  1. $ ansible -i inventory all -a uptime
  2. web1 | SUCCESS | rc=0 >>
  3. 21:43:27 up 25 min, 1 user, load average: 0.00, 0.01, 0.05
  4. dbserver | SUCCESS | rc=0 >>
  5. 21:43:27 up 24 min, 1 user, load average: 0.00, 0.01, 0.05
  6. web2 | SUCCESS | rc=0 >>
  7. 21:43:27 up 25 min, 1 user, load average: 0.00, 0.01, 0.05

这是只有一台服务器的另外一个例子:

  1. $ ansible -i inventory dbserver -a "df -h /"
  2. dbserver | SUCCESS | rc=0 >>
  3. Filesystem Size Used Avail Use% Mounted on
  4. /dev/sda1 40G 1.4G 37G 4% /

剧本

剧本(playbook)只是个 YAML 文件,它将清单文件中的服务器组与命令关联。在 ansible 中的对于关键字是 tasks,它可以是一个预期的状态、shell 命令或许多其它的选项。有关 ansible 可做的所有事情列表,可以查看所有模块的列表。

下面是一个运行 shell 命令的剧本示例,将其保存为 playbook1.yml

  1. ---
  2. - hosts: all
  3. tasks:
  4. - shell: uptime
  • --- 是 YAML 文件的开始
  • - hosts:指定要使用的组
  • tasks:标记任务列表的开始
  • - shell:指定第一个任务使用 shell 模块
  • 记住:YAML 需要缩进结构,确保你始终遵循剧本中的正确结构

用下面的命令运行它:

  1. $ ansible-playbook -i inventory playbook1.yml
  2. PLAY [all] *********************************************************************
  3. TASK [setup] *******************************************************************
  4. ok: [web1]
  5. ok: [web2]
  6. ok: [dbmaster]
  7. TASK [command] *****************************************************************
  8. changed: [web1]
  9. changed: [web2]
  10. changed: [dbmaster]
  11. PLAY RECAP *********************************************************************
  12. dbmaster : ok=2 changed=1 unreachable=0 failed=0
  13. web1 : ok=2 changed=1 unreachable=0 failed=0
  14. web2 : ok=2 changed=1 unreachable=0 failed=0

正如你所见,ansible 运行了 2 个任务,而不是只有剧本中的一个。TASK [setup] 是一个隐式任务,它会首先运行以捕获服务器的信息,如主机名、IP、发行版和更多详细信息,然后可以使用这些信息运行条件任务。

还有最后的 PLAY RECAP,其中 ansible 显示了运行了多少个任务以及每个对应的状态。在我们的例子中,因为我们运行了一个 shell 命令,ansible 不知道结果的状态,它被认为是 changed

安装软件

我们将使用 apt 在我们的服务器上安装软件,因为我们需要 root 权限,所以我们必须使用 become 语句,将这个内容保存在 playbook2.yml 中并运行它(ansible-playbook playbook2.yml):

  1. ---
  2. - hosts: webs
  3. become_user: root
  4. become: true
  5. tasks:
  6. - apt: name=git state=present

有一些语句可以应用于 ansible 中所有模块;一个是 name 语句,可以让我们输出关于正在执行的任务的更具描述性的文本。要使用它,保持任务内容一样,但是添加 name :描述性文本 作为第一行,所以我们以前的文本将改成:

  1. ---
  2. - hosts: webs
  3. become_user: root
  4. become: true
  5. tasks:
  6. - name: This task will make sure git is present on the system
  7. apt: name=git state=present

使用 with_items

当你要处理一个列表时,比如要安装的项目和软件包、要创建的文件,可以用 ansible 提供的with_items。下面是我们如何在 playbook3.yml 中使用它,同时添加一些我们已经知道的其他语句:

  1. ---
  2. - hosts: all
  3. become_user: root
  4. become: true
  5. tasks:
  6. - name: Installing dependencies
  7. apt: name={{item}} state=present
  8. with_items:
  9. - git
  10. - mysql-client
  11. - libmysqlclient-dev
  12. - build-essential
  13. - python-software-properties

使用 template 和 vars

vars 是一个定义变量语句,可以在 task 语句或 template 文件中使用。 Jinja2 是 Ansible 中使用的模板引擎,但是关于它你不需要学习很多。在你的剧本中定义变量,如下所示:

  1. ---
  2. - hosts: all
  3. vars:
  4. - secret_key: VqnzCLdCV9a3jK
  5. - path_to_vault: /opt/very/deep/path
  6. tasks:
  7. - name: Setting a configuration file using template
  8. template: src=myconfig.j2 dest={{path_to_vault}}/app.conf

正如你看到的,我可以使用 {{path_to_vault}} 作为剧本的一部分,但也因为我使用了 template语句,我可以使用 myconfig.j2 中的任何变量,该文件必须存在一个名为 templates 的子文件夹中。你项目树应该如下所示:

  1. ├── Vagrantfile
  2. ├── inventory
  3. ├── playbook1.yml
  4. ├── playbook2.yml
  5. └── templates
  6. └── myconfig.j2

当 ansible 找到一个 template 语句后它会在 templates 文件夹内查找,并将把被 {{ 和 }} 括起来的变量展开来。

示例模板:

  1. this is just an example vault_dir: {{path_to_vault}} secret_password: {{secret_key}}

即使你不扩展变量你也可以使用 template。考虑到将来会添加所以我先做了。比如创建一个hosts.j2 模板并加入主机名和 IP。

  1. 10.1.1.11 web1
  2. 10.1.1.12 web2
  3. 10.1.1.21 dbserver

这里要用像这样的语句:

  1. - name: Installing the hosts file in all servers
  2. template: src=hosts.j2 dest=/etc/hosts mode=644

shell 命令

你应该尽量使用模块,因为 Ansible 可以跟踪任务的状态,并避免不必要的重复,但有时 shell 命令是不可避免的。 对于这些情况,Ansible 提供两个选项:

  • command:直接运行一个命令,没有环境变量或重定向(|<> 等)
  • shell:运行 /bin/sh 并展开变量和支持重定向

其他有用的模块

  • apt_repository - 在 Debian 系的发行版中添加/删除包仓库
  • yum_repository - 在 RedHat 系的发行版中添加/删除包仓库
  • service - 启动/停止/重新启动/启用/禁用服务
  • git - 从 git 服务器部署代码
  • unarchive - 从 Web 或本地源解开软件包

只在一台服务器中运行任务

Rails 使用 migrations 来逐步更改数据库,但由于你有多个应用程序服务器,因此这些迁移任务不能被分配为组任务,而我们只需要一个服务器来运行迁移。在这种情况下,当使用 run_once时,run_once 将分派任务到一个服务器,并直到这个任务完成继续下一个任务。你只需要在你的任务中设置 run_once:true

  1. - name: 'Run db:migrate'
  2. shell: cd {{appdir}};rails db:migrate
  3. run_once: true

会失败的任务

通过指定 ignore_errors:true,你可以运行可能会失败的任务,但不会影响剧本中剩余的任务完成。这是非常有用的,例如,当删除最初并不存在的日志文件时。

  1. - name: 'Delete logs'
  2. shell: rm -f /var/log/nginx/errors.log
  3. ignore_errors: true

放到一起

现在用我们先前学到的,这里是每个文件的最终版:

Vagrantfile

  1. VMs = [
  2. [ "web1", "10.1.1.11"],
  3. [ "web2", "10.1.1.12"],
  4. [ "dbserver", "10.1.1.21"],
  5. ]
  6. Vagrant.configure(2) do |config|
  7. VMs.each { |vm|
  8. config.vm.define vm[0] do |box|
  9. box.vm.box = "ubuntu/trusty64"
  10. box.vm.network "private_network", ip: vm[1]
  11. box.vm.hostname = vm[0]
  12. box.vm.provider "virtualbox" do |vb|
  13. vb.memory = "512"
  14. end
  15. end
  16. }
  17. end

inventory

  1. [all:children]
  2. webs
  3. db
  4. [all:vars]
  5. ansible_user=vagrant
  6. ansible_ssh_pass=vagrant
  7. [webs]
  8. web1 ansible_host=10.1.1.11
  9. web2 ansible_host=10.1.1.12
  10. [db]
  11. dbserver ansible_host=10.1.1.21

templates/hosts.j2:

  1. 10.1.1.11 web1
  2. 10.1.1.12 web2
  3. 10.1.1.21 dbserver

templates/my.cnf.j2

  1. [client]
  2. port = 3306
  3. socket = /var/run/mysqld/mysqld.sock
  4. [mysqld_safe]
  5. socket = /var/run/mysqld/mysqld.sock
  6. nice = 0
  7. [mysqld]
  8. server-id = 1
  9. user = mysql
  10. pid-file = /var/run/mysqld/mysqld.pid
  11. socket = /var/run/mysqld/mysqld.sock
  12. port = 3306
  13. basedir = /usr
  14. datadir = /var/lib/mysql
  15. tmpdir = /tmp
  16. lc-messages-dir = /usr/share/mysql
  17. skip-external-locking
  18. bind-address = 0.0.0.0
  19. key_buffer = 16M
  20. max_allowed_packet = 16M
  21. thread_stack = 192K
  22. thread_cache_size = 8
  23. myisam-recover = BACKUP
  24. query_cache_limit = 1M
  25. query_cache_size = 16M
  26. log_error = /var/log/mysql/error.log
  27. expire_logs_days = 10
  28. max_binlog_size = 100M
  29. [mysqldump]
  30. quick
  31. quote-names
  32. max_allowed_packet = 16M
  33. [mysql]
  34. [isamchk]
  35. key_buffer = 16M
  36. !includedir /etc/mysql/conf.d/

final-playbook.yml

  1. - hosts: all
  2. become_user: root
  3. become: true
  4. tasks:
  5. - name: 'Install common software on all servers'
  6. apt: name={{item}} state=present
  7. with_items:
  8. - git
  9. - mysql-client
  10. - libmysqlclient-dev
  11. - build-essential
  12. - python-software-properties
  13. - name: 'Install hosts file'
  14. template: src=hosts.j2 dest=/etc/hosts mode=644
  15. - hosts: db
  16. become_user: root
  17. become: true
  18. tasks:
  19. - name: 'Software for DB server'
  20. apt: name={{item}} state=present
  21. with_items:
  22. - mysql-server
  23. - percona-xtrabackup
  24. - mytop
  25. - mysql-utilities
  26. - name: 'MySQL config file'
  27. template: src=my.cnf.j2 dest=/etc/mysql/my.cnf
  28. - name: 'Restart MySQL'
  29. service: name=mysql state=restarted
  30. - name: 'Grant access to web app servers'
  31. shell: echo 'GRANT ALL PRIVILEGES ON *.* TO "root"@"%" WITH GRANT OPTION;FLUSH PRIVILEGES;'|mysql -u root mysql
  32. - hosts: webs
  33. vars:
  34. - appdir: /opt/dummyapp
  35. become_user: root
  36. become: true
  37. tasks:
  38. - name: 'Add ruby-ng repo'
  39. apt_repository: repo='ppa:brightbox/ruby-ng'
  40. - name: 'Install rails software'
  41. apt: name={{item}} state=present
  42. with_items:
  43. - ruby-dev
  44. - ruby-all-dev
  45. - ruby2.2
  46. - ruby2.2-dev
  47. - ruby-switch
  48. - libcurl4-openssl-dev
  49. - libssl-dev
  50. - zlib1g-dev
  51. - nodejs
  52. - name: 'Set ruby to 2.2'
  53. shell: ruby-switch --set ruby2.2
  54. - name: 'Install gems'
  55. shell: gem install bundler rails
  56. - name: 'Kill puma if running'
  57. shell: file /run/puma.pid >/dev/null && kill `cat /run/puma.pid` 2>/dev/null
  58. ignore_errors: True
  59. - name: 'Clone app repo'
  60. git:
  61. repo=https://github.com/c0d5x/rails_dummyapp.git
  62. dest={{appdir}}
  63. version=staging
  64. force=yes
  65. - name: 'Run bundler'
  66. shell: cd {{appdir}};bundler
  67. - name: 'Run db:setup'
  68. shell: cd {{appdir}};rails db:setup
  69. run_once: true
  70. - name: 'Run db:migrate'
  71. shell: cd {{appdir}};rails db:migrate
  72. run_once: true
  73. - name: 'Run rails server'
  74. shell: cd {{appdir}};rails server -b 0.0.0.0 -p 80 --pid /run/puma.pid -d

放在你的环境中

将这些文件放在相同的目录,运行下面的命令打开你的开发环境:

  1. vagrant up
  2. ansible-playbook -i inventory final-playbook.yml

部署新的代码

确保修改了代码并推送到了仓库中。接下来,确保你 git 语句中使用了正确的分支:

  1. - name: 'Clone app repo'
  2. git:
  3. repo=https://github.com/c0d5x/rails_dummyapp.git
  4. dest={{appdir}}
  5. version=staging
  6. force=yes

作为一个例子,你可以修改 version 字段为 master,再次运行剧本:

  1. ansible-playbook -i inventory final-playbook.yml

检查所有的 web 服务器上的页面是否已更改:http://10.1.1.11 或 http://10.1.1.12。将其更改为version = staging 并重新运行剧本并再次检查页面。

你还可以创建只包含与部署相关的任务的替代剧本,以便其运行更快。

接下来是什么 ?!

这只是可以做的很小一部分。我们没有接触角色role、过滤器filter、调试等许多其他很棒的功能,但我希望它给了你一个良好的开始!所以,请继续学习并使用它。

原文发布时间为:2017-01-12

本文来自云栖社区合作伙伴“Linux中国”

Ansible 起步指南相关推荐

  1. 《Ansible权威指南 》一 第一篇 Part 1 基础入门篇

    本节书摘来自华章出版社<Ansible权威指南 >一书中的第1章,第1.1节,李松涛 魏 巍 甘 捷 著更多章节内容可以访问云栖社区"华章计算机"公众号查看. 第一篇 ...

  2. Ansible 入门指南 - ansible-playbook 命令

    上篇文章Ansible 入门指南 - 安装及 Ad-Hoc 命令使用介绍的额是 Ad-Hoc 命令方式,本文将介绍 Playbook 方式. Playbook 译为「剧本」,觉得还挺恰当的. play ...

  3. 《Ansible权威指南》一1.7 Ansible的安装部署

    本节书摘来自华章出版社<Ansible权威指南>一书中的第一章,第1.7节,作者 李松涛 魏 巍 甘 捷 更多章节内容可以访问云栖社区"华章计算机"公众号查看. 1.7 ...

  4. 《Ansible权威指南 》一第2章 Ansible基础元素介绍

    本节书摘来自华章出版社<Ansible权威指南 >一书中的第2章,第2.1节,李松涛 魏 巍 甘 捷 著更多章节内容可以访问云栖社区"华章计算机"公众号查看. 第2章 ...

  5. 自动化运维工具-Ansible实战指南

    Ansible实战 前言 一.Ansible简介 1.ansible是什么? 2.ansible特点 3.ansible架构 主要模块 工作流程 命令执行过程 二.Ansible 配置 1 安装ans ...

  6. Ansible详解(二)

    Ansible系列命令 Ansible系列命令有如下: ansible:这个命令是日常工作中使用率非常高的命令之一,主要用于临时一次性操作: ansible-doc:是Ansible模块文档说明,针对 ...

  7. Ansible中的playbook详解

    首先简单说明一下playbook,playbook是什么呢? 根本上说playbook和shell脚本没有任何的区别,playbook就像shell一样,也是把一堆的命令组合起来,然后加入对应条件判断 ...

  8. python权威指南 pdf_Ansible权威指南pdf txt mobi下载及读书笔记

    Ansible权威指南pdf txt mobi下载读书笔记 读书笔记:工作机制:基于openSSH通信,需安装SSH Python,底层基于SSH协议,windows基于PowerShell仅客户侧. ...

  9. 零基础带你硬核了解并上手“Ansible“!

    前言 最近有幸接触过一个自动化运维的项目,需要掌握的技术栈中就包括近年来越来越火的一款开源运维自动化工具--Ansible,通过Ansible可以实现运维自动化,提高运维工程师的工作效率,减少人为失误 ...

最新文章

  1. centos 安装 rabbitMq
  2. Repeater控件使用小结持续更新
  3. python选择日期控件_Flask学习笔记-使用bootstrap-datepicker实现页面日期选择
  4. C#基础篇三流程控制2
  5. MySQL完全备份与恢复
  6. C#各种结束进程的方法详细介绍
  7. Rtx 实时通知实现
  8. flask-session 在redis中存储session
  9. martin_pthread_pool
  10. C语言试题五十八之请编写函数fun,:计算并输出下列多项式的值(sn=1+1/1!+1/2!+1/3!+1/4!+…+1/n! )
  11. 使用磁盘为Linux添加swap
  12. 沟通艺术-善于揣摩对方心思,才能把话说到心坎上
  13. maven模块化分解项目
  14. mybatis与hibernate不同(重要)
  15. PHP书写规范 匈牙利命名法+驼峰法命名
  16. 利用单片机做手机连点器(附别踩白块自动点击程序)
  17. kk每日一句:第一句
  18. ZooKeeper 选举机制
  19. 随机向量函数链神经网络(RVFLNN)简介——附测试代码
  20. Spring系列第2篇:控制反转(IoC)与依赖注入(DI),晦涩难懂么?

热门文章

  1. springboot pom文件添加mysql组件_SpringBoot整合mybatis-plus+druid组件,实现增删改查
  2. 8_python基础—高级变量类型(字符串、列表、元组、字典、集合)
  3. python分布爬虫_13天搞定Python分布爬虫(第七天)(Scrapy)
  4. 七牛上传图片html,MWEB+七牛 上传图片
  5. python db2 linux 安装,python安装DB2模块
  6. linux redis客户端_10个 Linux 顶级开源缓存工具
  7. 关于JVM默认内存的增加
  8. Python编程基础02:Python基本语法
  9. Python学习笔记:目录与文件操作
  10. Gson案例:Java对象与JSON字符串相互转换