Posts for: #Cdk

副反応終わり

0時に寝て7時半に起きた。久しぶりによく寝た。ワクチン接種の副反応はおさまり体調は回復した。昼間は UI 改善やバグ修正をしていた。

インフラ勉強会

引き継ぎを兼ねて cdk の使い方、簡単な本番環境向けのデプロイ作業を共有した。wiki に一通りの説明は書いてあって、実際に cdk アプリケーションのソースや設定をみたり、cloud formation のドリフトを検出して解消するのを実践したりしてた。そんな中、別の不具合もみつけた。rds のデータベースのバージョン情報が次のように差分として出力される。

Resources
[~] AWS::RDS::DBInstance GodPostgreSQL/Instance1 GodPostgreSQLInstance1857D2683
 └─ [-] EngineVersion
     └─ 13.6

少し前にたまたま cdk のバージョンを 2.44.0 にアップグレードしてあったのだけど、この差分は 2.44.0 以降で発生する。次の issue の説明によると、メジャーバージョンをまたぐ rds のアップグレードを cdk で実行すると、Stack が回復不能な状態になるのでそのワークアラウンドとしてバージョン情報をみえなくしてしまって、誤って cdk で rds のアップグレードできないようにしているとのこと。この差分はとくに気にする必要なく、一度デプロイしてしまえば次回からは出力されなくなる。インフラにも影響は与えないとのこと。

過去にマイナーバージョンアップは試したことがあって、やはりエラーにはなるのだけど、実際のインフラはアップグレードされて、それで運用上は問題なかったようにみえる。但し、cdk 正しく rds のアップグレードを扱えないというのは確かであるようにみえる。

メンテンナンス切り替えの設計

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

waf のカスタムレスポンス

メンテンナンス時にエンドユーザーがアプリケーションにアクセスできないようにしたい。いろんなやり方があるけど、どういった手段で対応するのが最もシンプルで運用も容易かを少し前から頭を悩ませていた。

当初は cloudfront のディストリビューションをアプリケーションの assets とメンテナンスページの html の2つを作って、cdk のデプロイで切り替えできないかと考えた。cloudfront はドメイン名と密接に紐付いていて、同じドメイン名を2つのディストリビューションに設定することはできないようにみえる。あらかじめそれぞれのディストリビューションを2つ作っておいて route53 で必要に応じて向き先を切り替えるといった構成はできなかった。その次に waf について調べていたら waf がルールでカスタムレスポンスを返すことができることに気付いた。

ちょうどいまエンドユーザーからのアクセスは ipSet で許可し、それ以外のネットワークからのアクセスをブロックしていた。そのルールを更新して、メンテナンス時はエンドユーザーからのリクエストのみをブロックに変更してカスタムレスポンスを返せることに気付いた。waf を管理するスタックなら cdk デプロイで1分ぐらいで更新できた。このやり方のよいところの1つに開発者の ip アドレスを許可することで開発者だけはアクセスできるようにもできる。あと具体的に cdk でカスタムレスポンスをどう実装するかのドキュメントがわかりにくい。サンプルとしてはこんな感じ。

const acl = new wafv2.CfnWebACL(this, 'MyAcl', {
  defaultAction: { block: {} },
  name: 'my-acl',
  scope: 'CLOUDFRONT',
  customResponseBodies: {
    maintenanceInProgress: {
      content: '<html lang="en">Maintenance in progress</html>',
      contentType: 'TEXT_HTML',
    },
  },
  rules: [{
    action: { 
      block: {
        customResponse: {
          responseCode: 503,
          customResponseBodyKey: 'maintenanceInProgress',
        },
      },
    },
    statement: {
      ipSetReferenceStatement: {
        arn: ipSet.attrArn,
      },
    }
  }],
});

lambda から cronjob へ

0時に6時に起きた。今日は晴れたので自転車通勤。

優雅にドキュメントを書きながら障害対応

昨日は凸凹しながら乗り切ってサービスイン2日目。今日から外部システムとの連携なども絡んでくる。昨日の今日なんで何か起こるだろうと思いつつ、暇だったらドキュメント書くタスクがいくつも溜まっているのでそれを片付けるかと業務を始めた。私ぐらいの人間になると、いつ凸凹が発生してもよいように、この日のために取っておいたようなドキュメントタスクがいくつも溜まっている。午前中に私の出番はなく、優雅にドキュメントの1つを完成させた。

