对于想要开始使用Ansible的人
这篇文章所写的内容
这篇文章是针对初学者而写的,总结了以下关于入门 Ansible 的内容。
-
- 什么是Ansible
-
- 查找Ansible
-
- 尝试使用Ansible
- 进一步尝试使用Ansible
如果使用术语,面向已经了解的人。
「通过jinja模板实现文件的动态配置以及通过角色划分任务,一直进行实操操作」
在中国,仅需一个选项进行本地化解释:
及其后面的句子:唯Python版本是2.6.6,另外还使用了Ansible 1.9.4。
Ansible是什么
Ansible是一种被称为“配置管理工具”的工具,近期(至少在我的认知中)成为了一个热门话题。
在“基础设施即代码”这样的语境下,我认为它是与Chef、Puppet等工具并列出现的必备工具之一。
这里的谈话一旦开始,就可以单凭这些话题写篇文章,所以我会附上已经公开的可贵资料的链接。
- AnsibleによるInfrastructure as code入門 ( @kk_Ataka )
資料中已提到,如果专注于谈论构建管理工具Ansible的特点。
-
- エージェントレスな構成管理ツール
-
- サーバはPython 2.6+、ホストはPython 2.4+があれば動作可能
-
- 設定ファイルが少なく、記述がシンプル
- シンプルさ故に複雑構成は苦手
我想有以下的一些方面:
我认为可以提出以下的一些点:
Ansible的起源
因为我个人对产品名称和缩写的来历很感兴趣,所以我也研究了Ansible。
- アンシブル – wikipedia
看起来是这些家伙。这是出现在科幻小说中的 “超光速通信” 技术名称。
“就像暴风一样超光速搭建服务器”
这样的理解可以吗?
- Ansibleが登場するSF小説「エンダーのゲーム」読んだ
看起来IT圈的人觉得很有意思,我想点一下试试看。
研究Ansible
如果想要了解Ansible,请问除了谷歌搜索之外,大致有以下几种方法可供选择。
-
- Ansible本を読む
-
- 公式ドキュメントを読む
- Ansible(のソースコード)を読む
阅读Ansible的书籍
- 入門Ansible
意外的是,关于以Ansible为主题的日语书籍可能目前只有这一本。虽然如此,在本文中总结的内容之外,还包含了更进一步的内容,所以我认为没有特别的不方便。
由于本书是以撰写时的最新版本 Ansible 1.6.6 为基础编写的,因此需要特别注意这一点。但是,由于即将发布的 Ansible 2.0 也将保持兼容性,所以也许不需要过于担心。
阅读公式文档
无论是什么类型的软件,我认为官方文件都非常重要。
- Ansible Documentation
很遗憾,这段文字只用英文写成,但是我个人认为用Sphinx写的文本结构清晰,并且使用了一种字体,给人一种舒适易读的感觉。
对不起,我觉得这篇文章的结构很好,非常容易阅读。
在实际使用Ansible的过程中,我经常阅读Playbooks和Module Index。
阅读Ansible的源代码。
- ansible/ansible – GitHub
这个说法是半开玩笑、半认真的,但从理论上讲,如果在书或官方文件上找不到答案,阅读源代码是最好的选择。
然而,因为我还没有遇到过十分紧急的情况,所以从未认真地读过源代码。
我还在尝试使用Ansible 2.0,因为开发版本也在这里。
试用Ansible
在了解工具方面,“百闻不如一见”是最好的方式,因此我认为最好的方式是动手实践。Ansible只需要最小的两个文件即可运行,所以动手实践的门槛非常低。
好的,我们决定通过使用Vagrant + VirtualBox来准备实验环境。
引用:Vagrant + VirtualBox的安装指南
-
- Vagrant
- VirtualBox
请根据各自的环境下载并安装相应的软件。
基本上按照安装程序的指引进行操作应该没有问题。
安装后,只需通过以下命令确认版本即可。虽然我以Windows的风格来写,但Mac和Linux用户可以根据需要适当进行调整阅读。
C:\> vagrant -v
Vagrant 1.7.4
C:\> VBoxManage -v
5.0.12r104815
所以我们将使用更高版本创建Hands-on环境。
准备用于Ansible实践的虚拟机
在一个适当的文件夹中创建与Vagrant相关的文件。
C:\> mkdir vagrant
C:\> cd vagrant
C:\vagrant> vagrant init
执行 “vagrant init” 命令后,将在当前目录中生成一个 Vagrantfile 文件。
该 Vagrantfile 文件是用于配置由 Vagrant 启动的虚拟机的设置文件,请按照以下方式编写。
Vagrant.configure(2) do |config|
config.vm.define "controller" do |node|
node.vm.box = "centos6.7"
node.vm.hostname = "controller"
node.vm.network :private_network, ip: "192.168.100.10"
node.vm.network :forwarded_port, id: "ssh", guest: 22, host: 2210
end
config.vm.define "target" do |node|
node.vm.box = "centos6.7"
node.vm.hostname = "target"
node.vm.network :private_network, ip: "192.168.100.20"
node.vm.network :forwarded_port, id: "ssh", guest: 22, host: 2220
end
end
关于详细描述,我会省略,但我想你应该大致能够想象出内容来。
就用途而言,这是在控制器端执行Ansible,目标是成为其对象的感觉。
好的,每个 VM 都写着要基于 CentOS 6.7 的 Box,这就需要我们做好准备。
让我们执行以下命令吧。
vagrant box add centos6.7 https://github.com/CommanderK5/packer-centos-template/releases/download/0.6.7/vagrant-centos-6.7.box
我正在将这个是Vagrant中的虚拟机模板称为Box的文件添加到本地。这次我选择的是CentOS 6.7,没有特别的意义,你也可以在Vagrantbox.es上选择你喜欢的。请确保在添加Box和指定Vagrantfile中的Box名称时保持一致。
当您成功下载了Box之后,让我们在包含vagrantfile的文件夹内执行vagrant up命令。
C:\vagrant>vagrant up
Bringing machine 'controller' up with 'virtualbox' provider...
Bringing machine 'target' up with 'virtualbox' provider...
(略)
为了确保,请检查虚拟机的状态。请使用vagrant status命令。
C:\vagrant>vagrant status
Current machine states:
controller running (virtualbox)
target 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`.
安装Ansible
那么,让我们在控制器上引入Ansible吧。
我们在Vagrantfile中设置了将SSH转发到本地主机的localhost:2210。
我觉得想一想很麻烦,所以我写了一个teraterm宏。如果你愿意,可以试试看用一下(当然,连接使用Putty或者MobaXterm都可以)。
USERNAME = 'root'
PASSWORD = 'vagrant'
MESSAGE = 'Please choose a connection host.'#13#13
strconcat MESSAGE ' 1. controller'#13
strconcat MESSAGE ' 2. target'#13
inputbox MESSAGE 'Input host number.'
str2int hostnum inputstr
if hostnum = 1 then
HOSTADDR = '127.0.0.1:2210'
elseif hostnum = 2 then
HOSTADDR = '127.0.0.1:2220'
else
messagebox 'Input number 1 or 2.' 'Input error'
end
endif
COMMAND = HOSTADDR
strconcat COMMAND ' /ssh /2 /auth=password /user='
strconcat COMMAND USERNAME
strconcat COMMAND ' /passwd='
strconcat COMMAND PASSWORD
connect COMMAND
end
由于Ansible服务器的Ansible安装要求是Python 2.6以上,所以为了确保准确,我会再次确认一下。
[root@controller ~]# python --version
Python 2.6.6
让我们使用yum安装Ansible。
[root@controller ~]# yum install ansible
(略)
Installed:
ansible.noarch 0:1.9.4-1.el6
Dependency Installed:
PyYAML.x86_64 0:3.10-3.1.el6 libyaml.x86_64 0:0.1.3-4.el6_6
python-babel.noarch 0:0.9.4-5.1.el6 python-crypto.x86_64 0:2.0.1-22.el6
python-crypto2.6.x86_64 0:2.6.1-2.el6 python-httplib2.noarch 0:0.7.7-1.el6
python-jinja2.x86_64 0:2.2.1-2.el6_5 python-keyczar.noarch 0:0.71c-1.el6
python-paramiko.noarch 0:1.7.5-2.1.el6 python-pyasn1.noarch 0:0.0.12a-1.el6
python-setuptools.noarch 0:0.6.10-3.el6 python-simplejson.x86_64 0:2.0.9-3.1.el6
sshpass.x86_64 0:1.05-1.el6
Complete!
根据依赖关系,会安装各种不同的内容。
[root@controller ~]# ansible --version
ansible 1.9.4
因此,Ansible 1.9.4 已成功安装。
请在这里输入”ansible”并尝试使用Tab键来进行自动补全。
[root@controller ~]# ansible
ansible ansible-doc ansible-galaxy ansible-playbook ansible-pull ansible-vault
出现了很多东西,不过在这次的实践中基本上只会使用ansible-playbook。希望不会给你带来不便。
Ansible执行测试
在Ansible中,通过Playbook来编写目标配置步骤,但一开始要使用一行命令进行通信确认。
Ansible是一种无需代理的配置管理工具,使用SSH进行通信。
因此,为了能够从Ansible服务器登录到目标服务器,需要将公钥发送过去。
[root@controller ~]# ssh-keygen -t rsa
(パスフレーズは入力しない)
[root@controller ~]# ssh-copy-id root@192.168.100.20
(接続確認が出るのでyes)
通过这种方式,即使从 controller 运行 ssh root@192.168.100.20,也不会要求输入密码。 (出于操作演示的目的,我现在先用 root 账户运行 Ansible。请注意这并不符合道德规范,请注意。)
现在可以开始执行Ansible了。我们需要创建一个包含目标主机的inventory文件。
[root@controller ~]# mkdir /ansible
[root@controller ~]# cd /ansible
[root@controller ansible]# mkdir inventory
[root@controller ansible]# vi inventory/hosts
[targets]
192.168.100.20
在这个inventory文件中,将主机192.168.100.20分配给名为”targets”的组。
虽然在本次实操中只处理了一台主机,但实际使用时可能会在分组方面遇到各种问题和困惑。
让我们在漫长的路途后确认沟通。
[root@controller ansible] ansible all -i inventory/hosts -m ping
192.168.100.20 | success >> {
"changed": false,
"ping": "pong"
}
在这个命令中,使用ping模块对先前提到的inventory文件中的所有主机进行连通性检查。
结果将以 [主机] | [结果] >> [消息] 的形式显示出来。
尝试使用Playbook
好吧,让我们认真进行实践,边制作Playbook边进行操作。
让我们尝试将以下文件放置在这里。
message: "Hello Ansible !"
fruits:
apples:
amount: 10
bananas:
amount: 20
oranges:
amount: 30
在此文件中,我们描述了组变量。
我们使用 YAML 格式来描述,这也可以从文件扩展名中看出来。
YAML 是 YAML Ain’t Markup Language 的缩写,在 IT 行业中有时可以看到它作为递归定义的一种。还有一些其他著名的例如 GNU(GNU is Not Unix),PHP(PHP: Hypertext Processor),RPM(RPM Package Manager)。我有点喜欢类似于“何言うてまんねん”的感觉。
从官方网站可以看出,它有非常精确的定义,但在 Ansible 中,只要理解了 “数组” 和 “序列” 这类受限制的范围就足够了。
YAML语法
- hosts: targets
user: root
tasks:
- name: output message.
debug: msg="{{ message }}"
- name: output fruits
debug: msg="We want {{ item.value.amount }} {{ item.key }} !"
with_dict: "{{ fruits }}"
在中国,Playbook被称为这个文件。这也可以用YAML简洁地描述。让我们先执行,然后再解释。
[root@controller ansible]# ansible-playbook -i inventory/hosts test.yml
PLAY [targets] ****************************************************************
GATHERING FACTS ***************************************************************
ok: [192.168.100.20]
TASK: [output message.] *******************************************************
ok: [192.168.100.20] => {
"msg": "Hello Ansible !"
}
TASK: [output fruits] *********************************************************
ok: [192.168.100.20] => (item={'key': 'apples', 'value': {'amount': 10}}) => {
"item": {
"key": "apples",
"value": {
"amount": 10
}
},
"msg": "We want 10 apples !"
}
ok: [192.168.100.20] => (item={'key': 'oranges', 'value': {'amount': 30}}) => {
"item": {
"key": "oranges",
"value": {
"amount": 30
}
},
"msg": "We want 30 oranges !"
}
ok: [192.168.100.20] => (item={'key': 'bananas', 'value': {'amount': 20}}) => {
"item": {
"key": "bananas",
"value": {
"amount": 20
}
},
"msg": "We want 20 bananas !"
}
PLAY RECAP ********************************************************************
192.168.100.20 : ok=3 changed=0 unreachable=0 failed=0
像是這樣的感覺。
我希望你注意到執行的指令,但是我所使用的指令是
-
- inventoryファイル
- Playbook
通过查看执行结果,可以看出已经反映了在group_vars中定义的变量到目标组中。在这个模式中,由于在test.yml中指定了hosts: targets这个组作为执行目标,因此会自动加载targets.yml文件从group_vars中。
如此,由于Ansible会隐式地从特定目录中获取信息,因此需要理解这些依赖关系。但是,一旦理解了这些依赖关系,就可以通过简洁的描述来灵活地重复使用,因此强烈建议您充分理解。
另外,在每个任务中,我们只是使用了调试模块输出了变量,但请注意以字典形式编写的水果使用方式。
fruits:
apples:
amount: 10
bananas:
amount: 20
oranges:
amount: 30
- name: output fruits
debug: msg="We want {{ item.value.amount }} {{ item.key }} !"
with_dict: fruits
首先,该任务将使用with_dict从fruits变量中提取值,并通过debug模块输出消息。
每次循环提取的元素都会存储在预定义的变量item中,可以在处理过程中使用。
此时,可以通过item.key获取位于fruits下的水果名称,通过item.value.amount获取水果名称下的数量。
对于这个地方,您可能会感到有些不习惯,但是在写作的过程中,您会渐渐掌握它,所以我建议您边使用debug模块,边坚持不懈地尝试一下。
多试试Ansible
在此之前,我們已經在inventory文件、Playbook以及group_vars等文件中實際運行了Ansible。我們進行了包安裝、文件創建等操作,進行了配置管理。讓我們來實際操作一下吧。
包裝
可以使用yum模块等进行包管理操作。以下是一个示例:
- name: install packages from yum
yum: name={{ item }} state=latest
with_items:
- jq
- ruby
- httpd
这是一种最简单的写法,安装指定软件包的最新版本(latest)。还可以通过选项指定使用的yum仓库等。
此外,在上面的代码中,表示 “jq、ruby、httpd已安装”,但如果设置为 state=absent,则表示未安装的代码。
定时任务
也可以使用cron模块来设置作业。
- name: register cron job
cron: name="check ping" day="*/2" hour="12" minute="0" job="ping -c 3 192.168.100.10"
出于个人的角度而言,每次编写cron时我都会查找日期和时间的正确顺序,因此这个方法非常简单易懂,非常好用。-玩笑的
文件目录
在Ansible中,您当然可以创建目录并配置文件。
创建目录
在中文中,创建目录是通过 file 模块来完成的。
- name: create directories
file: path={{ item.path }} owner={{ item.owner }} group={{ item.group }} mode=0{{ item.mode }} state=directory
with_items:
- { "path":"/opt/ansible", "owner":"root", "group":"root", "mode":"755" }
- { "path":"/opt/vagrant", "owner":"vagrant", "group":"vagrant", "mode":"755" }
创建目录时,请使用state=directory,但还有其他选择,如link、file等。
与yum时一样,如果指定state=absent,path中的文件将被删除。
请注意,文件模块接受的mode应为八进制数,这也在文档中有说明。
请记住,模式实际上是八进制数(例如0644)。省略前导零可能会导致意外结果。
此外,似乎从Ansible 1.8版本开始,也可以使用符号(如u+rwx)来指定mode。
静态文件配置 de
静态文件配置即为文件复制。copy模块将Ansible服务器上保存的文件配置到主机。
- name: copy files
copy: src=./files/hoge dest=/opt/ansible/hoge owner=root group=root mode=0755
动态文件配置
利用template模块,在文件复制过程中展开变量并进行定位。
- name: copy template files
template: src=./templates/fuga.j2 dest=/opt/ansible/fuga owner=root group=root mode=0755
需要以 jinja2 格式来描述作为副本来源使用的模板文件。
This is a jinja template file.
{{ message }}
jinja template can extract variables. like, ...
{% for key,value in fruits.iteritems() %}
We want {{ value.amount }} {{ key }} !
{% endfor %}
Jinja2和YAML一样有很多规定,但不需要掌握全部。
模板设计师文档
如果您能理解这个内容,那应该基本上就可以了吧。在Jinja模板中,您也可以像Playbook内部一样使用变量。虽然您也可以像使用with_dict一样使用for循环进行迭代,但在这里您可以直接使用,而无需使用item。
让我们试试动起来。
让我们把之前的所有内容都整合在一起。
- hosts: targets
user: root
tasks:
- name: install packages from yum
yum: name={{ item }} state=latest
with_items:
- jq
- ruby
- httpd
- name: register cron job
cron: name="check ping" day="*/2" hour="12" minute="0" job="ping -c 3 192.168.100.10"
- name: create directories
file: path={{ item.path }} owner={{ item.owner }} group={{ item.group }} mode=0{{ item.mode }} state=directory
with_items:
- { "path":"/opt/ansible", "owner":"root", "group":"root", "mode":"755" }
- { "path":"/opt/vagrant", "owner":"vagrant", "group":"vagrant", "mode":"755" }
- name: copy files
copy: src=./files/hoge dest=/opt/ansible/hoge owner=root group=root mode=0755
- name: copy template files
template: src=./templates/fuga.j2 dest=/opt/ansible/fuga owner=root group=root mode=0755
我想,如果做到这个程度,可能会变成这样的感觉。
ansible/
├─inventory/
│ └─hosts
├─group_vars/
│ └─targets.yml
├─files/
│ └─hoge
├─templates/
│ └─fuga.j2
├─test.yml
└─main.yml
因此,由於我的擔心,我們先使用檢查模式運行一下,這樣可以讓我們確認在執行 Playbook 時哪些部分會被更改。一般而言,這種模式被稱為乾跑。當使用 –check 選項時,程式將運行於檢查模式下。
[root@controller ansible]# ansible-playbook --check -i inventory/hosts main.yml
PLAY [targets] ****************************************************************
GATHERING FACTS ***************************************************************
ok: [192.168.100.20]
TASK: [install packages from yum] *********************************************
changed: [192.168.100.20] => (item=jq,ruby,httpd)
TASK: [register cron job] *****************************************************
skipping: [192.168.100.20]
ok: [192.168.100.20]
TASK: [create directories] ****************************************************
changed: [192.168.100.20] => (item={'owner': 'root', 'path': '/opt/ansible', 'group': 'root', 'mode': '755'})
changed: [192.168.100.20] => (item={'owner': 'vagrant', 'path': '/opt/vagrant', 'group': 'vagrant', 'mode': '755'})
TASK: [copy files] ************************************************************
changed: [192.168.100.20]
TASK: [copy template files] ***************************************************
changed: [192.168.100.20]
PLAY RECAP ********************************************************************
192.168.100.20 : ok=5 changed=4 unreachable=0 failed=0
嗯,也许命令本身是有效的,但我有一点担心的是 cron 的部分。
显示为 skipping,这是什么意思呢?
为了更详细查看,请使用 -v 选项。
[root@controller ansible]# ansible-playbook --check -i inventory/hosts main.yml -v
(略)
TASK: [register cron job] *****************************************************
skipping: [192.168.100.20]
ok: [192.168.100.20] => {"changed": false, "msg": "remote module does not support check mode", "skipped": true}
(略)
如Check Mode的文档中所述,由于并非所有模块都支持Check Mode,所以可能会跳过确认操作。
“检查模式”(其中包含大部分主要核心模块,但并不要求所有模块都必须支持此模式)。
顺便提一下,关于 -v 选项,每次重复使用v,消息会更加详细,使用 -vvv 时可以了解SSH的具体运作方式。
现在让我们来执行Playbook吧。
[root@controller ansible]# ansible-playbook -i inventory/hosts main.yml
PLAY [targets] ****************************************************************
GATHERING FACTS ***************************************************************
ok: [192.168.100.20]
TASK: [install packages from yum] *********************************************
changed: [192.168.100.20] => (item=jq,ruby,httpd)
TASK: [register cron job] *****************************************************
changed: [192.168.100.20]
TASK: [create directories] ****************************************************
changed: [192.168.100.20] => (item={'owner': 'root', 'path': '/opt/ansible', 'group': 'root', 'mode': '755'})
changed: [192.168.100.20] => (item={'owner': 'vagrant', 'path': '/opt/vagrant', 'group': 'vagrant', 'mode': '755'})
TASK: [copy files] ************************************************************
changed: [192.168.100.20]
TASK: [copy template files] ***************************************************
changed: [192.168.100.20]
PLAY RECAP ********************************************************************
192.168.100.20 : ok=6 changed=5 unreachable=0 failed=0
看起来一切顺利完成了。
结果包括收集事实的6个任务全部完成,顺利解决了问题,同时还有5个项目进行了更改。
检查Jinja模板的内容。
让我们来确认一下,jinja模板是否按照预期展开。
[root@controller ansible]# ssh root@192.168.100.20 cat /opt/ansible/fuga
This is a jinja template file.
Hello Ansible !
jinja template can extract variables. like, ...
We want 10 apples !
We want 30 oranges !
We want 20 bananas !
好的,正如你所想的那样。
幂等性
让我们在这里再次尝试执行同样的 Playbook。
[root@controller ansible]# ansible-playbook -i inventory/hosts main.yml
(略)
PLAY RECAP ********************************************************************
192.168.100.20 : ok=6 changed=0 unreachable=0 failed=0
当然没有进行任何更改,但这表明目标已达到了Playbook中指定的应有状态。
另外,由于目标已经处于应有状态,因此并未进行任何更改,从而保持了幂等性。
在配置管理工具中,这种幂等性被认为是重要的。例如,复制模块甚至不会执行文件的覆盖,但如果只是简单地用cp命令执行该操作,就会导致覆盖。当然,通过脚本编写可以确保这种幂等性,但会增加额外的代码,并且会变得很麻烦。确保这种繁琐的幂等性是配置管理工具的优势所在。(但是要注意,Ansible无法保证shell模块或command模块的幂等性,使用时需要注意。)
使用角色
在这里,任务被整合到一个YAML文件中进行描述,但这样做无法重复使用以创建相似的主机。
因此,让我们将部分任务拆分为角色,并进行组件化。
- hosts: targets
user: root
tasks:
- name: register cron job
cron: name="check ping" day="*/2" hour="12" minute="0" job="ping -c 3 192.168.100.10"
roles:
- role-common
- role-web
- name: install packages from yum
yum: name={{ item }} state=latest
with_items:
- jq
- ruby
- name: create directories
file: path={{ item.path }} owner={{ item.owner }} group={{ item.group }} mode=0{{ item.mode }} state=directory
with_items:
- { "path":"/opt/ansible", "owner":"root", "group":"root", "mode":"755" }
- name: copy files
copy: src=./files/hoge dest=/opt/ansible/hoge owner=root group=root mode=0755
- name: install packages from yum
yum: name={{ item }} state=latest
with_items:
- httpd
- name: create directories
file: path={{ item.path }} owner={{ item.owner }} group={{ item.group }} mode=0{{ item.mode }} state=directory
with_items:
- { "path":"/opt/vagrant", "owner":"vagrant", "group":"vagrant", "mode":"755" }
- name: copy template files
template: src=./templates/fuga.j2 dest=/opt/ansible/fuga owner=root group=root mode=0755
请不要说我对 with_items 的处理很随意,因为我只是简单地剪裁了一下(笑)。
ansible/
├─inventory/
│ └─hosts
├─group_vars/
│ └─targets.yml
├─files/
│ └─hoge
├─templates/
│ └─fuga.j2
├─roles/
│ ├─role-common/
│ │ └─tasks/
│ │ └─main.yml
│ └─role-web/
│ └─tasks/
│ └─main.yml
├─test.yml
├─main.yml
└─master.yml
通过 roles 命令调用的角色将在 roles 目录下根据名称进行查找,并执行 tasks 目录下的 main.yml 文件。
角色可以使用 dependencies 等来建立依赖关系,但本次不处理。
让我们执行吧。
[root@controller ansible]# ansible-playbook --check -i inventory/hosts master.yml
(略)
TASK: [role-common | install packages from yum] *******************************
ok: [192.168.100.20] => (item=jq,ruby)
(略)
PLAY RECAP ********************************************************************
192.168.100.20 : ok=7 changed=0 unreachable=0 failed=0
由于内容在逻辑上没有发生改变,因此没有使用”changed”一词,但是我认为在角色中包含的任务的显示方式发生了变化。
最后
完成操作后,请不要忘记关闭虚拟机。
C:\vagrant>vagrant halt
如果你想从头开始玩的话,也可以毫不留情地使用 “vagrant destroy” 命令。
(如果使用 sahara 插件的话,会更加轻松一些,但是解释会很繁琐啊…)
我之所以开始写这篇文章,是因为 2015 年的 Ansible Advent Calendar 有一格空缺,让我觉得有点不舒服。
同时,我也正好在想我周围的人未来可能会接触到 Ansible,需要什么样的资料呢~所以我决定顺便练习一下,写一篇这样的文章。
当我开始写这篇文章的时候,我一开始以为它只是入门级别,内容应该不会很多。然而,随着我逐步解释下去,它比我想象的更多了。
不过,我认为通过这次的解释,大体上已经掌握了Ansible的主要内容,接下来只需要根据需要去逐个查找了吧。
如果Ansible能变得更加流行,我会很高兴能够帮上忙。