我们这里使用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脚本文件
我们可以看到如下的镜像构建步骤
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 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的时候,后面加参数也是可以覆盖更换这些配置的
怎么操作可以看篇【docker】 dockerfile 的CMD ENTRYPOINT命令区别解析