年中アイス

いろいろつらつら

aws-sdk-go-v2でassume roleしてAPIを呼ぶ

クロスアカウントを使うことがあったのと、そろそろv2をちゃんと使わないとなということでaws-sdk-go-v2を使ってassume roleする方法を調べました。EC2のDescribeInstancesを呼んでインスタンスリストを出すだけのサンプルを実装しています。コード全体はここに上げてあるのでみてみてください。MFAは無しとstdinからの入力のパターンに対応しています。

github.com

使い方はSDKドキュメントにしっかり書いてあってその通りなんですが、最初からそこは見つけられず、stack overflowでパッケージを把握して見に行きました。

サンプルからの抜粋

cfg, err := config.LoadDefaultConfig(ctx,
    config.WithRegion("ap-northeast-1"),
)
if err != nil {
    log.Fatal(err)
}

// ...中略...

if optRoleArn != "" {
    stsClient := sts.NewFromConfig(cfg)
    var provider *stscreds.AssumeRoleProvider
    if optMFAArn != "" {
        provider = stscreds.NewAssumeRoleProvider(stsClient, optRoleArn, func(o *stscreds.AssumeRoleOptions) {
        o.SerialNumber = aws.String(optMFAArn)
        o.TokenProvider = stscreds.StdinTokenProvider
        })
    } else {
        provider = stscreds.NewAssumeRoleProvider(stsClient, optRoleArn)
    }

    cfg.Credentials = aws.NewCredentialsCache(provider)
}

// 後はEC2など必要なAPIの呼び出し
cli := ec2.NewFromConfig(cfg)
  • aws-sdk-go-v2で必ずやるcfg, err := config.LoadDefaultConfig()
  • stsサービスのクライアントを作り、変身先のRoleARN(arn:aws:iam::<account id>:role/<role-name>)、必要な場合はMFAのARN(arn:aws:iam::<account id>:mfa/<user name>)と今回は標準入力からのMFA codeの受付を担うstscreds.StdinTokenProviderを使ってAssumeRoleProviderを作成
  • cfgにそのAssumeRoleProviderを設定し、その先はassume roleを行わない場合と同じ各種APIの呼び出し方法

ちなみに同じcfgを使っていれば処理中ではMFAは1回目だけ*1聞かれます。サンプルではその確認のために2回呼んでいます。もちろんコマンド起動ごとにはMFAを聞かれます。

他にも、Stack overflowの回答ではコメントアウトしてありましたが、ClientLogModeという設定があるのもわかりました。サンプルではSigningのログを出すオプションを付けています。リクエスト/レスポンスログなども出せるようなのでデバッグに便利そうです。

参考

*1:デフォルトは1時間の有効期限です。

2022年技術系、仕事やったこと

2022年は保守色の強い1年でした。年度はじめからフルリモート勤務(1on1等で月1,2出勤)となり勤務スタイルが大きく変わりました。前から温めていたDB(Aurora)のバージョンアップという重要度と期限がある重大タスクをPJリーダーとして計画通り年末に無事完遂したので、上々の出来だったと思います。アプリケーション的には新規開発を抑えてDBアップグレードをやったんですが、自分はインフラ寄りなので結局新しいことやっててなおかつインフラ的には結構いい経験を積めました。

扱ってるのは引き続きGo、AWS、Terraform、ansibleな感じですが、自分含め生み出してしまった過去の遺物をある程度整理していけました。毎年何やったか忘れてしまうので、サマリだけでも書いておくことに。詳細書けるのはそのうちまとめていきたいところ。

ざっくりサマリ

  • Aurora MySQL v1(MySQL5.6互換)からAurora MySQL v2(MySQL5.7互換)へのアップグレード
    • アップグレード方式の調査(2022/11末のre:Inventで発表されたBlue Green Deploymentで自動化されました)
    • 互換性のなくなる挙動の洗い出し、修正内容整理
    • karateを使ったAPIのE2Eテストのベース作成と主担当APIのケース拡充
    • 本番含め全環境アップグレード作業
  • その他保守系
    • ansibleのバージョンアップ
    • terraform対応拡充
    • 古のインフラ系を整理、順次縮退
    • ドキュメント整備
    • システム運用系の細いツール実装
  • 新規モノ
    • イベント用の短期インフラ整備(GithubActionsでS3デプロイ、CloudFrontでの静的配信)
    • SystemManagerでのport forwarding整備
    • 特定機能群のWebAPI実装(Go)
  • 定常タスク
    • サーバサイド全般のアーキテクチャ設計/コードレビュー
    • DBクエリパフォーマンスチューニング
    • リリース作業
    • 各種障害、不具合修正対応
    • 軽微な改善
  • OSS
    • やりきってないけど自分のツールのaws-sdk-go-v2対応
  • 個人的興味
    • Cloudflare関連、ZeroTrustとWorkers他
    • Datadog等Observability SaaS

