0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AWX連携編】Zabbix × ServiceNow × AWX × Bedrockで障害初動対応を自動化してみた

0
Posted at

はじめに

こんにちは!
しばらく期間が開いてしまいました。
毎年6月はITイベントが多くて刺激になりますね!
今年も無事AWSサミット参加できてほっとしています。

本題に戻して、前回のおさらいから!
前回までの記事では自動起票編ということで、ZabbixがNW機器の障害を検知したときにServiceNowへ自動でインシデントが起票され、復旧時に自動クローズされるところまで確認しましたね。

前回の記事はこちら
【自動起票編】Zabbix × ServiceNow × AWX × Bedrockで障害初動対応を自動化してみた

今回はその続きとしてAWX連携編を書いていこうと思います。

インシデントが起票されるところまではできましたが
このままだと「障害が起きたことは分かるけどログ取得は手動でNW機器にログインして取得する」という状態です。

そこで今回は、インシデント起票と同時にAWXのJobを自動起動して、NW機器のログを自動収集するところまで実装していきます。

今回のゴール

Zabbixが障害を検知してServiceNowにインシデントが起票されるタイミングで、AWXのJobが自動起動し、Cisco機器から複数のshowコマンドの結果を取得してS3に保存されることをゴールとします。

[Zabbix] 障害検知
    ↓
[Lambda] ServiceNowにインシデント起票
    ↓
[Lambda] AWX APIを呼び出してJob起動
    ↓
[AWX] Ansible Playbookを実行
    ↓
[Cisco機器] showコマンドを複数実行
    ↓
[S3] ログを保存

全体構成図だと赤枠の設定がメインとなります。
名称未設定のデザイン (5).png

今回の流れ

# 内容
0 事前準備(S3バケット作成・GitHubリポジトリ作成)
1 Playbookの作成
2 GitHubリポジトリへのプッシュ
3 AWXのCredentials設定(踏み台・Cisco・GitHub)
4 AWXのInventory設定
5 AWXのProject設定(GitHub連携)
6 AWXのJob Template作成
7 AWX APIトークンの発行
8 単体確認(AWX UIから手動実行)

なぜAWXを使うのか

Playbookを直接叩く方法でも動きますが、あえてAWXを挟むことで以下が実現できます。

観点 Playbook直叩き AWX経由
実行方法 サーバにログインしてコマンド実行 REST APIで呼び出すだけ
認証情報の管理 スクリプト内やサーバ内に平文で持ちがち Credentials機能で一元管理・暗号化保存
他システムとの連携 難しい(実行環境が必要) LambdaなどからHTTP1本で起動可能
実行履歴 自分でログを残す必要あり AWXが自動で記録・GUIで確認可能

今回のように「Lambdaからログ収集を自動起動したい」というケースでは、AWXをAnsibleの実行をAPI化してくれるレイヤーとして使うのがポイントです。
Lambda側はAWXの中身を意識せず、Job Templateの番号を指定してAPIを叩くだけで済みます。

事前準備:バケットとリポジトリを用意する

S3バケットの作成(AWSマネジメントコンソール)

まずはログ保管先のバケットから作成しましょう。

項目 理由
バケット名 nw-verify-logs 一意な名前である必要があります(既に他アカウントで使われている場合は末尾に自分の識別子を付けます)
AWSリージョン アジアパシフィック(東京) ap-northeast-1 他のリソース(EC2/Lambda)と同じリージョンに揃えることで、リージョン間の通信を避けレイテンシとコストを抑えます
オブジェクト所有者 ACL無効(推奨) バケットポリシーとIAMだけでアクセス制御する、現在のAWS推奨構成です
パブリックアクセスをすべてブロック チェックを入れたまま(デフォルト) 障害ログという社内情報を誰でも見られる状態にしないためです。インターネットに公開する必要は一切ありません
バケットのバージョニング 無効のままでOK 今回はログの追記保存が目的で、同名ファイルを上書きする想定がないため、バージョン管理までは不要です

設定したら「バケットを作成」をクリックします。

GitHubリポジトリの作成

GitHub → 右上の「+」→ New repository

項目 理由
Repository name nw-log-collector AWXのProject設定時に、このリポジトリ名をURLとして指定するため
Description 任意 省略可
Public / Private Private推奨 社内NW機器の情報(ホスト名など)が含まれるため、外部に公開する必要はありません
Initialize this repository with a README チェックを入れる 空のリポジトリだと初回のgit cloneが挙動しづらいため、READMEだけ入れておきます

「Create repository」をクリックします。

