Skip to content

Files

Latest commit

f87fb9e · Dec 29, 2021

History

History
570 lines (408 loc) · 26.7 KB

File metadata and controls

570 lines (408 loc) · 26.7 KB

十七、Ansible 的后续步骤

在本章中,我们将讨论如何将 Ansible 集成到您的日常工作流程中,从持续集成工具到监控工具和故障排除。我们将讨论以下主题:

  • 如何将 Ansible 与 Slack 等服务集成
  • 当问题出现时,如何使用 Ansible 进行故障排除
  • 一些真实的例子

让我们直入主题,看看如何将我们的行动手册与第三方服务挂钩。

与第三方服务集成

尽管您可能是运行行动手册的人,但您可以记录您的行动手册运行情况,或者让您团队的其他成员甚至其他部门了解行动手册运行的最新结果。Ansible 附带了几个核心模块,允许您使用第三方服务来提供实时通知。

松弛的

对于 IT 服务中的各个部门,Slack 已经非常迅速地成为基于团队的协作服务的首选。它不仅通过其应用目录支持第三方应用,而且它还有一个强大的应用编程接口,您可以使用它将您的工具带入 Slack 提供的聊天室。

我们将查看本节中的示例,完整的行动手册,可以在 GitHub 存储库中的Chapter17/slack文件夹中找到。我从第 9 章构建云网络中获取了剧本,我们在 AWS 中创建了一个 VPC,我已经将其改编为使用slack Ansible 模块。

生成令牌

在我们可以使用行动手册中的 Slack 模块之前,我们需要一个访问令牌来请求一次登录您的 Slack 工作区;如果你还没有工作区,可以在https://slack.com/免费注册一个工作区。

使用网络客户端或桌面应用登录工作区后,从“管理”菜单中选择“管理应用”选项,如下图所示:

这将打开您的浏览器,并将您带到工作区的应用目录;从这里搜索Incoming WebHooks,然后点击添加配置。

配置的第一部分是选择希望传入的 Webhook 将消息发布到哪个通道。我选择了普通频道——一旦选择,你将被带到一个给你一个网页链接的页面;请务必记下这个网址,因为我们很快就会需要它。在页面的底部,您可以选择自定义您的网络钩子。

在页面底部的集成设置中,我输入了以下信息:

  • 发布到频道:我将其保留为#general
  • 这个是为你预先填充的;您也可以在这里选择重新生成网址
  • 描述性标签:我在这里输入Ansible
  • 自定义名称:我也在这里输入了Ansible
  • 自定义图标:我让它保持原样

填写完前面的详细信息后,我点击了保存设置按钮;这给我留下了一个新的网络钩子:

如前所述,我还记下了 Webhook URL 对我来说,它是:

https://hooks.slack.com/services/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

现在我们拥有了一切,我们需要在 Slack 端进行配置,以便能够开始使用 Ansible 向我们的用户发送消息。

Ansible 的剧本

我在这里只介绍一个角色的任务更新,正是这个角色创建了 VPC。我做的第一个改变是在group_vars/common.yml文件中添加了几行:

---

environment_name: "VPC-Slack"
ec2_region: "eu-west-1"

slack:
  token: "TBCRVDMGA/BBCPTPNH1/tyudQIccviG7gh4JnfeoPScc"
  username: "Ansible"
  icon: "https://upload.wikimedia.org/wikipedia/commons/thumb/0/05/Robot_icon.svg/200px-Robot_icon.svg.png"

如您所见,我添加了以下三个嵌套变量:

  • token:这是取自于 Webhook 网址;如你所见,我在https://hooks.slack.com/services/之后输入了一切
  • username:我们可以覆盖发布更新的用户名,因为我只是把它保持为 Ansible
  • icon:这是将作为我们帖子的一部分显示的头像

如果你还记得之前的 VPC 角色,你会记得它包含一个使用ec2_vpc_net模块创建 VPC 的单一任务。现在,我们想要引入 Slack 通知,并且能够为我们的用户提供反馈。所以,首先,让我们发送一个通知,说我们正在检查 VPC 是否存在:

- name: Send notification message via Slack all options
  slack:
    token: "{{ slack.token }}"
    msg: "Checking for VPC called '{{ environment_name }}'"
    username: "{{ slack.username }}"
    icon_url: "{{ slack.icon }}"
    link_names: 0
    parse: 'full'

从前面的任务中可以看到,我们正在发送一条消息,在我们的例子中,这条消息将显示Checking for VPC called 'VPC-Slack',以及tokenusernameicon。角色中的下一个任务是来自原始角色的任务:

- name: ensure that the VPC is present
  ec2_vpc_net:
    region: "{{ ec2_region }}"
    name: "{{ environment_name }}"
    state: present
    cidr_block: "{{ vpc_cidr_block }}"
    resource_tags: { "Name" : "{{ environment_name }}", "Environment" : "{{ environment_name }}" }
  register: vpc_info

现在,两件事情中的一件将会发生:一个叫做VPC-Slack的 VPC 将会被创造出来,或者 Ansible 将会收集关于一个已经存在的叫做VPC-Slack的 VPC 的信息。当我们向用户发送消息时,它应该根据 Ansible 的行为而改变。以下任务会发送一条消息,通知我们的用户新的 VPC 已经创建:

- name: Send notification message via Slack all options
  slack:
    token: "{{ slack.token }}"
    msg: "VPC called '{{ environment_name }}' created with an ID of '{{ vpc_info.vpc.id }}'"
    username: "{{ slack.username }}"
    icon_url: "{{ slack.icon }}"
    link_names: 0
    parse: 'full'
  when: vpc_info.changed

请注意,我只在注册的vpc_info变量被标记为已更改时运行此任务。另外,我把 VPC 的身份证作为信息的一部分。如果vpc_info没有注册任何更改,则跳过前面的任务;将运行以下任务:

- name: Send notification message via Slack all options
  slack:
    token: "{{ slack.token }}"
    msg: "Found a VPC called '{{ environment_name }}' which has an ID of '{{ vpc_info.vpc.id }}'"
    username: "{{ slack.username }}"
    icon_url: "{{ slack.icon }}"
    link_names: 0
    parse: 'full'
  when: vpc_info.changed == false and vpc_info.failed == false

注意我是如何改变措辞的,以及它是如何在没有改变的时候被调用的。我浏览了其余的角色,使用与前面代码中相同的逻辑添加任务向 Slack 发送通知;如上所述,您可以在存储库中的Chapter17/slack文件夹中找到所有添加的内容。

运行剧本

运行行动手册时,请使用以下命令:

$ export AWS_ACCESS_KEY=AKIAI5KECPOTNTTVM3EDA
$ export AWS_SECRET_KEY=Y4B7FFiSWl0Am3VIFc07lgnc/TAtK5+RpxzIGTr
$ ansible-playbook -i production site.yml

我收到了来自 Slack 的以下通知:

正如你所看到的,很多信息都在谈论正在创建的 VPC 内的服务。之后立即重新运行行动手册会返回以下结果:

这一次,消息是关于找到现有服务并返回标识。懈怠只是一种服务。现在,让我们简单了解一下您可以从 Ansible 行动手册中获得的更多服务。

其他服务

Slack 不是 Ansible 唯一可以交互的服务;以下是您可能希望在行动手册中使用的更多内容。

营火

营火是一个内置于 Basecamp 的聊天服务;您可以使用此模块直接从 Ansible 向项目干系人发送更新,例如:

- name: Send a message to Campfire
  campfire:
    subscription: "my_subscription"
    token: "my_subscription"
    room: "Demo"
    notify: "loggins"
    msg: "The task has completed and all is well"

思科网络交流团队(思科火花)

思科 Webex Teams(正式名称为思科 Spark)是思科的协作服务,为您的团队提供虚拟会议空间、消息和视频通话。此外,它有一个丰富的应用编程接口,Ansible 可以配置为与它交互:

- name: Send a message to Cisco Spark
  cisco_spark:
    recipient_type: "roomId"
    recipient_id: "{{ spark.room_id }}"
    message_type: "markdown"
    personal_token: "{{ spark.token }}"
    message: "The task has **completed** and all is well"

加州福斯多克

