前言回顾系统学习docker系列已发布内容【docker基础】0、系统学习docker之总计划【docker基础】第一课、从零开始理解容器技术【docker基础】第二课安装、配置与基础命令相关文档windows下安装docker【docker基础】Ubuntu 安装 Docker 超详细小白教程本文是计划的第三周的内容内容纲要如下第3周镜像管理镜像操作拉取镜像docker pull查看镜像docker images删除镜像docker rmi构建镜像docker buildDockerfile 基础基础指令FROM、RUN、CMD、ENTRYPOINT构建上下文与 .dockerignore多阶段构建Docker第三周镜像管理与Dockerfile基础镜像管理和Dockerfile的基础知识这些是Docker使用的核心技能。一、原理镜像管理逻辑1.1、回顾镜像到底是什么一句话Docker 镜像 软件 运行环境 配置 的“压缩包/安装包”比如nginx镜像 Nginx 服务器 运行它需要的 Linux 环境mysql镜像 MySQL 数据库 依赖库 默认配置hello-world镜像 一个小测试程序你不用自己装环境镜像里已经打包好了一切。1.2、Docker 镜像管理的核心逻辑整个逻辑就 4 步拉取 → 本地存储 → 基于镜像启动容器 → 删除/清理1.3、第一层逻辑从哪里来远程仓库Docker 默认有一个官方仓库Docker Hub全世界的镜像都放这里。你执行docker pull nginx逻辑是本地没有 nginx 镜像Docker 去 Docker Hub 下载下载后存到你电脑本地以后再用就不用重新下载这就像从应用商店下载 APP 到手机1.4、第二层逻辑本地怎么存分层存储这是 Docker 最牛的设计必须懂。镜像不是一个大文件而是多层叠加。例如第一层基础 Linux 系统很小第二层依赖库第三层nginx 程序第四层默认配置好处共享底层比如 nginx 和 node 都用同一个基础层只存一份不重复占用空间。下载更快只下载你没有的层。体积小你可以理解成积木堆叠共用底层积木1.5、第三层逻辑镜像和容器的关系最容易混淆镜像 安装包只读不能改容器 运行起来的软件可写活的实例关系逻辑一个镜像可以启动 N 个容器容器死掉镜像还在镜像不动容器随便删、随便建比喻最准确镜像 游戏安装包容器 打开运行的游戏你不会因为关了游戏安装包就没了。1.6、第四层逻辑本地镜像怎么管理常用命令就是围绕这 4 件事1. 查看本地有哪些镜像docker images逻辑列出本地已经下载的所有镜像。2. 下载镜像docker pull 名字:版本例如docker pull nginx:latest3. 删除不用的镜像docker rmi 镜像ID或名字逻辑必须先删除依赖它的容器否则不让删镜像一旦删除下次要用必须重新拉取4. 搜索镜像docker search nginx逻辑去 Docker Hub 搜索可用镜像。1.7、第五层逻辑镜像版本与标签tag镜像不是只有一个而是有版本。格式名字:标签例如nginx:latest最新版nginx:1.25固定版本ubuntu:22.04逻辑不写标签默认latest不同标签 不同镜像可以同时存在本地多个版本1.8、完整流程串起来最清晰总结你要运行软件Docker 先看本地有没有镜像没有 → 去远程仓库Docker Hub拉取拉到本地按分层存储用镜像创建并启动容器容器运行不用了可以删除容器 / 删除镜像二、原理本地镜像构建当我们需要自己本地容器形成镜像时Docker 是怎么做的下面分解一下。2.1、一句话核心逻辑创建本地镜像 给当前的容器“拍个快照”把它固化成一个只读安装包。2.2、先搞懂 2 种创建镜像的方式必须分清Docker 只有2 种制作本地镜像的方法方式1docker commit最直观、最简单从正在运行的容器直接打包成镜像就像给虚拟机拍快照。方式2docker build标准、正式、企业都用通过 Dockerfile 文件自动构建镜像就像写好安装脚本让电脑自动一键安装打包。2.3、方式1docker commit 打包逻辑快照式逻辑流程超级简单你启动一个容器比如 Ubuntu进入容器装软件、改配置你觉得现在状态很好想保存下来用docker commit把容器打包成新镜像打包完成 → 本地出现一个新镜像核心原理把容器当前的所有改动读写层合并成一个新的只读层 → 生成新镜像。比喻容器 你正在画的画commit 把这幅画保存成图片文件图片文件 新的本地镜像commit 打包示例# 1. 运行一个容器 docker run -it ubuntu # 2. 在里面装东西举例 apt update apt install -y vim # 3. 退出容器 exit # 4. 把容器打包成新镜像 docker commit 容器ID 我的镜像名2.4、方式2docker build 构建逻辑脚本式最常用逻辑流程标准工作流你写一个Dockerfile纯文本告诉 Docker 怎么装环境Docker 读取 Dockerfile 里的命令FROM、RUN、COPY 等Docker自动启动临时容器执行每一步每执行一行就生成一层镜像全部执行完 → 自动合并成最终镜像镜像保存到本地核心原理分层构建 缓存复用每一行命令 一层下次构建没变的层直接用缓存速度超快。比喻Dockerfile 菜谱docker build 按照菜谱一步步做菜最终镜像 做好的成品菜Dockerfile 构建标准做法1、创建文件DockerfileFROM ubuntu RUN apt update apt install -y vim2、构建docker build -t 我的镜像名 .2.5、最关键的底层逻辑一定要懂1. 镜像是只读的你不能直接改镜像只能基于镜像启动容器在容器里修改再打包成新镜像2. 镜像永远是分层的无论 commit 还是 build都是加一层不会破坏底层。3. 本地镜像存在哪里存在你电脑本地的 Docker 仓库里。可以用docker images看到。4. 构建镜像不会影响原来的镜像比如你基于 nginx 打包出新镜像 my-nginx原来的 nginx 镜像还在不动。三、命令详解1. 镜像操作1.1 docker pull - 拉取镜像功能从Docker Hub或其他镜像仓库拉取镜像到本地。语法docker pull [选项] [镜像名:标签]示例# 拉取最新版的Ubuntu镜像 docker pull ubuntu # 拉取指定版本的Ubuntu镜像 docker pull ubuntu:20.04 # 拉取指定仓库的镜像 docker pull nginx:alpine命令输出解释当执行docker pull ubuntu:20.04时输出类似20.04: Pulling from library/ubuntu 5e0b432e8ba9: Pull complete 7d2f8da89488: Pull complete 9898bd3c8293: Pull complete 1966ea362d23: Pull complete Digest: sha256:4c972423568361b624a598a89a88f55f3643336776233985f7a936e19f20b752 Status: Downloaded newer image for ubuntu:20.04 docker.io/library/ubuntu:20.04Pulling from library/ubuntu表示从官方库拉取Ubuntu镜像5e0b432e8ba9: Pull complete表示镜像的各个层正在被拉取每个层都有一个唯一的IDDigest镜像的哈希值用于验证镜像的完整性Status拉取状态docker.io/library/ubuntu:20.04完整的镜像引用1.2 docker images - 查看镜像功能列出本地所有的镜像。语法docker images [选项] [镜像名]示例# 查看所有镜像 docker images # 查看指定镜像 docker images ubuntu # 查看所有镜像包括中间层镜像 docker images -a # 以精简模式查看镜像 docker images --format {{.ID}} {{.Repository}} {{.Tag}} {{.Size}}命令输出解释执行docker images时输出类似REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu 20.04 1e4467b07108 2 weeks ago 72.9MB ubuntu latest 1e4467b07108 2 weeks ago 72.9MB nginx alpine a1523e859360 3 weeks ago 22.1MBREPOSITORY镜像的仓库名TAG镜像的标签通常用于表示版本IMAGE ID镜像的唯一标识符CREATED镜像的创建时间SIZE镜像的大小1.3 docker rmi - 删除镜像功能删除本地的镜像。语法docker rmi [选项] 镜像名/ID [镜像名/ID...]示例# 通过镜像ID删除镜像 docker rmi 1e4467b07108 # 通过镜像名和标签删除镜像 docker rmi ubuntu:20.04 # 强制删除镜像即使有容器正在使用 docker rmi -f ubuntu:20.04 # 删除所有未使用的镜像 docker image prune -a命令输出解释执行docker rmi ubuntu:20.04时输出类似udocker rmi ubuntu:20.04 Untagged: ubuntu:20.04 Untagged: ubuntusha256:4c972423568361b624a598a89a88f55f3643336776233985f7a936e19f20b752 Deleted: sha256:1e4467b07108685d9f31e232b69b946b58766173b3f93b29563a139ee7687a4b Deleted: sha256:f0749404a60b7311769f6f2e5d88b2b1980a4351944273966550b23f42d2336aUntagged表示解除了镜像的标签Deleted表示删除了镜像的各个层1.4 docker build - 构建镜像功能根据Dockerfile构建镜像。语法docker build [选项] 构建上下文路径示例# 在当前目录构建镜像标签为myapp:v1 docker build -t myapp:v1 . # 指定Dockerfile文件路径 docker build -t myapp:v1 -f Dockerfile.dev . # 从URL构建镜像 docker build -t myapp:v1 https://github.com/username/repo.git#branch命令输出解释执行docker build -t myapp:v1 .时输出类似Sending build context to Docker daemon 2.048kB Step 1/3 : FROM ubuntu:20.04 --- 1e4467b07108 Step 2/3 : RUN apt-get update apt-get install -y nginx --- Running in 9f7a5c5a5b4f ... Removing intermediate container 9f7a5c5a5b4f --- 8a7b3c2f3c4d Step 3/3 : CMD [nginx, -g, daemon off;] --- Running in 2d9c17e6b8d9 Removing intermediate container 2d9c17e6b8d9 --- 3e2a8b93a8d5 Successfully built 3e2a8b93a8d5 Successfully tagged myapp:v1Sending build context发送构建上下文到Docker守护进程Step 1/3构建步骤从1到3FROM ubuntu:20.04使用Ubuntu 20.04作为基础镜像RUN apt-get update apt-get install -y nginx执行命令安装nginxRunning in 9f7a5c5a5b4f在临时容器中运行命令Removing intermediate container移除临时容器Successfully built构建成功返回镜像IDSuccessfully tagged标记镜像为myapp:v12. Dockerfile基础2.1 基础指令FROM作用指定基础镜像。语法FROM 镜像名:标签示例FROM ubuntu:20.04 FROM nginx:alpine说明每个Dockerfile必须以FROM指令开始指定构建镜像的基础镜像。RUN作用在构建过程中执行命令。语法RUN 命令或RUN [可执行文件, 参数1, 参数2]示例RUN apt-get update apt-get install -y nginx RUN [mkdir, -p, /app]ℹ️ℹ️命令分解说明第一行命令1.RUNDockerfile 专用指令意思在构建镜像时执行下面的 Linux 命令2.apt-get updateUbuntu 系统命令意思刷新软件源列表让系统知道最新的软件版本3.意思前面命令成功再执行后面的4.apt-get install -y nginx意思自动安装 Nginx 网页服务器-y 自动确认安装不用手动按回车整句大白话构建镜像时自动刷新软件源并且自动安装 Nginx。第二行命令1.RUN还是执行命令2.mkdirLinux 命令 make directory创建文件夹3.-p参数表示如果文件夹不存在就创建如果已存在就不报错4./app要创建的目录路径 在根目录下创建 app 文件夹整句大白话构建镜像时自动在容器里创建 /app 目录。两种写法对比非常重要第一行写法 shell 模式dockerfileRUN apt-get update apt-get install -y nginxshell 格式Docker 会调用/bin/sh -c 命令相当于在终端里执行第二行写法 exec 模式推荐dockerfileRUN [mkdir, -p, /app]exec 模式直接调用命令不经过 shell更干净、更稳定功能一样只是写法不同说明RUN指令在构建过程中创建新的镜像层多个RUN指令会创建多个层建议使用将多个命令组合在一个RUN指令中以减少镜像层数。CMD作用指定容器启动时执行的命令。语法CMD 命令或CMD [可执行文件, 参数1, 参数2]示例CMD [nginx, -g, daemon off;] CMD echo Hello World说明每个Dockerfile只能有一个CMD指令如果有多个只有最后一个生效。CMD指令可以被docker run命令后的参数覆盖。注意区别RUN构建镜像时执行打包阶段CMD容器运行时执行启动阶段创建目录是打包环境不是启动命令所以必须用 RUN。ENTRYPOINT作用指定容器启动时执行的命令与CMD类似但不可被docker run命令后的参数覆盖。语法ENTRYPOINT 命令或ENTRYPOINT [可执行文件, 参数1, 参数2]示例ENTRYPOINT [nginx, -g, daemon off;]说明告诉 Docker容器启动后必须直接运行这条完整命令nginx -g daemon off;而且谁也覆盖不掉容器一运行就执行它。daemon off让Nginx在前台一直运行→ 容器保持运行 → 服务正常使用否则Nginx 会默认后台运行→ 运行完直接退出 → 容器认为任务结束 →容器立刻停止CMD与ENTRYPOINT的对比CMD给容器提供默认命令 / 默认参数 **可被覆盖**ENTRYPOINT给容器指定固定入口程序 **不会被覆盖**最佳实践两者一起用ENTRYPOINT 写程序CMD 写默认参数COPY作用将构建上下文中的文件复制到镜像中。语法COPY 源路径 目标路径示例COPY . /app COPY package.json /app/说明COPY指令可以复制本地文件到镜像中源路径是相对于构建上下文的路径。ADD作用与COPY类似但支持URL和压缩文件。语法ADD 源路径 目标路径示例ADD https://example.com/file.tar.gz /app ADD file.tar.gz /app说明ADD指令会自动解压压缩文件而COPY不会。WORKDIR作用设置工作目录。语法WORKDIR 路径示例WORKDIR /app说明WORKDIR指令设置的目录会成为后续RUN、CMD、ENTRYPOINT等指令的工作目录。ENV作用设置环境变量。语法ENV 变量名 值或ENV 变量名值示例ENV NODE_ENVproduction ENV PATH/app/node_modules/.bin:$PATH说明环境变量可以在构建过程中使用也可以在容器运行时使用。2.2 构建上下文与.dockerignore构建上下文定义构建上下文是指docker build命令指定的路径下的所有文件和目录。作用Docker守护进程会将构建上下文发送到自身然后在其中执行构建操作。示例# 当前目录作为构建上下文 docker build -t myapp:v1 . # 指定其他目录作为构建上下文 docker build -t myapp:v1 /path/to/context注意构建上下文的大小会影响构建速度因此应尽量减小构建上下文的大小。.dockerignore文件作用用于指定构建上下文目录中哪些文件或目录应该被排除不发送到Docker守护进程。示例# 排除所有.git目录 .git # 排除所有node_modules目录 node_modules # 排除所有.log文件 *.log # 排除构建产物 dist/ build/ # 排除本地配置文件 .env.local说明使用.dockerignore文件可以减小构建上下文的大小提高构建速度同时避免将敏感文件复制到镜像中。2.3 多阶段构建优势减小镜像体积只包含运行所需的文件不包含构建工具和依赖。提高安全性减少镜像中的攻击面。简化构建流程在一个Dockerfile中完成构建和打包。示例# 第一阶段构建阶段 FROM node:14 as builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # 第二阶段运行阶段 FROM nginx:alpine COPY --frombuilder /app/build /usr/share/nginx/html EXPOSE 80 CMD [nginx, -g, daemon off;]解释第一阶段使用node:14作为基础镜像安装依赖并构建应用。第二阶段使用nginx:alpine作为基础镜像从第一阶段复制构建产物到nginx的默认静态文件目录。最终的镜像只包含nginx和构建产物不包含node.js和构建依赖体积更小。3. 实践练习3.1 练习1拉取并查看镜像拉取Ubuntu 20.04镜像docker pull ubuntu:20.04拉取Nginx Alpine镜像docker pull nginx:alpine查看所有镜像docker images尝试删除其中一个镜像docker rmi ubuntu:20.04再次查看镜像docker images3.2 练习2构建简单的Web应用镜像创建一个新目录mkdir my-webapp cd my-webapp创建index.html文件!DOCTYPE html html head titleMy Web App/title /head body h1Hello Docker!/h1 pThis is a simple web app running in a Docker container./p /body /html创建Dockerfile文件FROM nginx:alpine COPY index.html /usr/share/nginx/html/ EXPOSE 80构建镜像docker build -t my-webapp:v1 .查看构建的镜像docker images my-webapp3.3 练习3使用多阶段构建创建一个新目录mkdir multi-stage cd multi-stage创建package.json文件{ name: my-react-app, version: 1.0.0, scripts: { build: echo Building React app... mkdir -p build echo h1React App/h1 build/index.html } }创建Dockerfile文件# 构建阶段 FROM node:14 as builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # 运行阶段 FROM nginx:alpine COPY --frombuilder /app/build /usr/share/nginx/html EXPOSE 80构建镜像docker build -t my-react-app:v1 .查看构建的镜像大小docker images my-react-app4. 总结通过本周的学习我们掌握了Docker镜像的基本操作拉取镜像使用docker pull从仓库获取镜像查看镜像使用docker images查看本地镜像删除镜像使用docker rmi删除不需要的镜像构建镜像使用docker build根据Dockerfile构建镜像同时我们也学习了Dockerfile的基础指令和最佳实践基础指令FROM、RUN、CMD、ENTRYPOINT等构建上下文理解构建上下文的概念和作用.dockerignore使用.dockerignore减小构建上下文多阶段构建使用多阶段构建减小镜像体积这些知识是Docker使用的基础掌握这些操作后我们可以更加灵活地使用Docker来构建和管理应用。