Docker全攻略:从入门到精通,掌握容器构建关键技能

云掣YunChe8个月前行业资讯669

引言

        Dockerfile 是构建 Docker 镜像的核心文件。它定义了如何将应用程序及其依赖打包成一个可以跨平台运行的容器。本篇博客将从基础概念出发,逐步介绍 Dockerfile 的常见配置、使用注意事项,以及如何编写高效的 Dockerfile。




一、什么是 Dockerfile

        Dockerfile 是一组指令的集合,用于定义如何创建一个 Docker 镜像。每条指令对应于镜像构建中的一个步骤,这些步骤被逐个执行,最终生成一个可用的容器镜像。它不仅提升了应用的可移植性,还简化了部署和管理的流程。


        Dockerfile是一个没有后缀名的文件!Dockerfile是一个没有后缀名的文件!Dockerfile是一个没有后缀名的文件!放到用来执行cmd命令的文件夹中即可。



二、Dockerfile 的基本结构



        Dockerfile 中的每一行是一个命令,这些命令定义了镜像的构建流程。以下是 Dockerfile 常见指令的基础介绍:


FROM:定义基础镜像


格式:FROM <image>[:tag]

说明:Dockerfile 中每一个镜像必须从某个基础镜像开始,比如 FROM node:14,表示基于 Node.js 14 构建镜像。

RUN:执行命令


格式:RUN <command>

说明:用于在镜像构建过程中执行命令,如安装软件包等。

COPY 和 ADD:复制文件


格式:COPY <src> <dest> 或 ADD <src> <dest>

说明:将文件从主机复制到镜像中。ADD 可以处理 URL 和压缩文件,而 COPY 更为简单,通常推荐使用。

WORKDIR:设置工作目录


格式:WORKDIR <path>

说明:定义命令的执行路径,如果该路径不存在,Docker 会自动创建。

CMD 和 ENTRYPOINT:定义容器启动时的默认行为


格式:CMD ["executable", "param1", "param2"]

说明:CMD 提供了容器的默认运行命令,但可以被覆盖。ENTRYPOINT 则定义了固定的启动命令,通常配合 CMD 来设置参数。

EXPOSE:暴露端口


格式:EXPOSE <port>

说明:声明容器内部应用监听的端口,不过需要在运行容器时明确暴露该端口。

ENV:设置环境变量


格式:ENV <key> <value>

说明:用于定义在构建和运行时可用的环境变量。

三、Dockerfile 的常见配置项

        首先我们要知道,Dockerfile中的指令分为两大类,一部分为声明式指令,比如“FROM”、“WORKDIR”、“EXPOSE”、“ENV”、“VOLUME”、“USER”、“LABEL”,它们只是声明一个基础,一个规则或者一个关系,另一部分为创建式指令,如“RUN”、“COPY”、“ADD”、“CMD”、“ENTRYPOINT”,它们往往是增加部分文件或内容、预设部分命令等,都会创建一个镜像层,若干个镜像层合并起来就是你要生成的新镜像。比如:


FROM node:14              # 基础镜像,不创建新层

WORKDIR /app              # 设置工作目录,不创建新层

COPY package.json /app    # 复制文件,创建新层

RUN npm install           # 安装依赖,创建新层

COPY . /app               # 复制代码,创建新层

CMD ["npm", "start"]      # 设置容器启动命令,创建新层

        为了让 Dockerfile 更高效和可维护,以下是一些常见的优化配置:


1、多阶段构建 (Multi-stage Builds)

        在开发过程中可能会遇到需要在镜像内编译源代码,但编译后的产物才是最终的镜像内容。多阶段构建可以将编译和最终镜像的制作分离出来,减少镜像体积。


FROM golang:1.16 AS builder

WORKDIR /app

COPY . .

RUN go build -o myapp

 

FROM alpine:latest

WORKDIR /app

COPY --from=builder /app/myapp .

CMD ["./myapp"]

2、缓存优化

        Dockerfile 中的命令是逐行缓存的。当构建镜像时,如果 Docker 发现某个指令之前已经执行过,并且输入没有发生变化,它会直接使用缓存的结果,而不重新执行。这能显著提升构建速度。


        如果文件结构频繁变化,可以通过合理安排 COPY 和 RUN 来减少不必要的重新构建。


        例如,假设你在 Dockerfile 中执行如下指令:


COPY . /app

RUN npm install

        如果你的项目代码发生了变化,COPY . /app 会被重新执行,那么 RUN npm install 也会重新执行,即使 package.json 没有变化。重新安装依赖往往非常耗时,这是不必要的。


         解决办法是将 package.json 先单独复制并安装依赖,再复制其余代码:


# Step 1: 复制 package.json 并安装依赖

COPY package.json /app

RUN npm install

 

# Step 2: 复制项目的其他文件

COPY . /app

        这样,只有当 package.json 发生变化时,才会重新运行 npm install,否则它会使用缓存结果,极大地节省时间。


3、合并 RUN 命令

        每个 RUN 命令会创建一个镜像层,多个命令可以合并到一个 RUN 中,减少镜像层的数量,优化镜像大小。


RUN apt-get update && apt-get install -y \

    python3 \

    python3-pip && \

    apt-get clean && \

    rm -rf /var/lib/apt/lists/*

四、Dockerfile 使用须知

        .dockerignore 文件:类似于 .gitignore,.dockerignore 文件可以避免将不必要的文件(如 .git、node_modules)复制到镜像中,优化构建速度和镜像体积。


        命令顺序和缓存:Docker 构建是分层的,每个命令会生成一个新的层。如果前面的层没有变化,Docker 会使用缓存,避免重新执行命令。因此,优化命令顺序能加速构建过程。


        最小化镜像大小:选择合适的基础镜像(如 Alpine),移除不必要的包和文件,尽量减小镜像体积,提升启动速度和安全性。


        避免敏感信息:不要将密码、秘钥等敏感信息硬编码在 Dockerfile 中。可以使用环境变量、配置文件等方式进行注入。


        容器进程的管理:确保 Docker 容器中运行的进程是前台进程,否则容器可能会意外退出。可以通过 CMD 或 ENTRYPOINT 来定义正确的启动进程。 




五、一个完整的Dockerfile实例

# Step 1: 使用 Node.js 作为基础镜像

FROM node:14

 

# Step 2: 设置工作目录

WORKDIR /app

 

# Step 3: 复制 package.json 并安装依赖

COPY package.json /app

RUN npm install

 

# Step 4: 复制应用代码

COPY . /app

 

# Step 5: 构建应用

RUN npm run build

 

# Step 6: 暴露应用端口

EXPOSE 3000

 

# Step 7: 定义容器启动命令

CMD ["npm", "start"]

六、总结

        Dockerfile 是容器化开发中的关键工具。理解并掌握其使用方式,不仅能提高开发效率,还能让你的应用具备更强的可移植性和灵活性。通过优化配置和合理安排构建步骤,可以打造更轻量、更高效的容器镜像。



免责申明:

本文转载自网友公开分享,若有侵权,请联系我们删除!


相关文章

Docker 基础与实战指南(2)

Docker 基础与实战指南(2)

二、Docker 基础接下来,我们一起来学习 Docker 使用的一些基础知识,为将来部署项目打下基础。具体用法可以参考 Docker 官方文档:https://docs.docker.com/2.1...

云端之上的边缘:解读云计算与边缘计算的战略融合

云端之上的边缘:解读云计算与边缘计算的战略融合

一、引言在当前的信息化和数字化浪潮中,云计算和边缘计算作为两种重要的计算模型,各自在不同的场景中发挥着不可替代的作用。它们不仅在概念、特点、应用场景上有所不同,而且在实际应用中常常相互协作,形成了一种...

一个初级运维工程师对于运维工作的一些浅显认知

一个初级运维工程师对于运维工作的一些浅显认知

最近因为部门架构调整,之前工作做了交接,新的安排又没有确定,领导建议学习下JAVA开发,后续直接参与到研发工作中而不再负责运维工作。周围同事也都在说运维工作比较low,转研发会好一些。但是毕竟从毕业之...

RabbitMQ 进阶1(发送者和MQ的可靠性)

RabbitMQ 进阶1(发送者和MQ的可靠性)

我们可以通过 MQ 异步调用,来使程序的性能更好和解耦合。但是如果 MQ 的消息没有成功的被对应的程序处理,那么这样不就会造成数据不一致的情况。因此,我们这里必须要尽可能的确保 MQ 消息的可靠性,即...

MySQL运维之分库分表与读写分离

MySQL运维之分库分表与读写分离

分库分表1.介绍问题分析随着互联网以及移动互联网的发展,应用系统的数据量也是成指数式增长,若采用单数据库进行数据存储,存在以下性能瓶颈:IO瓶颈:热点数据太多,数据库缓存不足,产生大量磁盘IO,效率较...

MyBatisPlus从零到一:快速入门与核心功能详解(3)

MyBatisPlus从零到一:快速入门与核心功能详解(3)

2.3 Service 接口:MybatisPlus 不仅提供了 BaseMapper,还提供了通用的 Service 接口及默认实现,封装了一些常用的 service 模板方法。通用接口为IServ...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。