CA Flowdock 是一种消息服务,它从头开始设计,与以开发人员为中心的服务集成,例如 GitHub、Bitbucket、吉拉、Jenkins 和 Ansible:

- name: Send a message to a Flowdock inbox
  flowdock:
    type: "inbox"
    token: "{{ flowdock.token }}"
    from_address: "{{ flowdock.email }}"
    source: "{{ flowdock.source }}"
    msg: "The task has completed and all is well"
    subject: "Task Success"

Hipchat

Hipchat 是由 Atlassian 提供的群组消息服务;它与亚特兰蒂斯系列产品的其余部分紧密集成:

- name: Send a message to a Hipchat room
  hipchat:
    api: "https://api.hipchat.com/v2/"
    token: "{{ hipchat.token }}"
    room: "{{ hipchat.room }}"
    msg: "The task has completed and all is well"

邮件

这项服务不需要任何介绍;Ansible 可以配置为使用各种配置发送电子邮件。以下示例显示了通过外部 SMTP 服务器发送的电子邮件:

- name: Send an email using external mail servers
  mail:
    host: "{{ mail.smtp_host }}"
    port: "{{ mail.smtp_port }}"
    username: "{{ mail.smtp_username }}"
    password: "{{ mail.smtp_password }}"
    to: "Russ McKendrick <russ@mckendrick.io>"
    subject: "Task Success"
    body: "The task has completed and all is well"
  delegate_to: localhost

Mattermost

Mattermost 是专有服务的开源替代产品,就像我们在列表中其他地方介绍的服务一样(例如,Slack、思科 Webex Teams 和 Hipchat):

- name: Send a message to a Mattermost channel
  mattermost:
    url: "{{ mattermost.url }}"
    api_key: "{{ mattermost.api_key }}"
    text: "The task has completed and all is well"
    channel: "{{ mattermost.channel }}"
    username: "{{ mattermost.username }}"
    icon_url: "{{ mattermost.icon_url }}"

大多数现代计算机都内置了某种程度的语音合成;使用本模块,您可以让 Ansible 口头通知您行动手册的运行状态:

- name: Say a message on your Ansible host
  say:
    msg: "The task has completed and all is well"
    voice: "Daniel"
  delegate_to: localhost

服务现在

ServiceNow 是由 ServiceNow,Inc .提供的企业级 IT 服务管理软件即服务产品。使用snow_record模块,您的行动手册可以在 ServiceNow 安装中打开事件:

- name: Create an incident in ServiceNow
  snow_record:
    username: "{{ snow.username }}"
    password: "{{ snow.password }}"
    instance: "{{ snow.instance }}"
    state: "present"
    data:
      short_description: "The task has completed and all is well"
      severity: "3"
      priority: "3"
  register: snow_incident

系统记录

如果您从主机发送日志文件,那么您可能希望将剧本运行的结果发送到您的主机 syslog,以便将其发送到您的中央日志记录服务:

- name: Send a message to the hosts syslog
  syslogger:
    msg: "The task has completed and all is well"
    priority: "info"
    facility: "daemon"
    log_pid: "true"

特维利奥

使用您的 Twilio 帐户直接从您的 Ansible 行动手册发送短信,如下所示:

- name: Send an SMS message using Twilio
  twilio:
    msg: "The task has completed and all is well"
    account_sid: "{{ twilio.account }}"
    auth_token: "{{ twilio.auth }}"
    from_number: "{{ twilio.from_mumber }}"
    to_number: "+44 7911 123456"
  delegate_to: localhost

第三方服务概述

我希望你能从这本书中学到的一点是,自动化很棒——它不仅是一个实时节省器,而且使用像我们在上一章中介绍的工具,Ansible Tower 和 Ansible AWX,可以让非系统管理员或开发人员从友好的网络界面执行他们的行动手册。

我们在本节中介绍的模块不仅允许您记录结果,还允许您在行动手册运行期间自动进行一些内务处理,并让它通知您的用户,从而让您的自动化更上一层楼。

