Docker 端口映射

面试题: docker run -p 背后发生了什么?

image-20251120214851751

docker run -p <宿主机端口>:<容器端口> 的本质是打通宿主机与容器之间的网络通信,让外部(或宿主机)能通过宿主机的端口访问容器内的服务(如容器内 Nginx 的 80 端口映射到宿主机的 8080 端口)。

二、-p 背后的完整执行流程

当你执行 docker run -p 80:80 nginx 时,Docker 会依次完成以下操作:

  1. 创建容器的网络命名空间(Network Namespace)
  • Docker 为每个容器创建独立的网络命名空间(隔离网络环境:独立的网卡、IP、路由表),容器默认获得一个虚拟网卡 eth0,并分配私有 IP(如 172.17.0.2,属于 Docker 默认网桥 docker0 的子网)。
  • 宿主机通过 docker0 网桥与容器的 eth0 连通(docker0 是宿主机上的虚拟网桥,默认 IP 为 172.17.0.1)。
  1. 宿主机端口绑定(Port Binding)
  • Docker 在宿主机上

    绑定指定的端口

    (如 80 端口),监听来自外部的连接请求。

    • 若指定 0.0.0.0:<宿主机端口>(默认,如 -p 80:80),宿主机所有网卡都会监听该端口;
    • 若指定 127.0.0.1:80:80,则仅宿主机本地能访问该端口。
  1. 配置 iptables DNAT 转发规则(核心!)

Docker 会通过iptables 的 DNAT(目标地址转换)规则,将宿主机端口的请求转发到容器的私有 IP 和端口。

iptabes -nL
  1. 数据包流转过程(以访问宿主机 80 端口为例)

  2. 外部客户端发送请求到宿主机的 80 端口;

  3. 宿主机的 iptables 规则匹配到请求,将目标地址改为容器的私有 IP 和端口;

  4. 数据包通过 docker0 网桥发送到容器的 eth0 网卡;

  5. 容器内的服务(如 Nginx)监听 80 端口,接收并处理请求;

  6. 响应数据包按原路返回(iptables 自动做反向 SNAT 转换),最终回到客户端。

参数格式含义底层差异
-p 80:80宿主机所有网卡的 80 映射到容器 80iptables 规则匹配 anywhere:80,绑定宿主机 0.0.0.0:80
-p 127.0.0.1:80:80仅宿主机本地 80 映射到容器 80iptables 规则匹配 127.0.0.1:80,绑定宿主机 127.0.0.1:80
-p 80宿主机随机端口映射到容器 80Docker 随机分配宿主机端口(32768+),iptables 规则匹配该随机端口
-p 80-82:80-82端口范围映射为每个端口单独创建 iptables 规则和绑定

Dockerfile容器化(核心)⭐⭐⭐

Dockerfile 是一个纯文本格式的配置文件,包含了一系列构建 Docker 镜像的指令和参数。Docker 引擎可以读取 Dockerfile 中的指令,自动执行镜像构建过程,最终生成可重复、可移植的 Docker 镜像。它是实现 “一次构建,处处运行” 的核心载体,定义了镜像的分层结构和运行时配置。

工作原理(How)

Dockerfile 的构建过程遵循分层构建和缓存机制:

  1. 分层构建:Dockerfile 中的每一条指令都会生成一个独立的镜像层(Layer),层是只读的且可复用。上层基于下层修改,最终所有层叠加形成完整镜像。
  2. 缓存机制:Docker 会缓存每一步构建的结果,当指令内容或上下文文件未变化时,直接复用缓存层,大幅提升构建效率。
  3. 构建上下文:执行 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:保留,暂不使用
  1. 基础语法(带可选参数)
HEALTHCHECK [选项] CMD <检测命令>

可选参数(控制检测频率和超时):

  • --interval=<时长>:检测间隔(默认 30 秒,如 --interval=10s)
  • --timeout=<时长>:检测命令超时时间(默认 30 秒,如 --timeout=5s)
  • --retries=<次数>:检测失败后重试次数,连续失败则标记不健康(默认 3 次,如 --retries=2)
  • --start-period=<时长>:容器启动后 “初始化宽限期”,期间失败不计入重试(适用于启动慢的应用,默认 0 秒,如 --start-period=60s)
  1. 禁用健康检查

若基础镜像已定义 HEALTHCHECK,可通过以下指令覆盖并禁用:

HEALTHCHECK NONE

