编辑
2022-11-27
kubernetes
00
请注意,本文编写于 574 天前,最后修改于 566 天前,其中某些信息可能已经过时。

目录

Service
ClusterIP
NodePort
LoadBalancer

Service

经过前面几节的练习,可能你会有一些疑惑:

  • 为什么 pod 不就绪 (Ready) 的话,kubernetes 不会将流量重定向到该 pod,这是怎么做到的?
  • 前面访问服务的方式是通过 port-forword 将 pod 的端口暴露到本地,不仅需要写对 pod 的名字,一旦 deployment 重新创建新的 pod,pod 名字和 IP 地址也会随之变化,如何保证稳定的访问地址呢?。
  • 如果使用 deployment 部署了多个 Pod 副本,如何做负载均衡呢?

kubernetes 提供了一种名叫 Service 的资源帮助解决这些问题,它为 pod 提供一个稳定的 Endpoint。Servie 位于 pod 的前面,负责接收请求并将它们传递给它后面的所有pod。一旦服务中的 Pod 集合发生更改,Endpoints 就会被更新,请求的重定向自然也会导向最新的 pod。

ClusterIP

我们先来看看 Service 默认使用的 ClusterIP 类型,首先做一些准备工作,在之前的 hellok8s:v2 版本上加上返回当前服务所在的 hostname 功能,升级到 v3 版本。

go
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) }

Dockerfile 和之前保持一致,打包 tag=v3 并推送到远程仓库。

shell
docker build . -t guangzhengli/hellok8s:v3 docker push guangzhengli/hellok8s:v3

修改 deployment 的 hellok8sv3 版本。执行 kubectl apply -f deployment.yaml 更新 deployment。

yaml
apiVersion: apps/v1 kind: Deployment metadata: name: hellok8s-deployment spec: replicas: 3 selector: matchLabels: app: hellok8s template: metadata: labels: app: hellok8s spec: containers: - image: guangzhengli/hellok8s:v3 name: hellok8s-container

