Docker从入门到放弃

docker ginotang 756℃ 0评论

什么是Docker

Docker是一种为了使应用的创建、部署和运行更简单而设计的容器技术。可以理解容器是一个独立的,隔离的运行空间,里面包含了应用运行所需的一切东西:例如运行库以及其他依赖等。通过把应用打包进容器, 开发人员就可以保证开发环境的一致性,不需要为了更换开发环境而烦恼;有了容器,运维人员的工作更加简单高效。

Docker有如下优点

  • 灵活
  • 轻量
  • 可扩展
  • 跨平台

Docker和虚拟机

一般Docker都会被拿来和虚拟机进行比较,但实际上Docker并不是虚拟机,最大的差别就是虚拟机需要创建一个完整的操作系统;而Docker只需要提供自己的文件系统,它的内核由宿主机提供,因此Docker比虚拟机更加轻量,使用的资源更加少,资源利用率更高。至少,我们可以在虚拟机里运行Docker,但不能在Docker中运行虚拟机。

下图展示了Docker和虚拟机之间的区别

docker vs vms

docker vs vms

Docker的架构

从大的方面讲,Docker包括三个组件:客户端、服务器端和仓库。

  • 客户端通常指docker命令
  • 服务器端则包含docker服务进程和runtime,docker的runtime是runc
  • 仓库(registry)是保存image的地方,我们使用的image都是从指定的仓库拉取

镜像(image)和容器(container)

镜像和容器是Docker中两个基本概念,它们的关系如下:

  • 镜像是应用包裹器,可以理解它是一个有层次的软件包
  • 镜像中的内容添加后就不能修改
  • 容器是镜像的一个实例,即容器生于镜像
  • 容器处于镜像的最上层,容器中的数据可被修改

image和layer

镜像由一系列的layer构成。镜像不能被修改,但它可以被叠加,意思是我们不能修改现有镜像中的数据,但我们可以在现有镜像的基础上添加一层,然后在新添加的层中加入新数据。比如下面的Dockerfile中的命令

上面的Dockerfile包括了4个命令,每个命令执行之后都生成一个layer,每个layer依次叠加,也就是说,这个镜像包含了4个layer。当我们使用这个镜像创建容器,容器就在这个镜像的最上层添加一个可写的layer。这种结构的其中一个好处是资源的复用,减少储存空间的浪费。如图:

container layers

container layers

container和layer

容器和镜像的主要不同就是最上层的可写层,我们对容器的所有修改都保存在这个layer中,但它会随着容器的删除而消失,每个容器都拥有各自的可写层,因此容器之间的数据不会互相受影响。

sharing layers

sharing layers

copy on write(写时复制)策略

当我们修改容器里面的文件的时候,实际上它会从镜像中的只读层(从上往下)搜索,一旦找到需要的文件(停止搜索),就把它复制到容器的可写层进行修改,这种方式称为写时复制。之后对同一文件的修改都只发生在可写层,镜像中的这个文件已经对容器不可见。

安装Docker

Docker提供了社区版(community edition)和企业版(enterprise edition),当然,我们使用的是社区版,系统是centos7

运行第一个docker容器

运行之前先检查docker守护进程是否已经运行

然后就可以尝试运行一个容器

docker run命令用于运行容器,它包含下面几个过程

  • docker 客户端和docker 守护进程通讯
  • 检查本地是否有hell-world镜像,如果没有就从仓库拉取(默认仓库是hub.docker.com)
  • 从hello-world镜像创建一个容器
  • 运行容器

转变为命令如下:

通过容器运行nginx

  • -d的作用是使容器能够在后台运行
  • -p的作用是把宿主机的80端口映射到容器的80端口,否则我们无法在外界访问nginx服务

通常我们需要修改nginx配置文件的参数,这个时候我们就需要进入容器的内部,使用docker exec命令即可:

镜像的创建

虽然dockerhub上面已经有足够多的镜像提供下载,但是我们还是有自定义镜像的需求的。可以通过两种方式创建镜像

  • docker commit 基于现有的镜像进行增强
  • Dockerfile 可以从零创建镜像

docker commit

docker commit命令从已修改的容器创建新的镜像。例如,一般的容器都不会提供vim编辑器,如果要修改容器的文件,可能不是那么方便,我们可以基于现有的镜像安装vim,然后把这个安装了vim的容器创建为一个新的镜像

