TranslateProject/translated/tech/20161005 GETTING STARTED WITH ANSIBLE.md
2016-12-30 13:54:01 +08:00

17 KiB
Raw Blame History

开始使用Ansible

这是一篇关于 Ansible 的课程,你也可以用来作小项目的模板,或者继续深入这个工具。在本指南的最后,你将了解足够的自动化服务器配置、部署等。

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

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

要求

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

  • Virtualbox
  • Vagrant
  • Mac 用户: Homebrew

情景

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

准备

Vagrantfile

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

VMs = [
    [ "web1", "10.1.1.11"],
    [ "web2", "10.1.1.12"],
    [ "dbserver", "10.1.1.21"],
  ]

Vagrant.configure(2) do |config|
  VMs.each { |vm|
    config.vm.define vm[0] do |box|
      box.vm.box = "ubuntu/trusty64"
      box.vm.network "private_network", ip: vm[1]
      box.vm.hostname = vm[0]
      box.vm.provider "virtualbox" do |vb|
         vb.memory = "512"
      end
    end
  }
end

配置你的虚拟网络

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

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

测试虚拟机及虚拟网络

在终端中在具有Vagrantfile的目录中输入下面的命令

vagrant up

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

$ vagrant status
Current machine states:

web1                      running (virtualbox)
web2                      running (virtualbox)
master                    running (virtualbox)

This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run `vagrant status NAME`.

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

ssh vagrant@10.1.1.11 # password is `vagrant`
ssh vagrant@10.1.1.12
ssh vagrant@10.1.1.21

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

安装 Ansible

对于 Mac 用户:

$ brew install ansible

对于 Ubuntu 用户:

$ sudo apt install ansible

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

$ ansible --version
ansible 2.1.1.0

inventory

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

[all:children]
webs
db

[all:vars]
ansible_user=vagrant
ansible_ssh_pass=vagrant

[webs]
web1 ansible_host=10.1.1.11
web2 ansible_host=10.1.1.12

[db]
dbserver ansible_host=10.1.1.21
  • [allchildren] 定义一个组all的组
  • [allvars] 定义属于组all的变量
  • [webs] 定义一个组,就像[dbs]
  • 文件的其余部分只是主机的声明带有它们的名称和IP
  • 空行表示声明结束

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

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

下面是命令输出:

dbserver | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
web1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
web2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

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

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

  • -a <command>
$ ansible -i inventory all -a uptime
web1 | SUCCESS | rc=0 >>
 21:43:27 up 25 min,  1 user,  load average: 0.00, 0.01, 0.05

dbserver | SUCCESS | rc=0 >>
 21:43:27 up 24 min,  1 user,  load average: 0.00, 0.01, 0.05

web2 | SUCCESS | rc=0 >>
 21:43:27 up 25 min,  1 user,  load average: 0.00, 0.01, 0.05

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

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

Playbook

Playbook 只是 YAML 文件它将inventory中的服务器组与命令关联。ansible的正确用法是任务它可以是期望的状态shell 命令或许多其他选项。有关 ansible 可做的所有事情列表,可以查看所有模块的列表。

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

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

用下面的命令运行它:

$ ansible-playbook -i inventory playbook1.yml

PLAY [all] *********************************************************************

TASK [setup] *******************************************************************
ok: [web1]
ok: [web2]
ok: [dbmaster]

TASK [command] *****************************************************************
changed: [web1]
changed: [web2]
changed: [dbmaster]

PLAY RECAP *********************************************************************
dbmaster                   : ok=2    changed=1    unreachable=0    failed=0
web1                       : ok=2    changed=1    unreachable=0    failed=0
web2                       : ok=2    changed=1    unreachable=0    failed=0

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

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

安装软件

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

---
- hosts: webs
  become_user: root
  become: true
  tasks:
    - apt: name=git state=present

有可以应用于 ansible 中所有模块的语句; 一个是 name 语句,让我们可以打印关于正在执行的任务的更具描述性的文本。要使用它,任务还是一样,但是添加 name 字段:描述性文本作为第一行,所以我们以前的文本将是:

---
- hosts: webs
  become_user: root
  become: true
  tasks:
    - name: This task will make sure git is present on the system
      apt: name=git state=present

使用 with_items

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

---
- hosts: all
  become_user: root
  become: true
  tasks:
    - name: Installing dependencies
      apt: name={{item}} state=present
      with_items:
        - git
        - mysql-client
        - libmysqlclient-dev
        - build-essential
        - python-software-properties

使用 templatevars

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

---
- hosts: all
  vars:
    - secret_key: VqnzCLdCV9a3jK
    - path_to_vault: /opt/very/deep/path
  tasks:
    - name: Setting a configuration file using template
      template: src=myconfig.j2 dest={{path_to_vault}}/app.conf

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

├── Vagrantfile
├── inventory
├── playbook1.yml
├── playbook2.yml
└── templates
    └── myconfig.j2

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

示例模板:

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

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

10.1.1.11 web1
10.1.1.12 web2
10.1.1.21 dbserver

这里要求像这样的语句:

  -  name: Installing the hosts file in all servers
     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_oncetrue。

    - name: 'Run db:migrate'
      shell: cd {{appdir}};rails db:migrate
      run_once: true
