ansible学习笔记

工具介绍

Ansible是什么

Ansible 的网站上将之解释为 “一个超级简单的 IT 自动化引擎,可以自动进行云供给、配置管理、应用部署、服务内部编排,以及其他很多 IT 需求。” 通过在一个集中的位置定义好服务器集合,Ansible 可以在多个服务器上执行相同的任务。

如果你对 Bash 的 for 循环很熟悉,你会发现 Ansible 操作跟这很类似。区别在于 Ansible 具有幂等性。通俗来说就是 Ansible 一般只有在确实会发生改变时才执行所请求的动作。比如,假设你执行一个 Bash 的 for 循环来为多个机器创建用户,像这样子:

for server in serverA serverB serverC; do ssh ${server} "useradd myuser"; done

这会在 serverA、serverB,以及 serverC 上创建 myuser 用户;然而不管这个用户是否存在,每次运行这个 for 循环时都会执行 useradd 命令。一个幕等的系统会首先检查用户是否存在,只有在不存在的情况下才会去创建它。当然,这个例子很简单,但是幂等工具的好处将会随着时间的推移变得越发明显。

Ansible工作机制

Ansible 会将 Ansible playbooks 转换成通过 SSH 运行的命令,这在管理类 UNIX 环境时有很多优势:

  • 1、绝大多数类 UNIX 机器默认都开了 SSH。
  • 2、依赖 SSH 意味着远程主机不需要有代理。
  • 3、大多数情况下都无需安装额外的软件,Ansible 只需要 2.6 或更新版本的 Python。
  • 4、Ansible 无需主节点。他可以在任何安装有 Ansible 并能通过 SSH 访问的主机上运行。

安装Ansible

若你使用的是 Fedora,输入下面命令:

sudo dnf install ansible -y

若运行的是 CentOS,你需要为 EPEL 仓库配置额外的包,然后再使用 yum 来安装 Ansible:

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

对于基于 Ubuntu 的系统,可以从 PPA 上安装 Ansible:

sudo apt-get install software-properties-common -y
sudo apt-add-repository ppa:ansible/ansible
sudo apt-get update
sudo apt-get install ansible -y

若你使用的是 macOS,那么推荐通过 Python PIP 来安装:

sudo pip install ansible

更多版本安装参见Ansible安装指南

工具架构

Inventory(库存清单)

Ansible 使用一个 INI 风格的文件来追踪要管理的服务器,这种文件被称之为库存清单Inventory。默认情况下该文件位于 /etc/ansible/hosts。

# This is the default ansible 'hosts' file.
#
# It should live in /etc/ansible/hosts
#
#   - Comments begin with the '#' character
#   - Blank lines are ignored
#   - Groups of hosts are delimited by [header] elements
#   - You can enter hostnames or ip addresses
#   - A hostname/ip can be a member of multiple groups

# Ex 1: Ungrouped hosts, specify before any group headers.

## green.example.com
## blue.example.com
## 192.168.100.1
## 192.168.100.10

# Ex 2: A collection of hosts belonging to the 'webservers' group

[webservers]
# alpha.example.org
# 192.168.1.100
192.168.2.19

[dbservers]
192.168.2.15
# If you have multiple hosts following a pattern you can specify
# them like this:

## www[001:006].example.com

# Ex 3: A collection of database servers in the 'dbservers' group

[redhats]
192.168.2.15

[debians]
192.168.2.19
##
## db01.intranet.mydomain.net
## db02.intranet.mydomain.net
## 10.25.1.56
## 10.25.1.57

# Here's another example of host ranges, this time there are no
# leading 0s:
## db-[99:101]-node.example.com

每个分组由中括号和组名标识(像这样 [webservers] ),是应用于一组服务器的任意组名。一台服务器可以存在于多个组中,没有任何问题。在这个案例中,我有根据操作系统进行的分组(debians、redhats),也有根据服务器功能进行的分组(webservers、dbservers)。Ansible 主机文件可以处理比这复杂的多的情况。

Ad-Hoc Commands(临时命令)

临时命令是你可以键入的命令,它可以使你快速完成某些操作,但不想保存以备后用。

这是一个开始学习Ansible,在学习playbook(剧本)语言之前,可以用来做基础知识的好方法。

常用模块

command模块:

默认使用command模块。

ansible all -a 'ls -al'

shell模块:

功能类似command模块,但是shell模块更强大。可以使用 |、$等特殊符号。

ansible all -m shell -a 'ls -al | grep root'

file模块

用来操作file,更改文件属性、拥有者、创建、删除文件等操作。

#创建文件,设置权限,拥有权
ansible all -m file -a "path=/srv/foo/b.txt mode=600 owner=root group=root state=touch"

#创建目录,设置权限,拥有权
ansible all -m file -a "path=/root/test mode=755 owner=root group=root state=directory" 

#删除操作
ansible all -m file -a "path=/root/test state=absent"

#link操作
ansible all -m file -a "src=/path path=/path state=link"

yum模块

centos系统用来安装、删除package。

