Docker 与 K8S学习笔记(四)—— Dockerfile的编写

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 在上一篇中我们简单介绍了Docker镜像的获取与使用,其中在镜像制作中提到在实际使用中一定要用Dockerfile方式去创建镜像而不要用docker commit方式,那么我们该如何编写Dockerfile呢,在写Dockerfile时又有那些注意点呢?今天我们就来一起学习Dockerfile的编写

Docker 与 K8S学习笔记(四)—— Dockerfile的编写


在上一篇中我们简单介绍了Docker镜像的获取与使用,其中在镜像制作中提到在实际使用中一定要用Dockerfile方式去创建镜像而不要用docker commit方式,那么我们该如何编写Dockerfile呢,在写Dockerfile时又有那些注意点呢?今天我们就来一起学习Dockerfile的编写。

 

一、什么是Dockerfile?


Dockerfile 是一个用来构建镜像的文本文件,其内容包含了一条条构建镜像所需的指令和说明。

 

二、从一个简单的例子开始


1、制作一个JDK镜像


我们首先通过制作一个简单的JDK镜像来感受Dockerfile的魅力,既然要制作JDK镜像,我们首先需要准备好需要的安装的JDK安装包,这里我们使用jdk-8u231-linux-x64.tar.gz,接下来就是Dockerfile的编写了:


# 声明所使用的基础镜像
FROM ubuntu
# 指定构建镜像时的工作目录,后续命令都是基于此目录的,如果不存在则创建
WORKDIR /opt/soft/jdk
# 将jdk包复制并解压到/opt/soft/jdk目录下
ADD jdk-8u231-linux-x64.tar.gz /opt/soft/jdk/
# 设置环境变量
ENV JAVA_HOME=/opt/soft/jdk/jdk1.8.0_231
ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH=$JAVA_HOME/bin:$PATH


ok,这样我们Dockerfile就编写完毕了,其中涉及到的指令含义后续会专门讲解,大家暂时不需要纠结。我们通过docker build命令制作镜像。


$ sudo docker build -t ubuntu-jdk .
Sending build context to Docker daemon  194.2MB
Step 1/6 : FROM ubuntu
latest: Pulling from library/ubuntu
35807b77a593: Pull complete
Digest: sha256:9d6a8699fb5c9c39cf08a0871bd6219f0400981c570894cd8cbea30d3424a31f
Status: Downloaded newer image for ubuntu:latest
 ---> fb52e22af1b0
Step 2/6 : WORKDIR /opt/soft/jdk
 ---> Running in 1526a2a25872
Removing intermediate container 1526a2a25872
 ---> e5b5ee6e0f89
Step 3/6 : ADD jdk-8u231-linux-x64.tar.gz /opt/soft/jdk/
 ---> f22f968c43cd
Step 4/6 : ENV JAVA_HOME=/opt/soft/jdk/jdk1.8.0_231
 ---> Running in a71c57c44b12
Removing intermediate container a71c57c44b12
 ---> 876227810405
Step 5/6 : ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
 ---> Running in 8ff7aefbc820
Removing intermediate container 8ff7aefbc820
 ---> f04605d6e9c2
Step 6/6 : ENV PATH=$JAVA_HOME/bin:$PATH
 ---> Running in 88f21ce05cc4
Removing intermediate container 88f21ce05cc4
 ---> cb70bf70e1e9
Successfully built cb70bf70e1e9
Successfully tagged ubuntu-jdk:latest


我们先来学习下上面执行的docker build命令,其中 -t 将镜像重新命名为ubuntu-jdk,命令末尾的 “.”指明docker context是当前目录,Docker默认是从docker context中查找Dockerfile,所以一般情况下Dockerfile文件名我们是不需要修改的,如果我们修改了Dockerfile文件名,那一定记得使用 -f 参数去指定要使用的Dockerfile。


接下来我们分析下Docker镜像的创建过程:


1)Step 1:执行FROM,将ubuntu作为基础镜像,这里将tag为latest的ubuntu镜像拉取下来,镜像ID为fb52e22af1b0