工作机制

  1. 容器启动后,按 --start-period 等待初始化(期间状态为 starting)。
  2. 初始化完成后,按 --interval 定期执行 CMD 后的检测命令。
  3. 若命令执行超时(超过 --timeout),视为一次检测失败。
  4. 连续失败次数达到 --retries,容器状态标记为 unhealthy;若后续检测成功,状态恢复为 healthy。

示例

  1. 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。
  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,能执行则说明数据库可用。
  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里常用的阻塞命令:

  1. 运行应用程序的前台命令(推荐,生产环境)

这是最合理的阻塞方式,直接启动应用的主进程并保持前台运行,适用于实际业务场景。

# 运行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"]
  1. tail -f /dev/null(通用阻塞,测试 / 调试场景)

通过跟踪空设备文件(/dev/null)保持进程运行,适用于需要容器长期存活但无业务进程的场景(如调试、临时容器)。

 通用阻塞,让容器一直运行
CMD ["tail", "-f", "/dev/null"]
  1. 无限循环 / 睡眠命令(灵活阻塞)

通过while循环 +sleep或无限睡眠实现阻塞,可自定义循环逻辑。

# 无限循环睡眠
CMD ["bash", "-c", "while true; do sleep 3600; done"]

# 或更简洁的无限睡眠(部分系统支持)
CMD ["sleep", "infinity"]
# 或指定长时间睡眠(如一年)
CMD ["sleep", "365d"]

dockerfile相关故障(容器无法启动排查流程)⭐⭐⭐

  1. dockerfile书写问题
  2. docker run 然后看日志(后台)logs日志
  3. 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
  1. docker exec -it 连接进入容器,执行检查类命令 nginx -t / php-fpm -t /…手动检查配置文件
  2. 手动运行CMD指定的命令,启动服务查看错误提示

CMD和ENTRYPOINT

dockerfile2 个指令CMDENTRYPOINT
共同点用于设置容器入口命令容器启动后默认运行什么指令什么参数用于设置容器入口命令容器启动后默认运行什么指令什么参数
共同点(格式)CMD ["命令","参数1","参数2"]ENTRYPOINT ["命令","参数1","参数2"]
区别 (非同时时间)用户通过docker run/exec启动进入容器的时候,指定了命令.这个命令会替代 CMD 命令和参数.用户通过docker run/exec启动进入容器的时候,指定了命令.指定的命令,选项会成为 ENTRYPOINT 追加,命令的选项
一起区别CMD 命令后面的内容成为 ENTRYPOINT 的命令的选项 ENTRYPOINT+CMD如有传参,会把 CMD 内容替换.

ENV和ARG

说明ARGENV
共同点创建变量创建变量
区别 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 的默认运行模式和容器的运行机制:

  1. Nginx 的默认运行模式

默认情况下,Nginx 启动时会开启daemon on;(守护进程模式):

  • Nginx 启动后会fork出子进程来处理请求,父进程会立即退出,子进程在后台(守护进程)运行。
  • 此时如果直接在终端执行nginx,命令行很快会返回,Nginx 在后台默默工作。
  1. 容器的运行机制

Docker 容器的生命周期完全依赖于其启动时的主进程:

  • 如果主进程退出,容器会认为任务完成,立即停止运行。
  • 只有当主进程保持前台运行时,容器才会持续存活。
  1. 为什么daemon off;会让容器 “阻塞”(保持运行)

当执行nginx -g 'daemon off;'时:

  • Nginx 不会以守护进程模式运行,而是作为前台进程持续占据终端,不退出。
  • 这个前台的 Nginx 进程就成了容器的主进程,只要 Nginx 不停止,容器就会一直运行(看起来像 “阻塞”,实际是保持存活)。
  • 如果不设置daemon off;,Nginx 父进程启动后立即退出,容器的主进程消失,容器会直接停止。

Dockerfile 构建流程

  1. 准备上下文:将 Dockerfile 和所需文件(如代码、配置)放在同一目录(构建上下文)。
  2. 执行构建命令:
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

image-20251120154207352

数据库自定义创建库,用户镜像

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

image-20251120174642989

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的数据库服务名即可

image-20251120222115114

案例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

image-20251121174755063

案例2:制作jar包容器-ngxWebUI

nginxWebUI - 首页

目录结构

[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+端口

image-20251121175637234

案例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

Node.js — Download Node.js®

环境准备

[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

image-20251121195205554

多阶段构建

案例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

注意这个是提示更新,不影响使用

image-20251121202641566

浏览器访问

image-20251121203954573

案例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

image-20251124100252144

更多推荐