ansible

http://docs.ansible.com/ansible/intro_installation.html
http://docs.ansible.com/modules_by_category.html
http://docs.ansible.com/ansible/authorized_key_module.html

简介

自动化运维工具ansible
ansible基于python开发的自动化运维工具(saltstack)
python语言是运维人员最佳的语言
其功能实现基于SSH远程连接服务
批量系统配置、批量软件部署、批量文件拷贝、批量运行命令等功能

特点

1、不需要单独安装客户端,基于sshd服务的,sshd就相当于ansible的客户端
2、不需要服务端
3、依靠大量的模块实现

管理机安装

yum install epel-release  -y
yum install ansible -y

客户端安装(如果selinux开启则安装)

yum install libselinux-python -y

查看

[root@m01 ~]# ansible --version
ansible 2.7.8
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Oct 30 2018, 23:45:53) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]

ansible配置文件

[root@m01 ~]# tree /etc/ansible/
/etc/ansible/
├── ansible.cfg
├── hosts
└── roles

1 directory, 2 files

ansible配置文件读取顺序

  • ANSIBLE_CONFIG (如果设置了环境变量)
  • ansible.cfg (在当前目录中)
  • ~/.ansible.cfg (在家目录中)
  • /etc/ansible/ansible.cfg

管理机配置

vim /etc/ansible/hosts

[oldboy]
172.16.1.31
172.16.1.41
172.16.1.61
172.16.1.7
172.16.1.8

[oldboy]
172.16.1.31 ansible_ssh_user=oldboy ansible_ssh_pass=123456 ansible_ssh_port=12345
172.16.1.41 ansible_ssh_user=oldboy ansible_ssh_pass=123456 ansible_ssh_port=12345

查看主机列表

ansible web --list-host

ansible配置sudo

ansible及客户端都添加sudo授权用户

/etc/ansible/ansible.cfg
deprecation_warnings=False
sudo_user      = oldboy
remote_port      = 52113

内置变量

ansible_connection
连接类型到主机。这可以是任何ansible连接插件的名称。SSH协议类型是smart,ssh或paramiko。默认是智能的。基于非SSH的类型将在下一节中介绍。

ansible_host   要连接的主机的名称,如果与您希望提供给它的别名不同。
ansible_port   ssh端口号,如果不是22
ansible_user   要使用的默认ssh用户名。

ansible_ssh_host:   ansible使用ssh要连接的主机。
ansible_ssh_port:   ssh的端口。默认为22。
ansible_ssh_user:   ssh登录的用户名。默认为root。

ansible_ssh_pass
要使用的ssh密码(永远不要将此变量存储为纯文本;始终使用保险库。请参阅变量和保险柜)

ansible_ssh_private_key_file
ssh使用的私钥文件。如果使用多个密钥并且您不想使用SSH代理,则很有用。

ansible_ssh_common_args
此设置始终附加到sftp,scp和ssh的默认命令行。ProxyCommand用于为特定主机(或组)配置

ansible_sftp_extra_args  此设置始终附加到默认的sftp命令行。
ansible_scp_extra_args  此设置始终附加到默认scp命令行。
ansible_ssh_extra_args  此设置始终附加到默认的ssh命令行。

ansible_ssh_pipelining  确定是否使用SSH流水线。这可以覆盖中的pipelining设置ansible.cfg。

ansible_ssh_executable(在2.2版中添加)
此设置将覆盖使用系统ssh的默认行为。这可以覆盖中的ssh_executable设置ansible.cfg。

ansible_become  等同于ansible_sudo或ansible_su允许强制权限升级
ansible_become_method   允许设置权限提升方法

ansible_become_user  等同于ansible_sudo_user或ansible_su_user允许通过权限提升设置您成为的用户

ansible_become_pass   等效于ansible_sudo_pass或ansible_su_pass允许您设置权限提升密码(永远不要将此变量存储为纯文本;始终使用保管库。请参阅变量和保管库)

ansible_become_exe   等效于ansible_sudo_exe或ansible_su_exe允许您为所选的升级方法设置可执行文件

ansible_become_flags   等效于ansible_sudo_flags或ansible_su_flags允许您设置传递给选定升级方法的标志。这也可以ansible.cfg在sudo_flags选项中全局设置

ansible_shell_type
目标系统的shell类型。除非已将ansible_shell_executable设置为非Bourne(sh)兼容shell,否则不应使用此设置 。默认情况下,命令使用sh-style语法格式化。将此设置为csh或fish将导致在目标系统上执行的命令遵循这些shell的语法。

ansible_python_interpreter
目标主机python路径。这对于具有多个Python或不位于/ usr / bin / python的系统(如* BSD)或/ usr / bin / python 不是2.X系列Python的系统非常有用。我们不使用/ usr / bin / env机制,因为它需要设置远程用户的路径,并假设python可执行文件名为python,其中可执行文件可能被命名为python2.6。

ansible_shell_executable
这将设置ansible控制器将目标机器上使用的壳,将覆盖executable在ansible.cfg其中默认为 / bin / sh的。如果无法使用/ bin / sh(即/ bin / sh未安装在目标计算机上或无法从sudo运行),您应该只更改它。

普通用户批量管理

管理机与所有被管理机都创建普通用户并sudo授权

wuxingge      ALL=(ALL)       NOPASSWD: ALL

管理机普通用户创建密钥对并分发公钥

ansible配置

/etc/ansible/ansible.cfg

sudo_user      = wuxingge
deprecation_warnings = False
private_key_file = /home/wuxingge/.ssh/id_rsa

测试

ansible web -m copy -a "src=/etc/hosts dest=/root/" -s
ansible-playbook test.yml -s

ansible软件颜色信息

  • 绿色: 表示查看信息,对远程主机未做改动的命令
  • 红色: 批量管理产生错误信息
  • 黄色: 对远程主机做了相应改动
  • 粉色: 对操作提出建议或忠告

hosts

https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
/etc/ansible/hosts

mail.example.com

[webservers]
foo.example.com
bar.example.com

[dbservers]
one.example.com
two.example.com
three.example.com
###################yaml
all:
  hosts:
    mail.example.com:
  children:
    webservers:
      hosts:
        foo.example.com:
        bar.example.com:
    dbservers:
      hosts:
        one.example.com:
        two.example.com:
        three.example.com:
jumper ansible_port=5555 ansible_host=192.0.2.50
############# yaml
  hosts:
    jumper:
      ansible_port: 5555
      ansible_host: 192.0.2.50

children

[backup:children]
backupclient
backupserver
[backupclient]
172.16.1.7
172.16.1.31
[backupserver]
172.16.1.41
[atlanta]
host1
host2

[raleigh]
host2
host3

[southeast:children]
atlanta
raleigh

[southeast:vars]
some_server=foo.southeast.example.com
halon_system_timeout=30
self_destruct_countdown=60
escape_pods=2

[usa:children]
southeast
northeast
southwest
northwest
############ yaml
all:
  children:
    usa:
      children:
        southeast:
          children:
            atlanta:
              hosts:
                host1:
                host2:
            raleigh:
              hosts:
                host2:
                host3:
          vars:
            some_server: foo.southeast.example.com
            halon_system_timeout: 30
            self_destruct_countdown: 60
            escape_pods: 2
        northeast:
        northwest:
        southwest:

主机变量(vars)

[atlanta]
host1
host2

[atlanta:vars]
ntp_server=ntp.atlanta.example.com
proxy=proxy.atlanta.example.com
################yaml
atlanta:
  hosts:
    host1:
    host2:
  vars:
    ntp_server: ntp.atlanta.example.com
    proxy: proxy.atlanta.example.com

hosts (inventory)

vim ansible.cfg

...
inventory      = /etc/ansible/inventory/
...
cat inventory/part1
[nginx]
172.16.1.7

cat inventory/part2
[common]
172.16.1

按目录结构存储变量

group_vars/和**host_vars/目录可以存在于playbooks目录或inventory目录中。如果两个路径都存在,则playbook目录中的变量将覆盖inventory目录中设置的变量。
相关的Host和Group变量可以放在/etc/ansible/host_vars/和/etc/ansible/group_vars/下的主机同名目录中的文件,也支持'.yml'、'.yaml'和'.json'为后缀的YMAL和JSON文件

