【动机】
使用 docker 封装应用的优点在此不用赘述。由于 wordpress 的运行,一般涉及到两个容器:wordpress 和 mysql, 前者自带 apache 和 php 运行时。如果不采用 docker compose 编排容器,如何实现原生控制容器的启停、备份、还原?
【意图】
- 使用 docker –link 命令为两个容器建立连接;
- 首次运行网站,安装,配置网站相关信息;
- 完整备份 mysql、wordpress 到本地;
- 将本地备份上传到百度网盘;
- 从百度网盘下载备份到本地;
- 利用本地备份还原容器;
- 两个容器的相关配置,使用 json 文件保存。
【文件结构】

其中,wpsites 是根目录,guoshi 和 sanxiao(还可以自由添加其他实体)是网站实体目录,其下级 backup 是备份目录(其下级备份可以有多个,均以备份时间为名),running 是容器当前运行采用的实际内容。
【实现】
入口脚本文件 main.sh
#!/bin/bash
:<<COMMENT
rely:
1. jq; to parse json file
2. docker;
3. bypy
4. bc (进度条用到)
5. wget curl dig nslookup
COMMENT
set -e
# global constant:
declare -r __DEBUG__=
declare -r ENTRY_FILE_PATH=$(readlink -f $0)
declare -r ENTRY_FILE_NAME=$(basename $ENTRY_FILE_PATH)
declare -r ROOT_DIR_PATH=$(dirname $ENTRY_FILE_PATH)
declare -r ROOT_DIR_NAME=$(basename $ROOT_DIR_PATH)
declare -r CONFIG_FILE_REL_PATH="data/config.json"
declare -r CONFIG_FILE_PATH="$ROOT_DIR_PATH/$CONFIG_FILE_REL_PATH"
declare -r DEFAULT_CONFIG_ROOT="default"
# region Ignored
# 根据当前脚本与 library 和 execution 的相对位置, .. 可能有所变化
declare -r LNX_LIB_DIR="$ROOT_DIR_PATH/../../library"
# endregion Ignored
# 块 source, 详情查看 library/doc/compile.txt
# 排除目录: -path "$ROOT_DIR_PATH/document" -prune -o \
# 最后接 -print
# region Sourcing
for file in \
$(find $ROOT_DIR_PATH \
-path "$ROOT_DIR_PATH/log" -prune -o \
-path "$ROOT_DIR_PATH/data" -prune -o \
-path "$ROOT_DIR_PATH/doc" -prune -o \
-path "$ROOT_DIR_PATH/test" -prune -o \
-type f -regex .*?\.sh$ \
-not -wholename "$ENTRY_FILE_PATH" -print ) \
$(find $LNX_LIB_DIR \
-type f -regex .*?\.sh$ ) ; do
source $file
done
# endregion Sourcing
# global variable:
# 将 json 文件读入字符串, 以便程序启动后,即使删除整个脚本程序, 仍然保证可以顺利运行
declare __JSON_CONTENT__=$(jq . $CONFIG_FILE_PATH)
[ -z "$__JSON_CONTENT__" ] && error "$CONFIG_FILE_PATH 尚未准备好!" && exit 0
# 编译后的执行文件, 执行前立即保密配置文件中的敏感数据.
# 本地执行, 则不需要保密
# keep_line keep_config_secret
# 容器运行的数据父目录
declare RunningParentDir=
# 容器备份的数据父目录
declare LocalBackupParentDir=
# 百度网盘备份的数据父目录
declare BaiduBackupParentDir=
# 本地备份列表(空格分隔的多个目录名字符串)
declare LocalBackups
declare BaiduAvailable
# 百度备份列表(空格分隔的多个目录名字符串)
declare BaiduBackups
# 暂时废弃
#declare LocalRepoTags
#declare WebRepoTags
# -------------------------------start ---------------------------------
__TEST__=
# 如果采用 [ ... ] && ... || ... 的形式, 当 choose_entity 退出码非 0 时,
# 将继续执行 unit_test_start, 这可能不是需要的
if [ "$__TEST__" ]; then
unit_test_start
else
clear
check_baidu_available BaiduAvailable
choose_entity
fi
caption "Thanks for your use. Byebye!"
# region Ignored
# 自编译部分, 详情查看 library/doc/compile.txt
if [ "$1" ]; then
# 配置文件先保密, 再复制
keep_config_secret
menu "start compiling ..."
# 极简调用方式, 但要注意将源文件的 compiling 块和 soucing 块的名称写成函数默认, 即 Ignored 和 Sourcing:
# 编译后的文件达到 230k, 以至于报错: Argument list too long: /bin/bash, 无法运行。
# 故针对当前的 shc 版本, 只能使用 script2execution 函数做打包之用(添加 -g 标志)
cmdline="script2execution
--target-path \"$ROOT_DIR_PATH/../../execution/$ROOT_DIR_NAME/${ENTRY_FILE_NAME%.*}\"
--attaches \"$CONFIG_FILE_REL_PATH data/sql_adt.cnf \"
--give-up-self-compile
"
perform --prompt "编译中, 请稍候......" --cmdline "$cmdline" -delay 0.2
aop "success -e" center "Compiling finished."
# 恢复本地 json 文件
echo $__JSON_CONTENT__ | jq . > $CONFIG_FILE_PATH --tab
fi
# endregion Ignored
可以看到, 包含任意命令行参数,表示自编译为可执行体。
接下来是配置文件 config.json:
{
"default": {
"dataPath": {
"root": "/app/freezone/wpsites",
"runningParentFmt": "%s/running",
"backupParentFmt": "%s/backup"
},
"dateFmt": "+%Y%m%d-%H%M%S",
"baidu": {
"root": "websites",
"backupParentFmt": "%s",
"timeout": 10
},
"prompt": {
"congratulations": "congratulations! All the work has been done successfully."
},
"mysql": {
"tag": "9.1.0",
"container": {
"volume": "/var/lib/mysql",
"addConfig": "/etc/mysql/conf.d/sql_adt.cnf"
},
"host": {
"addConfig": "data/sql_adt.cnf"
},
"timeout": 320
},
"wordpress": {
"tag": "php8.3-apache",
"container": {
"volume": "/var/www/html"
}
}
},
"guoshi": {
"mysql": {
// "rootPwd": "mysql root 用户密码待填入",
// "hostname": "mysql 主机名称待填入",
"port": 1111,
"container": {
"name": "guoshi-mysql-container"
},
"host": {
"addConfig": null
}
},
"wordpress": {
"tag": null,
"port": 1119,
"container": {
"name": "guoshi-wordpress-container",
"volume": null
},
"database": {
// "name": "wp 在数据库服务器中, 对应的数据库名",
// "account": "wp 对应数据库专用账号名称待填入",
// "password": "上述账号密码待填入",
"tablePrefix": "guoshi_",
"backupSqlName": "wp.sql"
},
"webstation": {
"title": "漠北清音"
}
}
},
"sanxiao": {
"mysql": {
// "rootPwd": "mysql root 用户密码待填入",
// "hostname": "mysql 主机名称待填入",
"port": 2111,
"container": {
"name": "sanxiao-mysql-container"
},
"host": {
"addConfig": null
}
},
"wordpress": {
"tag": null,
"port": 2119,
"container": {
"name": "sanxiao-wordpress-container",
"volume": null
},
"database": {
// "name": "wp 在数据库服务器中, 对应的数据库名",
// "account": "wp 对应数据库专用账号名称待填入",
// "password": "上述账号密码待填入",
"tablePrefix": "sanxiao_",
"backupSqlName": "wp.sql"
},
"webstation": {
"title": "中华神作"
}
}
}
}
【运行效果】
此处以新安装“中华神作”为例, 演示容器的创建、备份、还原、移除, 以及运行结束后的自编译。
其中上传备份到百度,速度比较慢,就不继续示范了。
最后是自编译示范:

