Docker入门

1-简介

1.1-了解Docker的前生LXC

LXC为Linux Container的简写。可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。相当于C++中的NameSpace。容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。

与传统虚拟化技术相比,它的优势在于:

  • 与宿主机使用同一个内核,性能损耗小;
  • 不需要指令级模拟;
  • 不需要即时(Just-in-time)编译;
  • 容器可以在CPU核心的本地运行指令,不需要任何专门的解释机制;
  • 避免了准虚拟化和系统调用替换中的复杂性;
  • 轻量级隔离,在隔离的同时还提供共享机制,以实现容器与宿主机的资源共享。

总结:Linux Container是一种轻量级的虚拟化的手段。

Linux Container提供了在单一可控主机节点上支持多个相互隔离的server container同时执行的机制。Linux Container有点像chroot,提供了一个拥有自己进程和网络空间的虚拟环境,但又有别于虚拟机,因为lxc是一种操作系统层次上的资源的虚拟化。

1.2-LXC与docker什么关系?

docker并不是LXC替代品,docker底层使用了LXC来实现,LXC将linux进程沙盒化,使得进程之间相互隔离,并且能够课哦内阁制各进程的资源分配。

在LXC的基础之上,docker提供了一系列更强大的功能。

1.3-什么是Docker?

docker是一个开源的应用容器引擎,基于go语言开发并遵循了apache2.0协议开源。
docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的linux服务器,也可以实现虚拟化。
容器是完全使用沙箱机制,相互之间不会有任何接口(类iphone的app),并且容器开销极其低。

Docker 是一个开源的应用容器引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者在本地编译测试通过的容器可以批量地在生产环境中部署,包括VMs(虚拟机)、bare metal、OpenStack 集群和其他的基础应用平台。

简单的理解,Docker类似于集装箱,各式各样的货物,经过集装箱的标准化进行托管,而集装箱和集装箱之间没有影响。也就是说,Docker平台就是一个软件集装箱化平台,这就意味着我们自己可以构建应用程序,将其依赖关系一起打包到一个容器中,然后这容器就很容易运送到其他的机器上进行运行,而且非常易于装载、复制、移除,非常适合软件弹性架构。

因此,就像船只、火车或卡车运输集装箱而不论其内部的货物一样,软件容器充当软件部署的标准单元,其中可以包含不同的代码和依赖项。 按照这种方式容器化软件,开发人员和 IT 专业人员只需进行极少修改或不修改,即可将其部署到不同的环境。

总而言之,Docker 是一个开放平台,使开发人员和管理员可以在称为容器的松散隔离的环境中构建镜像、交付和运行分布式应用程序。以便在开发、QA 和生产环境之间进行高效的应用程序生命周期管理。

Docker官方文档:https://docs.docker.com/

1.4-为什么docker越来越受欢迎

官方话语:

  • 容器化越来越受欢迎,因为容器是:

    • 灵活:即使是最复杂的应用也可以集装箱化。
    • 轻量级:容器利用并共享主机内核。
    • 可互换:您可以即时部署更新和升级。
    • 便携式:您可以在本地构建,部署到云,并在任何地方运行。
    • 可扩展:您可以增加并自动分发容器副本。
    • 可堆叠:您可以垂直和即时堆叠服务。
  • 镜像和容器(contalners):

    通过镜像启动一个容器,一个镜像是一个可执行的包,其中包括运行应用程序所需要的所有内容包含代码,运行时间,库、环境变量、和配置文件。
    容器是镜像的运行实例,当被运行时有镜像状态和用户进程,可以使用docker ps 查看。

  • 容器和虚拟机:

    容器时在linux上本机运行,并与其他容器共享主机的内核,它运行的一个独立的进程,不占用其他任何可执行文件的内存,非常轻量。

    虚拟机运行的是一个完成的操作系统,通过虚拟机管理程序对主机资源进行虚拟访问,相比之下需要的资源更多。

1.5-容器和虚拟机的区别

  • 启动时间:Docker秒级启动,虚拟机分钟级启动。

  • 轻量级:docker镜像大小通常以M为单位,虚拟机以G为单位。容器资源占用小,要比虚拟机部署更快速。

  • 性能:docker共享宿主机内核,系统级虚拟化,占用资源少,没有Hypervisor层开销,性能基本接近物理机; 虚拟机需要Hypervisor层支持,虚拟化一些设备,具有完整的GuestOS,虚拟化开销大,因而降低性能,没有容器性能好。

  • 安全性:由于共享宿主机内核,只是进程级隔离,因此隔离性和稳定性不如虚拟机,docker具有一定权限访问宿主机内核,存在一定安全隐患。

  • 使用要求:VM基于硬件的完全虚拟化,需要硬件CPU虚拟化技术支持; docker共享宿主机内核,可运行在主流的Linux发行版,不用考虑CPU是否支持虚拟化技术。

总结:虚拟机更擅长于彻底隔离整个运行环境。例如,云服务提供商通常采用虚拟机技术隔离不同的用户。

docker虚拟机
相同点1. 都可在不同的主机之间迁移
2. 都具备 root 权限
3. 都可以远程控制
4. 都有备份、回滚操作
操作系统在性能上有优势,可以轻易的运行多个操作系统可以安装任何系统,但是性能不及容器
原理和宿主机共享内核,所有容器运行在容器引擎之上,容器并非一个完整的操作系统,所有容器共享操作系统,在进程级进行隔离每一个虚拟机都建立在虚拟的硬件之上,提供指令级的虚拟,具备一个完整的操作系统
资源管理弹性资源分配:资源可以在没有关闭容器的情况下添加,数据卷也无需重新分配大小虚拟机需要重启,虚拟机里边的操作系统需要处理新加入的资源,如磁盘等,都需要重新分区。
远程管理根据操作系统的不同,可以通过 shell 或者远程桌面进行远程控制由虚拟化平台提供,可以在虚拟机启动之前连接
配置快速,基本上是一键配置配置时间长
启动时间秒级分钟级
硬盘使用MBGB
性能接近原生态弱于原生态
系统支持数量单机支持上千个一般不多于几十个
优点高效、集中。一个硬件节点可以运行数以百计的的容器,非常节省资源,QoS 会尽量满足,但不保证一定满足。内核由提供者升级,服务由服务提供者管理对操作系统具有绝对权限,对系统版本和系统升级具有完全的管理权限。具有一整套的的资源:CPU、RAM 和磁盘。QoS 是有保证的,每一个虚拟机就像一个真实的物理机一样,可以实现不同的操作系统运行在同一物理节点上。
缺点对内核没有控制权限,只有容器的提供者具备升级权限。只有一个内核运行在物理节点上,几乎不能实现不同的操作系统混合。容器提供者一般仅提供少数的几个操作系统每一台虚拟机都具有更大的负载,耗费更多的资源,用户需要全权维护和管理。一台物理机上能够运行的虚拟机非常有限

1.6-docker版本

  • Docker Community Edition(CE)社区版

  • Enterprise Edition(EE) 商业版

1.7-docker和openstack的对比

1.8-容器在内核中支持2种重要技术

