Posts for: #Infrastructure

ログおじさん

23時に寝て3時半に起きて眠れそうになかったからそのまま5時からオフィスで作業してた。

システム構成の検討

コンサルタントから顧客要件のヒアリングを行い、プロダクトを提供するインフラのシステム概要を mermaid で書いた。オンプレとクラウド環境のそれぞれを同じコンテナアプリケーションで動かすための構成を検討した。クラウド環境の一例として aws の構成を考えていて、https と http のプロトコル変換のようなことをするには api gateway を経由しないといけないと考えていたら、alb に証明書を設定して api gateway なくてもいけるとはらさんに教えてもらった。昔からできたそうで、なぜか私が長い間ずっと勘違いしていた。また時間があるときに自分でもやってみようと思う。

Logger の再実装

プロダクトのコアな部分の実装は私がみた方がよいだろうと考えていて、そのうちの1つ Logger の設計がよくなかったので私が作り直した。といっても cybozu-go/log を使った薄いラッパーを設けただけ。チームメンバーからどこでエラーが起きているか追跡しにくいという声があったのでログ出力したところのソースコードの情報を出力しようと考えた。ググればたくさん出てくる。スタックフレームにアクセスする標準パッケージとして runtime を使うとできる。runtime.Caller と runtime.Callers は似て非なる関数のようでファイル名と行番号だけでよければ Caller を使った方がシンプルになると思う。関数名もほしかったら Callers を使ったスタックフレーム自体から取得する必要がある。

func Trace(skip int) (file string, funcName string) {
  pc := make([]uintptr, 15)
  n := runtime.Callers(skip, pc)
  frames := runtime.CallersFrames(pc[:n])
  frame, _ := frames.Next()
  _file := frame.File[strings.Index(frame.File, sourceRepositoryPath)+8:]
  file = fmt.Sprintf("%s:%d", _file, frame.Line)
  return file, frame.Function
}

この情報を cybozu-go/log の map に追加するようなログ関数を提供するようにした。cybozu-go/log は標準の log パッケージに足りないところだけを追加していて、そのシンプルさと拡張性の高さを私は気に入ってよく使っている。私が気に入っているのでもっと有名になってほしい。

前のお手伝いでもログ基盤を含めて Logger を作っていて、またいまも Logger を作り直していて、気付いたら私は Logger やログ出力に一家言あるような、ログおじさんになりつつある。

副反応終わり

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時に寝て3時に起きて6時半に起きた。だいぶよく眠れるようになってきた。

サイトコントローラーの障害

お手伝いしている宿泊業のシステムでトラブルが発生している。厳密には外部サービスになるのだが、複数のオンライン予約サイトの違いを吸収して単一のインターフェースを提供する宿泊業のハブシステムのような存在をサイトコントローラーと呼ぶらしい。週明けから全国旅行支援という新しいGoToトラベルが開始されて、その予約が想定以上のトラフィックになっていてサイトコントローラーがダウンしてしまった。web 開発者向けに例えれば aws の s3 が落ちて大半のサービスに影響が出てなんもできないみたいな状況かな。

全国レベルのニュースになるぐらいの障害規模が大きいらしい。それだけユーザーが多いシステム/事業者なんだろうとは思う。システムと向き合う上で障害が発生するのは仕方ないが、フォールトトレランスは常に意識して設計・運用しないといけないことを、今回の障害を傍からみていて感じた。

java のちょっとした小技

java で1つのリストを複数のリストに分割したいときに List.subList というメソッドがある。複数の値を並行処理するときなど入力を分割するのに便利そう。使い方は次の通り。toIndex を超えると IndexOutOfBoundsException が投げられるのでそこだけ注意かな。

var total = myList.size();
var step = 20;
for (int i = 0; i < total; i += step) {
    var toIndex = i + step;
    if (toIndex > total) {
        toIndex = total;
    }
    var subList = myList.subList(i, toIndex);
    // do something
}

シャンプー

散髪屋さんのマスターからシャンプーを変えた方がいいんじゃないかとアドバイスされた。髪は油分と水分のバランスが大事らしく、シャンプーによって変わることもあるらしい。いつからか記憶にないけど、少なくとも学生時代から私はずっと メリット リンスのいらないシャンプー を使っている。20年ぐらい?こだわりがあるわけでもないし、このシャンプーを使っていて懸念があったことは一度もない。マスターから h&s scalp がよいとお勧めされた。せっかくの機会だから試してみようかなと思う。

勉強会が2つ

0時に寝て6時に起きた。涼しくて体調は抜群。

