ansible笔记(二)

我是先在本地用Markdown记的笔记,然后直接粘贴到博客园,可能格式上会有些问题,还请谅解。

一、playbook和yaml语法

1.1 YAML语法

基本语法规则:

  1. 大小写敏感
  2. 使用缩进表示层级关系
  3. 缩进不允许使用Tab,只允许使用空格
  4. 缩进的空格数目不重要,相同层级的元素左侧对齐即可
  5. 使用#注释

支持三种数据结构

  1. 对象:键值对的集合
  2. 数组:一组按次序排列的值,以-开头
  3. 纯量:单个的,不可再分的值

1.2 playbook

ansible的配置、部署、编排语言。使用yaml语言编写。

1.2.1 示例:

- hosts: webservers	# 主机或组
  vars:	# 变量
    http_port: 80
    max_clients: 200
  remote_user: root	# 远程用户
  # sudo: yes	支持sudo执行命令
  tasks:
  - name: ensure apache is at the latest version	# 任务名(必须有)
    remote_user: yourname	# tasks下也可以定义远程用户
    sudo: yes	# 支持sudo执行命令
    sudo_user: postgres	# 登录到postfres用户
    yum: pkg=httpd state=latest	# 使用yum模块安装apache
    tags: t1	# 打标签
  - name: write the apache config file
  	# template模块
    template: src=/src/httpd.j2 dest=/etc/httpd.conf
    # 当/src/httpd.j2文件被改动时,重启apache
    notify:		# notify会在playbook的每一个task结束时被触发
    - restart apache
    tags: t2
  - name: ensure apache is running
    service: name=httpd
    tags: 
      - t3
      - tag3	# 添加多个标签

使用sudo时指定密码,在运行ansible-playbook命令时加上选项--ask-sudo-pass(-K)

每一个play包括一个task列表,前一个task执行完成才会执行下一个task

1.2.2 执行一个playbook

# 查看所有选项
ansible-playbook --help

# 检查playbook的语法是否正确
ansible-playbook --syntax-check playbook.yml

# 列出运行任务的主机
ansible-playbook --list-hosts playbook.yml

# 模拟执行playbook
ansible-playbook --check playbook.yml

# 普通的运行playbook
ansible-playbook playbook.yml

# 开启十个并发的进程执行playbook
ansible-playbook playbook.yml -f 10

# 根据tags指定执行哪一个任务,指定多个标签用逗号隔开
ansible-playbook --tags=xxx playbook.yml

# 根据tags指定不执行哪一个任务
ansible-playbook --skip-tags=xxx playbook.yml

1.2.3 notify和Handlers

一个notify示例:当/src/htpd.j2文件被改动时,重启apache,即使有多个task通知改动的发生,notify也只会触发一次。

- restart apache即是一个handlers,handlers其实是一种特殊的tasks。

- hosts: webservers
  ...
  tasks:
  ...
  - name: write the apache config file
    template: src=/src/httpd.j2 dest=/etc/httpd.conf
    notify:
    - restart apache

handlers示例

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

完整实例:

- hosts: webservers
  remote_user: root
  tasks:
    - name: write the apache config file
      template: src=/src/httpd.j2 dest=/etc/httpd.conf
      notify:
      - restart apache
  handlers:
    - name: restart apache
      service: name=apache state=restarted

1.2.4 Roles和Include语句

引入Roles和 Include的目的是为了提高playbook的重用性。

5.2.4.1 include

在多个playbook中重用同一个task列表

一个task include file由一个普通的task列表所组成

# 文件名:tasks/foo.yml

- name: placeholder foo
  command: /bin/foo
- name: placeholder bar
  command: /bin/bar

在playbook中使用include语句导入

tasks:
  - include: tasks/foo.yml

给include传递变量

tasks:
  - include: wordpress.yml wp_user=timmy
  - include: wordpress.yml wp_user=alice
  - include: wordpress.yml wp_user=bob
  
tasks:
  - include: wordpress.yml
    vars:
	wp_user: timmy
	some_list_variable:
	  - alpha
	  - beta
	  - gamma

include也可用在handlers语句中

# handlers/handlers.yml
- name: restart apache
  service: name=apache stae=restarted

然后在主playbook中使用include导入

handlers:
  - include: handlers/handlers.yml

将一个playbook导入另一个playbook

- name: this is a play at the top level of a file
  hosts: all
  remote_user: root

  tasks:

  - name: say hi
    tags: foo
    shell: echo "hi..."

- include: load_balancers.yml
- include: webservers.yml
- include: dbservers.yml

