Ansible是最近几年特别火的一款开源运维自动化工具,它能够帮助运维人员肉眼可见地提高工作效率,并减少人为失误。Ansible有上千个功能丰富且实用的模块,而且有详尽的帮助信息可供查阅,因此即便是小白用户也可以轻松上手。

  • Ansible介绍与安装

Ansible目前是运维自动化工具中最简单、容易上手的一款优秀软件,能够用来管理各种资源。

可以使用Ansible自动部署应用程序,以此实现IT基础架构的全面部署。

例如,借助于Ansible,可以轻松地对服务器进行初始化配置、安全基线配置,以及进行更新和打补丁操作。

相较于Chef、Puppet、SaltStack等C/S(客户端/服务器)架构的自动化工具来讲,尽管Ansible的性能并不是最好的,但由于它基于SSH远程会话协议,不需要客户端程序,只要知道受管主机的账号密码,就能直接用SSH协议进行远程控制,因此使用起来优势明显。

Ansible服务本身并没有批量部署的功能,它仅仅是一个框架,真正具有批量部署能力的是其所运行的模块。

Ansible内置的模块非常丰富,几乎可以满足一切需求,使用起来也非常简单,一条命令甚至影响上千台主机。

如果需要更高级的功能,也可以运用Python语言对Ansible进行二次开发。

Ansible服务专用术语对照表

术语

中文叫法

含义

Control node

控制节点

指的是安装了Ansible服务的主机,也被称为Ansible控制端,主要是用来发布运行任务、调用功能模块,对其他主机进行批量控制。

Managed nodes

受控节点

指的是被Ansible服务所管理的主机,也被称为受控主机或客户端,是模块命令的被执行对象。

Inventory

主机清单

指的是受控节点的列表,可以是IP地址、主机名称或者域名。

Modules

模块

指的是上文提到的特定功能代码,默认自带有上千款功能模块,在Ansible Galaxy有超多可供选择。

Task

任务

指的是Ansible客户端上面要被执行的操作。

Playbook

剧本

指的是通过YAML语言编写的可重复执行的任务列表,把常做的操作写入到剧本文件中,下次可以直接重复执行一遍。

Roles

角色

从Ansible 1.2版本开始引入的新特性,用于结构化的组织Playbook,通过调用角色实现一连串的功能。

由于受控节点不需要安装客户端,外加SSH协议是Linux系统的标配,因此可以直接通过SSH协议进行远程控制。在控制节点上,也不用每次都重复开启服务程序,使用ansible命令直接调用模块进行控制即可。

RHEL 8系统的镜像文件默认不带有Ansible服务程序,需要从Extra Packages for Enterprise Linux(EPEL)扩展软件包仓库获取。EPEL软件包仓库由红帽公司提供,是一个用于创建、维护和管理企业版Linux的高质量软件扩展仓库,通用于RHEL、CentOS、Oracle Linux等多种红帽系企业版系统,目的是对于默认系统仓库软件包进行扩展。

下面准备在系统上部署Ansible服务程序。

第1步:在“虚拟机设置”界面中,将“网络适配器”的“网络连接”选项调整为“桥接模式”,并将系统的网卡设置成“Automatic(DHCP)”模式

[root@localhost ~]# ping www.baidu.com -c 4
PING www.baidu.com (180.101.49.11) 56(84) bytes of data.
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=1 ttl=52 time=24.7 ms
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=2 ttl=52 time=96.6 ms
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=3 ttl=52 time=16.2 ms
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=4 ttl=52 time=16.6 ms--- www.baidu.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 173ms
rtt min/avg/max/mdev = 16.173/38.527/96.608/33.703 ms
[root@localhost ~]#

第2步:在原有软件仓库配置的下方,追加EPEL扩展软件包安装源的信息。

[root@localhost ~]# ls /etc/yum.repos.d/
redhat.repo  rhel8.repo
[root@localhost ~]# vim /etc/yum.repos.d/rhel8.repo
[BaseOS]
name=BaseOS
baseurl=file:///media/cdrom/BaseOS
enabled=1
gpgcheck=0[AppStream]
name=AppStream
baseurl=file:///media/cdrom/AppStream
enabled=1
gpgcheck=0[EPEL]
name=EPEL
baseurl=https://dl.fedoraproject.org/pub/epel/8/Everything/x86_64/
enabled=1
gpgcheck=0
[root@localhost ~]#

第3步:安装!

[root@localhost ~]# dnf install -y ansible
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
AppStream                                       351 kB/s | 3.2 kB     00:00
BaseOS                                          184 kB/s | 2.7 kB     00:00
EPEL                                            479 kB/s |  10 MB     00:21
Dependencies resolved.
================================================================================
Package                 Arch        Version               Repository      Size
================================================================================
Installing:
ansible                 noarch      2.9.24-1.el8          EPEL            17 M
Installing dependencies:
python3-babel           noarch      2.5.1-3.el8           AppStream      4.8 M
python3-jinja2          noarch      2.10-9.el8            AppStream      537 k
python3-jmespath        noarch      0.9.0-11.el8          AppStream       45 k
python3-markupsafe      x86_64      0.23-19.el8           AppStream       39 k
python3-pyasn1          noarch      0.3.7-6.el8           AppStream      126 k
libsodium               x86_64      1.0.18-2.el8          EPEL           162 k
python3-bcrypt          x86_64      3.1.6-2.el8.1         EPEL            44 k
python3-pynacl          x86_64      1.3.0-5.el8           EPEL           100 k
sshpass                 x86_64      1.06-9.el8            EPEL            27 k
Installing weak dependencies:
python3-paramiko        noarch      2.4.3-1.el8           EPEL           289 kTransaction Summary
================================================================================
Install  11 PackagesTotal size: 23 M
Total download size: 18 M
Installed size: 122 M
Downloading Packages:
(1/6): python3-bcrypt-3.1.6-2.el8.1.x86_64.rpm   18 kB/s |  44 kB     00:02
(2/6): libsodium-1.0.18-2.el8.x86_64.rpm         52 kB/s | 162 kB     00:03
(3/6): python3-pynacl-1.3.0-5.el8.x86_64.rpm     37 kB/s | 100 kB     00:02
(4/6): sshpass-1.06-9.el8.x86_64.rpm            103 kB/s |  27 kB     00:00
(5/6): python3-paramiko-2.4.3-1.el8.noarch.rpm   60 kB/s | 289 kB     00:04
(6/6): ansible-2.9.24-1.el8.noarch.rpm          529 kB/s |  17 MB     00:32
--------------------------------------------------------------------------------
Total                                           548 kB/s |  18 MB     00:32
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transactionPreparing        :                                                        1/1Installing       : sshpass-1.06-9.el8.x86_64                             1/11Installing       : python3-bcrypt-3.1.6-2.el8.1.x86_64                   2/11Installing       : libsodium-1.0.18-2.el8.x86_64                         3/11Installing       : python3-pynacl-1.3.0-5.el8.x86_64                     4/11Installing       : python3-pyasn1-0.3.7-6.el8.noarch                     5/11Installing       : python3-paramiko-2.4.3-1.el8.noarch                   6/11Installing       : python3-markupsafe-0.23-19.el8.x86_64                 7/11Installing       : python3-jmespath-0.9.0-11.el8.noarch                  8/11Installing       : python3-babel-2.5.1-3.el8.noarch                      9/11Installing       : python3-jinja2-2.10-9.el8.noarch                     10/11Installing       : ansible-2.9.24-1.el8.noarch                          11/11Running scriptlet: ansible-2.9.24-1.el8.noarch                          11/11Verifying        : python3-babel-2.5.1-3.el8.noarch                      1/11Verifying        : python3-jinja2-2.10-9.el8.noarch                      2/11Verifying        : python3-jmespath-0.9.0-11.el8.noarch                  3/11Verifying        : python3-markupsafe-0.23-19.el8.x86_64                 4/11Verifying        : python3-pyasn1-0.3.7-6.el8.noarch                     5/11Verifying        : ansible-2.9.24-1.el8.noarch                           6/11Verifying        : libsodium-1.0.18-2.el8.x86_64                         7/11Verifying        : python3-bcrypt-3.1.6-2.el8.1.x86_64                   8/11Verifying        : python3-paramiko-2.4.3-1.el8.noarch                   9/11Verifying        : python3-pynacl-1.3.0-5.el8.x86_64                    10/11Verifying        : sshpass-1.06-9.el8.x86_64                            11/11
Installed products updated.Installed:ansible-2.9.24-1.el8.noarch            python3-paramiko-2.4.3-1.el8.noarch    python3-babel-2.5.1-3.el8.noarch       python3-jinja2-2.10-9.el8.noarch       python3-jmespath-0.9.0-11.el8.noarch   python3-markupsafe-0.23-19.el8.x86_64  python3-pyasn1-0.3.7-6.el8.noarch      libsodium-1.0.18-2.el8.x86_64          python3-bcrypt-3.1.6-2.el8.1.x86_64    python3-pynacl-1.3.0-5.el8.x86_64      sshpass-1.06-9.el8.x86_64             Complete!
[root@localhost ~]#

安装完毕后,Ansible服务便默认已经启动。使用--version参数可以看到Ansible服务的版本及配置信息。