比方说,您需要在服务器上部署新的配置。您的服务台提出了一个变更,要求您在“立即服务”安装中执行工作。你的剧本可以这样写,在改变发生之前,它使用fetch模块将配置文件复制到你的可执行控制器。然后,行动手册可以使用snow_record模块将现有配置文件的副本附加到变更请求,继续进行变更,然后用结果自动更新变更请求。

您可以在以下网址找到本章这一部分提到的服务的详细信息:

Ansible 剧本调试器

Ansible 内置了调试器。让我们来看看如何通过创建一个简单的错误剧本来将它构建到您的剧本中。正如我们刚刚提到的,我们将编写一个使用say模块的剧本。剧本本身如下所示:

---

- hosts: localhost
  gather_facts: false
  debugger: "on_failed"

  vars:
    message: "The task has completed and all is well"
    voice: "Daniel"

  tasks:
    - name: Say a message on your Ansible host
      say:
        msg: "{{ massage }}"
        voice: "{{ voice }}"

有两点需要指出:第一点是错误。如您所见,我们正在定义一个名为message的变量,但是当我们在任务中使用它时,我打了一个错别字,而是输入了massage。幸运的是,在我开发剧本的时候,我已经指示 Ansible 每当任务失败时就转到交互式调试器。

调试任务

让我们运行剧本,看看会发生什么:

$ ansible-playbook playbook.yml

第一个问题是我们没有传递主机清单文件,所以会有警告说只有 localhost 可用;这很好,因为我们无论如何都只想在我们的 Ansible 控制器上运行say模块:

[WARNING]: Unable to parse /etc/ansible/hosts as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit
localhost does not match 'all'

接下来,Ansible 自己操刀;这将导致致命错误:

PLAY [localhost] ***********************************************************************************

TASK [Say a message on your Ansible host] **********************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'massage' is undefined\n\nThe error appears to have been in '/Users/russ/Documents/Code/learn-ansible-fundamentals-of-ansible-2x/chapter17/say/playbook.yml': line 12, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: Say a message on your Ansible host\n ^ here\n"}

通常,剧本运行将停止,您将返回到您的 shell 但是,因为我们已经指示 Ansible 进入交互式调试器,所以我们现在看到以下提示:

[localhost] TASK: Say a message on your Ansible host (debug)>

从这里,我们可以开始更多地研究这个问题;例如,我们可以通过键入以下命令来检查错误:

p result._result

一旦点击进入键,失败任务的结果将被返回:

[localhost] TASK: Say a message on your Ansible host (debug)> p result._result
{'failed': True,
 'msg': u"The task includes an option with an undefined variable. The error was: 'massage' is undefined\n\nThe error appears to have been in '/Users/russ/Documents/Code/learn-ansible-fundamentals-of-ansible-2x/chapter17/say/playbook.yml': line 12, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: Say a message on your Ansible host\n ^ here\n"}
[localhost] TASK: Say a message on your Ansible host (debug)>

让我们通过键入以下内容来进一步了解任务中使用的变量:

p task.args

这将返回我们在任务中使用的两个参数:

[localhost] TASK: Say a message on your Ansible host (debug)> p task.args
{u'msg': u'{{ massage }}', u'voice': u'{{ voice }}'}
[localhost] TASK: Say a message on your Ansible host (debug)>

现在,让我们使用以下内容来看看任务可用的变量:

p task_vars

您可能已经注意到,我们指示 Ansible 执行安装模块,作为行动手册运行的一部分;也就是将可用变量的列表保持在最小:

[localhost] TASK: Say a message on your Ansible host (debug)> p task_vars
{'ansible_check_mode': False,
 'ansible_connection': 'local',
 'ansible_current_hosts': [u'localhost'],
 'ansible_diff_mode': False,
 'ansible_facts': {},
 'ansible_failed_hosts': [],
 'ansible_forks': 5,
 'ansible_inventory_sources': [u'/etc/ansible/hosts'],
 'ansible_play_batch': [],
 'ansible_play_hosts': [u'localhost'],
 'ansible_play_hosts_all': [u'localhost'],
 'ansible_playbook_python': '/usr/bin/python',
 'ansible_python_interpreter': '/usr/bin/python',
 'ansible_run_tags': [u'all'],
 'ansible_skip_tags': [],
 'ansible_version': {'full': '2.5.5',
 'major': 2,
 'minor': 5,
 'revision': 5,
 'string': '2.5.5'},
 'environment': [],
 'group_names': [],
 'groups': {'all': [], 'ungrouped': []},
 'hostvars': {},
 'inventory_hostname': u'localhost',
 'inventory_hostname_short': u'localhost',
 u'message': u'The task has completed and all is well',
 'omit': '__omit_place_holder__0529a2749315462e1ae1a0d261987dedea3bfdad',
 'play_hosts': [],
 'playbook_dir': u'/Users/russ/Documents/Code/learn-ansible-fundamentals-of-ansible-2x/chapter17/say',
 u'voice': u'Daniel'}
