Ansible模块官网

CentOS7下使用ansible远程连接被管理主机

前提都是关闭防火墙以及selinux

准备阶段

准备三台纯新的 CentOS7 服务器,一台安装 ansible1 ,其他2台可以不安装ansible。分别为ansible2和ansible3.

hostname ip地址 用途
ansible1 192.168.32.7 ansible1 管理端
ansible2 192.168.32.17 ansible2 远程被管理主机
ansible3 192.168.32.27 ansible3 远程被管理主机

[root@ansible3 ~]#yum install ansible -y
安装过程省略......[root@ansible1 ~]#ansible --version
ansible 2.9.1config file = /etc/ansible/ansible.cfgconfigured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']ansible python module location = /usr/lib/python2.7/site-packages/ansibleexecutable location = /usr/bin/ansiblepython version = 2.7.5 (default, Aug  7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]#ansible 主机上配置被管理远程服务器信息
[root@ansible1 ~]#vi /etc/ansible/hosts
[root@ansible1 ~]#grep -Ev '^#|^$' /etc/ansible/hosts
[websrvs]            #管理的组名
192.168.32.17        #管理的主机IP
192.168.32.27
[appsrvs]
192.168.32.27
[root@ansible1 ~]#

使用 ansible 命令进行 ping 其他被控的测试

ansible 命令行使用格式 ansible 被管理主机 选项 -m 模块名称

192.168.32.27
[root@ansible1 ~]#ansible all -m ping
The authenticity of host '192.168.32.27 (192.168.32.27)' can't be established.
ECDSA key fingerprint is SHA256:f1DfAjEhgKwDKY3RyCy9Yu1HVakT5g3S2877lDSI8Dc.
ECDSA key fingerprint is MD5:77:35:51:be:5d:71:b5:0a:0c:8a:2f:c2:3f:eb:d8:7e.
Are you sure you want to continue connecting (yes/no)? The authenticity of host '192.168.32.17 (192.168.32.17)' can't be
established.
ECDSA key fingerprint is SHA256:f1DfAjEhgKwDKY3RyCy9Yu1HVakT5g3S2877lDSI8Dc.
ECDSA key fingerprint is MD5:77:35:51:be:5d:71:b5:0a:0c:8a:2f:c2:3f:eb:d8:7e.
Are you sure you want to continue connecting (yes/no)? yes
192.168.32.27 | UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Warning: Permanently added '192.168.32.27' (ECDSA) to the list ofknown hosts.\r\nPermission denied (publickey,gssapi-keyex,gssapi-with-mic,password).", "unreachable": true
}192.168.32.17 | UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Host key verification failed.", "unreachable": true
}
[root@ansible1 ~]#

上面的执行过程默认使用的 ssh 秘密验证登录方式,由于我们没有配置,因此 ping 操作失败

下面我们使用 -k 选项指明要求输入密码进行登录验证

[root@ansible1 ~]#ansible all -k -m ping
SSH password:            #输入远程被控主机密钥
192.168.32.17 | FAILED! => {"msg": "Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this.  Please add this host's fingerprint to your known_hosts file to manage this host."
}
192.168.32.27 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"
}
[root@ansible1 ~]#

可以看到,对于 -k 选择密钥ssh连接只是连接多主机中的一个。所以成功一个,一个依旧失败。
如果是管理单个主机,输入一次密码之后系统会自动记住,下次不用输入密码。
在删除 ansible 本地配置信息后,密码失效

SSH免密配置

配置免密登录秘钥进行主机访问

[root@ansible1 ~]#ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:43qdRpMhKltu4AH1x7Ag0LC4Dyajkovg/99xni1ww0M root@centos7mini
The keys randomart image is:
+---[RSA 2048]----+
|o+               |
|..o o .          |
|o  o o +         |
| ..   o + .E     |
|=. .   oS.oo     |
|+=  + o. o+=     |
|= .. B  .+o+o    |
|=.  o o.o B.o    |
|o....ooo o o..   |
+----[SHA256]-----+[root@ansible1 ~]#vi /etc/ssh/ssh_config StrictHostKeyChecking no         #这项不修改会导致先回答yes[root@ansible1 ~]#NET=192.168.32; for i in 17 27 ;do sshpass -p centos ssh-copy-id $NET.$i ;done
#编辑一个循环语句把公钥拷贝到被管理的主机
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keysNumber of key(s) added: 1Now try logging into the machine, with:   "ssh '192.168.32.17'"
and check to make sure that only the key(s) you wanted were added./usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keysNumber of key(s) added: 1Now try logging into the machine, with:   "ssh '192.168.32.27'"
and check to make sure that only the key(s) you wanted were added.[root@ansible1 ~]#
测试是否免密钥ssh连接:
[root@ansible1 ~]#ssh 192.168.32.17
Last login: Wed Dec  4 21:35:17 2019 from 192.168.32.7
[root@ansible2 ~]#exit
logout
Connection to 192.168.32.17 closed.
[root@ansible1 ~]#ssh 192.168.32.27
Last login: Wed Dec  4 21:38:44 2019 from 192.168.32.7
[root@ansible3 ~]#exit
logout
Connection to 192.168.32.27 closed.
[root@ansible1 ~]#
#测试 ansible1 访问 全部的受控主机 all代表所有 也可以只是管理组[wsbsrvs]或者[appsrvs]
[root@ansible1 ~]#ansible all -m ping
192.168.32.27 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"
}
192.168.32.17 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"
}
[root@ansible1 ~]#

CentOS7下 ansible 常用模块介绍

ansible-doc 模块

