mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-03-03 14:09:31 +00:00
332 lines
6.7 KiB
Go
332 lines
6.7 KiB
Go
## 7.5 ENTRYPOINT 入口点
|
||
|
||
### 什么是 ENTRYPOINT
|
||
|
||
`ENTRYPOINT` 指定容器启动时运行的入口程序。与 CMD 不同,ENTRYPOINT 定义的命令不会被 `docker run` 的参数覆盖,而是**接收这些参数**。
|
||
|
||
> **核心作用**:让镜像像一个可执行程序一样使用,`docker run` 的参数作为这个程序的参数。
|
||
|
||
---
|
||
|
||
### 语法格式
|
||
|
||
| 格式 | 语法 | 推荐程度 |
|
||
|------|------|---------|
|
||
| **exec 格式**| `ENTRYPOINT ["可执行文件", "参数1"]` | ✅**推荐** |
|
||
| **shell 格式** | `ENTRYPOINT 命令 参数` | ⚠️ 不推荐 |
|
||
|
||
```docker
|
||
## exec 格式(推荐)
|
||
|
||
ENTRYPOINT ["nginx", "-g", "daemon off;"]
|
||
|
||
## shell 格式(不推荐)
|
||
|
||
ENTRYPOINT nginx -g "daemon off;"
|
||
```
|
||
|
||
---
|
||
|
||
### ENTRYPOINT vs CMD
|
||
|
||
#### 核心区别
|
||
|
||
| 特性 | ENTRYPOINT | CMD |
|
||
|------|------------|-----|
|
||
| **定位** | 固定的入口程序 | 默认参数 |
|
||
| **docker run 参数** | 追加为参数 | 完全覆盖 |
|
||
| **覆盖方式** | `--entrypoint` | 直接指定命令 |
|
||
| **适用场景** | 把镜像当命令用 | 提供默认行为 |
|
||
|
||
#### 行为对比
|
||
|
||
```docker
|
||
## 只用 CMD
|
||
|
||
CMD ["curl", "-s", "http://example.com"]
|
||
```
|
||
|
||
```bash
|
||
$ docker run myimage # curl -s http://example.com
|
||
$ docker run myimage -v # 执行 -v(错误!)
|
||
$ docker run myimage curl -v ... # curl -v ...(完全替换)
|
||
```
|
||
|
||
```docker
|
||
## 只用 ENTRYPOINT
|
||
|
||
ENTRYPOINT ["curl", "-s"]
|
||
```
|
||
|
||
```bash
|
||
$ docker run myimage # curl -s(缺参数)
|
||
$ docker run myimage http://example.com # curl -s http://example.com ✓
|
||
```
|
||
|
||
```docker
|
||
## ENTRYPOINT + CMD 组合(推荐)
|
||
|
||
ENTRYPOINT ["curl", "-s"]
|
||
CMD ["http://example.com"]
|
||
```
|
||
|
||
```bash
|
||
$ docker run myimage # curl -s http://example.com(默认)
|
||
$ docker run myimage http://other.com # curl -s http://other.com ✓
|
||
$ docker run myimage -v http://other.com # curl -s -v http://other.com ✓
|
||
```
|
||
|
||
---
|
||
|
||
### 场景一:让镜像像命令一样使用
|
||
|
||
#### 需求(启动前准备)
|
||
|
||
创建一个查询公网 IP 的"命令"镜像。
|
||
|
||
#### 使用 CMD 的问题
|
||
|
||
```docker
|
||
FROM ubuntu:24.04
|
||
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||
CMD ["curl", "-s", "http://myip.ipip.net"]
|
||
```
|
||
|
||
```bash
|
||
$ docker run myip # ✓ 正常工作
|
||
当前 IP:61.148.226.66
|
||
|
||
$ docker run myip -i # ✗ 错误!
|
||
exec: "-i": executable file not found
|
||
## -i 替换了整个 CMD,被当作可执行文件
|
||
|
||
...
|
||
```
|
||
|
||
#### 使用 ENTRYPOINT 解决
|
||
|
||
```docker
|
||
FROM ubuntu:24.04
|
||
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||
ENTRYPOINT ["curl", "-s", "http://myip.ipip.net"]
|
||
```
|
||
|
||
```bash
|
||
$ docker run myip # ✓ 正常工作
|
||
当前 IP:61.148.226.66
|
||
|
||
$ docker run myip -i # ✓ 添加 -i 参数
|
||
HTTP/1.1 200 OK
|
||
...
|
||
当前 IP:61.148.226.66
|
||
```
|
||
|
||
#### 交互图示
|
||
|
||
```
|
||
ENTRYPOINT ["curl", "-s", "http://myip.ipip.net"]
|
||
│
|
||
docker run myip -i
|
||
│
|
||
▼
|
||
curl -s http://myip.ipip.net -i
|
||
└─────────────────────────────┘
|
||
ENTRYPOINT + docker run 参数
|
||
```
|
||
|
||
---
|
||
|
||
### 场景二:启动前的准备工作
|
||
|
||
#### 需求
|
||
|
||
在启动主服务前执行初始化脚本(如数据库迁移、权限设置)。
|
||
|
||
#### 实现方式
|
||
|
||
```docker
|
||
FROM redis:7-alpine
|
||
COPY docker-entrypoint.sh /usr/local/bin/
|
||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||
CMD ["redis-server"]
|
||
```
|
||
|
||
**docker-entrypoint.sh**:
|
||
|
||
```bash
|
||
#!/bin/sh
|
||
set -e
|
||
|
||
## 准备工作
|
||
|
||
echo "Initializing..."
|
||
|
||
## 如果第一个参数是 redis-server,以 redis 用户运行
|
||
|
||
if [ "$1" = 'redis-server' ]; then
|
||
chown -R redis:redis /data
|
||
exec gosu redis "$@"
|
||
fi
|
||
|
||
## 其他命令直接执行
|
||
|
||
exec "$@"
|
||
```
|
||
|
||
#### 工作流程
|
||
|
||
```
|
||
docker run redis docker run redis bash
|
||
│ │
|
||
▼ ▼
|
||
docker-entrypoint.sh redis-server docker-entrypoint.sh bash
|
||
│ │
|
||
├─ 初始化 ├─ 初始化
|
||
├─ chown -R redis:redis /data │
|
||
└─ exec gosu redis redis-server └─ exec bash
|
||
(以 redis 用户运行) (以 root 用户运行)
|
||
```
|
||
|
||
#### 关键点
|
||
|
||
1. **exec "$@"**:用传入的参数替换当前进程,确保信号正确传递
|
||
2. **条件判断**:根据 CMD 不同执行不同逻辑
|
||
3. **用户切换**:使用 `gosu` 切换用户(比 `su` 更适合容器)
|
||
|
||
---
|
||
|
||
### 场景三:带参数的应用
|
||
|
||
```docker
|
||
FROM python:3.12-slim
|
||
WORKDIR /app
|
||
COPY . .
|
||
RUN pip install -r requirements.txt
|
||
|
||
ENTRYPOINT ["python", "app.py"]
|
||
CMD ["--host", "0.0.0.0", "--port", "8080"]
|
||
```
|
||
|
||
```bash
|
||
## 使用默认参数
|
||
|
||
$ docker run myapp
|
||
## 执行: python app.py --host 0.0.0.0 --port 8080
|
||
|
||
## 覆盖参数
|
||
|
||
$ docker run myapp --host 0.0.0.0 --port 9000
|
||
## 执行: python app.py --host 0.0.0.0 --port 9000
|
||
|
||
## 完全不同的参数
|
||
|
||
$ docker run myapp --help
|
||
## 执行: python app.py --help
|
||
|
||
...
|
||
```
|
||
|
||
---
|
||
|
||
### 覆盖 ENTRYPOINT
|
||
|
||
使用 `--entrypoint` 参数覆盖:
|
||
|
||
```bash
|
||
## 正常运行
|
||
|
||
$ docker run myimage
|
||
|
||
## 覆盖 ENTRYPOINT 进入 shell 调试
|
||
|
||
$ docker run --entrypoint /bin/sh myimage
|
||
|
||
## 覆盖 ENTRYPOINT 并传入参数
|
||
|
||
$ docker run --entrypoint /bin/cat myimage /etc/os-release
|
||
```
|
||
|
||
---
|
||
|
||
### ENTRYPOINT 与 CMD 组合表
|
||
|
||
| ENTRYPOINT | CMD | 最终执行命令 |
|
||
|------------|-----|-------------|
|
||
| 无 | 无 | 无(容器无法启动) |
|
||
| 无 | `["cmd", "p1"]` | `cmd p1` |
|
||
| `["ep", "p1"]` | 无 | `ep p1` |
|
||
| `["ep", "p1"]` | `["cmd", "p2"]` | `ep p1 cmd p2` |
|
||
| `ep p1`(shell) | `["cmd", "p2"]` | `/bin/sh -c "ep p1"`(CMD 被忽略) |
|
||
|
||
> ⚠️ **注意**:shell 格式的 ENTRYPOINT 会忽略 CMD!
|
||
|
||
---
|
||
|
||
### 最佳实践
|
||
|
||
#### 1. 使用 exec 格式
|
||
|
||
```docker
|
||
## ✅ 推荐
|
||
|
||
ENTRYPOINT ["python", "app.py"]
|
||
|
||
## ❌ 避免 shell 格式
|
||
|
||
ENTRYPOINT python app.py
|
||
```
|
||
|
||
#### 2. 提供有意义的默认参数
|
||
|
||
```docker
|
||
ENTRYPOINT ["nginx"]
|
||
CMD ["-g", "daemon off;"]
|
||
```
|
||
|
||
#### 3. 入口脚本使用 exec
|
||
|
||
运行以下命令:
|
||
|
||
```bash
|
||
#!/bin/sh
|
||
## 准备工作...
|
||
|
||
## 使用 exec 替换当前进程
|
||
|
||
exec "$@"
|
||
```
|
||
|
||
#### 4. 处理信号
|
||
|
||
确保 ENTRYPOINT 脚本能正确传递信号:
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
trap 'kill -TERM $PID' TERM INT
|
||
|
||
## 启动应用
|
||
|
||
app "$@" &
|
||
PID=$!
|
||
|
||
## 等待应用退出
|
||
|
||
wait $PID
|
||
```
|
||
|
||
---
|
||
|
||
### 本章小结
|
||
|
||
| ENTRYPOINT | CMD | 适用场景 |
|
||
|------------|-----|---------|
|
||
| ✓ | ✗ | 镜像作为固定命令使用 |
|
||
| ✗ | ✓ | 简单的默认命令 |
|
||
| ✓ | ✓ | **推荐**:固定命令 + 可配置参数 |
|
||
|
||
### 延伸阅读
|
||
|
||
- [CMD 容器启动命令](7.4_cmd.md):默认命令
|
||
- [最佳实践](../16_appendix/16.1_best_practices.md):启动命令设计
|
||
- [后台运行](../05_container/5.2_daemon.md):前台/后台概念
|