AR ホームベーカリー

オイラのアウトプット用ホームベーカリー!

Gitlab で働いていた人のお気持ち

t2y.hatenablog.jp

よんだ。

思いの外面白かったけど、最後の「住んでる所で給料決まるのなんでや!」みたいなトコはグローバル企業に所属したことないのでよくわかんなかった。

現地の給与体系を勘案して適切以上のギャランティが支払われているならいいのでは……? と思ったけど、そうじゃなかったのかな。

「とにかくシャーディングだ!」をする経営陣に辟易していそう、というのは「とにかくテーピングだ!」を思い出してちょっとクスっとした。 いやそうじゃないが。

総じてドコでもエンジニアにはエンジニアの言い分があり、経営者には経営者の言い分がある、という感じですね。 ストックオプションできただけいいじゃん?

RDS の Blue/Green Deploy を今更やった

来月から AWS RDS で MySQL 5.7.x を使っていると拡張サポートで金が取られる、という話なのでバージョンアップしぐさなどをしている。

直接メジャーバージョンアップしてもいいかなあ、と思っていたのだけど、せっかくなので Blue/Green Deploy でロールバックできるようにしておくかー、という気持ちに。

まあ面倒だし、Blue 側を 8 にして、 Green は 5 にしておいてなんかあったら切り替えて縮退、みたいな感じでいいだろという目論見で作業。

Blue のメジャーバージョンアップをするな

RDS Blue/Green Deployments only support default option groups for major version upgrades. Don't specify a major version upgrade when you create the blue/green deployment. After you create the blue/green deployment, you can upgrade the database in the green environment.

RDS Blue/Green デプロイメントは、メジャー バージョン アップグレードのデフォルト オプション グループのみをサポートします。 ブルー/グリーン展開を作成するときは、メジャー バージョンのアップグレードを指定しないでください。 ブルー/グリーン展開を作成した後、グリーン環境でデータベースをアップグレードできます。

ファッキュー!

しかたないので同じ MySQL 5.7 系で Green 側を作成して、完了後に Blue を手動でアップグレードしましょう。

申し訳ありません。DB インスタンス example-muccha-saikyou の変更のリクエストが失敗しました。 One or more of the DB Instance's read replicas need to be upgraded: example-muccha-saikyou-green-1145141919

上流となる Blue のバージョンを新しくして、縮退系として Green を扱う、とできないのか。 まじかよ。

Blue/Gree 構成時の更新系クエリの扱い

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/blue-green-deployments-overview.html#blue-green-deployments-major-steps

上記に Blue/Green の仕組み解説されてるんだけど、

ブルー/グリーンデプロイを作成すると、DB エンジンのバージョンをアップグレードして、グリーン環境の DB インスタンスに別の DB パラメータグループを指定できます。RDS は、ブルー環境のプライマリ DB インスタンスからグリーン環境のプライマリ DB インスタンスへの論理レプリケーションも設定します。

と論理レプリケーションの記述があるので、そのあたりは乗り越えられるんだと思っていた。

結局どうした

  1. Blue となる MySQL 5.7 インスタンスと同じバージョンで Green を作成
  2. MySQL 8 対応のパラメタグループとオプショングループを作成
  3. Green を MySQL 8 にメジャーバージョンアップ
    • ついでに中間証明書も rds-ca-rsa2048-g1 へ更新した、次は 2061 年だ……
    • 2.で作成したものにそれぞれ変更する必要がある
  4. アプリケーションが参照するホスト名を Blue から Green のものへ変更
  5. 動作確認
    • 更新系クエリの確認が必要であれば別途インスタンスを建てざるを得ない
  6. (大丈夫そうなので) アプリケーションが参照するホスト名を Green から Blue へ戻す
  7. ロール:ブルー/グリーンデプロイを選択して、 切り替え から Blue と Green を切り替える
    • 切替に必要な時間はお好みで変更する
  8. これで無停止で切り替えが出来た
    • とはいえアプリケーション側でコネクションプールとかコネクションキャッシュしていると多分こわれるので、アプリケーション再起動が必要な場合もある
    • 今回は使ってなかったっぽいのでセーフだった、そうか使ってないのか……
  9. 不要になった Green (元 Blue: MySQL 5.7 インスタンス) を消す
    • 論理レプリケーションで更新系クエリは降ってくるので、数日は残しておいてもよさそう

