Slide 1

Slide 1 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG 7周年を迎えたサロン予約サービス「minimo」 におけるコンテナ移行で得られたCDKのノウハウ 2021年10月29日(金) JAWS-UG #20

Slide 2

Slide 2 text

自己紹介

Slide 3

Slide 3 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG 自己紹介 • 株式会社ミクシィ所属 ‣ minimoには1年ほど前にJoin ‣ コンテナ移行、Go言語移行などを担当 • よく触るのはAWSとUnity ‣ 最近はCDKやECSと戯れたり • 趣味はゲーム・なろう漁り IUUQTNJYJDPKQ

Slide 4

Slide 4 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG minimo紹介 • 1月で8周年を迎えるサービス! • 美容師やネイリスト、アイデザイナー などを検索・予約できるアプリ • サロンスタッフを直接予約&やり取り することができる • 最近では動画投稿機能なども追加 • (採用強化中です) IUUQTNJOJNPEFMKQJOGP

Slide 5

Slide 5 text

アジェンダ

Slide 6

Slide 6 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG アジェンダ • コンテナ移行の流れ • PRごとに開発環境を構築できるCI/CD • ローリングアップデート・Blue/Greenデプロイメント • CDKのスタック分割・ディレクトリ構成 • CDKで運用する上での注意点

Slide 7

Slide 7 text

コンテナ移行の流れ

Slide 8

Slide 8 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG 移行理由 • ローカル環境ではdockerが導入されていた • 特別インフラのコード化や自動化がなされていたわけではないので、手順の 複雑化やそれによる属人化 • 開発環境を気楽に増減したい ‣ EC2で固定数インスタンスを用意した形式(例: dev1~dev5)で、増減させ ようとするとやや手間 ‣ デプロイがCLI経由のため、エンジニア以外はデプロイも難しい • その他、SSH前提やライフサイクル、肥大化における課題

Slide 9

Slide 9 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG オーケストレーション • ECS ‣ ECSは他のAWSサービスと組み合わせて利用するため、ECS自体はシンプルで学 習コストが低い ‣ minimoの他サービスでECSを利用している ‣ (個人的にはバージョンを意識せずに済むのが特に嬉しい) • EKS ‣ 社内含めポピュラーで、Kubernetesの機能やエコシステムを活かしやすい ‣ Reconciliation LoopでGitOpsもしやすく、minimoの課題解決に繋がる • 学習コストやすでにECSを利用していることが決め手となり、ECSを採用

Slide 10

Slide 10 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG ECSのデプロイツール • 数が多い…(良い意味) • 手軽に個別URLの開発環境を構築できるようにしたい(ECSだけでなくALBな ども取り扱ってほしい) • また元々インフラのコード化があまりされていなかったので、ECS周辺以外を コードとして管理できるのは魅力 • 比較は「第2回 AWS Fargate かんたんデプロイ選手権」も参考 • minimoは専属のインフラチームが存在せず、アプリケーションコードを書き 慣れているメンバーが多い • 以上から、AWS CDKを選定する

Slide 11

Slide 11 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG その他 • 運用フローの変更と周知 ‣ 詳細は次の項目にて • 監視目的でDatadogを導入(インフラストラクチャー・APM・ログ管理) • 負荷試験を実施(Locust) • 加重レコードを利用し、ダウンタイムなしで監視しつつ徐々に移行

Slide 12

Slide 12 text

PRごとに開発環境を構築できる CI/CD

Slide 13

Slide 13 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG 課題 • 元々はdev1,dev2のように、事前に用意された環境にデプロイする仕組み で、数に限りがあり、空きがないと動作検証ができない • とはいえ新しく作ろうにも、すべき手順が多く難しい • 常駐されているため、常に一定のコストが発生 • どのPR、どのcommit時点でデプロイしているか判断がつかない • CLIからのデプロイのためサーバーエンジニア依存で、気楽に検証がしづらい • 開発環境間でDBが共通なため、好き勝手に弄るわけにはいかない

Slide 14

Slide 14 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG 変更後 PRにてコメント Slackでデプロイ完了通知 ※ PRマージで自動破棄も

Slide 15

Slide 15 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG 完

Slide 16

Slide 16 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG もっと詳しく • PRにコマンドをコメントするだけで、PRごとにレビュー用の環境が構築され る(マージすると自動で破棄) • 必要な時、必要な分だけ確保できる仕組み • 「PR固有のDBを用意する」などの様々なオプション付き • コメント式のため、不要なPRでは環境を構築せずに済む ‣ 加えて、どのcommit時点でデプロイしたかが一目瞭然 • これができるのもプログラミング言語で手続き的にインフラを構築できる CDKの恩恵(…)

Slide 17

Slide 17 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG イメージ図 コードで 「ブランチ名」「イメージタグ」 を管理 CDKのコード(下記) が更新されると、 CodeBuildデプロイが走る PRごとに独自DBを用意する オプション付きの場合は、 MySQLコンテナでサクッと ※ サンプルです

