docker系列11:Dockerfile入门

传送门

docker系列1:docker安装

docker系列2:阿里云镜像加速器

docker系列3:docker镜像基本命令

docker系列4:docker容器基本命令

docker系列5:docker安装nginx

docker系列6:docker安装redis

docker系列7:docker安装ES

docker系列8:容器卷挂载(上)

docker系列9:容器卷挂载(下)

docker系列10:Dockerfile挂载容器卷

通过Dockerfile文件挂载容器卷回顾

在上一节中介绍了Dockerfile挂载容器卷,其中的Dockerfile文件如下:

bash 复制代码
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol

该命令的大概意思就是:

  • FROM表示,从标准的ubuntu镜像为准
  • RUN表示执行shell命令,创建一个文件目录/myvol
  • 执行命令,打印"hello world"字符串到greeting文件
  • 最后VOLUME,就是这节要讲到卷挂载!

然后通过Dockerfile生成了一个镜像:

bash 复制代码
docker build -t getting-started .

并且成功运行该镜像,最后成功实现了容器卷的挂载!

那么**什么是Dockerfile文件?Dockerfile的有哪些指令?Dockerfile的编写有哪些最佳实践?**接下来就带着这些疑问来探索一下

什么是Dockerfile

要知道Dockerfile是什么,从官网的定义来看看:

Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.

Docker可以通过读取Dockerfile中的指令自动构建镜像。Dockerfile是一个文本文档,其中包含用户可以在命令行上调用的所有命令来组装镜像。

简单来说,可以通过Dockerfile文件来生成Docker镜像!首先Dockerfile文件是一个纯文本,然后在里面编写了一系列的指令,比如选择基础镜像(FROM)、拷贝文件(COPY)、运行脚本(RUN)等等,Docker 顺序执行这个文件里的所有步骤,最后就会创建出一个新的镜像出来。
https://docs.docker.com/build/guide/layers/

从上面的流程可以看出,左边用户编写的Dockerfile文件通过Docker构建之后,成为了右边的镜像。这个镜像跟官方镜像无本质区别(前面从DockerHub上安装的nginx、redis),称为"民间"、"草根"镜像。

镜像分类

DockerHub上可以看到对镜像的分类大致有3种:

  • Docker官方镜像(Docker Official Image)
  • 认证镜像(Verified Publisher)
  • 非官方镜像(Sponsored OSS)

官方镜像

像上面提到的nginx、redis都属于官方镜像!

这种镜像都会带上官方构建的标签,属于Docker公司提供的**"极品"**镜像,不仅质量上乘(有专门的团队负责审核、发布和更新),而且安全性也有保障(经过了严格的漏洞扫描和安全检测),并且也是标准的Dockerfile编写范例,是使用的首选!

认证镜像

认证镜像由Docker认证的出版商提供的高质量镜像。这些产品由商业公司直接发布和维护,比如 Bitnami、Rancher、Ubuntu 等。

这种由大公司**"出品"**的镜像,一般来说也具有相当高的质量,是不错选择。不过由于Docker认证需要收钱,店大欺客:

因为成为"Verified publisher"是要给 Docker 公司交钱的,而很多公司不想花这笔"冤枉钱",所以只在 Docker Hub 上开了公司账号,但并不加入认证。

--引自"虚伪"的 Docker 开始清退开源组织,不付费就删除所有镜像!|插件|云原生|存储库|应用程序|docker_网易订阅

所以有些不交钱认证的公司的镜像就被降级为"非官方镜像",比如OpenResty:

非官方镜像

队了上面2种,剩下的就是**非官方镜像,**也戏称为"民间"镜像!

对于这些镜像的选择,下载量是一个重要的参考标准。 这个时候就是韩信点兵,多多益善了!

这里面也包括我们自己的镜像,如果推送的DockerHub,也是归于这一类镜像中,后面会单开一节来讨论如果上传。

Dockfile有哪些指令

Dockerfile文件命名

在上面的例子中,通过命令docker build来构建的镜像。这是因为省略了-f参数,默认在当前目录下查看名字为Dockerfile的文件:

Specifies the filepath of the Dockerfile to use. If unspecified, a file named Dockerfile at the root of the build context is used by default.

所以一般对于编写的Dockerfile默认使用Dockerfile这个名字,如果要用其它名字,在build时用-f来指定!

指令详解

Dockerfile支持的指令不少,比如例子里面提到的FROM、RUN、VOLUME,更多的可以查看Dockerfile指令

FROM

有效的Dockerfile必须以FROM指令开头,命令格式为:

bash 复制代码
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

表示选择构建使用的基础镜像,比如ubuntu镜像。其中--platform用在多平台镜像中,一般不用指定:

