Slide 1

Slide 1 text

Poolにおける 開発の足を止めないシステム基盤構築 1

Slide 2

Slide 2 text

自己紹介 菅原 元気 株式会社カンム SRE @sgwr_dts @winebarrel 好きな技術 / / / / は勉強中 2

Slide 3

Slide 3 text

3

Slide 4

Slide 4 text

資産運用のハードルを下げる 投資ができるクレカを発行 予定利回り年率1% 決済還元1% https://pool-card.jp/ 4

Slide 5

Slide 5 text

本日話したいこと 既存のプロダクトで感じていた課題を解決するため コンテナなど社内では使ってこなかった技術スタックを使って Poolのシステム基盤を構築しました。 既存の技術とは大きく異なるシステムで 安全かつスピーディーに開発を行うために意識したところなどを 課題を踏まえながら振り返ります。 5

Slide 6

Slide 6 text

アジェンダ 決済サービスの簡単な解説 Poolの採用技術 Poolのシステム構成 デプロイ Terraform オペレーション モニタリング まとめ 6

Slide 7

Slide 7 text

決済サービスの簡単な解説 7

Slide 8

Slide 8 text

カード関連会社の関係 8

Slide 9

Slide 9 text

カード利用の流れ 9

Slide 10

Slide 10 text

オーソリ(仮売上処理)・クリアリング(売上精算) 利⽤者 お店 国際ブランドネットワーク Pool オーソリ カード利⽤ カード利⽤ オーソリ電⽂ オーソリ電⽂ 承認 承認 カード利⽤完了 カード利⽤完了 数⽇後 クリアリング 確定売上 確定売上 クリアリング電⽂ クリアリング電⽂ 利⽤者 お店 国際ブランドネットワーク Pool 10

Slide 11

Slide 11 text

Poolの採用技術 実装(バックエンド) Go インフラ Amazon ECS (Fargate) Amazon Aurora (PostgreSQL) AWS Step Functions サービス間通信 gRPC 11

Slide 12

Slide 12 text

Poolの採用技術 CI GitHub Actions モニタリング Datadog インフラ管理 Terraform DBマイグレーション sqldef 12

Slide 13

Slide 13 text

Poolのシステム構成 13

Slide 14

Slide 14 text

既存システムの課題 EC2インスタンスの保守 OSのアップデート パッケージのアップデート ログインユーザーの管理 バッチ EC2上でcron Ansibleの適用が手間 →コンテナを中心としたシステムの構築 14

Slide 15

Slide 15 text

APIサーバ・バッチ 15

Slide 16

Slide 16 text

オーソリ・クリアリング 16

Slide 17

Slide 17 text

Poolのサーバ構成 ECS (Fargate) SREの少なさからEKSは不採用 基本的にコンテナ化 オーソリサーバもコンテナ化 クリアリング電文受信のため最低限のEC2(Windowsサーバ) プロビジョニングはPacker Chefにできないか調査中 バッチ処理はStep Functionsでコンテナ実行 プロダクション・ステージングでAWSアカウントを分けている 17

Slide 18

Slide 18 text

バッチ Step FunctionsからECSタスクを実行 EventBridgeで定期実行 Windowsバイナリ実行もStep Functions できるだけ処理をAWSに寄せる ワークフローはterraformで定義 EventBridgeと合わせてモジュール化し記述をシ ンプルに Failした場合、SNS経由でDatadogに通知 18

Slide 19

Slide 19 text

