容器技术 - Docker

如果你刚刚听说Docker,这篇博客里给出清晰的介绍,跟着老司机上路。

问题 & 缘起

做项目或产品的程序猿们肯定没少见过下面的这种文档,从更改记录中可以看到完成一个文档的艰辛。更要命的是一个文档通常只对应一个版本,一个平台,一种环境,一种配置。

The Problem

The Problem

在现代化和大规模的软件作业里,文档的问题是显而易见的,维护文档更是苦不堪言。特别是做大型软件产品:

The Problem

文档的问题其实反应了背后的工程问题 - 软件的安装和配置,还好有自动化工具来帮忙(见我之前的博客):

DevOps tool

自动化的运维工具并没有从根本上解决软件的安装和配置问题,如何应对现代软件开发中多环境的问题:

The Challenge

开发高质量少BUG的软件的关键问题在于如何确保开发与生产环境的一致性

The Challenge

Docker & 容器

欢迎来到容器世界

Welcome 2 Docker World

什么是容器呢?容器有两面,一个是容器存储时的静态打包格式,另一个是它运行时的动态格式。静态格式就像Java的class文件,运行时由JVM执行操作,容器也一样,静态时以image形式存在,由容器引擎运行,从image产生一个容器(以image为蓝本产生)。但和Java class的byte code不同的是容器的image不只是上层的应用代码,而是整个文件系统,或者说运行环境,所以不只是夸语言,而且是自满足,夸系统,不依赖基础环境或第三方库或其它应用程序,例如可以把Java class,JVM, database通通打包到image里。而运行时,和JVM不同的是,容器带来是系统级别的隔离,每个容器自带CPU,内存,网络(这点上类似虚拟机VM),JVM没有提供这种隔离功能,同个宿主机上的各个JVM还是使用共同的资源。由于这个跨平台,自满足的特性,软件的安装和配置就消失了,image下载后就能运行(这点上也类似虚拟机VM)。

What is Container

要想真正理解什么是容器,必须稍微了解Linux内核和process,所有的计算,储存,通讯都是一系列系统调用至内核完成的(system call),一个大致的新线程流程是,父线程通过system call让内核产生新的线程,内核保留父程序的上下文,准备初始化子线程的上下文,切换至子线程,操作权这时交到了子线程,子线程的逻辑开始真正运行,逻辑运行结束后,会向父进程发信号,内核回收子进程资源,操作权回到父进程,父进程继续执行接下去的逻辑。举个例子,当在bash里运行ls的时候,bash的process id假设为10,该process通过fork()产生出一个新的process,id为11,process 11通过execve()把当前的程序/bin/bash unload,接着load入/bin/ls,当ls运行结束后,process 11通过系统调用exit()通知process 11的结束状态(status code),内核这时通过wait()唤醒终止运行的process 10,process 10收到process 11的status code并继续运行。这就是普通process产生过程。

Linux启动后的第一个线程是init,其process id为1,所有的程序运行都是通过init产生子线程来完成的,init就是个上帝线程。容器的秘密就发生在process产生的时候。

Linux Process

Linux内核通过namespace提供了资源隔离的功能,各种namespace对应于各种资源的抽象数据结构(共7种),内核通过这种结构来管理资源,有了namespace,相当于代码的package name,每个process有自己的资源视角,资源的使用可以单独定制。

在产生子线程时,可以通过参数告诉内核是否共享或为子线程产生单独的namespace,容器的核心其实落实到这么一个system call。

例如一旦新的process自带PID namespace,脱离父进程的PID namespace,其process id将变为1,其创建用户为root,这让它看起来就像是一个init process,所以启动一个容器就像启动了一台新的Linux系统。

Namespace

Namespace

有了资源隔离还不够,还需要资源限制 - Linux内核也提供了这种功能,CGroup,各种CGroup对应这各种资源的使用限制,与Linux的设计原则一致,CGroup通过文件形式定义的。关于cgroup的设计,进一步了解请看这个:cgroup v2

CGroup

所以容器的本质就是一个具备资源隔离和限制的procss,Namespace和CGroup是容器的两大核心技术,Docker Engine利用这两大内核功能进行系统调用完成容器的产生,交互,删除等等工作。这两大功能Linux内核十年前已经提供,所以Docker基本上在现有的各种Linux运行毫无问题,没有依赖性的问题。当然和VM不同的是,这种资源隔离和限制是内核提供的『软』设置,从安全系数来讲是没有VM彻底的。

