【docker】 docker是怎么运行容器服务的

我们这里使用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文件

我们可以看到如下的镜像构建步骤

  1. FROM: 从jdk-11的基础镜像开始构建
  2. ENV:然后设置了一系列环境变量(会存在于构建好的容器里面)
  3. RUN:然后添加用户组(zookeeper),添加用户(zookeeper),创建文件夹,设置用户组,用户在这些文件夹的权限
  4. RUN:安装一些必要的软件包
  5. ARG,构建参数,与 ENV(设置环境变量) 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。
  6. RUN:下载zookeeper以及一些列环境配置
  7. WORKDIR:指定工作目录。为 RUN、CMD、ENTRYPOINT、COPY 和 ADD 设置工作目录,就是切换目录
  8. VOLUME:定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
    作用:避免重要的数据,因容器重启而丢失,这是非常致命的。避免容器不断变大。
  9. EXPOSE:仅仅只是声明端口。
    作用:帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
    在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
  10. ENV:设置环境变量
  11. COPY:拷贝文件或目录到容器中,跟ADD命令类似,但不具备自动下载或解压的功能
  12. ENTRYPOINT:运行容器时执行的shell命令 -----docker-entrypoint.sh
    类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。
    但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 CMD 指令指定的程序。
    优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。
    注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
  13. 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的时候,后面加参数也是可以覆盖更换这些配置的

怎么操作可以看篇【docker】 dockerfile 的CMD ENTRYPOINT命令区别解析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值