インフラ引き継ぎ勉強会

先週引き継ぎのためのインフラドキュメントを書いていたものをチームの開発者に共有した。今日は開発者向けの話しなので 5日以上かけた非開発者向けのインフラドキュメント は使わないが、社員さんによると、(私が参加していない社員さんだけの) 別の開発者チャンネルで読まれているとのこと。時間をかけたので多くの人に読んでもらえるに越したことはない。但し、そのフィードバックは私には一切ないので役に立っているのかどうかの判断しようがない。業務委託にそういう外様感を与えるかどうかは組織によって大きく異なる。過去に働いたある会社では自由に勉強会に参加したり他チームのメンバーとも技術の議論をできたりした。私は技術の話題に対しては真摯なので当たり前のように感じていたが、あの会社のあの文化は特別だったのだとそれから別の数社で働いてみて気付いた。

書いたインフラドキュメントに沿って一通り説明して、ほとんど実務をやっていないメンバーではあまりイメージができないと思うので質問もそれほどなかった。次回は実際にどうやってインフラのデプロイをするかの作業をみんなで確認しながら cdk の使い方などの話しをしようと思う。

インフレ勉強会

fin-py の月例のインフレ勉強会に参加した。背景はわかってないが、connpass イベントではない。市場調査や金融の勉強のために最近は毎月出席している。

直前に政府の円買いの為替介入あったのが話題になってた。政府が本気?出せばこんな勢いで5円も動くらしい。為替介入はサプライズが大事らしくて、サプライズという側面ではみんなびっくりしたので効果はあったのかもしれない。

fin-py の中の人もこんな勢いで動くのは珍しいと話していた。日本が為替介入するときは米国にお伺いを立てないといけないらしく、米国がうんと言わないと実弾の為替介入はできないことが多いらしい。中の人によれば、いまも米国は日本の為替介入をよくは思ってないだろうと推測されるので、こんなことをずっと続けられるかどうかは懐疑的だという。さらに世の中のトレンドはドル高なので為替介入しても一時的なもので意味がないという考え方もあるとのこと。今回の介入の意味があったかどうかは今後の為替が安定するかどうかで判断される。日本は世界でトップクラスに外貨準備のドルをもっている国なのも事実なので為替介入の実弾もたくさんあるから今後も介入する可能性はある。

インフラと非開発者

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

インフラのドキュメント作成の続き

昨日はインフラ構成図を主に書いていたが、今日は引き継ぎのために wiki に概要を書いて補足事項なども肉付けしながら、私がやったことや今後運用していく上で知っておくべきことなどをまとめていた。それと同時に backlog の wiki の階層構造なども見直していた。backlog の wiki はタイトルに / を含めることで階層構造を表す。実際の url はエイリアスリンクが使われていて、タイトルとは無関係なので wiki のタイトルを変更することで階層構造が変わる。これはこれでお手軽とも言えるけれど、この階層以下のドキュメントをすべて移動したいといったときは1つずつタイトルを変えないといけないので面倒になる。デイリースクラムのときに来週のインフラ勉強会の時間もスケジュールを抑えてもらった。おそらく1回では終わらないのではないかと推測するが、説明してみて開発者の反応もみながら2回目の有無を決める。

さらに「プロダクトオーナーのためのインフラ入門」というタイトルで別のドキュメントも書き始めた。他の重要な業務がなくて暇だと言ってしまえばそうなのだけど、非開発者向けにクラウドのインフラや自分たちの web システムをどう理解してもらうのがよいか、私自身、試行錯誤で答えをもっているわけではないが、難しいからプロダクトオーナーはインフラを理解しなくてよいというつもりもない。以前からプロダクトオーナーが「自分たちもインフラがどうなっているのかを理解したい」というコメントを何度か聞いていたので筆を取った次第だ。技術的な詳細は理解できないだろうが、いまどきのインフラにおいてどういった概念や考え方が重要なのか、なぜこのような複雑なインフラになってしまっているのか、そうした背景を理解してもらおうと考えている。初めての試みなので、後日やってみて反応をみてふりかえりしようと思う。

インフラと引き継ぎ

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

インフラのドキュメント作成

契約終了まであと1ヶ月半。私が唯一引き継ぎしないといけないものにインフラがある。