使用 ansible-doc 命令查看指定模块的帮助信息
使用 ansible-doc -s 可以查看简略的帮助信息

ansible-doc [options] [module…]
-l, --list #列出可用模块
-s, --snippet #显示指定模块的playbook片段

#列出所有模块
ansible-doc -l
#查看指定模块帮助用法
ansible-doc ping
#查看指定模块帮助用法
ansible-doc -s ping

[root@ansible1 ~]#ansible-doc ping[root@ansible1 ~]#ansible-doc -s ping
- name: Try to connect to host, verify a usable python and return `pong' on successping:data:                  # Data to return for the `ping' return value. If this parameter is set to `crash',the module will cause an exception.

使用 ansible-doc --list 可以查看所有支持的 ansible 模块

[root@ansible1 ~]#ansible-doc --list

setup 模块使用

setup 模块可以获取被管理主机的系统信息和变量

[root@ansible1 ~]#ansible websrvs -m setup[root@ansible1 ~]#ansible websrvs -m setup|wc -l
1306

范例:

ansible srv -m setup
ansible srv -m setup -a "filter=ansible_nodename"
ansible srv -m setup -a "filter=ansible_hostname"
ansible srv -m setup -a "filter=ansible_domain"
ansible srv -m setup -a "filter=ansible_memtotal_mb"
ansible srv -m setup -a "filter=ansible_memory_mb"
ansible srv -m setup -a "filter=ansible_memfree_mb"
ansible srv -m setup -a "filter=ansible_os_family"
ansible srv -m setup -a "filter=ansible_distribution_major_version"
ansible srv -m setup -a "filter=ansible_distribution_version"
ansible srv -m setup -a "filter=ansible_processor_vcpus"
ansible srv -m setup -a "filter=ansible_all_ipv4_addresses"
ansible srv -m setup -a "filter=ansible_architecture"

由于默认获取全部信息,我们可以使用 filter 进行过滤显示指定信息:

[root@ansible1 ~]#ansible websrvs -m setup -a 'filter=ansible_*_mb'
192.168.32.17 | SUCCESS => {"ansible_facts": {"ansible_memfree_mb": 399, "ansible_memory_mb": {"nocache": {"free": 1018, "used": 472}, "real": {"free": 399, "total": 1490, "used": 1091}, "swap": {"cached": 0, "free": 2047, "total": 2047, "used": 0}}, "ansible_memtotal_mb": 1490, "ansible_swapfree_mb": 2047, "ansible_swaptotal_mb": 2047, "discovered_interpreter_python": "/usr/bin/python"}, "changed": false
}
192.168.32.27 | SUCCESS => {"ansible_facts": {"ansible_memfree_mb": 630, "ansible_memory_mb": {"nocache": {"free": 1195, "used": 295}, "real": {"free": 630, "total": 1490, "used": 860}, "swap": {"cached": 0, "free": 2047, "total": 2047, "used": 0}}, "ansible_memtotal_mb": 1490, "ansible_swapfree_mb": 2047, "ansible_swaptotal_mb": 2047, "discovered_interpreter_python": "/usr/bin/python"}, "changed": false
}
[root@ansible1 ~]#
[root@ansible1 ~]#ansible websrvs -m setup -a 'filter=*ipv4*'  #查看包含IPV4的管理主机信息
192.168.32.27 | SUCCESS => {"ansible_facts": {"ansible_all_ipv4_addresses": ["192.168.32.167", "192.168.32.27"], "ansible_default_ipv4": {"address": "192.168.32.167", "alias": "eth0", "broadcast": "192.168.32.255", "gateway": "192.168.32.2", "interface": "eth0", "macaddress": "00:0c:29:70:06:c4", "mtu": 1500, "netmask": "255.255.255.0", "network": "192.168.32.0", "type": "ether"}, "discovered_interpreter_python": "/usr/bin/python"}, "changed": false
}
192.168.32.17 | SUCCESS => {"ansible_facts": {"ansible_all_ipv4_addresses": ["192.168.32.166", "192.168.32.17"], "ansible_default_ipv4": {"address": "192.168.32.166", "alias": "eth0", "broadcast": "192.168.32.255", "gateway": "192.168.32.2", "interface": "eth0", "macaddress": "00:0c:29:ce:40:a9", "mtu": 1500, "netmask": "255.255.255.0", "network": "192.168.32.0", "type": "ether"}, "discovered_interpreter_python": "/usr/bin/python"}, "changed": false
}

command 模块

功能:在远程主机执行命令,此为默认模块,可忽略-m选项
注意:此命令不支持 $VARNAME < > | ; & 等,用shell模块实现

[root@ansible1 ~]#ansible appsrvs -m command -a 'ip a'
192.168.32.27 | CHANGED | rc=0 >>
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope host valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:0c:29:70:06:c4 brd ff:ff:ff:ff:ff:ffinet 192.168.32.167/24 brd 192.168.32.255 scope global noprefixroute dynamic eth0valid_lft 1621sec preferred_lft 1621secinet 192.168.32.27/24 brd 192.168.32.255 scope global secondary noprefixroute eth0valid_lft forever preferred_lft foreverinet6 fe80::d503:83cc:5e5:94fc/64 scope link noprefixroute valid_lft forever preferred_lft forever[root@ansible1 ~]#

command 命令并非使用 shell 执行,因此变量 和 shell 的特殊符号无法使用,比如管道 重定向等等

[root@ansible1 ~]#ansible appsrvs -m command -a 'ip a > ip_info.txt'
192.168.32.27 | FAILED | rc=255 >>
Command ">" is unknown, try "ip address help".non-zero return code

shell 模块