docker本质就是宿主机的一个进程,docker是通过namespace实现资源隔离,通过cgroup实现资源限制,通过写时复制技术(copy-on-write)实现了高效的文件操作(类似虚拟机的磁盘比如分配500g并不是实际占用物理磁盘500g)

  • namespaces 名称空间

  • control Group 控制组

    cgroup的特点是:      

    • cgroup的api以一个伪文件系统的实现方式,用户的程序可以通过文件系统实现cgroup的组件管理
    • cgroup的组件管理操作单元可以细粒度到线程级别,另外用户可以创建和销毁cgroup,从而实现资源载分配和再利用
    • 所有资源管理的功能都以子系统的方式实现,接口统一子任务创建之初与其父任务处于同一个cgroup的控制组

    四大功能:

    • 资源限制:可以对任务使用的资源总额进行限制
    • 优先级分配:通过分配的cpu时间片数量以及磁盘IO带宽大小,实际上相当于控制了任务运行优先级
    • 资源统计:可以统计系统的资源使用量,如cpu时长,内存用量等
    • 任务控制:cgroup可以对任务执行挂起、恢复等操作

1.9-docker的基本概念

  • image

    镜像:一个特殊的文件系统

    操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。

    Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。

    镜像不包含任何动态数据,其内容在构建之后也不会被改变。

    Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。 镜像实际是由多层文件系统联合组成。

    镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。

    比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。

    在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。

    因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。

    分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。

    docker镜像就是一个只读模板,比如,一个镜像可以包含一个完整的centos,里面仅安装apache或用户的其他应用,镜像可以用来创建docker容器,另外docker提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下周一个已经做好的镜像来直接使用。

  • container容器

    容器:镜像运行时的实体

    镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等 。

    容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。前面讲过镜像使用的是分层存储,容器也是如此。

    容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。

    按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据 ,容器存储层要保持无状态化。

    所有的文件写入操作,都应该使用数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。

    数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此, 使用数据卷后,容器可以随意删除、重新 run,数据却不会丢失。

    注意:

    容器在整个应用程序生命周期工作流中提供以下优点:隔离性、可移植性、灵活性、可伸缩性和可控性。 最重要的优点是可在开发和运营之间提供隔离。

    docker利用容器来运行应用,容器是从镜像创建的运行实例,它可以被启动,开始、停止、删除、每个容器都是互相隔离的,保证安全的平台,可以把容器看做是要给简易版的linux环境(包括root用户权限、镜像空间、用户空间和网络空间等)和运行再其中的应用程序。

  • repostory仓库

    仓库:集中存放镜像文件的地方

    镜像构建完成后,可以很容易的在当前宿主上运行,但是, 如果需要在其他服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。

    一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。

    所以说,镜像仓库是 Docker 用来集中存放镜像文件的地方,类似于我们之前常用的代码仓库。

    通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本 。

    我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

    这里补充一下 Docker Registry 公开服务和私有 Docker Registry 的概念:

    Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。

    一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。

    最常使用的 Registry 公开服务是官方的 Docker Hub ,这也是默认的 Registry,并拥有大量的高质量的官方镜像,网址为:hub.docker.com/ 。

    在国内访问 Docker Hub 可能会比较慢,国内也有一些云服务商提供类似于 Docker Hub 的公开服务。

    除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry 。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。

    开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 Docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。

    仓库是集中存储镜像文件的沧桑,registry是仓库主从服务器,实际上参考注册服务器上存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)

    仓库分为两种,公有参考,和私有仓库,最大的公开仓库是docker Hub,存放了数量庞大的镜像供用户下周,国内的docker pool,这里仓库的概念与Git类似,registry可以理解为github这样的托管服务。

1.10-docker的主要用途

  • 简化配置

    虚拟机的最大好处是能在你的硬件设施上运行各种配置不一样的平台(软件、系统),Docker在降低额外开销的情况下提供了同样的功能。它能让你将运行环境和配置放在代码中然后部署,同一个Docker的配置可以在不同的环境中使用,这样就降低了硬件要求和应用环境之间耦合度。

    简单的来说,容器镜像打包完成后,它就是个独立的个体了,丢到哪里都能跑,而无需针对各个平台去独立配置。

  • 代码流水线(Code Pipeline)管理

    前一个场景对于管理代码的流水线起到了很大的帮助。代码从开发者的机器到最终在生产环境上的部署,需要经过很多的中间环境。而每一个中间环境都有自己微小的差别,Docker给应用提供了一个从开发到上线均一致的环境,让代码的流水线变得简单不少。

  • 提高开发效率

    不同的开发环境中,我们都想把两件事做好。一是我们想让开发环境尽量贴近生产环境,二是我们想快速搭建开发环境。

    使用Docker非常简单的就能够实现这两点,而且哪怕是开发环境的机器配置一般的情况下搭建多个生成服务应用。一台一般配置服务器或开发机也能轻松的跑起多个Docker应用,而无需额外增加机器配置。因为Docker有个非常NB的特性,拥有虚拟化的特性,而几乎没有额外的开销。

  • 隔离应用

    很多情况下,我们需要在一台服务器上运行多个不同的应用,比如上面提到的提高开发效率的场景等。

    我们经常需要考虑三点,一是因为要降低成本而进行服务器整合,二是将一个整体式的应用拆分成松耦合的单个服务(比如微服务架构),三是还需要考虑应用之间的兼容性。而对于Docker来说,支持起来就非常简单了。同一台机器,我可以同时运行N个Docker web应用,托管到不同的Web服务器(Kestrel、Ngnix、Tomcat),而无需担心他们会搞起3Q大战,也无需担心我的开发机器会跑不起来。

  • 整合服务器

    正如通过虚拟机来整合多个应用,Docker隔离应用的能力使得Docker可以整合多个服务器以降低成本。由于没有多个操作系统的内存占用,以及能在多个实例之间共享没有使用的内存,Docker可以比虚拟机提供更好的服务器整合解决方案。

    这就意味着资源得到更有效的利用——可以做更多衣服,而且还没有边角料,成本还更低。

  • 调试能力

    Docker提供了很多的工具,这些工具不一定只是针对容器,但是却适用于容器。它们提供了很多的功能,包括可以为容器设置检查点、设置版本和查看两个容器之间的差别,这些特性可以帮助调试Bug。

  • 多租户环境

    在多租户的应用中,它可以避免关键应用的重写。比如IoT(物联网)的应用中,开发一个快速、易用的多租户环境。这种多租户的基本代码非常复杂,很难处理,重新规划这样一个应用不但消耗时间,也浪费金钱。

    使用Docker,可以为每一个租户的应用层的多个实例创建隔离的环境,这不仅简单而且成本低廉,当然这一切得益于Docker环境的启动速度和其高效的diff命令。

    就如同我们现在写了一个不支持多租户的业务程序,而实际的业务中经常会出现需要支持多租户或者有新客户需要使用的场景,这是我们通常的简单做法是——部署一套新的代码。当站点达到一定量的适合,要么重写程序,要么维护人员Game over。

  • 快速部署

    在虚拟机之前,引入新的硬件资源需要消耗几天的时间。虚拟化技术(Virtualization)将这个时间缩短到了分钟级别。而Docker通过为进程仅仅创建一个容器而无需启动一个操作系统,再次将这个过程缩短到了秒级。

    你可以在服务器中或云端创建销毁资源而无需担心重新启动带来的开销。通常情况下,服务器的资源利用率只有30%,而通过使用Docker并进行有效的资源分配可以提高资源的利用率。

