1. 简介

Service的出现,就是解决ip不固定的问题
当Pod宕机后重新生成时,其IP等状态信息可能会变动,Service会根据Pod的Label对这些状态信息进行监控和变更,保证上游服务不受Pod的变动而影响。

在 Kubernetes集群中,每个 Node运行一个 kube-proxy进程。 kube-proxy负责为 Service实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName的形式。在 Kubernetes v1.0版本,代理完全在 userspace。在 Kubernetes v1.1版本,新增了 iptables代理,但并不是默认的运行模式。从 Kubernetes v1.2起,默认就是 iptables代理。在 Kubernetes v1.8.0-beta.0中,添加了 ipvs代理。在 Kubernetes 1.14版本开始默认使用 ipvs代理。

2. Service 基础导论

  • 客户端访问节点时通过 iptables实现的
  • iptables规则是通过 kube-proxy写入的
  • apiserver通过监控 kube-proxy去进行对服务和端点的监控
  • kube-proxy通过 pod的标签( lables)去判断这个断点信息是否写入到 Endpoints里

3. 代理模式分类

3.1 iptables 代理模式

3.2 ipvs 代理模式

ipvs代理模式中 kube-proxy会监视 Kubernetes Service对象和 Endpoints,调用 netlink接口以相应地创建 ipvs规则并定期与 Kubernetes Service对象和 Endpoints对象同步 ipvs规则,以确保 ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod。

与 iptables类似, ipvs于 netfilter的 hook功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外, ipvs为负载均衡算法提供了更多选项,例如:

  • rr:轮询调度
  • lc:最小连接数
  • dh:目标哈希
  • sh:源哈希
  • sed:最短期望延迟
  • nq:不排队调度

4. service类型

Service在 K8s中有以下四种类型:

4.1 ClusterIp

ClusterIP主要在每个node节点使用iptables,将发向ClusterIP对应端口的数据,转发到kube-proxy中。然后kube-proxy自己内部实现有负载均衡的方法,并可以查询到这个service下对应pod的地址和端口,进而把数据转发给对应的pod的地址和端口。

为了实现图上的功能,主要需要以下几个组件的协同工作:

  • apiserver:用户通过 kubectl命令向 apiserver发送创建 service的命令, apiserver接收到请求后将数据存储到 etcd中
  • kube-proxy: Kubernetes的每个节点中都有一个叫做 kube-porxy的进程,这个进程负责感知 service、 pod的变化,并将变化的信息写入本地的 iptables规则中
  • iptables:使用 NAT等技术将 virtualIP的流量转至 endpoint中

4.2 Handless Service

有时不需要或不想要负载均衡,以及单独的 Service IP。遇到这种情况,可以通过指定 spec.clusterIP的值为 None来创建 Headless Service 。这类 Service并不会分配 Cluster IP, kube-proxy不会处理它们,而且平台也不会为它们进行负载均衡和路由。

4.3 NodePort

NodePort的原理在于在 Node上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy进一步到给对应的 pod。

4.3 LoadBalancer

LoadBalancer和NodePort其实是同一种方式。区别在于LoadBalancer比NodePort多了一步,就是可以调用Cloud provider去创建LB来向节点导流

4.4 ExternalName

这种类型的 Service通过返回 CNAME和它的值,可以将服务映射到 externalName字段的内容
ExternalName Service是 Service的特例,它没有 selector,也没有定义任何的端口和 Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。

5. 练习

5.1 创建deployment

创建一个nginx镜像3个副本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: web1
name: web1
spec:
replicas: 3
selector:
matchLabels:
app: web1
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: web1
app1: web1
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: nginx
resources: {}
status: {}

5.2 创建svc

1
kubectl expose --name web1 deployment web1 --port 80 --target-port 80 --dry-run=client -o yaml > svc1.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: web1
name: web1
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: web1
status:
loadBalancer: {}

5.3 列出svc关联的pod

1
2
3
4
5
[root@node001 chap9-svc]# kubectl get pod -l `kubectl get svc -o wide|awk '/svc2/{print $NF}'`
NAME READY STATUS RESTARTS AGE
web1-68c8cd7dbb-hjcp7 1/1 Running 0 25h
web1-68c8cd7dbb-xg4sj 1/1 Running 0 25h

6. 服务发现

6.1 通过变量方式发现

1.只能获取相同namespace里的变量
2.变量的获取有先后顺序,引用的变量必须要先创建

假设我们创建了一些服务,然后再创建了pod,在pod里会通过一些变量来记录这些服务的信息
SVCNAME_SERVICE_HOST=svcname的IP
SVCNAME_SERVICE_PORT=svcname的端口

6.2 通过DNS的方式发现

在kube-system里有dns,可以自动发现所有名称空间里的服务的clusterIP,所以在同一个名称空间里,一个服务访问另一个服务的时候,可以直接通过服务名来访问。
只要创建了一个服务(不管在哪个ns里创建的),都会自动向kube-system里的dns注册

如果是不同的名称空间,可以通过:服务名.名称空间名 来访问
服务名.名称空间.svc.cluster.local

7. 服务的发布

所谓发布指的是,如何让集群之外的主机能访问到服务

7.1 通过NodePort

1
kubectl expose --name svc3 pod pod1 --port 80 --type NodePort

7.2 通过external-ip

1
kubectl expose --name svc2 pod pod1 --port 80 --external-ip 192.168.10.42

7.3 通过LoadBalancer

7.3.1 安装metallb

创建名称空间

1
kubectl create ns metallb-system

安装metallb

1
kubectl apply -f metallb.yaml

创建metallb地址池

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: ConfigMap
metadata:
name: config
namespace: metallb-system
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.10.240-192.168.10.250

创建svc

1
kubectl expose --name svc4 pod pod1 --port 80 --type LoadBalancer

查看svc

1
2
3
[root@node001 chap9-svc]# kubectl get svc svc4 -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc4 LoadBalancer 10.99.214.113 192.168.10.240 80:30200/TCP 37s run=pod1

7.4 通过ingress

这里利用的是有Nginx实现的反向代理

7.4.1 安装ingress-nginx-controller

创建名称空间

1
kubectl create ns ingress-nginx

创建

1
kubectl aply -f nginx-controller.yaml 

7.4.2 创建ingress规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /

spec:
ingressClassName: nginx
rules:
- host: aa.rhce.cc
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc1
port:
number: 80

7.4.3 https类型的发布

创建tls类型的secret
使用网站证书可key创建secret

1
kubectl create secret tls mytlssec --key client-key.pem --cert client.pem

创建ingress规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mying
spec:
ingressClassName: nginx
tls:
- hosts:
- www1.ck8s.com
secretName: mytlssec
rules:
- host: www1.ck8s.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc1
port:
number: 80