「Ansible」- 基本概念及术语

2.1 安装

#!/bin/bash

################################################################################
# 远程主机
################################################################################
# 远程主机无需做过多的配置,只需启动SSH服务,并且具有Python 2.4以上的环境。

################################################################################
# 管理节点
################################################################################
# 安装Ansible
yum install -y epel-release.noarch
yum install -y ansible

# 配置「管理节点」到「远程主机」的连接
# 即SSH连接(这里配置的是密钥连接,而不是密码连接。在Ansible中,这两种都是可以的。)
ssh-keygen
ssh-copy-id username@hostname
ssh-keyscan hostname >> ~/.ssh/known_hosts

# 测试SSH连接
ssh username@hostname

2.2 设置要管理的主机

接下来,需要指定要被Ansible管理的主机:“主机目录(Host Inventory)”,一个配置文件,/etc/ansible/hosts,指定了被管理的主机,内容如下:

# 被管理的主机列表
a.example.com
b.example.com
c.example.com

# “[dbserver]”,定义名为dbserver的主机组,指定“dbserver”等价于指定了dbserver部分下的所有主机
[dbserver]
db0.example.com
db1.example.com
db2.example.com

# 同“[dbserver]”,定义主机组。
[webserver]
web0.example.com
web1.example.com
web2.example.com

2.3 使用命令管理主机

Ansible提供了命令行工具,称为“Ad-Hoc Commands”。

命令通常是ansible <host-pattern> [options]格式。下面是几个命令示例:

# 检查所有的远程主机,是否存在可以访问远程主机的bruce用户
ansible all -m ping -u bruce

# 以管理节点的当前登录用户名,在远程主机上执行命令
ansible all -a "/bin/echo hello"

# 复制文件
ansible web -m copy -a "src=/etc/hosts dest=/tmp/hosts"

# 安装软件包
ansible web -m yum -a "name=acme state=present"

# 添加用户
ansible all -m user -a "name=foo password=<crypted password>"

# 从仓库检出代码
ansible web -m git -a "repo=git://src.exammple.com/project.git dest=/srv/http/project version=HEAD"

# 启动服务
ansible web -m service -a "name=httpd state=started"

# 并行执行任务,启动10个进程执行任务。
ansible lb -a "/sbin/reboot" -f 10

# 查看远程主机的信息
ansible all -m setup

2.5 模块

什么是模块?

在Shell中,我们会执行cp、ls、mv、rm、yum等命令,进行复制、查看、用户管理、权限管理、服务管理等日常操作。

在Ansible中,既然是批量管理主机,那肯定也会进行复制、查看、服务管理等等操作。在Ansible中,完成这些操作的不是“命令”,而是称为“模块”的东西。例如,文件复制使用copy模块;软件安装使用yum模块;用户管理使用user模块;服务管理使用service模块等等。

如何使用模块?

每个模块都有自己的选项,如同Shell命令一样。

使用ansible-doc <module_name>进行查看模块手册,或者查看官方「Module Index」手册。

使用ansible-doc -l列出当前所有可用模块,并显示一句话描述模块的作用。

下面是「在命令行中」使用模块的示例:

#!/bin/bash

# 使用模块copy,复制管理节点文件/etc/hosts到所有远程主机/tmp/hosts
ansible all -m copy -a "src=/etc/hosts dest=/trnp/hosts"

# 使用模块yum,在远程主机 Web 上安装 httpd 包
ansible web -rn yum -a "narne=httpd state=present"

上述两个命令中,参数allweb是主机组,在/etc/ansible/hosts中进行配置;选项-m指定要使用的模块;选项-a指定要传递给模块的参数;

下面是「在Playbook中」使用模块的示例:

---
tasks:
  - name: ensure apache is at the latest version
    yum: pkg=httpd state=latest
  - name: write the apache config file
    template: src=ternplates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
  - name: ensure apache is running
    service: name=httpd state=started

在Playbook中,指令tasks中的每一个“动作(Action)”都是对模块的一次调用。在每个动作中:

  • 参数name指定了动作的描述;
  • 每个动作的第二行则是要执行的动作。冒号前面是模块的名字。冒号后面是调用模块的参数。

模块的总结:

  • 模块既可以在命令行中指定,也可以在Playbook中指定;
  • 模块作用与参数与所使用的模块有关,使用之前参阅文档;
  • 使用ansible-doc <module_name>进行查看模块手册,或者查看官方「Module Index」手册;
  • Ansible提供了一些常用的模块,用户也可以更具自己的需要开发自己的模块。