该模块将在远程主机上使用 /bin/sh 执行命令

[root@ansible1 ~]#ansible appsrvs -m shell -a 'ip a > ip_info.txt'
192.168.32.27 | CHANGED | rc=0 >>[root@ansible1 ~]#ansible appsrvs -m shell -a 'cat ip_info.txt'
192.168.32.27 | CHANGED | rc=0 >>
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope host valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:0c:29:70:06:c4 brd ff:ff:ff:ff:ff:ffinet 192.168.32.167/24 brd 192.168.32.255 scope global noprefixroute dynamic eth0valid_lft 1452sec preferred_lft 1452secinet 192.168.32.27/24 brd 192.168.32.255 scope global secondary noprefixroute eth0valid_lft forever preferred_lft foreverinet6 fe80::d503:83cc:5e5:94fc/64 scope link noprefixroute valid_lft forever preferred_lft forever[root@ansible1 ~]#

这里由于shell模块比默认的command模块更加的方便使用,所以可以把shell模块改为默认使用的模块

[root@ansible1 ~]#vi /etc/ansible/ansible.cfg
114 #module_name = shell[root@ansible1 ~]#ansible appsrvs -a 'cat ip_info.txt'  #省略-m shell 调用模块
192.168.32.27 | CHANGED | rc=0 >>
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope host valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:0c:29:70:06:c4 brd ff:ff:ff:ff:ff:ffinet 192.168.32.167/24 brd 192.168.32.255 scope global noprefixroute dynamic eth0valid_lft 1452sec preferred_lft 1452secinet 192.168.32.27/24 brd 192.168.32.255 scope global secondary noprefixroute eth0valid_lft forever preferred_lft foreverinet6 fe80::d503:83cc:5e5:94fc/64 scope link noprefixroute valid_lft forever preferred_lft forever[root@ansible1 ~]#

注意:调用bash执行命令 类似 cat /tmp/test.md | awk -F‘|’ ‘{print 2}’ &> /tmp/example.txt 这些复
杂命令,即使使用shell也可能会失败,解决办法:写到脚本时,copy到远程,执行,再把需要的结果
拉回执行命令的机器

script 模块

script 模块可以将本地脚本在远程服务器上执行

[root@ansible1 ~]#
[root@ansible1 ~]#mkdir /data/ansible
[root@ansible1 ~]#cd /data/ansible/
[root@ansible1 ansible]#vi shellscript.sh
[root@ansible1 ansible]#bash shellscript.sh
hello world!!!
centos7mini
[root@ansible1 ansible]#
[root@ansible1 ansible]#ansible all -m script -a shellscript.sh
192.168.32.27 | CHANGED => {"changed": true, "rc": 0, "stderr": "Shared connection to 192.168.32.27 closed.\r\n", "stderr_lines": ["Shared connection to 192.168.32.27 closed."], "stdout": "hello world!!!\r\ncentos7mini\r\n", "stdout_lines": ["hello world!!!", "centos7mini"]
}
192.168.32.17 | CHANGED => {"changed": true, "rc": 0, "stderr": "Shared connection to 192.168.32.17 closed.\r\n", "stderr_lines": ["Shared connection to 192.168.32.17 closed."], "stdout": "hello world!!!\r\ncentos7mini\r\n", "stdout_lines": ["hello world!!!", "centos7mini"]
}
[root@ansible1 ansible]#

copy 模块

使用 copy 模块拷贝本地文件到远程服务器
范例:

#如目标存在,默认覆盖,此处指定先备份
ansible srv -m copy -a “src=/root/test1.sh dest=/tmp/test2.sh owner=wang
mode=600 backup=yes”
#指定内容,直接生成目标文件
ansible srv -m copy -a “content='test content\n' dest=/tmp/test.txt”
#复制/etc/下的文件,不包括/etc/目录自身
ansible srv -m copy -a “src=/etc/ dest=/backup”
[root@ansible1 ansible]#ansible appsrvs -m copy -a "src=/data/ansible/shellscript.sh dest=/data/shells.sh"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "checksum": "2c8081b9c69fb8bd841d0dcd7115856ca7077239", "dest": "/data/shells.sh", "gid": 0, "group": "root", "md5sum": "1baab4bf9f632e509e980f74ed68ed2e", "mode": "0644", "owner": "root", "size": 52, "src": "/root/.ansible/tmp/ansible-tmp-1575469525.0-102991088187458/source", "state": "file", "uid": 0
}
[root@ansible1 ansible]#ansible appsrvs -m command -a 'ls -l /data'
192.168.32.27 | CHANGED | rc=0 >>
total 4
-rw-r--r-- 1 root root 52 Dec  4 22:25 shells.sh[root@ansible1 ansible]#

fetch 模块

fetch 模块可以将远程服务器的文件复制到本地 ansible1 主机中,目前不支持目录

[root@ansible1 ansible]#ansible appsrvs -m fetch -a "src=/etc/issue dest=/data/ansible/issue.bak"
192.168.32.27 | CHANGED => {"changed": true, "checksum": "5c76e3b565c91e21bee303f15c728c71e6b39540", "dest": "/data/ansible/issue.bak/192.168.32.27/etc/issue", "md5sum": "f078fe086dfc22f64b5dca2e1b95de2c", "remote_checksum": "5c76e3b565c91e21bee303f15c728c71e6b39540", "remote_md5sum": null
}
[root@ansible1 ansible]#ll
total 4
drwxr-xr-x 3 root root 27 Dec  4 22:28 issue.bak
-rw-r--r-- 1 root root 52 Dec  4 22:20 shellscript.sh
[root@ansible1 ansible]#

file 模块

管理远程主机上的文件属性和软硬连接功能

范例:

#创建空文件
ansible srv -m file -a 'path=/data/test.txt state=touch'
ansible srv -m file -a 'path=/data/test.txt state=absent'
ansible srv -m file -a "path=/root/test.sh owner=wang mode=755“
#创建目录
ansible srv -m file -a "path=/data/mysql state=directory owner=mysql
group=mysql"
#创建软链接
ansible srv -m file -a ‘src=/data/testfile dest=/data/testfile-link state=link’

修改文件属性

[root@ansible1 ~]#ansible appsrvs -a 'ls -l /data'
192.168.32.27 | CHANGED | rc=0 >>
total 4
-rw-r--r-- 1 root root 52 Dec  4 22:25 shells.sh[root@ansible1 ~]#
[root@ansible1 ~]#ansible appsrvs -m file -a 'path=/data/shells.sh mode=777'
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "gid": 0, "group": "root", "mode": "0777", "owner": "root", "path": "/data/shells.sh", "size": 52, "state": "file", "uid": 0
}
[root@ansible1 ~]#ansible appsrvs -a 'ls -l /data'
192.168.32.27 | CHANGED | rc=0 >>
total 4
-rwxrwxrwx 1 root root 52 Dec  4 22:25 shells.sh

创建软连接

[root@ansible1 ~]#ansible appsrvs -m file -a 'src=/data/shells.sh dest=/data/filelink state=link'
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "dest": "/data/filelink", "gid": 0, "group": "root", "mode": "0777", "owner": "root", "size": 15, "src": "/data/shells.sh", "state": "link", "uid": 0
}
[root@ansible1 ~]#ansible appsrvs -a 'ls -l /data'
192.168.32.27 | CHANGED | rc=0 >>
total 4
lrwxrwxrwx 1 root root 15 Dec  5 09:29 filelink -> /data/shells.sh
-rwxrwxrwx 1 root root 52 Dec  4 22:25 shells.sh[root@ansible1 ~]#

archive 模块

archive 可以完成打包压缩功能

[root@ansible1 ~]#ansible all -m archive -a "path=/etc/issue dest=/data/issue.tar.gz format=gz mode=0777"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "archived": ["/etc/issue"], "arcroot": "/etc/", "changed": true, "dest": "/data/issue.tar.gz", "expanded_exclude_paths": [], "expanded_paths": ["/etc/issue"], "gid": 0, "group": "root", "missing": [], "mode": "0777", "owner": "root", "size": 53, "state": "file", "uid": 0
}
192.168.32.17 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "archived": ["/etc/issue"], "arcroot": "/etc/", "changed": true, "dest": "/data/issue.tar.gz", "expanded_exclude_paths": [], "expanded_paths": ["/etc/issue"], "gid": 0, "group": "root", "missing": [], "mode": "0777", "owner": "root", "size": 53, "state": "file", "uid": 0
}
[root@ansible1 ~]#ansible all -a 'ls -l /data'
192.168.32.27 | CHANGED | rc=0 >>
total 8
lrwxrwxrwx 1 root root 15 Dec  5 09:29 filelink -> /data/shells.sh
-rwxrwxrwx 1 root root 53 Dec  5 09:32 issue.tar.gz
-rwxrwxrwx 1 root root 52 Dec  4 22:25 shells.sh192.168.32.17 | CHANGED | rc=0 >>
total 4
-rwxrwxrwx 1 root root 53 Dec  5 09:32 issue.tar.gz[root@ansible1 ~]#

unarchive 模块

解压文件模块

当选项中的 copy 为 yes 时 将解压 ansible 服务器端的压缩包到远程服务器上
本地解压然后发送到被管理主机逻辑
实现有两种用法: 1、将ansible主机上的压缩包传到远程主机后解压缩至特定目录,设置copy=yes 2、
将远程主机上的某个压缩包解压缩到指定路径下,设置copy=no
常见参数:
copy:默认为yes,当copy=yes,拷贝的文件是从ansible主机复制到远程主机上,如果设置为
copy=no,会在远程主机上寻找src源文件
remote_src:和copy功能一样且互斥,yes表示在远程主机,不在ansible主机,no表示文件在
ansible主机上
src:源路径,可以是ansible主机上的路径,也可以是远程主机上的路径,如果是远程主机上的路
径,则需要设置copy=no
dest:远程主机上的目标路径
mode:设置解压缩后的文件权限
范例:

ansible srv -m unarchive -a 'src=/data/foo.tgz dest=/var/lib/foo'
ansible srv -m unarchive -a 'src=/tmp/foo.zip dest=/data copy=no mode=0777'
ansible srv -m unarchive -a 'src=https://example.com/example.zip dest=/data
copy=no'

解压的时候需要保证被控机器里面有解压的工具命令。