2)Step 2:执行WORKDIR,设置工作目录,这里其实是首先启动ID为fb52e22af1b0的临时容器,然后在其中创建/opt/soft/jdk目录,创建完毕后删除此临时容器,并将此容器保存为ID是e5b5ee6e0f89的镜像。


3)Step 3:执行ADD,将jdk-8u231-linux-x64.tar.gz拷贝并解压到/opt/soft/jdk目录下,并保存为ID是f22f968c43cd的镜像。


4)Step 4 ~ Step 6:执行ENV,先开启临时容器,然后设置环境变量,最后保存为镜像。


5)最终镜像构建成功,镜像ID为cb70bf70e1e9,tag为ubuntu-jdk:latest。


2、查看镜像分层结构


大家看到上面是不是很懵逼,我明明只要制作一个镜像,怎么在执行Dockerfile时还创建了那么多镜像呢?其实在Dockerfile中,它的每条指令都会创建一个镜像层,执行操作后再将此镜像层保存。我们通过docker history可以很清楚看到这一点:


$ sudo docker history ubuntu-jdk
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
cb70bf70e1e9   22 minutes ago   /bin/sh -c #(nop)  ENV PATH=/opt/soft/jdk/jd…   0B
f04605d6e9c2   22 minutes ago   /bin/sh -c #(nop)  ENV CLASSPATH=.:/opt/soft…   0B
876227810405   22 minutes ago   /bin/sh -c #(nop)  ENV JAVA_HOME=/opt/soft/j…   0B
f22f968c43cd   22 minutes ago   /bin/sh -c #(nop) ADD file:610ae1ffb70fff692…   403MB
e5b5ee6e0f89   22 minutes ago   /bin/sh -c #(nop) WORKDIR /opt/soft/jdk         0B
fb52e22af1b0   12 days ago      /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>      12 days ago      /bin/sh -c #(nop) ADD file:d2abf27fe2e8b0b5f…   72.8MB


$ sudo  docker history ubuntu
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
fb52e22af1b0   12 days ago   /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>      12 days ago   /bin/sh -c #(nop) ADD file:d2abf27fe2e8b0b5f…   72.8MB


我们可以看到ubuntu-jdk相比于ubuntu镜像多了很多层,这些层就是我们在执行WORKDIR、ADD、ENV指令时产生的。细心的同学可能发现了:既然我们在构建jdk镜像时创建了那么多镜像,那为什么通过docker image ls 命令只能看到基础镜像和jdk镜像呢?


$ sudo docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
ubuntu-jdk   latest    cb70bf70e1e9   39 minutes ago   476MB
ubuntu       latest    fb52e22af1b0   12 days ago      72.8MB
ubuntu       18.04     39a8cfeef173   6 weeks ago      63.1MB
nginx        1.21.1    08b152afcfae   7 weeks ago      133MB


这里需要大家注意我们构建jdk镜像时产生的那些镜像都是中间镜像,这类镜像会被别的镜像所依赖,如果想看到这些镜像,我们要使用docker image ls -a命令:


$ sudo docker image ls -a
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
<none>       <none>    f04605d6e9c2   42 minutes ago   476MB
ubuntu-jdk   latest    cb70bf70e1e9   42 minutes ago   476MB
<none>       <none>    876227810405   42 minutes ago   476MB
<none>       <none>    f22f968c43cd   42 minutes ago   476MB
<none>       <none>    e5b5ee6e0f89   42 minutes ago   72.8MB
ubuntu       latest    fb52e22af1b0   12 days ago      72.8MB
ubuntu       18.04     39a8cfeef173   6 weeks ago      63.1MB
nginx        1.21.1    08b152afcfae   7 weeks ago      133MB


3、镜像缓存特性


我们在制作jdk镜像时docker创建了多个中间层镜像,这些镜像一般都是会被重复利用的,无需重新构建,这样便能提升镜像构建效率,为了验证这一点,我们简单修改下我们的Dockerfile,我们在最后加一句java文件生成的操作:

FROM ubuntu
WORKDIR /opt/soft/jdk
ADD jdk-8u231-linux-x64.tar.gz /opt/soft/jdk/
ENV JAVA_HOME=/opt/soft/jdk/jdk1.8.0_231
ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH=$JAVA_HOME/bin:$PATH
RUN touch test.java

接着我们构建镜像:


$ sudo docker build -t ubuntu-jdk-2 .
Sending build context to Docker daemon  194.2MB
Step 1/7 : FROM ubuntu
 ---> fb52e22af1b0
Step 2/7 : WORKDIR /opt/soft/jdk
 ---> Using cache
 ---> e5b5ee6e0f89
Step 3/7 : ADD jdk-8u231-linux-x64.tar.gz /opt/soft/jdk/
 ---> Using cache
 ---> f22f968c43cd
Step 4/7 : ENV JAVA_HOME=/opt/soft/jdk/jdk1.8.0_231
 ---> Using cache
 ---> 876227810405
Step 5/7 : ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
 ---> Using cache
 ---> f04605d6e9c2
Step 6/7 : ENV PATH=$JAVA_HOME/bin:$PATH
 ---> Using cache
 ---> cb70bf70e1e9
Step 7/7 : RUN touch test.java
 ---> Running in e5f462e9c451
Removing intermediate container e5f462e9c451
 ---> 7dc9fe245160
Successfully built 7dc9fe245160
Successfully tagged ubuntu-jdk-2:latest


大家可以很明显看出来,在构建过程中第1~6步都是用了镜像缓存,那我们能不能不使用缓存呢?当然可以我们只需要在构建时加入参数--no-cache即可:


$ sudo docker build --no-cache -t ubuntu-jdk-3 .
Sending build context to Docker daemon  194.2MB
Step 1/7 : FROM ubuntu
 ---> fb52e22af1b0
Step 2/7 : WORKDIR /opt/soft/jdk
 ---> Running in 8560b572d2ac
Removing intermediate container 8560b572d2ac
 ---> 7cecd9874b7c
Step 3/7 : ADD jdk-8u231-linux-x64.tar.gz /opt/soft/jdk/
 ---> 01d1539300e6
Step 4/7 : ENV JAVA_HOME=/opt/soft/jdk/jdk1.8.0_231
 ---> Running in daa99a7adfe0
Removing intermediate container daa99a7adfe0
 ---> 16e8e58ff40b
Step 5/7 : ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
 ---> Running in e9d598aa1cd3
Removing intermediate container e9d598aa1cd3
 ---> 868daae44996
Step 6/7 : ENV PATH=$JAVA_HOME/bin:$PATH
 ---> Running in e13199a0ba85
Removing intermediate container e13199a0ba85
 ---> 0e9732a41c0e
Step 7/7 : RUN touch test.java
 ---> Running in 90d76edd935d
Removing intermediate container 90d76edd935d
 ---> 2c73ecf1af57
Successfully built 2c73ecf1af57
Successfully tagged ubuntu-jdk-3:latest


三、常用的Dockerfile指令:


Docker为了方便我们制作镜像,提供了多种Dockerfile指令,接下来我们一起看看这些指令都是什么含义:


  • FORM:指定基础镜像;
  • MAINTAINER:设置镜像作者;
  • WORKDIR:设置构建镜像的工作目录,如果目录不存在则自动创建;
  • COPY:将文件从docker context拷贝到镜像;
  • ADD:与COPY类似,都是将文件从docker context拷贝到镜像,不同的是当文件是归档类型(tar、tar.gz、zip等)时,会自动解压到目标路径;
  • ENV:设置环境变量,并可被后面的指令使用;
  • EXPOSE:指定容器中进程会监听的端口,docker可将该端口暴露出来;
  • VOLUME:将文件或目录设置为volume;
  • RUN:在容器中运行指定的指令;
  • CMD:设置容器启动时运行的指令,当设置多条CMD指令时,只有最后一条生效;
  • ENTRYPOINT:设置容器启动时运行的命令,当设置多条ENTRYPOINT时,只有最后一条生效。

 

PS:RUN、CMD和ENTRYPOINT的区别:


1、RUN

执行命令并创建新的镜像层,通常用于镜像构建中软件包安装等操作。