接下来是 Service 资源的定义,我们使用 ClusterIP 的方式定义 Service,通过 kubernetes 集群的内部 IP 暴露服务,当我们只需要让集群中运行的其他应用程序访问我们的 pod 时,就可以使用这种类型的Service。首先创建一个 service-hellok8s-clusterip.yaml` 文件。

yaml
apiVersion: v1 kind: Service metadata: name: service-hellok8s-clusterip spec: type: ClusterIP selector: app: hellok8s ports: - port: 3000 targetPort: 3000

首先通过 kubectl get endpoints 来看看 Endpoint。被 selector 选中的 Pod,就称为 Service 的 Endpoints。它维护着 Pod 的 IP 地址,只要服务中的 Pod 集合发生更改,Endpoints 就会被更新。通过 kubectl get pod -o wide 命令获取 Pod 更多的信息,可以看到 3 个 Pod 的 IP 地址和 Endpoints 中是保持一致的,你可以试试增大或减少 Deployment 中 Pod 的 replicas,观察 Endpoints 会不会发生变化。

shell
kubectl apply -f service-hellok8s-clusterip.yaml kubectl get endpoints # NAME ENDPOINTS AGE # service-hellok8s-clusterip 172.17.0.10:3000,172.17.0.2:3000,172.17.0.3:3000 10s kubectl get pod -o wide # NAME READY STATUS RESTARTS AGE IP NODE # hellok8s-deployment-5d5545b69c-24lw5 1/1 Running 0 112s 172.17.0.7 minikube # hellok8s-deployment-5d5545b69c-9g94t 1/1 Running 0 112s 172.17.0.3 minikube # hellok8s-deployment-5d5545b69c-9gm8r 1/1 Running 0 112s 172.17.0.2 minikube # nginx 1/1 Running 0 112s 172.17.0.9 minikube kubectl get service # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE # service-hellok8s-clusterip ClusterIP 10.104.96.153 <none> 3000/TCP 10s

接着我们可以通过在集群其它应用中访问 service-hellok8s-clusterip 的 IP 地址 10.104.96.153 来访问 hellok8s:v3 服务。

这里通过在集群内创建一个 nginx 来访问 hellok8s 服务。创建后进入 nginx 容器来用 curl 命令访问 service-hellok8s-clusterip

yaml
apiVersion: v1 kind: Pod metadata: name: nginx labels: app: nginx spec: containers: - name: nginx-container image: nginx
shell
kubectl get pods # NAME READY STATUS RESTARTS AGE # hellok8s-deployment-5d5545b69c-24lw5 1/1 Running 0 27m # hellok8s-deployment-5d5545b69c-9g94t 1/1 Running 0 27m # hellok8s-deployment-5d5545b69c-9gm8r 1/1 Running 0 27m # nginx 1/1 Running 0 41m kubectl get service # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE # service-hellok8s-clusterip ClusterIP 10.104.96.153 <none> 3000/TCP 10s kubectl exec -it nginx-pod /bin/bash # root@nginx-pod:/# curl 10.104.96.153:3000 # [v3] Hello, Kubernetes!, From host: hellok8s-deployment-5d5545b69c-9gm8r # root@nginx-pod:/# curl 10.104.96.153:3000 #[v3] Hello, Kubernetes!, From host: hellok8s-deployment-5d5545b69c-9g94t

可以看到,我们多次 curl 10.104.96.153:3000 访问 hellok8s Service IP 地址,返回的 hellok8s:v3 hostname 不一样,说明 Service 可以接收请求并将它们传递给它后面的所有 pod,还可以自动负载均衡。你也可以试试增加或者减少 hellok8s:v3 pod 副本数量,观察 Service 的请求是否会动态变更。调用过程如下图所示:

service-clusterip-fix-name

除了上述的 ClusterIp 的方式外,Kubernetes ServiceTypes 允许指定你所需要的 Service 类型,默认是 ClusterIPType 的值包括如下:

  • ClusterIP:通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。 这也是默认的 ServiceType
  • NodePort:通过每个节点上的 IP 和静态端口(NodePort)暴露服务。 NodePort 服务会路由到自动创建的 ClusterIP 服务。 通过请求 <节点 IP>:<节点端口>,你可以从集群的外部访问一个 NodePort 服务。
  • LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。
  • ExternalName:通过返回 CNAME 和对应值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。 无需创建任何类型代理。

NodePort

我们知道kubernetes 集群并不是单机运行,它管理着多台节点即 Node,可以通过每个节点上的 IP 和静态端口(NodePort)暴露服务。如下图所示,如果集群内有两台 Node 运行着 hellok8s:v3,我们创建一个 NodePort 类型的 Service,将 hellok8s:v33000 端口映射到 Node 机器的 30000 端口 (在 30000-32767 范围内),就可以通过访问 http://node1-ip:30000 或者 http://node2-ip:30000 访问到服务。

service-nodeport-fix-name

这里以 minikube 为例,我们可以通过 minikube ip 命令拿到 k8s cluster node IP地址。下面的教程都以我本机的 192.168.59.100 为例,需要替换成你的 IP 地址。

shell
minikube ip # 192.168.59.100

接着以 NodePort 的 ServiceType 创建一个 Service 来接管 pod 流量。通过minikube 节点上的 IP 192.168.59.100 暴露服务。 NodePort 服务会路由到自动创建的 ClusterIP 服务。 通过请求 <节点 IP>:<节点端口> -- 192.168.59.100:30000,你可以从集群的外部访问一个 NodePort 服务,最终重定向到 hellok8s:v33000 端口。

yaml
apiVersion: v1 kind: Service metadata: name: service-hellok8s-nodeport spec: type: NodePort selector: app: hellok8s ports: - port: 3000 nodePort: 30000

创建 service-hellok8s-nodeport Service 后,使用 curl 命令或者浏览器访问 http://192.168.59.100:30000 可以得到结果。

shell
kubectl apply -f service-hellok8s-nodeport.yaml kubectl get service # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE # service-hellok8s-nodeport NodePort 10.109.188.161 <none> 3000:30000/TCP 28s kubectl get pods # NAME READY STATUS RESTARTS AGE # hellok8s-deployment-5d5545b69c-24lw5 1/1 Running 0 27m # hellok8s-deployment-5d5545b69c-9g94t 1/1 Running 0 27m # hellok8s-deployment-5d5545b69c-9gm8r 1/1 Running 0 27m curl http://192.168.59.100:30000 # [v3] Hello, Kubernetes!, From host: hellok8s-deployment-5d5545b69c-9g94t curl http://192.168.59.100:30000 # [v3] Hello, Kubernetes!, From host: hellok8s-deployment-5d5545b69c-24lw5

LoadBalancer

LoadBalancer 是使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上,假如你在 AWSEKS 集群上创建一个 Type 为 LoadBalancer 的 Service。它会自动创建一个 ELB (Elastic Load Balancer) ,并可以根据配置的 IP 池中自动分配一个独立的 IP 地址,可以供外部访问。

这里因为我们使用的是 minikube,可以使用 minikube tunnel 来辅助创建 LoadBalancer 的 EXTERNAL_IP,具体教程可以查看官网文档,但是和实际云提供商的 LoadBalancer 还是有本质区别,所以 Repository 不做更多阐述,有条件的可以使用 AWSEKS 集群上创建一个 ELB (Elastic Load Balancer) 试试。

下图显示 LoadBalancer 的 Service 架构图。

service-loadbalancer-fix-name

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:Joker

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!