k8s-service
1. 简介
kubernets service 是将运行一组pods上的应用程序公开为网络服务的抽象方法。
有了 kubernets service,你就无需修改应用程序即可使用服务发现机制,kubernets 为 pods 提供自己的ip地址,并为一组pod提供相同的DNS名,并且可以在它们之间进行负载均衡。
2. 为什么要用services
创建和销毁 kubernets pod 以匹配集群状态。pod 是非永久性资源。如果你使用 Deployment 来运行你的应用程序,则它可以动态创建和销毁 Pod。
每个 Pod 都有自己的 IP 地址,但是在 Deployment 中,在同一时刻运行的 Pod 集合可能与稍后运行该应用程序的 Pod 集合不同。
这导致了一个问题: 如果一组 Pod(称为“后端”)为集群内的其他 Pod(称为“前端”)提供功能, 那么前端如何找出并跟踪要连接的 IP 地址,以便前端可以使用提供工作负载的后端部分?
3. quick start
3.1 创建svc
资源模板 svc-deploy-nginx.yaml
信息如下:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deploy-test
name: nginx-test-1
spec:
replicas: 3
selector:
matchLabels:
app: nginx-test
template:
metadata:
labels:
app: nginx-test
spec:
containers:
- image: nginx:latest
name: nginx
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
# must be match pod template .spec.template.labels
app: nginx-test
ports:
- protocol: TCP
port: 8000
targetPort: 80
创建一个deploy,和一个 svc
注意 svc selector 对应的是 deploy 的 .spec.template.labels
创建资源
$ kubectl create -f svc-deploy-nginx.yaml
3.2 查看svc
-
使用资源文件查看
$ kubectl get -f svc-deploy-nginx.yaml -o wide # 输出内容如下 # deploy 信息 NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR deployment.apps/nginx-test-1 3/3 3 3 42m nginx nginx:latest app=nginx-test # svc 信息 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR service/my-service ClusterIP 10.96.112.5
8000/TCP 42m app=nginx-test -
使用命令查看
查看svc 信息
$ kubectl get svc/my-service -owide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR my-service ClusterIP 10.96.112.5
8000/TCP 45m app=nginx-test 查看endpoints信息
可以看到成功的绑定了三个pod endpoint
$ kubectl get ep/my-service NAME ENDPOINTS AGE my-service 10.100.132.133:80,10.100.132.139:80,10.100.132.140:80 46m
查看svc 详细信息
$ kubectl describe svc/my-service Name: my-service Namespace: default Labels:
Annotations: Selector: app=nginx-test Type: ClusterIP IP: 10.96.112.5 Port: 8000/TCP TargetPort: 80/TCP Endpoints: 10.100.132.133:80,10.100.132.139:80,10.100.132.140:80 Session Affinity: None Events:
3.3 访问svc。
ClusterIp
类型的svc。4.2 NodePort
node(节点)port(端口),顾名思义 NodePort
类型的svc是通过在集群的宿主机上开放访问的端口,并通过 <节点 IP>:<节点端口>
(集群任意节点ip:nodeport 都可以访问)来实现从集群外部访问其svc的。
nodePort 的原理在于在 node 上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy 进一步到给对应的 pod。(能够将内部服务暴露给外部的一种方式)
-
创建资源
一个deploy 和 一个 nodeport svc,且指定了node 端口为
30007
(如果不指定,k8s会默认分配一个)apiVersion: apps/v1 kind: Deployment metadata: labels: app: nginx-deploy-test name: nginx-test-2 spec: replicas: 3 selector: matchLabels: app: nginx-test-02 template: metadata: labels: app: nginx-test-02 spec: containers: - image: nginx:latest name: nginx imagePullPolicy: IfNotPresent --- apiVersion: v1 kind: Service metadata: name: my-service-2 spec: type: NodePort selector: app: nginx-test-02 ports: - protocol: TCP port: 8000 targetPort: 80 # 可选字段 # 默认情况下,为了方便起见,Kubernetes 会从范围内分配一个端口号(默认:30000-32767) nodePort: 30007
-
访问svc
4.3 LoadBalancer
使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort
服务和 ClusterIP
服务上。
4.4 ExternalName类型的服务,查找其 CNAME 记录
Endpoints
的记录关于Headless Services 在博文中 有详细的记录,感兴趣的可以访问了解下。
5. externalIPs
Service 的 externalIPs(spec.externalIPs
)可以设置一组外部的 IP 地址,并且将流量导入到集群内部。
如图:
启动一个springboot项目 并初始化一个接口,访问后返回
Hello World~
在本机上访问:
接下来我们就使用externalIPs
将 192.168.0.101
流量导入到集群内部
目标:集群pod中访问
192.168.0.101:8080
时不是访问此时的这个springboot项目,而是访问集群内的externalIPs
svc
-
首先到集群master节点访问一下springboot项目
访问成功
$ curl 192.168.0.101:8080 Hello World~
-
创建
externalIPs
svc,和想导入到集群的目标pod本实例主要是为了展示流量的切换,但其实
spec.externalIPs
可以指定符合ip 规则的任意地址(即使设置的ip不可达),k8s都会帮你 在访问设置地址的服务时,将流量导入到spec.selector
的服务中去实际上就是 如果你把
spec.externalIPs
地址设置成123.123.123.123
,当你在服务中访问123.123.123.123
时,其实访问的是spec.selector
指定的服务其实更像是 给
spec.selector
到服务指定了一个 ipv4 的别名apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 8080 targetPort: 80 name: web externalIPs: - "192.168.0.101" # 将 192.168.0.101 流量导入到 下面的Deployment中 selector: app: nginx --- apiVersion: apps/v1 kind: Deployment metadata: name: web spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 name: web
服务信息如下:
$ kubectl get deploy -owide NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR web 2/2 2 2 3h25m nginx nginx app=nginx $ kubectl get svc/nginx -owide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR nginx ClusterIP 10.96.41.170 192.168.0.101 8080/TCP 4m app=nginx
-
创建一个pod 并进入pod ,在pod中访问
192.168.0.101:8080
查看访问的是外部的springboot
项目还是集群内的deploy/web
很显然:k8s成功的将流量导入到了集群内部
如果在集群节点上访问
192.168.0.101:8080
,是不能将结果导入到deploy上的,只有在集群服务内部访问才有效 -
此时我们将svc/nginx 删除掉,看下访问的结果
符合预期
6. 管理外部的服务
场景:如果我们的中间件或者数据库服务不是在k8s集群内的,而是在其他的服务器上,但是我们还是想通过访问svc的方式去访问这些外部的服务,我们应该怎么做呢?
- 声明一个没有选择符的svc
- 创建一个同名(和第一步中的svc name 相同)的
endpoints
服务去管理这些外部的服务地址
实例:
实例通过创建svc 和 endpoints,实现访问svc-name,svc 将流量导出到外部的springboot(项目信息如5.0中提到的)项目中
-
创建服务
apiVersion: v1 kind: Service metadata: name: my-service spec: ports: - protocol: TCP port: 80 targetPort: 8080 --- apiVersion: v1 kind: Endpoints metadata: # must be match svc-name name: my-service subsets: - addresses: # 指向外部的springboot 服务 - ip: 192.168.0.101 ports: - port: 8080
-
访问测试
7. 选择自己的 IP 地址
在 Service
创建的请求中,可以通过设置 spec.clusterIP
字段来指定自己的集群 IP 地址。