Service 概述
Service是K8s里的 固定统一入口 + 负载均衡 抽象资源。
Service解决的问题:
- Pod IP不固定的问题
- pod ip是动态分配的,之前在实操时也看到了
- 如果直接用pod ip来访问服务,那么pod一变更服务就无法使用,完全用不了
- service ip能够给一组同类pod绑定一个固定的访问地址,来统一暴露后面的pod服务,即使pod变化也无感知
- 多Pod负载均衡问题
- 内置简单轮询负载均衡分发到多个后端pod
- 服务发现问题
- service能够自动生成集群内DNS域名
- 故障Pod自动剔除
- 只有pod标记为
Ready状态,才会被service发现
Service 操作实践
还是把前面的代码拿来改成V3版本,在之前的 hellok8sworker1节点。
package main
import ( "fmt" "io" "net/http" "os")
func hello(w http.ResponseWriter, r *http.Request) { host, _ := os.Hostname() io.WriteString(w, fmt.Sprintf("[v3] Hello, Kubernetes!, From host: %s", host))}
func main() { http.HandleFunc("/", hello) http.ListenAndServe(":3000", nil)}master1上修改deployment.yaml文件镜像为v3版本。
apiVersion: apps/v1kind: Deploymentmetadata: name: nginx-redis-deploymentspec:
strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1
replicas: 3 selector: matchLabels: app: nginx-redis template: metadata: labels: app: nginx-redis spec: containers: # 容器1:hellok8s v3 - name: hellok8s image: fakeragments/hellok8s:v3 imagePullPolicy: IfNotPresent ports: - containerPort: 3000
# 容器2:Redis - name: redis image: redis:alpine ports: - containerPort: 6379定义一个 service 资源,使用 ClusterIP 的方式定义,通过 ClusterIP 暴露服务。
ClusterIP是Service默认类型。会给Service分配一个仅集群内部可达的虚拟固定 IP,只能在集群内 Pod、节点上访问,外网 / 集群外默认无法访问。
创建 service.yaml 文件,内容如下:
apiVersion: v1kind: Servicemetadata: name: nginx-redis-servicespec: selector: app: nginx-redis ports: - port: 80 targetPort: 3000 protocol: TCP name: http - port: 6379 targetPort: 6379 protocol: TCP name: redis创建后使用apply应用,再查看相关状态:
root@k8s-master1:~# kubectl apply -f service.yaml# service/nginx-redis-service createdroot@k8s-master1:~# kubectl get endpoints# NAME ENDPOINTS AGE# kubernetes 192.168.24.130:6443 6d23h# nginx-redis-service 172.30.194.85:6379,172.30.194.86:6379,172.30.194.87:6379 + 3 more... 8sroot@k8s-master1:~# kubectl get pods -o wide# NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES# nginx-redis-deployment-578b9464fd-6nqz7 3/3 Running 0 22h 172.30.194.85 k8s-worker1 <none> <none># nginx-redis-deployment-578b9464fd-mrs57 3/3 Running 0 22h 172.30.194.87 k8s-worker1 <none> <none># nginx-redis-deployment-578b9464fd-xfnch 3/3 Running 0 22h 172.30.194.86 k8s-worker1 <none> <none>
root@k8s-master1:~# kubectl get svc# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE# kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d23h# nginx-redis-service ClusterIP 10.105.64.217 <none> 3000/TCP,6379/TCP 39s其中 endpoints 包含了 selector 选择的 pod ,这里的kubernetes为k8s自带的service,不管。
service的selector要和deployment中的selector一致。
再对比下 pods 的内部 ip ,可以看到 endpoints 中的 ip 和内部保持一致。
将 deployment.yaml 的 replica 修改为 2 ,应用后再看:
root@k8s-master1:~# kubectl apply -f deployment.yaml# deployment.apps/nginx-redis-deployment configuredroot@k8s-master1:~# kubectl get endpoints# NAME ENDPOINTS AGE# kubernetes 192.168.24.130:6443 6d23h# nginx-redis-service 172.30.194.88:6379,172.30.194.89:6379,172.30.194.88:3000 + 1 more... 6m33sroot@k8s-master1:~# kubectl get pods -o wide# NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES# nginx-redis-deployment-fcd7c9bcd-jskkj 3/3 Running 0 12s 172.30.194.89 k8s-worker1 <none> <none># nginx-redis-deployment-fcd7c9bcd-mms9f 3/3 Running 0 12s 172.30.194.88 k8s-worker1 <none> <none>root@k8s-master1:~# kubectl get svc# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE# kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d23h# nginx-redis-service ClusterIP 10.105.64.217 <none> 3000/TCP,6379/TCP 12m可以看到 pod ip 发生了变化,service 中的ip未发生改变,且 endpoints 中的ip和 pod ip 仍然保持一致。
ServiceTypes
service类型包含以下几种:
- ClusterIP(默认)
- 只在集群内部可见,分配一个集群内部虚拟ip
- pod之间互访,外部无法使用
- NodePort
- 在 ClusterIP 基础上,在每个
节点开放一个端口 - 外部可通过节点IP
访问服务,不适合生产高可用
- LoadBalancer
- 云厂商使用,外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。
- ExternalName
- 不代理流量、不创建 IP、不关联 Pod,
- 把集群内部的 Service 名字,映射成一个外部的域名。
- Headless(无头服务,特殊 ClusterIP)
- 不分配虚拟 IP,DNS 直接解析到所有后端 Pod IP
- 用于有状态应用(StatefulSet)、需要直连 Pod 的场景
1. ClusterIP
前面说过 service ip(ClusterIP)是集群内部可达的,外网 / 集群外默认无法访问。
集群内部创建一个 nginx.yaml 文件,内容如下:
apiVersion: v1kind: Podmetadata: name: nginx-podspec: containers: - name: nginx image: nginx应用这个 nginx.yaml 文件新增个pod:
root@k8s-master1:~# kubectl apply -f nginx.yaml# pod/nginx createdroot@k8s-master1:~# kubectl get pod -o wide# NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES# nginx 1/1 Running 0 15s 172.30.194.90 k8s-worker1 <none> <none>进入这个 nginx pod 内部,使用curl来测试pod service ip 可达状态,之后在master1和worker1上测试 curl service ip:
root@k8s-master1:~# kubectl exec -it nginx -- /bin/bashroot@nginx:/# curl 172.30.194.88:3000# [v3] Hello, Kubernetes!, From host: nginx-redis-deployment-fcd7c9bcd-mms9froot@nginx:/# curl 172.30.194.89:3000# [v3] Hello, Kubernetes!, From host: nginx-redis-deployment-fcd7c9bcd-jskkj
# master1root@k8s-master1:~# curl 172.30.194.89:3000# [v3] Hello, Kubernetes!, From host: nginx-redis-deployment-fcd7c9bcd-jskkjroot@k8s-master1:~# curl 172.30.194.88:3000# [v3] Hello, Kubernetes!, From host: nginx-redis-deployment-fcd7c9bcd-mms9f
# worker1root@k8s-worker1:~# curl 172.30.194.88:3000# [v3] Hello, Kubernetes!, From host: nginx-redis-deployment-fcd7c9bcd-mms9froot@k8s-worker1:~# curl 172.30.194.89:3000# [v3] Hello, Kubernetes!, From host: nginx-redis-deployment-fcd7c9bcd-jskkj可以看同一集群内Pod之间通过 service ip 是可达的,集群每个节点属于集群内部网络域,节点上能直接访问 ClusterIP。并且两次返回的 pod 是不同的,说明了 service 的负载均衡机制。
2. NodePort
K8S的集群是由多个 Node 组成,NodePort 通过 Node的IP地址 和 静态端口 来暴露服务,如图所示:
外部通过访问node对外映射的30000端口,来达到访问服务的目的。

新增 service-nodeport.yaml 文件,将 type 改为 NodePort:
apiVersion: v1kind: Servicemetadata: name: nginx-redis-servicespec: type: NodePort selector: app: nginx-redis ports: - port: 3000 nodePort: 30000 protocol: TCP name: http - port: 6379 nodePort: 30001 protocol: TCP name: redis应用 service-nodeport.yaml, selector 会自动选择 tag 为 nginx-redis 的 pod。
root@k8s-master1:~# kubectl apply -f service-nodeport.yaml# service/nginx-redis-service created应用成功后查看svc状态,可以看到 service 也会被分配一个 clusterip,selector 选择了 app tag 为 nginx-redis 的pod。3000 和 6379 只在 ClusterIP(10.97.125.187) 上监听,而 30000 和 30001 在所有节点的真实IP上监听。至于为啥是所有节点,主要是kube-proxy特性导致的,会监听所有节点的Nodeport端口。
root@k8s-master1:~# kubectl get svc -o wide# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR# kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 9d <none># nginx-redis-service NodePort 10.97.125.187 <none> 3000:30000/TCP,6379:30001/TCP 74s app=nginx-redis这时候在集群外部(宿主机)访问 节点IP:30000,可以正常访问到服务。

