前回は作成するアプリケーションの全体像と、開発環境で使うDockerを紹介しました。今回はDiscordからメッセージを送信して、そのメッセージをEC2上で受け取るプログラムを作成します。
今回の概要とその準備
Discord上で使える!make
コマンドを作成し、コマンドと一緒にメッセージを送信します。具体的にはBotを通して、EC2上のPythonプログラムでそのメッセージを取得し、Discord上に応答が返ってくる仕組みを作ります。
今回使用するコードは前回ローカルマシン上に用意したgihyo-torecaディレクトリ内のchapter-2ディレクトリに用意してあります。
cd gihyo-toreca/chapter-2
Discordの開発者向けポータルサイトでの設定
まず最初に、Discordの開発者向けポータルサイトで新しいBotを作成し、Botのトークンを取得します。
Botを導入することで、Discord上のメッセージをAWSのEC2などの外部のサーバーで受信できるようになります。
Discordサーバーを用意する
Discord上でオリジルアプリを作成するには、管理権限のあるDiscordサーバーが必要なので、自分が作ったサーバーがまだない場合は事前に作成しておきましょう。
アプリをインストールしていない方はDiscord公式サイトからダウンロードしてインストールしてください。
サーバーの追加はDiscordアプリ内の

「オリジナルの作成」-
BotユーザーとBotトークンを作成する
Botユーザーを作成します。Discordの開発者ポータルサイトを開いてログインします。次に
ポップアップウィンドウが開くので、ボット名前を入力し、規約を確認しチェックを入れ、Createボタンをクリックします。

左側のメニューから

「Reset Token」

必ずこのトークンをコピーしておきましょう。なお、コピーできるのはボットを作成した直後だけです。トークンはパスワードのようなもので、また後で使いますので、メモ帳など、ローカル上の安全な場所に一旦保存しましょう。

最後に

BotをDiscordサーバーに追加する
次にBotをDiscordサーバーに追加します。左側のメニューから
環境によってはメニューのナビゲーション表示が画像と違う時がありますが、特に気にしないで大丈夫です。

「SCOPES」

ページ下の

これをブラウザのURL欄に貼り付け、エンターキーを押して遷移します。

認証画面に遷移しますので、管理権限のある追加したいサーバーを選択して

以上でBotが指定のDiscordサーバーに追加されました。Discordサーバーのメンバーリストには次のように表示されます。

Botアプリの作成
次に、Pythonを使ってとてもシンプルなBotを作ります。オリジナルのコマンド!make
)
この制御には、discord.
Botのプログラム「app.py」
実際のBotのプログラムはapp.
import discord # discord.pyライブラリをインポート。Discord Botの作成に必要です。
from discord.ext import commands # commandsフレームワークをインポート。コマンドベースのBotを簡単に作成できます。
import logging # loggingモジュールをインポート。ログ出力のために使用します。
import os # osモジュールをインポート。環境変数へのアクセスに使用します。
# 環境変数からDiscord Botのトークンを取得
TOKEN = os.getenv('DISCORD_BOT_TOKEN')
# loggingの基本設定を行います。ログレベルをINFOに設定し、ログのフォーマットを指定します。
logging.basicConfig(level=logging.INFO, format='%(asctime)s:%(levelname)s: %(message)s')
# Botクライアントの初期化。コマンドのプレフィックスを'!'に設定し、Botが受信するイベントの設定します。
intents = discord.Intents.default()
intents.message_content = True # メッセージ内容へのアクセスを有効にします。これにより、メッセージのテキストを読み取れるようになります。
bot = commands.Bot(command_prefix='!', intents=intents)
@bot.event
async def on_ready():
# BotがDiscordに接続したときに実行されるイベント。Botのログインが完了したことをログに記録します。
logging.info(f'Botが準備できました: {bot.user}')
@bot.command()
async def make(ctx, *, text: str):
# '!make <text>'というコマンドに反応して実行される関数。
# 受け取ったテキストメッセージをログに記録し、ユーザーに「ただいま作成中...」というメッセージを送信します。
logging.info(f'受信したメッセージ: {text}')
await ctx.send('ただいま作成中...')
bot.run(TOKEN) # Botを起動します。この関数により、BotはDiscordサーバーに接続し、コマンドの待受を開始します。
このプログラムの中でTOKEN = os.
と、Botのトークンを指定する箇所があります。しかしトークンはパスワードのようなものなので、プログラムに直接書くことは基本的にしません。
トークンを直接書かずに連携させる方法はいくつかありますが、今回は次のコマンドをターミナルで実行して、一時的に自分のPCにBotのトークンを環境変数として記憶してもらいます。
export DISCORD_BOT_TOKEN='ボット作成時にコピーしたトークン'
requirements.txtにdiscord.pyライブラリを追加
Pythonで必要になるライブラリをrequirements.
discord.py # 第2回に追加
ローカル環境でのBotアプリの動作確認
作成したBotプログラムをDockerコンテナ内で実行して、ローカルで動作を確認します。
requirements.
# requirements.txtを修正した場合は一度buildし直し直してから起動する docker-compose build --no-cache # アプリの起動 docker-compose up
Dockerコンテナが起動後、ターミナル上のログに
Discord上で次のコマンドを送ってみましょう。
!make 石の巨人のモンスター
Discord上で


