首页 » Web前端 » 文件镜像php技巧_运用 Dockerfile 定制镜像超具体

文件镜像php技巧_运用 Dockerfile 定制镜像超具体

访客 2024-11-22 0

扫一扫用手机浏览

文章目录 [+]

Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,便是描述该层应该如何构建。

以 nginx 镜像为例,这次我们利用 Dockerfile 来定制。

文件镜像php技巧_运用 Dockerfile 定制镜像超具体

在一个空缺目录中,建立一个文本文件,并命名为 Dockerfile:

文件镜像php技巧_运用 Dockerfile 定制镜像超具体
(图片来自网络侵删)

$mkdirmynginx $cdmynginx $touchDockerfile

其内容为:

FROMnginx RUNecho'<h1>Hello,Docker!</h1>'>/usr/share/nginx/html/index.html

这个 Dockerfile 很大略,一共就两行。
涉及到了两条指令,FROM 和 RUN。

1.2、FROM 指定根本镜像

所谓定制镜像,那一定因此一个镜像为根本,在其上进行定制。
就像我们之前运行了一个 nginx 镜像的容器,再进行修正一样,根本镜像是必须指定的。
而 FROM 便是指定根本镜像,因此一个 Dockerfile 中 FROM是必备的指令,并且必须是第一条指令。

在 Docker Store 上有非常多的高质量的官方镜像,有可以直接拿来利用的做事类的镜像,如 nginx、redis、mongo、mysql、httpd、php、tomcat 等;也有一些方便开拓、构建、运行各种措辞运用的镜像,如node、openjdk、python、ruby、golang等。
可以在个中探求一个最符合我们终极目标的镜像为根本镜像进行定制。

如果没有找到对应做事的镜像,官方镜像中还供应了一些更为根本的操作系统镜像,如ubuntu、debian、centos、fedora、alpine 等,这些操作系统的软件库为我们供应了更广阔的扩展空间。

除了选择现有镜像为根本镜像外,Docker 还存在一个分外的镜像,名为 scratch。
这个镜像是虚拟的观点,并不实际存在,它表示一个空缺的镜像。

FROMscratch ...

如果你以 scratch 为根本镜像的话,意味着你不以任何镜像为根本,接下来所写的指令将作为镜像第一层开始存在。

不以任何系统为根本,直接将可实行文件复制进镜像的做法并不罕见,比如swarm、coreos/etcd。
对付 Linux 下静态编译的程序来说,并不须要有操作系统供应运行时支持,所需的统统库都已经在可实行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧。
利用 Go 措辞开拓的运用很多会利用这种办法来制作镜像,这也是为什么有人认为 Go 是特殊适宜容器微做事架构的措辞的缘故原由之一。

1.3、RUN 实行命令

RUN 指令是用来实行命令行命令的。
由于命令行的强大能力,RUN 指令在定制镜像时是最常用的指令之一。
其格式有两种:

shell 格式:RUN <命令>,就像直接在命令行中输入的命令一样。
刚才写的 Dockerfile 中的 RUN 指令便是这种格式。

RUNecho'<h1>Hello,Docker!</h1>'>/usr/share/nginx/html/index.html exec 格式:RUN ["可实行文件", "参数1", "参数2"],这更像是函数调用中的格式。

既然 RUN 就像 Shell 脚本一样可以实行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢?比如这样:

FROMdebian:jessie RUNapt-getupdate RUNapt-getinstall-ygcclibc6-devmake RUNwget-Oredis.tar.gz"http://download.redis.io/releases/redis-3.2.5.tar.gz" RUNmkdir-p/usr/src/redis RUNtar-xzfredis.tar.gz-C/usr/src/redis--strip-components=1 RUNmake-C/usr/src/redis RUNmake-C/usr/src/redisinstall

之前说过,Dockerfile 中每一个指令都会建立一层,RUN 也不例外。
每一个 RUN 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上实行这些命令,实行结束后,commit 这一层的修正,构成新的镜像。

而上面的这种写法,创建了 7 层镜像。
这是完备没故意义的,而且很多运行时不须要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。
结果便是产生非常臃肿、非常多层的镜像,不仅仅增加了构建支配的韶光,也很随意马虎出错。
这是很多初学 Docker 的人常犯的一个缺点。

