Ansible-剧本与变量-程序员宅基地

技术标签: ansible  

对于运维小伙伴来讲,Ansible并不陌生,配置简单,上手容易,只要掌握几个基本的模块就可以解决好多运维中重复的事,但是对于处理更为高级的功能和更大、更复杂的项目时,管理和维护Ansible Playbook或高效使用将变得更加困难。

下面的的playbook是一个k8s安装环境初始化的剧本,其实现方式简单,是在k8s集群中所有节点都需要做的一些处理,实现如下功能

  • 配置firewallselinux,配置hosts
  • 关闭swap
  • 配置yum
  • 安装docker-ce,导入缺少的镜像,配置docker加速
  • 安装k8s相关包:kubelet、kubeadm、kubectl
  • 启动kubelet服务
- name: init k8s
  hosts: all
  tasks:
    # 关闭防火墙
    - shell: firewall-cmd --set-default-zone=trusted
    # 关闭selinux
    - shell: getenforce
      register: out
    - debug: msg="{
   {out}}"
    - shell: setenforce 0
      when: out.stdout != "Disabled"
    - replace:
        path: /etc/selinux/config
        regexp: "SELINUX=enforcing"
        replace: "SELINUX=disabled"
    - shell: cat /etc/selinux/config
      register: out
    - debug: msg="{
   {out}}"
    - copy:
        src: ./hosts
        dest: /etc/hosts
        force: yes
   # 关闭交换分区
    - shell: swapoff -a
    - shell: sed -i '/swap/d' /etc/fstab
    - shell: cat /etc/fstab
      register: out
    - debug: msg="{
   {out}}"
    # 配置yum源
    - shell: tar -cvf /etc/yum.tar /etc/yum.repos.d/
    - shell: rm -rf /etc/yum.repos.d/*
    - shell: wget ftp://ftp.rhce.cc/k8s/* -P  /etc/yum.repos.d/
    # 安装docker-ce
    - yum:
        name: docker-ce
        state: present
    # 配置docker加速
    - shell: mkdir /etc/docker
    - copy:
        src: ./daemon.json
        dest: /etc/docker/daemon.json
    - shell: systemctl daemon-reload
    - shell: systemctl restart docker
    # 配置属性,安装k8s相关包
    - copy:
        src: ./k8s.conf
        dest: /etc/sysctl.d/k8s.conf
    - shell: yum install -y kubelet-1.21.1-0 kubeadm-1.21.1-0 kubectl-1.21.1-0 --disableexcludes=kubernetes
    # 缺少镜像导入
    - copy:
        src: ./coredns-1.21.tar
        dest: /root/coredns-1.21.tar
    - shell: docker load -i /root/coredns-1.21.tar
    # 启动服务
    - shell: systemctl restart kubelet
    - shell: systemctl enable kubelet

如果搭建的集群节点很多,那么使用ansible要方便很多,但是上面的剧本没有使用角色,所有的操作都耦合在一起,所以看起来不是特别清晰,可读性差,而且一些可变的变量也没有抽离出来。复用性差,也没有考虑失败回滚的问题,大部分的操作是通过shell模块来完成的,尤其是对一些文件的操作,shell模块不满足幂等性。

高效的使用Ansible不仅仅在于功能或工具的使用,对于实践方法和项目组织更重要,对于剧本的编写规范,有以下三点:

  • 保持简单
  • 井然有序
  • 经常测试

保持简单

Ansible 的一大优势是简洁性。使用playbook保持简单,我们就能更加轻松地使用、修改和理解它们。

保持 Playbook 的可读性

  • 确保playbook有恰当注释且易于阅读。合理地使用垂直空白和注释。
  • 始终为play和任务提供有意义的名称,明确play或任务的用途。
  • 对于剧本编写文件格式,YAML 它非常适合表述⼀系列的字典和数组。
  • 对于难以在Ansible Playbook 中表述⼀些复杂的控制结构或条件,可以通过模板Jinja2过滤器巧妙地处理变量中的数据。
  • 使用原生 YAML 语法,而不是“折叠”的语法,以下示例不是推荐的格式:
- hosts: node1,node2
  tasks:
    - yum: name=httpd state=present
    - copy: content="RHCE Test" dest=/var/www/html/index.html force=yes
    - service: name=httpd state=restarted enabled=yes
    - service: name=firewalld state=restarted enabled=yes
    - firewalld: service=http state=enabled permanent=yes immediate=yes

使用现有的模块

  • 编写新playbook时,从基础playbook开始,并尽可能使用静态清单
  • 在构建设计时,将debug 模块用作测试或存根。
  • playbook按预期工作后,使用importincludeplaybook分成较小的逻辑组件。
  • 尽量使用Ansible中包含的特殊用途模块,而不是command、shell、raw这样的通用模块。使用为特定任务设计的模块可以轻松地使 Playbook 具有幂等性,且易于维护。

遵循标准样式

编写Ansible项目时,应考虑和同时遵循标准的样式:遵循统一的标准有助于提高可维护性和可读性。

  • 缩进多少个空格
  • 如何使用垂直空白
  • 如何命名任务剧本角色和变量
  • 应对什么进行注释
  • 如何注释

井然有序

Ansible项目的组织和Playbook的运行方式有助于维护、故障排除和审计

遵循变量命名约定

因为 Ansible 具有相对扁平的命令空间,所以变量名非常重要。应使用描述性变量且应阐明内容,如 apache_tls_port ,在角色中给最好能给角色变量添加前缀,如myapp_apache_tls_port 。

标准化项目结构

在文件系统上构建 Ansible 项目时,请使用统一的模式,推荐的示例:在这里插入图片描述

Playbook 结构的一大优势在于,可以将较⼤的playbook分成较小的⽂件,使其更易阅读,而较小的子playbook 可能会包含可以独立运行的、适合特定用途的 play

 

使用动态清单

动态清单支持从⼀个真实的中央来源集中管理主机和组,并确保清单自动更新。动态清单一般与云提供商、容器和虚拟机管理系统结合使用。

如果无法使用动态清单,则其它工具可以动态构建组或其他信息。group_by 模块根据事实动态生成组成员资格,该组成员资格对 playbook 的其余部分有效。

# Create nested groups
- group_by:
    key: el{
   { ansible_distribution_major_version }}-{
   { ansible_architecture }}
    parents:
      - el{
   { ansible_distribution_major_version }}

TASK [group_by] ****************************************************************************************************
task path: /root/ansible/group_by.yaml:5
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
changed: [192.168.26.82] => {
    "add_group": "el7-x86_64",
    "changed": true,
    "parent_groups": [
        "el7"
    ]
}
changed: [192.168.26.81] => {
    "add_group": "el7-x86_64",
    "changed": true,
    "parent_groups": [
        "el7"
    ]
}

充分利用组

主机可以是多个组的成员,可以按以下特征将主机划分不同的种类:

  • 地理位置
  • 环境
  • 站点或服务

将角色用于可重复使用的内容

  • 角色可以是 playbook 保持简单,能够通过重复利用项目间的通用代码来减少工作量。
  • 通过变量使角色成为可配置的通同角色,以便在将它们用于⼀组不同的playbook时无需对其进行编辑。
  • 使用ansible-galaxy init命令来初始化角色的目录结构。
  • RHEL 中的redhat-system-roles 软件提供的角色受到官方支持。
  • 也可以通过Ansible Galaxy 提供的角色,但是注意其质量和安全。
  • 将角色保存在项目的roles子目录中。

集中运行 Playbook

使用一个专用的控制节点来控制对系统的访问和审计 Ansible 活动,让所有的 Ansible Playbook 都从上面运行。

系统管理员仍应在系统上拥有自己的账户,以及用于连接受管主机的凭据,并在需要时可以进行权限提升。当系统管理员离职时,因从受管主机的authorized_keys文件中删除其 SSH 密钥,同时撤销其 sudo 权限。也可以考虑使用红帽 Ansible Tower 作为中央控制节点。

经常测试

在开发过程中、任务运行时以及Playbook投入使用后,应经常测试 Playbook 和 task

测试任务的结果

如果需要确认任务是否成功,请验证任务的结果,而不要信任模块的返回代码

- start web server 
  service: 
    name: httpd 
    status: started
- name: Check web site from web server 
  uri: 
    ur1: http://{
   { ansible_fqdn}}
    return_content: yes 
  register: example_webpage 
  failed_when: example_webpage. status !=200

使用 Block/Rescue 来恢复或回滚

block 指令可用于对任务进行分组,与 rescue 指令结合使用时,可帮助从错误和故障中恢复。

---
- name: block test
  hosts: node1
  tasks:
    - block:
        - debug: msg="vg myvg not found"   #提示卷组没找到
        - debug: msg="create vg myvg .. .."   #做其他操作(比如创建这个卷组...)
      when: ('myvg' not in ansible_lvm.vgs)   #当卷组myvg不存在时
      rescue:
        - debug: msg="creating failed .. .."     #block失败时提示创建卷组失败
      always:
        - shell: vgscan          #列出卷组信息
          register: list         #保存到名为list的变量
        - debug: msg={
   {list.stdout_lines}}     #提示卷组扫描结果

使用最新的 Ansible 版本开发 Playbook

即使不在⽣产中使用最新版本的 Ansible,也应该定期针对 Ansible 的最新版本测试 playbook。这将避免在Ansible 模块和功能不断演变时出现的问题。

如果 playbook 在运行时显示警告或弃用消息,应注意它们并做出相应的调整。通常,Ansible 中的某⼀功能已弃用或有变化,则该项目会在删除或更改功能之前提早四个小版本提供弃用通知。

使用测试工具

使用 ansible-playbook --syntax-check 命令进行语法检测

┌──[[email protected]]-[~/ansible]
└─$ansible-playbook group_by.yaml --syntax-check

playbook: group_by.yaml
┌──[[email protected]]-[~/ansible]
└─$echo 22 >> group_by.yaml
┌──[[email protected]]-[~/ansible]
└─$ansible-playbook group_by.yaml --syntax-check
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: No JSON object could be decoded

Syntax Error while loading YAML.
  could not find expected ':'

The error appears to be in '/root/ansible/group_by.yaml': line 11, column 1, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:


22
^ here

使用 ansible-playbook --check 命令,检查模式,针对check_mode中的实际受管主机运行 Playbook(不会改变主机状态),以查看Playbook执行的更改。此项检查不能保证完全准确性,因为 playbook 可能需要实际运行⼀些任务,playbook 中的后续任务才能正常运行。可能有⼀些标记有check_mode: no指令的任务。这些任务即使在检查模式中也会运行。

tasks:
  - name: This task will always make changes to the system
    ansible.builtin.command: /something/to/run --even-in-check-mode
    check_mode: no

  - name: This task will never make changes to the system
    ansible.builtin.lineinfile:
      line: "important config"
      dest: /path/to/myconfig.conf
      state: present
    check_mode: yes
    register: changes_to_important_config

一个Demo

下面我们来看一个完整的Demo,这个Demo做的事很简单,但是剧本编写清晰,在三台机器部署一个web服务,其中一台机器用haproxy作为负载,剩下的两台机器提供web能力(安装http服务并部署APP),剧本中创建了四个角色,用于描述四种行为:

  • 安装配置负载均衡器
  • 安装配置web服务器
  • 部署服务到web服务器
  • LB、HTTP 服务的firewall配置

配置、清单、主剧本文件编写

编写一个ansible.cfg 配置文件,这个不多讲,指定主机清单文件位置和ssh用户,配置sudo 提权方式。

[defaults]
inventory=inventory
remote_user=devops

[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False

inventory 主机清单文件,定义两个分组,

  • 作为LB的机器为 servera
  • 提供web能力的机器为serverb和serverc
[lb_servers]
servera.lab.example.com

[web_servers]
server[b:c].lab.example.com

site.yml为定义的实际执行的主剧本,这里通过,这里通过import_playbook模块来引入一个外部的调用角色的模块。一般情况下,当一个playbook很长很复杂,可以通过对剧本进行拆分。通过模块化的方式将多个playbook组合为一个完整的playbook,或者把文件中的任务列表插入到play中.

嗯,简单介绍下,ansible 可以使用两种方式实现剧本的模块化:

  • 包含内容:动态操作(include_task),在playbook运行期间,Ansible会在内容到达时处理包含的内容
  • 导入内容: 静态包含(import_task,import_playbook),在playbook运行之前,Ansible在最初解析的时候预处理导入的内容

和Java web体系中的Jsp脚本有些类似,通过include指令和include动作引入文件

我们可以看到,site.yml执行的三个剧本都是通过导入的方式。

- name: Deploy HAProxy
  import_playbook: deploy_haproxy.yml

- name: Deploy Web Server
  import_playbook: deploy_apache.yml

- name: Deploy Web App
  import_playbook: deploy_webapp.yml

执行顺序为,创建LB、创建web Serve,部署 web app,这里把剧本行为抽象为角色,然后在deploy_*里面调用角色,实现了行为和剧本的解耦。

调用角色剧本编写

看一下导入的执行角色的剧本deploy_haproxy.yml

- name: Ensure HAProxy is deployed
  hosts: lb_servers
  force_handlers: True

  roles:
    # The "haproxy" role has a dependency
    # on the "firewall" role. The
    # "firewall" role requires a
    # "firewall_rules" variable be defined.
    - role: haproxy
      firewall_rules:
        # Allow 80/tcp connections
        - port: 80/tcp

通过剧本执行LB角色,并且定义·变量firewall_rules,声明开放的端口协议,这里有一个force_handlers,我们看一下,剧本中handlers用于任务处理(布雷),可以设置一个或一块任务,但是他不会主动执行,需要通过notify通知触发(引爆),还有一些需要注意的点:

  • 每个剧本中handlers任务只会执行一次,即使收到多个任务的触发通知
  • handlers组的每一个任务都要设置名称(name)
  • handlers的层次与tasks平级
  • 其他任务在必要时,使用notify语句通知handlers任务名
  • 仅当发起notify的任务的执行状态为changed时,handlers任务才会被执行

看一个Demo

---
- name: handlers test
  hosts: node5
  tasks:
      - lvol: lv=vo001 size=100M vg=search   #创建逻辑卷vo001
      notify: mkfs     #如果changed则通知格式化(否则无需格式化)
  handlers:
    - name: mkfs     #定义格式化操作处理
      filesystem: dev=/dev/search/vo001 fstype=xfs force=yes 
...

那么这里的force_handlers即强制执行的意思,当触发他的通知对应的任务执行失败,但是handlers任然会执行,

deploy_apache.yml

- name: Ensure Apache is deployed
  hosts: web_servers
  force_handlers: True

  roles:
    # The "apache" role has a dependency
    # on the "firewall" role. The
    # "firewall" role requires a
    # "firewall_rules" variable be defined.
    - role: apache
      firewall_rules:
        # Allow http requests from any
        # internal zone source.
        - zone: internal
          service: http

        # Add servera, the load balancer,
        # to the internal zone.
        - zone: internal
          source: "172.25.250.10"

deploy_webapp.yml

- name: Ensure Web App is deployed
  hosts: web_servers
  vars:
    - webapp_version: v1.0

  roles:
    - role: webapp

这里需要说明下,vars定义的变量属于剧本变量,而在roles下面的变量为角色变量

自定义角色编写

我们来看一下角色,一共有四个角色,其中三个在上面的deplay_*.yaml 文件中被调用,firewall角色apache和haproxy依赖调用

  • apache http web服务器部署
  • firewall 防火墙部署配置
  • haproxy LB部署配置
  • wen app 能力部署
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles]
└─$ls
apache  firewall  haproxy  webapp

关于角色这里我们简单的回顾下

ansible 中的role指的是,为了方便复杂任务(包含大批量任务操作、模板、变量等资源)的重复使用,降低playbook剧本编写难度,而预先定义好的一套目录结构。

针对每一个角色,ansible会到固定的目录去调取特定的数据,关于角色在剧本中的使用,可以看看上面 deplay_*.yaml

角色内一般不指定hosts: 清单主机列表,而是交给调用此角色的剧本来指定,当然测试除外,具体看下

haproxy 角色

haproxy 角色 在剧本中负责LB 相关行为,简单看一下目录结构

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles]
└─$cd haproxy/ #角色根目录
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$tree
.
├── defaults      #定义变量的缺省值,优先级较低
│   └── main.yml  
├── handlers      #定义handlers处理任务
│   └── main.yml  
├── meta          #定义作者、版本、兼容性、依赖项等描述信息
│   └── main.yml  
├── tasks         #任务入口,最主要的文件
│   └── main.yml  
├── templates     #存放模板文件
│   └── haproxy.cfg.j2  
└── tests  # 用于角色测试
    ├── inventory
    └── test.yml

6 directories, 7 files

当然,这里的角色目录并不是最全的,正常的角色中还会有vars目录用于定义变量,相对于defaults优先级更高,files目录存放一些静态文件,README.md文件用于描述自述信息,我们通过init命令生成一个角色看一下目录

┌──[[email protected]]-[~/ansible/roles]
└─$ansible-galaxy init demo
- Role demo was created successfully
┌──[[email protected]]-[~/ansible/roles]
└─$ls
demo
┌──[[email protected]]-[~/ansible/roles]
└─$tree
.
└── demo
    ├── defaults
    │   └── main.yml
    ├── files
    ├── handlers
    │   └── main.yml
    ├── meta
    │   └── main.yml
    ├── README.md
    ├── tasks
    │   └── main.yml
    ├── templates
    ├── tests
    │   ├── inventory
    │   └── test.yml
    └── vars
        └── main.yml

9 directories, 8 files
┌──[[email protected]]-[~/ansible/roles]
└─$

嗯,回到我们的haproxy来看一下 defaults目录下的yaml文件用于定义一些缺省的变量。

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat defaults/main.yml
# Log-level for HAProxy logs
log_level: info

# Port exposed to clients
port: 80

# Name for the default backend
backend_name: app

# Port backend is exposed to
backend_port: 80

# The appservers variable is a list
# of backend servers that provide
# the web service that is proxied
# haproxy.  Each server must define:
# name, address, port. Below is
# and example structure:
# appservers:
#   - name: serverb.lab.example.com
#     ip_address: 1.2.3.4
#     port: 5000
#   - name: serverc.lab.example.com
#     ip_address: 1.2.3.5
#     port: 5000
# The default is no defined backend servers.
appservers: []

# Socket used to communicate with haproxy service. DO NOT CHANGE
socket: /var/run/haproxy.sock

handlers这个目录用于定义需要处理被激活的任务。这里定义了两个任务,都用到了Service模块

  • 重新启动haproxy服务
  • 重新加载haproxy服务配置文件
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat handlers/main.yml
---
# handlers file for haproxy

- name: restart haproxy
  service:
    name: haproxy
    state: restarted

- name: reload haproxy
  service:
    name: haproxy
    state: reloaded

看下meth元数据,作者信息,版本,以及通过dependencies我们可以看到该角色依赖角色firewall

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat meta/main.yml
galaxy_info:
  author: Ophelia Dunham
  description: A role to ensure deployment of HAProxy
  company: Example, Inc.
。。。。
  license: license (GPLv2, CC-BY, etc)

  min_ansible_version: 2.4
。。。。
  galaxy_tags: []
。。
dependencies:
  - name: firewall

这里我们简单聊聊角色依赖,角色依赖可以在使用角色时自动拉入其他角色。Ansible 执行角色依赖项,则必须使用关键字dependenciesmate文件夹下的main.yaml中声明在指定角色之前插入的角色和参数列表,我们这里的参数是定义在deploy_*.yaml

「主任务剧本」

  • 通过yum模块下载负载均衡工具haproxy和反向代理工具socat
  • 通过Service模块启动haproxy,并设置开启自启
  • 通过template模块,利用jieja2 模板,替换配置文件,这里处理完要通知前面重载配置文件的handlers
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat tasks/main.yml
---
# tasks file for haproxy
- name: Ensure haproxy packages are present
  yum:
    name:
      - haproxy
      - socat
    state: present

- name: Ensure haproxy is started and enabled
  service:
    name: haproxy
    state: started
    enabled: yes

- name: Ensure haproxy configuration is set
  template:
    src: haproxy.cfg.j2
    dest: /etc/haproxy/haproxy.cfg
    #validate: haproxy -f %s -c -q
  notify: reload haproxy

模板文件编写,这里用到了jieja2模板引擎,在一般的python web项目中用的比较多一点,这里简单的理解为变量替换。

haproxy.cfg.j2模板里用到了我们之前定义的大量变量,包括default目录的下main.yaml中定义的变量,以及appservers.yaml中的变量。

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat templates/haproxy.cfg.j2
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    #Send events/messages to rsyslog server.
    log         127.0.0.1:514 local0 {
   { log_level }}

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon

   #state changes due to API calls are stored in this file.
   server-state-file /usr/local/haproxy/haproxy.state

    # turn on stats unix socket
    #  required for the ansible haproxy module.
    stats socket {
   { socket }} level admin

    # utilize system-wide crypto-policies
    ssl-default-bind-ciphers PROFILE=SYSTEM
    ssl-default-server-ciphers PROFILE=SYSTEM

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
    mode                        http
    log                         global
    option                      httplog
    option                      dontlognull
    option http-server-close
    option forwardfor           except 127.0.0.0/8
    option                      redispatch
    retries                     3
    timeout http-request        10s
    timeout queue               1m
    timeout connect             10s
    timeout client              1m
    timeout server              1m
    timeout http-keep-alive     10s
    timeout check               10s
    maxconn                     3000

    #Loads state changes from the state file.
    load-server-state-from-file global

#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend main
    mode http
    bind *:{
   { port }}
    default_backend {
   { backend_name }}

#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend {
   { backend_name }}
    balance roundrobin
{% for server in appservers %}
    server {
   { server.name }} {
   { server.ip }}:{
   { backend_port }}
{% endfor %}

appservers 清单变量用于定义需要负载的机器域名和ip。这里为了角色的复用性,单独分离出来。

┌──[[email protected]]-[/home/student/DO447/labs/development-practices]
└─$cat appservers.yml

appservers:
  - name: serverb.lab.example.com
    ip: "172.25.250.11"
  - name: serverc.lab.example.com
    ip: "172.25.250.12"

剩下的就是测试相关的yaml文件,不多介绍,remote_user指定连接受管机的远程用户名

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat tests/inventory
localhost

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat tests/test.yml
---
- hosts: localhost
  remote_user: root
  roles:
    - haproxy

apache 角色

apache 角色用于提供http 服务,目录结构相对简单

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles]
└─$cd apache/
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/apache]
└─$tree
.
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
└── tests
    ├── inventory
    └── test.yml

3 directories, 4 files
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/apache]
└─$

meta 文件夹我们这里不多介绍了,涉及防火墙操作,所以依赖firewall角色,看一下主任务剧本

  • yum模块安装 http相关包
  • seboolean模块用于设置selinux开机自启,允许httpd_can_network_connect 访问网络
  • service模块用于启动http服务
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/apache]
└─$cat tasks/main.yml
---
# tasks file for apache

- name: Install http
  yum:
    name:
      - httpd
      - php
      - git
      - php-mysqlnd
    state: present

- name: Configure SELinux to allow httpd to connect to remote database
  seboolean:
    name: httpd_can_network_connect_db
    state: true
    persistent: yes

- name: http service state
  service:
    name: httpd
    state: started
    enabled: yes

webapp 角色

webapp角色用于部署web 项目到httpd服务,主要涉及缺省变量编写和主任务剧本。

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles]
└─$cd webapp/
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/webapp]
└─$tree
.
├── defaults
│   └── main.yml
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
└── tests
    ├── inventory
    └── test.yml

4 directories, 5 files
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/webapp]
└─$

defaults目录下的清单变量只有一个webapp_message,meta目录下的元数据不多介绍

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/webapp]
└─$cat defaults/main.yml
webapp_message: "This is"
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/webapp]
└─$cat meta/main.yml

主任务剧本中,用了一个dufault目录下的缺省变量和一个ansible的魔法变量,一个使用角色时定义的剧本变量。通过copy模块向http服务的引导页写入一句话。

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/webapp]
└─$cat tasks/main.yml
---
# tasks file for webapp

#- name: Copy the code from the repository
#  git:
#    repo: "{
   { webapp_repo }}"
#    version: "{
   { webapp_version }}"
#    dest: /var/www/html/
#    accept_hostkey: yes
##    key_file: deployment key??

- name: Copy a stub file.
  copy:
    content: "{
   { webapp_message }} {
   { ansible_hostname }}. (version {
   { webapp_version}})\n"
    dest: /var/www/html/index.html

最后来看一下firewall角色

firewall 角色

firewall 角色并没有被显示的调用,那么它是如何被调用的?

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles]
└─$cd firewall/
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/firewall]
└─$tree
.
├── defaults
│   └── main.yml
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
└── tests
    ├── inventory
    └── test.yml

5 directories, 6 files
┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/firewall]
└─$

这里就要讲到角色依赖,我们上面的haproxy角色apache角色都在meta/main.yaml 文件中依赖了firewall角色,所以haproxy角色apache角色在执行的时候要先执行firewall角色.

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/firewall]
└─$cat defaults/main.yml
---
# defaults file for firewall


# This role requires that firewall_rules variable
# be defined. The variable is a list of rules, and
# each rule defines:
#
#   service: (optional)
#   port:    (optional)
#   zone:    (optional)
#   source:  (optional)
#
# A rule should only define a service or a port.
# And example definition is:
#
# firewall_rules:
#   - service: http
#     zone: internal
#   - port: 8443
#     source: 192.168.0.2

# By default, no rules are implemented.
firewall_rules: []

一个重载firewall 配置文件的任务

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/firewall]
└─$cat handlers/main.yml
---
# handlers file for firewall

- name: reload firewalld
  service:
    name: firewalld
    state: reloaded

主任务文件,编写防火墙配置,在配置完通知上面的handlers

┌──[[email protected]]-[/home/student/DO447/labs/development-practices/roles/firewall]
└─$cat tasks/main.yml
---
# tasks file for firewall

- name: Ensure Firewall Sources Configuration
  firewalld:
    source: "{
   { item.source if item.source is defined else omit }}"
    zone: "{
   { item.zone if item.zone is defined else omit }}"
    permanent: yes
    state: "{
   { item.state | default('enabled') }}"
    service: "{
   { item.service if item.service is defined else omit }}"
    immediate: true
    port: "{
   { item.port if item.port is defined else omit }}"
  loop: "{
   { firewall_rules }}"
  notify: reload firewalld

对剧本的clean

当我们不需要这套环境了需要编写一个卸载当前环境的剧本clean.yml

- name: Clean Load Balancers
  hosts: lb_servers
  gather_facts: no
  tasks:
    - name: Remove packages
      yum:
        name: haproxy
        state: absent
    - set_fact:
        firewall_rules:
          - port: 80/tcp

- name: Clean Web Servers
  hosts: web_servers
  gather_facts: no
  tasks:
    - name: Remove packages
      yum:
        name: httpd
        state: absent
    - set_fact:
        firewall_rules:
          - zone: internal
            service: http
          - zone: internal
            source: 172.25.250.10



- name: Clean Firewall rules
  hosts: lb_servers, web_servers
  tasks:
    - name: Ensure Firewall Sources Configuration
      firewalld:
        source: "{
   { item.source if item.source is defined else omit }}"
        zone: "{
   { item.zone if item.zone is defined else omit }}"
        permanent: yes
        state: 'disabled'
        service: "{
   { item.service if item.service is defined else omit }}"
        port: "{
   { item.port if item.port is defined else omit }}"
      loop: "{
   { firewall_rules }}"

    - name: reload firewalld
      service:
        name: firewalld
        state: reloaded

- name: Remove web application
  hosts: web_servers
  tasks:
    - name: Remove stub file
      file:
        state: absent
        path: "/var/www/html/index.html"


下面是Demo完整的结构

┌──[[email protected]]-[/home/student/DO447/labs/development-practices]
└─$ls
ansible.cfg     clean.yml          deploy_haproxy.yml  inventory  site.yml
appservers.yml  deploy_apache.yml  deploy_webapp.yml   roles
┌──[[email protected]]-[/home/student/DO447/labs/development-practices]
└─$
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/GGB_GG/article/details/135587665

智能推荐

毕设分享 基于STM32的智能水产养殖系统_毕业设计水产养殖-程序员宅基地

文章浏览阅读238次。这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是毕设分享 基于STM32的智能水产养殖系统(源码+硬件+论文)学长这里给一个题目综合评分(每项满分5分)难度系数:3分工作量:3分创新点:4分包含内容。_毕业设计水产养殖

【资料分享】基于单片机大气压监测报警系统电路方案设计、基于飞思卡尔的无人坚守点滴监控自动控制系统设计(程序,原理图,pcb,文档)_基于单片机和无线通信的压强压力报警系统-程序员宅基地

文章浏览阅读1.2k次,点赞21次,收藏21次。如果我们说“10元给我汽车小零花钱妈妈”,或者“妈妈给我10亿人民币,我要买F-22”,妈妈就会觉得奇怪,听不懂我们的意思,或者理解错误,责备我们。汇编语言是计算机发展初期的一门语言,它的执行效率非常高,但是开发效率非常低,所以在常见的应用程序开发中不会使用汇编语言,只有在对效率和实时性要求极高的关键模块才会考虑汇编语言,例如操作系统内核、驱动、仪器仪表、工业控制等。我们说的是汉语,是“中国语言”,只要把我们的需求告诉父母,父母就会满足,我们用“中国语言”来控制父母,让父母做我们喜欢的事情。_基于单片机和无线通信的压强压力报警系统

Python里的全局变量、局部变量、类的self.*-程序员宅基地

文章浏览阅读853次,点赞20次,收藏21次。全局变量没有在任何函数内部定义,并且具有全局范围的变量;局部变量的定义:在函数内部定义的变量,范围仅限于该函数;是在一个函数内部声明一个全局变量,以便在函数内部对全局变量进行修改,这个全局变量本身已经定义过了,而不是我想当然认为的定义了一个全新的全局变量!️如果没有将其声明为全局变量,则在函数内部更改或创建的任何变量都是局部变量。当全局变量和局部变量的命名一样时,在一个函数内部还是会使用自己内部的局部变量;

国内可访问GPG Keyserver列表-程序员宅基地

文章浏览阅读1.7w次,点赞3次,收藏5次。apt-get update 或者aptitude update出现以下错误:The following signatures couldn't be verified because the public key is not available: : NO_PUBKEY B5B7720097BB3B58解决方法:1,从任何一个key server获得缺失的公钥B5B7720_keyserver

51-矩阵键盘和矩阵键盘密码锁_矩阵键盘密码锁如何不可见-程序员宅基地

文章浏览阅读907次,点赞3次,收藏4次。矩阵键盘和矩阵键盘密码锁_矩阵键盘密码锁如何不可见

vulnhub靶场——Hacker-Kid-v1-0-1_hacker_kid-程序员宅基地

文章浏览阅读5.9k次。vulnhub靶场——Hacker-Kid-v1-0-1 WP_hacker_kid

随便推点

使用Autolayout实现UITableView的Cell动态布局和高度动态改变_ios cell布局完成会调用什么方法?-程序员宅基地

文章浏览阅读604次。http://blog.163.com/china_uv/blog/static/1171372672014111681232340/本文翻译自:stackoverflow有人在stackoverflow上问了一个问题:如何在UITableViewCell中使用Autolayout来实现Cell的内容和子视图自动计算行高,并且能够保持平滑滚动的?_ios cell布局完成会调用什么方法?

《剑指 Offer》专项突破版 - 面试题 88 : 动态规划的基础知识(C++ 实现)-程序员宅基地

文章浏览阅读1k次,点赞29次,收藏16次。面试题 88 : 爬楼梯的最少成本(C++ 实现)

argmin是什么意思-程序员宅基地

文章浏览阅读9.3k次,点赞13次,收藏11次。举个简单的例子,考虑一个函数 f(x),其中 x 是实数。如果 f(x) 在 x = 3 时取得最小值,那么。经常用于描述需要找到某个函数的最小值时,所需的自变量的值。是一个数学术语,用于表示一个函数在其定义域中取得最小值的参数值(自变量的值),而不是最小值本身。表示函数的自变量(通常是一个实数或向量),当输入到该函数后,使函数取得最小值的自变量的值。的名称来源于 “argument of the minimum”,意思是最小值的参数。这表示当 x = 3 时,函数 f(x) 的值最小。_argmin

ssm/php/node/python基于人脸识别的学生上课考勤与监控系统_人脸识别考勤系统上课-程序员宅基地

文章浏览阅读919次,点赞21次,收藏12次。该系统能够有效提高考勤的准确性和效率,减少人为错误和作弊行为,确保考勤数据的真实性和可靠性。这一技术的应用也体现了教育信息化的发展趋势,有助于培养学生的信息素养,为他们适应未来社会的发展奠定基础。在教育领域,传统的考勤方式如点名、签到等不仅耗时耗力,而且容易出现代签、漏签等问题,影响了考勤的准确性和效率。这种系统可以无缝集成到现有的校园网络中,通过实时比对数据库中存储的学生面部信息,快速准确地完成考勤工作,大大减轻了教师的管理负担,提高了教学管理的效率和质量。文末可领取本课题的JAVA源码参考。_人脸识别考勤系统上课

该选择算法还是开发_开发与算法-程序员宅基地

文章浏览阅读8.5k次,点赞10次,收藏21次。最近有很多同学问小猿,我该选择算法还是开发?这本来不应该是一个问题,一个标准的答案就是你喜欢什么就做什么。但是,因为这样那样的问题,很动人开始动摇,我们把大家动摇的原因总结了一下,我们一条一条为大家分析:我喜欢算法,但是算法竞争太大了,我想转开发我读研了,感觉不做算法亏了我非科班,想转计算机,应该学什么我喜欢算法,但是算法竞争太大了,我想转开发算法岗五大标配:论文、比赛、实习、项目..._开发与算法

一文解决scrapy带案例爬取当当图书_scrapy爬虫当当网-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏43次。Scrapy框架简介Scrapy的五大组件Spiders(爬虫):它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器)Engine(引擎):负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。Scheduler(调度器):它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。Down_scrapy爬虫当当网