那么这么多的应用部署到一起就需要解决三个主要问题: 应用隔离、应用部署和资源限制。
应用隔离很容易理解,比如 不同的应用依赖了冲突的基础软件包,如果都直接部署在操作系统环境下,必然会引起冲突。
这个时候就需要应用之间互相隔离。 Docker 使用操作系统底层的 Namespace 技术来做隔离是一种主流的技术方案。
在容器技术诞生之前,所有的应用都是 直接部署在操作系统上面的,彼此之间共享底层的操作系统资源,比如内存、 CPU 、磁盘等。
打个比方,如果我们要将应用 A 和应用 B 部署到同一台机器上,那么这两个应用需要的环境信息,我们都需要满足。如果应用 A 和 B 的环境依赖之间存在冲突,或者说不兼容,那么管理起来就会非常的困难。
而这个问题,我们通过容器的镜像技术却可以非常简单地解决掉。除此之后, Docker 技术还有很多相比于传统技术更加优势的地方,这也 是Docker 技术能够迅猛发展起来的根本原因。
资源限制不同的应用共享集群不可避免的涉及到资源使用限制的问题。比如一个公司同时有多个业务部门使用一个统一的集群,在统计研发成本时,需要统计各个部门的资源使用情况然后做划分。这样我们就可以根据资源使用限制来划分,当某些应用资源使用超限就杀掉应用。 Docker 使用内核提供的 Cgroup 技术来做资源限制正好可以应用到这个场景。
Kuberneters前面介绍了 Docker 技术的一些理论知识和最佳实践,但是正如前面所说, Docker 技术从来都不是一个孤立的技术。 Docker 更多的还是作为一种技术基石,基于 Docker 我们还需要做一些包括容器编排管理等工作。
容器编排和集群管理系统说到容器编排和集群管理系统,从早期的 docker-compose 到 swarm ,可以说是诸侯林立。但是自从 Kubernetes出现后慢慢的出现了大一统的趋势,凭借着 Google 内部早期积累的经验和优秀的云原生设计理念, Kubernetes 可以说是一骑绝尘,目前已经全面占领了云端统一管理的地位。
第一章: Docker 基础第二章: Docker 核心技术
第三章: Docker 最佳实践
第四章: 云原生容器技术 Kubernetes
第五章: 云原生监控方案 Prometheus
Docker 确实是也是使用 NameSpace 和 CGroup 技术没有错,但是 Docker 相比于各种 PaaS 云平台技术的核心优势其实是它的镜像技术。正是利用镜像技术, Docker 在诞生之后的几个月在各大 PaaS 软件还没有反应过来的时候迅速占领市场的有力地位.
Docker 解决的痛点! 难以维护,打包步骤繁琐PaaS 软件在运行用户的应用上和 Docker 技术没有区别,但是在打包应用上却显得极为繁琐。以 Cloud Foundry为例, 它为不同的主流编程语言定义不同的打包方式,维护起来极其繁琐且容易出问题,用户往往也是因为这个环节苦不堪言。那么 Docker 镜像是如何解决这个问题呢?
Docker 镜像是一套操作系统文件 + 应用程序。镜像一般都会有一个 base 镜像,而这个 base 镜像一般都是操作系统或者其 mini 版本。这样如果我们的应用在云端是运行在 Centos 7.4 上面,那么我们的 base 镜像直接使用 Centos 7.4 的操作系统文件即可,这样就解除了用户要保持本地环境和云上环境的底层一致的心智负担。
简单来说, Docker 镜像的精髓是保证了环境的一致性。
除此之外, Docker 镜像技术是一套统一的技术,我们再也 不需要根据我们应用的开发语言不同而选择不同的打包方式。在 Docker 中,我们打包镜像使用的技术叫做 Dockerfile 技术,我们只需要按照镜像技术的规范去编写Dockerfile 即可,下面是一个简单的 Dockerfile 例子。
FROM quay.io/prometheus/busybox:glibc LABEL maintainer="The Prometheus Authors" COPY node_exporter /bin/node_exporter EXPOSE 9100 USER nobody ENTRYPOINT [ "/bin/node_exporter" ]
其中第一行 FROM 就是引用基础镜像,这里的 busybox 就是一个精简版的操作系统镜像,相当于 Docker 镜像中hello world 。第 4 行将我们本地的可执行文件拷贝到镜像中;第 6 行和第 7 行设置端口和用户,最后一行设置应用的启动入口。
容器编排技术对于规模稍微大一点应用,在生存环境中,需要发布的容器数量很可能极其庞大,这也就意味着如何管理容器之间的联系和拓扑结构并不是一件简单的事情。
举个例子,对于 一个成熟的 web 应用,首先要具备高可用架构,其次其内部可能包含数据库、缓存等各种依赖,除此之外还有很多运维管理需求,比如监控告警等。对于这些操作,通过人工来管理肯定是不现实的。这个时候我们就需要定义容器集群的编排和部署工具。
说到容器编排,不得不提三剑客: Compose 、 Machine 和 Swarm compose为了展示容器编排的内容,我们这里简单展示一下如何去部署一个 Flask 应用。 Flask 应用如下,定义一个route : “/” ,当访问到来的时候, redis 会做一个访问计数。
from flask import Flask
from redis import Redis
import os
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
@app.route('/')
def hello():
redis.incr('hits')
return "Hello World. I am a Flask App!"
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
然后我们要生成一个 Docker 镜像,打包镜像只需要定义一个 Dockerfile ,如下:
From python:2.7 ADD . /code WORKDIR /code RUN pip install flask RUN pip install redis
最后我们通过 compose.yml 来定义部署拓扑。
web: build: . command: python app.py ports: - "5000:5000" volumes: - .:/code links: - redis redis: image: redi
这里是编排部署的核心,这里定义了两个最高级别的 key: web 和 redis ,也就是说 compose 定义了由两个 ” 服务 “( web 和 redis )组成的 Docker ” 集群 “ 。这里 compose 部署的 docker 集群其实是在一台机器上的.通过 compose 定义完 Docker 集群之后,通过 docker-compose up 就可以将服务部署起来了。。
MachineMachine 的主要功能是帮助用户在不同的云主机提供商上创建和管理虚拟机,并在虚拟机中安装 Docker 。除了搭建环境之外, Machine 还可以帮助用户配置 Docker 的连接参数等、管理 Docker 主机,比如启动、关闭、重启、删除等操作。
Machine 简单来说就是一个统一管理工具,成功的关键与否还是要看各大云厂商买不买单,接不接入。目前来看,情况不太乐观。
SwarmSwarm 也是 Docker 集群的编排和管理工具,和 Compose 不同的是,这里的集群是真正的由多个主机组成的集群。
Docker 生态依托 Compose 、 Machine 和 Swarm 重新定义了一个容器生态的 PaaS.
CNCF ,全名 Cloud Native Computing Foundation ,由 Google 、 RedHat 等开源基础设施领域玩家们共同发起。这个基金会的目的其实很容易理解: 它希望以 Kubernetes 项目为基础,建立一个由开源基础设施领域厂商主导的、按照独立基金会方式运营的平台级社区,来对抗以 Docker 公司为核心的容器商业生态。而为了打造出这样一个围绕 Kubernetes 项目的 “ 护城河 ” , CNCF 社区就需要至少确保两件事情;
Kubernetes 项目必须能够在容器编排领域取得足够大的竞争优势;
CNCF 社区必须以 Kubernetes 项目为核心,覆盖足够多的场景。
Kubernetes 并不是凭空设计出来的,其前身来自 Google 内部的 Borg 和 Omega 系统。我们可以看一下下图,感受一下 Borg 和 Omega 系统在 Google 内部的位置。
关于优秀的设计理念,这里简单举一个例子: Pod 。
如果我们有两个容器 A 和 B 需要调度在一台机器上,其中 A 和 B 各需要内存 1 G ,但是这个时候机器只有 1.5G ,如果先调度 A , A 是可以被调度到机器上的,但是后面的 B 调度却会有问题了。要想解决这个问题,如果 A 和B 单独调度,往往需要考虑非常多的场景和复杂的解决算法。
Kubernetes 针对这个问题提出了 Pod 的概念, Pod 是一组容器的集合,类似进程组, Pod 是 Kubernetes 中一个最小的调度单位,也就是说同一个 Pod 中的不同容器一定会被调度到一台机器上,并且可以互相共享NameSpace 。 Pod 的提出解决了很多调度需要考虑的复杂问题.
声明式 API所谓声明式 API ,是直接描述我们要什么或者要达到一种什么状态。与之对于是命令式 API ,命令式 API 对应的则是一个具体的动作:比如 “ 创建容器 ” , “ 修改容器 ” 等。
声明式 API 的一个典型例子就是 SQL ,在下面的 SQL 中,我们直接说明我们需要的东西是 a, b, c 三个字段,现在条件是 id > 123 。但是我们并不需要管这条 SQL 如果是数据库引擎中的真正的执行计划是什么样的.
select a, b, c from table where id > 123;
那么在 Kubernetes 中,我们如何去声明我们需要什么呢?下面是一个创建两个 nginx 副本的 Deployment 。其中的replicas 就是我们最终需要的副本数,也就是最终状态需要有两个副本运行, Kubernetes 如何去保证,我们不需要关心。
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx## 标题 replicas: 2 # tells deployment to run 2 pods matching the template template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80声明式 API 的最大好处是解放开发者的心智负担,让编程变得简单 开放的生态
我们看一下下面的 Kubernetes 分层架构图。
在上图的最底部包含了很多开放生态,包括容器运行时,网络插件,存储插件、以及云厂商 Provider 。这样各大云厂商在将 Kubernetes 运行在自己的云上环境的时候就可以很方便的将自己的存储以及网络产品以一种插件式的方式集成到 Kubernetes 中。
Docker 是什么?Docker 是一个开源的平台,我们可以用 Docker 来开发、部署和运行我们的应用程序。 Docker 可以帮助我们将应用程序和底层基础设施进行分离,以帮助我们更快的实现交付。通过 Docker 技术,我们可以像管理我们的应用一样管理我们的基础设施(比如基础依赖等,这里的具体技术其实就是镜像)。通过 Docker 技术,我们可以精简我们的整个开发和交互流程.
这里面的几个核心点包括:
平台: 镜像 + 容器 + daemon (管理镜像和 容器)平台( platform ):官方将 Docker 定义为一个平台,但是其实更准确的来说, Docker 其实是一个技术栈,包括 Docker 镜像,可以类比为程序,是磁盘上面的静态文件,可以通过 Docker 来启动运行; Docker 容器,也就是 Docker 镜像的运行时实例; Docker Daemon ,用来管理机器上的镜像、容器等。
应用程序和底层基础设施分离:传统的软件开发模式是我们先开发好我们的应用,然后在线上环境或者测试环境上先安装应用的基础依赖,比如像一些 C++ 的二进制文件会依赖一些操作系统的动态链接库。
依赖安装完成之后,然后将应用程序部署上去。使用 Docker 的镜像技术我们可以将这些依赖和应用程序都打包到镜像中,然后测试或者正式上线的时候只需要将整个镜像部署上去就可以了,不需要关心目标服务器上面的基础环境,这也就是应用程序和基础设施分离。也是精简开发流程的核心的所在。
熟悉 Docker 的同学肯定会意识到这里对 Docker 的定义其实少了很多东西,确实是这样的,比如 Docker 的隔离性和资源限制在定义里面都没有体现出来。定义还是从一种更加宏观的角度来介绍 Docker ,也没错.
Docker v.s. 虚拟机很多人学习 Docker 的过程中都会看到有人把 Docker 拿来和虚拟机做对比,也就是下面这张图。左边是 Docker 的架构,右边是虚拟机的架构图。我们可以看到 Docker 和虚拟机的主要区别有:
所有的 Docker 应用共享一个宿主机操作系统,每个虚拟机有自己的操作系统;
每个 Docker 应用通过 Docker 层和宿主机的操作系统交互,而虚拟机应用直接和操作系统交互。
但是上图左边的图中的 Docker 的位置其实很不严谨,实际上 Docker 并不会像 Hypervisor 那样对应用进程的隔离环境负责,也不会创建任何实体的容器,真正对环境负责的是宿主机操作系统本身。所以上图中 Docker 的问题应该是处于靠边的位置,因为通过 Docker 启动的容器本质上和操作系统中运行的进程并没有本质的区别。



