springbootstarterDocker容器

换个角度看Docker

2017-12-05  本文已影响74人  monkey01

1. Docker

Docker是什么?维基百科给出的解释是Docker是一个开放源代码软件项目,让应用程序布署在软件容器下的工作可以自动化进行,借此在Linux操作系统上,提供一个额外的软件抽象层,以及操作系统层虚拟化的自动管理机制。整体上说的有点抽象,我的理解是它通过分层镜像标准化和内核虚拟化技术,使得应用开发者和运维工程师可以以统一的方式跨平台发布应用,并且在几乎没有额外开销的情况下提供资源隔离的应用运行环境。

使用Docker开发人员只需要关心容器中运行的程序,而运维人员只需要关心如何管理容器。Docker设计的目的就是要加强开发人员写代码的开发环境与应用程序部署的生产环境的一致性,从而减少我们经常遇到的开发时没问题,上生产就出问题的情况发生。

2. 容器化和虚拟化技术对比

谈到容器化技术必然会拿来和比较成熟的虚拟化技术进行对比,之前刚开始接触docker的时候去咨询一位朋友,他是资深运维,做了很多年运维工作,问他‘我们公司计划使用docker,将现有的这些工程部署到docker上,你有什么建议?’,他给我的回复直接是‘目前容器技术不成熟,还是安稳的用虚拟化私有云吧’,我知道这位朋友公司私有云用的openstack,将应用都部署在LVM上,比较成熟的一套私有云方案,后来又看了国外和国内big3使用docker的情况,还是坚持了尝鲜的道路。从下面这张图能够很好的解释容器化技术和虚拟机技术的差异点也解释了常说的容器化技术比虚拟化技术快的问题,从图中我们很容易发现虚拟化技术是所有每个运行环境都是运行在虚拟机操作系统上的,我们都知道在宿主机上启动一个虚拟机操作系统是相当耗费宿主机系统资源的,所有的虚拟机管理还需要在宿主机上安装虚拟机管理程序,这就又产生了资源的消耗;再来看旁边的容器化技术,容器化技术是直接基于OS的,只是容器技术基于OS本身提供了相关的环境隔离和资源隔离,并提供了运行时环境,将应用程序跑在容器的运行时环境里,通过OS原生的环境隔离和资源隔离来保证整体隔离。

docker-vs-vm.png

可以看到两个技术解决的问题以及他的侧重点是不一样的,对于docker来说,它只有一个二进制文件,在这上面,如果我们又装了一些虚拟软件,它会在上面再虚拟一个操作系统出来,这个操作系统在才会去装各种各样的应用软件。

容器的操作模式是,它没有虚拟机和操作系统这一层,它直接相当于在上面运行一个容器。比如容器它本身不是虚拟化技术,那么我们也可以看出来,虚拟机是服务器硬件和操作系统之间的解耦,并且虚拟机这个操作系统是比较重,比较大,相对于容器来说它启动比较慢。而docker是更靠近应用这个层的,所以它是应用和操作系统之间的解耦。但是容器本身是一个进程。比如我们这个操作系统上面运行了10个程序,其实就是这个操作系统上面又多了10个进程而已。

这里是比较浅显易懂的解释,让大家从宏观上有个虚拟化和容器化技术差异的了解,后面会从容器化底层实现再解释下容器是如何在原生OS上实现环境隔离和资源隔离的。

3. Docker解决了什么问题

上面大概介绍了docker并且也介绍了容器化和狭义虚拟化(容器化也可以理解为一种广义的虚拟化技术)的差异,那么对于刚接触容器的朋友可能会问“为什么要使用docker?他能为我们解决什么问题?”,从社区和一些技术论坛里,经常会有同学也存在一样的问题,有些用了docker的同学也不知道为什么要用,只是公司要求用或者是为了顺应潮流就用了。这里笔者就给大家解释下原因,让大家用的知其所以然,而不是为了用而用。

对于有一定IT工作经验的同学一定对下面的场景很有感触:

场景1:工程拆分部署
开发A君:我们要对现有的系统进行模块拆分、功能解耦。
运维B君:你们要拆分出多少应用?
开发A君:拆完大概十几个吧。
运维B君:*(哔),你要累死我啊!现在就一个大应用,HA部署两个节点,每次发版部署验证都要花1小时,你拆那么多出来那不是要发版到日出!!!
开发A君:沉默不语。。。
场景2:扩容
运维B君:生产CPU、内存到阈值了,系统监控那边告警了,开发快来解决!
开发A君:让我瞧瞧。
---检查日志、监控系统运行情况---
开发A君:嗯,程序没有问题,就是交易量大了资源占用正常升高了(起身准备离开堡垒机)。
运维B君:*(哔),问题没解决啊!想办法解决啊!(准备掏出刀子)
开发A君:这是正常现象啊,我们程序支持无状态线性扩展,你加CPU、内存或者直接加新节点吧。
运维B君:沉默不语。。。
场景3:日常运维
运维B君:好烦啊,每天要管理那么多机器,有那么多新环境要部署,有那么多生产环境要监控。
开发A君:我不是给你写好了应用部署脚本吗?
运维B君:你那个只能解决我的应用部署问题,系统要装、中间件要装、数据库要装,还要装好几套环境,每套环境还要部署那么多节点,哎,想象就头大。
开发A君:沉默不语。。。