#安装package。
ansible redhats -m yum -a 'name=httpd state=present'

#删除package。
ansible redhats -m yum -a 'name=httpd state=absent'

service模块

centos系统用来操作服务项模块。

ansible redhats -m service -a 'name=httpd state=started'

#enabled:开机启动
ansible redhats -m service -a 'name=httpd state=started enabled=yes'

copy模块

用来向受控端移动文件操作。

ansible redhats -m copy -a 'src=本地path dest=远端path'

#backup:备份
ansible redhats -m copy -a 'src=本地path dest=远端path backup=yes'

fetch模块

用来向控制端移动文件操作。

ansible redhats -m fetch -a 'src=远端path dest=本地path'

#backup:备份
ansible redhats -m fetch -a 'src=远端path dest=本地path backup=yes'

setup模块

用来获取系统消息,获取到的信息,均为ansible中自带的变量,可以在playbook中直接使用。

ansible all -m setup -a 'filter=**os**'

user模块

用来操作系统账户

ansible redhats -m user -a 'name=hello system=yes shell=/sbin/nologin uid=80 password=123456 home=yes state=present'

group模块

用来操作系统用户组

ansible redhats -m group -a 'name=hello system=yes gid=80 state=present'

template模块

用来向受控端推送文件,与copy模块比较,template模块可以根据系统特性,推送不同内容的同名文件。具体形式,在playbook中讲解。

playbook(剧本)

综述

剧本是真正简单的配置管理和多机部署系统的基础,与现有的系统不同,它非常适合部署复杂的应用程序。

剧本可以声明配置,它们可以实现受控主机执行命令的操作,即使不同的命令需要在特定的机器进行执行。他们可以同步或异步执行命令。

尽管可以运行/usr/bin/ansible主程序来执行临时任务,但更方便将剧本保留在源代码管理中,并用于推送你的配置或确保远程系统的配置符合规范。

ansible playbook(剧本)就是包含 ansible 指令的 YAML 格式的文件。

---
- hosts: all
  remote_user: root
  tasks:
    - name: run command
      shell: ls -al

剧本可以包含多个剧本。你可能有一本首先针对Web服务器,然后针对数据库服务器的剧本。例如:

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

语法