Slide 18

Slide 18 text

ローリングアップデート・ Blue/Greenデプロイメント

Slide 19

Slide 19 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG ローリングアップデート • ECSのデフォルトがローリングアップデート • タスク起動などの失敗時に自動でロールバックするサーキットブレーカーが 追加された ‣ CFnでECSを管理していた場合、以前までは失敗時に停止→再起動→停止… の無限ループになっていたので、とてもありがたい機能 ‣ ただし、閾値は最小10・最大200で変更ができないため、開発環境などで Desired Countを1としている場合などはなかなかロールバックしない • デプロイが遅い場合、大体はderegistration_delay.timeout_secondsが原因

Slide 20

Slide 20 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG Blue/Greenデプロイメント • 現在稼働しているものとは別にリソースを用意し、全体の何%を流すかなどの 戦略を元に新旧の入れ替えを実施する ‣ その性質上、負荷のコントロールやトラブル時のロールバックが容易 • deploymentControllerがCODE_DEPLOYまたはEXTERNALで可能(大体の人 はCODE_DEPLOYではないだろうか) • CFnでもECSのBlue/Greenデプロイメントができるようになった ‣ マクロを利用して作られており、EXTERNALを指定する ‣ そのため、ロールバック時はCFnの更新をキャンセルする

Slide 21

Slide 21 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG Blue/Greenデプロイメントの検討理由 • CFnでもBlue/Greenデプロイメントが可能となったことから、CDK下でも管 理できるようになった • ローリングアップデートにおける課題 ‣ 何%だけ新に流すといった細かいコントロールはできない ‣ タスク起動時のhealthcheck以外は動作保証するものではない ‣ Blue/Greenに比べ、ロールバックするまでに時間がかかる ‣ バージョンの混在 ‣ 特定のタスクに負荷が偏ることがある

Slide 22

Slide 22 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG CFnでのBlue/Greenデプロイメントの問題 • そもそもCDKでの対応はまだ完了していない ‣ https://github.com/aws/aws-cdk/issues/1559 ‣ とはいえ、L1などを活用すればCDKでも実装することが可能 • AWS::ECS::TaskDefinitionとAWS::ECS::TaskSet以外の更新を含めることができない • 他スタックからの値インポートなどができない(クロススタック参照封じ) • ならEXTERNALで自力という方法も頭をよぎったが、EXTERNALの場合はサービス のAuto Scalingがサポートされないといった問題もある • とはいえCODE_DEPLOYにすると、CFnからはupdate-serviceできなくなる

Slide 23

Slide 23 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG CFnでのBlue/Greenデプロイメント方針 ๏他からの参照はせずAWS::ECS::TaskDefinitionまたはAWS::ECS::TaskSetの みの更新になるようスタック分割でき、Auto Scalingを自前で対応するか、 利用しない方法を模索できる ➡ Perform ECS blue/green deployments through CodeDeploy using AWS CloudFormationを参考に構築する ๏上記の運用が難しい場合 ➡ 途中までCDKでリソースを作成し、CodeDeployはマネコンやCLIなどを利 用してデプロイするようにする

Slide 24

Slide 24 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG ローリングアップデートでの対策 • ローリングアップデートでは厳しいかと言われると、そんなことはない • ローリングアップデートを利用して真っ先に顕著な課題となるのが負荷の偏り ‣ 特定のタスクに負荷が偏るなどして、デプロイ時にレスポンスが悪化 • この対策として特に効果が高いもの ‣ ALBでLORアルゴリズムに変更(LBが未処理のリクエスト数が最も少ないタ ーゲットにリクエストを送信するようになる) ‣ 不格好だが、entrypoint.sh等で適当なリクエストをかけるなどして、ター ゲットグループ入り前にwarmupさせることが効果的なプロダクトも

Slide 25

Slide 25 text

CDKのスタック分割・ ディレクトリ構成

Slide 26

Slide 26 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG CDKでのコーディング • 「プログラミング言語で手続き的にインフラを構築」というのは、メンテで きないコードを生む危険がある • 構成管理の観点から考えると、宣言的が望ましい • CDK自体は宣言的だが、構文を利用できるがゆえに手続き的になり、結果的 に把握しづらいコードが生み出される可能性がある • 手続き的に書くこと自体は悪くなく、ただ無秩序に書かれ分散していると辛 いという話なので、チームでコーディング規約を定めることが大事 • (せめて最低限、Constructのidの命名規則だけでも決めておけると良い)

Slide 27

Slide 27 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG スタック分割の基準 この分割方法は下記を参考にしています https://d1.awsstatic.com/webinars/jp/pdf/services/20200826_AWS-BlackBelt_AWS-CloudFormation.pdf

Slide 28

