如何开始测试 Ansible,在一年内重构项目而不发疯

如何开始测试 Ansible,在一年内重构项目而不发疯

这是文字记录 演出DevOps-40 2020-03-18:

从第二次提交开始,任何代码都将成为遗留代码,因为最初的想法开始偏离严酷的现实。 这既不好也不坏,这是一个难以争论的既定事实,并且必须接受。 这个过程的一部分是重构。 将基础设施重构为代码。 让故事从如何在一年内重构 Ansible 并且不疯狂开始吧。

遗产的诞生

第一天:零号病人

如何开始测试 Ansible,在一年内重构项目而不发疯

从前有一个有条件的项目。 它有一个开发团队和运营工程师。 他们正在解决同一个问题:如何部署服务器和运行应用程序。 问题是每个团队都以自己的方式解决这个问题。 在该项目中,决定使用 Ansible 在开发和运营团队之间同步知识。

第 89 天:遗产的诞生

如何开始测试 Ansible,在一年内重构项目而不发疯

他们自己没有注意到这一点,他们想尽可能地做到最好,但结果却成了遗产。 这是怎么发生的?

  • 我们这里有一项紧急任务,让我们进行一次肮脏的黑客攻击,然后修复它。
  • 您不必编写文档,这里发生的一切都很清楚。
  • 我了解 Ansible/Python/Bash/Terraform! 看我怎么躲!
  • 我是一名完整的 Stack Overflow 开发人员,从 stackoverflow 复制了这个,我不知道它是如何工作的,但它看起来很酷并且解决了问题。

结果,你可能会得到一类难以理解的代码,没有文档,不清楚它是做什么的,是否需要它,但问题是你需要开发它,修改它,添加拐杖和支持,使情况变得更加糟糕。

- hosts: localhost
  tasks:
    - shell: echo -n Z >> a.txt && cat a.txt
      register: output
      delay: 1
      retries: 5
      until: not output.stdout.find("ZZZ")

第 109 天:意识到问题

如何开始测试 Ansible,在一年内重构项目而不发疯

最初构想和实现的 IaC 模型不再满足用户/业务/其他团队的要求,对基础设施进行更改的时间也不再可接受。 此时此刻,我们明白是时候采取行动了。

IaC重构

第 139 天:你真的需要重构吗?

如何开始测试 Ansible,在一年内重构项目而不发疯

在急于重构之前,您必须回答一些重要问题:

  1. 为什么你需要这一切?
  2. 你有时间吗?
  3. 知识够用吗?

如果你不知道如何回答这些问题,那么重构就会在开始之前就结束,或者只会变得更糟。 因为有经验( 我从测试 200 行基础设施代码中学到了什么),然后该项目收到了帮助修复角色并通过测试覆盖它们的请求。

第 149 天:准备重构

如何开始测试 Ansible,在一年内重构项目而不发疯

首先要做的就是准备。 决定我们要做什么。 为此,我们进行沟通、查找问题领域并找出解决方法。 我们以某种方式记录由此产生的概念,例如一篇汇合的文章,这样当问题出现时“什么是最好的?” 或“哪个是正确的?” 我们还没有迷失方向。 就我们而言,我们坚持这个想法 分而治之:我们将基础设施分解成小块/砖块。 这种方法允许您采用一个孤立的基础设施,了解它的作用,通过测试覆盖它并对其进行更改,而不必担心会破坏任何内容。

如何开始测试 Ansible,在一年内重构项目而不发疯

事实证明,基础设施测试成为基石,这里值得一提的是基础设施测试金字塔。 与正在开发中的想法完全相同,但对于基础设施而言:我们正在从检查缩进等简单内容的廉价快速测试转向部署整个基础设施的昂贵的全面测试。

Ansible 测试尝试

在我们描述如何覆盖项目的 Ansible 测试之前,我将描述我之前有机会使用的尝试和方法,以便了解所做决策的背景。

第-997 天:SDS 规定

如何开始测试 Ansible,在一年内重构项目而不发疯

我第一次测试 Ansible 是在一个开发 SDS(软件定义存储)的项目中。 关于这个主题有一篇单独的文章
测试分发时如何用拐杖折断自行车,但简而言之,我们最终得到了一个倒置的测试金字塔,并且我们在一个角色上花费了 60-90 分钟的测试时间,这是一个很长的时间。 基础是 e2e 测试,即我们部署了一个完整的安装,然后对其进行了测试。 更让人恼火的是他自己发明了自行车。 但我必须承认,这个解决方案有效并且允许稳定发布。

第 #-701 天:Ansible 和测试厨房

如何开始测试 Ansible,在一年内重构项目而不发疯

