我们这里使用zookeeper来举例子
传统的zookeeper部署方式就是下载zookeeper的jar包,部署好zookeeper所依赖的环境,然后通过运行jar包里面的zkServer.sh脚本来启动运行zookeeper服务
那么现在比较流行的docker容器是怎么启动zookeeper服务的呢
Docker-compose部署单主机zookeeper集群 ,我使用docker-compose 搭建了zookeeper集群,我拉取的镜像是zookeeper3.7.0官方镜像。
所以首先我们去 docker hub
然后在搜索框里面搜索 zookeeper,我们这里是拉取的官方的zookeeper镜像,所以点击进入zookeeper官方image,看到如下介绍页面
然后点击上图链接去github,因为我们拉取的是zookeeper 3.7.0,所以点击进入
看到有构建镜像的dockerfile文件和一个docker-entrypoint.sh脚本文件
Dockerfile文件
我们可以看到如下的镜像构建步骤
- FROM: 从jdk-11的基础镜像开始构建
- ENV:然后设置了一系列环境变量(会存在于构建好的容器里面)
- RUN:然后添加用户组(zookeeper),添加用户(zookeeper),创建文件夹,设置用户组,用户在这些文件夹的权限
- RUN:安装一些必要的软件包
- ARG,构建参数,与 ENV(设置环境变量) 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。
- RUN:下载zookeeper以及一些列环境配置
- WORKDIR:指定工作目录。为 RUN、CMD、ENTRYPOINT、COPY 和 ADD 设置工作目录,就是切换目录
- VOLUME:定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
作用:避免重要的数据,因容器重启而丢失,这是非常致命的。避免容器不断变大。 - EXPOSE:仅仅只是声明端口。
作用:帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。 - ENV:设置环境变量
- COPY:拷贝文件或目录到容器中,跟ADD命令类似,但不具备自动下载或解压的功能
- ENTRYPOINT:运行容器时执行的shell命令 -----docker-entrypoint.sh
类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。
但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 CMD 指令指定的程序。
优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。
注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。 - CMD: zkServer.sh, start-foreground
类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:
CMD 在docker run 时运行。
RUN 是在 docker build中运行
作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。
注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。
FROM openjdk:11-jre-slim
ENV ZOO_CONF_DIR=/conf \
ZOO_DATA_DIR=/data \
ZOO_DATA_LOG_DIR=/datalog \
ZOO_LOG_DIR=/logs \
ZOO_TICK_TIME=2000 \
ZOO_INIT_LIMIT=5 \
ZOO_SYNC_LIMIT=2 \
ZOO_AUTOPURGE_PURGEINTERVAL=0 \
ZOO_AUTOPURGE_SNAPRETAINCOUNT=3 \
ZOO_MAX_CLIENT_CNXNS=60 \
ZOO_STANDALONE_ENABLED=true \
ZOO_ADMINSERVER_ENABLED=true
# Add a user with an explicit UID/GID and create necessary directories
RUN set -eux; \
groupadd -r zookeeper --gid=1000; \
useradd -r -g zookeeper --uid=1000 zookeeper; \
mkdir -p "$ZOO_DATA_LOG_DIR" "$ZOO_DATA_DIR" "$ZOO_CONF_DIR" "$ZOO_LOG_DIR"; \
chown zookeeper:zookeeper "$ZOO_DATA_LOG_DIR" "$ZOO_DATA_DIR" "$ZOO_CONF_DIR" "$ZOO_LOG_DIR"
# Install required packges
RUN set -eux; \
apt-get update; \
DEBIAN_FRONTEND=noninteractive \
apt-get install -y --no-install-recommends \
ca-certificates \
dirmngr \
gosu \
gnupg \
netcat \
wget; \
rm -rf /var/lib/apt/lists/*; \
# Verify that gosu binary works
gosu nobody true
ARG GPG_KEY=AF3D175EC05DB249738D01AC8D8C3C3ED0B02E66
ARG SHORT_DISTRO_NAME=zookeeper-3.7.0
ARG DISTRO_NAME=apache-zookeeper-3.7.0-bin
# Download Apache Zookeeper, verify its PGP signature, untar and clean up
RUN set -eux; \
ddist() { \
local f="$1"; shift; \
local distFile="$1"; shift; \
local success=; \
local distUrl=; \
for distUrl in \
'https://www.apache.org/dyn/closer.cgi?action=download&filename=' \
https://www-us.apache.org/dist/ \
https://www.apache.org/dist/ \
https://archive.apache.org/dist/ \
; do \
if wget -q -O "$f" "$distUrl$distFile" && [ -s "$f" ]; then \
success=1; \
break; \
fi; \
done; \
[ -n "$success" ]; \
}; \
ddist "$DISTRO_NAME.tar.gz" "zookeeper/$SHORT_DISTRO_NAME/$DISTRO_NAME.tar.gz"; \
ddist "$DISTRO_NAME.tar.gz.asc" "zookeeper/$SHORT_DISTRO_NAME/$DISTRO_NAME.tar.gz.asc"; \
export GNUPGHOME="$(mktemp -d)"; \
gpg --keyserver ha.pool.sks-keyservers.net --recv-key "$GPG_KEY" || \
gpg --keyserver pgp.mit.edu --recv-keys "$GPG_KEY" || \
gpg --keyserver keyserver.pgp.com --recv-keys "$GPG_KEY"; \
gpg --batch --verify "$DISTRO_NAME.tar.gz.asc" "$DISTRO_NAME.tar.gz"; \
tar -zxf "$DISTRO_NAME.tar.gz"; \
mv "$DISTRO_NAME/conf/"* "$ZOO_CONF_DIR"; \
rm -rf "$GNUPGHOME" "$DISTRO_NAME.tar.gz" "$DISTRO_NAME.tar.gz.asc"; \
chown -R zookeeper:zookeeper "/$DISTRO_NAME"
WORKDIR $DISTRO_NAME
VOLUME ["$ZOO_DATA_DIR", "$ZOO_DATA_LOG_DIR", "$ZOO_LOG_DIR"]
EXPOSE 2181 2888 3888 8080
ENV PATH=$PATH:/$DISTRO_NAME/bin \
ZOOCFGDIR=$ZOO_CONF_DIR
COPY docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["zkServer.sh", "start-foreground"]
docker-entrypoint.sh
这是一个脚本文件,在docker run执行的时候会被调用,主要细节可以看看下面这个
【docker】 dockerfile 的CMD ENTRYPOINT命令区别解析
这里会执行如下代码,第一个是脚本文件,会放在容器里面的根目录下面
第二个和第三个是脚本文件执行的时候,传入的参数
/docker-entrypoint.sh zkServer.sh start-foreground
脚本文件里面写了注释:得知这个脚本主要是运行容器启动命令和基本配置文件的部署(zoo.cfg和myid等)
#!/bin/bash
set -e
# Allow the container to be started with `--user`
# $1 代表获取脚本的第一个参数,因为我们在docker run的时候可以自己指定参数,就会覆盖掉CMD中的zhServer.sh 和 start-foreground
# shell判断是否为root权限(id -u = 0) $@ 表示所有参数 $0表示脚本文件
# 容器中不要使用root账号,gosu是个工具,用来提升指定账号的权限,作用与sudo命令类似,
#所以这里是使用 dockerfile 创建的zookeeper账户来运行这个脚本文件的
if [[ "$1" = 'zkServer.sh' && "$(id -u)" = '0' ]]; then
chown -R zookeeper "$ZOO_DATA_DIR" "$ZOO_DATA_LOG_DIR" "$ZOO_LOG_DIR"
exec gosu zookeeper "$0" "$@"
fi
# 如果zookeeper配置文件不存在,这里会帮我们创建一个配置文件,并设置好如下配置内容
# Generate the config only if it doesn't exist
if [[ ! -f "$ZOO_CONF_DIR/zoo.cfg" ]]; then
CONFIG="$ZOO_CONF_DIR/zoo.cfg"
{
echo "dataDir=$ZOO_DATA_DIR"
echo "dataLogDir=$ZOO_DATA_LOG_DIR"
echo "tickTime=$ZOO_TICK_TIME"
echo "initLimit=$ZOO_INIT_LIMIT"
echo "syncLimit=$ZOO_SYNC_LIMIT"
echo "autopurge.snapRetainCount=$ZOO_AUTOPURGE_SNAPRETAINCOUNT"
echo "autopurge.purgeInterval=$ZOO_AUTOPURGE_PURGEINTERVAL"
echo "maxClientCnxns=$ZOO_MAX_CLIENT_CNXNS"
echo "standaloneEnabled=$ZOO_STANDALONE_ENABLED"
echo "admin.enableServer=$ZOO_ADMINSERVER_ENABLED"
} >> "$CONFIG"
# -z 如果该变量的字符串长度为0则为真
if [[ -z $ZOO_SERVERS ]]; then
ZOO_SERVERS="server.1=localhost:2888:3888;2181"
fi
for server in $ZOO_SERVERS; do
echo "$server" >> "$CONFIG"
done
if [[ -n $ZOO_4LW_COMMANDS_WHITELIST ]]; then
echo "4lw.commands.whitelist=$ZOO_4LW_COMMANDS_WHITELIST" >> "$CONFIG"
fi
for cfg_extra_entry in $ZOO_CFG_EXTRA; do
echo "$cfg_extra_entry" >> "$CONFIG"
done
fi
#配置zookeeper的myid文件(如果没找到myid文件的话)
# Write myid only if it doesn't exist
if [[ ! -f "$ZOO_DATA_DIR/myid" ]]; then
echo "${ZOO_MY_ID:-1}" > "$ZOO_DATA_DIR/myid"
fi
exec "$@"
根据如上脚本可以得知,是使用的dockerfile 创建的zookeeper用户来运行这个代码的
gosu 命令运行的更多信息可以参考 这篇: docker与gosu
脚本代码中的真正运行容器的执行指令如下
exec gosu zookeeper "$0" "$@" == exec gosu zookeeper /docker-entrypoint.sh zhServer.sh start-foreground
gosu zookeeper “$ 0” “$@” 前面加上个exec,表示以gosu zookeeper " $ 0" “$@” 这个命令启动的进程替换正在执行的docker-entrypoint.sh进程
总结:dockerfile构建好镜像之后,docker run 运行这个镜像的一个容器的时候(镜像是类,容器是对象,这么一个对比关系)
那么就会执行如下代码,来启动容器
/docker-entrypoint.sh zhServer.sh start-foreground
在docker run的时候,后面加参数也是可以覆盖更换这些配置的