15 Commits

19 changed files with 2878 additions and 646 deletions
+5 -6
View File
@@ -50,22 +50,21 @@ build-amd64:
- docker buildx build --platform=linux/amd64 --pull -t "$IMG_URL$IMG_TAG-linux-amd64" . --push
- docker rmi "$IMG_URL$IMG_TAG-linux-amd64"
# 生成多架构制品,并在上传后删除本地文件
build-artifact:
stage: build
needs: [build-amd64, build-arm64]
script:
- docker manifest create "$IMG_URL$IMG_TAG" "$IMG_URL$IMG_TAG-linux-arm64" "$IMG_URL$IMG_TAG-linux-amd64"
- docker manifest push "$IMG_URL$IMG_TAG"
- docker manifest rm "$IMG_URL$IMG_TAG"
- docker manifest push -p "$IMG_URL$IMG_TAG"
# 测试阶段任务
test:
stage: test
script:
- docker run --rm --platform=linux/arm64 "$IMG_URL$IMG_TAG" --version
- sleep 1 && docker rmi -f "$IMG_URL$IMG_TAG"
- docker run --rm --platform=linux/amd64 "$IMG_URL$IMG_TAG" --version
- sleep 1 && docker rmi -f "$IMG_URL$IMG_TAG"
- docker run --pull always --rm --platform=linux/arm64 "$IMG_URL$IMG_TAG" --version
- docker run --pull always --rm --platform=linux/amd64 "$IMG_URL$IMG_TAG" --version
- docker images -q "$IMG_URL" | sort -u | xargs docker rmi -f
# 部署阶段任务
deploy:
+60 -70
View File
@@ -1,9 +1,8 @@
# Ver: 1.9 by Endial Fang (endial@126.com)
# Ver: 1.11 by Endial Fang (endial@126.com)
#
# 默认变量 ========================================================================
# 系统默认变量 ====================================================================
# 该部分变量为系统根据编译命令默认设置
# `TARGETPLATFORM`:构建后的目标平台信息。如 `linux/amd64``linux/arm/v7``windows/amd64`
# `TARGETOS`:目标平台信息(TARGETPLATFORM)中的操作系统部分,如:`linux`、`windows`
# `TARGETARCH`:目标平台信息(TARGETPLATFORM)中的平台架构部分,如:`amd64`、`arm`
@@ -16,18 +15,11 @@
# 可变参数 ========================================================================
# 该部分变量,在编译命令中通过 `--build-arg` 传入;如果未设置,则使用下面对应的默认值
# 设置当前应用名称及版本
ARG APP_NAME=redis
ARG APP_VER=6.2.13
# 设置默认仓库地址,默认为本地仓库;定义时需要包含末尾的`/`
ARG REGISTRY_URL="docker.colovu.com/"
# 设置 apt-get 源:default / ustc / aliyun
ARG APT_SOURCE=aliyun
# 编译镜像时指定用于加速的本地软件包存储服务器地址
ARG LOCAL_URL="http://local.colovu.com/dist"
ARG APP_NAME=redis # 设置当前应用名称
ARG APP_VER=6.2.13 # 设置当前应用版本
ARG REGISTRY_URL="docker.colovu.com/" # 设置默认仓库地址,默认为本地仓库;定义时需要包含末尾的`/`
ARG APT_SOURCE=aliyun # 设置 apt-get 源:default / ustc / aliyun
ARG LOCAL_URL="http://local.colovu.com/dist" # 编译镜像时指定用于加速的本地软件包存储服务器地址
# 0. 预处理 ======================================================================
FROM --platform=${TARGETPLATFORM:-linux/amd64} ${REGISTRY_URL}colovu/dbuilder:12 as builder
@@ -38,14 +30,11 @@ ARG APP_VER
ARG APT_SOURCE
ARG LOCAL_URL
# 选择软件包源(Optional),以加速后续软件包安装
# 选择软件包源加速后续软件包安装
RUN select_source ${APT_SOURCE};
# 安装依赖的软件包及库(Optional)
#RUN install_pkg xz-utils
# 设置工作目录
WORKDIR /tmp
# 安装依赖的软件包及库
RUN install_pkg libjemalloc2 libjemalloc-dev;
# 下载并解压软件包
RUN set -eux; \
@@ -61,17 +50,34 @@ RUN set -eux; \
RUN set -eux; \
APP_SRC="/tmp/${APP_NAME}-${APP_VER}"; \
cd ${APP_SRC}; \
# 禁用安全保护模式,在 Docker 中运行时不需要
\
# 禁用安全保护模式,在 Docker 中运行时不需要
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *1 *,.*[)],$' ./src/config.c; \
sed -ri 's!^( *createBoolConfig[(]"protected-mode",.*, *)1( *,.*[)],)$!\10\2!' ./src/config.c; \
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *0 *,.*[)],$' ./src/config.c; \
make MALLOC=libc BUILD_TLS=yes \
-j "$(nproc)" all; \
\
# 根据操作系统设置内存页大小
gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
extraJemallocConfigureFlags="--build=$gnuArch"; \
dpkgArch="$(dpkg --print-architecture)"; \
case "${dpkgArch##*-}" in \
amd64 | i386 | x32) extraJemallocConfigureFlags="$extraJemallocConfigureFlags --with-lg-page=12" ;; \
*) extraJemallocConfigureFlags="$extraJemallocConfigureFlags --with-lg-page=16" ;; \
esac; \
extraJemallocConfigureFlags="$extraJemallocConfigureFlags --with-lg-hugepage=21"; \
grep -F 'cd jemalloc && ./configure ' ./deps/Makefile; \
sed -ri 's!cd jemalloc && ./configure !&'"$extraJemallocConfigureFlags"' !' ./deps/Makefile; \
grep -F "cd jemalloc && ./configure $extraJemallocConfigureFlags " ./deps/Makefile; \
\
make MALLOC=libjemalloc2 BUILD_TLS=yes \
-j "$(nproc)" all; \
make PREFIX=/usr/local/${APP_NAME} install; \
# 将配置文件模板拷贝至应用安装目录的 etc/${APP_NAME} 目录下
\
# 将配置文件模板拷贝至应用安装目录的 etc/${APP_NAME} 目录下
mkdir -p /usr/local/${APP_NAME}/etc/${APP_NAME}; \
cp /tmp/${APP_NAME}-${APP_VER}/*.conf /usr/local/${APP_NAME}/etc/${APP_NAME}/; \
# 删除重复的应用程序,并生成对应的连接
\
# 删除重复的应用程序,并生成对应的连接
serverMd5="$(md5sum /usr/local/redis/bin/redis-server | cut -d' ' -f1)"; export serverMd5; \
find /usr/local/redis/bin/redis* -maxdepth 0 \
-type f -not -name redis-server \
@@ -89,12 +95,9 @@ RUN set -eux; \
# 检测并生成依赖文件记录
RUN set -eux; \
find /usr/local/${APP_NAME} -type f -executable -exec ldd '{}' ';' | \
awk '/=>/ { print $(NF-1) }' | \
sort -u | \
xargs -r readlink -f | \
xargs -r dpkg-query --search 2>/dev/null | \
cut -d: -f1 | \
sort -u >>/usr/local/${APP_NAME}/runDeps;
awk '/=>/ { print $(NF-1) }' | xargs -r basename -a | sort -u | \
xargs -r dpkg-query --search 2>/dev/null | cut -d: -f1 | sort -u \
>>/usr/local/${APP_NAME}/runDeps;
# 1. 生成镜像 =====================================================================
FROM --platform=${TARGETPLATFORM:-linux/amd64} ${REGISTRY_URL}colovu/debian:12
@@ -109,12 +112,8 @@ ENV APP_NAME=${APP_NAME} \
APP_VER=${APP_VER} \
APP_EXEC=redis-server \
APP_USER=${APP_NAME} \
APP_HOME_DIR=/usr/local/${APP_NAME} \
APP_DEF_DIR=/etc/${APP_NAME}
# 增加应用可执行文件及库文件搜索路径
ENV PATH="${APP_HOME_DIR}/sbin:${APP_HOME_DIR}/bin:${PATH}" \
LD_LIBRARY_PATH="${APP_HOME_DIR}/lib"
LD_LIBRARY_PATH="/usr/local/${APP_NAME}/lib" \
PATH="${PATH}:/usr/local/${APP_NAME}/bin"
LABEL \
"Version"="v${APP_VER}" \
@@ -122,56 +121,47 @@ LABEL \
"Github"="https://github.com/colovu/docker-${APP_NAME}" \
"Vendor"="Endial Fang (endial@126.com)"
# 从预处理过程中拷贝软件包(Optional),可以使用阶段编号或阶段命名定义来源
# 拷贝多阶段构建结果输出及客制化脚本
COPY --from=builder /usr/local/${APP_NAME} /usr/local/${APP_NAME}
# 拷贝应用使用的客制化脚本
COPY customer /
RUN set -eux; \
\
# 创建对应的用户及数据存储目录
prepare_env; \
useradd -U -u 996 -d /srv/${APP_NAME} -s /usr/sbin/nologin -r ${APP_USER}; \
mkdir -p /var/log/${APP_NAME} /var/run/${APP_NAME} /var/cache/${APP_NAME}; \
mkdir -p /srv/${APP_NAME}/conf /srv/${APP_NAME}/data /srv/${APP_NAME}/cert /srv/${APP_NAME}/log; \
chown -R ${APP_USER}:${APP_USER} /var/log/${APP_NAME} /var/run/${APP_NAME} /var/cache/${APP_NAME}; \
chown -R ${APP_USER}:${APP_USER} /usr/local/${APP_NAME} /srv/${APP_NAME}; \
\
/bin/bash -c "ln -sf /usr/local/${APP_NAME}/etc/${APP_NAME} /etc/"; \
\
# 选择软件包源(Optional),以加速后续软件包安装
# 选择软件包源,以加速后续软件包安装
select_source ${APT_SOURCE}; \
\
# 安装依赖的软件包及库(Optional)
install_pkg `cat /usr/local/${APP_NAME}/runDeps`; \
# 安装应用依赖的软件包及库
install_pkg ncat; \
install_pkg `cat /usr/local/${APP_NAME}/runDeps`; \
\
# 执行后处理脚本,并验证安装的应用
override_file="/usr/local/overrides/overrides-${APP_VER}.sh"; \
[ -e "${override_file}" ] && /bin/bash "${override_file}"; \
# 执行后处理脚本
overrideShell="/usr/local/overrides/overrides-${APP_VER}.sh"; \
[ -e "${overrideShell}" ] && /bin/bash "${overrideShell}"; \
\
# 验证安装的应用
redis-cli --version; \
${APP_EXEC} --version;
# 默认提供的数据卷
VOLUME ["/srv/conf", "/srv/data", "/srv/datalog", "/srv/cert", "/var/log"]
# 默认使用gosu切换为新建用户启动,必须保证端口在1024之上
# 配置容器的数据卷、工作目录及服务端口(必须保证端口在1024之上)
VOLUME ["/srv/${APP_NAME}/conf", "/srv/${APP_NAME}/data", "/srv/${APP_NAME}/cert", "/srv/${APP_NAME}/log"]
WORKDIR /srv/${APP_NAME}/data
EXPOSE 6379
# 关闭基础镜像的健康检查
#HEALTHCHECK NONE
#HEALTHCHECK --interval=30s --timeout=30s --retries=3 CMD curl -fs http://localhost:8080/ || exit 1
HEALTHCHECK --interval=10s --timeout=10s --retries=3 CMD netstat -ltun | grep 6379
# 应用健康状态检查
#HEALTHCHECK --interval=30s --timeout=30s --retries=3 \
# CMD curl -fs http://localhost:8080/ || exit 1
HEALTHCHECK --interval=10s --timeout=10s --retries=3 \
CMD netstat -ltun | grep 6379
# 使用 dumb-init 启动入口 Shell,确保容器可以接收控制信号;并使用前台方式启动应用程序
ENTRYPOINT ["dumb-init", "entry.sh"]
CMD ["run.sh"]
# 使用 non-root 用户运行后续的命令
USER 1001
# 设置工作目录
WORKDIR /srv/data
# 容器入口命令脚本,'/usr/local/bin/entry.sh'
ENTRYPOINT ["entry.sh"]
# 应用程序的启动命令,可为应用程序可执行命令或脚本
# 必须使用非守护进程方式运行,'/usr/local/bin/run.sh'
CMD ["run.sh"]
+57 -41
View File
@@ -15,12 +15,11 @@
**镜像信息:**
* 镜像地址:
- 阿里云: registry.cn-shenzhen.aliyuncs.com/colovu/redis:latest
- DockerHubcolovu/redis:latest
- Colovu Registry: docker.colovu.com/colovu/redis:latest
- 阿里云: registry.cn-shenzhen.aliyuncs.com/colovu/redis:6.2
- Colovu Registry: docker.colovu.com/colovu/redis:6.2
- 依赖镜像:colovu/debian:12
> 后续相关命令行默认使用`[Colovu Registry](https://docker.colovu.com)`镜像服务器做说明
> 后续相关命令行默认使用 Aliyun ACR 镜像服务器做说明
## TL;DR
@@ -28,10 +27,10 @@ Docker 快速启动命令:
```shell
# 从 Registry 服务器下载镜像并启动
$ docker run -d -e ALLOW_ANONYMOUS_LOGIN=yes --name imgname docker.colovu.com/colovu/redis:latest
$ docker run -d -e ALLOW_ANONYMOUS=yes --name imgname registry.cn-shenzhen.aliyuncs.com/colovu/redis:6.2
```
- `docker.colovu.com/colovu/imgname:<TAG>`:镜像名称及版本标签 TAG;标签不指定时默认使用`latest`
- `registry.cn-shenzhen.aliyuncs.com/colovu/imgname:<TAG>`:镜像名称及版本标签 TAG;标签不指定时默认使用`latest`
Docker-Compose 快速启动命令:
@@ -69,11 +68,11 @@ $ docker-compose -f docker-compose-cluster.yml up -d
镜像默认提供以下数据卷定义,默认数据分别存储在自动生成的应用名对应`redis`子目录中:
```shell
/srv/data # Redis 数据文件,主要存放Redis持久化数据;自动创建子目录redis
/srv/datalog # Redis 数据操作日志文件;自动创建子目录redis
/srv/conf # Redis 配置文件;自动创建子目录redis
/var/log # 日志文件,日志文件名为:redis.log
/var/run # 进程运行PID文件,PID文件名为:redis_6379.pid、redis_sentinel.pid
/srv/redis/data # Redis 数据文件,主要存放 Redis 持久化数据
/srv/redis/conf # Redis 配置文件
/srv/redis/cert # TLS 通讯证书文件
/srv/redis/log # 日志文件,日志文件名为:redis.log
/var/run/redis # 进程运行 PID 文件,PID 文件名为:redis.pid、redis_sentinel.pid
```
如果需要持久化存储相应数据,需要**在宿主机建立本地目录**,并在使用镜像初始化容器时进行映射。宿主机相关的目录中如果不存在对应应用 Redis 的子目录或相应数据文件,则容器会在初始化时创建相应目录及文件。
@@ -83,26 +82,54 @@ $ docker-compose -f docker-compose-cluster.yml up -d
在初始化 Redis 容器时,如果没有预置配置文件,可以在命令行中设置相应环境变量对默认参数进行修改。类似命令如下:
```shell
$ docker run -d -e "APP_ENV_KEY_NAME=key_value" docker.colovu.com/colovu/redis:latest
$ docker run -d -e "REDIS_CFG_KEYNAME=key_value" registry.cn-shenzhen.aliyuncs.com/colovu/redis:6.2
```
### 自动变量替换
针对应用配置文件中的配置项,支持由环境变量名自动替换生成,该类环境变量需要使用统一前缀,定义规则为:`REDIS_CFG_*=<val>`
- `REDIS_CFG_`:环境变量自动替换标识,具备该前缀的环境变量会被自动处理并更新至配置文件
- `*`:配置文件中对应的配置项名,大小写需要符合实际参数名要求;特殊字符需要符合`特殊字符替换规则`
- `<val>`:配置项对应值
**特殊字符替换规则**
因为 Shell 变量只能以字母、数字和下划线组成,针对'xml'、'ini'等配置文件中使用的'.'、'-'等特殊字符,需要进行重定义及转换。预定义如下:
+ "_" ==> "-" : 应用配置属性中的`-`(中划线),与环境变量中由`_`(单下划线)表示
+ "__" ==> "_" : 应用配置属性中的`_`(下划线),在环境变量中由`__`(双下划线)表示
+ "___" ==> "." : 应用配置属性中的`.`(半角点),在环境变量中由`___`(三下划线)表示
例如:
```shell
# 用于`key-value`类型的配置
REDIS_CFG_REQUIREPASS=colovu123
REDIS_CFG_APPENDONLY=no
# 容器启动后,应用配置文件中对应配置项生效,且设置为相应值:
requirepass colovu123
appendonly no
```
### 常规配置参数
常使用的环境变量主要包括:
- **ALLOW_ANONYMOUS_LOGIN**:默认值:**no**。设置是否允许无密码连接。如果没有设置`REDIS_PASSWORD`,则必须设置当前环境变量为 `yes`
- **REDIS_PASSWORD**:默认值:**无**。客户端认证的密码
- **REDIS_DISABLE_COMMANDS**:默认值:****。设置禁用的 Redis 命令
- **REDIS_AOF_ENABLED**:默认值:**yes**。设置是否启用 Append Only File 存储
- **ALLOW_ANONYMOUS**:默认值:**no**。设置是否允许无密码连接。如果没有设置`REDIS_CFG_REQUIREPASS`,则必须设置当前环境变量为 `yes`
- **REDIS_CFG_REQUIREPASS**:默认值:**无**。客户端认证的密码
- **REDIS_CFG_APPENDONLY**:默认值:**no**。设置是否启用 Append Only File 存储
### 可选配置参数
### 常规可选参数
如果没有必要,可选配置参数可以不用定义,直接使用对应的默认值,主要包括:
- **ENV_DEBUG**:默认值:**false**。设置是否输出容器调试信息。可设置为:1、true、yes
- **REDIS_PORT**:默认值:**6379**。设置用的默认客户访问端口
- **REDIS_PASSWORD_FILE**:默认值:**无**。以绝对地址指定的客户端认证用户密码存储文件。该路径指的是容器内的路径
- **REDIS_MASTER_PASSWORD_FILE**:默认值:**无**。以绝对地址指定的服务器密码存储文件。该路径指的是容器内的路径
- `ENV_DEBUG`:默认值:**false**。设置是否输出容器调试信息。可选值:false、no、true、yes
- **DISABLE_COMMANDS**:默认值:****。设置用的 Redis 命令
- **PASSWORD_FILE**:默认值:**无**。以绝对地址指定的客户端认证用户密码存储文件。该路径指的是容器内的路径
- **MASTER_PASSWORD_FILE**:默认值:**无**。以绝对地址指定的服务器密码存储文件。该路径指的是容器内的路径
- **REDIS_CFG_PORT**:默认值:**6379**。设置应用的默认客户访问端口
### Sentinel配置参数
@@ -110,28 +137,17 @@ $ docker run -d -e "APP_ENV_KEY_NAME=key_value" docker.colovu.com/colovu/redis:l
- **REDIS_SENTINEL_MASTER_NAME**:默认值:**无**
- **REDIS_SENTINEL_PORT_NUMBER**:默认值:**26379**。设置 Sentinel 默认端口
### 集群配置参数
使用 Redis 镜像,可以很容易的建立一个 [redis](https://redis.apache.org/doc/r3.1.2/redisAdmin.html) 集群。针对 redis 的集群模式(复制模式),有以下参数可以配置:
- **REDIS_REPLICATION_MOD**:默认值:**无**。当前主机在集群中的工作模式,可使用值为:`master`/`slave`/`replica`
- **REDIS_MASTER_HOST**:默认值:**无**。作为`slave`/`replica`时,对应的 master 主机名或 IP 地址
- **REDIS_MASTER_PORT_NUMBER**:默认值:**6379**。master 主机对应的端口
- **REDIS_MASTER_PASSWORD**:默认值:**无**。master 主机对应的登录验证密码
### TLS配置参数
使用证书加密传输时,相关配置参数如下:
- **REDIS_TLS_ENABLED**:启用或禁用 TLS。默认值:**no**
- **REDIS_TLS_PORT**:使用 TLS 加密传输的端口。默认值:**6379**
- **REDIS_TLS_CERT_FILE**TLS 证书文件。默认值:**无**
- **REDIS_TLS_KEY_FILE**TLS 私钥文件。默认值:**无**
- **REDIS_TLS_CA_FILE**TLS 根证书文件。默认值:****
- **REDIS_TLS_DH_PARAMS_FILE**:包含 DH 参数的配置文件 (DH 加密方式时需要)。默认值:**无**
- **REDIS_TLS_AUTH_CLIENTS**:配置客户端是否需要 TLS 认证。 默认值:**yes**
- **REDIS_CFG_TLS_PORT**:使用 TLS 加密传输的端口。默认值:**6379**
- **REDIS_CFG_TLS_CERT_FILE**TLS 证书文件。默认值:****
- **REDIS_CFG_TLS_KEY_FILE**TLS 私钥文件。默认值:**无**
- **REDIS_CFG_TLS_CA_CERT_FILE**TLS 根证书文件。默认值:**无**
- **REDIS_CFG_TLS_AUTH_CLIENTS**:配置客户端是否需要 TLS 认证。 默认值:**no**
当使用 TLS 时,则默认的 non-TLS 通讯被禁用。如果需要同时支持 TLS 与 non-TLS 通讯,可以使用参数`REDIS_TLS_PORT`配置容器使用不同的 TLS 端口。
当使用 TLS 时,则默认的 non-TLS 通讯被禁用。如果需要同时支持 TLS 与 non-TLS 通讯,可以使用参数`REDIS_CFG_TLS_PORT`配置容器使用不同的 TLS 端口。
## 安全
@@ -140,13 +156,13 @@ $ docker run -d -e "APP_ENV_KEY_NAME=key_value" docker.colovu.com/colovu/redis:l
Redis 镜像默认禁用了无密码访问功能,在实际生产环境中建议使用用户名及密码控制访问;如果为了测试需要,可以使用以下环境变量启用无密码访问功能:
```shell
ALLOW_ANONYMOUS_LOGIN=yes
ALLOW_ANONYMOUS=yes
```
通过配置环境变量`REDIS_PASSWORD`,可以启用基于密码的用户认证功能。命令行使用参考:
```shell
$ docker run -d -e APPNAME_PASSWORD=colovu docker.colovu.com/colovu/redis:latest
$ docker run -d -e REDIS_PASSWORD=colovu registry.cn-shenzhen.aliyuncs.com/colovu/redis:6.2
```
使用 Docker-Compose 时,`docker-compose.yml`应包含类似如下配置:
@@ -156,7 +172,7 @@ services:
redis:
...
environment:
- REDIS_PASSWORD=colovu
- REDIS_CFG_REQUIREPASS=colovu
...
```
+217 -343
View File
@@ -1,9 +1,8 @@
#!/bin/bash
# Ver: 1.2 by Endial Fang (endial@126.com)
# Ver: 1.4 by Endial Fang (endial@126.com)
#
# 应用通用业务处理函数
# 加载依赖脚本
. /colovu/lib/libcommon.sh # 通用函数库
. /colovu/lib/libfile.sh
@@ -13,17 +12,57 @@
. /colovu/lib/libservice.sh
. /colovu/lib/libvalidations.sh
# 函数列表
# 检测应用相应的配置文件是否存在,如果不存在,则从默认配置文件目录拷贝一份
# 默认配置文件路径:/etc/${APP_NAME}
# 目标配置文件路径:/srv/conf/${APP_NAME}
# 参数:
# $1 - 目标路径
# $2 - 源路径
# $* - 基础路径下的文件及目录列表,以" "分割
# 例子:
# ensure_config_file_exist /etc/${APP_NAME} conf.d server.conf
app_ensure_config_file_exist() {
local -r dist_path="${1:?dist paths is missing}"
local -r base_path="${2:?source paths is missing}"
local f=""
# 使用环境变量中以 "APP_CFG_" 开头的的全局变量更新配置文件中对应项(全小写,以"."分隔)
# 举例:
# APP_CFG_LOG_DIRS 对应配置文件中的配置项:log.dirs
redis_configure_from_env_variables() {
# Map environment variables to config properties
for var in "${!APP_CFG_@}"; do
key="$(echo "$var" | sed -e 's/^APP_CFG_//g' -e 's/_/\./g' | tr '[:upper:]' '[:lower:]')"
shift 2
LOG_D "List to check in ${base_path}: $@"
while [ "$#" -gt 0 ]; do
f="${1}"
LOG_D " Process \"${f}\""
if [ -d "${base_path}/${f}" ]; then
[[ ! -d "${dist_path}/${f}" ]] && LOG_D " Create directory: ${dist_path}/${f}" && mkdir -p "${dist_path}/${f}"
[[ ! -z $(ls -A "${base_path}/${f}") ]] && app_ensure_config_file_exist "${dist_path}/${f}" "${base_path}/${f}" $(ls -A "${base_path}/${f}")
else
[[ ! -e "${dist_path}/${f}" ]] && LOG_D " Copy: ${base_path}/${f} to ${dist_path}" && cp "${base_path}/${f}" "${dist_path}"
fi
shift
done
}
# 检测以 "<PREFIX>" 开头的环境变量,并更新指定配置文件中对应配置项的值
# 如果需要全部转换为小写,可使用命令: tr '[:upper:]' '[:lower:]'
# 环境变量与配置项替换规则 : 环境变量中下划线 ==> 配置参数中特殊字符
# - "_" ==> "-"(中划线)
# - "__" ==> "_"(下划线)
# - "___" ==> "."(半角点)
#
# 变量:
# $1 - 配置文件
# $2 - <PREFIX>前缀(不含结束的"_")
app_configure_from_environment() {
local confFile="${1:?missing file}"
local envPrefix="${2:-APP_CFG}"
LOG_D "Configuration File: ${confFile}"
# 更新普通key-value配置文件,转换为小写后写入文件
for var in $(eval echo \${!${envPrefix}_@}); do
key="$(echo "$var" | sed -e 's/^'${envPrefix}'_//g' -e 's/___/./g' -e 's/__/--/g' -e 's/_/-/g' -e 's/--/_/g' | tr '[:upper:]' '[:lower:]')"
value="${!var}"
redis_conf_set "$key" "$value"
LOG_D " ${key} ${value}"
app_common_conf_set "$confFile" "$key" "$value"
done
}
@@ -32,7 +71,7 @@ redis_configure_from_env_variables() {
# $1 - 文件
# $2 - 变量
# $3 - 值(列表)
redis_common_conf_set() {
app_common_conf_set() {
local file="${1:?missing file}"
local key="${2:?missing key}"
shift
@@ -44,7 +83,7 @@ redis_common_conf_set() {
return 1
elif [[ "${#values[@]}" -ne 1 ]]; then
for i in "${!values[@]}"; do
redis_common_conf_set "$file" "${key[$i]}" "${values[$i]}"
app_common_conf_set "$file" "${key[$i]}" "${values[$i]}"
done
else
value="${values[0]}"
@@ -53,10 +92,30 @@ redis_common_conf_set() {
value="${value//&/\\&}"
value="${value//\?/\\?}"
[[ "$value" = "" ]] && value="\"$value\""
# Check if the value was set before
if grep -q "^[# ]*${key} .*" "$file"; then
# Update the existing key
replace_in_file "$file" "^[# ]*${key} .*" "${key} ${value}" false
# 检测配置文件中是否有相应的配置项(含以"#"起始的)
if grep -q "^[#]*${key} .*" "$file"; then
# 更新当前被注释掉的配置项(以“#”开始)
case ${key} in
include)
if grep -q "^${key} ${value}.*" "$file"; then
LOG_W "Duplicated include: ${value}"
else
LOG_D "Add new include: ${value}"
replace_in_file "$file" "^#${key} .*" "#include /path/to/local.conf\n${key} ${value}" false
fi
;;
loadmodule)
if grep -q "^${key} ${value}.*" "$file"; then
LOG_W "Duplicated loadmodule: ${value}"
else
LOG_D "Add new loadmodule: ${value}"
replace_in_file "$file" "^#${key} .*" "#loadmodule /path/to/my_module.so\n${key} ${value}" false
fi
;;
*)
replace_in_file "$file" "^[#]*${key} .*" "${key} ${value}" false
;;
esac
else
# 增加一个新的配置项;如果在其他位置有类似操作,需要注意换行
printf "\n%s %s" "$key" "$value" >>"$file"
@@ -64,151 +123,21 @@ redis_common_conf_set() {
fi
}
# 获取配置文件中指定关键字对应的值
# 变量:
# $1 - 变量
redis_conf_get() {
local key="${1:?missing key}"
# 使用环境变量中配置,更新配置文件
app_update_conf() {
LOG_I "Update configure files..."
app_configure_from_environment "${REDIS_CONF_FILE}" "REDIS_CFG"
grep -E "^\s*$key " "${REDIS_CONF_FILE}" | awk '{print $2}'
}
# 更新 redis.conf 配置文件中指定变量值,设置关键字及对应值
# 变量:
# $1 - 变量
# $2 - 值(列表)
redis_conf_set() {
redis_common_conf_set "${REDIS_CONF_FILE}" "$@"
}
# 更新 sentinel.conf 配置文件中指定变量值,设置关键字及对应值
# 变量:
# $1 - 变量
# $2 - 值(列表)
redis_sentinel_conf_set() {
redis_common_conf_set "${REDIS_SENTINEL_FILE}" "$@"
}
# 更新 redis.conf 配置文件中指定变量值,取消关键字设置信息
# 变量:
# $1 - 变量
redis_conf_unset() {
local key="${1:?missing key}"
remove_in_file "${REDIS_CONF_FILE}" "^\s*$key .*" false
}
# 获取 Redis 版本信息
redis_version() {
redis-cli --version | grep -E -o "[0-9]+.[0-9]+.[0-9]+"
}
# 获取 Redis 主版本号
redis_major_version() {
redis_version | grep -E -o "^[0-9]+"
}
# 禁用 Redis 不安全的命令
# 参数:
# $1 - 待禁用的命令列表
redis_disable_unsafe_commands() {
# The current syntax gets a comma separated list of commands, we split them
# before passing to redis_disable_unsafe_commands
read -r -a disabledCommands <<< "$(tr ',' ' ' <<< "$REDIS_DISABLE_COMMANDS")"
LOG_D "Disabling commands: ${disabledCommands[*]}"
echo "" >> "${REDIS_CONF_FILE}"
for cmd in "${disabledCommands[@]}"; do
if grep -E -q "^\s*rename-command\s+$cmd\s+\"\"\s*$" "${REDIS_CONF_FILE}"; then
LOG_D "$cmd was already disabled"
continue
fi
echo "rename-command $cmd \"\"" >> "${REDIS_CONF_FILE}"
done
}
# 生成默认配置文件
redis_generate_conf() {
redis_conf_set port "$REDIS_PORT"
redis_conf_set dir "${APP_DATA_DIR}"
redis_conf_set logfile "${APP_LOG_DIR}/redis.log" # Log to stdout
redis_conf_set pidfile "${REDIS_PID_FILE}"
redis_conf_set daemonize no
redis_conf_set bind 127.0.0.1 # disallow remote connections when init
# Enable AOF https://redis.io/topics/persistence#append-only-file
# Leave default fsync (every second)
redis_conf_set appendonly "${REDIS_AOF_ENABLED}"
# Disable RDB persistence, AOF persistence already enabled.
# Ref: https://redis.io/topics/persistence#interactions-between-aof-and-rdb-persistence
redis_conf_set save ""
# TLS configuration
if is_boolean_yes "$REDIS_TLS_ENABLED"; then
if [[ "$REDIS_PORT" == "6379" ]] && [[ "$REDIS_TLS_PORT" == "6379" ]]; then
# If both ports are set to default values, enable TLS traffic only
redis_conf_set port 0
redis_conf_set tls-port "$REDIS_TLS_PORT"
else
# Different ports were specified
redis_conf_set port "$REDIS_PORT"
redis_conf_set tls-port "$REDIS_TLS_PORT"
fi
redis_conf_set tls-cert-file "$REDIS_TLS_CERT_FILE"
redis_conf_set tls-key-file "$REDIS_TLS_KEY_FILE"
redis_conf_set tls-ca-cert-file "$REDIS_TLS_CA_FILE"
[[ -n "$REDIS_TLS_DH_PARAMS_FILE" ]] && redis_conf_set tls-dh-params-file "$REDIS_TLS_DH_PARAMS_FILE"
redis_conf_set tls-auth-clients "$REDIS_TLS_AUTH_CLIENTS"
fi
if [[ -n "$REDIS_PASSWORD" ]]; then
redis_conf_set requirepass "$REDIS_PASSWORD"
else
redis_conf_unset requirepass
fi
if [[ -n "$REDIS_DISABLE_COMMANDS" ]]; then
redis_disable_unsafe_commands
fi
}
# 配置 Redis 复制模式参数
# 参数:
# $1 - 复制模式
redis_configure_replication() {
LOG_I "Configuring replication mode..."
redis_conf_set replica-announce-ip "$(get_machine_ip)"
redis_conf_set replica-announce-port "$REDIS_MASTER_PORT_NUMBER"
if [[ "$REDIS_REPLICATION_MODE" = "master" ]]; then
if [[ -n "$REDIS_PASSWORD" ]]; then
redis_conf_set masterauth "$REDIS_PASSWORD"
fi
elif [[ "$REDIS_REPLICATION_MODE" =~ ^(slave|replica)$ ]]; then
if [[ -n "$REDIS_SENTINEL_HOST" ]]; then
local sentinel_info_command
if is_boolean_yes "$REDIS_TLS_ENABLED"; then
sentinel_info_command="redis-cli -h ${REDIS_SENTINEL_HOST} -p ${REDIS_SENTINEL_PORT_NUMBER} --tls --cert ${REDIS_TLS_CERT_FILE} --key ${REDIS_TLS_KEY_FILE} --cacert ${REDIS_TLS_CA_FILE} sentinel get-master-addr-by-name ${REDIS_SENTINEL_MASTER_NAME}"
else
sentinel_info_command="redis-cli -h ${REDIS_SENTINEL_HOST} -p ${REDIS_SENTINEL_PORT_NUMBER} sentinel get-master-addr-by-name ${REDIS_SENTINEL_MASTER_NAME}"
fi
REDIS_SENTINEL_INFO=($($sentinel_info_command))
REDIS_MASTER_HOST=${REDIS_SENTINEL_INFO[0]}
REDIS_MASTER_PORT_NUMBER=${REDIS_SENTINEL_INFO[1]}
fi
LOG_I "Waitting for Redis Master ready..."
redis_wait_service "${REDIS_MASTER_HOST}:${REDIS_MASTER_PORT_NUMBER}"
[[ -n "$REDIS_MASTER_PASSWORD" ]] && redis_conf_set masterauth "$REDIS_MASTER_PASSWORD"
# Starting with Redis 5, use 'replicaof' instead of 'slaveof'. Maintaining both for backward compatibility
local parameter="replicaof"
[[ $(redis_major_version) -lt 5 ]] && parameter="slaveof"
redis_conf_set "$parameter" "$REDIS_MASTER_HOST $REDIS_MASTER_PORT_NUMBER"
# Configure replicas to use TLS for outgoing connections to the master
if is_boolean_yes "$REDIS_TLS_ENABLED"; then
redis_conf_set tls-replication yes
fi
if [[ -n "$DISABLE_COMMANDS" ]]; then
app_disable_unsafe_commands
fi
}
# 检测用户参数信息是否满足条件; 针对部分权限过于开放情况,打印提示信息
redis_verify_minimum_env() {
app_verify_minimum_env() {
local error_code=0
LOG_D "Validating settings in REDIS_* env vars..."
LOG_D "Validating settings in ENV vars..."
print_validation_error() {
LOG_E "$1"
@@ -216,77 +145,63 @@ redis_verify_minimum_env() {
}
# Redis authentication validations
if is_boolean_yes "$ALLOW_ANONYMOUS_LOGIN"; then
LOG_W "You set the environment variable ALLOW_ANONYMOUS_LOGIN=${ALLOW_ANONYMOUS_LOGIN}. For safety reasons, do not use this flag in a production environment."
elif [[ -z "$REDIS_PASSWORD" ]]; then
print_validation_error "The REDIS_PASSWORD environment variable is empty or not set. Set the environment variable ALLOW_ANONYMOUS_LOGIN=yes to allow the container to be started with blank passwords. This is recommended only for development."
if is_boolean_yes "$ALLOW_ANONYMOUS"; then
LOG_W "You set the environment variable ALLOW_ANONYMOUS=${ALLOW_ANONYMOUS}. For safety reasons, do not use this flag in a production environment."
elif [[ -z "$REDIS_CFG_REQUIREPASS" ]]; then
print_validation_error "The REDIS_CFG_REQUIREPASS environment variable is empty or not set. Set the environment variable ALLOW_ANONYMOUS=yes to allow the container to be started with blank passwords. This is recommended only for development."
fi
if [[ -n "$REDIS_REPLICATION_MODE" ]]; then
if [[ "$REDIS_REPLICATION_MODE" =~ ^(slave|replica)$ ]]; then
if [[ -n "$REDIS_MASTER_PORT_NUMBER" ]]; then
if ! err=$(validate_port "$REDIS_MASTER_PORT_NUMBER"); then
print_validation_error "An invalid port was specified in the environment variable REDIS_MASTER_PORT_NUMBER: $err"
fi
fi
if ! is_boolean_yes "$ALLOW_ANONYMOUS_LOGIN" && [[ -z "$REDIS_MASTER_PASSWORD" ]]; then
print_validation_error "The REDIS_MASTER_PASSWORD environment variable is empty or not set. Set the environment variable ALLOW_ANONYMOUS_LOGIN=yes to allow the container to be started with blank passwords. This is recommended only for development."
fi
elif [[ "$REDIS_REPLICATION_MODE" != "master" ]]; then
print_validation_error "Invalid replication mode. Available options are 'master/replica'"
fi
fi
if is_boolean_yes "$REDIS_TLS_ENABLED"; then
if [[ "$REDIS_PORT" == "$REDIS_TLS_PORT" ]] && [[ "$REDIS_PORT" != "6379" ]]; then
# If both ports are assigned the same numbers and they are different to the default settings
print_validation_error "Enviroment variables REDIS_PORT and REDIS_TLS_PORT point to the same port number (${REDIS_PORT}). Change one of them or disable non-TLS traffic by setting REDIS_PORT=0"
fi
if [[ -z "$REDIS_TLS_CERT_FILE" ]]; then
if [[ -n "${REDIS_CFG_TLS_PORT:-}" ]]; then
if [[ -z "${REDIS_CFG_TLS_CERT_FILE:-}" ]]; then
print_validation_error "You must provide a X.509 certificate in order to use TLS"
elif [[ ! -f "$REDIS_TLS_CERT_FILE" ]]; then
print_validation_error "The X.509 certificate file in the specified path ${REDIS_TLS_CERT_FILE} does not exist"
elif [[ ! -f "$REDIS_CFG_TLS_CERT_FILE" ]]; then
print_validation_error "The X.509 certificate file in the specified path ${REDIS_CFG_TLS_CERT_FILE} does not exist"
fi
if [[ -z "$REDIS_TLS_KEY_FILE" ]]; then
if [[ -z "${REDIS_CFG_TLS_KEY_FILE:-}" ]]; then
print_validation_error "You must provide a private key in order to use TLS"
elif [[ ! -f "$REDIS_TLS_KEY_FILE" ]]; then
print_validation_error "The private key file in the specified path ${REDIS_TLS_KEY_FILE} does not exist"
elif [[ ! -f "$REDIS_CFG_TLS_KEY_FILE" ]]; then
print_validation_error "The private key file in the specified path ${REDIS_CFG_TLS_KEY_FILE} does not exist"
fi
if [[ -z "$REDIS_TLS_CA_FILE" ]]; then
if [[ -z "${REDIS_CFG_TLS_CA_CERT_FILE:-}" ]]; then
print_validation_error "You must provide a CA X.509 certificate in order to use TLS"
elif [[ ! -f "$REDIS_TLS_CA_FILE" ]]; then
print_validation_error "The CA X.509 certificate file in the specified path ${REDIS_TLS_CA_FILE} does not exist"
elif [[ ! -f "$REDIS_CFG_TLS_CA_CERT_FILE" ]]; then
print_validation_error "The CA X.509 certificate file in the specified path ${REDIS_CFG_TLS_CA_CERT_FILE} does not exist"
fi
if [[ -n "$REDIS_TLS_DH_PARAMS_FILE" ]] && [[ ! -f "$REDIS_TLS_DH_PARAMS_FILE" ]]; then
print_validation_error "The DH param file in the specified path ${REDIS_TLS_DH_PARAMS_FILE} does not exist"
if [[ -n "${REDIS_CFG_TLS_DH_PARAMS_FILE:-}" ]] && [[ ! -f "$REDIS_CFG_TLS_DH_PARAMS_FILE" ]]; then
print_validation_error "The DH param file in the specified path ${REDIS_CFG_TLS_DH_PARAMS_FILE} does not exist"
fi
fi
[[ "$error_code" -eq 0 ]] || exit "$error_code"
}
# 更改默认监听地址为 "*" 或 "0.0.0.0",以对容器外提供服务;默认配置文件应当为仅监听 localhost(127.0.0.1)
redis_enable_remote_connections() {
LOG_D "Modify default config to enable all IP access"
# 更改默认监听地址为 "*" 或 "0.0.0.0",以对容器外提供服务
app_enable_remote_connections() {
LOG_I "Modify default config to ENABLE external IP access"
redis_conf_set daemonize no
redis_conf_set bind 0.0.0.0 # Allow remote connections
app_conf_set bind "* -::*"
}
# 更改默认监听地址为 "localhost" 或 "127.0.0.1",以禁止对容器外提供服务
app_disable_remote_connections() {
LOG_I "Modify default config to DISABLE external IP access"
app_conf_set bind "127.0.0.1 -::1"
}
# 检测依赖的服务端口是否就绪;该脚本依赖系统工具 'netcat'
# 参数:
# $1 - host:port
redis_wait_service() {
local serviceport=${1:?Missing server info}
local service=${serviceport%%:*}
local port=${serviceport#*:}
app_wait_service() {
local serviceInfo=${1:?Missing server info}
local service=${serviceInfo%%:*}
local port=${serviceInfo#*:}
local retry_seconds=5
local max_try=100
local max_try=6
let i=1
if [[ -z "$(which nc)" ]]; then
LOG_E "Nedd nc installed before, command: \"apt-get install netcat\"."
exit 1
install_pkg ncat
fi
LOG_I "[0/${max_try}] check for ${service}:${port}..."
@@ -296,13 +211,12 @@ redis_wait_service() {
result=$?
until [ $result -eq 0 ]; do
LOG_D " [$i/${max_try}] not available yet"
if (( $i == ${max_try} )); then
LOG_E "${service}:${port} is still not available; giving up after ${max_try} tries."
exit 1
fi
LOG_D " [$i/${max_try}] not available yet, try in ${retry_seconds}'s latter ..."
LOG_I "[$i/${max_try}] try in ${retry_seconds}s once again ..."
let "i++"
sleep ${retry_seconds}
@@ -315,97 +229,89 @@ redis_wait_service() {
}
# 以后台方式启动应用服务,并等待启动就绪
redis_start_server_bg() {
redis_is_server_running && return
app_start_server_bg() {
app_is_server_running && return
LOG_I "Starting ${APP_NAME} in background..."
local pass
pass="$(cat /dev/urandom | head -n 24 | md5sum | head -c 24)"
export REDISCLI_AUTH="${pass}"
if is_boolean_yes "${ENV_DEBUG}"; then
"redis-server" "${REDIS_CONF_FILE}" "--daemonize" "yes"
"redis-server" "${REDIS_CONF_FILE}" "--daemonize" "yes" "--requirepass" "${pass}"
else
"redis-server" "${REDIS_CONF_FILE}" "--daemonize" "yes" >/dev/null 2>&1
"redis-server" "${REDIS_CONF_FILE}" "--daemonize" "yes" "--requirepass" "${pass}" >/dev/null 2>&1
fi
local counter=3
while ! redis_is_server_running ; do
LOG_D "Checking ${APP_NAME} ready status..."
local counter=10
while ! app_is_server_running ; do
LOG_D "Waiting for ${APP_NAME} to ready ... $counter"
if [[ "$counter" -ne 0 ]]; then
break
fi
sleep 1;
counter=$((counter - 1))
done
# 通过命令或特定端口检测应用是否就绪
LOG_I "Checking ${APP_NAME} ready status..."
#wait-for-port --timeout 60 "$REDIS_PORT"
LOG_D "${APP_NAME} is ready for service..."
}
# 停止应用服务
redis_stop_server() {
redis_is_server_running || return
app_stop_server() {
if app_is_server_running ; then
LOG_I "Stopping ${APP_NAME}..."
local pass
local port
local args
LOG_I "Stopping ${APP_NAME}..."
# 已通过环境变量设置了 REDISCLI_AUTH
if is_boolean_yes "${ENV_DEBUG}"; then
"redis-cli" shutdown
else
"redis-cli" shutdown >/dev/null 2>&1
fi
pass="$(redis_conf_get "requirepass")"
is_boolean_yes "$REDIS_TLS_ENABLED" && port="$(redis_conf_get "tls-port")" || port="$(redis_conf_get "port")"
[[ -n "$pass" ]] && args+=("-a" "\"$pass\"")
[[ "$port" != "0" ]] && args+=("-p" "$port")
#args+=("--daemonize" "yes")
if is_boolean_yes "${ENV_DEBUG}"; then
"redis-cli" "${args[@]}" shutdown
else
"redis-cli" "${args[@]}" shutdown >/dev/null 2>&1
fi
# 检测停止是否完成
local counter=5
while [[ "$counter" -ne 0 ]] && is_app_server_running; do
LOG_D "Waiting for ${APP_NAME} to stop..."
sleep 1
counter=$((counter - 1))
done
# 检测停止是否完成
LOG_D "Checking ${APP_NAME} running status..."
local counter=10
while [[ "$counter" -ne 0 ]] && app_is_server_running; do
LOG_D "Waiting for ${APP_NAME} to stop ... $counter"
sleep 1
counter=$((counter - 1))
done
fi
}
# 检测应用服务是否在后台运行中
redis_is_server_running() {
app_is_server_running() {
LOG_D "Check if ${APP_NAME} is running..."
local pid
pid="$(get_pid_from_file "${REDIS_PID_FILE}")"
pid="$(get_pid_from_file '${REDIS_PID_FILE}')"
LOG_D "${APP_NAME} PID: ${pid}"
if [[ -z "${pid}" ]]; then
false
else
if [[ -n "${pid}" ]]; then
is_service_running "${pid}"
else
false
fi
}
redis_is_server_not_running() {
! redis_is_server_running
app_is_server_not_running() {
if [[ app_is_server_running == false ]]; then
true
else
flse
fi
}
# 清理初始化应用时生成的临时文件
redis_clean_tmp_file() {
app_clean_tmp_file() {
LOG_D "Clean ${APP_NAME} tmp files for init..."
}
# 在重新启动容器时,删除标志文件及必须删除的临时文件 (容器重新启动)
redis_clean_from_restart() {
LOG_D "Clean ${APP_NAME} tmp files for restart..."
local -r -a files=(
"${REDIS_PID_FILE}"
)
for file in ${files[@]}; do
if [[ -f "$file" ]]; then
LOG_I "Cleaning stale $file file"
LOG_D " Remove $file"
rm "$file"
fi
done
@@ -413,86 +319,31 @@ redis_clean_from_restart() {
# 应用默认初始化操作
# 执行完毕后,生成文件 ${APP_CONF_DIR}/.app_init_flag 及 ${APP_DATA_DIR}/.data_init_flag 文件
redis_default_init() {
redis_clean_from_restart
LOG_D "Check init status of ${APP_NAME}..."
app_default_init() {
LOG_I "Process default init for ${APP_NAME}..."
# 检测配置文件是否存在
if [[ ! -f "${APP_CONF_DIR}/.app_init_flag" ]]; then
LOG_I "No injected configuration file found, creating default config files..."
redis_generate_conf
# Configure Replication mode
if [[ -n "$REDIS_REPLICATION_MODE" ]]; then
redis_configure_replication
fi
touch "${APP_CONF_DIR}/.app_init_flag"
echo "$(date '+%Y-%m-%d %H:%M:%S') : Init success." >> "${APP_CONF_DIR}/.app_init_flag"
else
LOG_I "User injected custom configuration detected!"
fi
if [[ ! -f "${APP_DATA_DIR}/.data_init_flag" ]]; then
LOG_I "Deploying ${APP_NAME} from scratch..."
# 启动后台服务
#redis_start_server_bg
touch ${APP_DATA_DIR}/.data_init_flag
echo "$(date '+%Y-%m-%d %H:%M:%S') : Init success." >> ${APP_DATA_DIR}/.data_init_flag
else
LOG_I "Deploying ${APP_NAME} with persisted data..."
fi
}
# 用户自定义的前置初始化操作,依次执行目录 preinitdb.d 中的初始化脚本
# 执行完毕后,生成文件 ${APP_DATA_DIR}/.custom_preinit_flag
redis_custom_preinit() {
LOG_I "Check custom pre-init status of ${APP_NAME}..."
# 检测用户配置文件目录是否存在 preinitdb.d 文件夹,如果存在,尝试执行目录中的初始化脚本
if [ -d "/srv/conf/${APP_NAME}/preinitdb.d" ]; then
# 检测数据存储目录是否存在已初始化标志文件;如果不存在,检索可执行脚本文件并进行初始化操作
if [[ -n $(find "/srv/conf/${APP_NAME}/preinitdb.d/" -type f -regex ".*\.\(sh\)") ]] && \
[[ ! -f "${APP_DATA_DIR}/.custom_preinit_flag" ]]; then
LOG_I "Process custom pre-init scripts from /srv/conf/${APP_NAME}/preinitdb.d..."
# 检索所有可执行脚本,排序后执行
find "/srv/conf/${APP_NAME}/preinitdb.d/" -type f -regex ".*\.\(sh\)" | sort | process_init_files
touch "${APP_DATA_DIR}/.custom_preinit_flag"
echo "$(date '+%Y-%m-%d %H:%M:%S') : Init success." >> "${APP_DATA_DIR}/.custom_preinit_flag"
LOG_I "Custom preinit for ${APP_NAME} complete."
else
LOG_I "Custom preinit for ${APP_NAME} already done before, skipping initialization."
fi
fi
# 检测依赖的服务是否就绪
#for i in ${SERVICE_PRECONDITION[@]}; do
# redis_wait_service "${i}"
#done
app_update_conf
app_enable_remote_connections
}
# 用户自定义的应用初始化操作,依次执行目录initdb.d中的初始化脚本
# 执行完毕后,生成文件 ${APP_DATA_DIR}/.custom_init_flag
redis_custom_init() {
LOG_I "Check custom initdb status of ${APP_NAME}..."
app_custom_init() {
LOG_I "Process customer init ${APP_NAME}..."
# 检测用户配置文件目录是否存在 initdb.d 文件夹,如果存在,尝试执行目录中的初始化脚本
if [ -d "/srv/conf/${APP_NAME}/initdb.d" ]; then
if [ -d "${APP_CONF_DIR}/initdb.d" ]; then
# 检测数据存储目录是否存在已初始化标志文件;如果不存在,检索可执行脚本文件并进行初始化操作
if [[ -n $(find "/srv/conf/${APP_NAME}/initdb.d/" -type f -regex ".*\.\(sh\|sql\|sql.gz\)") ]] && \
if [[ -n $(find "${APP_CONF_DIR}/initdb.d/" -type f -regex ".*\.\(sh\|sql\|sql.gz\)") ]] && \
[[ ! -f "${APP_DATA_DIR}/.custom_init_flag" ]]; then
LOG_I "Process custom init scripts from /srv/conf/${APP_NAME}/initdb.d..."
LOG_I "Process custom init scripts from ${APP_CONF_DIR}/initdb.d..."
# 启动后台服务
#redis_start_server_bg
app_disable_remote_connections
app_start_server_bg
# 检索所有可执行脚本,排序后执行
find "/srv/conf/${APP_NAME}/initdb.d/" -type f -regex ".*\.\(sh\|sql\|sql.gz\)" | sort | while read -r f; do
find "${APP_CONF_DIR}/initdb.d/" -type f -regex ".*\.\(sh\|sql\|sql.gz\)" | sort | while read -r f; do
case "$f" in
*.sh)
if [[ -x "$f" ]]; then
@@ -501,15 +352,7 @@ redis_custom_init() {
LOG_D "Sourcing $f"; . "$f"
fi
;;
*.sql)
LOG_D "Executing $f";
postgresql_execute "${PG_DATABASE}" "${PG_INITSCRIPTS_USERNAME}" "${PG_INITSCRIPTS_PASSWORD}" < "$f"
;;
*.sql.gz)
LOG_D "Executing $f";
gunzip -c "$f" | postgresql_execute "${PG_DATABASE}" "${PG_INITSCRIPTS_USERNAME}" "${PG_INITSCRIPTS_PASSWORD}"
;;
*)
*)
LOG_D "Ignoring $f" ;;
esac
done
@@ -517,18 +360,49 @@ redis_custom_init() {
touch "${APP_DATA_DIR}/.custom_init_flag"
echo "$(date '+%Y-%m-%d %H:%M:%S') : Init success." >> "${APP_DATA_DIR}/.custom_init_flag"
LOG_I "Custom init for ${APP_NAME} complete."
# 检测服务是否运行中;如果运行,则停止后台服务
app_is_server_running && app_stop_server
app_clean_tmp_file
app_enable_remote_connections
else
LOG_I "Custom init for ${APP_NAME} already done before, skipping initialization."
fi
fi
# 检测服务是否运行中;如果运行,则停止后台服务
redis_is_server_running && redis_stop_server
# 删除第一次运行生成的临时文件
redis_clean_tmp_file
# 绑定所有 IP ,启用远程访问
redis_enable_remote_connections
}
# 更新 redis.conf 配置文件中指定变量值,设置关键字及对应值
# 变量:
# $1 - 变量
# $2 - 值(列表)
app_conf_set() {
app_common_conf_set "${REDIS_CONF_FILE}" "$@"
}
# 获取配置文件中指定关键字对应的值
# 变量:
# $1 - 变量
app_conf_get() {
local key="${1:?missing key}"
grep -E "^$key " "${REDIS_CONF_FILE}" | awk '{print $2}'
}
# 禁用 Redis 不安全的命令
# 参数:
# $1 - 待禁用的命令列表
app_disable_unsafe_commands() {
# The current syntax gets a comma(",") separated list of commands, we split them
# before passing to redis_disable_unsafe_commands
read -r -a disabledCommands <<< "$(tr ',' ' ' <<< "$DISABLE_COMMANDS")"
LOG_D "Disabling commands: ${disabledCommands[*]}"
echo "" >> "${REDIS_CONF_FILE}"
for cmd in "${disabledCommands[@]}"; do
if grep -E -q "^\s*rename-command\s+$cmd\s+\"\"\s*$" "${REDIS_CONF_FILE}"; then
LOG_D "$cmd was already disabled"
continue
fi
echo "rename-command $cmd \"\"" >> "${REDIS_CONF_FILE}"
done
}
+16 -14
View File
@@ -1,29 +1,31 @@
#!/bin/bash
# Ver: 1.3 by Endial Fang (endial@126.com)
#!/usr/bin/dumb-init /bin/bash
# Ver: 1.5 by Endial Fang (endial@126.com)
#
# 容器入口脚本
# 容器入口脚本;当前脚本执行完毕时,使用默认用户执行镜像 CMD 定义的命令(默认为'/usr/local/bin/run.sh'
# 设置 shell 执行参数,可使用'-'(打开)'+'(关闭)控制。常用:
# -e: 命令执行错误则报错(errexit); -u: 变量未定义则报错(nounset); -x: 打印实际待执行的命令行; -o pipefail: 设置管道中命令遇到失败则报错
set -eu
set -o pipefail
set -euo pipefail
. /colovu/lib/libcommon.sh # 加载通用函数库
. /colovu/lib/libcommon.sh # 加载通用函数库
. /usr/local/bin/environment.sh # 设置环境变量
LOG_I "** Processing entry.sh **"
if [[ "$*" = "/usr/local/bin/run.sh" ]]; then
print_image_welcome
# 优先处理'-'开始的版本信息、帮助信息显示命令,如果是该类命令,处理后退出容器
[[ "${1:0:1}" == '-' ]] && set -- "${APP_EXEC:-/bin/bash}" "$@" && print_command_help "$@"
LOG_I "** Starting ${APP_NAME} setup **"
# 处理 root 用户**且**使用默认启动脚本时的初始化
if [[ "$(id -u)" == '0' ]] && [[ "$1" == "run.sh" ]]; then
print_welcome_info
/usr/local/bin/setup.sh
/usr/local/bin/init.sh
LOG_I "** ${APP_NAME} setup finished! **"
# 执行应用启动脚本并替换当前进程
exec gosu "${APP_USER}" "$@"
fi
# 检测是否仅打印帮助信息
[ "${1:0:1}" = '-' ] && set -- "${APP_EXEC:-/bin/bash}" "$@"
print_command_help "$@"
# 处理非以上情形的自定义命令
LOG_I "Start container with command: $@"
exec "$@"
+33 -50
View File
@@ -1,17 +1,16 @@
#!/bin/bash
# Ver: 1.1 by Endial Fang (endial@126.com)
# Ver: 1.2 by Endial Fang (endial@126.com)
#
# 应用环境变量定义及初始化
# 通用设置
export ENV_DEBUG=${ENV_DEBUG:-false}
export ALLOW_ANONYMOUS_LOGIN="${ALLOW_ANONYMOUS_LOGIN:-no}"
export ALLOW_ANONYMOUS="${ALLOW_ANONYMOUS:-no}"
# 通过读取变量名对应的 *_FILE 文件,获取变量值;如果对应文件存在,则通过传入参数设置的变量值会被文件中对应的值覆盖
# 通过读取变量名对应的`*_FILE`文件,获取变量值
# 变量优先级: *_FILE > 传入变量 > 默认值
app_env_file_lists=(
REDIS_PASSWORD
REDIS_MASTER_PASSWORD
PASSWORD
MASTER_PASSWORD
)
for env_var in "${app_env_file_lists[@]}"; do
file_env_var="${env_var}_FILE"
@@ -22,59 +21,43 @@ for env_var in "${app_env_file_lists[@]}"; do
done
unset app_env_file_lists
# 应用路径参数
export APP_HOME_DIR="/usr/local/${APP_NAME}"
export APP_DEF_DIR="/etc/${APP_NAME}"
export APP_CONF_DIR="/srv/conf/${APP_NAME}"
export APP_DATA_DIR="/srv/data/${APP_NAME}"
export APP_DATA_LOG_DIR="/srv/datalog/${APP_NAME}"
# 应用路径参数Dockerfile 已定义:APP_NAME、APP_VER,可能定义 APP_USER、APP_EXEC
export APP_EXEC="${APP_EXEC:-${APP_NAME}}"
export APP_USER="${APP_USER:-${APP_NAME}}"
export APP_GROUP="${APP_USER:-${APP_NAME}}"
export APP_HOME="${APP_HOME:-/srv/${APP_NAME}}"
export APP_BASE="${APP_BASE:-/usr/local/${APP_NAME}}"
export APP_DEF_DIR="${APP_BASE}/etc/${APP_NAME}"
export APP_CONF_DIR="/srv/${APP_NAME}/conf"
export APP_DATA_DIR="/srv/${APP_NAME}/data"
export APP_CERT_DIR="/srv/${APP_NAME}/cert"
export APP_LOG_DIR="/srv/${APP_NAME}/log"
export APP_CACHE_DIR="/var/cache/${APP_NAME}"
export APP_RUN_DIR="/var/run/${APP_NAME}"
export APP_LOG_DIR="/var/log/${APP_NAME}"
export APP_CERT_DIR="/srv/cert/${APP_NAME}"
# Paths
export REDIS_CONF_FILE="${APP_CONF_DIR}/redis.conf"
export REDIS_SENTINEL_FILE="${APP_CONF_DIR}/sentinel.conf"
export REDIS_PID_FILE="${APP_RUN_DIR}/redis.pid"
# Config files
export REDIS_CONF_FILE="${REDIS_CONF_FILE:-${APP_CONF_DIR}/redis.conf}"
export REDIS_SENTINEL_FILE="${REDIS_SENTINEL_FILE:-${APP_CONF_DIR}/sentinel.conf}"
export REDIS_PID_FILE="${REDIS_PID_FILE:-${APP_RUN_DIR}/redis.pid}"
# Redis settings
export REDIS_PORT="${REDIS_PORT:-6379}"
export REDIS_DISABLE_COMMANDS="${REDIS_DISABLE_COMMANDS:-}"
export REDIS_AOF_ENABLED="${REDIS_AOF_ENABLED:-yes}"
# Cluster configuration
export REDIS_SENTINEL_HOST="${REDIS_SENTINEL_HOST:-}"
export REDIS_SENTINEL_MASTER_NAME="${REDIS_SENTINEL_MASTER_NAME:-}"
export REDIS_SENTINEL_PORT_NUMBER="${REDIS_SENTINEL_PORT_NUMBER:-26379}"
export REDIS_MASTER_HOST="${REDIS_MASTER_HOST:-}"
export REDIS_MASTER_PORT_NUMBER="${REDIS_MASTER_PORT_NUMBER:-6379}"
export REDIS_MASTER_PASSWORD="${REDIS_MASTER_PASSWORD:-}"
export REDIS_REPLICATION_MODE="${REDIS_REPLICATION_MODE:-}"
# Redis Settings
export DISABLE_COMMANDS=${DISABLE_COMMANDS:-}
# Redis TLS Settings
export REDIS_TLS_ENABLED="${REDIS_TLS_ENABLED:-no}"
export REDIS_TLS_PORT="${REDIS_TLS_PORT:-6379}"
export REDIS_TLS_CERT_FILE="${REDIS_TLS_CERT_FILE:-}"
export REDIS_TLS_KEY_FILE="${REDIS_TLS_KEY_FILE:-}"
export REDIS_TLS_CA_FILE="${REDIS_TLS_CA_FILE:-}"
export REDIS_TLS_DH_PARAMS_FILE="${REDIS_TLS_DH_PARAMS_FILE:-}"
export REDIS_TLS_AUTH_CLIENTS="${REDIS_TLS_AUTH_CLIENTS:-yes}"
# Authentication
export REDIS_PASSWORD="${REDIS_PASSWORD:-}"
if [[ -n "${REDIS_CFG_TLS_PORT:-}" ]]; then
[[ "${REDIS_CFG_TLS_PORT}" == "${REDIS_CFG_PORT:-}" ]] && export REDIS_CFG_PORT=0
fi
# 应用配置参数
# 内部变量
export APP_PID_FILE="${REDIS_PID_FILE:-${APP_RUN_DIR}/${APP_NAME}.pid}"
export APP_DAEMON_USER="${APP_NAME}"
export APP_DAEMON_GROUP="${APP_NAME}"
# Sentinel configuration
export REDIS_SENTINEL_HOST="${REDIS_SENTINEL_HOST:-}"
export REDIS_SENTINEL_QUORUM="${REDIS_SENTINEL_QUORUM:-2}"
export REDIS_SENTINEL_MASTER_NAME="${REDIS_SENTINEL_MASTER_NAME:-cvmaster}"
export REDIS_SENTINEL_PORT_NUMBER="${REDIS_SENTINEL_PORT_NUMBER:-26379}"
# 个性化变量
# 如果设置了用户密码,设置环境变量 REDISCLI_AUTH,用于 `redis-cli` 登录时使用;不显示输入,保证安全
if [[ -n "${REDIS_PASSWORD}" ]]; then
export REDISCLI_AUTH="${REDIS_PASSWORD:-}"
if [[ -n "${REDIS_CFG_REQUIREPASS:-}" ]]; then
export REDISCLI_AUTH="${REDIS_CFG_REQUIREPASS:-}"
fi
+7 -14
View File
@@ -1,29 +1,22 @@
#!/bin/bash
# Ver: 1.2 by Endial Fang (endial@126.com)
# Ver: 1.3 by Endial Fang (endial@126.com)
#
# 应用初始化脚本
# 设置 shell 执行参数,可使用'-'(打开)'+'(关闭)控制。常用:
# -e: 命令执行错误则报错; -u: 变量未定义则报错; -x: 打印实际待执行的命令行; -o pipefail: 设置管道中命令遇到失败则报错
set -eu
set -o pipefail
set -euo pipefail
. /usr/local/bin/common.sh # 应用专用函数库
. /usr/local/bin/environment.sh # 设置环境变量
. /usr/local/bin/common.sh # 应用专用函数库
LOG_I "** Processing init.sh **"
trap "app_stop_server" EXIT
trap "${APP_NAME}_stop_server" EXIT
${APP_NAME}_verify_minimum_env
# 执行应用预初始化操作
${APP_NAME}_custom_preinit
app_verify_minimum_env
# 执行应用初始化操作
${APP_NAME}_default_init
app_default_init
# 执行用户自定义初始化脚本
${APP_NAME}_custom_init
LOG_I "** Processing init.sh finished! **"
app_custom_init
+9 -15
View File
@@ -1,29 +1,23 @@
#!/bin/bash
# Ver: 1.5 by Endial Fang (endial@126.com)
# Ver: 1.6 by Endial Fang (endial@126.com)
#
# 应用启动脚本
# 应用启动脚本;组合默认的配置参数及容器启动时传入的 CMD 参数,启动应用
# 设置 shell 执行参数,可使用'-'(打开)'+'(关闭)控制。常用:
# -e: 命令执行错误则报错(errexit); -u: 变量未定义则报错(nounset); -x: 打印实际待执行的命令行; -o pipefail: 设置管道中命令遇到失败则报错
set -eu
set -o pipefail
set -euo pipefail
. /colovu/lib/liblog.sh # 日志输出函数库
. /usr/local/bin/common.sh # 应用专用函数库
. /usr/local/bin/environment.sh # 设置环境变量
LOG_I "** Processing run.sh **"
readonly START_COMMAND="$(command -v ${APP_EXEC})"
readonly START_COMMAND="$(command -v ${APP_EXEC:-${APP_NAME}})"
# 配置默认启动参数(应用配置文件、前台方式启动)
flags=("${REDIS_CONF_FILE:-}" "--daemonize" "no")
# 将启动时使用 REDIS_EXTRA_FLAGS 指定的参数附加在启动参数中
[[ -z "${REDIS_EXTRA_FLAGS:-}" ]] || flags+=("${REDIS_EXTRA_FLAGS[@]}")
# 增加 "@" 以使用用户在命令行添加的扩展标识
flags=("${REDIS_CONF_FILE}" "--daemonize" "no")
[[ -n "${APP_EXTRA_FLAGS:-}" ]] && flags+=("${APP_EXTRA_FLAGS[@]}")
flags+=("$@")
LOG_I "** Starting ${APP_NAME} **"
#is_root && flags=("-u" "$APP_DAEMON_USER" "${flags[@]}")
LOG_I "Command: ${START_COMMAND[@]} ${flags[@]}"
LOG_I "Start ${APP_NAME} with command: ${START_COMMAND[@]} ${flags[@]}"
exec "${START_COMMAND[@]}" "${flags[@]}"
+13 -12
View File
@@ -1,34 +1,35 @@
#!/bin/bash
# Ver: 1.2 by Endial Fang (endial@126.com)
# Ver: 1.3 by Endial Fang (endial@126.com)
#
# 应用环境及依赖文件设置脚本
# 应用环境及依赖文件设置脚本;当前脚本以‘root’用户执行
# 设置 shell 执行参数,可使用'-'(打开)'+'(关闭)控制。常用:
# -e: 命令执行错误则报错(errexit); -u: 变量未定义则报错(nounset); -x: 打印实际待执行的命令行; -o pipefail: 设置管道中命令遇到失败则报错
set -eu
set -o pipefail
set -euo pipefail
. /colovu/lib/libcommon.sh # 加载通用函数库
. /colovu/lib/libfs.sh # 加载文件操作函数库
. /colovu/lib/libos.sh # 加载系统管理函数库
. /usr/local/bin/environment.sh # 设置环境变量
. /usr/local/bin/common.sh # 应用专用函数库
LOG_I "** Processing setup.sh **"
APP_DIRS="${APP_CONF_DIR:-} ${APP_DATA_DIR:-} ${APP_LOG_DIR:-} ${APP_CERT_DIR:-} ${APP_DATA_LOG_DIR:-}"
APP_DIRS=(/var/log/${APP_NAME} /var/run/${APP_NAME} /var/cache/${APP_NAME} ${APP_HOME})
APP_DIRS+=(${APP_HOME}/conf ${APP_HOME}/data ${APP_HOME}/cert ${APP_HOME}/log)
LOG_I "Ensure directory exists: ${APP_DIRS}"
for dir in ${APP_DIRS}; do
LOG_I "Ensure directory exists: ${APP_DIRS[@]}"
for dir in ${APP_DIRS[@]}; do
ensure_dir_exists ${dir}
done
# 检测指定文件是否在配置文件存储目录存在,如果不存在则拷贝(新挂载数据卷、手动删除都会导致不存在)
LOG_I "Check config files in: ${APP_CONF_DIR}"
if [[ ! -z "$(ls -A "${APP_DEF_DIR}")" ]]; then
ensure_config_file_exist "${APP_DEF_DIR}" $(ls -A "${APP_DEF_DIR}")
if [[ -z "$(ls -A "${APP_CONF_DIR}")" ]]; then
app_ensure_config_file_exist "${APP_CONF_DIR}" "${APP_DEF_DIR}" $(ls -A "${APP_DEF_DIR}")
fi
is_root && ensure_user_exists "$APP_DAEMON_USER" -g "$APP_DAEMON_GROUP"
LOG_I "** Processing setup.sh finished! **"
# 解决使用non-root后,[emerg] open() "/dev/stdout" failed (13: Permission denied)
LOG_D "Change permissions of stdout/stderr to 0662"
chmod 0662 /dev/stdout /dev/stderr
@@ -1,17 +0,0 @@
#!/bin/bash -e
# Ver: 1.0 by Endial Fang (endial@126.com)
#
# 在安装完应用后,使用该脚本修改默认配置文件中部分配置项; 如果相应的配置项已经定义为容器环境变量,则不需要在这里修改
# 定义要修改的文件
CONF_FILE="${APP_DEF_DIR}/redis.conf"
echo "Process overrides for: ${CONF_FILE}"
# 修改默认配置信息
sed -i -E 's/^#?pidfile .*/pidfile \/var\/run\/redis\/redis.pid/g' "${CONF_FILE}"
sed -i -E 's/^#?logfile .*/logfile \"\/var\/log\/redis\/redis.log\"/g' "${CONF_FILE}"
SENTINEL_FILE="${APP_DEF_DIR}/sentinel.conf"
echo "Process overrides for: ${SENTINEL_FILE}"
# 修改 Sentinel 默认配置信息
sed -i -E 's/^#?pidfile .*/pidfile \/var\/run\/redis\/redis-sentinel.pid/g' "${SENTINEL_FILE}"
sed -i -E 's/^#?logfile .*/logfile \"\/var\/log\/redis\/redis-sentinel.log\"/g' "${SENTINEL_FILE}"
@@ -0,0 +1,23 @@
#!/bin/bash -e
# Ver: 1.0 by Endial Fang (endial@126.com)
#
# 在安装完应用后,使用该脚本修改默认配置文件中部分配置项; 如果相应的配置项已经定义为容器环境变量,则不需要在这里修改
# 定义要修改的文件
CONF_FILE="/usr/local/${APP_NAME}/etc/${APP_NAME}/redis.conf"
echo "Process overrides for: ${CONF_FILE}"
# 修改默认配置信息
sed -i -E 's/^#?pidfile .*/pidfile \/var\/run\/redis\/redis.pid/g' "${CONF_FILE}"
sed -i -E 's/^#?logfile .*/logfile \"\/srv\/redis\/log\/redis.log\"/g' "${CONF_FILE}"
sed -i -E 's/^#?protected-mode .*/protected-mode no/g' "${CONF_FILE}"
sed -i -E 's/^#?daemonize .*/daemonize no/g' "${CONF_FILE}"
sed -i -E 's/^#?dir .*/dir \/srv\/'${APP_NAME}'\/data/g' "${CONF_FILE}"
SENTINEL_FILE="/usr/local/${APP_NAME}/etc/${APP_NAME}/sentinel.conf"
echo "Process overrides for: ${SENTINEL_FILE}"
# 修改 Sentinel 默认配置信息
sed -i -E 's/^#?pidfile .*/pidfile \/var\/run\/redis\/redis-sentinel.pid/g' "${SENTINEL_FILE}"
sed -i -E 's/^#?logfile .*/logfile \"\/srv\/redis\/log\/redis-sentinel.log\"/g' "${SENTINEL_FILE}"
sed -i -E 's/^#?protected-mode .*/protected-mode no/g' "${SENTINEL_FILE}"
sed -i -E 's/^#?daemonize .*/daemonize no/g' "${SENTINEL_FILE}"
sed -i -E 's/^#?dir .*/dir \/srv\/'${APP_NAME}'\/data/g' "${CONF_FILE}"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,342 @@
# Example sentinel.conf
# 本文件经过变更,删除了配置项前的前置空格(“#”与关键字之间),以便后续可以使用 sed 进行处理
# *** IMPORTANT ***
#
# By default Sentinel will not be reachable from interfaces different than
# localhost, either use the 'bind' directive to bind to a list of network
# interfaces, or disable protected mode with "protected-mode no" by
# adding it to this configuration file.
#
# Before doing that MAKE SURE the instance is protected from the outside
# world via firewalling or other means.
#
# For example you may use one of the following:
#
# bind 127.0.0.1 192.168.1.1
#
#protected-mode no
# port <sentinel-port>
# The port that this sentinel instance will run on
port 26379
# By default Redis Sentinel does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis-sentinel.pid when
# daemonized.
daemonize no
# When running daemonized, Redis Sentinel writes a pid file in
# /var/run/redis-sentinel.pid by default. You can specify a custom pid file
# location here.
pidfile /var/run/redis-sentinel.pid
# Specify the log file name. Also the empty string can be used to force
# Sentinel to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null
logfile ""
# sentinel announce-ip <ip>
# sentinel announce-port <port>
#
# The above two configuration directives are useful in environments where,
# because of NAT, Sentinel is reachable from outside via a non-local address.
#
# When announce-ip is provided, the Sentinel will claim the specified IP address
# in HELLO messages used to gossip its presence, instead of auto-detecting the
# local address as it usually does.
#
# Similarly when announce-port is provided and is valid and non-zero, Sentinel
# will announce the specified TCP port.
#
# The two options don't need to be used together, if only announce-ip is
# provided, the Sentinel will announce the specified IP and the server port
# as specified by the "port" option. If only announce-port is provided, the
# Sentinel will announce the auto-detected local IP and the specified port.
#
# Example:
#
#sentinel announce-ip 1.2.3.4
# dir <working-directory>
# Every long running process should have a well-defined working directory.
# For Redis Sentinel to chdir to /tmp at startup is the simplest thing
# for the process to don't interfere with administrative tasks such as
# unmounting filesystems.
dir /tmp
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
#
# Tells Sentinel to monitor this master, and to consider it in O_DOWN
# (Objectively Down) state only if at least <quorum> sentinels agree.
#
# Note that whatever is the ODOWN quorum, a Sentinel will require to
# be elected by the majority of the known Sentinels in order to
# start a failover, so no failover can be performed in minority.
#
# Replicas are auto-discovered, so you don't need to specify replicas in
# any way. Sentinel itself will rewrite this configuration file adding
# the replicas using additional configuration options.
# Also note that the configuration file is rewritten when a
# replica is promoted to master.
#
# Note: master name should not include special characters or spaces.
# The valid charset is A-z 0-9 and the three characters ".-_".
sentinel monitor mymaster 127.0.0.1 6379 2
# sentinel auth-pass <master-name> <password>
#
# Set the password to use to authenticate with the master and replicas.
# Useful if there is a password set in the Redis instances to monitor.
#
# Note that the master password is also used for replicas, so it is not
# possible to set a different password in masters and replicas instances
# if you want to be able to monitor these instances with Sentinel.
#
# However you can have Redis instances without the authentication enabled
# mixed with Redis instances requiring the authentication (as long as the
# password set is the same for all the instances requiring the password) as
# the AUTH command will have no effect in Redis instances with authentication
# switched off.
#
# Example:
#
#sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
#sentinel auth-user <master-name> <username>
#
# This is useful in order to authenticate to instances having ACL capabilities,
# that is, running Redis 6.0 or greater. When just auth-pass is provided the
# Sentinel instance will authenticate to Redis using the old "AUTH <pass>"
# method. When also an username is provided, it will use "AUTH <user> <pass>".
# In the Redis servers side, the ACL to provide just minimal access to
# Sentinel instances, should be configured along the following lines:
#
# user sentinel-user >somepassword +client +subscribe +publish \
# +ping +info +multi +slaveof +config +client +exec on
# sentinel down-after-milliseconds <master-name> <milliseconds>
#
# Number of milliseconds the master (or any attached replica or sentinel) should
# be unreachable (as in, not acceptable reply to PING, continuously, for the
# specified period) in order to consider it in S_DOWN state (Subjectively
# Down).
#
# Default is 30 seconds.
sentinel down-after-milliseconds mymaster 30000
# IMPORTANT NOTE: starting with Redis 6.2 ACL capability is supported for
# Sentinel mode, please refer to the Redis website https://redis.io/topics/acl
# for more details.
# Sentinel's ACL users are defined in the following format:
#
# user <username> ... acl rules ...
#
# For example:
#
# user worker +@admin +@connection ~* on >ffa9203c493aa99
#
# For more information about ACL configuration please refer to the Redis
# website at https://redis.io/topics/acl and redis server configuration
# template redis.conf.
# ACL LOG
#
# The ACL Log tracks failed commands and authentication events associated
# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked
# by ACLs. The ACL Log is stored in memory. You can reclaim memory with
# ACL LOG RESET. Define the maximum entry length of the ACL Log below.
acllog-max-len 128
# Using an external ACL file
#
# Instead of configuring users here in this file, it is possible to use
# a stand-alone file just listing users. The two methods cannot be mixed:
# if you configure users here and at the same time you activate the external
# ACL file, the server will refuse to start.
#
# The format of the external ACL user file is exactly the same as the
# format that is used inside redis.conf to describe users.
#
#aclfile /etc/redis/sentinel-users.acl
#requirepass <password>
#
# You can configure Sentinel itself to require a password, however when doing
# so Sentinel will try to authenticate with the same password to all the
# other Sentinels. So you need to configure all your Sentinels in a given
# group with the same "requirepass" password. Check the following documentation
# for more info: https://redis.io/topics/sentinel
#
# IMPORTANT NOTE: starting with Redis 6.2 "requirepass" is a compatibility
# layer on top of the ACL system. The option effect will be just setting
# the password for the default user. Clients will still authenticate using
# AUTH <password> as usually, or more explicitly with AUTH default <password>
# if they follow the new protocol: both will work.
#
# New config files are advised to use separate authentication control for
# incoming connections (via ACL), and for outgoing connections (via
# sentinel-user and sentinel-pass)
#
# The requirepass is not compatable with aclfile option and the ACL LOAD
# command, these will cause requirepass to be ignored.
#sentinel sentinel-user <username>
#
# You can configure Sentinel to authenticate with other Sentinels with specific
# user name.
#sentinel sentinel-pass <password>
#
# The password for Sentinel to authenticate with other Sentinels. If sentinel-user
# is not configured, Sentinel will use 'default' user with sentinel-pass to authenticate.
# sentinel parallel-syncs <master-name> <numreplicas>
#
# How many replicas we can reconfigure to point to the new replica simultaneously
# during the failover. Use a low number if you use the replicas to serve query
# to avoid that all the replicas will be unreachable at about the same
# time while performing the synchronization with the master.
sentinel parallel-syncs mymaster 1
# sentinel failover-timeout <master-name> <milliseconds>
#
# Specifies the failover timeout in milliseconds. It is used in many ways:
#
# - The time needed to re-start a failover after a previous failover was
# already tried against the same master by a given Sentinel, is two
# times the failover timeout.
#
# - The time needed for a replica replicating to a wrong master according
# to a Sentinel current configuration, to be forced to replicate
# with the right master, is exactly the failover timeout (counting since
# the moment a Sentinel detected the misconfiguration).
#
# - The time needed to cancel a failover that is already in progress but
# did not produced any configuration change (SLAVEOF NO ONE yet not
# acknowledged by the promoted replica).
#
# - The maximum time a failover in progress waits for all the replicas to be
# reconfigured as replicas of the new master. However even after this time
# the replicas will be reconfigured by the Sentinels anyway, but not with
# the exact parallel-syncs progression as specified.
#
# Default is 3 minutes.
sentinel failover-timeout mymaster 180000
# SCRIPTS EXECUTION
#
# sentinel notification-script and sentinel reconfig-script are used in order
# to configure scripts that are called to notify the system administrator
# or to reconfigure clients after a failover. The scripts are executed
# with the following rules for error handling:
#
# If script exits with "1" the execution is retried later (up to a maximum
# number of times currently set to 10).
#
# If script exits with "2" (or an higher value) the script execution is
# not retried.
#
# If script terminates because it receives a signal the behavior is the same
# as exit code 1.
#
# A script has a maximum running time of 60 seconds. After this limit is
# reached the script is terminated with a SIGKILL and the execution retried.
# NOTIFICATION SCRIPT
#
# sentinel notification-script <master-name> <script-path>
#
# Call the specified notification script for any sentinel event that is
# generated in the WARNING level (for instance -sdown, -odown, and so forth).
# This script should notify the system administrator via email, SMS, or any
# other messaging system, that there is something wrong with the monitored
# Redis systems.
#
# The script is called with just two arguments: the first is the event type
# and the second the event description.
#
# The script must exist and be executable in order for sentinel to start if
# this option is provided.
#
# Example:
#
#sentinel notification-script mymaster /var/redis/notify.sh
# CLIENTS RECONFIGURATION SCRIPT
#
# sentinel client-reconfig-script <master-name> <script-path>
#
# When the master changed because of a failover a script can be called in
# order to perform application-specific tasks to notify the clients that the
# configuration has changed and the master is at a different address.
#
# The following arguments are passed to the script:
#
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
#
# <state> is currently always "failover"
# <role> is either "leader" or "observer"
#
# The arguments from-ip, from-port, to-ip, to-port are used to communicate
# the old address of the master and the new address of the elected replica
# (now a master).
#
# This script should be resistant to multiple invocations.
#
# Example:
#
#sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
# SECURITY
#
# By default SENTINEL SET will not be able to change the notification-script
# and client-reconfig-script at runtime. This avoids a trivial security issue
# where clients can set the script to anything and trigger a failover in order
# to get the program executed.
sentinel deny-scripts-reconfig yes
# REDIS COMMANDS RENAMING
#
# Sometimes the Redis server has certain commands, that are needed for Sentinel
# to work correctly, renamed to unguessable strings. This is often the case
# of CONFIG and SLAVEOF in the context of providers that provide Redis as
# a service, and don't want the customers to reconfigure the instances outside
# of the administration console.
#
# In such case it is possible to tell Sentinel to use different command names
# instead of the normal ones. For example if the master "mymaster", and the
# associated replicas, have "CONFIG" all renamed to "GUESSME", I could use:
#
# SENTINEL rename-command mymaster CONFIG GUESSME
#
# After such configuration is set, every time Sentinel would use CONFIG it will
# use GUESSME instead. Note that there is no actual need to respect the command
# case, so writing "config guessme" is the same in the example above.
#
# SENTINEL SET can also be used in order to perform this configuration at runtime.
#
# In order to set a command back to its original name (undo the renaming), it
# is possible to just rename a command to itself:
#
#SENTINEL rename-command mymaster CONFIG CONFIG
# HOSTNAMES SUPPORT
#
# Normally Sentinel uses only IP addresses and requires SENTINEL MONITOR
# to specify an IP address. Also, it requires the Redis replica-announce-ip
# keyword to specify only IP addresses.
#
# You may enable hostnames support by enabling resolve-hostnames. Note
# that you must make sure your DNS is configured properly and that DNS
# resolution does not introduce very long delays.
#
SENTINEL resolve-hostnames no
# When resolve-hostnames is enabled, Sentinel still uses IP addresses
# when exposing instances to users, configuration files, etc. If you want
# to retain the hostnames when announced, enable announce-hostnames below.
#
SENTINEL announce-hostnames no
-12
View File
@@ -1,12 +0,0 @@
#!/bin/bash
# Ver: 1.2 by Endial Fang (endial@126.com)
#
# shell 执行参数,分别为 -e(命令执行错误则退出脚本) -u(变量未定义则报错) -x(打印实际待执行的命令行)
set -eux
groupadd --gid 1001 --system ${APP_USER}
#useradd --gid 1001 --uid 1001 --shell /bin/bash --home /srv/data/${APP_NAME} --system ${APP_USER}
useradd --gid 1001 --uid 1001 --shell /usr/sbin/nologin --home /srv/data/${APP_NAME} --system ${APP_USER}
# 如果需要 sudo 权限,需要在 Dockerfile 中安装 su 软件包:RUN install_pkg sudo
#sed -i -e 's/^\sDefaults\s*secure_path\s*=/# Defaults secure_path=/' /etc/sudoers
#echo "${APP_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash
# Ver: 1.3 by Endial Fang (endial@126.com)
#
# shell 执行参数,分别为 -e(命令执行错误则退出脚本) -u(变量未定义则报错) -x(打印实际待执行的命令行)
set -eux
APP_DIRS=" \
/srv/conf/${APP_NAME} \
/srv/data/${APP_NAME} \
/srv/datalog/${APP_NAME} \
/var/cache/${APP_NAME} \
/var/run/${APP_NAME} \
/var/log/${APP_NAME} \
/srv/cert/${APP_NAME}"
mkdir -p ${APP_DIRS}
chmod -R g+rwX ${APP_DIRS} /usr/local/${APP_NAME}
# 针对对用户有特殊要求的应用,创建对应的默认用户,并将相关目录赋予该用户操作权限
#chown -Rf ${APP_USER}:${APP_USER} ${APP_DIRS}
+10 -13
View File
@@ -6,30 +6,27 @@ version: '3.8'
# 当前配置仅保证可以启动容器;更多配置参数请参考镜像 README.md 文档中说明
services:
redis:
image: 'colovu/redis:latest'
redis-master:
image: 'registry.cn-shenzhen.aliyuncs.com/colovu/redis:latest'
ports:
- '6379'
environment:
- REDIS_REPLICATION_MODE=master
- REDIS_PASSWORD=colovu
- REDIS_DISABLE_COMMANDS=FLUSHDB,FLUSHALL
- REDIS_CFG_REQUIREPASS=colovu
- DISABLE_COMMANDS=FLUSHDB,FLUSHALL
volumes:
- 'redis_data:/srv/data'
redis-replica:
image: 'colovu/redis:latest'
image: 'registry.cn-shenzhen.aliyuncs.com/colovu/redis:latest'
ports:
- '6379'
environment:
- REDIS_REPLICATION_MODE=slave
- REDIS_MASTER_HOST=redis
- REDIS_MASTER_PORT_NUMBER=6379
- REDIS_MASTER_PASSWORD=colovu
- REDIS_PASSWORD=colovu
- REDIS_DISABLE_COMMANDS=FLUSHDB,FLUSHALL
- REDIS_CFG_REPLICAOF="redis-master 6379"
- REDIS_CFG_MASTERAUTH=colovu
- REDIS_CFG_REQUIREPASS=colovu
- DISABLE_COMMANDS=FLUSHDB,FLUSHALL
depends_on:
- redis
- redis-master
# 定义本地数据卷,由系统管理,需要手动删除
volumes:
+4 -4
View File
@@ -4,13 +4,13 @@ version: '3.8'
# 当前配置仅保证可以启动容器;更多配置参数请参考镜像 README.md 文档中说明
services:
redis:
image: 'colovu/redis:latest'
image: 'registry.cn-shenzhen.aliyuncs.com/colovu/redis:latest'
ports:
- '6379:6379'
environment:
# ALLOW_ANONYMOUS_LOGIN is recommended only for development.
- ALLOW_ANONYMOUS_LOGIN=yes
- REDIS_DISABLE_COMMANDS=FLUSHDB,FLUSHALL
# ALLOW_ANONYMOUS is recommended only for development.
- ALLOW_ANONYMOUS=yes
- DISABLE_COMMANDS=FLUSHDB,FLUSHALL
volumes:
- 'redis_data:/srv/data'
-8
View File
@@ -1,8 +0,0 @@
# 说明
## 用途
本目录下相关 Hooks 脚本主要用于 Docker Hub 服务器编译镜像时,获取用户设置的环境变量,并根据环境变量进行条件编译。相关脚本说明参照[官方文档](https://docs.docker.com/docker-hub/builds/advanced/)。
目录`hooks`必须与镜像编译文件 Dockerfile 同目录。
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
# v1.0 by Endial Fang (endial@126.com)
#
# 用户 docker.hub 的自动编译钩子文件,相应的变量在镜像库自动编译界面进行配置(如:registry_url、apt_source
# 参见: https://docs.docker.com/docker-hub/builds/advanced/
docker build --build-arg registry_url=${registry_url:-docker.io} --build-arg apt_source=${apt_source:-default} -f $DOCKERFILE_PATH -t $IMAGE_NAME .