1.2.4.2 Roles

Roles基于一个已知的文件结构、自动的加载某些vars_files,tasks以及handlers

一个项目结构如下所示:

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

一个playbook如下:

- hosts: webservers
  roles:
    - common
    - webservers

以common为例,以上这段playbook的含义如下

  • 如果roles/common/tasks/main.yml存在,其中的tasks将添加到play中

  • 如果roles/common/handlers/main.yml存在,其中列出的handlers将被添加到play中

  • 如果roles/common/vars/main.yml存在,其中列出的variables将被添加到play中

  • 如果roles/common/meta/main.yml存在,其中列出的角色依赖将会添加到roles中

    这个角色依赖没看懂是干啥用的

  • 所有copy tasks都可以引用roles/common/file中的文件,不需要指明文件的路径

  • 所有script tasks可以引用roles/common/file中的脚本,不需要知名文件的路径

  • 所有template tasks可以引用roles/common/templates/中的文件,不需要指明文件的路径

  • 所有include tasks可以引用roles/common/tasks/中的文件,不需要指明文件的路径

Roles是playbook编排的一种方式,可以在使用Roles的同时在playbook中松散地列出 tasks,vars_files 以及 handlers,这种方式仍然可用

如果roles目录下有文件不存在也没有关系,这些文件将被忽略

参数化的roles

- hosts: webservers
  roles:
    - common
    - { role: foo_app_instance, dir: '/opt/a',  port: 5000 }
    - { role: foo_app_instance, dir: '/opt/b',  port: 5001 }

为roles设置触发条件

- hosts: webservers
  roles:
    - { role: some_role, when: "ansible_os_family ='RedHat'" }

给roles分配指定的tags

- hosts: webservers
  roles:
    - { role: foo, tags: ["bar", "baz"]}

定义一些 tasks,让它们在 roles 之前以及之后执行

- hosts: webservers

  pre_tasks:
    - shell: echo 'hello'

  roles:
    - { role: some_role }

  tasks:
    - shell: echo 'still busy'

  post_tasks:
    - shell: echo 'goodbye'

1.2.5 Variables 变量

1.2.5.1 合法的变量名

  • 变量名可以为字母,数字以及下划线.
  • 变量始终应该以字母开头.

1.2.5.2 在playbook中定义变量

直接定义变量

- hosts: webservers
  vars:
    http_port: 80

在文件和role中定义变量

roles/item/vars/

1.2.5.3 使用变量

ansible使用Jinja2模板系统引用变量

template: src=foo.cfg.j2 dest={{ remote_install_path }}/foo.cfg

{{ remote_install_path }}部分就是变量引用部分

使用外部变量

- hosts: all
  remote_user: root
  vars:
    favcolor: blue
  vars_files:
    - /vars/external_vars.yml
  tasks:
  - name: this is just a placeholder
    command: /bin/echo foo

vars/external_vars.yml文件内容如下:

somevar: somevalue
password: magic

命令行中传递变量

ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"

1.2.6 条件选择语句 when

when的值是一个条件表达式,如果条件处理,tasks才执行。

示例:

tasks:
  - name: "shutdown Debian flavored system"
    command: /sbin/shutdown -t now
    when: 
      - ansible_os_family == "Debian"

下面这段没看懂是啥意思

tasks:
  - command: /bin/false
    register: result
    ignore_errors: True
    # 当result等于failed时,执行
  - command: /bin/something
    when: result|failed
  - command: /bin/something_else
    when: result|success
  - command: /bin/still/something_else
    when: result|skipped

有些时候得到一个返回参数的值是一个字符串,并且你还想使用数学操作来比较它,那么可以执行以下操作:

tasks:
  - shell: echo "only on Red Hat 6, derivatives, and later"
    when: ansible_os_family == "RedHat" and ansible_lsb.major_release|int >= 6

如果一个变量不存在,使用Jinja2的defined命令跳过或略过

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

在roles和includes上应用when语句

- include: tasks/sometasks.yml
  when: "'reticulationg splines' in output"
- hosts: webservers
  roles:
    - { role: debain_stock_config, when: ansible_os_family == 'Debian' }

1.2.7 循环

1.2.7.1 标准循环 with_item模块

使用with_items循环模块创建两个用户testuser1和testuser2

- name: add serveral users
  user: name={{ item }} stat=present groups=wheel
  with_items:
    - testuser1
    - testuser2
- name: add several users
  user: name={{ item.name }} state=present groups={{ item.groups }}
  with_items:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'root' }

1.2.7.2 嵌套循环 with_nested模块

