🎨
Docker
  • Docker入门
  • Docker overview
    • 隔离与限制
    • copy-on-write(CoW: 写时复制)
    • docker中的联合文件系统
    • Docker存储
      • tmpfs mounts
      • volume
      • bind mounts
    • Docker网络
      • host模式
      • bridge模式
        • default bridge模式
        • User-defined bridge模式
      • none模式
      • container模式
  • 容器监控方式
    • prometheus + grafana+cAdvisor监控容器
  • Dockerfile指令
    • Usage
    • BuildKit
    • Format
    • Parser directives
      • syntax
      • escape
    • Environment replacement
    • FROM
      • 了解ARG和FROM之间的交互方式
    • .dockerignore file
    • RUN
    • CMD
    • COPY
    • ADD
    • ENV
    • WORKDIR
    • ARG
      • Impact on build caching(对构建缓存的影响)
      • Default(默认值)
      • Scope(作用域)
      • Using ARG Variables(使用ARG变量)
      • Predefined ARGS(预先定义好的ARG变量)
      • Automatic platform ARGs in the global scope(全局作用域中自动化平台的ARGS)
    • STOPSIGNAL
    • HEALTHCHECK
    • SHELL
    • ONBUILD
    • Dockerfile示例
    • LABEL
    • MAINTAINER (deprecated)
    • EXPOSE
    • ENTRYPOINT
      • Shell形式的ENTRYPOINT示例
      • 理解CMD和ENTRYPOINT是如何交互的
    • VOLUME
    • USER
  • docker-compose.yaml文件中常用指令
    • compose文件结构和示例
    • 服务配置参考
      • build
        • dockerfile
        • context
        • shm_size
        • network
        • labels
        • cache_from
        • args
        • target
      • cap_add, cap_drop
      • cgroup_parent
      • command
      • configs
        • 短语法形式
        • 长语法形式
      • container_name
      • credential_spec
        • EXAMPLE GMSA CONFIGURATION
      • depends_on
      • deploy
        • endpoint_mode
        • labels
        • mode
        • placement
        • max_replicas_per_node
        • replicas
        • resource
        • restart_policy
        • rollback_config
        • Update_config
        • Not supported for docker stack deploy
      • devices
      • dns
      • dns_search
      • entrypoint
      • env_file
      • environment
      • expose
      • external_links
      • extra_hosts
      • healthcheck
      • image
      • init
      • isolation
      • labels
      • links
      • logging
      • network_mode
      • networks
        • alias
        • IPV4_ADDRESS, IPV6_ADDRESS
      • pid
      • ports
        • 长语法
        • 短语法
      • profiles
      • restart
      • secrets
        • 长语法
        • 短语法
      • security_opt
      • stop_grace_period
      • stop_signal
      • sysctls
      • tmpfs
      • ulimits
      • userns_mode
      • volumes
        • 长语法
        • 短语法
        • VOLUMES FOR SERVICES, SWARMS, AND STACK FILES
      • domainname, hostname, ipc, mac_address, privileged, read_only, shm_size, stdin_open, tty, user, work
      • Specifying durations
      • Specifying byte values
      • Volume configuration reference
        • driver
        • driver_opts
        • external
        • labels
        • name
      • Network configuration reference
        • name
        • external
        • labels
        • internal
        • ipam
        • enable_ipv6
        • driver
          • overlay
          • bridge
          • host或者none
        • driver_opts
        • attachable
      • configs configuration reference
      • secrets configuration reference
      • Variable substitution
      • Extension fields
  • docker-compose示例
    • WordPress
    • PostgreSQL
    • Django和PostgreSQL
    • Rails和PostgreSQL
  • Dockerfile最佳实践
Powered by GitBook
On this page

Was this helpful?

  1. Dockerfile指令

ENTRYPOINT

ENTRYPOINT有两种形式:

exec形式(我们优先选择的形式)

ENTRYPOINT ["executable", "param1", "param2"]

shell形式

ENTRYPOINT command param1 param2

ENTRYPOINT允许我们配置容器并作为可执行文件运行。

例如,以下代码以其默认内容启动nginx,监听端口80:

$ docker run -i -t --rm -p 80:80 nginx

docker run的命令行参数将添加到exec形式的ENTRYPOINT的所有元素之后,并覆盖使用CMD指定的所有元素。这允许我们将参数传递给入口(entry point),即docker run -d将-d参数传递给入口(entry point)。我们可以使用docker run --entrypoint覆盖ENTRYPOINT指令。

Demon1:

main.go:

package main

import (
    "flag"
    "fmt"
)

type Usage struct {
    Name    string
    Age     string
    Address string
    Sex     string
}

const (
    name = `
The name flag is used to set a new name.
The default value is TonyYang.
`
    age = `
The age flag is used to set a new age.
The default value is 31.
`
    address = `
The address flag is used to set a new address.
The default value is ChangZhou.
`
    sex = `
The sex flag is used to set a new sex.
The default value is TonyYang.
`
)

var usage = Usage{
    Name:    name,
    Age:     age,
    Address: address,
    Sex:     sex,
}

func main() {

    var (
        fName    = flag.String("name", "TonyYang", usage.Name)
        fAge     = flag.String("age", "31", usage.Age)
        fAddress = flag.String("address", "ChangZhou", usage.Address)
        fSex     = flag.String("sex", "male", usage.Sex)
    )

    flag.Parse()

    infos := map[string]*string{
        "name":    fName,
        "age":     fAge,
        "address": fAddress,
        "sex":     fSex,
    }

    for k, v := range infos {
        fmt.Printf("The %s is %s\n", k, *v)
    }

}

