Ansible

Ansible安装配置和介绍

安装

安装Python3

1
2
3
4
yum install -y openssl-devel
tar zxvf Python-3.5.5.tgz
./configure --prefix=/usr/local/
make && make install

安装ansible

1
$ pip3 install ansible

Ansible依赖:

1
2
$ sudo easy_install pip
$ sudo pip install -r ./requirements.txt

HOSTS

组和节点

默认在/etc/ansible/hosts新建,亦可自定义~/.ansible.cfg
[dpdata]为组名,dpdata1为节点别名,ansible_host后为IP,ansible_ssh_port后为ssh端口。
可以定义用户名密码,不过推荐ssh免密

1
2
3
4
5
6
7
8
[dpdata]
dpdata1 ansible_host=192.168.21.138
dpdata2 ansible_host=192.168.21.141
dpdata3:1222 ansible_host=192.168.21.143
dp_data4 ansible_host=192.168.21.145
dp_data6 ansible_host=192.168.21.149
dp_data7 ansible_host=192.168.21.151 ansible_ssh_user=root ansible_ssh_pass=weidai@123
dp_data8 ansible_host=192.168.21.152 ansible_ssh_port=1222

清单/inventory

Ansible 使用清单文件来了解要使用的服务器,以及如何将它们分组以并行执行任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[all:children]
webs
db

[all:vars]
ansible_user=vagrant
ansible_ssh_pass=vagrant

[webs]
web1 ansible_host=10.1.1.11
web2 ansible_host=10.1.1.12

