# 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**&#x5C06;**-d**参数传递给**入口（entry point）**。我们可以使用**docker run --entrypoint**覆盖**ENTRYPOINT指令**。

Demon1:

**main.go:**

```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语言**编写。

```bash
$ docker run camelgem/entrypoint_demo:v1
```

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

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

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

```bash
$ 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**将作&#x4E3A;**/bin/sh -c**的子命令启动，该子命令不传递信号。这意味着可执行文件进程将不是容器的**PID 1**，并且不会接收**Unix**信号，因此可执行文件将不会从**docker stop** 接收到**SIGTERM**。

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

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

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

当运行容器时，可以看到**top**是唯一的进程：

```bash
$ 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**:

```bash
$ 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**信号：

```bash
#!/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**信号，传递它们，然后执行更多工作：

```bash
#!/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**进程。

```bash
$ 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:**

我们可以通过使&#x7528;**--entrypoint**来覆盖**ENTRYPOINT**的设置，但是这只能设置二进制的可执行程序（不会使用**sh -c**）。

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

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

###


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://camelgemonion.gitbook.io/docker/dockerfile-zhi-ling/entrypoint.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
