Postgres XL运行在Docker容器中

这次实践是在Postgres XL实践基础上进一步尝试结合docker使用,主要目的是为了简化节点环境配置和Postgres XL版本升级过程。

目标是启动一批由相同镜像创建的容器,支持SSH访问,然后通过pgxl_ctl脚本管理Postgres-XL集群。

Docker镜像构建

首先对文件和目录进行划分,分为docker镜像部分和非docker镜像部分。

镜像部分

  • 编译后的Postgres-XL二进制文件。
  • Postgres-XL运行时依赖的lib、系统参数和常用工具。
  • 用户和SSH权限配置,使容器彼此通过postgres用户免密码访问。
  • .pgpass文件的软链接。因为尝试指定该文件路径的方式没成功,所以改为软链接的方式。

非镜像部分

  • 配置和数据文件。
  • 日志文件。
  • pgpass.conf文件。

其中镜像部分写入Dockerfile,内容如下:

# Postgres-XL 
# User: postgres
# Postgres home:  /usr/local/pgxl
# Data directory: /mfpdata/pgxl_data
# Log directory:  /mfplog/pgxl_log
# Notice: http proxy and pgxl directory

FROM centos

MAINTAINER Hengbin Wang hengbin.wang@microfun.com

# make a http proxy for yum
ENV http_proxy="http://172.31.48.102:3128/"

RUN yum update -y
RUN yum install -y \
        net-tools \
        telnet \
        nc \
        openssh-server \
        openssh-clients \
        vim

RUN ssh-keygen -A -N "";

