自定义容器
Docker 端口映射
面试题: docker run -p 背后发生了什么?

docker run -p <宿主机端口>:<容器端口> 的本质是打通宿主机与容器之间的网络通信,让外部(或宿主机)能通过宿主机的端口访问容器内的服务(如容器内 Nginx 的 80 端口映射到宿主机的 8080 端口)。
二、-p 背后的完整执行流程
当你执行 docker run -p 80:80 nginx 时,Docker 会依次完成以下操作:
- 创建容器的网络命名空间(Network Namespace)
- Docker 为每个容器创建独立的网络命名空间(隔离网络环境:独立的网卡、IP、路由表),容器默认获得一个虚拟网卡
eth0,并分配私有 IP(如172.17.0.2,属于 Docker 默认网桥docker0的子网)。 - 宿主机通过
docker0网桥与容器的eth0连通(docker0是宿主机上的虚拟网桥,默认 IP 为172.17.0.1)。
- 宿主机端口绑定(Port Binding)
-
Docker 在宿主机上
绑定指定的端口
(如 80 端口),监听来自外部的连接请求。
- 若指定
0.0.0.0:<宿主机端口>(默认,如-p 80:80),宿主机所有网卡都会监听该端口; - 若指定
127.0.0.1:80:80,则仅宿主机本地能访问该端口。
- 若指定
- 配置 iptables DNAT 转发规则(核心!)
Docker 会通过iptables 的 DNAT(目标地址转换)规则,将宿主机端口的请求转发到容器的私有 IP 和端口。
iptabes -nL
-
数据包流转过程(以访问宿主机 80 端口为例)
-
外部客户端发送请求到宿主机的 80 端口;
-
宿主机的 iptables 规则匹配到请求,将目标地址改为容器的私有 IP 和端口;
-
数据包通过
docker0网桥发送到容器的eth0网卡; -
容器内的服务(如 Nginx)监听 80 端口,接收并处理请求;
-
响应数据包按原路返回(iptables 自动做反向 SNAT 转换),最终回到客户端。
| 参数格式 | 含义 | 底层差异 |
|---|---|---|
-p 80:80 | 宿主机所有网卡的 80 映射到容器 80 | iptables 规则匹配 anywhere:80,绑定宿主机 0.0.0.0:80 |
-p 127.0.0.1:80:80 | 仅宿主机本地 80 映射到容器 80 | iptables 规则匹配 127.0.0.1:80,绑定宿主机 127.0.0.1:80 |
-p 80 | 宿主机随机端口映射到容器 80 | Docker 随机分配宿主机端口(32768+),iptables 规则匹配该随机端口 |
-p 80-82:80-82 | 端口范围映射 | 为每个端口单独创建 iptables 规则和绑定 |
Dockerfile容器化(核心)⭐⭐⭐
Dockerfile 是一个纯文本格式的配置文件,包含了一系列构建 Docker 镜像的指令和参数。Docker 引擎可以读取 Dockerfile 中的指令,自动执行镜像构建过程,最终生成可重复、可移植的 Docker 镜像。它是实现 “一次构建,处处运行” 的核心载体,定义了镜像的分层结构和运行时配置。
工作原理(How)
Dockerfile 的构建过程遵循分层构建和缓存机制:
- 分层构建:Dockerfile 中的每一条指令都会生成一个独立的镜像层(Layer),层是只读的且可复用。上层基于下层修改,最终所有层叠加形成完整镜像。
- 缓存机制:Docker 会缓存每一步构建的结果,当指令内容或上下文文件未变化时,直接复用缓存层,大幅提升构建效率。
- 构建上下文:执行
docker build时指定的路径(如.)为构建上下文,Docker 会将上下文内的所有文件发送给 Docker 守护进程,供COPY/ADD等指令使用。
| 指令类别 | 指令 | 作用与语法 | 示例 |
|---|---|---|---|
| 基础镜像 | FROM | 指定基础镜像(必须是第一条非注释指令),scratch 表示空镜像。 | FROM ubuntu:22.04 / FROM scratch |
| 元数据 | LABEL | 添加镜像元数据(如作者、版本、描述),键值对形式。 | LABEL maintainer="dev@example.com" version="1.0" description="Web App" |
MAINTAINER | (已过时,推荐用 LABEL)指定镜像维护者。 | MAINTAINER dev@example.com | |
| 构建操作 | RUN | 构建阶段执行命令,创建新层。分 shell 格式(RUN <命令>)和 exec 格式(RUN ["可执行文件", "参数"])。 | RUN apt update && apt install -y nginx / RUN ["apt", "update"] |
COPY | 将构建上下文内的文件 / 目录复制到镜像中,支持通配符和路径重命名。 | COPY ./app /usr/local/app / COPY index.html /var/www/html/ | |
ADD | 功能同 COPY,但支持解压压缩包(如 .tar)和下载远程文件(不推荐)。 | ADD app.tar.gz /usr/local/ / ADD https://example.com/file.txt /tmp/ | |
| 运行时配置 | CMD | 指定容器启动时默认执行的命令,可被 docker run 命令行参数覆盖。 | CMD ["nginx", "-g", "daemon off;"] / CMD nginx -g 'daemon off;' |
ENTRYPOINT | 容器启动的 “入口点”,命令行参数会作为其参数追加(不可被覆盖,除非用 --entrypoint)。 | ENTRYPOINT ["nginx"](配合 CMD ["-g", "daemon off;"]) | |
ENV | 设置环境变量,构建和运行时均有效,可通过 $变量名 引用。 | ENV APP_PORT=8080 / ENV PATH=$PATH:/usr/local/bin | |
EXPOSE | 声明容器暴露的端口(仅为文档说明,不会自动映射到主机)。 | EXPOSE 80 443 | |
VOLUME | 定义匿名卷,用于持久化数据(容器删除时卷数据保留)。 | VOLUME ["/data"] / VOLUME /var/lib/mysql | |
| 环境配置 | WORKDIR | 设置后续指令的工作目录(推荐用绝对路径),不存在则自动创建。 | WORKDIR /usr/local/app |
USER | 指定后续指令执行的用户 / 用户组(需提前创建用户)。 | USER appuser / USER 1000:1000 | |
| 构建优化 | ARG | 定义构建参数(仅构建时有效,运行时失效),可通过 --build-arg 传递。 | ARG VERSION=1.0 / FROM nginx:$VERSION |
ONBUILD | 触发器,当当前镜像作为基础镜像时,子镜像构建时执行该指令。 | ONBUILD COPY ./app /usr/local/app | |
HEALTHCHECK | 定义容器健康检查规则,判断容器是否正常运行。 | `HEALTHCHECK --interval=30s CMD curl -f http://localhost/ |
FROM
指定基础镜像
作用:定义构建镜像的基础层,必须是 Dockerfile 的第一条指令(除注释外)。
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
参数说明:
--platform=<platform>:指定基础镜像的平台(如linux/amd64、linux/arm64),用于跨架构构建。<image>[:<tag>]:基础镜像名称 + 标签(标签默认latest)。AS <name>:为构建阶段命名(多阶段构建用)。
示例
FROM --platform=linux/amd64 python:3.11-slim AS builder
WORKDIR
设置工作目录
作用:为后续指令(RUN/COPY/CMD 等)设置当前工作目录,不存在则自动创建。
参数说明:
<dirpath>:绝对路径或相对路径(相对前一个 WORKDIR)。
示例
WORKDIR /app # 绝对路径
WORKDIR src # 相对路径,最终为/app/src
USER
指定执行用户
作用:设置后续指令的运行用户 / 用户组(默认 root)。
语法格式
USER <user>[:<group>] # 用户名/UID + 组名/GID
USER <UID>[:<GID>]
示例:
USER appuser # 使用已创建的用户
USER 1000:1000 # 使用UID/GID
RUN
执行构建时命令
RUN ls
CMD
容器启动默认命令
作用:定义容器启动时执行的命令(可被docker run命令行参数覆盖)。
语法格式
# Shell格式
CMD <command>
# Exec格式(推荐)
CMD ["<executable>", "<param1>", "<param2>"]
# 参数格式(配合ENTRYPOINT使用)
CMD ["<param1>", "<param2>"]
示例
CMD ["python", "app.py"] # Exec格式(推荐)
CMD echo "Hello Docker" # Shell格式
ENTRYPOINT
容器入口点
作用:定义容器启动的固定入口命令(不可被覆盖,仅可通过--entrypoint参数修改),常与 CMD 配合传参。
语法格式:
# Exec格式(推荐)
ENTRYPOINT ["<executable>", "<param1>"]
# Shell格式
ENTRYPOINT <command>
示例(与 CMD 配合):
ENTRYPOINT ["python"] # 固定执行python
CMD ["app.py"] # 默认参数为app.py
# 容器启动:docker run <image> → 执行python app.py
# 传参启动:docker run <image> test.py → 执行python test.py
COPY
复制文件 / 目录
作用:从构建上下文复制文件到容器(基础复制功能)。
语法格式:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src1>", "<src2>",... "<dest>"] # 路径含空格时用
参数说明:
--chown=<user>:<group>:设置复制后文件的所属用户 / 组(需基础镜像支持)。<src>:源路径(构建上下文内,支持通配符*)。<dest>:目标路径(绝对 / 相对路径)。
示例:
COPY --chown=appuser:appuser app/ /app/ # 复制并修改所属用户
COPY config*.yaml /app/config/ # 通配符匹配
ADD
作用:COPY 的增强版,支持自动解压本地压缩包、URL 下载。
语法格式(同 COPY):
ADD [--chown=<user>:<group>] <src>... <dest>
额外参数 / 特性:
- 若
<src>是本地压缩包(.tar/.tar.gz等),自动解压到<dest>(URL 下载的压缩包不解压)。
示例:
ADD app.tar.gz /app/ # 自动解压本地压缩包
VOLUME
定义匿名 / 命名卷
作用:创建数据卷(持久化数据,容器删除后数据保留)。
语法格式:
VOLUME ["<path1>", "<path2>"] # Exec格式(推荐)
VOLUME <path> # Shell格式
示例:
VOLUME ["/data", "/logs"] # 定义两个数据卷
ENV
设置环境变量
作用:定义环境变量(容器运行时仍有效,可被docker run -e覆盖)。
语法格式:
ENV <key>=<value> ... # 推荐(支持多个变量)
ENV <key> <value> # 旧格式(单个变量)
示例:
ENV PYTHONUNBUFFERED=1 APP_PORT=8080 # 多个变量
ENV PATH="/app/bin:$PATH" # 修改系统路径
ARG
构建参数
作用:定义构建时的临时参数(容器运行时无效),可通过docker build --build-arg <key>=<value>传参。
语法格式:
ARG <name>[=<default value>] # 可指定默认值
示例:
ARG VERSION=1.0 # 默认值1.0
RUN apt-get install -y app=${VERSION}
# 构建时传参:docker build --build-arg VERSION=2.0 .
LABEL
添加镜像元数据
作用:为镜像添加键值对形式的元数据(如作者、版本)。
语法格式:
LABEL <key>=<value> <key>=<value> ...
示例:
LABEL maintainer="dev@example.com" version="1.0" description="Nginx镜像"
EXPOSE
声明端口
作用:声明容器运行时监听的端口(仅文档说明,不自动映射,需docker run -p映射)。
语法格式:
EXPOSE <port>[/<protocol>] ... # protocol默认tcp
示例:
EXPOSE 80 443/udp # 声明80(tcp)、443(udp)端口
ONBUILD
设置触发指令
作用:当前镜像作为基础镜像时,触发子镜像构建时执行的命令(如父镜像定义 ONBUILD,子镜像 FROM 父镜像时自动执行)。
语法格式:
ONBUILD <INSTRUCTION> # 可接COPY/RUN等指令
示例:
# 父镜像Dockerfile
ONBUILD COPY . /app
ONBUILD RUN npm install
# 子镜像Dockerfile(FROM父镜像)
# 自动执行上述COPY和RUN命令
HEALTHCHECK容器健康检查
语法格式
Docker 支持两种基础语法,核心是通过命令退出码判断状态:
- 退出码
0:容器健康(healthy) - 退出码
1:容器不健康(unhealthy) - 退出码
2:保留,暂不使用
- 基础语法(带可选参数)
HEALTHCHECK [选项] CMD <检测命令>
可选参数(控制检测频率和超时):
--interval=<时长>:检测间隔(默认 30 秒,如--interval=10s)--timeout=<时长>:检测命令超时时间(默认 30 秒,如--timeout=5s)--retries=<次数>:检测失败后重试次数,连续失败则标记不健康(默认 3 次,如--retries=2)--start-period=<时长>:容器启动后 “初始化宽限期”,期间失败不计入重试(适用于启动慢的应用,默认 0 秒,如--start-period=60s)
- 禁用健康检查
若基础镜像已定义 HEALTHCHECK,可通过以下指令覆盖并禁用:
HEALTHCHECK NONE
工作机制
- 容器启动后,按
--start-period等待初始化(期间状态为starting)。 - 初始化完成后,按
--interval定期执行CMD后的检测命令。 - 若命令执行超时(超过
--timeout),视为一次检测失败。 - 连续失败次数达到
--retries,容器状态标记为unhealthy;若后续检测成功,状态恢复为healthy。
示例
- Web 服务(HTTP 响应检测)
检测 Nginx/Apache 能否正常返回 HTTP 200 响应:
# 每 10 秒检测一次,超时 5 秒,重试 2 次
HEALTHCHECK --interval=10s --timeout=5s --retries=2 \
CMD curl -f http://localhost/ || exit 1
curl -f:请求失败(如 4xx/5xx)时返回非 0 退出码。- 若应用需认证或指定路径,可修改为
curl -f http://localhost/api/health || exit 1。
- 数据库(MySQL 连接检测)
检测 MySQL 能否正常接收连接:
HEALTHCHECK --interval=15s --timeout=5s --retries=3 \
CMD mysql -h localhost -u root -p$MYSQL_ROOT_PASSWORD -e "SELECT 1" || exit 1
- 利用
mysql客户端执行简单查询SELECT 1,能执行则说明数据库可用。
- 自定义应用(端口监听检测)
检测应用是否在指定端口监听(适用于无 HTTP 接口的服务):
# 使用 netstat 或 ss 检测 8080 端口是否监听
HEALTHCHECK --interval=20s --timeout=3s \
CMD ss -tulpn | grep :8080 || exit 1
多阶段构建容器
AS
阶段命名与引用
核心作用
为构建阶段命名,方便后续通过COPY --from=<阶段名>引用该阶段的文件,是多阶段构建的基础参数。
语法格式
FROM <基础镜像> AS <阶段名>
示例
# 构建阶段命名为builder
FROM maven:3.8 AS builder
WORKDIR /app
RUN mvn package
# 运行阶段引用builder阶段
FROM openjdk:17-slim
COPY --from=builder /app/target/app.jar ./ # 引用命名阶段
COPY --from
跨阶段复制核心
核心作用
从指定阶段或外部镜像复制文件到当前阶段,是多阶段构建实现 “瘦身” 的关键参数。
语法格式
# 从命名阶段复制
COPY --from=<阶段名> <源路径> <目标路径>
# 从外部镜像复制
COPY --from=<镜像名[:标签]> <源路径> <目标路径>
# 进阶:指定平台(跨架构构建)
COPY --from=<源> --platform=<平台> <源路径> <目标路径>
# 进阶:设置文件所有者
COPY --from=<阶段名> --chown=<user>:<group> <源路径> <目标路径>
示例
# 从builder阶段复制产物,并设置所有者
COPY --from=builder --chown=appuser:appuser /app/target/app.jar /app/
# 从nginx镜像复制配置文件
COPY --from=nginx:latest /etc/nginx/nginx.conf /tmp/
指定构建目标
--target=<阶段名>(构建命令参数)
核心作用
在执行docker build时,指定只构建到某个阶段(而非完整构建所有阶段),常用于调试或复用中间阶段。
语法格式(构建命令)
docker build --target=<阶段名> -t <镜像名> .
示例
FROM node:18 AS frontend-build # 前端构建阶段
RUN npm run build
FROM golang:1.21 AS backend-build # 后端构建阶段
RUN go build -o app
FROM alpine:latest AS final # 最终阶段
# 仅构建前端阶段(用于调试前端构建)
docker build --target=frontend-build -t my-frontend:test .
ARG+阶段内使用
跨阶段传参
核心作用
通过ARG定义构建参数,可在多个阶段共享(需在每个阶段重新声明),实现跨阶段参数传递。
示例
# 全局声明参数(可选默认值)
ARG APP_VERSION=1.0
# 阶段1:使用参数
FROM maven:3.8 AS builder
ARG APP_VERSION # 需在阶段内重新声明才能使用
RUN mvn package -Dversion=${APP_VERSION}
# 阶段2:使用同一参数
FROM openjdk:17-slim
ARG APP_VERSION
LABEL version=${APP_VERSION} # 给最终镜像打版本标签
COPY --from=builder /app/target/app-${APP_VERSION}.jar ./app.jar
准备工作
配置脚本添加命令解释器
vim /etc/vimrc #kylin|rocky
vim /etc//vim/vimrc #ubuntu
--------------------------------
autocmd BufNewFile *.py,*.cc,*.sh,*.java,*.bash,Dockerfile,docker-compose.yml exec ":call SetTitle()"
func SetTitle()
if expand("%:e") =~ 'sh\|bash'
call setline(1,"#!/bin/bash")
call setline(2, "##############################################################")
call setline(3, "# File Name: ".expand("%"))
call setline(4, "# Version: V1.0")
call setline(5, "# Author: song")
call setline(6, "# Organization: ")
call setline(7, "# Description:")
call setline(8, "##############################################################")
call setline(9, "")
endif
if expand("%") == 'Dockerfile'
call setline(1, "#####################Dockerfile###############################")
call setline(2, "##############################################################")
call setline(3, "# File Name: ".expand("%"))
call setline(4, "# Version: V1.0")
call setline(5, "# Author: song")
call setline(6, "# Organization: ")
call setline(7, "# Description:")
call setline(8, "##############################################################")
call setline(9, "")
call setline(10, "FROM")
call setline(11, "LABEL maintaniner='123@qq.com' author=song")
call setline(12, "CMD []")
endif
if expand("%") == 'docker-compose.yml'
call setline(1, "#####################docker-compose###########################")
call setline(2, "##############################################################")
call setline(3, "# File Name: ".expand("%"))
call setline(4, "# Version: V1.0")
call setline(5, "# Author: song")
call setline(6, "# Organization:")
call setline(7, "# Description:")
call setline(8, "##############################################################")
call setline(9, "")
call setline(10, "version: '3.3'")
call setline(11, "services:")
call setline(12, "volumes:")
endif
endfunc
常用阻塞命令及场景
Docker 容器的持续运行依赖于前台运行的主进程,以下是 Dockerfile 中CMD或ENTRYPOINT里常用的阻塞命令:
- 运行应用程序的前台命令(推荐,生产环境)
这是最合理的阻塞方式,直接启动应用的主进程并保持前台运行,适用于实际业务场景。
# 运行Nginx前台服务(默认Nginx容器会配置前台运行)
CMD ["nginx", "-g", "daemon off;"]
# 运行Java应用(jar包前台运行)
CMD ["java", "-jar", "/app/app.jar"]
# 运行Node.js应用
CMD ["node", "/app/server.js"]
# 运行Python Web服务(如Flask)
CMD ["python", "/app/app.py"]
tail -f /dev/null(通用阻塞,测试 / 调试场景)
通过跟踪空设备文件(/dev/null)保持进程运行,适用于需要容器长期存活但无业务进程的场景(如调试、临时容器)。
通用阻塞,让容器一直运行
CMD ["tail", "-f", "/dev/null"]
- 无限循环 / 睡眠命令(灵活阻塞)
通过while循环 +sleep或无限睡眠实现阻塞,可自定义循环逻辑。
# 无限循环睡眠
CMD ["bash", "-c", "while true; do sleep 3600; done"]
# 或更简洁的无限睡眠(部分系统支持)
CMD ["sleep", "infinity"]
# 或指定长时间睡眠(如一年)
CMD ["sleep", "365d"]
dockerfile相关故障(容器无法启动排查流程)⭐⭐⭐
- dockerfile书写问题
- docker run 然后看日志(后台)logs日志
- docker run -itd 运行查看启动流程与提示/bin/bash,替换掉CMD指定的命令
docker run -itd --name ubt ubuntu:22.04 /bin/bash
docker run -d -name xxxx 镜像
# 1. 创建并启动容器(用tail -f /dev/null让容器长期运行)
docker run -itd --name node node:16-alpine tail -f /dev/null
# 2. 进入容器交互终端
docker exec -it node sh
# 直接启动容器并进入交互shell,退出后容器会停止(适合临时操作)
docker run -it --rm node:16-alpine sh
- docker exec -it 连接进入容器,执行检查类命令 nginx -t / php-fpm -t /…手动检查配置文件
- 手动运行CMD指定的命令,启动服务查看错误提示
CMD和ENTRYPOINT
| dockerfile2 个指令 | CMD | ENTRYPOINT |
|---|---|---|
| 共同点 | 用于设置容器入口命令容器启动后默认运行什么指令什么参数 | 用于设置容器入口命令容器启动后默认运行什么指令什么参数 |
| 共同点(格式) | CMD ["命令","参数1","参数2"] | ENTRYPOINT ["命令","参数1","参数2"] |
| 区别 (非同时时间) | 用户通过docker run/exec启动进入容器的时候,指定了命令.这个命令会替代 CMD 命令和参数. | 用户通过docker run/exec启动进入容器的时候,指定了命令.指定的命令,选项会成为 ENTRYPOINT 追加,命令的选项 |
| 一起区别 | CMD 命令后面的内容成为 ENTRYPOINT 的命令的选项 ENTRYPOINT+CMD | 如有传参,会把 CMD 内容替换. |
ENV和ARG
| 说明 | ARG | ENV |
|---|---|---|
| 共同点 | 创建变量 | 创建变量 |
| 区别 1 | 变量仅在docker build过程 / Dockerfile 内生效,是局部变量,可通过--build-arg传入 | 变量是容器的环境变量,在 Docker 容器运行时全局生效 |
| 区别 2 | 常用于记录版本号等构建阶段的变量 | 用于 Dockerfile 内指令、容器中运行的脚本等场景的变量 |
COPY和ADD
ADD 如果文件是tar包,自动解压并发送,支持url下载
COPY 是只传文件目录,–from 可以跨阶段复制
docker logs 查看的是容器的哪里?
核心结论:docker logs 查看的是容器的 标准输出(stdout)和标准错误(stderr) 日志,默认存储在 Docker 主机的 /var/lib/docker/containers/ 目录下,具体存储位置和方式由 日志驱动(log driver) 决定。
一、日志来源:容器的 stdout 和 stderr
容器运行时,应用程序打印到 “控制台” 的所有信息,都会被 Docker 捕获并记录,这就是 docker logs 能查看的核心内容:
- stdout(标准输出):应用的正常运行日志(如 Nginx 的访问日志、PHP 的业务日志)。
- stderr(标准错误):应用的错误日志(如数据库连接失败、代码语法错误)。
root@4fb625e11a8b:/# ls -l /dev/stdout
lrwxrwxrwx 1 root root 15 Nov 20 19:03 /dev/stdout -> /proc/self/fd/1
root@4fb625e11a8b:/# ls -l /dev/stderr
lrwxrwxrwx 1 root root 15 Nov 20 19:03 /dev/stderr -> /proc/self/fd/2
nginx -g ‘daemon off’; 容器为什么会阻塞?
命令含义解析
nginx -g 'daemon off;'是启动 Nginx 时的参数配置,我们可以拆解来看:
nginx:启动 Nginx 服务的主命令。-g:全称是--global-config,用于在启动时临时设置 Nginx 的全局配置指令,优先级高于配置文件(如nginx.conf)中的指令。daemon off;:这是 Nginx 的核心配置指令,意思是关闭 Nginx 的守护进程模式,让 Nginx 以前台进程的方式运行。
容器 “阻塞” 的原因
要理解这个现象,需要结合 Nginx 的默认运行模式和容器的运行机制:
- Nginx 的默认运行模式
默认情况下,Nginx 启动时会开启daemon on;(守护进程模式):
- Nginx 启动后会
fork出子进程来处理请求,父进程会立即退出,子进程在后台(守护进程)运行。 - 此时如果直接在终端执行
nginx,命令行很快会返回,Nginx 在后台默默工作。
- 容器的运行机制
Docker 容器的生命周期完全依赖于其启动时的主进程:
- 如果主进程退出,容器会认为任务完成,立即停止运行。
- 只有当主进程保持前台运行时,容器才会持续存活。
- 为什么
daemon off;会让容器 “阻塞”(保持运行)
当执行nginx -g 'daemon off;'时:
- Nginx 不会以守护进程模式运行,而是作为前台进程持续占据终端,不退出。
- 这个前台的 Nginx 进程就成了容器的主进程,只要 Nginx 不停止,容器就会一直运行(看起来像 “阻塞”,实际是保持存活)。
- 如果不设置
daemon off;,Nginx 父进程启动后立即退出,容器的主进程消失,容器会直接停止。
Dockerfile 构建流程
- 准备上下文:将 Dockerfile 和所需文件(如代码、配置)放在同一目录(构建上下文)。
- 执行构建命令:
docker build -t <镜像名:标签> <构建上下文路径>
# 示例:docker build -t myapp:1.0 .
小鸟飞飞镜像
root@docker01 /server/Dockerfile/bird]# ls
bird.tar.gz Dockerfile
cat Dockerfile
FROM nginx:1.24
LABEL maintaniner='song @oldboyedu.com' author=songhongfei
ADD bird.tar.gz /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx","-g","daemon off;"]
#g
[root@docker01 /server/Dockerfile/bird]# docker build -t web:bird .
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
Install the buildx component to build images with BuildKit:
https://docs.docker.com/go/buildx/
Sending build context to Docker daemon 94.21kB
Step 1/5 : FROM nginx:1.24
---> b6c621311b44
Step 2/5 : LABEL maintaniner='song @oldboyedu.com' author=songhongfei
---> Running in d89959adc364
---> Removed intermediate container d89959adc364
---> c127e1efcab9
Step 3/5 : ADD bird.tar.gz /usr/share/nginx/html/
---> 29f308908ea0
Step 4/5 : EXPOSE 80
---> Running in 91a253e87daf
---> Removed intermediate container 91a253e87daf
---> d121edda3468
Step 5/5 : CMD ["nginx","-g","daemon off;"]
---> Running in 4484c49f91dd
---> Removed intermediate container 4484c49f91dd
---> 022f00f4bd11
Successfully built 022f00f4bd11
Successfully tagged web:bird
[root@docker01 ~]# docker images web
REPOSITORY TAG IMAGE ID CREATED SIZE
web bird 022f00f4bd11 25 seconds ago 142MB
#构建镜像
docker run -d -p 80:80 --restart always --name bird web:bird
浏览器访问服务器ip

数据库自定义创建库,用户镜像
MySQL 服务在镜像构建阶段(RUN 指令执行时)并未启动,因此不能直接在 RUN 中执行 CREATE DATABASE/CREATE USER 命令,需通过 MySQL 官方镜像的初始化脚本机制实现。
- 查看数据库镜像的初始化目录在哪?
在官网上查看
- 访问 Docker Hub,搜索目标镜像(如
mysql、postgres); - 进入镜像详情页,查看 “Initialization scripts” 或 “Configuration” 章节。
#临时启动,进入容器查看
docker run -it --rm mysql:8.0-debian bash
root@84edb92ac5eb:/#
root@84edb92ac5eb:/#
root@84edb92ac5eb:/# ls
bin docker-entrypoint-initdb.d home media proc sbin tmp
boot entrypoint.sh lib mnt root srv usr
dev etc lib64 opt run sys var
root@84edb92ac5eb:/# ls docker-entrypoint-initdb.d/
目录结构
mysql_blog/
├── Dockerfile
└── init.sql
初始化脚本init.sql
create database blog;
create user 'blog'@'%' identified by '123';
grant all on blog.* to 'blog'@'%';
flush privileges;
Dockerfile脚本
FROM mysql:8.0-debian
LABEL maintaniner='song 13938454021@qq.com' author=hong
ENV MYSQL_ROOT_PASSWORD=123
COPY init.sql /docker-entrypoint-initdb.d/
CMD ["mysqld"]
构建与启动镜像
#创建自定义网络
docker network create app-network
#创建数据卷,存放数据库数据
docker volume create mysql-blog-data
#构建镜像
docker build -t mysql-blog:8.0 .
#启动镜像
docker run -d --name mysql_blog --network app-network -p 3306:3 -v mysql-blog-data:/var/lib/mysql mysql-blog:8.0
#检查是否成功
docker exec -it mysql_blog mysql -uroot -p123
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| blog |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> select user,host from mysql.user;
+------------------+------------+
| user | host |
+------------------+------------+
| root | % |
| blog | 172.16.1.% |
| blog | localhost |
| mysql.infoschema | localhost |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
+------------------+------------+
自定义镜像ngx+php,ubuntu22.04
先在容器中执行一遍,在写Dockerfile

6 亚洲 回车 70 上海
启动镜像
docker run
cat >/etc/apt/sources.list<<EOF
deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
# deb http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restricted universe multiverse
# deb-src http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
deb [trusted=yes] http://nginx.org/packages/ubuntu/ jammy nginx
deb-src [trusted=yes] http://nginx.org/packages/ubuntu/ jammy nginx
EOF
apt update && apt install -y nginx && apt install -y php8.1-bcmath php8.1-bz2 php8.1-cgi php8.1-cli php8.1-common php8.1-curl php8.1-fpm php8.1-gd php8.1-intl \
php8.1-mbstring php8.1-mysql php8.1-opcache php8.1-readline php8.1-soap \
php8.1-xml php8.1-zip php8.1-apcu php8.1-redis php8.1-snmp
目录结构
[root@docker01 /server/Dockerfile]# tree -L 1 ngx_php/
ngx_php/
├── blog.songhong.com.conf
├── Dockerfile
├── endir.sh
├── nginx.conf
├── sources.list
├── wordpress
└── wordpress-6.8.3-zh_CN.zip
- blog.songhong.com.conf
server{
listen 80;
server_name blog.songhong.com;
root /app/code/blog/;
error_log /var/log/nginx/blog-error.log notice;
access_log /var/log/nginx/blog-access.log main;
location / {
index index.php;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
- Dockerifle
FROM ubuntu:22.04
LABEL maintaniner='song 13938454021@qq.com' author=hong
ENV APT sources.list
ENV TZ=Asia/Shanghai
ENV blog_dir /app/code/blog/
ADD ${APT} /etc/apt/sources.list
RUN apt update -qq \
&& DEBIAN_FRONTEND=noninteractive \
&& apt install -y tzdata && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
echo $TZ > /etc/timezone && \
apt install -y -qq nginx && apt install -y php8.1-bcmath php8.1-bz2 php8.1-cgi php8.1-cli php8.1-common php8.1-curl php8.1-fpm php8.1-gd php8.1-intl \
php8.1-mbstring php8.1-mysql php8.1-opcache php8.1-readline php8.1-soap \
php8.1-xml php8.1-zip php8.1-apcu php8.1-redis php8.1-snmp && \
ln -sf /var/log/nginx/blog-error.log /dev/stderr && \
ln -sf /var/log/nginx/blog-access.log /dev/stdout && \
rm -f /etc/nginx/conf.d/default.conf && \
sed -i 's/^listen =.*/listen = 127.0.0.1:9000/g' /etc/php/8.1/fpm/pool.d/www.conf && \
mkdir -p ${blog_dir}
ADD nginx.conf /etc/nginx/nginx.conf
ADD blog.songhong.com.conf /etc/nginx/conf.d/
ADD endir.sh /
ADD wordpress /tmp/wordpress
RUN chown -R www-data:www-data ${blog_dir} && chmod +x /endir.sh && \
mv /tmp/wordpress/* ${blog_dir} && \
rm -rf /tmp/wordpress
EXPOSE 80 443
CMD ["/endir.sh"]
- endir.sh
#!/bin/bash
php-fpm8.1 -F &
nginx -g "daemon off;"
- nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
}
http {
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
gzip on;
include /etc/nginx/conf.d/*.conf;
}
- sources.list
deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
# deb http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restricted universe multiverse
# deb-src http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
deb [trusted=yes] http://nginx.org/packages/ubuntu/ jammy nginx
deb-src [trusted=yes] http://nginx.org/packages/ubuntu/ jammy nginx
- 构建镜像
docker build -t web:wp .
创建网络,数据卷
docker volume create mysql-blog-data
docker network create app-network
docker-compose.yaml
#version: 3.8
networks:
app-network:
driver: bridge
volumes:
mysql-blog-data:
driver: local
services:
mysql:
image: "mysql:8.0-debian"
container_name: app-mysql
networks:
- app-network
volumes:
- mysql-blog-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=123
- MYSQL_DATABASE=blog
- MYSQL_USER=blog
- MYSQL_PASSWORD=123
ports:
- 3306:3306
restart: always
nginx:
image: "web:wp"
container_name: app-ngx
networks:
- app-network
ports:
- 80:80
restart: always
启动
docker-compose up -d
windows: hosts文件解析
浏览器访问宿主机ip
数据库主机填docker-compose的数据库服务名即可

案例1: war包hallo容器tomcat
目录结构
通过网盘分享的文件:hello-world-war-src-code.zip
链接: https://pan.baidu.com/s/1IJsx2Q_Djk76zCnZZjCqWg 提取码: ybc5
–来自百度网盘超级会员v2的分享
[root@docker01 /server/Dockerfile]# tree 01-java-hallo/
01-java-hallo/
├── Dockerfile
└── hello-world-war-1.0.0.war
Dockerfile
FROM tomcat:9.0-jdk8
LABEL maintaniner='song 13938454021@qq.com' author=hong
ENV HALLO hello-world-war-1.0.0.war
ENV DIR /usr/local/tomcat/
WORKDIR ${DIR}
ADD ${HALLO} ./webapps/ROOT.war
EXPOSE 8080
CMD ["catalina.sh","run"]
HEALTHCHECK --interval=2s --timeout=20s --retries=2 \
CMD curl 127.0.0.1:8080
- 构建镜像与启动
#构建镜像
docker build -t hallo:tomcat .
#启动容器
docker run -d -p 8080:8080 --name hallo hallo:tomcat
浏览器访问ip 10.0.0.81:8080

案例2:制作jar包容器-ngxWebUI
目录结构
[root@docker01 /server/Dockerfile]# tree 3_nginxWebUi_java11/
3_nginxWebUi_java11/
├── Dockerfile
├── enriy.sh
├── nginxWebUI-4.3.4.jar
└── nginxWebUI-4.4.0.jar
Dockerfile
FROM openjdk:11-ea-slim
LABEL maintaniner='song 13938454021@qq.com' author=hong
ARG VER=4.3.4
ENV JAR nginxWebUI-${VER}.jar
ENV DIR /app/code/WebUI/
ADD ${JAR} /app/code/WebUI/
ADD enriy.sh /
EXPOSE 8080
CMD ["/enriy.sh"]
enriy.sh- 注意:需要有执行权限
#!/bin/bash
java -jar -Dfile.encoding=UTF-8 ${DIR}/${JAR} --server.port=8080
- 构建与启动
#构建镜像
docker build -t jdk11:WebUI .
#启动容器
docker run -d -p 8080:8080 --name ngxWeb jdk11:WebUI
浏览器访问 ip+端口

案例3:ARG与END搭配案例
[root@docker01 /server/Dockerfile/3_nginxWebUi_java11]# cat Dockerfile
FROM openjdk:11-ea-slim
LABEL maintaniner='song 13938454021@qq.com' author=hong
ARG VER=4.3.4
ENV JAR nginxWebUI-${VER}.jar
ENV DIR /app/code/WebUI/
ADD ${JAR} /app/code/WebUI/
ADD enriy.sh /
EXPOSE 8080
CMD ["/enriy.sh"]
docker build --build-arg VER=4.4.0 -t web:ngxui_440 .
案例4:编译前端代码china与自定义镜像
Welcome to Apache Maven – Maven
环境准备
[root@docker01 ~]# java --version
openjdk 11.0.28 2025-07-15
OpenJDK Runtime Environment BiSheng (build 11.0.28+6)
OpenJDK 64-Bit Server VM BiSheng (build 11.0.28+6, mixed mode, sharing)
[root@docker01 ~]# node -v
v16.19.1
[root@docker01 ~]# npm -v
8.19.3
[root@docker01 ~]# mvn -v
Apache Maven 3.9.11 (3e54c93a704957b63ee3494413a2b544fd3d825b)
通过网盘分享的文件:china-ex-main.zip
链接: https://pan.baidu.com/s/1RwI6UfWcH80PXKQ-EvIIow 提取码: qw5h
–来自百度网盘超级会员v2的分享
上传软件包china-ex-main.zip
[root@docker01 ~]# ls china-ex-main.zip
china-ex-main.zip
unzip china-ex-main.zip
mkdir -p /server/Dockerfile/4_china_npm/
mv china-ex-main/* /server/Dockerfile/4_china_npm/
cd /server/Dockerfile/4_china_npm/
npm i
npm run build
- Dockerfile
FROM nginx:latest
LABEL maintaniner='song 13938454021@qq.com' author=hong
WORKDIR /usr/share/nginx/html/
ADD dist/* ./
EXPOSE 80
CMD ["nginx","-g","daemon off;"]
- 构建与启动
#构建镜像
docker build -t china:exam .
#启动容器
docker run -d -p 80:80 --name china china:exam
浏览器访问ip

多阶段构建
案例5-前端代码china与自定义镜像-多阶段构建
#拉取镜像
docker pull node:16-alpine
目录结构
cp -r china-ex-main/* /server/Dockerfile/5_ngx_node/
[root@docker01 /server/Dockerfile]# tree 5_ngx_node/
5_ngx_node/
├── 生成字体子集.js
├── 转译.js
├── china-ex.svg
├── cover.png
├── Dockerfile
├── html
│ ├── 脚本.js
│ ├── 样式.less
│ └── index.html
├── package.json
└── README.md
Dockerfile
FROM node:16-alpine AS build
ENV CODE /app/code/china/
RUN mkdir -p ${CODE}
WORKDIR ${CODE}
COPY ./ ./
RUN npm config set registry https://registry.npmmirror.com && \
npm i && \
npm run build
FROM nginx:latest
LABEL author=honga desc="前端 china"
COPY --from=build /app/code/china/dist/* /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx","-g","daemon off;"]
- 构建与启动
docker build -t web:china .
docker run -d -p 80:80 --name china web:china
注意这个是提示更新,不影响使用

浏览器访问

案例6-多阶段构建nginxWebUI-jar包
nginxWebUI: 方便快捷易用的Nginx可视化UI网页管理部署配置工具系统.
上传软件包nginxWebUI-master.zip
[root@docker01 ~]# ls nginxWebUI-master.zip
nginxWebUI-master.zip
unzip nginxWebUI-master.zip
mv nginxWebUI-master/* /server/Dockerfile/6_mav_WebUI/
cd /server/Dockerfile/6_mav_WebUI/
mv Dockerfile Dockerfile.bak
目录结构
root@docker01 ~]# tree -L 1 /server/Dockerfile/6_mav_WebUI/
/server/Dockerfile/6_mav_WebUI/
├── buildx.sh
├── Dockerfile
├── Dockerfile.bak
├── enrsy.sh
├── entrypoint.sh
├── LICENSE
├── local_build.sh
├── pom.xml
├── README
├── README_EN.md
├── README.md
└── src
需要镜像
[root@docker01 ~]# docker images | egrep 'maven|openjdk'
maven 3.9 0f45bb102b13 8 days ago 479MB
openjdk 11-ea-slim f097550f7cc7 7 years ago 502MB
Dockerfile
FROM maven:3.9 AS WebUI
ENV DIR /app/code/ngxWebUI/
ADD ./ ${DIR}
WORKDIR ${DIR}
RUN mvn clean package
FROM openjdk:11-ea-slim AS post
LABEL maintaniner='song 13938454021@qq.com' author=hong
ENV JAR nginxWebUI-4.3.4.jar
ENV DIR /app/code/ngxWebUI/
COPY --from=WebUI /app/code/ngxWebUI/target/* ${DIR}
ADD enrsy.sh /
RUN chmod +x /enrsy.sh
EXPOSE 80
CMD ["/enrsy.sh"]
enrsy.sh
#!/bin/bash
java -jar -Dfile.encoding=UTF-8 ${DIR}${JAR} --server.port=80 --project.home=${DIR}
- 构建与启动
docker build -t mvn:ngxWEB .
docker run -d --name ngx -p 80:80 mvn:ngxWEB
案例7-多阶段构建hello-word-war包
目录结构
unzip hello-world-war-src-code.zip -d /server/Dockerfile/7_java_src/
[root@docker01 /server/Dockerfile]# tree -L 1 7_java_src/
7_java_src/
├── dist
├── Dockerfile
├── pom.xml
├── README.md
├── settings.xml
├── src
└── target
Dockerfile
FROM maven:3-eclipse-temurin-11-alpine AS build
ENV CODE /app/code/
WORKDIR ${CODE}
ADD settings.xml /usr/share/maven/conf/settings.xml
ADD ./ ${CODE}
RUN mvn clean package
FROM tomcat:9.0-jdk8
LABEL maintaniner='song 13938454021@qq.com' author=hong
COPY --from=build /app/code/target/hello-world-war-1.0.0.war /usr/local/tomcat/webapps/ROOT.war
EXPOSE 8080
CMD ["catalina.sh","run"]
- 需要的镜像
maven:3-eclipse-temurin-11-alpine
tomcat:9.0-jdk8
- 需要的软件包
hello-world-war-src-code.zip
- 构建与启动
docker build -t web:maven_hell .
docker run -d --name hello -p 8080:8080 web:maven_hell
浏览器访问服务器ip:8080

更多推荐

所有评论(0)