Open59

AWS_terraformでとりあえずうんちアプリのインフラつくる

Kumamoto-HamachiKumamoto-Hamachi

前提

こんな感じの構成のうんちアプリ(Djangoで作ったTODOアプリ)を用意

構成ツリー
./
├── config/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── todo/
│   ├── migrations/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── templates/
│   │   └── todo/
│   │       ├── todo_confirm_delete.html
│   │       ├── todo_form.html
│   │       └── todo_list.html
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── manage.py
├── Dockerfile
├── docker-compose.yml
├── pyproject.toml
├── poetry.lock
├── staticfiles
├── nginx.conf
├── entrypoint.sh
├── staticfiles
└── .git

Dockerfileとかentrypoint.shはこんなかんじ。
DockerfileにCOPYを使うのはイケてない的な話を聞くので直したい。
参考:2024年版のDockerfileの考え方&書き方 | フューチャー技術ブログ

というかentrypoint.shもこれでええのか?

Dockerfile
FROM python:3.10-slim

RUN apt-get update && apt-get install -y nginx

RUN pip install poetry

WORKDIR /app

COPY pyproject.toml poetry.lock* ./
RUN poetry install --no-root

# アプリケーションのソースコードをコピ
COPY . .
# Nginxの設定ファイルをコピー
COPY nginx.conf /etc/nginx/nginx.conf

COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh

EXPOSE 8000

ENTRYPOINT ["sh", "/app/entrypoint.sh"]

entrypoint.sh
#!/bin/sh

if [ "$DATABASE" = "postgres" ]
then
    echo "Waiting for postgres..."

    while ! nc -z $SQL_HOST $SQL_PORT; do
      sleep 0.1
    done

    echo "PostgreSQL started"
fi

poetry run python manage.py migrate
poetry run python manage.py collectstatic --no-input

# Nginx起動
nginx -t && service nginx start || (nginx -t && cat /var/log/nginx/error.log && exit 1)
# Gunicorn起動
exec poetry run gunicorn --workers 3 --bind 0.0.0.0:8000 config.wsgi:application

今回はキニシナイが、いずれプライベートのPJでもAWSリソース名の命名規則もしっかりさせたい。

参考:弊社で使っているAWSリソースの命名規則を紹介します | DevelopersIO

{システム/PJ名}-{環境}-{レイヤー}-{タイプ(何サーバー)}-{目的(ログ・静的コンテンツ配信)}-AWSサービス名

そのほかメモ

RDS、SSHレスオペレーション。
ログやSSMパラメーターストア周りも怪しい?

便利リンク

https://zenn.dev/kumamoto/scraps/f3b15aeefecb18
https://www.kagoya.jp/howto/cloud/container/dockercommand/

Kumamoto-HamachiKumamoto-Hamachi

ECS基礎的な概念めも

Amazon Elastic Container Serviceさん。
フルマネージドなコンテナオーケストレーションサービス。EKSとなにがちがうの...?

データプレーンとコントロールプレーン

コンテナオーケストレーションは「データプレーン」と「コントロールプレーン」という2つの概念から成り立っている(らしい)

  • コントロールプレーン
    コンテナを管理するためのサービス。

  • データプレーン(実行環境)
    コンテナを稼働させるためのサービス。

要は下記。

ネットワークの世界とは別の意味で使われているのか...?

planeは「平面,水平面」という訳だがITの世界においてプレーンとは「役割ごとに分かれたシステムの層」くらいのニュアンスの言葉。

https://e-words.jp/w/コントロールプレーン.html

Fargateだとssh接続できなくなると聞いたことがあるが...

「ECS Exec」を使用することでコンテナにSSH接続することができる(らしい)


参考1:コントロールプレーンとは?| コントロールプレーンとデータプレーンの対比 | Cloudflare

参考2:AWSでコンテナを支えるサービスについて ECS、EKS、Fargateの違いは? 第1回 | SunnyCloud

参考3:ECSとは? コンテナの基礎からECSの概要まで解説 | ベアサポート

Kumamoto-HamachiKumamoto-Hamachi

ECS 初歩

とりあえずECRにリポジトリを作ってDockerのイメージ焼いて、それをpush

まずはリポジトリ作成。

provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_ecr_repository" "django_web" {
  name = "django-web-app"
}

output "repository_url" {
  value = aws_ecr_repository.django_web.repository_url
}

terraformでやらないならaws ecr create-repository --repository-name HOGEHOGE --region ap-northeast-1とか?(TODO:実際にはやってない。)

次にDockerイメージのビルド。

$ docker build -t django-todo-app .