以前から定期実行やバッチ処理は lambda 関数をトリガーに作られていた。それらを serverless framework から cdk へ移行した んだけど、その後にバッチ処理を k8s の cronjob で実装した 。この cronjob が思いの外、うまくいって、私がフルスクラッチで cli を作っているのだから、私からみてさいきょうのばっちしょりの土台を実装している。定期実行もすべて cronjob でやればいいやんと気付いて、過去に lambda 関数 (cdk/python) で実装したものをすべて移行することに決めた。昨日の流れからわかるように過去に作成済みの一連の lambda 関数はまだ本番環境にデプロイされていない。m1 chip macbook 問題 で同僚のマシンからデプロイできないという不運もあったんだけど、もうデプロイしなくていいよ、すべて cronjob で置き換えるからと伝えて2つ移行した。あと半日もあれば完了できそうな見通し。

lambda 関数から cronjob への移行作業をしていると、本番環境でのバッチ処理の一部のロジックが誤っているとわかってそれを修正したり、当然のようにテスト環境のデータも誤っているので正しいかどうかは本番環境にデプロイするしかないみたいな凸凹した状況を横切りながら本日の作業を終えた。いくつか残課題は残っているものの、明日中には平常業務に戻れるぐらいの状況にはなりつつあるのかもしれない。サービスイン2日目を終えて致命的な問題は起こっていないようにみえる。ひとまずはよかったという感じ。

サービスインは突然に

1時に寝て6時に起きた。暑くてあまり眠れない。今朝も雨降りで徒歩通勤。

cdk の ECS サービスに紐づくセキュリティグループの設定

明日がサービスイン初日だと思っていたら、私の勘違いで今日だった。私が直近でやっている作業はアプリケーションの直接的な機能ではなく、インフラやバッチ処理などの間接的な機能を作っているわけだけど、それでも1日調整を間違えていて、あれーって感じでサービスインが始まった。とはいえ、私は本番環境にアクセスできなければ、ログすらもみれないので同僚ががんばっているのを傍から応援しつつ、平常通りタスクをこなしていくだけのはずであった。

のほほんと通常通りのタスクをやっていたら、本番環境の ecs サービスと通信できないという連絡がくる。私が前任者から引き継いで構築したインフラなので何だろう?と調査していて、本番環境でセキュリティグループの設定が漏れていることがわかった。これはわかりにくい問題で cdk の FargateService で ecs サービスを構築している。このプロパティは securityGroups のパラメーターをもっている。このパラメーターを指定しない場合、新規にセキュリティグループそのものは作成してくれるけれど、その ecs サービスへ通信するポートへのインバウンドルールは作ってくれない。

securityGroups?

Type: ISecurityGroup[] (optional, default: A new security group is created.)

The security groups to associate with the service. If you do not specify a security group, a new security group is created.

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs.FargateService.html#securitygroups

テスト環境はセキュリティグループに対して、インバウンドルールを管理画面から手動で追加していたために疎通できていた。セキュリティグループは ecs サービスに紐付いているものだから、ecs サービスを再作成しない限りはインバウンドルールが消えることもなくてこの作業漏れに気付けなかったという落ちだった。さらに、そのセキュリティグループのインバウンドルールを設定したのは私ではない。その説明欄に次のコメントが書かれていた。

atode-kesu

今すぐ消してやろうかという気持ちを抑えつつ、cdk でインバウンドルールを設定したセキュリティグループを紐付けるようにして解決した。疎通ができないと、ロードバランサーのヘルスチェックが通らず、ecs のタスクが延々と再起動を繰り返すというわかりにくい障害となっていた。1時間ぐらい唸っていた。

未検証の本番環境

前節で障害の原因自体はわかりにくいものだが、なぜサービスインの初日にこんなことが起こるのだろうか?という当然の疑問。そう。これまでこのインフラの本番環境は一切検証されていなかった。4月から5月にかけて構築されたインフラだった。この後にデータを格納するための s3 bucket がない、一部の設定はテスト環境の設定しかない、アプリケーションのコード中にテスト環境の設定がハードコードされているとか。追加であちこち直してデプロイしていた。私は本番環境に一切アクセスできないので過去にこれらの検証をすることはできなかったわけではあるけど、いろいろ思うところはあるなぁと感慨に浸っていた。

m1 chip macbook と cdk の追加調査

0時に寝て7時に起きた。昨日はずっと寝てて、今朝は雨降りで徒歩通勤。週始めからしんどい。

デバッグ調査を断念