[root@localhost ~]# ansible --version
ansible 2.9.25config file = /etc/ansible/ansible.cfgconfigured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']ansible python module location = /usr/lib/python3.6/site-packages/ansibleexecutable location = /usr/bin/ansiblepython version = 3.6.8 (default, Jan 11 2019, 02:17:16) [GCC 8.2.1 20180905 (Red Hat 8.2.1-3)]
[root@localhost ~]#
  • 设置主机清单

Ansible服务的主配置文件存在优先级的顺序关系,默认存放在/etc/ansible目录中的主配置文件优先级最低。如果在当前目录或用户家目录中也存放着一份主配置文件,则以当前目录或用户家目录中的主配置文件为主。同时存在多个Ansible服务主配置文件时,

Ansible服务主配置文件优先级顺序

优先级

文件位置

./ansible.cfg

~/.ansible.cfg

/etc/ansible/ansible.cfg

Ansible服务是用于实现主机批量自动化控制的管理工具,可以把要管理的主机IP地址预先写入主机清单文件(/etc/ansible/hosts),这样后续再通过执行ansible命令来执行任务时就自动包含这些主机了

测试受管主机信息

操作系统

IP地址

功能用途

RHEL 8

192.168.10.20

dev

RHEL 8

192.168.10.30

test

将测试主机写入到主机清单文件中,并将主机进行分组,主机清单文件在修改后会立即生效,一般使用“ansible-inventory --graph”命令以结构化的方式显示出受管主机的信息。

[root@localhost ~]# vim /etc/ansible/hosts
[dev]
192.168.10.20
[test]
192.168.10.30
[root@localhost ~]# ansible-inventory --graph
@all:|--@dev:|  |--192.168.10.20|--@test:|  |--192.168.10.30|--@ungrouped:
[root@localhost ~]#

Ansible常用变量汇总

参数

作用

ansible_ssh_host

受管主机名

ansible_ssh_port

端口号

ansible_ssh_user

默认账号

ansible_ssh_pass

默认密码

ansible_shell_type

Shell终端类型

再次配置主机清单文件,使其登录各主机时自动登录,因测试主机密码都一样,可以使用all统一配置。

[root@localhost ~]# cat /etc/ansible/hosts
[dev]
192.168.10.20
[test]
192.168.10.30
[root@localhost ~]# vim /etc/ansible/hosts
[root@localhost ~]# cat /etc/ansible/hosts
[dev]
192.168.10.20
[test]
192.168.10.30
[all:vars]
ansible_user=root
ansible_password=123456
[root@localhost ~]# 

将Ansible主配置文件中的第71行设置成默认不需要SSH协议的指纹验证,以及将第107行设置成默认执行剧本时所使用的管理员名称为root

[root@localhost ~]# vim /etc/ansible/ansible.cfg
...
70 # uncomment this to disable SSH key host checking
- 71 #host_key_checking = False
+ 71 host_key_checking = False
72
73 # change the default callback, you can only have one 'stdout' type  enabled     at a time.
...
106 # (/usr/bin/ansible will use current user as default)
- 107 #remote_user = root
+ 107 remote_user = root
108
109 # logging is off by default unless this path is defined
[root@localhost ~]#

不需要重启服务,在以上操作完全搞定后就可以开始后面的实验了。

由于刚才是将Ansible服务器设置成了桥接及DHCP模式,现在将网络适配器修改回“仅主机模式”以及192.168.10.10/24的IP地址。在修改完成后重启网卡,然后执行ping操作,测试 192.168.10.20、192.168.10.30 通信。