Ansible 测试思想的发展是使用现成的工具,即 test kitchen/kitchen-ci 和 inspec。 这个选择是由对 Ruby 的了解决定的(更多详细信息,请参阅关于 Habré 的文章: YML 程序员梦想测试 Ansible 吗?)工作速度更快,40个角色大约需要10分钟。 我们创建了一组虚拟机并在其中运行测试。

如何开始测试 Ansible,在一年内重构项目而不发疯

总体而言,该解决方案有效,但由于不均匀性,存在一些沉积物。 当他们将测试数量增加到 13 个基本角色和 2 个组合较小角色的元角色时,测试突然开始运行 70 分钟,几乎延长了 2 倍。 谈论 XP(极限编程)实践很困难,因为…… 没有人愿意等70分钟。 这就是改变方法的原因

第#-601天:Ansible 和 molecular

如何开始测试 Ansible,在一年内重构项目而不发疯

从概念上讲,这与 testkitchen 类似,只是我们将角色测试移至 docker 并更改了堆栈。 结果,20个角色的时间减少到稳定的25-7分钟。

如何开始测试 Ansible,在一年内重构项目而不发疯

通过将测试角色数量增加到 17 个并检查 45 个角色,我们在 28 个 jenkins 从机上运行了 2 分钟。

第 167 天:向项目添加 Ansible 测试

如何开始测试 Ansible,在一年内重构项目而不发疯

最有可能的是,不可能很快完成重构任务。 这个任务必须是可衡量的,这样你就可以把它分成小块,然后用茶匙一块一块地吃掉大象。 必须了解自己是否在朝着正确的方向前进,还要走多长时间。

如何开始测试 Ansible,在一年内重构项目而不发疯

一般来说,如何完成并不重要,你可以写在一张纸上,你可以在衣柜上贴贴纸,你可以在 Jira 中创建任务,或者你可以打开 Google Docs 并记下当前状态那里。 腿的生长是因为这个过程不是立竿见影的,而是漫长而乏味的。 不太可能有人希望你在重构过程中失去想法、感到疲倦、不知所措。

重构很简单:

  • 吃。
  • 睡觉。
  • 码。
  • IaC 测试。
  • 重复新密码

我们重复此操作,直到达到预期目标。

如何开始测试 Ansible,在一年内重构项目而不发疯

可能无法立即开始测试所有内容,因此我们的首要任务是从 linting 和检查语法开始。

第 181 天:绿色构建大师

如何开始测试 Ansible,在一年内重构项目而不发疯

Linting 是迈向 Green Build Master 的一小步。 这几乎不会破坏任何东西,但它允许您调试流程并在 Jenkins 中进行绿色构建。 这个想法是在团队中养成习惯:

  • 红色测试很糟糕。
  • 我来修复一些东西,同时使代码比你之前更好一些。

第 193 天:从 linting 到单元测试

如何开始测试 Ansible,在一年内重构项目而不发疯

构建了将代码放入 master 的过程后,您可以开始逐步改进的过程 - 用启动角色替换 linting,您甚至可以在没有幂等性的情况下做到这一点。 您需要了解如何应用角色以及它们如何工作。

第 211 天:从单元测试到集成测试

如何开始测试 Ansible,在一年内重构项目而不发疯

当大多数角色都被单元测试覆盖并且所有内容都被检查时,您可以继续添加集成测试。 那些。 测试的不是基础设施中的单个砖块,而是它们的组合,例如完整的实例配置。

如何开始测试 Ansible,在一年内重构项目而不发疯

使用 jenkins,我们生成了许多并行检查角色/剧本的阶段,然后是容器中的单元测试,最后是集成测试。

Jenkins + Docker + Ansible = 测试

如何开始测试 Ansible,在一年内重构项目而不发疯

  1. 检查存储库并生成构建阶段。
  2. 并行运行 lint playbook 阶段。
  3. 并行运行 lint 角色阶段。
  4. 并行运行语法检查角色阶段。
  5. 并行运行测试角色阶段。
    1. 棉绒的作用。
    2. 检查对其他角色的依赖性。
    3. 检查语法。
    4. 创建docker实例
    5. 运行 molecular/default/playbook.yml。
    6. 检查幂等性。
  6. 运行集成测试
  7. 完成

第 271 天:巴士因素

如何开始测试 Ansible,在一年内重构项目而不发疯

最初,重构是由两三个人组成的小组进行的。 他们审查了master中的代码。 随着时间的推移,团队掌握了如何编写代码和代码审查的知识,这有助于传播有关基础设施及其工作原理的知识。 这里的亮点是审稿人是根据时间表一一选出的,即您有可能会进入一个新的基础设施。