2023年

Aurora v3(MySQL8互換)アップグレードに向けての準備兼サービスのリファクタリング/リアーキテクチャかな。あとは自分のOSS整理とか。

Fault Injectionを使ったAurora MySQLの任意のレプリケーション遅延

Aurora MySQLでReaderへの反映が遅延した場合の挙動をテストするために遅延発生方法を調べたのでメモ。Aurora 2.10.3 (MySQL5.7互換)で行いました。

Fault Injection

Fault InjectionのReplica Failureを使うことで任意の時間レプリケーションを遅延させることができます。

ALTER SYSTEM SIMULATE percentage_of_failure PERCENT READ REPLICA FAILURE
  [ TO ALL | TO "replica name" ]
  FOR INTERVAL quantity { YEAR | QUARTER | MONTH | WEEK | DAY | HOUR | MINUTE | SECOND };

これは一時的なものでFOR INTERVALの時間が経過すると解除されます。以下のSQLで、「Writerから全てのReaderへの変更を100%、実行後15秒間ブロックする」という障害の擬似再現になります。

ALTER SYSTEM SIMULATE 100 PERCENT READ REPLICA FAILURE TO ALL FOR INTERVAL 15 SECOND;

これをWriterで実行した後、Writerでテーブルのレコード追加や変更を行なっても15秒間はReader側には反映されません。Reader側も止まるわけではなくクエリを実行して結果を取得することはできますが、データ反映が遅延します。*1

100の部分を変えることで何%にFailureを発生させるか、TO ALLの部分にReader名を指定することで任意のReaderに限定して発生させることもできるそうです。

ドキュメントの注意事項にもありますが、遅延時間は長く指定することも可能ですがWriterの書き込み量(データサイズ?)によってはReaderがクラッシュしたと判断してインスタンスが置き換えられる状況に陥るようなのでWriteがそれなりにある環境だと遅延だけで済まない状況になる点は注意が必要です。

Take care when specifying the time interval for your Aurora Replica failure event. If you specify too long of a time interval, and your writer instance writes a large amount of data during the failure event, then your Aurora DB cluster might assume that your Aurora Replica has crashed and replace it.

MySQL Delayed Replicationとの違い

Failt Injectionを使うと任意の時間、任意のReaderや一部遅延など障害らしい挙動が起こせますが、常時一定時間遅延させることはできません。常時遅延させる必要がある場合はbinlogレプリケーション+MASTER_DELAYの指定を試す必要があります。

MySQLには遅延レプリケーション(Delayed Replication)という仕組みがあり、これはReader側でCHANGE MASTER TO MASTER_DELAY = Nを設定することでN秒遅延するレプリケーションが構成可能です。意図的に遅延させることで誤ったデータ破壊(DROP TABLEやDELETE ALLのようなオペミスや条件ミスでの実行など)が起きた場合に復帰時間を短縮できるように使うケースがあるようです。RDS MySQLはこの機能がマネージドで提供されているそうです。

Aurora MySQLは通常のMySQLと異なり、とても簡単に言うとストレージを共有してWriter/ReaderのVMをそこにかぶせる形になっています。*2

Aurora MySQLも通常のMySQLと同様にbinlogを出力し、それを使った遅延レプリケーションを組むことは自分でやればおそらく可能です。ただ全て手動で用意しないといけないため、それなりに手間がかかります。Auroraのメジャーバージョンアップ時にこのbinlogレプリケーション構成を使う最小限のダウンタイムによるアップグレードと言う記事が公式ブログで紹介されています。遅延レプリケーションは使っていないですが、この手順でbinlogレプリケーションを組んだ後にCHANGE MASTER TO MASTER_DELAY = Nを指定したらできると思います。*3

まとめ

今回はFault Injectionを使ってレプリケーション遅延を起こしてみました。構成やコードで何かしら遅延対策を入れていても実際に起きるまでそのテストができず、実際起きたらダメみたいなことはよくあるのでちゃんとテストしていきたいですね。

Fault Injectionのドキュメントを見ると、他にもインスタンス障害、ディスクエラー、ディスク輻輳が再現できるそうです。インスタンス障害は構成、ディスクは読み書きエラー発生テストとかでしょうか。

参考ページ

*1:ここ厳密にWriterからReaderへのリクエストをブロックという表現がレプリケーションのことだけかは読めません。

blocks all requests from the writer instance to an Aurora Replica or all Aurora Replicas in the DB cluster

*2:細かいところはAWSのドキュメント読んでください

*3:Auroraのアップグレードのためにこの構成を組んだことはありますが、遅延レプリケーションは試したことはないです。