皆さんはおじさんは好きカナ?( ̄ー ̄?
僕は好きだヨ!!✋( ̄▽ ̄ ♥ 💕
定期的におじさんからメッセージが届いたら素敵じゃないカナ?😚😘😄
と言うわけで作ってみタヨ!!(^з<)❗😃✋💕
概要
ECS Scheduled Taskを使っておじさんから定期的に Slack メッセージが届く仕組みを作ります。
おじさんのメッセージ生成には Ojichat を使わせていただきました。
環境構築
AWS CLI用のIAMユーザーの作成
AWS CLI用のIAMユーザーを作成します。以下の記事を参考にしながらIAMユーザーを作成します。
今回は Terraform の例もあるため、 Admin権限を持つユーザーを作成しました。
イメージをプッシュする権限のみ必要な場合は ecr:GetAuthorizationToken
を付与してください。
SecretAccessKey
は再発行されないため、分からなくなってしまったらユーザーを再作成する必要があります。
AWS CLI のインストール
AWS CLI をインストールし、設定を行います。インストール方法は下記記事を参考にしてください
macOS に AWS CLI をインストールする
AWS CLI の設定
インストールが完了したら AWS CLI の設定を行い、AWS CLI から S3 のバケット一覧を取得できることを確認します。(動作確認)
$ aws --version
$ aws configure
$ aws s3api list-buckets # S3 のバケット一覧を表示するコマンド
Slack に Ojichat の絵文字 :ojichat:
を作成する
:ojichat:
を追加するとテンション上がります。😚💕
Slack のWebHookURL を作成する
おじさんからの Slackメッセージを受け取るのに必要です。
WebHook の設定を行うことで、curlコマンドで簡単にメッセージを Slack に送信することができます。
イメージを作成
おじさんメッセージの生成 & Slack にメッセージを飛ばす Docker イメージを作ります。
同ディレクトリ に Dockerfile
と entrypoint.sh
を作成し、以下のコマンドでビルドします。
entrypoint.sh
中の {your_channnel_name} にはおじさんからのメッセージを受け取りたいチャンネル名を入力してください。
「おのじゅん」は私のあだ名です。
FROM golang:1.13.4-stretch
RUN go get -u github.com/greymd/ojichat
COPY entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]
#!/bin/bash
COMMENT=$(ojichat $NAME)
echo $COMMENT
curl -X POST --data-urlencode "payload={\"channel\": \"##{your_channnel_name}\", \"username\": \"Ojichat\", \"text\": \"$COMMENT\", \"icon_emoji\": \":ojichat:\"}" $WEB_HOOK_URL
$ docker build -t ojichat:latest --no-cache .
ビルドができたら以下のコマンドでおじさんからSlackにメッセージが届くことを確認します。
NAME
にはおじさんに呼んで欲しい名前を、WEB_HOOK_URL
には Slack のWebHookのURLを入力します。
$ docker run --rm -e NAME=おのじゅん -e WEB_HOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzzz ojichat:latest
無事おじさんからメッセージを受け取ることができました。
おじさんを ECR にプッシュする
おじさんイメージがきちんと動くことがわかったので、次はおじさんイメージを ECR にプッシュします。
GUI からおじさんを格納するリポジトリを作成します。
プッシュコマンドを表示
ボタンを押すとイメージのビルドからプッシュまでの手順が表示されます。
その手順に従ってイメージを ECR にプッシュします。
$(aws ecr get-login --no-include-email --region ap-northeast-1)
docker build -t ojichat .
docker tag onojun-ojichat:latest {YOUR_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/onojun-ojichat:latest
docker push {YOUR_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/onojun-ojichat:latest
おじさんを動かすロールを作成する
おじさんイメージは Fargate 上で動かします。
必要な権限を持つロールを作成します。
ojichat-policy
を作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
次に ojichat-role
を作成し、ojichat-policy
をアタッチさせ、信頼関係を編集します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ecs.amazonaws.com",
"ecs-tasks.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
おじさんタスク定義を作成する
おじさんのタスク定義を作成します。
必要な環境変数を設定します。
今回はハードコードしてしまいましたが、WEB_HOOK_URL
などのクレデンシャル情報は ParameterStore
に格納し、それを FromValue
で参照するとタスク定義に書かずに済みます。
おじさんクラスターを作成し、おじさんタスクを実行する
ojichat-cluster
を作成し、作成したタスク定義 ojichat-task
からタスクを実行します。
おじさんを ScheduledTask で動かす
Fargate にいるおじさんからメッセージが届いたら、次はおじさんのメッセージを自動化します。
スケジュールタスクを設定することで実現することができます。
僕はおじさんが好きなので1分ごとにメッセージが飛ぶようにしました。
Terraform
########################
## Credential Infos
########################
provider "aws" {
access_key = local.access_key
secret_key = local.secret_key
region = "ap-northeast-1"
}
########################
## ECR
########################
# ECS Repository
resource "aws_ecr_repository" "repository" {
name = "ojichat"
}
# Repositry Policy
# Permit pull image
resource "aws_ecr_repository_policy" "repository_policy" {
repository = aws_ecr_repository.repository.name
policy = data.aws_iam_policy_document.repository_policy.json
}
data "aws_iam_policy_document" "repository_policy" {
statement {
effect = "Allow"
actions = [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage"
]
principals {
type = "*"
identifiers = ["*"]
}
}
}
########################
## IAM
########################
# IAM Policy
resource "aws_iam_policy" "policy" {
name = "ojicaht-policy"
description = "for ojichat"
policy = data.aws_iam_policy_document.policy.json
}
data "aws_iam_policy_document" "policy" {
statement {
effect = "Allow"
actions = [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecs:RunTask",
"logs:CreateLogStream",
"logs:PutLogEvents",
"iam:PassRole"
]
resources = ["*"]
}
}
# IAM Role
resource "aws_iam_role" "role" {
name = "ojichat-role"
description = "role for ojichat"
assume_role_policy = data.aws_iam_policy_document.role.json
}
data "aws_iam_policy_document" "role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs.amazonaws.com", "ecs-tasks.amazonaws.com", "events.amazonaws.com"]
}
}
}
resource "aws_iam_role_policy_attachment" "schedule_policy_attachment" {
role = aws_iam_role.role.name
policy_arn = aws_iam_policy.policy.arn
}
resource "aws_iam_role_policy_attachment" "event_role_policy_attachment" {
role = aws_iam_role.role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceEventsRole"
}
########################
## ECS
########################
# cluster
resource "aws_ecs_cluster" "cluster" {
name = "ojichat-cluster"
}
# task difinition
resource "aws_ecs_task_definition" "task_definition" {
family = "ojichat-task"
task_role_arn = aws_iam_role.role.arn
container_definitions = data.template_file.container_definitions.rendered
network_mode = "awsvpc"
cpu = 256
memory = 512
requires_compatibilities = ["FARGATE"]
execution_role_arn = aws_iam_role.role.arn
}
data "template_file" "container_definitions" {
template = <<EOF
[
{
"cpu": 0,
"environment": [
{
"name": "WEB_HOOK_URL",
"value": "${local.web_hook_url}"
},
{
"name": "NAME",
"value": "おのじゅん"
}
],
"name": "ojichat",
"image": "${aws_ecr_repository.repository.repository_url}",
"logConfiguration": {
"logDriver": "awslogs",
"secretOptions": null,
"options": {
"awslogs-group": "/ecs/ojichat-task",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
},
"essential": true
}
]
EOF
}
# scueduled task
resource "aws_cloudwatch_event_rule" "schedule_rule" {
name = "ojichat-event"
schedule_expression = "cron(* * * * ? *)"
is_enabled = true
}
resource "aws_cloudwatch_event_target" "fargate_scheduled_task" {
rule = aws_cloudwatch_event_rule.schedule_rule.name
arn = aws_ecs_cluster.cluster.arn
role_arn = aws_iam_role.role.arn
ecs_target {
task_definition_arn = aws_ecs_task_definition.task_definition.arn
task_count = 1
launch_type = "FARGATE"
network_configuration {
subnets = [local.subnets_id]
assign_public_ip = true
}
}
}
参考
greymd/ojichat
Amazon Elastic Container Service とは
Amazon ECS クラスター
Amazon ECS タスク定義
Amazon ECS サービス