ansbile实战应用系列教程3:部署ansible
目标:配置可执行的并运行临时命令
章节:
1、描述ansible inventory概念并建立静态inventory。
2、管理ansible的配置文件。
3、运行一个可执行的ad hoc命令(临时命令)。
4、动态库存管理。
创建ansible inventory
inventory文件
inventory定义了Ansible将要管理的主机的集合。还可以将这些主机分配给groups,可以对组进行集体管理。组可以包含子组,主机可以是多个组的成员。该目录还可以设置应用于其定义的主机和组的变量。
主机inventory可以用两种不同的方式定义。静态主机inventory可以由文本文件定义。动态主机目录可以根据需要由脚本或其他程序使用外部信息提供者生成。
静态inventory:
静态inventory文件是一个类似于ini的文本文件,它指定Ansible目标的托管主机。在其最简单的形式中,静态inventory是托管主机的主机名或IP地址的列表。
[sector1]
key1=value1
[sector2]
key2=value1
[sector3]
key3=value1
1、主机清单 可以是主机名,也可以是IP地址,只要能通信
web1.example.com
web2.example.com
db1.example.com
db2.example.com
192.0.2.42
验证:
[root@taijitao-server ansibledemo]# ansible -i inventory --list-hosts all
hosts (5):
web1.example.com
web2.example.com
db1.example.com
db2.example.com
192.0.2.42
2、分组
通常您将托管主机组织为主机组。主机组允许您更有效地针对一组系统运行可运行。在本例中,每个部分都以方括号([])括起来的主机组名开始。后面是组中每个托管主机的主机名或IP地址,每个主机在一行上。
在下面的示例中,主机目录定义了两个主机组:webservers和db-servers。
192.0.2.43 #该主机不属于任何组,将主机或者ip地址写到最上边。
[webservers]
web1.example.com
web2.example.com
[db-servers]
db1.example.com
db2.example.com
192.0.2.42
[east-datacenter]
web1.example.com
db1.example.com
[west-datacenter]
web2.example.com
db2.example.com
验证:
[root@taijitao-server ansibledemo]# ansible -i inventory --list-hosts webservers
hosts (2):
web1.example.com
web2.example.com
同一主机可以存在多个组中。实际上,推荐的做法是将您的主机组织成多个组,根据主机的角色、它的物理位置、是否在生产环境中等等,可能以不同的方式组织。这使您可以更容易地将ansible playbook应用到特定的主机。
Important |
---|
有两个组总是存在的: all:代表所有主机; ungrouped:未分组主机。 |
定义嵌套组
Ansible inventory可以包括嵌套主机组。这是通过:children后缀完成的。主机组以及children组必须出现在同一个inventory文件中。
格式:
[groupname:children]
groupname1
groupname2
下面的示例创建一个名为north-america的新组,其中包括来自usa和Canada组的所有主机。
[usa]
washington1.example.com
washington2.example.com
[canada]
ontario01.example.com
ontario02.example.com
[north-america:**children**]
canada
usa
验证:
[root@taijitao-server ansibledemo]# ansible -i inventory --list-hosts usa
hosts (2):
washington1.example.com
washington2.example.com
[root@taijitao-server ansibledemo]# ansible -i inventory --list-hosts north-america
hosts (4):
ontario01.example.com
ontario02.example.com
washington1.example.com
washington2.example.com
简化表达方式:
可以通过在主机名或IP地址中指定范围来简化主机inventory。可以指定数字范围,也支持字母范围。范围有以下语法:
格式:[start:end]
范围匹配从开始到结束的所有值。
示例:
192.168.4.[1:20]代表192.168.4.1-192.168.4.20
192.168.[4:7].[0:255]代表192.168.4.0-192.168.7.255
server[01:20].example.com代表01,02…20
[a:c].example.com 代表a b c
需要将以下内容放置在inventory文件的最上边。因为以下的四组主机没有归置在group中。
192.168.4.[1:20]
192.168.[4:7].[0:255]
server[01:20].example.com
[a:c].example.com
验证:
[root@taijitao-server ansibledemo]# ansible -i inventory --list-hosts 192.168.4.2
hosts (1):
192.168.4.2
覆盖inventory的位置
/etc/ansible/hosts文件被认为是系统默认的静态inventory文件。使用ansible和ansible-playbook命令,运行一个ad hoc命令和playbook后面的课程还可以指定一个目录文件的位置在命令行与–inventory路径名或-i路径名选项,其中,路径名是期望执行的inventory文件的路径。不加-i inventory参数,则使用/etc/ansible/hosts 默认位置。
管理ansible主机清单配置文件
/etc/ansible/hosts 是主机清单配置文件,由 ansible.cfg文件中的 inventory 变量配置,默认值为/etc/ansible/hosts
在使用ansible命令前,需要对hosts文件进行相关主机清单配置。
1.可以不对主机进行分组,如果不指定分组,需要配置在所有的分组前
2.可以对主机进行分组,中括号里包含的名字代表组名
3.主机可以使用域名,主机名,ip地址表示,一般以IP居多
4.常用内置参数
参数名称 | 作用说明 | 举例 |
---|---|---|
ansible_ssh_host | #用于指定被管理的主机的真实IP | ansible_ssh_host=192.168.1.1 |
ansible_ssh_port | #用于指定连接到被管理主机的ssh端口号,默认是22 | ansible_ssh_port=2000 |
ansible_ssh_user | #ssh连接时默认使用的用户名 | ansible_ssh_user=dba |
ansible_ssh_pass | #ssh连接时的密码 | ansible_ssh_pass=test1234 |
ansible_sudo | #sudo用户 | ansible_sudo=dba |
ansible_sudo_pass | #使用sudo连接用户时的密码 | ansible_ssh_pass=test1234 |
ansible_sudo_exec | #如果sudo命令不在默认路径,需要指定sudo命令路径 | ansible_sudo_exec=/usr/bin/sudo |
ansible_ssh_private_key_file | #秘钥文件路径,秘钥文件如果不想使用ssh-agent管理时可以使用此选项 | ansible_ssh_private_key_file=/root/key |
ansible_shell_type | #目标系统的shell的类型,默认sh | ansible_shell_type=bash |
ansible_connection | SSH 连接的类型: local , ssh , paramiko,在 ansible 1.2 之前默认是 paramiko ,后来智能选择,优先使用基于 ControlPersist 的 ssh (支持的前提) | ansible_connection=local |
ansible_python_interpreter | #用来指定python解释器的路径,默认为/usr/bin/python 同样可以指定ruby 、perl 的路径 | ansible_python_interpreter=/usr/bin/python2.7 |
ansible_*_interpreter | #其他解释器路径,用法和ansible_python_interpreter类似,这里"*"可以是ruby或才perl等其他语言 | ansible_*_interpreter=/usr/bin/ruby |
hosts文件写法举例
# vi /etc/ansible/hosts
# 不进行分组,放在所有分组前面,可以为server起个名字,可以显示加参数的方式,也可以直接写ip,分组时写法也一样
name1 ansible_ssh_host=172.16.79.231 ansible_ssh_user=dba ansible_ssh_pass=test1234
name2 ansible_ssh_host=172.16.79.232
172.16.79.233
# 中括号内为分组名,可以对某一类型的服务器进行分类,执行命令时,可按照组进行操作
[oraservers]
name1 ansible_ssh_host=172.16.79.231 ansible_ssh_user=dba ansible_ssh_pass=test1234
name2 ansible_ssh_host=172.16.79.232
172.16.79.233
[webservers]
web1 ansible_ssh_host=172.16.79.233
web2 ansible_ssh_host=172.16.79.234
# 带参数的群组,为组内所有的成员指定共同的参数,这样就不需要在每一个IP后面重复写系统的参数,格式为 [组名:vars]
[parserver]
name1 ansible_ssh_host=172.16.79.232
name2 ansible_ssh_host=172.16.79.233
[parserver:vars]
ansible_ssh_user=dba
ansible_ssh_pass=test1234
# 群组整合,格式为[<newgroup_name>:children], children底下为父群的子群组,可直接调用父组名
[group:chindren]
oraservers
webservers
# 连续的IP写法,表示192.168.1.20到192.168.1.50,共31台主机:
[test1]
name1 ansible_ssh_host=192.168.1.[20:50] ansible_ssh_user=“root” ansible_ssh_pass=“1234” ansible_ssh_port=22
# 放在所有分组前面,可以为server起个名字,
# 可以显示加参数的方式,也可以直接写ip,分组时写法也一样:
# 这里name1,name2为别名
name1 ansible_ssh_host=172.16.79.231 ansible_ssh_user=dba ansible_ssh_pass=test1234
name2 ansible_ssh_host=172.16.79.232
172.16.79.233
# 群组整合,格式为[<newgroup_name>:children],
# children底下为父群的子群组,可直接调用父组名
[group:children]
oraservers
webservers
#示例二
[nginx]
192.168.56.108
[tomcat]
192.168.56.109
[zabbix-agent:children]
nginx
tomcat
#ssh root@服务器B
#ssh-keygen 如果要重命名可以自己指定, 回车后生成密钥对
# echo ~/.ssh/id_rsa.pub >>known_hosts 这里把生成的公钥放入known_hosts
# 如果自己配置了ssh_config,关闭了known_hosts, 可能就需要写进~/.authorized_keys里面去了.
# 把服务器B上的私钥回传到ansible执行的服务器A的ssh_keys里, 命名为
# 服务器B_ip_.key
# 给ssh_keys文件夹授权为700, 一定要700, 其它的都会报错.
# 建立hosts文件里面指定server, 每个server一行.
[test_server]
10.0.1.5ansible_ssh_private_key_file=ssh_keys/10.0.1.5.key ansible_ssh_user=root
调用方式
· 按名称调用
o ansible name1 -m ping
· 调用所有host机器
o ansible all -m ping
· 按ip调用
o ansible172.16.79.233 -m ping
· 按组调用
o ansible webservers -m ping
· 两个组一起调用,以":"连接多个组
o 调用两个主机组的写法,以下webservers和dbservers都会被调用:
o ansible parserver:oraservers -m ping
· 在webservers组中但不在dbsersers中的调用
o ansible webservers:!dbservers -m win_ping
· 在webservers组中并且在dbservers组中的才会调用:
o ansible webservers:&dbservers -m ping
· 对整合后的群组进行调用,会调用父群group下的子组oraservers和webservers下所有的成员
o ansible group -m ping
· 在调用前加~,代表正则表达式
o ansible ~(web|db).*.91it.org -m win_ping
· 组合的例子
o webserver:dbservers:&nginx:!ntp
· 列出主机清单
o ansible || --list
o Eg:ansible oraservers --list
· 自定义新的主机清单文件
o cat /etc/ansible/test-hosts
o [testserver]
o 10.10.10.1
o 10.10.10.2
# 以-i指定新的主机清单文件
ansible -i /etc/ansible/test-hoststestserver -m ping
· hosts 文件中没有定义的IP或别名,在进行调用中,会提示错误。ansible对单台服务器的调用,服务器IP或域名必须有写在hosts里.
管理ansible配置文件
Configuring Ansible【优先级】
ansible的行为可以通过修改Ansible configuration file中的设置来定制。Ansible从控制节点上的几个可能位置之一选择其配置文件。
/etc/ansible/ansible.cfg
ansible包提供了一个位于/etc/ansible/ansible.cfg的基本配置文件。如果没有找到其他配置文件,则使用此文件。
~/.ansible.cfg
Ansible查找~/.Ansible .cfg在用户的家目录。如果该文件存在,则使用该文件,而不是/etc/ansible/ansible.cfg,如果ansible.cfg文件不存在,则使用当前工作目录下的ansible.cfg。
./ansible.cfg
如果ansible.cfg文件存在于执行ansible命令的目录中,则使用它来代替全局文件或用户的个人文件。这允许管理员创建一个目录结构,其中不同的环境或项目存储在不同的目录中,每个目录包含一个配置文件,该配置文件使用一组独特的设置进行了定制。
$ANSIBLE_CONFIG
您可以通过将不同的配置文件放在不同的目录中,然后在适当的目录中执行ansible的命令来使用不同的配置文件,但是随着配置文件数量的增长,这种方法可能会受到限制并且难以管理。一种更灵活的选择是使用$ ANSIBLE_CONFIG环境变量定义配置文件的位置。当定义此变量时,Ansible使用该变量指定的配置文件,而不是前面提到的任何配置文件。
优先级由高到低排序如下:
$ANSIBLE_CONFIG | 环境变量 |
---|---|
./ansible.cfg | 本地路径下 |
~/.ansible.cfg | 家目录下 |
/etc/ansible/ansible.cfg | 默认配置文件 |
优先级配置文件
配置文件的搜索顺序与前面列表相反。位于搜索顺序中的第一个是Ansible将使用配置的环境变量。即使存在其他具有较低优先级的文件,它们的设置也将被忽略,并且不会与所选配置文件中的设置合并。
$ANSIBLE_CONFIG环境变量指定的任何文件将覆盖所有其他配置文件。如果未设置该变量,那么接下来将检查运行ansible命令的目录是否有ansible.cfg文件。如果该文件不存在,则检查用户的主目录是否存在.ansible.cfg文件。全局/etc/ansible/ansible.cfg文件仅在没有找到其他配置文件时使用。
因此,如果您选择创建自己的配置文件,以支持全局的/etc/ansible/ansible.cfg配置文件,您需要从该文件复制所有所需的设置到您自己的用户级配置文件。在用户级配置文件中没有定义的设置仍然设置为内置默认值,即使它们被全局配置文件设置为其他一些值。
由于Ansible configuration file可以放置在多个位置,所以可能会混淆Ansible正在使用哪个配置文件,特别是当控制节点上存在多个文件时。您可以运行ansible–version命令来明确识别安装了哪个版本的ansible,以及正在使用哪个配置文件。
[root@taijitao-server ansibledemo]# ansible --version
ansible [core 2.13.5]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3.9/site-packages/ansible
ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/bin/ansible
python version = 3.9.14 (main, Dec 5 2022, 13:41:22) [GCC 8.5.0 20210514 (Red Hat 8.5.0-17)]
jinja version = 3.1.2
libyaml = True
另一种显示活动的Ansible configuration file的方法是,当在命令行上执行ansible命令时使用-v选项.
[root@taijitao-server ansibledemo]# ansible --list-hosts dev -v
Using /etc/ansible/ansible.cfg as config file
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
[WARNING]: Could not match supplied host pattern, ignoring: dev
hosts (0):
Managing Settings in the Configuration File
Ansible2.13中配置文件由几个部分组成,每个部分包含定义为键-值对的设置。Section title用方括号括起来。在默认的Ansible configuration file中,设置分组在以下13个部分:
[root@workstation ~]# grep "^\["/etc/ansible/ansible.cfg
[defaults]
[privilege_escalation]
[persistent_connection]
[connection]
[colors]
[selinux]
[diff]
[galaxy]
[inventory]
[netconf_connection]
[paramiko_connection]
[jinja2]
[tags]
配置文件中的大多数设置都在[defaults]部分下分组。
[privilege_escalation]节包含用于定义需要升级特权的操作如何在manage hosts上执行的设置。
[paramiko_connection],[ ssh_connection]和[accelerate]部分包含设置优化manage hosts的连接。
[selinux]用于定义如何配置selinux交互的设置。虽然没有包含在由Ansible包提供的默认全局Ansible配置文件中,[galaxy]部分也可以用于定义与Ansible galaxy相关的参数,这将在后面的章节中讨论。
通过在当前活动的配置文件中更改设置的值来定制设置。文件保存后,更改立即生效。一些设置是预定义的,默认值在可选项内,这些值是有效的,即使他们各自的设置在配置文件注释掉了。要确定这些预定义设置的默认值,请参考全局/etc/ansible/ansible中的注释。可选包提供的cfg配置文件。ansible手册页还提供了关于这些预定义设置的默认值的信息。
Configuring Connections
Ansible需要知道如何与托管主机manage hosts通信。更改配置文件最常见的原因之一是为了控制Ansible将使用什么方法和用户来管理托管主机。所需的一些资料包括:
l 列出托管主机和主机组的inventory在哪里
l 使用哪种连接协议来与托管主机通信(默认是SSH),以及是否需要非标准网络端口来连接到服务器
l 在托管主机上使用哪个远程用户;这可能是root用户,也可能是非特权用户
l 如果远程用户没有特权,Ansible需要知道是否应该尝试将特权升级到root用户,以及如何做到这一点(例如,通过使用sudo)
l 是否提示输入SSH密码或sudo密码以登录或获得特权
Inventory Location
在[defaults]部分中,inventory指令可以直接指向一个静态inventory文件,或者指向一个包含多个静态inventory文件和/或动态inventory脚本的目录:
[defaults]
inventory = ./inventory
Connection Settings
默认情况下,Ansible使用SSH协议连接到托管主机。控制Ansible如何连接到托管主机的最重要的参数设置在[defaults]部分。
默认情况下,Ansible尝试使用与运行Ansible命令的本地用户相同的用户名连接到托管主机。要指定一个不同的远程用户,请将remote_user参数设置为该用户名。
如果运行Ansible的本地用户配置了一个或多个私有SSH密钥,允许他们作为托管主机上的远程用户进行身份验证,Ansible会自动登录。如果不是这种情况,你可以配置Ansible通过设置指令ask_pass = true来提示本地用户输入远程用户使用的密码。
[defaults]
inventory = ./inventory
remote_user = root
ask_pass = true
假设您在托管主机上使用Linux控制节点和OpenSSH,如果您可以使用密码登录到远程用户,那么您可能可以设置SSH基于密钥的身份验证,这将允许您设置ask_pass = false。
第一步是确保控制节点上的用户在~/.SSH中配置了一个SSH密钥对。您可以运行ssh-keygen命令来完成此操作。
对于一个现有的托管主机,您可以在托管主机上安装您的公钥并填充您的本地~/.ssh/known_hosts文件及其主机密钥使用ssh-copy-id.
因为托管主机还没有配置基于SSH密钥的身份验证,您必须使用ansible-playbook命令和–ask-pass选项来运行playbook,以便命令作为远程用户进行身份验证。
Privilege Escalation
出于安全和审计原因,Ansible可能需要作为非特权用户连接到远程主机,然后升级特权以获得作为root用户的管理访问权限。这可以在Ansible configuration file的[privilege_escalation]部分中设置。
要在默认情况下启用特权升级,请在配置文件中设置指令become = true。即使这是默认设置,有多种方法来覆盖它时,运行临时命令或可选剧本。(例如,有时您可能想要运行一个task或play,而不升级特权。)
指令become_method指定如何升级特权。有几个选项可用,但默认使用sudo。同样,become_user指令指定要升级到哪个用户,但默认是root用户。
如果选择的become_method机制要求用户输入密码来升级特权,您可以在配置文件中设置become_ask_pass = true指令。
考虑您为特权升级所选择的任何方法的安全性影响。不同的组织和部署可能需要考虑不同的权衡。
下面的示例ansible.cfg文件假设您可以使用基于SSH密钥的身份验证以某个用户的身份连接到托管主机,并且某个用户可以使用sudo作为根用户运行命令,而无需输入密码:
[defaults]
inventory = ./inventory
remote_user = someuser
ask_pass = false
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
Configuration File Comments
可执行配置文件允许有两个注释字符:井号(#)和分号(😉。
开头为#字符注释掉整行。它不能与指令在同一行。
分号;字符在该行中注释它右边的所有内容。它可以与指令在同一行,只要那个指令在它的左边。
实战:
1、此示例需要普通用户提取,需在三台 主机上创建普通用户student,并切换到普通用户。
[root@taijitao-server ansible]# useradd student
[root@taijitao-server ansible]# echo student |passwd --stdin student
更改用户 student 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@taijitao-server ansible]# su - student
2、在student家目录下创建ansible-dev目录,并在ansible-dev目录下创建ansible.cfg文件,添加如下内容:
[student@taijitao-server ~]$ mkdir ansible-dev
[student@taijitao-server ~]$ cd ansible-dev/
[student@taijitao-server ansible-dev]$ vim ansible.cfg
[defaults]
inventory = ./inventory
3、在ansible-dev目录下创建静态inventory文件,静态inventory文件中创建三个组:
myself中的主机为localhost;
intranetweb的主机为taijitao-node1.example.com、taijitao-node2.example.com;
everyone中包含上述的myself、intranetweb主机组。
[student@taijitao-server ansible-dev]$ vim inventory
[myself]
localhost
[intranetweb]
taijitao-node1.example.com
taijitao-node2.example.com
[everyone:child]
myself
intranetweb
4、使用–list-hosts参数,测试inventory文件配置是否正确。
[student@taijitao-server ansible-dev]$ ansible myself --list-hosts
hosts (1):
localhost
[student@taijitao-server ansible-dev]$ ansible intranetweb --list-hosts
hosts (2):
taijitao-node1.example.com
taijitao-node2.example.com
[student@taijitao-server ansible-dev]$ ansible everyone --list-hosts
hosts (3):
localhost
taijitao-node1.example.com
taijitao-node2.example.com
5、在~/ansible-dev/ansible.cfg中添加[privilege_escalation]模块,以供student用户提权到root权限。
[student@taijitao-server ansible-dev]$ vim ansible.cfg
[defaults]
inventory = ./inventory
[privilege_escalation]
become = true # 可以提权,如果加上该参数,在普通用户下执行ansible,需要加上sudo,否则会提示“module_stdout": "sudo: 需要密码\r\n”,在做完该章节联系可以将true改为false,或者将[privilge_escalation]删除。
become_method = sudo #提权的方式
become_user = root #提权的目标用户
become_ask_pass = true #提权时需不需要密码,后续为了方便,可以将该参数注释或者删除,省的每次都输入密码
6、执行ansible --list-hosts命令,输入sudo提示用户的密码:
[student@taijitao-server ansible-dev]$ ansible intranetweb --list-hosts -v
Using /home/student/ansible-dev/ansible.cfg as config file
BECOME password: <font color="red">student</font>
hosts (2):
taijitao-node1.example.com
taijitao-node2.example.com
7、确保列举的主机名,现在执行我们的第一条ansible命令:
[student@taijitao-server ansible-dev]$ ansible -m ping intranetweb
taijitao-node1.example.com | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
taijitao-node2.example.com | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}