【問題の背景】
先日、「『Nginx + Lua』による統合権限検証機能」をリリースしました。この機能では、Nginx デーモン スレッドが MySQL から「パートナー データ」を定期的にロードする必要があります。MySQLなどの構成情報 (ハードコーディング) を Lua ファイルに直接書き込み、その後、「中間構成ファイル」を手動で変更して、異なる展開環境に異なるデータ ソースを選択します。
ここに落とし穴があります。開発側と運用保守側の両方がこの「中間設定ファイル」の変更を忘れると、異なる環境にデプロイする際に指されるデータソースが同じになってしまい、検証失敗エラーが発生してしまいます。デフォルトでは、オンライン データ ソースを指します。オンラインでデプロイし、すべてが正常であることを確認した後、すぐにテスト ジョイント デバッグ環境のデプロイを開始します。その結果、この「中間構成ファイル」が変更されます (デプロイメントの完了後、ローカルの CURL では確認されません。実際、これにより間違いを犯す可能性を減らすことができます! )。午後には、関連するすべての環境デプロイメントが置き換えられました。その結果、午後には同じコールがまだ正常に行われていたため、夕方には運用部門に顧客から API 検証エラーに関する苦情 (「お金を掘り出す」) が届きました。
【オンライントラブルの緊急対応】
しかし、運用上のフィードバックを受けたリーダーは、午後の機能開始を踏まえ、新たに開始した「統合権限検証機能」が原因である可能性があることをすぐに確認し、お客様が共同デバッグできるよう、テスト用共同デバッグ環境のNginx構成を直ちにロールバックしました。(後に、問題の根本原因はデータ ソースの読み込みエラーであることが確認されました。テスト用共同デバッグ環境のデータ ソースを読み込む必要がありましたが、オンライン データ ソースが読み込まれていました)
【解決】
上記の質問から、人間による手動修正のみに頼って正しいことを行うのは非現実的であり、そのような状況は可能な限り回避する必要があることがわかります。したがって、上記の問題に対応するには、「Spring XML とプロパティ」の分離と分離のような技術で実現できないか、Maven と同様のプリコンパイルを通じて関連する設定ファイルを自動的に生成できないかと考えます。上記のアイデアを受けて、私は「Lua 設定テンプレート ファイルと設定プロパティ ファイルをアセンブルして、シェル スクリプトを通じて設定ファイルを生成する」という試みを行いました。神の甲斐あって、最終的にそれが完了しました。プログラムの手順は次のとおりです。
- 「Lua設定テンプレートファイル」を定義する
- さまざまな展開環境に応じて、関連する「構成プロパティ ファイル」を定義します。
- シェル スクリプトを使用して最終構成ファイルを生成します (もちろん、このステップは自動化することもできます)
具体的な実装手順は次のとおりです。
1. config-generator.shに「実行権限」があるか確認します
ll -h nginx/lua/bin/config-generator.sh
2. config-generator.sh に「実行権限」がない場合は、「実行権限」を付与します
chmod a+x nginx/lua/bin/config-generator.sh
3. 「構成テンプレート」、「構成プロパティファイル」、および「デプロイ環境」に従って構成ファイル (config.lua) を生成します。
nginx/lua/bin/config-generator.sh -e test -c /usr/apps/nginx/lua/lib/cn/fraudmetrix/ngx/ または nginx /lua/bin/config-generator.sh -e test -c nginx/lua/lib/cn/fraudメトリクスx/
ngx
/
【ビフォー・アフター計画の実現】
本来の実現
中間設定ファイル
- 開発中 --return 「cn.fraudmetrix.ngx.config.config-dev」が必要 - 生産中 return require "cn.fraudmetrix.ngx.config.config-prod"
開発環境の構成
-- 設定値 ローカル設定 = { バージョン = "1.1"、 mysql = { ホスト = "127.0.0.1"、 ポート = 3306、 データベース = "開発"、 ユーザー = "開発者"、 パスワード = "123456"、 max_packet_size = 1024 * 1024, -- 1M mysql はパケット サイズの上限を返します timeout = 500, -- ms idle_timeout = 60000, -- ms pool_size = 4 }, cache = { -- 缓存大小上限 partner = 10000, -- 合作方 upstream = 100 -- 上游 }, timer = { -- timer执行周期 partner = 3600, -- s }, } return config
现在实现
完整的代码见附件~~~
Lua配置模板文件
-- config value -- 【变量占位符替换】若替换值为“字符串”类型的,以单引号(\')定义;若替换值为“数值”类型的,以双引号(\")定义。 local config = { version = '1.1', mysql = { host = '$mysql_host', port = "$mysql_port", database = '$mysql_database', user = '$mysql_user', password = '$mysql_password', max_packet_size = "$mysql_max_packet_size", -- 1M mysql返回包大小上限 timeout = "$mysql_timeout", -- ms idle_timeout = "$mysql_idle_timeout", -- ms pool_size = "$mysql_pool_size" }, cache = { -- 缓存大小上限 partner = "$cache_partner_max_num", -- 合作方 upstream = "$cache_upstream_max_num" -- 上游 }, timer = { -- timer执行周期 partner = "$timer_partner_period", -- s }, } return config
测试环境的配置属性文件
# 配置属性名称定义以下划线("_")分割,不能以点号(".")分割(因为在Shell脚本中,点号(".")是一个特殊字符) # MySQL mysql_host=127.0.0.1 mysql_port=3306 mysql_database=test mysql_user=test mysql_password=123456 # 风险点:随着”合作方“接入增多,未来”合作方“MySQL加载的数据大小可能超过该值! mysql_max_packet_size=1024*1024 mysql_timeout=500 mysql_idle_timeout=60000 mysql_pool_size=4 # Local Cache # 风险点:随着”合作方“接入增多,未来”合作方“的数量可能超过该值! cache_partner_max_num=10000 cache_upstream_max_num=100 # Timer timer_partner_period=3600
Shell解析脚本
#!/usr/bin/env bash # ----------------------------------------------------------------------------- # Generates 'Configuration' file of Lua (config.lua) automatically. # ----------------------------------------------------------------------------- usage() { echo "Usage:" echo " sh config-generator.sh [-e app_deploy_env] [-c config_file_dir]" echo "" echo "Description:" echo " -e application deploy environment. The option value is prod, test or dev." echo " -c configuration file directory for Lua (/absolute/path/to/lua/config)." echo "" echo "Example:" echo " nginx/lua/bin/config-generator.sh -e test -c /usr/apps/nginx/lua/lib/cn/fraudmetrix/ngx/" echo " nginx/lua/bin/config-generator.sh -e test -c nginx/lua/lib/cn/fraudmetrix/ngx/" echo "" exit 1 } # 默认是“生产环境” app_deploy_env="prod" # “配置文件”默认放在“当前目录下” config_file_dir="." # 1. 接收“参数” while getopts ":e:c:h" arg do case $arg in e) app_deploy_env="$OPTARG";; c) config_file_dir="$OPTARG";; h) usage;; ?) usage;; esac done # 2. get 'config_file' # 兼容“path”与“path/”的情况 config_file_dir_last_char="${config_file_dir: -1}" if [ "$config_file_dir_last_char"x = "/"x ]; then # "path/" + "config.lua" config_file="$config_file_dir"config.lua else # "path" + "/config.lua" config_file="$config_file_dir"/config.lua fi # 兼容“path”与“/path”(相对路径与绝对路径)的情况 config_file_dir_first_char="${config_file_dir:0:1}" if [ "$config_file_dir_first_char"x != "/"x ]; then # relative path cur_prg_dir=`pwd` config_file="$cur_prg_dir"/"$config_file" fi # 3. 根据“配置模板”、“配置属性文件”和“部署环境”来生成“配置内容” # resolve links - $0 may be a softlink (解析链接 - $0 可能是一个“软链接”) # 执行程序 PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done # ”执行程序“所在的”相对根目录“ PRGDIR=`dirname "$PRG"` # 转换为”绝对根目录“ PRGDIR=`readlink -f "$PRGDIR"` config_template_file="$PRGDIR"'/../config/config-template.lua' config_properties_file="$PRGDIR"/../properties/config-"$app_deploy_env".properties # 执行“配置模板解析器” CONFIG_PARSER_EXECUTABLE="$PRGDIR"/config-parser.sh if [ ! -x "$CONFIG_PARSER_EXECUTABLE" ]; then `sudo chmod a+x "$CONFIG_PARSER_EXECUTABLE"` fi exec "$CONFIG_PARSER_EXECUTABLE" "$config_file" "$config_template_file" "$config_properties_file"
#!/usr/bin/env bash # ----------------------------------------------------------------------------- # Generates 'config.lua' by 'config-template.lua' and 'config-${mode}.properties'. # ----------------------------------------------------------------------------- config_file="$1" config_template_file="$2" config_properties_file="$3" # source the properties . "$config_properties_file" # replace the placeholder eval "echo \"$(<$config_template_file)\"" > "$config_file" echo "Generates 'Configuration' file of Lua to '$config_file'"