Docker通常用于如下场景:

  • web应用的自动化打包和发布;
  • 自动化测试和持续集成、发布;
  • 在服务型环境中部署和调整数据库或其他的后台应用;
  • 从头编译或者扩展现有的OpenShift或Cloud Foundry平台来搭建自己的PaaS环境。

官方就是Bulid 、ship、run any app/any where,编译、装载、运行、任何app/在任意地放都能运行。

就是实现了应用的封装、部署、运行的生命周期管理只要在glibc的环境下,都可以运行。

运维生成环境中:docker化。

  • 发布服务不用担心服务器的运行环境,所有的服务器都是自动分配docker,自动部署,自动安装,自动运行
  • 再不用担心其他服务引擎的磁盘问题,cpu问题,系统问题了
  • 资源利用更出色
  • 自动迁移,可以制作镜像,迁移使用自定义的镜像即可迁移,不会出现什么问题
  • 管理更加方便了

1.11-docker改变了什么?

综上所述,Docker到底改变了什么?笔者是这么理解的:

  • Docker改变了云服务,使云服务的共融共通的理想逐步成为了可能。并且Docker 已经是云策略的一部分,许多开发者正在计划使用 Docker 将业务迁移到云端。另外,为了避免被云服务供应商绑定,Docker成为很多开发者的首选。
  • Docker改变了产品交付,为产品的整个生命周期提供了一整套的解决方案和流程。
  • Docker改变了开发方式,提供了简化的环境配置、封装的运行环境以及统一的环境。并且提供了快速部署的方式。
  • Docker改变了测试,多版本测试变得极为方便,快速构建测试环境也变得更加简单并且无需开发人员干预或者搭建。
  • Docker改变了运维,环境的一致性让运维变得更加简单,同时热更新的支持让运维不再需要半夜加班部署更新,更新可以随时进行。当出现重大问题时,还能快速回滚到指定版本。
  • Docker改变了架构,自动化扩容支持让架构变得更加简单,分布式系统也更加易于搭建和支持。同时遗留的单体应用也很易于转变为现代应用。

总之,在某种程度上,Docker改变了产品开发中的一些游戏规则。虽然Docker是一项技术,但是它也带来了新的思维,新的流程和工作方法,Docker在推动行业的发展,Docker已经在改变世界,并且在逐步的变为事实……

  • 面向产品:产品交付
  • 面向开发:简化环境配置
  • 面向测试:多版本测试
  • 面向运维:环境一致性
  • 面向架构:自动化扩容(微服务)

2-docker架构

2.1-总体架构

  • distribution 负责与docker registry交互,上传洗澡镜像以及v2 registry 有关的源数据
  • registry负责docker registry有关的身份认证、镜像查找、镜像验证以及管理registry mirror等交互操作
  • image 负责与镜像源数据有关的存储、查找,镜像层的索引、查找以及镜像tar包有关的导入、导出操作
  • reference负责存储本地所有镜像的repository和tag名,并维护与镜像id之间的映射关系
  • layer模块负责与镜像层和容器层源数据有关的增删改查,并负责将镜像层的增删改查映射到实际存储镜像层文件的graphdriver模块
  • graghdriver是所有与容器镜像相关操作的执行者

2.2-docker架构2

如果觉得上面架构图比较乱可以看这个架构:

从上图不难看出,用户是使用Docker Client与Docker Daemon建立通信,并发送请求给后者。

而Docker Daemon作为Docker架构中的主体部分,首先提供Server的功能使其可以接受Docker Client的请求;而后Engine执行Docker内部的一系列工作,每一项工作都是以一个Job的形式的存在。

Job的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动graphdriver将下载镜像以Graph的形式存储;当需要为Docker创建网络环境时,通过网络管理驱动networkdriver创建并配置Docker容器网络环境;当需要限制Docker容器运行资源或执行用户指令等操作时,则通过execdriver来完成。

而libcontainer是一项独立的容器管理包,networkdriver以及execdriver都是通过libcontainer来实现具体对容器进行的操作。当执行完运行容器的命令后,一个实际的Docker容器就处于运行状态,该容器拥有独立的文件系统,独立并且安全的运行环境等。

2.3-docker架构3

再来看看另外一个架构,这个个架构就简单清晰指明了server/client交互,容器和镜像、数据之间的一些联系。

docker daemon就是docker的守护进程即server端,可以是远程的,也可以是本地的,这个不是C/S架构吗,客户端Docker client 是通过rest api进行通信。

docker cli 用来管理容器和镜像,客户端提供一个只读镜像,然后通过镜像可以创建多个容器,这些容器可以只是一个RFS(Root file system根文件系统),也可以ishi一个包含了用户应用的RFS,容器再docker client中只是要给进程,两个进程之间互不可见。

用户不能与server直接交互,但可以通过与容器这个桥梁来交互,由于是操作系统级别的虚拟技术,中间的损耗几乎可以不计。

2.4-docker架构各个模块的功能

主要的模块有:Docker Client、Docker Daemon、Docker Registry、Graph、Driver、libcontainer以及Docker container。   

1. docker client

docker client 是docker架构中用户用来和docker daemon建立通信的客户端,用户使用的可执行文件为docker,通过docker命令行工具可以发起众多管理container的请求。

docker client可以通过一下三宗方式和docker daemon建立通信:tcp://host:port;unix:path_to_socket;fd://socketfd。,docker client可以通过设置命令行flag参数的形式设置安全传输层协议(TLS)的有关参数,保证传输的安全性。

docker client发送容器管理请求后,由docker daemon接受并处理请求,当docker client 接收到返回的请求相应并简单处理后,docker client 一次完整的生命周期就结束了,当需要继续发送容器管理请求时,用户必须再次通过docker可以执行文件创建docker client。

2.docker daemon

docker daemon 是docker架构中一个常驻在后台的系统进程,功能是:接收处理docker client发送的请求。该守护进程在后台启动一个server,server负载接受docker client发送的请求;接受请求后,server通过路由与分发调度,找到相应的handler来执行请求。

docker daemon启动所使用的可执行文件也为docker,与docker client启动所使用的可执行文件docker相同,在docker命令执行时,通过传入的参数来判别docker daemon与docker client。

docker daemon的架构可以分为:docker serverenginejobdaemon

3. docker server

docker server在docker架构中时专门服务于docker client的server,该server的功能时:接受并调度分发docker client发送的请求,架构图如下:

在Docker的启动过程中,通过包gorilla/mux(golang的类库解析),创建了一个mux.Router,提供请求的路由功能。在Golang中,gorilla/mux是一个强大的URL路由器以及调度分发器。该mux.Router中添加了众多的路由项,每一个路由项由HTTP请求方法(PUT、POST、GET或DELETE)、URL、Handler三部分组成。

若Docker Client通过HTTP的形式访问Docker Daemon,创建完mux.Router之后,Docker将Server的监听地址以及mux.Router作为参数,创建一个httpSrv=http.Server{},最终执行httpSrv.Serve()为请求服务。

在Server的服务过程中,Server在listener上接受Docker Client的访问请求,并创建一个全新的goroutine来服务该请求。在goroutine中,首先读取请求内容,然后做解析工作,接着找到相应的路由项,随后调用相应的Handler来处理该请求,最后Handler处理完请求之后回复该请求。

