构建镜像的核心文件 Dockerfile

Java技术迷

共 14802字,需浏览 30分钟

 ·

2022-07-17 02:50

点击关注公众号,Java干货及时送达


Java | 


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

通过Dockerfile我们能够将项目构建成一个镜像,这样的好处是可以将一个复杂的项目直接打包成一个镜像存储和运行。

Dockerfile提供了非常多的指令供我们操作,下面例举一些常用的命令:

指令作用
FROM指定当前镜像是基于哪个镜像的
RUN构建镜像时需要运行的指令
EXPOSE当前容器对外暴露的端口号
WORKDIR指定在创建容器后终端默认登录进来的工作目录
ENV用于在构建镜像过程中设置环境变量
ADD将宿主机目录下的文件拷贝到镜像
COPY拷贝文件和目录到镜像
VOLUME容器数据卷,用于数据持久化
CMD指定一个容器启动时要运行的命令
ENTRYPOINT指定一个容器启动时要运行的命令

FROM

该指令用于指定当前镜像是基于哪个镜像进行构建的,因为镜像的构建是一层一层进行的,Docker Server会先基于某个基础镜像将项目打包成一个新镜像,再基于这个新镜像继续打包,以此类推,不断打包构建,最终生成一个处理完成的镜像。

下面就来简单地使用一下FROM指令,在/opt目录下新建一个test目录,并创建Dockerfile文件,编写如下内容:

FROM centos

文件内容非常简单,此时我们就可以根据Dockerfile进行镜像构建了,执行指令:

docker build -t mycentos_test01 .

这里我们指定FROM为centos,Docker Server将基于centos镜像进行构建,当Docker中没有该镜像时,还会先将centos镜像拉取下来再进行构建, . 表示Dockerfile文件所在的位置为当前目录:

[root@localhost test]# docker build -t mycentos_test01 .
Sending build context to Docker daemon  2.048kB
Step 1/1 : FROM centos
latest: Pulling from library/centos
7a0437f04f83: Pull complete
Digest: sha256:5528e8b1b1719d34604c87e11dcd1c0a20bedf46e83b5632cdeac91b8c04efc1
Status: Downloaded newer image for centos:latest
 ---> 300e315adb2f
Successfully built 300e315adb2f
Successfully tagged mycentos_test01:latest

当指令执行成功后,就会生成一个新的镜像,可以使用 docker images 指令进行查看:

[root@localhost test]# docker images
REPOSITORY        TAG            IMAGE ID       CREATED        SIZE
centos            latest         300e315adb2f   7 months ago   209MB
mycentos_test01   latest         300e315adb2f   7 months ago   209MB

此时我们就可以启动这个镜像,同时进入交互终端:

docker run -it mycentos_test01
[root@31c51ab4d120 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

可以看到这就是一个新的centos,这是因为我们只使用了FROM指令基于centos镜像构建,所以得到的镜像仍然还是一个基础的centos镜像。

需要注意的是Dockerfile文件中的第一条指令必须是 FROM 。

RUN

该指令会将在当前镜像之上的新层中执行任何命令并提交结果,生成的镜像将用于下一步。

修改Dockerfile文件,内容如下:

FROM centos
RUN yum install -y vim

我们知道在没加RUN命令之前构建出来的镜像就是一个centos,但现在,构建出来的镜像就是一个包含vim编辑器的centos系统,马上来构建它:

docker build -t mycentos_test02 .

构建成功后就可以进行启动:

docker run -it mycentos_test02

可以检查一下镜像中是否真的已经包含了vim编辑器:

[root@46992a057ea4 /]# rpm -qa|grep vim
vim-minimal-8.0.1763-15.el8.x86_64
vim-common-8.0.1763-15.el8.x86_64
vim-enhanced-8.0.1763-15.el8.x86_64
vim-filesystem-8.0.1763-15.el8.noarch

RUN指令还支持以数组的方式传递需要执行的命令,比如:

RUN ["yum","install","-y","vim"]

EXPOSE

该指令用于指定构建的镜像在运行时为对外暴露的端口,只有向外暴露了端口,外部才能够访问到这个进镜像提供的服务。

修改Dockerfile文件:

FROM centos
RUN ["yum","install","-y","vim"]
EXPOSE 80

但事实上centos镜像添加端口并没有作用,因为它不像tomcat、mysql、redis那样会向外部主机提供某项服务,所以可以将基础镜像指定为tomcat:

FROM tomcat:8.0

此时构建镜像然后启动:

docker run -p 80:80 mytomcat_test01

CMD

该指令用于为启动的容器指定需要执行的命令,修改Dockerfile:

FROM centos
RUN ["yum","install","-y","vim"]
EXPOSE 80
CMD ["echo","hello"]

构建镜像:

docker build -t mycentos_test03

此时启动镜像:

docker run -it mycentos_test03

因为CMD指令的作用,所以在启动这个镜像的时候就会立马执行CMD中的命令,从而输出 hello 字符串:

[root@localhost test]# docker run -it mycentos_test03
hello

需要注意的是Dockerfile中只能有一条CMD命令,如果写出了多条CMD,则以最后一条的内容为准。

ENTRYPOINT

该指令与CMD类似,也是用于指定容器启动时需要执行的命令,修改Dockerfile文件:

FROM centos
RUN ["yum","install","-y","vim"]
EXPOSE 80
ENTRYPOINT ["echo","hello"]

构建镜像:

docker build -t mycentos_test04 .

启动镜像:

[root@localhost test]# docker run -it mycentos_test04
hello

可以看到ENTRYPOINT和CMD两个指令的效果是一样的,那么它俩有什么区别呢?其区别在于命令的覆盖方式,对于CMD指令,我们可以在启动镜像的时候直接拼写命令来覆盖它,比如:

[root@localhost test]# docker run -it mycentos_test03 echo hello centos
hello centos

此时在Dockerfile中配置的CMD命令将会被这里的 echo hello centos 覆盖掉,最终输出 hello cnetos 。

然而对于ENTRYPOINT,它是无法通过直接追加命令来覆盖的,而是需要用到一个参数:

[root@localhost test]# docker run --entrypoint="echo hello centos" mycentos_test04
hello centos

由此可见,ENTRYPOINT指令对于命令的覆盖是比CMD指令更加复杂的,为此,可以将那些基本固定不变的命令配置到ENTRYPOINT中,而将需要修改的命令配置到CMD中,比如:

FROM centos
RUN ["yum","install","-y","vim"]
EXPOSE 80
ENTRYPOINT ["echo"]
CMD ["hello centos"]

WORKDIR

该指令用于指定镜像的落脚点,即:启动镜像后初始进入的目录,若是没有配置,则默认是 / ,比如:

[root@localhost test]# docker run -it mycentos_test02
[root@9c958bd3830f /]#

启动镜像后首先进入的便是 / 目录,但如果配置了WORKDIR:

FROM centos
WORKDIR /opt/work

构建镜像:

docker build -t mycentos_test05 .

启动镜像:

[root@localhost test]# docker run -it mycentos_test05
[root@37f177c1e546 work]# pwd
/opt/work

需要注意,只要配置了WORKDIR,无论你有没有在后续的指令中使用到它,WORKDIR配置的目录是一定会在镜像中被创建的。

ENV

该指令用于为镜像设置环境变量,比如:

FROM centos
ENV name centos
WORKDIR /$name

在这里配置了一个环境变量name,值为centos,并在WORKDIR指令中引用了该变量,所以通过该文件构建出来的镜像在启动时就会直接进入/centos目录,验证一下:

[root@localhost test]# docker build -t mycentos_test06 .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM centos
 ---> 300e315adb2f
Step 2/3 : ENV name centos
 ---> Running in 4c272c71fc62
Removing intermediate container 4c272c71fc62
 ---> 0c00112be488
Step 3/3 : WORKDIR /$name
 ---> Running in 19bb2ad9d3cb
Removing intermediate container 19bb2ad9d3cb
 ---> d7405fa45cb0
Successfully built d7405fa45cb0
Successfully tagged mycentos_test06:latest
[root@localhost test]# docker run -it mycentos_test06
[root@0a505138f024 centos]# pwd
/centos

VOLUME

该指令用于定义容器运行时可以挂载到宿主机的目录,修改一下Dockerfile:

FROM centos
ENV name centos
WORKDIR /$name
VOLUME /$name

此时配置了VOLUME,值为 /$name ,则容器中的/centos目录就允许被外部挂载。

构建镜像:

docker build -t mycentos_test07 .

然后在启动镜像的同时挂载一下数据卷:

docker run -it -v /opt/centos:/centos mycentos_test07

这样两个目录就实现了共享,此时在/opt/centos目录下的操作都会被同步到容器中的/centos目录:

[root@localhost test]# cd /opt/centos/
[root@localhost centos]# touch test
[root@localhost centos]# docker run -it -v /opt/centos:/centos mycentos_test07
[root@37450c713b08 centos]# ls
test

ADD

该指令用于将context目录中指定的文件复制到镜像的指令目录中去,那么首先我们就要知道何为context目录。

context目录意为上下文目录,指的是Dockerfile文件所在的目录,比如:

[root@localhost opt]# ll
total 262144
drwx--x--x. 4 root  root         28 Jun 18 11:27 containerd
-rw-r--r--. 1 root  root  268435456 Jun 23 15:46 disk.bin
-rw-r--r--. 1 root  root          0 Jul 23 07:08 Dockerfile
drwxr-xr-x. 3 root  root         25 Jul 22 12:45 javaproject
drwxr-xr-x. 8 10143 10143       273 Apr  7 19:26 jdk1.8.0_291
drwxr-xr-x. 2 root  root         24 Jul 23 07:04 test
drwxr-xr-x. 8 root  root        160 Jul  5 12:52 zookeeper-3.5.7

若是此时Dockerfile文件处在/opt目录下,则context目录则为/opt目录,因为DockerServer会将context目录中的所有文件,包括子目录及其子文件进行打包构建,所以这也是为什么我们一开始创建一个空的文件夹,并将Dockerfile放在其中,因为文件过多会导致构建速度变慢。

修改Dockerfile:

FROM centos
ENV name centos
WORKDIR /$name
VOLUME /$name
ADD test.txt /

在Dockerfile所在目录下有一个test.txt,现在要将其复制到容器中的 / 目录下,构建镜像:

docker build -t mycentos_test08 .

启动镜像:

[root@localhost test]# docker run -it mycentos_test08
[root@184a8824b583 centos]# cd /
[root@184a8824b583 /]# ls -l | grep test
-rw-r--r--.  1 root root   0 Jul 23 07:25 test.txt

若是想修改添加到容器后的文件名,则直接指定名字即可:

ADD test.txt /ntest.txt

ADD不仅能够添加文件、目录,还能够添加一个url,比如:

ADD http://www.baidu.com /test.html

则启动构建后的镜像时,便会在 / 目录下创建test.html文件,并将访问http://www.baidu.com所得到的响应结果写入该文件。

构建一个SpringBoot应用的镜像

大概了解Dockerfile中的一些指令之后,我们就可以通过它来构建一个SpringBoot应用的镜像了,所以首先来编写一个非常简单的SpringBoot应用:

@RestController
public class TestController {
    
    @GetMapping("/testMethod")
    public String testMethod(){
        return "success";
    }
}

创建一个控制器用于测试即可,然后将应用打成一个jar包,使用maven插件即可进行打包,指令如下:

mvn clean package

将得到的jar包上传至服务器:

[root@localhost ~]# cd /opt/test/
[root@localhost test]# ls -l | grep .jar
-rw-r--r--. 1 root root 16568766 Jul 23 07:56 SpringBootDemo-0.0.1-SNAPSHOT.jar

编写Dockerfile:

FROM openjdk:8 # 指定基础镜像
WORKDIR /test # 指定工作目录
ADD SpringBootDemo-0.0.1-SNAPSHOT.jar /test # 将jar包复制到容器中
EXPOSE 8080 # 向外暴露8080端口
ENTRYPOINT ["java","-jar"]
CMD ["SpringBootDemo-0.0.1-SNAPSHOT.jar"]

因为运行jar包需要JDK环境,所以直接使用openjdk8作为基础镜像即可,然后指定工作目录,接着将jar包复制到容器里的工作目录中,并向外暴露8080端口,最后使用ENTRYPOINT和CMD指令联合组成jar包的运行命令。

这样就可以构建镜像了:

docker build -t springbootdemo .

运行镜像:

docker run -p 8080:8080 springbootdemo

镜像启动后会自动运行jar包,此时外部主机就可以访问到pringBoot应用了:

img

上传镜像到服务器

这里以阿里云为例,我们将刚才构建的镜像上传至阿里云的镜像服务器中,首先登录阿里云,搜索 容器镜像服务 并开通:

img

开通后进入管理控制台,点击左侧的命名空间,创建一个命名空间:

img

然后点击左侧的镜像仓库,创建一个镜像仓库:

img

选择刚刚创建的命名空间,并填写仓库名称:

img

点击下一步后选择本地仓库:

img

点击创建镜像仓库,完成后可以看到阿里云提供的操作指南,我们照着操作指南进行操作即可。

首先登录到阿里云:

docker login --username=取个名字好困难a registry.cn-hangzhou.aliyuncs.com

然后会要求输入密码,密码在开通容器镜像服务的时候就会让你设置,如果没有设置密码,也可以在页面右侧的新手引导中点击访问凭证进行设置:

img

然后为镜像设置tag:

docker tag 8d4d7be1d926 registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo:1.0.0

这里有两处地方需要设置(ImageId和版本号):

docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo:[镜像版本号]

其中ImageId为镜像的id,通过 docker images 即可查询到,镜像版本号可以随意填写。

最后将镜像推送到服务器上:

docker push registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo:1.0.0

此时点击镜像版本查看一下镜像是否成功上传了:

img

接下来测试一下拉取功能,首先删除掉之前构建的镜像:

docker rmi springbootdemo

执行指令进行拉取:

docker push registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo:1.0.0
[root@localhost ~]# docker images
REPOSITORY                                                          TAG            IMAGE ID       CREATED             SIZE
registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo   1.0.0          8d4d7be1d926   41 minutes ago      531MB
openjdk                                                             8              f67a59e543c1   19 hours ago        514MB
centos                                                              latest         300e315adb2f   7 months ago        209MB
mycentos_test01                                                     latest         300e315adb2f   7 months ago        209MB
tomcat                                                              8.0            ef6a7c98d192   2 years ago         356MB

拉取下来的镜像就可以启动了,只不过镜像的名字变得比较长了:

docker run -p 8080:8080 registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo:1.0.0

本文作者:汪伟俊 为Java技术迷专栏作者 投稿,未经允许请勿转载!

    

1、拖动文件就能触发7-Zip安全漏洞,波及所有版本

2、进程切换的本质是什么?

3、一次 SQL 查询优化原理分析:900W+ 数据,从 17s 到 300ms

4、Redis数据结构为什么既省内存又高效?

5、IntelliJ IDEA快捷键大全 + 动图演示

6、全球第三浏览器,封杀中国用户这种操作!(文末送书)

浏览 27
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报