AWS Chalice + Docker で定期実行 Slack 通知を作る — ローカル開発から AWS デプロイまで

ノートパソコンの画面に9時を示す表示があり、通知ベルのアイコンが表示されているイラスト。みなさん、こんにちは。万次郎です。

「毎日決まった時間に Slack へ通知を送りたい」——そんな要件に出会ったことはないでしょうか。日次のレポート、定期的なリマインダー、システムの稼働状況の報告など、スケジュール実行と Slack 通知の組み合わせは、実務でよく登場するパターンです。

AWS Lambda と EventBridge を使えば、この仕組みは比較的簡単に実現できます。しかし、『CloudFormation の YAML を書くのが面倒』『ローカルでの動作確認がしづらい』といった課題を感じることもあるのではないでしょうか。

今回は、AWS Chalice というフレームワークを使って、この課題を解決していきます。Chalice は Python のデコレータでインフラ設定が完結するため、YAML を書く必要がありません。さらに Docker を組み合わせることで、ローカルでの開発体験も向上させます。

この記事を読み終えるころには、ローカル開発から AWS デプロイまで、一連の流れを体験できるようになっています。ぜひ最後までお付き合いください。

※「定刻に Slack へ固定の文面を送るだけ」であれば、EventBridge Scheduler から Webhook を直接叩くほうが手軽です(Lambda のコードも不要)。この記事は、レポートの中身を組み立てるのにコードが必要なケース——データを取得して整形する、条件によって内容を変える、といった処理を Lambda に載せる前提で進めます。

1. 今回作るもの

まずは、今回構築するシステムの全体像を把握しておきましょう。

EventBridge (スケジュール)
    ↓
Lambda (Dockerコンテナ)
    ↓
Slack Webhook
    ↓
Slackチャンネル

仕組みはシンプルです。EventBridge が毎日決まった時間に Lambda 関数を起動し、Lambda 関数が Slack Webhook を通じてメッセージを送信します。

この構成自体は、SAM や Serverless Framework でも実現できます。では、なぜ Chalice を選ぶのでしょうか。

YAML を書かずにスケジュール設定ができる

Lambda + EventBridge で定期実行を実現する場合、一般的には CloudFormation や SAM のテンプレートを書く必要があります。たとえば、毎日 9 時(UTC)に Lambda を実行したい場合、SAM では以下のような設定が必要です:

# SAMの場合(template.yaml)
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Resources:
  DailyReportFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: app.handler
      Runtime: python3.11
      Events:
        ScheduleEvent:
          Type: Schedule
          Properties:
            Schedule: cron(0 9 * * ? *)
            Name: daily-report-schedule
            Enabled: true

YAML の構文を調べながら、インデントに気をつけながら、リソース名を考えながら書いていく。慣れれば問題ありませんが、最初のうちはなかなか手間がかかります。

一方、Chalice ではこうなります:

# Chaliceの場合(app.py)
from chalice import Chalice, Cron

app = Chalice(app_name="slack-reporter")

@app.schedule(Cron(0, 9, "*", "*", "?", "*"))
def daily_report(event):
    # ここに処理を書く
    return {"status": "success"}

見比べてみてください。Chalice では、関数に@app.schedule()デコレータを付けるだけです。YAML ファイルを別途用意する必要もなければ、リソース名や ARN を意識する必要もありません。デプロイすると、Chalice が EventBridge ルールの作成から Lambda との紐付けまで、すべて自動で行ってくれます。

Python を書いている延長線上で、インフラ設定が完了する。

ここが Chalice を選ぶ最大の理由です。

Docker を組み合わせる理由

Chalice 単体でも十分便利ですが、今回はさらに Docker を組み合わせます。

Lambda 関数の開発でよくある悩みが、『ローカルで動作確認がしづらい』という点です。chalice localというコマンドもありますが、これは HTTP エンドポイントのテスト用であり、スケジュール関数のテストには向いていません。

そこで、AWS 公式が提供する Lambda 用の Docker イメージを使います。このイメージには Lambda 実行環境がそのまま入っているため、ローカルでも本番と同じ方法で関数を呼び出せます。docker compose upでコンテナを起動し、curl でリクエストを送るだけで動作確認ができるようになります。

また、Docker を使うことで、依存パッケージの問題も回避できます。ローカルの macOS や Windows で動いていたのに、Lambda 環境(Amazon Linux)では動かない——特にネイティブライブラリを含むパッケージで起こりがちなこの問題も、最初から Linux 環境で開発することで防げます。

では、実際に構築していきましょう。まずは環境の準備からです。

2. 前提条件・環境

実装に入る前に、開発環境を整えておきましょう。今回のシステムでは、ローカル開発に Docker を、AWS 連携に Chalice と AWS CLI を使います。それぞれ順番に確認していきます。

Python 環境の確認

まずは Python です。Chalice は Python フレームワークなので、これがないと始まりません:

python --version
# Python 3.11.x 以上

Python 3.11 以上であれば問題ありません。もし古いバージョンの場合は、pyenv などでアップデートしてください。

Docker 環境の確認

次に Docker です。ローカルで Lambda 関数をテストするために使います。AWS にデプロイする前に手元で動作確認できるので、開発効率が格段に上がります:

docker --version
# Docker version 20.10.x 以上

docker compose version
# Docker Compose version v2.x.x

AWS CLI・Chalice の準備

ここまではローカル開発に必要なツールでした。記事の後半では AWS にデプロイするので、AWS 関連のツールも準備しておきます。

まずは AWS CLI がインストールされていて、認証情報が設定されているか確認します:

aws --version
# aws-cli/2.x.x

aws sts get-caller-identity
# アカウント情報が表示されればOK

aws sts get-caller-identityでアカウント情報が表示されれば、認証情報は正しく設定されています。表示されない場合は、aws configureで設定してください。

続いて、Chalice 本体をインストールします:

uv pip install chalice --system
chalice --version
# chalice 1.31.0 以上

これで AWS へのデプロイ準備も完了です。

Slack Incoming Webhook の取得

最後に、Slack 通知のための Webhook URL を取得します。これがないと Slack にメッセージを送れません。

すでに Webhook URL を持っている方は、この手順をスキップして構いません。まだの方は、以下の手順で作成してください:

  1. Slack APIにアクセス
  2. 「Create New App」→「From scratch」を選択
  3. App 名(例:Daily Reporter)と通知先のワークスペースを設定
  4. 左メニューの「Incoming Webhooks」をクリック
  5. 「Activate Incoming Webhooks」を ON にする
  6. 「Add New Webhook to Workspace」で通知先チャンネルを選択
  7. 生成された Webhook URL(https://hooks.slack.com/services/...)をコピー

この URL は後ほど環境変数として設定します。安全な場所にメモしておいてください。

これで開発に必要なツールがすべて揃いました。次は、実際にプロジェクトを作成していきます。

3. プロジェクト構成

環境が整ったので、プロジェクトを作成していきましょう。まずはディレクトリを作ります:

mkdir slack-reporter
cd slack-reporter

次に、Chalice が必要とするディレクトリを作成します:

mkdir -p .chalice chalicelib

ここで.chalicechalicelibという 2 つのディレクトリが登場しました。どちらも Chalice 独自の規約によるもので、この名前でないと正しく動作しません。それぞれの役割を説明します。

.chalice/は、Chalice の設定やデプロイ情報が格納されるディレクトリです。この中にconfig.jsonを置くことで、Lambda 関数のメモリサイズやタイムアウト、環境変数などを設定できます。

chalicelib/は、app.pyから参照する追加モジュールを置く場所です。今回は Slack 通知の処理をここに切り出します。Chalice は、デプロイ時にこのディレクトリを自動的にパッケージに含めてくれます。lib/src/など別の名前にしてしまうと、デプロイ時にファイルが含まれないので注意してください。

最終的なディレクトリ構成は以下のようになります:

slack-reporter/
├── .chalice/
│   └── config.json      # Chaliceの設定ファイル
├── chalicelib/
│   ├── __init__.py
│   └── slack.py         # Slack通知モジュール
├── .env                  # 環境変数(Webhook URL)
├── .env.example          # 環境変数のテンプレート
├── .gitignore
├── app.py                # メインのアプリケーション
├── docker-compose.yaml   # ローカル開発用
├── Dockerfile            # Lambda用コンテナ定義
└── requirements.txt      # Pythonパッケージ

ファイル数は多く見えますが、一つずつ順番に作っていけば難しくありません。まずは Docker 関連のファイルから始めましょう。

4. Docker 環境の構築

ここでは、Lambda 用の Docker イメージを構築します。

通常の Docker イメージではなく、AWS 公式が提供する Lambda 専用のベースイメージを使用します。このイメージには、Lambda 実行環境(Runtime Interface Client)が組み込まれており、ローカルでも Lambda と同じ方法で関数を呼び出せます。

requirements.txt

まずは依存パッケージを定義します:

chalice>=1.31.0
requests>=2.31.0
  • chalice: 今回のメインフレームワーク。デコレータや Cron 式の定義に使用。app.pyfrom chalice import ...するため、Docker コンテナ内でも必須
  • requests: Slack Webhook への HTTP リクエストに使用

Dockerfile

FROM public.ecr.aws/lambda/python:3.11

# uv のバイナリを公式イメージから取得(バージョンは固定しておくと再現性が高い)
COPY --from=ghcr.io/astral-sh/uv:0.11.26 /uv /uvx /bin/

WORKDIR ${LAMBDA_TASK_ROOT}

# 依存パッケージをインストール(uv を使用)
COPY requirements.txt .
RUN uv pip install --system --no-cache -r requirements.txt

# アプリケーションコードをコピー
COPY app.py .
COPY chalicelib/ ./chalicelib/

# Lambdaハンドラーを指定
CMD ["app.handler"]

各行を見ていきましょう:

説明
FROM public.ecr.aws/lambda/python:3.11 AWS 公式の Lambda 用 Python イメージ。Lambda 実行環境が組み込まれている
COPY --from=ghcr.io/astral-sh/uv:0.11.26 /uv /uvx /bin/ uv の公式イメージから uv バイナリだけを取り出して配置(マルチステージ)。ホストに uv が入っていなくてもビルドできる
WORKDIR ${LAMBDA_TASK_ROOT} Lambda のコード配置先(/var/task)に移動
COPY requirements.txt .RUN uv pip install --system ... 依存パッケージを先にインストール(Docker のレイヤーキャッシュを活用)。--system でイメージのシステム Python に直接入れ、--no-cache でレイヤーを軽くする
COPY app.py . / COPY chalicelib/ ... アプリケーションコードをコピー
CMD ["app.handler"] Lambda 起動時に呼び出す関数を指定(app.pyhandler関数)

pip install のままでも動きますが、COPY --from で uv バイナリを差し込むだけで、ビルド時の依存解決が高速化します。ghcr.io/astral-sh/uv:0.11.26 のようにバージョンを固定しておくと、いつビルドしても同じ uv で解決されるため再現性が上がります(latest でも動きますが、固定を推奨)。

docker-compose.yaml

ローカル開発用の Docker Compose 設定です:

services:
  lambda:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "9000:8080"
    environment:
      - SLACK_WEBHOOK_URL=${SLACK_WEBHOOK_URL}
    volumes:
      - ./app.py:/var/task/app.py:ro
      - ./chalicelib:/var/task/chalicelib:ro

設定のポイントを説明します:

ポートマッピング(9000:8080

Lambda 用のベースイメージは、内部でポート 8080 をリッスンしています。これをホストの 9000 番ポートにマッピングすることで、http://localhost:9000から Lambda 関数を呼び出せるようになります。

環境変数(SLACK_WEBHOOK_URL

${SLACK_WEBHOOK_URL}は、ホスト側の環境変数または.envファイルから読み込まれます。機密情報を docker-compose.yaml に直接書かずに済むのがメリットです。

ボリュームマウント(volumes

app.pychalicelib/をマウントしているため、コードを編集するとコンテナを再ビルドせずに反映されます。開発中の試行錯誤が格段に楽になります。

環境変数ファイルの作成

.env.exampleをテンプレートとして作成し、.envにコピーして使用します:

# .env.example(テンプレート)
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXXX/XXXX/XXXX
# .envを作成してWebhook URLを設定
cp .env.example .env
# .envを編集して、実際のWebhook URLを設定

.envファイルには Webhook URL が含まれるため、.gitignoreに追加してリポジトリにコミットしないようにしましょう:

# .gitignore
.env
.chalice/deployed/
.chalice/deployments/
__pycache__/
*.pyc

5. Chalice アプリケーションの実装

ここでは、定期実行される Lambda 関数のメインロジックを実装します。

コードの全体構造

app.pyには 3 つの関数を定義します:

関数 役割
create_daily_report() レポート作成のコアロジック。Slack 通知を実行する
daily_report(event) Chalice のスケジュール関数。AWS デプロイ時に EventBridge から呼び出される
handler(event, context) Lambda ハンドラー。Docker でのローカルテスト時に呼び出される

『なぜ 2 つのエントリポイントが必要なのか?』と思われるかもしれません。

Chalice の@app.schedule()で定義した関数は、Chalice 独自のイベントラッパーを経由して呼び出されます。しかし、Docker コンテナとしてローカル実行する場合、このラッパーは使えません。そこで、コアロジックをcreate_daily_report()に切り出し、両方のエントリポイントから呼び出す構造にしています。

app.py

from datetime import datetime

from chalice import Chalice, Cron
from chalicelib.slack import send_slack_message

app = Chalice(app_name="slack-reporter")

def create_daily_report():
    """レポート作成のコアロジック"""
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    message = f"日次レポート -{now}\n\n定期実行されたスケジュールレポートです。"
    # 他にもここに複雑な処理を書けます

    send_slack_message(message)

    return {"status": "success", "timestamp": now}

@app.schedule(Cron(0, 9, "*", "*", "?", "*"))
def daily_report(event):
    """毎日9時 UTC(=日本時間18時)に実行される定期レポート"""
    return create_daily_report()

def handler(event, context):
    """Lambdaハンドラー(ローカルテスト用)"""
    return create_daily_report()

Cron 式の書き方

@app.schedule()に渡すCronオブジェクトは、6 つの引数を取ります:

Cron(分, 時, 日, 月, 曜日, 年)

今回のCron(0, 9, "*", "*", "?", "*")を分解すると:

位置 意味
0 0 分
9 9 時(UTC)
* 毎日
* 毎月
曜日 ? 指定なし
* 毎年

注意:時刻は UTC で指定します。

日本時間(JST = UTC+9)で実行したい場合は、9 時間引いた値を指定してください:

実行したい時刻(JST) 指定する時刻(UTC)
朝 9 時 Cron(0, 0, ...)
昼 12 時 Cron(0, 3, ...)
夕方 18 時 Cron(0, 9, ...)
深夜 0 時 Cron(0, 15, ...)

その他のスケジュール例

日次以外のスケジュールも簡単に設定できます:

# 毎週月曜日の朝9時(UTC)
@app.schedule(Cron(0, 9, "?", "*", "MON", "*"))
def weekly_report(event):
    ...

# 毎月1日の0時(UTC)
@app.schedule(Cron(0, 0, "1", "*", "?", "*"))
def monthly_report(event):
    ...

# 平日の毎朝9時(UTC)
@app.schedule(Cron(0, 9, "?", "*", "MON-FRI", "*"))
def weekday_report(event):
    ...

曜日を指定する場合は、日(3 番目の引数)を?にする必要があります。日と曜日は同時に*を指定できません。

6. Slack 通知の実装

次に、Slack へメッセージを送信するモジュールを実装します。

chalicelib/init.py

まず、chalicelibを Python パッケージとして認識させるため、空の__init__.pyを作成します:

touch chalicelib/__init__.py

chalicelib/slack.py

import os

import requests

def send_slack_message(message: str) -> dict:
    """Slack Webhookを使ってメッセージを送信する"""
    webhook_url = os.environ.get("SLACK_WEBHOOK_URL")

    if not webhook_url:
        raise ValueError("SLACK_WEBHOOK_URL is not set")

    payload = {"text": message}

    response = requests.post(webhook_url, json=payload, timeout=10)
    response.raise_for_status()

    return {"status_code": response.status_code, "body": response.text}

コードの各部分を見ていきましょう:

環境変数から Webhook URL を取得

webhook_url = os.environ.get("SLACK_WEBHOOK_URL")

Webhook URL はソースコードに直接書かず、環境変数から取得します。これにより、開発環境と本番環境で異なる Slack チャンネルに通知を送ることも容易になります。

Webhook へのリクエスト送信

payload = {"text": message}
response = requests.post(webhook_url, json=payload, timeout=10)
response.raise_for_status()

Slack Webhook は、{"text": "メッセージ"}という形式の JSON を POST するだけで通知が送れます。timeout=10で 10 秒以上応答がない場合はエラーにし、raise_for_status()で HTTP エラー時に例外を発生させます。

Block Kit でリッチなメッセージを送る(発展)

シンプルなテキストメッセージで十分な場合が多いですが、見出しやボタンを含むリッチなメッセージを送りたい場合は、payloadを拡張します:

payload = {
    "blocks": [
        {
            "type": "header",
            "text": {"type": "plain_text", "text": "Daily Report"}
        },
        {
            "type": "section",
            "text": {"type": "mrkdwn", "text": f"*日時*:{now}"}
        }
    ]
}

Block Kit の詳細は Slack 公式ドキュメントを参照してください。


7. Chalice 設定

最後に、Chalice の設定ファイルを作成します。このファイルは、chalice deployで AWS にデプロイする際に使用されます。

.chalice/config.json

{
  "version": "2.0",
  "app_name": "slack-reporter",
  "stages": {
    "dev": {
      "api_gateway_stage": "dev",
      "lambda_memory_size": 256,
      "lambda_timeout": 30,
      "environment_variables": {
        "SLACK_WEBHOOK_URL": ""
      }
    }
  }
}

各設定項目の意味は以下の通りです:

項目 説明
version Chalice 設定ファイルのバージョン。現在は"2.0"を使用
app_name アプリケーション名。AWS リソースの命名に使われる
stages 環境ごとの設定。devprodなど複数定義可能
lambda_memory_size Lambda に割り当てるメモリ(MB)。Slack 通知程度なら 256MB で十分
lambda_timeout Lambda 実行のタイムアウト(秒)。Webhook 送信なら 30 秒で余裕がある
environment_variables Lambda 実行時の環境変数

本番環境での環境変数の扱い

開発時は.envファイルで Webhook URL を管理しますが、本番デプロイ時はセキュリティ上、別の方法を検討してください:

方法 1: config.json に直接記述(シンプルだが非推奨)

"environment_variables": {
  "SLACK_WEBHOOK_URL": "https://hooks.slack.com/services/..."
}

config.json がリポジトリに含まれる場合、機密情報が漏洩するリスクがあります。

方法 2: AWS Systems Manager Parameter Store(推奨)

Webhook URL を Parameter Store に保存し、Lambda 実行時に取得する方法です。Chalice は直接の Parameter Store 連携はサポートしていませんが、コード内でboto3を使って取得できます。

8. ローカルでの動作確認

ここまでで必要なファイルはすべて揃いました。実際に動かしてみましょう。

ファイル一覧の確認

$ ls -la
.chalice/
chalicelib/
.env
.env.example
.gitignore
app.py
docker-compose.yaml
Dockerfile
requirements.txt

Docker イメージのビルドと起動

# Dockerイメージをビルドしてコンテナを起動
docker compose up -d --build

初回はベースイメージのダウンロードがあるため、数分かかる場合があります。

起動したかどうかは以下で確認できます:

$ docker compose ps
NAME           SERVICE   STATUS    PORTS
src-lambda-1   lambda    running   0.0.0.0:9000->8080/tcp

Lambda 関数の呼び出し

Lambda 用の Docker イメージには、Runtime Interface Emulator(RIE)が含まれています。これにより、本番の Lambda と同じエンドポイント形式で関数を呼び出せます:

curl -X POST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'

成功すると以下のようなレスポンスが返ります:

{ "status": "success", "timestamp": "2025-12-30 06:00:43" }

同時に、Slack チャンネルにメッセージが届いているはずです。確認してみてください。

この呼び出しはhandlerを直接叩くため、@app.schedule経由で届く EventBridge イベントの中身は検証できていません。イベントフィールドを参照するように拡張した場合は、後述の EventBridge 形式ペイロードやaws lambda invokeを使って確認してください。

うまくいかない場合

コンテナのログを確認する

docker compose logs lambda

よくあるエラーと対処法:

エラー 原因 対処
SLACK_WEBHOOK_URL is not set 環境変数が設定されていない .envファイルを確認
401 Unauthorized Webhook URL が無効 Slack API で URL を再生成
Connection refused コンテナが起動していない docker compose up -dを再実行

コンテナの停止

確認が終わったら、コンテナを停止します:

docker compose down

9. AWS へのデプロイ

ローカルでの動作確認ができました。Slack にメッセージが届くことを確認できたので、次は本番環境である AWS にデプロイしてみましょう。

ここで一つ疑問が浮かぶかもしれません。『Docker コンテナの中でデプロイするの?』という点です。

答えは「いいえ」です。Docker はあくまでローカルでの開発・テスト用であり、AWS へのデプロイはchalice deployコマンドをホストマシンで実行します。ローカル環境から AWS CLI の認証情報を使って AWS にアクセスし、必要なリソースを作成してくれます。

デプロイ前の準備

chalice deployを実行する前に、一つ準備が必要です。

Chalice はrequirements.txtを見て自動で依存関係をパッケージ化するため、デプロイだけを行うならホストに事前インストールする必要はありません。準備として行うのは、環境変数の設定です。

ローカルテストでは.envファイルで Webhook URL を管理しましたが、AWS にデプロイする際は.chalice/config.jsonに設定します。このファイルを開いて、SLACK_WEBHOOK_URLに実際の URL を設定してください:

{
  "version": "2.0",
  "app_name": "slack-reporter",
  "stages": {
    "dev": {
      "api_gateway_stage": "dev",
      "lambda_memory_size": 256,
      "lambda_timeout": 30,
      "environment_variables": {
        "SLACK_WEBHOOK_URL": "https://hooks.slack.com/services/XXXX/XXXX/XXXX"
      }
    }
  }
}

なお、config.json を Git リポジトリにコミットする場合、Webhook URL が漏洩するリスクがあります。本番運用では、AWS Systems Manager Parameter Store を使用するなどの対策が必要です(応用編で紹介予定)。今回は開発環境なので、config.json に直接記述して進めます。

いよいよデプロイ

準備が整いました。以下のコマンドを実行してください:

chalice deploy

初回デプロイでは、以下のような出力が表示されます:

Creating deployment package.
Creating IAM role: slack-reporter-dev
Creating lambda function: slack-reporter-dev-daily_report
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:xxxx:function:slack-reporter-dev-daily_report

出力を見ると、Chalice がいくつかのリソースを作成していることがわかります。まずデプロイパッケージ(アプリケーションコードと依存パッケージをまとめた ZIP ファイル)を作成し、次に Lambda 実行用の IAM ロールを作成、最後に Lambda 関数本体を作成しています。

ここで注目すべき点があります。出力には明示的に表示されていませんが、実は EventBridge ルールも同時に作成されています。Chalice は@app.schedule()デコレータを検出して、自動的にスケジュールを設定してくれるのです。

本当にスケジュールが設定されているか確認する

デプロイが完了したら、本当に EventBridge ルールが作成されているか確認してみましょう:

aws events list-rules --name-prefix slack-reporter
{
  "Rules": [
    {
      "Name": "slack-reporter-dev-daily_report-event",
      "State": "ENABLED",
      "ScheduleExpression": "cron(0 9 * * ? *)"
    }
  ]
}

app.py@app.schedule(Cron(0, 9, "*", "*", "?", "*"))と定義したスケジュールが、そのまま EventBridge ルールとして作成されています。State: ENABLEDとなっているので、スケジュールはすでに有効です。毎日 9 時(UTC)、つまり日本時間 18 時になると、このルールが Lambda 関数を自動的に起動します。

また、Chalice は Lambda 実行に必要な IAM ポリシーも自動で生成しています:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:*:logs:*:*:*"
    }
  ]
}

CloudWatch Logs への書き込み権限のみが付与されています。Slack Webhook への通知は HTTPS リクエストなので、特別な AWS 権限は不要です。Chalice は必要最小限の権限(Least Privilege)を自動で設定してくれます。

手動で Lambda 関数を実行してみる

スケジュールの時間まで待つのは時間がかかります。今すぐ動作確認したい場合は、手動で Lambda 関数を呼び出すことができます。

ただし、一点注意があります。Chalice のスケジュール関数は、EventBridge から送られてくる特定の形式のイベントを期待しています。単に空のペイロードを送ると、イベントの解析でエラーになってしまいます。

そこで、EventBridge 形式のテストイベントを作成します:

cat << 'EOF' > /tmp/event.json
{
  "version": "0",
  "id": "test-event",
  "detail-type": "Scheduled Event",
  "source": "aws.events",
  "account": "123456789012",
  "time": "2026-01-01T00:00:00Z",
  "region": "ap-northeast-1",
  "resources": [],
  "detail": {}
}
EOF

この JSON は、実際に EventBridge が Lambda 関数を呼び出す際に送信するイベントの形式を模倣しています。ファイルを作成したら、AWS CLI で Lambda 関数を呼び出します:

aws lambda invoke \
  --function-name slack-reporter-dev-daily_report \
  --payload fileb:///tmp/event.json \
  /tmp/output.json
  • -payload fileb://...fileb://は、ファイルをバイナリとして読み込む指定です。AWS CLI v2 ではこの形式が必要になります(代替として、-cli-binary-format raw-in-base64-out を付けたうえで file:// を使う方法もあります)。

実行すると、以下のような出力が表示されます:

{
  "StatusCode": 200,
  "ExecutedVersion": "$LATEST"
}

StatusCode: 200は、Lambda 関数が正常に実行されたことを示しています。戻り値も確認してみましょう:

cat /tmp/output.json
{ "status": "success", "timestamp": "2025-12-30 06:28:09" }

ローカルテストの時と同じレスポンスが返ってきました。同時に、Slack チャンネルにメッセージが届いているはずです。確認してみてください。

これで、ローカル開発から AWS デプロイまでの一連の流れが完了しました。

コードを修正したら

開発を進めていく中で、コードを修正することがあるでしょう。その場合は、再度chalice deployを実行するだけで更新できます:

chalice deploy
Creating deployment package.
Reusing existing deployment package.
Updating lambda function: slack-reporter-dev-daily_report
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:xxxx:function:slack-reporter-dev-daily_report

Reusing existing deployment packageと表示されているのは、依存パッケージに変更がないためです。コードの差分だけがアップロードされるので、2 回目以降のデプロイは高速です。

使い終わったらクリーンアップ

開発やテストが終わって、AWS リソースが不要になった場合は削除しておきましょう:

chalice delete
Deleting function: arn:aws:lambda:ap-northeast-1:xxxx:function:slack-reporter-dev-daily_report
Deleting IAM role: slack-reporter-dev

このコマンドで、Chalice が作成した Lambda 関数、IAM ロール、EventBridge ルールがすべて削除されます。

なお、Lambda 関数は実行時間に応じた課金なので、待機しているだけでは料金は発生しません。そのため、すぐに削除する必要はありませんが、不要なリソースは整理しておくと管理がしやすくなります。

10. まとめ

今回は、Chalice + Docker + EventBridge を使った定期 Slack 通知システムの構築方法をご紹介しました。

今回作ったもの

slack-reporter/
├── .chalice/config.json    # Chalice設定
├── chalicelib/slack.py     # Slack通知モジュール
├── app.py                  # メインアプリケーション
├── Dockerfile              # Lambda用コンテナ
└── docker-compose.yaml     # ローカル開発環境

たったこれだけのファイルで、ローカル開発から AWS デプロイまで完結できました。

Chalice を使うメリット(振り返り)

冒頭で SAM との比較をしましたが、改めて整理すると:

  • YAML を書かなくていい: @app.schedule()デコレータだけで EventBridge 連携が完了
  • Python だけで完結: インフラ設定もビジネスロジックも同じ言語で書ける
  • 学習コストが低い: Flask を使ったことがあれば、すぐに馴染める
  • デプロイが簡単: chalice deploy一発で Lambda + EventBridge が構築される

次のステップ

この構成をベースに、以下のような拡張が可能です:

  • 複数スケジュールの追加: 日次・週次・月次レポートを 1 つのアプリで管理
  • 動的なレポート生成: データベースや API から情報を取得してレポートに含める
  • エラーハンドリング: 失敗時のリトライやアラート通知
  • Parameter Store 連携: 機密情報をセキュアに管理

これらの発展的な内容は、次回の応用編で紹介予定です。

ぜひ、みなさんのプロジェクトでも活用してみてください。