实际项目中的Dockerfile例子
示例1:Python Flask Web应用
# 1. 基础镜像
FROM python:3.9-slim
# 2. 设置环境变量
ENV PYTHONUNBUFFERED=1 \
FLASK_APP=app.py \
FLASK_ENV=production \
PORT=5000
# 3. 设置工作目录
WORKDIR /app
# 4. 安装系统依赖(需要root权限)
RUN apt-get update && \
apt-get install -y --no-insa=tall-recommends \
gcc \
python3-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 5. 复制依赖文件
COPY requirements.txt .
# 6. 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt
UN useradd -m -u 1000 -s /bin/bash appuser
# 8. 复制应用代码
COPY --chown=appuser:appuser . .
# 9. 创建数据卷
VOLUME ["/app/logs", "/app/uploads"]
# 10. 切换到非root用户
USER appuser
# 11. 声明暴露端口
EXPOSE 5000
# 12. 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
# 13. 启动命令
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
- 1. FLASK 是一个轻量级的python web框架(只能用作python),提供了构建网站和API所需的基本工具,可以不用从零开始写所有代码
- 2. 环境变量
1)PYTHONUNBUFFERED=1 禁用python输出缓冲,设置日志实时输出,无延时
2)FLASK_APP=该应用的启动文件,从这个文件开始启动
- 4. 安装系统依赖
1)操作符:
cmd1 && cmd2: 前一个成功才执行后一个,依赖顺序的命令
`cmd1 [换行] cmd2`: 前一个失败才执行后一个,备用方案
cmd1;cmd2: 无论成败都执行后一个,独立命令
cmd1 & cmd2: 在后台执行,并行任务
2)install -y --no-install-recommends:提示默认选择yes;
3)大部分python包都需要C扩展,所以要下载gcc
4)python3-dev:用来提供python C API头文件
5)/var/lib/apt/lists/*:镜像包含软件包列表缓存的地方,rm可以节省空间
- 6. --no-cache-dir -r requirements.txt:下载安装后立即删除缓存;从requirement.txt安装依赖
- 7. -m -u 1000 -s /bin/bash appuser:创建用户的家目录;规定UID为1000;设置登录shell为bash(而不是默认的sh),提供更丰富的命令行功能;appuser为用户名
- 8. --chown=appuser:appuser:将复制过来的文件都设置为该用户/用户组
- 12. 容器启动---等待5s(start-period)---健康检查:curl http://localhost:5000/health
1)timeout:若是3s没有相应,就失败一次
2)interval:失败后,先等待30s,再去尝试
3)retries:最多只能失败3次,就代表不健康
4)curl -f || exit 1:失败时返回非零状态码;当返回非零状态码就exit 1
执行 curl -f http://localhost:5000/health ↓ 如果成功(返回 0)→ 健康检查通过 ✅ ↓ 如果失败(返回非 0)→ 执行 exit 1 → 健康检查失败 ❌
示例2:Node.js React 前端应用
docker build -t my-app
# 1. 多阶段构建:构建阶段
FROM node:16-alpine AS builder
# 2. 设置环境变量
ENV NODE_ENV=production
# 3. 设置工作目录
WORKDIR /app
# 4. 复制依赖文件
COPY package*.json ./
# 5. 安装依赖
RUN npm ci --only=production
# 6. 复制源代码
COPY . .
# 7. 构建应用
RUN npm run build
# 8. 多阶段构建:运行阶段
FROM nginx:alpine
# 9. 复制构建产物
COPY --from=builder /app/build /usr/share/nginx/html
# 10. 复制自定义nginx配置
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 11. 创建非root用户(nginx镜像已创建nginx用户)
USER nginx
# 12. 声明暴露端口
EXPOSE 80
# 13. 启动命令(使用nginx默认CMD)
- 0. Node.js+React前端应用=用JavaScript同时搞定前端页面和后端服务;
1)React 向 Node.js发送HTTP请求/响应;Node.js再去调用数据库信息,传递给React,React再返回到页面中
2)而HTML和CSS穿插在React中,React作为逻辑语言,设计页面的动态变化(如果怎样怎样,就使用这段HTML/CSS语句)
- 1. 多阶段构建:
1)构建builder阶段:
-- Docker会基于
FROM node:16-alpine AS builder创建一个完整的中间镜像-- 这个镜像包含所有构建工具和依赖,用于生成构建产物
-- 它在构建缓存中存在,但没有用户友好的标签
2)构建最终阶段:
-- 从
FROM nginx:alpine开始一个全新的、独立的镜像构建-- 使用
COPY --from=builder从builder阶段的中间镜像复制构建产物-- 这个阶段只包含运行环境(如Nginx)和构建产物,不包含构建工具
3)构建完成后:
-- 最终镜像被标记为
my-app:latest并推送到镜像仓库(如需要)-- builder阶段的中间镜像变成"悬空镜像"(dangling image)【如果中间镜像没有被其他镜像引用,就会变成悬空镜像】
-- 可以使用
docker image prune清理这些悬空镜像来释放磁盘空间-- 最终结果是:一个小巧、安全、只包含运行产物的生产级镜像
#只构建builder阶段,不构建最终阶段 docker build -t my-app:builder --target builder . # 1.用来调试 npm run build失败原因; # 2. 验证依赖安装、代码编译是否正确,但不关心最终的部署; # 3. 保留构建环境,避免builder镜像变成悬空镜像,方便后续使用 # 完整构建整个Dockerfile,生成最终的生产镜像,builder成为悬空镜像 docker build -t my-app:latest
- 4. 依赖文件的安装
1)npm install:需要package.json,该文件里面说明了需要安装的依赖,但没有说明具体版本,会根据文件 安装指定范围内的最新版本,并更新lock文件,速度较慢【日常开发,需要添加、更新或移除依赖】
2)npm ci:需要package.json和package-lock.josn,lock文件指定了需要安装的依赖及其版本,使用npm ci会根据文件直接安装,不更新lock文件,速度较快【在持续集成流水线、部署生产环境或与团队成员确保完全一致的依赖树时使用】
- 7. RUN npm run build:根据配置文件,从入口文件开始,把整个项目的代码文件收集打包,并构建好,最终生成优化后的成品文件到配置文件工作目录中,文件夹名称由配置文件中的参数决定【在react中,默认输出文件夹名称为build】
- 8. 没有任何命名,所以这个是最终镜像
- 9. --from=builder /app/build:从同dockerfile中的阶段命名为builder的镜像中复制 /app/build文件(这样就实现了镜像中只有需要的产物,而没有多余的依赖工具)
示例3:Java Spring Boot应用
# 1. 多阶段构建:构建阶段
FROM maven:3.8.4-openjdk-11 AS builder
# 2. 设置工作目录
WORKDIR /build
# 3. 复制POM文件(利用Docker缓存层)
COPY pom.xml .
# 4. 下载依赖(单独步骤利用缓存)
RUN mvn dependency:go-offline
# 5. 复制源代码
COPY src ./src
# 6. 构建应用
RUN mvn package -DskipTests
# 7. 多阶段构建:运行阶段
FROM openjdk:11-jre-slim
# 8. 设置环境变量
ENV JAVA_OPTS="-Xmx512m -Xms256m" \
SPRING_PROFILES_ACTIVE=production
# 9. 创建应用用户
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
# 10. 设置工作目录
WORKDIR /app
# 11. 从构建阶段复制JAR文件
COPY --from=builder /build/target/*.jar app.jar
# 12. 创建数据卷
VOLUME ["/app/logs", "/app/config"]
# 13. 修改文件权限
RUN chown -R appuser:appgroup /app
# 14. 切换到非root用户
USER appuser
# 15. 声明暴露端口
EXPOSE 8080
# 16. 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
# 17. 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
- 0. Spring Boot = Java应用的"一键启动"工具包,让复杂的企业开发变得简单高效。
- 4. mvn
1)mvn dependency:go-offline 根据pom.xml文件下载依赖
2)mvn package -DskipTests 打包构建整个代码文件
方面 mvn(Java/Maven)npm run build(JavaScript/Node.js)技术栈 Java项目 JavaScript/Node.js项目 配置文件 pom.xmlpackage.json构建工具 Apache Maven Webpack/Vite/Rollup等 输出结果 JAR/WAR文件 静态HTML/JS/CSS文件
- 5. 如果源代码有修改,就会从这里开始重新运行,进行更新,所以34要先运行,这样就不用重新下载依赖了
- 8. Xmx:初始分配的内存大小;Xms:可增长到最大的内存;Xss:每个线程栈的内存
- 9. 创建用户
1)groupadd -r appgroup:创建一个名叫appgroup的系统组
2)useradd -r -g appgroup appuser:创建一个名叫appuser的系统用户 -g指定用户组为appgroup
3)系统与登录 用户/组的区别
方面 系统级用户 登录账户 用途 运行服务/应用 真人交互操作 登录 ❌ 不能登录系统 ✅ 可以登录系统 家目录 通常没有或为空 ✅ 有完整的家目录 Shell 通常为 /bin/false/bin/bash等完整shellUID范围 1-999 (系统范围) 1000+ (用户范围)
- ENTRYPOINT:
容器启动时: 1. sh -c 启动一个shell进程 2. shell看到 $JAVA_OPTS → 查找环境变量 3. 找到 JAVA_OPTS="..."参数 4. 执行该命令:java [JAVA_OPTS参数] -jar app.jar 【以JAVA_OPTS参数来运行名为app.jar的可执行JAR包】
- docker build -t my-image:创建镜像,将[参数]记录到ENTRYPOINT中
- docker run my-image:运行镜像(也就是创建容器),运行参数
示例4:PostgreSQL 数据库
# 1. 使用官方PostgreSQL镜像
FROM postgres:13
# 2. 设置环境变量
ENV POSTGRES_DB=myapp \
POSTGRES_USER=appuser \
POSTGRES_PASSWORD=changeme \
PGDATA=/var/lib/postgresql/data/pgdata
# 3. 复制初始化脚本
COPY init-scripts/ /docker-entrypoint-initdb.d/
# 4. 复制自定义配置
COPY postgresql.conf $PGDATA/postgresql.conf
# 5. 修改配置文件的权限
RUN chown postgres:postgres /etc/postgresql/postgresql.conf
# 6. 数据卷(PostgreSQL镜像已声明,这里显式说明)
VOLUME ["/var/lib/postgresql/data"]
# 7. 声明暴露端口
EXPOSE 5432
# 8. 使用postgres用户(镜像已创建)
USER postgres
# 9. 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD pg_isready -U $POSTGRES_USER -d $POSTGRES_DB || exit 1
# 10. 使用基础镜像的CMD
- 0. PostgreSQL:PostgreSQL 是一个功能强大的开源关系型数据库管理系统,用于安全可靠地存储和管理结构化数据。PostgreSQL ≈ MySQL的"增强专业版"
- 2. PGDATA:设置SQL所有存储的数据的地方,统一位置,方便管理
- 3. 在官方PostgreSQL中
1)init-scripts目录结构:
init-scripts/ ├── 01-create-database.sql # 创建数据库 ├── 02-create-tables.sql # 创建表结构 ├── 03-insert-data.sql # 插入初始数据 └── 04-setup-users.sh # 配置用户权限2)/docker-entrypoint-initdb.d/:在该容器首次加载或者数据为空(主要看$PGDATA中是否为空)时,就会自动执行/docker-entrypoint-initdb.d/中的4个代码文件
- 4. $PGDATA/postgresql.conf:在加载的时候,默认加载这个文件中的配置
- 6. VOLUME:这里也是备份$PGDATA这个重要数据目录
- 10.运行命令:如果需要自定义启动命令/参数就需要用CMD(如何启动);覆盖环境变量就是用ENV(在什么环境下启动)
示例5:Redis 缓存服务
# 1. 使用官方Redis镜像
FROM redis:6.2-alpine
# 2. 复制自定义配置
COPY redis.conf /usr/local/etc/redis/redis.conf
# 3. 创建数据目录
RUN mkdir -p /data && chown redis:redis /data
# 4. 数据卷
VOLUME ["/data"]
# 5. 声明暴露端口
EXPOSE 6379
# 6. 使用redis用户(镜像已创建)
USER redis
# 7. 启动命令(使用自定义配置)
CMD ["redis-server", "/usr/local/etc/redis/redis.conf"]
- 0. Redis缓存服务:Redis 是一个基于内存的键值数据库,通过将常用数据存储在内存中来大幅提升访问速度,用作应用程序的缓存层。
- 2. Redis配置文件路径:/usr/local/etc/redis/redis.conf
1)配置文件:应用的完整操作手册-定义应用如何运行的所有规则和行为
2)环境变量:运行的上下文参数-提供运行时的配置参数和条件
简单来说,配置文件是“操作规程”;环境变量是“工作条件”
- 7. CMD:
1)redis-server:启动Redis服务器程序
2)"/usr/local/etc/redis/redis.conf":规定配置文件
3)固定搭配:redis-server 指定配置文件 = 以指定配置文件启动Redis服务器程序
示例6:Go 微服务
# 1. 多阶段构建:构建阶段
FROM golang:1.17-alpine AS builder
# 2. 安装构建依赖
RUN apk add --no-cache git ca-certificates
# 3. 设置工作目录
WORKDIR /src
# 4. 复制Go模块文件
COPY go.mod go.sum ./
# 5. 下载依赖
RUN go mod download
# 6. 复制源代码
COPY . .
# 7. 构建应用(静态链接,减少依赖)
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /app .
# 8. 多阶段构建:运行阶段
FROM scratch
# 9. 复制CA证书(用于HTTPS请求)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# 10. 复制构建的可执行文件
COPY --from=builder /app /app
# 11. 设置环境变量
ENV PORT=8080
# 12. 声明暴露端口
EXPOSE 8080
# 13. 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD ["/app", "healthcheck"]
# 14. 启动命令
CMD ["/app"]
- 0. Go 微服务是用 Go 语言编写的、专注于单一业务功能的小型独立服务,通过 API 相互通信来共同构建完整应用。
- 2. 安装依赖:
1)apk = Alpine Linux的包管理器(类似Ubuntu的apt)【Alpine Linux是一个专为容器和嵌入式系统设计的超轻量Linux发行版】
2)add --no-cache = 安装软件包但不保留缓存(减小镜像体积)
3)git = 版本控制工具(用于克隆代码仓库)
4)ca-certificates = SSL证书包(用于HTTPS安全连接)
- 4. 模块文件
1)go.mod:定义了项目需要哪些依赖包、依赖的版本要求、项目的基本信息
2)go.sum:每个依赖包的哈希值
- 5. go mod download:根据
go.mod和go.sum文件下载所有项目依赖包到本地缓存。- 6. 这里也是先下载好了依赖,在复制的代码文件,这样的话,要是源代码变化,就可以不用重新下载依赖了
- 7. 构建应用
1)CGO_ENABLED=0:禁用CGO(纯go代码,不链接C库)
2)GOOS=linux:编译为Linux可执行文件
3)go build -a:-a 强制重新编译所有依赖
go mod download只是下载了这些依赖,而go build是利用这些依赖去生成各种功能。可能在该虚拟机中之前使用过这些依赖,就会有缓存,系统就会直接用这些依赖,有可能使用的时候该依赖的版本和现在构造的系统不兼容,所以需要重新编译所有依赖包。
4)-installsuffix cgo:使用独立的编译缓存(这里的编译缓存就是利用依赖生成的功能的缓存)使用独立编译缓存:每次构建都在该镜像的build阶段创建单独的缓存目录,运行阶段只复制产物。这里是linux_amd64_cgo/;不使用独立编译缓存:缓存可能持续影响后续构建,默认使用虚拟机中可能存在的旧缓存。
5)-o /app:输出文件名为/app
6).:编译当前目录的代码
将当前目录的代码编译为linux可执行文件,在编译期间,不能使用C库,只能使用go语言的函数库,生成到/app中
- 9. CA证书:就是第2步下载的ca-certificates
更多推荐

所有评论(0)