はじめに
最近、Fargateを用いてローカルで開発したものをデプロイしてデモサイトを公開する機会がありました。そのときにコストを削減するために、ELBやRoute53を用いず、さらにFargateをスケジューリングで停止と起動を繰り返すようにしていたら、FargateにはElastic IPを割り当てられないのでアクセスするURLを固定化できないという欠点に当たりました。ここでは、それを無理やり対処した方法を紹介します。
対象者
- Fargateを使って楽にサービスをデプロイしたい人
- コストの問題でELB, Route53が使えない人
- AWSでとりあえずデモサイトを一般に公開したい人
結論
API gatewayを用いてlambda関数にアクセスし、lambda関数からタスクを動かしているFargateのIPアドレスを持ってきてリダイレクトさせます。
方法
テスト用にECSを作成する
ここではnginxのimageをECSとしてFargateに公開します。既に自分でECSを構築している場合は飛ばしても問題ありません。
ECSのタスク定義から「新しいタスク定義の作成」を選択します。以下のように入力して、作成をクリックします。
タスク定義を作成した後、クラスターを作成します。ここではdemo-nginx-clusterという名前で基本的にデフォルトの設定のものを作成します。
作成されたクラスターを選択してサービスを作成します。コンピューティングオプションとして起動タイプを選択し、アプリケーションタイプでサービスを選択して、使用するタスク定義として先ほど作成したdemo-nginx
を選択します。
作成を押して、サービスが展開されるまで数分待ちます。起動したら、demo-nginx-clusterの詳細画面からdemo-nginx-serviceを選択して、タスクタブを開いて、動いているタスクを選択すると以下の画面が表示されます。
パブリックIPに書いてあるIPアドレスにアクセスするとnginxのコンテナに接続できます。
ECSの構築はgithub Actionsを用いてCI/CDを設定しておくとpushするたびにECSへデプロイしておくようにすると楽です。
ECSのIPアドレスを取得しリダイレクトさせる
前の節でECSをデプロイしてパブリックIPからアクセスできるようになりました。しかし、問題としてこのパブリックIPはタスクが起動しなおすたびに新しいものが割り振られてしまいます。またFargateを用いているときはelastic IPを用いて固定化することができません。一般的にはELBやRoute53を用いてこの動的IPに対処するのですが、デモサイトを公開する場合だとコストの問題で使用することができないかもしれません。ここではLambda関数を作成していきます。
Lambda関数のロールでAmazonECS_FullAccessポリシーを追加して、Lambda関数を次のように書きます。clusterとserviceNameはIPを取得したいものを設定します。
import { ECSClient, ListTasksCommand,DescribeTasksCommand} from "@aws-sdk/client-ecs";
import { EC2Client, DescribeNetworkInterfacesCommand } from "@aws-sdk/client-ec2"; // ES Modules import
export const handler = async (event) => {
const client = new ECSClient({region:"ap-northeast-1"});
const ec2Client = new EC2Client({region:"ap-northeast-1"});
const cluster = "demo-nginx-cluster"
const serviceName = "demo-nginx-service"
const input = {
cluster: cluster,
serviceName: serviceName,
};
const command = new ListTasksCommand(input);
const response = await client.send(command);
const task = response.taskArns[0]
const input1 = {
cluster: cluster,
tasks: [
task
]
};
const command1 = new DescribeTasksCommand(input1);
const response1 = await client.send(command1);
const ni = response1.tasks[0].attachments[0].details[1].value
const input2 = {
"NetworkInterfaceIds": [
ni
]
};
const command2 = new DescribeNetworkInterfacesCommand(input2);
const response2 = await ec2Client.send(command2);
const ip = response2.NetworkInterfaces[0].Association.PublicIp
const appPath = event.queryStringParameters?.appPath
return {
"statusCode":303,
"headers": {
"Location": "http://" + ip
}
}
};
あとはAPI gatewayとLambda関数を関連付けるだけです。
トリガーを追加からAPI gatewayを選択して、次のように設定して追加をします。
作成したAPI gatewayにアクセスすると、リダイレクトされてECSで起動しているサービスにアクセスできます。
さいごに
ELBやRoute53を用いず、ECSに固定したアクセスポイントを設定する方法を紹介しました。この手法はHTTPしか使えないので、デモサイトを公開する以外ではあまり推奨されないと思いますが、Dockerを使ってささっとデモサイトを公開するのには役立つと思います。
参考