看了上面3个场景,是不是在几年前很常见,这就是我们几年前的场景,随着虚拟化和容器化的快速发展,这几个问题都得到了解决,上一家公司在两年前将所有的服务端应用都部署到了openstack上,随着这两年容器化的快速发展,现在的公司正在准备将所有的服务端应用部署到docker上,docker现在已经快成了容器化的标准技术了,现在说到容器基本就是指docker,docker对于上面3种场景都能比较好的解决。

场景1中的工程微服务拆分后导致的运维部署问题,通过开发提供的dockerfile可以快速build出新版本的镜像,快速在每个节点上创建应用容器,并将容器run起来,速度快的令人发指;

场景2中的扩容问题,对于传统物理机要扩容是要停机的,很麻烦,以前大部分公司也都是基于物理机划Lpar来分namespace使用环境,这样划分的可以做到不停机,但是需要停节点应用;如果涉及到需要扩机器的情况那就更麻烦了,不但要装系统还要装一堆中间件数据库什么的。用了docker之后,再通过k8s这类的容器管理工具可以做到非常方便的不停机动态扩容,直接复用镜像,甚至可以做到新节点秒开的地步,想想还有点小激动;

场景3日常运维的问题,目前大部分公司都已经可以做到自动化部署,但这里的自动化一般还停留在应用层面的自动化,还没有涉及到系统环境部署和运维的自动化,通过docker+k8s就可以通过镜像做到系统环境和应用的快速部署,通过k8s的监控可以做到全容器的日常监控和动态弹性扩容。

docker容器化对于我来说可以解决上面这些问题,当然这还只是容器化一部分的功能还有很多功能,可以通过docker来解决,这里就不一一赘述了,感兴趣的同学可以自己去探索。

4. Docker三大件

docker的三大件为镜像、容器、仓库,下面分别对它们的基础概念做了描述。

镜像

Docker运行容器前需要本地存在对应的镜像,如果本地没有需要的镜像docker run的时候会自动去docker hub下载镜像。

镜像可以用来创建Docker容器,一个镜像可以包含一个完整的操作系统环境和用户需要的其它应用程序。在docker hub 里面有大量现成的镜像提供下载。docker的镜像是只可读的,一个镜像可以创建多个容器。

容器
docker利用容器来开发、运行应用。
容器是镜像创建的实例。它可以被启动、开始、停止、删除。每个容器都是 相互隔离的、保证安全的平台。

仓库
仓库是集中存放镜像文件的场所。
每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。一般公司内部也都会创建私有仓库来保证内部的镜像安全管理和镜像的访问速度。

5. Docker底层技术原理

上面介绍了docker的基础内容,大家对docker应该有了一个大概的认识,下面我们介绍下docker的底层实现技术原理,Docker利用Linux核心中的资源分脱机制,例如cgroups,以及Linux核心名字空间](namespace),来创建独立的软件容器(containers)。Linux核心对名字空间的支持完全隔离了工作环境中应用程序的视野,包括进程树、用户ID与挂载文件系统,而核心的cgroup提供资源隔离,包括CPU、内存、block I/O与网络。

namespace

docker通过底层linux的namespace进行隔离,主要对存储、IPC、进程、用户进行隔离。

docer的底层对namespace的隔离主要是通过下面3个系统函数来实现的,这3个函数对于做过C++操作系统编程的同学应该比较熟悉,这里就不详细描述了,本篇的目的是让大家对docker技术有个全面的概览,并不是让大家去研究docker的底层,如果想了解docker底层实现的同学可以去github上看看源码,实现思路是一样的。

cgroup

namespace主要解决的是环境隔离的问题,CGGroup解决资源隔离的问题。Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等);Linux CGroupCgroup 可让您为系统中所运行任务(进程)的用户定义组群分配资源 — 比如 CPU 时间、系统内存、网络带宽或者这些资源的组合。您可以监控您配置的 cgroup,拒绝 cgroup 访问某些资源,甚至在运行的系统中动态配置您的 cgroup。

文件系统架构

在传统的linux引导过程中,root文件系统会最先以只读的方式加载,当引导结束并完成了完整性检查后,才会被切换到读写模式。但是在docker中,root文件系统永远都只能是只读的,并且docker利用union mount(AUFS)技术在root文件系统层面加载更多的只读文件系统,union mount将各层文件系统叠加在一起,这些文件组成了一个镜像,一个镜像会包含所有底层的文件和目录。一个镜像可以放到另一个镜像的顶层,位于下面的镜像称为父镜像。最后,当从一个镜像启动容器时,docker会在镜像的最顶层加载一个读写文件系统,只有最顶层的的容器部分是可读写的。当容器运行后,文件系统发生变化都会体现在这一层,当改变一个文件的时候,这个文件首先会从下面的只读层复制到读写层,然后读写层对该文件的操作会隐藏在只读层的该文件,这就是传说中的copy on write。

