ansible基础信息

  1. 特性:
    • 有Paramiko,PyYAML,jinja2(模版语言)三个关键模块
    • 幂等性:一个任务执行1遍和执行n遍效果一样,不因重复执行带来意外情况(例如复制,不会因为第二次执行文件已存在就报错)
    • 无需代理不依赖PKI(无需ssl)
    • 可使用任何编程语言写模块
    • YAML格式,编排任务,支持丰富的数据结构
    • 较强大的多层解决方案
  2. 安装:
    使用epel源:```

    yum install -y epel-release

    yum -y install ansible

    ```
    配置文件:/etc/ansible/ansible.cfg
  3. 常用命令:
    ansible-doc:
    • -a 显示所有模块的文档
    • -l,—list 列出可用模块
    • -s,—snippet 显示指定模块的playbook模块

示例:

  1. ansible-doc -l
  2. ansible-doc ping 查看指定模块帮助用法
  3. ansible-doc -s ping 查看指定模块帮助用法,以简单模式来显示

host-pattern:

  • *:通配符
  • 或关系符:: ansible "websrvs:dbsrvs" -m ping
  • 逻辑与符:ansible "websrvs:&dbsrvs" -m ping
  • 逻辑非:ansible 'websrvs:!dbsrvs' -m ping,在websrvs组,但不在dbsrvs组;此处为单引号
  • 综合逻辑:ansible 'websrvs:dbsrvs:&appsrvs:!ftpsrvs'
  • 正则:
    ansible "websrvs:dbsrvs" -m ping
    ansible "~(web|db).*\.magedu\.com" -m ping
    

其他ansible命令

  • ansible-galaxy:连接https://galaxy.ansible.com/,下载对应的roles
    • 列出所有已安装的galaxy:ansible-galaxy list
    • 安装:ansible-galaxy install geerlingguy.redis
    • 删除:ansible-galaxy remove geerlingguy.redis
  • ansible-pull:推送命令至远程,效率无限提升,对运维要求较高
  • ansible-vault: 加密文件
    • 加密:ansible-vault encrypt test.yml
    • 解密:ansible-vault decrypt test.yml
    • 查看:view
    • 编辑:edit
    • 更改口令:rekey
    • 加密创建:create
  • ansible-console: 交互执行命令,支持tab
    • root@test(2)[f:10]$:执行用户@当前操作的主机组(当前组的主机数量)[f:并发数]$:
    • 设置并发数,forks n
    • 切换组,cd 组名
    • 列出当前组主机列表:list
    • 列出所有的内置命令:?或help

常用用法及模块

ansible是通过ssh来完成对远程主机的控制,不需要安装client/agents,所以需要先配置免密登录ssh-copy-id [-i identity_file] [user@]hostname或者在执行时加上 -k 参数,使用交互式输入密码;

ansible是通过主机清单(Inventory)里的主机或主机组,默认的主机清单位置:/etc/ansible/hosts,也可以手动配置环境变量指定路径:export ANSIBLE_INVENTORY=~/ANSIBLE_HOSTS_PATH,或者通过python来调用数据库中的主机组信息;

Inventory文件格式:

  • inventory文件遵循INI文件风格,中括号中的字符为组名。可以将同一个主机同时归并到多个不同的组中;此外,当如若目标主机使用了非默认的SSH端口,还可以在主机名称之后使用冒号加端口号来标明:
ntp.com
IP1

[webservers]
www1.com:2222
IP2
IP3

[dbservers]
db1.com
db2.com
  • 如果主机名称遵循相似的命名模式,还可以使用列表的方式标识各主机,例如:
[webservers]
www[01:50].example.com
192.168.1.[2:20]

[databases]
db-[a:f].example.com
  • 主机变量: 可以在inventory中定义主机时为其添加主机变量以便于在playbook中使用。例如:
[webservers]
IP1 http_port=80 maxRequestsPerChild=808
www2.com http_port=8080 maxRequestsPerChild=909
  • 组变量:
