分享如何将自定义容器镜像切换到 Bitnami 容器镜像,以及如何搭配反向代理软件(如 Traefik)配置使用。
写在前面
去年曾分享过一篇关于 Phabricator 的内容,《使用 Docker 和 Traefik v2 搭建 Phabricator》,当时介绍了如何构建自定义的容器镜像,以及如何搭配汉化补丁使用应用。
时隔一年,伴随着软硬件升级,Phabricator 的服务迁移也被提上了日程。
考虑到尽可能省心的长期使用,我选择将镜像切换至了 Bitnami 的镜像,这样可以使用到每小时都由 GitHub 构建的透明可信的镜像,以及更少的操心各种安全补丁和升级的事情。
梳理问题
Bitnami 的镜像提供非常多的环境变量配置,用来应对各种场景。然而针对以下两个场景的支持却不够完善:
- 使用已有数据库运行软件,而非从零到一进行初始化。
- 使用反向代理服务,而非直接提供服务。
收集线索
浏览仓库代码中的 Dockerfile ,可以看到项目启动前的入口脚本和预执行脚本各有一个文件:
ENTRYPOINT [ "/opt/bitnami/scripts/phabricator/entrypoint.sh" ]
CMD [ "/opt/bitnami/scripts/phabricator/run.sh" ]
观察 entrypoint.sh
脚本,可以看到这个脚本调用了 Web 服务器、数据库、以及应用初始化相关的脚本。
#!/bin/bash
# shellcheck disable=SC1091
set -o errexit
set -o nounset
set -o pipefail
# set -o xtrace # Uncomment this line for debugging purpose
# Load Phabricator environment
. /opt/bitnami/scripts/phabricator-env.sh
# Load libraries
. /opt/bitnami/scripts/libbitnami.sh
. /opt/bitnami/scripts/liblog.sh
. /opt/bitnami/scripts/libwebserver.sh
print_welcome_page
if [[ "$1" = "/opt/bitnami/scripts/phabricator/run.sh" || "$1" = "/opt/bitnami/scripts/$(web_server_type)/run.sh" || "$1" = "/opt/bitnami/scripts/nginx-php-fpm/run.sh" ]]; then
info "** Starting Phabricator setup **"
/opt/bitnami/scripts/"$(web_server_type)"/setup.sh
/opt/bitnami/scripts/php/setup.sh
/opt/bitnami/scripts/mysql-client/setup.sh
/opt/bitnami/scripts/phabricator/setup.sh
/post-init.sh
info "** Phabricator setup finished! **"
fi
echo ""
exec "$@"
其中有一个脚本路径 /opt/bitnami/scripts/phabricator/setup.sh
比较可疑,对这个脚本进行 phabricator/setup.sh
翻阅,可以看到除了一些检查环境就绪与否的准备工作外,还可以找到两个基础依赖:
...
# Load libraries
. /opt/bitnami/scripts/libphabricator.sh
. /opt/bitnami/scripts/libwebserver.sh
...
继续翻阅 /opt/bitnami/scripts/libphabricator.sh
这个脚本,会看到这个脚本真正定义了 phabricator 所有的应用配置,有一部分和容器环境变量(包含未被文档说明的),也和这个脚本进行了绑定,所以从这里入手进行修改,再合适不过了。
调整脚本:添加时区设置
如果想让 phabricator 时间展示正确,需要进行时区设置,我们找到 phabricator_initialize
函数,在其中添加对 phabricator.timezone
的设置:
########################
# Ensure Phabricator is initialized
# Globals:
# PHABRICATOR_*
# Arguments:
# None
# Returns:
# None
#########################
phabricator_initialize() {
# Check if Phabricator has already been initialized and persisted in a previous run
local -r app_name="phabricator"
local -r port="${WEB_SERVER_HTTP_PORT_NUMBER:-"$WEB_SERVER_DEFAULT_HTTP_PORT_NUMBER"}"
if ! is_app_initialized "$app_name"; then
info "Creating Phabricator configuration file"
# Modified by @soulteary
phabricator_conf_set "phabricator.timezone" "Asia/Shanghai"
调整脚本:更新数据库命名空间
找到 phabricator_configure_database_credentials
函数,对 storage.default-namespace
配置项目进行更新,如果你没有设置过,需要将这个项目删除或注释掉,避免应用启动之后找不到之前的数据:
#########################
# Configure Phabricator database
# Globals:
# PHABRICATOR_*
# Arguments:
# $1 - database user name
# $2 - database user password
# Returns:
# None
#########################
phabricator_configure_database_credentials() {
local -r db_user="${1:?missing database user}"
local -r db_pass="${2:?missing database password}"
info "Configuring database"
phabricator_conf_set "mysql.host" "$PHABRICATOR_DATABASE_HOST"
phabricator_conf_set "mysql.port" "$PHABRICATOR_DATABASE_PORT_NUMBER"
phabricator_conf_set "mysql.user" "$db_user"
phabricator_conf_set "mysql.pass" "$db_pass"
# Modified by @soulteary
# phabricator_conf_set "storage.default-namespace" "bitnami_phabricator"
phabricator_conf_set "storage.mysql-engine.max-size" "0"
}
调整编排文件:设置数据库
因为我们要直接使用老数据库,所以这里不能让脚本运行“数据库初始化”那一套流程,需要针对编排文件进行环境变量设置,让脚本认为数据库结构已就绪,不需要进行初始化,并且使用已有的数据库配置提供服务:
...
environment:
...
# 需要和 PHABRICATOR_SKIP_BOOTSTRAP 一起使用,否则还需要设置更多的冗余内容
- ALLOW_EMPTY_PASSWORD=yes
- PHABRICATOR_SKIP_BOOTSTRAP=yes
- PHABRICATOR_DATABASE_HOST=database
- PHABRICATOR_DATABASE_PORT_NUMBER=3306
- PHABRICATOR_EXISTING_DATABASE_USER=root
- PHABRICATOR_EXISTING_DATABASE_PASSWORD=QV8}!P![&QmR
...
调整脚本:调整应用链接以支持反向代理
为了能够支持反向代理环境,尤其是支持由反向代理网关提供“HTTPS”协议访问的能力,我们需要修改 phabricator_configure_host
和 phabricator_configure_alternate_file_domain
函数,让应用能够在运行在非 HTTPS 状况下,将页面链接渲染为 https://
协议。
#########################
# Configure Phabricator host
# Globals:
# PHABRICATOR_*
# Arguments:
# None
# Returns:
# None
#########################
phabricator_configure_host() {
local host
local scheme
get_hostname() {
if [[ -n "${PHABRICATOR_HOST:-}" ]]; then
echo "$PHABRICATOR_HOST"
else
dns_lookup "$(hostname)" "v4"
fi
}
host="$(get_hostname)"
if is_boolean_yes "$PHABRICATOR_ENABLE_HTTPS"; then
scheme="https"
[[ "$PHABRICATOR_EXTERNAL_HTTPS_PORT_NUMBER" != "443" ]] && host+=":${PHABRICATOR_EXTERNAL_HTTPS_PORT_NUMBER}"
else
scheme="http"
[[ "$PHABRICATOR_EXTERNAL_HTTP_PORT_NUMBER" != "80" ]] && host+=":${PHABRICATOR_EXTERNAL_HTTP_PORT_NUMBER}"
fi
info "Configuring Phabricator URL to ${scheme}://${host}"
# Modified by @soulteary
scheme="https"
phabricator_conf_set "phabricator.base-uri" "${scheme}://${host}"
}
#########################
# Configure Phabricator alternate file domain
# Globals:
# PHABRICATOR_*
# Arguments:
# None
# Returns:
# None
#########################
phabricator_configure_alternate_file_domain() {
local afd="$PHABRICATOR_ALTERNATE_FILE_DOMAIN"
local scheme
if is_boolean_yes "$PHABRICATOR_ENABLE_HTTPS"; then
scheme="https"
[[ "$PHABRICATOR_EXTERNAL_HTTPS_PORT_NUMBER" != "443" ]] && afd+=":${PHABRICATOR_EXTERNAL_HTTPS_PORT_NUMBER}"
else
scheme="http"
[[ "$PHABRICATOR_EXTERNAL_HTTP_PORT_NUMBER" != "80" ]] && afd+=":${PHABRICATOR_EXTERNAL_HTTP_PORT_NUMBER}"
fi
# Modified by @soulteary
scheme="https"
info "Configuring Phabricator Alternate File Domain to ${scheme}://${afd}"
phabricator_conf_set "security.alternate-file-domain" "${scheme}://${afd}"
}
编排文件对应的配置也需要声明:
...
environment:
...
- PHABRICATOR_ENABLE_HTTPS=false
- PHABRICATOR_HOST=board.lab.com
- PHABRICATOR_ALTERNATE_FILE_DOMAIN=board-file.lab.com
...
完整的容器编排配置
将上面提到的内容更新到 libphabricator.sh
中,然后编写容器编排配置文件:
version: '3.7'
services:
phabricator:
image: bitnami/phabricator:2021.13.0
expose:
- 8080
environment:
- APACHE_HTTP_PORT_NUMBER=8080
- PHABRICATOR_ENABLE_HTTPS=false
- PHABRICATOR_HOST=board.lab.com
- PHABRICATOR_ALTERNATE_FILE_DOMAIN=board-file.lab.com
- ALLOW_EMPTY_PASSWORD=yes
- PHABRICATOR_SKIP_BOOTSTRAP=yes
- PHABRICATOR_DATABASE_HOST=board.data.lab.com
- PHABRICATOR_DATABASE_PORT_NUMBER=3306
- PHABRICATOR_EXISTING_DATABASE_USER=root
- PHABRICATOR_EXISTING_DATABASE_PASSWORD=QV8}!P![&QmR
- PHABRICATOR_ENABLE_PYGMENTS=true
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.phab0.middlewares=https-redirect@file"
- "traefik.http.routers.phab0.entrypoints=http"
- "traefik.http.routers.phab0.rule=Host(`board.lab.com`, `board-file.lab.com`, `phabricator.lab.io`, `phabricator-file.lab.io`)"
- "traefik.http.routers.phab1.middlewares=content-compress@file"
- "traefik.http.routers.phab1.entrypoints=https"
- "traefik.http.routers.phab1.tls=true"
- "traefik.http.routers.phab1.rule=Host(`board.lab.com`, `board-file.lab.com`, `phabricator.lab.io`, `phabricator-file.lab.io`)"
- "traefik.http.services.phabbackend.loadbalancer.server.scheme=http"
- "traefik.http.services.phabbackend.loadbalancer.server.port=8080"
volumes:
- ./libphabricator.sh:/opt/bitnami/scripts/libphabricator.sh:ro
networks:
traefik:
external: true
将上面的内容保存为 docker-compose.yml
, 使用 docker-compose up -d
,即可将应用启动起来了。
重设用户密码
在切换数据库和应用版本后,我们可能会遇到用户无法登陆的状况。
这里可以采取官方issue中的方式进行用户密码重置,以用户名 soulteary
为例,执行下面的脚本,可以快速获得密码重置链接:
docker-compose exec phabricator /opt/bitnami/phabricator/bin/auth recover soulteary
Use this link to recover access to the "soulteary" account from the web interface:
https://board.lab.com/login/once/recover/1/eedkghmtxrvkktaqof7di54n5lkabcd/
After logging in, you can use the "Auth" application to add or restore authentication providers and allow normal logins to succeed.
访问链接,即可重置密码,再次登陆应用中。
最后
这篇关于 Phabricator 切换 Bitnami 镜像的内容,就先写到这里。
–EOF