![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F141ff6be-44c5-4b0b-b103-1ee456e81a11%2FUntitled.png?table=block&id=e7537931-11a4-41be-abdc-97ec3d7c6988&cache=v2)
ansible安装:
apt安装:
apt install ansible #安装ansible ansible --version #ansible
ansible简单使用:
ansible 主机列表 参数 主机列表默认值得是 /etc/ansible/hosts 可默认指定为仅可以用localhost但是不隐式指定 ansible localhost -m command -a "参数" #-m指定模块,默认为command ex: ansible localhost -m command -a "ls" #在本机执行ls命令,可以用-v,-vv,-vvv来显示更加详细的命令执行信息 ansible localhost -m ping #执行ping命令操作 ansible-doc -l #列出所有模块,按Q退出 ansible-doc -h #ansible-doc帮助命令 ansible-doc -s [模块] #显示某个模块的简单参数信息 ex: root@dokcer:~# ansible localhost -m command -a "chdir=/tmp pwd" #使用command的chdir参数改变执行命令的目录 [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' localhost | SUCCESS | rc=0 >> /tmp #显示已经更改了执行命令的目录 ansible-config -h #显示配置的帮助信息 ansible-config list #以yaml的格式来显示配置信息 ansible-config view #以json的格式来显示配置信息 ansible-config dump #显示所有配置的变量信息
主机清单:
主机清单文件:
文件位置:/etc/ansible/hosts 主机属性:(用于过滤主机) all 所有主机 ungrouped 所有没有组的主机 localhost 表示本机 主机格式: 散列主机列表 主机名/IP地址:[ssh端口] 主机组列表 [主机组名] 主机列表1 [主机组:children] 子组1 子组2 ... 主机范围 主机域名[001:006] IP地址[01:60]
如果连接过主机但是失败了,会在~/.ssh/known_hosts中有记录,删除之后重新连接就可以了。
报错:
to use the 'ssh' connection type with passwords, you must install the sshpass program sudo apt-get install sshpass #安装sshpass模块 PermitRootLogin yes #ansible默认执行命令的用户为root,设置为root可以登录
ansible连接主机的属性
192.168.0.50 ansible_connection=local #设置主机连接方式为本地连接 ansible 192.168.0.50 -a "ls" #不用输入密码就可以连接,相当于localhost方式 192.168.0.50 ansible_ssh_pass=ssh登录密码 ansible 192.168.0.50 -a "ls" #不用输入ssh密码就可以输入命令了 ansible免密钥认证: 1.生成密钥对 root@dokcer:~# ssh-keygen -t rsa 2.发送控制端的公钥到目标主机 ssh-copy-id -i /root/.ssh/id_rsa.pub lion@192.168.0.50 #输入lion用户的密码就传输完成了 error: No such file or directory #没有.ssh文件夹 lion@dokcer:~$ ssh localhost #连接本地生成ssh文件夹 3.实现免密码登录
ansible配置文件:
配置文件目录:
root@dokcer:/root/.ssh# tree /etc/ansible/ /etc/ansible/ ├── ansible.cfg #核心配置文件 └── hosts #主机列表文件 └── roles #角色管理文件 核心配置文件为ansible.cfg 通过修改 1.环境变量$ANSIBLE_CONFIG #通过环境变量来定义配置文件的位置 2.工作目录 #仅限当前目录 3.用户家目录 #仅限当前系统用户 4.软件目录 #全局生效 配置格式: [配置段] 配置项:属性值 root@dokcer:~# egrep -v '^$|^#' /etc/ansible/ansible.cfg #过滤掉空格和#号显示 [defaults] [inventory] [privilege_escalation] [paramiko_connection] [ssh_connection] [persistent_connection] [accelerate] [selinux] [colors] [diff] [defaults] #默认配置 # some basic default values... #inventory = /etc/ansible/hosts #主机配置列表 #library = /usr/share/my_modules/ #模块位置 #module_utils = /usr/share/my_module_utils/ #remote_tmp = ~/.ansible/tmp #远程主机临时执行命令目录 #local_tmp = ~/.ansible/tmp #本地主机临时执行命令目录 #plugin_filters_cfg = /etc/ansible/plugin_filters.yml #forks = 5 #poll_interval = 15 #sudo_user = root #ask_sudo_pass = True #ask_pass = True #transport = smart #remote_port = 22 #ssh端口 #remote_user = root #远程登录用户 #module_lang = C #module_set_locale = False
ansilbe利用指定用户提权执行命令
ansible localhost -u lion -a "ls /root" -b -K #输入sudo密钥后执行超级管理员命令 设置某一个用户拥有root权限 root@dokcer:~# cat /etc/sudoers # # This file MUST be edited with the 'visudo' command as root. # # Please consider adding local content in /etc/sudoers.d/ instead of # directly modifying this file. # # See the man page for details on how to write a sudoers file. # Defaults env_reset Defaults mail_badpass Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" # Host alias specification # User alias specification # Cmnd alias specification # User privilege specification root ALL=(ALL:ALL) ALL # Members of the admin group may gain root privileges %admin ALL=(ALL) ALL #admin组用用户拥有root权限 # Allow members of group sudo to execute any command %sudo ALL=(ALL:ALL) ALL #sudo组用户拥有sudo来执行任何命令 # See sudoers(5) for more information on "#include" directives: #includedir /etc/sudoers.d root@dokcer:~# grep sudo /etc/gshadow #在gshandown文件中查看sudo组的成员 sudo:*::lion root@dokcer:~# usermod -G sudo lion #增加某一用户到sudo组中 %sudo ALL=(ALL:ALL) ALL / %sudo ALL=(ALL:ALL) NOPASSWD:ALL #默认切换sudo执行命令时不需要输入密码 root@dokcer:~# ansible localhost -u lion -a "ls /root" -b #不用-K输入密码就可以执行超级命令 localhost | SUCCESS | rc=0 >> snap
ansible主机匹配:
匹配所有: all 正则匹配 *(通配符) 逻辑或 :(并集),一般由于主机组 逻辑或 :&(交集),一般由于主机组 逻辑非 :!(补集),一般由于主机组 root@dokcer:/root/.ssh# ansible '*' -m ping -k #匹配所有主机进行ping,用单引号括住主机列表部分 SSH password: 192.168.0.50 | SUCCESS => { "changed": false, "ping": "pong" }
ansible命令模块:
命令模块 command:默认模块,可以远程权限运行所有的shell命令,不支持特殊符号,但是支持系统变量,不支持命令别名 shell:用于执行某些带特殊符号的命令<>!#$ scripts:可以执行脚本文件,也可以从控制主机的脚本运行到远程主机 command模块: root@dokcer:~# ansible localhost -a "chdir=/tmp ls" #切换执行命令的目录 root@dokcer:~# ansible localhost -a 'chdir=/tmp creates=1.txt ls' #如果creates文件存在,则跳过命令执行,反之亦然 localhost | SUCCESS | rc=0 >> skipped, since 1.txt exists root@dokcer:~# ansible localhost -a 'chdir=/tmp removes=1.txt ls' #如果removes的文件存在则执行命令,反之亦然 localhost | SUCCESS | rc=0 >> 1.txt ansible_VtVGmv snap.docker systemd-private-66d00680e20c4dba801130d9387e8e6f-systemd-resolved.service-KVfCH2 systemd-private-66d00680e20c4dba801130d9387e8e6f-systemd-timesyncd.service-xhgHcZ root@dokcer:~# ansible localhost -a 'echo $SHELL' #command值支持系统变量不支持自定义变量 localhost | SUCCESS | rc=0 >> /bin/bash root@dokcer:~# ansible localhost -a 'la=lala; echo $la' localhost | FAILED | rc=2 >> [Errno 2] No such file or directory root@dokcer:~# ansible localhost -a 'env | grep 1' #command不支持特殊管道命令 localhost | FAILED | rc=127 >> env: ‘|’: No such file or directorynon-zero return code shell模块: root@dokcer:~# ansible localhost -m shell -a "la=lala; echo $la" #双引号有特殊符号时不生效 localhost | SUCCESS | rc=0 >> root@dokcer:~# ansible localhost -m shell -a 'la=lala; echo $la' #单引号命令成功 localhost | SUCCESS | rc=0 >> lala root@dokcer:~# ansible localhost -m shell -a 'env | grep $SHELL' #shell执行管道符命令 localhost | SUCCESS | rc=0 >> SUDO_COMMAND=/bin/bash SHELL=/bin/bash root@dokcer:~# ansible localhost -m shell -a '/bin/bash /tmp/1.sh admin' #shell执行远程脚本 localhost | SUCCESS | rc=0 >> user is admin script模块: root@dokcer:~# ansible 192.168.0.50 -m script -a '/bin/bash /tmp/1.sh amdin' -kSSH password: 192.168.0.50 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.0.50 closed.\r\n", "stdout": "user is amdin\r\n", "stdout_lines": [ "user is amdin" #可以执行存在于控制端,但是不存在被控端的脚本文件 ] } #script使用executable参数来指定不存在于被控端的脚本文件 root@dokcer:~# ansible 192.168.0.50 -m script -a 'executable=/bin/bash /tmp/1.sh amdin' -k SSH password: 192.168.0.50 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.0.50 closed.\r\n", "stdout": "user is amdin\r\n", "stdout_lines": [ "user is amdin" ] }
ansible系统模块:
hostname模块 修改主机名,立刻生效,并且永久生效,hostname只会修改/etc/hostname,而不会修改/etc/hosts root@dokcer:~# ansible localhost -m hostname -a 'name=lion' #修改hostname localhost | SUCCESS => { "ansible_facts": { "ansible_domain": "", "ansible_fqdn": "lion", "ansible_hostname": "lion", "ansible_nodename": "lion" }, "changed": true, "name": "lion" } root@dokcer:~# ansible localhost -a 'hostname' #修改成功 localhost | SUCCESS | rc=0 >> docker user 创建一个用户,name lion2 系统输入,用户组是root, uid 10010 注释是lion2 禁止登录 state=presen状态为存在 root@dokcer:~# ansible localhost -m user -a 'name=lion2 system=yes groups=root uid=10010 comment=lion2 shell=/sbin/nologin state=present' localhost | SUCCESS => { "changed": true, "comment": "lion2", "create_home": true, "group": 999, "groups": "root", "home": "/home/lion2", "name": "lion2", "shell": "/sbin/nologin", "state": "present", "system": true, "uid": 10010 } root@dokcer:~# ansible localhost -a "id lion2" #创建用户成功,要注意uid localhost | SUCCESS | rc=0 >> uid=10010(lion2) gid=999(lion2) groups=999(lion2),0(root) root@dokcer:~# ansible localhost -a "getent passwd lion2" #查看lion2用户的密码 localhost | SUCCESS | rc=0 >> lion2:x:10010:999:lion2:/home/lion2:/sbin/nologin #创建带ssh私钥的用户 root@dokcer:~# ansible localhost -m user -a 'name=lion2 system=yes generate_ssh_key=yes ssh_key_bits=2048 ssh_key_file=.ssh/id_ras' localhost | SUCCESS => { "append": false, "changed": true, "comment": "lion2", "group": 999, "home": "/home/lion2", "move_home": false, "name": "lion2", "shell": "/sbin/nologin", "ssh_fingerprint": "2048 SHA256:9hjxzWM3jQFqBIqhEceXCdWMRv94CYthiSTB3PoPnCg ansible-generated on docker (RSA)", "ssh_key_file": "/home/lion2/.ssh/id_ras", "ssh_public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD0LwW+HVOsSCjDMX9U64ugIU+P6O+NWmly4Dy+XHXPShYumFUxAFe7WIq000YBOQgFbrTreOzoGkH5712uZIiUll3Gmbq+zNgY2231KrkpxsVAMjcjq8VidsiOihUaO3CgZAwsIUucuYZo19UTZTThl7h3i28xtaGXbJaWNbC9Xhx2Jrs+QU904JnVWxw8jmDJkePSDiasYsagpICHOL6LpKNUB4QUSnhwwk02wxyIWhAxVEFf59Ub6FCvp77a3K/vBeFoLDlMkySqYoCbieT943ibmBhiNWDkF//YNYlnIa9Fjb5tx3bovic3Br8vgh47PkaE937OY8+6gU3hPTjj ansible-generated on docker", "state": "present", "uid": 10010 } #删除用户state=absent remove=ye 用户禁用和删除 root@dokcer:~# ansible localhost -m user -a 'name=lion2 state=absent remove=yes' localhost | SUCCESS => { "changed": true, "force": false, "name": "lion2", "remove": true, "state": "absent", "stderr": "userdel: lion2 mail spool (/var/mail/lion2) not found\n", "stderr_lines": [ "userdel: lion2 mail spool (/var/mail/lion2) not found" ] } group模块: #创建和删除用户组,gid要唯一 #创建用户组 root@dokcer:~# ansible localhost -m group -a 'name=admin system=yes gid=10010' localhost | SUCCESS => { "changed": true, "gid": 10010, "name": "admin", "state": "present", "system": true } root@dokcer:~# ansible localhost -a 'getent group admin' #获取用户组 localhost | SUCCESS | rc=0 >> admin:x:10010: #删除用户组 root@dokcer:~# ansible localhost -m group -a 'name=admin state=absent' localhost | SUCCESS => { "changed": true, "name": "admin", "state": "absent" } root@dokcer:~# ansible localhost -a 'getent group admin' localhost | FAILED | rc=2 >> non-zero return code cron模块: #指定定时任务,禁用定时任务,启用定时任务,删除定时任务 #指定定时任务 root@dokcer:~# ansible localhost -m cron -a 'name="cron_test" minute=*/1 hour=* day=* month=* weekday=* state=present job="/usr/sbin/ntpdate 192.168.0.5" ' localhost | SUCCESS => { "changed": true, "envs": [], "jobs": [ "cron_test" ] } root@dokcer:~# ansible localhost -a 'crontab -l' localhost | SUCCESS | rc=0 >> #Ansible: cron_test */1 * * * * /usr/sbin/ntpdate 192.168.0.5 #禁用定时任务 root@dokcer:~# ansible localhost -m cron -a 'disabled=yes name="cron_test" minute=*/1 hour=* day=* month=* weekday=* state=present job="/usr/sbin/ntpdate 192.168.0.5" ' localhost | SUCCESS => { "changed": true, "envs": [], "jobs": [ "cron_test" ] } root@dokcer:~# ansible localhost -a 'crontab -l' localhost | SUCCESS | rc=0 >> #Ansible: cron_test #*/1 * * * * /usr/sbin/ntpdate 192.168.0.5 #启用定时任务 root@dokcer:~# ansible localhost -m cron -a 'disabled=no name="cron_test" minute=*/1 hour=* day=* month=* weekday=* state=present job="/usr/sbin/ntpdate 192.168.0.5" ' localhost | SUCCESS => { "changed": true, "envs": [], "jobs": [ "cron_test" ] } root@dokcer:~# ansible localhost -a 'crontab -l' localhost | SUCCESS | rc=0 >> #Ansible: cron_test */1 * * * * /usr/sbin/ntpdate 192.168.0.5 #删除定时任务 root@dokcer:~# ansible localhost -m cron -a 'state=absent name="cron_test" job="/usr/sbin/ntpdate 192.168.0.5" ' localhost | SUCCESS => { "changed": true, "envs": [], "jobs": [] } root@dokcer:~# ansible localhost -a 'crontab -l' localhost | SUCCESS | rc=0 >> setup模块: 收集目标主机的属性信息 root@dokcer:~# ansible localhost -m setup #获取主机所有属性信息 用filter获取被控主机的内存信息,filter支持正则表达式 root@dokcer:~# ansible localhost -m setup -a 'filter=ansible_memtotal_mb' localhost | SUCCESS => { "ansible_facts": { "ansible_memtotal_mb": 1993 }, "changed": false }
ansible文件模块:
copy模块: #文件纯拷贝,拷贝时更改属性,拷贝时备份,根据内容增加文件 #文件复制,src控制端源路径,dest为被控端目标路径,目标和源路径最好一致 root@docker:~# ansible localhost -m copy -a 'src=/home/lion/1.txt dest=/tmp/1.txt' localhost | SUCCESS => { "changed": false, "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "dest": "/tmp/1.txt", "gid": 0, "group": "root", "mode": "0644", "owner": "root", "path": "/tmp/1.txt", "size": 0, "state": "file", "uid": 0 } root@docker:~# ansible localhost -a 'ls /tmp -l' -o #-o参数代表一行输出 localhost | CHANGED | rc=0 | (stdout) total 20\n-rw-r--r-- 1 root root 30 Jul 8 13:24 1.sh\n-rw-r--r-- 1 root root 0 Jul 8 13:08 1.txt\ndrwx------ #拷贝时更改属主和文件权限 owner属主 group组 mode文件模式 root@docker:~# ansible localhost -m copy -a 'src=/home/lion/1.txt dest=/tmp/1.txt owner=root group=sudo mode=666' localhost | SUCCESS => { "changed": true, "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "dest": "/tmp/1.txt", "gid": 27, "group": "sudo", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "mode": "0666", "owner": "root", "size": 0, "src": "/home/lion/.ansible/tmp/ansible-tmp-1657418921.53-172677670998462/source", "state": "file", "uid": 0 } root@docker:~# ansible localhost -a 'ls /tmp -l' localhost | SUCCESS | rc=0 >> total 20 -rw-r--r-- 1 root root 30 Jul 8 13:24 1.sh -rw-rw-rw- 1 root sudo 0 Jul 10 02:08 1.txt #属性666 2-4-1 #读写执行 #文件备份 backup=yes,备份时文件一点要有变化否则copy失败, root@docker:~# ansible localhost -m copy -a 'src=/home/lion/1.txt dest=/tmp/1.txt owner=root group=sudo mode=666 backup=yes' localhost | SUCCESS => { "backup_file": "/tmp/1.txt.8363.2022-07-10@02:15:01~", #备份文件包含时间戳 "changed": true, "checksum": "a48f53309f72df9fe7124c65605c328c0c4dd415", "dest": "/tmp/1.txt", "gid": 27, "group": "sudo", "md5sum": "6e5599d95ad03eb9ead7390bd1be4146", "mode": "0666", "owner": "root", "size": 7, "src": "/home/lion/.ansible/tmp/ansible-tmp-1657419301.17-100695084433159/source", "state": "file", "uid": 0 } content模块: #利用content直接把字符串写入文件 root@docker:~# ansible localhost -m copy -a 'content="hello world" dest=/tmp/content.txt' localhost | SUCCESS => { "changed": true, "checksum": "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", "dest": "/tmp/content.txt", "gid": 0, "group": "root", "md5sum": "5eb63bbbe01eeed093cb22bb8f5acdc3", "mode": "0644", "owner": "root", "size": 11, "src": "/home/lion/.ansible/tmp/ansible-tmp-1657419516.99-173271494695010/source", "state": "file", "uid": 0 } root@docker:~# ansible localhost -a 'cat /tmp/content.txt' localhost | SUCCESS | rc=0 >> hello world fetch模块: fetch模块与copy模块正好相反,是从远程主机被控端传输文件到控制端的本地主机 #拉取文件 #localhost上的/tmp/2.txt文件拉取到/home/lion/localhost/tmp/2.txt #目录为dest/[主机]/src root@docker:~# ansible localhost -m fetch -a 'src=/tmp/2.txt dest=/home/lion' localhost | SUCCESS => { "changed": true, "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "dest": "/home/lion/localhost/tmp/2.txt", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "remote_checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "remote_md5sum": null } root@docker:~# ls /home/lion 1.txt localhost root@docker:~# ls /home/lion/localhost/ tmp root@docker:~# ls /home/lion/localhost/tmp 2.txt #多文件拷贝通过压缩包的方式 #添加/tmp文件下的所有文件到压缩包 root@docker:~# ansible localhost -a 'tar zcf tmp.tar.gz /tmp' #拉取压缩包文件 root@docker:~# ansible localhost -m fetch -a 'src=tmp.tar.gz dest=/tmp' localhost | SUCCESS => { "changed": true, "checksum": "385215126769067cd0ea21c9a4461458f52d2344", "dest": "/tmp/localhost/tmp.tar.gz", "md5sum": "819dee6bdbd3fef5707637139d0dff6a", "remote_checksum": "385215126769067cd0ea21c9a4461458f52d2344", "remote_md5sum": null } file模块: 文件创建,设置属性,文件删除 #创建目录 root@docker:~# ansible localhost -m file -a 'path=/file/ state=directory' localhost | SUCCESS => { "changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/file/", "size": 4096, "state": "directory", "uid": 0 } #创建文件 state=touch root@docker:~# ansible localhost -m file -a 'path=/file/1.txt state=touch' localhost | SUCCESS => { "changed": true, "dest": "/file/1.txt", "gid": 0, "group": "root", "mode": "0644", "owner": "root", "size": 0, "state": "file", "uid": 0 } #创建link文件 root@docker:~# ansible localhost -m file -a 'src=/etc/fstab dest=/file/1.fstab state=link' localhost | SUCCESS => { "changed": true, "dest": "/file/1.fstab", "gid": 0, "group": "root", "mode": "0777", "owner": "root", "size": 10, "src": "/etc/fstab", "state": "link", "uid": 0 } #file模块修改文件属主和权限 root@docker:~# ansible localhost -m file -a 'path=/file/1.txt owner=root mode=777' localhost | SUCCESS => { "changed": true, "gid": 0, "group": "root", "mode": "0777", "owner": "root", "path": "/file/1.txt", "size": 0, "state": "file", "uid": 0 } #file删除文件,要确保文件没有正在被使用 root@docker:~# ansible localhost -m file -a 'path=/file/1.txt state=absent' localhost | SUCCESS => { "changed": true, "path": "/file/1.txt", "state": "absent" } #file查看文件属性 root@docker:~# ansible localhost -m stat -a 'path=/tmp/1.txt'
ansible应用模块:
apt模块 #安装软件 ansible localhost -m apt -a 'package=wget state=present' #卸载软件 ansible localhost -m apt -a 'package=wget state=absent' #更新软件 root@docker:~# ansible localhost -m apt -a 'package=wget state=latest' #更新软件 #root@docker:~# ansible localhost -m apt -a 'upgrade=yes' server模块使用: #开机自启动,服务重启,启动,停止 #服务自启动 root@docker:~# ansible localhost -m service -a 'name=nginx enabled=yes' #验证服务是否自启动 root@docker:~# ansible localhost -a 'systemctl is-enabled nginx' localhost | SUCCESS | rc=0 >> enabled #服务已设置自启动 #服务启动 root@docker:~# ansible localhost -m service -a 'name=nginx state=started' #服务停止 root@docker:~# ansible localhost -m service -a 'name=nginx state=stopped' #服务重载 root@docker:~# ansible localhost -m service -a 'name=nginx state=reloaded' #服务重启 root@docker:~# ansible localhost -m service -a 'name=nginx state=restarted' debug模块: #查看帮助信息,主要在playbook中使用 #显示默认debug信息 root@docker:~# ansible localhost -m debug localhost | SUCCESS => { "msg": "Hello world!" } #设置debug信息输出 root@docker:~# ansible localhost -m debug -a 'msg='ss'' localhost | SUCCESS => { "msg": "ss" }
ansible命令:
ansible-doc -t 指定特殊的插件类型来显示,默认为module ansible-glaxy 官方的模板网站 ansible-playbook #执行playbook脚本 ansible-vault 采用安全加密的方式来操作文件 #创建文件,输入密码和确认密码 root@docker:~# ansible-vault create 1.txt #输入密码查看文件 root@docker:~# ansible-vault view 12.txt #编辑文件 root@docker:~# ansible-vault edit 12.txt #解密文件 root@docker:~# ansible-vault decrypt 12.txt #加密文件 root@docker:~# ansible-vault encrypt 12.txt #重新设置密钥 root@docker:~# ansible-vault rekey 12.txt ansible-console #可交互式终端执行命令 root@docker:~# ansible-console Welcome to the ansible console. Type help or ? to list commands. root@all (1)[f:5]$ list #主机数为1 fork数量为5 192.168.0.50 root@192.168.0.50 -k (1)[f:5]$ cd localhost #进入主机列表中的主机 root@localhost (1)[f:5]$ ls #就和一样执行命令 localhost | SUCCESS | rc=0 >> 12.txt 1.txt localhost tmp.tar.gz root@localhost (1)[f:5]$ #apt模块安装wget软件 wget -o /文件/ /http路径 root@localhost (1)[f:5]$ apt package=wget state=present ansible-inventory #获取主机清单信息的专用命令 #列出所有主机 root@docker:~# ansible-inventory --export --list #列出所有主机 root@docker:~# ansible-inventory --vars --list
ansible-playbook模块:
playbook其实就是一个自动化执行ansible的命令方式罢了 使用ansible-playbook执行playbook文件,playbook文件 使用yaml语言编写 ansible在执行playbook的时候,执行效果具有幂等性(即一个命令执行多次与执行一次效果一样)
playbook文件内容组成
组成 | 功能 | 常见属性 | 备注 |
Target section | 执行playbook的目标主机 | hosts remote_user order | 必备内容 |
Variable section | 执行playbook的需要的变量 | vars | 可选内容 |
Task section | playbook中的功能任务 | task(name 模块名称) | 必备内容 |
Handler section | 各种任务之间的关联关系 | handler(name 模块名称) | 可选内容 |
YAML语法:
YAML文件示例:
--- - hosts: 192.168.0.50 remote_user: root tasks: - name: hello world command: wall 'hello world' tags: - hello world YAML语法特点 大小写敏感 使用缩进表示层级关系 缩进时不允许使用TAB键,只允许使用空格 缩进的空格不重要,只要相同层级的元素左侧对齐即可 #表示注释 ---表示分割符,是多个文件合并在一个文件中的分割效果
playbook文件实例:
1.安装
httpd
服务2.开启
httpd server
- hosts: 192.168.0.50,localhost remote_user: root tasks: - name: install httpd_package apt: package=nginx state=present - name: start service service: name=nginx state=started enabled=yes
root@docker:~# ansible-playbook test.yaml --syntax-check #语法检测 root@docker:~# ansible-playbook test.yaml -C test.yaml #模拟执行 root@docker:~# ansible-playbook test.yaml -l#执行playbook文件 #-l表示指定某一台主机执行playbook文件 root@docker:~# netstat -tnulp #查看端口监听状态 Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 14869/nginx: master tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN 885/systemd-resolve
playbook host属性:
单个主机: -hosts: 192.168.0.50 -hosts: master.ansible.com 多个主机: -hosts: 192.168.0.* -hosts: *.ansible.com 特定主机:(交集,并集,补集) -hosts: web:mysql -hosts: web:&mysql -hosts: web:!mysql
playbook remote_user属性:
remote_user作用相当于-u(登录用户)+ -k(登录密码) + -K(提权密码)
默认登录用户(全局用户),用于所有task任务的默认执行用户
- hosts: localhost remote_user: root 或者 - hosts: localhost remote_user: otheruser
特殊登录用户:
- hosts: localhost remote_user: root tasks: - name test ping: remote_user: lion sudo: yes #sudo切换到root用户 sudo_user: otheruser #sudo切换到其他用户身份 #在task中sudoh和sudo_user只存在一个即可 #子任务在切换任务时候,要和全局默认的登录用户识别清楚
playbook tasks:
playbook文件只要就是实现各种功能,每一个tasks代表一个实现功能,playbook中的功能具有幂等特性
tasks根据顺序从上到下依次执行,如果中途某一个tasks出现错误,那么整个任务就会中断
tasks格式 tasks: - name: install_nginx #名称 ping: #执行的动作,也就是ansible模块命令,格式二 - name: ping action: ping #格式一 ansible模块的动作分为两种格式 1: action: 模块 具体动作 2: 模块: 具体动作 对于 shell和command模块来说具体动作就是可执行命令,对于其他模块,具体动作就是参数key=value格式 在一个tasks中有多个action动作,只有最后一个action动作生效 如果想要执行多条命令,只有写多个子任务,也就是多个name 不过如果想要用shell模块执行多个命令,可以使用&;之类的命令连接符号,但是动作只能有一个 可以使用 -v,-vv,-vvvv参数查看执行过程
playbook异常中断
playbook是从上到下依次执行的,如果中间出现异常就会中断执行,必须要解决中断 palybook的任务能否按顺序执行下去,主要是依据任务之间的指令的执行状态返回码来 判断的,正常执行为0否则为非0,可以通过||逻辑符号来强制让指令返回0 1:强制正确(|| /bin/true) 2:忽略错误,并抛出异常 - hosts: localhost remote_user: root tasks: - name: touch1 shell: rm 4.txt || /bin/true #4.txt文件不存在,所以会报错,/bin/true强制执行正确 playbook有一种专门在处理任务列表意外中断的属性值ignore_erros,用于判断任务是否异常 - hosts: localhost remote_user: root tasks: - name: touch1 shell: rm 4.txt ignore_errors: True #使用ignore_errors值为true,这样就可忽略异常然后抛出错误
playbook任务依赖:
playbook中存在一种依赖关系,分为监控器和触发器
notify: notify是task中的一个子属性,监控我们指定的动作涉及的内容是否发生了变化,比如配置文件
notify: handler名称
handlers: 接收到变化的动作后,自动执行的其他操作命令
应该先写handlers,然后再在notify中调用,要先写handlers
- hosts: localhost remote_user: root tasks: - name: copy4 copy: src=/home/lion/4.txt dest=/tmp/4.txt notify: touch5 #监听器当4.txt出现修改时就会执行handlers中的touch5子任务 ignore_errors: True handlers: #触发器 - name: touch5 shell: touch 5.txt
让一个notify执行多个触发器
- hosts: localhost remote_user: root tasks: - name: copy4 copy: src=/home/lion/4.txt dest=/tmp/4.txt notify: - touch5 #监听器当4.txt文件出现修改时,会同时触发touch5和touch6两个动作 - touch6 ignore_errors: True handlers: - name: touch5 #触发器动作 shell: touch 5.txt - name: touch6 shell: touch 6.txt
playbook标签:
通过playbook标签可以在playbook中执行某一个任务,或者让某一个主机执行任务
- hosts: localhost tags: localhost #localhost主机 remote_user: root tasks: - name: touch1 shell: touch 7.txt tags: touch #标签为touch,可以直接用-t参数来执行touch任务 ignore_errors: True - name: touch2 command: ls root@docker:~# ansible-playbook 4.yaml --list-tags #查看yaml文件中的标签 playbook: 4.yaml play #1 (localhost): localhost TAGS: [localhost] TASK TAGS: [localhost, touch] root@docker:~# ansible-playbook 4.yaml -t touch #执行taskz中的标签任务 root@docker:~# ansible-playbook 4.yaml -t localhost #用localhost主机执行任务 #可以在不同的动作下添加同一个标签,这样就可以在执行同一个标签的同时在执行同一个标签的任务, 实现任务分割同时来增加文件层次 - hosts: localhost remote_user: root tasks: - name: touch1 shell: touch 11.txt tags: touch - name: touch2 shell: touch 10.txt tags: touch root@docker:~# ansible-playbook 4.yaml -t touch #同时执行touch1和touch2两个动作
playbook变量:
变量实现方式 | 优先级 | 编号 |
目标主机默认属性 | 最低 | 1 |
主机清单中的特有属性 | 次低 | 2 |
命令行定义的特定属性 | 最高 | 3 |
playbook通过vars定义的属性 | 低 | 4 |
专用yaml文件定义的属性文件 | 次高 | 5 |
1<2<4<5<3 | ㅤ | ㅤ |
我们可以基于获取目标主机的主机属性来定制化yaml文件 可以用setup的方式来获取主机属性 root@docker:~# ansible localhost -m setup | grep ' "' | wc -l 847 #默认情况下 #获取cpu型号 -o输出通过一行 root@docker:~# ansible localhost -m setup -a 'filter=ansible_processor' -o localhost | SUCCESS => {"ansible_facts": {"ansible_processor": ["0", "AuthenticAMD", "AMD Athlon(tm) X4 870K Quad Core Processor"]}, "changed": false}
#playbook变量实例 通过ansible setup模块获取cpu信号并且写入cpu.txt - hosts: localhost remote_user: root tasks: - name: get_cpu_name shell: echo '{{ ansible_processor[2] }}' >> cpu.txt - name: debug debug: msg='{{ ansible_processor[2] }}' #通过debug模块打印出获取的变量,方便调试 #效果: TASK [debug] ok: [localhost] => { "msg": "AMD Athlon(tm) X4 870K Quad Core Processor" #debug模块显示 } root@docker:~# cat cpu.txt #把cpu型号写入cpu.txt,查看cpu.txt AMD Athlon(tm) X4 870K Quad Core Processor
play变量种类和优先级
在/etc/ansible/host文件中定义主机变量有两种样式: 公共变量和普通变量 普通变量比公共变量的优先级高,如果普通变量不存在然后才会使用公共变量 普通变量 root@docker:~#cat /etc/ansible/hosts [nginx] 192.168.0.50 ansible_connection=local hostname=localhost #定义普通变量,ansible定义为本地连接 root@docker:~# cat local.yaml #打印获取到的hosts文件中的普通变量 - hosts: all tasks: - name: print_hostname debug: msg='{{ hostname }}' #效果: ok: [192.168.0.50] => { "msg": "localhost" #打印了获取到的localhost变量 } 公共变量 /etc/ansible/hosts root@docker:~# tail -n 4 /etc/ansible/hosts [nginx] 192.168.0.50 ansible_connection=local hostname=localhost [nginx:vars] #公共变量 hostname=local ok: [192.168.0.50] => { #由于普通变量优先级更高,所以变量仍然等于localhost "msg": "localhost" } root@docker:~# tail -n 4 /etc/ansible/hosts [nginx] 192.168.0.50 ansible_connection=local #删除了普通变量,所以所以hostname等于local [nginx:vars] #[主机组:vars]定义变量 hostname=local ok: [192.168.0.50] => { "msg": "local" } playbook命令行变量: 通过-e 参数在命令行指定变量,所有变量用单引号括住,多个变量用空格隔开 root@docker:~# ansible all -e host='localhost' -m debug -a "msg='this is {{ host }}'" 192.168.0.50 | SUCCESS => { "msg": "this is localhost" } 通过命令行定义多个变量: root@docker:~# ansible all -e 'host=localhost user=lion' -m debug -a "msg='this is {{ host }} user is {{ user }}'" 192.168.0.50 | SUCCESS => { "msg": "this is localhost user is lion" } playbook yaml文件中定义变量: 通过vars列表的方式来定义变量 root@docker:~# cat var.yaml - hosts: all remote_user: root vars: - user: root - hostname: master tasks: - name: print_user_hostname debug: msg='user is {{ user }} hostname is {{ hostname }}' ok: [192.168.0.50] => { "msg": "user is root hostname is master" #打印出定义的变量 } #命令行的变量优先级高于yaml文件中的变量 root@docker:~# ansible-playbook -e user='lion' -C var.yaml TASK [print_user_hostname] ok: [192.168.0.50] => { "msg": "user is lion hostname is master" #打印出命令行中定义的user变量 } PLAY RECAP 192.168.0.50 : ok=2 changed=0 unreachable=0 failed=0 ansible专用yaml变量文件 可以在专用的yaml文件中定义统一变量,可以应用多个变量文件,推荐用绝对路径,一般情况下 放到和yaml执行文件的同一路径下,使用相对路径 vars.yaml专用变量文件 root@docker:~# cat vars.yaml user: lion hostname: localhost root@docker:~# cat var.yaml - hosts: all remote_user: root vars_files: - vars.yaml #变量文件 vars: #playbook中定义的变量 - user: root - hostname: master tasks: - name: print_user_hostname debug: msg='user is {{ user }} hostname is {{ hostname }}' 执行结果: 变量文件中的变量的优先级大于yaml文件中的变量 ok: [192.168.0.50] => { "msg": "user is lion hostname is localhost" } #显示root组是否存在 root@docker:~# getent group | grep root root:x:0:
ansible模板模块:
ansible可以通过jinja2模块来实现主机配置文件的灵活定制 使用jinja2模板语言来定制配置文件,j2模板文件推荐使用相对目录,文件路径在playbook文件下的 template目录下 root@docker:~# cat template.yaml - hosts: all remote_user: root vars: - cpu: 'x4 870k' tasks: - name: get_cpu_model template: src=./template/cpu.txt.j2 dest=/tmp/cpu.txt #使用j2模板文件,生成/tmp/cpu.txt文件 root@docker:~# cat /tmp/cpu.txt #cpu model CPU Model is x4 870k #模板语法条件判断 when支持算数运算符和比较运算符和逻辑运算符 - hosts: all remote_user: root tasks: - name: AMD debug: msg='CPU is AMD' when: ansible_processor[1] == 'AuthenticAMD' #当cpu为AMD时 - name: INTEL debug: msg='CPU is INTEL' when: ansible_processor[1] != 'AuthenticAMD' #当cpu不是AMD时 #执行结果: ok: [192.168.0.50] => { "msg": "CPU is AMD" } TASK [INTEL] skipping: [192.168.0.50] #跳过执行 #模板迭代执行 with_itmes可以是列表和字典 一个任务中一个action动作只能执行一个,所以可以通过类似于循环的方式通过一个action执行多个动作 - hosts: all remote_user: root tasks: - name: print_process_info debug: msg='{{ item }}' #把item替换成with_items列表中的每一项,with_items可以是列表和字典 with_items: - '{{ ansible_processor[0] }}' - '{{ ansible_processor[1] }}' - '{{ ansible_processor[2] }}' 打印结果: ok: [192.168.0.50] => (item=None) => { "msg": "0" } ok: [192.168.0.50] => (item=None) => { "msg": "AuthenticAMD" #打印cpu种类 } ok: [192.168.0.50] => (item=None) => { "msg": "AMD Athlon(tm) X4 870K Quad Core Processor" #打印cpu型号 } #迭代和流程流程判断结合 - hosts: all remote_user: root tasks: - name: AMD debug: msg='CPU is AMD' when: ansible_processor[1] == 'AuthenticAMD' - name: INTEL debug: msg='CPU is INTEL' when: ansible_processor[1] != 'AuthenticAMD' - name: AMD_OR_INTEL debug: msg='{{ item }}' when: ansible_processor[1] == 'AuthenticAMD' #当when成立时,才执行迭代操作 with_items: - the cpu is AMD_CPU - AMD_yyds! #迭代进阶 使用多值迭代,通过类似python字典的方式来,使用多个字段来多值迭代 - hosts: all remote_user: root tasks: - name: AMD_OR_INTEL debug: msg='the cpu is {{ item.model }} {{ item.cpu_info }}' with_items: - { model: 'AMD', cpu_info: 'AMD YYDS!!' } #模板流程控制 {{}} #用于表达式,变量 {% %} #流程控制,if,for {# #} #用于注释内容 条件控制语句 {% if 条件 %} 执行语句 {% endif %} 条件判断 {%if 变量 is denfind %} 循环语句 {% for 条件 %} 执行语句 {% end for %} fo循环 {% for 变量 in 变量列表 %} 只有当条件成立时,执行语句才会执行 #模板文件: #cpu_info {%if cpu is defined %} the cpu is {{ cpu }}; {% endif %} #yaml文件 - hosts: all remote_user: root tasks: - name: cpu_info template: src=./template/cpuz.txt.j2 dest=/tmp/cpuz.txt #/etc/ansible/hosts 192.168.0.50 ansible_connection=local cpu=AMD #定义了cpu变量 #cpu_info for循环实例 yaml文件 - hosts: all remote_user: root vars: cpu_info: - INTEL - AMD tasks: - name: cpu_info template: src=./template/cpus.txt.j2 dest=/tmp/cpus.txt 模板文件 #cpu_info {% for cpu in cpu_info %} {%if cpu is defined %} the cpu is {{ cpu }}; {% endif %} {% endfor%} 执行结果: cpus.txt root@docker:~# cat /tmp/cpus.txt #cpu_info the cpu is INTEL; the cpu is AMD;
ansible role:
ansible role角色
ansible role可以将不同的功能的yaml文件整合在一起,比如tasks,template,file,等不同功能的模块
分割成单个yaml文件,然后再在入口的yaml(mian.yaml)文件中导入其他的功能模块(include)。这样就可以实现更加灵活的管理和增加功能,实现模块化管理。
role不适用中小场景管理环境,不复杂的场景不必应用role,role适用于大型业务管理
#创建roles文件夹 root@docker:~# mkdir test_role root@docker:~# cd test_role/ root@docker:~/test_role# mkdir roles root@docker:~/test_role# mkdir roles/role_cpu root@docker:~/test_role# tree . └── roles └── role_cpu 2 directories, 0 files root@docker:~/test_role# mkdir roles/role_cpu/{tasks,vars} #创建tasks,vars模块文件夹 root@docker:~/test_role# tree . └── roles └── role_cpu ├── tasks └── vars 4 directories, 0 files root@docker:~/test_role# touch roles/role_cpu/tasks/{main,debug}.yaml #创建子任务的yaml文件 root@docker:~/test_role# tree . └── roles └── role_cpu ├── tasks │ ├── debug.yaml │ └── main.yaml └── vars 4 directories, 2 files root@docker:~/test_role# touch roles/role_cpu/vars/main.yaml #创建变量main目录 root@docker:~/test_role# tree . └── roles └── role_cpu ├── tasks │ ├── debug.yaml │ └── main.yaml └── vars └── main.yaml 4 directories, 3 files #test_role.yaml - hosts: all remote_user: root roles: - role: role_cpu #引入角色,标准写法 ------------------------------------------------------------------------------------ - hosts: all remote_user: root roles: - role_cpu #引入角色,简写 #tasks/main.yaml - include: debug.yaml #导入其他task模块,在项目中使用相对路径以main.yaml目录为准 (./main.yaml 当前目录,../ main.yaml的上一级目录) #tasks/debug.yaml - name: cpu_info debug: msg={{ ansible_processor[1] }} #打印出cpu型号 - name: os debug: msg={{ systeminfo[0] }} #在var/main.yaml中定义的变量 - name: hostname debug: msg='hostname is {{ ansible_facts['nodename'] }}' #打印hostname #vars/main.yaml systeminfo: - '{{ ansible_os_family }}' #变量定于格式必须时变量字典的形式,否则会报错 执行结果: root@docker:~/test_role# ansible-playbook -C test_role.yaml ok: [192.168.0.50] TASK [role_cpu : cpu_info] "msg": "AuthenticAMD" } TASK [role_cpu : os] ok: [192.168.0.50] => { "msg": "Debian" } TASK [role_cpu : hostname] ok: [192.168.0.50] => { "msg": "hostname is docker" } #可以在role主yaml文件中定义变量,执行role时引用定义的变量 { role: role角色目录,变量:变量值} - hosts: all remote_user: root roles: - { role: role_cpu, user: root } #在debug目录中使用user变量 - name: cpu_info debug: msg={{ ansible_processor[1] }} - name: os debug: msg={{ systeminfo[0] }} - name: hostname debug: msg='hostname is {{ ansible_facts['nodename'] }}' - name: user debug: msg='{{ user }}' #使用定义的变量 #roles结合条件判断,只有当when条件判断成立时,才可以定义变量,才会执行roles角色tasks #执行roles角色 - hosts: all remote_user: root roles: - { role: role_cpu, user: root, when: ansible_facts.nodename == 'docker' } #不执行roles角色 - hosts: all remote_user: root roles: - { role: role_cpu, user: root, when: ansible_facts.nodename == 'doc' } #定义多个role角色,用标签的方式调用不同的roles,执行不同的tasks - hosts: all remote_user: root roles: - { role: role_cpu, user: root, tags: ['cpu_info', 'localhost'] } #用-t运行指定标签的role,用-l指定执行的主机 root@docker:~/test_role# ansible-playbook -C test.yaml -t cpu_info -l all
ansible示例:
ansbile部署prometheus,并创建prometheus service,设置开机启动
prometheus.yml:
- hosts: localhost remote_user: root vars: - version: 2.37.0 #下载的版本 tasks: - name: downlaod prometheus command: wget https://github.com/prometheus/prometheus/releases/download/v{{ version }}/prometheus-{{ version }}.linux-amd64.tar.gz - debug: msg='wget https://github.com/prometheus/prometheus/releases/download/v{{ version }}/prometheus-{{ version }}.linux-amd64.tar.gz' - name: tar prometheus shell: tar -xzvf prometheus-{{ version }}.linux-amd64.tar.gz -C /usr/local/ - name: link shell: ln -sv /usr/local/prometheus-{{ version }}.linux-amd64/prometheus /usr/local/bin/prometheus - name: touch prometheus service template: src=./prometheus.service dest=/etc/systemd/system/prometheus.service tags: prometheus_service - name: start prometheus service shell: systemctl start prometheus.service - name: enable prometheus service shell: systemctl enable prometheus.service
prometheus.service文件模板:
[Unit] Description=prometheus [Service] Restart=on-failure ExecStart=/usr/local/bin/prometheus --config.file=/usr/local/prometheus-{{ version }}.linux-amd64/prometheus.yml [Install] WantedBy=multi-user.target