深入浅出Dockerfile编写指南

深入浅出Dockerfile编写指南

  • 引言
  • [什么是 Dockerfile](#什么是 Dockerfile)
  • [Dockerfile 的基本语法](#Dockerfile 的基本语法)
  • [Dockerfile 示例](#Dockerfile 示例)
  • 多阶段构建优化镜像
  • [常见的 Dockerfile 最佳实践](#常见的 Dockerfile 最佳实践)
    • [1. 减少镜像层数](#1. 减少镜像层数)
    • [2. 使用轻量基础镜像](#2. 使用轻量基础镜像)
    • [3. 使用 `.dockerignore`](#3. 使用 .dockerignore)
    • [4. 使用多阶段构建](#4. 使用多阶段构建)
    • [5. 最小化容器中的权限](#5. 最小化容器中的权限)
  • 结论

引言

Docker 是一种非常流行的容器化技术,它使得应用的开发、测试和部署更加轻松。Dockerfile 是 Docker 项目中定义镜像的核心文件,通过它我们可以构建自定义的 Docker 镜像。本篇博文将带领你从基础开始,逐步深入理解和编写 Dockerfile。


什么是 Dockerfile

Dockerfile 是一个包含指令的文本文件,这些指令会告诉 Docker 如何构建镜像。每个指令对应一个镜像的构建步骤,Docker 将按顺序执行这些步骤来构建最终的镜像。镜像是容器的静态快照,容器则是镜像的动态实例。


Dockerfile 的基本语法

Dockerfile 中的指令是分层的,且每条指令都会生成一层新的镜像。在介绍 Dockerfile 的高级功能之前,先了解最基础的指令。

以下是 Dockerfile 基本语法的表格:

指令 用途 示例
FROM 指定基础镜像,Dockerfile 必须以此开始 FROM ubuntu:20.04
RUN 在构建镜像时运行指定命令,常用于安装依赖、配置系统 RUN apt-get update && apt-get install -y python3
CMD 指定容器启动时默认运行的命令,可被 docker run 命令覆盖 CMD ["python", "app.py"]
ENTRYPOINT 指定容器启动时固定执行的命令,通常与 CMD 结合使用 ENTRYPOINT ["python"]
COPY 从宿主机复制文件或目录到镜像 COPY . /app
ADD 类似于 COPY,但支持从远程 URL 下载文件 ADD http://example.com/file.tar.gz /app/
WORKDIR 设置工作目录,后续的命令将在该目录下执行 WORKDIR /usr/src/app
ENV 设置环境变量 ENV APP_ENV=production
EXPOSE 声明容器运行时暴露的端口,但不会自动开启端口,只是作为文档声明 EXPOSE 8080
USER 指定运行容器时使用的用户,避免以 root 身份运行以提高安全性 USER appuser
VOLUME 定义挂载点,方便数据持久化 VOLUME ["/data"]
ARG 定义构建时使用的变量,类似于 ENV,但仅在构建时有效 ARG VERSION=1.0
LABEL 为镜像添加元数据,例如作者信息或版本号 LABEL maintainer="you@example.com"
STOPSIGNAL 指定容器停止时发送的信号 STOPSIGNAL SIGTERM
HEALTHCHECK 定义容器的健康检查,确保其状态正常 HEALTHCHECK CMD curl --fail http://localhost:8080
SHELL 指定构建镜像时使用的 shell 命令,默认是 /bin/sh SHELL ["/bin/bash", "-c"]

这个表格总结了常见的 Dockerfile 指令以及它们的用途和示例,可以帮助你快速查阅和理解各个命令的作用。

FROM

每个 Dockerfile 必须以 FROM 指令开头。它定义了基础镜像,也就是我们构建新镜像时所依赖的现有镜像。

dockerfile 复制代码
FROM ubuntu:20.04

上面这条指令表示基于 Ubuntu 20.04 构建新的镜像。

RUN

RUN 指令用于在镜像构建期间执行命令。比如安装软件、设置环境等。

dockerfile 复制代码
RUN apt-get update && apt-get install -y python3

上面这条命令会在基础镜像上安装 Python3。

CMD 和 ENTRYPOINT

CMDENTRYPOINT 都是用于指定容器启动时要执行的命令。它们的区别在于,CMD 可以被 docker run 命令覆盖,而 ENTRYPOINT 通常是不可替换的。

dockerfile 复制代码
CMD ["python3", "-m", "http.server", "8000"]

这个 CMD 指令会在容器启动时运行一个 Python HTTP 服务器。

dockerfile 复制代码
ENTRYPOINT ["python3", "-m", "http.server"]
CMD ["8000"]

这种组合方式意味着 ENTRYPOINT 确定了固定的执行程序,而 CMD 则允许我们在启动时修改默认的端口。

COPY 和 ADD

COPYADD 都是用于将文件从宿主机复制到镜像中。COPY 更简单直接,而 ADD 还支持从远程 URL 下载文件。

dockerfile 复制代码
COPY ./app /usr/src/app

WORKDIR

WORKDIR 用于设置接下来运行指令的工作目录。

dockerfile 复制代码
WORKDIR /usr/src/app

ENV

ENV 用来定义环境变量。

dockerfile 复制代码
ENV APP_ENV=production

EXPOSE

EXPOSE 指令告诉 Docker 容器在运行时哪个端口会被监听。注意,它并不会自动开启端口,只是一个声明。

dockerfile 复制代码
EXPOSE 8080

Dockerfile 示例

下面是一个简单的 Python 应用 Dockerfile:

dockerfile 复制代码
# 使用官方的 Python 镜像作为基础镜像
FROM python:3.8-slim-buster

# 设置工作目录
WORKDIR /app

# 复制当前目录内容到 /app
COPY . /app

# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt

# 声明容器运行时使用的端口
EXPOSE 5000

# 启动应用
CMD ["python", "app.py"]

这个 Dockerfile 实现了以下功能:

  1. 使用 Python 3.8 作为基础镜像
  2. 设置 /app 作为工作目录
  3. 复制项目文件到 /app
  4. 安装 Python 依赖
  5. 声明容器会使用 5000 端口
  6. 运行应用程序

多阶段构建优化镜像

多阶段构建允许我们使用多个 FROM 指令,将应用的构建和运行环境分离,从而减小镜像大小。

dockerfile 复制代码
# 第一个阶段:构建
FROM golang:1.18-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

# 第二个阶段:运行
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/myapp .
CMD ["./myapp"]

在这个例子中,我们使用 golang 镜像来构建 Go 应用,而最终的镜像只保留了运行应用所需的 alpine 镜像,从而减少了镜像体积。


常见的 Dockerfile 最佳实践

1. 减少镜像层数

尽量合并多个 RUN 指令来减少镜像层数。

dockerfile 复制代码
RUN apt-get update && apt-get install -y \
    curl \
    git \
    && rm -rf /var/lib/apt/lists/*

2. 使用轻量基础镜像

选择轻量的基础镜像,如 alpine,可以大大减少镜像大小。

dockerfile 复制代码
FROM node:14-alpine

3. 使用 .dockerignore

通过 .dockerignore 文件来忽略不需要的文件,避免它们被复制到镜像中,从而减小镜像大小。

dockerignore 复制代码
node_modules
.git

4. 使用多阶段构建

正如上面所提到的,多阶段构建可以减少镜像大小,尤其在需要编译的应用中。

5. 最小化容器中的权限

为了安全性,尽量不要以 root 用户运行容器中的应用。

dockerfile 复制代码
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

结论

通过本篇博文,你应该已经对 Dockerfile 的基础与进阶用法有了清晰的认识。从基础语法到多阶段构建,再到优化镜像的最佳实践,Dockerfile 可以帮助我们高效地构建和管理容器镜像。在实际项目中,建议根据需求不断优化 Dockerfile,以提升构建效率和运行安全性。

掌握 Dockerfile 编写不仅能帮助你更好地理解容器化的概念,还能显著提升开发和运维效率。希望这篇文章能为你提供帮助,在未来的 Docker 项目中写出更优雅的 Dockerfile。


延伸阅读

相关推荐
猿与禅14 天前
devops-Dockerfile+Jenkinsfile方式部署Java前后端应用
java·运维·jenkins·devops·jenkinsfile·dockerfile
老司机张师傅1 个月前
【微服务实战之Docker容器】第七章-Dockerfile解析
容器·dockerfile·虚悬镜像·docker学习
Ops菜鸟(Xu JieHao)1 个月前
Dockerfile构建镜像(练习一Apache镜像)(5-1)
服务器·docker·容器·apache·脚本·dockerfile·系统运维
强哥之神2 个月前
如何构建一个支持GPU的Llamafile容器
人工智能·机器学习·语言模型·gpu·dockerfile·1024程序员节·llamafile
强哥之神2 个月前
Dockerfile最佳实践:如何创建高效的容器
人工智能·docker·语言模型·k8s·dockerfile·docker镜像·镜像制作
丁总学Java3 个月前
使用dockerfile来构建一个包含Jdk17的centos7镜像(构建镜像:centos7-jdk17)
dockerfile
SilentCodeY3 个月前
docker build前耗时太长,不明所以
运维·docker·容器·镜像·dockerfile
丁总学Java3 个月前
dockerfile部署springboot项目(构建镜像:ebuy-docker:v1.0)
java·spring boot·后端·dockerfile
瞭望清晨4 个月前
Docker容器创建时,无法访问镜像源:Could not connect to archive.ubuntu.com:80
ubuntu·docker·容器·镜像源·dockerfile