⚠️ 資訊:這篇文章的內容已在後續文章中被撤銷 ⚠️
🚫 請參考後續更新的內容,避免使用本篇提到的方法。 🚫
今天我們要改善的地方跟昨天一樣, entrypoint.sh
,再加上裡面的 wait-for-psql.py
。
在我還在初步調整 Dockerfile 的時候,每次清空資料庫後第一次啟動 odoo,都會出現錯誤訊息並且啟動失敗:
ERROR odoo odoo.modules.loading: Database odoo not initialized, you can force it with `-i base`
解決方法就是根據錯誤訊息,手動啟動 odoo 並加上 -i base
參數,才能成功啟動。
雖然手動加上 -i
參數「一次」可以緩解問題,但這顯然不是一個好的部署實踐。接下來我想到最直覺的解決方案是:先檢查資料庫狀態,如果尚未初始化,就自動執行初始化,然後再重新啟動。
首先,我們看看原先的 entrypoint.sh
:
# 根據傳入的參數決定如何啟動 odoo
case "$1" in
-- | odoo)
shift
if [[ "$1" == "scaffold" ]] ; then
exec odoo "$@" # 如果傳入的參數是 scaffold,直接執行 odoo scaffold 命令
else
wait-for-psql.py ${DB_ARGS[@]} --timeout=30 # 等待 PostgreSQL 就緒,然後啟動 odoo
exec odoo "$@" "${DB_ARGS[@]}"
fi
;;
-*)
wait-for-psql.py ${DB_ARGS[@]} --timeout=30 # 同樣等待資料庫準備就緒,然後啟動 odoo
exec odoo "$@" "${DB_ARGS[@]}"
;;
*)
exec "$@" # 如果傳入的不是 odoo 相關指令,則執行傳入的指令
esac
接著,修改後的 entrypoint.sh
:
case "$1" in
odoo)
shift # 移除 $1("odoo")
if [[ "$1" == "scaffold" ]]; then
echo "$HEADER Running scaffold command..."
echo "$HEADER Executing: $ODOO_BIN $@"
exec $ODOO_BIN "$@"
else
# 新增資料庫狀態檢查:替換了 wait-for-psql.py,改用 check-db-status.py,並在啟動前檢查資料庫是否已初始化
echo "$HEADER Checking PostgreSQL readiness and database initialization..."
check-db-status.py "${DB_ARGS[@]}" --timeout=30
result=$?
if [ $result -eq 2 ]; then
# 自動初始化資料庫:如果 check-db-status.py 返回狀態碼 2(表示資料庫尚未初始化),則執行 odoo -i base --stop-after-init,自動初始化資料庫
echo "$HEADER Database not initialized. Running initialization..."
echo "$HEADER Executing: $ODOO_BIN -i base --stop-after-init ${DB_ARGS[@]}"
exec $ODOO_BIN -i base --stop-after-init "${DB_ARGS[@]}"
fi
# 資料庫已初始化,啟動 odoo
echo "$HEADER Database is initialized. Starting odoo..."
echo "$HEADER Executing: $ODOO_BIN $@ ${DB_ARGS[@]}"
exec $ODOO_BIN "$@" "${DB_ARGS[@]}"
fi
;;
*)
echo "$HEADER Executing custom command: $@"
echo "$HEADER Executing: $@"
exec "$@"
;;
esac
接下來,修改後的 check-db-status.py
:
#!/usr/bin/env python3
import argparse
import psycopg2
import sys
import time
def check_db_initialized(conn):
try:
with conn.cursor() as cur:
# 新增資料庫初始化檢查:透過查詢 ir_model 表,判斷資料庫是否已初始化
cur.execute("SELECT 1 FROM ir_model LIMIT 1;")
return True # 資料庫已初始化
except psycopg2.Error:
return False # 資料庫尚未初始化
if __name__ == '__main__':
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('--db_host', required=True)
arg_parser.add_argument('--db_port', required=True)
arg_parser.add_argument('--db_user', required=True)
arg_parser.add_argument('--db_password', required=True)
arg_parser.add_argument('--database', required=False)
arg_parser.add_argument('--timeout', type=int, default=5)
args = arg_parser.parse_args()
start_time = time.time()
while (time.time() - start_time) < args.timeout:
try:
# 連線參數調整:使用傳入的資料庫資訊嘗試連線,並在指定的超時時間內不斷重試
conn = psycopg2.connect(user=args.db_user, host=args.db_host, port=args.db_port, password=args.db_password, dbname=args.database)
error = ''
db_initialized = check_db_initialized(conn)
break
except psycopg2.OperationalError as e:
error = e
else:
conn.close()
time.sleep(1)
if error:
print("Database connection failure: %s" % error, file=sys.stderr)
sys.exit(1)
if not db_initialized:
# 自訂狀態碼:如果資料庫尚未初始化,程式以狀態碼 2 結束,供 entrypoint.sh 判斷
sys.exit(2) # Custom exit code to indicate uninitialized database
sys.exit(0)
這樣一來,新部署的環境只要啟動後就會自動初始化資料庫,然後重新啟動,完成部署。
後來,雖然直接啟動失敗的現象在我修改 Dockerfile 與官方高度相似後已經不再出現,而是會帶我到 /web/database/selector
頁面,需要手動填寫資料庫名稱、管理員密碼等資訊來建立資料庫。但這個功能剛好也可以讓我們跳過這個手動填寫的步驟,直接使用設定檔中的資料自動初始化,對我來說非常方便。