如何开始测试 Ansible,在一年内重构项目而不发疯

而且这里应该很舒服。 进行审查很方便,可以在框架内查看已完成的任务以及讨论的历史记录。 我们集成了jenkins + bitbucket + jira。

但因此,审查并不是万能的;不知何故,我们进入了主代码,这使我们的测试失败了:

- get_url:
    url: "{{ actk_certs }}/{{ item.1 }}"
    dest: "{{ actk_src_tmp }}/"
    username: "{{ actk_mvn_user }}"
    password: "{{ actk_mvn_pass }}"
  with_subelements:
    - "{{ actk_cert_list }}"
    - "{{ actk_certs }}"
  delegate_to: localhost

- copy:
    src: "{{ actk_src_tmp }}/{{ item.1 }}"
    dest: "{{ actk_dst_tmp }}"
  with_subelements:
    - "{{ actk_cert_list }}"
    - "{{ actk_certs }}"

然后他们修复了它,但沉积物仍然存在。

get_url:
    url: "{{ actk_certs }}/{{ actk_item }}"
    dest: "{{ actk_src_tmp }}/{{ actk_item }}"
    username: "{{ actk_mvn_user }}"
    password: "{{ actk_mvn_pass }}"
  loop_control:
    loop_var: actk_item
  with_items: "{{ actk_cert_list }}"
  delegate_to: localhost

- copy:
    src: "{{ actk_src_tmp }}/{{ actk_item }}"
    dest: "{{ actk_dst_tmp }}"
  loop_control:
    loop_var: actk_item
  with_items: "{{ actk_cert_list }}"

第 311 天:加快测试速度

如何开始测试 Ansible,在一年内重构项目而不发疯

随着时间的推移,测试越来越多,构建运行速度越来越慢,最坏的情况下最多需要一个小时。 在其中一次回顾中,有这样一句话:“有测试很好,但速度很慢。” 因此,我们放弃了虚拟机上的集成测试,转而针对 Docker 进行了改造,使其速度更快。 我们还用 ansible verifier 替换了 testinfra,以减少使用的工具数量。

如何开始测试 Ansible,在一年内重构项目而不发疯

严格来说,有一系列措施:

  1. 切换到码头工人。
  2. 删除角色测试,该测试由于依赖性而重复。
  3. 增加奴隶数量。
  4. 试运行顺序。
  5. 掉毛能力 ALL 通过一个命令在本地进行操作。

如何开始测试 Ansible,在一年内重构项目而不发疯

结果jenkins上的Pipeline也统一了

  1. 生成构建阶段。
  2. Lint 全部并行。
  3. 并行运行测试角色阶段。
  4. 完成。

吸取的经验教训

避免全局变量

Ansible 使用全局变量,形式中有部分解决方法 私有角色变量,但这并不是灵丹妙药。

让我举一个例子。 让我们有 role_a и role_b

# cat role_a/defaults/main.yml
---
msg: a

# cat role_a/tasks/main.yml
---
- debug:
    msg: role_a={{ msg }}

# cat role_b/defaults/main.yml
---
msg: b

# cat role_b/tasks/main.yml
---
- set_fact:
    msg: b
- debug:
    msg: role_b={{ msg }}

- hosts: localhost
  vars:
    msg: hello
  roles:
    - role: role_a
    - role: role_b
  tasks:
    - debug:
        msg: play={{msg}}

如何开始测试 Ansible,在一年内重构项目而不发疯

有趣的是,剧本的结果将取决于并不总是显而易见的事情,例如角色列出的顺序。 不幸的是,这是 Ansible 的本质,最好的办法就是使用某种协议,例如,在一个角色中,仅使用该角色中描述的变量。

: 使用全局变量。

# cat roles/some_role/tasks/main.yml
---
debug:
  var: java_home

:五 defaults 定义必要的变量,然后仅使用它们。

# cat roles/some_role/defaults/main.yml
---
r__java_home:
 "{{ java_home | default('/path') }}"

# cat roles/some_role/tasks/main.yml
---
debug:
  var: r__java_home

前缀角色变量

: 使用全局变量。

# cat roles/some_role/defaults/main.yml
---
db_port: 5432

:在变量的角色中,使用以角色名称为前缀的变量;通过查看清单,这将使您更容易理解正在发生的情况。

# cat roles/some_role/defaults/main.yml
---
some_role__db_port: 5432

使用循环控制变量

: 在循环中使用标准变量 item,如果此任务/剧本包含在某处,这可能会导致意外行为

---
- hosts: localhost
  tasks:
    - debug:
        msg: "{{ item }}"
      loop:
        - item1
        - item2

