本文共 10349 字,大约阅读时间需要 34 分钟。
作者:王志斌,曾获得中国PostgreSQL数据库管理工程师(PGCE),是PostgreSQL官方认证讲师,盘古云课堂特邀金牌讲师。
上一篇文章向大家介绍了一个容器的单机数据库从创建开始到运行的整个过程,相信大家对Dockerfile的基本概念有了一个大致的了解,下面将给大家介绍如何在容器下创建主从复制环境。
关于如何搭建docker环境,在第一篇中已经介绍过,这里就不赘述。下面我们直接进入主题,构建docker镜像的步骤与单机的基本镜像构建基本一样,只是在具体的脚本内容上有所差别。
为了方便理解,此处仍按步骤进行操作:
定义创建镜像文件,最后通过本文件在本地docker仓库中创建镜像。
注:这部分内容与之前单机构建镜像文件没有差别。
#基础镜像版本from centos:7 #维护人员信息MAINTAINER wangzhibin #变量PG_VERSION=10 \ PG_USER=postgresql \ PG_HOME=/var/lib/postgresql\ PG_BINDIR=/usr/lib/postgresql//bin \ PG_LIBDIR=/usr/lib/postgresql/lib \ PG_LOGDIR=/var/log/postgresql \PG_INCLUDEDIR=/usr/lib/postgresql/include \ PG_CLIENTDIR=/usr/bin \ PG_SHAREDIR=/usr/lib/postgresql/share #运行时环境变量ENV PG_APP_HOME="/etc/docker-postgresql" \ ENV LD_LIBRARY_PATH /usr/lib/postgresql/lib #安装依赖并创建用户组和用户RUN yum install -y acl sudo locales libxml2-dev libssl-dev net-tools \ && groupadd postgresql\ && useradd postgresql -g postgresql -p "123456" \ && echo root:rootpwd|chpasswd \ && echo postgresql:123456|chpasswd #拷贝文件COPY bin/ ${PG_BINDIR} COPY lib/ ${PG_LIBDIR} COPY include/ ${PG_INCLUDEDIR} COPY bin/ ${PG_CLIENTDIR} COPY share/ ${PG_SHAREDIR} COPY runtime/ ${PG_APP_HOME}/ COPY start-stop-daemon /sbin/COPY entrypoint.sh /sbin/entrypoint.sh && chmod 755 /sbin/start-stop-daemon#修改入口执行文件权限RUN chmod 755 /sbin/entrypoint.sh #映射端口EXPOSE 5432/tcp#设定工作目录,默认启动后切换至该目录 WORKDIR ${PG_HOME} #设置执行的入口文件ENTRYPOINT ["/sbin/entrypoint.sh"]
#!/bin/bashset -e# shellcheck source=runtime/functionssource "${PG_APP_HOME}/functions"[[ ${DEBUG} == true ]] && set -x# 接收外部参数if [[ ${1:0:1} = '-' ]]; then EXTRA_ARGS="$@" set --elif [[ ${1} == postgres || ${1} == $(command -v postgres) ]]; then EXTRA_ARGS="${@:2}" set --fi# default behaviour is to launch postgresif [[ -z ${1} ]]; then create_datadir configure_postgresql #启动数据库 echo "开始Postgresql ..." exec start-stop-daemon --start --chuid "${PG_USER}:${PG_USER}" \ --exec "${PG_BINDIR}/postgres" -- -D "${PG_DATADIR}" ${EXTRA_ARGS}else exec "$@"fi
该文件的主要功能是:初始化数据库目录、修改配置参数及运行数据库,在备用模式下,根据设置角色不同,设置流复制,实现主从模式,下面为部分脚本代码:
create_datadir() { echo "初始化数据目录" mkdir -p ${PG_HOME} if [[ -d ${PG_DATADIR} ]]; then chmod 0600 $( find ${ PG_DATADIR} -type f ) chmod 0700 $( find ${ PG_DATADIR} -type d ) fi chown -R ${PG_USER}:${PG_USER} ${PG_HOME} }
注:此处与之前单机有不同,通过pg_baskbackup全量备份后,并创建流复制。
initialize_database() { if [[ ! -f ${PG_DATADIR}/PG_VERSION ]]; then case ${REPLICATION_MODE} in slave|snapshot|backup) if [[ -z $REPLICATION_HOST ]]; then echo "未设置复制主机" exit 1 fi if [[ -z $REPLICATION_USER ]]; then echo "未设置复制用户" exit 1 fi if [[ -z $REPLICATION_PASS ]]; then echo "未设置复制密码" exit 1 fi echo -n "等待 $REPLICATION_HOST 接受连接 (60s timeout)" timeout=60 #检查主机连接状态 while ! ${PG_BINDIR}/pg_isready -h $REPLICATION_HOST -p $REPLICATION_PORT -t 1 >/dev/null 2>&1 do timeout=$(expr $timeout - 1) if [[ $timeout -eq 0 ]]; then echo "超时退出" exit 1 fi echo -n "." sleep 1 done case ${REPLICATION_MODE} in slave) echo "从$REPLICATION_HOST 复制数据" exec_as_postgres PGPASSWORD=$REPLICATION_PASS ${PG_BINDIR}/pg_basebackup -D ${PG_DATADIR} \ -h ${REPLICATION_HOST} -p ${REPLICATION_PORT} -U ${REPLICATION_USER} -X stream -w >/dev/null ;; snapshot) echo "在复制主机 $REPLICATION_HOST 生成快照" exec_as_postgres PGPASSWORD=$REPLICATION_PASS ${PG_BINDIR}/pg_basebackup -D ${PG_DATADIR} \ -h ${REPLICATION_HOST} -p ${REPLICATION_PORT} -U ${REPLICATION_USER} -X fetch -w >/dev/null ;; backup) echo "备份 $REPLICATION_HOST 数据" exec_as_postgres PGPASSWORD=$REPLICATION_PASS ${PG_BINDIR}/pg_basebackup -D ${PG_DATADIR} \ -h ${REPLICATION_HOST} -p ${REPLICATION_PORT} -U ${REPLICATION_USER} -X fetch -w >/dev/null exit 0 ;; esac ;; *) if [[ -n $PG_PASSWORD ]]; then echo "${PG_PASSWORD}" > /tmp/pwfile Fi exec_as_postgres ${PG_BINDIR}/initdb --pgdata=${PG_DATADIR} \ --username=${PG_USER} --encoding=unicode --dbstyle=${PG_MODE} --auth=trust ${PG_PASSWORD:+--pwfile=/tmp/pwfile} >/dev/null ;; esac configure_hot_standby # 配置数据路径 set_postgresql_param "data_directory" "${PG_DATADIR}" # 配置日志 set_postgresql_param "logging_collector" on set_postgresql_param "log_directory" "${PG_LOGDIR}" set_postgresql_param "log_filename" "postgresql-${PG_VERSION}-main.log" # 创建本地信任连接 if [[ ${PG_TRUST_LOCALNET} == true ]]; then set_hba_param "host all all samenet trust" fi # 允许所有远端连接到postgresql set_hba_param "host all all 0.0.0.0/0 md5"}
#设置pg_hba文件 set_hba_param() { local value=${1} if ! grep -q "$(sed "s| | \\\+|g" <<< ${ value})" ${PG_HBA_CONF}; then echo "${value}" >> ${PG_HBA_CONF} fi }
#设置postgresql参数 set_postgresql_param() { local key=${1} local value=${2} local verbosity=${3:-verbose} if [[ -n ${value} ]]; then local current=$(exec_as_postgres sed -n -e "s/^\(${ key} = '\)\([^ ']*\)\(.*\)$/\2/p" ${PG_CONF}) if [[ "${current}" != "${value}" ]]; then if [[ ${verbosity} == verbose ]]; then echo "? 设置postgresql.conf 参数: ${key} = '${value}'" fi value="$(echo "${value}" | sed 's|[&]|\\&|g')" exec_as_postgres sed -i "s|^[#]*[ ]*${key} = .*|${key} = '${value}'|" ${PG_CONF} fi fi }
set_recovery_param() { local key=${1} local value=${2} local hide=${3} if [[ -n ${value} ]]; then local current=$(exec_as_postgres sed -n -e "s/^\(.*\)\(${key}=\)\([^ ']*\)\(.*\)$/\3/p" ${PG_RECOVERY_CONF}) if [[ "${current}" != "${value}" ]]; then case ${hide} in true) echo "? 设置primary_conninfo 参数: ${key}" ;; *) echo "? 设置 primary_conninfo 参数: ${key} = '${value}'" ;; esac exec_as_postgres sed -i "s|${key}=[^ ']*|${key}=${value}|" ${PG_RECOVERY_CONF} fi fi}
configure_recovery() { if [[ ${REPLICATION_MODE} == slave ]]; then echo "配置还原" if [[ ! -f ${PG_RECOVERY_CONF} ]]; then exec_as_postgres touch ${PG_RECOVERY_CONF} ( echo "standby_mode = 'on'"; echo "primary_conninfo = 'host=${REPLICATION_HOST} port=${REPLICATION_PORT} user=${REPLICATION_USER} password=${REPLICATION_PASS}'"; ) > ${PG_RECOVERY_CONF} else set_recovery_param "host" "${REPLICATION_HOST}" set_recovery_param "port" "${REPLICATION_PORT}" set_recovery_param "user" "${REPLICATION_USER}" set_recovery_param "password" "${REPLICATION_PASS}" "true" set_recovery_param "sslmode" "${REPLICATION_SSLMODE}" fi else rm -rf ${PG_RECOVERY_CONF} fi}
create_database() { if [[ -n ${DB_NAME} ]]; then case $REPLICATION_MODE in slave|snapshot|backup) echo "不能在复制模式的节点下创建数据库" ;; *) for database in $(awk -F',' '{ for (i = 1 ; i <= NF ; i++) print $i}' <<< "${DB_NAME}"); do echo "创建数据库: ${database}..." if [[ -z $(psql -U ${PG_USER} -d ${PG_USER} -Atc "SELECT 1 FROM pg_catalog.pg_database WHERE datname = '${database}'";) ]]; then psql -U ${PG_USER} -d ${PG_USER} -c "CREATE DATABASE \"${database}\" WITH TEMPLATE = \"${DB_TEMPLATE}\";" >/dev/null fi if [[ -n ${DB_USER} ]]; then echo "分配给用户 ${DB_USER} ..." psql -U ${PG_USER} -d ${PG_USER} -c "GRANT ALL PRIVILEGES ON DATABASE \"${database}\" to \"${DB_USER}\";" >/dev/null fi done ;; esac fi}
create_user() { if [[ -n ${DB_USER} ]]; then case $REPLICATION_MODE in slave|snapshot|backup) echo "用户不能创建在复制节点上" ;; *) if [[ -z ${DB_PASS} ]]; then echo "需要指定数据库用户密码" exit 1 fi echo "创建数据库用户: ${DB_USER}" if [[ -z $(psql -U ${ PG_USER} -d ${ PG_USER} -Atc "SELECT 1 FROM pg_catalog.pg_user WHERE usename = '${DB_USER}'";) ]]; then psql -U ${PG_USER} -d ${PG_USER} -c "CREATE ROLE \"${DB_USER}\" with LOGIN CREATEDB PASSWORD '${DB_PASS}';" >/dev/null fi ;; esac fi}
create_replication_user() { if [[ -n ${REPLICATION_USER} ]]; then case $REPLICATION_MODE in slave|snapshot|backup) ;; *) if [[ -z ${REPLICATION_PASS} ]]; then echo "未设置复制用户密码" exit 1 fi echo "创建复制用户: ${REPLICATION_USER}" if [[ -z $(psql -U ${ PG_USER} -d ${ PG_USER} -Atc "SELECT 1 FROM pg_catalog.pg_user WHERE usename = '${REPLICATION_USER}'";) ]]; then psql -U ${PG_USER} -d ${PG_USER} -c "CREATE ROLE \"${REPLICATION_USER}\" WITH REPLICATION LOGIN ENCRYPTED PASSWORD '${REPLICATION_PASS}';" >/dev/null fi set_hba_param "host replication ${REPLICATION_USER} 0.0.0.0/0 md5" ;; esac fi}
#配置postgresql数据库 source /etc/profile initialize_database configure_recovery rm -rf ${PG_DATADIR}/postmaster.pid set_postgresql_param "listen_addresses" "127.0.0.1" quiet exec_as_postgres ${PG_BINDIR}/pg_ctl -D ${PG_DATADIR} -w start >/dev/null create_user create_database create_replication_user # stop the postgres server exec_as_postgres ${PG_BINDIR}/pg_ctl -D ${PG_DATADIR} -w stop >/dev/null # listen on all interfaces set_postgresql_param "listen_addresses" "*" quiet
进入到程序所在目录,通过命令创建镜像,注意最后的.
docker build -t postgresql .构建镜像完成后,可以调用分别调用一下命令,启动主从环境并进行验证。
docker run --name postgresql-master -itd --restart always --env ‘DB_USER=dbuser’ --env ‘DB_PASS=dbuserpass’ --env ‘DB_NAME=test’ --env ‘REPLICATION_USER=repluser’ --env ‘REPLICATION_PASS=repluserpass’ postgresql
docker run --name postgresql-slave -itd --restart always --link postgresql-master:master --env ‘REPLICATION_MODE=slave’ --env ‘REPLICATION_SSLMODE=prefer’ --env ‘REPLICATION_HOST=master’ --env ‘REPLICATION_PORT=5432’ --env ‘REPLICATION_USER=repluser’ --env ‘REPLICATION_PASS=repluserpass’ postgresql
[root@localhost ~]# docker exec -it postgresql-master /bin/bash
[root@487bfb4d0bf1 postgres]# psql -Upostgres -dpostgres -u了解更多PostgreSQL热点资讯、新闻动态、精彩活动,请访问中国PostgreSQL官方网站:
解决更多PostgreSQL相关知识、技术、工作问题,请访问中国PostgreSQL官方问答社区:
下载更多PostgreSQL相关资料、工具、插件问题,请访问中国PostgreSQL官方下载网站:
转载地址:http://qmmxf.baihongyu.com/