という感じです。

なんて呼べばいいんだろう、管理アレイ?

計画メンテに入れて停止時間が必要かなあ面倒だなあ、と思っていたので、無停止でなんとか出来てよかった。

とはいえあと数台あるんだよなあ。

参考

qiita.com

RDBMS よもやま

いまどきの PHP って RDBMS と接続する箇所、コネクションプーリングをしてんの? と調べていた。

qiita.com

togetter.com

yamaz.hatenablog.com

blog.yuuk.io

という感じで見ている。 はー勉強になる、と思っているけど、やっぱ知りたい内容と周辺知識を適切にピックアップできるか、みたいなトコ横着せずに、コンピュータサイエンスから入門するのが一番そう。

やはり放送大学か。

homebrew 環境で bundle install しても mysql2 がインストールできない時

小さい結論

.bundle/config に以下を書く。

BUNDLE_BUILD__MYSQL2: "--with-mysql-lib=/opt/homebrew/opt/mysql/lib --with-mysql-dir=/opt/homebrew/opt/mysql --with-mysql-config=/opt/homebrew/opt/mysql/bin/mysql_config \ --with-mysql-include=/opt/homebrew/opt/mysql/include --with-ldflags=-L/opt/homebrew/opt/zlib/lib"
続きを読む

Nginx の client_max_body_size と client_body_buffer_size の設定を雑にやる

実際どうすりゃいいの? というの、調べてもまあわりと具体的に書いてないので雑にやる。

まあこんなもん個々人の環境によるから、書きようがないので心持ちの話だよ。

client_max_body_size

1 リクエストあたりでアップロードできる (扱える) 最大ファイルサイズです。

デフォルト値は 1M。

例えば複数ファイルアップロードできる CMS とかだと、 20M のファイル 5 つ添付したらそれで一杯です。 そういう感じで設定してください。

どんくらい設定してんの?

だいたいのサービス、初期は「そんなの見積もれないよ〜」と泣き言を言うので、入力側 UI か API で制限しろや! と思いつつ、僕はいつも 100M で設定します。

client_body_buffer_size

1 リクエストでメモリにバッファリングできる最大サイズです。

デフォルトは OS というかアーキテクチャによって異なるそうですが、 8K もしくは 16K だそうです。

client_max_body_size と微妙にニュアンスが似ていますが、client_max_body_size > client_body_buffer_size となった時、あふれた分がディスクに temp ファイルとして書き出されます。 雰囲気的にはスラッシングってやつですね。

メモリから溢れちゃうとディスク I/O が発生して処理速度に影響するので、なるべく一度のリクエストで扱えるファイルが全部バッファに乗るようにすると良いですね。

どんくらい設定してんの?

だいたいのサービス、初期は「そんなの見積もれないよ〜」と泣き言を言うので、入力側 UI か API で制限しろや! と思いつつ、僕はいつも設定していません。

client_max_body_size とは違い、設定していないからといって制限が出るものでもないので。 適切に要件出してくれないなら性能を犠牲にするしかない、という気持ちを持っていけ。

最適化する時は、 client_max_body_size と同じ値を書いておくと全部メモリにバッファリングできてマルいです。

つまり?

とりあえずはこんな感じでデフォルト投下しておくと具合が良いでしょう。

概ね添付ファイルを扱う所への同時アクセスが 5 〜 10 程度で、Nginx が常時メモリを 1G 〜 確保しようとする想定です。

添付ファイルのサイズや数が増える (機能として指定される) と、そこから「どのくらい設定すればいいか」というのが仮定でも設定できるようになるので、仕様策定は大事、というオチで申し訳ないけど。

インスタンスやコンテナ全体の RAM が 2G 以下
server {
        client_max_body_size 10M;
        client_body_buffer_size 10M;
}
インスタンスやコンテナ全体の RAM が 4G 以下
server {
        client_max_body_size 100M;
        client_body_buffer_size 100M;
}

参考

SPDY 設定していると以下のような話もあるので、環境によっては tempfs 使う場合など注意する必要があります。

qiita.com

exec /docker-entrypoint.sh: exec format error

exec format error とは

とは。

どうも動作させようとしている環境に対して、 docker build 時の platform と異なっているとこうなるらしい。 ヘーッ知らなかった。

zenn.dev

なんで発生したのか

対象の環境は AWS ECR + ECS Fargate で linux/amd64 な Nginx を動作させているのだけど、確認したら前回 build して push したのが一年前だった。

その頃はまだ Lima (docker はサンプルの x86_64 イメージをそのまま利用していた) を使っていて、最近アサインされたプロジェクトの都合で Lima だと動かないので DockerDesktop (AppleSillicon 環境) に戻したのだった。

で、今回コンフィグ変更を実施して久しぶりに build -> push としたらタイトルの通りってワケ。

解決方法

元々の dockerfile がこんな感じだった。

FROM nginx:latest

以下略

ハイ。 platform を指定しましょうね。

FROM --platform=linux/amd64 nginx:latest

以下略

これでヨイ。

動いたからこれでいいや、で暗黙のプラットフォーム指定はやめようね!という学びだった。

Terraform から binbashar/waf-owasp/aws を削除しようとしたら出来なかったので、 terraform state rm で管理外にした

WAF の module

WAF を一部 Terraform 管理下でテスト導入していた (BFF に組み込み評価中) のだけど、利用していた binbashar/waf-owasp/aws がもうメンテしないっぽいので、順次削除することにした。

DEPRECATION NOTICE: This module will be not longer maintain because there are other Terraform modules that support these features based on ´wafv2´ Managed rules for AWS Web Application Firewall

registry.terraform.io

削除する

だいたい Terraform から記述削除して apply したら消えるでしょ! という感じで作業。

cloudfront.tf
resource "aws_cloudfront_distribution" "example" {

...

# WAF 利用の記述を削除する
-  web_acl_id = module.waf_global_test.web_acl_id

...

}
waf.tf

評価中なので、動作がブロックされると困るからと全部 COUNT 動作としていた。

# WAF 利用の記述を削除する
-  module "waf_global_test" {
-      source = "git::github.com:binbashar/terraform-aws-waf-owasp.git//modules/waf-global?ref=v1.0.17"
-  
-      # Just a prefix to add some level of organization
-      waf_prefix = "${var.prefix}-waf-cloudfront"
-  
-      # List of IPs that are blacklisted
-      blacklisted_ips = []
-  
-      # List of IPs that are allowed to access admin pages
-      admin_remote_ipset = []
-  
-      # By default seted to COUNT for testing in order to avoid service affection; when ready, set it to BLOCK
-      rule_size_restriction_action_type   = "COUNT"
-      rule_sqli_action                    = "COUNT"
-      rule_xss_action                     = "COUNT"
-      rule_lfi_rfi_action                 = "COUNT"
-      rule_ssi_action_type                = "COUNT"
-      rule_auth_tokens_action             = "COUNT"
-      rule_admin_access_action_type       = "COUNT"
-      rule_php_insecurities_action_type   = "COUNT"
-      rule_csrf_action_type               = "COUNT"
-      rule_blacklisted_ips_action_type    = "COUNT"
-  }

反映する

plan して apply。

module.waf.〜 削除される一覧が出るけど、長すぎて標準出力からはみ出るのでリダイレクトさせる。 特にエラーはないようだ。

❯ terraform plan > ./delete.txt

実行する。

❯ terraform apply

#=> module.waf_global_test.aws_waf_byte_match_set.match_admin_url (destroy): 1 error(s) occurred:
#=>   〜 WAFReferencedItemException: This entity is still referenced by other entities.

ンガーッ!!

ナンデ?

という訳でザッとググった、こちとら Terraform アカチャンゆえな。

github.com

github.com

どうも依存関係を考慮できない? っぽいのかな、もういちど作り直して、waf のリソースに create_before_destroy つけてやれば良さそうっぽい。

I just hit this issue today. A simple lifecycle block on the aws_wafregional_rule seems to have done the trick:

lifecycle {
   create_before_destroy = true
 }

コメント兄貴サンキュー!

github.com

とはいえ、もう半分以上リソース削除は実行されていて、「ここから復元すんの……?」というダルさとなにか発生した時の面倒臭さ、というのが勝ったので、別の方法で回避した。

terraform state rm

terraform state rm コマンドを利用すると、リソースを管理下から外す事ができる。 この際、管理下から外れたリソースには削除などは実行されない。

コレ本当に Terraform の管理から外れるだけなので、今回みたいにコード中から削除を伴う変更でコケた時は、 state から除外してしまうのが一番マルいと思う。

zenn.dev

というわけで、コケたリソースを調べて全部順番に state から外した。

ダメだったのは以下。

terraform state rm module.waf_global_test.aws_waf_byte_match_set.match_admin_url
terraform state rm module.waf_global_test.aws_waf_byte_match_set.match_auth_tokens
terraform state rm module.waf_global_test.aws_waf_byte_match_set.match_csrf_method
terraform state rm module.waf_global_test.aws_waf_byte_match_set.match_php_insecure_uri
terraform state rm module.waf_global_test.aws_waf_byte_match_set.match_php_insecure_var_refs
terraform state rm module.waf_global_test.aws_waf_byte_match_set.match_rfi_lfi_traversal
terraform state rm module.waf_global_test.aws_waf_byte_match_set.match_ssi
terraform state rm module.waf_global_test.aws_waf_ipset.admin_remote_ipset
terraform state rm module.waf_global_test.aws_waf_ipset.blacklisted_ips
terraform state rm module.waf_global_test.aws_waf_rule.detect_admin_access
terraform state rm module.waf_global_test.aws_waf_rule.detect_bad_auth_tokens
terraform state rm module.waf_global_test.aws_waf_rule.detect_blacklisted_ips
terraform state rm module.waf_global_test.aws_waf_rule.detect_php_insecure
terraform state rm module.waf_global_test.aws_waf_rule.detect_rfi_lfi_traversal
terraform state rm module.waf_global_test.aws_waf_rule.detect_ssi
terraform state rm module.waf_global_test.aws_waf_rule.enforce_csrf
terraform state rm module.waf_global_test.aws_waf_rule.mitigate_sqli
terraform state rm module.waf_global_test.aws_waf_rule.mitigate_xss
terraform state rm module.waf_global_test.aws_waf_rule.restrict_sizes
terraform state rm module.waf_global_test.aws_waf_size_constraint_set.csrf_token_set
terraform state rm module.waf_global_test.aws_waf_size_constraint_set.size_restrictions
terraform state rm module.waf_global_test.aws_waf_sql_injection_match_set.sql_injection_match_set
terraform state rm module.waf_global_test.aws_waf_web_acl.waf_acl
terraform state rm module.waf_global_test.aws_waf_xss_match_set.xss_match_set

これで無事 Terraform 上では削除できない module.waf. シリーズが認識されなくなったので、再度 terraform apply して各 Cloudfront の Distribution から waf を外して完了。 お疲れ様でした。

後処理

前述の通り terraform state rm は実リソースに影響を及ぼさないので、 AWS 上には上記の module.waf. シリーズが一部残っている。 これはあとで ManagementConsole から手動で削除するようにしましょう。

おまけ

apply で削除できなかった WAF のリソースは、 -target で単独指定して削除しようとすると Route53 やら複数リソースを破壊する、みたいな状態になっていて「ナンデー!?」という状態だったので、今回はこのように対応した。

確認と plan 実行大事、とは思うが、マジでなんで Route53 を吹き飛ばす必要があったんだ……? と謎が残ってしまった。

一応 OWASP Top10 対応のルール、 Cloudformation ならザッと利用できるらしいのだが、こういうのあるとヘタに module 使わずに自分で組んでメンテした方がマシだなー、となる。 ウーン。