Merge pull request #4840 from geekpi/master

translated
This commit is contained in:
geekpi 2016-12-30 13:55:16 +08:00 committed by GitHub
commit f750bfe185
2 changed files with 603 additions and 602 deletions

View File

@ -1,602 +0,0 @@
GETTING STARTED WITH ANSIBLE
==========
This is a crash course on Ansible that you can also use as a template for small projects or to get you into this awesome tool. By the end of this guide, you will know enough to automate server configurations, deployments and more.
### What is Ansible and why you should care ?
Ansible is a configuration management system known for its simplicity. You only need ssh access to your servers or equipment. It also differs from other options because it pushes changes instead of pulling like puppet or chef normally do. You can deploy code to any number of servers, configure network equipment or automate anything in your infrastructure.
#### Requirements
Its assumed that you are using Mac or Linux as your workstation, Ubuntu Trusty for your servers and have some experience installing packages. Also, you will need the following software on your computer. So, if you dont have them already, go ahead and install:
- Virtualbox
- Vagrant
- Mac users: Homebrew
#### Scenario
We are going to emulate 2 web application servers connecting to a MySQL database. The web application uses Rails 5 with Puma.
### Preparations
#### Vagrantfile
Create a folder for this project and save the following content in a file called: 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
```
### Configure your virtual network
We want our VMs to talk to each other, but dont let that traffic go out to your real network, so we are going to create aHost-Only adapter in Virtualbox.
1. Open Virtualbox
2. Go to Preferences
3. Go to Network
4. Click on Host-Only networks
5. Click to add a network
6. Click on Adapter
7. Set IPv4 to 10.1.1.1, IPv4 Network Mark: 255.255.255.0
8. Click Ok
#### Test your VMs and virtual network
In a terminal, in the directory for this project where you have the Vagrantfile, type the following command:
```
vagrant up
```
This will create your VMs so it may take a while. Check that everything worked by typing this command and verifying the output:
```
$ 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`.
```
Now log into each one of the VMs using user & password vagrant and the IPs in the Vagrantfile, this will validate the VMs and add their keys to your known hosts file.
```
ssh vagrant@10.1.1.11 # password is `vagrant`
ssh vagrant@10.1.1.12
ssh vagrant@10.1.1.21
```
Congratulations! Now you have servers to play with. Here comes the exiting part!
### Install Ansible
For Mac users:
```
$ brew install ansible
```
For Ubuntu users:
```
$ sudo apt install ansible
```
Make sure you got a recent version of ansible that is 2.1 or superior:
```
$ ansible --version
ansible 2.1.1.0
```
### The Inventory
Ansible uses an inventory to know what servers to work with and how to group them to perform tasks(in parallel). Lets create our inventory for this project and name it inventory in the same folder as the 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
```
- `[all:children]` defines a group(all) of groups
- `[all:vars]` defines variables that belong to the group all
- `[webs]` defines a group just like [dbs]
- The rest of the file is just declarations of hosts, with their names and IPs
- A blank line means end of a declaration
Now that we have an inventory we can start using ansible from the command line, specifying a host or a group to perform commands. Here is a typical example of a command to check connectivity to your servers:
```
$ ansible -i inventory all -m ping
```
- `-i` specifies the inventory file
- `all` specifies the server or group of servers to operate
- `-m` specifies an ansible module, in this case ping
Here is the output of this command:
```
dbserver | SUCCESS => {
"changed": false,
"ping": "pong"
}
web1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
web2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
```
Note that servers respond with a different order. This only depends on who responds first, but is not relevant, because ansible keeps the status of each server separate.
You can also run any command using another switch:
- `-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
```
Here is another example with only one server:
```
$ 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% /
```
### Playbooks
Playbooks are just YAML files that associate groups of servers in an inventory with commands. The correct word in ansible is tasks, and it can be a desired state, a shell command, or many other options. For a list of all the things you can do with ansible take a look at the list of all modules.
Here is an example of a playbook for running a shell command, save this as playbook1.yml:
```
---
- hosts: all
tasks:
- shell: uptime
```
- `---` is the start of the YAML file
- `- hosts`: specifies what group is going to be used
- `tasks`: marks the start of a list of tasks
- `- shell`: specifies the first task using the shell module
- REMEMBER: YAML requires indentation so make sure you are always following the correct structure in your playbooks
Run it with:
```
$ 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
```
As you can see ansible ran 2 tasks, instead of just one we have in our playbook. The TASK [setup] is an implicit task that runs first to capture information of the servers like hostnames, IPs, distributions, and many more details, that information can then be used to run conditional tasks.
There is also a final PLAY RECAP where ansible shows how many tasks ran and the corresponding state for each. In our case, since we ran a shell command, ansible doesnt know the resulting state and its then considered as changed.
### Installing Software
We are going to use apt to install software on our servers, for this we need to be root, so we have to use the become statement, save this content in playbook2.yml and run it(ansible-playbook playbook2.yml):
```
---
- hosts: webs
become_user: root
become: true
tasks:
- apt: name=git state=present
```
There are statements you can apply to all modules in ansible; one is the name statement that lets you print a more descriptive text about the task being executed. In order to use it you keep your task the same but add name: descriptive text as the first line, so our previous text will be:
```
---
- 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
```
### Using `with_items`
When you are dealing with a list of items, packages to install, files to create, etc. ansible provides with_items. Here is how we use it in our playbook3.yml, adding at the same time some other statements we already know:
```
---
- 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
```
### Using `template` and `vars`
`vars` is one statement that defines variables you can use either in `task` statements or inside `template` files. Jinja2 is the templating engine used in Ansible, but you dont need to learn a lot about it to use it. Define variables in your playbook like this:
```
---
- 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
```
As you can see I can use {{path_to_vault}} as part of the playbook, but also since I am using a template statement, I can use any variable inside the myconfig.j2 file, which has to be stored in a subfolder called templates. Your project tree should look like:
```
├── Vagrantfile
├── inventory
├── playbook1.yml
├── playbook2.yml
└── templates
└── myconfig.j2
```
When ansible finds a template statement it will look into the templates folder and expand the variables surrounded by{{ and }}.
Example template:
```
this is just an example vault_dir: {{path_to_vault}} secret_password: {{secret_key}}
```
You can also use `template` even if you are not expanding variables. I do this in advance considering I may add them later. For example, lets create a `hosts.j2` template and add the hostnames and IPs:
```
10.1.1.11 web1
10.1.1.12 web2
10.1.1.21 dbserver
```
This will require a statement like this:
```
- name: Installing the hosts file in all servers
template: src=hosts.j2 dest=/etc/hosts mode=644
```
### Shell commands
You should always try to use modules because Ansible can track the state of the task and avoid repeating it unnecessarily, but there are times when a shell command is unavoidable. For those cases Ansible offers two options:
- command: Literally just running a command without environment variables or redirections (|, <, >, etc.)
- shell: Runs /bin/sh and expands variables and redirections
#### Other useful modules
- apt_repository Add/Remove package repositories in Debian family
- yum_repository Add/Remove package repositories in RedHat family
- service Start/Stop/Restart/Enable/Disable services
- git Deploy code from a git server
- unarchive Unarchive packages from the web or local sources
#### Running a task only in one server
Rails uses `migrations` to make gradual changes to your DB, but since you have more than one app server, these migrations can not be assigned as a group task, instead we need only one server to run the migrations. In cases like this is when run_once is used, run_once will delegate the task to one server and continue with the next task until this task is done. You only need to set run_once: true in your task.
```
- name: 'Run db:migrate'
shell: cd {{appdir}};rails db:migrate
run_once: true
```
##### Tasks that can fail
By specifying ignore_errors: true you can run a task that may fail but doesnt affect the completion of the rest of your playbook. This is useful, for example, when deleting a log file that initially will not exist.
```
- name: 'Delete logs'
shell: rm -f /var/log/nginx/errors.log
ignore_errors: true
```
##### Putting it all together
Now using what we previously learned, here is the final version of each file:
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
```
### Turn up your environment
Having these files in the same directory, turn up your dev environment by running:
```
vagrant up
ansible-playbook -i inventory final-playbook.yml
```
#### Deployment of new code
Make changes to your code and push those changes to your repo. Then, simply make sure you have the correct branch in your git statement:
```
- name: 'Clone app repo'
git:
repo=https://github.com/c0d5x/rails_dummyapp.git
dest={{appdir}}
version=staging
force=yes
```
As an example, you can change the version field with master, run the playbook again:
```
ansible-playbook -i inventory final-playbook.yml
```
Check that the page has changed on any of the web servers: `http://10.1.1.11` or `http://10.1.1.12`. Change it back to `version=staging` and rerun the playbook and check the page again.
You can also create an alternative playbook that has only the tasks related to the deployment so that it runs faster.
### What is next !?
This is a very small portion of what ansible can do. We didnt touch roles, filters, debugor many other awesome features that it offers, but hopefully it gives you a good start! So, go ahead and start using it and learn as you go. If you have any questions you can reach me on twitter or comment below and let me know what else youd like to find out about ansible!
--------------------------------------------------------------------------------
via: https://gorillalogic.com/blog/getting-started-with-ansible/?utm_source=webopsweekly&utm_medium=email
作者:[JOSE HIDALGO][a]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 组织编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://gorillalogic.com/author/josehidalgo/

View File

@ -0,0 +1,603 @@
开始使用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
```
### 使用 `template``vars`
`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.11` 或 `http// 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][a]
译者:[geekpi](https://github.com/geekpi)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 组织编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://gorillalogic.com/author/josehidalgo/