2、CMD

为容器指定默认的启动执行命令,此命令会在容器启动且docker run没有指定其他命令时运行,也就是说CMD中的命令是可以在docker run中被其他命令所覆盖的。


3、ENTRYPOINT

和CMD很像,不同的是ENTRYPOINT指定的命令一定会被执行,即使docker run中指定了其他命令,这也就意味着ENTRYPOINT可以用于让容器以应用程序或服务的形式运行。

好了,以上就是本文的内容,关于Dockerfile的其它指令后续会在各个实践案例中逐步使用。

 

分类: 容器技术

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
11天前
|
Kubernetes Java Docker
利用(K8S)配合Docker部署jar包
通过Docker打包并部署到Kubernetes(K8S)集群的过程。首先,通过SpringBoot生成jar包,接着在K8S环境中创建并编辑Dockerfile文件。随后构建Docker镜像,并将其推送到镜像仓库。最后,通过一系列kubectl命令(如get pods、get svc、logs等),展示了如何在K8S中管理应用,包括查看Pod状态、服务信息、Pod日志以及重启Pod等操作。
59 2
|
9天前
|
应用服务中间件 nginx Docker
Docker镜像-基于DockerFile制作编译版nginx镜像
这篇文章介绍了如何基于Dockerfile制作一个编译版的nginx镜像,并提供了详细的步骤和命令。
84 17
Docker镜像-基于DockerFile制作编译版nginx镜像
|
9天前
|
Ubuntu Linux pouch
Docker容器管理工具
文章介绍了Docker容器管理工具,以及早期使用的LXC容器管理工具,包括它们的安装、使用和相关技术特点。
38 10
Docker容器管理工具
|
9天前
|
应用服务中间件 Linux nginx
Docker镜像-基于DockerFile制作yum版nginx镜像
本文介绍了如何使用Dockerfile制作一个基于CentOS 7.6.1810的yum版nginx镜像,并提供了详细的步骤和命令。
51 20
|
6天前
|
Cloud Native 持续交付 Docker
云原生技术实践:Docker容器化部署教程
【9月更文挑战第4天】本文将引导你了解如何利用Docker这一云原生技术的核心工具,实现应用的容器化部署。文章不仅提供了详细的步骤和代码示例,还深入探讨了云原生技术背后的哲学,帮助你理解为何容器化在现代软件开发中变得如此重要,并指导你如何在实际操作中运用这些知识。
|
11天前
|
Kubernetes Cloud Native 开发者
探索云原生技术:从Docker到Kubernetes的旅程
【8月更文挑战第31天】云原生技术正在改变软件开发、部署和运维的方式。本文将带你了解云原生的核心概念,并通过实际代码示例,展示如何使用Docker容器化应用,并进一步通过Kubernetes进行集群管理。我们将一起构建一个简单的微服务架构,体验云原生带来的高效与便捷。
|
11天前
|
Kubernetes Cloud Native 云计算
云原生入门:从Docker到Kubernetes的旅程
【8月更文挑战第31天】 在数字化转型的浪潮中,云原生技术成为推动现代软件开发的关键力量。本文将引导读者理解云原生的基本概念,通过Docker和Kubernetes的实际应用示例,展示如何在云平台上部署和管理容器化应用。我们将一起探索服务编排、持续集成和微服务架构的实践之路,旨在为初学者揭开云原生技术的神秘面纱,并激发对这一前沿领域的深入探索。
|
Kubernetes 负载均衡 Linux
Docker从入门到掉坑(四) 国内搭建k8s避坑指南
Docker从入门到掉坑(四) 国内搭建k8s避坑指南
411 0
|
存储 JSON Kubernetes
Docker从入门到掉坑(五):继续挖一挖 k8s
Docker从入门到掉坑(五):继续挖一挖 k8s
Docker从入门到掉坑(五):继续挖一挖 k8s
|
Kubernetes 负载均衡 Linux
Docker从入门到掉坑(四):上手k8s避坑指南
Docker从入门到掉坑(四):上手k8s避坑指南
Docker从入门到掉坑(四):上手k8s避坑指南