[图片上传失败...(image-7de1dd-1560937738186)]

group_vars目录下的文件定义Roles中调用的变量
文件名为all的文件定义的变量针对所有Roles生效

tree inventory
inventory
├── group_vars
│   ├── all
│   └── nginx
├── part1
└── part2

cat inventory/part1 
[nginx]
172.16.1.7

cat inventory/part2
[common]
172.16.1.8

cat inventory/group_vars/nginx 
nginx_port: 8080

cat inventory/group_vars/all 
nginx_port: 8888
cat inventory/hosts_vars/127.0.0.1
---
myname: wuxing
address: beijing
cat playbooks/nginx.yml 
- hosts: all
  roles:
    - nginx
ansible-playbook -i inventory playbooks/nginx.yml
inventory/
  01-openstack.yml          # configure inventory plugin to get hosts from Openstack cloud
  02-dynamic-inventory.py   # add additional hosts with dynamic inventory script
  03-static-inventory       # add static hosts
  group_vars/
    all.yml                 # assign variables to all hosts

模块

ansible   <host-pattern>   [-m module_name]   [-a args]  [options]

command模块

oldboy为主机组的名字
-m 后边是模块名
-m MODULE_NAME, --module-name=MODULE_NAME
module name to execute (default=command)
默认的模块是command

-a 后面是要执行的命令,也可以写一个ip,针对一台机器来执行命令
-a MODULE_ARGS, --args=MODULE_ARGS
module arguments

ansible oldboy -m command -a 'uptime'
- name: return motd to registered var
  command: cat /etc/motd
ansible oldboy -m command -a "chdir=/tmp touch hosts01"
ansible oldboy -m command -a "creates=/tmp/hosts01 touch hosts01"

shell模块

http://docs.ansible.com/ansible/latest/shell_module.html
执行脚本(脚本需要分发到远程机器)

[root@m01 ~]# echo 'yum install -y ipvsadm' >> /server/scripts/yum.sh
[root@m01 ~]# cat /server/scripts/yum.sh
yum install -y ipvsadm
ansible oldboy -m copy -a "src=/server/scripts/yum.sh dest=/server/scripts/ mode=0755"
ansible oldboy -m shell -a "/bin/bash /server/scripts/yum.sh"
- name: Execute the command in remote shell; stdout goes to the specified file on the remote.
  shell: somescript.sh >> somelog.txt

- name: Change the working directory to somedir/ before executing the command.
  shell: somescript.sh >> somelog.txt
  args:
    chdir: somedir/

# You can also use the 'args' form to provide the options.
- name: This command will change the working directory to somedir/ and will only run when somedir/somelog.txt doesn't exist.
  shell: somescript.sh >> somelog.txt
  args:
    chdir: somedir/
    creates: somelog.txt