以前 m1 chip macbook で aws-lambda-python-alpha のデプロイができない ことについて書いた。同僚がそのためにデプロイできないと運用上の不都合があるので調査してみることにした。ワークアラウンドの1つとして、ビルドに使う Docker イメージを任意のものに置き換える仕組みを使えば、arm64 アーキテクチャでビルド処理のプロセス自体は通ることを確認していた。しかし、その後の python distribution が生成されていなかった。同僚にデバッグを手伝ってもらってログをみていると、python distribution をバンドルする処理のログが出ていない。

おそらく docker イメージのビルド処理ではなく、aws-cdk の core 側の bundling のところに原因がありそうに思える。core のライブラリをみると、たしかにいくつか条件次第で bundling をスキップする実装はあったし、ログが出力されていないことからも意図したステップが実行されていないことだけはわかった。その周辺から当たりをつけて aws-cdk の issues なども検索してみたけど、それっぽい issue をみつけることはできなかった。何よりも私がもっていないマシン環境の、様々な環境設定を調べることもできず、aws-cdk の core をデバッグするのも大変かなと午前中いっぱい調べて断念することに決めた。もしかしたら同僚のホスト環境に特化した問題が発生している可能性もある。

アプリケーションレベルで再現できた問題を解決できないというのは情けないけど、自分でデバッグできないものは仕方ないかと諦めることにした。本当に悔しいけれど。

m1 chip macbook と cdk/aws-lambda は相性が悪い

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

m1 chip macbook で aws-lambda-python-alpha のデプロイができない

少し前に aws lambda の管理を serverless framework から cdk 移行した 。lambda 関数は python スクリプトで実装されているので @aws-cdk/aws-lambda-python-alpha を使っている。このライブラリでは python distribution を作るときの python インタープリターをローカルのものではなく docker イメージを使って管理しているようにみえる。私の環境 (linux, x86_64) では何の問題もなかったのだけど、同僚が m1 chip macbook を使っていて、そのマシンからだと docker イメージを使ったビルド処理でエラーが発生する。それは既知の問題で次の issue で報告されている。

このワークアラウンドの1つとして Custom Bundling の仕組みがある。任意の Dockerfile を指定することで任意の Docker イメージやプラットフォーム向けにビルド用の python インタープリターを設定できる。そうしたらビルド処理そのものは通るようになったけど、python distribution (python の依存関係を含めたスクリプト群) が asset として生成されない。この現象自体も cdk でよくある issue として報告されていて cdk.out を削除して再実行したら直ったという報告もいくつかあるものの、同僚のマシンではそれでは解決しなかった。

私が m1 chip macbook をもっていないので cdk のコードを修正して push して同僚に git pull して実行してもらうみたいな作業になっている。このデバッグはなかなか大変。

cdk で既存の eks クラスターを管理すべきか

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

cdk から既存の eks クラスターを制御する

1ヶ月ほど前に検証していた cdk による eks クラスターの helm 管理 を再検証した。kubectlRoleArn にどういった権限をもつ iam role を設定したらよいかがよくわからなくて苦労していた。最終的にそれが理解できて helm 管理もできるようになったのでまとめておく。

kubectlRoleArn - the ARN of an IAM role mapped to the system:masters RBAC role. If the cluster you are importing was created using the AWS CDK, the CloudFormation stack has an output that includes an IAM role that can be used. Otherwise, you can create an IAM role and map it to system:masters manually. The trust policy of this role should include the the arn:aws::iam::${accountId}:root principal in order to allow the execution role of the kubectl resource to assume it.

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_eks-readme.html#using-existing-clusters

aws-auth の configmap に設定されている system:masters に所属している iam role を調べる。

$ kubectl describe configmap -n kube-system aws-auth

この iam role には sts:AssumeRole 権限を与え、trust relationships に arn:aws:iam::${accountId}:root といった root ユーザーを含める必要がある。この root ユーザーの設定がないと次のような権限エラーが発生する。この権限エラーの修正方法がわからなくて苦労していた。結果的には関係なかった kubectlLambdaRole の設定も必要なんじゃないかと検証していたのが前回の作業の中心だった。

An error occurred (AccessDenied) when calling the AssumeRole operation:
  User: arn:aws:sts::${accountId}:assumed-role/xxx is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::${accountId}:role/myrole
Error: Kubernetes cluster unreachable: Get "https://xxx.gr7.ap-northeast-1.eks.amazonaws.com/version

ようやく cdk で既存の eks クラスターをインポートして helm パッケージを管理できるようになった。とはいえ、cdk/cf の実行時間を測ってみると次のようになった。

  • helm パッケージの新規インストール: 約5分
  • helm パッケージのアンインストール: 約25分