RUN useradd -m postgres; \
    chsh -s /bin/bash postgres; \
    mkdir -p /mfpdata/pgxl_data; \
    mkdir -p /mfplog/pgxl_log; \
    chown postgres:postgres /mfpdata/pgxl_data; \
    chown postgres:postgres /mfplog/pgxl_log; \
    echo -e '\n\
postgres         -       nofile          655360\n\
postgres         -       nproc           65536' > /etc/security/limits.d/postgres-limits.conf; \
    echo 'export PATH=$PATH:/usr/local/pgxl/bin' >> /home/postgres/.bashrc; \
    mkdir -p /home/postgres/.ssh; \
    chmod 700 /home/postgres/.ssh; \
    echo -e 'StrictHostKeyChecking no\n\
UserKnownHostsFile /dev/null' > /home/postgres/.ssh/config; \
    echo -e '-----BEGIN RSA PRIVATE KEY-----\n\
MIIEowIBAAKCAQEArsWUxqsB3wSXpx5XeUufrH7FY0mpM4mQOTzGOHAaRw8wa0sI\n\
j+X2DUG6gJCivUbkxCnyZEo5GqdQd7HCBSH+DDwJG0GpSzXrkZxBl8ruHZkMWf7/\n\
hanRIon4ZVvs9fHE+c2BrrNyA3klpxlqmZ39lUuhdqNgMJ0Y1WEndysNehpSL/yA\n\
Hgghly6+zek4T40uNqZf3Zk+amVZUl9JEB+TAroQK6YRID2rUB5MlicUdd9R0l4s\n\
ZLjD5cy+F+2wtLceBRNrbPoQ/XnXGU5PlSRj2n2564NemI4YMARf2b7P2j6Yrcu3\n\
VIXH/I3JcWdVjnEidzxp7I5TsagGMtqx2PaWdwIDAQABAoIBADooJUm2vfioXo6N\n\
2i3SrF+KD8TqThOIQyXIw20kYJokw8fwP/kiXK4soRMHDiBN5vIfwzj8OeRUqMIr\n\
tJRgq1kG4UDgqKUOXEUn6evI61OOCj4oTH7980m6/5066a8ttI4rhGvN6rKdLZpd\n\
KsvTdv6nHCOOWgPTQuA+ZBOTj/iBHLXeSSGy9P8l/nx65tsZIBfl1XdqzOmkdRyR\n\
AFcAtvVEutA1QsrFx/TPHTkyh3pzVdjBbv2FPJ1ov14KTZOfk4E6YvG4xh8HFTBa\n\
2GIyklgw2HMUH2XnQJ6az/KDwK1okecnmtWhWGW0xkMpvQgnXQuFmhpCDe23adxL\n\
hEDyqwECgYEA4B3ShyuYa78DL/bQNQpQEig7Jn7NA8EWdrBMmW7/wN9HT5+F1x/A\n\
0mHkemlFzDQ140vHPA8po1HgNYjBtBFEnqQhN9r6ReWmecs1qM5xT2XXfY6Qa7zk\n\
DNAegZ5TMBEeJYl5qsBH9/Wbj3W4Oz+3KVKmEjhaetKr7jP10BP9y+8CgYEAx6Ko\n\
Myv0m+aA20EbOjtIz5nmdzIX3sPGWqOA+SlIOe7os+fIGnKVC1SBW1/4YFVBkDEL\n\
rCrfxXQeiBqb+sjbZ+RB+fR3uAgU3U/JSdbpTo3HqRC741bL9qVB6tj1coHVhGy1\n\
MfznD/3GswjU2j0FCmN56mTMoygsFkMCjRqJdfkCgYBCnnrLNVlMhe4TnuFLMvKU\n\
QyM6ES0t9YKQ610JquYZVgWjlH67cLPnlbPN82cNCWQPWaIypyswYhEsuKcjuJJW\n\
OeGp8cy9ZEN1JsOflyY1fMduiidB71c2u9KlowOFYUE91Ty2VgvOmFKSdWK7Mfhn\n\
LyswdVHn+qGi4A0suhSSPwKBgBn5eOcHjhEGwbdFVlSSSBvyy0Cw/NgYIuPCKFLC\n\
40neYtqdirSeOfQ6b4ZDC18FWL15FCq3fVwEisBsreTIY3u2ADuUaktAie5tpiP8\n\
WWjGG7dMnRWsQmd6AwSaa/0VFYHgXwb8/9ddzo+W05L9o31BiihMKzADVxEicN+J\n\
F1+JAoGBAN6L89YCiGH4NwyQR72KMAZv8PB1y4oiQ72GDPJs9nuO93gHYnrhYbiU\n\
4L6trHv6cCgEhWXiqo8DgiWqcFuyvyKIvqmGTVB6eCnEDvw1eVXXUIV7sSgeWxEG\n\
+pK1lwP8jAOHDdESO0+zu2/da5lXfzwQhNCkoV9RKUrkptTzQhjK\n\
-----END RSA PRIVATE KEY-----' > /home/postgres/.ssh/id_rsa; \
    echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuxZTGqwHfBJenHld5S5+sfsVjSakziZA5PMY\
4cBpHDzBrSwiP5fYNQbqAkKK9RuTEKfJkSjkap1B3scIFIf4MPAkbQalLNeuRnEGXyu4dmQxZ/v+FqdEiifhlW+z\
18cT5zYGus3IDeSWnGWqZnf2VS6F2o2AwnRjVYSd3Kw16GlIv/IAeCCGXLr7N6ThPjS42pl/dmT5qZVlSX0kQH5M\
CuhArphEgPatQHkyWJxR131HSXixkuMPlzL4X7bC0tx4FE2ts+hD9edcZTk+VJGPafbnrg16YjhgwBF/Zvs/aPpi\
ty7dUhcf8jclxZ1WOcSJ3PGnsjlOxqAYy2rHY9pZ3 postgres' > /home/postgres/.ssh/authorized_keys; \
    chmod 600 /home/postgres/.ssh/authorized_keys; \
    chmod 600 /home/postgres/.ssh/id_rsa; \
    ln -s /mfpdata/pgxl_data/pgpass.conf /home/postgres/.pgpass; \
    chown -R postgres:postgres /home/postgres;

# set local timezone and language
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime; \
    echo -e '\nexport LANG="en_US.UTF-8"' >> /etc/bashrc;
    
# copy pgxl directory from host to docker image
COPY pgxl /usr/local/pgxl

# run sshd when container starts
CMD ["/usr/sbin/sshd", "-D"]

而非镜像部分需要在创建容器时通过挂载数据卷的方式来设置。

Docker容器创建