- name: Run a command that uses non-posix shell-isms (in this example /bin/sh doesn't handle redirection and wildcards together but
  shell: cat < /tmp/*txt
  args:
    executable: /bin/bash

ping模块

https://docs.ansible.com/ansible/latest/modules/ping_module.html#ping-module

ansible oldboy -m ping

copy模块

https://docs.ansible.com/ansible/latest/modules/copy_module.html#copy-module
copy模块将文件从本地复制到远程计算机上的某个位置

ansible oldboy -m copy -a "src=/etc/passwd dest=/tmp/oldgirl.txt owner=oldboy group=oldboy mode=0755"

ansible copy模块 会自动创建多级目录

[root@m01 scripts]# ansible oldboy -m copy -a "src=/etc/hosts  dest=/tmp/a/b/c/e/d/f/f/g/"

目标目录都存在的情况下,可以传输过去同时修改文件名

ansible oldboy -m copy -a "src=/etc/hosts dest=/tmp/HOSTS"
ansible oldboy -m copy -a "src=/etc/hosts dest=/mnt backup=yes"

backup=yes 备份

Example from Ansible Playbooks

- copy:
    src: /srv/myfiles/foo.conf
    dest: /etc/foo.conf
    owner: foo
    group: foo
    mode: 0644
- copy:
    content: xxxxxxxxxxx
    dest: /some/path/systems.csv
    owner: foo
    group: foo
    mode: 0644
    backup: yes

- name: Copy a new "ntp.conf file into place, backing up the original if it differs from the copied version
  copy:
    src: /mine/ntp.conf
    dest: /etc/ntp.conf
    owner: root
    group: root
    mode: '0644'
    backup: yes

- name: Copy a "sudoers" file on the remote machine for editing
  copy:
    src: /etc/sudoers
    dest: /etc/sudoers.edit
    remote_src: yes
    validate: /usr/sbin/visudo -cf %s

remote_src

ansible all -m copy -a 'src=/root/file1 dest=/opt/'
#如果src指定的文件在远程机器存在,则拷贝远程机器此文件到dest
ansible all -m copy -a 'src=/opt/file1 dest=/opt/file1.bak remote_src=yes'

template

https://docs.ansible.com/ansible/latest/modules/template_module.html#template-module

# Example from Ansible Playbooks
- template:
    src: /mytemplates/foo.j2
    dest: /etc/file.conf
    owner: bin
    group: wheel
    mode: 0644

# The same example, but using symbolic modes equivalent to 0644
- template:
    src: /mytemplates/foo.j2
    dest: /etc/file.conf
    owner: bin
    group: wheel
    mode: "u=rw,g=r,o=r"

# Create a DOS-style text file from a template
- template:
    src: config.ini.j2
    dest: /share/windows/config.ini
    newline_sequence: '\r\n'

# Copy a new "sudoers" file into place, after passing validation with visudo
- template:
    src: /mine/sudoers
    dest: /etc/sudoers
    validate: '/usr/sbin/visudo -cf %s'

# Update sshd configuration safely, avoid locking yourself out
- template:
    src: etc/ssh/sshd_config.j2
    dest: /etc/ssh/sshd_config
    owner: root
    group: root
    mode: '0600'
    validate: /usr/sbin/sshd -t -f %s
    backup: yes

fetch 模块

从远程节点获取文件

    fetch:
      src: /tmp/uniquefile
      dest: /tmp/special/
      flat: yes
ansible 10.0.0.41 -m fetch -a "src=/etc/rsyncd.conf dest=/etc/ansible/conf/rsync/ flat=yes"

script 模块(不需要分发脚本)

https://docs.ansible.com/ansible/latest/modules/script_module.html

echo 'mkdir -p /tmp/oldboy/{a..d}' > /server/scripts/mkdir.sh
ansible oldboy -m script -a "/server/scripts/mkdir.sh"
cat >>/server/scripts/yuan.sh<<EOF
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-6.repo
EOF
ansible oldboy -m script -a "/server/scripts/yuan.sh"
- name: Run a script with arguments
  script: /some/local/script.sh --some-argument 1234

- name: Run a script only if file.txt does not exist on the remote node
  script: /some/local/create_file.sh --some-argument 1234
  args:
    creates: /the/created/file.txt

- name: Run a script only if file.txt exists on the remote node
  script: /some/local/remove_file.sh --some-argument 1234
  args:
    removes: /the/removed/file.txt

- name: Run a script using an executable in a non-system path
  script: /some/local/script
  args:
    executable: /some/remote/executable

- name: Run a script using an executable in a system path
  script: /some/local/script.py
  args:
    executable: python3

cron模块

https://docs.ansible.com/ansible/latest/modules/cron_module.html#cron-module

 ansible oldboy -m cron -a 'name=hello minute=*/2 job="echo hello >>/tmp/oldboy.log 2>&1" '
- name: Ensure a job that runs at 2 and 5 exists. Creates an entry like "0 5,2 * * ls -alh > /dev/null"
  cron:
    name: "check dirs"
    minute: "0"
    hour: "5,2"
    job: "ls -alh > /dev/null"

- name: 'Ensure an old job is no longer present. Removes any job that is prefixed by "#Ansible: an old job" from the crontab'
  cron:
    name: "an old job"
    state: absent

- name: Creates an entry like "@reboot /some/job.sh"
  cron:
    name: "a job for reboot"
    special_time: reboot
    job: "/some/job.sh"

- name: Creates an entry like "PATH=/opt/bin" on top of crontab
  cron:
    name: PATH
    env: yes
    value: /opt/bin

- name: Creates an entry like "APP_HOME=/srv/app" and insert it after PATH declaration
  cron:
    name: APP_HOME
    env: yes
    value: /srv/app
    insertafter: PATH

- name: Creates a cron file under /etc/cron.d
  cron:
    name: yum autoupdate
    weekday: 2
    minute: 0
    hour: 12
    user: root
    job: "YUMINTERACTIVE=0 /usr/sbin/yum-autoupdate"
    cron_file: ansible_yum-autoupdate

- name: Removes a cron file from under /etc/cron.d
  cron:
    name: "yum autoupdate"
    cron_file: ansible_yum-autoupdate
    state: absent

- name: Removes "APP_HOME" environment variable from crontab
  cron:
    name: APP_HOME
    env: yes
    state: absent

disabled=yes 注释定时任务

mount模块

https://docs.ansible.com/ansible/latest/modules/mount_module.html#mount-module

Control active and configured mount points
This module controls active and configured mount points in /etc/fstab

# Before 2.3, option 'name' was used instead of 'path'
- name: Mount DVD read-only
  mount:
    path: /mnt/dvd
    src: /dev/sr0
    fstype: iso9660
    opts: ro
    state: present

- name: Mount up device by label
  mount:
    path: /srv/disk
    src: LABEL=SOME_LABEL
    fstype: ext4
    state: present

- name: Mount up device by UUID
  mount:
    path: /home
    src: UUID=b3e48f45-f933-4c8e-a700-22a159ec9077
    fstype: xfs
    opts: noatime
    state: present
    - name: mount
      mount:
        path: /mnt
        src: 10.0.0.31:/data
        fstype: nfs
        state: mounted

file模块

https://docs.ansible.com/ansible/latest/modules/file_module.html

# change file ownership, group and mode
- file:
    path: /etc/foo.conf
    owner: foo
    group: foo
    # when specifying mode using octal numbers, add a leading 0
    mode: 0644
- file:
    path: /work
    owner: root
    group: root
    mode: 01777
- file:
    src: /file/to/link/to
    dest: /path/to/symlink
    owner: foo
    group: foo
    state: link
- file:
    src: '/tmp/{{ item.src }}'
    dest: '{{ item.dest }}'
    state: link
  with_items:
    - { src: 'x', dest: 'y' }
    - { src: 'z', dest: 'k' }

# touch a file, using symbolic modes to set the permissions (equivalent to 0644)
- file:
    path: /etc/foo.conf
    state: touch
    mode: "u=rw,g=r,o=r"

# touch the same file, but add/remove some permissions
- file:
    path: /etc/foo.conf
    state: touch
    mode: "u+rw,g-wx,o-rwx"

# touch again the same file, but dont change times
# this makes the task idempotents
- file:
    path: /etc/foo.conf
    state: touch
    mode: "u+rw,g-wx,o-rwx"
    modification_time: "preserve"
    access_time: "preserve"

# create a directory if it doesn't exist
- file:
    path: /etc/some_directory
    state: directory
    mode: 0755

setup ( 收集远程主机信息)

https://docs.ansible.com/ansible/latest/modules/setup_module.html#setup-module

ansible all -m setup -vvvv
ansible all -m setup -a "filter=ansible_all_ipv4_addresses"
ansible all -m setup -a "filter=ansible_eth0[ipv4]"
ansible_all_ipv4_addresses:仅显示ipv4的信息。
ansible_devices:仅显示磁盘设备信息。
ansible_distribution:显示是什么系统,例:centos,suse等。
ansible_distribution_major_version:显示是系统主版本。
ansible_distribution_version:仅显示系统版本。
ansible_machine:显示系统类型,例:32位,还是64位。
ansible_eth0:仅显示eth0的信息。
ansible_hostname:仅显示主机名。
ansible_kernel:仅显示内核版本。
ansible_lvm:显示lvm相关信息。
ansible_memtotal_mb:显示系统总内存。
ansible_memfree_mb:显示可用系统内存。
ansible_memory_mb:详细显示内存情况。
ansible_swaptotal_mb:显示总的swap内存。
ansible_swapfree_mb:显示swap内存的可用内存。
ansible_mounts:显示系统磁盘挂载情况。
ansible_processor:显示cpu个数(具体显示每个cpu的型号)。
ansible_processor_vcpus:显示cpu个数(只显示总的个数)。

debug

ansible 172.16.1.7 -m debug -a "var=ansible_eth1.ipv4.address"

帮助

ansible-doc -l            列出所有的模块
ansible-doc service      查看指定模块用法

问题:

[root@m01 ~]# ansible-doc -l
[DEPRECATION WARNING]: docker is kept for backwards compatibility but usage is discouraged.
 The module documentation details page may explain more about this rationale..
This feature
 will be removed in a future release. Deprecation warnings can be disabled by setting
deprecation_warnings=False in ansible.cfg.
 [ERROR]: unable to parse /usr/lib/python2.6/site-packages/ansible/modules/extras/cloud/misc/rhevm.py**

ERROR! module rhevm has a documentation error formatting or is missing documentation

解决:

[root@m01 ~]# grep deprecation_warnings /etc/ansible/ansible.cfg
\#deprecation_warnings = True
[root@m01 ~]# sed -i.bak 's@#deprecation_warnings = True@deprecation_warnings = False@g' /etc/ansible/ansible.cfg
[root@m01 ~]# grep deprecation_warnings /etc/ansible/ansible.cfg
deprecation_warnings = False
mv /usr/lib/python2.6/site-packages/ansible/modules/extras/cloud/misc/rhevm.py /tmp/

yum模块

https://docs.ansible.com/ansible/latest/modules/yum_module.html

ansible oldboy -m yum -a "name=keepalived state=installed"
- name: install the latest version of Apache
  yum:
    name: httpd
    state: latest

- name: ensure a list of packages installed
  yum:
    name: "{{ packages }}"
  vars:
    packages:
    - httpd
    - httpd-tools

- name: remove the Apache package
  yum:
    name: httpd
    state: absent
- name: install the latest version of Apache from the testing repo
  yum:
    name: httpd
    enablerepo: testing
    state: present

yum_repository模块

- name: Add repository
  yum_repository:
    name: epel
    description: EPEL YUM repo
    baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/

- name: Add multiple repositories into the same file (1/2)
  yum_repository:
    name: epel
    description: EPEL YUM repo
    file: external_repos
    baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
    gpgcheck: no

- name: Add multiple repositories into the same file (2/2)
  yum_repository:
    name: rpmforge
    description: RPMforge YUM repo
    file: external_repos
    baseurl: http://apt.sw.be/redhat/el7/en/$basearch/rpmforge
    mirrorlist: http://mirrorlist.repoforge.org/el7/mirrors-rpmforge
    enabled: no

# Handler showing how to clean yum metadata cache
- name: yum-clean-metadata
  command: yum clean metadata
  args:
    warn: no

# Example removing a repository and cleaning up metadata cache
- name: Remove repository (and clean up left-over metadata)
  yum_repository:
    name: epel
    state: absent
  notify: yum-clean-metadata


- name: Remove repository from a specific repo file
  yum_repository:
    name: epel
    file: external_repos
    state: absent

- name: Add Base Yum Repository
  yum_repository:
    name: base
    description: Base Aliyun Repository
    baseurl: http://mirrors.aliyun.com/centos/$releasever/os/$basearch/
    gpgcheck: yes
    gpgkey: http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

- name: Add nginx Yum Repository
  yum_repository:
    name: nginx-stable
    description: nginx-stable Repository
    baseurl: http://nginx.org/packages/centos/$releasever/$basearch/
    gpgcheck: yes
    gpgkey: https://nginx.org/keys/nginx_signing.key

service模块

https://docs.ansible.com/ansible/latest/modules/service_module.html

ansible oldboy -m service -a "name=crond state=started enabled=yes"
- name: Start service httpd, if not started
  service:
    name: httpd
    state: started

- name: Stop service httpd, if started
  service:
    name: httpd
    state: stopped

- name: Restart service httpd, in all cases
  service:
    name: httpd
    state: restarted

- name: Reload service httpd, in all cases
  service:
    name: httpd
    state: reloaded

- name: Enable service httpd, and not touch the state
  service:
    name: httpd
    enabled: yes

- name: Start service foo, based on running process /usr/bin/foo
  service:
    name: foo
    pattern: /usr/bin/foo
    state: started

- name: Restart network service for interface eth0
  service:
    name: network
    state: restarted
    args: eth0

systemd

https://docs.ansible.com/ansible/latest/modules/systemd_module.html#systemd-module

- name: Make sure a service is running
  systemd:
    state: started
    name: httpd

- name: stop service cron on debian, if running
  systemd:
    name: cron
    state: stopped

- name: restart service cron on centos, in all cases, also issue daemon-reload to pick up config changes
  systemd:
    state: restarted
    daemon_reload: yes
    name: crond

- name: reload service httpd, in all cases
  systemd:
    name: httpd
    state: reloaded

- name: enable service httpd and ensure it is not masked
  systemd:
    name: httpd
    enabled: yes
    masked: no

- name: enable a timer for dnf-automatic
  systemd:
    name: dnf-automatic.timer
    state: started
    enabled: yes

- name: just force systemd to reread configs (2.4 and above)
  systemd:
    daemon_reload: yes

- name: just force systemd to re-execute itself (2.8 and above)
  systemd:
    daemon_reexec: yes

user模块

https://docs.ansible.com/ansible/latest/modules/user_module.html#user-module

- name: Add the user 'johnd' with a specific uid and a primary group of 'admin'
  user:
    name: johnd
    comment: John Doe
    uid: 1040
    group: admin
    shell: /bin/bash
    groups: admins,developers
    create_home: no
    append: yes
- name: Remove the user 'johnd'
  user:
    name: johnd
    state: absent
    remove: yes

生成密码

ansible all -i localhost, -m debug -a "msg={{ 'mypassword' | password_hash('sha512', 'mysecretsalt') }}"
ansible all -i localhost, -m debug -a "msg={{ '123456' | password_hash('sha512', 'oldboy123') }}"

创建带密码的用户

ansible backup -m user -a 'name=Alex02 password="$6$oldgirl$kAUTXVC2z1agr1HlmpFe9abFhWKwJ1fNyg64F95U3rVumwQfqOuhV3YkyZU9.H79TChzIKn5epl5M18B199qV1"'

group模块

- name: Ensure group "somegroup" exists
  group:
    name: somegroup
    gid: 1000
    state: present

archive模块

https://docs.ansible.com/ansible/latest/modules/archive_module.html#archive-module

- name: Create a bz2 archive of a globbed path, while excluding a glob of dirnames
  archive:
    path:
    - /path/to/foo/*
    dest: /path/file.tar.bz2
    exclude_path:
    - /path/to/foo/ba*
    format: bz2
- name: Create a zip archive of /path/to/foo
  archive:
    path: /path/to/foo
    format: zip

unarchive模块

https://docs.ansible.com/ansible/latest/modules/unarchive_module.html#unarchive-module
默认解压方式 /usr/bin/gtar /usr/bin/unzip

- name: Unarchive a file that is already on the remote machine
  unarchive:
    src: /tmp/foo.zip
    dest: /usr/local/bin
    remote_src: yes

- name: Unarchive a file that needs to be downloaded (added in 2.0)
  unarchive:
    src: https://example.com/example.zip
    dest: /usr/local/bin
    remote_src: yes

selinux模块

- selinux:
    state: disabled

synchronize模块

# Synchronization of src on the control machine to dest on the remote hosts
- synchronize:
    src: some/relative/path
    dest: /some/absolute/path

# Synchronization using rsync protocol (push)
- synchronize:
    src: some/relative/path/
    dest: rsync://somehost.com/path/

# Synchronization using rsync protocol (pull)
- synchronize:
    mode: pull
    src: rsync://somehost.com/path/
    dest: /some/absolute/path/

authorized_key模块

1.修改配置文件

[root@m01 ~]# grep host_key_checking /etc/ansible/ansible.cfg -n
59:#host_key_checking = False
[root@m01 ~]# sed -i.ori '59s@#host_key_checking = False@host_key_checking = False@g' /etc/ansible/ansible.cfg
[root@m01 ~]# grep host_key_checking /etc/ansible/ansible.cfg -n
59:host_key_checking = False

2.分发公钥

ssh-keygen -t dsa -P "" -f /root/.ssh/id_dsa &> /dev/null
ansible oldboy -m authorized_key -a "user=oldboy key='{{ lookup('file', '/home/oldboy/.ssh/id_rsa.pub') }}'" -k
- name: Set authorized key took from file
  authorized_key:
    user: charlie
    state: present
    key: "{{ lookup('file', '/root/.ssh/id_rsa.pub') }}"
---
- hosts: nginx
  tasks:
  - name: Set authorized key took from file
    authorized_key:
      user: root
      state: present
      key: "{{ lookup('file', '/root/.ssh/id_rsa.pub') }}"

cat authkey.yml

---
- hosts:
  - nfs01
  - backup
  tasks: 
    - name: Set authorized key took from file
      authorized_key:
        user: root
        state: present
        key: "{{ lookup('file', '/root/.ssh/id_rsa.pub') }}"
...

debug

- debug:
    msg: "System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}"

mysql_db模块

添加或删除数据库
ansible及远程主机安装

yum install MySQL-python
yum install python2-PyMySQL

cat ~/.my.cnf

[mysql]
host=172.16.1.7
user=root
password=123456
- name: Create a new database with name 'bobdata'
  mysql_db:
    name: bobdata
    state: present

# Copy database dump file to remote host and restore it to database 'my_db'
- name: Copy database dump file
  copy:
    src: dump.sql.bz2
    dest: /tmp
- name: Restore database
  mysql_db:
    name: my_db
    state: import
    target: /tmp/dump.sql.bz2

- name: Dump all databases to hostname.sql
  mysql_db:
    state: dump
    name: all
    target: /tmp/{{ inventory_hostname }}.sql

- name: Import file.sql similar to mysql -u <username> -p <password> < hostname.sql
  mysql_db:
    state: import
    name: all
    target: /tmp/{{ inventory_hostname }}.sql

state

present
absent
dump
import

---
- hosts: 172.16.1.7
  tasks:
  - name: Create a new database with name 'bobdata'
    mysql_db:
      name: blog
      state: present

mysql_user模块

# Removes anonymous user account for localhost
- mysql_user:
    name: ''
    host: localhost
    state: absent

# Removes all anonymous user accounts
- mysql_user:
    name: ''
    host_all: yes
    state: absent

# Create database user with name 'bob' and password '12345' with all database privileges
- mysql_user:
    name: bob
    password: 12345
    priv: '*.*:ALL'
    state: present

# Create database user with name 'bob' and previously hashed mysql native password '*EE0D72C1085C46C5278932678FBE2C6A782821B4' with all database privileges
- mysql_user:
    name: bob
    password: '*EE0D72C1085C46C5278932678FBE2C6A782821B4'
    encrypted: yes
    priv: '*.*:ALL'
    state: present

# Creates database user 'bob' and password '12345' with all database privileges and 'WITH GRANT OPTION'
- mysql_user:
    name: bob
    password: 12345
    priv: '*.*:ALL,GRANT'
    state: present

# Modify user Bob to require SSL connections. Note that REQUIRESSL is a special privilege that should only apply to *.* by itself.
- mysql_user:
    name: bob
    append_privs: true
    priv: '*.*:REQUIRESSL'
    state: present

# Ensure no user named 'sally'@'localhost' exists, also passing in the auth credentials.
- mysql_user:
    login_user: root
    login_password: 123456
    name: sally
    state: absent

# Ensure no user named 'sally' exists at all
- mysql_user:
    name: sally
    host_all: yes
    state: absent

# Specify grants composed of more than one word
- mysql_user:
    name: replication
    password: 12345
    priv: "*.*:REPLICATION CLIENT"
    state: present

# Revoke all privileges for user 'bob' and password '12345'
- mysql_user:
    name: bob
    password: 12345
    priv: "*.*:USAGE"
    state: present

# Example privileges string format
# mydb.*:INSERT,UPDATE/anotherdb.*:SELECT/yetanotherdb.*:ALL

# Example using login_unix_socket to connect to server
- mysql_user:
    name: root
    password: abc123
    login_unix_socket: /var/run/mysqld/mysqld.sock

# Example of skipping binary logging while adding user 'bob'
- mysql_user:
    name: bob
    password: 12345
    priv: "*.*:USAGE"
    state: present
    sql_log_bin: no

# Example .my.cnf file for setting the root password
# [client]
# user=root
# password=n<_665{vS43y
---
- hosts: 172.16.1.7
  tasks:
  - name: create mysql user
    mysql_user:
      login_host: 172.16.1.7
      login_user: root
      login_password: 123456
      name: www
      host: '%'
      password: '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9'
      encrypted: yes
      priv: '*.*:ALL'
      state: present

cat ~/.my.cnf

[mysql]
host=172.16.1.7
user=root
password=123456
---
- hosts: 172.16.1.7
  tasks:
  - name: create mysql user
    mysql_user:
      name: wuxingge
      host: '%'
      password: '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9'
      encrypted: yes
      priv: '*.*:ALL'
      state: present

mysql_replication模块

# Stop mysql slave thread
- mysql_replication:
    mode: stopslave

# Get master binlog file name and binlog position
- mysql_replication:
    mode: getmaster

# Change master to master server 192.0.2.1 and use binary log 'mysql-bin.000009' with position 4578
- mysql_replication:
    mode: changemaster
    master_host: 192.0.2.1
    master_log_file: mysql-bin.000009
    master_log_pos: 4578

# Check slave status using port 3308
- mysql_replication:
    mode: getslave
    login_host: ansible.example.com
    login_port: 3308

mode

getslave
getmaster
changemaster
stopslave
startslave
resetslave
resetslaveall

ansible playbook(剧本)

https://docs.ansible.com/ansible/latest/user_guide/playbooks.html
关键字
https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.html

语法格式

ansible剧本格式    yaml语法格式
rsync配置文件格式   ini语法格式
sersync配置文件格式   xml语法格式
json语法格式

yaml语法

1.缩进

aaaa
  bbbb
    cccc

2.列表

项目:
  - 项目名1
  - 项目名2

3.冒号空格

key: value

剧本存放目录

[root@m01 ~]# cd /etc/ansible/
[root@m01 ansible]# ll
总用量 32
-rw-r--r-- 1 root root 18066 6月   2 05:49 ansible.cfg
-rw-r--r-- 1 root root    33 6月  30 10:14 hosts
drwxr-xr-x 2 root root  4096 6月   2 05:49 roles

一个标准的剧本写法

cat /etc/ansible/test.yml

---
- hosts: 10.0.0.31
  gather_facts: no
  tasks:
  - name: add user
    user:
      name: rsync
      shell: /sbin/nologin
      createhome: no
...
---
- hosts:
    - backup
    - nfs
  gather_facts: no
  tasks:
  - name: touch file
    file:
      path: /tmp/test01.txt
      state: touch
...
atlanta:
  hosts:
    host1:
    host2:
  vars:
    ntp_server: ntp.atlanta.example.com
    proxy: proxy.atlanta.example.c
---
- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  remote_user: root
  tasks:
  - name: ensure apache is at the latest version
    yum:
      name: httpd
      state: latest
  - name: write the apache config file
    template:
      src: /srv/httpd.j2
      dest: /etc/httpd.conf
    notify:
    - restart apache
  - name: ensure apache is running
    service:
      name: httpd
      state: started
  handlers:
    - name: restart apache
      service:
        name: httpd
        state: restarted
---
- hosts: webservers
  remote_user: root

  tasks:
  - name: ensure apache is at the latest version
    yum:
      name: httpd
      state: latest
  - name: write the apache config file
    template:
      src: /srv/httpd.j2
      dest: /etc/httpd.conf

- hosts: databases
  remote_user: root

  tasks:
  - name: ensure postgresql is at the latest version
    yum:
      name: postgresql
      state: latest
  - name: ensure that postgresql is started
    service:
      name: postgresql
      state: started

检查剧本语法

ansible-playbook --syntax-check /etc/ansible/ansible_playbook/test01.yaml

模拟执行

ansible-playbook /etc/ansible/show.yml -C

handler

web.yaml

- hosts: web
  tasks:
    - name: Install Httpd Server
      yum:
        name: httpd,httpd-tools
        state: installed
    - name: Config Httpd Server
      copy:
        src: ./httpd.conf
        dest: /etc/httpd/conf/httpd.conf
      notify: restart httpd
    - name: Start Httpd Server
      service:
        name: httpd
        state: started
        enabled: yes
  handlers:
    - name: restart httpd
      service:
        name: httpd
        state: restarted

部署rsync

---
- hosts: 10.0.0.31
  tasks:
  - name: add user
    user: name=rsync shell=/sbin/nologin createhome=no
  - name: install rsync
    yum: name=rsync state=latest
  - name: copy rsyncd.conf
    copy: src=/etc/rsyncd.conf dest=/etc/rsyncd.conf owner=root group=root mode=0644
  - name: rsync password
    copy: content='rsync_backup:123' dest=/etc/rsync.password owner=root group=root mode=0600
  - name: create /backup dir
    file: path=/backup state=directory owner=rsync group=rsync mode=0755
  - name: start rsync
    shell: /usr/bin/rsync --daemon
...
---
- hosts: 10.0.0.31
  tasks:
  - name: add user
    user:
      name: rsync
      shell: /sbin/nologin
      createhome: no
  - name: install rsync
    yum:
      name: rsync
      state: latest
  - name: copy rsyncd.conf
    copy:
      src: /etc/rsyncd.conf
      dest: /etc/rsyncd.conf
      owner: root
      group: root
      mode: 0644
  - name: rsync password
    copy:
      content: rsync_backup:123
      dest: /etc/rsync.password
      owner: root
      group: root
      mode: 0600
  - name: create /backup dir
    file:
      path: /backup
      state: directory
      owner: rsync
      group: rsync
      mode: 0755
  - name: start rsync
    shell: rsync --daemon
    args:
      chdir: /usr/bin/
...
---
- hosts: 10.0.0.31
  tasks:
  - name: add user
    user:
      name: rsync
      shell: /sbin/nologin
      createhome: no
  - name: install rsync
    yum:
      name: rsync
      state: latest
  - name: copy rsyncd.conf
    copy:
      src: /etc/rsyncd.conf
      dest: /etc/rsyncd.conf
      owner: root
      group: root
      mode: 0644
  - name: rsync password
    copy:
      content: rsync_backup:123
      dest: /etc/rsync.password
      owner: root
      group: root
      mode: 0600
  - name: create /backup dir
    file:
      path: /backup
      state: directory
      owner: rsync
      group: rsync
      mode: 0755
  - name: start rsync
    shell: /usr/bin/rsync --daemon
...

变量

https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html

在剧本文件中进行定义 vars

- hosts: webservers
  vars:
    http_port: 80

利用执行参数赋值变量 --extra-vars

ansible-playbook -e dir_info=/oldboy/ -e file_info=oldboy01.txt  test_var.yaml 

单独在一个文件中定义 /etc/ansible/hosts

[oldboy]
172.16.1.31
172.16.1.41
172.16.1.7
[oldboy:vars]
dir_info=/oldboy/
file_info=oldboy01.txt

采用注册方式定义变量 register

  - name: check rpc port
    shell: ss -lntup |grep 111
    register: check_port_info
  - name: output check info
    debug:
      msg: '{{ check_port_info.stdout_lines }}'

变量优先级

命令行 > 剧本 > 主机清单

foo:
  field1: one
  field2: two
foo['field1']
foo.field1

引用变量

{{ foo }}

cat var.yml

--- 
- hosts: oldboy
  remote_user: root
  vars:
    file_info: oldboy01.txt
    dir_info: /oldboy/
  tasks:
  - name: create dir
    file:
      path: '{{ dir_info }}'
      state: directory
  - name: copy file
    copy:
      src: '{{ dir_info }}{{ file_info }}'
      dest: '{{ dir_info }}'

变量优先级(由低到高)

  • role defaults
  • dynamic inventory variables
  • inventory variables
  • inventory group_vars
  • inventory host_vars
  • playbook group_vars
  • playbook host_vars
  • host facts
  • registered variables
  • set_facts
  • play variables
  • play vars_prompt
  • play vars_files
  • role variables and include variables
  • block variables
  • task variables
  • extra variables

tags

https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html
指定位置添加标签,可以指定标签执行剧本

- hosts: 172.16.1.7
  gather_facts: no
  tasks:
  - name: mount
    mount:
      src: "172.16.1.31:/data"
      path: "/mnt"
      fstype: nfs
      state: mounted
    tags: mount-nfs

只执行标签

ansible-playbook  -t mount-nfs nfs.yml 

不执行标签

ansible-playbook  --skip-tags "mount-nfs"   nfs.yml

忽略错误

ignore_errors: yes

notify和handler

notify

  tasks:
  - name: copy template file to remote host
    template:
      src: /etc/ansible/nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify:
      - restart nginx
      - test web page
    copy:
      src: nginx/index.html.j2
      dest: /usr/share/nginx/html/index.html
    notify:
      - restart nginx

handler

  handlers:
  - name: restart nginx
    service:
      name: nginx
      state: restarted
  - name: test web page
    shell: curl -I http://192.168.100.10/index.html | grep 200 || /bin/false

nfs.yml

---
- hosts: all
  gather_facts: no
  tasks:
  - name: install nfs
    yum:
      name:
        - nfs-utils
        - rpcbind
      state: installed

- hosts: 172.16.1.31
  gather_facts: no
  tasks:
  - name: push exports
    copy:
      src: export.temp
      dest: /etc/exports
    notify: restart nfs
  - name: create data dir
    file:
      path: /data
      owner: nfsnobody
      group: nfsnobody
      state: directory
  - name: start nfs
    service:
      name: rpcbind
      name: nfs
      state: started
      enabled: yes
    tags: test-nfs-start
  - name: check rpc port
    shell: ss -lntup |grep 111
    register: check_port_info
  - name: output check info
    debug:
      msg: '{{ check_port_info.stdout_lines }}'
    tags: test_port
  handlers:
    - name: restart nfs
      service:
        name: nfs
        state: restarted
- hosts: 172.16.1.7
  gather_facts: no
  tasks:
  - name: mount
    mount:
      src: "172.16.1.31:/data"
      path: "/mnt"
      fstype: nfs
      state: mounted
    tags: mount-nfs
...

cat rsync.yml

---
- hosts: all
  remote_user: root
  tasks:
  - name: install rsync
    yum:
      name: rsync
      state: installed
    tags: install rsync

- hosts: 172.16.1.41
  tasks:
  - name: copy rsyncd.conf
    copy:
      src: '{{ item.src }}'
      dest: '{{ item.dest }}'
      mode: '{{ item.mode }}'
    with_items:
      - { src: 'rsyncd.conf.temp',dest: /etc/rsyncd.conf,mode: '0644' }
      - { src: 'rsync.password.temp',dest: /etc/rsync.password,mode: '0600' }
    notify: restart rsync
  - name: create user
    user:
      name: rsync
      shell: /sbin/nologin
      create_home: no
  - name: create backup directory
    file:
      path: '{{ item }}'
      owner: rsync
      group: rsync
      state: directory
    with_items:
      - /backup
      - /wuxing
  - name: start rsync
    service:
      name: rsyncd
      state: started
  handlers:
    - name: restart rsync
      service:
        name: rsyncd
        state: restarted

- hosts:
    - 172.16.1.31
    - 172.16.1.7
  tasks:
  - name: create pass file
    copy:
      content: "123"
      dest: /etc/rsync.password
      mode: 0600

条件

https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html

when

tasks:
  - name: "shut down CentOS 6 systems"
    command: /sbin/shutdown -t now
    when:
      - ansible_facts['distribution'] == "CentOS"
      - ansible_facts['distribution_major_version'] == "6"
tasks:
  - command: /bin/false
    register: result
    ignore_errors: True

  - command: /bin/something
    when: result is failed

  # In older versions of ansible use ``success``, now both are valid but succeeded uses the correct tense.
  - command: /bin/something_else
    when: result is succeeded

  - command: /bin/still/something_else
    when: result is skipped

布尔

vars:
  epic: true
tasks:
  - shell: echo "This certainly is epic!"
    when: epic

tasks:
  - shell: echo "This certainly isn't epic!"
    when: not epic
tasks:
  - shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
    when: foo is defined

    - fail: msg="Bailing out. this play requires 'bar'"
      when: bar is undefined

cat judge.yml

---
- hosts: oldboy
  remote_user: root
  tasks:
  - name: push file01 info
    copy:
      src: /oldboy/oldboy01
      dest: /tmp/
    when: ansible_hostname == 'web01'

cat judge.yml

---
- hosts: oldboy
  remote_user: root
  tasks:
  - name: push file01 info
    copy:
      src: /oldboy/oldboy01
      dest: /tmp/
    when: ansible_facts.eth1.ipv4.address == '172.16.1.7'
---
- hosts: oldboy
  remote_user: root
  tasks:
  - name: push file01 info
    copy:
      src: /oldboy/oldboy01
      dest: /tmp/
    when: ansible_facts["eth1"]["ipv4"]["address"] == '172.16.1.7'

and

when: (ansible_hostname == "web01") and (ansible_distribution == "CentOS")

or

not

循环和条件

tasks:
    - command: echo {{ item }}
      loop: [ 0, 2, 4, 6, 8, 10 ]
      when: item > 5

循环

https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html

loop

- name: add several users
  user:
    name: "{{ item }}"
    state: present
    groups: "wheel"
  loop:
     - testuser1
     - testuser2
  loop: "{{ somelist }}"
- name: optimal yum
  yum:
    name: "{{list_of_packages}}"
    state: present

- name: non optimal yum, not only slower but might cause issues with interdependencies
  yum:
    name: "{{item}}"
    state: present
  loop: "{{list_of_packages}}"
- name: add several users
  user:
    name: "{{ item.name }}"
    state: present
    groups: "{{ item.groups }}"
  loop:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'root' }
- name: create a tag dictionary of non-empty tags
  set_fact:
    tags_dict: "{{ (tags_dict|default({}))|combine({item.key: item.value}) }}"
  loop: "{{ tags|dict2items }}"
  vars:
    tags:
      Environment: dev
      Application: payment
      Another: "{{ doesnotexist|default() }}"
  when: item.value != ""
- shell: "echo {{ item }}"
  loop:
    - "one"
    - "two"
  register: echo

Loop Control

# main.yml
- include_tasks: inner.yml
  loop:
    - 1
    - 2
    - 3
  loop_control:
    loop_var: outer_item

# inner.yml
- debug:
    msg: "outer item={{ outer_item }} inner item={{ item }}"
  loop:
    - a
    - b
    - c

直到循环 until

- shell: /usr/bin/foo
  register: result
  until: result.stdout.find("all systems go") != -1
  retries: 5
  delay: 10

with_list

- name: with_list
  debug:
    msg: "{{ item }}"
  with_list:
    - one
    - two

- name: with_list -> loop
  debug:
    msg: "{{ item }}"
  loop:
    - one
    - two

with_items

- name: with_items
  debug:
    msg: "{{ item }}"
  with_items: "{{ items }}"

- name: with_items -> loop
  debug:
    msg: "{{ item }}"
  loop: "{{ items|flatten(levels=1) }}"

  - name: create backup directory
    file:
      path: '{{ item }}'
      owner: rsync
      group: rsync
      state: directory
    with_items:
      - /backup
      - /wuxing

cat with_items.yml

---
- hosts: oldboy
  tasks:
  - name: create user
    user:
      name: '{{ item.name }}'
      uid: '{{ item.uid }}'
      shell: '{{ item.shell }}'
    with_items:
      - { name: "alex01", uid: "3001", shell: "/sbin/nologin" }
      - { name: "alex02", uid: "3002", shell: "/sbin/nologin" }
      - { name: "alex03", uid: "3003", shell: "/sbin/nologin" }

with_indexed_items

- name: with_indexed_items
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  with_indexed_items: "{{ items }}"

- name: with_indexed_items -> loop
  debug:
    msg: "{{ index }} - {{ item }}"
  loop: "{{ items|flatten(levels=1) }}"
  loop_control:
    index_var: index

with_flattened

- name: with_flattened
  debug:
    msg: "{{ item }}"
  with_flattened: "{{ items }}"

- name: with_flattened -> loop
  debug:
    msg: "{{ item }}"
  loop: "{{ items|flatten }}"

with_together

- name: with_together
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  with_together:
    - "{{ list_one }}"
    - "{{ list_two }}"

- name: with_together -> loop
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  loop: "{{ list_one|zip(list_two)|list }}"

with_dict

- name: with_dict
  debug:
    msg: "{{ item.key }} - {{ item.value }}"
  with_dict: "{{ dictionary }}"

- name: with_dict -> loop (option 1)
  debug:
    msg: "{{ item.key }} - {{ item.value }}"
  loop: "{{ dictionary|dict2items }}"

- name: with_dict -> loop (option 2)
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  loop: "{{ dictionary|dictsort }}"

with_sequence

- name: with_sequence
  debug:
    msg: "{{ item }}"
  with_sequence: start=0 end=4 stride=2 format=testuser%02x

- name: with_sequence -> loop
  debug:
    msg: "{{ 'testuser%02x' | format(item) }}"
  # range is exclusive of the end point
  loop: "{{ range(0, 4 + 1, 2)|list }}"

with_subelements

- name: with_subelements
  debug:
    msg: "{{ item.0.name }} - {{ item.1 }}"
  with_subelements:
    - "{{ users }}"
    - mysql.hosts

- name: with_subelements -> loop
  debug:
    msg: "{{ item.0.name }} - {{ item.1 }}"
  loop: "{{ users|subelements('mysql.hosts') }}"

with_nested/with_cartesian

- name: with_nested
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  with_nested:
    - "{{ list_one }}"
    - "{{ list_two }}"

- name: with_nested -> loop
  debug:
    msg: "{{ item.0 }} - {{ item.1 }}"
  loop: "{{ list_one|product(list_two)|list }}"

with_random_choice

- name: with_random_choice
  debug:
    msg: "{{ item }}"
  with_random_choice: "{{ my_list }}"

- name: with_random_choice -> loop (No loop is needed here)
  debug:
    msg: "{{ my_list|random }}"
  tags: random

剧本整合

指令参数1 roles

- hosts:
  tasks:
    - include_tasks: nfs.yml
    - include_tasks: rsync.yml

指令参数2

- import_playbook: nfs.yml
- import_playbook: rsync.yml

roles/x/tasks/main.yml

---
- include: create_dir.yml
- include: static_git_pull.yml
- include: git_checkout.yml

roles

https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html

image.png

  • group_vars目录下的文件定义Roles中调用的变量
  • 文件名为all的文件定义的变量针对所有Roles生效

角色目录结构

site.yml
webservers.yml
fooservers.yml
roles/
   common/
     tasks/
     handlers/
     files/
     templates/
     vars/
     defaults/
     meta/
   webservers/
     tasks/
     defaults/
     meta/

角色期望文件位于某些目录名称中。角色必须至少包含其中一个目录,但是排除任何未使用的目录是完全正确的。在使用时,每个目录必须包含一个main.yml文件,其中包含相关内容
tasks - 包含角色要执行的主要任务列表
handlers - 包含处理程序,可以由此角色使用,甚至可以在此角色之外的任何位置使用
defaults - 角色的默认变量(有关更多信息,请参阅使用变量)
vars - 角色的其他变量(有关更多信息,请参阅使用变量)
files - 包含可以通过此角色部署的文件
templates - 包含可以通过此角色部署的模板
meta - 为此角色定义一些元数据
其他YAML文件可能包含在某些目录中。例如,通常的做法是从tasks/main.yml文件中包含特定于平台的任务

#roles/example/tasks/main.yml
- name: added in 2.4, previously you used 'include'
  import_tasks: redhat.yml
  when: ansible_facts['os_family']|lower == 'redhat'
- import_tasks: debian.yml
  when: ansible_facts['os_family']|lower == 'debian'
# roles/example/tasks/redhat.yml
- yum:
    name: "httpd"
    state: present
# roles/example/tasks/debian.yml
- apt:
    name: "apache2"
    state: present
---
- hosts: webservers
  roles:
    - common
    - webservers
---
- hosts: webservers
  roles:
    - role: '/path/to/my/roles/common'
---
- hosts: 172.16.1.31
  roles:
    - { role: nfs }
- hosts: 172.16.1.41
  roles:
    - { role: rsync }
---
- hosts: webservers
  roles:
    - common
    - role: foo_app_instance
      vars:
        dir: '/opt/a'
        app_port: 5000
    - role: foo_app_instance
      vars:
        dir: '/opt/b'
        app_port: 5001
---
- hosts: webservers
  tasks:
  - include_role:
       name: foo_app_instance
    vars:
      dir: '/opt/a'
      app_port: 5000
  ...
---
- hosts: webservers
  tasks:
  - include_role:
      name: some_role
    when: "ansible_facts['os_family'] == 'RedHat'"
---
- hosts: webservers
  roles:
    - role: bar
      tags: ["foo"]
    # using YAML shorthand, this is equivalent to the above
    - { role: foo, tags: ["bar", "baz"] }
---
- hosts: webservers
  tasks:
  - import_role:
      name: foo
    tags:
    - bar
    - baz
- hosts: webservers
  tasks:
  - include_role:
      name: bar
    tags:
     - foo
---
- hosts: webservers
  roles:
  - role: foo
    vars:
         message: "first"
  - { role: foo, vars: { message: "second" } }

角色中使用模块与插件

roles/
   my_custom_modules/
       library/
          module1
          module2
- hosts: webservers
  roles:
    - my_custom_modules
    - some_other_role_using_my_custom_modules
    - yet_another_role_using_my_custom_modules

roles剧本实例

创建目录

[root@m01 roles]# mkdir {nfs,rsync}/{tasks,files,templates,meta,handlers,vars,defaults}
[root@m01 roles]# tree
.
├── nfs
│   ├── files
│   ├── handlers
│   ├── meta
│   ├── tasks
│   ├── templates
│   └── vars
└── rsync
    ├── files
    ├── handlers
    ├── meta
    ├── tasks
    ├── templates
    └── vars
[root@m01 roles]# pwd
/etc/ansible/roles
[root@m01 roles]# ll
total 8
drwxr-xr-x 8 root root 89 Mar  8 11:32 nfs
drwxr-xr-x 8 root root 89 Mar  8 11:32 rsync
-rw-r--r-- 1 root root 35 Mar  8 12:13 site.yml
[root@m01 roles]# tree nfs
nfs
├── files
│   └── exports
├── handlers
│   └── main.yml
├── meta
├── tasks
│   └── main.yml
├── templates
└── vars
    └── main.yml

vars(变量)

[root@m01 roles]# cat nfs/vars/main.yml

data_dir: /data

vars/main.yml中的变量是role变量,优先级比较高,放置一些不想被覆盖的变量,所以变量在命名的时候一般都加入role的名字作为前缀,防止不小心被Playbook中定义的变量覆盖

defaults(默认变量)

defaults/main.yml中的变量是默认变量。优先级在所有的变量中是最低的,用于放置一些需要被覆盖的变量

tasks(任务)

[root@m01 roles]# cat nfs/tasks/main.yml

- name: install nfs
  yum:
    name:
      - nfs-utils
      - rpcbind
    state: installed

- name: push exports
  copy:
    src: exports
    dest: /etc/exports
  when: ansible_facts.eth1.ipv4.address == '172.16.1.31'
  notify: restart nfs

- name: create data dir
  file:
    path: '{{ data_dir }}'
    owner: nfsnobody
    group: nfsnobody
    state: directory
  when: ansible_facts.eth1.ipv4.address == '172.16.1.31'

- name: start nfs
  service:
    name: rpcbind
    name: nfs
    state: started
    enabled: yes
  when: ansible_facts.eth1.ipv4.address == '172.16.1.31'
  tags: test-nfs-start

- name: check rpc port
  shell: ss -lntup |grep 111
  when: ansible_facts.eth1.ipv4.address == '172.16.1.31'
  register: check_port_info

- name: output check info
  debug:
    msg: '{{ check_port_info.stdout_lines }}'
  when: ansible_facts.eth1.ipv4.address == '172.16.1.31'
  tags: test_port

- name: mount
  mount:
    src: "172.16.1.31:/data"
    path: "/mnt"
    fstype: nfs
    state: mounted
  when: ansible_facts.eth1.ipv4.address == '172.16.1.41' or ansible_facts.eth1.ipv4.address == '172.16.1.7'
  tags: mount-nfs

handlers(触发任务)

[root@m01 roles]# cat nfs/handlers/main.yml

- name: restart nfs
  service:
    name: nfs
    state: restarted

files(文件)

templates(模板文件)

cat roles/nginx/templates/default.conf.j2

server {
    listen       {{ nginx_port }};
    server_name  localhost;
    location / {
        root   {{ html_root }};
        index  index.html index.htm;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

cat roles/nginx/templates/nginx.conf.j2

user  nginx;
worker_processes  {{ ansible_processor_vcpus }};

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

meta(角色依赖)

roles/myapp/meta/main.yml

---
dependencies:
  - role: common
    vars:
      some_parameter: 3
  - role: apache
    vars:
      apache_port: 80
  - role: postgres
    vars:
      dbname: blarg
      other_parameter: 12
---
dependencies:
- { role: common, name: "NameInDb"}

roles/nginx/meta/main.yml

---
dependencies:
  - role: common

roles/common/tasks/main.yml

- name: Add nginx Yum Repository
  yum_repository:
    name: nginx-stable
    description: nginx-stable Repository
    baseurl: http://nginx.org/packages/centos/$releasever/$basearch/
    gpgcheck: yes
    gpgkey: https://nginx.org/keys/nginx_signing.key

角色调用

cat site.yml

- hosts: oldboy
  roles:
    - nfs
---
- hosts: webservers
  roles:
  - role: foo
    vars:
         message: "first"
  - { role: foo, vars: { message: "second" } }

Templating (Jinja2)

https://docs.ansible.com/ansible/latest/user_guide/playbooks_templating.html
[root@m01 roles]# cat rsync/templates/rsyncd.conf.j2

uid = rsync
gid = rsync
port = {{ port }}
fake super = yes
use chroot = no
max connections = 200
timeout = 600
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd.log
ignore errors
read only = false
list = true
hosts allow = 172.16.1.0/24
auth users = rsync_backup
secrets file = /etc/rsync.password
[backup]
path = /backup
[wuxing]
path = /wuxing

[root@m01 roles]# cat rsync/vars/main.yml

port: 874

[root@m01 roles]# cat rsync/tasks/main.yml

- name: install rsync
  yum:
    name: rsync
    state: installed

- name: push conf
  template:
    src: rsyncd.conf.j2
    dest: /etc/rsyncd.conf

[root@m01 roles]# cat site.yml

- hosts: oldboy
  roles:
    - nfs
- hosts: 172.16.1.41
  roles:
    - { role: rsync }

完整的ansible配置目录

[root@m01 ansible]# pwd
/etc/ansible
[root@m01 ansible]# ll
total 24
-rw-r--r-- 1 root root 20318 Mar  6 12:32 ansible.cfg
-rw-r--r-- 1 root root  1015 Jun 17 16:01 hosts
drwxr-xr-x 2 root root    32 Jun 17 18:33 inventory
drwxr-xr-x 3 root root    41 Jun 17 18:59 playbooks
drwxr-xr-x 6 root root    73 Jun 18 09:29 roles
[root@m01 ansible]# tree -L 2 /etc/ansible/
/etc/ansible/
├── ansible.cfg
├── hosts
├── inventory
│   ├── part1
│   └── part2
├── playbooks
│   ├── group_vars
│   └── nginx.yml
└── roles
    ├── common
    ├── nfs
    ├── nginx
    ├── rsync
    └── site.yml

jinja

http://jinja.pocoo.org/

步骤1:编排目录如下

nginxconf.yml
roles/nginxconf/
├── tasks
│   ├── file.yml
│   └── main.yml
├── templates
│   └── nginx.conf.j2
└── vars
    └── main.yml

步骤2:编辑nginxconf role的tasks调度文件roles/nginxconf/tasks/{file.yml,main.yml}

编辑file.yml,定义nginxconf role的一个功能集(一个文件一个功能集)。

---
- name: nginx.conf.j2 tempalte transfer example
  template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf.template

编辑main.yml,定义任务功能集合、nginxconf role功能集入口

---
- include: file.yml

步骤3:这是最重要的一步,定义nginxconf role的模板文件roles/nginxconf/templates/nginx.conf.j2,该模板的灵活性将直接影响Ansible-playbook的代码行数和整体Playbook的灵活性健壮性,该模板文件将被替换变量后生成最终的Nginx配置文件

{% if nginx_use_proxy %}
{% for proxy in nginx_proxies %}
upstream {{ proxy.name }} {
    # server 127.0.0.1:{{ proxy.port }};
    server {{ ansible_eth0.ipv4.address }}:{{ proxy.port }};
}
{% endfor %}
{% endif %}
server {
    listen 80;
    server_name {{ nginx_server_name }};
    access_log off;
    error_log /dev/null crit;
    rewrite ^ https:// $server_name$request_uri? permanent;
}
server {
    listen 443 ssl;
    server_name {{ nginx_server_name }};
    ssl_certificate /etc/nginx/ssl/{{ nginx_ssl_cert_name }};
    ssl_certificate_key /etc/nginx/ssl/{{ nginx_ssl_cert_key }};

    root {{ nginx_web_root }};
    index index.html index.html;

{% if nginx_use_auth %}
    auth_basic            "Restricted";
    auth_basic_user_file  /etc/nginx/{{project_name}}.htpasswd;
{% endif %}

{% if nginx_use_proxy %}
{% for proxy in nginx_proxies %}

    location {{ proxy.location }} {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto http;
        proxy_set_header X-Url-Scheme $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_redirect off;
        proxy_pass http://{{ proxy.name }};
        break;
    }
{% endfor %}
{% endif %}

{% if nginx_server_static %}
    location / {
        try_files $uri $uri/ =404;
    }

{% endif %}

}

步骤4:编辑nginxconf role的变量文件roles/nginxconf/vars/main.yml

---
nginx_server_name: www.wuxingge.com
nginx_web_root: /opt/wuxingge/
nginx_proxies:
  - name: suspicious
    location: /
    port: 2368
  - name: suspicious-api
    location: /api
    port: 3000

该变量文件需要关注的是nginx_proxies定义的变量组,其下的变量列表通过for循环读取后可以通过“. ”来引用,即如下proxy.name这样的引用方式

{% for proxy in nginx_proxies %}
upstream {{ proxy.name }} {
    # server 127.0.0.1:{{ proxy.port }};

步骤5:编辑总调度文件nginxconf.yml

- name: Nginx Proxy Server's Conf Dynamic Create
    hosts: "192.168.37.130:192.168.37.158"
    vars:
      nginx_use_proxy: true
      nginx_ssl_cert_name: ifa.crt
      nginx_ssl_cert_key: ifa.key
      nginx_use_auth: true
      project_name: suspicious
      nginx_server_static: true
    gather_facts: true
    roles:
      - { role: nginxconf }

- name: Nginx WebServer's Conf Dynamic Create
    hosts: 192.168.37.159
    vars:
      nginx_use_proxy: false
      nginx_ssl_cert_name: ifa.crt
      nginx_ssl_cert_key: ifa.key
      nginx_use_auth: false
      project_name: suspicious
      nginx_server_static: false
    gather_facts: no
    roles:
      - { role: nginxconf }

Galaxy

Tower

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容