これは cdk が helm パッケージを管理するための lambda 環境を構築/削除するときの時間になる。cdk はアプリケーションの stack から nested stack を作成して、そこに lambda や iam role などをまとめて作成する。一度作成してしまえば、バージョンのアップグレードは30秒ほどで完了した。

この振る舞いを検証した上で、cdk で eks クラスターをインポートする管理はやめようとチームに提案した。正しい設定を作ってしまえば運用は楽になると言える一面もあるが、新規に helm パッケージを追加するときのちょっとした typo や設定ミスなどがあると、1回の試行に30分かかる。私がこの検証に1週間以上のデバッグ時間を割いている理由がそれに相当する。お手伝い先の運用ではテスト/本番環境ともにローカルから接続できる状態なので helm コマンドを直接実行した方が遥かに管理コストや保守コストを下げると言える。cdk を使って嬉しいことは helm コマンドでわかるバージョン情報と設定内容が cdk のコードとして管理されているぐらいでしかない。ドキュメントと helm コマンドで管理する方が現状ではよいだろうと私は結論付けた。同じような理由で eks クラスターも cdk ではなく eksctl コマンドで管理されている。

1週間以上の労力と時間を費やしてやらない方がよいとわかったという、一般的には失敗と呼ばれる作業に終わったわけだけど、eks/cdk の勉強にはなった。

コンテンツは狙ってバズらない

1時に寝て8時に起きた。昨晩はたくさん話してテンション上がって眠れなくてバテ気味。

serverless framework から cdk 移行の背景

木曜日はスプリントレビューがある。ステークホルダーが出席する唯一のスプリントイベントなので、大半はステークホルダーとの情報共有や質疑応答、プロジェクトにとっての大きい括りでの現状の共有になる。大半はお手伝い先の社員さん同士のやり取りで、協力会社の開発者は詳細が必要になったときだけ説明するといったイベント。前スプリントで 既存の lambda 関数を serverless framework から cdk へ移行 した。プロジェクトメンバーではないのだけど、業務のリーダークラスの方が cdk に関心をもって質問してくれた。聞くところによると、他プロジェクトでも cdk を使うようになってきているらしく、なぜいま移行しているのか?という質問だった。普段インフラ作業を孤独にやっているのもあって、業務の人が関心を示してくれたのが嬉しくて、変なスイッチが入っていろいろ説明した。serverless framework は2015年10月リリース、cdk は2018年7月リリースで、歴史的に serverless framework が普及して、その後に cdk が台頭してきたので実績や機能性から serverless framework が広く利用されている。但し、いま aws のインフラ管理をするのであれば、cdk は serverless framework 以上の管理ができることから cdk に一元管理した方がツールの学習コストを減らし、保守コストを下げることにつながるといった話しを丁寧に説明した。相手がそこまでの回答を求めていたかはわからないけど、関心を示してくれたことそのもので私が救われた気がした。

terapyon channel のコンテンツ公開

昨日の今日で公開された。ほとんど無編集だったのかな。web サイトのコンテンツ紹介の内容も手伝って夕方には公開された。

私の中ではいろんな話題を楽しく話せて充実感があった。一部にだけ関心をもつ人にも聞きたいところだけ聞けていいんじゃないかと思えた。基本的に自分の podcast を聞き直すことはないんだけど、今回は自分が充実感をもって話せたせいか、2回ほど聞き直しておもしろい話だなぁと自画自賛してた (笑) 。自分がよいものは周りもそう思うはずだと、ついつい先入観で考えがちだけど、全然そうじゃなかった。全くいいねがつかなかったので周りからみたら私の自己満足のコンテンツでしかなかった (笑) 。コンテンツあるあるだけど、狙ってコンテンツをバズらせるのは難しい。ブログでもがんばって書いた記事が全く読まれないことはあるし、手間暇かけずに軽く書いた記事がめっちゃバズったこともある。コンテンツがバズるかどうかは、最初にみた人たちが拡散するかにも依ってくる。いずれにしても、他者が関心をもつようなコンテンツかどうかは本人ではわからないというのは確かかな。

今期から会社のマーケティングも少しずつやっていく予定になる。自分がよいと思ったコンテンツに全く関心を示されないことは今後も多々あるだろう。作ったコンテンツを多くの人に見聞きしてもらおうと思ったらやることは1つだけで、当たるまでひたすら作り続けて、いつか当たるのを気長に待つという戦略しか、いまのところ思いつかない。

aws sns を介した pubsub

0時に寝て4時半に起きた。21時頃からオフィスで作業してたらそのまま寝落ちした。朝の掃除機をかける音で目覚めて、始業までワーケーションのふりかえりをしていた。