- name: give users access to multiple databases
  mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo
  with_nested:
    - [ 'alice', 'bob' ]
    - [ 'clientdb', 'employeedb', 'providerdb' ]

1.2.7.3 对哈希表使用循环 with_dict

有如下哈希表

users:
  alice:
    name: Alice Appleworth
    telephone: 123-456-7890
  bob:
    name: Bob Bananarama
    telephone: 987-654-3210

使用with_dict来循环哈希表中的元素

tasks:
  - name: Print phone records
    debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
    with_dict: "{{users}}"

debug: msg="User alice is Alice Appleworth (123-456-7890)"

debug: msg="User Bob is Bob Bananarama (987-654-3210)"

1.2.7.4 对文件列表使用循环 with_fileglob

---
- hosts: all

  tasks:

    # first ensure our target directory exists
    - file: dest=/etc/fooapp state=directory

    # copy each file over that matches the given pattern
    - copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600
      with_fileglob:
        - /playbooks/files/fooapp/*

1.2.7.5 对并行数据集使用循环 with_together

alpha: [  'a', 'b', 'c', 'd' ]
numbers: [ 1, 2, 3, 4 ]
tasks:
    - debug: msg="{{ item.0 }} and {{ item.1 }}"
      with_together:
        - "{{alpha}}"
        - "{{numbers}}"

1.2.7.6 对子元素使用循环 with_subelements

如下定义了一个复合结构的字典变量users,在with_subelements循环中,指定了users变量和users变量的子元素hobby

---
- hosts: testB
  remote_user: root
  gather_facts: no
  vars:
    users:
    - name: bob
      gender: male
      hobby:
        - Skateboard
        - VideoGame
    - name: alice
      gender: female
      hobby:
        - Music
  tasks:
  - debug:
      msg: "{{ item.0.name }}'hobby is {{item.1}}'"
    with_subelements:
    - "{{users}}"
    - hobby

执行效果为

bob's hobby is Skateboard
bob's hobby is VideoGame
alice's hobby is Music

item.0获取到users变量的name、gender部分,item.1获取到hobby部分

1.2.7.7 对整数序列使用循环 with_sequence

---
- hosts: all

  tasks:
    - group: name=evens state=present
    - group: name=odds state=present
    # 创建四个用户,用户名为testuser00到testuser32
    - user: name={{ item }} state=present groups=evens
      with_sequence: start=0 end=32 format=testuser%02x
    - file: dest=/var/stuff/{{ item }} state=directory
    # stride 跨度
      with_sequence: start=4 end=16 stride=2
    - group: name=group{{ item }} state=present
      with_sequence: count=4

1.2.7.8 随机选择 with_random_choice

- debug: msg={{ item }}
  with_random_choice:
     - "go through the door"
     - "drink from the goblet"
     - "press the red button"
     - "do nothing"

1.2.7.9 Do-Until循环

重复一个任务直到某个条件满足

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

递归运行shell模块,直到模块结果中的stdout输出中包含all systems go字符串,或者该任务按照10秒的延迟重试超过5次

1.2.7.10 循环中使用注册器

使用register来注册变量,结果包含一个results属性

示例:

- shell: echo "{{ item }}"
  with_items:
    - one
    - two
  register: echo

返回如下数据结构

{
    "changed": true,
    "msg": "All items completed",
    "results": [
        {
            "changed": true,
            "cmd": "echo \"one\" ",
            "delta": "0:00:00.003110",
            "end": "2013-12-19 12:00:05.187153",
            "invocation": {
                "module_args": "echo \"one\"",
                "module_name": "shell"
            },
            "item": "one",
            "rc": 0,
            "start": "2013-12-19 12:00:05.184043",
            "stderr": "",
            "stdout": "one"
        },
        {
            "changed": true,
            "cmd": "echo \"two\" ",
            "delta": "0:00:00.002920",
            "end": "2013-12-19 12:00:05.245502",
            "invocation": {
                "module_args": "echo \"two\"",
                "module_name": "shell"
            },
            "item": "two",
            "rc": 0,
            "start": "2013-12-19 12:00:05.242582",
            "stderr": "",
            "stdout": "two"
        }
    ]
}

在tasks中循环注册变量,用来检测结果值

- name: Fail if return code is not 0
  fail:
    msg: "The command ({{{ item.cmd }}}) did not have a 0 return code"
  when: item.rc != 0
  with_items: "{{ echo.results }}"
posted @ 2021-05-14 11:28  Charramma  阅读(85)  评论(0编辑  收藏  举报