这是文字记录
从第二次提交开始,任何代码都将成为遗留代码,因为最初的想法开始偏离严酷的现实。 这既不好也不坏,这是一个难以争论的既定事实,并且必须接受。 这个过程的一部分是重构。 将基础设施重构为代码。 让故事从如何在一年内重构 Ansible 并且不疯狂开始吧。
遗产的诞生
第一天:零号病人
从前有一个有条件的项目。 它有一个开发团队和运营工程师。 他们正在解决同一个问题:如何部署服务器和运行应用程序。 问题是每个团队都以自己的方式解决这个问题。 在该项目中,决定使用 Ansible 在开发和运营团队之间同步知识。
第 89 天:遗产的诞生
他们自己没有注意到这一点,他们想尽可能地做到最好,但结果却成了遗产。 这是怎么发生的?
- 我们这里有一项紧急任务,让我们进行一次肮脏的黑客攻击,然后修复它。
- 您不必编写文档,这里发生的一切都很清楚。
- 我了解 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 天:意识到问题
最初构想和实现的 IaC 模型不再满足用户/业务/其他团队的要求,对基础设施进行更改的时间也不再可接受。 此时此刻,我们明白是时候采取行动了。
IaC重构
第 139 天:你真的需要重构吗?
在急于重构之前,您必须回答一些重要问题:
- 为什么你需要这一切?
- 你有时间吗?
- 知识够用吗?
如果你不知道如何回答这些问题,那么重构就会在开始之前就结束,或者只会变得更糟。 因为有经验(
第 149 天:准备重构
首先要做的就是准备。 决定我们要做什么。 为此,我们进行沟通、查找问题领域并找出解决方法。 我们以某种方式记录由此产生的概念,例如一篇汇合的文章,这样当问题出现时“什么是最好的?” 或“哪个是正确的?” 我们还没有迷失方向。 就我们而言,我们坚持这个想法 分而治之:我们将基础设施分解成小块/砖块。 这种方法允许您采用一个孤立的基础设施,了解它的作用,通过测试覆盖它并对其进行更改,而不必担心会破坏任何内容。
事实证明,基础设施测试成为基石,这里值得一提的是基础设施测试金字塔。 与正在开发中的想法完全相同,但对于基础设施而言:我们正在从检查缩进等简单内容的廉价快速测试转向部署整个基础设施的昂贵的全面测试。
Ansible 测试尝试
在我们描述如何覆盖项目的 Ansible 测试之前,我将描述我之前有机会使用的尝试和方法,以便了解所做决策的背景。
第-997 天:SDS 规定
我第一次测试 Ansible 是在一个开发 SDS(软件定义存储)的项目中。 关于这个主题有一篇单独的文章
第 #-701 天:Ansible 和测试厨房
Ansible 测试思想的发展是使用现成的工具,即 test kitchen/kitchen-ci 和 inspec。 这个选择是由对 Ruby 的了解决定的(更多详细信息,请参阅关于 Habré 的文章:
总体而言,该解决方案有效,但由于不均匀性,存在一些沉积物。 当他们将测试数量增加到 13 个基本角色和 2 个组合较小角色的元角色时,测试突然开始运行 70 分钟,几乎延长了 2 倍。 谈论 XP(极限编程)实践很困难,因为…… 没有人愿意等70分钟。 这就是改变方法的原因
第#-601天:Ansible 和 molecular
从概念上讲,这与 testkitchen 类似,只是我们将角色测试移至 docker 并更改了堆栈。 结果,20个角色的时间减少到稳定的25-7分钟。
通过将测试角色数量增加到 17 个并检查 45 个角色,我们在 28 个 jenkins 从机上运行了 2 分钟。
第 167 天:向项目添加 Ansible 测试
最有可能的是,不可能很快完成重构任务。 这个任务必须是可衡量的,这样你就可以把它分成小块,然后用茶匙一块一块地吃掉大象。 必须了解自己是否在朝着正确的方向前进,还要走多长时间。
一般来说,如何完成并不重要,你可以写在一张纸上,你可以在衣柜上贴贴纸,你可以在 Jira 中创建任务,或者你可以打开 Google Docs 并记下当前状态那里。 腿的生长是因为这个过程不是立竿见影的,而是漫长而乏味的。 不太可能有人希望你在重构过程中失去想法、感到疲倦、不知所措。
重构很简单:
- 吃。
- 睡觉。
- 码。
- IaC 测试。
- 重复新密码
我们重复此操作,直到达到预期目标。
可能无法立即开始测试所有内容,因此我们的首要任务是从 linting 和检查语法开始。
第 181 天:绿色构建大师
Linting 是迈向 Green Build Master 的一小步。 这几乎不会破坏任何东西,但它允许您调试流程并在 Jenkins 中进行绿色构建。 这个想法是在团队中养成习惯:
- 红色测试很糟糕。
- 我来修复一些东西,同时使代码比你之前更好一些。
第 193 天:从 linting 到单元测试
构建了将代码放入 master 的过程后,您可以开始逐步改进的过程 - 用启动角色替换 linting,您甚至可以在没有幂等性的情况下做到这一点。 您需要了解如何应用角色以及它们如何工作。
第 211 天:从单元测试到集成测试
当大多数角色都被单元测试覆盖并且所有内容都被检查时,您可以继续添加集成测试。 那些。 测试的不是基础设施中的单个砖块,而是它们的组合,例如完整的实例配置。
使用 jenkins,我们生成了许多并行检查角色/剧本的阶段,然后是容器中的单元测试,最后是集成测试。
Jenkins + Docker + Ansible = 测试
- 检查存储库并生成构建阶段。
- 并行运行 lint playbook 阶段。
- 并行运行 lint 角色阶段。
- 并行运行语法检查角色阶段。
- 并行运行测试角色阶段。
- 棉绒的作用。
- 检查对其他角色的依赖性。
- 检查语法。
- 创建docker实例
- 运行 molecular/default/playbook.yml。
- 检查幂等性。
- 运行集成测试
- 完成
第 271 天:巴士因素
最初,重构是由两三个人组成的小组进行的。 他们审查了master中的代码。 随着时间的推移,团队掌握了如何编写代码和代码审查的知识,这有助于传播有关基础设施及其工作原理的知识。 这里的亮点是审稿人是根据时间表一一选出的,即您有可能会进入一个新的基础设施。
而且这里应该很舒服。 进行审查很方便,可以在框架内查看已完成的任务以及讨论的历史记录。 我们集成了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 天:加快测试速度
随着时间的推移,测试越来越多,构建运行速度越来越慢,最坏的情况下最多需要一个小时。 在其中一次回顾中,有这样一句话:“有测试很好,但速度很慢。” 因此,我们放弃了虚拟机上的集成测试,转而针对 Docker 进行了改造,使其速度更快。 我们还用 ansible verifier 替换了 testinfra,以减少使用的工具数量。
严格来说,有一系列措施:
- 切换到码头工人。
- 删除角色测试,该测试由于依赖性而重复。
- 增加奴隶数量。
- 试运行顺序。
- 掉毛能力 ALL 通过一个命令在本地进行操作。
结果jenkins上的Pipeline也统一了
- 生成构建阶段。
- Lint 全部并行。
- 并行运行测试角色阶段。
- 完成。
吸取的经验教训
避免全局变量
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 的本质,最好的办法就是使用某种协议,例如,在一个角色中,仅使用该角色中描述的变量。
差: 使用全局变量。
# 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模块时,代码变得难以阅读。 为了解决这个问题并保持简单易懂,通过创建自己的模块来解决这种复杂性并不是多余的。
总结提示和技巧
- 避免全局变量。
- 角色变量的前缀。
- 使用循环控制变量。
- 检查输入变量。
- 避免使用哈希字典,使用扁平结构。
- 创建幂等剧本和角色。
- 避免使用命令 shell 模块。
- 通过分子测试你的角色。
- 将复杂的逻辑放入模块和插件中。
结论
即使您拥有 IaC,您也不能直接重构项目的基础设施。 这是一个漫长的过程,需要耐心、时间和知识。
链接
- 幻灯片
如何测试 Ansible 并且不要发疯 - 视频
如何测试 Ansible 并且不要发疯 我从测试 200 行基础设施代码中学到了什么 Ansible:120 个月内将 18 个虚拟机配置从 Coreos 迁移到 Centos 测试分发时如何用拐杖折断自行车 测试我是否可以,或者 YML 程序员是否梦想测试 Ansible? 精彩的 IaC 测试文章、演讲和链接列表 交叉帖子 中文版
UPD1 2020.05.01 20:30 — 对于剧本的初步分析,您可以使用 callback_whitelist = profile_tasks
了解什么是长期有效的。 然后我们通过
UPD2 2020.05.03 16:34 -
来源: habr.com