先日作ったのでその覚書的なものです。 簡単に言うとこんな感じのものを作りました。

GKE上のDockerアプリ -> Stackdriver logging -> Pub/Sub -> Lambda -> Slack

作成の動機

概ね以下の背景です。

  • GCPをメインに、AWSも多少織り交ぜつつな感じのアプリケーションを作っている。
  • Stackdriverでアプリの監視を行っているがエラーログを検知したらSlackに通知する仕組みが欲しかった。
  • Stackdriver単体だとログ内容をSlackに通知できなさそうだった。
  • StackdriverはフィルタリングしたログをPub/Subへ流すことができるらしい。
  • Pub/Subはキューにデータが流れてきたときに指定したエンドポイントにPush通知できるらしい。

と、こんなかんじの前提があり Pub/Sub から lambda に流して、そこからさらにSlackに流せば実現できると目論んだ次第です。

LambdaにもPub/Subにも今回始めて触ったということもあってか結構迷走してしまいました。。

以降から本題となります。

Terraformに読み込ませるHCL

これがほとんど全てな感じですが、実際に使ったHCLをコメントを交えつつ添付しておきます。 varになっているところは適宜置換が必要かと思われますのでご注意下さい。

基本的にはTerraformサイト上のサンプルを組み合わせているだけです。 Terraformマジ便利ですね、最高です。 もっといろいろな知見がネット上に溜まってくれるとうれしいです。

data "aws_caller_identity" "self" {}

#### ここから API Gateway 周り####
resource "aws_api_gateway_rest_api" "alert_api" {
  name = "alerter"
}

resource "aws_api_gateway_method" "root_post_method" {
  rest_api_id   = "${aws_api_gateway_rest_api.alert_api.id}"
  resource_id   = "${aws_api_gateway_rest_api.alert_api.root_resource_id}"
  http_method   = "POST"
  authorization = "NONE"
}

resource "aws_api_gateway_integration" "alert_integration" {
  rest_api_id             = "${aws_api_gateway_rest_api.alert_api.id}"
  resource_id             = "${aws_api_gateway_rest_api.alert_api.root_resource_id}"
  http_method             = "${aws_api_gateway_method.root_post_method.http_method}"
  integration_http_method = "POST"
  type                    = "AWS"
  uri                     = "arn:aws:apigateway:${var.aws_region}:lambda:path/2015-03-31/functions/${aws_lambda_function.alerter.arn}/invocations"
}

resource "aws_api_gateway_method_response" "alert_200_response" {
  rest_api_id = "${aws_api_gateway_rest_api.alert_api.id}"
  resource_id = "${aws_api_gateway_rest_api.alert_api.root_resource_id}"
  http_method = "${aws_api_gateway_method.root_post_method.http_method}"
  status_code = "200"
}

resource "aws_api_gateway_integration_response" "alert_response" {
  rest_api_id = "${aws_api_gateway_rest_api.alert_api.id}"
  resource_id = "${aws_api_gateway_rest_api.alert_api.root_resource_id}"
  http_method = "${aws_api_gateway_method.root_post_method.http_method}"
  status_code = "${aws_api_gateway_method_response.alert_200_response.status_code}"
}

## stage名に使う名前をランダム文字列として生成する
resource "random_id" "secret_path" {
  keepers = {
    ami_id = "${aws_lambda_function.alerter.id}"
  }

  byte_length = 40
}

resource "aws_api_gateway_deployment" "alert_deployment" {
  depends_on = ["aws_api_gateway_method.root_post_method"]

  rest_api_id = "${aws_api_gateway_rest_api.alert_api.id}"
  stage_name  = "${random_id.secret_path.hex}"
}

resource "aws_api_gateway_domain_name" "alert" {
  domain_name = "${var.alerter_domain}"

  certificate_name        = "alert-ssl"
  certificate_body        = "${file("cert_path")}"
  certificate_chain       = "${file("chain_path")}"
  certificate_private_key = "${file("key_path")}"
}

resource "aws_api_gateway_base_path_mapping" "alert" {
  api_id      = "${aws_api_gateway_rest_api.alert_api.id}"
  domain_name = "${aws_api_gateway_domain_name.alert.domain_name}"
}

#### ここから IAM 周り####
resource "aws_iam_role" "lambda_alert_role" {
  name               = "lambda_alert_role"
  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      }
    }
  ]
}
POLICY
}

#### ここから Lambda 周り####
resource "aws_lambda_permission" "apigw_alerter" {
  statement_id  = "AllowExecutionFromAPIGateway"
  action        = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.alerter.arn}"
  principal     = "apigateway.amazonaws.com"

  source_arn    = "arn:aws:execute-api:${var.aws_region}:${data.aws_caller_identity.self.account_id}:${aws_api_gateway_rest_api.alert_api.id}/*/${aws_api_gateway_method.root_post_method.http_method}/"
}

resource "aws_lambda_function" "alerter" {
  filename         = "${var.path_to_lambda_source}"
  function_name    = "alerter"
  role             = "${aws_iam_role.lambda_alert_role.arn}"
  handler          = "index.handler"
  runtime          = "nodejs4.3"
  source_code_hash = "${base64sha256(file(${var.path_to_lambda_source}))}"
  environment {
    variables = {
      webhook_url = "${var.slack_webhook_url}"
      channel = "${var.slack_channel}"
    }
  }
}

#### ここから Route53 周り ####
resource "aws_route53_record" "alerter" {