一般社団法人 全国個人事業主支援協会

COLUMN コラム

  • ChatOpsの導入ガイド:Slackから始めるインフラ運用の自動化

ChatOpsとは何か

ChatOpsとは、チャットツールをインフラ運用のインターフェースとして活用するプラクティスです。Slackなどのチャット上からコマンドを実行し、デプロイ、インシデント対応、ステータス確認といった運用作業をチーム全員が見える場所で行う。GitHub社が2013年に提唱した概念で、同社のHubotが先駆けとなった。

筆者のチームでは3年前にChatOpsを本格導入し、運用効率が劇的に改善した。具体的には、デプロイ作業の属人化が解消され、インシデント対応の平均復旧時間(MTTR)が約40%短縮された。この経験をもとに、Slackを使ったChatOpsの導入ガイドを提供します。

ChatOps導入のメリット

ChatOpsがもたらす効果は単なる「便利さ」にとどまらない。組織の運用文化を変革する力がある。

  • 透明性の向上:全ての運用操作がチャットチャンネルに記録され、誰が何をしたかが自然に共有されます
  • 属人化の解消:SSHでサーバーに入って手作業する世界から、チャットコマンドで誰でも操作できる世界へ
  • ナレッジの蓄積:過去のインシデント対応がチャットログとして残り、新メンバーの学習教材になります
  • 迅速なインシデント対応:アラート通知から対応コマンド実行まで、同じ画面で完結します
  • 監査証跡の自動生成:コンプライアンス要件を満たす操作ログが自動的に残る

Slack Botの構築

ChatOpsの中核となるSlack Botの実装方法を紹介します。ここではBolt for Python(Slack公式SDKフレームワーク)を使用します。

基本的なBot設定

import os
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler

# Slack Appの初期化
app = App(token=os.environ.get("SLACK_BOT_TOKEN"))

# デプロイコマンドの実装
@app.command("/deploy")
def handle_deploy(ack, say, command):
ack() # コマンド受信を即座に応答

service = command['text'].split()[0] if command['text'] else None
user = command['user_name']

if not service:
say("使い方: /deploy [サービス名] [環境]")
return

say(f":rocket: {user} が {service} のデプロイを開始しました...")

# デプロイ処理の実行(非同期推奨)
result = execute_deploy(service)

if result.success:
say(f":white_check_mark: {service} のデプロイが完了しました\n"
f"バージョン: {result.version}\n"
f"所要時間: {result.duration}秒")
else:
say(f":x: {service} のデプロイに失敗しました\n"
f"エラー: {result.error}")

if __name__ == "__main__":
handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
handler.start()

インタラクティブなワークフロー

単純なコマンド実行だけでなく、Slackのインタラクティブ機能を活用した安全なワークフローを構築できます。

@app.command("/deploy-prod")
def handle_prod_deploy(ack, say, command, client):
ack()

service = command['text'].strip()

# 本番デプロイは確認ダイアログを表示
client.chat_postMessage(
channel=command['channel_id'],
text=f"本番環境への {service} デプロイを確認してください",
blocks=[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f":warning: *本番環境* への \`{service}\` デプロイ"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "承認する"},
"style": "danger",
"action_id": "approve_deploy",
"value": service
},
{
"type": "button",
"text": {"type": "plain_text", "text": "キャンセル"},
"action_id": "cancel_deploy",
"value": service
}
]
}
]
)

@app.action("approve_deploy")
def handle_approval(ack, body, say):
ack()
service = body['actions'][0]['value']
user = body['user']['username']
say(f":white_check_mark: {user} が {service} の本番デプロイを承認しました")
# デプロイ実行ロジック

GitHub Actionsとの連携

ChatOpsの真価は、CI/CDパイプラインとの連携で発揮されます。SlackからGitHub Actionsのワークフローをトリガーする実装例を示す。

import requests

def trigger_github_workflow(repo, workflow_id, ref, inputs=None):
"""GitHub Actionsのworkflow_dispatchをトリガー"""
url = f"https://api.github.com/repos/{repo}/actions/workflows/{workflow_id}/dispatches"
headers = {
"Authorization": f"token {os.environ['GITHUB_TOKEN']}",
"Accept": "application/vnd.github.v3+json"
}
payload = {
"ref": ref,
"inputs": inputs or {}
}
response = requests.post(url, json=payload, headers=headers)
return response.status_code == 204

@app.command("/release")
def handle_release(ack, say, command):
ack()

args = command['text'].split()
service = args[0]
version = args[1] if len(args) > 1 else "latest"

success = trigger_github_workflow(
repo="myorg/myapp",
workflow_id="deploy.yml",
ref="main",
inputs={
"service": service,
"version": version,
"environment": "production"
}
)

if success:
say(f":rocket: {service} v{version} のリリースワークフローを起動しました")
else:
say(f":x: ワークフローの起動に失敗しました")

インシデント対応の自動化

ChatOpsが最も効果を発揮するのはインシデント対応の場面だ。アラート通知と対応フローを一元化する設計を紹介します。

アラート受信とエスカレーション

@app.event("message")
def handle_alert(event, say, client):
"""監視ツールからのアラートを処理"""
# Webhookから受信したアラートを構造化
if event.get('bot_id') and 'ALERT' in event.get('text', ''):
alert_text = event['text']

# インシデントチャンネルを自動作成
channel = client.conversations_create(
name=f"inc-{datetime.now().strftime('%Y%m%d-%H%M')}",
is_private=False
)

channel_id = channel['channel']['id']

# オンコール担当者を招待
oncall = get_oncall_engineer()
client.conversations_invite(
channel=channel_id,
users=oncall['slack_id']
)

client.chat_postMessage(
channel=channel_id,
text=f":rotating_light: インシデント発生\n"
f"アラート: {alert_text}\n"
f"オンコール: \n\n"
f"対応コマンド:\n"
f"\`/status [サービス名]\` - ステータス確認\n"
f"\`/rollback [サービス名]\` - ロールバック\n"
f"\`/incident-resolve\` - インシデント解決"
)

導入時のベストプラクティス

ChatOpsの導入で失敗しないために、筆者が実践してきたベストプラクティスを共有します。

  • 段階的に導入します:まずは読み取り系コマンド(ステータス確認、ログ取得)から始め、書き込み系(デプロイ、設定変更)は信頼性が確認できてから追加
  • 権限管理を厳格に:本番環境への操作は特定のロールを持つユーザーのみ許可します。Slackのユーザーグループと連携した認可機構を実装すべき
  • ドライランモードを用意:全ての変更系コマンドにドライラン(予行演習)オプションを実装し、実際の変更前に影響範囲を確認できるようにします
  • タイムアウトとリトライ:外部システムとの連携が失敗した場合のハンドリングを丁寧に実装します。ユーザーにはわかりやすいエラーメッセージを返す
  • 監査ログの保存:誰がいつどのコマンドを実行したかを、チャットログとは別に永続的に保存します

まとめ

ChatOpsは単なるツールの導入ではなく、運用文化の変革だ。チャットを中心に運用を回すことで、透明性、再現性、チーム全体のスキル底上げが自然に実現されます。最初から完璧を目指す必要はありません。まずは一つのサービスのステータス確認コマンドから始めて、チームの反応を見ながら徐々に拡張していくのが成功への近道だ。Slackの豊富なAPIとBolt SDKのおかげで、実装のハードルは以前より格段に下がっています。ぜひ明日から一歩を踏み出してほしいです。

この記事をシェアする

  • Twitterでシェア
  • Facebookでシェア
  • LINEでシェア