[webservers]
www1.com
www2.com

[webservers:vars]
ntp_server=ntp.com
nfs_server=nfs.com

Inventory其他的参数:

参数 功能
ansible_ssh_host 远程主机
ansible_ssh_port 指定远程主机ssh端口
ansible_ssh_user ssh连接远程主机的用户,默认为root
ansible_ssh_pass 连接远程主机使用的密码,在文件中明文,建议使用-k
ansible_sudo_pass sudo密码,建议使用-K
ansible_connection 指定连接
ansible_ssh_private_key_file ssh连接使用的私钥 |
ansible_shell_type 指定连接对端的shell类型,默认sh,支持cash,fish
ansible_python_interpreter 指定对端使用的python编译器的路径

基于ad-hoc模式使用:ansible [-f forks] -m module_name -a “module_args” [-u REMOTE_USER -b —become-user root -K]

ansible dbsrvs -m command -a 'ls /root' -u test -k -b(--become)

显示所有主机列表,也可以指定某一组主机;

ansible all --list-hosts
ansible all --list

如果文件不存在则执行:ansible all -a "removes=/etc/fstab cat /etc/fstab"

如果文件存在则不执行:ansible all -a "create=/etc/fs cat /etc/fstab"

本地一个脚本运行在远程主机上:

ansible all -m script -a 'SCRIPT_PATH'
ansible all -m script -a f1.sh
也可以指定参数:
    --some-arguments 1234
  1. copy模块,复制文件:ansible all -m copy -a 'src=FILE_PATH dest=FILE_PATH backup=yes mode= owner=' backup: 做备份 mode: 权限 owner:所有者
    直接在客户端创建一个文件,内容自定义:ansible all -m copy -a 'content="something" dest=/data/test'
  2. fetch模块,从客户端取文件到服务端,远程为一个文件,本地为一个存在该文件的文件夹:ansible all -m fetch -a 'scr=FILE_PATH dest=DIC_PATH'

  3. archive/unarchive,打包解包模块:```

  • name: Compress directory /path/to/foo/ into /path/to/foo.tgz archive: path: /path/to/foo dest: /path/to/foo.tgz

  • name: Create a zip archive of /path/to/foo archive: path: /path/to/foo format: zip

  • name: Create a tar.gz archive of a single file. archive: path: /path/to/foo/single.file dest: /path/file.tar.gz format: gz force_archive: true

  • name: Extract foo.tgz into /var/lib/foo unarchive: src: foo.tgz dest: /var/lib/foo

  • 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 ```

  1. file,文件模块:新建:ansible all -m file -a 'name=/data/f1 state=touch' 删除:ansible all -m file -a 'name=/data/f1 state=absent' 新建文件夹:ansible all -m file -a 'name=/data/f1 state=directory' 新建软链接:ansible all -m file -a 'src=/etcf/fstab dest=/data/fstab.link state=link' 删除软链接:ansible all -m file -a 'dest=/data/fstab.link state=absent'

  2. hostname,主机名模块:ansible IP1 -m hostname -a 'name=websrvs'

  3. cron,计划任务模块:创建任务:ansible srv -m cron -a "minute=*/5 job='/usr/bin/ntpdate 172.16.0.1 &>/dev/null' name=Syntime" 删除任务:ansible srv -m cron -a 'state=absent name=Synctime' 禁用任务:ansible srv -m cron -a 'disabled=true job='/usr/bin/ntpdate 172.16.0.1 &>/dev/null' name=Synctime'

  4. yum/apt,安装程序包模块:``` 安装:ansible all -m yum -a ‘name=httpd,memcached,… state=latest’ 卸载:ansible all -m yum -a ‘name=httpd state=absent/removed’ 显示所有已经装好的包:ansible all -m yum -a ‘list=installed’

先将本地的rpm包copy到客户端上,在yum模块的name=FILE_PATH来安装该软件,可以带参数: disable_gpg_check=yes update_cache=yes


8. service,服务管理模块:

ansible all -m service -a ‘name=vsftpd state=started/stopped/restarted/reloaded enabled=yes/no’


9. user,用户管理模块:

创建:ansible all -m user -a ‘name=nginx shell=/sbin/nologin system=yes home=/var/nginx groups=root,bin uid=80 comment(描述)=”nginx server”‘ 删除:ansible all -m user -a ‘name=nginx state-absent remove=yes(删除家目录))’


10. group,组管理模块:

创建:ansible all -m group -a ‘name=nginx system=yes gid=80’


11. setup,主机配置模块:

ansible all -m setup //获取所有的变量信息; 显示过滤变量,支持通配符:ansible all -m setup -a ‘filter=…’


12. lineinfile/replace,替换文件内容:

行替换:ansible all -m lineinfile -a “path=/etc/selinux/config regexp=’SELINUX=’ line=SELINUX=enforcing” 字符串替换:ansible all -m replace -a “path=/etc/selinux/config regexp=’enforing’ replace=disabled”



<a name="6a91f5a3"></a>
### ansible剧本(playbook)

playbook的核心元素:

- hosts: 执行的远程主机列表
- tasks: 任务集
- varniables: 内置变量或自定义变量在playbook中调用
- templates:模版,可替换模版文件中的变量并实现一些简单逻辑的文件
- handlers和notify结合使用,由特定条件处罚的操作,满足条件才执行,否则不执行
- tags: 标签,指定某条任务执行,用于选择运行playbook中的部分代码;

ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常的长;此时,如果确信其没有变化,就可以通过tags跳过这些代码片段:`ansible-playbook -t tagsname user.yml`

如果命令或脚本的退出码不为零,但是依然想继续运行,可以使用如下方式替换:

tasks:

  • name: run this command and ignore the result shell: /usr/bin/somecommand || bin/true ```