Dockerfile

Dockerfile可以从零开始创建镜像,但是这种情况是很少遇到的,大多数情况是使用一个基础镜像来创建新镜像。

还是以上面的为例子,在nginx镜像里面添加vim,基本的Dockerfile格式如下:

另一个例子是创建tomcat容器

注意:最后的CMD命令不能使用startup.sh脚本启动tomcat,因为startup.sh执行完毕会退出,最终会导致容器执行完CMD命令后自动停止

准备好Dockerfile之后就可以用docker build命令创建镜像

使用docker build创建镜像

创建运行容器

可以通过docker history查看镜像的构建历史

Dockerfile常用指令

  • FROM

    指定基础镜像

  • MAINTAINER(deprecated)

    维护者的信息,现在建议使用LABEL指令替换

  • COPY

    把文件添加进镜像

  • ADD

    把文件添加进镜像,和COPY不一样的是,如果文件是一个压缩包,会先被解压再放进镜像

  • ENV

    为镜像添加环境变量,ENV之后的所有指令都可以使用由ENV指令添加的变量

  • EXPOSE

    指定docker容器运行时监听的端口

  • CMD

    指定容器运行时执行的命令,多个CMD指令只有最后一个会被执行

  • ENTRYPOINT

    指定容器运行时执行的命令

CMD和ENTRYPOINT的作用十分相似,它们都有两种书写形式,分别是exec形式和shell形式。

CMD的书写形式:

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

ENTRYPOINT的书写形式:

  • ENTRYPOINT ["executable", "param1", "param2"] (exec form, preferred)
  • ENTRYPOINT command param1 param2 (shell form)

我们之前说过,ENV之后的指令都可以使用它定义的环境变量,但在使用CMD和ENTRYPOINT的exec形式的时候,需要特别注意,例如:

上面ENTRYPOINT和CMD指令的原意是使用ENV定义的变量值替换变量$HOME的值,实际上这个替换过程并没有发生,$HOME这里直接作为字符串输出,变量替换失败。如果希望exec形式进行变量替换,则需要显式引用sh,例如:

下表是ENTRYPOINT和CMD指令混合使用时的行为

No ENTRYPOINT ENTRYPOINT exec_entry p1_entry ENTRYPOINT [“exec_entry”, “p1_entry”]
No CMD error, not allowed /bin/sh -c exec_entry p1_entry exec_entry p1_entry
CMD [“exec_cmd”, “p1_cmd”] exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry exec_cmd p1_cmd
CMD [“p1_cmd”, “p2_cmd”] p1_cmd p2_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry p1_cmd p2_cmd
CMD exec_cmd p1_cmd /bin/sh -c exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

从上表可以总结三个比较明显的结论

  • shell形式的ENTRYPOINT指令默认作为sh的一个子进程运行

  • shell形式的ENTRYPOINT指令会阻止CMD指令的执行

  • 只有exec形式的ENTRYPOINT指令可以和CMD指令同时存在

第一个结论造成的问题是:ENTRYPOINT执行的命令不响应任何信号,即当使用docker stop的时候,无法停止由ENTRYPOINT执行的命令。例如:

如果希望改变这种行为,则需要使用exec命令

exec可以进行进程替换,因此top进程会替换为sh的进程ID,这样top就可以对信号作出响应。

自建Registry

Registry是一个镜像仓库,默认所有的镜像都存放在dockerhub上面。如果想把镜像保存在私有的仓库中,可以使用官方提供的registry镜像搭建。

第一步:拉取registry镜像并运行容器

第二步:为将要推送的镜像打tag

镜像完整的repository名称建议为:hostname:portnum/maintainer/imageName,只有官方registry的hostname:portnum可以被省略,因此,镜像要推送到本地仓库,需要提供完整的hostname和portnum。

例如把nginx推送到本地registry

第三步:使用docker push推送镜像

http和https的问题,当使用非localhost作为registry的时候,Registry会拒绝http请求,如果希望强制使用http协议,可以修改/etc/docker/daemon.json文件(如果没有该文件自己创建一个),并添加以下内容:

完成后重启docker守护进程即可。

转载请注明:Pure nonsense » Docker从入门到放弃

喜欢 (0)
0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x
()
x