Docker 镜像构建详解:docker commit、Dockerfile 与 docker build 实战
- Docker 镜像构建详解:docker commit、Dockerfile 与 docker build 实战
- 一、使用 docker commit 构建镜像
-
-
- 1. docker commit 的作用
- 二、将运行中的 Tomcat 容器制作成镜像
- 三、查看生成的镜像
- 四、为镜像添加仓库信息
- 五、登录 Docker Hub
- 六、上传镜像到 Docker Hub
-
- 七、docker commit 的特点
- 八、使用 Dockerfile 构建镜像
- 九、Dockerfile 常用指令
- 十、Dockerfile 实战:构建一个 Nginx Web 镜像
-
- 十一、使用 docker build 构建镜像
- 十二、Docker 镜像的分层构建机制
- 十三、查看构建完成的镜像
- 十四、启动自己构建的 Nginx 容器
- 十五、Dockerfile 综合案例:CentOS + JDK + Tomcat + Web 应用
- 十六、编写 Java Web Dockerfile
- 十七、逐条理解 Dockerfile
-
-
- 1. 指定基础镜像
- 2. 设置维护者信息
- 3. 创建目录
- 4. 复制 JDK
- 5. 解压 JDK
- 6. 设置 Java 环境变量
- 7. 获取 Tomcat
- 8. 解压 Tomcat
- 9. 部署 Web 应用
- 10. 设置容器启动程序
-
- 十八、构建 Java Web 镜像
- 十九、运行 Java Web 容器
- 二十、docker commit 和 Dockerfile 怎么选?
- 二十一、几个容易混淆的 Docker 命令
-
-
- docker commit
- docker build
- docker run
- docker tag
- docker push
-
- 二十二、总结
Docker 镜像构建详解:docker commit、Dockerfile 与 docker build 实战
在 Docker 的日常使用中,我们通常会直接使用 Docker Hub 或其他镜像仓库中的现成镜像,例如 CentOS、Nginx、Tomcat、MySQL 等。
但在实际项目中,经常需要把自己的应用程序、运行环境和相关配置一起制作成镜像。这就涉及 Docker 自定义镜像的构建。
常见的构建方式主要有两种:
其中,docker commit 更适合快速验证和理解镜像生成过程;实际开发、部署和持续集成中,则更推荐使用 Dockerfile。
一、使用 docker commit 构建镜像
1. docker commit 的作用
docker commit 可以把一个容器当前的文件系统状态保存为新的 Docker 镜像。
基本语法:
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
常用参数包括:
-a, –author string 设置作者信息
-c, –change list 对生成的镜像应用 Dockerfile 指令
-m, –message string 设置提交说明
-p, –pause commit 时暂停容器,默认为 true
它的核心逻辑可以理解为:
基础镜像
↓
创建并运行容器
↓
在容器中安装软件、部署应用、修改文件
↓
docker commit
↓
生成新的 Docker 镜像
二、将运行中的 Tomcat 容器制作成镜像
假设已经基于 Tomcat 镜像创建了一个容器,并且已经在容器中部署好了 Web 应用。
首先查看当前正在运行的容器:
docker ps
也可以通过自定义的命令别名查看:
myps
输出类似:
CONTAINER ID IMAGE NAMES PORTS
33a3e34fcf0a tomcat mytomcat 0.0.0.0:8080->8080/tcp
这里需要重点记住容器 ID:
33a3e34fcf0a
接下来将这个容器保存为新的镜像:
docker commit 33a3e34fcf0a tomcat_with_application
命令执行成功后,会返回新镜像对应的 SHA256 ID。
三、查看生成的镜像
可以使用:
docker images
如果配置了别名,也可以使用:
myimages
可以看到刚刚生成的镜像:
REPOSITORY TAG
tomcat_with_application latest
docker.io/nginx latest
docker.io/tomcat latest
docker.io/mysql 5.7.19
如果没有显式指定 Tag,Docker 默认使用:
latest
因此:
docker commit 33a3e34fcf0a tomcat_with_application
实际上相当于生成:
tomcat_with_application:latest
四、为镜像添加仓库信息
如果准备把镜像上传到远程镜像仓库,需要按照目标仓库的命名规则给镜像重新打标签。
例如:
docker tag tomcat_with_application:latest username/tomcat_with_application:latest
再次执行:
docker images
就可以看到带有仓库路径的新镜像名称。
需要注意,docker tag 不会重新复制一份完整的镜像数据,本质上只是给已有镜像增加一个新的引用名称。
五、登录 Docker Hub
上传镜像之前需要先登录:
docker login
根据终端提示输入 Docker Hub 的用户名和认证信息。
登录成功后即可进行后续的镜像推送操作。
六、上传镜像到 Docker Hub
使用:
docker push username/tomcat_with_application:latest
Docker 会逐层上传镜像,例如:
Preparing
Pushing
Pushed
Layer already exists
Docker 镜像采用分层存储机制。如果远程仓库已经存在某些相同的镜像层,这些层不需要重复上传,因此可能出现:
Layer already exists
这种机制能够减少重复数据传输,提高镜像上传和下载效率。
七、docker commit 的特点
docker commit 使用起来非常直接:
启动容器
↓
进入容器
↓
安装软件
↓
修改配置
↓
部署应用
↓
docker commit
↓
生成镜像
但是它也存在比较明显的问题。
容器内部执行了哪些操作,很难仅通过最终镜像完整追溯。例如一个人可能在容器中执行了几十条命令,最后只执行:
docker commit ...
其他开发者拿到镜像之后,并不知道这个镜像具体是如何制作出来的。
因此,docker commit 更适合:
- 快速保存当前容器状态;
- 实验和测试;
- 临时制作镜像;
- 学习 Docker 镜像和容器之间的关系。
对于正式项目,更推荐使用 Dockerfile。
八、使用 Dockerfile 构建镜像
Dockerfile 本质上是一个文本文件。
文件中按照一定规则写入 Docker 构建镜像需要执行的指令,例如:
FROM nginx
RUN …
COPY …
ENV …
ENTRYPOINT …
然后通过:
docker build
读取 Dockerfile 中的指令,自动完成整个镜像构建过程。
其基本流程为:
Dockerfile
↓
docker build
↓
逐条执行构建指令
↓
生成镜像层
↓
最终 Docker 镜像
与 docker commit 相比,Dockerfile 最大的优势就是构建过程可以被记录、复现和版本管理。
九、Dockerfile 常用指令
常见 Dockerfile 指令如下:
| FROM | 指定基础镜像 |
| MAINTAINER | 指定维护者信息,属于旧式写法 |
| RUN | 在镜像构建过程中执行命令 |
| COPY | 将构建上下文中的文件复制到镜像 |
| ADD | 添加文件,并具有额外处理能力 |
| ENV | 设置环境变量 |
| WORKDIR | 设置后续指令的工作目录 |
| EXPOSE | 声明容器提供服务的端口 |
| VOLUME | 声明数据卷 |
| USER | 指定运行命令时使用的用户 |
| CMD | 设置容器默认执行命令 |
| ENTRYPOINT | 设置容器启动时执行的程序 |
现在编写新的 Dockerfile 时,一般建议使用 LABEL 维护作者等元数据,而不是继续使用已经过时的 MAINTAINER。
十、Dockerfile 实战:构建一个 Nginx Web 镜像
先从一个简单示例开始。
创建一个名为:
Dockerfile
的文件。
注意:
Dockerfile
通常没有扩展名。
写入:
FROM nginx
RUN echo 'This is a Demo HTML' > /usr/share/nginx/html/index.html
这里做了两件事情。
首先:
FROM nginx
表示以 Nginx 镜像作为基础镜像。
然后:
RUN echo 'This is a Demo HTML' > /usr/share/nginx/html/index.html
在构建镜像过程中修改 Nginx 默认首页。
十一、使用 docker build 构建镜像
进入 Dockerfile 所在目录后执行:
docker build -t mynginx .
这里有两个非常重要的参数。
-t mynginx
用于指定最终生成的镜像名称。
最后的:
.
同样非常重要,它表示当前目录作为构建上下文(Build Context)。
所以完整命令不要漏掉最后的点:
docker build -t mynginx .
构建过程中会看到类似:
Sending build context to Docker daemon
Step 1/2 : FROM nginx
—> …
Step 2/2 : RUN echo 'This is a Demo HTML' > /usr/share/nginx/html/index.html
—> Running in …
Successfully built …
Successfully tagged mynginx:latest
Docker 会按照 Dockerfile 中指令的顺序逐条完成构建。
十二、Docker 镜像的分层构建机制
Dockerfile 中的很多指令都会产生新的镜像层。
例如:
FROM nginx
RUN apt-get update
RUN echo "hello" > /usr/share/nginx/html/index.html
可以简单理解为:
基础层:nginx
↓
RUN apt-get update
↓
新的镜像层
↓
RUN echo …
↓
新的镜像层
↓
最终镜像
Docker 在构建过程中会利用缓存机制。
如果某一层以及它之前的构建条件没有变化,那么再次执行:
docker build
时,就可能直接复用已有缓存,而不需要重新执行全部操作。
这也是为什么 Dockerfile 的指令顺序会影响镜像构建效率。
十三、查看构建完成的镜像
执行:
docker images
可以看到:
REPOSITORY TAG
mynginx latest
nginx latest
然后就可以使用这个镜像启动容器。
十四、启动自己构建的 Nginx 容器
执行:
docker run -d -p 7788:80 mynginx
参数含义:
-d
表示容器在后台运行。
-p 7788:80
表示端口映射:
宿主机 7788
↓
容器 80
Nginx 默认监听容器内部的 80 端口,因此访问:
http://服务器IP:7788
即可访问刚刚创建的页面。
十五、Dockerfile 综合案例:CentOS + JDK + Tomcat + Web 应用
下面进一步构建一个完整 Java Web 运行环境。
目标环境:
CentOS
↓
安装 JDK
↓
安装 Tomcat
↓
部署 WAR
↓
生成 Docker 镜像
↓
运行 Java Web 容器
需要准备:
Dockerfile
jdk-8u144-linux-x64.tar.gz
MyDemoWeb.war
目录结构类似:
demo/
├── Dockerfile
├── jdk-8u144-linux-x64.tar.gz
└── MyDemoWeb.war
十六、编写 Java Web Dockerfile
示例:
FROM centos
MAINTAINER zhaoyuqian
RUN mkdir /root/training
RUN mkdir /root/tools
COPY jdk-8u144-linux-x64.tar.gz /root/tools
RUN tar zxvf /root/tools/jdk-8u144-linux-x64.tar.gz \\
-C /root/training
ENV JAVA_HOME /root/training/jdk1.8.0_144
ENV PATH $JAVA_HOME/bin:$PATH
ADD https://dlcdn.apache.org/tomcat/tomcat-8/v8.5.73/bin/apache-tomcat-8.5.73.tar.gz /root/tools
RUN tar zxvf /root/tools/apache-tomcat-8.5.73.tar.gz \\
-C /root/training
COPY MyDemoWeb.war \\
/root/training/apache-tomcat-8.5.73/webapps
ENTRYPOINT ["/root/training/apache-tomcat-8.5.73/bin/catalina.sh","run"]
这个 Dockerfile 描述了从基础操作系统一直到 Java Web 应用部署的完整过程。
十七、逐条理解 Dockerfile
1. 指定基础镜像
FROM centos
表示后续环境建立在 CentOS 基础之上。
2. 设置维护者信息
MAINTAINER zhaoyuqian
用于记录镜像维护者。
不过现在更推荐:
LABEL maintainer="your-name"
3. 创建目录
RUN mkdir /root/training
RUN mkdir /root/tools
其中:
/root/tools
用于保存安装包。
/root/training
用于安装 JDK、Tomcat 等运行环境。
4. 复制 JDK
COPY jdk-8u144-linux-x64.tar.gz /root/tools
把构建上下文中的 JDK 安装包复制到镜像内部。
5. 解压 JDK
RUN tar zxvf /root/tools/jdk-8u144-linux-x64.tar.gz \\
-C /root/training
将 JDK 安装到:
/root/training
6. 设置 Java 环境变量
ENV JAVA_HOME /root/training/jdk1.8.0_144
ENV PATH $JAVA_HOME/bin:$PATH
这样后续构建步骤以及最终容器都能够直接找到 Java。
现代 Dockerfile 中也可以写成:
ENV JAVA_HOME=/root/training/jdk1.8.0_144
ENV PATH="${JAVA_HOME}/bin:${PATH}"
7. 获取 Tomcat
示例中使用:
ADD https://dlcdn.apache.org/tomcat/tomcat-8/v8.5.73/bin/apache-tomcat-8.5.73.tar.gz /root/tools
将 Tomcat 安装包放入:
/root/tools
8. 解压 Tomcat
RUN tar zxvf /root/tools/apache-tomcat-8.5.73.tar.gz \\
-C /root/training
最终 Tomcat 位于:
/root/training/apache-tomcat-8.5.73
9. 部署 Web 应用
COPY MyDemoWeb.war \\
/root/training/apache-tomcat-8.5.73/webapps
将 WAR 包放入 Tomcat:
webapps
目录中。
Tomcat 启动之后即可加载该 Web 应用。
10. 设置容器启动程序
ENTRYPOINT ["/root/training/apache-tomcat-8.5.73/bin/catalina.sh","run"]
这意味着使用该镜像创建容器时,默认启动:
catalina.sh run
从而让 Tomcat 以前台方式运行。
这点对 Docker 容器非常重要:容器需要一个持续运行的前台主进程。
十八、构建 Java Web 镜像
准备好:
Dockerfile
jdk-8u144-linux-x64.tar.gz
MyDemoWeb.war
之后,在当前目录执行:
docker build -t mywebapp .
注意最后仍然有:
.
构建成功后执行:
docker images
可以看到:
REPOSITORY TAG
mywebapp latest
十九、运行 Java Web 容器
使用刚刚生成的镜像:
docker run -d -p 8080:8080 mywebapp
这里的端口关系为:
宿主机 8080
↓
Docker 端口映射
↓
容器 8080
↓
Tomcat
↓
MyDemoWeb
然后通过:
http://服务器IP:8080/MyDemoWeb/
访问部署完成的 Java Web 应用。
二十、docker commit 和 Dockerfile 怎么选?
两种方式最终都能够生成 Docker 镜像,但思路有明显区别。
| 构建方式 | 基于已有容器 | 基于构建脚本 |
| 操作过程 | 手工修改容器 | 自动执行指令 |
| 可读性 | 较差 | 高 |
| 可复现性 | 较差 | 高 |
| 版本管理 | 不方便 | 非常方便 |
| 自动化构建 | 不适合 | 适合 |
| CI/CD | 不推荐 | 推荐 |
| 学习理解 | 直观 | 更接近实际开发 |
可以把两种方式理解为:
docker commit
= 先手工把环境配置好,再保存结果
Dockerfile
= 先把构建过程写下来,再让 Docker 自动执行
在实际项目中,优先选择:
Dockerfile + docker build
二十一、几个容易混淆的 Docker 命令
docker commit
把容器当前状态制作成镜像:
docker commit 容器ID 镜像名
docker build
根据 Dockerfile 构建镜像:
docker build -t 镜像名 .
docker run
根据镜像创建并启动容器:
docker run -d -p 宿主机端口:容器端口 镜像名
docker tag
给已有镜像增加新的名称或标签:
docker tag 原镜像 新镜像名:标签
docker push
把本地镜像上传到镜像仓库:
docker push 用户名/镜像名:标签
因此整个流程可以串起来理解:
┌─ docker commit ─┐
运行中的容器 ────┤ ├──→ Docker 镜像
└─────────────────┘
Dockerfile ── docker build ───────────→ Docker 镜像
│
├─ docker run → Docker 容器
│
└─ docker push → 镜像仓库
二十二、总结
Docker 自定义镜像构建需要重点掌握两条路线。
第一条:
基础镜像
→ 创建容器
→ 修改容器
→ docker commit
→ 新镜像
→ docker tag
→ docker push
第二条,也是实际开发中更重要的一条:
准备应用程序和运行环境
→ 编写 Dockerfile
→ docker build
→ 生成镜像
→ docker run
→ 启动容器
→ 对外提供服务
其中最需要掌握的是 Dockerfile 中几个核心指令:
FROM
RUN
COPY
ADD
ENV
WORKDIR
EXPOSE
CMD
ENTRYPOINT
同时要理解三个基本概念之间的关系:
Dockerfile:描述“镜像应该怎么构建”
↓
docker build
↓
Image:构建完成、可重复使用的运行模板
↓
docker run
↓
Container:镜像真正运行起来后的实例
掌握这条链路之后,再学习镜像仓库、容器编排以及 Kubernetes 时,很多概念就会自然衔接起来。
若有转载,请标明出处:
网硕互联帮助中心




评论前必须登录!
注册