Union FS 是有最大层数限定的,比如 AUFS,曾经是最大不能超过 42 层,现在是不能超过 127 层。

上面的 Dockerfile 精确的写法该当是这样:

FROMdebian:jessie RUNbuildDeps='gcclibc6-devmake'\ &&apt-getupdate\ &&apt-getinstall-y$buildDeps\ &&wget-Oredis.tar.gz"http://download.redis.io/releases/redis-3.2.5.tar.gz"\ &&mkdir-p/usr/src/redis\ &&tar-xzfredis.tar.gz-C/usr/src/redis--strip-components=1\ &&make-C/usr/src/redis\ &&make-C/usr/src/redisinstall\ &&rm-rf/var/lib/apt/lists/\ &&rmredis.tar.gz\ &&rm-r/usr/src/redis\ &&apt-getpurge-y--auto-remove$buildDeps

首先,之前所有的命令只有一个目的,便是编译、安装 Redis 可实行文件。
因此没有必要建立很多层,这只是一层的事情。
因此,这里没有利用很多个 RUN 对逐一对应不同的命令,而是仅仅利用一个 RUN 指令,并利用 && 将各个所需命令串联起来。
将之前的 7 层,简化为了 1 层。
在撰写 Dockerfile 的时候,要常常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。

并且,这里为了格式化还进行了换行。
Dockerfile 支持 Shell 类的行尾添加 \ 的命令换行办法,以及行首 # 进行注释的格式。
良好的格式,比如换行、缩进、注释等,会让掩护、排障更为随意马虎,这是一个比较好的习气。

此外,还可以看到这一组命令的末了添加了清理事情的命令,删除了为了编译构建所须要的软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。
这是很主要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会不才一层被删除,会一贯跟随着镜像。
因此镜像构建时,一定要确保每一层只添加真正须要添加的东西,任何无关的东西都该当清理掉。

很多人初学 Docker 制作出了很臃肿的镜像的缘故原由之一,便是忘却了每一层构建的末了一定要清理掉无关文件。

1.4、构建镜像

再回到之前定制的 Nginx 镜像的 Dockerfile 来。
现在我们明白了这个 Dockerfile 的内容,那么让我们来构建这个镜像吧。

在 Dockerfile 文件所在目录实行:

$dockerbuild-tnginx:v3. SendingbuildcontexttoDockerdaemon2.048kB Step1:FROMnginx --->e43d811ce2f4 Step2:RUNecho'<h1>Hello,Docker!</h1>'>/usr/share/nginx/html/index.html --->Runningin9cdc27646c7b --->44aa4490ce2c Removingintermediatecontainer9cdc27646c7b Successfullybuilt44aa4490ce2c

从命令的输出结果中,我们可以清晰的看到镜像的构建过程。
在 Step 2中,犹如我们之前所说的那样,RUN 指令启动了一个容器 9cdc27646c7b,实行了所哀求的命令,并末了提交了这一层 44aa4490ce2c,随后删除了所用到的这个容器 9cdc27646c7b。

这里我们利用了 docker build 命令进行镜像构建。
其格式为:

dockerbuild[选项]<高下文路径/URL/->

在这里我们指定了终极镜像的名称 -t nginx:v3,构建成功后,我们可以像之前运行 nginx:v2 那样来运行这个镜像,其结果会和 nginx:v2一样。

1.5、镜像构建高下文(Context)

如果把稳,会看到 docker build 命令末了有一个 .,. 表示当前目录,而 Dockerfile 就在当前目录,因此不少初学者以为这个路径是在指定 Dockerfile 所在路径,这么理解实在是不准确的。
如果对应上面的命令格式,你可能会创造,这是在指定高下文路径。
那么什么是高下文呢?

首先我们要理解 docker build 的事情事理。
Docker 在运行时分为 Docker 引擎(也便是做事端守护进程)和客户端工具。
Docker 的引擎供应了一组 REST API,被称为 Docker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。
因此,虽然表面上我们彷佛是在本机实行各种 docker 功能,但实际上,统统都是利用的远程调用形式在做事端(Docker 引擎)完成。
也由于这种 C/S 设计,让我们操作远程做事器的 Docker 引擎变得轻而易举。

当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,常常会须要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。
而 docker build 命令构建镜像,实在并非在本地构建,而是在做事端,也便是 Docker 引擎中构建的。
那么在这种客户端/做事真个架构中,如何才能让做事端得到本地文件呢?

这就引入了高下文的观点。
当构建的时候,用户会指定构建镜像高下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。
这样 Docker 引擎收到这个高下文包后,展开就会得到构建镜像所需的统统文件。

如果在 Dockerfile 中这么写:

COPY./package.json/app/

这并不是要复制实行 docker build 命令所在的目录下的 package.json,也不是复制 Dockerfile 所在目录下的 package.json,而是复制 高下文(context) 目录下的 package.json。

因此,COPY 这类指令中的源文件的路径都是_相对路径_。
这也是初学者常常会问的为什么 COPY ../package.json /app 或者 COPY /opt/xxxx /app 无法事情的缘故原由,由于这些路径已经超出了高下文的范围,Docker 引擎无法得到这些位置的文件。
如果真的须要那些文件,该当将它们复制到高下文目录中去。

现在就可以理解刚才的命令 docker build -t nginx:v3 . 中的这个 .,实际上是在指定高下文的目录,docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。

如果不雅观察 docker build 输出,我们实在已经看到了这个发送上下文的过程:

$dockerbuild-tnginx:v3. SendingbuildcontexttoDockerdaemon2.048kB ...

理解构建高下文对付镜像构建是很主要的,避免犯一些不应该的缺点。
比如有些初学者在创造 COPY /opt/xxxx /app 不事情后,于是干脆将 Dockerfile 放到了硬盘根目录去构建,结果创造 docker build 实行后,在发送一个几十 GB 的东西,极为缓慢而且很随意马虎构建失落败。
那是由于这种做法是在让 docker build 打包全体硬盘,这显然是利用缺点。

一样平常来说,该当会将 Dockerfile 置于一个空目录下,或者项目根目录下。
如果该目录下没有所需文件,那么该当把所需文件复制一份过来。
如果目录下有些东西确实不肯望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个 .dockerignore,该文件是用于剔除不须要作为高下文通报给 Docker 引擎的。

那么为什么会有人误以为 . 是指定 Dockerfile 所在目录呢?这是由于在默认情形下,如果不额外指定 Dockerfile 的话,会将高下文目录下的名为 Dockerfile 的文件作为 Dockerfile。

这只是默认行为,实际上 Dockerfile 的文件名并不哀求必须为 Dockerfile,而且并不哀求必须位于高下文目录中,比如可以用 -f ../Dockerfile.php 参数指定某个文件作为 Dockerfile。

当然,一样平常大家习气性的会利用默认的文件名 Dockerfile,以及会将其置于镜像构建高下文目录中

1.6、其他 docker build 的用法1.6.1、直接用 Git repo 进行构建

docker build 还支持从 URL 构建,比如可以直接从 Git repo 中构建:

$dockerbuildhttps://github.com/twang2218/gitlab-ce-zh.git#:8.14 dockerbuildhttps://github.com/twang2218/gitlab-ce-zh.git\#:8.14 SendingbuildcontexttoDockerdaemon2.048kB Step1:FROMgitlab/gitlab-ce:8.14.0-ce.0 8.14.0-ce.0:Pullingfromgitlab/gitlab-ce aed15891ba52:Alreadyexists 773ae8583d14:Alreadyexists ...

这行命令指定了构建所需的 Git repo,并且指定默认的 master 分支,构建目录为 /8.14/,然后 Docker 就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始构建。

1.6.2、用给定的 tar 压缩包构建

$dockerbuildhttp://server/context.tar.gz

如果所给出的 URL 不是个 Git repo,而是个 tar 压缩包,那么 Docker 引擎会下载这个包,并自动解压缩,以其作为高下文,开始构建。

1.6.3、从标准输入中读取 Dockerfile 进行构建

dockerbuild-<Dockerfile

catDockerfile|dockerbuild-

如果标准输入传入的是文本文件,则将其视为 Dockerfile,并开始构建。
这种形式由于直接从标准输入中读取 Dockerfile 的内容,它没有高下文,因此不可以像其他方法那样可以将本地文件 COPY 进镜像之类的事情。

1.6.4、从标准输入中读取高下文压缩包进行构建

$dockerbuild-<context.tar.gz

如果创造标准输入的文件格式是 gzip、bzip2 以及 xz 的话,将会使其为高下文压缩包,直接将其展开,将里面视为高下文,并开始构建。

二、Dockerfile 指令

我们已经先容了 FROM,RUN,还提及了 COPY, ADD,实在 Dockerfile 功能很强大,它供应了十多个指令。
下面我们连续讲解其他的指令。

2.1、COPY

格式:

COPY <源路径>... <目标路径>COPY ["<源路径1>",... "<目标路径>"]

和 RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。

COPY 指令将从构建高下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。
比如:

COPYpackage.json/usr/src/app/

<源路径> 可以是多个,乃至可以是通配符,其通配符规则要知足 Go 的 filepath.Match 规则,如:

COPYhom/mydir/ COPYhom?.txt/mydir/

<目标路径> 可以是容器内的绝对路径,也可以是相对付事情目录的相对路径(事情目录可以用 WORKDIR 指令来指定)。
目标路径不须要事先创建,如果目录不存在会在复制文件前先行创建缺失落目录。

此外,还须要把稳一点,利用 COPY 指令,源文件的各种元数据都会保留。
比如读、写、实行权限、文件变更韶光等。
这个特性对付镜像定制很有用。
特殊是构建干系文件都在利用 Git 进行管理的时候。

2.2、ADD

ADD 指令和 COPY 的格式和性子基本同等。
但是在 COPY 根本上增加了一些功能。

比如 <源路径> 可以是一个 URL,这种情形下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去。
下载后的文件权限自动设置为 600,如果这并不是想要的权限,那么还须要增加额外的一层 RUN 进行权限调度,其余,如果下载的是个压缩包,须要解压缩,也一样还须要额外的一层 RUN 指令进行解压缩。
以是不如直策应用 RUN 指令,然后利用 wget 或者 curl 工具下载,处理权限、解压缩、然后清理无用文件更合理。
因此,这个功能实在并不实用,而且不推举利用。

如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2以及 xz 的情形下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。

在某些情形下,这个自动解压缩的功能非常有用,比如官方镜像 ubuntu中:

FROMscratch ADDubuntu-xenial-core-cloudimg-amd64-root.tar.gz/ ...

但在某些情形下,如果我们真的是希望复制个压缩文件进去,而不解压缩,这时就不可以利用 ADD 命令了。

在 Docker 官方的 Dockerfile 最佳实践文档 中哀求,尽可能的利用 COPY,由于 COPY 的语义很明确,便是复制文件而已,而 ADD 则包含了更繁芜的功能,其行为也不一定很清晰。
最适宜利用 ADD 的场合,便是所提及的须要自动解压缩的场合。

其余须要把稳的是,ADD 指令会令镜像构建缓存失落效,从而可能会令镜像构建变得比较缓慢。

因此在 COPY 和 ADD 指令中选择的时候,可以遵照这样的原则,所有的文件复制均利用 COPY 指令,仅在须要自动解压缩的场合利用 ADD。

2.3、CMD

CMD 指令的格式和 RUN 相似,也是两种格式:

shell 格式:CMD <命令>exec 格式:CMD ["可实行文件", "参数1", "参数2"...]参数列表格式:CMD ["参数1", "参数2"...]。
在指定了 ENTRYPOINT指令后,用 CMD 指定详细的参数。

之前先容容器的时候曾经说过,Docker 不是虚拟机,容器便是进程。
既然是进程,那么在启动容器的时候,须要指定所运行的程序及参数。
CMD 指令便是用于指定默认的容器主进程的启动命令的。

在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如,ubuntu 镜像默认的 CMD 是 /bin/bash,如果我们直接 docker run -it ubuntu 的话,会直接进入 bash。
我们也可以在运行时指定运行别的命令,如 docker run -it ubuntu cat /etc/os-release。
这便是用 cat /etc/os-release 命令更换了默认的 /bin/bash 命令了,输出了系统版本信息。

在指令格式上,一样平常推举利用 exec 格式,这类格式在解析时会被解析为 JSON 数组,因此一定要利用双引号 ",而不要利用单引号。

如果利用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行实行。
比如:

CMDecho$HOME

在实际实行中,会将其变更为:

CMD["sh","-c","echo$HOME"]

