Posts for: #Infrastructure

cloudfront のキャッシュ設定

1時に寝て5時に起きた。

フロントエンドのインフラ作業

これまでバックエンドのインフラ作業をしてきたが、そちらの移行作業は完了した。フロントエンドのインフラも積み残しが多々あるのでこの機にリファクタリングする。差分が出ないことを確認して cf のテンプレートのドリフト結果を解消したものをデプロイしたらフロントエンドが壊れた。具体的には xhr の web api 呼び出しに対して認可エラーが返るようになった。数時間ほどの調査の結果、cloudfront の distribution 設定のビヘイビアのキャッシュ設定で web api 呼び出しのときに authorization ヘッダーを転送するための設定が漏れていることがわかった。cdk のコードにはそんな設定がどこにもなく差分も表示されないことから、フロントエンドの cdk コードも全く保守されていないことがわかった。デプロイするときに前回のデプロイが半年前だったのでなんか悪い予感はしたんよね。インフラ担当者の怠慢が度を越し過ぎてもう何が起きても私は驚かないけど。設定に漏れがある前提でフロントエンドのインフラをリファクタリングしていかないといけない。

cdk/cf の Stack とライフサイクル

0時に寝て5時に起きた。

インフラ変更の本番作業

先週やっていた様々なインフラ構築の改善を本番環境に適用する。cf の changeset は23とかになっていた気がする。大きな括りで次の3つの移行作業をした。

  • rds をスタックから切り離す
  • cdk の v1 から v2 へのアップグレード
  • ポリシーとセキュリティグループのドリフト解消

私は本番環境へのアクセス権限をもっていないので社員さんの作業内容を伝えてやってもらう。cf のテンプレートの更新やドリフトの解消など、テスト環境で10時間以上は費やした検証結果が功を奏して、本番作業は想定外の事象も発生せず1.5時間で完了した。cdk のコードも意図した設定になるように修正済みだし、なにも問題は発生しない。cdk のコードを書くときは cf のイベントログやドリフト結果と実際のインフラの振る舞いを確認するといった検証には時間がかかるが、それができてしまえば本番環境の構築はうまくできる。それがわかっているインフラ担当者がいなくなると、また1から担当者が検証する必要があって保守は大変かもしれないけど。

同じ Stack でどういったリソースを管理するかというライフサイクルは難しい問題かもしれない。今回 rds を削除したのは、なんらかの理由で手動運用で rds を変更することがあって、アプリケーション Stack から外してしまった方が保守しやすいのではないかという意見が出たため。それが正しいかどうかはわからないが、一度作ったリソースを削除しないもの (vpc や s3 bucket など) をアプリケーション Stack で管理すると、再作成できなくて面倒くさいことがある。というのは、再作成は新規に作成 → 古いリソースを削除の順番で処理されるため、一意な名前をもつリソースは基本的に再作成できない。

壊れた cf スタックのリストアと cdk の再同期

2時に寝て6時半に起きた。インフラエンジニアになったのでみんなが作業していない時間にインフラの保守作業をするようにしている。昼はアプリケーションエンジニア、夜はインフラエンジニアみたいな生活になっていてしんどい。

壊れた cf スタックの更新

テスト環境の cf スタックを手動で更新して壊れているのを cdk で管理できるように直した。壊れていたのは次の3つ。

  • rds をスナップショットからリストアしたので cf が管理している rds リソースが存在しない
  • iam の acl 設定が異なる
  • セキュリティグループのインバウンドルールが異なる

aws 的にもそういった状況は認識していて cdk で同期できなくなった cf スタックを更新する手順を提供している。

ざっくり手順をまとめると次になる。

  1. 対象のリソースに DeletetionPolicy=Retain にセットする
  2. テンプレートからリソースを削除して、スタックの更新を実行する
  3. テンプレート内のリソースの実際の状態を describe して、スタック内に既存のリソースをインポートする

リソースの設定ぐらいなら既存のリソースからインポートしなくても cf のテンプレートを直接書き換えたものをアップロードしてスタックを更新するのでも大丈夫だったりする。しかし、cdk もそのテンプレートにあうように修正しないといけないため、cdk のコードとテンプレートのコードの両方をチェックしながら検証する必要がある。cdk でリソース管理ができるようになったからといって、それが変更前の既存のリソースの設定と同じかどうかは人間が目でみて検証しないといけない。これがあちこちで参照されているリソースだと追跡するのが面倒くさいといった手間暇がかかる。

cdk がよいものかどうか、私はまだ判断がつかないけど、cf を抽象化して便利になっているところは認めるものの、cf のスタックが壊れたときのトラブルシューティングが必要以上に複雑で厄介というのも事実ではある。一方で壊れた cf スタックを5時間ぐらいかけて直したのではまりポイントはいくつかも学ぶことができた。しんどかったけど。例えば、あるセキュリティグループのインバウンドルールに別のセキュリティグループを関連付けるとき、1つの設定ではうまくいかなくて次の2つの設定を追加した。これが適切かどうかわからないが、この設定で cdk でデプロイしたスタックの環境と既存リソースとの環境が整合した状態 (ドリフトが解消される) になった。こういうのが cdk の抽象化による訳のわからないところの1つ。

