查看原文
其他

Ansible 起步指南

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

编译自:  
作者: JOSE HIDALGO
译者: geekpi


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

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

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

前置要求

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

  • [1]

  • [2]

  • Mac 用户:[3]

情景

我们将模拟 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 可做的所有事情列表,可以查看[4]

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

  1. ---

  2. - hosts: all

  3.  tasks:

  4.    - shell: uptime

  • --- 是 YAML 文件的开始

  • - hosts:指定要使用的组

  • tasks:标记任务列表的开始

  • - shell:指定第一个任务使用 [5] 模块

  • 记住: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

安装软件

我们将使用 [6] 在我们的服务器上安装软件,因为我们需要 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 文件中使用。 [7] 是 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 提供两个选项:

  • [8]:直接运行一个命令,没有环境变量或重定向(|<> 等)

  • [9]:运行 /bin/sh 并展开变量和支持重定向

其他有用的模块

  • [10] - 在 Debian 系的发行版中添加/删除包仓库

  • [11] - 在 RedHat 系的发行版中添加/删除包仓库

  • [12] - 启动/停止/重新启动/启用/禁用服务

  • [13] - 从 git 服务器部署代码

  • [14] - 从 Web 或本地源解开软件包

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

Rails 使用 [15] 来逐步更改数据库,但由于你有多个应用程序服务器,因此这些迁移任务不能被分配为组任务,而我们只需要一个服务器来运行迁移。在这种情况下,当使用 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、调试等许多其他很棒的功能,但我希望它给了你一个良好的开始!所以,请继续学习并使用它。如果你有任何问题,你可以在 [16] 或评论栏联系我,让我知道你还想知道哪些关于 ansible 的东西!


via: 

作者:[17] 译者: 校对:

本文由 [18] 组织编译, 荣誉推出


推荐文章

< 左右滑动查看相关文章 >

输入文章 ID 或长按二维码直达


[1]: https://www.virtualbox.org/
[2]: https://www.vagrantup.com/downloads.html
[3]: http://brew.sh/
[4]: http://docs.ansible.com/ansible/list_of_all_modules.html
[5]: http://docs.ansible.com/ansible/shell_module.html
[6]: http://docs.ansible.com/ansible/apt_module.html
[7]: http://jinja.pocoo.org/docs/dev/
[8]: http://docs.ansible.com/ansible/command_module.html
[9]: http://docs.ansible.com/ansible/shell_module.html
[10]: http://docs.ansible.com/ansible/apt_repository_module.html
[11]: https://docs.ansible.com/ansible/yum_repository_module.html
[12]: http://docs.ansible.com/ansible/service_module.html
[13]: http://docs.ansible.com/ansible/git_module.html
[14]: http://docs.ansible.com/ansible/unarchive_module.html
[15]: http://edgeguides.rubyonrails.org/active_record_migrations.html
[16]: https://twitter.com/c0d5x
[17]: https://gorillalogic.com/author/josehidalgo/
[18]: https://github.com/LCTT/TranslateProject


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存