playbook采用YAML语法。特别注重空格和tab的使用。一个YAML文件中至少存在一个playbook。

  • hosts:在整个剧本中某任务里,我们需要指定哪些主机去执行我们的写的操作命令。

    #选取在host文件中标签为web的主机
    ---
    - hosts: web
    #选取在hosts文件中标签为redhats的主机
    ---
    - hosts: redhats
    #选取hosts文件中去重之后的全部主机
    ---
    - hosts: all
    
  • remote_user:为剧本、任务定义远程用户

    #以root用户权限登录,在ansible.cfg配置中,默认使用root身份进行登录
    ---
    - hosts: all
     remote_user: root
    #以hello用户权限登录,此时需要注意,由于配置ssh key时,配置为root免密码登录,所以以hello用户登录时,需要在执行命令中使用 -k 参数,也可以将hello用户配置为免密码登录。
    ---
    - hosts: all
     remote_user: hello
    
  • tasks:playbook中的任务,一个playbook应该存在一个tasks,每个任务的目标是执行带有特定参数的模块。变量可以在模块的参数中使用。

  • become:为任务更改远程用户,如果使用become,可能需要输入用户密码,在ansible-playbook命令中使用--ask-become-pass or -K参数

    #本playbook等同于在主机上以hello用户权限执行 sudo service nginx start
    ---
    - hosts: all
     remote_user: hello
     tasks:
       - service: name=nginx state=started
         become: yes
         become_method: sudo
    #本playcook等同于以root身份进行ssh登录,然后将用户换为hello。接下来的所有任务(tasks)都以hello用户身份执行。
    ---
    - hosts: webservers
     remote_user: root
     become: yes
     become_user: hello
    
  • name:每个任务中的动作可以指定名字,用于在执行playbook时进行output。如果不指定,就根据动作类型进行output。

    #本playbook在进行output时,执行的任务标签为start nginx service
    ---
    - hosts: all
     remote_user: root
     tasks:
       - name: start nginx service
         service: name=nginx state=started
    
  • ignore_errors:每个任务中的每个动作在执行时,都有可能会出错,有时主机一旦出错,会导致之前所有执行完毕的tasks全部回滚。想要跳过一些可以忽略的错误,需要使用该参数。 

    #本playbook在进行start httpd服务时,如果执行过程中遇到错误,仍然会继续执行,不会发生回滚操作。
    ---
    - hosts: all
     remote_user: root
     tasks:
       - name: start httpd service
         service: name=httpd state=started
         ignore_errors: True
    #如果是使用shell模块的情况,即可使用如下方法忽略错误。
    ---
    - hosts: all
     remote_user: root
     tasks:
       - name: run command
         shell: ls -al || /bin/true
    
  • vars:为playbook定义变量。在编写playbook时,有时需要设置变量,执行不同动作。

    #使用变量进行安装httpd服务
    ---
    - hosts: redhats
     remote_user: root
     vars:
       - pkname: httpd
     tasks:
       - name: yum {{ pkname }} service
         yum: name={{ pkname }} #使用变量执行动作时,需要使用 =。
    
  • vars_files:为playbook定义变量文件。

    #使用变量文件中定义的变量进行安装httpd服务
    ---
    - hosts: redhats
     remote_user: root
     vars_files:
       - var.yml
     tasks:
       - name: yum {{ pkname }} service
         yum: name={{ pkname }}
    #var.yml内容格式
    pkname: httpd
    

    (ansible中变量使用优先级为-e > tasks变量 > hosts 变量 > inventory变量)

  • template:template为ansible中的一个模块,放在这里讲,是因为在Ad-Hoc模式中template几乎不被使用,template模块用于实现复杂功能,template模块使用jinja2语言。

    #template模块,类似于copy模块,但是copy模块只能copy相同文件到受控端。template模块可以根据变量,生成不同文件上传到特定受控端。
    ---
    - hosts: webservers
     remote_user: root
     tasks:
       - name: copy conf
         template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
    
  • handlers:如果动作对主机发生了更改,即去执行相对应操作。与notify配合使用。

    ---
    - hosts: redhats
     remote_user: root
     tasks:
       - name: copy conf
         template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
         notify:
            - restart nginx
     handlers:
       - name: restart nginx
         service: name=nginx state=restarted
    
  • when:用于跳过特定主机上的特定动作,条件判断。

    tasks:
    - name: "shut down Debian flavored systems"
      command: /sbin/shutdown -t now
      when: ansible_facts['os_family'] == "Debian"
    #使用括号对条件进行分组
    tasks:
    - name: "shut down CentOS 6 and Debian 7 systems"
      command: /sbin/shutdown -t now
      when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
          (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")
    

循环

ansible中的循环都是借助迭代来实现的。基本都是以"with_"开头。以下是常见的几种循环。

  • with_items迭代列表
#安装httpd、nginx、vsftpd服务
---
- hosts: redhats
 tasks: 
   - yum: name={{ item }} state=installed
     with_items: 
       - httpd
       - nginx
       - vsftpd
#指定文件列表,然后使用grep搜索出给定文件列表中包含"www.example.com"字符串的文件
---
- hosts: redhats
 tasks: 
   - shell: grep -Rl "www\.example\.com" "{{ item }}"
     with_items: 
       - file1
       - file2
       - file3
     register: match_file #将with_items迭代后的结果注册为变量时,其注册结果也是列表式的,且其key为"results"。
   - debug: msg="{% for i in match_file.results %} {{i.stdout}} {% endfor %}"
#上面,是使用for循环进行引用的。如果不使用for循环,那么就需要使用数组格式。
- debug: var=match_file.results[0].stdout
- debug: var=match_file.results[1].stdout
- debug: var=match_file.results[2].stdout
#with_items迭代的是列表项,也能迭代列表中的各字典
---
- hosts: localhost
 tasks: 
   - shell: echo "name={{item.name}},age={{item.age}}"
     with_items: 
       - {name: zhangsan,age: 32}
       - {name: lisi,age: 33}
       - {name: wangwu,age: 35}
     register: who
   - debug: msg="{% for i in who.results %} {{i.stdout}} {% endfor %}"
  • with_dict迭代字典
#使用"with_dict"迭代时,使用"item.key"表示字典的key,"item.value"表示字典的值。
---
- hosts: redhats
 tasks:
   - debug: msg="{{item.key}} & {{item.value}}"
     with_dict: { address: 1,netmask: 2,gateway: 3 }
#直接引用playbook中定义的vars
---
- hosts: redhats
gather_facts: False
vars:
 user: 
   longshuai_key: 
      name: longshuai
      gender: Male
   xiaofang_key: 
      name: xiaofang
      gender: Female
tasks:
 - name: print hash loop var
   debug: msg="{{ item.key }} & {{ item.value.name }} & {{ item.value.gender }}"
   with_dict: "{{ user }}"
  • with_fileglob迭代文件
---
- hosts: redhats
 tasks: 
   - copy: src="{{item}}" dest=/tmp/
       with_fileglob:
         - /tmp/*.sh
         - /tmp/*.py
  • with_lines迭代行
#with_lines可以将命令行的输出结果按行迭代
---
- hosts: redhats
 tasks:
   - copy: src="{{item}}" dest=/tmp/yaml
     with_lines:
       - find /tmp -type f -name "*.yml"
  • with_nested嵌套迭代
#嵌套迭代是指多次迭代列表项
---
- hosts: localhost
 tasks:
   - debug: msg="{{item[0]}} & {{item[1]}}"
     with_nested: 
       - [a,b]
       - [1,2,3]

 

后记

以上这些,只是接触到ansible这款自动化运维工具之后,通过自主学习获得到的一点皮毛知识,如果各位读者觉得作者哪里说得不正确、不严谨。可以留言讨论。




posted @ 2019-11-22 15:28  青山堂  阅读(417)  评论(0编辑  收藏  举报