lambda 関数と sns の連携

定期実行で数百程度の web api 呼び出しを行いたい。これまで定期実行を lambda 関数で実装してきたが、負荷分散を考慮して作ってほしいと言われたので sns を使ってメッセージ分割した上で1つ1つの lambda 関数は sns のメッセージを受け取って実行されるように構成した。lambda 関数を使って pubsub するときは sns を使えばよいらしい。sns はあまり使ったことがないので私自身ノウハウをもっていないし、運用の勘所もよくわかっていない。ドキュメントをいくつか読みながら cdk のコードを書いてた。producer と consumer を lambda 関数で作成し、sns を介してリクエストの負荷分散を図る。lambda 関数は同時並行数を設定できるのでこれがスロットル制限のような役割にもなる。インフラやスクリプトのコードはすぐに実装できたが、lambda 関数の destroy にやたら時間がかかる。権限周りでいくつか設定を試すために destroy しながら検証をしたかった。destroy するのに20分はかかるので deploy や設定の手作業などをやっていると1つの設定を試すのに平気で1時間ぐらいかかってしまう。3回ぐらいやって疲れて検証作業はやや妥協した。

aws サービスと ipv6

0時に寝て6時頃に起きて7時半に起きた。

cloudfront と waf と s3 と ipv6 と

aws サービスについて ipv6 について調べると、少しずつ対応してきた経緯があって、ipv4 と ipv6 の両対応のことを aws は dualstack と読んでいる。エンドポイントのサブドメインに dualstack があれば、ipv6 対応していると考えてよいと思う。リモートワークしている開発者がテスト環境に接続するときに、その開発者の IP アドレスを登録する aws lambda 関数がある。その lambda 関数にリクスとすると、リクエストしたクライアントの IP アドレスを waf の IP sets に登録することで、パケットフィルタリングのルールを更新している。たまたま、そのインフラを cdk 移行したところ、ある開発者は ipv6 で登録しようとしてエラーになるということがわかった。移行のときに api gateway を使わずに直接 lambda の fuction url を使うようにしたところ、lambda は ipv6 対応しているのでそのまま ipv6 の IP アドレスを登録しようとして waf の設定が ipv4 しか対応していなかったためにエラーになっていた。

cloudfront, waf, s3 の ipv6 対応は2016年頃に対応していて、この3つのサービスに対して一緒にブログ記事を書いているのは、これらのサービスを一緒に使うことを想定しているとみなすべきだろう。

cdk のコードで cloudfront, waf は ipv6 対応したのだけど、cloudfront のオリジンに当たる s3 の ipv6 対応を cdk からどうやって設定していいかわからなかった。aws-cloudfront-originsS3Origin のコードを読むと、どうも s3 の ipv6 対応のエンドポイント (dualstack) を考慮して制御しているようにはみえない。バグではないけど、設定方法がわからないので feature request として issue 登録してみた。aws-cdk に issue 登録するのは初めて。余談だけど、ISSUE_TEMPLATE もよく出来ていて、これを参考にして自分たちのリポジトリにも取り入れてもよさそう。

aws-lambda-python-alpha を使っておけばいい

0時に寝て6時に起きて7時半に起きた。なんかバテバテ。

aws-lambda-python-alpha

cdk 標準の lambda モジュールで python スクリプトをデプロイすることはもちろんできるが、その python スクリプト内でサードパーティのライブラリを使いたい場合、layer という機能を使って複数の lambda 関数で再利用するというのがもともとのやり方っぽい。とはいえ、サードパーティの依存ライブラリも python スクリプトによって使うものが違ったりするので lambda 関数単位に管理したいという場合もある。その要件を考慮した場合、python のパッケージングを lambda 関数のデプロイ時に再利用できるのが望ましくて、そのためのモジュールが aws-lambda-python-alpha になる。説明が長い。

JJUG CCC 2022 Spring のタイムテーブル

タイムテーブルが公開された。

私の前の発表が大橋さんという、一緒のチームでお仕事している方で github actions で運用している開発のワークフロー全般を改善したという話しをされる。その大半は私が設計して既存のワークフローを作り直したものなので応援している。改善の前後におけるワークフローの実行時間の比較や資料のレビューなども手伝っていて、私が主導してやったんだから内容についての責任は私にあると考えていろいろサポートしていた。おそらく github actions つながりで連続しているのかなと推測する。当初はお互いに送客すればいいかなと考えていたけど、普通のセッションの規定が厳しくて、スポンサーじゃなければ会社や製品の宣伝はダメという話しなので送客は断念した。