Dockerクライアントの認証をする。

aws ecr get-login-password --region ap-northeast-1
| docker login --username AWS --password-stdin your-account-id.dkr.ecr.your-region.amazonaws.com

--password-stdin: 標準入力からパスワードを読み取ることを指定
your-account-id.dkr.ecr.your-region.amazonaws.com: ログイン対象のDockerレジストリのURLを指定します。ここにはECRリポジトリのエンドポイントを指定(さっきリポジトリをterraformでつくった時にoutputで出てきていたやつからリポジトリ名省けばいいはず)

ECRにプッシュするためには、イメージにリポジトリURLを含むタグを付ける必要がある。

というわけで、さっきビルドしたイメージにECRのリポジトリのタグをつける。(最初にビルドする時にやってもよかったな)

$ docker tag django_todo-web:latest your-account-id.dkr.ecr.your-region.amazonaws.com/your-django-todo-app:latest

※docker image ls コマンドでは、イメージの各タグを個別のエントリとして表示するので注意。

最初(ビルド)からやるなら下記。

$ docker build -t your-account-id.dkr.ecr.your-region.amazonaws.com/your-django-todo-app:latest

で、ようやくイメージpush

$ docker push your-account-id.dkr.ecr.your-region.amazonaws.com/your-django-todo-app:latest

TODO

ECRでは常にAWSをユーザー名として使用(なぜ?)

get-loginとget-login-password。
AWS CLIでECRにログインする時はget-loginではなくget-login-passwordを使おう #Docker - Qiita

クレデンシャルヘルパー

WARNING! Your password will be stored unencrypted in /home/kumamoto/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credential-stores

タグにつけるlatest...?
=>gitのコミットハッシュがよさそう。

$ GIT_COMMIT=$(git rev-parse --short HEAD)
$ docker tag your-django-todo-app:latest your-account-id.dkr.ecr.your-region.amazonaws.com/your-django-todo-app:$GIT_COMMIT

git rev-parseを使いこなす #Git - Qiita

massage:適切な形式に変換する的なニュアンス

Kumamoto-HamachiKumamoto-Hamachi

ECR ライフサイクルポリシー

リポジトリが保持できるイメージ数には限界があるので最新20件のみ保持にしてみた。

resource "aws_ecr_lifecycle_policy" "django_web" {
  repository = aws_ecr_repository.django_web.name

  policy = <<EOF
  {
    "rules": [
      {
        "rulePriority": 1,
        "description": "Keep last 20 release tagged images",
        "selection": {
          "tagStatus": "tagged",
          "tagPrefixList": ["release"],
          "countType": "imageCountMoreThan",
          "countNumber": 20
        },
        "action": {
          "type": "expire"
        }
      }
    ]
  }
  EOF
}

Kumamoto-HamachiKumamoto-Hamachi

CodeBuild

ビルドサービス。ソースコードのコンパイル、ユニットテスト実行、アーティファクト(バイナリやパッケージのこと)作成が役割。

What is AWS CodeBuild? - AWS CodeBuild

CodePipeline

継続的デリバリーサービス。ソフトウェアのリリースに必要なステップをモデル化、視覚化、自動化するために使用。

What is AWS CodePipeline? - AWS CodePipeline

Use AWS CodePipeline with AWS CodeBuild to test code and run builds - AWS CodeBuild

Out of the box:カスタマイズいらずに箱から出したら(インストールしたら)すぐ使えまっせ。

Kumamoto-HamachiKumamoto-Hamachi

TODO:GitHub Actionsでイメージ焼きやECRデプロイを自動化

code なんちゃらシリーズでやるかも。

Kumamoto-HamachiKumamoto-Hamachi

TODO:コンテナイメージのレイヤーとは?

コンテナイメージは、複数のレイヤー(層)から構成されています。各レイヤーは、ファイルシステムのスナップショットのようなものであり、基本的に以下のように機能します:

ベースレイヤー:

基本となるファイルシステムのスナップショットで、通常はOSの最小限のセットアップが含まれます。
中間レイヤー:

ベースレイヤーの上に追加されるレイヤーで、アプリケーションの依存関係や設定ファイルなどが含まれます。
最上位レイヤー:

アプリケーション自体や最終的な設定が含まれるレイヤーです。

by chatgpt

Kumamoto-HamachiKumamoto-Hamachi

TODO

Effectは書かなくておK?

// 信頼ポリシードキュメント
data "aws_iam_policy_document" "assume_role" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type = "Service"
      // 指定サービスにのみIAMロールをアタッチ出来る
      identifiers = [var.identifier]
    }
  }
}