或者使用ignore_errors来忽略错误信号:

tasks:
  - name: run this command and ignore the result
    shell: /usr/bin/somecommand
    ignore_errors: True

ansible-playbook常见选项:

  • -C,—check
  • —list-hosts: 列出运行任务的主机
  • —limit: 主机列表,只针对主机列表中的执行任务
  • -v:显示过程 -vv -vvv 更详细
  • —list-tasks: 列出剧本中的tasks,ansible-playbook file.yml —list-tasks

回调handlers和notify:

vim httpd.yml
- hosts: websrvs
    remote_user: root
    tasks:
    - name: install httpd service
        yum: name=httpd 
    - name: copy conf file 
        copy: src=files/httpd.conf dest=/vaer/www/html/conf/ backup=yes
        notify: restart httpd service
    - name: start httpd service
        service: name=httpd state=started

    handlers: 
    - name: restart httpd service 
        service: name=httpd state=restarted

如果触发多个notify:

notify:
  - HANDLERS_NAME1
  - HANDLERS_NAME1

tags,标签,给某一个任务标记标签,可以只单独执行该任务:

vim httpd.yml
- hosts: websrvs
    remote_user: root
    tasks:
    - name: install httpd service
        yum: name=httpd
        tags: inshttpd 
    - name: copy conf file 
        copy: src=files/httpd.conf dest=/vaer/www/html/conf/ backup=yes
        notify: restart httpd service
    - name: start httpd service
        service: name=httpd state=started
        tags: starhttpd

    handlers: 
    - name: restart httpd service 
        service: name=httpd state=restarted

ansible-playbook -t starhttpd httpd.yml

注:多个动作可以用同一个tags

模版templates:

一般在/etc/ansible创建templates文件夹,且命名.j2,可以直接使用任何地方的变量:主机清单,变量文件,剧本中的vars定义的变量等
例如:

cp /etc/nginx/nginx.conf /etc/ansible/templates/nginx.conf.j2

在nginx.conf.j2中:
    workprocess auto --> worker_processes {{..cpu*2}}

在剧本中使用模版:

...
- name: copy template
    template: src=FILE_NAME(可以直接写文件名,会自动寻找到/etc/ansible/templates) dest=