[root@localhost ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500inet 192.168.0.133  netmask 255.255.255.0  broadcast 192.168.0.255inet6 fe80::a7bc:9261:b95:a2f6  prefixlen 64  scopeid 0x20<link>ether 00:0c:29:4a:53:0b  txqueuelen 1000  (Ethernet)RX packets 10650  bytes 12302250 (11.7 MiB)RX errors 0  dropped 0  overruns 0  frame 0TX packets 2147  bytes 170750 (166.7 KiB)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536inet 127.0.0.1  netmask 255.0.0.0inet6 ::1  prefixlen 128  scopeid 0x10<host>loop  txqueuelen 1000  (Local Loopback)RX packets 48  bytes 4608 (4.5 KiB)RX errors 0  dropped 0  overruns 0  frame 0TX packets 48  bytes 4608 (4.5 KiB)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0virbr0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500inet 192.168.122.1  netmask 255.255.255.0  broadcast 192.168.122.255ether 52:54:00:53:93:e3  txqueuelen 1000  (Ethernet)RX packets 0  bytes 0 (0.0 B)RX errors 0  dropped 0  overruns 0  frame 0TX packets 0  bytes 0 (0.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0[root@localhost ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500inet 192.168.10.10  netmask 255.255.255.0  broadcast 192.168.10.255inet6 fe80::a7bc:9261:b95:a2f6  prefixlen 64  scopeid 0x20<link>ether 00:0c:29:4a:53:0b  txqueuelen 1000  (Ethernet)RX packets 10723  bytes 12309131 (11.7 MiB)RX errors 0  dropped 0  overruns 0  frame 0TX packets 2222  bytes 179074 (174.8 KiB)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536inet 127.0.0.1  netmask 255.0.0.0inet6 ::1  prefixlen 128  scopeid 0x10<host>loop  txqueuelen 1000  (Local Loopback)RX packets 83  bytes 8112 (7.9 KiB)LinuxProbe 0x17 DHCP动态管理主机地址、电子邮件系统RX errors 0  dropped 0  overruns 0  frame 0TX packets 83  bytes 8112 (7.9 KiB)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0virbr0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500inet 192.168.122.1  netmask 255.255.255.0  broadcast 192.168.122.255ether 52:54:00:53:93:e3  txqueuelen 1000  (Ethernet)RX packets 0  bytes 0 (0.0 B)RX errors 0  dropped 0  overruns 0  frame 0TX packets 0  bytes 0 (0.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0[root@localhost ~]#
[root@localhost ~]# ping 192.168.10.20 -c 4
PING 192.168.10.20 (192.168.10.20) 56(84) bytes of data.
64 bytes from 192.168.10.20: icmp_seq=1 ttl=64 time=0.749 ms
64 bytes from 192.168.10.20: icmp_seq=2 ttl=64 time=0.669 ms
64 bytes from 192.168.10.20: icmp_seq=3 ttl=64 time=0.574 ms
64 bytes from 192.168.10.20: icmp_seq=4 ttl=64 time=0.539 ms--- 192.168.10.20 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 97ms
rtt min/avg/max/mdev = 0.539/0.632/0.749/0.087 ms
[root@localhost ~]# ping 192.168.10.30 -c 4
PING 192.168.10.30 (192.168.10.30) 56(84) bytes of data.
64 bytes from 192.168.10.30: icmp_seq=1 ttl=64 time=0.893 ms
64 bytes from 192.168.10.30: icmp_seq=2 ttl=64 time=97.0 ms
64 bytes from 192.168.10.30: icmp_seq=3 ttl=64 time=1.00 ms
64 bytes from 192.168.10.30: icmp_seq=4 ttl=64 time=0.459 ms--- 192.168.10.30 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 18ms
rtt min/avg/max/mdev = 0.459/24.845/97.030/41.676 ms
[root@localhost ~]#
  • 运行临时命令

Ansible服务的强大之处在于只需要一条命令,便可以操控成千上万台的主机节点,而ansible命令便是最得力的工具之一。

可以使用“ansible-doc模块名称”的命令格式查询使用方法 ,或是使用ansibe-doc -l命令列出所有的模块信息以供选择。

Ansible服务常用模块名称及作用

模块名称

模块作用

ping

检查受管节点主机网络是否能够联通。

yum

安装、更新及卸载软件包。

yum_repository

管理主机的软件仓库配置文件。

template

复制模板文件到受管节点主机。

copy

新建、修改及复制文件。

user

创建、修改及删除用户。

group

创建、修改及删除用户组。

service

启动、关闭及查看服务状态。

get_url

从网络中下载文件。

file

设置文件权限及创建快捷方式。

cron

添加、修改及删除计划任务。

command

直接执行用户指定的命令。

shell

直接执行用户指定的命令(支持特殊字符)。

debug

输出调试或报错信息。

mount

挂载硬盘设备文件。

filesystem

格式化硬盘设备文件。

lineinfile

通过正则表达式修改文件内容。

setup

收集受管节点主机上的系统及变量信息。

firewalld

添加、修改及删除防火墙策略。

lvg

管理主机的物理卷及卷组设备。

lvol

管理主机的逻辑卷设备。

在Ansible服务中,ansible是用于执行临时任务的命令,也就在是执行后即结束(与剧本文件的可重复执行不同)。

在使用ansible命令时,必须指明受管主机的信息,如果已经设置过主机清单文件(/etc/ansible/hosts),则可以使用all参数来指代全体受管主机,或是用dev、test等主机组名称来指代某一组的主机。

ansible命令常用的语法格式为“ansible受管主机节点 -m模块名称[-a模块参数]”。

其中,-a是要传递给模块的参数,只有功能极其简单的模块才不需要额外参数,所以大多情况下-m与-a参数都会同时出现。

ansible命令常用参数

参数

作用

-k

手动输入SSH协议密码

-i

指定主机清单文件

-m

指定要使用的模块名

-M

指定要使用的模块路径

-S

使用su命令

-T

设置SSH协议连接超时时间

-a

设置传递给模块的参数

--version

查看版本信息

-h

帮助信息

如果想实现某个功能,但是却不知道用什么模块,又或者是知道了模块名称,但不清楚模块具体的作用,则建议使用ansible-doc命令进行查找。

例:列举出当前Ansible服务所支持的所有模块信息

[root@localhost ~]# ansible-doc -l
a10_server                                                    Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' server object
a10_server_axapi3                                             Manage A10 Networks AX/SoftAX/Thunder/vThunder devices
a10_service_group                                             Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' service groups
a10_virtual_server                                            Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' virtual servers
aci_aaa_user                                                  Manage AAA users (aaa:User)
aci_aaa_user_certificate                                      Manage AAA user certificates (aaa:UserCert)
aci_access_port_block_to_access_port                          Manage port blocks of Fabric interface policy leaf profile interface sele...
aci_access_port_to_interface_policy_leaf_profile              Manage Fabric interface policy leaf profile interface selectors (infra:HP...
aci_access_sub_port_block_to_access_port                      Manage sub port blocks of Fabric interface policy leaf profile interface ...
aci_aep                                                       Manage attachable Access Entity Profile (AEP) objects (infra:AttEntityP, ...
aci_aep_to_domain                                             Bind AEPs to Physical or Virtual Domains (infra:RsDomP)
aci_ap                                                        Manage top level Application Profile (AP) objects (fv:Ap)
aci_bd                                                        Manage Bridge Domains (BD) objects (fv:BD)
aci_bd_subnet                                                 Manage Subnets (fv:Subnet)
aci_bd_to_l3out                                               Bind Bridge Domain to L3 Out (fv:RsBDToOut)
aci_config_rollback                                           Provides rollback and rollback preview functionality (config:ImportP)
aci_config_snapshot                                           Manage Config Snapshots (config:Snapshot, config:ExportP)
aci_contract                                                  Manage contract resources (vz:BrCP)
aci_contract_subject                                          Manage initial Contract Subjects (vz:Subj)
aci_contract_subject_to_filter                                Bind Contract Subjects to Filters (vz:RsSubjFiltAtt)
aci_domain                                                    Manage physical, virtual, bridged, routed or FC domain profiles (phys:Dom...
aci_domain_to_encap_pool                                      Bind Domain to Encap Pools (infra:RsVlanNs)
aci_domain_to_vlan_pool                                       Bind Domain to VLAN Pools (infra:RsVlanNs)
aci_encap_pool                                                Manage encap pools (fvns:VlanInstP, fvns:VxlanInstP, fvns:VsanInstP)
aci_encap_pool_range                                          Manage encap ranges assigned to pools (fvns:EncapBlk, fvns:VsanEncapBlk)
aci_epg                                                       Manage End Point Groups (EPG) objects (fv:AEPg)
aci_epg_monitoring_policy                                     Manage monitoring policies (mon:EPGPol)
aci_epg_to_contract                                           Bind EPGs to Contracts (fv:RsCons, fv:RsProv)
aci_epg_to_domain                                             Bind EPGs to Domains (fv:RsDomAtt)
aci_fabric_node                                               Manage Fabric Node Members (fabric:NodeIdentP)
aci_fabric_scheduler                                          This modules creates ACI schedulers
aci_filter                                                    Manages top level filter objects (vz:Filter)
aci_filter_entry                                              Manage filter entries (vz:Entry)
aci_firmware_group                                            This module creates a firmware group
aci_firmware_group_node                                       This modules adds and remove nodes from the firmware group
aci_firmware_policy                                           This creates a firmware policy
:
...

按 q 退出

ansible-doc命令会在屏幕上显示出这个模块的作用、可用参数及实例等信息

[root@localhost ~]# ansible-doc a10_server
> A10_SERVER    (/usr/lib/python3.6/site-packages/ansible/modules/network/a10/a>Manage SLB (Server Load Balancer) server objects on A10Networks devices via aXAPIv2.* This module is maintained by The Ansible Community
OPTIONS (= is mandatory):- client_certPEM formatted certificate chain file to be used for SSL clientauthentication.This file can also include the key as well, and if the key isincluded, `client_key' is not required.[Default: (null)]type: path- client_key
...

检查一下这些主机的网络连通性。

ping模块用于进行简单的网络测试(类似于常用的ping命令)。

可以使用ansible命令直接针对所有主机调用ping模块,不需要增加额外的参数,返回值若为SUCCESS,则表示主机当前在线。

[root@localhost ~]# ansible all -m ping
192.168.10.20 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"
}
192.168.10.30 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false,"ping": "pong"
}
[root@localhost ~]#

除了使用-m参数直接指定模块名称之外,还可以用-a参数将参数传递给模块,让模块的功能更高级,更好地满足当前生产的需求。

例如,yum_repository模块的作用是管理主机的软件仓库,能够添加、修改及删除软件仓库的配置信息,参数相对比较复杂。遇到这种情况时,建议先用ansible-doc命令对其进行了解。尤其是下面的EXAMPLES结构段会有该模块的实例,对用户来说有非常高的参考价值。

[root@localhost ~]#  ansible-doc yum_repository
> YUM_REPOSITORY    (/usr/lib/python3.6/site-packages/ansible/modules/packaging>Add or remove YUM repositories in RPM-based Linuxdistributions. If you wish to update an existing repositorydefinition use [ini_file] instead.* This module is maintained by The Ansible Core Team
OPTIONS (= is mandatory):- asyncIf set to `yes' Yum will download packages and metadata fromthis repo in parallel, if possible.[Default: yes]type: bool- attributesThe attributes the resulting file or directory should have.To get supported flags look at the man page for `chattr' onthe target system.This string should contain the attributes in the same order asthe one displayed by `lsattr'.The `=' operator is assumed as default, otherwise `+' or `-'operators need to be included in the string.
[root@localhost ~]#

例:  为主机清单中的所有服务器新增一个软件仓库,

新增软件仓库信息

仓库名称

EX294_BASE

仓库描述

EX294 base software

仓库地址

file:///media/cdrom/BaseOS

GPG签名

启用

GPG密钥文件

file:///media/cdrom/RPM-GPG-KEY-redhat-release

可以对照着EXAMPLE实例段,逐一对应填写需求值和参数,其标准格式是在-a参数后接整体参数(用单引号圈起),而各个参数字段的值则用双引号圈起。这是最严谨的写法。

在执行下述命令后如果出现CHANGED字样,则表示修改已经成功:

[root@localhost ~]# ansible all -m yum_repository -a 'name="EX294_BASE" description="EX294 base software" baseurl="file:///media/cdrom/BaseOS" gpgcheck=yes enabled=1'
192.168.10.20 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": true,"repo": "EX294_BASE","state": "present"
}
192.168.10.30 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": true,"repo": "EX294_BASE","state": "present"
}
[root@localhost ~]#

在命令执行成功后,可以到主机清单中的任意机器上查看新建成功的软件仓库配置文件。

[ 192.168.10.20 ]

[root@localhost ~]# cd  /etc/yum.repos.d/
[root@localhost yum.repos.d]# ls
EX294_BASE.repo  redhat.repo  rhel8.repo
[root@localhost yum.repos.d]# cat EX294_BASE.repo
[EX294_BASE]
baseurl = file:///media/cdrom/BaseOS
enabled = 1
gpgcheck = 1
name = EX294 base software[root@localhost yum.repos.d]#
  • 剧本文件实战

Ansible服务允许用户根据需求,在类似于Shell脚本的模式下编写自动化运维脚本,然后由程序自动、重复地执行,从而大大提高了工作效率。

Ansible服务的剧本(playbook)文件采用YAML语言编写,具有强制性的格式规范,它通过空格将不同信息分组,因此有时会因一两个空格错位而导致报错。

YAML文件的开头需要先写3个减号(---),多个分组的信息需要间隔一致才能执行,而且上下也要对齐,后缀名一般为.yml。

剧本文件在执行后,会在屏幕上输出运行界面,内容会根据工作的不同而变化。在运行界面中,绿色表示成功,黄色表示执行成功并进行了修改,而红色则表示执行失败。

剧本文件的结构由4部分组成,分别是target、variable、task、handler,其各自的作用如下。

target:用于定义要执行剧本的主机范围。

variable:用于定义剧本执行时要用到的变量。

task:用于定义将在远程主机上执行的任务列表。

handler:用于定义执行完成后需要调用的后续任务。

YAML语言编写的Ansible剧本文件会按照从上到下的顺序自动运行,格式有严格的要求。

例如,创建一个名为packages.yml的剧本,让dev、test组的主机可以自动安装数据库软件,并且将dev组主机的软件更新至最新。

安装和更新软件需要使用yum模块。先看一下帮助信息中的示例吧:

[root@localhost ~]# ansible-doc yum
> YUM    (/usr/lib/python3.6/site-packages/ansible/modules/packaging/os/yum.py)Installs, upgrade, downgrades, removes, and lists packages andgroups with the `yum' package manager. This module only workson Python 2. If you require Python 3 support see the [dnf]module.* This module is maintained by The Ansible Core Team* note: This module has a corresponding action plugin.OPTIONS (= is mandatory):- allow_downgradeSpecify if the named package and version is allowed todowngrade a maybe already installed higher version of thatpackage. Note that setting allow_downgrade=True can make thismodule behave in a non-idempotent way. The task could end upwith a set of packages that does not match the complete listof specified packages to install (because dependencies betweenthe downgraded package and others can cause changes to thepackages which were in the earlier transaction).[Default: no]type: bool
...

在知道yum模块的使用方法和格式后,就可以开始编写剧本了。初次编写剧本文件时,务必看准格式,模块及play(动作)格式也要上下对齐,否则会出现“参数一模一样,但不能执行”的情况。

[root@localhost ~]# vim packages.yml
[root@localhost ~]# cat packages.yml
---
- name: 安装软件包hosts: dev,testtasks:- name: oneyum:name: mariadbstate: latest
[root@localhost ~]#

其中,

name字段表示此项play(动作)的名字,用于在执行过程中提示用户执行到了哪一步,以及帮助管理员在日后阅读时能想起这段代码的作用。

hosts字段表示要在哪些主机上执行该剧本,多个主机组之间用逗号间隔;如果需要对全部主机进行操作,则使用all参数。

tasks字段用于定义要执行的任务,每个任务都要有一个独立的name字段进行命名,并且每个任务的name字段和模块名称都要严格上下对齐,参数要单独缩进。

在编写Ansible剧本文件时,RHEL 8系统自带的Vim编辑器具有自动缩进功能,这可以给我们提供很多帮助。在确认无误后就可以用ansible-playbook命令运行这个剧本文件了。

[root@localhost ~]# ansible-playbook packages.ymlPLAY [安装软件包] *******************************************************************************TASK [Gathering Facts] *********************************************************************
ok: [192.168.10.20]
ok: [192.168.10.30]TASK [one] *********************************************************************************
changed: [192.168.10.20]
changed: [192.168.10.30]PLAY RECAP *********************************************************************************
192.168.10.20              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
192.168.10.30              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   [root@localhost ~]#

在执行成功后,主要观察最下方的输出信息。

其中,ok和changed表示执行及修改成功。

如遇到unreachable或failed大于0的情况,建议手动检查剧本是否在所有主机中都正确运行了,以及有无安装失败的情况。

在正确执行过packages.yml文件后,随机切换到dev、test组中的任意一台主机上,再次安装mariadb软件包,此时会提示该服务已经存在。这说明刚才的操作一切顺利!

[root@localhost yum.repos.d]# dnf install mariadb
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
AppStream                                       1.7 MB/s | 3.2 kB     00:00
BaseOS                                          1.3 MB/s | 2.7 kB     00:00
Package mariadb-3:10.3.11-1.module+el8+2765+cfa4f87b.x86_64 is already installed.
Dependencies resolved.
Nothing to do.
Complete!
[root@localhost yum.repos.d]#

( 实验与实验之间有可能存在冲突,建议实验与实验之间可以恢复下虚拟机,避免冲突。 这里就因为前一个实验在控制主机中新增了个仓库EX294_BASE, 在分主机上测试安装执行 dnf install mariadb 出现以下情况,删除EX294_BASE配置文件再次 执行  dnf install mariadb  则正常)

[root@localhost ~]# dnf install mariadb
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.You have enabled checking of packages via GPG keys. This is a good thing.
However, you do not have any GPG public keys installed. You need to download
the keys for packages you wish to install and install them.
You can do that by running the command:rpm --import public.gpg.keyAlternatively you can specify the url to the key you would like to use
for a repository in the 'gpgkey' option in a repository section and DNF
will install it for you.For more information contact your distribution or package provider.Problem repository: [EX294_BASE]
bandwidth: 0
baseurl: [file:///media/cdrom/BaseOS]
cachedir: /var/cache/dnf
cost: 1000
deltarpm: 1
deltarpm_percentage: 75
enabled: 1
enabled_metadata:
enablegroups: 1
exclude: []
excludepkgs: []
fastestmirror: 0
gpgcheck: 1
gpgkey: []
includepkgs: []
ip_resolve: whatever
max_parallel_downloads: 3
mediaid:
metadata_expire: 172800
metalink:
minrate: 1000
mirrorlist:
module_hotfixes: 0
name: EX294 base software
password:
priority: 99
protected_packages: [dnf, dnf, systemd, systemd-udev, sudo, dnf, systemd, systemd-udev, sudo]
proxy:
proxy_auth_method: any
proxy_password:
proxy_username:
repo_gpgcheck: 0
retries: 10
skip_if_unavailable: 1
sslcacert:
sslclientcert:
sslclientkey:
sslverify: 1
throttle: 0
timeout: 30
type:
username:
[root@localhost ~]#
  • 创建及使用角色

在日常编写剧本时,会存在剧本越来越长的情况,这不利于进行阅读和维护,而且还无法让其他剧本灵活地调用其中的功能代码。

角色(role)这一功能则是自Ansible 1.2版本开始引入的新特性,用于层次性、结构化地组织剧本。角色功能分别把变量、文件、任务、模块及处理器配置放在各个独立的目录中,然后对其进行便捷加载。

简单来说,角色功能是把常用的一些功能“类模块化”,然后在用的时候加载即可。

Ansible服务的角色功能类似于编程中的封装技术—将具体的功能封装起来,用户不仅可以方便地调用它,而且甚至可以不用完全理解其中的原理。

角色的好处就在于将剧本组织成了一个简洁的、可重复调用的抽象对象,使得用户把注意力放到剧本的宏观大局上,统筹各个关键性任务,只有在需要时才去深入了解细节。角色的获取有3种方法,分别是加载系统内置角色、从外部环境获取角色以及自行创建角色。

加载系统内置角色

在使用RHEL系统的内置角色时,不需要联网就能实现。用户只需要配置好软件仓库的配置文件,然后安装包含系统角色的软件包rhel-system-roles,随后便可以在系统中找到它们了,然后就能够使用剧本文件调用角色了。

[root@localhost ~]# dnf install -y rhel-system-roles
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
AppStream                                                                                   34 kB/s | 3.2 kB     00:00
BaseOS                                                                                     140 kB/s | 2.7 kB     00:00
EPEL                                                                                       0.0  B/s |   0  B     00:20
Failed to synchronize cache for repo 'EPEL', ignoring this repo.
Dependencies resolved.
===========================================================================================================================
Package                            Arch                    Version                       Repository                  Size
===========================================================================================================================
Installing:
rhel-system-roles                  noarch                  1.0-5.el8                     AppStream                  127 kTransaction Summary
===========================================================================================================================
Install  1 PackageTotal size: 127 k
Installed size: 827 k
Downloading Packages:
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transactionPreparing        :                                                                                                   1/1Installing       : rhel-system-roles-1.0-5.el8.noarch                                                                1/1Verifying        : rhel-system-roles-1.0-5.el8.noarch                                                                1/1
Installed products updated.Installed:rhel-system-roles-1.0-5.el8.noarch                                                                                       Complete!
[root@localhost ~]#

安装完毕后,使用ansible-galaxy list命令查看RHEL 8系统中有哪些自带的角色可用

[root@localhost ~]# ansible-galaxy list
# /usr/share/ansible/roles
- linux-system-roles.kdump, (unknown version)
- linux-system-roles.network, (unknown version)
- linux-system-roles.postfix, (unknown version)
- linux-system-roles.selinux, (unknown version)
- linux-system-roles.timesync, (unknown version)
- rhel-system-roles.kdump, (unknown version)
- rhel-system-roles.network, (unknown version)
- rhel-system-roles.postfix, (unknown version)
- rhel-system-roles.selinux, (unknown version)
- rhel-system-roles.timesync, (unknown version)
# /etc/ansible/roles
[WARNING]: - the configured path /root/.ansible/roles does not exist.
[root@localhost ~]#

ansible系统角色描述

角色名称

作用

rhel-system-roles.kdump

配置kdump崩溃恢复服务

rhel-system-roles.network

配置网络接口

rhel-system-roles.selinux

配置SELinux策略及模式

rhel-system-roles.timesync

配置网络时间协议

rhel-system-roles.postfix

配置邮件传输服务

rhel-system-roles.firewall

配置防火墙服务

rhel-system-roles.tuned

配置系统调优选项

以rhel-system-roles.timesync角色为例,它用于设置系统的时间和NTP服务,让主机能够同步准确的时间信息。剧本模板文件存放在/usr/share/doc/rhel-system-roles/目录中,可以复制过来修改使用

[root@localhost ~]# cp /usr/share/doc/rhel-system-roles/timesync/example-timesync-playbook.yml timesync.yml
[root@localhost ~]#

NTP服务器主要用于同步计算机的时间,可以提供高精度的时间校准服务,帮助计算机校对系统时钟。在复制来的剧本模板文件中,删除掉多余的代码,将NTP服务器的地址填写到timesync_ntp_servers变量的hostname字段中即可。

timesync_ntp_servers变量参数含义

参数

作用

hostname

NTP服务器主机名

iburst

启用快速同步

[root@localhost ~]# vim timesync.yml
---
- hosts: allvars:timesync_ntp_servers:- hostname: pool.ntp.orgiburst: yesroles:- rhel-system-roles.timesync
[root@localhost ~]# 

从外部获取角色

Ansible Galaxy (https://galaxy.ansible.com/) 是Ansible的一个官方社区,用于共享角色和功能代码,用户可以在网站自由地共享和下载Ansible角色。该社区是管理和使用角色的不二之选。

Ansible Galaxy官网中,左侧有3个功能选项,分别是首页(Home)、搜索(Search)以及社区(Community)。单击Search按钮进入到搜索界面,这里以nginx服务为例进行搜索,即可找到Nginx官方发布的角色信息

当单击nginx角色进入到详情页面后,会显示这个项目的软件版本、评分、下载次数等信息。在Installation字段可以看到相应的安装方式。

如果需要使用这个角色,可以在虚拟机联网的状态下直接按照“ansible-galaxy install角色名称”的命令格式自动获取:

[root@localhost ~]# ansible-galaxy install nginxinc.nginx
- downloading role 'nginx', owned by nginxinc
- downloading role from https://github.com/nginxinc/ansible-role-nginx/archive/0.21.0.tar.gz
- extracting nginxinc.nginx to /root/.ansible/roles/nginxinc.nginx
- nginxinc.nginx (0.21.0) was installed successfully
[root@localhost ~]#

执行完毕后,再次查看系统中已有的角色,便可找到nginx角色信息了

[root@localhost ~]# ansible-galaxy list
# /root/.ansible/roles
- nginxinc.nginx, 0.21.0
# /usr/share/ansible/roles
- linux-system-roles.kdump, (unknown version)
- linux-system-roles.network, (unknown version)
- linux-system-roles.postfix, (unknown version)
- linux-system-roles.selinux, (unknown version)
- linux-system-roles.timesync, (unknown version)
- rhel-system-roles.kdump, (unknown version)
- rhel-system-roles.network, (unknown version)
- rhel-system-roles.postfix, (unknown version)
- rhel-system-roles.selinux, (unknown version)
- rhel-system-roles.timesync, (unknown version)
# /etc/ansible/roles
[root@localhost ~]#

如果加载非官网的角色需要自行编写一个YAML语言格式的文件, 指明网址链接和角色名称,然后再用 ansible-galaxy install -r filename  进行加载。

# cat nginx.yml
---
- src: https:网站地址/nginxinc-nginx_core-0.3.0.tar.gzname: nginx-core
# ansible-galaxy install -r nginx.yml

创建新的角色

创建一个名为apache的新角色,它能够帮助我们自动安装、运行httpd网站服务,设置防火墙的允许规则,以及根据每个主机生成独立的index.html首页文件。

在Ansible的主配置文件中,第68行定义的是角色保存路径。如果用户新建的角色信息不在规定的目录内,则无法使用ansible-galaxy list命令找到。因此需要手动填写新角色的目录路径,或是进入/etc/ansible/roles目录内再进行创建。

[root@localhost ~]# vim /etc/ansible/ansible.cfg
67 # additional paths to search for roles in, colon separated
- 68 #roles_path    = /etc/ansible/roles
+ 68 roles_path    = /etc/ansible/roles
69
70 # uncomment this to disable SSH key host checking
71 #host_key_checking = False
[root@localhost ~]#

在ansible-galaxy命令后面跟一个init参数,创建一个新的角色信息,且建立成功后便会在当前目录下生成出一个新的目录

[root@localhost ~]# cd /etc/ansible/roles
[root@localhost roles]# ansible-galaxy init apache
- Role apache was created successfully
[root@localhost roles]# ls
apache
[root@localhost roles]#

此时的apache即是角色名称,也是用于存在角色信息的目录名称。切换到该目录下,查看它的结构

[root@localhost roles]# cd apache
[root@localhost apache]# ls
defaults  files  handlers  meta  README.md  tasks  templates  tests  vars
[root@localhost apache]#

在创建新角色时,最关键的便是能够正确理解目录结构。

Ansible角色目录结构及含义

目录

含义

defaults

包含角色变量的默认值(优先级低)。

files

包含角色执行tasks任务时做引用的静态文件。

handlers

包含角色的处理程序定义。

meta

包含角色的作者、许可证、频台和依赖关系等信息。

tasks

包含角色所执行的任务。

templates

包含角色任务所使用的Jinja2模板。

tests

包含用于测试角色的剧本文件。

vars

包含角色变量的默认值(优先级高)。

第1步:打开用于定义角色任务的tasks/main.yml文件。

在该文件中不需要定义要执行的主机组列表,因为后面会单独编写剧本进行调用,此时应先对apache角色能做的事情(任务)有一个明确的思路,在调用角色后yml文件会按照从上到下的顺序自动执行。

任务1:安装httpd网站服务。

任务2:运行httpd网站服务,并加入到开机启动项中。

任务3:配置防火墙,使其放行HTTP协议。

任务4:根据每台主机的变量值,生成不同的主页文件。

先写出第一个任务。使用yum模块安装httpd网站服务程序

[root@localhost apache]# vim tasks/main.yml
---
- name: oneyum:name: httpdstate: latest
[root@localhost apache]# 

第2步:使用service模块启动httpd网站服务程序,并加入到启动项中,保证能够一直为用户提供服务。在初次使用模块前,先用ansible-doc命令查看一下帮助和实例信息。

# ansible-doc service
> SERVICE    (/usr/lib/python3.6/site-packages/ansible/modules/system/service.py)Controls services on remote hosts. Supported init systemsinclude BSD init, OpenRC, SysV, Solaris SMF, systemd, upstart.For Windows targets, use the [win_service] module instead.* This module is maintained by The Ansible Core Team* note: This module has a corresponding action plugin.………………省略部分输出信息………………EXAMPLES:- name: Start service httpd, if not startedservice:name: httpdstate: started- name: Enable service httpd, and not touch the stateservice:name: httpdenabled: yes

通过输出信息可得知,启动服务为“state: started”参数,而加入到开机启动项则是“enabled: yes”参数

[root@localhost apache]# vim tasks/main.yml
---
- name: oneyum:name: httpdstate: latest
- name: twoservice:name: httpdstate: startedenabled: yes
[root@localhost apache]# 

第3步:配置防火墙的允许策略,让其他主机可以正常访问。在配置防火墙时,需要使用firewalld模块

# ansible-doc firewalld
> FIREWALLD    (/usr/lib/python3.6/site-packages/ansible/modules/system/firewalld.py)This module allows for addition or deletion of services andports (either TCP or UDP) in either running or permanentfirewalld rules.* This module is maintained by The Ansible Community
OPTIONS (= is mandatory):
EXAMPLES:- firewalld:service: httpspermanent: yesstate: enabled- firewalld:port: 8081/tcppermanent: yesstate: disabledimmediate: yes

依据输出信息可得知,在firewalld模块设置防火墙策略时,指定协议名称为“service: http”参数,放行该协议为“state: enabled”参数,设置为永久生效为“permanent: yes”参数,当前立即生效为“immediate: yes”参数。

[root@localhost apache]# vim tasks/main.yml
---
- name: oneyum:name: httpdstate: latest
- name: twoservice:name: httpdstate: startedenabled: yes
- name: threefirewalld:service: httppermanent: yesstate: enabledimmediate: yes
[root@localhost apache]#

第4步:让每台主机显示的主页文件均不相同。

在使用Ansible的常规模块时,都是采用“查询版主示例并模仿”的方式搞定的,这里为了增加难度,再提出个新需求,即能否让每台主机上运行的httpd网站服务都能显示不同的内容呢?例如显示当前服务器的主机名及IP地址。

这就要用到template模块及Jinja2技术了。

使用ansible-doc命令来查询template模块的使用方法

# ansible-doc template
> TEMPLATE    (/usr/lib/python3.6/site-packages/ansible/modules/files/template.>Templates are processed by the L(Jinja2 templatinglanguage,http://jinja.pocoo.org/docs/). Documentation on thetemplate formatting can be found in the L(Template DesignerDocumentation,http://jinja.pocoo.org/docs/templates/).Additional variables listed below can be used in templates.`ansible_managed' (configurable via the `defaults' section of`ansible.cfg') contains a string which can be used to describethe template name, host, modification time of the templatefile and the owner uid. `template_host' contains the node nameof the template's machine. `template_uid' is the numeric userid of the owner. `template_path' is the path of the template.`template_fullpath' is the absolute path of the template.`template_destpath' is the path of the template on the remotesystem (added in 2.8). `template_run_date' is the date thatthe template was rendered.* This module is maintained by The Ansible Core Team* note: This module has a corresponding action plugin.………………省略部分输出信息………………EXAMPLES:- name: Template a file to /etc/files.conftemplate:src: /mytemplates/foo.j2dest: /etc/file.confowner: bingroup: wheelmode: '0644'

从template模块的输出信息中可得知,这是一个用于复制文件模板的模块,能够把文件从Ansible服务器复制到受管主机上。其中,src参数用于定义本地文件的路径,dest参数用于定义复制到受管主机的文件路径,而owner、group、mode参数可选择性地设置文件归属及权限信息。

[root@localhost apache]# vim tasks/main.yml
---
- name: oneyum:name: httpdstate: latest
- name: twoservice:name: httpdstate: startedenabled: yes
- name: threefirewalld:service: httppermanent: yesstate: enabledimmediate: yes
- name: fourtemplate:src: index.html.j2dest: /var/www/html/index.html
[root@localhost apache]# 

Jinja2 是Python语言中一个被广泛使用的模板引擎,最初的设计思想源自Django的模块引擎。Jinja2基于此发展了其语法和一系列强大的功能,能够让受管主机根据自身变量产生出不同的文件内容。 使用Jinja2技术时,不是在原始文件中直接写入文件内容,而是写入一系列的变量名称。在使用template模块进行复制的过程中,由Ansible服务负责在受管主机上收集这些变量名称所对应的值,然后再逐一填写到目标文件中,从而让每台主机的文件都根据自身系统的情况独立生成。

想要让每个网站的输出信息值为“Welcome to主机名on主机地址”,也就是用每个主机自己独有的名称和IP地址来替换文本中的内容,这样就有趣太多了。这个实验的难点在于查询到对应的变量名称、主机名及地址所对应的值保存在哪里?可以用setup模块进行查询。

# ansible-doc setup
> SETUP    (/usr/lib/python3.6/site-packages/ansible/modules/system/setup.py)This module is automatically called by playbooks to gatheruseful variables about remote hosts that can be used inplaybooks. It can also be executed directly by`/usr/bin/ansible' to check what variables are available to ahost. Ansible provides many `facts' about the system,automatically. This module is also supported for Windowstargets.

setup模块的作用是自动收集受管主机上的变量信息,使用-a参数外加filter命令可以对收集来的信息进行二次过滤。

相应的语法格式为ansible all -m setup -a 'filter="*关键词*"',其中*号是第3章节讲到的通配符,用于进行关键词查询。

例如,如果想搜索各个主机的名称,可以使用通配符搜索所有包含fqdn关键词的变量值信息。

FQDN(Fully Qualified Domain Name,完全限定域名)用于在逻辑上准确表示出主机的位置。FQDN常常被作为主机名的完全表达形式,比/etc/hostname文件中定义的主机名更加严谨和准确。通过输出信息可得知,ansible_fqdn变量保存有主机名称。随后进行下一步操作:

[root@localhost apache]# ansible all -m setup -a 'filter="*fqdn*"'
192.168.10.20 | SUCCESS => {"ansible_facts": {"ansible_fqdn": "localhost.localdomain","discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false
}
192.168.10.30 | SUCCESS => {"ansible_facts": {"ansible_fqdn": "localhost.localdomain","discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false
}
[root@localhost apache]#

用于指定主机地址的变量可以用ip作为关键词进行检索。可以看到,ansible_all_ipv4_addresses变量中的值是我们想要的信息。如果想输出IPv6形式的地址,则可用ansible_all_ipv6_addresses变量。

[root@localhost apache]# ansible all -m setup -a 'filter="*ip*"'
192.168.10.20 | SUCCESS => {"ansible_facts": {"ansible_all_ipv4_addresses": ["192.168.10.20","192.168.122.1"],"ansible_all_ipv6_addresses": ["fe80::a7bc:9261:b95:a2f6","fe80::b10f:c9ee:e04b:f32a"],"ansible_default_ipv4": {"address": "192.168.10.20","alias": "ens160","broadcast": "192.168.10.255","gateway": "192.168.10.1","interface": "ens160","macaddress": "00:0c:29:79:79:70","mtu": 1500,"netmask": "255.255.255.0","network": "192.168.10.0","type": "ether"},"ansible_default_ipv6": {},"ansible_fips": false,"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false
}
192.168.10.30 | SUCCESS => {"ansible_facts": {"ansible_all_ipv4_addresses": ["192.168.10.30","192.168.122.1"],"ansible_all_ipv6_addresses": ["fe80::bd99:7be7:aa88:d431"],"ansible_default_ipv4": {"address": "192.168.10.30","alias": "ens160","broadcast": "192.168.10.255","gateway": "192.168.10.1","interface": "ens160","macaddress": "00:0c:29:96:ad:24","mtu": 1500,"netmask": "255.255.255.0","network": "192.168.10.0","type": "ether"},"ansible_default_ipv6": {},"ansible_fips": false,"discovered_interpreter_python": "/usr/libexec/platform-python"},"changed": false
}
[root@localhost apache]#

在确认了主机名与IP地址所对应的具体变量名称后,在角色所对应的templates目录内新建一个与上面的template模块参数相同的文件名称(index.html.j2)。Jinja2在调用变量值时,格式为在变量名称的两侧格加两个大括号

[root@localhost apache]# vim templates/index.html.j2
Welcome to {{ ansible_fqdn }} on {{ ansible_all_ipv4_addresses }}
[root@localhost apache]# 

进行到这里,任务基本就算完成了。

最后要做的就是编写一个用于调用apache角色的yml文件,以及执行这个文件。

[root@localhost apache]# cd ~
[root@localhost ~]# vim roles.yml
---
- name: 调用自建角色hosts: allroles:- apache
[root@localhost ~]# ansible-playbook roles.ymlPLAY [调用自建角色] *****************************************************************************************************************TASK [Gathering Facts] ********************************************************************************************************
ok: [192.168.10.20]
ok: [192.168.10.30]TASK [apache : one] ***********************************************************************************************************
changed: [192.168.10.20]
changed: [192.168.10.30]TASK [apache : two] ***********************************************************************************************************
changed: [192.168.10.20]
changed: [192.168.10.30]TASK [apache : three] *********************************************************************************************************
changed: [192.168.10.20]
changed: [192.168.10.30]TASK [apache : four] **********************************************************************************************************
changed: [192.168.10.20]
changed: [192.168.10.30]PLAY RECAP ********************************************************************************************************************
192.168.10.20              : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
192.168.10.30              : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   [root@localhost ~]# 

执行完毕后,在浏览器中随机输入几台主机的IP地址,即可访问到包含主机FQDN和IP地址的网页了

创建和使用逻辑卷

创建一个能批量、自动管理逻辑卷设备的剧本,不但能大大提高硬盘设备的管理效率,而且还能避免手动创建带来的错误。

在每台受管主机上都创建出一个名为data的逻辑卷设备,大小为150MB,归属于research卷组。如果创建成功,则进一步用Ext4文件系统进行格式化操作;如果创建失败,则给用户输出一条报错提醒,以便排查原因。

在这种情况下,使用Ansible剧本要比使用Shell脚本的优势大,原因主要有下面两点。

Ansible模块化的功能让操作更标准,只要在执行过程中无报错,那么便会依据远程主机的系统版本及配置自动做出判断和操作,不用担心因系统变化而导致命令失效的问题。

Ansible服务在执行剧本文件时会进行判断:如果该文件或该设备已经被创建过,或是某个动作(play)已经被执行过,则绝对不会再重复执行;而使用Shell脚本有可能导致设备被重复格式化,导致数据丢失。

在两台主机上分别添加一块硬盘设备,大小为5GB,类型为SATA,其余选项选择默认值。

让剧本文件依次创建物理卷(PV)、卷组(VG)及逻辑卷(LV)。

需要先使用lvg模块让设备支持逻辑卷技术,然后创建一个名为research的卷组。

lvg模块的帮助信息如下

# ansible-doc lvg
> LVG    (/usr/lib/python3.6/site-packages/ansible/modules/system/lvg.py)This module creates, removes or resizes volume groups.* This module is maintained by The Ansible Community………………省略部分输出信息………………EXAMPLES:- name: Create a volume group on top of /dev/sda1 with physical extent size = 3>lvg:vg: vg.servicespvs: /dev/sda1pesize: 32- name: Create a volume group on top of /dev/sdb with physical extent size = 12>lvg:vg: vg.servicespvs: /dev/sdbpesize: 128K

通过输出信息可得知,创建PV和VG的lvg模块总共有3个必备参数。其中,vg参数用于定义卷组的名称,pvs参数用于指定硬盘设备的名称,pesize参数用于确定最终卷组的容量大小(可以用PE个数或容量值进行指定)。这样一来,我们先创建出一个由/dev/sdb设备组成的名称为research、大小为150MB的卷组设备。

[root@localhost ~]# vim lv.yml
---
- name: 创建和使用逻辑卷hosts: alltasks:- name: onelvg:vg: researchpvs: /dev/sdbpesize: 150M
[root@localhost ~]#

接下来使用lvol模块创建出逻辑卷设备

# ansible-doc lvol
> LVOL    (/usr/lib/python3.6/site-packages/ansible/modules/system/lvol.py)This module creates, removes or resizes logical volumes.* This module is maintained by The Ansible Community………………省略部分输出信息………………EXAMPLES:- name: Create a logical volume of 512mlvol:vg: fireflylv: testsize: 512- name: Create a logical volume of 512m with disks /dev/sda and /dev/sdblvol:vg: fireflylv: testsize: 512pvs: /dev/sda,/dev/sdb

lvol是用于创建逻辑卷设备的模块。

其中,vg参数用于指定卷组名称,lv参数用于指定逻辑卷名称,size参数则用于指定最终逻辑卷设备的容量大小(不用加单位,默认为MB)。

填写好参数,创建出一个大小为150MB、归属于research卷组且名称为data的逻辑卷设备

[root@localhost ~]# vim lv.yml
---
- name: 创建和使用逻辑卷hosts: alltasks:- name: onelvg:vg: researchpvs: /dev/sdbpesize: 150M- name: twolvol:vg: researchlv: datasize: 150M
[root@localhost ~]# 

使用filesystem模块来完成设备的文件系统格式化操作

# ansible-doc filesystem
> FILESYSTEM    (/usr/lib/python3.6/site-packages/ansible/modules/system/filesy>This module creates a filesystem.* This module is maintained by The Ansible Community………………省略部分输出信息………………EXAMPLES:- name: Create a ext2 filesystem on /dev/sdb1filesystem:fstype: ext2dev: /dev/sdb1

filesystem模块的参数真是简练,fstype参数用于指定文件系统的格式化类型,dev参数用于指定要格式化的设备文件路径。

[root@localhost ~]# vim lv.yml
---
- name: 创建和使用逻辑卷hosts: alltasks:- name: onelvg:vg: researchpvs: /dev/sdbpesize: 150M- name: twolvol:vg: researchlv: datasize: 150M- name: threefilesystem:fstype: ext4dev: /dev/research/data
[root@localhost ~]#

用block操作符将上述的3个模块命令作为一个整体(相当于对这3个模块的执行结果作为一个整体进行判断),然后使用rescue操作符进行救援,且只有block块中的模块执行失败后才会调用rescue中的救援模块。

其中,debug模块的msg参数的作用是,如果block中的模块执行失败,则输出一条信息到屏幕,用于提醒用户。

[root@localhost ~]# vim lv.yml
---
- name: 创建和使用逻辑卷hosts: alltasks:- block:- name: onelvg:vg: researchpvs: /dev/sdbpesize: 150M- name: twolvol:vg: researchlv: datasize: 150M- name: threefilesystem:fstype: ext4dev: /dev/research/datarescue:           - debug:msg: "Could not create logical volume of that size"
[root@localhost ~]#

YAML语言对格式有着硬性的要求,既然rescue是对block内的模块进行救援的功能代码,因此recue和block两个操作符必须严格对齐,错开一个空格都会导致剧本执行失败。

[root@localhost ~]# ansible-playbook lv.ymlPLAY [创建和使用逻辑卷] **************************************************************************TASK [Gathering Facts] *******************************************************************
ok: [192.168.10.20]
ok: [192.168.10.30]TASK [one] *******************************************************************************
changed: [192.168.10.20]
changed: [192.168.10.30]TASK [two] *******************************************************************************
changed: [192.168.10.20]
changed: [192.168.10.30]TASK [three] *****************************************************************************
changed: [192.168.10.20]
changed: [192.168.10.30]PLAY RECAP *******************************************************************************
192.168.10.20              : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
192.168.10.30              : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   [root@localhost ~]#

在执行了逻辑卷主机上 输入 # lvdisplay  可以查看到新创建的逻辑卷

[ 192.168.10.20 ]

[root@localhost ~]# lvdisplay--- Logical volume ---LV Path                /dev/research/dataLV Name                dataVG Name                researchLV UUID                WltmOq-4l5T-or3D-0MTY-oty5-cSNm-JNkK7xLV Write Access        read/writeLV Creation host, time localhost.localdomain, 2021-11-26 14:21:28 +0800LV Status              available# open                 0LV Size                150.00 MiBCurrent LE             1Segments               1Allocation             inheritRead ahead sectors     auto- currently set to     8192Block device           253:2--- Logical volume ---LV Path                /dev/rhel/swapLV Name                swapVG Name                rhelLV UUID                gdsTVM-5yAN-oej7-FQq7-Mtfh-LRel-YPjy1LLV Write Access        read/writeLV Creation host, time localhost, 2021-04-01 14:46:09 +0800LV Status              available# open                 2LV Size                2.00 GiBCurrent LE             512Segments               1Allocation             inheritRead ahead sectors     auto- currently set to     8192Block device           253:1--- Logical volume ---LV Path                /dev/rhel/rootLV Name                rootVG Name                rhelLV UUID                o2vlUv-ZZ2U-vfYg-5kwM-uzB8-EFIx-Cxa6uoLV Write Access        read/writeLV Creation host, time localhost, 2021-04-01 14:46:09 +0800LV Status              available# open                 1LV Size                <17.00 GiBCurrent LE             4351Segments               1Allocation             inheritRead ahead sectors     auto- currently set to     8192Block device           253:0[root@localhost ~]#

判断主机组名

在每个客户端中都会有一个名为inventory_hostname的变量,用于定义每台主机所对应的Ansible服务的主机组名称,也就是/etc/ansible/hosts文件中所对应的分组信息,例如dev、test、prod、balancers。

inventory_hostname是Ansible服务中的魔法变量,这意味着无法使用setup模块直接进行查询,诸如ansible all -m setup -a 'filter="*关键词*"'这样的命令将对它失效。

魔法变量需要在执行剧本文件时的Gathering Facts阶段进行搜集,直接查询是看不到的,只能在剧本文件中进行调用。

在获得了存储主机组名称的变量名称后,接下来开始实战。

需求:

若主机在dev分组中,则修改/etc/issue文件内容为Development;

若主机在test分组中,则修改/etc/issue文件内容为Test;

可以用  Ansible 的 copy 来完成此需求,copy 模块的主要作用是新建、修改及复制文件。

# ansible-doc copy
> COPY    (/usr/lib/python3.6/site-packages/ansible/modules/files/copy.py)The `copy' module copies a file from the local or remotemachine to a location on the remote machine. Use the [fetch]module to copy files from remote locations to the local box.If you need variable interpolation in copied files, use the[template] module. Using a variable in the `content' fieldwill result in unpredictable output. For Windows targets, usethe [win_copy] module instead.* This module is maintained by The Ansible Core Team* note: This module has a corresponding action plugin.………………省略部分输出信息………………EXAMPLES:- name: Copy file with owner and permissionscopy:src: /srv/myfiles/foo.confdest: /etc/foo.confowner: foogroup: foomode: '0644'- name: Copy using inline contentcopy:content: '# This file was moved to /etc/other.conf'dest: /etc/mine.conf

在输出信息中列举了两种管理文件内容的示例。

第一种用于文件的复制行为,

第二种是通过content参数定义内容,通过dest参数指定新建文件的名称。

依据inventory_hostname变量中的值进行判断。若主机为dev组,则执行第一个动作;若主机为test组,则执行第二个动作;

[root@localhost ~]# vim issue.yml
---
- name: 修改文件内容hosts: alltasks:- name: onecopy:content: 'Development'dest: /etc/issuewhen: "inventory_hostname in groups.dev"- name: twocopy:content: 'Test'dest: /etc/issue          when: "inventory_hostname in groups.test"
[root@localhost ~]#
[root@localhost ~]# ansible-playbook issue.ymlPLAY [修改文件内容] ****************************************************************************TASK [Gathering Facts] *******************************************************************
ok: [192.168.10.20]
ok: [192.168.10.30]TASK [one] *******************************************************************************
skipping: [192.168.10.30]
changed: [192.168.10.20]TASK [two] *******************************************************************************
skipping: [192.168.10.20]
changed: [192.168.10.30]PLAY RECAP *******************************************************************************
192.168.10.20              : ok=2    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
192.168.10.30              : ok=2    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   [root@localhost ~]#

[ 192.168.10.20 ]

[root@localhost ~]# cat /etc/issue
Development

管理文件属性

学习剧本的目的是为了满足日常的工作需求,把重复的事情写入到脚本中,然后再批量执行下去,从而提高运维工作的效率。

其中,创建文件、管理权限以及设置快捷方式几乎是每天都用到的技能。

Ansible服务将常用的文件管理功能都合并到了file模块中。

# ansible-doc file
> FILE    (/usr/lib/python3.6/site-packages/ansible/modules/files/file.py)Set attributes of files, symlinks or directories.Alternatively, remove files, symlinks or directories. Manyother modules support the same options as the `file' module -including [copy], [template], and [assemble]. For Windowstargets, use the [win_file] module instead.* This module is maintained by The Ansible Core Team………………省略部分输出信息………………EXAMPLES:- name: Change file ownership, group and permissionsfile:path: /etc/foo.confowner: foogroup: foomode: '0644'- name: Create a symbolic linkfile:src: /file/to/link/todest: /path/to/symlinkowner: foogroup: foostate: link- name: Create a directory if it does not existfile:path: /etc/some_directorystate: directorymode: '0755'- name: Remove file (delete file)file:path: /etc/foo.txtstate: absent

通过上面的输出示例,已经能够了解file模块的基本参数了。其中,path参数定义了文件的路径,owner参数定义了文件所有者,group参数定义了文件所属组,mode参数定义了文件权限,src参数定义了源文件的路径,dest参数定义了目标文件的路径,state参数则定义了文件类型。

实验:

创建出一个名为 /zhangsan 的新目录,所有者及所属组均为root管理员身份;

设置所有者和所属于组拥有对文件的完全控制权,而其他人则只有阅读和执行权限;

给予SGID特殊权限;

仅在dev主机组的主机上实施。

再创建一个名称为 /lisi 的快捷方式文件,指向刚刚建立的 /zhangsan 目录

[root@localhost ~]# vim chmod.yml
---
- name: 管理文件属性hosts: devtasks:- name: onefile:path: /zhangsanstate: directoryowner: rootgroup: rootmode: '2775'- name: twofile:src: /zhangsandest: /lisistate: link
[root@localhost ~]#
[root@localhost ~]# ansible-playbook chmod.ymlPLAY [管理文件属性] ****************************************************************************TASK [Gathering Facts] *******************************************************************
ok: [192.168.10.20]TASK [one] *******************************************************************************
changed: [192.168.10.20]TASK [two] *******************************************************************************
changed: [192.168.10.20]PLAY RECAP *******************************************************************************
192.168.10.20              : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   [root@localhost ~]#

[ 192.168.10.20 ]

[root@localhost ~]# ls -ld /zhangsan
drwxrwsr-x. 2 root root 6 Nov 26 14:27 /zhangsan
[root@localhost ~]# ls -ld /lisi
lrwxrwxrwx. 1 root root 9 Nov 26 14:27 /lisi -> /zhangsan
[root@localhost ~]#

管理密码库文件

自Ansible 1.5版本发布后,vault作为一项新功能进入到了运维人员的视野。

它不仅能对密码、剧本等敏感信息进行加密,而且还可以加密变量名称和变量值,从而确保数据不会被他人轻易阅读。

使用ansible-vault命令可以实现内容的新建(create)、加密(encrypt)、解密(decrypt)、修改密码(rekey)及查看(view)等功能。

第1步:创建出一个名为locker.yml的配置文件,其中保存了两个变量值

[root@localhost ~]# vim locker.yml
---
pw_developer: Imadev
pw_manager: Imamgr
[root@localhost ~]# 

第2步:使用ansible-vault命令对文件进行加密。由于需要每次输入密码比较麻烦,因此还应新建一个用于保存密码值的文本文件,以便让ansible-vault命令自动调用。为了保证数据的安全性,在新建密码文件后将该文件的权限设置为600,确保仅管理员可读可写

[root@localhost ~]# vim /root/secret.txt
whenyouwishuponastar
[root@localhost ~]# chmod 600 /root/secret.txt
[root@localhost ~]#

在Ansible服务的主配置文件中,在第140行的vault_password_file参数后指定密码值保存的文件路径

[root@localhost ~]# vim /etc/ansible/ansible.cfg
138 # If set, configures the path to the Vault password file as an alternative to
139 # specifying --vault-password-file on the command line.
- 140 #vault_password_file = /path/to/vault_password_file
+ 140 vault_password_file = /root/secret.txt
141
142 # format of string {{ ansible_managed }} available within Jinja2
[root@localhost ~]# 

第3步:在设置好密码文件的路径后,Ansible服务便会自动进行加载。用户也就不用在每次加密或解密时都重复输入密码了。

在加密刚刚创建的locker.yml文件时,只需要使用encrypt参数即可

[root@localhost ~]# ansible-vault encrypt locker.yml
Encryption successful
[root@localhost ~]# cat locker.yml
$ANSIBLE_VAULT;1.1;AES256
34623765653933353164353962336431303961336438313037366436323639316234333362333233
6465386466356332626235643239663438363466616635340a373031313463313536326139386231
63653366623538643939383031646661613730663435633463363937346330366333356565636631
6231353762333163660a373431666266333335613139376633356434363438303132643462333466
37666264626330313665633334633137383239646166343638376536393965303663643566613237
3739386566383439363533653333643539373536383862616637
[root@localhost ~]#

如果不想使用原始密码了呢?也可以使用rekey参数手动对文件进行改密操作,同时应结合--ask-vault-pass参数进行修改,否则Ansible服务会因接收不到用户输入的旧密码值而拒绝新的密码变更请求

[root@localhost ~]# ansible-vault rekey --ask-vault-pass locker.yml
Vault password: #旧密码
New Vault password: #新密码
Confirm New Vault password: #重复新密码
Rekey successful
[root@localhost ~]#

第4步:如果想查看和修改加密文件中的内容,该怎么操作呢?对于已经加密过的文件,需要使用ansible-vault命令的edit参数进行修改,随后用view参数即可查看到修改后的内容。ansible-vault命令对加密文件的编辑操作默认使用的是Vim编辑器,在修改完毕后请记得执行wq操作保存后退出

[root@localhost ~]# ansible-vault edit locker.yml
---
pw_developer: Imadev
pw_manager: Imamgr
pw_production: Imaprod
[root@localhost ~]# 

最后,再用view参数进行查看,便是最新的内容了

[root@localhost ~]# ansible-vault view locker.yml
---
pw_developer: Imadev
pw_manager: Imamgr
pw_production: Imaprod
[root@localhost ~]#

LinuxProbe 0x21 使用Ansible服务实现自动化运维相关推荐

  1. puppet、Ansible、SaltStack 自动化运维工具简单对比

    puppet: 有产品线已经在用,优点是历史悠久,比较成熟,在可远程可本地,功能强劲,不过这厮批量执行功能没得,为了批量执行个命令写个配置文件,好像有点大刀砍蚊子腿的感觉了,而且有客户端在,和授权系统 ...

  2. 自动化运维工具——ansible安装及模块介绍

    ansbile 前言 一.主流自动化运维工具简介 1.1 Puppet 1.2 Saltstack 1.3 Ansible 二.Ansible 运维工具原理 三.Ansible安装 3.1 下载软件包 ...

  3. WebHook入门教程:快速实现自动化运维,如自动热部署、自动重启服务、自动备份数据库等等

    WebHook入门教程 当我们向Github仓库(其他支持webhook的Git仓库都可以)Push代码后,可以通过webhook向特定URL发起一次Post请求,本篇文章所说的WebHook,就是运 ...

  4. Linux 自动化运维工具 ansible

    文章目录 1.简介 2.安装 3.常用命令选项说明 4.使用 4.1.执行远程命令 4.2.执行远程脚本 4.3.分发文件到远程服务器 4.3.1.复制单个文件 4.3.2.复制压缩文件到远程并解压 ...

  5. Linux自动化运维工具ansible详解

    文章目录 认识ansible ansible的组成 ansible的相关文件 ansible的使用 ansible的常用模块 1.copy模块 2.fetch模块 3.command模块 4.shel ...

  6. 自动化运维—saltstack

    2019独角兽企业重金招聘Python工程师标准>>> 自动化运维--saltstack .ansible 一.自动化运维介绍 传统运维:传统运维效率低,大多工作需要人工完成,工作繁 ...

  7. 自动化运维之 安装部署 Ansible 服务

    Ansible 概述 由于互联网的快速发展导致产品更新换代速度逐渐加快,运维人员每天都要进行大量的维护操作,仍旧按照传统方式进行维护使得工作效率低下.这是,部署自动化运维就可以尽可能的安全.高效地完成 ...

  8. 大型企业中如何批量管理千万台服务器之ansible自动化运维工具详解 [⭐建议收藏⭐]

    文章目录 ansible 自动化运维工具 详解 关于作者 作者介绍 一.ansible 概述 1.1 ansible 概述 1.2 是什么要使用 ansible 1.3 ansible 功能 1.4 ...

  9. 自动化运维工具----ansible

    自动化运维工具----ansible ansible是新出现的运维工具是基于Python研发的糅合了众多老牌运维工具的优点实现了批量操作系统配置.批量程序的部署.批量运行命令等功能. 主要模块以及功能 ...

最新文章

  1. Django 搭建CMDB系统完整[1](用户登录)
  2. Translation
  3. 验证码广告:站长增加收入新渠道
  4. Python将序列分解为单独的变量
  5. C++中的多态(一)
  6. C#比較对象的相等性
  7. 怎样增强MyEclipse的代码自动提示功能
  8. java学习(162):同步对象锁
  9. I/O流之缓冲流的文件复制代码及节点流处理数据加密
  10. Python基础笔记(一)数据类型、变量、字符串
  11. 程序员面试金典——9.1上楼梯
  12. Android图片的三级缓存整理
  13. linux命令hexdump,Linux中hexdump命令起什么作用呢?
  14. linux进程显示exit是怎么回事,linux 进程退出exit,_exit区别即atexit函数
  15. GreeDAO 使用
  16. 读《Oracle 数据库应用与实践》
  17. Qt实现mqtt客户端和mqtt服务器搭建
  18. MAC下的Sublime Text关闭自动更新提示,关闭更新检查,适用于Sublime 3和Sublime 4
  19. iphone13支持双卡双待吗 苹果13是5g吗
  20. 数字密码锁verilog设计+仿真+上板验证

热门文章

  1. TinyG 入门06
  2. LabVIEW辨识颜色小游戏
  3. 微信公众号订阅通知(go+vue)
  4. java语言相比于c,【多选题】Java 语言相比于C,C++的不同之处在于:( ) A. 不支持指针 B. Java...
  5. Access violation reading location 0x000000XX
  6. 【蓝桥杯考前一天总结PYthon终结篇】
  7. 人脸图像切割分离工具
  8. 通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core? 转自:https://www.cnblogs.com/1996V/p/9037603.html#net1...
  9. 器械传递的方法_手术器械的传递方法
  10. HTML如何使用隐藏图片,css3如何隐藏图片?