The optional --platform flag can be used to specify the platform of the image in case FROM references a multi-platform image. For example, linux/amd64, linux/arm64, or windows/amd64. By default, the target platform of the build request is used.

可选的--Platform标志可用于指定镜像的平台,以防FROM引用多平台镜像。例如,linux/amd64、linux/arm64或windows/amd64。默认情况下,使用构建请求的目标平台。

也可以指定镜像对应的tag,比如nginx的Dockerfile,选择debian操作系统,版本为bookworm-slim:

RUN

Run指令可以说是Dockerfile 里最重要的一个指令了 ,它可以执行任意的 Shell 命令它也是Dockerfile里面最复杂的一条指令了,这取决于对于shell的编写。比如例子里面的简单shell命令:

bash 复制代码
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
  • 创建一个文件夹
  • 打印"hello world"到文件greeting文件中

也可以特别复杂,比如nginx的RUN命令:

这整个就是一个复杂的shell脚本,因为太长了(RUN只能执行一行命令),所以采用续行符 \,命令之间也会用 && 来连接,这样保证在逻辑上是一行,如果写错了,build的时候会报错:

CMD

CMD指令设置从镜像运行容器时要执行的命令,格式为:

  • CMD ["executable","param1","param2"] (exec form)
  • CMD ["param1","param2"] (exec form, as default parameters to ENTRYPOINT)
  • CMD command param1 param2 (shell form)

比如,在容器运行之后,打印出当前目录,修改Dockerfile文件,ls -al

bash 复制代码
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
CMD ["ls", "-al"]
VOLUME /myvol

构建并运行容器,之后就会执行 ls -al命令:

Dockerfile中只能有一条CMD指令。如果列出多个CMD,则只有最后一个生效。这一点在官网上有明确说明:

There can only be one CMD instruction in a Dockerfile. If you list more than one CMD, only the last one takes effect.

而且官网上也对RUN与CMD的区别做了说明:

Don't confuse RUN with CMD. RUN actually runs a command and commits the result; CMD doesn't execute anything at build time, but specifies the intended command for the image.

不要混淆RUN和CMD。RUN实际上运行一个命令并提交结果;CMD在构建时不执行任何东西,但指定镜像的预期命令。
图片来自: https://docs.docker.com/guides/docker-overview/

VOLUME

这个卷挂载命令在前面已经讨论过了,这里就不赘述了。

COPY

COPY指令顾名思义,就是复制命令,有点类似于cp命名,格式如下:

bash 复制代码
COPY [OPTIONS] <src> ... <dest>
COPY [OPTIONS] ["<src>", ... "<dest>"]

其中src表示源文件,dest表示目标文件。比如上面的例子中,在当前目录下建一个test_cp.txt文件,并打包进镜像中:

bash 复制代码
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
COPY ./test_cp.txt /tmp/test_cp.txt
VOLUME /myvol

然后重新构建并运行,发现构建成功之后,运行对应的镜像,容器中已经有了对应的文件,证明复制成功!

不过这里要注意的是, 拷贝的源文件必须是"**构建上下文"**路径里的,不能随意指定文件,甚至连绝对路径也不行。

如果要从本机向镜像复制文件,还要把这些文件放到一个专门的目录,然后在 docker build 里指定"构建上下文" 到这个目录才行。在这里例子中,"构建上下文"为当前目录.

所以如果指定当前"构建上下文"为绝对路径:/root/test-docker

bash 复制代码
docker build -f Dockerfile_copy -t greetting_stared_cp3 /root/test-docker

Dockerfile里面COPY改为相对路径是可以的。由此得出,COPY只能基于相对路径来操作!

这个原理呢,其实就要从docker build的说起了,跟Dockerfile没有多大关系,后面再讨论。

WORKDIR

WORKDIR指令是设置工作目录,格式为:

bash 复制代码
WORKDIR /path/to/workdir

WORKDIR指令为Dockerfile中跟随它的任何RUN、CMD、ENTRYPOINT、COPY和ADD指令设置工作目录,默认为"/"目录下。 比如创建一个Dockerfile文件内容如下:

bash 复制代码
FROM ubuntu
WORKDIR /usr
WORKDIR share
WORKDIR bug
RUN pwd > greeting

然后打包运行看下效果:

并且细心的观察就会发现,这个进入容器的运行目录,正是刚才通过WORKDIR设置的/usr/share/bug!所以WORKDIR也代表设置的容器运行的目录,这个通过退出容器再进去也能证实这一点:

在官方推荐指定这个WORKDIR的:

Therefore, to avoid unintended operations in unknown directories, it's best practice to set your WORKDIR explicitly.

--因此,为了避免未知目录中的意外操作,最好的做法是显式设置WORKDIR。

EXPOSE

