GitHub ActionsとAWX Operatorで実現するGitOpsによるリリース自動化 - 後編 -

GitHub ActionsとAWX Operatorで実現するGitOpsによるリリース自動化 - 後編 -

はじめに

こんにちは、EC基盤開発本部SRE部カート決済SREブロックの金田・小松です。普段はSREとしてZOZOTOWNのカート決済機能のリプレイスや運用を担当し、AWSやAkamaiの管理者としても活動しています。

前編では、手動リリース作業が抱える課題を解決するために、GitHub Actionsを活用したリリースプロセス自動化の概要について解説しました。GitHub Actionsによる変更検知、ジョブ制御、結果通知の仕組みを構築することで、手作業の工数削減と安定性向上を実現する一連のフローをご紹介しました。

techblog.zozo.com

後編では、GitHub Actionsと連携してリリース作業を具体的に遂行するためのツールであるAWX Operatorについて、導入の背景や構成、実装の詳細に焦点を当ててご紹介します。AWX Operatorを活用することで、いかに効率的なリリース自動化を実現したのかをお伝えします(図における赤枠部分を中心に説明します)。

AWX Operatorを中心としたリリースプロセスの全体像

目次

リリース自動化の中核となるAWX Operator

リリース自動化の話に入る前に、まずAWX Operatorに関連する基本用語について整理します。

AWXとは?

AWXは、AnsibleのWebベースのUIやAPIを提供するオープンソースプロジェクトで、Ansibleの自動化タスクの管理、スケジュール、実行が可能です。

github.com

既存のZOZOTOWNオンプレミス環境でもAWXサーバが存在しており、オンプレミス環境における運用作業を自動化しています。例えば、以下のような様々な運用タスクの自動化に活用されています。

  • 数百台のクライアントやサーバーへのエージェント一括インストール
  • 定期バッチ処理の自動停止・再開
  • Windows Updateの自動適用
  • ネットワーク疎通確認
  • IISアプリケーションプールのリサイクルによるWebサービスの可用性維持など

これらの作業を自動化することで、人的エラーを最小限に抑えながら、安定した運用を実現しています。既存AWXの運用については、以下のブログで紹介されているので、ぜひこちらもご参照ください。

techblog.zozo.com

AWX Operatorとは?

AWX Operatorは、Kubernetes環境でAWXをデプロイ・管理するためのツールです。

github.com

Kubernetesは、コンテナ化されたアプリケーションの管理を自動化するプラットフォームです。AWX Operatorを使うことで、Kubernetesクラスタ内にAWXを簡単にインストールし、設定やアップデートを効率的に行うことができます。また、デプロイ作業の自動化やサーバー設定の適用といったタスクを、Playbookで効率的に管理・実行が可能です。

Playbookとは?

Playbookは、Ansibleが実行する一連のタスクを定義したYAML形式のファイルです。例えば、以下のような操作を自動化できます。

  • サーバーの設定(例:WebサーバーやDBサーバーの構築)
  • アプリケーションのデプロイ(例:アプリのインストールと設定)
  • ネットワーク機器の設定(例:ルータやスイッチの構成変更)

以下は、Webサーバーを構築するPlaybookの例です。

---
- name: Install and configure Apache
  hosts: web_servers
  tasks:
    - name: Install Apache
      yum:
        name: httpd
        state: present
    - name: Copy configuration
      template:
        src: /path/to/httpd.conf.j2
        dest: /etc/httpd/conf/httpd.conf
    - name: Restart Apache
      service:
        name: httpd
        state: restarted

Playbookは「何を実行するか」を細かく順序立てて記載でき、リリースフローの中核を担います。

ジョブテンプレートとは?

ジョブテンプレートは、AWXでPlaybookを実行するための「設定パッケージ」のようなものです。具体的には以下の要素を含みます。

  • Playbook:実行するタスクの定義。
  • Inventory:タスクを適用するサーバーやホストの一覧。
  • Extra Vars:実行時に渡すオプションのパラメータ(例:環境やリリース条件)。
  • Execution Node:Playbookを実行する対象ノード。

ジョブテンプレートを使うことで、「どのサーバーに、どのPlaybookを、どのパラメータで実行するか」を簡単に指定できます。そのため、リリース作業の自動化が容易になります。

AWX Operatorの選定理由

本プロジェクトでAWX Operatorを選定した主な理由は以下の通りです。

  • 既存のAWX運用ノウハウを活用
    • 既にオンプレミスでAWXを利用しており、運用に関する知識や技術をそのまま活用できるため、スムーズに移行が可能であること。
  • Ansible Playbookの再利用が容易
    • 従来のPlaybookをほぼそのままKubernetes環境に適用できるため、導入コストを低減し、既存の運用プロセスを簡単に移行できる。
  • Kubernetes環境でのデプロイ・管理が容易
    • 弊社のインフラでは多くのリソースがコード化されており、EKSクラスタを使用しているため、AWX Operatorの導入により、インフラとアプリケーションの管理を一元化できるメリットがあった。

これらの理由から、AWX Operatorを選定し、GitHub Actionsと統合することで効率的なリリースフローを実現しています。

AWX Operatorの導入と構成

本プロジェクトでは、AWX OperatorをAmazon EKS上で運用しつつ、ジョブ実行ノード(以降Execution Nodeと称します)をオンプレミス環境に配置しています。この構成を採用した主な理由は、本プロジェクトのリリース対象がオンプレミス環境のサーバであるケースが大半を占めているためです。

AWX Operator 構成図

オンプレミス環境に配置しない場合の選択肢

ジョブ実行ノードをオンプレミス環境に配置しない場合、AWX Operatorとジョブ実行ノードの両方をEKS上で運用できます。ただし、この場合、リリース対象がオンプレミス環境であることを考慮すると、以下のデメリットが懸念されます。

  • ネットワーク遅延
    • クラウド上のジョブ実行ノードがオンプレミスリソースにアクセスする際、クラウドとオンプレミス間で頻繁に通信が発生します。この通信に伴い、ネットワーク遅延がリリース全体の速度低下につながる可能性があります。
  • データ転送量の増大とコスト
    • ジョブの実行に必要なデータ(例:ファイル、設定情報)がクラウドとオンプレミス間で往復するため、ネットワーク帯域を圧迫するリスクがあります。また、クラウドプロバイダーの通信料金(特にアウトバウンド通信料金)も増加し、運用コスト増につながる可能性があります。

今回の構成のメリット

オンプレミス環境のリソースに対するリリースが大半を占めるため、ジョブ実行ノードをオンプレミス環境に配置する構成を採用することで、以下のメリットが得られます。

  • ネットワーク遅延の最小化
    • ジョブがオンプレミス環境内で直接実行されるため、ジョブの実行に必要な主要な通信が内部ネットワークで行われます。これにより、クラウドとオンプレミス間の往復通信が最小限となり、リリース速度が向上します。
  • データ転送量の削減とコスト効率の向上
    • ジョブの処理やデータのやり取りが主にオンプレミス環境内で完結するため、クラウドとの間で発生するデータ転送量が削減されます。これにより、ネットワーク帯域の負担を軽減すると同時に、クラウドプロバイダーの通信料金が抑えられるため、コスト効率が向上します。

以上のように、オンプレミスリソースに対する高速かつ効率的なリリースが可能になるため、本構成を採用しました。

AWX Operatorの導入方法については、インストールガイドを参照してください。ここでは、Execution NodeをAWXに登録する手順をご紹介します。

事前準備

Execution NodeではAnsibleが必要です。Ansible公式のインストールガイドに従い、インストールします。

docs.ansible.com

構築手順

以下に、Execution Nodeの構成とAWX Operatorの設定手順を説明します。

1. AWX Operatorのインスタンスグループ作成とインスタンス追加
  • Webコンソールの「インスタンスグループ」メニューから、「インスタンスグループの追加」をクリックし、必要な項目を入力します。
    • 名前:インスタンスグループの名前を設定します(例:execution-group)。
    • ポリシーインスタンスの最小値:インスタンスグループ内で最低限必要なインスタンス数を設定します。
    • ポリシーインスタンスの割合:必要なインスタンス数を割合で指定できます。
    • Max concurrent jobs:同時に実行可能なジョブ数の上限を設定します。
    • Max forks:ジョブごとに使用できる最大フォーク数を設定します。

インスタンスグループ作成

  • Webコンソールの「インスタンス」メニューから「追加」ボタンをクリックし、インスタンスを追加します。以下のポイントに従って設定します。
    • ホスト名:Execution Nodeの完全修飾ドメイン名(FQDN)またはIPアドレスを指定します。
    • リスナーポート27199を設定します。これはReceptorが使用するポートです。
    • インスタンスタイプExecutionを選択します。
    • Peers from control nodes:制御ノードから自動的にピアリングする場合はこのオプションを有効にします。

インスタンス追加

2. インスタンスの関連付け

作成したインスタンスグループを選択した状態で、インスタンスタブより、追加したインスタンスを関連付けます。

インスタンスの関連付け

3. Execution Nodeのバンドルインストール

Execution Nodeでは、AWX Operatorがジョブを分散実行する基盤となるReceptorサービスを利用しています。Receptorは、AWXのコントローラーとExecution Node間の通信を担当するメッセージングシステムであり、ジョブの実行を効率的に分散・管理する重要な役割を果たします。以下に、Receptorを含むExecution Nodeのインストール手順を説明します。

  • バンドルのダウンロード:AWXコンソールの「インスタンス」詳細ページで、バンドルのダウンロードボタンをクリックし、必要なファイルを取得します。このバンドルにはTLS証明書、Receptor設定ファイルが含まれます。

バンドルのダウンロード

  • インストール:ダウンロードしたファイルをExecution Nodeへ転送し、以下のコマンドで展開してインストールを実行します。
tar -xzf [バンドルファイル]
ansible-galaxy collection install -r requirements.yml

Tips:トラブルが発生した場合、Ansibleのフォーラムに解決策が掲載されている場合があります。

forum.ansible.com

以上の手順で、AWX OperatorにExecution Nodeが登録され、AWX上からExecution Node経由でジョブを実行できるようになります。これにより、オンプレミス環境とAWS EKSを連携させたリリース自動化の基盤が構築され、システム全体の操作効率が向上します。

Receptorネットワーク構成について

Receptorは、Execution NodeとAWXのコントローラー間で通信するための分散ネットワークを構成します。通常、以下の設定内容は前述のバンドルインストールで自動的に生成されるため、手動で編集する必要はありませんが、参考までに設定例を紹介します。

# Receptorノードの基本設定。ノードIDとピアとして許可するノードIDを指定します。
- node:
    id: <node-id>                   # このノードのID(任意の一意な名前)
    allowedpeers: <peer-node-ids>    # 接続を許可するピアノードIDのリスト

# ログレベルの設定。デフォルトはinfo、デバッグ用途にはdebugに設定可能。
- log-level: info

# ジョブの実行設定。ansible-runnerを使用してジョブを実行します。
- work-command:
    worktype: ansible-runner         # 実行するワークの種類
    command: ansible-runner          # 実行コマンド
    params: worker                   # ワーカーとして機能する設定

# サーバー用のTLS設定。接続に使用する証明書とキーのパスを指定します。
- tls-server:
    name: default
    filename: /etc/receptor/tls/tls.crt   # TLS証明書ファイル
    key: /etc/receptor/tls/tls.key        # TLSキー

# クライアント用のTLS設定。接続先の証明書を検証するためのCA証明書を指定します。
- tls-client:
    name: receptor-client
    rootcas: /etc/receptor/tls/ca.crt     # ルートCA証明書

# UDP接続の設定。ピアノードとの通信に使用するホストとポートを指定します。
- connection:
    type: udp
    peer: <peer-ip>:27199                 # ピアのIPアドレスとポート

# TCP接続の設定。UDPと同様にピアノードとの通信に使用します。
- connection:
    type: tcp
    peer: <peer-ip>:27199                 # ピアのIPアドレスとポート
接続状況の確認

設定後、以下のコマンドで他ノードとの接続状態を確認できます。

sudo receptorctl status

Receptorを用いることで、ジョブ実行の分散性や拡張性が向上し、大規模環境でも安定したリリースを実現します。また、ピアノード間の通信設定を最適化することで、効率的なネットワーク構築が可能です。

デバッグとトラブルシューティングのTips
  • ログレベルの調整:デフォルトのlog-level: infoでは一般的なログが表示されますが、トラブルシューティングが必要な場合はdebugに変更すると詳細なログを取得できます。
  • ノード再起動時の接続確認:ノードの再起動後、sudo receptorctl ping <peer-node-id>を使って個別に接続が正常かテストできます。
  • 証明書の確認:Receptorで使用するTLS証明書に問題がある場合は、opensslコマンドを使って証明書の有効期限や接続テストを行うと便利です。
openssl s_client -connect <peer-ip>:27199

ジョブテンプレートのコード管理

AWXのCLI(Command Line Interface)を利用したジョブテンプレートとインベントリのコード管理を実施しており、GitリポジトリとAWX間での同期を保つようにしています。

docs.ansible.com

  • 主要なコマンド
    • ジョブテンプレートの作成awx job importコマンドで新規テンプレートを自動反映。
    • 変更のエクスポートawx exportコマンドで変更を検出し、Gitと同期。
使用例:ジョブテンプレートの同期プロセス(GitHub Actionsを使用)

以下のようなGitHub Actions Workflowを設定し、AWXのジョブテンプレート変更をGitリポジトリに反映させるプルリクエストを自動で作成しています。

name: Sync AWX Job Templates
on:
  schedule:
    - cron: '0 0 * * *'  # 毎日0時に実行
  workflow_dispatch:  # 手動での実行トリガー

jobs:
  sync-job-templates:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
      # リポジトリのチェックアウト。最新のテンプレートと比較するために必要です。

    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install -y jq python3-pip
        pip3 install awxkit
      # 必要な依存ツールとパッケージのインストールを行います。

    - name: Export job templates
      run: |
        mkdir -p job_templates
        templates=$(awx job_templates list -f json | jq -r '.results[] | {id: .id, name: .name}')
        for template in $(echo "$templates" | jq -r '.name'); do
          echo "Exporting template: $template"
          template_id=$(echo "$templates" | jq -r --arg name "$template" 'select(.name == $name) | .id')
          ./scripts/awx/export_job_template.sh "$template_id"
        done
        echo "Templates exported"
      # AWXから各ジョブテンプレートをエクスポートし、リポジトリ内のjob_templatesフォルダに保存します。

    - name: Create Pull Request
      uses: peter-evans/create-pull-request@v6
      with:
        commit-message: "Update AWX Job Templates"
        branch: "update-job-templates"
        title: "Update AWX Job Templates"
        body: "This PR updates the AWX job templates and ensures they are in sync with the latest operational settings."
        labels: "automated pr"
        base: "main"
        delete-branch: true
      # エクスポートしたテンプレートをもとにGitHubで自動プルリクエストを作成します。
  1. リポジトリのチェックアウト
  2. 最初にリポジトリをクローンし、現在のジョブテンプレートと最新テンプレートとの差分を取得できるようにします。
  3. 依存関係のインストール
  4. awxkitなどの依存ツールをインストールし、AWXからのデータ取得が可能な状態にします。
  5. テンプレートのエクスポート
  6. AWXからジョブテンプレートをエクスポートし、各テンプレートのIDと名前を取得してローカルフォルダに保存します。
  7. プルリクエストの作成
  8. エクスポートしたテンプレートを基に、リポジトリに自動的にプルリクエストを作成し、更新をチームでレビューできるようにします。

このようにして、テンプレートの更新はプルリクエストを通じてレビューされ、AWXの設定内容がGitリポジトリに同期されるため、一貫性と追跡性を保証しながら変更内容を管理できます。

AWX APIを利用したGitHub Actionsとの連携

GitHub ActionsとAWX Operatorの連携は、AWXのREST APIを通じて行います。以下の図は、GitHub Actionsでのスクリプト実行からAWX APIを通じたジョブ実行、そしてリリースプロセス全体の流れを示しています。

AWX APIを利用したGitHub Actionsとの連携

この図をもとに、GitHub Actionsからリリース作業を開始し、ジョブを実行・監視するまでの一連の流れを解説します。

APIリクエストの基本構造

まず、APIリクエストを送信するためのcurl構造を作成します。以下の関数で、POSTやGETリクエストをAPI送信できるようにしています。

#!/usr/bin/env bash

function call_awx_api() {
  token="$1"
  base_url="$2"
  method="$3"
  path="$4"
  if [ "${method}" == "POST" ]; then
    req="$5"
    response=$(curl -s -X POST \
      -H "Authorization: Bearer ${token}" \
      -H "Content-Type: application/json" \
      -d "${req}" \
      "${base_url}/api/v2/${path}")
    echo "${response}"
    return
  fi

  response=$(curl -s -X "${method}" \
    -H "Authorization: Bearer ${token}" \
    -H "Content-Type: application/json" \
    "${base_url}/api/v2/${path}")
  echo "${response}"
  return
}

この関数で利用している各パラメータの役割について、以下に簡単に説明します。

  • token:APIリクエストで認証するためのトークンです。本記事で紹介するプロジェクトでは、このリリース用に作成したAWXユーザーのトークンを使用しています。トークンの作成方法は、対象のAWXユーザーでログイン後、ユーザー > AWXユーザー > トークンタブにて作成可能です。トークンベース認証の詳細については以下のドキュメントをご参照ください。

docs.ansible.com

このトークンはGitHubのSecretsに格納しており、GitHub Actionsから参照しています。

  • base_url:AWX OperatorのServiceで指定しているドメイン名です。APIエンドポイントのベースURLとして使用され、AWXサーバーのアドレスを指定します。
  • method:APIリクエストのHTTPメソッドを指定します。通常はGETまたはPOSTを指定し、POSTの場合は追加のデータ(req)が送信されます。
  • path:api/v2以降のパスを指定します。たとえば、job_templates//launchでジョブテンプレートの起動や、jobs/<job_id>でジョブのステータスを取得できます。APIの詳細についてはAWXのドキュメントを参照してください。

ansible.readthedocs.io

ジョブの実行リクエスト

次に、GitHub ActionsからAWXのジョブを起動するためのリクエストを構成します。以下のように、リクエストボディでextra_varsを利用して、ジョブ実行に必要な変数(例:commit hash、GitHubトークン、リリースの順序)を渡します。これにより、後続のスクリプトでこれらの変数を利用できます。また、limitパラメータでリリース対象のサーバーを指定して、ジョブが実行される範囲を制限しています。

# GitHub ActionsのワークフローからAWX OperatorのAPIを利用してジョブ実行するスクリプト例
requestbody=$(cat << EOS
{
  "extra_vars": {
    "commit_hash": "${ASP_COMMIT_HASH}",
    "github_token": "${ASP_GITHUB_TOKEN}"
  },
  "limit": "${limitation}",
  "inventory": "${inventory}",
  "job_type": "run"
}
EOS
)

response=$(call_awx_api "${API_TOKEN}" "${AWX_BASE_URL}" "POST" "job_templates/<id>/launch/" "${requestbody}")
job_id=$(echo "${response}" | jq -r '.job')
echo "Job ID: ${job_id}"

ここで行っている処理は以下の通りです。

  • extra_vars:後続のスクリプトで使用する変数(例:commit hash、GitHubトークンなど)を渡しています。
  • limit:指定されたサーバー範囲内でジョブを実行し、リリース対象のサーバーを制限します。
  • inventory:AWX上で管理されている特定のインベントリを指定します。
  • job_type:実行ジョブの種類を指定します。ここでは通常の実行を示すrunを設定しています。

ジョブの監視

ジョブの実行が開始されると、ジョブIDを取得し、そのステータスを定期的に監視します。以下のループでjqを使ってジョブのステータスを取得し、進行状況をチェックしています。ジョブがpendingrunning以外の状態(成功や失敗など)になるまで監視し、完了したタイミングでループを終了します。

while true; do
    response=$(call_awx_api "${API_TOKEN}" "${AWX_BASE_URL}" "GET" "jobs/${job_id}/")
    job_status=$(echo "${response}" | jq -r '.status')
    echo "Job Status: ${job_status}"
    if [ "${job_status}" != "pending" ] && [ "${job_status}" != "running" ]; then
        break
    fi
    sleep 5
done

上記の構成で、ジョブが完了するまでの間、定期的にジョブの進行状況を確認できるようにしています。ジョブの進行状況の監視が完了すれば、一連のリリース作業は終了となります。

この仕組みにより、GitHub Actionsを起点としたリリースフローが安定して実現し、手動作業による負担やリスクを大幅に軽減できました。

まとめ

後編では、GitHub Actionsと連携してリリース作業を具体的に遂行するためのツールであるAWX Operatorについて、その導入背景や構成、実装の詳細に焦点を当てて解説しました。本記事で取り上げた主要なポイントは以下の通りです。

主要なポイント

  • AWX Operatorの導入と活用
    • Kubernetes環境での簡易なAWXデプロイを実現し、クラウドとオンプレミスを統合した分散リリース環境を構築。
    • Receptorを利用して、安定したジョブ分散と効率的な通信を可能に。
  • ジョブテンプレートの構成管理
    • Playbook、Inventory、Extra Varsを活用し、柔軟かつ一貫したリリースフローを管理。
  • GitHub Actionsとの連携によるリリースフローの自動化
    • REST APIを利用し、ジョブの実行・進行状況の監視・エラー検知を自動化。
    • 手作業を排除し、迅速で信頼性の高いリリースを実現。

これにより、リリース作業の自動化による運用効率の向上が期待できます。リリース自動化の導入はまだ始まったばかりですが、すでに以下のような成果が期待されています。

期待される成果

  • リリースの安定化
    • 自動化されたプロセスにより、手動エラーは排除され、リリースの一貫性の向上が期待されます。
  • エラーの削減
    • 段階的リリースごとにエラーをリアルタイムで監視し、迅速に対処することで、エラーによる影響を最小限に抑えることが可能です。
  • リリース速度の向上
    • リリース作業の自動化により、全体の工数削減と迅速なリリースが見込まれます。

さいごに

本記事では、前編と後編の2部構成で、GitHub ActionsとAWX Operatorを活用したリリース自動化の取り組みをご紹介しました。従来の手動リリースから脱却し、効率化と安定性を実現することで、工数削減やリリースリスクの低減を目指しています。

リリース自動化は継続的な改善が必要な分野ですが、本取り組みを通じて、さらなる最適化や監視機能の強化を進める予定です。この記事が、皆さんのシステム運用やリリース自動化の参考になれば幸いです。

私たちZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。興味をお持ちの方は、ぜひ以下のリンクからご応募ください。

hrmos.co

カテゴリー