:在循环中重新定义变量 loop_var.

---
- hosts: localhost
  tasks:
    - debug:
        msg: "{{ item_name }}"
      loop:
        - item1
        - item2
      loop_control:
        loop_var: item_name

检查输入变量

我们同意使用变量前缀;检查它们是否按照我们的预期定义,例如没有被空值覆盖,这并不是多余的

:检查变量。

- name: "Verify that required string variables are defined"
  assert:
    that: ahs_var is defined and ahs_var | length > 0 and ahs_var != None
    fail_msg: "{{ ahs_var }} needs to be set for the role to work "
    success_msg: "Required variables {{ ahs_var }} is defined"
  loop_control:
    loop_var: ahs_var
  with_items:
    - ahs_item1
    - ahs_item2
    - ahs_item3

避免哈希字典,使用扁平结构

如果一个角色期望其参数之一包含哈希/字典,那么如果我们想要更改其中一个子参数,我们将需要覆盖整个哈希/字典,这将增加配置复杂性。

:使用哈希/字典。

---
user:
  name: admin
  group: admin

:使用平面变量结构。

---
user_name: admin
user_group: "{{ user_name }}"

创建幂等剧本和角色

角色和剧本必须是幂等的,因为减少配置漂移和对破坏某些东西的恐惧。 但如果您使用分子,那么这是默认行为。

避免使用命令 shell 模块

使用 shell 模块会产生命令式描述范式,而不是声明式描述范式,后者是 Ansible 的核心。

通过分子测试你的角色

分子是一个非常灵活的东西,我们来看几个场景。

分子多实例

В molecule.yml 在部分 platforms 您可以描述许多可以部署的主机。

---
    driver:
      name: docker
    platforms:
      - name: postgresql-instance
        hostname: postgresql-instance
        image: registry.example.com/postgres10:latest
        pre_build_image: true
        override_command: false
        network_mode: host
      - name: app-instance
        hostname: app-instance
        pre_build_image: true
        image: registry.example.com/docker_centos_ansible_tests
        network_mode: host

因此,这些主机可以 converge.yml 使用:

---
- name: Converge all
  hosts: all
  vars:
    ansible_user: root
  roles:
    - role: some_role

- name: Converge db
  hosts: db-instance
  roles:
    - role: some_db_role

- name: Converge app
  hosts: app-instance
  roles:
    - role: some_app_role

Ansible验证器

在分子中,可以使用 ansible 检查实例是否已正确配置,而且,自版本 3 以来,这一直是默认设置。 它不像 testinfra/inspec 那么灵活,但我们可以检查文件的内容是否符合我们的期望:

---
- name: Verify
  hosts: all
  tasks:
    - name: copy config
      copy:
        src: expected_standalone.conf
        dest: /root/wildfly/bin/standalone.conf
        mode: "0644"
        owner: root
        group: root
      register: config_copy_result

    - name: Certify that standalone.conf changed
      assert:
        that: not config_copy_result.changed

或者部署服务,等待其可用并进行冒烟测试:

---
  - name: Verify
    hosts: solr
    tasks:
      - command: /blah/solr/bin/solr start -s /solr_home -p 8983 -force
      - uri:
          url: http://127.0.0.1:8983/solr
          method: GET
          status_code: 200
        register: uri_result
        until: uri_result is not failed
        retries: 12
        delay: 10
      - name: Post documents to solr
        command: /blah/solr/bin/post -c master /exampledocs/books.csv

将复杂的逻辑放入模块和插件中

Ansible提倡声明式方法,因此当你进行代码分支、数据转换、shell模块时,代码变得难以阅读。 为了解决这个问题并保持简单易懂,通过创建自己的模块来解决这种复杂性并不是多余的。

总结提示和技巧

  1. 避免全局变量。
  2. 角色变量的前缀。
  3. 使用循环控制变量。
  4. 检查输入变量。
  5. 避免使用哈希字典,使用扁平结构。
  6. 创建幂等剧本和角色。
  7. 避免使用命令 shell 模块。
  8. 通过分子测试你的角色。
  9. 将复杂的逻辑放入模块和插件中。

结论

如何开始测试 Ansible,在一年内重构项目而不发疯

即使您拥有 IaC,您也不能直接重构项目的基础设施。 这是一个漫长的过程,需要耐心、时间和知识。

UPD1 2020.05.01 20:30 — 对于剧本的初步分析,您可以使用 callback_whitelist = profile_tasks 了解什么是长期有效的。 然后我们通过 Ansible 加速经典。 你也可以尝试 有丝分裂原
UPD2 2020.05.03 16:34 - 中文版

来源: habr.com

添加评论