when:在task后添加when子句即可使用条件测试,通过判断某一变量(默认变量:系统版本=ansible_distribution_major_version)是否满足条件,是否继续执行,例如:

- name: copy template for centos7
    template: src=nginx.conf7.j2 dest=/etc/nginx/nginx.conf
    when: ansible_distribution_major_version == "7"
    notify: restart service

with_items,迭代:

如1:
    tasks: 
        - name: create files
        file: /data/{{ item }} state=touch
        with_items:
            - file1
            - file2
            - file3

如2:
    tasks:
        - name: create some user
        user: name={{ item.name }} group={{item.group }}
        with_items:
            - { name: 'user1', group: 'g1' }
            - { name: 'user2', group: 'g2' }
            - { name: 'user3', group: 'g3' }

for循环:

{% for .. in ..%}
{% endfor %}

if:

{% if .. %}
{% endif %}

ansible角色(role)

用于层次性、结构化地组织playbook;roles能够根据层次型结构自动装载变量文件、tasks以及handlers等;要使用roles只需要在playbook中使用include(老版本)指令即可;

复杂场景:建议使用 roles

  • 变更指定主机或主机组
  • 如命名不规范维护和传承成本大
  • 某些功能需多个playbook,通过include即可实现

建议在/etc/ansible下创建roles:

/etc/ansible/
    nginx_role.yml
        ...
        roles:
            - nginx 
    roles/
        nginx/
            tasks/
                group.yml
                main.yml(定义执行顺序)
                    - include: group.yml
                    - include: user.yml
                    ...
                restart.yml
                start.yml
                templ.yml
                user.yml
                yum.yml
            templates/
                nginx.conf.j2

在角色中添加标签:

roles:
    - { role: httpd, tags: ['web','httpd'] }
    - { role: nginx, tags: ['web','nginx'], when ansible_distribution_major_version == "7" }

需要单独执行某个任务,在任务中加上:

tags:
  - TAG_NAM

ansible-playbook agent_role.yml --tags "TAG_NAME"

需要从某个任务开始执行:

ansible-playbook playbook.yml --start-at-task="install packages"

docker_container模块:主要是用于ansible-playbook操作docker容器的一个模块;

  • docker_container: 部署docker容器;
  • docker_image: 用于编译镜像;
  • docker_service: 部署docker compose;

通过python动态获取Inventory

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pymysql
import json
import argparse


def execude_sql_hosts(re):
    hostlist = {}

    return hostlist

def execude_sql_group(re):
    hostlist = {}
    grouplist = {}
    for i in re:
        grouplist['jumper'] = {}
        grouplist['jumper']['hosts'] = []
        hostlist['jumper'] = []

    grouplist['_meta'] = {}
    grouplist['_meta']['hostvars'] = {}

    for i in re:
        hostlist['jumper'].append(i[0])
        grouplist['jumper']['hosts'] = hostlist['jumper']
        grouplist['_meta']['hostvars'][i[0]] = {'ansible_port': i[1], 'ansible_become_password': i[3]}

    return grouplist

def group(data):
    print json.dumps(data, indent=4)


def host(data, ip):
    dict_host = {}
    print json.dumps(dict_host,indent=4)

if __name__ == "__main__":
    global file,con,cur
    conn = pymysql.connect(host='xx', database='xx', user='xx', password='xx')
    sql = 'SELECT a.ip_out,a.ssh_port,b.name,b.password from asset as a right join region as b on a.id = b.jumper_id'
    cur = conn.cursor()
    cur.execute(sql)
    re = cur.fetchall()
    host_data = execude_sql_hosts(re)
    group_data = execude_sql_group(re)

    parser = argparse.ArgumentParser()
    parser.add_argument('--list',action='store_true',dest='list',help='get all hosts')
    parser.add_argument('--host',action='store',dest='host',help='get sigle host')
    args = parser.parse_args()
    if args.list:
        group(group_data)
    if args.host:
        host(host_data, args.host)