这便是为什么我们可以利用环境变量的缘故原由,由于这些环境变量会被 shell 进行解析处理。

提到 CMD 就不得不提容器中运用在前台实行和后台实行的问题。
这是初学者常涌现的一个稠浊。

Docker 不是虚拟机,容器中的运用都该当以前台实行,而不是像虚拟机、物理机里面那样,用 upstart/systemd 去启动后台做事,容器内没有后台做事的观点。

一些初学者将 CMD 写为:

CMDservicenginxstart

然后创造容器实行后就立即退出了。
乃至在容器内去利用 systemctl 命令结果却创造根本实行不了。
这便是由于没有搞明白前台、后台的观点,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器。

对付容器而言,其启动程序便是容器运用进程,容器便是为了主进程而存在的,主进程退出,容器就失落去了存在的意义,从而退出,其它赞助进程不是它须要关心的东西。

而利用 service nginx start 命令,则是希望 upstart 来往后台守护进程形式启动 nginx 做事。
而刚才说了 CMD service nginx start 会被理解为 CMD [ "sh", "-c", "service nginx start"],因此主进程实际上是 sh。
那么当 service nginx start 命令结束后,sh 也就结束了,sh 作为主进程退出了,自然就会令容器退出。

精确的做法是直接实行 nginx 可实行文件,并且哀求以前台形式运行。
比如:

CMD["nginx","-g","daemonoff;"] 2.4、ENTRYPOINT

ENTRYPOINT 的格式和 RUN 指令格式一样,分为 exec 格式和 shell格式。

ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。
ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,须要通过 docker run 的参数 --entrypoint 来指定。

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际实行时,将变为:

<ENTRYPOINT>"<CMD>"

那么有了 CMD 后,为什么还要有 ENTRYPOINT 呢?这种 <ENTRYPOINT> "<CMD>" 有什么好处么?让我们来看几个场景。

2.4.1、场景一:让镜像变成像命令一样利用

假设我们须要一个得知自己当前公网 IP 的镜像,那么可以先用 CMD 来实现:

FROMubuntu:16.04 RUNapt-getupdate\ &&apt-getinstall-ycurl\ &&rm-rf/var/lib/apt/lists/ CMD["curl","-s","http://ip.cn"]

如果我们利用 docker build -t myip . 来构建镜像的话,如果我们须要查询当前公网 IP,只须要实行:

$dockerrunmyip 当前 IP:160.155.224.xx 来自:XX市联通

这么看起来彷佛可以直接把镜像当做命令利用了,不过命令总有参数,如果我们希望加参数呢?比如从上面的 CMD 中可以看到本色的命令是 curl,那么如果我们希望显示 HTTP 头信息,就须要加上 -i 参数。
那么我们可以直接加 -i 参数给 docker run myip 么?

$dockerrunmyip-i docker:Errorresponsefromdaemon:invalidheaderfieldvalue"ociruntimeerror:container_linux.go:247:startingcontainerprocesscaused\"exec:\\\"-i\\\":executablefilenotfoundin$PATH\"\n".

我们可以看到可实行文件找不到的报错,executable file not found。
之前我们说过,跟在镜像名后面的是 command,运行时会更换 CMD 的默认值。
因此这里的 -i 更换了原来的 CMD,而不是添加在原来的 curl -s http://ip.cn 后面。
而 -i 根本不是命令,以是自然找不到。

那么如果我们希望加入 -i 这参数,我们就必须重新完全的输入这个命令:

$dockerrunmyipcurl-shttp://ip.cn-i

这显然不是很好的办理方案,而利用 ENTRYPOINT 就可以办理这个问题。
现在我们重新用 ENTRYPOINT 来实现这个镜像:

FROMubuntu:16.04 RUNapt-getupdate\ &&apt-getinstall-ycurl\ &&rm-rf/var/lib/apt/lists/ ENTRYPOINT["curl","-s","http://ip.cn"]

这次我们再来考试测验直策应用 docker run myip -i:

$dockerrunmyip 当前 IP:160.155.224.xx 来自:XX市联通 $dockerrunmyip-i HTTP/1.1200OK Server:nginx/1.8.0 Date:Tue,22Nov201605:12:40GMT Content-Type:text/html;charset=UTF-8 Vary:Accept-Encoding X-Powered-By:PHP/5.6.24-1~dotdeb+7.1 X-Cache:MISSfromcache-2 X-Cache-Lookup:MISSfromcache-2:80 X-Cache:MISSfromproxy-2_6 Transfer-Encoding:chunked Via:1.1cache-2:80,1.1proxy-2_6:8006 Connection:keep-alive 当前 IP:160.155.224.xx 来自:XX市联通

可以看到,这次成功了。
这是由于当存在 ENTRYPOINT 后,CMD 的内容将会作为参数传给 ENTRYPOINT,而这里 -i 便是新的 CMD,因此会作为参数传给 curl,从而达到了我们预期的效果。

2.4.2、场景二:运用运行前的准备事情

启动容器便是启动主进程,但有些时候,启动主进程前,须要一些准备事情。

比如 mysql 类的数据库,可能须要一些数据库配置、初始化的事情,这些事情要在终极的 mysql 做事器运行之前办理。

此外,可能希望避免利用 root 用户去启动做事,从而提高安全性,而在启动做事前还须要以 root 身份实行一些必要的准备事情,末了切换到做事用户身份启动做事。
或者除了做事外,其它命令依旧可以利用 root 身份实行,方便调试等。

这些准备事情是和容器 CMD 无关的,无论 CMD 为什么,都须要事前辈行一个预处理的事情。
这种情形下,可以写一个脚本,然后放入 ENTRYPOINT 中去实行,而这个脚本会将接到的参数(也便是 <CMD>)作为命令,在脚本末了实行。
比如官方镜像 redis 中便是这么做的:

FROMalpine:3.4 ... RUNaddgroup-Sredis&&adduser-S-Gredisredis ... ENTRYPOINT["docker-entrypoint.sh"] EXPOSE6379 CMD["redis-server"]

可以看到个中为了 Redis 做事创建了 Redis 用户,并在末了指定了 ENTRYPOINT 为 docker-entrypoint.sh 脚本。

#!/bin/sh ... #allowthecontainertobestartedwith`--user` if["$1"='redis-server'-a"$(id-u)"='0'];then chown-Rredis. execsu-execredis"$0""$@" fi exec"$@"

该脚本的内容便是根据 CMD 的内容来判断,如果是 redis-server 的话,则切换到 redis 用户身份启动做事器,否则依旧利用 root 身份实行。
比如:

$dockerrun-itredisid uid=0(root)gid=0(root)groups=0(root) 2.5、ENV

格式有两种:

ENV <key> <value>ENV <key1>=<value1> <key2>=<value2>...

这个指令很大略,便是设置环境变量而已,无论是后面的其它指令,如 RUN,还是运行时的运用,都可以直策应用这里定义的环境变量。

ENVVERSION=1.0DEBUG=on\ NAME="HappyFeet"

这个例子中演示了如何换行,以及对含有空格的值用双引号括起来的办法,这和 Shell 下的行为是同等的。

定义了环境变量,那么在后续的指令中,就可以利用这个环境变量。
比如在官方 node 镜像 Dockerfile 中,就有类似这样的代码:

ENVNODE_VERSION7.2.0 RUNcurl-SLO"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz"\ &&curl-SLO"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"\ &&gpg--batch--decrypt--outputSHASUMS256.txtSHASUMS256.txt.asc\ &&grep"node-v$NODE_VERSION-linux-x64.tar.xz\$"SHASUMS256.txt|sha256sum-c-\ &&tar-xJf"node-v$NODE_VERSION-linux-x64.tar.xz"-C/usr/local--strip-components=1\ &&rm"node-v$NODE_VERSION-linux-x64.tar.xz"SHASUMS256.txt.ascSHASUMS256.txt\ &&ln-s/usr/local/bin/node/usr/local/bin/nodejs

在这里先定义了环境变量 NODE_VERSION,其后的 RUN 这层里,多次利用 $NODE_VERSION 来进行操作定制。
可以看到,将来升级镜像构建版本的时候,只须要更新 7.2.0 即可,Dockerfile 构建掩护变得更轻松了。

下列指令可以支持环境变量展开:ADD、COPY、ENV、EXPOSE、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD。

可以从这个指令列表里觉得到,环境变量可以利用的地方很多,很强大。
通过环境变量,我们可以让一份 Dockerfile 制作更多的镜像,只需利用不同的环境变量即可。

