1 容器技术
1.1 从虚拟机到容器
容器(container),是一种虚拟化技术。相比虚拟机技术,它有如下的优点:
- 极其轻量:只打包了必要的Bin/Lib;
- 秒级部署:根据镜像的不同,容器的部署大概在毫秒与秒之间(比虚拟机强很多);
- 易于移植:一次构建,随处部署;
- 弹性伸缩:Kubernetes、Swam、Mesos这类开源、方便、好使的容器管理平台有着非常强大的弹性管理能力。
下图是虚拟机与容器的比较。在容器中,去除了Hypervisor和虚拟机内的操作系统,仅仅包括一个轻量级的ContainerRuntime(容器运行时),以及少量容器内的应用程序所依赖的库。从而大大的缩小了容器的体积。
从更高层面上来讲,Hypervisor是硬件的虚拟化(Hardware Virtualization),Hypervisor将机器的硬件进行虚拟化,然后将虚拟出来的硬件给各个虚拟机使用,虚拟机通过安装自己的操作系统来管理这些虚拟化的硬件。而容器,则是操作系统的虚拟化(OS Virtualization),容器的Container Runtime将操作系统虚拟出来,然后容器的应用程序运行在这个虚拟出来的操作系统上。
容器的更详细的原理可参考如下文档:
1.2 容器的应用场景
容器是伴随着微服务的普及而出现的。一个容器一般就运行一个微服务,即一个大型应用的其中一个功能模块。
下图中,左边是传统的大型单体应用的部署场景,右边是微服务部署场景,每个微服务部署在一个容器中。传统单体部署方案中,当需要实现HA(High Availability)时,需要使用多台服务器,在每台服务器上都部署全部的功能。这样的做法有一个缺点,就是有些不重要的功能模块可能不需要部署那么多的副本,但是因为这些功能被绑定在一个单体应用中,不得不与其他重要功能模块一样,部署多个副本,造成资源的浪费。而基于容器的微服务部署,则可以将不重要的模块尽量少的部署副本数。容器化的部署方案还有一个优点,各个功能模块可以使用不同的编程语言进行开发,实现了开发语言的解耦,使得各个功能模块可以使用更合适的语言进行开发。
1.3 容器的开发流程
基本的开发流程如下:
1,寻找基础镜像
2,基于基础镜像编写Dockerfile脚本
3,根据Dockerfile脚本创建项目镜像
4,将创建的镜像推送到容器仓库
5,基于项目镜像创建并运行容器
这只是一个基本的技术流程,这里面有非常多的细节需要处理。概括起来说,就是首先开发人员把开发好的应用程序加上必要的库文件打包成一个容器镜像文件,然后把这个镜像文件推送到镜像仓库,然后这个镜像文件就可以在实际环境部署中被使用。实际生产环境中部署的时候,容器启动前要检查容器的镜像在本地是否存在,如果不存在则会根据指定的镜像所有的地址去拉取相应的镜像文件。把镜像文件拉到本地后,再根据镜像文件启动容器。
因为容器本身没有操作系统,必须依赖宿主机的操作系统。所以容器镜像所属的操作系统必须与宿主机的操作系统内核相同。比如Linux的容器镜像必须运行在Linux系统中,Windows的容器镜像必须运行在Windows系统中。而虚拟机则没有这个限制。
2 容器的调度和编排系统(Kubernetes)
当资源池中有大量的容器后,对容器的管理和调度就成为了一个亟待解决的问题。对容器的调度和编排有多种不同的技术,其中最有名,使用最广泛的是Kubernetes。
2.1 什么是Kubernetes
Kubernetes,又称为 k8s(首字母为 k、首字母与尾字母之间有 8 个字符、尾字母为 s,所以简称 k8s)或者简称为"kube" ,是用于自动部署,扩展和管理容器化应用程序的开源系统。它可以帮助用户省去应用容器化过程的许多手动部署和扩展操作。也就是说,您可以将运行 Linux 容器的多组主机聚集在一起,由 Kubernetes 帮助您轻松高效地管理这些集群。而且,这些集群可跨公共云、私有云或混合云部署主机。
2.2 Kubernetes体系结构
下图是Kubernetes的体系机构
左边框是控制平面,也叫Master,包括API Server,etcd,Scheduler和Controller Manager几个组件。右边是Worker nodes(工作节点),包括Kubelet,kube-proxy,Container Runtime几个组件。下面简单介绍几个组件的功能。
- etcd保存了整个集群的状态,是整个系统的数据库;
- apiserver提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
- controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
- scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上;
- kubelet负责维护容器的生命周期,同时也负责Volume(CSI)和网络(CNI)的管理;
- Container runtime负责镜像管理以及Pod和容器的真正运行(CRI);
- kube-proxy负责为Service提供cluster内部的服务发现和负载均衡;
Kubernetes里的所有操作对象都被定义为资源,所有的资源都可以用yaml格式的文件来定义。资源被定义之后会存储在etcd里面。系统中etcd,controller manager,scheduler,kubelet,kube-proxy这几个模块都是直接与apiserver进行通信,它们彼此之间不直接互通。也就是说所有的模块要写入或者从etcd读取数据都是通过apiserver来完成的。
2.3 Kubernetes的基本概念
Pod:Pod 是 kubernetes 中你可以创建和部署的最小也是最简的单位。Pod 代表着集群中运行的进程。
Pod 中封装着应用的容器(有的情况下是好几个容器),存储、独立的网络 IP,管理容器如何运行的策略选项。Pod 代表着部署的一个单位:kubernetes 中应用的一个实例,可能由一个或者多个容器组合在一起共享资源。
在 Kubernetes 集群中Pod 有如下两种使用方式:
- 一个 Pod 中运行一个容器。“每个 Pod中一个容器” 的模式是最常见的用法;在这种使用方式中,你可以把Pod 想象成是单个容器的封装,kuberentes 管理的是Pod 而不是直接管理容器。
- 在一个 Pod 中同时运行多个容器。一个 Pod 中也可以同时封装几个需要紧密耦合互相协作的容器,它们之间共享网络和存储资源。每个 Pod 都会被分配一个唯一的 IP 地址。Pod 中的所有容器共享网络空间,包括 IP 地址和端口。Pod 内部的容器可以使用 localhost 互相通信。Pod 中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)。可以为一个 Pod 指定多个共享的 Volume。Pod中的所有容器都可以访问共享的 volume。Volume 也可以用来持久化 Pod 中的存储资源,以防容器重启后文件丢失。
Namespace:Namespace(命名空间)是Kubernetes系统中的另一个非常重要的概念。在很多的地方都有Namespace这个概念,比如C++,Linux系统等等,他们都作用都是相似的,都是用来做资源隔离的。在Kubernetes中,将集群内的资源对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。
Label:给每个pod打上一个标签,方便其他的资源(比如service)来选择具有相应标签的pod。还可以同一功能的不同版本的pod打上不同的版本标签,以便进行应用的滚动升级。除了pod外,Kubernetes的所有资源都可以打标签。
Pod Controller:控制pod的副本数量,启动方式等。包括Replication Controller,ReplicaSet,Daemonset,Deployment。
Service:即微服务架构中的一个微服务。实际应用中,对应一个IP:Port。集群内外的客户端要访问该需要,就需要指定该服务对应的IP和端口。服务是一个虚拟概念,而正在提供服务的是运行在pod中的容器,当有多个pod提供相同的服务时,在服务端需要一个负载均衡器将流量负载均衡到pod中,如下图示。
2.4 Pod的启动过程
下图是一个pod的启动流程:
以使用deployment来创建pod为例,来说明创建pod的完整过程。
整个流程的前提条件:已经使用yaml文件定义好一个需创建的pod。
第一步:用户使用yaml文件定义好一个deployment,在deployment里面会指定需要创建的pod的数量以及pod的模板,然后使用kubectl命令创建该deployment。kubectl的命令会到达apiserver,然后apiserver会把创建好的deployment保存到etcd中。
第二步:位于controller manager模块中的deployment controller会通过watch机制检测到新创建的deployment。
第三步:deployment不直接创建pod,需要通过replicaset这个资源来创建pod。deployment controller会自动创建一个replicaset。Replicaset创建好之后,同样是通过apiserver将replicaset保存到etcd。
第四步:位于controller manager模块中的replicaset controller会通过watch机制检测到新创建的replicaset。
第五步:replicaset根据从deployment拷贝过来的pod的模板,创建pod。创建好的pod同样是通过apiserver保存到etcd中。
第六步:Scheduler模块通过watch机制检测到有新创建的pod。
第七步:Scheduler根据一定的算法来决定将该pod部署到哪个worker node上。然后将调度的结果通过apiserver写入到etcd中。
第八步:worker node中的kubelete模块通过watch机制检测到有pod被调度到本节点。
第九步:kubelet调用容器运行时(container runtime)来启动容器。
第十步:容器运行。
从以上步骤可见,所有的资源创建之后都会首先保存到etcd中。其他模块都是通过watch机制,经过apiserver检测到资源的变化,然后根据资源的变化情况进行相关的操作。整个系统只有apiserver直接读写etcd数据库,这样做的目的是为了避免多个模块同时读取数据时,可能存在数据不一致的问题。
3 操作指南
3.1 kubectl
kubectl是一个连接Kubernetes集群的工具,可以使用这个工具来创建,查看,删除,修改系统的资源。kubectl的安装及使用见如下连接:
3.2 创建Pod
Kubernetes中所有的操作对象都可以被定义为资源。在实际应用中,一般使用yaml格式的文件来定义Kubernetes系统的资源。
Pod:以下是定义一个Pod使用的yaml文件。
但是在实际应用中,一般都不会直接使用Pod的yaml文件来创建Pod,而是使用Pod的控制器来启动Pod,比如Deployment,Daemonset,ReplicationController等。因为使用Pod的yaml文件启动Pod,很难被Kubernetes系统进行有效的管理,比如当Pod故障进程死了,无法重新再启动一个同类型的Pod,而使用Pod控制器来启动Pod就可以做到这一点,Pod控制器可以控制系统中有多少个同类型的Pod,当有一个或者多个Pod故障死了之后,会自动拉起同样数量的Pod。
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.0
Deployment:如下是一个Deployment的yaml定义文件,该yaml文件中的replicas就是指定创建多少个同类型的Pod
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: nginx-v1
namespace: dp-test-01
name: nginx-deploy-1
spec:
replicas: 10
selector:
matchLabels:
app: nginx
version: v1
template:
metadata:
labels:
app: nginx
version: v1
spec:
containers:
- image: nginx:1.14.0
name: nginx
定义好Deployment后,我们就可以使用kubectl工具来创建Pod。
因为在Deployment的yaml文件中指定了namespace,所有需要先创建namespace:
(如果不指定namespace,则Pod会自动的创建到default这个namespace下。)
kubectl --kubeconfig=vc_1.config create namespace dp-test-01
创建好namespace后,就可以创建Pod了:
kubectl --kubeconfig=vc_1.config apply -f deploy01.yaml
Pod创建成功后,可以查看到已创建的Pod:
kubectl --kubeconfig=vc_1.config get pods -n dp-test-01
查看的结果如下:
[centos@localhost edge-scheduler]$ kubectl --kubeconfig=vc_1.config get pods -n dp-test-01
NAME READY STATUS RESTARTS AGE
nginx-deploy-1-5889665c9f-56js6 1/1 Running 0 9s
nginx-deploy-1-5889665c9f-7jlkv 1/1 Running 0 9s
nginx-deploy-1-5889665c9f-7tnr7 1/1 Running 0 8s
nginx-deploy-1-5889665c9f-l5bf4 1/1 Running 0 8s
nginx-deploy-1-5889665c9f-mf6jx 1/1 Running 0 9s
nginx-deploy-1-5889665c9f-nt5sq 1/1 Running 0 9s
nginx-deploy-1-5889665c9f-phjpg 1/1 Running 0 9s
nginx-deploy-1-5889665c9f-pl6tt 1/1 Running 0 9s
nginx-deploy-1-5889665c9f-sk9l9 1/1 Running 0 9s
nginx-deploy-1-5889665c9f-vwphh 1/1 Running 0 8s
参考资料:
https://martinfowler.com/articles/microservices.html
https://www.redhat.com/zh/topics/containers/what-is-kubernetes
https://jimmysong.io/kubernetes-handbook/cloud-native/cloud-native-definition.html
https://kubernetes.io/docs/home/