29
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DockerAdvent Calendar 2019

Day 10

Docker と ECS Scheduled Taskを使って定期的におじさんからメッセージが届く仕組みを作る😚💕

Last updated at Posted at 2019-12-09

皆さんはおじさんは好きカナ?( ̄ー ̄?
僕は好きだヨ!!✋( ̄▽ ̄ ♥ 💕
定期的におじさんからメッセージが届いたら素敵じゃないカナ?😚😘😄
と言うわけで作ってみタヨ!!(^з<)❗😃✋💕

概要

ECS Scheduled Taskを使っておじさんから定期的に Slack メッセージが届く仕組みを作ります。
おじさんのメッセージ生成には Ojichat を使わせていただきました。

greymd/ojichat

環境構築

AWS CLI用のIAMユーザーの作成

AWS CLI用のIAMユーザーを作成します。以下の記事を参考にしながらIAMユーザーを作成します。
今回は Terraform の例もあるため、 Admin権限を持つユーザーを作成しました。
イメージをプッシュする権限のみ必要な場合は ecr:GetAuthorizationToken を付与してください。
SecretAccessKey は再発行されないため、分からなくなってしまったらユーザーを再作成する必要があります。

IAM ユーザーの作成 (コンソール)
イメージのプッシュ

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のWebhook URL取得手順

イメージを作成

おじさんメッセージの生成 & Slack にメッセージを飛ばす Docker イメージを作ります。
同ディレクトリ に Dockerfileentrypoint.sh を作成し、以下のコマンドでビルドします。
entrypoint.sh 中の {your_channnel_name} にはおじさんからのメッセージを受け取りたいチャンネル名を入力してください。
「おのじゅん」は私のあだ名です。

Dockerfile
FROM golang:1.13.4-stretch
RUN go get -u github.com/greymd/ojichat
COPY entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]
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
スクリーンショット 2019-11-26 15.10.26.png

無事おじさんからメッセージを受け取ることができました。

おじさんを ECR にプッシュする

おじさんイメージがきちんと動くことがわかったので、次はおじさんイメージを ECR にプッシュします。
GUI からおじさんを格納するリポジトリを作成します。

スクリーンショット 2019-11-26 15.13.05.png

プッシュコマンドを表示 ボタンを押すとイメージのビルドからプッシュまでの手順が表示されます。
その手順に従ってイメージを 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 を作成します。

スクリーンショット 2019-11-26 15.39.39.png
{
    "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をアタッチさせ、信頼関係を編集します。

スクリーンショット 2019-11-26 15.42.34.png
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "ecs.amazonaws.com",
          "ecs-tasks.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

おじさんタスク定義を作成する

おじさんのタスク定義を作成します。

スクリーンショット 2019-11-26 16.05.26.png スクリーンショット 2019-11-26 16.05.51.png スクリーンショット 2019-11-26 16.05.58.png

必要な環境変数を設定します。
今回はハードコードしてしまいましたが、WEB_HOOK_URL などのクレデンシャル情報は ParameterStore に格納し、それを FromValue で参照するとタスク定義に書かずに済みます。

おじさんクラスターを作成し、おじさんタスクを実行する

ojichat-cluster を作成し、作成したタスク定義 ojichat-task からタスクを実行します。

スクリーンショット 2019-11-26 16.09.23.png

おじさんを ScheduledTask で動かす

Fargate にいるおじさんからメッセージが届いたら、次はおじさんのメッセージを自動化します。
スケジュールタスクを設定することで実現することができます。

スクリーンショット 2019-11-26 16.19.39.png

僕はおじさんが好きなので1分ごとにメッセージが飛ぶようにしました。

スクリーンショット 2019-11-26 16.40.15.png

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 サービス

29
17
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?