[root@ansible1 ~]#ll
total 4
-rw-------. 1 root root 1572 Dec  3 13:25 anaconda-ks.cfg
[root@ansible1 ~]#tar zcvf file.tar.gz anaconda-ks.cfg
anaconda-ks.cfg
[root@ansible1 ~]#ll
total 8
-rw-------. 1 root root 1572 Dec  3 13:25 anaconda-ks.cfg
-rw-r--r--  1 root root  977 Dec  5 09:35 file.tar.gz[root@ansible1 ~]#tar zcvf code.tar.gz index.html
index.html
[root@ansible1 ~]#ll
total 16
-rw-------. 1 root root 1572 Dec  3 13:25 anaconda-ks.cfg
-rw-r--r--  1 root root  139 Dec  5 09:38 code.tar.gz
-rw-r--r--  1 root root  977 Dec  5 09:35 file.tar.gz
-rw-r--r--  1 root root   18 Dec  5 09:37 index.html[root@ansible1 ~]#ansible appsrvs -m unarchive -a 'src=/root/code.tar.gz dest=/data copy=yes'
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "dest": "/data", "extract_results": {"cmd": ["/usr/bin/gtar", "--extract", "-C", "/data", "-z", "-f", "/root/.ansible/tmp/ansible-tmp-1575510019.76-203687970200065/source"], "err": "", "out": "", "rc": 0}, "gid": 0, "group": "root", "handler": "TgzArchive", "mode": "0755", "owner": "root", "size": 77, "src": "/root/.ansible/tmp/ansible-tmp-1575510019.76-203687970200065/source", "state": "directory", "uid": 0
}
[root@ansible1 ~]#ansible appsrvs -m unarchive -a 'src=/root/file.tar.gz dest=/data copy=yes'
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "dest": "/data", "extract_results": {"cmd": ["/usr/bin/gtar", "--extract", "-C", "/data", "-z", "-f", "/root/.ansible/tmp/ansible-tmp-1575510030.18-177824913187722/source"], "err": "", "out": "", "rc": 0}, "gid": 0, "group": "root", "handler": "TgzArchive", "mode": "0755", "owner": "root", "size": 100, "src": "/root/.ansible/tmp/ansible-tmp-1575510030.18-177824913187722/source", "state": "directory", "uid": 0
}
[root@ansible1 ~]#ansible appsrvs -a 'ls -l /data'
192.168.32.27 | CHANGED | rc=0 >>
total 16
-rw------- 1 root root 1572 Dec  3 13:25 anaconda-ks.cfg
lrwxrwxrwx 1 root root   15 Dec  5 09:29 filelink -> /data/shells.sh
-rw-r--r-- 1 root root   18 Dec  5 09:37 index.html
-rwxrwxrwx 1 root root   53 Dec  5 09:32 issue.tar.gz
-rwxrwxrwx 1 root root   52 Dec  4 22:25 shells.sh[root@ansible1 ~]#

当 copy 为 no 时,将解压远程主机上的压缩包到远程主机指定路径下
让远程主机中的压缩包解压到远程被管理主机中

hostname 模块

这样在一个管理组中的机器都会改为一样的名字,这个和生产中不一致,后面会学到如何改为不同主机名的方法

[root@ansible1 ~]#ansible-doc -s hostname
- name: Manage hostnamehostname:name:                  # (required) Name of the hostuse:                   # Which strategy to use to update the hostname. If not set we try to autodetect, butthis can be problematic, specially with containersas they can present misleading information.[root@ansible1 ~]#ansible 192.168.32.17 -m hostname -a "name=node-1"
192.168.32.17 | CHANGED => {"ansible_facts": {"ansible_domain": "", "ansible_fqdn": "node-1", "ansible_hostname": "node-1", "ansible_nodename": "node-1", "discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "name": "node-1"
}
[root@ansible1 ~]#ansible 192.168.32.17 -a 'hostname'
192.168.32.17 | CHANGED | rc=0 >>
node-1[root@ansible1 ~]#

cron 模块

用于设置远程主机计划任务功能,支持时间:minute,hour,day,month,weekday
范例:

#备份数据库脚本
[root@centos8 ~]#cat mysql_backup.sh
mysqldump -A -F --single-transaction --master-data=2 -q -uroot |gzip >
/data/mysql_`date +%F_%T`.sql.gz
#创建任务
ansible 192.168.39.28 -m cron -a 'hour=2 minute=30 weekday=1-5 name="backup
mysql" job=/root/mysql_backup.sh'
ansible srv -m cron -a "minute=*/5 job='/usr/sbin/ntpdate 172.20.0.1
&>/dev/null' name=Synctime"
#禁用计划任务
ansible srv -m cron -a "minute=*/5 job='/usr/sbin/ntpdate 172.20.0.1
&>/dev/null' name=Synctime disabled=yes"
#启用计划任务
ansible srv -m cron -a "minute=*/5 job='/usr/sbin/ntpdate 172.20.0.1
&>/dev/null' name=Synctime disabled=no"
#删除任务
ansible srv -m cron -a "name='backup mysql' state=absent"
ansible srv -m cron -a ‘state=absent name=Synctime’
[root@ansible1 ~]#ansible-doc -s cron
- name: Manage cron.d and crontab entriescron:backup:                # If set, create a backup of the crontab before it is modified. The location of thebackup is returned in the `backup_file' variable bythis module.cron_file:             # If specified, uses this file instead of an individual user's crontab. If this is arelative path, it is interpreted with respect to`/etc/cron.d'. If it is absolute, it will typicallybe `/etc/crontab'. Many linux distros expect (andsome require) the filename portion to consist solelyof upper- and lower-case letters, digits,underscores, and hyphens. To use the `cron_file'parameter you must specify the `user' as well.day:                   # Day of the month the job should run ( 1-31, *, */2, etc )disabled:              # If the job should be disabled (commented out) in the crontab. Only has effect if`state=present'.env:                   # If set, manages a crontab's environment variable. New variables are added on top crontab. `name' and `value' parameters are the nameand the value of environment variable.hour:                  # Hour when the job should run ( 0-23, *, */2, etc )insertafter:           # Used with `state=present' and `env'. If specified, the environment variable will inserted after the declaration of specifiedenvironment variable.insertbefore:          # Used with `state=present' and `env'. If specified, the environment variable will inserted before the declaration of specifiedenvironment variable.job:                   # The command to execute or, if env is set, the value of environment variable. Thecommand should not contain line breaks. Required if`state=present'.minute:                # Minute when the job should run ( 0-59, *, */2, etc )month:                 # Month of the year the job should run ( 1-12, *, */2, etc )name:                  # Description of a crontab entry or, if env is set the name of environmenvariable.Required if `state=absent'. Note that if name is notset and `state=present', then a new crontab entrywill always be created, regardless of existing ones.This parameter will always be required in futurereleases.reboot:                # If the job should be run at reboot. This option is deprecated. Users should usespecial_time.special_time:          # Special time specification nickname.state:                 # Whether to ensure the job or environment variable is present or absent.user:                  # The specific user whose crontab should be modified. When unset, this parameterdefaults to using `root'.weekday:               # Day of the week that the job should run ( 0-6 for Sunday-Saturday, *, etc )[root@ansible1 ~]#ansible all -m cron -a "minute=*/2 job='/etc/fstab >> /data/fstab.bak' name=cron_test"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "envs": [], "jobs": ["cron_test"]
}
192.168.32.17 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "envs": [], "jobs": ["cron_test"]
}
[root@ansible1 ~]#ansible all -a "crontab -l"
192.168.32.27 | CHANGED | rc=0 >>
#Ansible: cron_test
*/2 * * * * /etc/fstab >> /data/fstab.bak192.168.32.17 | CHANGED | rc=0 >>
#Ansible: cron_test
*/2 * * * * /etc/fstab >> /data/fstab.bak[root@ansible1 ~]#ansible all -a "ls -l /data"
192.168.32.17 | CHANGED | rc=0 >>
total 8
-rw-r--r-- 1 root root  0 Dec  5 10:10 fstab.bak
-rwxrwxrwx 1 root root 23 Dec  5 09:32 issue.tar
-rwxrwxrwx 1 root root 53 Dec  5 09:58 issue.tar.gz192.168.32.27 | CHANGED | rc=0 >>
total 16
-rw------- 1 root root 1572 Dec  3 13:25 anaconda-ks.cfg
lrwxrwxrwx 1 root root   15 Dec  5 09:29 filelink -> /data/shells.sh
-rw-r--r-- 1 root root    0 Dec  5 10:10 fstab.bak
-rw-r--r-- 1 root root   18 Dec  5 09:37 index.html
-rwxrwxrwx 1 root root   53 Dec  5 09:58 issue.tar.gz
-rwxrwxrwx 1 root root   52 Dec  4 22:25 shells.sh[root@ansible1 ~]#