Slide 28 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG ディレクトリ構成 • スタック分割時のレイヤーを元にディレク トリを分割 • その下にリソースごとのディレクトリ及び constructs.tsを作成し、そこにConstruct を宣言する • 環境ごとにリソースのパラメータ等を変更 する場合は、個別にディレクトリを切る • constructs.tsをstack.tsから参照し、 stack.tsがbin以下で呼ばれる形 • スタックは環境別に作られる(Stack- production、Stack-staging、など) ECSアプリケーションレイヤー ネットワークレイヤー 環境ごとの差異を吸収する どの環境でも同じ手続きを踏む

Slide 29

Slide 29 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG 参照の仕組み • クロススタック参照を用いて、レイヤーごとの依存関係に従い手動で参照 ‣ 例えばネットワークレイヤーで作成したHostedZoneのidをcdk.CfnOutputでエクスポート、ア プリケーションレイヤーなどのHostedZoneを必要とするレイヤーではcdk.Fn.importValueを使 いインポートする • なぜCDKの機能を用いてプログラミング的に参照しないのか ‣ https://github.com/aws/aws-cdk/issues/3414 ‣ 上記とは別に、例えばFugaStackがHogeStackで作成されたhostedZoneをインポートして hostedZone.addXXXというようにリソースを追加更新した場合、この処理をFugaStackで記述 したにもかかわらず、HogeStackの管理下にその追加分のリソースが置かれるといった現象が 発生してしまう プログラミング的な参照

Slide 30

Slide 30 text

CDKで運用する上での注意点

Slide 31

Slide 31 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG CFnでScheduled Tasksを管理する場合 CFnの管理下に置かれるAWS::Events::Ruleの数が多くなってくると、 APIのRate exceededに接触する • あるスタックにてScheduled Tasksをコード管理している場合 • CFnは互いに依存関係のないリソースに関しては、並列作成&更新を試みる関 係上、AWS::Events::Ruleが同時更新されると、DescribeClustersのAPI制限 に接触する可能性がある • 対策として、DenpendsOnで依存関係を定義し、同時更新数を制御する (CDKになるので正確にはaddDependency)

Slide 32

Slide 32 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG CDKでScheduled Tasksを管理する場合 Scheduled Tasksのイメージ更新と実行時のタイミングが重なった際に is not authorizedになりRunTaskが失敗してしまう • 同じくあるスタックにてScheduled Tasksをコード管理している場合 • Scheduled TasksがECSとCloudWatch Eventsの2サービスに跨っている影響 もあり、イメージ更新のデプロイで変更される対象が2リソースとなり、片方 が更新済みで片方が更新待ちのタイミングでcronトリガーされるとRunTask が失敗してしまう • 対策として、更新される2リソースのうち(RuleとPolicy)、Policy側のイメ ージタグを*に変更し、イメージ更新時に片方の更新が走らないようにする

Slide 33

Slide 33 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG CDKデプロイの注意点 cdk deployのデプロイを並列化させる際、一つ前の状態に巻き戻ってし まう可能性がある • CDKのコードをgit管理しCI/CDを組んでいる場合、cdk deployが連続することがある • CDKではUPDATE_IN_PROGRESSなスタックとの差分チェックをUPDATE後の状態で行ってく れるため、cdk deployを並列化することが可能 • その際、順序が入れ替わらないようcommitのタイムスタンプなどをDynamoDB等で要管理 • ただ、それでも--allを使用していると、全スタックに一度しかタイムスタンプのチェックが 走らないことになるので、巻き戻ってしまう可能性がある • 対策として、--exclusivelyでスタックごとにタイムスタンプを管理する(それ以外の手段と して最も手っ取り早いのは完全な排他制御だが、モノレポ度合いで速度低下)

Slide 34

Slide 34 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG CFnでSecrets Managerを取り扱う場合 原因が判明した際に頭を抱えるものです ECSでSecrets Managerを参照しようとすると、unable to pull secrets or registry authが発生しコンテナが起動しない • 一見するとただ権限がないだけに見える • だがどう見ても権限は足りている(のにエラーは消えず) • Secrets ManagerのARNの末尾にハイフン+6文字の識別子が追加されるが、もしシ ークレット名の末尾がハイフン+6文字の文字列(-secret、など)だった場合、そ れを完全なARNとみなしてしまい、結果エラーに繋がっていた • そのため、-secretsなど6文字にしなければ解決(実装&調査&解決されていたチー ムメンバーに圧倒的感謝)

Slide 35

Slide 35 text

ツールの紹介

Slide 36

Slide 36 text

&RS\ULJKW&PL[L,QF$OOULJKWVUHVHUYHG ツールの紹介 インタラクティブにECS API(run-task, etc)を呼び出したり、ECSとロー カル間でファイルをコピーしたり、 ログを見たりするツールを作りました https://github.com/yukiarrr/ecsk • 今回のような自動化の仕組みを作る際のデバッグ等で使用 • runには-itや--rm相当の機能を用意 • これ系には珍しいcpも実装(双方向にファイル転送可) • 必要な情報は対話形式で選択肢が表示されるため、Webコンソールで確認すると いった作業が発生しない • 直近ではタスクのフィルタリング対象にIPアドレスを追加