引き継ぐ前のインフラは本当にひどい状態だった。手抜き工事のまま放置されたような状態だった。cdk のコードから実際のインフラを構築することはできなくて、コード化されていないところを aws のマネジメントコンソールから手作業で設定していた上、どこを手作業で設定していたかの情報は一切残されていなかった。新しいインフラが追加されるときに cdk のコードと乖離があるからデプロイできなくて、それを前任者は直せなくて私が引き継いだという経緯がある。私が1ヶ月ほどかけて30以上の pr を作ってコードと実際のインフラをほぼ完全に同期させた。その過程で3回ほど (テスト環境ではあるが) 障害も発生させている。デプロイしたら手動設定が消えて壊れてしまう。何が手動設定で何がコード管理なのかの情報が何もないのだからデプロイしてみないと動くかどうかわからないという状況だった。そのため、現在のインフラは私が作り直したと言っても過言ではない。cdk のバージョンも v1 から v2 にアップグレードした。半分以上の cdk のコードは私が書いたと思う。その引き継ぎならびにドキュメント化の作業にようやく着手した。これまで書いてなかったのはインフラを管理しているのは私だけだったのでドキュメントなくても運用上の問題はなく、ドキュメントタスクの優先順位が低かったから。今日は draw.io で aws のシステム構成図を書き上げて、wiki のインフラドキュメントの再構築に着手したところ。

今週中に引き継ぎのドキュメントを書いて、来週ぐらいからインフラ勉強会やって引き継ぎを完了させる予定。

たまには画面作り

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

リファクタリングとインフラ移行

ここ2週間ほどリファクタリングやらインフラ変更やらをしてきて、来週からまた新しい施設がサービスインするので区切りとしてリファクタリングは一旦終わりにする。今日がその集大成となるインフラ移行も含めた本番リリースだった。インフラ移行するときはなにかしら障害が起きる前提で待機しているものの、今日もすんなりと意図した通りに移行できて、してやったりではあるものの、モノ足りなさで拍子抜けしてしまった。また昨日から社内 wiki にも minikube の使い方、k8s cronjob の設計、バッチ処理の設計と実装についてドキュメントなどを書いていた。いままですべて私が1人で担当していたものを他メンバーでも作業できるようにドキュメント化した。近いうちにいなくなるので引き継ぎのドキュメントにもなる。

nuxt で画面作り

ここ最近2種類の web api の機能を作ったのでその管理画面も2つ作る必要がある。私はフロントエンド開発の素人なので他のメンバーが作ってくれないかと声をかけてはいたけど、みんな忙しいようなので私が作ることにした。今週は nuxtjs の新規画面の開発をがんばってみようと思う。既存のソースを読む限りはそんなに複雑ではなさそう。素人が雰囲気で実装しても動くんじゃないかと思っている。ソースコードを読んでいて url 設計はめちゃくちゃだし、一覧画面にはページング機能も実装されていない。素人がソースを読んで基本的な骨子や機能が正しく実装されてないことがわかってしまうのは品質レベルとしてなにかがおかしい。圧倒的低品質と呼ぶのか、こんなことが起こってしまうのはよい開発文化がないせいなのだろうと考えている。

無障害インフラ移行

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

本番環境リリースのサポート

サービスイン のバタバタによって毎週の定例リリースを先週はやらなかった。そのため、今回は2週間分の変更をまとめてリリースすることになった。インフラの変更が複数あったので私も万全の準備をして作業に備えた。

他にもいくつか本番のマスターデータの更新など、箇条書きにした作業項目は15個になった。今回はインフラとアプリケーションが相互に依存する変更を加えているので意図した順番で作業しないといけない。ノードグループ導入による k8s の作業内容は wiki にすべて作業方法を書いていたのでその通りに作業してもらった。メンテナンスモードに切り替える初動もうまくいったので現場の運用担当者が間違って使うこともない。意図した作業内容と順番でうまくリリース作業もできた。メンテナンス時間を2時間もらっていて、宿泊業だとその時間帯が12-14時になる。宿泊客のチェックアウト後、チェックイン前の空き時間だ。メンテナンス後に運用担当者にすぐ確認もしてもらえるし、リリース作業の時間帯としては深夜早朝にしなくてよいのは助かる。私が想定した通りに作業は進み、1時間でリリース作業を終えた。障害も一切なかった。インフラ担当者は想定外の障害に備えて、切り戻しやリスク管理をする。まったく問題なく想定内だったので晴れ晴れしい気持ちになった。こういう気持ちはインフラ担当者にしかわからないと思う。

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

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,
      },
    }
  }],
});

サービスインは突然に

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 をデバッグするのも大変かなと午前中いっぱい調べて断念することに決めた。もしかしたら同僚のホスト環境に特化した問題が発生している可能性もある。

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