常用模块示例

调试和测试类的模块:
ping:ping 一下你的远程主机 , 如果可以通过 Ansible 连接成功,那么返回 pong 。

debug:用于调试的模块,只是简单打印 些消息 , 有点像 Linux 的 echo 命令。

文件类的模块:
copy:从本地复制文件到远程节点。

template:从本地复制文件到远程节点 , 并进行变量的替换。

file:设置文件属性。

系统管理相关的模块:
user:管理用户账户。

yum:RedHat系列的发行版上的包管理。

service:管理服务。

firewalld:管理防火墙中的服务和端口。

执行 shell 命令:
shell:在节点上执行shell命令,支持“$HOME”、“<”、“>”、“|” 、“;”、“&” 。

command:在远程节点上执行命令 ,不支持“$HOME” 、“<”、 “>“

模块:ping
测试能否通过SSH连接远程主机,并且远程主机的Python版本是否满足要求。如果成功则返回“pong”。

#!/bin/bash

ansible all -m ping

模块:debug
打印信息,类似于Linux中的echo命令。

- hosts: all
  remote_user: root
  tasks:
    - name: "debug module"
      debug:
        msg: "System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}"

{{ inventory_hostname }}{{ ansible_default_ipv4.gateway }}会被自动替换成变量,这些变量是由Ansible在执行Playbook之前注入的,不需要定义即可使用。

变量可以是系统变量 ,也可以是动态的执行结果。如下示例,通过regester关键字将执行结果注入result变量中,然后使用debug模块打印:

- hosts: all
  remote_user: root
  tasks:
    - name: "debug module register"
      shell: /usr/bin/uptime
      register: result
    - name: "debug module var"
      debug:
        var: result

模块:copy
复制「本地」文件到「远程」主机。该模块会比较文件校验和,如果相同则不会复制,并返回OK,如果不同则进行复制,并返回changed。

- hosts: all
  remote_user: root
  tasks:
    - name: "module copy"
      template:
        src: /etc/sudoers
        dest: /tmp/sudoers.backup
        backup: yes
        owner: root
        group: root
        mode: 0600
        validate: 'visudo - cf %s'

与之前的例子类似,name为动作的描述;src是本地文件位置;dest为远程主机文件保存位置;backup表示文件存在则进行备份,文件名追加时间形式的后缀;owner指定所有者;group指定所属组;mode指定权限;validate对文件进行校验,参数为命令,并且只有命令以“0”退出,则表示复制成功,%s代表文件。

模块:template
复制静态文件很简单。但有时复制文件的时,我们需要根据远程主机的当前配置调整文件的内容(比如需要在配置文件中设置当前主机的IP地址)。这时候就可以使用template模块。该模块使用Jinja2模板引擎,使用{{ var_name }}来表示变量。文件/tmp/index.html.j2的内容如下:

<h1>PORT: {{ http_port }}<h1/>
<p>Served by {{ ansible_hostname }} ({{ ansible_default_ipv4.address }}) . </p>

下面是Playbook的内容:

- hosts: all
  vars:
    http_port: 80
  remote_user: root
  tasks:
    - name: "module copy"
      template:
        src: /tmp/index.html.j2
        dest: /tmp/index.html
        backup: yes
        owner: root
        group: root
        mode: 0600

如上示例,/tmp/index.html.j2为模板文件,使用的两个变量ansible_hostnameansible_default_ipv4.address都是远程主机的系统变量,可以直接使用,http_port是环境变量,使用var关键字进行定义,并且template模块具有和copy模块类似的备份、所有权、模式设定等等功能。

模块:file
设置远程主机上的文件、目录、软链接的权限,也可以创建和删除它们。

- hosts: all
  remote_user: root
  tasks:
    - name: "创建文件夹"
      file:
        state: directory
        path: /tmp/module-file
        mode: 0755
    - name: "创建文件"
      file:
        state: touch
        path: /tmp/module-file/foo.txt
    - name: "创建到文件的软链接"
      file:
        state: link
        src: /tmp/module-file/foo.txt
        dest: /tmp/module-file/link-to-foo.txt
        owner: root
        group: root
    - name: "修改文件权限"
      file:
      	path: /tmp/module-file/foo.txt
        owner: root
        group: root
        # mode: "u=rw,g=r,o=r"
        # mode: "u+w,a-x"
        mode: 0600