EXPOSE指令通知Docker容器在运行时侦听指定的网络端口,您可以指定端口侦听TCP还是UDP,如果不指定协议,则默认为TCP,格式为:

bash 复制代码
EXPOSE <port> [<port>/<protocol>...]

比如在docker系列7:docker安装ES 里面运行容器的命令里面通过-op指定了端口映射:

bash 复制代码
$ docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch

这个命令里面,-p表示容器对外暴露9200端口与主机的9200端口映射。但是如果Dockerfile里面也可以达到这种效果:

bash 复制代码
EXPOSE 9200

对外暴露9200端口,默认是TCP协议。如果要支持UDP协议,要指定协议类型:

bash 复制代码
EXPOSE 9200/udp

或者一块暴露tcp、udp,要写2条指令:

bash 复制代码
EXPOSE 9200/tcp
EXPOSE 9200/udp

无论EXPOSE设置如何,都可以在运行时使用-p标志覆盖。这是因为EXPOSE在Dockerfile中,一般运行时要结合真实宿主机的情况来映射端口,所以一般来说EXPOSE更多的是一种文档化的指引,告诉用户哪个服务暴露哪个端口。比如nginx的Dockerfile中,EXPOSE了80,但是真实使用的时候还是要通过-p来指定:

ENV

ENV指令用于设置环境变量,格式为:

bash 复制代码
ENV <key>=<value> ...

可以一次性设置多个变量:

bash 复制代码
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy
ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
    MY_CAT=fluffy

然后就可以在Dockerfile中引用这个变量了:

bash 复制代码
FROM ubuntu
ENV MY_NAME=DOCKER_ENV_TEST
RUN mkdir /myvol
RUN echo $MY_NAME > /myvol/greeting
VOLUME /myvol

然后打包运行看下效果:

通过ENV这种方式设置变量,官方并不推荐,就是因为在镜像里面也会生效,而不是仅仅在Docker构建过程中生效,所以一般推荐ARG:

If an environment variableis only needed during build, and not in the final image, consider setting a value for a single command instead.

Or using ARG, which is not persisted in the final image

ARG

对于ARG指令,刚才已经谈到了,用法跟ENV相似,区别就是在于 ARG 创建的变量只在镜像构建过程中可见,容器运行时不可见,而 ENV 创建的变量不仅能够在构建镜像的过程中使用,在容器运行时也能够以环境变量的形式被应用程序使用。所以可通过ARG 自行查看!

安全性问题

Dockerfile里面明确提到了,不建议使用变量来传递敏感数据,这又是另外一个话题了

MAINTAINER

MAINTAINER指令设置生成镜像的作者字段,格式为:

bash 复制代码
MAINTAINER <name>

比如在上面的Dockerfile增加作者信息:

总结

图片来自: 百度安全验证

命令式与声明式

上面介绍了Dockerfile文件与对应的指令,通过编写Dockerfile文件一步步的指定docker构建要执行的命令,"告诉"计算机每步该做什么,所有的步骤都列清楚,这样程序才能够一步步走下去,最后完成任务。这种就是命令式的文件,与之相反Kubernetes编写的YAML文件,就是**"声明式"**的:

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: ngx-pod
  labels:
    env: demo
    owner: chrono

spec:
  containers:
  - image: nginx:alpine
    name: ngx
    ports:
    - containerPort: 80

**"声明式"**不关心具体的过程,更注重结果(是不是有点耳熟?)

关于镜像的上传

在下一节,会尝试打包自己的镜像并推送到镜像仓库中,并用Dockerfile文件编写打包自己的java程序!

相关推荐
小锋学长生活大爆炸31 分钟前
【教程】Docker更换存储位置
运维·docker·容器
THMAIL6 小时前
mac M芯片运行docker-desktop异常问题
macos·docker·容器
两点王爷8 小时前
IDEA中springboot项目中连接docker
spring boot·docker·intellij-idea
家庭云计算专家8 小时前
还没用过智能文档编辑器吗?带有AI插件的ONLYOFFICE介绍
服务器·人工智能·docker·容器·编辑器
孤的心了不冷10 小时前
【Docker】CentOS 8.2 安装Docker教程
linux·运维·docker·容器·eureka·centos
头疼的程序员11 小时前
docker学习与使用(概念、镜像、容器、数据卷、dockerfile等)
学习·docker·容器
IT小郭.11 小时前
使用 Docker Desktop 安装 Neo4j 知识图谱
windows·python·sql·docker·知识图谱·database·neo4j
淡水猫.11 小时前
hbit资产收集工具Docker(笔记版)
运维·docker·容器
旧故新长20 小时前
访问 Docker 官方镜像源(包括代理)全部被“重置连接”或超时
运维·docker·容器
white.tie20 小时前
Docker部署单节点Elasticsearch
elasticsearch·docker·jenkins