Upgrade to Pro — share decks privately, control downloads, hide ads and more …

layerx-invoice-practical-devops-20211029

shnjtk
October 30, 2021
15k

 layerx-invoice-practical-devops-20211029

shnjtk

October 30, 2021
Tweet

Transcript

  1. © LayerX Inc. 自己紹介 高江 信次 (Shinji Takae) 株式会社LayerX CTO室

    リードエンジニア LayerX インボイスの開発メンバーとして主にインフラを担当 コーポレートエンジニア的な業務も一部兼任 好きなAWSサービス 2 AWS Fargate Amazon Aurora Lambda
  2. © LayerX Inc. 普段の開発の様子 • インフラの全体的な設計や開発など大掛かりな作業は基本的に インフラ担当が行う • 軽微な変更はアプリ担当が自分で対応することもある •

    IaC化する前に、テスト的に管理画面から手動でリソースを作ることもある • 手動で作って不要になったリソースの削除やIaC化はインフラ担当が行う • 結果的に、チーム全員が自然とインフラに関わっている 6
  3. © LayerX Inc. IaCリポジトリのディレクトリ構成 9 invoice-infra ├── aws_cloudwatch_logs.tf ├── aws_ecs.tf

    ├── aws_iam_ecs.tf ├── aws_iam_lambda.tf ├── aws_kms.tf ├── aws_rds.tf ├── aws_route53.tf ├── aws_s3.tf ├── aws_secrets.tf ├── buildspec.yml ├── data.tf ├── datadog_dashboard_ecs.tf ├── datadog_integration.tf ├── datadog_monitor_ecs.tf ├── datadog_monitor_rds.tf ├── datadog_synthetics_test.tf ├── env │ ├── dev.tfvars │ ├── prd.tfvars │ └── stg.tfvars ├── locals.tf ├── provider.tf └── variables.tf (一部抜粋) • リポジトリ直下に.tfファイルをフラットに配置 • 環境変数は.tfvarsファイルで定義して envディレクトリに配置 ◦ terraformコマンド実行時に引数で指定 • プロジェクト固有の定数はlocals.tfで定義
  4. © LayerX Inc. Terraformファイルの命名規則 • (provider)_(resource)_(target).tf ◦ 例: aws_iam_ecs.tf、aws_iam_lambda.tf •

    targetがなかったり、自明な場合は省略 ◦ 例: aws_rds.tf、aws_kms.tf • 目的 ◦ インフラのコードに普段はあまり関わらないメンバーであっても、コードを変更する際にどのファイル を編集すればいいか一目で分かる • 上記はあくまで一例 ◦ チーム内で一貫性があって、変更したい目的のファイルをすぐに特定できればよい 10
  5. © LayerX Inc. タグ付けによるIaCリポジトリの明確化 • default_tagsを利用して、各リソースを管理してい るIaCリポジトリがどれであるかを示すタグを付与 する • 目的

    ◦ アプリ担当がリソースに変更を加えたい場合に IaCリポジトリがすぐに分かる ◦ これが付与されていないものは手動で作ったテスト用リ ソースであると判断できる → リソース棚卸し時の削除対象 11 locals { service_id = "invoice" managed_by = "invoice-infra" } provider "aws" { region = “ap-northeast-1” default_tags { tags = { service_id = local.service_id managed_by = local.managed_by env = var.env } } } locals.tf provider.tf
  6. © LayerX Inc. 再利用性を意識したリソース定義 • アプリ開発メンバーがインフラに変更 を加えるケースの例 ◦ S3 bucketを追加したい

    ◦ IAM policyを変更したい ◦ DynamoDB tableを追加したい • なるべくコピペしやすいようにlocals やvarを使って共通部分は そのまま使えるようにする • 暗号化設定や非公開設定など、守っ てほしい部分も漏れなく カバーされるようにリソース定義を隣 接させておく 12 resource "aws_s3_bucket" "csv_files" { bucket = "${local.service_id}-${var.env}-csv-files" acl = "private" versioning { enabled = true } server_side_encryption_configuration { rule { apply_server_side_encryption_by_default { kms_master_key_id = aws_kms_key.main.arn sse_algorithm = "aws:kms" } } } } resource "aws_s3_bucket_public_access_block" "csv_files" { bucket = aws_s3_bucket.csv_files.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true }
  7. © LayerX Inc. CI/CDの自動化とtfstateのバージョン管理 • plan/applyの実行は自動化する ◦ planやapplyの結果をチームで共有することにより レビューしやすくなる •

    tfstateファイルは必ずリモートバックエンドで保管しバージョ ン管理を有効にする ◦ バージョン管理することで、もし何かあった場合でも tfstateをロール バックすることができる ◦ 「何かあってもすぐに元に戻せる」という安心感があることで 失敗を恐れずにインフラを変更できる 13 PR plan apply tfstate
  8. © LayerX Inc. plan実行時に差分を減らす工夫 14 例: data sourceが参照しているリソースを変更した場合 resource "aws_s3_bucket"

    "csv_files" { bucket = "csv-files" tags = { foo = “bar” # -> “baz” に変更すると... } } data “aws_iam_policy_document” “csv_reader” { statement { effect = “Allow” actions = [“s3:GetObject”] resources = [“${aws_s3_bucket.csv_files.arn}/*”] } } resource “aws_iam_policy” “csv_reader” { name = “csv-reader” policy = data.aws_iam_policy_document.csv_reader.json } # aws_s3_bucket.sample will be updated in-place ~ resource "aws_s3_bucket" "csv_files" { ~ tags = { ~ "foo" = "bar" -> "baz" } # (config refers to values not yet known) <= data "aws_iam_policy_document" "csv_reader" { ~ id = "232721XXXX" -> (known after apply) ~ json = jsonencode( { - Statement = [ - { - Action = "s3:GetObject" - Effect = "Allow" - Resource = "arn:aws:s3:::csv-files/*" - Sid = "" }, ] - Version = "2012-10-17" } ) -> (known after apply) - version = "2012-10-17" -> null } # aws_iam_policy.sample will be updated in-place ~ resource "aws_iam_policy" "csv_reader" { id = "arn:aws:iam::XXX:policy/csv-reader" name = "csv-reader" ~ policy = jsonencode( { - Statement = [ - { - Action = "s3:GetObject" - Effect = "Allow" - Resource = "arn:aws:s3:::csv-files/*" - Sid = "" }, ] - Version = "2012-10-17" } ) -> (known after apply) } Plan: 0 to add, 2 to change , 0 to destroy.
  9. © LayerX Inc. plan実行時に差分を減らす工夫 15 resource "aws_s3_bucket" "csv_files" { bucket

    = "csv-files" tags = { foo = “bar” # -> “baz” に変更すると... } } resource "aws_iam_policy" "csv_reader" { name = "csv-reader" policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Action = ["s3:GetObject"] Resource = ["${aws_s3_bucket.csv_files.arn}/*"] }] }) } Terraform will perform the following actions: # aws_s3_bucket.sample will be updated in-place ~ resource "aws_s3_bucket" "csv_files" { id = “csv-files" ~ tags = { ~ "foo" = "bar" -> "baz" } ~ tags_all = { ~ "foo" = "bar" -> "baz" } # (9 unchanged attributes hidden) # (1 unchanged block hidden) } Plan: 0 to add, 1 to change, 0 to destroy. 対策: jsonencodeでインライン化する
  10. © LayerX Inc. ツールの使い分け • 基本的にはTerraformを使用してリソースを管理する • Lambda関数はケース・バイ・ケースで使い分ける ◦ jsファイル1つで済む程度の軽いものであればTerraform

    ◦ パッケージインストールや複雑なビルドが必要なものは AWS CDK • AWS CDKを使う場合は対象をLambda関数だけにする ◦ 厳密には、Lambda関数とそれをデプロイするためのCloudFormation stack ◦ Lambda関数に付与するIAM role/policyや、その他必要なリソースはTerraformで管理 • 目的 ◦ Lambda関数はアプリ担当が変更を加えることが多く、デプロイの際に関数以外のことを意識しなくても 済むように ◦ リソースはそれぞれ関連(参照)している場合が多く、生成や変更の順序に依存性があったりするので、 そういった点はTerraformで解決する 16
  11. © LayerX Inc. まとめ • 誰でもインフラ開発ができるように、一貫性のあるルールを設けて シンプルなファイル構成、再利用性の高いリソース定義にしよう • タグを活用してリソースの管理状況を可視化しよう •

    CI/CD環境を構築して処理を自動化し、チームメンバーがレビューできる ようにしよう • plan実行時の差分を減らすためにコードをリファクタしよう • ツールは1つに絞らず適材低所で採用しよう 17 開発スピードを落とさず、IaCもきちんと行うために: