crontabで秒レベルの排他実行を登録する

crontabのスケジューリング最小単位は分指定だが、秒単位で実行させて、並列処理されては困るときの排他実行の組み方。

10秒おきに排他実行をスケジュールする例

* * * * * flock -xn /path/to/task.lock -c /path/to/task.sh
* * * * * sleep 10; flock -xn /path/to/task.lock -c /path/to/task.sh
* * * * * sleep 20; flock -xn /path/to/task.lock -c /path/to/task.sh
* * * * * sleep 30; flock -xn /path/to/task.lock -c /path/to/task.sh
* * * * * sleep 40; flock -xn /path/to/task.lock -c /path/to/task.sh
* * * * * sleep 50; flock -xn /path/to/task.lock -c /path/to/task.sh

秒単位の指定

sleepコマンドで寝かせて遅延実行させる

# 30秒周期で実行
* * * * *  コマンド
* * * * *  sleep 30; コマンド
# 20秒周期で実行
* * * * *  コマンド
* * * * *  sleep 20; コマンド
* * * * *  sleep 40; コマンド

排他制御

flockファイルロックでカーネルリソースを利用してクリティカルセクションを確保する。
この感じWindowsSDKのMutexとかを思い出す。

flock

-xオプション

ファイルロックを取得する。

-nオプション

取得できない場合は終了させる。

-cオプション

ファイルロックを取得できたら実行させるコマンド。

* * * * * flock -xn /path/to/task.lock -c /path/to/task.sh

Bearというノートウェア

Bearはマークダウンのノートウェア


プライベートなメモからナレッジ、マニュアルまであらゆるメモをBearにアウトプットしていて、ブログのソースはすべてBearからほぼそのままhtmlエクスポートしている。

ノートウェアの要件



ノートウェアに求める個人的な要件としてサクッと書いてサクッと辞書的に検索できたら良い。後は自分でなんとかするから。
そんなUXにフィットするのがBear。それだけじゃないのもBear(後述)

ノートを残す意義



新たに仕入れる情報は検証がついて回るけど、成功・失敗体験を綴ったノートは自分の環境にフィットしたナレッジなので自分の環境において再現精度が高いのは言うまでもないし、
新たに仕入れた情報は適宜アップデートするので自分だけの最新・最高のナレッジとなる。この価値を体験できるからいつまでも続けられる。

スタイル



使い始めて約4年。ほぼ毎日何かしら書くので1,000件以上貯まっているけど、その中でスタイルも固まってきているので私のスタイルを紹介。

  1. 新規ノートを作ったらプレーンテキストで作成日時のタイムスタンプを入れてくれる
  2. タイトル書く
    基本#(h1)で装飾するけどなにを装飾をしようがノート先頭がタイトル
  3. 後で引っ掛けやすかろう思いついたタグを書く
    #bear #ブログ
  4. アイコンイメージを貼り付ける


    後からビジュアルで索引になるし、アイコンはかわいくてモチベが上がる
  5. ラインを入れたらあとはマークダウンでフリーフォーマット

{ この記事の目的を書く }


{ ## (h2)アウトライン見出しを書く }
{ h2の下にラインを入れるのがマイルール }


{ ###  (h3)さらに見出し刻む }
h3以降は見出しにインデントを入れるのがマイルール、見やすいし


{ ###  (h3)さらに見出し刻む }


{ ####   (h4)さらにさらに見出し刻む }

それだけじゃないBear

 UI

書くも探すも異常なほど軽快

  • スクショの一番左のペインは #タグ を階層管理でビジュアライズしてくれるが全く使わない、使わなくていいので普段は折りたたんでいる。ちなみにタグは #タグ/サブタグ のようにネストすることで階層管理できる。これはやりたければやったらいいし、どっちでもいい。
  • スクショ真ん中ペインが重要。更新日付の降順で並べている。ワードを検索すればインクリメンタル検索で精度の高いものから順にサクサクとフィルタしてくれる。ここで索引的にアイコンイメージが活きてくる。
  • エディタはフルマークダウンで画像とかファイルとかなんでもコピペで張り付けてアーカイブできる。なにより軽快さが超優秀。すぐ書きたくなる。

 アプリ同期

iCound同期なので、MacとiPhoneで常時スイッチ可能

 safariプラグイン

Bearアプリを入れたら、iPhone/safariのアクションシートにBearが追加されてwebページをまるっとマークダウン変換してBearにアーカイブしたり、タイトル付きリンクを埋め込ませることができる。

 エクスポート

ノートをhtmlとかpdfにエクスポートできる
htmlはブログソースにする
pdfはマニュアルにする

おしいBear

よく見る惜しい点

  • Mac/iOSアプリのみで、web版、windows版がない。web版は欲しい。Bear使うためだけに会社にMac持っていっている。
  • デバイス同期とかスキンは有償($2.99/月)。物理的にノート買っていると思ったら高いとは思わない。むしろ無償で広告とか付きだしたら快適なUXが損なわれるので嫌なのでこれでいいと思っているし寄付したいくらいだ。

SouceTreeでコミット間の差分ファイルを取得するカスタムアクションスクリプト



SourceTreeのカスタムアクションに登録すると、2点のコミット間のフォワード差分とリバート差分をサクッとアーカイブで出力出来るので、デプロイでやらかした時など緊急の切り戻し資材として便利。

シェル




ポイントはgit diff--name-status --diff-filter={差分シンボル}オプションでコミット間の差分ファイルを選択抽出できるのでforward/revertケースに合わせてフィルタしてawkでファイル名のトークンを選択する


git diff --name-status --diff-filter=ACMRD コミットハッシュ(from) コミットハッシュ(to)
A		{path/to/追加ファイル}
C		{path/to/コピー元ファイル}		{path/to/コピー先ファイル}
M		{path/to/変更ファイル}
R100		{path/to/リネーム前ファイル}	{path/to/リネーム後ファイル} < R*はリネーム認識精度(%)
D		{path/to/削除ファイル}

Mac版 - zshスクリプト版


forward


コミットハッシュ$fromから$toの次の差分ファイルを$toコミットから抽出してアーカイブする


  • --diff-filterで抽出する差分
    • 変更(M) - (awk:$2)
    • 追加(A) - (awk:$2)
    • コピー(C) - コピー先ファイル(awk:$3)
    • リネーム(R) - 変更後ファイル(awk:$3)
  • --diff-filterで抽出しない差分
    • 削除(D)されたファイル・・・$toコミットには存在しないためエラーになる

#!/bin/bash

if [ "$2" = "" ]; then
  to=HEAD
  from=${1}
else
  to=${1}
  from=${2}
fi

git archive --format=zip --prefix=forward/ ${to} `git diff --name-status --diff-filter=AMCR ${from} ${to} | awk '/^R|^C/ {print $3; next} /^A|^M/ {print $2}'` -o forward.zip

revert


コミットハッシュ$fromから$toの次の差分ファイルを$fromコミット時点から抽出してアーカイブする


  • --diff-filterで抽出する差分
    • 変更(M) - (awk:$2)
    • リネーム(R) - 変更前ファイル(awk:$2)
    • 削除(D) - 削除されたファイル(awk:$2)
  • --diff-filterで抽出しない差分
    • 追加(A)・・・$fromコミットには存在しないためエラーになる
    • コピー(C)・・・$fromコミットには存在しないためエラーになる

#!/bin/bash

if [ "$2" = "" ]; then
  to=HEAD
  from=${1}
else
  to=${1}
  from=${2}
fi

git archive --format=zip --prefix=revert/ ${from} `git diff --name-status --diff-filter=MRD ${from} ${to} | awk '{print $2}'` -o revert.zip

Windows - コマンド版


  • バッチではコマンド結果をワンライナーで中継できないので、ファイルリストを抽出してからgit archiveにわたす
  • awkコマンドはbusyboxからバイナリを取得&どこかに展開してシステム環境変数のパスを通しておく

forward


if "%2" EQU "" (
  set PARAM1=HEAD
  set PARAM2=%1
) else (
  set PARAM1=%1
  set PARAM2=%2
)

setlocal enabledelayedexpansion
set RET_DIR=
for /F "usebackq" %%i in (`git diff --name-status --diff-filter=AMCR %PARAM2% %PARAM1% ^| awk "/^R/ {print $3; next} /^C/ {print $3; next} /^A/ {print $2; next} /^M/ {print $2}"`) do (
  set RET_DIR=!RET_DIR! "%%i"
)

git archive --format=zip --prefix=forward/ %PARAM1% %RET_DIR% -o forward.zip

revert


if "%2" EQU "" (
  set PARAM1=HEAD
  set PARAM2=%1
) else (
  set PARAM1=%1
  set PARAM2=%2
)

setlocal enabledelayedexpansion
set RET_DIR=
for /F "usebackq" %%i in (`git diff --name-status --diff-filter=MRD %PARAM2% %PARAM1% ^| awk "{print $2}"`) do (
  set RET_DIR=!RET_DIR! "%%i"
)

git archive --format=zip --prefix=revert/ %PARAM2% %RET_DIR% -o revert.zip

SrouceTreeで使う



カスタムアクションにスクリプトを登録する


上記スクリプトファイルとパラメータに$SHA(コミットハッシュ)を指定するとコミットハッシュを引数にしてスクリプトを実行してくれる。

アーカイブを作成する


2点のコミットを選択する > コンテキストメニュー > カスタムアクション > スクリプトを選択


出力されたアーカイブ


こいつを展開したらforward/revertのファイル差分が入っている


ls -lav /path/to/this_repository

total 200
drwxr-xr-x  16 user  staff    512  2 16 21:51 .git
-rw-r--r--   1 user  staff  16701  2 16 21:54 forward.zip < これ
...

RDS ProxyのコネクションプールでToo many connectionsを対策する

  

RDS - MySQLインスタンスの同時接続数max_connectionsパラメタは変更可能ではあるが規定値{DBInstanceClassMemory/12582880}はインスタンスのメモリ容量に応じた接続数に最適化されていて、接続数の引き上げはインスタンスサイズのアップグレードが推奨されている。


これらの値を変更することはお勧めできません。もっと接続が必要な場合は、もっと大きい RDS インスタンスサイズにアップグレードすることをお勧めします。


故にマイクロなRDSインスタンスにがしがしパラレルで接続して検証するとあっという間に接続上限に到達してエラーになってしまう


Error 1040: Too many connections

コネクションプーリングでスケーリングしたいところだが、サーバーレス(lambda)やランタイム(phpとかruby)&フレームワークレベルでサクッと準備できない場合に
RDS ProxyをRDSインスタンスの前段に入れるとサクッとプーリング機能を提供してくれる優れもので、マネージドのコネクションプーリング付きhaproxy的なイメージ。
使い方は簡単で、ALBのようにターゲットグループにRDSインスタンスを設定するだけ。


RDSインスタンスのID&PWクレデンシャルはRDS Proxyの構築時にこれまたSecretManagerにフルマネージドで管理される。


Access Denied



  • RDS Proxyの構築と同時にIAMロールrds-proxy-role-{タイムスタンプ}が作成&アタッチ状態で、ロールにSecretManagerの当該クレデンシャルのARNを含むアクセスポリシーrds-proxy-policy-{タイムスタンプ}が割当てられていて、クレデンシャルを参照している。
  • RDS Proxyの構築後に自分でSecretManagerに追加したクレデンシャルはポリシーにARNを追加してやる必要がある。

ここがうまくいっていないと、アプリケーション側では延々とAccess deniedなエラーログが出るだけでなんで?と首をひねる事となる。


SQLSTATE[HY000] [1045] Access denied for user 'dbuser'@'XXX.XXX.XXX.XXX' (using password: YES)

が、CloudWatchLogrds-proxyのロググループにはばっちり権限エラーとして出力されていたので、追加したクレデンシャルのARNを追加して解決。


Credentials couldn't be retrieved. The IAM role "arn:aws:iam::XXXXXXXXXXXX:role/service-role/rds-proxy-role-XXXXXXXXXXXXX" is not authorized to read the AWS Secrets Manager secret with the ARN "arn:aws:secretsmanager:ap-northeast-1:XXXXXXXXXXXX:secret:{secret_name}.{secret_id}"

bncert-toolでサーバー証明書をインストール

SSL証明書の更新

Amazon Lightsail の WordPress インスタンスで HTTPS を有効化する | Lightsail ドキュメント
Lightsail wordpress bitnami SSL と自動更新の設定メモ | gworks web site



Amazon LightsailのBitnamiインスタンスのbncertツールは、Let’sEncryptでサーバー証明書の取得からインストールまでワンストップでやってくれる上に更新設定まで組んでやってくれる便利ツール。

初回作成

bitnami redmineにSSL証明書(Let's Encrypt)を導入する



/opt/bitnami/bncert-toolツールがダイアログで証明書作成のお手伝いをしてくれる


##############################################
# ダイアログ起動
##############################################
sudo /opt/bitnami/bncert-tool

##############################################
# ツールの更新がある場合のガイダンス
##############################################
An updated version is available. Would you like to download it? You would need to run it manually later. [Y/n]: Y

----------------------------------------------------------------------------
Welcome to the Bitnami HTTPS Configuration tool.
----------------------------------------------------------------------------

##############################################
# 対象ドメインを指定する 複数ある場合はスペースで列挙する
# bncert-toolではワイルドカード非対応
##############################################

Domains

Please provide a valid space-separated list of domains for which you wish to 
configure your web server.

Domain list []:hoge.com www.hoge.com

##############################################
# すでに同じドメイン名の証明書がインストール済の場合には表示されるが気にせず進む
##############################################
Warning: A certificate for the list of domains you entered already exists. It 
will be used instead of generating a new one.
Press [Enter] to continue:

##############################################
# wwwドメインがなくリダイレクトできないが?と言われているが不要であれば気にせず進む
##############################################
Warning: No www domains (e.g. www.example.com) or non-www domains (e.g. 
www.example.com) have been provided, so the following redirections will be 
disabled: non-www to www, www to non-www.
Press [Enter] to continue:
----------------------------------------------------------------------------

##############################################
# HTTP=>HTTPSリダイレクトさせる
##############################################

Enable/disable redirections

Please select the redirections you wish to enable or disable on your Bitnami 
installation.

Enable HTTP to HTTPS redirection [Y/n]: Y
----------------------------------------------------------------------------

##############################################
# バックグラウンドのインストール手順を具体的に説明してくれている
# 	WEBサーバーをとめる
# 	証明書を更新する
# 	更新スケジュールをcron登録する
# 	登録ドメインへのhttpリクエストをhttpsにリダイレクトする
# 	WEBサーバーを起動する
##############################################

Changes to perform

The following changes will be performed to your Bitnami installation:

1. Stop web server
2. Configure web server to use an existing Lets Encrypt certificate and renew: 
/opt/bitnami/letsencrypt/certificates/tomon-wp.musicsecurities.com.crt
3. Configure a cron job to automatically renew the certificate each month
4. Configure web server name to: tomon-wp.musicsecurities.com
5. Enable HTTP to HTTPS redirection (example: redirect 
http://tomon-wp.musicsecurities.com to https://tomon-wp.musicsecurities.com)
6. Start web server once all changes have been performed

Do you agree to these changes? [Y/n]: Y
----------------------------------------------------------------------------

##############################################
# 設定内容の確認とサブスクライブの同意を求めている
##############################################

Create a free HTTPS certificate with Let's Encrypt

Please provide a valid e-mail address for which to associate your Let's Encrypt 
certificate.

Domain list: hoge.com www.hoge.com

Server name: hoge.com www.hoge.com

E-mail address []: test@hoge.com

The Let's Encrypt Subscriber Agreement can be found at:

https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf

Do you agree to the Let's Encrypt Subscriber Agreement? [Y/n]: Y


----------------------------------------------------------------------------
Performing changes to your installation

The Bitnami HTTPS Configuration Tool will perform any necessary actions to your 
Bitnami installation. This may take some time, please be patient.
----------------------------------------------------------------------------
Success

The Bitnami HTTPS Configuration Tool succeeded in modifying your installation.

The configuration report is shown below.

Backup files:
* /opt/bitnami/apache/conf/httpd.conf.back.202211241742
* /opt/bitnami/apache/conf/bitnami/bitnami.conf.back.202211241742
* /opt/bitnami/apache/conf/bitnami/bitnami-ssl.conf.back.202211241742
* /opt/bitnami/apache/conf/vhosts/wordpress-https-vhost.conf.back.202211241742
* /opt/bitnami/apache/conf/vhosts/wordpress-vhost.conf.back.202211241742

Find more details in the log file:

/tmp/bncert-202211241742.log

If you find any issues, please check Bitnami Support forums at:

https://github.com/bitnami/vms

Press [Enter] to continue:

legoコマンドで有効な証明書のリストが確認できる


sudo /opt/bitnami/letsencrypt/lego --path /opt/bitnami/letsencrypt list
Found the following certs:
  Certificate Name: hoge.jp
    Domains: hoge.jp, www.hoge.jp
    Expiry Date: 2024-05-13 14:27:55 +0000 UTC
    Certificate Path: /opt/bitnami/letsencrypt/certificates/hoge.jp.crt

crontab



  • 初回作成を完了すると、crontabに更新用のlegoコマンドを設定してくれるので、以降は自動更新
  • 複数ドメインの場合は--domainsディレクティブを複数指定する

【SSL】Let’s Encryptで2つ以上のドメインを指定して証明書を発行する方法


50 3 * * * sudo /opt/bitnami/letsencrypt/lego --path /opt/bitnami/letsencrypt --email="test@hoge.com" --http --http-timeout 30 --http.webroot /opt/bitnami/apps/letsencrypt --domains=hoge.com --domains=www.hoge.com --user-agent bitnami-bncert/1.0.0 renew && sudo /opt/bitnami/apache/bin/httpd -f /opt/bitnami/apache/conf/httpd.conf -k graceful # bncert-autorenew

自動更新できてない



crontabで自動更新を組んだはずが、Let’x Encrypt Expiry Botから有効期限のリマインドメールが入った


Hello,

Your certificate (or certificates) for the names listed below will expire in 19 days (on 2024-02-29). Please make sure to renew your certificate before then, or visitors to your web site will encounter errors.

We recommend renewing certificates automatically when they have a third of their total lifetime left. For Let's Encrypt's current 90-day certificates, that means renewing 30 days before expiration. See https://letsencrypt.org/docs/integration-guide/ for details.

hoge.jp
www.hoge.jp

For details about when we send these emails, please visit: https://letsencrypt.org/docs/expiration-emails/ In particular, note that this reminder email is still sent if you've obtained a slightly different certificate by adding or removing names. If you've replaced this certificate with a newer one that covers more or fewer names than the list above, you may be able to ignore this message.

For any questions or support, please visit: https://community.letsencrypt.org/ Unfortunately, we can't provide support by email.

To learn more about the latest technical and organizational updates from Let's Encrypt, sign up for our newsletter: https://letsencrypt.org/opt-in/

If you are receiving this email in error, unsubscribe at:
  http://delivery.letsencrypt.org/track/unsub.php?u=30850198&id=5e5be6988e3c420d8c7aac87504171d2.S7DjHBHYLMMrczOlcrO3VqAyMvw%3D&r=https%3A%2F%2Fmandrillapp.com%2Funsub%3Fmd_email%3Dc%252A%252A%252A%252A%2540m%252A%252A%252A%252A.%252A%252A%252A
Please note that this would also unsubscribe you from other Let's Encrypt service notices, including expiration reminders for any other certificates.

Regards,
The Let's Encrypt Team

crontabのlego更新コマンドを直接叩くとtest@fuga.com(仮)などというアカウントは知らんと言われる


sudo /opt/bitnami/letsencrypt/lego --path /opt/bitnami/letsencrypt --email="test@fuga.com" --http --http-timeout 30 --http.webroot /opt/bitnami/apps/letsencrypt --domains=hoge.com --domains=www.hoge.com --user-agent bitnami-bncert/1.0.0 renew && sudo /opt/bitnami/apache/bin/httpd -f /opt/bitnami/apache/conf/httpd.conf -k graceful

> It produced this output: Account “test@fuga.com” is not registered. Use 'run' to register a new account.

そういえば初回作成時にサブスクライブ登録したメールアドレス=crontabの更新コマンドの--emailを手動で更新したのを思い出し、、runオプションで新しいアカウントを登録する
AWS Lightsail の WordPress の SSL証明書(Let's Encrypt)を更新する


sudo /opt/bitnami/letsencrypt/lego --tls --email="test@fuga.com" --domains="hoge.jp" --domains="www.hoge.jp" --path="/opt/bitnami/letsencrypt" run

すると443ポートが使われているというエラーで怒られる


[hoge.jp] [hoge.jp] acme: error presenting token: could not start HTTPS server for challenge: listen tcp :443: bind: address already in use
[www.hoge.jp] [www.hoge.jp] acme: error presenting token: could not start HTTPS server for challenge: listen tcp :443: bind: address already in use

443: bind: address already in use Err, for subsite of WP Multisite · Issue #833 · go-acme/lego


再度crontabのlego更新コマンドを叩くと今度はアカウントエラーが解消されて証明書の更新が成功した。runコマンドで新しいメールアドレスへの更新は完了していたようだ。
ちょっとよくわからないが、おそらくrenewオプションがapacheの再起動系やら一連の更新オペレーションのフラグになっていそう


sudo /opt/bitnami/letsencrypt/lego --path /opt/bitnami/letsencrypt --email="test@fuga.com" --http --http-timeout 30 --http.webroot /opt/bitnami/apps/letsencrypt --domains=hoge.com --domains=www.hoge.com --user-agent bitnami-bncert/1.0.0 renew && sudo /opt/bitnami/apache/bin/httpd -f /opt/bitnami/apache/conf/httpd.conf -k graceful



PHP DotEnvのスレッドセーフ対応


 

EC2のマイクロインスタンスでホストしているWEBサーバーに同時並列リクエストでワークロード負荷を与えると、DBやセッションストアのmemcachedへのTCP接続が稀に失敗する事象に遭遇して手間取ったはなし


MySQL・・・No such file or directory
→ TCP/IP接続なんだが、UNIXドメインソケットが見つからない系のエラーなんで?


memcached・・・can not to connect memcached


切り分け調査


  • ワークロード?
    • EC2インスタンスのロードアベレージは余裕で1未満
    • ネットワーク帯域は余裕
    • コネクション数、パケットロストなどRDS, Elasticacheのメトリクスに異常が見られない
  • DNS?
    • 一応Route53のプライベートホストゾーンから、エンドポイント直に変更してみるが状況変わらず
    • VPCのDNSスロットリング制限で蹴られている?いやそこまでいじめてない

Amazon が提供する DNS サーバーは、Elastic Network Interface ごとに 1 秒あたり 1024 パケットの制限を適用します。Amazon が提供する DNS サーバーは、この制限を超えたトラフィックをすべて拒否します。

VPC DNS スロットリングが原因で、Amazon が提供する DNS サーバーへの DNS クエリが失敗しているかどうかを確認するにはどうすればよいですか?


ここまで遠回りして、フレームワークのセッションアダプタからphpネイティブに変更するも失敗するが、きれいに{ホスト}:{ポート}が歯抜けのエラーログが出た


PHP Warning:  session_start(): Failed to parse session.save_path (error at offset 0, url was 'tcp://:')

DotEnv


apache mpm_event_module×スレッドセーフmod_phpな環境で接続コンフィグにDotEnvにImmutableで.envファイルをローディングさせて$_ENVスーパーグローバルで参照していた


$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();

DotEnvをcreateImmutableで起動するとputenv/getenvがスレッドセーフではないため、次の順序で並列リクエストの環境変数がタイミングによってクリーンアップされたようだ。


1 番目のリクエスト: 変数が存在しない → ロード 2 番目のリクエスト: 変数が存在する → ロードしない 1 番目のリクエスト: 終了、変数をクリーンアップする 2 番目のリクエスト: 変数を使用する → 変数はもう存在しない、によってクリーンアップされる1回目のリクエスト

https://github.com/vlucas/phpdotenv/issues/248
When you have two or more sites on one server, the sites's .env will affect each other · Issue #219 · vlucas/phpdotenv


Using getenv() and putenv() is strongly discouraged due to the fact that these functions are not thread safe, however it is still possible to instruct PHP dotenv to use these functions. Instead of calling Dotenv::createImmutable, one can call Dotenv::createUnsafeImmutable, which will add the PutenvAdapter behind the scenes. Your environment variables will now be available using the getenv method, as well as the super-globals:

公式にもスレッドセーフでない旨ため注意書きがあった


Immutableモードは定義済みの環境変数のローディングは無視され(2)、リクエストの終了とともに破棄される(4)
一方でMutableモードはリクエストごとに独立したストアに確保するとみえて、スレッド競合の問題は起きなくなった
createUnsafeImmutableでv5系以降の実装で、v4系にはなかったのでMutableがそれにあたるっぽい


$dotenv = Dotenv::createMutable(__DIR__);
$dotenv->load();

Microsoft 365 Defenderで検閲ブロックされたメールを解除する


Microsoft 365で組織外部からきた添付ファイル付きメールとかビジネスメールが届かない、迷惑メールフォルダにもない。
どうなっとんのやと助けを求められたときに。


Azureコンソールは統廃合が激しくて久しぶりに開くと高確率で迷子になるのでモチベ下がりつつ、
Exchangeコンソールでブロックリスト的なの探しても見つからないと思ったら、Microsoft 365 Defenderコンソールに独立していた


メールとコラボレーションカテゴリ > 確認 > 検疫
に問題メールがあるので選択してメールを開放するを実行すると、解除とともにTOにデリバリーしてくれる。一応、誤認報告もしておく。


Microsoftにメールをブラックリストから解除してもらう方法さまに感謝

AWS WAFでマネージドルールの誤検出を除外する


悪意のないリクエストがPOSTしたパラメタがSQLインジェクションルールAWSManagedRulesSQLiRuleSet.SQLi_BODYに引っかかってブロックされるため特定条件で除外する

ルール単位にアクションをオーバーライドする


AWSManagedRulesSQLiRuleSetのルールセット以下のルールにもいろいろある


  • SQLi_QUERYARGUMENTS
  • SQLi_BODY
  • SQLi_COOKIE
  • SQLi_URIPATH

デフォルトはルールにマッチしたらBlockになるが、ルールレベルでAllowとかCount(カウントのみ)とか上書きできる。
しかしながらルールごと上書きされるので、特定の条件のみ除外とか細かいチューニングはできない。



ルールセットに対してスクリーニング条件を定義する


Scope of inspectionではルールセットに対してscope-downでステートベースでスクリーニング条件を定義できる。
次はいずれの(AND Statement)の否定条件(NOT Statement)にもひっかからないときにフィルターをかけるステートメント



  • Match Typeの説明

文字列一致ルールステートメント - AWS WAF、AWS Firewall Manager、および AWS Shield Advanced


  • 否定条件を複数充てる場合

AWS WAF のルールビルダーで複数の否定条件を設定する方法を教えてください | DevelopersIO

RDS - MySQL5.7から8.0へブルーグリーン移行する


鬼気迫るMYSQL5サポート終了の対応


ダウンタイムなしでRDSのMySQL5.7インスタンスをMySQL8.0インスタンスへ移行した備忘録


ファクター


  • ダウンタイムなしで可用性を確保しつつ
    • この記事

  • スループットで非機能要件を保ちつつ
    • 新旧のベンチ観測とメトリクスを観察
      • SysBenchベンチ結果はMYSQLネイティブでスループットが向上した 5 < 8
    • リソース消費は 5 < 8 と増加したのでインスタンスタイプを1ランクスケールアップした
  • ユーザーインターフェースはなにひとつ変化のない完全性を保つ
    • 全クエリレベルとE2Eでクエリ由来の全ページでHTML差分検証した
      • Orderステートメントのあいまいなところでちょいちょい差分が出たので修正
      • テーブル名が一部8系の予約後にひっかかってエラーが出たので修正

全体像

事前検証編


MySQLバージョンネイティブな問題・課題を集めるために、まずはdockerコンテナで 5 → 7 にダンプファイルをレストアしてアプリケーションをつないでみる


認証方式をプレーンに変更する(5.7互換)

phpでThe server requested authentication method unknown to the client. - それマグで!
要件 - Manual
開発環境に影響あり



PHP5系のPDOドライバで接続すると認証エラーがでる


SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client**

  • MySQL8からクライアント認証方式default_authentication_pluginのデフォルトがハッシュ強制caching_sha2_passwordに変更になった。
  • PHP5以前のPDOはcaching_sha2_passwordに対応していないため、MySQLサーバーのdefault_authentication_pluginとユーザーのIDENTIFIED WITHで認証プラグインをmysql_native_password(平文)にデグレードする
  • RDSのMySQL8デフォルトパラメーターグループではdefault_authentication_plugin=mysql_native_passwordなので対応不要であった
  • MySQLとアプリケーションのトラフィックにインターネットは経由しないのでOK。

開発環境ではサーバーとユーザの認証方式を次のように変更した


/etc/my.cnf


[mysqld]
default_authentication_plugin=mysql_native_password
-- ユーザ作成時にプロトコルを指定
CREATE USER `user`@`%` IDENTIFIED WITH mysql_native_password  BY 'パスワード'; 
-- あるいはプロトコルを更新
ALTER USER `user`@`%` IDENTIFIED WITH mysql_native_password BY 'パスワード';

utf8はutf8mb3になった

影響なし



MySQL8においてはutf8utf8mb3に名詞変更された


MySQL5から取得したダンプファイルのCRATE TABLE ....ステートメントのキャラセットutf8と照合順序utf8_general_ci
8にレストアするとキャラセットutf8mb3照合順序utf8mb3_general_ciになる


utf8mb4にする

影響あり



utfmb3はデプレケイト予定でいずれ移行は避けられないならutf8mb4に対応していく。
MySQL 8 のデフォルト文字セット系をすべて utf8mb4 にする cnf の書き方メモ


注意点として、キャラクタセットutf8mb4かつ照合順序が未定義のテーブルをMySQL8にインポートするとデフォルトでは照合順序にutf8mb4_0900_ai_ciが充てられるが、旧世代(php5系)のPDOでは解釈できない。これはMVCフレームワークを使っている場合に、ORM層がMySQLにメタデータをリクエストしてPDOにパスしたときにエラーとして顕在化する。


そもそも環境影響を受けないためにもテーブル定義ではutf8mb4_general_ciなど旧世代でも互換性のある照合順序をセットで定義してやる。


つまり、CREATE TABLE文にDEFAULT CHARSET=utf8mb4のみ記述すると、collation_serverシステム変数に設定されているサーバーのデフォルトcollationでもデータベースレベルのcollationでもない、MySQLのデフォルトcollationが設定されてしまうのです。CREATE TABLE文を実行するときは予期せぬトラブルを避けるため、DEFAULT CHARSET=xx COLLATE=xxを省略せずに記述するのが良いでしょう。

第157回 MySQLのデフォルトcollationの注意点


ということでテーブルをはじめ、未定義のエスレーション先である、データベース > サーバーに対してもキャラセットutf8mb4/照合順序utf8mb4_general_ciをがっつり設定していく。


 テーブル



ダンプファイルのすべてのCREATE TABLEステートメントに対して次の正規表現で「DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;」に変更する


# `DEFAULT CHARSET`句を正規表現で
DEFAULT CHARSET=[^(COLLATE)].*$

# まとめて置換する
DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

 データベースのデフォルト



CREATE DATABASE `データベース名` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

 MySQLサーバーのデフォルト



; mysqlサーバー
[mysqld]
; キャラクタセット
character-set-server=utf8mb4
; 照合順序
collation-server=utf8mb4_general_ci

; mysqlクライアントも一応
[mysql]
default-character-set=utf8mb4
mysql> show variables like 'character%';
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | utf8mb4                        |
| character_set_connection | utf8mb4                        |
| character_set_database   | utf8mb4                        |
| character_set_filesystem | binary                         |
| character_set_results    | utf8mb4                        |
| character_set_server     | utf8mb4                        |
| character_set_system     | utf8mb3                        |
| character_sets_dir       | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+
8 rows in set (0.00 sec)
  • サーバーデフォルトがutfmb4に統一された
  • character_set_systemはMySQLサーバーシステムのキャラクタセットなのでutf8mb3(3バイト)でOK

MySQLバージョン違いのレプリケーション



ブルーグリーン移行に備えて移行先インスタンスを現行インスタンスとミラーリングホットスタンバイ状態にしておくため、RDSインスタンス間でマニュアルレプリケーションが組めるか調査


そもそもMySQLネイティブでバージョン違いのレプリケーションはできるのか


できる


次を除いて、1世代(5=>8) までのレプリケーションがサポートされている
MySQL :: MySQL 8.0 リファレンスマニュアル :: 17.5.2 MySQL バージョン間のレプリケーション互換性


  • バイナリログフォーマットがステートメントベースではないことMySQL&MariaDBのbinlog formatの話
  • 後方互換性のない定義を含まないこと(たとえば、64 文字を超える外部キー名は、MySQL 8.0 からサポートされない)

RDSインスタンス間でマニュアルレプリケーションが組めるか


できる


  • マルチAZ構成にしているからかバイナリログを出力しているし、MySQLネイティブレベルのチューニングが可能
  • レプリケーション絡みのコマンドは権限がないため蹴られるが、次の代替のストアドでCHANGE MASTER TO ...相当の設定ができるのでインスタンスレベルでレプリケーションが可能

RDSをAuroraのスレーブにする


CALL mysql.rds_set_external_master ('host.to.endpoint', 3306, 'repl', 'repl', 'mysql-bin-changelog.000003', 481507, 0);

 ダンプとバイナリログ座標の取得



ダンプファイルとバイナリログ座標をどこから取るか


  • mysqldumpはI/O停止が発生するため稼働中の移行元5.7のプライマリインスタンスには打ちたくない。
  • マルチAZセカンダリインスタンスはエンドポイント非公開なので打てない。
  • スナップショット復元インスタンスは立ち上げた瞬間から別系統なのでダンプもバイナリログもどう動いているかわからないのでレプリケーションのソースにならない。
  • リードレプリカはマスタのレプリケーション状態で起動するので、レプリケーションを停止すればダンプファイルと&マスタのバイナリログ座標をキャプチャできる。
  • ただし、リードレプリカの起動にはスナップショットを必要とするためシングルAZではI/O停止が発生するが、マルチAZ配置ならスナップショットをセカンダリからとってくれるのでサービス影響の心配はない。

RDSリードレプリカのメンテナンスでダウンタイムを発生させない方法|スクショはつらいよ
DB インスタンスのリードレプリカの操作 - Amazon Relational Database Service
MySQL リードレプリカの使用 - Amazon Relational Database Service
Amazon RDS for MySQL または MariaDB での mysqldump エラーの解決

  • RDSインスタンスではmysqldumpのバイナリログ座標オプション--master-dataは使用不可。
    mysqldumpはREAD LOCKを要求するが、RDSではREAD LOCKに必要なユーパーユーザ権限は制限されているための次のエラーをくらう。

mysqldump: Couldn't execute 'FLUSH TABLES WITH READ LOCK': Access denied for user 'user'@'%' (using passw
ord: YES) (1045)

--master-data オプションは FLUSH TABLES WITH READ LOCK を取得します。これには、Amazon RDS マスターのユーザーが持っていない SUPER 権限が必要です。また、Amazon RDS は GLOBAL READ LOCK をサポートしていません。


よって、リードレプリカから--master-dataオプションなしでダンプファイルを取得して、バイナリログの座標はリードレプリカのスレーブコンディションから取得する。


SHOW SLAVE STATUS 

Master_Log_Fille ...
Exec_Master_Log_Pos ...

移行ロードマップ



  • ソースインスタンス(5.7プライマリ)のチューニング
    • バイナリログの保持期間を延長する
    • マニュアルレプリケーション用のユーザを作成する
  • リードレプリカから起点となるダンプファイルとバイナリログ座標を取得する
    • 起動する
    • リードレプリカのレプリケーションを停止する
    • ダンプファイルを取得する
    • バイナリログの座標を取得する
    • キャラクタセットと照合順序をutf8mb4に変更する
  • 移行先インスタンス MySQL8.0.34を構築する
    • パラメータグループ
    • アプリクライント用のMySQLユーザ作成
    • MySQL8インスタンスにダンプファイルをレストアする
    • レプリケーション
      • MySQL5
        • レプリーション用ユーザーを作成する
        • バイナリログの保存期間を延長する
      • MySQL8
        • パラメタチューニング
        • レプリケーションを開始
  • プライベートホストゾーンで移行する

1. ソースインスタンス(5.7プライマリ)のチューニング





Amazon RDSのMySQLバイナリログ保持時間を設定する。 #AWS - Qiita


バイナリログの保持期間を延長する


マスターインスタンスのバイナリログはレプリケーションのストリームに送るとデフォルトでは即時で破棄されるため、リードレプリカ→ダンプ→移行先から移行元へのレプリケーション開始時にはロストしてしまう。そのためバイナリログの保持期間を一時的に延長する。バイナリログはインスタンスのストレージを食うので[[レプリケーション作業が完了したら元に戻す。


次のストアドで保持期間の確認と設定ができる


-- バイナリログの保持期間を確認する
CALL mysql.rds_show_configuration

-- 時間(h)で指定
CALL mysql.rds_set_configuration('binlog retention hours', 24);

マニュアルレプリケーション用のユーザを作成する


-- レプリケーション用ユーザを作成
CREATE USER 'repl'@'%' IDENTIFIED BY 'passrepl';

-- レプリケーション権限を付与
-- 余談だが、データベース単位の指定は不可だった
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';

2. リードレプリカから起点となるダンプファイルとバイナリログ座標を取得する

Amazon RDS for MySQL または MariaDB での mysqldump エラーの解決



起動する


シングルAZ、バックアップなど不要なオプションは落として、基本ソースインスタンスとコンディションを揃えて起動


リードレプリカのレプリケーションを停止する


ダンプを取得する前にレプリカでレプリケーションを停止してダンプとバイナリログ座標を固定する。


-- レプリケーション停止ストアド
CALL mysql.rds_stop_replication;

ダンプファイルを取得する



 注意ポイント

  • マルチAZのRDSインスタンスはGTIDで全DBをセカンダリにレプリケーションしているため、単体DBを抜き出そうとすると対象外のGTIDも含まれるそうなので、-set-gtid-purgeed=OFFオプションでmysqldumpからGTIDを落とす
    マルチAZのmysqldumpでWarningが出た
  • mysqldumpエラー
    • 公式のOracleLinux7.9×MySQL5コンテナバンドルのmysqldumpを使うとunknow option --connect-expired-passwordエラーで動かなかったので、OracleLinux8.8×MySQL8コンテナからやりました。

# OracleLinux8.8×MySQL8コンテナからEC2にsshポートフォワードしてます
mysqldump
-h {ソースインスタンスのエンドポイント}
-u {ユーザ名}
-p{パスワード}
set-gtid-purged=OFF -- GTIDなし
{データベース名} > mysqldump.sql

バイナリログの座標を取得する


SHOW SLAVE STATUSでレプリカごしにバイナリログ座標のスタンプをとる


# OracleLinux8.8×MySQL`コンテナからEC2にsshポートフォワードしてます

SHOW SLAVE STATUS;

> Master_Log_File ...
> Exec_Master_Log_Pos ...

charsetとcollateを補完する


ダンプファイルの全CREATE TABLEステートメントにcharsetとcollateを当てる


DEFAULT CHARSET句を正規表現で
DEFAULT CHARSET=[^(COLLATE)].*$

まとめて置換する
DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

3. 移行先インスタンス MySQL8.0.34を構築する


 パラメータグループ


パラメータグループファミリーはmysql8.0を使用する


  • 認証プラグイン
    • default_authentication_pluginmysql_native_password
      → 変更不可でが適応済

  •  キャラクタセットを変更する
    • character_set_clientutf8mb4
    • character_set_connectionutf8mb4
    • character_set_databaseutf8mb4
    • character_set_filesystembinary
    • character_set_resultutf8mb4
    • character_set_serverutf8mb4

  • 照合順序を変更する
    • collation_serverutf8mb4_general_ci
    • collation_connectionutf8mb4_general_ci

  • タイムゾーンを変更する
    • time_zoneAsia/Tokyo

  • クライアントオプションを変更する
    • クライアントのキャラクタセット指定を無視
init_connectSET NAMES utf8mb4 
    • クライアントのキャラクタセット指定を無視
skip-character-set-client-handshake1

  • レプリケーションの非不可逆スケールを許可する
    • 非不可逆スケールを許可する slave_type_conversionsALL_NON_LOSSY

 キャラセットと照合順序を指定してデータベースを作成する


CREATE DATABASE `データベース` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

 アプリクライアント用のMySQLユーザを作成する


-- ユーザ作成
CREATE USER `ユーザ`@`%` IDENTIFIED BY 'パスワード';
-- ユーザ権限とフラッシュ
GRANT ALL ON `データベース`.* TO `ユーザ`@`%`;
FLUSH PRIVILEGES;

4. ダンプファイルで移行先インスタンスをレストアする


気になっていたこと



# リモートでSSHトンネル経由でレストア
mysql -u root -p -P 3307 -h 127.0.0.1 レストア先のデータベース名 < ダンプファイル

5. 移行先インスタンスから移行元インスタンスへレプリケーションを開始する


レプリケーション系のストアド



レプリケーションを開始する


mysql> CALL mysql.rds_set_external_master ('5.7インスタンスのエンドポイント', 3306, 'repluser', 'password', 'bin.000001', 12345, 0);
mysql> CALL mysql.rds_start_replication;
mysql> SHOW SLAVE STATUS\G

6. プライベートホストゾーンでインスタンスのエンドポイントを切り替える

-- DNS解決が安定するまで待機
watch -n 5 dig エンドポイントのDNS

vscodeをv1.86にするとcent7コンテナでdevcontainerサーバーの起動に失敗する



vscodeをv1.86にするとcent7コンテナでdevcontainerサーバーの起動に失敗する
 

Warning: Missing GLIBCXX >= 3.4.25! from /usr/lib64/libstdc++.so.6.0.19
Warning: Missing GLIBC >= 2.28! from /usr/lib64/libc-2.17.so

 
次のvscodeの有志リポジトリ掲載のパッチを当てて解決した。とても感謝。

npurson/vscode-server-toolchain-workaround: Workaround for the raised toolchain requirements of VS Code Server 1.86+
 vscode server can not launch · Issue #204036 · microsoft/vscode
cd && \
wget https://github.com/npurson/vscode-server-toolchain-workaround/archive/refs/heads/main.zip && \
unzip main.zip && \
sh vscode-server-toolchain-workaround-main/run.sh && \
rm -f main.zip && \
rm -rf vscode-server-toolchain-workaround-main

補足

直後に気付いたが、vscodeに「Visual Studio CodeでサポートされていないOSバージョンに接続しています。」とのワーニングが出ていた。 vscodeかdevcontainerのバージョンアップによってvscodeserverサーバーの動作要件がアップデートし、一定のOSバージョニングを判定してデプリケートをワーニング通知しているようだ。

上記パッチによって動作はするが、要件を満たさないコンテナはこのワーニングがフラグされ続けるようだ。