Docker Engine

接下来是容器的存储格式 - image,Docker采用AUFS来存储容器,一种层级格式的文件系统,有点像Source Version Control,每一层都是新的叠加,Docker遵循immutable infrastruture,每一层都是不可更改的(只读),只有运行时最外的一层可写,一旦commit(把运行的容器发生的一切存储为新的一层),新的一个image在物理存储介质所谓的硬盘上诞生了,如果不commit成image,所有的变更将随着容器的删除而消失。

Docker image可通过Docker file来构建,该文件包含了一系列构建的指令(伴随着增加新的层级),类似make/ant file等编程构建机制,如上所述,当然也可以把运行的一个容器存为image,背后的原理是一样的。

这种层级设计,带来了两大亮点,一是类似开发软件,不需要从头开始build image,可以在使用别人的image,例如可以在标准的tomcat image上定制自己所需的tomcat image,软件安装和配置的最佳方案通过软件得到重用而不是文档或安装手册,Docker Hub上已有大量现成的image,各种OS,各种数据库,各种中间件,各种软件;二是由于image是分层的,tomcat on CentOS和mysql on CentOS两个image不需要下载CentOS这个层两次,和下载VM image相比,使用Docker的速度快了不是一点半点,速度就是生命,就是一切。

所以Docker不仅仅是省工省时,而且本质上是一种更好的方法。

AUFS

Docker Image

Docker Build File

现在可以来看看Docker的整体样子,首先Docker是一个软件公司的名字,就像Microsoft,其次它的软件产品也叫Docker,现在这个名字有版权,别人不能乱用了。网上有时统统叫Docker,初学时会搞不清楚,其实Docker包含了以下几个基本的组件:

  • Docker Image
  • Docker Container
  • Docker Engine
  • Docker Registry
  • Docker Machine
  • Docker Compose
  • Docker Swarm
  • ……

container, image, engine的概念我们已经介绍了,也是最基础最核心的。其它概念如docker networking,scheduler,volume,docker build file,image registry,composer,swarm,等等,请移步awesome docker资源进一步学习(链接在最后)。

Docker太成功,以至于成为了容器的代名词,Docker即容器,容器即Docker,就像Sun的JVM我们直接简称为JVM而非Sun JVM,其实Docker只是容器技术的一种实现,还有其它同样利用了Linux Namespace和CGroup的容器技术。

Docker采用RESTful架构,client端,默认的是Docker命令行,把Docker的命令(下载image,产生容器,运行容器,停止容器,删除容器,等等)以REST形式传递给Docker Engine执行。

Docker Architecture

Docker workflow & command

Docker workflow & command

更新2017-4-18:Docker公司重新调整了Docker的各个component,同时取消Docker开源项目:

Moby

绝大多数的Docker介绍是从虚拟机/VM开始的,通过上面的介绍可以清楚看到,两者是完全不同的东西,但两者有类似的地方,下面是Docker和VM的比较:

Docker vs. VM

Docker vs. VM

Container Management

单个容器,或者单机上的容器,犹如Hello World,上手还是很快的,但大规模的实际应用需要的是则是个J2EE的问题 - 如何组合多容器,如何调度多容器,如何管理多容器等等:

  • orchestration
  • scheduling
  • service discovery
  • auto-scaling
  • health monitoring & auto-healing
  • 0-down time upgrades
  • registry
  • security
  • network
  • storage
  • container as a service (CaaS)

Google的Kubernates,Docker Swarm, Mesos就是要解决基本的容器组合的问题,让其上升到更高的概念层次,得到更大规模程度应用。我接下来会介绍kubernates。

容器是一切,一切是容器,容器将成为云计算的单位,同时辐射出软件工程的很多基础性问题。路漫漫,只能走一步是一步,更多的问题留待以后的博客。

容器生态圈

未来

在我看来,容器&Docker有两点是革命性的,一是全新的软件打包方式,带来了全新的运维方式;其次,解决了环境一致性问题,这将反过来深刻影响软件的架构以及开发方式,让我们拭目以待。

reusable code -> libraries: code once, import to every project
reusable dev environment -> VM: create once, work for every team member
reusable infrastructure -> cloud + configuration management tool: define once, provision everywhere
reusable system -> container: assembly once, deploy everywhere

福利

最后,Docker网上教程,让你亲手实践Docker:play with docker

Docker的资源:awesome docker

Happy Dockering!