简明教程 | Docker篇 · 其二:Dockerfile的编写
Dockerfile是什么
一个包含用于组合 image 的命令的文本文件,docker 通过 dockerfile 和构建环境的上下文来构建 image 。
编写Dockerfile
FROM
首先,我们必须用 FROM
指定一个基础image,然后后续的指令会运行在该image上
FROM [--platform=<platform>] <image>[:<tag>] [AS <名称>]
FROM [--platform=<platform>] <image>[:@<digest>] [AS <名称>]
示例:
FROM redis:5.0.12
LABEL
然后让我们添加维护者的基本信息
MAINTAINER <name>
不过要注意的是该命令已经被标记为deprecated,所以我们最好用 LABEl
代替它
LABEL maintainer="lihua@163.com"
同时,LABEL
指令还可以设置任何的元数据,就像这样
LABEL version="1.0"
当我们对 build
后的 image 使用 docker inspect
命令时会就能看见该 LABEl
WORKDIR
好的,现在我们已经编写完了基本的指令,接下来来编写执行指令。
在我们正式工作之前,先来设置工作目录。
设置一个工作目录只需要
WORKDIR /mydata
然后我们下面的大多数命令都会在这个目录下运行。
当然,如果它不存在则会自动创建的。
RUN
RUN
指令应该是最重要的指令之一,它可以在容器内执行指定的指令,并把结果保存下来,一条 RUN
指令应该长这样:
RUN <command> # 这是shell格式
RUN ["exec", "arg1", "arg2"] # 这是exec格式
Exec 和 Shell 格式
shell 格式需要一个字符串,其会传给 "/bin/sh -c" 执行
exec 格式需要接收一个JSON数组,其中第一个元素是可执行文件,其他元素为执行时用到的参数
和shell对比,exec格式适用于需要规避shell对字符串作出错误解析的情况,或当基础镜像里没有 "/bin/sh" 时
你可以这样使用它,以下两条命令是等价的:
RUN bash -c 'touch /hello.txt'
RUN ["bash", "-c", "touch", "/hello.txt"]
ENV、ARG
如果你想设置环境变量,那么你将用到 ENV
指令,就像这样:
ENV <key>=<value> ...
ENV name=lihua local=cn
当你想使用它时只需要 ${<key>}
ENV my_version=1.0
RUN apt-get install -y mypackage=${my_version}
不过,exec格式下不会调用命令shell,所以变量替换不会生效。
RUN ["echo", "$name"]
要使其生效可以使用
RUN ["sh", "echo $name"]
你还可以使用标准的bash修饰符
${name:-default}
- name未设置则为"default"
${name:+lihua}
- name设置了则为"lihua",否则为空字符串
需要注意的是,该环境变量会保留到容器中,如果只是想在构建中使用变量,可以使用 ARG
指令,ARG
的用法和 ENV
十分接近
ADD、COPY
然后我们还需要一个能够将本地文件添加到容器中的指令,ADD
指令,像这样
ADD <src> ... <dest>
ADD ["<src>", ..., "<dest>"]
该指令将当前上下文中的 <src>
添加 image
里的 <dest>
文件或目录中,<src>
可以是文件系统,也可以网络,同时如果是压缩文件会自动解压。并且,你还可以用 ?
或 *
匹配一个或多个字符
ADD ./system.jar /app.jar
特别要注意的是,我们不能指定上下文以外的
src
路径,例如../book/b.txt
,这是不被允许的
COPY
指令是功能与 ADD
相近的指令,区别在于,COPY
不会访问网络资源且不会解压文件
ENTRYPOINT、CMD
通常我们可以用 ENTRYPOINT
来设置一个可执行文件在容器启动后运行,同样的,你可以使用 shell
格式或 exec
格式。不过如果定义了多个 ENTRYPOINT
,那只有最后一个会生效。
ENTRYPOINT ["java", "-jar", "/service.jar"]
CMD
指令和 ENTRYPOINT
相似,也是在容器启动时执行指定的指令,不过如果还同时出现了 ENTRYPOINT
指令,那 CMD
将作为 ENTRYPOINT
默认参数的形式在容器中执行。
同时如果 CMD
只是被用来为 ENTRYPOINT
提供参数,则可以使用 ["param1","param2"]
的格式。
并且,CMD
的参数会被 docker run
的参数覆盖,而 ENTRYPOINT
不会。
关于 CMD
和 ENTRYPOINT
的组合示例可以看看这张图。
从图中我们可以看出,ENTRYPOINT
如果以 shell
的形式,则它会忽略所有的 CMD
参数和 docker run
的参数,而且会运行在 sh -c
内。
这就代表进程的 pid
不是 1 ,并且无法接收 UNIX
信号,也就是说接收不到 docker stop
的 SIGTERM
信号,这样的话最后会被强制 kill
掉。
VOLUME
我们当然可以在 Dockerfile 中设置数据卷,可以使用以下两种方式
VOLUME ["/data"]
VOLUME /data
不过,主机的目录只能在使用 docker run
的时候申明,这是为了保持 image
的可移植性。
USER
这里有两种方法可以指定所使用的用户
USER <user>[:<group>]
USER <UID>[:<GID>]
EXPOSE
EXPOSE
能通知 Docker
在运行时监听指定的端口,但它不会实际的发布端口,只是一个文档信息,并且你可以在 docker run
时带上 -P
标签,这个标签能将 EXPOSE
通知的端口全部发布出去,不过会使用随机的端口。
EXPOSE 80/tcp
ONBUILD
这个命令可以让子镜像在构建时执行这个命令里的命令。
例如,我们在父镜像的 Dockerfile 的文件中编写
FROM grandfather
ONBUILD echo "hey! my child"
然后将该镜像构建好后,再构建子镜像
FROM father
构建子镜像的时候,就会打印出 hey! my child
。
build
在我们编写完 Dockerfile 文件后,就可以开始构建了。
我们使用 docker build
进行构建
docker build \
# -f 用来指定我们在本次build时需要用到的Dockerfile的位置,不指定则为当前目录
-f /path/to/a/Dockerfile \
# -t 用来指定build后的存储库的<名称>:<标记>
-t myApplication:latest \
# 此处的上下文为".",即当前目录,该目录下的所有文件会被放入一个tar文件(除非你编写了dockerignore文件)
.
当然也可以直接使用 docker build
,此时会在以当前目录为上下文,并在当前上下文中寻找 Dockerfile 文件。
Dockerfile 的 ADD
、COPY
和 RUN
指令会生成新的镜像层,下一个指令会用这个新的镜像层执行指令后,再保存为一个新的镜像层,提供给下一个指令使用。
这就代表即使用 RUN
指令运行了一些持久化的进程,在启动容器的时候也会消失。不过要想启动容器时同时运行一个进程,可以使用 ENTERPOINT
或 CMD
。
我们甚至可以使用 docker history
来查看组成 image 的所有层,当构建失败的时候,我们可以将中间层启动起来,就如同下面这个例子一样。
我们先编写一个 Dockerfile
FROM ubuntu:20.04
RUN no_cmd
ENTRYPOINT ["echo","world"]
然后使用 docker build
[root@VM-0-3-centos docker]# docker build -t test:1 .
Sending build context to Docker daemon 4.608kB
Step 1/3 : FROM ubuntu:20.04
---> 26b77e58432b
Step 2/3 : RUN no_cmd
---> Running in 6e1c94e4ccf6
/bin/sh: 1: no_cmd: not found
The command '/bin/sh -c no_cmd' returned a non-zero code: 127
毫无疑问,它出错了。虽然这次演示的错误很简单,我们可以直接看出来。
然后我们使用使用 docker run
进入中间的层
[root@VM-0-3-centos docker]# docker run -it 26b77e58432b
root@59c3c3641111:/# no_cmd
bash: no_cmd: command not found
通过演示我们发现,原因当然就是这个不存在的命令。
最后,在你的 image
构建成功后,你就可以像一般的 image
来任意使用你构建的 image
了~
简明教程 | Docker篇 · 其二:Dockerfile的编写的更多相关文章
- 简明教程 | Docker篇 · 其一:基础入门
了解Docker Docker是什么 Docker是指容器化技术,用于支持创建和使用 Linux 容器,同时Docker也是软件容器平台. 什么是容器(container) 容器是主机上与其他进程隔离 ...
- Docker简明教程
Docker简明教程 [编者的话]使用Docker来写代码更高效并能有效提升自己的技能.Docker能打包你的开发环境,消除包的依赖冲突,并通过集装箱式的应用来减少开发时间和学习时间. Docker作 ...
- Docker入门教程(三)Dockerfile
Docker入门教程(三)Dockerfile [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第三篇,介绍了Dockerfile的语法,DockerOn ...
- Docker的基本使用及DockerFile的编写
前言: 最近在准备面试,在复习到Docker相关内容时,想写一些东西分享给大家然后加深一下自己的印象,有了这篇随笔. Docker的简介: docker从文件系统.网络互连到进程隔离等等,极大的简化了 ...
- Docker 与 K8S学习笔记(四)—— Dockerfile的编写
在上一篇中我们简单介绍了Docker镜像的获取与使用,其中在镜像制作中提到在实际使用中一定要用Dockerfile方式去创建镜像而不要用docker commit方式,那么我们该如何编写Dockerf ...
- Docker 入门教程(3)——Dockerfile
Dockerfile Dockerfile是一个文本文件,用来定制镜像. 镜像是分层存储的,前一层会是下一层的基础.而镜像的定制就是定制每一层镜像在上一层做了什么改变. Dockerfile其内包含一 ...
- Java8简明教程(转载)
ImportNew注:有兴趣第一时间学习Java 8的Java开发者,欢迎围观<征集参与Java 8原创系列文章作者>. 以下是<Java 8简明教程>的正文. “Java并没 ...
- Lisp简明教程
此教程是我花了一点时间和功夫整理出来的,希望能够帮到喜欢Lisp(Common Lisp)的朋友们.本人排版很烂还望多多海涵! <Lisp简明教程>PDF格式下载 <Lisp简明教程 ...
- NSIS安装制作基础教程[初级篇], 献给对NSIS有兴趣的初学者
NSIS安装制作基础教程[初级篇], 献给对NSIS有兴趣的初学者 作者: raindy 来源:http://bbs.hanzify.org/index.php?showtopic=30029 时间: ...
随机推荐
- linq 集合按照多列进行distinct
List<TaskBatch> sourceList = (from c in BatchCollecion ...
- 解决git bash闪退问题 报openssl错误
问题描述:今天安装git之后发现Git Bash工具闪退. 于是试了各种办法之后,最后终于解决. 背景描述:git 下载地址:https://git-scm.com/download/win 下载成功 ...
- 【转载】flag标志什么?哦,它标志代码馊了
几乎每次在代码中发现flag变量,我总是能嗅到一股馊味.不管你闻没闻到,反正我闻到了. 在代码中,flag通常作为标志变量的名字.但问题在于,不是所有的问题或代码都需要使用这种标志变量,更不是使用标志 ...
- java版gRPC实战之七:基于eureka的注册发现
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- JAVA语言程序设计课程评价
紧张的又短暂的一个学期结束了,这个学期也许将成为我人生中一个重要的转折点,作为一名半路出家的选手,在初次了解Java语言时我感到非常的迷茫与不知所措.因为之前很多同学都是通过假期时间在家自学,刚转入新 ...
- 重磅来袭!!!Elasticsearch7.14.1(ES 7.14.1)与Springboot2.5.4的整合
1. 概述 前面我们聊了 Elasticsearch(ES)集群的搭建,今天我们来聊一下,Elasticsearch(ES)集群如何与 Springboot 进行整合. Elasticsearch(E ...
- 『Python』优雅的记录日志——loguru
1. 安装 pip install loguru 2. 初识 from loguru import logger logger.debug("This is a debug..." ...
- 小米路由器4a千兆版刷openwrt
现在网上搜小米路由器4a千兆版刷机的都是刷的padavan的,很少能找到openwrt的刷机教程. 首先刷openwrt系统的时候要先刷入引导程序breed,网上有一篇帖子写的很详细(https:// ...
- GoLang设计模式08 - 命令模式
命令模式是一种行为型模式.它建议将请求封装为一个独立的对象.在这个对象里包含请求相关的全部信息,因此可以将其独立执行. 在命令模式中有如下基础组件: Receiver:唯一包含业务逻辑的类,命令对象会 ...
- NOIP模拟69
T1 石子游戏 大坑未补 T2 大鱼吃小鱼 解题思路 set+桶可以得到 60pts (code) 线段树上二分每一次优先递归右区间从右区间贪心选择,并且记录下更改过的值,在处理完答案之后再复原回去. ...