Dockerfile:

FROM golang:1.16 AS go
WORKDIR /my_executable/
COPY . .
RUN go build main.go && rm main.go

FROM alpine:latest
WORKDIR /tmp/executable/
COPY --from=go /my_executable/main .
ENTRYPOINT ["./main"]
CMD ["--name=Mae", "--age=30", "--address=ShangHai", "--sex=female"]

上面定义了一个测试ENTRYPOINT的Dockerfile,通过go语言编写。

$ docker run camelgem/entrypoint_demo:v1

默认不为run指定运行的参数,输出结果如下所示:

The name is Mae
The age is 30
The address is ShangHai
The sex is female

输出的结果符合我们CMD中设置的参数默认值。

$ docker run camelgem/entrypoint_demo:v1 --name="camelgem"
The address is ChangZhou
The sex is male
The name is CamelGem
The age is 31

当为run指定参数以后,输出结果发生了改变,输出的值不再按照CMD中设定的那样。在这里name变成了CamelGem,而其余的值是我在程序中设置的默认值。由此可以看出,一旦为run指定了参数,CMD中的值就会被全部覆盖掉。

Shell形式可防止使用任何CMD或run命令行指定的参数,但具有以下缺点:ENTRYPOINT将作为/bin/sh -c的子命令启动,该子命令不传递信号。这意味着可执行文件进程将不是容器的PID 1,并且不会接收Unix信号,因此可执行文件将不会从docker stop 接收到SIGTERM。

只有Dockerfile中的最后一条ENTRYPOINT指令才会生效。

我们可以使用ENTRYPOINT的exec形式来设置相当稳定的默认命令和参数,然后使用CMD来设置更有可能被更改的其他的默认值。

FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

当运行容器时,可以看到top是唯一的进程:

$ docker run -it --rm --name test  top -H

top - 08:25:00 up  7:27,  0 users,  load average: 0.00, 0.01, 0.05
Threads:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.1 us,  0.1 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   2056668 total,  1616832 used,   439836 free,    99352 buffers
KiB Swap:  1441840 total,        0 used,  1441840 free.  1324440 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
    1 root      20   0   19744   2336   2080 R  0.0  0.1   0:00.04 top

要进一步检查结果,可以使用docker exec:

$ docker exec -it test ps aux

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  2.6  0.1  19752  2352 ?        Ss+  08:24   0:00 top -b -H
root         7  0.0  0.1  15572  2164 ?        R+   08:25   0:00 ps aux

我们可以使用docker stop test优雅地请求top关闭。

以下的示例,我们使用ENTRYPOINT来使用Apache服务器运行在前台:

FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

如果我们需要为单个可执行文件编写启动脚本,则可以使用exec和gosu命令确保最终可执行程序能够接收到Unix信号:

#!/usr/bin/env bash
set -e

if [ "$1" = 'postgres' ]; then
    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then
        gosu postgres initdb
    fi

    exec gosu postgres "$@"
fi

exec "$@"

最后,如果我们需要在关闭进程时进行一些额外的清理(或与其他容器通信),或正在协调多个可执行的容器,我们需要确保ENTRYPOINT脚本接收到Unix信号,传递它们,然后执行更多工作:

#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too

# USE the trap if you need to also do manual cleanup after the service is stopped,
#     or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM

# start service in background here
/usr/sbin/apachectl start

echo "[hit enter key to exit] or run 'docker stop <container>'"
read

# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop

echo "exited $0"

如果我们通过docker run -it --rm -p 80:80 --name test apache,我们可以通过docker exec或docker top来查看容器的进程,然后让脚本停止Apache进程。

$ docker exec -it test ps aux

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  0.0   4448   692 ?        Ss+  00:42   0:00 /bin/sh /run.sh 123 cmd cmd2
root        19  0.0  0.2  71304  4440 ?        Ss   00:42   0:00 /usr/sbin/apache2 -k start
www-data    20  0.2  0.2 360468  6004 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
www-data    21  0.2  0.2 360468  6000 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
root        81  0.0  0.1  15572  2140 ?        R+   00:44   0:00 ps aux

$ docker top test

PID                 USER                COMMAND
10035               root                {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054               root                /usr/sbin/apache2 -k start
10055               33                  /usr/sbin/apache2 -k start
10056               33                  /usr/sbin/apache2 -k start

$ /usr/bin/time docker stop test

test
real    0m 0.27s
user    0m 0.03s
sys    0m 0.03s

NOTE:

我们可以通过使用--entrypoint来覆盖ENTRYPOINT的设置,但是这只能设置二进制的可执行程序(不会使用sh -c)。

exec形式会被解析为JSON数组,这意味着我们必须使用双引号来包围单词,而不是通过单引号。

与shell形式不同,exec形式不会调用一个命令行shell。这意味着不会发生正常的shell处理。例如,ENTRYPOINT ["echo", "$HOME"]将不会进行变量替换。如果你希望发生shell处理,那么既可以使用shell形式也可以直接执行一个shell,例如:ENTRYPOINT ["sh", "-c", "echo $HOME"]。当使用exec形式并且直接执行一个shell,这种情况下,对环境变量进行扩展的是shell而不是docker。

PreviousEXPOSENextShell形式的ENTRYPOINT示例

Last updated 4 years ago

Was this helpful?