確認ポイントhttps://github.com/(ユーザー名)/nw-log-collector にアクセスして、README.mdが表示されていればOKです。

ローカル環境にはこのリポジトリをcloneしておきます。

Playbookの作成

設計の整理

まずは何をするPlaybookなのかを整理します。

① Cisco機器にSSH接続(踏み台EC2経由)
        ↓
② 事前に定義した複数のshowコマンドを実行
        ↓
③ コマンド名付きで結果を整形
        ↓
④ S3バケットにアップロード

収集するコマンドは以下のように、障害調査でよく見るものを中心に選定しています。

commands:
  - show version
  - show interfaces status
  - show ip interface brief
  - show logging
  - show interfaces GigabitEthernet1/0/1
  # ...(実際は19個のコマンドを実行)

Playbookの実装

それではPlaybookの作成に取り掛かります。
collect_logs.yml として以下を作成します。

---
- name: Cisco機器ログ取得
  hosts: all
  gather_facts: no
  vars:
    ansible_network_os: cisco.ios.ios
    ansible_connection: network_cli
    ansible_become: false
    ansible_ssh_common_args: '-o ProxyJump=ubuntu@172.16.1.166 -o StrictHostKeyChecking=no'
  tasks:
    - name: show versionを実行する
      cisco.ios.ios_command:
        commands: show version
      register: r01

    - name: show interfacesを実行する
      cisco.ios.ios_command:
        commands: show interfaces
      register: r02

    - name: show ip interface briefを実行する
      cisco.ios.ios_command:
        commands: show ip interface brief
      register: r03

    - name: show running-configを実行する
      cisco.ios.ios_command:
        commands: show running-config
      register: r04

    - name: show vlan briefを実行する
      cisco.ios.ios_command:
        commands: show vlan brief
      register: r05

    - name: show spanning-treeを実行する
      cisco.ios.ios_command:
        commands: show spanning-tree
      register: r06

    - name: show mac address-tableを実行する
      cisco.ios.ios_command:
        commands: show mac address-table
      register: r07

    - name: show arpを実行する
      cisco.ios.ios_command:
        commands: show arp
      register: r08

    - name: show cdp neighbors detailを実行する
      cisco.ios.ios_command:
        commands: show cdp neighbors detail
      register: r09

    - name: show loggingを実行する
      cisco.ios.ios_command:
        commands: show logging
      register: r10

    - name: show clockを実行する
      cisco.ios.ios_command:
        commands: show clock
      register: r11

    - name: show inventoryを実行する
      cisco.ios.ios_command:
        commands: show inventory
      register: r12

    - name: show processes cpu sortedを実行する
      cisco.ios.ios_command:
        commands: show processes cpu sorted
      register: r13

    - name: show environment allを実行する
      cisco.ios.ios_command:
        commands: show environment all
      register: r14

    - name: show power inlineを実行する
      cisco.ios.ios_command:
        commands: show power inline
      register: r15

    - name: ログをS3に保存する
      amazon.aws.s3_object:
        bucket: nw-verify-logs
        object: "logs/{{ inventory_hostname }}/{{ lookup('pipe', 'date +%Y%m%d_%H%M%S') }}.log"
        content: |
          ========================================
          # show version
          ========================================
          {{ r01.stdout[0] }}

          ========================================
          # show interfaces
          ========================================
          {{ r02.stdout[0] }}

          ========================================
          # show ip interface brief
          ========================================
          {{ r03.stdout[0] }}

          ========================================
          # show running-config
          ========================================
          {{ r04.stdout[0] }}

          ========================================
          # show vlan brief
          ========================================
          {{ r05.stdout[0] }}

          ========================================
          # show spanning-tree
          ========================================
          {{ r06.stdout[0] }}

          ========================================
          # show mac address-table
          ========================================
          {{ r07.stdout[0] }}

          ========================================
          # show arp
          ========================================
          {{ r08.stdout[0] }}

          ========================================
          # show cdp neighbors detail
          ========================================
          {{ r09.stdout[0] }}

          ========================================
          # show logging
          ========================================
          {{ r10.stdout[0] }}

          ========================================
          # show clock
          ========================================
          {{ r11.stdout[0] }}

          ========================================
          # show inventory
          ========================================
          {{ r12.stdout[0] }}

          ========================================
          # show processes cpu sorted
          ========================================
          {{ r13.stdout[0] }}

          ========================================
          # show environment all
          ========================================
          {{ r14.stdout[0] }}

          ========================================
          # show power inline
          ========================================
          {{ r15.stdout[0] }}
        mode: put
        region: ap-northeast-1
      delegate_to: localhost

コマンドを1個ずつ実行するのではなくios_commandにリストで渡してまとめて実行するようにしています。
また、取得結果を見やすくするために===== コマンド名 =====区切りで整形しているのがポイントです。
これにより、S3に保存されたログを見るだけでどのコマンドの結果かが一目で分かるようになります。

GitHubリポジトリへのプッシュ

GitHubへのプッシュにはパスワード認証ではなくPersonal Access Token(PAT)が必要です。
作成してない場合は以下で発行します。

GitHub → 右上アイコン → Settings → Developer settings → Personal access tokens → Tokens(classic) → Generate new token

項目
Note awx-access
Expiration 90days
scope repoにチェック

AWXのCredentials設定

続いてAWX側の設定に入ります。
AWXでは「誰の権限で」「何に対して」実行するかをCredentialsという単位で管理します。
今回作るのは2種類です。「踏み台用」と「GitHub用」で、Cisco機器の認証はここでは作りません(理由は後述のInventory設定の章で説明します)。

Credential名 タイプ 用途
nw-verify-bastion-credential Machine 踏み台EC2へのSSH接続用
github-credential Source Control GitHubからPlaybookを取得する用

なぜこの2つを分けるのかというと、認証情報の性質が違うからです。踏み台への接続は「SSH鍵によるサーバーへの入口の認証」、GitHubへの接続は「PATによるソースコード取得の認証」で、どちらも役割がはっきり分かれています。混在させず別々に管理しておくことで、片方を変更したときにもう片方まで巻き込んで壊れるリスクを減らせます。

無知だったのですが、AWXのMachineタイプの認証情報スロットはJob Template1つにつき1個しか設定できないという制約があるみたいです。
今回はこの1枠を踏み台用に使うため、Cisco機器の認証情報をもう1つMachineタイプで作ってここに追加するということができませんでした。
NetworkタイプのCredentialを追加する方法もありますが、その場合Playbook側に変換処理を追加する必要が出て複雑になります。今回はあくまで検証なので認証はシンプルにInventory側へ直書きする方針にします。

github-credentialの作成

まずはgithub-credentialの作成をします。

AWX UI → 左メニュー「Credentials」→「Add」

項目
Name github-credential
Organization Default
Credential Type Source Control
Username (GitHubユーザー名)
Password 先ほど作成したPAT

「Save」をクリックします。

nw-verify-bastion-credentialの作成

AWX UI → 左メニュー「Credentials」→「Add」

項目
Name nw-verify-bastion-credential
Organization Default
Credential Type Machine
Username ubuntu
SSH Private Key 踏み台EC2の秘密鍵の中身を貼り付け

「Save」をクリックします。


AWXのInventory設定

InventoryはAWXが「どのホストに対して実行するか」を管理する台帳です。

AWX UI → 左メニュー「Inventories」→「Add」→「Add Inventory」

項目
Name nw-verify-inventory
Organization Default

作成後、「Hosts」タブからホストを追加します。

項目
ホスト名 devnetsandboxiosxec9k.cisco.com

ホスト変数(Variables)の設定

ホスト詳細画面の「Variables」に、接続方式を指定します。

ansible_host: devnetsandboxiosxec9k.cisco.com
ansible_network_os: cisco.ios.ios
ansible_connection: network_cli
ansible_become: false
ansible_user: DevNet ユーザー名
ansible_password: "DevNet パスワード"
ansible_ssh_common_args: '-o ProxyJump=ubuntu@172.16.1.166 -o StrictHostKeyChecking=no'

ansible_ssh_common_argsProxyJumpがポイントです。DevNet SandboxのCisco機器はSNMP/ICMPがブロックされている上、直接インターネットからSSH接続できない構成のため、踏み台EC2(172.16.1.166)を経由してSSH接続する設定をここで行っています。
名称未設定のデザイン (7).png
image.png

なぜInventory側の変数ではなくJob Template側でも同じ設定をするのか(後述)と疑問に思うかもしれませんが、Inventory側は「このホストに接続する際は常にこの経路を通る」という恒久設定、Job Template側は「このJob実行時に限り上書きする」という一時設定という役割分担になっています。基本的にはInventory側の設定だけで動きます。

AWXのProject設定(GitHub連携)

ProjectはAWXが「どこからPlaybookを取得するか」を定義する設定です。ここでようやくGitHubリポジトリとAWXがつながります。

AWX UI → 左メニュー「Projects」→「Add」

項目
Name nw-log-collector
Organization Default
Source Control Type Git
Source Control URL https://github.com/(ユーザー名)/nw-log-collector.git
Source Control Credential github-credential

「Save」をクリックすると、保存と同時にAWXが自動でGitHubからPlaybookを取得しにいきます。

確認ポイント:Project一覧画面で該当Projectの左側に緑のアイコンが表示されていればSync成功です。赤や黄色の場合はGitHubへの接続に失敗しているので、Source Control URLとCredentialを再確認します。

名称未設定のデザイン (6).png
image.png


AWXのJob Template作成

Job Templateは、これまで作ったCredentials・Inventory・Projectを1つにまとめて「実行の単位」にする設定です。AWX APIから呼び出すときも、このJob Templateの番号(ID)を指定します。

AWX UI → 左メニュー「Templates」→「Add」→「Add Job Template」

項目
Name nw-log-collect-job
Job Type Run
Inventory nw-verify-inventory
Project nw-log-collector
Playbook collect_logs.yml
Credentials nw-verify-bastion-credential

保存後、「Edit」を開いて「Variables」に以下を追加します。

ansible_ssh_common_args: '-o ProxyJump=ubuntu@172.16.1.166 -o StrictHostKeyChecking=no'

「Save」をクリックします。

AWX APIトークンの発行

次回、Lambdaからこのjob TemplateをAPI経由で起動するために、APIトークンを発行しておきます。

AWX UI → 右上ユーザーアイコン → トークン → トークンを追加

項目
説明 lambda-access
スコープ 書き込み

「保存」をクリックすると、トークンの文字列が表示されます。

このトークンは発行時の1回しか表示されません。閉じてしまうと再確認できず、再発行が必要になるので、必ずこの場でコピーしてメモしておきます。
image.png

単体確認

Lambdaからの自動起動を作り込む前に、まずはAWX UIから手動でJobを起動して、Playbookが正しく動くかを確認します。

AWX UI → 「Templates」→「nw-log-collect-job」→「起動(ロケットアイコン)」

実行が始まると「Jobs」画面に遷移し、リアルタイムでログが流れます。

以下のように表示されていれば成功です。

PLAY [Collect Cisco device logs] **********************************
TASK [Run show commands] **********************************
ok: [devnetsandboxiosxec9k.cisco.com]
TASK [Upload to S3] **********************************
ok: [devnetsandboxiosxec9k.cisco.com]
 
PLAY RECAP **********************************
devnetsandboxiosxec9k.cisco.com : ok=3  changed=1  unreachable=0  failed=0

image.png

failed=0になっていることと、unreachable=0になっていることがポイントです。unreachableが1以上の場合は踏み台経由のSSH接続自体に失敗しているので、Inventoryのansible_ssh_common_argsや踏み台EC2のセキュリティグループを再確認します。

続いてS3側も確認します。

S3コンソール → nw-verify-logsバケット → logs/フォルダ

devnetsandboxiosxec9k.cisco.com_(日時).log のようなファイルが作成されていれば成功です。中身を開いて===== show version =====のようにコマンドごとに区切られたログが入っていることを確認します。

image.png
image.png

しっかりログが取れてること確認できました!

うまくいかない場合の見方

症状 考えられる原因 確認ポイント
Job自体が起動しない Job Templateの設定不備 Inventory/Project/Credentialsが正しく紐づいているか
unreachableになる 踏み台経由のSSH接続失敗 nw-verify-bastion-credentialのSSH秘密鍵、ProxyJumpの記述、踏み台EC2のSG・起動状態
failedになる(showコマンドでエラー) Cisco機器側の認証失敗 Inventoryホスト変数のansible_user/ansible_password(DevNet Sandboxは定期的にリセットされる)
S3にアップロードされない IAM権限不足 AWXが動いているEC2のIAMロールにS3書き込み権限があるか
Project同期が失敗する(赤アイコン) GitHub接続失敗 github-credentialのPAT有効期限、Source Control URLのタイプミス

まとめ

今回はAWXとGitHubを連携させ、Playbookを実行してCisco機器のログをS3に保存するところまで実装しました。
AWXの認証情報周りがうまくいかず直書きになってしまったのでいい方法ないか模索しようと思います。

一旦、AWX単体では「NW機器のログをS3に自動収集する」ところまで準備が整いました。

次回はLambda連携編として、ServiceNowへのインシデント起票と同じタイミングでLambdaからAWX APIを呼び出し、障害検知からログ収集までを完全に自動化する部分を実装していきます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?