考虑到要实现跨宿主机的容器联网和PostgresXL集群各节点通信,尝试了以下几种方式:

  1. 容器使用bridge模式,能够访问子网中的各个宿主机,宿主机和内部容器设置端口映射关系,各容器通过宿主机IP加端口映射来实现PostgresXL集群各节点通信和SSH操作。其中每个宿主机上只能存在一个容器实例,相当于把原来在宿主机上关于PostgresXL的设置转移到了容器内部。
  2. 在各宿主机上创建相同的macvlan或ipvlan网络,容器加入该网络,与宿主机处于同一子网,拥有相同的网关和自己独一无二的IP,各容器通过自己的IP和端口实现PostgresXL集群各节点通信和SSH操作。这样宿主机成为相对透明的一层,容器使用更加便捷。
  3. 使用swarm统一管理所有加入集群的宿主机,各宿主机上的容器通过overlay网络互联。但经尝试发现,目前swarm仅支持docker service,不支持容器加入overlay网络。可能swarm更适合无状态的微服务,而不适合这种有状态的分布式数据库节点,数据卷挂载是个问题。

方式一:宿主机端口映射

这种方式实际是通过ip_forward实现容器互通,容器访问隔壁宿主机上的容器需要sysctl开启net.ipv4.ip_forward=1,如果宿主机没有开启这项则docker启动容器时将提示“WARNING: IPv4 forwarding is disabled. Networking will not work.”,容器内无法ping通隔壁宿主机上的容器。实际上当docker-engine启动时会检查内存中这项的值并修改为1,但是docker服务随宿主机自启时可能会因为启动顺序或其他原因导致该项被覆盖回0,所以对于这种方式的互连需要在宿主机的sysctl.conf文件中设定该值为1。

创建容器,部分参数解释如下:

  • -p 设置宿主机和容器的端口映射关系,启动容器后可以在宿主机上看到N个对应的/usr/bin/docker-proxy进程。
  • –add-host 增加主机名称IP映射关系,进入容器可以看到设置被写入/etc/hosts文件。
  • –dns 设置dns服务器地址,进入容器可以看到nameserver被写入/etc/resolv.conf文件。
  • -v 挂载数据卷,设置宿主机目录和容器目录映射关系。
# 创建hosts名称映射方式的容器
docker create -it -h=pgdocker --name=pgdocker \
    -v /mfpdata/ct_data:/mfpdata/pgxl_data -v /mfplog/ct_log:/mfplog/pgxl_log \
    -p 30022:22 -p 20001:20001 -p 20002:20002 -p 21001:21001 -p 21101:21101 -p 23001:23001 -p 23101:23101 \
    --add-host gtm-master:172.31.32.18 --add-host gtm-slave:172.31.32.19 \
    --add-host coord-1:172.31.32.15 --add-host coord-2:172.31.32.16 --add-host coord-3:172.31.32.18 --add-host coord-4:172.31.32.19 \
    --add-host pgxl-dn-1:172.31.32.15 --add-host pgxl-dn-2:172.31.32.16 \
    --add-host pgxl-dn-s-1:172.31.32.18 --add-host pgxl-dn-s-2:172.31.32.19 \
    test-pgxl

# 创建并后台运行dns方式的容器
docker run -itd -h=pgdocker2 --name=pgdocker2 \
    -v /mfpdata/ct_data:/mfpdata/pgxl_data -v /mfplog/ct_log:/mfplog/pgxl_log \
    -p 30022:22 -p 20001:20001 -p 20002:20002 -p 21001:21001 -p 21101:21101 -p 23001:23001 -p 23101:23101 \
    --dns 172.31.32.2 --dns 172.31.32.3 \
    test-pgxl2

端口映射是配合pgxl_ctl脚本约定的端口号,20001是gtm,20002是gtm proxy,21001是coordinator,21101是coordinator connection pool,23001是datanode,23101是datanode connection pool。另外使用宿主机的30022端口映射容器的22端口,可以通过ssh -p 30022进入容器内。

关于Postgres XL还需要修改pg_hba.conf相关模板,额外增加容器的bridge网络的172.17.0.0/16网段的访问权限。