[db]
db-[a:f].example.com
db[01:50].example.com

  • [all: children]定义由几个组组成的组(all
  • [all: vars]定义属于组all的变量
  • [webs]定义一个由具体节点组成的组,就像[db]一样

$ ansible -i inventory all -m ping

  • -i 指定清单文件
  • all 指定要操作的服务器或服务器组
  • -m 指定一个ansible模块,在这种情况下为ping

管理节点密钥分发

  1. 生成:ssh-keygen -t rsa
  2. 分发:
    • ssh-copy-id root@$HOST
    • 若有多台节点且密码一致,可以通过ansible的authorized_key模块来实现批量发送:
      ansible dpdata --ask-pass -u 用户名 -m authorized_key -a "user=用户名 key='$(cat ~/.ssh/id_rsa.pub)'"

使用

格式

ansible [group] -m $module -a "args"
如:ansible dpdata -m ping
-m 后接模块名
-a 指传给模块的参数
[group] 要执行的组

  • all/* inventory中所有机器
  • 192.168.1.1/a.example.com 具体IP或者主机名
  • *.example.com 通配符主机名
  • one*.com:dbservers 通配符和groups混用
  • webservers[0-22] 组里面部分机器
  • webserver 具体组名
  • webservers:!phoenix 隶属 webservers 组但同时不在 phoenix组
  • webservers:&staging 同时隶属于 webservers 和 staging 组
  • webservers:dbservers:&staging:!phoenix webservers和dbservers两个组中隶属于staging组并且不属于phoenix组的机器

ansible [group] -a "ls /tmp"
-a 后接具体shell命令

参数:

1
2
3
4
5
6
7
8
9
10
11
-v,–verbose								详细模式,如果命令执行成功,输出详细的结果(-vv –vvv -vvvv)
-i PATH,–inventory=PATH 指定host文件的路径,默认是在/etc/ansible/hosts(生产环境经常用到)
-f NUM,–forks=NU NUM是指定一个整数,默认是5,指定fork开启同步进程的个数。
-m NAME,–module-name=NAME 指定使用的module名称,默认是command
-m DIRECTORY,–module-path=DIRECTORY 指定module的目录来加载module,默认是/usr/share/ansible,
-a,MODULE_ARGS 指定module模块的参数
-k,-ask-pass 提示输入ssh的密码,而不是使用基于ssh的密钥认证
-sudo 指定使用sudo获得root权限(生产环境经常用到)
-K,-ask-sudo-pass 提示输入sudo密码,与–sudo一起使用 (生产环境经常用到)
-u USERNAME,-user=USERNAME 指定移动端的执行用户
-C,-check 测试此命令执行会改变什么内容,不会真正的去执行

Man/Help

ansible-doc -list 查看当前所有模块
ansible-doc $module 查看具体某个模块用法

常用模块

  1. 命令类模块:

    • command模块:
      ansible all -m command -a "pwd" -f 4
      -m command是可以省略的,ansible 默认模块就是command
      -f 4指定同步进程数
      无法支持$HOME < > | &等操作符
      操作符:chdir creates removes stdin.

      • chdir: 切换至此目录
      • creates: 创建文件,已存在则不重复创建
      • removes: 移除文件
      • stdin: 将命令的stdin直接设置为指定的值
    • shell模块:

      • chdir: 同cd
      • creates: 同touch,若已存在则不创建
      • executable: 可执行文件的绝对路径,同chmod +x
      • free_form: 必选项,运行自由格式的命令
      • removes: 文件不存在则不运行
      • stdin(>2.4): 将命令的stdin直接设置为指定的值。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        \- 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 bash does)
        shell: cat < /tmp/*txt
        args:
        executable: /bin/bash

        \- name: Run a command using a templated variable (always use quote filter to avoid injection)
        shell: cat {{ myfile|quote }}

        # You can use shell to run other executables to perform actions inline
        \- name: Run expect to wait for a successful PXE boot via out-of-band CIMC
        shell: |
        set timeout 300
        spawn ssh admin@{{ cimc_host }}

        expect "password:"
        send "{{ cimc_password }}\n"

        expect "\n{{ cimc_name }}"
        send "connect host\n"

        expect "pxeboot.n12"
        send "\n"

        exit 0
        args:
        executable: /usr/bin/expect
        delegate_to: localhost
    • script模块:传输本地脚本至目标节点并运行

      • chdir(>2.4): 在运行脚本前先cd到目录中
      • creates(>1.5): 当文件已存在则不创建
      • removes(>1.5): 当文件不存在则不删除
      • free_form: 必选项,自由使用命令

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        # Example from Ansible Playbooks
        \- script: /some/local/script.sh --some-arguments 1234

        # Run a script that creates a file, but only if the file is not yet created
        \- script: /some/local/create_file.sh --some-arguments 1234
        args:
        creates: /the/created/file.txt

        # Run a script that removes a file, but only if the file is not yet removed
        \- script: /some/local/remove_file.sh --some-arguments 1234
        args:
        removes: /the/removed/file.txt
  2. ping模块:
    ansible all -m ping -f 1
    成功返回pong,失败返回data:crash

  3. 文件传输类模块:

    • copy模块:
      ansible all -m copy -a "src=/etc/hosts dest=/tmp/hosts owner=root group=root mode:0644"

      1
      2
      3
      4
      5
      6
      7
      \- name: example for copy module
      copy:
      src: /path/to/src/file
      dest: /path/to/dest/file
      owner: root
      group: root
      mode: 0644(u=rw,g=r,o=r)(u+rw,g-wx,o-rwx)
      • attributes(>2.3): chattr命令
      • backup: 创建含时间戳信息的备份文件
      • checksum: 检验传输文件SHA1校验和
      • src: 拷贝源路径
      • dest: 拷贝目标路径,属性须和源属性一致(目录对目录,文件对文件);若目标目录不存在且dest以/结尾或者src是目录,则创建dest目录;如果src和dest都是文件,此时若dest目录不存在则失败。
      • DIRECTORY_MODE(>1.5): 目录复制模式,进行递归复制
      • follow(>1.8): 遵循目标文件系统链接
      • force: 默认开启,替换dest已存在同名文件;关闭则不替换
      • owner: 设定拥有者
      • group: 设定组
      • local_follow: 默认开启,遵循src文件系统链接
      • mode: chmod,例如0644,>1.8支持符号模式(u+rwxu=rw,g=r,o=r)
      • remote_src(>2.0): 开启后目标主机作为src;目前remote_src不支持递归拷贝
    • file模块:
      file模块允许更改文件的所有权和权限
      ansible all -m file -a "dest=/srv/foo/a.txt mode=600 owner=root group=root"
      也可创建目录,类似mkdir -p
      ansible all -m file -a "dest=/path/to/c mode=755 owner=root group=root state=directory"
      删除目录(递归)和文件
      ansible all -m file -a "dest=/path/to/c state=absent"

      attributes(>2.3): chattr命令
      follow(>1.8): 遵循文件系统链接,在2.5版本后默认开启,之前默认关闭
      force(>1.8): 两种情况下会创建符号链接:目标文件不存在但稍后会有;目标文件已存在且是个文件
      group: 组,同chown
      owner: 拥有者,同chown
      mode: 类似copy模块中的mode
      path: 文件路径
      recurse: 递归设置文件属性
      src: 链接的文件路径(仅适用于state=link/hard;接受绝对和相对路径)
      state: absent/directory/file/hard/link/touchabsent会递归删除目录,若目录不存在语句不执行。touch会创建空文件,若文件存在则会更新权限和修改时间

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      \- file
      path: /etc/foo.conf
      owner: root
      group: root
      mode: 0644
      \- file
      src: /file/to/link
      dest: /path/to/symlink
      owner: root
      group: root
      state: link
      \- file
      src: '/tmp/{{ item.src }}'
      dest: '{{ item.dest }}'
      state: link
      with_items:
      \- { src: 'x', dest: 'y' }
      \- { src: 'z', dest: 'k' }
    • fetch模块:从远程节点获取文件
      该模块和copy模块相反,它用于获取远程节点的文件并存储在本地由hostname组织的文件树中。

      • src: 必选项,远端文件,目前不能为目录名
        dest: 必选项,若dest=/backup,远端节点host.example.com的src file为/etc/profile,则文件被保存成/backup/host.example.com/etc/profile
        fail_on_missing: 默认值yes,如果远端节点文件无法读取则任务失败
        validate_checksum(>1.4): 默认值yes,传输前后MD5校验
      • flat: 是否覆盖

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        # Store file into /tmp/fetched/host.example.com/tmp/somefile
        \- fetch:
        src: /tmp/somefile
        dest: /tmp/fetched

        # Specifying a path directly
        \- fetch:
        src: /tmp/somefile
        dest: /tmp/prefix-{{ inventory_hostname }}
        flat: yes

        # Specifying a destination path
        \- fetch:
        src: /tmp/uniquefile
        dest: /tmp/special/
        flat: yes

        # Storing in a path relative to the playbook
        \- fetch:
        src: /tmp/uniquefile
        dest: special/prefix-{{ inventory_hostname }}
        flat: yes
  4. 包管理类模块:

    • yum模块:
      确保已安装软件包,但不要进行更新:
      ansible all -m yum -a "name=acme state=present"
      确保软件包已安装到特定版本:
      ansible webservers -m yum -a“name = acme-1.5 state = present”
      确保软件包是最新版本:
      ansible webservers -m yum -a“name = acme state = latest”
      确保软件包未安装:
      ansible webservers -m yum -a“name = acme state = absent”

      yum:使用yum软件包管理器管理软件包

      • conf_file: 本次事务的远程yum配置文件
      • disable_gpg_check: 默认为no,禁用正在安装的GPG签名检查
      • exclude(>2.0): 排除软件包名称,在state=present/latest有效
      • name: 安装的软件名,必须指定。可以包含版本:httpd-2.2.29-1.4
      • security(>2.4): 当值为yesstate=latest则只安装标记为安全相关的更新
      • state: 值:absent/installed/latest/present/removed,分别为确保未安装/确保已安装/确保安装的为最新版本/确保安装(默认)/删除
      • update_cache(>1.9): 默认为no,强制yum检查缓存是否过期并在需要时重新下载。只有state=present/latest有效
      • update_cache(>2.5): 默认为no,仅更新软件包,若未安装不安装。仅在state=latest有效

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        \- name: install one specific version of Apache
        yum:
        name: httpd-2.2.29-1.4.amzn1
        state: present

        \- name: upgrade all packages
        yum:
        name: '*'
        state: latest

        \- name: upgrade all packages, excluding kernel & foo related packages
        yum:
        name: '*'
        state: latest
        exclude: kernel*,foo*

        \- name: install the nginx rpm from a remote repo
        yum:
        name: http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
        state: present

        yum_repository: 在基于RPM的Linux发行版中添加或删除YUM存储库。

      • name: 必填,Unique repository ID.
      • attributes(>2.3): chattr命令
      • throttle: 为下载启用带宽限制。以字节/秒表示。SI前缀(k,M或G)可被附加到带宽值。
      • bandwidth: 最大可用网络带宽,以字节/秒为单位。低于throttle的优先级。
      • baseurl: URL to the directory where the yum repository’s ‘repodata’ directory lives.可以是多个URL的列表
      • enabled: 默认yes,用于指定是否启用这个源
      • enablegroups: 确定yum是否允许使用此源的包组。
      • exclude: 从更新或安装中排除的软件包列表。由空格分隔,允许使用通配符*;也支持YAML数组
      • includepkgs: 只想从源中使用的软件包列表。由空格分隔,允许使用通配符*;也支持YAML数组
      • failovermethod: roundrobin:从URL列表随机选取,故障则轮训选取;priority:按列表中顺序为优先级选取
      • file: 默认为name的值,指定后按指定名称加.repo扩展名保存
      • reposdir: 默认为/etc/yum.repos.d,存储.repo文件的目录
      • retries: 默认为10,重试次数,设置0为永远重试
      • timeout: 默认为30,超时前等待连接的秒数。
      • owner: chown方法
      • group: chown方法
      • mode: chmod方法
      • gpgcakey: A URL pointing to the ASCII-armored CA key file for the repository.
      • gpgcheck: 指定yum是否对软件包进行GPG签名检查,默认为NO
      • gpgkey: A URL pointing to the ASCII-armored GPG key file for the repository.可以为URL list

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        \- 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
    • apt模块
      同yum模块:

  5. 用户和组模块:
    创建和删除用户
    ansible all -m user -a "name=foo password=<在此输入密码>"
    ansible all -m user -a "name=foo state=absent"

    • user模块

      • name: 用户名称,必选项
      • password: 密码
      • create_home: 默认为yes,是否为用户创建主目录
      • force: 默认为no,当与state=absent使用,类似userdel --force
      • remove: 默认为no,当state=absent时类似userdel --force
      • group: 设置用户的主组
      • groups: 把用户包含在组列表中(逗号分隔),当groups=时,将会从主组意外的所有组中剔除
      • home: 设置用户主目录路径
      • shell: 设置用户shell
      • local(>2.4): 默认为no,强制在实施它的平台上使用local命令选项
      • move_home: 默认为no,当设置为yes时使用home=,移动主目录
      • state: 选项absent/present,保证账户是否存在
      • uid: 设置uid
      • update_password: 选项always/on_create,前者只有新密码和原密码不同时才更新,后者只用于新建用户
      • system: 设置是否为系统账户,对系统已有账户无法操作
    • group模块

      • name: 组名称,必选项
      • gid: 设置gid
      • state: 选项absent/present,保证组是否存在
      • system: 设置是否为系统组
  6. Git模块:
    ansible webservers -m git -a“repo=https://foo.example.org/repo.git dest=/srv/myapp version=HEAD”
    该模块要求git>=1.7.1

    • dest: 必选项,仓库路径/URL
    • repo: 必选项,git, SSH, or HTTP(S) protocol address of the git repository.
    • accept_hostkey(>1.5): 默认为no,若为yes确保-o StrictHostKeyChecking = no作为ssh选项存在。
    • archive(>2.4): 指定带扩展名的存档文件路径。允许的压缩格式[zip,tar.gz,tar,tgz]
    • bare(>1.4): 默认为no,是否建为裸仓库
    • clone(>1.9): 默认为yes,若no如果本地仓库不存在则不克隆
    • force: 默认为no,若为yes正在运行的存储库所有文件将被丢弃
    • key_file: 指定一个可选的私钥用于checkout
    • recursive(>1.6): 如果为no,repository会被克隆,但跳过子模块
    • reference: git clone --reference
    • remote: 默认值origin,remote name
    • update: 默认值yes,如果为no,则不会从原始库中获得更新
    • verify_commit(>2.0): 默认值no,如果为yes,则在克隆和检查时验证GPG签名。需要git>2.1.0

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      # Example git checkout from Ansible Playbooks
      \- git:
      repo: 'https://foosball.example.org/path/to/repo.git'
      dest: /srv/checkout
      version: release-0.22

      # Example read-write git checkout from github
      \- git:
      repo: ssh://git@github.com/mylogin/hello.git
      dest: /home/mylogin/hello

      # Example just ensuring the repo checkout exists
      \- git:
      repo: 'https://foosball.example.org/path/to/repo.git'
      dest: /srv/checkout
      update: no

      # Example just get information about the repository whether or not it has
      # already been cloned locally.
      \- git:
      repo: 'https://foosball.example.org/path/to/repo.git'
      dest: /srv/checkout
      clone: no
      update: no

      # Example checkout a github repo and use refspec to fetch all pull requests
      \- git:
      repo: https://github.com/ansible/ansible-examples.git
      dest: /src/ansible-examples
      refspec: '+refs/pull/*:refs/heads/*'

      # Example Create git archive from repo
      \- git:
      repo: https://github.com/ansible/ansible-examples.git
      dest: /src/ansible-examples
      archive: /tmp/ansible-examples.zip
  7. 服务管理模块:
    确保服务启动/重启/停止
    ansible webservers -m service -a "name=httpd state=started/restarted/stopped"

    • service模块:

      • arguments: 在命令行上提供的其他参数
      • name: 必选属性,服务名
      • enabled: 是否开机启动服务。在stateenabled中必选一个。
      • state: 属性值reloaded/restarted/running/started/stopped,重载/重启/运行/启动/停止.started/stopped为幂等动作,非必要不会运行命令。在stateenabled中必选一个。
      • pattern: 若该服务没有响应状态命令,则用ps命令的输出中寻找服务。
      • sleep(>1.3): 服务restart期间停留的时间

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        \- name: Start service httpd, if not running
        service:
        name: httpd
        state: started

        \- name: Stop service httpd, if running
        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 running 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模块(>2.2):适合用systemd管理服务的系统

      • name: 服务名。在chroot环境使用需要指定全名(crond.service)
      • state: 属性值started/stopped/restarted/reloaded,service最终操作后的状态。enabledstate至少要有一个被定义
      • enabled: 服务是否开机启动。enabledstate至少要有一个被定义
      • masked: 是否将服务设置为masked状态,被mask的服务是无法启动的
      • daemon_reload: 默认值no,在其它操作前运行守护进程重新加载,以确保systemd已经读取其他更改
      • no_block(>2.3): 默认值no,不要同步等待操作请求完成
      • user: 默认值no,使用服务的调用者运行systemctl,而不是系统的服务管理者

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        \- 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: True

        \- name: just force systemd to reread configs (2.4 and above)
        systemd: daemon_reload=yes
  8. sysctl模块:管理sysctl.conf中的条目,用于处理sysctl,并可在更改他们之后执行/sbin/sysctl -p

    • name: sysctl变量
    • value: sysctl变量的值
    • reload: 若为yes,则执行/sbin/sysctl -p sysctl_file
    • state: 属性值present/absent,确保是否在sysctl中存在该条目
    • ignoreerrors: 默认值no,忽略有关未知键的错误
    • sysctl_file: 默认值/etc/sysctl.conf。手动指定sysctl.conf绝对路径

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      # Set vm.swappiness to 5 in /etc/sysctl.conf
      \- sysctl:
      name: vm.swappiness
      value: 5
      state: present

      # Remove kernel.panic entry from /etc/sysctl.conf
      \- sysctl:
      name: kernel.panic
      state: absent
      sysctl_file: /etc/sysctl.conf

      # Set kernel.panic to 3 in /tmp/test_sysctl.conf
      \- sysctl:
      name: kernel.panic
      value: 3
      sysctl_file: /tmp/test_sysctl.conf
      reload: no

      # Set ip forwarding on in /proc and do not reload the sysctl file
      \- sysctl:
      name: net.ipv4.ip_forward
      value: 1
      sysctl_set: yes

      # Set ip forwarding on in /proc and in the sysctl file and reload if necessary
      \- sysctl:
      name: net.ipv4.ip_forward
      value: 1
      sysctl_set: yes
      state: present
      reload: yes
  9. unarchive模块:解压

    • 默认情况:在解压前从本地系统到目标系统
    • 设置remote_src=yes解压目标系统中的压缩文档

      • dest: 必选项,远程绝对路径
      • src: 必选项。若remote_src=no,将本地文件(绝对/相对路径)复制到目标系统;若remote_src=yes,对目标系统已存在的压缩文件进行解压;若remote_src=yessrc包含://,目标系统会先下载压缩文件再解压。
      • remote_src: 默认值no,设置为yes表示压缩文件已经在远程系统上
      • creates: 如果指定的绝对路径(文件或目录)已经存在,则此步骤将不会运行
      • exclude(>2.1): 需要排除的目录和文件
      • extra_opts(>2.1): 通过传入数组来指定其他选项
      • keep_newer(>2.1): 忽略比压缩文件中更新的文件
      • list_files(>2.0): 如果设为yes则返回包含在tarball中的文件列表
      • mode: chmod
      • owner: chown
      • group: chown
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      \- name: Extract foo.tgz into /var/lib/foo
      unarchive:
      src: foo.tgz
      dest: /var/lib/foo

      \- 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
  10. synchronize模块:对rsync的封装

    • archive: 默认值yes。Mirrors the rsync archive flag, enables recursive, links, perms, times, owner, group flags and -D.
    • checksum(>1.6): 默认值no。通过checksum来判断是否跳过同步,而非文件修改时间和大小。
    • compress(>1.7): 默认值yes,压缩数据传输,除非因此引起问题,否则不建议关闭
    • copy_links: 默认值no,是否复制符号链接。否则是指向的内容被复制。
    • links: 将符号链接复制为符号链接
    • link_dest(>2.5): 在rsync期间添加一个目标到硬链接
    • delete: 默认值no,传输完成后,删除src不存在dest存在的文件。需要recursive=yes
    • src: 必选项,源主机上将被同步到目标的路径(支持绝对/相对地址)
    • dest: 必选项,目标主机上将从源同步的路径(支持绝对/相对地址)
    • dest_port(>1.5): 目标主机的ssh端口(默认不需要指定)
    • dirs: 默认值no,传输目录,不需要递归
    • recursive: 递归目录
    • existing_only(>1.5): 默认值no,在接收主机忽略创建新文件
    • perms: 保留权限
    • private_key(>1.6): 指定用于rsync的SSH连接密钥
    • use_ssh_args(>2.0): 使用在ansible.cfg中指定的ssh_args
    • verify_host(>2.0): 默认值no,验证目标主机密钥
    • times: 保留修改时间
    • rsync_opts(>1.6): 传入数组指定其他rsync选项
    • rsync_path: 同--rsync-path
    • rsync_timeout: 超时设置
    • rsync_remote_user: put user@ for the remote path
    • mode: 选项值pull/push,指定同步的方向。在推模式下,本地主机是源; 在拉模式中,远程主机是源。
    • owner: 选项值yes/no,保留所有者(仅限超级用户使用)
    • group: 选项值yes/no,保留组
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    \- name: Synchronization of src on the control machine to dest on the remote hosts
    synchronize:
    src: some/relative/path
    dest: /some/absolute/path

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

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

    \- name: Synchronization using rsync protocol on delegate host (push)
    synchronize:
    src: /some/absolute/path/
    dest: rsync://somehost.com/path/
    delegate_to: delegate.host

    \- name: Synchronization using rsync protocol on delegate host (pull)
    synchronize:
    mode: pull
    src: rsync://somehost.com/path/
    dest: /some/absolute/path/
    delegate_to: delegate.host

    \- name: Synchronization without any --archive options enabled
    synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    archive: no

    \- name: Synchronization with --archive options enabled except for --recursive
    synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    recursive: no

    \- name: Synchronization with --archive options enabled except for --times, with --checksum option enabled
    synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    checksum: yes
    times: no

    \- name: Synchronization without --archive options enabled except use --links
    synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    archive: no
    links: yes

    \- name: Synchronization of two paths both on the control machine
    synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    delegate_to: localhost

    \- name: Synchronization of src on the inventory host to the dest on the localhost in pull mode
    synchronize:
    mode: pull
    src: some/relative/path
    dest: /some/absolute/path

    \- name: Synchronization of src on delegate host to dest on the current inventory host.
    synchronize:
    src: /first/absolute/path
    dest: /second/absolute/path
    delegate_to: delegate.host

    \- name: Synchronize two directories on one remote host.
    synchronize:
    src: /first/absolute/path
    dest: /second/absolute/path
    delegate_to: "{{ inventory_hostname }}"

    \- name: Synchronize and delete files in dest on the remote host that are not found in src of localhost.
    synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    delete: yes
    recursive: yes

    # This specific command is granted su privileges on the destination
    \- name: Synchronize using an alternate rsync command
    synchronize:
    src: some/relative/path
    dest: /some/absolute/path
    rsync_path: "su -c rsync"

    # Example .rsync-filter file in the source directory
    # - var # exclude any path whose last part is 'var'
    # - /var # exclude any path starting with 'var' starting at the source directory
    # + /var/conf # include /var/conf even though it was previously excluded

    \- name: Synchronize passing in extra rsync options
    synchronize:
    src: /tmp/helloworld
    dest: /var/www/helloworld
    rsync_opts:
    \- "--no-motd"
    \- "--exclude=.git"

    # Hardlink files if they didn't change
    \- name: Use hardlinks when synchronizing filesystems
    synchronize:
    src: /tmp/path_a/foo.txt
    dest: /tmp/path_b/foo.txt
    link_dest: /tmp/path_a/
  11. wait_for模块:等待预设事情发生并继续(如启动tomcat后等待8080端口被占用)

    • connect_timeout: 默认值5,预设事件关闭和重试前等待链接的时间,单位秒
    • delay: 默认值no,开始轮询前等待的秒数
    • exclude_hosts: 忽略的主机
    • host: 默认值127.0.0.1,执行该模块的主机
    • path(>1.4): 当路径存在时才继续执行
    • port: 端口号
    • search_regex(>1.4): 匹配连接名中的字符串,默认为多行正则表达式
    • sleep(>2.3): 默认yes,Number of seconds to sleep between checks,之前是硬编码为1秒
    • timeout: 默认300,超时时间
    • state: 选项值absent/drained/present/staerted/stopped,对象是端口的时候start状态会确保端口是打开的,stoped状态会确认端口是关闭的;对象是文件的时候,present或者started会确认文件是存在的,而absent会确认文件是不存在的。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    \- name: sleep for 300 seconds and continue with play
    wait_for: timeout=300
    delegate_to: localhost

    \- name: Wait 300 seconds for port 8000 to become open on the host, don't start checking for 10 seconds
    wait_for:
    port: 8000
    delay: 10

    \- name: Wait 300 seconds for port 8000 of any IP to close active connections, don't start checking for 10 seconds
    wait_for:
    host: 0.0.0.0
    port: 8000
    delay: 10
    state: drained

    \- name: Wait 300 seconds for port 8000 of any IP to close active connections, ignoring connections for specified hosts
    wait_for:
    host: 0.0.0.0
    port: 8000
    state: drained
    exclude_hosts: 10.2.1.2,10.2.1.3

    \- name: Wait until the file /tmp/foo is present before continuing
    wait_for:
    path: /tmp/foo

    \- name: Wait until the string "completed" is in the file /tmp/foo before continuing
    wait_for:
    path: /tmp/foo
    search_regex: completed

    \- name: Wait until the lock file is removed
    wait_for:
    path: /var/lock/file.lock
    state: absent

    \- name: Wait until the process is finished and pid was destroyed
    wait_for:
    path: /proc/3466/status
    state: absent

    \- name: Output customized message when failed
    wait_for:
    path: /tmp/foo
    state: present
    msg: Timeout to find file /tmp/foo

    # Don't assume the inventory_hostname is resolvable and delay 10 seconds at start
    \- name: Wait 300 seconds for port 22 to become open and contain "OpenSSH"
    wait_for:
    port: 22
    host: '{{ (ansible_ssh_host|default(ansible_host))|default(inventory_hostname) }}'
    search_regex: OpenSSH
    delay: 10
    connection: local

    # Same as above but you normally have ansible_connection set in inventory, which overrides 'connection'
    \- name: Wait 300 seconds for port 22 to become open and contain "OpenSSH"
    wait_for:
    port: 22
    host: '{{ (ansible_ssh_host|default(ansible_host))|default(inventory_hostname) }}'
    search_regex: OpenSSH
    delay: 10
    vars:
    ansible_connection: local
  12. wait_for_connection模块:等待远程主机可达/可用

    • connect_timeout: 默认值5,在关闭或重试前的最大秒数
    • delay: 默认值no,开始轮询前等待的秒数
    • sleep: 默认值yes,检查之间睡眠的秒数
    • timeout: 默认值600,等待的最大秒数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    \- name: Wait 600 seconds for target connection to become reachable/usable
    wait_for_connection:

    \- name: Wait 300 seconds, but only start checking after 60 seconds
    wait_for_connection:
    delay: 60
    timeout: 300

    # Wake desktops, wait for them to become ready and continue playbook
    \- hosts: all
    gather_facts: no
    tasks:
    \- name: Send magic Wake-On-Lan packet to turn on individual systems
    wakeonlan:
    mac: '{{ mac }}'
    broadcast: 192.168.0.255
    delegate_to: localhost

    \- name: Wait for system to become reachable
    wait_for_connection:

    \- name: Gather facts for first time
    setup:

    # Build a new VM, wait for it to become ready and continue playbook
    \- hosts: all
    gather_facts: no
    tasks:
    \- name: Clone new VM, if missing
    vmware_guest:
    hostname: '{{ vcenter_ipaddress }}'
    name: '{{ inventory_hostname_short }}'
    template: Windows 2012R2
    customization:
    hostname: '{{ vm_shortname }}'
    runonce:
    \- powershell.exe -ExecutionPolicy Unrestricted -File C:\Windows\Temp\ConfigureRemotingForAnsible.ps1 -ForceNewSSLCert -EnableCredSSP
    delegate_to: localhost

    \- name: Wait for system to become reachable over WinRM
    wait_for_connection:
    timeout: 900

    \- name: Gather facts for first time
    setup:
  13. template模块:Templates a file out to a remote server
    将带有参数的配置文件传递到目标地址,对文件进行属组属主的修改以及备份

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    # 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
  14. 网络获取类模块:

    • uri模块:

      • HEADER_: HTTP请求头
      • headers(>2.1): HTTP请求头,YAML哈希的格式
      • body: HTTP消息体
      • body_format: 选项值JSON/RAW(默认值)
      • client_cert(>2.4): PEM格式的证书链,用于SSL连接
      • client_key(>2.4): PEM格式KEY,包含私钥用于SSL。若client_cert包含证书和密钥,则不需要此选项。
      • creates: 文件名称
      • dest: 文件下载路径
      • follow_redirects: 选项值all/safe/none
      • force_basic_auth: 默认值no,强制认证
      • method: 选项值GET/POST/PUT/HEAD/DELETE/OPTIONS/PATCH/TRACE/CONNECT/REFRESH
      • return_content: 默认值no,返回内容
      • status_code: 默认值200,状态码
      • timeout: 默认值30,超时时间
      • url: 必选项,请求链接
      • user: 运行模块的用户
      • validate_certs(>1.9.2): 默认值yes,是否验证SSL证书,用于访问自签名站点

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        \- name: Check that you can connect (GET) to a page and it returns a status 200
        uri:
        url: http://www.example.com

        # Check that a page returns a status 200 and fail if the word AWESOME is not
        # in the page contents.
        \- uri:
        url: http://www.example.com
        return_content: yes
        register: webpage

        \- name: Fail if AWESOME is not in the page content
        fail:
        when: "'AWESOME' not in webpage.content"


        \- name: Create a JIRA issue
        uri:
        url: https://your.jira.example.com/rest/api/2/issue/
        method: POST
        user: your_username
        password: your_pass
        body: "{{ lookup('file','issue.json') }}"
        force_basic_auth: yes
        status_code: 201
        body_format: json

        # Login to a form based webpage, then use the returned cookie to
        # access the app in later tasks

        \- uri:
        url: https://your.form.based.auth.example.com/index.php
        method: POST
        body: "name=your_username&password=your_password&enter=Sign%20in"
        status_code: 302
        headers:
        Content-Type: "application/x-www-form-urlencoded"
        register: login

        \- uri:
        url: https://your.form.based.auth.example.com/dashboard.php
        method: GET
        return_content: yes
        headers:
        Cookie: "{{login.set_cookie}}"

        \- name: Queue build of a project in Jenkins
        uri:
        url: "http://{{ jenkins.host }}/job/{{ jenkins.job }}/build?token={{ jenkins.token }}"
        method: GET
        user: "{{ jenkins.user }}"
        password: "{{ jenkins.password }}"
        force_basic_auth: yes
        status_code: 201
    • get_url模块:

      • dest: 必选项,文件保存路径
      • url: 必选项,(http|https|ftp)://[user[:pass]]@host.domain[:port]/path
      • attributes(>2.3): 同chattr
      • backup(>2.1): 默认值no,新建一个带时间戳的备份文件
      • checksum(>2.0): 用例:checksum: sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
      • client_cert(>2.4): SSL证书
      • client_key(>2.4): SSL密钥
      • force: 默认值no,如果是yes将每次强制下载并替换原文件
      • force_basic_auth: 默认值no,默认强制认证
      • url_password(>1.6): HTTP认证密码
      • url_username(>1.6): HTTP认证用户名
      • headers(>2.0): HTTP请求头,格式”key:value,key:value”
      • mode: 同chmod
      • owner: 同chown
      • group: 同chown
      • timeout(>1.8): 默认值10,超时时间

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        \- name: Download foo.conf
        get_url:
        url: http://example.com/path/file.conf
        dest: /etc/foo.conf
        mode: 0440

        \- name: Download file and force basic auth
        get_url:
        url: http://example.com/path/file.conf
        dest: /etc/foo.conf
        force_basic_auth: yes

        \- name: Download file with custom HTTP headers
        get_url:
        url: http://example.com/path/file.conf
        dest: /etc/foo.conf
        headers: 'key:value,key:value'

        \- name: Download file with check (sha256)
        get_url:
        url: http://example.com/path/file.conf
        dest: /etc/foo.conf
        checksum: sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c

        \- name: Download file with check (md5)
        get_url:
        url: http://example.com/path/file.conf
        dest: /etc/foo.conf
        checksum: md5:66dffb5228a211e61d6d7ef4a86f5758

        \- name: Download file from a file path
        get_url:
        url: file:///tmp/afile.txt
        dest: /tmp/afilecopy.txt
  15. 待续

Playbooks

格式

  • 定义:剧本[playbook.yml],事先定义好的操作,日常重复执行的任务
  • 执行:ansible-playbook playbookName.yml -f 10
    -f参数指多台主机同时运行指令。

    1
    2
    3
    4
    5
    ---
    - hosts: dpdata
    tasks:
    - name: 确认主机是否存活
    shell: uptime
    • --- 是YAML文件的开始
    • - hosts 指定要使用的组
    • tasks 标记任务列表的开始
    • - name 定义任务名称,用于区别不同任务
    • shell 调用shell模块,后接模块命令
    • YAML遵循严格缩进结构

重用

import * 在解析剧本时进行预处理
include * 在执行剧本时处理语句(仅在2.4版本后存在)

导入剧本:

1
2
3
---
- import_playbook: webservers.yml
- import_playbook: databases.yml

导入任务:

1
2
3
4
tasks:
- import_tasks: common_tasks.yml
# or
- include_tasks: common_tasks.yml

导入变量:

1
2
3
4
5
6
7
8
9
10
tasks:
- import_tasks: wordpress.yml
vars:
wp_user: timmy
- import_tasks: wordpress.yml
vars:
wp_user: alice
- import_tasks: wordpress.yml
vars:
wp_user: bob

样例

  1. 列表:with_items

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ---
    - hosts: all
    become_user: root
    become: true
    tasks:
    - name: Installing dependencies
    apt: name={{item}} state=present
    with_items:
    - git
    - mysql-client
    - libmysqlclient-dev
    - build-essential
    - python-software-properties
  2. 变量:vars
    vars是一个定义变量语句,可以在task语句或template文件中使用。Jinja2是 Ansible中使用的模板引擎。

    1
    2
    3
    4
    5
    6
    7
    8
    ---
    - hosts: all
    vars:
    - secret_key: VqnzCLdCV9a3jK
    - path_to_vault: /opt/very/deep/path
    tasks:
    - name: Setting a configuration file using template
    template: src=myconfig.j2 dest={{path_to_vault}}/app.conf

    使用作为剧本的一部分,但也因为使用了template语句,可以使用myconfig.j2中的任何变量,该文件必须存在一个名为templates 的子文件夹中。项目树应该如下所示:

    1
    2
    3
    4
    5
    6
    ├── Vagrantfile
    ├── inventory
    ├── playbook1.yml
    ├── playbook2.yml
    └── templates
    └── myconfig.j2

Tips

  1. 显示所有的组
    ansible localhost -m debug -a "var=groups.keys()"
  2. 显示所有的组和主机
    ansible localhost -m debug -a "var=groups"
  3. 显示调试信息加 -vvv
  4. 会失败的任务:
    通过指定ignore_errors:true,你可以运行可能会失败的任务,但不会影响剧本中剩余的任务完成。这是非常有用的,例如,当删除最初并不存在的日志文件时。
  5. 只在一台服务器中运行任务:
    有多个应用程序服务器,而我们只需要一个服务器来运行迁移。在这种情况下,当使用run_once时,run_once将分派任务到一个服务器,并直到这个任务完成继续下一个任务。你只需要在你的任务中设置run_once:true
  6. 后台操作:
    长时间运行的操作可以在后台运行,并可以稍后检查其状态。例如,要long_running_operation 在后台异步执行,超时时间为3600秒(-B),并且没有轮询(-P):
    $ ansible -B 3600 -P 0 -a "/usr/bin/long_running_operation --do-stuff"
    如果您确定稍后要检查作业状态,则可以使用async_status模块,并将它传递给在后台运行原始作业时返回的作业ID:
    $ ansible web1.example.com -m async_status -a "jid=488359678239.2844"
    轮询是内置的,看起来像这样:
    $ allsible -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff"
    上面的例子说“最多运行30分钟(-B30 * 60 = 1800),轮询状态(-P)每60秒”。
    轮询模式非常聪明,所有任务都将在任何机器上开始轮询之前启动。--forks如果你想快速开始所有的工作,一定要使用足够高的值。时间限制(以秒为单位)超时(-B)后,远程节点上的进程将终止。
    通常情况下,您只会背景长时间运行的shell命令或软件升级。后台复制模块不会执行后台文件传输。 手册还支持轮询,并且具有简化的语法。
如果文章对您有帮助,感谢您的赞助支持!