otherSecurityGroup.addIngressRule(
  ec2.SecurityGroup.fromSecurityGroupId(this, 'my security group', mySgId),
  ec2.Port.tcp(80),
  "my inboud rule",
)
otherSecurityGroup.addIngressRule(
  ec2.Peer.securityGroupId(mySgId),
  ec2.Port.tcp(80),
  "my inboud rule",
)

cdk のメジャーバージョンのマイグレーション

0時に寝て5時に起きた。開発者にインフラ変更の影響を出さないように6時半からインフラのお仕事してた。

cdk v1 と v2 の違い

AWS CDK Versions には v1 と v2 の2つがある。新規で作るものは v2 を選択すればよいけど、既存のスタックが v1 だとマイグレーションが必要になる。cdk は bootstrap したときに CDKToolkit というスタックを生成する。cdk をアップグレードするというのはこのスタックの設定も更新する必要がある。デフォルト設定をそのまま使っていればマイグレーションはそんなに難しくはないはずだけど、設定をカスタマイズしていたりするといくつかパラメーターを調整したりしなかったりしてややこしいかもしれない。

また v2 は v1 の experimental な機能は移行されていないため、v1 のライブラリを直接使うか、自前でその機能を実装するといったことも必要になる可能性がある。

例えば、v1 の apigwv2.VpcLink というメソッドは experimental で v2 に移行されていないため、v2 に移行されている stable な CfnVpcLink という機能を使って次のように実装した。これは v1 の cdk の実装をみて同じように実装しただけ。

-    const apiGwVpcLink = new apigwv2.VpcLink(this, 'ApiGwVpcLink', {
-      vpc: vpc,
-      vpcLinkName: 'my-vpc-link',
-      securityGroups: [mySecurityGroup]
+    const apiGwVpcLink = new  apigwv2.CfnVpcLink(this, 'ApiGwVpcLink', {
+     name: 'my-vpc-link',
+     subnetIds: vpc.privateSubnets.map(sb => sb.subnetId),
+     securityGroupIds: [mySecurityGroup.securityGroupId]

ecs の draining とタスクの停止時間

0時に寝て4時に起きた。なんか起きてから sns のタイムラインを眺めてた。6時半にはオフィスについて cdk のコードを読み始めた。

ecs の draining に時間がかかる?

cdk でインフラのデプロイをしていて、ecs のタスクの置き換えにやたら時間がかかっているのに気付いた。調べてみると、aws のドキュメントがすぐにヒットした。デフォルトでは停止するまでに5分ぐらいかかってしまうようだけど、それを調整したかったらいくつかパラメーターがある。

ecs サービスの deployment configuration

  • minimumHealthyPercent: 同時に停止できるタスクの割合設定
  • maximumPercent: draining されるタスクが停止するまで置き換えるタスクを開始するかどうかの設定?

ロードバランサーの deregistration delay

  • deregistrationDelay: elb(nlb) が登録解除処理が完了するまでに待つ時間。タスクが draining の状態になってこの時間が過ぎた後に登録解除して target が未使用になる

ecs タスク定義の stop timeout

  • stopTimeout: コンテナーが正常終了しないときに ecs が強制的にプロセスを kill するまでの待ち時間

それぞれのインフラの状況にあわせて適切なパラメーターを変更すればよい。私が管理しているのは次の2つを変更した。

  • maximumPercent: 100 -> 200 (%)
  • deregistrationDelay: 300 -> 30 (秒)

これで18分ほどかかっていたデプロイ時間を8分ぐらいまで短縮できた。テスト環境の設定なので多少のエラーが発生したとしても速い方がよい。

再びのインフラエンジニア

0時に寝て7時に起きた。

インフラタスクに専念

本当はインフラ担当者が別途いるのだけど、多忙過ぎて、インフラタスクが1ヶ月近く遅延していて、プロジェクト内で合意を得て私がすべて巻き取ることにした。内容の如何に依らず、その一切合切をすべて巻き取ると宣言した。過去に働いた会社でも他の担当者ができなかった業務を後からリカバリするのはよくやってたのでそれ自体は構わない。ただ他人のタスクを肩代わりしても評価されないことも多くて、もともと私のタスクではないから誰がやったかなんか忘れてしまうんよね。私もとくにアピールしないからそう認識されても構わないのだけど、そういう業務が増えてくるとその職場を辞めるきっかけにもなってた。

インフラ担当者や他の社員さんにヒアリングしながら現時点でも十数個のタスクがある。過去のインフラの負債も含めて2-3週間ぐらい、私が集中的にやればすべて片がつくのではないかと考えている。今日たまたまスクラムのリファインメントやってて、業務の人から他の機能開発が遅れているのに2-3週間もインフラ作業に専念するってどういうこと?インフラタスクってインフラ担当者にやってもらえないんですか?と質問を受けて、できるんならその方が望ましいけど、過去の実績からまったく進捗しないのでこちらでやるざるを得ない状況というのを説明した。業務の人からみたらインフラなんか何をやっているかわからないからそんなもんよね。これから2-3週間経って蓄積したインフラタスクをすべて解決した後で少し時間が経つとインフラ担当者が全部やったように外部からはみえてしまうというのを、過去に何度も経験した。