k8s 总体概览
前言
学习 k8s 之前首先对 k8s 中具体的组件做个简单的了解。
Pod
Pod 是 k8s 中集群部署应用和服务的最小单元,一个 pod 中可以部署多个容器。
Pod 的设计理念是支持多个容器在一个 Pod *享网络地址和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。Pod 对多容器的支持是 K8 最基础的设计理念。
副本控制器(Replication Controller,RC)
RC 是 k8s 集群中最早的保证 Pod 高可用的 API 对象。它的作用就是保证集群中有指定数目的 pod 运行。
当前运行的 pod 数目少于指定的数目,RC 就会启动新的 pod 副本,保证运行 pod 数量等于指定数目。
当前运行的 pod 数目大于指定的数目,RC 就会杀死多余的 pod 副本。
副本集(Replica Set,RS)
RS 是新一代 RC,提供同样的高可用能力,区别主要在于 RS 后来居上,能支持更多种类的匹配模式。副本集对象一般不单独使用,而是作为 Deployment 的理想状态参数使用。
部署(Deployment)
Deployment 提供了一种对 Pod 和 ReplicaSet 的管理方式,每一个 Deployment 都对应集群中的一次部署,是非常常见的 Kubernetes 对象。
Deployment 是一个比 RS 应用模式更广的 API 对象,可以用来创建一个新的服务,更新一个新的服务,也可以用来滚动升级一个服务。
滚动升级一个服务,滚动升级一个服务,实际是创建一个新的 RS,然后逐渐将新 RS 中副本数增加到理想状态,将旧 RS 中的副本数减小到 0 的复合操作;这样一个复合操作用一个 RS 是不太好描述的,所以用一个更通用的 Deployment 来描述。
服务(Service)
RC、RS 和 Deployment 只是保证了支撑服务的微服务 Pod 的数量。但是没有解决如何访问这些服务的问题。
一个 Pod 只是一个运行服务的实例,随时可能在节点上停止,然后再新的节点上用一个新的 IP 启动一个新的 Pod,因此不能使用确定的 IP 和端口号提供服务。这对于业务来说,就不能根据 Pod 的 IP 作为业务调度。kubernetes 就引入了 Service 的概 念,它为 Pod 提供一个入口,主要通过 Labels 标签来选择后端Pod,这时候不论后端 Pod 的 IP 地址如何变更,只要 Pod 的 Labels 标签没变,那么 业务通过 service 调度就不会存在问题。
同时 service 对绑定的 Pod 提供了负载均衡的功能,我们业务直接使用 service 即可。
当声明Service的时候,会自动生成一个cluster IP,这个IP是虚拟IP。我们就可以通过这个IP来访问后端的Pod,当然,如果集群配置了DNS服务,比如现在 的CoreDNS,那么也可以通过Service的名字来访问,它会通过DNS自动解析Service的IP地址。
Service 对外暴露服务的方式
1、ClusterIP (默认) :在集群的内部 IP 上公开 Service 。这种类型使得 Service 只能从集群内访问,一般这种类型的 Service 上层会挂一个 Ingress,通过 Ingress 暴露服务;
2、NodePort:在每个选定 Node 的相同端口上公开 Service,使用 <NodeIP>:<NodePort>
即可从集群外部访问 Service;
3、LoadBalancer:使用云厂商的 K8S 集群,即可使用这种方式的暴露 Service,自建的服务一般不支持。使用 LoadBalancer ,会生成一个 IP 地址,通过这个即可访问 Service, 通知这个 IP 也是高可用的;
4、ExternalName: 通过返回带有该名称的 CNAME 记录,使用任意名称(由 spec 中的externalName指定)公开 Service。不使用代理。这种类型需要kube-dns的v1.7或更高版本。
什么是 CNAME:这种记录允许您将多个名字映射到同一台计算机。
例如:当您拥有多个域名需要指向同一服务器IP,此时您就可以将一个域名做A记录指向服务器IP,然后将其他的域名做别名(即CNAME)到A记录的域名上;那么当您的服务器IP地址变更时,您就可以不必对一个一个域名做更改指向了,只需要更改A记录的那个域名到服务器新IP上,其他做别名(即CNAME)的那些域名的指向将自动更改到新的IP地址上(以上操作均需要在DNS处执行)。
ingress
Ingress 是反向代理规则,用来规定 HTTP/S 请求应该被转发到哪个 Service 上,比如根据请求中不同的 Host 和 url 路径让请求落到不同的 Service 上。
Ingress Controller
就是一个反向代理程序,它负责解析 Ingress 的反向代理规则,如果 Ingress 有增删改的变动,所有的 Ingress Controller
都会及时更新自己相应的转发规则,当 Ingress Controller
收到请求后就会根据这些规则将请求转发到对应的 Service。
Kubernetes 并没有自带 Ingress Controller
,它只是一种标准,具体实现有多种,需要自己单独安装,常用的是 Nginx Ingress Controller
和 Traefik Ingress Controller
。
一个集群中可以有多个 Ingress Controller
, 在Ingress 中可以指定使用哪一个 Ingress Controller
。
Ingress Controller
是部署在集群中的,怎么让 Ingress Controller
本身能够被外面访问到呢?
1、Ingress Controller 用 Deployment 方式部署,给它添加一个 Service,类型为 LoadBalancer,这样会自动生成一个 IP 地址,通过这个 IP 就能访问到了,并且一般这个 IP 是高可用的(前提是集群支持 LoadBalancer,通常云服务提供商才支持,自建集群一般没有);
2、使用 hostPort;
-
1、
Ingress Controller
用 DaemonSet 方式部署,使用集群内部的某个或某些节点作为边缘节点,给 node 添加 label 来标识,使用 nodeSelector 绑定到边缘节点,保证每个边缘节点启动一个Ingress Controller
实例,用 hostPort 直接在这些边缘节点宿主机暴露端口,然后我们可以访问边缘节点中Ingress Controller
暴露的端口,这样外部就可以访问到Ingress Controller
了; -
2、使用非亲缘性策略,使需要部署
Ingress Controller
的节点,每个节点都有一个Ingress Controller
部署,然后用 hostPort 直接在这些边缘节点宿主机暴露端口,我们就能通过这些节点的 IP 和 hostPort来访问Ingress Controller
了。
不过使用 hostPort 这种方式,我们还需要再上面部署一层负载均衡。
什么是 hostPort
这是一种直接定义 Pod 网络的方式。
hostPort 是直接将容器的端口与所调度的节点上的端口路由,这样用户就可以通过宿主机的 IP 加上来访问 Pod 了,比如:
apiVersion: v1
kind: Pod
metadata:
name: influxdb
spec:
containers:
- name: influxdb
image: influxdb
ports:
- containerPort: 8086
hostPort: 8086
节点(Node)
k8s 中通过将容器放入到节点 Node 上运行的 Pod 中来执行工作负载。 k8s 中的计算能力就是由 node 提供。
节点可以是物理机也可以是虚拟机,取决于集群的配置,通常 k8s 集群中,有越多的 node 节点,意味着有更强的计算能力。
命名空间(Namespace)
命名空间为 Kubernetes 集群提供虚拟的隔离作用,Kubernetes 集群初始有两个命名空间,分别是默认命名空间 default 和系统命名空间 kube-system,除此以外,管理员可以可以创建新的命名空间满足需要。
名字空间适用于存在很多跨多个团队或项目的用户的场景。对于只有几到几十个用户的集群,根本不需要创建或考虑名字空间。
k8s 中的部署过程
这里用一个简单的栗子来看下,看下 k8s 中应用的部署过程。
1、创建命名空间
$ kubectl create namespace study-k8s
2、使用 deployment 部署 pod
$ cat go-web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: go-web
name: go-web
namespace: study-k8s
spec:
replicas: 5
selector:
matchLabels:
app: go-web
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: go-web
spec:
containers:
- image: liz2019/test-docker-go-hub
name: go-app-container
resources: {}
status: {}
运行
$ kubectl apply -f go-web.yaml -n study-k8s
$ kubectl get pods -n study-k8s
NAME READY STATUS RESTARTS AGE
go-web-59f7dc559c-g2hjg 1/1 Running 0 5h21m
go-web-59f7dc559c-g6p8k 1/1 Running 0 5h21m
go-web-59f7dc559c-l4fgm 1/1 Running 0 5h21m
go-web-59f7dc559c-lqvpj 1/1 Running 0 5h21m
go-web-59f7dc559c-rrdjp 1/1 Running 0 5h21m
$ kubectl describe deployment nginx-deploy -n study-k8s
Name: go-web
Namespace: study-k8s
CreationTimestamp: Wed, 07 Sep 2022 15:33:58 +0800
Labels: app=go-web
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=go-web
Replicas: 5 desired | 5 updated | 5 total | 5 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=go-web
Containers:
go-app-container:
Image: liz2019/test-docker-go-hub
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: go-web-59f7dc559c (5/5 replicas created)
Events: <none>
Deployment 为 Pod 和 Replica Set
提供声明式更新。所以可以看到创建的 Deployment 里面就同时也创建好了 Replica Set
。
3、为服务创建 service
上面我们创建了一组 Pod ,接下来,我们借助于 service 来实现对这些 Pod 的访问。
$ cat go-web-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: go-web-svc
labels:
run: go-web-svc
spec:
selector:
app: go-web
ports:
- protocol: TCP
port: 8000
targetPort: 8000
name: go-web-http
运行
$ kubectl apply -f go-web-svc.yaml -n study-k8s
$ kubectl get svc -n study-k8s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
go-web-svc ClusterIP 10.233.9.188 <none> 8000/TCP 37s
$ kubectl describe svc go-web-svc -n study-k8s
Name: go-web-svc
Namespace: study-k8s
Labels: run=go-web-svc
Annotations: <none>
Selector: app=go-web
Type: ClusterIP
IP: 10.233.9.188
Port: go-web-http 8000/TCP
TargetPort: 8000/TCP
Endpoints: 10.233.111.104:8000,10.233.111.105:8000,10.233.111.106:8000 + 2 more...
Session Affinity: None
Events: <none>
可以看到 service 已经创建完成。
4、配置 ingress 的转发策略
service 已经创建成功了,接下来我们使用 ingress
$ cat go-web-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: go-web-ingress
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: www.go-web.com
http:
paths:
- path: /index
pathType: Prefix
backend:
service:
name: go-web-svc
port:
number: 8000
部署 ingress
$ kubectl apply -f go-web-ingress.yaml -n study-k8s
$ kubectl get ingress -n study-k8s
NAME CLASS HOSTS ADDRESS PORTS AGE
go-web-ingress <none> www.go-web.com 11.11.111.113 80 107s
通过 ingress 访问
$ curl '11.11.111.113:80/index' \
--header 'Host: www.go-web.com'
<h1>hello world</h1><div>你好</div>%
也可以在本地添加 host,通过域名访问
$ sudo vi /etc/hosts
// 根据ingress部署的iP
11.11.111.113 www.liz-test.com
总结
1、Pod 是 k8s 中集群部署应用和服务的最小单元;
2、RC 是 k8s 集群中最早的保证 Pod 高可用的 API 对象。它的作用就是保证集群中有指定数目的 pod 运行;
3、RS 是新一代 RC,提供同样的高可用能力,是目前主要使用的对象;
4、Deployment 提供了一种对 Pod 和 ReplicaSet 的管理方式,RS 的使用都是结合 Deployment 来完成的。
5、一般使用 Deployment 来滚动升级一个服务,滚动升级一个服务,实际是创建一个新的 RS,然后逐渐将新 RS 中副本数增加到理想状态,将旧 RS 中的副本数减小到 0 的复合操作;这样一个复合操作用一个 RS 是不太好描述的,所以用一个更通用的 Deployment 来描述。
6、RC、RS 和 Deployment 只是保证了支撑服务的微服务 Pod 的数量。但是没有解决如何访问这些服务的问题。一个 Pod 只是一个运行服务的实例,随时可能在节点上停止,然后再新的节点上用一个新的 IP 启动一个新的 Pod,因此不能使用确定的 IP 和端口号提供服务。这对于业务来说,就不能根据 Pod 的 IP 作为业务调度。kubernetes 就引入了 Service 的概 念,它为 Pod 提供一个入口,主要通过 Labels 标签来选择后端Pod,这时候不论后端 Pod 的 IP 地址如何变更,只要 Pod 的 Labels 标签没变,那么 业务通过 service 调度就不会存在问题。
7、Service 是后端真实服务的抽象,一个 Service 可以代表多个相同的后端服务;
8、Ingress 是反向代理规则,用来规定 HTTP/S 请求应该被转发到哪个 Service 上,比如根据请求中不同的 Host 和 url 路径让请求落到不同的 Service 上;
参考
【Kubernetes 的设计理念】https://jimmysong.io/kubernetes-handbook/concepts/concepts.html
【Kubernetes中文文档】http://docs.kubernetes.org.cn/
【通俗理解Kubernetes中Service、Ingress与Ingress Controller的作用与关系】https://cloud.tencent.com/developer/article/1167282
【CNAME记录】https://zh.m.wikipedia.org/zh-hans/CNAME记录
【CNAME】https://baike.baidu.com/item/CNAME/9845877
【k8s中几个基本概念的理解】https://boilingfrog.github.io/2022/09/11/k8s总体概览/