2.6、VOLUME

格式为:

VOLUME ["<路径1>", "<路径2>"...]VOLUME <路径>

之前我们说过,容器运行时该当只管即便保持容器存储层不发生写操作,对付数据库类须要保存动态数据的运用,其数据库文件该当保存于卷(volume)中,后面的章节我们会进一步先容 Docker 卷的观点。
为了防止运行时用户忘却将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其运用也可以正常运行,不会向容器存储层写入大量数据。

VOLUME/data

这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而担保了容器存储层的无状态化。
当然,运行时可以覆盖这个挂载设置。
比如:

dockerrun-d-vmydata:/dataxxxx

在这行命令中,就利用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置。

2.7、EXPOSE

格式为 EXPOSE <端口1> [<端口2>...]。

EXPOSE 指令是声明运行时容器供应做事端口,这只是一个声明,在运行时并不会由于这个声明运用就会开启这个端口的做事。
在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像利用者理解这个镜像做事的守护端口,以方便配置映射;另一个用途则是在运行时利用随机端口映射时,也便是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

此外,在早期 Docker 版本中还有一个分外的用途。
以前所有容器都运行于默认桥接管集中,因此所有容器相互之间都可以直接访问,这样存在一定的安全性问题。
于是有了一个 Docker 引擎参数 --icc=false,当指定该参数后,容器间将默认无法互访,除非相互间利用了 --links 参数的容器才可以互通,并且只有镜像中 EXPOSE 所声明的端口才可以被访问。
这个 --icc=false 的用法,在引入了 docker network 后已经基本不用了,通过自定义网络可以很轻松的实现容器间的互联与隔离。

要将 EXPOSE 和在运行时利用 -p <宿主端口>:<容器端口> 区分开来。
-p,是映射宿主端口和容器端口,换句话说,便是将容器的对应端口做事公开给外界访问,而 EXPOSE 仅仅是声明容器打算利用什么端口而已,并不会自动在宿主进行端口映射。

2.8、WORKDIR

格式为 WORKDIR <事情目录路径>。

利用 WORKDIR 指令可以来指定事情目录(或者称为当前目录),往后各层确当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。

之条件到一些初学者常犯的缺点是把 Dockerfile 等同于 Shell 脚本来书写,这种缺点的理解还可能会导致涌现下面这样的缺点:

UNcd/app RUNecho"hello">world.txt

如果将这个 Dockerfile 进行构建镜像运行后,会创造找不到 /app/world.txt 文件,或者其内容不是 hello。
缘故原由实在很大略,在 Shell 中,连续两行是同一个进程实行环境,因此前一个命令修正的内存状态,会直接影响后一个命令;而在 Dockerfile 中,这两行 RUN 命令的实行环境根本不同,是两个完备不同的容器。
这便是对 Dockerfile 构建分层存储的观点不理解所导致的缺点。

之前说过每一个 RUN 都是启动一个容器、实行命令、然后提交存储层文件变更。
第一层 RUN cd /app 的实行仅仅是当提高程的事情目录变更,一个内存上的变革而已,其结果不会造成任何文件变更。
而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完备没紧要,自然不可能继续前一层构建过程中的内存变革。

因此如果须要改变往后各层的事情目录的位置,那么该当利用 WORKDIR指令。

来源:https://micromaple.blog.csdn.net/article/details/125804242

标签:

相关文章

介绍百度码,技术革新背后的智慧之光

随着科技的飞速发展,互联网技术已经成为我们生活中不可或缺的一部分。而在这个信息爆炸的时代,如何快速、准确地获取信息,成为了人们关注...

Web前端 2025-01-03 阅读1 评论0

介绍皮箱密码,开启神秘之门的钥匙

皮箱,作为日常生活中常见的收纳工具,承载着我们的珍贵物品。面对紧闭的皮箱,许多人却束手无策。如何才能轻松打开皮箱呢?本文将为您揭秘...

Web前端 2025-01-03 阅读1 评论0

介绍盗号器,网络安全的隐忧与应对步骤

随着互联网的快速发展,网络安全问题日益突出。盗号器作为一种非法工具,对网民的个人信息安全构成了严重威胁。本文将深入剖析盗号器的原理...

Web前端 2025-01-03 阅读1 评论0