会失败的任务

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

    - name: 'Delete logs'
      shell: rm -f /var/log/nginx/errors.log
      ignore_errors: true
放到一起

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

Vagrantfile

VMs = [
    [ "web1", "10.1.1.11"],
    [ "web2", "10.1.1.12"],
    [ "dbserver", "10.1.1.21"],
  ]

Vagrant.configure(2) do |config|
  VMs.each { |vm|
    config.vm.define vm[0] do |box|
      box.vm.box = "ubuntu/trusty64"
      box.vm.network "private_network", ip: vm[1]
      box.vm.hostname = vm[0]
      box.vm.provider "virtualbox" do |vb|
         vb.memory = "512"
      end
    end
  }
end

inventory:

[all:children]
webs
db

[all:vars]
ansible_user=vagrant
ansible_ssh_pass=vagrant

[webs]
web1 ansible_host=10.1.1.11
web2 ansible_host=10.1.1.12

[db]
dbserver ansible_host=10.1.1.21

templates/hosts.j2:

10.1.1.11 web1
10.1.1.12 web2
10.1.1.21 dbserver

templates/my.cnf.j2:

[client]
port        = 3306
socket      = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket      = /var/run/mysqld/mysqld.sock
nice        = 0

[mysqld]
server-id   = 1
user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 3306
basedir     = /usr
datadir     = /var/lib/mysql
tmpdir      = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
bind-address        = 0.0.0.0
key_buffer      = 16M
max_allowed_packet  = 16M
thread_stack        = 192K
thread_cache_size       = 8
myisam-recover         = BACKUP
query_cache_limit   = 1M
query_cache_size        = 16M
log_error = /var/log/mysql/error.log
expire_logs_days    = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet  = 16M

[mysql]

[isamchk]
key_buffer      = 16M

!includedir /etc/mysql/conf.d/

final-playbook.yml:

- hosts: all
  become_user: root
  become: true
  tasks:
    - name: 'Install common software on all servers'
      apt: name={{item}} state=present
      with_items:
        - git
        - mysql-client
        - libmysqlclient-dev
        - build-essential
        - python-software-properties
    - name: 'Install hosts file'
      template: src=hosts.j2 dest=/etc/hosts mode=644

- hosts: db
  become_user: root
  become: true
  tasks:
    - name: 'Software for DB server'
      apt: name={{item}} state=present
      with_items:
        - mysql-server
        - percona-xtrabackup
        - mytop
        - mysql-utilities
    - name: 'MySQL config file'
      template: src=my.cnf.j2 dest=/etc/mysql/my.cnf
    - name: 'Restart MySQL'
      service: name=mysql state=restarted
    - name: 'Grant access to web app servers'
      shell: echo 'GRANT ALL PRIVILEGES ON *.* TO "root"@"%" WITH GRANT OPTION;FLUSH PRIVILEGES;'|mysql -u root mysql

- hosts: webs
  vars:
    - appdir: /opt/dummyapp
  become_user: root
  become: true
  tasks:
    - name: 'Add ruby-ng repo'
      apt_repository: repo='ppa:brightbox/ruby-ng'
    - name: 'Install rails software'
      apt: name={{item}} state=present
      with_items:
        - ruby-dev
        - ruby-all-dev
        - ruby2.2
        - ruby2.2-dev
        - ruby-switch
        - libcurl4-openssl-dev
        - libssl-dev
        - zlib1g-dev
        - nodejs
    - name: 'Set ruby to 2.2'
      shell: ruby-switch --set ruby2.2
    - name: 'Install gems'
      shell: gem install bundler rails
    - name: 'Kill puma if running'
      shell: file /run/puma.pid >/dev/null && kill `cat /run/puma.pid` 2>/dev/null
      ignore_errors: True
    - name: 'Clone app repo'
      git:
           repo=https://github.com/c0d5x/rails_dummyapp.git
           dest={{appdir}}
           version=staging
           force=yes
    - name: 'Run bundler'
      shell: cd {{appdir}};bundler
    - name: 'Run db:setup'
      shell: cd {{appdir}};rails db:setup
      run_once: true
    - name: 'Run db:migrate'
      shell: cd {{appdir}};rails db:migrate
      run_once: true
    - name: 'Run rails server'
      shell: cd {{appdir}};rails server -b 0.0.0.0 -p 80 --pid /run/puma.pid -d

打开你的环境

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

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

部署新的代码

确保修改了代码并push到了仓库中。接下来确保你git语句中有正确的分支

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

作为一个例子你可以在master上修改version字段再次运行 playbook

ansible-playbook -i inventory final-playbook.yml

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

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

接下来是什么

这只是可以做的很小一部分。我们没有接触角色、过滤器、调试器等许多其他很棒的功能,但我希望它给了你一个良好的开始!所以,请继续学习并使用它。如果你有任何问题,你可以在 twitter 或评论栏联系我,让我知道你还想知道哪些关于 ansible 的东西!


via: https://gorillalogic.com/blog/getting-started-with-ansible/?utm_source=webopsweekly&utm_medium=email

作者:JOSE HIDALGO

译者:geekpi

校对:校对者ID

本文由 LCTT 组织编译,Linux中国 荣誉推出