[localhost] TASK: Say a message on your Ansible host (debug)>

如您所见,那里有很多关于我们行动手册执行环境的信息。在变量列表中,您会注意到其中两个变量以u为前缀:它们是voicemessage。我们可以通过以下方式了解更多信息:

p task_vars['message']
p task_vars['voice']

这将显示变量的内容:

[localhost] TASK: Say a message on your Ansible host (debug)> p task_vars['message']
u'The task has completed and all is well'
[localhost] TASK: Say a message on your Ansible host (debug)> p task_vars['voice']
u'Daniel'
[localhost] TASK: Say a message on your Ansible host (debug)>

我们知道我们正在将一个拼写错误的变量传递给msg参数,因此我们在运行中进行了一些更改,并继续运行剧本。为此,我们将运行以下命令:

task.args['msg'] = '{{ message }}'

这将更新参数以使用正确的变量含义,这样我们就可以通过运行以下命令来重新运行任务:

redo

这将立即用正确的参数重新运行任务,如果运气好的话,您应该会听到任务已经完成,一切正常:

[localhost] TASK: Say a message on your Ansible host (debug)> task.args['msg'] = '{{ message }}'
[localhost] TASK: Say a message on your Ansible host (debug)> redo
changed: [localhost]

PLAY RECAP ************************************************************************************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0

从前面的输出中可以看出,因为我们只有一个任务,所以剧本完成了。如果我们有更多,那么它将从它停止的地方继续下去。现在,您可以用正确的拼写更新您的行动手册,并继续一天的工作。

另外,如果我们想的话,我们可以分别键入continuequit来继续或停止。

Ansible 调试器摘要

当您正在创建大型行动手册时,Ansible 调试器是一个非常有用的启用选项,例如,假设您有一个花费大约 20 分钟运行的行动手册,但是在接近结尾的某个地方出现了错误,例如,在您第一次运行行动手册 15 分钟后。

让 Ansible 进入交互式调试器外壳不仅意味着您可以准确地看到什么是定义的,什么不是定义的,还意味着您不必盲目地对您的剧本进行更改,然后再等 15 分钟,看看这些更改是否解决了导致致命错误的问题。

现实世界的例子

在我们读完这一章和这本书之前,我想我会举几个例子来说明我是如何使用 Ansible 和与 Ansible 交互的:第一个例子是使用聊天与 Ansible 交互。

聊天示例

几个月前,我需要设置一个演示来展示自动化的工作——然而,我需要能够在我的笔记本电脑或手机上展示这个演示,这意味着我不能假设我可以访问命令行。

我提出的演示最终使用了 Slack 和其他一些我们在本书中没有涉及的工具,即 Hubot 和 Jenkins 在我详细介绍之前,让我们快速看一下正在运行的演示的输出:

正如您从前面的输出中看到的,我在 Slack 通道中询问了以下问题:

@bot 给我一台 linux 服务器

这随后触发了 Ansible playbook 运行,该运行在 AWS 中启动了一个实例,并在 playbook 确认服务器在网络上可用后返回该实例的信息。我还配置了它,通过询问以下问题来删除所有正在运行的实例:

@bot 终止所有服务器

如您所见,这将运行另一个剧本,这一次,一旦实例被移除,将返回一个动画 GIF:

那么,我用什么来做这个?如前所述,首先,我使用了 Hubot。Hubot 是由 GitHub 开发的开源可扩展 chatbot。它是使用我的 Slack 频道中的hubot-slack插件配置的,它会监听给它的任何命令。

