镜像是容器运行基础,容器是镜像运行后的形态,二者紧密相连又有不同。
1、镜像的概念
镜像是一个包含程序运行必要依赖环境和代码的只读文件,它采用分层的文件系统,将每一次改变以读写层的形式增加到原来的只读文件上。
1.1镜像与容器
如果将容器理解为一套程序运行的虚拟环境,那么镜像就是用来构建这个环境的模板。通过同一镜像,我们可以构造出很多相互独立但运行环境一样的容器。
镜像的最底层必须是一个称为启动文件(bootfs)的镜像,用户不会与这一层直接打交道。bootfs的上层镜像叫作根镜像(rootfs),它在通常情况下是一个操作系统,如Ubuntu、Debina和CentOS等。
(素材来源与网络)
2、本地镜像的管理
2.1查看
docker images
docker images ub* //使用通配符
查看结果类似下表
REPOSITORY | TAG | IMAGE_ID | CREATED | VIRTUAL SIZE |
centos | laster | 10009deww3 | 45 hours ago | 199MB |
REPOSITORY是仓库名称,一般用来存放同一类型的镜像,其名称由创建者指定。
命令规则:
1、[namespace\ubuntu]:由命名空间和实际的仓库名组成,中间用”\“隔开,如果要提交到DOcker Hub 非官方的仓库名必须要遵守此规则。
2、[ubuntu]:只有仓库名,它属于顶级命名空间,该空间只能用于官方的镜像。本地也可以这种命名,但是无法分发到DOcker Hub上进行分享。
3、【dl.dockerpool.com:5000\ubuntu:12.04]:指定URL路径的方式。一般是用于自己搭建的Hub或者第三方Hub上,dl.dockerpool.com:5000是第三方Hub的主机名及端口。
TAG用于区分同一仓库中的不同镜像。如果未指定,默认为laster
IMAGE_ID每个镜像都有一个字符串类型、长为64位的HashID,同容器ID一样,用来唯一标识一个镜像。一般取前面一部分只要在本机唯一标识一个镜像即可,和Git的CommitID规则类似。
CREATED镜像创建时间。
VIRTUAL SIZE镜像所占用的虚拟大小,该大小包含了所有共享文件的大小。
2.2下载
docker run命令运行一个镜像时,首先会在本地查找镜像,如果本地不存在会继续去Docker Hub上面搜索符合条件的镜像并将其下载下来运行。
docker pull ubuntu:13.10
13.10: Pulling from library/ubuntu //本地已经有了
6599cadaf950: Pull complete
23eda618d451: Pull complete
f0be3084efe9: Pull complete
52de432f084b: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:15b79a6654811c8d992ebacdfbd5152fcf3d165e374e264076aa435214a947a3
Status: Downloaded newer image for ubuntu:13.10
docker search wordpress //Docker Hub搜索符合条件的镜像
搜索结果会显示
镜像的名称(NAME)、描述(DESCRIPTION)、
镜像评分(STARS)(越高表明质量越好)、
是否为官方镜像(OFFICIAL)、是否使用了自动构建(AUTOMATED)
有时运行的镜像本地没有,会从Docker Hub上获取,可以通过docker pull 命令预先下载到本地,再使用docker run命令。
docker pull ubuntu
2.3删除
删除的镜像如果被容器所依赖,即便容器已停止了,也仍然需要依赖镜像,这里不能直接删除,如果一定要强制删除可以使用-f参数。但是为了安全起见建议先停止容器,删除容器后再删除镜像。
docker rmi 镜像ID或名称 //比删除容器命令多了一个i
docker rmi -f 镜像ID或名称 //强制删除镜像
3、创建本地镜像
创建本地镜像有三种方式:
1、通过镜像导出成tar文件的方式,然后导入镜像创建一个新镜像。(这种方式在 Docker开发实践笔记一 容器的导入和导出已说明)
2、使用commit命令保存对基础镜像的修改创建出新的镜像
运行镜像容器后,对容器进行修改,然后使用commit提交创建。
docker run -i -t ubuntu
修改容器,比如安装软件、修改配置等。
docker commit -m="Message" --author="LIU" 运行的容器ID myDOcker/redis:v1
如果需要对上面的镜像再修改,进入容器内更新后再使用上面的命令提交一个新版本镜像即可。
docker commit -m="Message" --author="LIU" 运行的容器ID myDOcker/redis:v2
3、使用Dockerfile文件创建镜像(推荐方式)
Dockerfile用来创建一个自定义的image,包含了用户指定的软件依赖等。
下面是一个DOckerfile文件实例,本例只是介绍语法无任何实际意义。
#version: 1.00
#指定待扩展的父级镜像。除注释外文件开头必须是一个FROM指令
FROM ubuntu:latest
#作者信息
MAINtAINER jiuwu "jiuwu@qq.com"
#设置root用户为后续命令的执行者
USET root
#执行操作
RUN apt-age update
RUN apt-get install -y nginx
#使用&&拼接命令
RUN touch test.txt && echo "abc" >> abc.txt
#对外暴露端口
EXPOSE 80 8080 1038
#添加文件 第一个是源文件,第二个是要添到的容器中的目标路径
ADD abc.txt /opt/
#添加文件件
ADD /webapp /opt/webapp
#添加网络文件
ADD https://www.baidu.com/logo.jpg /opt/
#设置环境变量
ENV WEBAPP_PORT=9000
#设置工作目录
WORKDIR /opt/
#设置启动命令,只能出现一次,后面的会覆盖前面的
ENTRYPOINT ["ls"]
#设置启动参数,只能出现一次,后面的会覆盖前面的
CMD ["-a","-l"]
#设置卷
VOLUME ["/data","/var/www"]
#设置子镜像的触发操作(当有镜像以此镜像为父镜像时,会执行下面的操作)
ONBUILD ADD ./app/src
ONBUILD RUN echo "on build excuted " >> onbuild.txt
下面是对上述指令的详细说明:
指令不区分大小写。但是,命名约定为全部大写。
所有Dockerfile都必须以FROM
命令开始。 FROM
命令会指定镜像基于哪个基础镜像创建,接下来的命令也会基于这个基础镜像(译者注:CentOS和Ubuntu有些命令可是不一样的)。FROM
命令可以多次使用,表示会创建多个镜像。具体语法如下:
FROM <image name>
1. MAINTAINER:设置该镜像的作者。语法如下:
MAINTAINER <author name>
2. RUN:在shell或者exec的环境下执行的命令。RUN
指令会在新创建的镜像上添加新的层面,接下来提交的结果用在Dockerfile的下一条指令中。语法如下:
RUN 《command》
RUN apt-get update //这种形式使用/bin/sh环境中执行的命令
RUN ["apt-get","update"] //直接使用系统调用exec来执行
多个命令可以用&&连接执行
RUN apt-get update && apt-get install nginx
这里需要注意一下,因为一次RUN会增加一个新层,所有最佳实践是减少RUN次数,尽量将多个操作合并到RUN中一次执行。
3. ADD:复制文件指令。它有两个参数<source>和<destination>。destination是容器内的路径。source可以是URL或者是启动配置上下文中的一个文件。语法如下:
ADD 《src》 《destination》
如果源文件是主机上的zip或tar形式的压缩文件,Docker会先解压缩,然后将文件添加到镜像指定的位置,如果是URL指定的网络压缩文件则不解压。
4. CMD:提供了容器启动时默认执行的命令。 Dockerfile只允许使用一次CMD指令。 使用多个CMD会抵消之前所有的指令,只有最后一个指令生效。 CMD有三种形式:
CMD ["executable","param1","param2"]
CMD ["param1","param2"]
CMD command param1 param2
5. EXPOSE:指定容器在运行时监听的端口。语法如下:
EXPOSE <port>; //多个端口使用空格隔开
EXPOSE 80 8080 9000
运行容器时通过参数-P(大写)即可将EXPOSE里所指定的端口映射到主机上另外的随机端口,其它容器或主机就可以通过映射后的端口与此容器通信。同时,我们也可以通过-p(小写)参数将DOckerfile中EXPOSE中没有列出的端口设置成公开的。
6. ENTRYPOINT:配置给容器一个可执行的命令,这意味着在每次使用镜像创建容器时一个特定的应用程序可以被设置为默认程序。同时也意味着该镜像每次被调用时仅能运行指定的应用。类似于CMD
,Docker只允许一个ENTRYPOINT,多个ENTRYPOINT会抵消之前所有的指令,只执行最后的ENTRYPOINT指令。语法如下:
ENTRYPOINT ["executable", "param1","param2"]
ENTRYPOINT command param1 param2
7. WORKDIR:指定RUN
、CMD
与ENTRYPOINT
命令的工作目录。语法如下:
WORKDIR /path/to/workdir
8. ENV:设置环境变量。它们使用键值对,增加运行程序的灵活性。语法如下:
ENV <key> <value>
在运行容器的时候可以通过-e参数来设置或修改环境变量:
docker run -e WEBAPP_PORT=8000 -e WEBAPP_HOST=www.test.com ....
9. USER:镜像正在运行时设置一个UID。语法如下:
USER <uid> //比如:USER root
10. VOLUME:授权访问从容器内到主机上的目录。语法如下:
VOLUME ["/data"]
此挂载一般是在运行容器的时候指定,因为要关联到宿主机上,这样分享Dockerfile就不方便了。多个容器可以通过同一个挂载点共享数据,即便其中一个容器已经停止,挂载点也仍然可以访问,只有当挂载点的容器引用全部消失时,挂载点才会自动删除。
11. ONBUILD: 指定的命令在构建镜像时并不执行,而是在它的子镜像中执行。
ONBUILD中的命令会在当前镜像的子镜像构建时执行。可以把ONBUILD命令当成父镜像的Dockerfile传递给子镜像的Dockerfile的指令。
在子镜像的构建过程中,Docker 会在执行Dockerfile中的任何指令之前,先执行父镜像通过ONBUILD传递的指令。
当从给定镜像构建新镜像时,ONBUILD指令很有用。例如,你可能会在一个语言栈镜像中使用ONBUILD,语言栈镜像用于在Dockerfile中构建用户使用相应语言编写的任意软件,正如 Ruby 的ONBUILD变体
使用ONBUILD构建的镜像应用一个单独的标签,例如:ruby:1.9-onbuild或ruby:2.0-onbuild。
在ONBUILD中使用ADD或COPY时要格外小心。如果新的构建上下文中缺少对应的资源,“onbuild”镜像会灾难性地失败。添加一个单独的标签,允许Dockerfile的作者做出选择,将有助于缓解这种情况。
最后再重点说明一下CMD和ENTRYPOINT命令:
两个命令都是用来指定容器启动时默认运行的命令。
假如,我们指定的ENTRYPOINT是这样的:
ENTRYPOINT ["ls","-l"]
并构建出名为 jiuwu/newImage的镜像,那么 docker run jiuwu/newImage的运行结果与docker run ubuntu ls -l是一样,而docker run jiuwu/newImage -a 的运行结果与docker run ubuntu ls -l -a 一样。
不然发现,ENTRYPOINT与CMD的区别在于运行容器时添加在镜像名之后的参数,对ENTRYPOINT 是拼接,而对CMD则是覆盖。我们可以在运行容器的时候使用--entrypoint来覆盖Dockerfile中的指定:
docker run --entrypoint echo jiuwu/newImage "hello docker"
输出为:hello docker
通常情况下风们会将ENTRYPOINT 和CMD搭配起来使用。ENTRYPOINT 用于指定需要运行的命令,CMD用于指定运行命令所需要的参数(这是个知识点,记住)。示例如下:
ENTRYPOINT ["ls"]
CMD ["-a","-l"]
Dockerfile 最佳实践
来源:oschina
链接:https://my.oschina.net/u/915967/blog/1608777