Ansible 中变量的系统方法

ansible devops 代码风格

嘿! 我的名字是 丹尼斯·卡柳日内 我在开发过程自动化部门担任工程师。 每天,新的应用程序版本都会在数百个活动服务器上推出。 在这篇文章中,我分享了我使用 Ansible 实现这些目的的经验。

本指南提供了一种在部署中组织变量的方法。 本指南适用于那些已经在剧本中使用角色并阅读过的人 最佳实践,但面临类似的问题:

  • 在代码中找到一个变量后,不可能立即明白它负责什么;
  • 角色有好几个,变量需要与一个值关联,但就是不行;
  • 难以向他人解释你的剧本中变量的逻辑如何运作

我们在公司的项目中遇到了这些问题,因此我们在playbook中制定了变量设计规则,在一定程度上解决了这些问题。

Ansible 中变量的系统方法

角色中的变量

角色是部署系统的一个单独的对象。 与任何系统对象一样,它必须具有与系统其余部分交互的接口。 这样的接口就是角色变量。

我们以角色为例 api,它会在服务器上安装 Java 应用程序。 它可能有哪些变数?

Ansible 中变量的系统方法

变量角色按类型可分为2种:

1. Свойства
    a) независимые от среды
    б) зависимые от среды
2. Связи
    a) слушатели 
    б) запросы внутри системы
    в) запросы в среду

可变属性 是决定角色行为的变量。

查询变量 - 这些变量的值用于指定角色外部的资源。

可变监听器 - 这些变量的值用于形成请求变量。

另一方面,1a、2a、2b是不依赖于环境(硬件、外部资源等)的变量,可以在defaults角色中填充默认值。 然而,不可能用“example”以外的值填充类型1.b和2.c的变量,因为它们会根据环境的不同而变化。

代码风格

  • 变量名称必须以角色名称开头。 这将使将来很容易弄清楚该变量来自什么角色以及它负责什么。
  • 在角色中使用变量时,一定要遵循封装的原则,使用角色本身或当前角色所依赖的角色中定义的变量。
  • 避免使用字典作为变量。 Ansible 不允许您方便地覆盖字典中的单个值。

    错误变量的示例:

    myrole_user:
        login: admin
        password: admin

    这里登录名是自变量,密码是因变量。 但
    因为它们被组合成一个字典,所以你必须完整地指定它
    总是。 这是非常不方便的。 这样更好:

    myrole_user_login: admin
    myrole_user_password: admin

部署手册中的变量

在编译部署 playbook(以下简称 playbook)时,我们遵循将其放置在单独的存储库中的规则。 与角色相同:每个角色都有自己的 git 存储库。 这使您可以理解角色和剧本是部署系统的不同独立对象,一个对象的更改不应影响另一个对象的操作。 这是通过改变变量的默认值来实现的。

总而言之,在编译剧本时,可以在两个地方覆盖角色变量的默认值:剧本变量和库存变量。

mydeploy                        # Каталог деплоя
├── deploy.yml                  # Плейбук деплоя
├── group_vars                  # Каталог переменных плейбука
│   ├── all.yml                 # Файл для переменных связи всей системы
│   └── myapi.yml               # Файл переменных свойств группы myapi
└── inventories                 #
    └── prod                    # Каталог окружения prod
        ├── prod.ini            # Инвентори файл
        └── group_vars          # Каталог для переменных инвентори
            └── myapi           #
                ├── vars.yml    # Средозависимые переменные группы myapi
                └── vault.yml   # Секреты (всегда средозависимы) *

* - 变量和库

不同之处在于,当调用与其位于同一级别的 playbook 时,始终使用 playbook 变量。 这意味着这些变量非常适合更改与环境无关的变量的默认值。 相反,库存变量将仅用于特定环境,这对于特定于环境的变量来说是理想的。

需要注意的是,变量优先级不允许您先覆盖剧本变量中的变量,然后再单独覆盖一个清单中的变量。

这意味着在这个阶段就必须确定变量是否依赖于环境并将其放置在适当的位置。

例如,在一个项目中,负责启用 SSL 的变量在很长一段时间内依赖于环境,因为我们无法在其中一个展位上出于我们无法控制的原因启用 SSL。 我们解决了这个问题后,它变得独立于环境并转移到剧本变量中。

组的属性变量

让我们通过添加 1 组具有不同 Java 应用程序但设置不同的服务器来扩展图 2 中的模型。

Ansible 中变量的系统方法

让我们想象一下在这种情况下剧本会是什么样子:

- hosts: myapi
  roles:
    - api

- hosts: bbauth
  roles:
    - auth

- hosts: ghauth
  roles:
    - auth

我们在 playbook 中有三个组,因此立即建议在 group_vars 库存变量和 playbook 变量中创建相同数量的组文件。 在这种情况下,一组文件是对剧本中上述应用程序的一个组件的描述。 当您在剧本变量中打开组文件时,您会立即看到与组上安装的角色的默认行为的所有差异。 库存变量中:不同摊位的群体行为存在差异。

代码风格

  • 尽量不要使用 host_vars 变量,因为它们不描述系统,而只是一种特殊情况,这在将来会导致问题:“为什么这个主机与其他主机不同?”,答案不是总是很容易找到。

通讯变量

然而,这就是属性变量的全部内容,但是通信变量呢?
它们的区别在于它们在不同的群体中应该具有相同的含义。