模块:user
用于管理系统的用户账户,并且设置相关的属性。

- hosts: all
  remote_user: root
  tasks:
    - name: "创建用户"
      user:
        name: zheng
        comment: "Mr. Zheng"
        # 设置UID
        uid: 1040
        # 设置默认的Shell
        shell: /bin/bash
        # 设置组
        group: root
        # 追加两个附属组,而不是覆盖原由附属组
        groups: bin, daemon
        append: yes
        # 为用户添加过期时间。使用date --date '2019-01-10' +%s命令生成
        expires: 1547049600
        # 为账户 jsmith 创建一个 2048 位的 SSH 密钥 , 放 在~jsmith/.ssh/id_rsa 中 。
        generate_ssh_key: yes
        ssh_key_bits: 2048
        ssh_key_file: .ssh/id_rsa
    - name: "删除用户"
      user:
        name: zheng
        state: absent
        remove: yes

模块:yum
用于在RHEL、CentOS、Fedora21等系统发行版中管理软件包。

- hosts: all
  remote_user: root
  tasks:
    - name: "使用URL安装软件包"
      yum:
        name: http://nginx.org/packages/centos/7/x86_64/RPMS/nginx-1.14.2-1.el7_4.ngx.x86_64.rpm
        state: present
    - name: "安装本地包"
      yum:
        name: /tmp/nginx-1.14.2-1.el7_4.ngx.x86_64.rpm
        state: present
    - name: "安装软件包组"
      yum:
        name: "@gnome-desktop-environment"
        state: present
    - name: "安装指定仓库中的包"
      yum:
        name: httpd
        enablerepo: testing
        state: present
    - name: "安装最后一个版本的Apache服务"
      yum:
        name: httpd
        state: latest
    - name: "安装指定版本的Apache服务"
      yum:
        name: httpd-2.2.29-1.4.amznl
        state: present
    - name: "删除软件包"
      yum:
        name: httpd
        state: absent

模块:service
用于管理服务器中的服务,进行启动、重启、开机启动等操作。

- hosts: all
  remote_user: root
  tasks:
    - name: "启动"
      service:
        name: httpd
        state: started
    - name: "停止"
      service:
        name: httpd
        state: stopped
    - name: "重新启动"
      service:
        name: restarted
        state:
    - name: "开机启动"
      service:
        name: httpd
        enabled: yes
    - name: "重新启动网络服务"
      service:
        name: network
        state: restarted
        args: eth0

模块:firewalld
用于添加防火墙规则,这要求远程远程主机的firewalld的版本在0.2.11以上。

- hosts: all
  remote_user: root
  tasks:
    - name: "允许HTTPS请求"
      firewalld:
        service: https
        permanent: true
        state: enable
    - name: "允许HTTP请求"
      firewalld:
        zone: dmz
        service: https
        permanent: true
        state: enable
    - name: "禁用TCP端口"
      firewalld:
        port: 8081/tcp
        permanent: true
        state: disable
    - name: "启用UDP端口"
      firewalld:
        port: 162-189/udp
        permanent: true
        state: enable

模块:shell
使用/bin/sh执行命令,如果一个命令能通过其他模块完成,则不建议使用shell模块。因为shell模块的状态判断能力比较弱。

- hosts: all
  remote_user: root
  tasks:
    - name: "支持 $HOME 、<、>、|、;、& 操作符号"
      shell: service jboss start && chkconfig jboss on
    - name: "支持脚本调用"
      shell: somescript.sh > somelog.txt
    - name: "执行命令前改变工作目录"
      shell: somescript.sh > somelog.txt
      args:
        chdir: somedir/
    - name: "执行命令前改变工作目录,并且仅在文件 some_log.txt 不存在时执行命令"
      shell: somescript.sh > somelog.txt
      args:
        chdir: somedir/
        creates: somelog.txt
    - name: "使用Bash运行命令"
      shell: cat < /tmp/\*txt
      args:
        executable: /bin/bash

模块:command
该模块与shell模块类似,但是不支持 $HOME 、<、>、|、;、& 操作符号。

- hosts: all
  remote_user: root
  tasks:
    - name: "调用单条命令"
      command: /sbin/shutdown -t now
    - name: "执行命令前改变工作目录,并且仅在文件不存在时执行命令"
      command : /usr/bin/make_database.sh argl arg2
      args:
        chdir: somedir/
        creates: /path/to/database
    - name: "另外一种传参方式"
      command: /sbin/shutdown -t now creates=/path/to/database