バッチ resource "aws_sfn_state_machine" "xxx" { name = "xxx" role_arn = aws_iam_role.state_machine.arn definition = jsonencode({ StartAt = "Run ECS Task", States = { "Run ECS Task" = { Type = "Task" Resource = "arn:aws:states:::ecs:runTask.sync" Parameters = { LaunchType = "FARGATE" EnableExecuteCommand = true NetworkConfiguration = { ... } Cluster = ... TaskDefinition = ... Overrides = { ContainerOverrides = [ { Name = "app" Command = [ ... ] Environment = { ... } }, 19

Slide 20

Slide 20 text

バッチ module "app_cron" { source = "../modules/cron" name = "app-cron" cluster_arn = aws_ecs_cluster.app.arn subnet_ids = [for sn in aws_subnet.app : sn.id] definitions = { "batch" = { command  = ["./batch-cli", "xxx"] environment = [ { Name = "INPUT_FILE", "Value.$" = "$.INPUT_FILE" }, ] input = { INPUT_FILE = "" } schedule_expression = "cron(5 15 * * ? *)" task_definition = "arn:aws:ecs:ap-northeast-1:123456789012:task-definition/batch" security_groups = [ ... ] task_roles = [ ... ] }, 20

Slide 21

Slide 21 text

Step Functions AWSのAPIはだいたい何でも実行できる Session ManagerでPowerShellを実行など ワークフローの記述は冗長 待ち合わせ・失敗の処理などに数ステップ必要になる 入出力加工用のJSONPathが貧弱 @ .. , : ? * がサポートされない 使える関数が限られている HTTPを直接リクエストできない Lambda経由・SNS経由…etc サーバレスのメリットが大きい Step FunctionsのDXは改善したい 21

Slide 22

Slide 22 text

デプロイ 22

Slide 23

Slide 23 text

既存システムの課題 テストの実行とデプロイが分離 デプロイは手作業で Ansibleの実行 AMI作成 インスタンスのサービスイン 手間がかかる・ミスが起きる →GitHub Actionsを使ったデプロイの高速化 23

Slide 24

Slide 24 text

GitHub Actions 24

Slide 25

Slide 25 text

GitHub Actions 25

Slide 26

Slide 26 text

テスト・デプロイ GitHub Actions releaseブランチなしのGit Flow masterが直接プロダクションにリリースされる OpenID ConnectでAWSのクレデンシャルを取得 同じワークフローでプロダクション・ステージングを実行 AWS_ACCOUNT: ${{ (github.ref == 'refs/heads/master' && '123456789012') || '210987654321' }} DBマイグレーションはCodeBuild経由で実行 Dockerイメージ不要でVPCにアクセスできるようにするため 26

Slide 27

Slide 27 text

テスト・デプロイ ECSへのデプロイはecspresso https://github.com/kayac/ecspresso Windowsへのデプロイ CIでビルドしたバイナリをS3にアップロード Session ManagerでWindows上でスクリプトを実行 S3→Windows バッチ用ECSタスク定義 バージョン未指定の場合、latestが実行される ecspresso run でタスク定義の更新+動作確認 27

Slide 28

Slide 28 text

ECSタスク定義 / ├── lib │ ├── datadog.libsonnet │ └── utils.libsonnet ├── prd │ ├── pool-app │ │ ├── api │ │ │ ├── ecs-service-def.jsonnet │ │ │ ├── ecs-task-def.jsonnet │ │ │ └── ecspresso.yml │ │ ├── batch │ │ │ └── ... │ │ ├── common │ │ │ ├── api-container.libsonnet │ │ │ └── container-common.libsonnet │ │ └── external-service │ │ └── sendgrid.libsonnet │ └── pool-card │ ├── ... └── stg ├── ... 28

Slide 29

Slide 29 text

ECSタスク定義 local datadog = import '../../../lib/datadog.libsonnet'; local utils = import '../../../lib/utils.libsonnet'; local apiCommon = import '../common/api-container.libsonnet'; { containerDefinitions: [ utils.objectToArray(apiCommon { environment+: { USE_CLIENT: 'true', }, secrets+: { CLIENT_SECRET: '{{ tfstate `aws_secretsmanager_secret.secret.arn` }}:secret::', }, logConfiguration: datadog.logConfiguration(...), }), datadog.agentContainer, datadog.fluentBitContainer, ], cpu: '256', executionRoleArn: '{{ tfstate `aws_iam_role.task_execution_role.id` }}', family: 'api', memory: '512', networkMode: 'awsvpc', requiresCompatibilities: ['FARGATE'], taskRoleArn: '{{ tfstate `aws_iam_role.task_role.id` }}', } 29

Slide 30

Slide 30 text

ECSタスク定義 jsonnetで定義 タイムリーにecspressoがjsonnetサポート https://sfujiwara.hatenablog.com/entry/ecspresso-v1.7.0 Datadog Agentなどのサイドカーや共通部分は.libsonnetに切り出し コードの重複をなくす Terraformリソースの参照はecspressoのtfstateプラグインを利用 直接のIDの記述をなくして可読性を向上 30

Slide 31

Slide 31 text

リリース 31

Slide 32

Slide 32 text

リリース 32

Slide 33

Slide 33 text

リリース @kabot pr-release でリリース用PR作成 masterにマージされたらリリース 手作業のミスをなくし、masterに入るPRをわかりやすく Slackボットは自作 Hubotの更新が止まっていたので… Bolt+Slackソケットモード Hubot/Rubotyの設計を踏襲 PRの作成はボットがgit-pr-releaseを実行 https://github.com/x-motemen/git-pr-release 33

Slide 34

Slide 34 text

Terraform 34

Slide 35

Slide 35 text

既存システムの課題 各種インフラリソースがコード化されていない AWS、PostgreSQLロール… リソース管理まわりオペレーションの委譲がしにくい 強力な権限を渡すか、SREが作業する →Terraformによるコード化・作業の委譲 35

Slide 36

Slide 36 text

Terraform pool/ ├── aws │ ├── modules │ ├── prd │ │ ├── ecs.tf │ │ ├── rds.tf │ │ ├── s3.tf │ │ └── ... │ └── stg ├── datadog │ ├── prd │ │ ├── moniter.tf │ │ └── ... │ └── stg ├── pagerduty │ └── prd │ ├── service.tf │ └── ... └── postgresql ├── prd │ ├── app-db │ │ ├── app_user.tf │ │ └── ...terraform.tf │ └── card-db └── stg 36

Slide 37

Slide 37 text

Terraform Pool以外のサービスのTerraformも含まれるモノレポ AWSに限らずDatadog/PagerDuty/PostgreSQLも管理 モジュールはなるべく使わない方針 コードを追いやすい リソースの相互参照がしやすい リソースの変更を局所化できる 環境ごとのコードの記述量が多くなるのが課題 記述量の削減のために for_each を積極的に使用 37

Slide 38

Slide 38 text

Terraform resource "aws_subnet" "pool" { for_each = { ap-northeast-1a = "10.50.0.0/24", ap-northeast-1b = "10.50.1.0/24", } vpc_id = aws_vpc.pool.id cidr_block = each.value availability_zone = each.key tags = { Name = "pool-${each.key}" } } resource "aws_route_table" "pool" { for_each = aws_subnet.pool vpc_id = aws_vpc.pool_app_prd.id } resource "aws_route_table_association" "pool" { for_each = aws_subnet.pool subnet_id = each.value.id route_table_id = aws_route_table.pool[each.value.availability_zone].id } 38

Slide 39

Slide 39 text

Terraform PRで terraform plan だけGitHub Actionsで実行 結果はPRにコメント 開発者がPR経由でリソースの変更の提案を行える applyはSREが手動で実行 terraform apply の自動化は課題 ステージング環境については開発者にapply可能な権限を付与 ステージング環境は開発者が自由に操作できる 39

Slide 40

Slide 40 text

オペレーション 40

Slide 41

Slide 41 text

既存システムの課題 基本的には管理画面での操作 まれにCLIを操作するオペレーションが発生する 管理用コマンドの実行 ネットワークの疎通確認 どうしても必要なDBの操作 サービスコンテナへのECS Exec セキュリティ上よくない オペレーション用EC2インスタンス 管理コストがかかる →demitasというツールを作成 41

Slide 42

Slide 42 text

demitas 42

Slide 43

Slide 43 text

demitas https://github.com/winebarrel/demitas https://tech.kanmu.co.jp/entry/2021/12/14/100000 ecspressoのラッパー サービスと同じVPC・サブネット・SGで作業用タスクを起動 作業が終わったらタスクは破棄 コンテナ経由のポートフォワーディングも可能 kubectl run kubectl port-forward をイメージして作成 43

Slide 44

Slide 44 text

demitas $ demitas-exec -e bash Start ECS task... ECS task is running: 9b71b0c7e090f7e0ac8a7bc4fbf051a7 The Session Manager plugin was installed successfully. Use the AWS CLI to start a session. Starting session with SessionId: ecs-execute-command-5daee3efe79819c1 root@ip-10-50-0-10:/# 44

Slide 45

Slide 45 text

demitas サービスのコンテナにログインすることなくVPC内作業が可能 使い捨てのコンテナなので自由にパッケージを入れられる タイミングによってはECSタスクが残ったままになる クリーニングが課題 実装がきれいではないのでリファクタリングしたい 45

Slide 46

Slide 46 text

モニタリング 46

Slide 47

Slide 47 text

既存システムの課題 通知や情報が分散している ログがEC2サーバ上 Slackチャンネルへの通知、PagerDutyへの通知 SaaSが分散している Sentry、Mackerel →Datadogによる一元管理 47

Slide 48

Slide 48 text

モニタリング 基本的にDatadog 一部PagerDuty モニターはTerraformで管理 閾値の変更が誰でもしやすくなる ログもDatadogに送信 Live Tailも可能でログの検索性が向上 APMやエラートラッキングとの紐付けで調査がしやすく Datadogの保持期間以上はS3に 48

Slide 49

Slide 49 text

アラート PagerDutyとSlackに通知 すべてのアラートはPagerDutyでインシデント化 アラートのアサイン漏れを防ぐ 緊急度の高くないものはLow Urgencyで Log Urgencyはoncall-statusでOS Xのデスクトップに通知 https://github.com/mtougeron/oncall-status 通知メッセージにはRunbookのリンクを含める 誰でもインシデントに対応できるように 49

Slide 50

Slide 50 text

まとめ システム構成 コンテナ化で管理コストの削減 デプロイ GitHub Actionsでのデプロイの高速化 Terraform コード化による適切な作業の委譲 オペレーション demitasによる安全で自由度の高い作業の実現 モニタリング Datadogでの一元管理やアラートの集約で効率化 50

Slide 51

Slide 51 text

We are hiring! https://team.kanmu.co.jp/ 51