1 为什么使用Kubernetes
1)从单机走向集群已成为必然趋势。
2)Kubernetes可以全面拥抱微服务架构。
3)可以随时随地将系统整体“搬迁”到公有云上。
华为云(CCE)、阿里云(ACK)和腾讯云(TKE)先后宣布支持Kubernetes集群,基于Service的虚拟IP地址(Cluster IP)的设计思路让架构与底层的硬件拓扑无关,我们无须改变运行期的配置文件,就能将系统从现有的物理机环境无缝迁移到公有云上。
4)Kubernetes内在的服务弹性扩容机制可以让我们轻松应对突发流量。
5)Kubernetes系统架构具有超强的横向扩容能力。
利用Kubernetes提供的工具,不用修改代码,就能将一个Kubernetes集群从只包含几个Node的小集群平滑扩展到拥有上百个Node的大集群。
2 K8s使用案例
功能:JSP页面通过JDBC直接访问MySQL数据库并展示数据。
此应用需要启动两个容器:Web App容器和MySQL容器,并且Web App容器需要访问MySQL容器。
在Docker时代,我们在一个宿主机上启动了这两个容器,需要把MySQL容器的IP地址通过环境变量注入Web App容器里;同时,需要将WebApp容器的8080端口映射到宿主机的8080端口,以便在外部访问。
k8s的时代,实现方法如下:
首先,为MySQL服务创建一个RC定义文件mysql-rc.yaml:
apiVersion: v1
kind: ReplicationController # 副本控制器RC
metadata:
name: mysql # RC的名称,全局唯一
spec:
replicas: 1 # Pod副本的期待数量
selector:
app: mysql # 符合目标的Pod拥有此标签
template: # 根据此模板创建Pod的副本(实例)
metadata:
labels:
app: mysql # Pod副本拥有的标签,对应RC的Selector
spec:
containers: # Pod内容器的定义部分
- name: mysql # 容器的名称
image: mysql # 容器对应的Docker Image
ports:
- containerPort: 3306 # 容器应用监听的端口号
env: # 注入容器内的环境变量
- name: MYSQL_ROOT_PASSWORD
value: "123456"
在Master上执行命令:
# kubectl create -f mysql-rc.yaml
# kubectl get rc
# kubectl get pods
通过docker ps指令查看正在运行的容器,发现提供MySQL服务的Pod容器已经创建并正常运行了,此外还多创建了一个来自谷歌的pause容器,这就是Pod的“根容器”。
最后,创建一个与之关联的Kubernetes Service——MySQL的定义文件(文件名为mysql-svc.yaml)
apiVersion: v1
kind: Service # 表明是Kubernetes Service
metadata:
name: mysql # Service的全局唯一名称
spec:
ports:
- port: 3306 # Service提供服务的端口号
selector: # Service对应的Pod拥有这里定义的标签
app: mysql
运行kubectl命令,创建Service:
# kubectl create -f mysql-svc.yaml
# kubectl get svc
# kubectl get endpoints
# kubectl get svc mysql-svc -o yaml
MySQL服务会被分配Cluster IP地址。随后,Kubernetes集群中其他新创建的Pod就可以通过Service的Cluster IP+端口号3306来连接和访问它了。
接下来按照上面方式启动tomcat ,创建对应的RC文件myweb-rc.yaml,内容如下:
apiVersion: v1
kind: ReplicationController
metadata:
name: myweb
spec:
replicas: 2
selector:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
containers:
- name: myweb
image: kubeguide/tomcat-app:v1
ports:
- containerPort: 8080
env:
- name: MYSQL_SERVICE_HOST
value: 'mysql'
- name: MYSQL_SERVICE_PORT
value: '3306'
最后,创建对应的Service。以下是完整的YAML定义文件(myweb-svc.yaml):
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
type: NodePort
ports:
- port: 8080
nodePort: 30001
selector:
app: myweb
type=NodePort和nodePort=30001的两个属性表明此Service开启了NodePort方式的外网访问模式。在Kubernetes集群之外,比如在本机的浏览器里,可以通过30001这个端口访问myweb(对应到8080的虚端口上)。
3 k8s基础概念
Kubernetes具有完备的集群管理能力,包括多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和服务发现机制、内建的智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制,以及多粒度的资源配额管理能力。
3.1 Service
在Kubernetes中,Service是分布式集群架构的核心,一个Service对象拥有如下关键特征。
- ◎ 拥有唯一指定的名称(比如mysql-server)。
- ◎ 拥有一个虚拟IP(Cluster IP、Service IP或VIP)和端口号。
- ◎ 能够提供某种远程服务能力。
- ◎ 被映射到提供这种服务能力的一组容器应用上。
一个Service通常由多个相关的服务进程提供服务,可以通过Service(虚拟Cluster IP +Service Port)连接到指定的Service。
每个Service其实就是我们经常提起的微服务架构中的一个微服务,Service定义了一个服务的访问入口地址,前端的应用(Frontend Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,Service与其后端Pod副本集群之间则是通过Label Selector来实现无缝对接的。RC的作用实际上是保证Service的服务能力和服务质量始终符合预期标准。
Kubernetes中的IP:
- ◎ Node IP:Node的IP地址。
- ◎ Pod IP:Pod的IP地址。Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络。
- ◎ Cluster IP:Service的IP地址。
4.2 Pod
容器提供了强大的隔离功能,所以有必要把为Service提供服务的这组进程放入容器中进行隔离。为此,Kubernetes设计了Pod对象,将每个服务进程都包装到相应的Pod中,使其成为在Pod中运行的一个容器(Container)。为了建立Service和Pod间的关联关系,Kubernetes首先给每个Pod都贴上一个标签(Label)。
通常在一个Node节点上可运行几百个Pod。在每个Pod中都运行着一个特殊的被称为Pause的根容器,其他容器则为业务容器,这些业务容器共享Pause容器的网络栈和Volume挂载卷。
Kubernetes为每个Pod都分配了唯一的IP地址,称之为Pod IP,一个Pod里的多个容器共享Pod IP地址。Pod的IP加上这里的容器端口(containerPort),组成了一个新的概念——Endpoint,它代表此Pod里的一个服务进程的对外通信地址。
3.3 master和node
1)master - 控制平⾯
控制平⾯的组件对集群做出全局决策,为了稳定⼀般不会在上⾯部署应用。
API server 是 k8s 控制⾯的前端,也是⽤户唯⼀可以直接交互的 k8s 组件内部系统组件以及外部⽤户组件都通过相同的API进⾏通信。
kube-scheduler 监听 apiserver,发现有待调度的 pod,就按照⼀定的调度策略绑定到合适的 Node 上,并把信息写⼊到 etcd。
kube-controller-manager 控制器的作⽤是从API Server获得所需状态。它检查要控制的节点的当前状态,确定是否与所需状态存在任何差异,并解决它们。
2)node - 计算节点(⼯作节点)
维护运⾏的pod,提供 kubernetes 运⾏环境
Node作为集群中的工作节点,在Node上运行着Kubernetes的kubelet、kube-proxy、Docker Engine(docker)服务进程,负责Pod的创建、启动、监控、重启、销毁,以及实现软件模式的负载均衡器。
通过kubectl describe node <node_name>查看某个Node的详细信息:
3.4 服务扩容
在Kubernetes集群中,只需为需要扩容的Service关联的Pod创建一个RC(Replication Controller),在一个RC定义文件中包括以下3个关键信息。
- ◎ 目标Pod的定义。
- ◎ 目标Pod需要运行的副本数量(Replicas)。
- ◎ 要监控的目标Pod的标签。
3.5 Label
Label是用来资源管理的。一些常用的Label示例如下:
◎ 版本标签:"release" : "stable"、"release" : "canary"。
◎ 环境标签:"environment":"dev"、"environment":"qa"、"environment":"production"。
◎ 架构标签:"tier" : "frontend"、"tier" : "backend"、"tier" : "middleware"。
◎ 分区标签:"partition" : "customerA"、"partition" : "customerB"。
◎ 质量管控标签:"track" : "daily"、"track" : "weekly"。
Label标签会再其他pod的Label Selector里查询到,Label Selector可以被类比为SQL语句中的where查询条件,
name=redis-slave,env!=production
name notin (php-frontend),env!=production
其次,matchLabels用于定义一组Label,与直接写在Selector中的作用相同;matchExpressions用于定义一组基于集合的筛选条件,包括In 、 NotIn 、 Exists和DoesNotExist。
selector:
matchLabels:
app: myweb
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
- {key: environment, operator: NotIn, values: [dev]}
3.6 Replication Controller 资源对象
声明某种Pod的副本数量在任意时刻都符合某个预期值,RC的定义包括如下几个部分。
◎ Pod期待的副本数量。
◎ 用于筛选目标Pod的Label Selector。
◎ 当Pod的副本数量小于预期数量时,用于创建新Pod的Pod模板(template)。
可以使用如下命令定义副本数量
kubectl scale rc redis-slave --replicas=3 scaled
注意:Replica Set与Deployment这两个重要的资源对象逐步替代了之前RC的作用。
3.7 Deployment 资源对象
创建namespace:kubectl create ns test
创建Deployment:kubectl create deployment nginx-manual --image=nginx:alpine -n test
创建Deployment:kubectl apply -f nginx.yaml -n test
查看信息:kubectl get deploy -n test
动态扩缩容:kubectl scale deployment nginx-deployment -n test --replicas=0
3.8 Horizontal Pod Autoscaler 资源对象
通过追踪分析指定RC控制的所有目标Pod的负载变化情况,来确定是否需要有针对性地调整目标Pod的副本数量。
HPA有以下两种方式作为Pod负载的度量指标。
◎ CPUUtilizationPercentage。
◎ 应用程序自定义的度量指标,比如服务在每秒内的相应请求数(TPS或QPS)。
apiVersion:
autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
namespace: default
spec:
maxReplicas: 10
minReplicas: 1
scaleTargetRef:
kind: Deployment
name: php-apache
targetCPUUtilizationPercentage: 90
当这些Pod副本的CPUUtilizationPercentage的值超过90%时会触发自动动态扩容行为,在扩容或缩容时必须满足的一个约束条件是Pod的副本数为1~10。
3.9 StatefulSet 资源对象
StatefulSet从本质上来说,可以看作Deployment/RC的一个特殊变种,它有如下特性。
◎ StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。
◎ StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经是运行且准备好的状态。
◎ StatefulSet里的Pod采用稳定的持久化存储卷,通过PV或PVC来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(为了保证数据的安全)。
3.10 Job 资源对象
批处理任务通常并行(或者串行)启动多个计算进程去处理一批工作项(work item),在处理完成后,整个批处理任务结束。Job就是用来并行处理的。
(1)Job所控制的Pod副本是短暂运行的,可以将其视为一组Docker容器,其中的每个Docker容器都仅仅运行一次。
(2)Job所控制的Pod副本的工作模式能够多实例并行计算。
3.11 Volume
Volume(存储卷)是Pod中能够被多个容器访问的共享目录。
先在Pod上声明一个Volume,然后在容器里引用该Volume并挂载(Mount)到容器里的某个目录上。
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
volumes:
- name: datavol
emptyDir: {}
containers:
- name: tomcat-demo
image: tomcat
volumeMounts:
- mountPath: /mydata-data
name: datavol
imagePullPolicy: IfNotPresent
Kubernetes提供了非常丰富的Volume类型,下面逐一进行说明。
1.emptyDir
一个emptyDir Volume是在Pod分配到Node时创建的。当Pod从Node上移除时,emptyDir中的数据也会被永久删除。
2.hostPath
hostPath为在Pod上挂载宿主机上的文件或目录。
3.NFS
使用NFS网络文件系统提供的共享目录存储数据时,我们需要在系统中部署一个NFS Server。
4. 其他公有云存储
3.12 Persistent Volume
Volume是被定义在Pod上的,属于计算资源的一部分,而实际上,网络存储是相对独立于计算资源而存在的一种实体资源。
在使用虚拟机的情况下,我们通常会先定义一个网络存储,然后从中划出一个“网盘”并挂接到虚拟机上。Persistent Volume(PV)和与之相关联的Persistent Volume Claim(PVC)也起到了类似的作用。
◎ PV只能是网络存储,不属于任何Node,但可以在每个Node上访问。
◎ PV并不是被定义在Pod上的,而是独立于Pod之外定义的。
3.13 namespace
Namespace在很多情况下用于实现多租户的资源隔离。如果不特别指明Namespace,则用户创建的Pod、RC、Service都将被系统创建到这个默认的名为default的Namespace中。