删除一个远程主机的计划任务

[root@ansible1 ~]#ansible all -m cron -a "name=cron_test state=absent"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "envs": [], "jobs": []
}
192.168.32.17 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "envs": [], "jobs": []
}
[root@ansible1 ~]#ansible all -a "crontab -l"
192.168.32.17 | CHANGED | rc=0 >>192.168.32.27 | CHANGED | rc=0 >>[root@ansible1 ~]#

yum 模块

功能:管理软件包
范例:

ansible srv -m yum -a 'name=httpd state=present' #安装
ansible srv -m yum -a 'name=httpd state=absent' #删除
[root@ansible1 ~]#ansible all -m yum -a "name=httpd state=present"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "changes": {"installed": ["httpd"]}, "msg": "", "rc": 0, "results": ["Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\nResolving Dependencies\n--省略......192.168.32.17 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "changes": {"installed": ["httpd"]}, "msg": "Repository epel is listed more than once in the configuration\n", "rc": 0, "results": ["Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\nResolving Dependencies\n-->         省略......    [root@ansible1 ~]#

卸载刚安装的 httpd:

[root@ansible1 ~]#ansible all -m yum -a "name=httpd state=absent"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "changes": {"removed": ["httpd"]}, "msg": "", "rc": 0, "results": ["Loaded plugins: fastestmirror\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 省略......    ]
}
192.168.32.17 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "changes": {"removed": ["httpd"]}, "msg": "Repository epel is listed more than once in the configuration\n", "rc": 0, "results": [省略......      ]
}
[root@ansible1 ~]#

service 模块

管理服务的启动和关闭