image

容器是可写的,镜像是可读的。如果有很多镜像的话,比如本地主机有很多很多的镜像,很多的镜像有些层是可以共享的,可能,有些层他们之间是一样的,一样的话,这些层只会占用一个空间,这样比较节省空间。

可以同个一个镜像,启动很多个容器,这些容器它共用的都是这一个镜像,它不像虚拟机似得,如果有一个虚拟机是100G,我又启动了一个,它又会占用100G,它的内存资源是非常消耗的,那么容器是共有这一个镜像,不会启动很多很多的容器,非常的省资源,这是docker容器的特点,下面这张图比较形象的表示了docker容器和镜像的物理关系。

image

6. Docker常用命令实践

看了上面理论化的内容,下面我们来动手实践,在系统中安装docker的过程这里就不赘述了,网上有很多资料,也可以直接到docker官网查看get-start,官网列出了各种操作系统的安装过程,照着做就可以了。下面我们介绍下docker中常用的一些命令,所有的命令都可以通过man或者docker --help查看具体参数用法。

docker info

使用docker info查看docker是否可用,如果报Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?则需要重启docker daemon.

docker ps

通过docker ps可以查看当前在运行的容器有哪些,加上参数-a,可以查看这台宿主机器上所有的docker容器实例,包含没有运行的。

docker run

创建容器,直接举个例子docker run —name container-name -i -t ubuntu /bin/bash,这句命令的含义为启动一个名字叫container-name的容器,-i表示Keep STDIN open even if not attached始终保持stdin打开,-t表示Allocate a pseudo-TTY启动一个tty,具体参数可以使用docker run —help.ubuntu表示该容器的镜像的名称,如果该镜像在本地不存在则直接去配置的register去下载,register可以理解为一个类似git的repo库。默认的register是docker hub,因为国内访问特别慢,所以我们一般会使用国内一些互联网公司提供的register或者是自己公司的私有库。这里学习推荐使用阿里云的register。阿里云镜像服务地址,可以参考帮助文档进行配置。

docker start

启动容器,docker start container-name

docker attach

依附到容器,docker attach container_name。这里的依附你可以理解成就是登陆到这个容器上,你attach后就可以操作容器中的所有内容,就好像我们登录了一个远程服务器一样。

docker logs

查看容器日志,docker logs container-name,通过宿主机可以查看容器的日志,也可以加上-f的参数就类似于我们使用的tail -f。

docker top

查看容器内进程运行情况,docker top container-name

docker status

监控一台宿主机上的几个容器运行状态,docker status contain1 container2 container3 ,通过status命令可以实现多容器的同时监控,对于运维人员比较方便。

docker rm

docker rm container-name 使用rm来删除不使用的容器。

docker images

查看宿主机存储了哪些镜像。

docker commit

创建镜像,在现有的容器基础上,使用commit可以将现在的容器直接创建成一个镜像文件,将当前容器的读写层也打包成只读的镜像,但是这种形式创建的镜像很难进行管理,因为你不知道这个镜像在base image基础上到底执行了哪些命令,推荐使用下面的dockerfile方式创建镜像。

docker build

编译创建镜像,docker build命令和dockerfile,不推荐使用docker commit,应该使用相对灵活和可维护的dockerfile来构建docker镜像;docker build -t="monkey01/test_web:v1"该命令的含义为使用该目录下的dockerfile来build一个镜像,镜像保存在monkey01的repo里,镜像名字为test_web,版本tag为v1.

FROM ubuntu:16.04
MAINTAINER monkey01 "monkey01@126.com"
ENV PEFRESHED_AT 2017-12-01
RUN apt-get update && apt-get install nginx
RUN mkdir /var/www/web
ADD nginx/global.conf /etc/nginx/conf.d/
ADD nginx/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80

dockerfile使用基于DSL语法来构建一个docker image。每个dockerfile的第一条指令都必须是FROM,FROM指令指定一个已经存在的镜像,MAINTAINER指令告诉docker该镜像作者;RUN指令会在镜像中运行指定的命令;每条RUN指令都会创建一个新的镜像层,如果指令执行成功,就会将此镜像层提交。EXPOSE指令,用语打开容器运行中的服务端口,docker出于安全考虑默认不会自动打开端口。上面这个dockerfile例子比较简单一看就知道什么意思。

小结

看了上面对docker理论介绍和实际的命令操作,是否对docker有了一个大概的了解,看完这篇文章你应该知道docker能帮我们解决什么问题,docker底层实现原理是什么样的,如何使用docker创建容器和镜像,如果看完还是不理解那么建议再读一篇。后续我会再写几篇关于docker的文章,先做个预告,会涵盖几个方面:镜像私有库的搭建、在docker上部署springcloud项目、k8s相关知识和k8s对docker的管理。

上一篇下一篇

猜你喜欢

热点阅读