Kumamoto-HamachiKumamoto-Hamachi

S3

パブリックバケット

aclは【アップデート】S3でACLを無効化できるようになりました #reinvent | DevelopersIOもチェックしたい。

古い書き方(terraform 0.12とか?)
// パブリックバケット
resource "aws_s3_bucket" "public" {
  bucket = "django-todo-public-for-kumamoto-pj"
  acl    = "public-read"

  cors_rule {
    allowed_methods = ["https://example.com"]
    allowed_origins = ["GET"]
    allowed_headers = ["*"]
    max_age_seconds = 3000
  }
}

今の書き方
resource "aws_s3_bucket" "public" {
  bucket = "django-todo-public-for-kumamoto-pj"
}

resource "aws_s3_bucket_public_access_block" "public" {
  bucket = aws_s3_bucket.public.id
  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

resource "aws_s3_bucket_ownership_controls" "public" {
  bucket = aws_s3_bucket.public.id

  rule {
    // バケット所有者がアップロードされたオブジェクトの所有権を取得 =>  これにより、バケット所有者がACLを管理できるように 
    object_ownership = "BucketOwnerPreferred"
  }
}

resource "aws_s3_bucket_acl" "public" {
  depends_on = [
    aws_s3_bucket_public_access_block.public,
    aws_s3_bucket_ownership_controls.public,
  ]

  bucket = aws_s3_bucket.public.id
  acl    = "public-read"
}

resource "aws_s3_bucket_cors_configuration" "public" {
  bucket = aws_s3_bucket.public.id

  cors_rule {
    allowed_headers = ["*"]
    allowed_methods = ["GET"]
    allowed_origins = ["https://example.com"]
    max_age_seconds = 3000
  }
}

Kumamoto-HamachiKumamoto-Hamachi

ACM(Aws Certificated Manager)

https://aws.amazon.com/jp/certificate-manager/

SSL証明書の発行・更新・AWS内のサービスと連携出来る。

こんな感じ。リソース名のセンスはない

resource "aws_acm_certificate" "django_tod_ssl_certificate" {
  domain_name               = aws_route53_zone.django_todo_route53_zone.name
  subject_alternative_names = []
  validation_method         = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

domain_nameを「*.example.com」のように指定すると、ワイルドカード証明書を発⾏出来る。
validation_methodはEメール検証 or DNS検証。後者じゃないと自動更新出来ない。
create_before_destroyをtrueにしているので、「新しい SSL 証明書を作ってから古い証明書と差し替え」となる。(普通はリソース削除してから、作成。逆にするのはどのリソースもこれでいける)

ワイルドカード証明書

複数のサブドメインを1つのSSL/TLS証明書でカバーできる証明書
2階層以上のサブドメインには適用不可。

JPRSの提供するワイルドカード証明書なら、「.example.jp」の証明書を「example.jp」のようにアスタリスク()を除いたホスト名のサーバーでも利用できます。

逆に言うとルートドメインは普通なら適用されないってことか。
https://jprs.jp/pubcert/about/wildcard/

SSL 証明書の検証 TODO

DNS 検証を選択すると、 ACMはこのデータベースに追加する必要がある 1 つ以上のCNAMEレコードを提供します

https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/dns-validation.html

現在では主にCDN (Contents Delivery Network)や、/24未満のIPv4アドレスの逆引きを設定する際に使われています。

https://jprs.jp/glossary/index.php?ID=0212

cnameはエイリアスと正式名の突き合わせのためのレコードだが、それでdns検証するというのはどういうこと?

そもそも...「DNS検証」は、ユーザーが特定のドメイン(例: example.com)の所有者であることを確認する方法の一つ。証明書を発行する機関(ACM)がドメイン持っているよね?という確認するためにDNSを使う。

ACM証明書を発行する際、証明書のドメイン所有権を確認するために、DNS検証用のCNAMEレコードがACMより提供される。

で、TerraformでCNAMEレコードをRoute 53に追加。
AWSは、このCNAMEレコードを使ってドメインの所有権を検証し、SSL/TLS証明書を有効化。

resource "aws_route53_record" "django_todo_dns_certificate" {
  name    = aws_acm_certificate.django_todo_ssl_certificate.domain_validation_options[0].resource_record_name
  type    = aws_acm_certificate.django_todo_ssl_certificate.domain_validation_options[0].resource_record_type
  records = [aws_acm_certificate.django_todo_ssl_certificate.domain_validation_options[0].resource_record_value]
  zone_id = aws_route53_zone.django_todo_route53_zone.zone_id
  ttl     = 60
}

https://booth.pm/ja/items/1318735

=>いまはこの書き方だめ

https://qiita.com/m-oka-system/items/f1d773f2a43a0c41c582

複数のドメインがある場合はどうする? TODO

chatgpt先輩に聞いてみたら下記コード出てきた。未検証

resource "aws_route53_record" "example_cert_validation" {
  for_each = {
    for dvo in aws_acm_certificate.example_cert.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  zone_id = aws_route53_zone.example_zone.zone_id
  name    = each.value.name
  type    = each.value.type
  ttl     = 300

  records = [each.value.record]
}

検証の待機

resource "aws_acm_certificate_validation" "django_todo_ssl_certificate_validation" {
  certificate_arn         = aws_acm_certificate.django_todo_ssl_certificate.arn
  validation_record_fqdns = [aws_route53_record.django_todo_dns_certificate.fqdn]
}

validation_record_fqdns には作成されたCNAMEレコードのFQDN(完全修飾ドメイン名)を指定。

https://www.nic.ad.jp/ja/basics/terms/fqdn.html

Kumamoto-HamachiKumamoto-Hamachi

エラー対処

// DNS検証
// aws_acm_certificate.django_todo_ssl_certificate.domain_validation_optionsの型がセット型に変わったのよ(list of => set of)
resource "aws_route53_record" "django_todo_dns_certificate" {
  for_each = { for dvo in aws_acm_certificate.django_todo_ssl_certificate.domain_validation_options : dvo.domain_name => {
    name   = dvo.resource_record_name
    record = dvo.resource_record_value
    type   = dvo.resource_record_type
    }
  }

  zone_id = aws_route53_zone.django_todo_route53_zone.zone_id
  name    = each.value.name
  type    = each.value.type
  ttl     = 60

  records = [each.value.record]
}

// 検証の待機
resource "aws_acm_certificate_validation" "django_todo_ssl_certificate_validation" {
  certificate_arn = aws_acm_certificate.django_todo_ssl_certificate.arn
  validation_record_fqdns = [aws_route53_record.django_todo_dns_certificate.fqdn]
}

╷
│ Error: Missing resource instance key
│ 
│   on modules/proxy_server/main.tf line 107, in resource "aws_acm_certificate_validation" "django_todo_ssl_certificate_validation":
│  107:   validation_record_fqdns = [aws_route53_record.django_todo_dns_certificate.fqdn]
│ 
│ Because aws_route53_record.django_todo_dns_certificate has "for_each" set, its attributes must be accessed on specific instances.
│ 
│ For example, to correlate with indices of a referring resource, use:
│     aws_route53_record.django_todo_dns_certificate[each.key]

なおした

// 検証の待機
resource "aws_acm_certificate_validation" "django_todo_ssl_certificate_validation" {
  certificate_arn         = aws_acm_certificate.django_todo_ssl_certificate.arn
  // record.value.fqdn?
  validation_record_fqdns = [for record in aws_route53_record.django_todo_dns_certificate : record.fqdn]
}

Kumamoto-HamachiKumamoto-Hamachi

AWS SSMパラーメーターストア

平文:設定・取得・更新・削除

aws ssm put-parameter --name 'plain_name' --value 'plain value' --type String

aws ssm get-parameter --output text --name 'plain_name' --query Parameter.Value

aws ssm put-parameter --name 'plain_name' --value 'modified value' --type String --overwrite

aws ssm delete-parameter --name 'plain_name'

text以外のoutput形式としては、json、yaml、tableなどがある。

--queryではJSON出力の中から特定のデータを抽出するためのフィルターを指定する。ここでは、Parameter オブジェクト内の Value フィールドを取得。これにより、パラメーターの値のみが出力される。


$ aws ssm get-parameter --output table --name 'plain_name' --query Parameter
--------------------------------------------------------------------------------------
|                                    GetParameter                                    |
+------------------+-----------------------------------------------------------------+
|  ARN             |  arn:aws:ssm:ap-northeast-1:{AWSアカウントID}:parameter/plain_name   |
|  DataType        |  text                                                           |
|  LastModifiedDate|  2024-08-03T23:36:46.237000+09:00                               |
|  Name            |  plain_name                                                     |
|  Type            |  String                                                         |
|  Value           |  plain value                                                    |
|  Version         |  1                                                              |
+------------------+-----------------------------------------------------------------+

暗号化:設定・取得

aws ssm put-parameter --name 'encrypotion_name' --value 'encrypotion value' --type SecureString

aws ssm get-parameter --output text --name 'encrypotion_name' --query Parameter.Value --with-decryption

その他メモ(未検証)

https://qiita.com/ma2kazu/items/695448bd8426e2259766
https://dev.classmethod.jp/articles/aws-cli-all-ssm-parameter-get/

Kumamoto-HamachiKumamoto-Hamachi

ECS

サービス

プラットフォームのバージョン。
24/8/4現在、1.4.0が最新っぽい。(1.3も使える。それより下は廃止済)
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/platform-versions-retired.html
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/platform-versions-changelog.html

タスクの起動に時間がかかる場合、⼗分な猶予期間を設定しておかないと、ヘルスチェックに引っかかり、タスクの起動と終了が無限に続いてしまいます

health_check_grace_period_secondsは十分な時間を設定しておこう。

subnetsはサービスが実行されるサブネット。

resource "aws_ecs_service" "django_todo_ecs_service" {
  name                              = "django-todo-ecs-service"
  cluster                           = aws_ecs_cluster.django_todo_ecs_cluster.arn
  task_definition                   = aws_ecs_task_definition.django_todo_ecs_task.arn
  desired_count                     = 2
  launch_type                       = "FARGATE"
  platform_version                  = "1.4.0"
  health_check_grace_period_seconds = 60

  network_configuration {
    assign_public_ip = false
    security_groups  = [var.nginx_sg_id]
    subnets = [
      var.django_todo_private_1_subnet_id,
      var.django_todo_private_2_subnet_id,
    ]
  }

  load_balancer {
    target_group_arn = var.alb_target_group_arn
    container_name   = "django-todo-container"
    container_port   = 80
  }

  lifecycle {
    // Fargateの場合、デプロイの度にタスク定義が更新されてしまうため、変更を無視するように設定
    ignore_changes = [task_definition]
  }
}

Kumamoto-HamachiKumamoto-Hamachi

リスナー・リスナールール(ALB)

セキュリティーポリシーは24/8/4現在、ELBSecurityPolicy-TLS13-1-2-2021-06が推奨されている。
https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/create-https-listener.html#describe-ssl-policies

resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.django_todo_alb.arn
    port              = "443"
    protocol          = "HTTPS"
    certificate_arn = aws_acm_certificate.django_todo_ssl_certificate.arn
    ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
    default_action {
    type             = "fixed-response"

    fixed_response {
      content_type = "text/plain"
      message_body = "This is a HTTPS"
      status_code  = "200"
    }
  }
}

Kumamoto-HamachiKumamoto-Hamachi

メモ:ALB、NATゲートウェイ、EIP

ALBは複数のAZ(のパブリックサブネット)にまたがって配置されるが、ユーザーからは単一のDNS名とIPアドレスで見える。=>論理的ALBは1つ。

NATゲートウェイは、プライベートサブネットに配置されたリソース(例えば、EC2インスタンスやECSタスク)がインターネットにアクセスする際に使用される。インターネットへのアウトバウンド通信はいるからね。
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-nat-gateway.html

EIPは固定されたパブリックIPアドレス。
普通のパブリックIPアドレスとは異なり、EIPは一度割り当てると、インスタンスが停止・再起動しても変更されない。

NATゲートウェイを経由してプライベートサブネット内のリソースがインターネットにアクセスする際、外部のサーバーやサービスはどのIPアドレスからのリクエストかを認識する必要があります。この時に使われるのがEIP。
外部のサーバーやサービスが、どのIPアドレスからの通信かを認識し、適切に応答。
EIPが「送り出し元のIPアドレス」として使われる。

Q. ALBにはEIPは必要ないのか?なぜ?
ALBはインバウンド通信の処理に使われるものだから。

エンドユーザーは、WebブラウザやモバイルアプリケーションでサービスのURL(例えば、www.example.comとか)にアクセスする。

このURLは通常、Route 53などのDNSサービスを使用して、ALBのDNS名(例えば、my-alb-1234567890.us-west-2.elb.amazonaws.com)に解決される。

DNSは、www.example.comをALBのDNS名にマッピングし、そのDNS名を解決することで、エンドユーザーのリクエストをALBにルーティングする。

ALBのDNS名は、内部的に動的なパブリックIPアドレスに関連付けられており、AWSがその管理を行っている。

TODO:

// DNSレコード
resource "aws_route53_record" "django_todo_route53_record" {
  zone_id = aws_route53_zone.django_todo_route53_zone.zone_id
  name    = aws_route53_zone.django_todo_route53_zone.name
  type    = "A"

  alias {
    name    = aws_lb.django_todo_alb.dns_name
    zone_id = aws_lb.django_todo_alb.zone_id
    // ヘルスチェックの結果、ヘルスでないと判断された場合に、エイリアスレコードを解決しないようにする
    evaluate_target_health = true
  }
}

Kumamoto-HamachiKumamoto-Hamachi

CloudWatch Logs

CloudWatch Logs supports two log classes. Log groups in the CloudWatch Logs Standard log class support all CloudWatch Logs features. Log groups in the CloudWatch Logs Infrequent Access log class incur lower ingestion charges and support a subset of the Standard class capabilities. For more information, see

2つのロググラスがある。一つが Standardログクラスのロググループ。
もう一つがCloudWatch Logs Infrequent Access(低頻度アクセス)ログクラス。データ取り込みの費用が安いよ。でもStandardのサブセットしかもたない(つまり一部機能しかサポートせんよ)

incur:まねく、被る
ingestion:摂取
https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/CloudWatchLogsConcepts.html

CloudWatch Logs Live Tail

なんかログのストリーミングがいい感じに見えてインシデントのトラブルシュートに役立つとか言ってる。

Kumamoto-HamachiKumamoto-Hamachi

EventBridge

役割と用語

  • イベントを受信するルーター
    Event Bus(3種類あるよ)
  • イベントの送信元となるサービスやアプリケーション
    Event Source
  • 一定の間隔ごとに実行する「イベントパターン」や特定の時間に実行する「スケジュール」
    Rules
  • イベントを送信する先のリソースまたはエンドポイント
    Target

ターゲットが送り先。送り元はイベントソースじゃ。

使う流れ

(1)「イベントパターン」「スケジュール」を作成:Rulesを作成
(2)ターゲット指定、ターゲットへのアクセス制御のためIAMポリシー(or リソースベースポリシー)使用

その他

  • パイプ TODO
    イベントプロデューサーとコンシューマー間のポイントツーポイント統合を作成する機能?

  • API Destinations
    イベントバスから任意のWebベースのアプリケーションにイベントを送信する機能

  • イベントリプレイ
    過去のイベント再送信するで

  • スキーマレジストリ
    イベントのスキーマを管理する機能。イベントのスキーマ is イベントデータの構造。
    https://business.ntt-east.co.jp/content/cloudsolution/column-488.html

EventBridge Scheduler

タスクやイベントを大規模にスケジュールできるサーバーレススケジューラ

https://docs.aws.amazon.com/ja_jp/scheduler/latest/UserGuide/what-is-scheduler.html

https://acro-engineer.hatenablog.com/entry/2022/12/01/200000

既存の「EventBridge ルール」とは別物
イベントのクォータ数(割当数)とタイムゾーンのサポート。(UTCに変換すると日跨ぎになってしまうようなスケジュールを書きやすくなりました。)
https://dev.classmethod.jp/articles/amazon-eventbridge-scheduler-new-way-to-launch-tasks/

Kumamoto-HamachiKumamoto-Hamachi
  • task execution role
    タスクを実行する際に必要な権限をこのロールにまとめている。
    AmazonECSTaskExecutionRolePolicyでは
    (1)コンテナイメージをECRからgetしてpullするためにECRの権限諸々を付与
    (2)ログの出力先の作成と出力のためにCloudWatchLogsの権限諸々を付与

https://zenn.dev/sugay0519/articles/88f13ca589fcba

  • task role
    タスク実行して起動したコンテナたちがAWSリソースにアクセスする際の権限を付与
Kumamoto-HamachiKumamoto-Hamachi

cloudwatch logsでやったこと思い出し

cloudwatch logsのロググループのリソース定義。
タスク実行(task execution)ロールの定義。それに関わるiamポリシードキュメントの定義とか。
タスク実行ロールをECSタスクに付与。
ECSタスクが指定しているコンテナのlogConfigurationを設定。

Kumamoto-HamachiKumamoto-Hamachi

バッチ処理でやったこと思い出しTODO

やりたいこと:/bin/dateを2分に1回実行してcloud watch logsに吐く

  • ロググループの作成(ログ)

  • イベントルール設定:2分に1回吐けよ。descriptionはこちらです。(ルール)

  • IAMロールの設定 * 2種類(IAM)
    1つ目はタスク実行ロール(executionロール)。『ECSタスク』に割り当てる。ECRからpullしたりするのにいるからね。
    2つ目はタスクロール。管理ポリシー(AmazonEC2ContainerServiceEventsRole)を利用。後々、『イベントターゲット』に付与して「タスク実行」や「タスクにIAMロールを渡す」のに使う。

  • バッチ用のECSタスクとそれに使うコンテナ定義を設定(送り元)
    ECSタスクにはタスク実行ロール付与。(タスクはクラスタ、サービスと紐付かない?)

  • (cloud watch)イベントターゲットの作成:送り先はこちらです。(送り先)
    ECSクラスタ、Event Bridgeルール、タスクロールの指定もここで行う。

イベントターゲットはcloud watchだが、ECSクラスタが関連してくる?
そうするとイベントターゲットへの送信担当のECSタスクもクラスタと紐付いていないように見えて、イベントターゲットを介して紐付いている?

AmazonEC2ContainerServiceEventsRole
https://docs.aws.amazon.com/ja_jp/aws-managed-policy/latest/reference/AmazonEC2ContainerServiceEventsRole.html

https://made.livesense.co.jp/entry/2023/02/27/083000

Kumamoto-HamachiKumamoto-Hamachi

AWS Key Management Service(KMS)

暗号化キーの作成と管理のためのマネージドサービス。
キーのライフサイクル管理(作成、無効化、削除など)や、キーへのアクセス制御をKMSで一元的に行える。
https://business.ntt-east.co.jp/content/cloudsolution/column-186.html#section-1

カスタマーマスターキー(カスタマー管理キー)とAWS管理キー

AWS管理キー:特定のAWSサービスに対してのみ利用可能

エイリアス

カスタマーマスターキーにはそれぞれ UUID が割り当てられますが、⼈間には分かりづらいです。そこでエイリアスを設定し、どういう⽤途で使われているか識別しやすくします

AWS Secrets Manager

認証情報の管理やローテーションを行うサービス

Kumamoto-HamachiKumamoto-Hamachi

設定管理

環境ごとに異なる設定をどのように管理するか?
=>ECS のようなコンテナ環境では、設定をコンテナ起動時に注⼊
https://zenn.dev/link/comments/43ca3cceb97b4e

SSMパラメーターストアをTerraform によるコード化の意味?

SSM パラメータストアの値を、ECS の Docker コンテナ内で環境変数として参照出来る
※平⽂の値と暗号化した値は透過的に扱うことができ、ECS で意識する必要はない

Kumamoto-HamachiKumamoto-Hamachi

RDS

  • DBパラメーターグループ

  • DBオプショングループ

  • DBサブネットグループ
    データベースを稼働させるサブネットを指定。

DBインスタンス定義

terraform destoryで消したかったらdeletion_protectionskip_final_snapshotを反転させること。()

// DBインスタンスを作成
resource "aws_db_instance" "example" {
  identifier          = "example"
  engine              = "mysql"
  engine_version      = "5.7.23"
  instance_class      = "db.t3.small"
  allocated_storage   = 20
  storage_type        = "gp2"
  storage_encrypted   = true
  kms_key_id          = example_key_arn
  username            = "admin"
  password            = "password"
  multi_az            = true
  publicly_accessible = false
  // バックアップ(UTC時間)
  backup_window = "09:10-09:40"
  // バックアップ保存期間(Max:35)
  backup_retention_period = 30
  // メンテナンスウィンドウ(UTC時間)
  maintenance_window         = "Mon:10:10-Mon:10:40"
  auto_minor_version_upgrade = false
  deletion_protection        = true
  skip_final_snapshot        = false
  port                       = 3306
  // ⼀部の設定変更は再起動が伴い、予期せぬダウンタイムが発⽣する場合があるのでメンテナンスウィンドウを利用
  apply_immediately      = false
  vpc_security_group_ids = [var.mysql_sg_id]
  parameter_group_name   = aws_db_parameter_group.example.name
  option_group_name      = aws_db_option_group.example.name
  db_subnet_group_name   = aws_db_subnet_group.example.name

  lifecycle {
    ignore_changes = [password]
  }
}

DBのパスワードは平文で残ってしまうので。
aws rds modify-db-instance --db-instance-identifier 'example' --master-user-password 'NewMasterPassword!'

TODO:スナップショットどこ?

Kumamoto-HamachiKumamoto-Hamachi

ElastCache

ElastiCacheはインメモリデータベース。
インメモリデータベースはデータの保存、処理をメモリ上で行う。
https://zenn.dev/fdnsy/articles/6dd98ba5318bb6

TODO:なぜ揮発性のメモリはSSD/HDDより圧倒的に早いのか?

RDSのときはDBインスタンス以外に、パラメーター、サブネット、DBオプショングループあった。
※インスタンスに用いるセキュリティーグループ_sgも!

じゃあ、ElastiCacheは?

パラメーター

サブネットグループ

レプリケーショングループ

number_cache_clustersでノード数を指定します。ノード数はプライマリノードとレ
プリカノードの合計値

レプリケーショングループでは、各シャード(データパーティション)に対して複数のキャッシュノードを配置できます。この場合、number_cache_clusters は各シャードごとのノード数ではなく、レプリケーショングループ全体のノード数を指します。

レプリケーショングループは、一つのプライマリノード(主ノード)と、複数のリーダーノード(レプリカノード)で構成されます。プライマリノードは書き込み操作を受け付け、リーダーノードはそのデータを複製して持ちます。

// ElastiCacheレプリケーショングループ
resource "aws_elasticache_replication_group" "example" {
  replication_group_id          = "example"
  replication_group_description = "Example replication group for Redis"
  engine                        = "redis"
  engine_version                = "7.1.0"
  // レプリケーショングループ内に作成されるキャッシュノード(インスタンス)の数を指定
  number_cache_clusters = 3
  node_type             = "cache.m3.medium"
  snapshot_window       = "09:10-10:10"
  // スナップショット保持期間(キャッシュとしての利用なので短めでOK)
  snapshot_retention_limit   = 7
  maintenance_window         = "mon:10:40-mon:11:40"
  automatic_failover_enabled = true
  port                       = 6379
  apply_immediately          = false
  security_group_ids         = [var.redis_sg_id]
  parameter_group_name       = aws_elasticache_parameter_group.example.name
  subnet_group_name          = aws_elasticache_subnet_group.example.name
}

Kumamoto-HamachiKumamoto-Hamachi

TODO

num_cache_clusters はクラシックなElastiCache設定で使われていましたが、現在はレガシーで、特にマルチAZを利用する場合は replicas_per_node_group を使うことが推奨されています。num_cache_clusters が指定されていると、単一のシャードとして扱われ、マルチAZ設定が有効にならない可能性があります。

=> ちがうっぽい?

multi_az_enabledが重要?

Kumamoto-HamachiKumamoto-Hamachi

num_cache_clusters = 3replicas_per_node_group = 2にしてもNo Changesになる。

いったん個別destoryをしようとterraform destroy -target=aws_elasticache_replication_group.example -refresh=trueしたがNo objects need to be destroyed. No changes. No objects need to be destroyed. Either you have not created any objects yet or the existing objects were already deleted outside of Terraform.とでた。
うーん。

Kumamoto-HamachiKumamoto-Hamachi

https://qiita.com/kazurego7/items/57f5fb80b4783b7633a1

https://zenn.dev/hashito/articles/caa579a9aa8b4f

ラッパーにするやり方は複数サービスを最初のPID1のラッパースクリプトの子プロセスとするやり方。でもこれはgraceful shutdownにならない危険性がある(らしい)

Dockerとプロセス、サービス

https://qiita.com/wawon1111/items/8607ca2ef5efcc6650f9

ENTRYPOINTとCMDはどちらもコンテナが起動された際に実行される。
CMDはコンテナを起動する際にコマンドや引数を指定すると、その指定されたコマンドや引数で上書きされる。

プロセスは、コンピュータ上で実行されているプログラムやコマンドのこと。
たとえば、ターミナルでlsというコマンドを実行すると、このコマンドが実行される間、lsというプログラムがプロセスとして動作。プロセスは一時的なもので、プログラムの実行が終了するとプロセスも終了。

サービスは、通常バックグラウンドで動作し続けるプロセスのこと。、システムが提供する特定の機能や役割を強調(デーモンに対して)

フォアグラウンド

nginxはデフォルトでデーモンモード(バックグラウンドで実行される)で動作します。しかし、コンテナ内でnginxを実行する場合、プロセスがフォアグラウンドで動作している必要があります。これは、フォアグラウンドで動作しているプロセスがコンテナのメインプロセス(PID 1)として機能し、そのプロセスが終了するとコンテナ自体も終了するか

gunicornめも

Gunicornのsockファイルとは、主にGunicornがNginxなどのWebサーバーと通信するために使用するソケットファイル

Kumamoto-HamachiKumamoto-Hamachi

アプリケーションコードが GitHub の mainブランチにマージされたら、⾃動的に
ECS へデプロイされる仕組みを構築したい。

Kumamoto-HamachiKumamoto-Hamachi

codepipeline

  1. Source ステージ - GitHub からソースコードを取得する
  2. Build ステージ - CodeBuild を実⾏し、ECR に Docker イメージをプッシュする
  3. Deploy ステージ - ECS へ Docker イメージをデプロイする

アーティファクトストア

データの受け渡しに使⽤。s3。