运维
PythonJava前端数据库
Docker
Docker
  • Docker简介
  • 基本概念
    • 镜像
    • 容器
    • 仓库
  • 安装
  • 使用镜像
    • 获取镜像
    • 列出镜像
    • 删除镜像
    • 导入和导出
  • Dockerfile
    • 构建镜像
    • 多阶段构建
    • 构建多种系统架构支持的镜像
    • Dockerfile 命令介绍
      • COPY 复制文件
      • ADD 更高级的复制
      • CMD 容器启动命令
      • ENTPYPOINT 入口点
      • ENV 设置环境变量
      • ARG 构建参数
      • VOLUME 匿名卷
      • USER 指定当前用户
      • EXPOSE 暴露端口
      • WORKDIR 工作目录
      • SHELL 指令
      • LABEL 为镜像添加源数据
      • ONBUILD 为他人作嫁衣裳
  • 数据管理
    • 数据卷
    • 挂载主机目录
  • 操作容器
    • 启动、停止和删除
    • 进入容器
    • 导入和导出
    • 重启策略
  • 网络配置
    • 基本网络配置
    • 高级网络配置
  • Docker Buildx
    • 使用buildx构建镜像
    • 使用buildx构建多种系统架构镜像
  • Docker Compose
    • 简介
    • 简单使用
    • 多个配置文件
    • Compose网络配置
    • 控制启动服务启动顺序
    • 命令说明
  • Kubernetes
由 GitBook 提供支持
在本页
  • 场景一:
  • 场景二

这有帮助吗?

  1. Dockerfile
  2. Dockerfile 命令介绍

ENTPYPOINT 入口点

ENTRYOINT 的格式和 RUN 指令格式一样,分为 exec 格式和 shell 格式。

ENTRYPOINT 的目的和 CMD 一样,都是指定容器启动程序及其参数。ENTRYPOINT 在运行时可以替代,不过要比 CMD 略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行命令,而是将 CMD 的内容作为参数传递给 ENTRYPOITN 指令。实际执行时,将变成:

<ENTRYPOINT> "<CMD>"

为什么有了 CMD 后还要有 ENTRYPOINT 呢?ENTRYPOINT 到底有什么好处呢?让我们来看几个场景。

场景一:

假设我们需要一个得知自己当前公网 IP 的镜像,那么可以先用 CMD 来实现:

FROM ubuntu:18.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
    
CMD ["curl", "-s", "http://myip.ipip.net"]

假设我们使用 docker build -t myip . 来构建镜像的话,如果我们需要查询当前公网 IP,只需要执行:

docker run myip

不过命令总有参数,如果我们希望加参数呢?比如从上面的 CMD 总可以看到实质的命令是 curl,那么如果我们希望显示 HTTP 头信息,就需要加上 -i 参数,那么我们就不能说是在运行镜像的时候添加 -i 参数。

docker run myip -i
docker: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".

之前我们说过,跟在镜像名后面的是 command,运行时会替换 CMD 的默认值。因此这里的 -i 替换了原来的 CMD,所以自然找不到命令。况且 -i 根本就不是一个命令。

那么如果我们希望加入 -i 参数,就只能完整的输入这个命令:

docker run myip curl -s http://myip.ipip.net -i

这显然不是一个好的解决方案,因此诞生了 ENTRYPOINT,使用ENTRYPOINT 就可以很好的解决命令被替换的问题。现在我们重新用 ENTRYPOINT 来实现这个镜像。

FROM ubuntu:18.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "http://myip.ipip.net" ]

再次尝试执行 docker run myip -i:

$ docker run myip
当前 IP:61.148.226.66 来自:北京市 联通

$ docker run myip -i
HTTP/1.1 200 OK
Server: nginx/1.8.0
Date: Tue, 22 Nov 2016 05:12:40 GMT
Content-Type: text/html; charset=UTF-8
Vary: Accept-Encoding
X-Powered-By: PHP/5.6.24-1~dotdeb+7.1
X-Cache: MISS from cache-2
X-Cache-Lookup: MISS from cache-2:80
X-Cache: MISS from proxy-2_6
Transfer-Encoding: chunked
Via: 1.1 cache-2:80, 1.1 proxy-2_6:8006
Connection: keep-alive

当前 IP:61.148.226.66 来自:北京市 联通

可以看到,这次是成功了,因为当 ENTRYPOINT 存在后,CMD 的内容将会作为参数传给 ENTRYPOINT,而这里的 -i 就是新的 CMD,因此会作为参数传给 curl,从而达到预期的效果。

场景二

启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。

比如 mysql 数据库,可能需要一些数据库配置、初始化工作,这些工作需要在 mysql 服务器运行之前处理。

此外,可能希望避免使用 root 用户去启动服务,从而提供安全性,而在启动服务器之前还需要以 root 身份执行一些必要的准备工作,最后切换到服务用户身份启动服务。或者除了服务外,其它命令依旧可以使用 root 身份,方便调试等。

这些准备工作适合容器 CMD 无关的,无论 CMD 是什么,都需要事先进行一个预处理的工作。这种情况下,可以写一个脚本,然后范围 ENTRYPOINT 中去执行,从而这个脚本会将接收到的参数,作为命令,在脚本最后执行,比如 redis 中就是这么做的。

FROM alpine:3.4
...
RUN addgroup -S redis && adduser -S -G redis redis
...
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 6379
CMD [ "redis-server" ]

可以看到其中为了 redis 服务创建了 redis 用户,并在最后指定了 ENTRYPOINT 为 docker-entrypoint.sh 脚本。

#!/bin/sh
...
# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
	find . \! -user redis -exec chown redis '{}' +
	exec gosu redis "$0" "$@"
fi

exec "$@"

该脚本的内容就是根据 CMD 的内容来判断,如果是 redis-server 的话,则切换到 redis 用户身份启动容器,否则依旧使用 root 身份执行。例如:

$ docker run -it redis id
uid=0(root) gid=0(root) groups=0(root)
上一页CMD 容器启动命令下一页ENV 设置环境变量

最后更新于1年前

这有帮助吗?