起初是 这个想法 使用一个可怕的结构,例如:
hostvars[groups['bbauth'][0]]['auth_bind_port'],但他们立即拒绝了
因为它有缺点。 首先,体积大。 其次,对组内特定主机的依赖。 第三,在开始部署之前,如果我们不想收到未定义变量的错误,则有必要从所有主机收集事实。

因此,决定使用通信变量。

通讯变量 - 这些是属于剧本的变量,需要连接系统对象。

通信变量填充在通用系统变量中 group_vars/all/vars 和 是通过从每个组中删除所有侦听器变量并将侦听器被删除的组的名称添加到变量的开头而形成的。

这确保了名称的一致性和不重叠。

让我们尝试绑定上面示例中的变量:

Ansible 中变量的系统方法

假设我们有相互依赖的变量:

# roles/api/defaults:
# Переменная запроса
api_auth1_address: "http://example.com:80"
api_auth2_address: "http://example2.com:80"

# roles/auth/defaults:
# Переменная слушатель
auth_bind_port: "20000"

让我们把它放入公共变量中 group_vars/all/vars 所有听众,并将组名添加到标题中:

# group_vars/all/vars
bbauth_auth_bind_port: "20000"
ghauth_auth_bind_port: "30000"

# group_vars/bbauth/vars
auth_bind_port: "{{ bbauth_auth_bind_port }}"

# group_vars/ghauth/vars
auth_bind_port: "{{ ghauth_auth_bind_port }}"

# group_vars/myapi/vars
api_auth1_address: "http://{{ bbauth_auth_service_name }}:{{ bbauth_auth_bind_port }}"
api_auth2_address: "http://{{ ghauth_auth_service_name }}:{{ ghauth_auth_bind_port }}"

现在,通过更改连接器的值,我们将确保请求将发送到端口所在的同一位置。

代码风格

  • 由于角色和组是不同的系统对象,因此它们需要具有不同的名称,然后链接变量将准确地指示它们属于特定的服务器组,而不是系统中的角色。

环境相关文件

角色可能使用因环境而异的文件。

SSL 证书就是此类文件的一个示例。 以文本形式存储它们
在变量中不是很方便。 但将它们的路径存储在变量中会很方便。

例如,我们使用变量 api_ssl_key_file: "/path/to/file".

由于很明显密钥证书会随着环境的不同而变化,因此这是一个依赖于环境的变量,这意味着它应该位于文件中
group_vars/myapi/vars 变量清单,并包含值“例如”。

在这种情况下,最方便的方法是将密钥文件沿着路径放入 playbook 存储库中
files/prod/certs/myapi.key,那么变量的值将是:
api_ssl_key_file: "prod/certs/myapi.key"。 便利之处在于,负责在特定展位部署系统的人员在存储库中也有自己的专用空间来存储他们的文件。 同时,如果证书由另一个系统提供,仍然可以指定服务器上证书的绝对路径。

一个环境中的多个展位

通常需要在同一环境中部署几个几乎相同的展位,且差异极小。 在这种情况下,我们将环境相关变量分为在该环境内不发生变化的变量和发生变化的变量。 我们将后者直接转移到库存文件本身。 经过此操作后,可以直接在环境目录中创建另一个清单。

它将重用 group_vars 库存,并且还能够直接为自己重新定义一些变量。

部署项目的最终目录结构:

mydeploy                        # Каталог деплоя
├── deploy.yml                  # Плейбук деплоя
├── files                       # Каталог для файлов деплоя
│   ├── prod                    # Католог для средозависимых файлов стенда prod
│   │   └── certs               # 
│   │       └── myapi.key       #
│   └── test1                   # Каталог для средозависимых файлов стенда test1
├── group_vars                  # Каталог переменных плейбука
│   ├── all.yml                 # Файл для переменных связи всей системы
│   ├── myapi.yml               # Файл переменных свойств группы myapi
│   ├── bbauth.yml              # 
│   └── ghauth.yml              #
└── inventories                 #
    ├── prod                    # Каталог окружения prod
    │   ├── group_vars          # Каталог для переменных инвентори
    │   │   ├── myapi           #
    │   │   │   ├── vars.yml    # Средозависимые переменные группы myapi
    │   │   │   └── vault.yml   # Секреты (всегда средозависимы)
    │   │   ├── bbauth          # 
    │   │   │   ├── vars.yml    #
    │   │   │   └── vault.yml   #
    │   │   └── ghauth          #
    │   │       ├── vars.yml    #
    │   │       └── vault.yml   #
    │   └── prod.ini            # Инвентори стенда prod
    └── test                    # Каталог окружения test
        ├── group_vars          #
        │   ├── myapi           #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   ├── bbauth          #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   └── ghauth          #
        │       ├── vars.yml    #
        │       └── vault.yml   #
        ├── test1.ini           # Инвентори стенда test1 в среде test
        └── test2.ini           # Инвентори стенда test2 в среде test

加起来

按照文章组织变量后:每个变量文件负责一个特定的任务。 由于文件具有某些任务,因此可以指派专人负责每个文件的正确性。 例如,系统部署的开发人员负责正确填写剧本变量,而在清单中描述立场的管理员则直接负责填写变量清单。

角色成为具有自己接口的自己的开发单元,允许角色开发人员开发功能而不是根据系统定制角色。 这个问题尤其涉及战役中所有系统的共同角色。

系统管理员不再需要了解部署代码。 成功部署所需要做的就是填写环境相关变量的文件。

文学

  1. Документация

作者

卡柳日内·丹尼斯·亚历山德罗维奇

来源: habr.com

添加评论