我用hubot-alias插件定义了一个别名,将 @bot 给我一个 linux 服务器翻译成build AWS launch OS = Linux;这使用了hubot-yardmaster插件来触发我的詹金斯安装中的一个构建。

Jenkins 是一个开源自动化服务器,主要用于持续集成和持续交付——它也有一个插件架构。使用 Jenkins Ansible 插件和 Jenkins Git 插件,我能够将用于启动 AWS 实例的剧本和角色拉到我的 Jenkins 服务器上,然后让 Jenkins 为我运行剧本——剧本本身与我们在第 9 章构建云网络第 10 章高可用性云部署中完成的剧本没有太大不同。

行动手册内置了一点逻辑,限制了可以启动的实例数量,随机化了它正在启动的实例的名称,还显示了几个选项列表中的一个随机 GIF 所有这些信息,以及实例和 AMI 的详细信息,都是通过 Ansible Slack 模块传递给用户的,给人的印象是行动手册实际上做得比它多。

在前面两个例子中,机器人用户是 Hubot,而 Jenkins 实际上是剧本运行的反馈。

自动化部署

另一个例子——我最近和几个开发人员一起工作,他们需要一种方法来自动地将他们的代码部署到他们的开发和暂存服务器上。使用 Docker、GitHub、Jenkins 和 Ansible AWX 的组合,我能够为开发人员提供一个工作流,每当他们将代码推送到 GitHub 上他们的存储库的开发或暂存分支时,就会触发这个工作流。

为了实现这一点,我将代码部署在他们自己的 Jenkins 服务器上,使用 Ansible 将 Jenkins 部署在一个容器中,还在同一服务器上使用 Docker 部署了 AWX。然后,使用詹金斯 GitHub 插件,我将詹金斯项目连接到 GitHub,以创建触发构建所需的网络钩子。然后使用詹金斯可变形塔插件,我让詹金斯在 AWX 触发一个剧本运行。

我这样做是因为目前,AWX 并不容易与 GitHub Webhooks 挂钩,而 JenkinsJenkins GitHub 插件具有很高的兼容性——我想以 AWX 的开发速度,这个小问题很快就会得到解决。

由于 AWX 允许您授予基于角色的行动手册访问权限,我授予开发经理和操作工程师运行生产行动手册的权限,开发人员拥有只读权限,以便他们可以查看行动手册运行的结果。

这意味着生产部署也能够实现自动化,也就是说,拥有正确权限的人必须手动触发剧本运行。

AWX 允许我们对谁可以触发部署进行控制,这种控制水平符合我们现有的部署策略,即开发人员不应该访问生产系统来部署他们编写的代码。

摘要

现在我们不仅结束了这一章,也结束了这本书。我一直在试图想一个方法来总结 Ansible,我在 Ansible 的创作者 Michael DeHaan 的一条推文中找到了它,他在回复一位技术招聘人员时说道:

"Anyone using Ansible for a few months is as good as anyone using Ansible for three years. It's a simple tool on purpose."

这完美地总结了我和你的 Ansible 的经历。一旦您了解了基础知识,就很容易快速前进,开始构建越来越复杂的行动手册,这不仅有助于部署基本代码和应用,还可以部署复杂的云甚至物理架构。

不仅可以重用自己的角色,还可以通过 Ansible Galaxy 访问大量社区贡献的角色,这意味着您的下一个项目有很多例子或快速的起点。所以,你可以卷起袖子,比使用其他工具更快地陷进去。此外,如果有 Ansible 做不到的事情,那么很可能有一个工具可以与之集成,以提供缺失的功能。

回到我们在第 1 章中讨论的 Ansible 简介,能够以可重复和可共享的方式用代码定义您的基础架构和部署,从而鼓励其他人为您的行动手册做出贡献,这应该是您开始将 Ansible 引入日常工作流程的最终目的。我希望,通过这本书,你已经开始思考日常任务,Ansible 可以帮助你,节省你的时间。

进一步阅读

有关本章中提到的工具的更多信息,请访问以下网址: