以前からLocalStackのPro版を使っていたのですが、忘れっぽいので記事にしたいと思います。今回はDocker上にLocalstack(pro)を立てて、Lambda・SNSを作成し外部端末からLocalstackのSNSトピック宛てメッセージを送ってみたいと思います。Lambda関数は外部メールを送信するようセットしておきます。
前提
ホストOS:Ubuntu 22.04.1 LTS
Docker version: 27.0.3, build 7d4bcd8
Docker Compose version: v2.28.1
1. Dockerコンテナ構築
まずは任意のディレクトリを作成し、そこにdocker-compose.ymlを準備します。
※マニュアル記載の内容からアドレスのみ変更(ホストOSのIP)に変更します。
version: "3.8"
services:
localstack:
container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}"
image: localstack/localstack-pro # required for Pro
ports:
- "10.x.x.x:4566:4566" # 自分の環境に合わせる
- "10.x.x.x:4510-4559:4510-4559" # 自分の環境に合わせる
- "10.x.x.x:443:443" # 自分の環境に合わせる
environment:
# Activate LocalStack Pro: https://docs.localstack.cloud/getting-started/auth-token/
- LOCALSTACK_AUTH_TOKEN=${LOCALSTACK_AUTH_TOKEN:?} # required for Pro
# LocalStack configuration: https://docs.localstack.cloud/references/configuration/
- DEBUG=${DEBUG:-0}
- PERSISTENCE=${PERSISTENCE:-0}
volumes:
- "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
コンテナを起動する前にAuthTokenを確認します。
LocalStack管理画面のWorkspace -> Auth Token から確認できます。
AuthTokenが確認できたら、以下コマンドでAuthTokenをexportしたのちにコンテナを起動します。
export LOCALSTACK_AUTH_TOKEN="ls-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
docker compose up -d
2. 管理画面からステータスを確認する。
以下、URLにアクセスします。
https://app.localstack.cloud/inst/default/status
画面右上に表示されているEditボタンをクリックして、EndpointのIPをホストOSのIPに変更しておきます。正しく反映されるとステータスがrunningになります。
3. Lambda関数作成
続いて、LocalstackのコンテナにてLambda関数を登録していきます。コンテナにログインしてコマンドを実行てLambda関数用のコードを作成します。
docker exec -it localstack-main /bin/bash
apt update
apt install vim #ファイル作成の為インストールしておく
vim send-e-mail.py
import json
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import os
def lambda_handler(event, context):
sns_message = event['Records'][0]['Sns']['Message']
sender_email = 'xxxxxxxx@example.com' # 送信アドレス
recipient_email = 'xxxxxxxx@example.com' # 宛先アドレス
smtp_server = 'example.com' # メールサーバ
smtp_port = '587' # Port番号
smtp_user = 'xxxxxxxx@example.com' # ユーザ
smtp_password = 'XXXXXXXXXX' # パスワード
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = recipient_email
msg['Subject'] = 'SNS Notification'
msg.attach(MIMEText(sns_message, 'plain'))
try:
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls()
server.login(smtp_user, smtp_password)
server.sendmail(sender_email, recipient_email, msg.as_string())
return {
'statusCode': 200,
'body': json.dumps('Email sent successfully!')
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps(f'Failed to send email: {str(e)}')
}
次にIAMロールの登録に必要なJSONファイルをvimで作成しておきます。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
では、以下コマンドを順に実行してIAMロール作成します。
# IAMロールを作成
awslocal iam create-role --role-name lambda-role --assume-role-policy-document file://trust-policy.json
# IAMロールにポリシーをアタッチ
awslocal iam attach-role-policy --role-name lambda-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
# ロールのARNを確認
awslocal iam get-role --role-name lambda-role
作成したロールを使ってLambda関数を登録します。登録する前にコードを圧縮しておいて、圧縮したファイルを登録する形になります。
# 作成したコードを圧縮
zip send-e-mail.zip send-e-mail.py
# Lambda関数を登録
awslocal lambda create-function --function-name send-e-mail --runtime python3.10 --role arn:aws:iam::000000000000:role/lambda-role --handler send-e-mail.lambda_handler --zip-file fileb://send-e-mail.zip
無事作成されるとLocalStack管理画面から状況を確認できます。
4. SNSトピック作成
次はSNSトピックを作成します。コマンドで作成してもよいのですが、管理画面からも作成できるのでこちらから実施したいと思います。Status画面を開き、SNSをクリックします。
SNSトピック名を入れてSubmitボタンをクリックします。
作成されたトピックをクリックして、Subscriptionsをクリックします。
ProtocolにLambdaとして、Endpointには作成されたLambda関数のARNを入れて、画面右したのSubmitをクリックします。
これで、Lambda関数とSNSトピックが紐づきました。
試しにDockerコンテナから以下のコマンドを実行してLambda関数が実行されるかどうか確認してみましょう
awslocal sns publish --topic-arn arn:aws:sns:us-east-1:000000000000:test --message "Hello, this is a test message"
LocalStack管理画面からCloudWatch Logsを見ると実行結果がわかります。
5. 外部端末からSNSトピック宛てにアクション
それでは最後に、外部端末からSNSトピック宛てに通信させてみたいと思います。
import boto3
from botocore.config import Config
from botocore.exceptions import NoCredentialsError
# LocalStackエンドポイントを指定
my_config = Config(
region_name = 'us-east-1',
signature_version = 'v4',
retries = {
'max_attempts': 10,
'mode': 'standard'
},
)
# dummy認証情報(これが無いとエラーとなる)
session = boto3.Session(
aws_access_key_id='dummy',
aws_secret_access_key='dummy',
region_name='us-east-1'
)
sns = session.client('sns', endpoint_url='http://10.x.x.x:4566', config=my_config)
topic_arn = 'arn:aws:sns:us-east-1:000000000000:test'
response = sns.publish(
TopicArn=topic_arn,
Message='Hello from another Linux OS!',
Subject='Test Message'
)
print(response)
SNS/Lambdaなどを使って他システムと連携したりするパターンもあるかと思いますが、Localstackを利用すればある程度の事がローカル環境で実現できるので、非常に便利です。