AWS_terraformでとりあえずうんちアプリのインフラつくる
前提
こんな感じの構成のうんちアプリ(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パラメーターストア周りも怪しい?
便利リンク
ECS基礎的な概念めも
Amazon Elastic Container Serviceさん。
フルマネージドなコンテナオーケストレーションサービス。EKSとなにがちがうの...?
データプレーンとコントロールプレーン
コンテナオーケストレーションは「データプレーン」と「コントロールプレーン」という2つの概念から成り立っている(らしい)
-
コントロールプレーン
コンテナを管理するためのサービス。 -
データプレーン(実行環境)
コンテナを稼働させるためのサービス。
要は下記。
ネットワークの世界とは別の意味で使われているのか...?
planeは「平面,水平面」という訳だがITの世界においてプレーンとは「役割ごとに分かれたシステムの層」くらいのニュアンスの言葉。
Fargateだとssh接続できなくなると聞いたことがあるが...
「ECS Exec」を使用することでコンテナにSSH接続することができる(らしい)
参考1:コントロールプレーンとは?| コントロールプレーンとデータプレーンの対比 | Cloudflare
参考2:AWSでコンテナを支えるサービスについて ECS、EKS、Fargateの違いは? 第1回 | SunnyCloud
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:適切な形式に変換する的なニュアンス
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
}
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:カスタマイズいらずに箱から出したら(インストールしたら)すぐ使えまっせ。
TODO:GitHub Actionsでイメージ焼きやECRデプロイを自動化
code なんちゃらシリーズでやるかも。
ログの種類ごとにロググループを作成し、ログを出力するアプリケーションのインスタンスごとにストリームを作成することをおすすめします。要は、lambdaと同じ構成ですね。
TODO:コンテナイメージのレイヤーとは?
コンテナイメージは、複数のレイヤー(層)から構成されています。各レイヤーは、ファイルシステムのスナップショットのようなものであり、基本的に以下のように機能します:
ベースレイヤー:
基本となるファイルシステムのスナップショットで、通常はOSの最小限のセットアップが含まれます。
中間レイヤー:
ベースレイヤーの上に追加されるレイヤーで、アプリケーションの依存関係や設定ファイルなどが含まれます。
最上位レイヤー:
アプリケーション自体や最終的な設定が含まれるレイヤーです。
by chatgpt
TODO
Effectは書かなくておK?
// 信頼ポリシードキュメント
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
// 指定サービスにのみIAMロールをアタッチ出来る
identifiers = [var.identifier]
}
}
}
環境変数直渡しはできなかった
リモートステート
The terraform_remote_state data source uses the latest state snapshot from a specified state backend to retrieve the root module output values from some other Terraform configuration.
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
}
}
アカウントエイリアス作成
リモートステート利用のために
tfstateファイルをリモートのストレージにおいてバックエンドとして利用する。
terraform {
backend "s3" {
bucket = "django-todo-private-tfstate-bucket"
key = "terraform.tfstate"
region = "ap-northeast-1"
}
required_version = "= 1.1.7"
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.59.0"
}
}
}
tfstateの分割
【Terraform】.terraform.lock.hclについて理解する
bastion設計
TODO 踏み台サーバー自体がインターネットに公開されているのは問題ない?
ACM(Aws Certificated 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」のようにアスタリスク()を除いたホスト名のサーバーでも利用できます。
逆に言うとルートドメインは普通なら適用されないってことか。
SSL 証明書の検証 TODO
DNS 検証を選択すると、 ACMはこのデータベースに追加する必要がある 1 つ以上のCNAMEレコードを提供します
現在では主にCDN (Contents Delivery Network)や、/24未満のIPv4アドレスの逆引きを設定する際に使われています。
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
}
=>いまはこの書き方だめ
複数のドメインがある場合はどうする? 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(完全修飾ドメイン名)を指定。
エラー対処
// 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]
}
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
その他メモ(未検証)
ECS
サービス
プラットフォームのバージョン。
24/8/4現在、1.4.0
が最新っぽい。(1.3も使える。それより下は廃止済)
タスクの起動に時間がかかる場合、⼗分な猶予期間を設定しておかないと、ヘルスチェックに引っかかり、タスクの起動と終了が無限に続いてしまいます
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]
}
}
リスナー・リスナールール(ALB)
セキュリティーポリシーは24/8/4現在、ELBSecurityPolicy-TLS13-1-2-2021-06
が推奨されている。
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"
}
}
}
EIP
メモ:ALB、NATゲートウェイ、EIP
ALBは複数のAZ(のパブリックサブネット)にまたがって配置されるが、ユーザーからは単一のDNS名とIPアドレスで見える。=>論理的ALBは1つ。
NATゲートウェイは、プライベートサブネットに配置されたリソース(例えば、EC2インスタンスやECSタスク)がインターネットにアクセスする際に使用される。インターネットへのアウトバウンド通信はいるからね。
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
}
}
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:摂取
CloudWatch Logs Live Tail
なんかログのストリーミングがいい感じに見えてインシデントのトラブルシュートに役立つとか言ってる。
ほーん。ポリシードキュメントの継承どうするんだろ
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
タスクやイベントを大規模にスケジュールできるサーバーレススケジューラ
既存の「EventBridge ルール」とは別物
イベントのクォータ数(割当数)とタイムゾーンのサポート。(UTCに変換すると日跨ぎになってしまうようなスケジュールを書きやすくなりました。)
- task execution role
タスクを実行する際に必要な権限をこのロールにまとめている。
AmazonECSTaskExecutionRolePolicyでは
(1)コンテナイメージをECRからgetしてpullするためにECRの権限諸々を付与
(2)ログの出力先の作成と出力のためにCloudWatchLogsの権限諸々を付与
- task role
タスク実行して起動したコンテナたちがAWSリソースにアクセスする際の権限を付与
cloudwatch logsでやったこと思い出し
cloudwatch logsのロググループのリソース定義。
タスク実行(task execution)ロールの定義。それに関わるiamポリシードキュメントの定義とか。
タスク実行ロールをECSタスクに付与。
ECSタスクが指定しているコンテナのlogConfigurationを設定。
バッチ処理でやったこと思い出し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
AWS Key Management Service(KMS)
暗号化キーの作成と管理のためのマネージドサービス。
キーのライフサイクル管理(作成、無効化、削除など)や、キーへのアクセス制御をKMSで一元的に行える。
カスタマーマスターキー(カスタマー管理キー)とAWS管理キー
AWS管理キー:特定のAWSサービスに対してのみ利用可能
エイリアス
カスタマーマスターキーにはそれぞれ UUID が割り当てられますが、⼈間には分かりづらいです。そこでエイリアスを設定し、どういう⽤途で使われているか識別しやすくします
AWS Secrets Manager
認証情報の管理やローテーションを行うサービス
設定管理
環境ごとに異なる設定をどのように管理するか?
=>ECS のようなコンテナ環境では、設定をコンテナ起動時に注⼊
SSMパラメーターストアをTerraform によるコード化の意味?
SSM パラメータストアの値を、ECS の Docker コンテナ内で環境変数として参照出来る
※平⽂の値と暗号化した値は透過的に扱うことができ、ECS で意識する必要はない
RDS
-
DBパラメーターグループ
-
DBオプショングループ
-
DBサブネットグループ
データベースを稼働させるサブネットを指定。
DBインスタンス定義
terraform destory
で消したかったらdeletion_protection
とskip_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:スナップショットどこ?
ElastCache
ElastiCacheはインメモリデータベース。
インメモリデータベースはデータの保存、処理をメモリ上で行う。
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
}
TODO
num_cache_clusters はクラシックなElastiCache設定で使われていましたが、現在はレガシーで、特にマルチAZを利用する場合は replicas_per_node_group を使うことが推奨されています。num_cache_clusters が指定されていると、単一のシャードとして扱われ、マルチAZ設定が有効にならない可能性があります。
=> ちがうっぽい?
multi_az_enabledが重要?
num_cache_clusters = 3
をreplicas_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.
とでた。
うーん。
なむなむ〜。予想より高いのは古いmysqlのバージョンにしていたから?
RDSの設定からマルチAZになっていることやスタンバイしているDBのAZを確認出来る。
ラッパーにするやり方は複数サービスを最初のPID1のラッパースクリプトの子プロセスとするやり方。でもこれはgraceful shutdownにならない危険性がある(らしい)
Dockerとプロセス、サービス
ENTRYPOINTとCMDはどちらもコンテナが起動された際に実行される。
CMDはコンテナを起動する際にコマンドや引数を指定すると、その指定されたコマンドや引数で上書きされる。
プロセスは、コンピュータ上で実行されているプログラムやコマンドのこと。
たとえば、ターミナルでlsというコマンドを実行すると、このコマンドが実行される間、lsというプログラムがプロセスとして動作。プロセスは一時的なもので、プログラムの実行が終了するとプロセスも終了。
サービスは、通常バックグラウンドで動作し続けるプロセスのこと。、システムが提供する特定の機能や役割を強調(デーモンに対して)
フォアグラウンド
nginxはデフォルトでデーモンモード(バックグラウンドで実行される)で動作します。しかし、コンテナ内でnginxを実行する場合、プロセスがフォアグラウンドで動作している必要があります。これは、フォアグラウンドで動作しているプロセスがコンテナのメインプロセス(PID 1)として機能し、そのプロセスが終了するとコンテナ自体も終了するか
gunicornめも
Gunicornのsockファイルとは、主にGunicornがNginxなどのWebサーバーと通信するために使用するソケットファイル
ssl終端を考える
codebuildふたたび
Dockerイメージのビルド & DockerイメージのECRへのpush!
aws codebuild list-curated-environment-images
アプリケーションコードが GitHub の mainブランチにマージされたら、⾃動的に
ECS へデプロイされる仕組みを構築したい。
codepipeline
- Source ステージ - GitHub からソースコードを取得する
- Build ステージ - CodeBuild を実⾏し、ECR に Docker イメージをプッシュする
- Deploy ステージ - ECS へ Docker イメージをデプロイする
アーティファクトストア
データの受け渡しに使⽤。s3。
SQSとSNS
Why S3バケットポリシー