このBotアプリの停止には、ターミナルで
EC2でアプリの公開
ローカル開発環境からの動作が確認できたところで、今度はEC2上に公開しましょう。
本番環境への公開への流れは次のとおりです。
- AWSの本番環境をローカル上からCDKで構築する
- 作成したEC2へローカルからアクセスする
- EC2にGitHubからアプリをクローンする
- EC2内でDockerを起動させて、アプリを実行する
ローカルでは、PCを閉じてしまったらプログラムは停止しますが、クラウド上のサーバーであるEC2に公開すれば24時間動き続けてくれます。なお、EC2についての詳しい情報についてはEC2の公式サイトを参照してください。
AWS CDKを利用して本番環境を構築する
AWS CDKを使うことでローカル上からコードでインフラを設計し、自動で作成できます。コードによる管理は設定漏れを防ぎ、修正や変更履歴はGitで管理できるため、変更内容が分かりやすく、共有するのにも便利です。
CDKの連携に必要なアクセスキーの発行
CDKの連携に必要なアクセスキーの発行には、AWS Consoleで

IAMのダッシュボードより、

遷移先ページの中ほどにある

今回、手順を簡略化するためルートユーザーで作っているため次のように注意書きが表示されますが、このままアクセスキーを作成します。

アクセスキーとシークレットアクセスキーが作成できました。この2つのキーは後述のCDKの連携設定時に必要となりますのでメモしてください。また厳重に管理してください。

CDK環境が用意されたDockerの準備
前回言及しましたが、AWS CDKがすでイントールされているDockerを用意しています。次のリポジトリをフォークしてください。
フォーク後、リポジトリをローカル上にクローンします。
git clone https://github.com/あなたのアカウント名/cdk-gihyo-toreca.git
Dockerの起動
まずdockerが置いてあるディレクトリに移動します。PCの環境に合わせて次のどちらかのディレクトリに移動します。
# macでm1およびm2を使っている場合 cd mac-m1-m2 # 上記以外の環境の場合 cd cdk
前回の開発環境時とは違い、今回はコンテナ内に入って作業します。そのため、手軽にDocker環境のコンテナ入れるように、Docker Composeのrun
コマンドを使います。その際に--rm
オプションを付けて、コンテナ終了後の自動削除を実行するようにしておきます。
docker-compose run --rm cdk
コンテナに入るとターミナルの表示が変わるはずです。次のように表示されれば準備完了です。
root@cdk-container:/work$
ちなみにコンテナから出る時はexit
を入力してください。
exit
CDK環境の初期設定
コンテナに入った状態で、AWSと連携できるようにCDKの初期設定を行います。それにはaws configure
コマンドを使います。
この入力後にap-northeast-1
)、出力形式にはjson
を指定しました。
aws configure --- # AWS Access Key ID [None]: 自分のAWS AccessKeyID # AWS Secret Access Key [None]: 自分のAWS SecretAccessKey # Default region name [None]: ap-northeast-1 # Default output format [None]: json ---
この設定は一度登録されると、~/.awsディレクトリ内のcredentialsとconfigに保存されます。
なお、このDocker環境ではPC上の~/.awsにも同じ設定が残るようにしているため、コンテナを削除しても以降は設定が不要となります。
CDKの初回起動とそのコードの設定
CDKを起動します。初回時のみ、次のコマンドを実行します。これによりCDKに必要な設定がAWSに上に作成されます。
cdk bootstrap
AWSに構築する設定はapp.
主にEC2の用意を実施し、これに伴い、EC2が配置される場所であるVPCとEC2へのアクセス権を管理するIAMを用意します。また、EC2作成時にDockerをインストールします。
デプロイ後はローカルからEC2に接続する必要があります。EC2との接続には、AWSのサービスのSSMを利用します。SSMはEC2のインスタンス名で簡単に接続できるようなるサービスで、一般的なSSHでの接続方法よりも簡単でセキュリティに強いのでオススメです。
デプロイ時のログにSSMの接続先情報を表示させるためにCfnOutputを利用しています。
なお、app.
from aws_cdk import (
App,
Stack,
aws_iam as iam,
aws_ec2 as ec2,
CfnOutput
)
from constructs import Construct
class DiscordBotEC2Stack(Stack):
def __init__(self, scope: Construct, id: str, **kwargs):
super().__init__(scope, id, **kwargs)
# VPCの作成
# AWSリソースを配置するための仮想ネットワークを作成します。ここでは最大2つのアベイラビリティーゾーンを使用します。
vpc = ec2.Vpc(self, "VPC", max_azs=2)
# IAMロールの作成
# EC2インスタンスがAWSサービスとやり取りするための認証・認可を管理します。
ec2_role = iam.Role(
self, "EC2Role",
assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
)
# EC2セキュリティグループの定義
# インバウンドおよびアウトバウンドのトラフィック制御を行うセキュリティグループを定義します。
ec2_sg = ec2.SecurityGroup(
self, "DiscordBotEC2-Sg",
vpc=vpc,
allow_all_outbound=True,
)
# ユーザーデータスクリプトの定義
# EC2インスタンス起動時に自動的に実行されるスクリプトを定義します。ここではシステムの更新、GitとDockerのインストール、
# Dockerサービスの設定を行います。
user_data = ec2.UserData.for_linux()
user_data.add_commands(
"sudo dnf update -y", # システムの更新
"sudo dnf install -y git docker", # GitとDockerのインストール
"sudo systemctl start docker", # Dockerサービスの開始
"sudo systemctl enable docker", # Dockerサービスの自動起動設定
"sudo usermod -aG docker ec2-user", # ec2-userをdockerグループに追加
)
# EC2インスタンスの定義
# Discord Botを実行するためのEC2インスタンスを定義します。ここではインスタンスタイプ、マシンイメージ、
# VPC、IAMロール、セキュリティグループ、ユーザーデータスクリプトを指定しています。
ec2_instance = ec2.Instance(
self, "EC2Instance",
instance_type=ec2.InstanceType("t2.micro"),
machine_image=ec2.GenericLinuxImage({'ap-northeast-1': 'ami-012261b9035f8f938'}),
vpc=vpc,
role=ec2_role,
security_group=ec2_sg,
user_data=user_data,
ssm_session_permissions=True # SSMセッションマネージャを通じたEC2インスタンスへのアクセスを許可
)
# SSMセッション開始コマンドの出力
# AWS CLIを使用してSSMセッションマネージャを介してEC2インスタンスに接続するためのコマンドを出力します。
CfnOutput(
self,
"StartSsmSessionCommand",
value=f"aws ssm start-session --target {ec2_instance.instance_id}\n\n"
"sudo su - ec2-user",
description="上記のコマンドを実行してSSMを利用してEC2に接続してください",
)
AWS本番環境の構築の実施
それでは起動しましょう。次のコマンドを実行するだけです。途中、構築を進めていいか確認されますが
cdk deploy
実行後およそ5分ほどでAWSの作成が完了します。
作成したEC2へローカルからアクセスする
AWSが構築できましたので、早速EC2に接続してみましょう。
EC2との接続にはAWSで用意されているSSMを利用します。SSMの接続コマンドをデプロイ完了時にターミナルに表示されていますので、コピーして使いましょう。