另外在启动gtm proxy的时候还遇到一个问题,可能是gtm_ctl的bug或者是docker的问题,暂时未找到原因。命令gtm_ctl start -Z gtm_proxy -D /mfpdata/pgxl_data/gtm_pxy报错gtm_proxy cannot access the server configuration file “/home/postgres/gtm_proxy.conf”: No such file or directory。只有proxy有问题,-Z gtm正常,后来改为直接用gtm_proxy命令启动,绕过gtm_ctl。

方式二:容器加入macvlan或ipvlan网络

macvlan和ipvlan的配置步骤类似,一个是依据mac转发包,另一个是依据ip转发包。

创建网络,部分参数解释如下:

  • -d 设置network类型。
  • -o parent=eth0 创建在eth0网卡上。
  • –subnet 子网IP段,须与宿主机eth0网卡的相同。
  • –gateway 子网网关,须与宿主机eth0网卡的相同。
  • –ip-range IP范围,可选参数。
# 创建macvlan网络
docker network create -d macvlan  \
    --subnet=172.31.48.0/24  \
    --gateway=172.31.48.1 \
    --ip-range=172.31.48.200/25 \
    -o parent=eth0 pgxl_macvlan_net

# 创建ipvlan网络
docker network create -d ipvlan  \
    --subnet=172.31.32.0/20  \
    --gateway=172.31.32.1 \
    -o parent=eth0 pgxl_ipvlan_net

创建容器,指定网络为上面创建的网络。

# 创建容器,设置网络--net=pgxl_ipvlan_net,设置ip地址--ip=172.31.32.151,设置随docker引擎启动--restart=always
docker run -itd -h=pgdocker --name=pgdocker --restart=always \
    -v /mnt/vdb/pgxl_data:/mfpdata/pgxl_data \
    -v /mnt/vdb/pgxl_log:/mfplog/pgxl_log \
    --net=pgxl_ipvlan_net \
    --ip=172.31.32.151 \
    --dns 172.31.48.2 --dns 172.31.48.3 \
    mf_pgxl

这种方式需要注意容器ip的管理,避免与网段内的宿主机或其他容器发生冲突。

另外如果云服务商的安全体系对mac做了数据包传输限制,则macvlan上的容器与容器或容器与其他宿主机无法通讯。通过以下方法验证:

# 创建并运行busybox容器实例
docker run --net=pgxl_macvlan_net --ip=172.31.48.222 -id --name test101 busybox sh
# 在实例内访问其他容器或宿主机的1234端口
docker exec -it test101 nc 172.31.48.60 1234
# 在容器或宿主机抓包
tcpdump -XvvennS -i eth0 -s 0 'port 1234'
# 在容器或宿主机监听1234端口
nc -lp 1234
Creative Commons License

本文基于署名-非商业性使用-相同方式共享 4.0许可协议发布,欢迎转载、使用、重新发布,但请保留文章署名wanghengbin(包含链接:https://wanghengbin.com),不得用于商业目的,基于本文修改后的作品请以相同的许可发布。

评论(2) “Postgres XL运行在Docker容器中

  1. “使用swarm统一管理所有加入集群的宿主机,各宿主机上的容器通过overlay网络互联。但经尝试发现,目前swarm仅支持docker service,不支持容器加入overlay网络。可能swarm更适合无状态的微服务,而不适合这种有状态的分布式数据库节点,数据卷挂载是个问题。”
    这句话 有很多问题: 首先, 你要明白 overlay网络 的前提条件,必须是基于swarm 集群的, 你说的 不支持加入overlay网络简直是无稽之谈;docker swarm 支持的是 docker service 命令 ,你如果想要使用overlay网络完全可以 添加 –network 参数
    ; docker swarm 完全可以搭建任何形式的 有状态 服务; 如果你真的高端到了想要将分布式多宿主机的docker volumes 统一管理, 请使用 flocker 等分布式 volume 驱动插件。

    希望以后博主对自己的文章负责,每次发表前三思而后行。建议通读docker文档,回炉再造; 建议 删除本文章。

    或者将标题改为 “100% 错误的 教学实例 ”

发表评论