需要注意的是:Docker Server的运行在Docker的启动过程中,是靠一个名为”serveapi”的job的运行来完成的。原则上,Docker Server的运行是众多job中的一个,但是为了强调Docker Server的重要性以及为后续job服务的重要特性,将该”serveapi”的job单独抽离出来分析,理解为Docker Server。

4. engine

Engine是Docker架构中的运行引擎,同时也Docker运行的核心模块。它扮演Docker container存储仓库的角色,并且通过执行job的方式来操纵管理这些容器。

在Engine数据结构的设计与实现过程中,有一个handler对象。该handler对象存储的都是关于众多特定job的handler处理访问。举例说明,Engine的handler对象中有一项为:{“create”: daemon.ContainerCreate,},则说明当名为”create”的job在运行时,执行的是daemon.ContainerCreate的handler。

5. job

一个Job可以认为是Docker架构中Engine内部最基本的工作执行单元。Docker可以做的每一项工作,都可以抽象为一个job。例如:在容器内部运行一个进程,这是一个job;创建一个新的容器,这是一个job,从Internet上下载一个文档,这是一个job;包括之前在Docker Server部分说过的,创建Server服务于HTTP的API,这也是一个job,等等。

Job的设计者,把Job设计得与Unix进程相仿。比如说:Job有一个名称,有参数,有环境变量,有标准的输入输出,有错误处理,有返回状态等。

6. docker registry

Docker Registry是一个存储容器镜像的仓库。而容器镜像是在容器被创建时,被加载用来初始化容器的文件架构与目录。

在Docker的运行过程中,Docker Daemon会与Docker Registry通信,并实现搜索镜像、下载镜像、上传镜像三个功能,这三个功能对应的job名称分别为”search”,”pull” 与 “push”。

其中,在Docker架构中,Docker可以使用公有的Docker Registry,即大家熟知的Docker Hub,如此一来,Docker获取容器镜像文件时,必须通过互联网访问Docker Hub;同时Docker也允许用户构建本地私有的Docker Registry,这样可以保证容器镜像的获取在内网完成。

7. Graph

Graph在Docker架构中扮演已下载容器镜像的保管者,以及已下载容器镜像之间关系的记录者。一方面,Graph存储着本地具有版本信息的文件系统镜像,另一方面也通过GraphDB记录着所有文件系统镜像彼此之间的关系。

Graph的架构如下:

其中,GraphDB是一个构建在SQLite之上的小型图数据库,实现了节点的命名以及节点之间关联关系的记录。它仅仅实现了大多数图数据库所拥有的一个小的子集,但是提供了简单的接口表示节点之间的关系。

同时在Graph的本地目录中,关于每一个的容器镜像,具体存储的信息有:该容器镜像的元数据,容器镜像的大小信息,以及该容器镜像所代表的具体rootfs。

8. driver

Driver是Docker架构中的驱动模块。通过Driver驱动,Docker可以实现对Docker容器执行环境的定制。由于Docker运行的生命周期中,并非用户所有的操作都是针对Docker容器的管理,另外还有关于Docker运行信息的获取,Graph的存储与记录等。因此,为了将Docker容器的管理从Docker Daemon内部业务逻辑中区分开来,设计了Driver层驱动来接管所有这部分请求。

在Docker Driver的实现中,可以分为以下三类驱动:graphdriver、networkdriver和execdriver。

graphdriver主要用于完成容器镜像的管理,包括存储与获取。即当用户需要下载指定的容器镜像时,graphdriver将容器镜像存储在本地的指定目录;同时当用户需要使用指定的容器镜像来创建容器的rootfs时,graphdriver从本地镜像存储目录中获取指定的容器镜像。

在graphdriver的初始化过程之前,有4种文件系统或类文件系统在其内部注册,它们分别是aufs、btrfs、vfs和devmapper。而Docker在初始化之时,通过获取系统环境变量”DOCKER_DRIVER”来提取所使用driver的指定类型。而之后所有的graph操作,都使用该driver来执行。

graphdriver的架构如下:

networkdriver的用途是完成Docker容器网络环境的配置,其中包括Docker启动时为Docker环境创建网桥;Docker容器创建时为其创建专属虚拟网卡设备;以及为Docker容器分配IP、端口并与宿主机做端口映射,设置容器防火墙策略等。networkdriver的架构如下:

execdriver作为Docker容器的执行驱动,负责创建容器运行命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。在execdriver的实现过程中,原先可以使用LXC驱动调用LXC的接口,来操纵容器的配置以及生命周期,而现在execdriver默认使用native驱动,不依赖于LXC。

具体体现在Daemon启动过程中加载的ExecDriverflag参数,该参数在配置文件已经被设为”native”。这可以认为是Docker在1.2版本上一个很大的改变,或者说Docker实现跨平台的一个先兆。execdriver架构如下:

9. libcontainer

libcontainer是Docker架构中一个使用Go语言设计实现的库,设计初衷是希望该库可以不依靠任何依赖,直接访问内核中与容器相关的API。
正是由于libcontainer的存在,Docker可以直接调用libcontainer,而最终操纵容器的namespace、cgroups、apparmor、网络设备以及防火墙规则等。这一系列操作的完成都不需要依赖LXC或者其他包。libcontainer架构如下:

另外,libcontainer提供了一整套标准的接口来满足上层对容器管理的需求。或者说,libcontainer屏蔽了Docker上层对容器的直接管理。又由于libcontainer使用Go这种跨平台的语言开发实现,且本身又可以被上层多种不同的编程语言访问,因此很难说,未来的Docker就一定会紧紧地和Linux捆绑在一起。而于此同时,Microsoft在其著名云计算平台Azure中,也添加了对Docker的支持,可见Docker的开放程度与业界的火热度。

暂不谈Docker,由于libcontainer的功能以及其本身与系统的松耦合特性,很有可能会在其他以容器为原型的平台出现,同时也很有可能催生出云计算领域全新的项目。

10. docker container

Docker container(Docker容器)是Docker架构中服务交付的最终体现形式。

Docker按照用户的需求与指令,订制相应的Docker容器:

  • 用户通过指定容器镜像,使得Docker容器可以自定义rootfs等文件系统;
  • 用户通过指定计算资源的配额,使得Docker容器使用指定的计算资源;
  • 用户通过配置网络及其安全策略,使得Docker容器拥有独立且安全的网络环境;
  • 用户通过指定运行的命令,使得Docker容器执行指定的工作。

![](https://gitee.com/lemon-cs/images/raw/master/docker container.png)

3-docker的使用

3.1-安装(CentOS)

yum install docker -y 
systemctl enable docker
systemctl start docker

注意:启动前应当设置源

vim /usr/lib/systemd/system/docker.service

这里设置阿里的,注册阿里云账户号每个用户都有:

[root@web1 ~]# vim /usr/lib/systemd/system/docker.service

[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.com
After=network.target
Wants=docker-storage-setup.service
Requires=docker-cleanup.timer

[Service]
Type=notify
NotifyAccess=main
EnvironmentFile=-/run/containers/registries.conf
EnvironmentFile=-/etc/sysconfig/docker
EnvironmentFile=-/etc/sysconfig/docker-storage
EnvironmentFile=-/etc/sysconfig/docker-network
Environment=GOTRACEBACK=crash
Environment=DOCKER_HTTP_HOST_COMPAT=1
Environment=PATH=/usr/libexec/docker:/usr/bin:/usr/sbin
ExecStart=/usr/bin/dockerd-current --registry-mirror=https://rfcod7oz.mirror.aliyuncs.com #这个值可以登陆阿里云账号请参考下图
--add-runtime docker-runc=/usr/libexec/docker/docker-runc-current
--default-runtime=docker-runc
--exec-opt native.cgroupdriver=systemd
--userland-proxy-path=/usr/libexec/docker/docker-proxy-current
--init-path=/usr/libexec/docker/docker-init-current
--seccomp-profile=/etc/docker/seccomp.json
$OPTIONS
$DOCKER_STORAGE_OPTIONS
$DOCKER_NETWORK_OPTIONS
$ADD_REGISTRY
$BLOCK_REGISTRY
$INSECURE_REGISTRY
$REGISTRIES
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
TimeoutStartSec=0
Restart=on-abnormal
KillMode=process

[Install]
WantedBy=multi-user.target

3.2-docker版本查询

[root@web1 ~]# docker version
Client:
Version: 1.13.1
API version: 1.26
Package version: docker-1.13.1-96.gitb2f74b2.el7.centos.x86_64
Go version: go1.10.3
Git commit: b2f74b2/1.13.1
Built: Wed May 1 14:55:20 2019
OS/Arch: linux/amd64

Server:
Version: 1.13.1
API version: 1.26 (minimum version 1.12)
Package version: docker-1.13.1-96.gitb2f74b2.el7.centos.x86_64
Go version: go1.10.3
Git commit: b2f74b2/1.13.1
Built: Wed May 1 14:55:20 2019
OS/Arch: linux/amd64
Experimental: false

3.3-搜索下载镜像

docker pull alpine          #下载镜像
docker search nginx          #查看镜像
docker pull nginx

3.4-查看已经下载的镜像

[root@web1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
zxg/my_nginx v1 b164f4c07c64 8 days ago 126 MB
zxg/my_nginx latest f07837869dfc 8 days ago 126 MB
docker.io/nginx latest e445ab08b2be 2 weeks ago 126 MB
docker.io/alpine latest b7b28af77ffe 3 weeks ago 5.58 MB
docker.io/centos latest 9f38484d220f 4 months ago 202 MB
[root@web1 ~]#

3.5-导出镜像

docker  save nginx >/tmp/nginx.tar.gz

3.6-删除镜像

docker rmi -f nginx

3.7-导入镜像

docker load </tmp/nginx.tar.gz

3.8-默认配置文件

vim /usr/lib/systemd/system/docker.service

[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.com
After=network.target
Wants=docker-storage-setup.service
Requires=docker-cleanup.timer

[Service]
Type=notify
NotifyAccess=main
EnvironmentFile=-/run/containers/registries.conf
EnvironmentFile=-/etc/sysconfig/docker
EnvironmentFile=-/etc/sysconfig/docker-storage
EnvironmentFile=-/etc/sysconfig/docker-network
Environment=GOTRACEBACK=crash
Environment=DOCKER_HTTP_HOST_COMPAT=1
Environment=PATH=/usr/libexec/docker:/usr/bin:/usr/sbin
ExecStart=/usr/bin/dockerd-current --registry-mirror=https://rfcod7oz.mirror.aliyuncs.com
--add-runtime docker-runc=/usr/libexec/docker/docker-runc-current
--default-runtime=docker-runc
--exec-opt native.cgroupdriver=systemd
--userland-proxy-path=/usr/libexec/docker/docker-proxy-current
--init-path=/usr/libexec/docker/docker-init-current
--seccomp-profile=/etc/docker/seccomp.json
$OPTIONS
$DOCKER_STORAGE_OPTIONS
$DOCKER_NETWORK_OPTIONS
$ADD_REGISTRY
$BLOCK_REGISTRY
$INSECURE_REGISTRY
$REGISTRIES
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
TimeoutStartSec=0
Restart=on-abnormal
KillMode=process

[Install]
WantedBy=multi-user.target
~
~
~
~

如果更改存储目录就添加  

--graph=/opt/docker

如果更改DNS——默认采用宿主机的dns

--dns=xxxx的方式指定

3.9-运行hello world

这里用centos镜像echo一个hello word

[root@web1 overlay2]#  docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
zxg/my_nginx v1 b164f4c07c64 8 days ago 126 MB
zxg/my_nginx latest f07837869dfc 8 days ago 126 MB
docker.io/nginx latest e445ab08b2be 2 weeks ago 126 MB
docker.io/alpine latest b7b28af77ffe 3 weeks ago 5.58 MB
docker.io/centos latest 9f38484d220f 4 months ago 202 MB
[root@web1 overlay2]# docker run centos echo "hello world"
hello world
[root@web1 overlay2]#

3.10-运行一个容器(run)

[root@web1 overlay2]# docker run -it alpine sh   #运行并进入alpine
/ #
/ #
/ #
/ #
/ #
/ # ls
bin etc lib mnt proc run srv tmp var
dev home media opt root sbin sys usr
/ # cd tmp
/tmp # exit

后台运行(-d后台运行)(–name添加一个名字)


[root@web1 overlay2]# docker run -it -d --name test1 alpine
ac46c019b800d34c37d4f9dcd56c974cb82eca3acf185e5f8f80c8a60075e343
[root@web1 overlay2]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ac46c019b800 alpine "/bin/sh" 5 seconds ago Up 3 seconds test1
[root@web1 overlay2]#

还有一种-rm参数,ctrl+c后就删除,可以测试环境用,生成环境用的少

[root@web1 overlay2]# docker run -it  --rm --name centos nginx
^C[root@web1 overlay2]#
##另开一个窗口
[root@web1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3397b96ea7bd nginx "nginx -g 'daemon ..." 27 seconds ago Up 25 seconds 80/tcp centos
ac46c019b800 alpine "/bin/sh" 4 minutes ago Up 4 minutes test1
[root@web1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ac46c019b800 alpine "/bin/sh" 4 minutes ago Up 4 minutes test1
[root@web1 ~]#

输出详情介绍:

CONTAINER ID: 容器 ID。

IMAGE: 使用的镜像。

COMMAND: 启动容器时运行的命令。

CREATED: 容器的创建时间。

STATUS: 容器状态。

状态有7种:

  • created(已创建)
  • restarting(重启中)
  • running(运行中)
  • removing(迁移中)
  • paused(暂停)
  • exited(停止)
  • dead(死亡)

PORTS: 容器的端口信息和使用的连接类型(tcp\udp)。

NAMES: 自动分配的容器名称。

3.11-如何进入容器

三种方法,上面已经演示了一种
第一种,需要容器本身的pid及util-linux,不推荐,暂时不演示了
第二种,不分配bash终端的一种实施操作,不推荐,这种操作如果在开一个窗口也能看到操作的指令,所有人都能看到。

[root@web1 overlay2]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9fc796e928d7 nginx "sh" 2 minutes ago Up 8 seconds 80/tcp mynginx
ac46c019b800 alpine "/bin/sh" 12 minutes ago Up 12 minutes test1
[root@web1 overlay2]# docker attach mynginx

#
#
#
#
# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
# exit  
[root@web1 overlay2]# docker attach mynginx
You cannot attach to a stopped container, start it first
[root@web1 overlay2]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ac46c019b800 alpine "/bin/sh" 13 minutes ago Up 13 minutes test1
[root@web1 overlay2]#

第三种:exec方式,终端时分开的,推荐

[root@web1 overlay2]# docker exec -it mynginx sh
#
#
#
# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
# exit
[root@web1 overlay2]#
[root@web1 overlay2]#
[root@web1 overlay2]#
[root@web1 overlay2]# docker pa
docker: 'pa' is not a docker command.
See 'docker --help'
[root@web1 overlay2]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6fc2d091cfe9 nginx "nginx -g 'daemon ..." 45 seconds ago Up 43 seconds 80/tcp mynginx
ac46c019b800 alpine "/bin/sh" 16 minutes ago Up 16 minutes test1

3.12-查看docker进程及删除容器

上面已经演示:

[root@web1 overlay2]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9fc796e928d7 nginx "sh" 2 minutes ago Up 8 seconds 80/tcp mynginx
ac46c019b800 alpine "/bin/sh" 12 minutes ago Up 12 minutes test1

[root@web1 overlay2]# docker ps -a       #-a :显示所有的容器,包括未运行的
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9fc796e928d7 nginx "sh" 4 minutes ago Exited (0) About a minute ago mynginx
ac46c019b800 alpine "/bin/sh" 15 minutes ago Up 15 minutes test1
3bf234febeaa alpine "sh" 17 minutes ago Exited (0) 16 minutes ago youthful_lumiere
ab113c63f0b4 centos "echo 'hello world'" 31 minutes ago Exited (0) 31 minutes ago infallible_torvalds
b326027dcf42 zxg/my_nginx "nginx" 8 days ago Exited (0) 8 days ago my_nginx
4f1f1ca319f2 centos "bash" 8 days ago Exited (137) 8 days ago musing_lichterman
64b4e32991c7 nginx "nginx -g 'daemon ..." 12 days ago Exited (0) 12 days ago mynginx1
aee506fe7b5a alpine "sh" 12 days ago Created infallible_haibt
70620c73b9a0 alpine "sh" 12 days ago Created gallant_volhard
7655cbf87bb0 alpine "sh" 12 days ago Created agitated_brahmagupta
33fb949372e8 fce289e99eb9 "/hello" 12 days ago Created elastic_dijkstra
9de47616aea4 fce289e99eb9 "/hello" 13 days ago Created confident_fermi
[root@web1 overlay2]# docker rm 9fc796e928d7 #rm时删除一个或多个容器
9fc796e928d7

3.13-查看容器详细信息

并不需要进入到容器里面,通过查看详细信息看到了刚才运行的nginx,宿主机curl ip地址访问一下运行情况。

docker inspect mynginx

3.14-查看日志

[root@web1 ~]# docker logs  -f mynginx

4-docker容器使用

  • 获取镜像

    如果我们本地没有 ubuntu 镜像,我们可以使用 docker pull 命令来载入 ubuntu 镜像:

    $ docker pull ubuntu
  • 启动容器

    以下命令使用 ubuntu 镜像启动一个容器,参数为以命令行模式进入该容器:

    $ docker run -it ubuntu /bin/bash

    参数说明:

    • -i: 交互式操作。
    • -t: 终端。
    • ubuntu: ubuntu 镜像。
    • /bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。

    要退出终端,直接输入 exit:

    root@ed09e4490c57:/# exit
  • 启动已停止运行的容器

    查看所有的容器命令如下:

    $ docker ps -a

    使用 docker start 启动一个已停止的容器:

    $ docker start b750bbbcfd88 
  • 后台运行

    在大部分的场景下,我们希望 docker 的服务是在后台运行的,我们可以过 -d 指定容器的运行模式。

    $ docker run -itd --name ubuntu-test ubuntu /bin/bash

    注:加了 -d 参数默认不会进入容器,想要进入容器需要使用指令 docker exec(下面会介绍到)。

  • 停止一个容器

    停止容器的命令如下:

    $ docker stop <容器 ID>

    停止的容器可以通过 docker restart 重启:

    $ docker restart <容器 ID>
  • 进入容器

    在使用 -d 参数时,容器启动后会进入后台。此时想要进入容器,可以通过以下指令进入:

    • docker attach
    • docker exec:推荐大家使用 docker exec 命令,因为此退出容器终端,不会导致容器的停止。

    attach 命令

    下面演示了使用 docker attach 命令。

    $ docker attach 1e560fca3906 

    注意: 如果从这个容器退出,会导致容器的停止。

    exec 命令

    下面演示了使用 docker exec 命令。

    docker exec -it 243c32535da7 /bin/bash

    注意: 如果从这个容器退出,不会导致容器的停止,这就是为什么推荐大家使用 docker exec 的原因。

    更多参数说明请使用 docker exec –help 命令查看。

  • 导出和导入容器

    • 导出容器

      如果要导出本地某个容器,可以使用 docker export 命令。

      $ docker export 1e560fca3906 > ubuntu.tar

      导出容器 1e560fca3906 快照到本地文件 ubuntu.tar。

    • 导入容器快照

      可以使用 docker import 从容器快照文件中再导入为镜像,以下实例将快照文件 ubuntu.tar 导入到镜像 test/ubuntu:v1:

      $ cat docker/ubuntu.tar | docker import - test/ubuntu:v1
    • 此外,也可以通过指定 URL 或者某个目录来导入,例如:

      $ docker import http://example.com/exampleimage.tgz example/imagerepo
  • 删除容器

    删除容器使用 docker rm 命令:

    $ docker rm -f 1e560fca3906
  • 运行一个 web 应用

    前面我们运行的容器并没有一些什么特别的用处。

    接下来让我们尝试使用 docker 构建一个 web 应用程序。

    我们将在docker容器中运行一个 Python Flask 应用来运行一个web应用。

    runoob@runoob:~# docker pull training/webapp  # 载入镜像
    runoob@runoob:~# docker run -d -P training/webapp python app.py

    参数说明:

    • **-d:**让容器在后台运行。
    • **-P:**将容器内部使用的网络端口映射到我们使用的主机上。
  • 查看 WEB 应用容器

    使用 docker ps 来查看我们正在运行的容器:

    runoob@runoob:~#  docker ps
    CONTAINER ID IMAGE COMMAND ... PORTS
    d3d5e39ed9d3 training/webapp "python app.py" ... 0.0.0.0:32769->5000/tcp

    这里多了端口信息。

    PORTS
    0.0.0.0:32769->5000/tcp

    Docker 开放了 5000 端口(默认 Python Flask 端口)映射到主机端口 32769 上。

    这时我们可以通过浏览器访问WEB应用。

    我们也可以通过 -p 参数来设置不一样的端口:

    runoob@runoob:~$ docker run -d -p 5000:5000 training/webapp python app.py

    docker ps查看正在运行的容器

    runoob@runoob:~#  docker ps
    CONTAINER ID IMAGE PORTS NAMES
    bf08b7f2cd89 training/webapp ... 0.0.0.0:5000->5000/tcp wizardly_chandrasekhar
    d3d5e39ed9d3 training/webapp ... 0.0.0.0:32769->5000/tcp xenodochial_hoov

    容器内部的 5000 端口映射到我们本地主机的 5000 端口上。

  • 网络端口的快捷方式

    通过 docker ps 命令可以查看到容器的端口映射,docker 还提供了另一个快捷方式 docker port,使用 docker port 可以查看指定 (ID 或者名字)容器的某个确定端口映射到宿主机的端口号。

    上面我们创建的 web 应用容器 ID 为 bf08b7f2cd89 名字为 wizardly_chandrasekhar

    我可以使用 docker port bf08b7f2cd89docker port wizardly_chandrasekhar 来查看容器端口的映射情况。

    runoob@runoob:~$ docker port bf08b7f2cd89
    5000/tcp -> 0.0.0.0:5000
    runoob@runoob:~$ docker port wizardly_chandrasekhar
    5000/tcp -> 0.0.0.0:5000
  • 查看 WEB 应用程序日志

    docker logs [ID或者名字] 可以查看容器内部的标准输出。

    runoob@runoob:~$ docker logs -f bf08b7f2cd89
    * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
    192.168.239.1 - - [09/May/2016 16:30:37] "GET / HTTP/1.1" 200 -
    192.168.239.1 - - [09/May/2016 16:30:37] "GET /favicon.ico HTTP/1.1" 404 -

    -f:docker logs 像使用 tail -f 一样来输出容器内部的标准输出。

    从上面,我们可以看到应用程序使用的是 5000 端口并且能够查看到应用程序的访问日志。

  • 查看WEB应用程序容器的进程

    我们还可以使用 docker top 来查看容器内部运行的进程

    runoob@runoob:~$ docker top wizardly_chandrasekhar
    UID PID PPID ... TIME CMD
    root 23245 23228 ... 00:00:00 python app.py
  • 检查 WEB 应用程序

    使用 docker inspect 来查看 Docker 的底层信息。它会返回一个 JSON 文件记录着 Docker 容器的配置和状态信息。

    runoob@runoob:~$ docker inspect wizardly_chandrasekhar
    [
    {
    "Id": "bf08b7f2cd897b5964943134aa6d373e355c286db9b9885b1f60b6e8f82b2b85",
    "Created": "2018-09-17T01:41:26.174228707Z",
    "Path": "python",
    "Args": [
    "app.py"
    ],
    "State": {
    "Status": "running",
    "Running": true,
    "Paused": false,
    "Restarting": false,
    "OOMKilled": false,
    "Dead": false,
    "Pid": 23245,
    "ExitCode": 0,
    "Error": "",
    "StartedAt": "2018-09-17T01:41:26.494185806Z",
    "FinishedAt": "0001-01-01T00:00:00Z"
    },
    ......
  • 停止 WEB 应用容器

    runoob@runoob:~$ docker stop wizardly_chandrasekhar   
    wizardly_chandrasekhar
  • 重启WEB应用容器

    已经停止的容器,我们可以使用命令 docker start 来启动。

    runoob@runoob:~$ docker start wizardly_chandrasekhar
    wizardly_chandrasekhar

    docker ps -l 查询最后一次创建的容器:

    #  docker ps -l 
    CONTAINER ID IMAGE PORTS NAMES
    bf08b7f2cd89 training/webapp ... 0.0.0.0:5000->5000/tcp wizardly_chandrasekhar

    正在运行的容器,我们可以使用 docker restart 命令来重启。

  • 移除WEB应用容器

    我们可以使用 docker rm 命令来删除不需要的容器

    runoob@runoob:~$ docker rm wizardly_chandrasekhar  
    wizardly_chandrasekhar

    删除容器时,容器必须是停止状态,否则会报如下错误

    runoob@runoob:~$ docker rm wizardly_chandrasekhar
    Error response from daemon: You cannot remove a running container bf08b7f2cd897b5964943134aa6d373e355c286db9b9885b1f60b6e8f82b2b85. Stop the container before attempting removal or force remove

5-docker镜像使用

当运行容器时,使用的镜像如果在本地中不存在,docker 就会自动从 docker 镜像仓库中下载,默认是从 Docker Hub 公共镜像源下载。

  • 列出镜像列表

    我们可以使用 docker images 来列出本地主机上的镜像。

    runoob@runoob:~$ docker images           
    REPOSITORY TAG IMAGE ID CREATED SIZE
    ubuntu 14.04 90d5884b1ee0 5 days ago 188 MB
    php 5.6 f40e9e0f10c8 9 days ago 444.8 MB
    nginx latest 6f8d099c3adc 12 days ago 182.7 MB
    mysql 5.6 f2e8d6c772c0 3 weeks ago 324.6 MB
    httpd latest 02ef73cf1bc0 3 weeks ago 194.4 MB
    ubuntu 15.10 4e3b13c8a266 4 weeks ago 136.3 MB
    hello-world latest 690ed74de00f 6 months ago 960 B
    training/webapp latest 6fae60ef3446 11 months ago 348.8 MB

    各个选项说明:

    • REPOSITORY:表示镜像的仓库源
    • TAG:镜像的标签
    • IMAGE ID:镜像ID
    • CREATED:镜像创建时间
    • SIZE:镜像大小

    同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本,如 ubuntu 仓库源里,有 15.10、14.04 等多个不同的版本,我们使用 REPOSITORY:TAG 来定义不同的镜像。

    所以,我们如果要使用版本为15.10的ubuntu系统镜像来运行容器时,命令如下:

    runoob@runoob:~$ docker run -t -i ubuntu:15.10 /bin/bash 
    root@d77ccb2e5cca:/#

    参数说明:

    • -i: 交互式操作。
    • -t: 终端。
    • ubuntu:15.10: 这是指用 ubuntu 15.10 版本镜像为基础来启动容器。
    • /bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。

    如果要使用版本为 14.04 的 ubuntu 系统镜像来运行容器时,命令如下:

    runoob@runoob:~$ docker run -t -i ubuntu:14.04 /bin/bash 
    root@39e968165990:/#

    如果你不指定一个镜像的版本标签,例如你只使用 ubuntu,docker 将默认使用 ubuntu:latest 镜像。

  • 获取一个新的镜像

    当我们在本地主机上使用一个不存在的镜像时 Docker 就会自动下载这个镜像。如果我们想预先下载这个镜像,我们可以使用 docker pull 命令来下载它。

    Crunoob@runoob:~$ 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 Hub 网站来搜索镜像,Docker Hub 网址为: https://hub.docker.com/

    我们也可以使用 docker search 命令来搜索镜像。比如我们需要一个 httpd 的镜像来作为我们的 web 服务。我们可以通过 docker search 命令搜索 httpd 来寻找适合我们的镜像。

    runoob@runoob:~$  docker search httpd

    NAME: 镜像仓库源的名称

    DESCRIPTION: 镜像的描述

    OFFICIAL: 是否 docker 官方发布

    stars: 类似 Github 里面的 star,表示点赞、喜欢的意思。

    AUTOMATED: 自动构建。

  • 拖取镜像

    我们决定使用上图中的 httpd 官方版本的镜像,使用命令 docker pull 来下载镜像。

    runoob@runoob:~$ docker pull httpd
    Using default tag: latest
    latest: Pulling from library/httpd
    8b87079b7a06: Pulling fs layer
    a3ed95caeb02: Download complete
    0d62ec9c6a76: Download complete
    a329d50397b9: Download complete
    ea7c1f032b5c: Waiting
    be44112b72c7: Waiting

    下载完成后,我们就可以使用这个镜像了。

    runoob@runoob:~$ docker run httpd
  • 删除镜像

    镜像删除使用 docker rmi 命令,比如我们删除 hello-world 镜像:

    $ docker rmi hello-world
  • 创建镜像

    当我们从 docker 镜像仓库中下载的镜像不能满足我们的需求时,我们可以通过以下两种方式对镜像进行更改。

    • 1、从已经创建的容器中更新镜像,并且提交这个镜像
    • 2、使用 Dockerfile 指令来创建一个新的镜像

    更新镜像

    更新镜像之前,我们需要使用镜像来创建一个容器。

    runoob@runoob:~$ docker run -t -i ubuntu:15.10 /bin/bash
    root@e218edb10161:/#

    在运行的容器内使用 apt-get update 命令进行更新。

    在完成操作之后,输入 exit 命令来退出这个容器。

    此时 ID 为 e218edb10161 的容器,是按我们的需求更改的容器。我们可以通过命令 docker commit 来提交容器副本。

    runoob@runoob:~$ docker commit -m="has update" -a="runoob" e218edb10161 runoob/ubuntu:v2
    sha256:70bf1840fd7c0d2d8ef0a42a817eb29f854c1af8f7c59fc03ac7bdee9545aff8

    各个参数说明:

    • -m: 提交的描述信息
    • -a: 指定镜像作者
    • e218edb10161:容器 ID
    • runoob/ubuntu:v2: 指定要创建的目标镜像名

    我们可以使用 docker images 命令来查看我们的新镜像 runoob/ubuntu:v2

    runoob@runoob:~$ docker images
    REPOSITORY TAG IMAGE ID CREATED SIZE
    runoob/ubuntu v2 70bf1840fd7c 15 seconds ago 158.5 MB
    ubuntu 14.04 90d5884b1ee0 5 days ago 188 MB
    php 5.6 f40e9e0f10c8 9 days ago 444.8 MB
    nginx latest 6f8d099c3adc 12 days ago 182.7 MB
    mysql 5.6 f2e8d6c772c0 3 weeks ago 324.6 MB
    httpd latest 02ef73cf1bc0 3 weeks ago 194.4 MB
    ubuntu 15.10 4e3b13c8a266 4 weeks ago 136.3 MB
    hello-world latest 690ed74de00f 6 months ago 960 B
    training/webapp latest 6fae60ef3446 12 months ago 348.8 MB

    使用我们的新镜像 runoob/ubuntu 来启动一个容器

    runoob@runoob:~$ docker run -t -i runoob/ubuntu:v2 /bin/bash                            
    root@1a9fbdeb5da3:/#

    构建镜像

    我们使用命令 docker build , 从零开始来创建一个新的镜像。为此,我们需要创建一个 Dockerfile 文件,其中包含一组指令来告诉 Docker 如何构建我们的镜像。

    runoob@runoob:~$ cat Dockerfile 
    FROM centos:6.7
    MAINTAINER Fisher "fisher@sudops.com"

    RUN /bin/echo 'root:123456' |chpasswd
    RUN useradd runoob
    RUN /bin/echo 'runoob:123456' |chpasswd
    RUN /bin/echo -e "LANG=\"en_US.UTF-8\"" >/etc/default/local
    EXPOSE 22
    EXPOSE 80
    CMD /usr/sbin/sshd -D

    每一个指令都会在镜像上创建一个新的层,每一个指令的前缀都必须是大写的。

    第一条FROM,指定使用哪个镜像源

    RUN 指令告诉docker 在镜像内执行命令,安装了什么。。。

    然后,我们使用 Dockerfile 文件,通过 docker build 命令来构建一个镜像。

    runoob@runoob:~$ docker build -t runoob/centos:6.7 .
    Sending build context to Docker daemon 17.92 kB
    Step 1 : FROM centos:6.7
    ---&gt; d95b5ca17cc3
    Step 2 : MAINTAINER Fisher "fisher@sudops.com"
    ---&gt; Using cache
    ---&gt; 0c92299c6f03
    Step 3 : RUN /bin/echo 'root:123456' |chpasswd
    ---&gt; Using cache
    ---&gt; 0397ce2fbd0a
    Step 4 : RUN useradd runoob
    ......

    参数说明:

    • -t :指定要创建的目标镜像名
    • . :Dockerfile 文件所在目录,可以指定Dockerfile 的绝对路径

    使用docker images 查看创建的镜像已经在列表中存在,镜像ID为860c279d2fec

    runoob@runoob:~$ docker images 
    REPOSITORY TAG IMAGE ID CREATED SIZE
    runoob/centos 6.7 860c279d2fec About a minute ago 190.6 MB
    runoob/ubuntu v2 70bf1840fd7c 17 hours ago 158.5 MB
    ubuntu 14.04 90d5884b1ee0 6 days ago 188 MB
    php 5.6 f40e9e0f10c8 10 days ago 444.8 MB
    nginx latest 6f8d099c3adc 12 days ago 182.7 MB
    mysql 5.6 f2e8d6c772c0 3 weeks ago 324.6 MB
    httpd latest 02ef73cf1bc0 3 weeks ago 194.4 MB
    ubuntu 15.10 4e3b13c8a266 5 weeks ago 136.3 MB
    hello-world latest 690ed74de00f 6 months ago 960 B
    centos 6.7 d95b5ca17cc3 6 months ago 190.6 MB
    training/webapp latest 6fae60ef3446 12 months ago 348.8 MB

    我们可以使用新的镜像来创建容器

    runoob@runoob:~$ docker run -t -i runoob/centos:6.7  /bin/bash
    [root@41c28d18b5fb /]# id runoob
    uid=500(runoob) gid=500(runoob) groups=500(runoob)

    从上面看到新镜像已经包含我们创建的用户 runoob。

    设置镜像标签

    我们可以使用 docker tag 命令,为镜像添加一个新的标签。

    runoob@runoob:~$ docker tag 860c279d2fec runoob/centos:dev

    docker tag 镜像ID,这里是 860c279d2fec ,用户名称、镜像源名(repository name)和新的标签名(tag)。

    使用 docker images 命令可以看到,ID为860c279d2fec的镜像多一个标签。

    runoob@runoob:~$ docker images
    REPOSITORY TAG IMAGE ID CREATED SIZE
    runoob/centos 6.7 860c279d2fec 5 hours ago 190.6 MB
    runoob/centos dev 860c279d2fec 5 hours ago 190.6 MB
    runoob/ubuntu v2 70bf1840fd7c 22 hours ago 158.5 MB
    ubuntu 14.04 90d5884b1ee0 6 days ago 188 MB
    php 5.6 f40e9e0f10c8 10 days ago 444.8 MB
    nginx latest 6f8d099c3adc 13 days ago 182.7 MB
    mysql 5.6 f2e8d6c772c0 3 weeks ago 324.6 MB
    httpd latest 02ef73cf1bc0 3 weeks ago 194.4 MB
    ubuntu 15.10 4e3b13c8a266 5 weeks ago 136.3 MB
    hello-world latest 690ed74de00f 6 months ago 960 B
    centos 6.7 d95b5ca17cc3 6 months ago 190.6 MB
    training/webapp latest 6fae60ef3446 12 months ago 348.8 MB