ターミナルにコピーしたコマンドを貼り付けて実行します。
aws ssm start-session --target 作成したEC2のインスタンスID
接続後、EC2内のシステムユーザーをec2-userに切り替えます。
sudo su - ec2-user

[ec2-user@ip-xxx-xxx-xxx-xxx]$
と表示されたら接続成功です。
EC2にGitHubからアプリをクローンする
EC2にGitHubからアプリをクローンします。もしオリジナルの変更を加えた場合はクローン前にGitHubのリポジトリへソースを反映させてからEC2へクローンしましょう。
git clone https://github.com/あなたのGitHubアカウント名/gihyo-toreca.git cd gihyo-toreca #実行したい各チャプターを指定 cd chapter-2 #READMEのマニュアルを参照 cat README.md
EC2内でDockerを起動させて、アプリを実行する
本番環境にはDocker Composeが用意されていないため、Dockerfileからイメージを作成して、そのイメージを起動します。
docker build --no-cache -t gihyo-toreca .
このDockerイメージを起動する際、開発時に利用したDISCORD_
の値を直接、起動コマンド内で指定します。
# Dockerイメージを起動 docker run --rm -it --name app-container -v "$(pwd)":/app -e DISCORD_BOT_TOKEN='あなたのボットのトークン' gihyo-toreca
これでアプリが起動しましたので、Discord上から!make 石の巨人のモンスター
と入力してみましょう。開発環境時と変わらずDiscord上からメッセージをターミナル上のログで表示できていたら成功です!

Discord上で
また、ローカルからEC2への接続を解除します。
exit
AWS本番環境の削除
SSMの接続解除までできたら、CDKによるAWS環境の削除をしてみましょう。途中、削除を進めていいか確認されますが
cdk destroy
最後に、Dockerを終了します。
exit
以上でEC2へのデプロイから削除の方法まで終わりました。
これでいつでも気軽にAWSの構築と削除ができるようになりました!
次回、EC2とBedrockの連携
第3回ではAmazonの生成AIであるBedrockとEC2の連携に挑戦します。そしてBedrockでは文章生成を行います。お楽しみに!