[root@ansible1 ~]#ansible-doc -s service
- name: Manage servicesservice:arguments:             # Additional arguments provided on the command line.enabled:               # Whether the service should start on boot. *At least one of state and enabled arerequired.*name:                  # (required) Name of the service.pattern:               # If the service does not respond to the status command, name a substring to look foras would be found in the output of the `ps' commandas a stand-in for a status result. If the string isfound, the service will be assumed to be started.runlevel:              # For OpenRC init scripts (e.g. Gentoo) only. The runlevel that this service belongsto.sleep:                 # If the service is being `restarted' then sleep this many seconds between the stopand start command. This helps to work around badly-behaving init scripts that exit immediately aftersignaling a process to stop. Not all servicemanagers support sleep, i.e when using systemd thissetting will be ignored.state:                 # `started'/`stopped' are idempotent actions that will not run commands unlessnecessary. `restarted' will always bounce theservice. `reloaded' will always reload. *At leastone of state and enabled are required.* Note thatreloaded will start the service if it is not alreadystarted, even if your chosen init system wouldn'tnormally.use:                   # The service module actually uses system specific modules, normally through autodetection, this setting can force a specific module.Normally it uses the value of the'ansible_service_mgr fact and falls back to the old'service' module when none matching is found.
[root@ansible1 ~]#
先安装一个 httpd:
[root@ansible1 ~]#ansible 192.168.32.27 -m yum -a "name=httpd state=present"
安装提示代码省略......查看端口:
[root@ansible1 ~]#ansible 192.168.32.27 -a 'ss -ntl'
192.168.32.27 | CHANGED | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
LISTEN     0      100    127.0.0.1:25                       *:*
LISTEN     0      128          *:22                       *:*
LISTEN     0      100      [::1]:25                    [::]:*
LISTEN     0      128       [::]:22                    [::]:*     启动服务:
[root@ansible1 ~]#ansible 192.168.32.27 -m service -a "name=httpd enabled=yes state=started"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "enabled": true, "name": "httpd", "state": "started", "status": {"ActiveEnterTimestampMonotonic": "0", "ActiveExitTimestampMonotonic": "0",
省略......查看端口:
[root@ansible1 ~]#ansible 192.168.32.27 -a 'ss -ntl'
192.168.32.27 | CHANGED | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
LISTEN     0      100    127.0.0.1:25                       *:*
LISTEN     0      128          *:22                       *:*
LISTEN     0      100      [::1]:25                    [::]:*
LISTEN     0      128       [::]:80                    [::]:*
LISTEN     0      128       [::]:22                    [::]:*    停止服务:
[root@ansible1 ~]#ansible 192.168.32.27 -m service -a "name=httpd  state=stopped"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "name": "httpd", "state": "stopped", "status": {"ActiveEnterTimestamp": "Thu 2019-12-05 10:44:31 CST", "ActiveEnterTimestampMonotonic": "13285420044", "ActiveExitTimestampMonotonic": "0",
省略......
查看端口:
[root@ansible1 ~]#ansible 192.168.32.27 -a 'ss -ntl'
192.168.32.27 | CHANGED | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
LISTEN     0      100    127.0.0.1:25                       *:*
LISTEN     0      128          *:22                       *:*
LISTEN     0      100      [::1]:25                    [::]:*
LISTEN     0      128       [::]:22                    [::]:*  重新启动服务:
[root@ansible1 ~]#ansible 192.168.32.27 -m service -a "name=httpd  state=restarted"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "name": "httpd", "state": "started", "status": {"ActiveEnterTimestamp": "Thu 2019-12-05 10:44:31 CST", "ActiveEnterTimestampMonotonic": "13285420044", "ActiveExitTimestamp": "Thu 2019-12-05 10:45:41 CST",
省略......
查看端口:
[root@ansible1 ~]#ansible 192.168.32.27 -a 'ss -ntl'
192.168.32.27 | CHANGED | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
LISTEN     0      100    127.0.0.1:25                       *:*
LISTEN     0      128          *:22                       *:*
LISTEN     0      100      [::1]:25                    [::]:*
LISTEN     0      128       [::]:80                    [::]:*
LISTEN     0      128       [::]:22                    [::]:*                  

user 模块

用于管理远程主机的操作系统用户功能
范例:

#创建用户
ansible srv -m user -a 'name=user1 comment=“test user” uid=2048 home=/app/user1
group=root‘
ansible srv -m user -a 'name=nginx comment=nginx uid=88 group=nginx
groups="root,daemon" shell=/sbin/nologin system=yes create_home=no
home=/data/nginx non_unique=yes'
#删除用户及家目录等数据
ansible srv -m user -a 'name=nginx state=absent remove=yes'

创建用户:

[root@ansible1 ~]#
[root@ansible1 ~]#ansible appsrvs -m user -a "name=duanxin system=yes"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "comment": "", "create_home": true, "group": 995, "home": "/home/duanxin", "name": "duanxin", "shell": "/bin/bash", "state": "present", "system": true, "uid": 997
}
[root@ansible1 ~]#
[root@ansible1 ~]#ansible appsrvs -a "id duanxin"
192.168.32.27 | CHANGED | rc=0 >>
uid=997(duanxin) gid=995(duanxin) groups=995(duanxin)

删除用户:

[root@ansible1 ~]#ansible appsrvs -m user -a "name=duanxin remove=yes state=absent"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "force": false, "name": "duanxin", "remove": true, "state": "absent", "stderr": "userdel: duanxin mail spool (/var/spool/mail/duanxin) not found\n", "stderr_lines": ["userdel: duanxin mail spool (/var/spool/mail/duanxin) not found"]
}
[root@ansible1 ~]#ansible appsrvs -a "id duanxin"
192.168.32.27 | FAILED | rc=1 >>
id: duanxin: no such usernon-zero return code[root@ansible1 ~]#

group 模块

管理远程主机操作系统用户组功能

[root@ansible1 ~]#ansible-doc -s group
- name: Add or remove groupsgroup:gid:                   # Optional `GID' to set for the group.local:                 # Forces the use of "local" command alternatives on platforms that implement it. Thisis useful in environments that use centralizedauthentication when you want to manipulate the localgroups. (e.g. it uses `lgroupadd' instead of`groupadd'). This requires that these commands existon the targeted host, otherwise it will be a fatalerror.name:                  # (required) Name of the group to manage.non_unique:            # This option allows to change the group ID to a non-unique value. Requires `gid'.Not supported on macOS or BusyBox distributions.state:                 # Whether the group should be present or not on the remote host.system:                # If `yes', indicates that the group created is a system group.
[root@ansible1 ~]#
查看组信息:
[root@ansible1 ~]#ansible appsrvs -a "tail -10 /etc/group"
192.168.32.27 | CHANGED | rc=0 >>
systemd-network:x:192:
dbus:x:81:
polkitd:x:998:
ssh_keys:x:997:
sshd:x:74:
postdrop:x:90:
postfix:x:89:
kaivi:x:1000:kaivi
chrony:x:996:
apache:x:48:
创建likai组:
[root@ansible1 ~]#ansible appsrvs -m group -a "name=likai system=yes state=present"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "gid": 995, "name": "likai", "state": "present", "system": true
}
[root@ansible1 ~]#ansible appsrvs -a "tail -10 /etc/group"
192.168.32.27 | CHANGED | rc=0 >>
dbus:x:81:
polkitd:x:998:
ssh_keys:x:997:
sshd:x:74:
postdrop:x:90:
postfix:x:89:
kaivi:x:1000:kaivi
chrony:x:996:
apache:x:48:
likai:x:995:

删除likai组:

[root@ansible1 ~]#ansible appsrvs -m group -a "name=likai state=absent"
192.168.32.27 | CHANGED => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "name": "likai", "state": "absent"
}
[root@ansible1 ~]#ansible appsrvs -a "tail -10 /etc/group"
192.168.32.27 | CHANGED | rc=0 >>
systemd-network:x:192:
dbus:x:81:
polkitd:x:998:
ssh_keys:x:997:
sshd:x:74:
postdrop:x:90:
postfix:x:89:
kaivi:x:1000:kaivi
chrony:x:996:
apache:x:48:[root@ansible1 ~]#
  1. 其他模块
    ansible 中模块众多, 使用模块前想想好自己的业务需要场景以及原生命令的使用细节,然后查询 ansible 是否有对应的功能模块,根据 ansible-doc 自己的需求找到对应的实现选项进进测试验证。不要刻意记忆具体选项,有使用经验和文档参考即可。

ansible 自动化运维工具——ansible Ad-Hoc 使用相关推荐

  1. ansible自动化运维工具

    ansible自动化运维工具 ansible自动化运维脚本工具 ansible自动化运维工具 一.ssh工作原理和基本命令 1.ssh原理 2.ssh的基本命令 二.anible 自动化运维工具 1. ...

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

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

  3. Ansible自动化运维工具介绍

    介绍 Ansible自动化运维工具,是基于Python开发的,可以实现批量系统配置,批量程序部署.批量运行命令等等. ansible是基于模块工作的,本身没有批量部署的能力.真正具有批量部署的是ans ...

  4. Ansible自动化运维工具使用

    概述 本文描述自动化运维工具 Ansible 的安装及基础使用方法,包含: Centos 下的安装 主机配置 Ad-Hoc command(命令行执行) Playbook (任务剧本) Ansible ...

  5. 自动化运维工具ansible(安装与模块介绍)

    自动化运维工具ansible(安装与模块介绍) 一.ansible运维工具概述 (一).ansible的特点 (二).ansible的原理 (三)ansible的优点 二.安装ansible 三.an ...

  6. 自动化运维工具Ansible详细部署 - 人生理想在于坚持不懈 - 51CTO技术博客

    自动化运维工具Ansible详细部署 - 人生理想在于坚持不懈 - 51CTO技术博客 自动化运维工具Ansible详细部署 - 人生理想在于坚持不懈 - 51CTO技术博客 自动化运维工具Ansib ...

  7. 【Ansible自动化运维工具】Ansible变量之lookup生成变量方法

    [Ansible自动化运维工具]Ansible变量之lookup生成变量方法 一.lookup插件介绍 1.lookup简介 2.lookup使用场景 3.lookup获取的数据源 4.lookup的 ...

  8. 自动化运维工具Ansible实战---常用模块

    Ansible默认提供了很多模块来供我们使用.在Linux中,我们可以通过 ansible-doc -l 命令查看到当前Ansible支持哪些模块,通过 ansible-doc -s [模块名] 又可 ...

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

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

最新文章

  1. linux 如何运行.sql文件
  2. 统计学习方法-最大熵模型
  3. 里程碑:BCH网络出现首例Schnorr Multisig交易
  4. 配置 Keepalived + LVS-DR模式, 实现高可用和负载均衡
  5. 项目管理自动化实践之路
  6. 无法显示 xml 页 解决方案
  7. Android引入library失败的可能原因
  8. SAP CRM和C4C的订单Number range
  9. php程序layer,php 提交表单 关闭layer弹窗iframe的实例讲解
  10. 业务流程、长周期服务和微服务
  11. Spring Cloud Alibaba基础教程:使用Nacos作为配置中心
  12. C++第一个综合项目
  13. QQ 临时会话+图标 HTML代码
  14. 封装0603和0805的区别
  15. 网站搜索引擎优化外链工具
  16. javascript常用编辑器推荐
  17. JAVAWEB开发Myeclipse 项目中报“无法解析类型 java.io.ObjectInputStream,从必需的 .class 文件间接引用了它”解决办法
  18. 忠和资本:2021年后市刘国忠行情预测
  19. HTTPS中的数字证书是什么?数字签名又是什么?
  20. java cxf webservice应用

热门文章

  1. 学习 Go 语言 1 — 基础语法
  2. 苹果键盘怎么手写_支持9种外语语音识别,新增4款外语键盘,搜狗输入法10.8版本上线丨18周新闻...
  3. UE4添加人物摄像机
  4. Java数据结构与算法———(8)单链表应用实例,删除节点,根据输入的整数
  5. Windows XP 超级140个技巧
  6. php 框架效率测试,关于DoitPHP,ThinkPHP,Yii,CI,DooPHP等框架的性能对比测试
  7. 手机上的android版本下载视频播放器,ZZPlayer手机版下载
  8. 阿里巴巴云游戏(元境)春季2023届校园招聘正式开启
  9. Office2007-2010 OpenXML
  10. 轴承_常用硬件的种类以及选用_day16