一、Service定义 通过创建 Service ,可以为一组具有相同功能的容器应用提供一个统一的入口地址,井且将请求负载分发到后端的各个容器应用上。
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 apiVersion : vl kind: Service metadata: name : string namespace : string labels: - name: string annotations: - name: string spec : selector : [] type : string clusterIP: string sessionAffinity: string ports: - name: string protocol: string port: int targetPort : int nodePort: int status: loadBalancer : ingress: ip: string hostname: string
二、Service基本用法 对外提供服务的应用程序需要通过某种机制来实现,对于容器应用最简便的方式就是通过 TCP/IP 机制及监听 和端口号来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 apiVersion : v1 kind: ReplicationController metadata: name : webapp spec: replicas: 2 template: metadata: name: webapp labels: app: webapp spec: containers: - name: webapp image : tomcat ports: - containerPort: 8080
创建:
1 kubectl create -f webapp-rc.yaml
快速创建service ,通过kubectl expose
:
1 2 3 4 kubectl expose rc webapp kubectl get svc
通过配置文件创建
1 2 3 4 5 6 7 8 9 10 apiVersion : v1 kind: Service metadata: name: webapp spec: ports: - port: 8081 targetPort: 8080 selector: app: webapp
Service 定义中的关键字段是 port和selector ports 定义部分指定了 Service 所需的虚拟端口号为 8081 ,由于与 Pod 容器端口号8080 不一样,所以需要再通过 targetPort
来指定后Pod端口号。 selector 定义部分设置的是后端 Pod 所拥有的 label: app=webapp
。
目前有2种负载分发策略:
RoundRobin
:轮询模式,即轮询将请求转发到后端的各个 Pod 上。默认
SessionAffinity :基于客户端IP地址进行会话保持的模式 ,即第一次将某个客户端发起的请求转发到后端的某个 Pod 上,之后从相同的客户端发起的请求都将被转发到后端相同的 Pod 上。
Service 还可以进行其他类型的设置,例如设置多个端口号、直接设置为集群外部服务,或实现为无头服务( Headless )模式
多端口Service 有时一个容器应用也可能提供多个端口的服务,那么在 Service 的定义中也可以相应地设置为将多个端口对应到多个应用服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 apiVersion : v1 kind: Service metadata: name: webapp spec: ports: - port: 8080 targetPort: 8080 name: web - port: 8005 targetPort: 8005 name: management selector: app: webapp
两个端口号使用了不同的4层协议一一TCP和UDP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 apiVersion : v1 kind: Service metadata: name: kube-dns namespace: kube-system labels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" kubernetes.io/name : "KubeDNS" spec: selector: k8s-app : kube-dns clusterIP : 169.169 .0 .100 ports: - name: dns port: 53 protocol: UDP - name : dns-tcp port: 53 protocol: TCP
外部服务Service 应用系统需要将一个外部数据库作为后端服务进行连接,或将另一个集群或Namespace 中的服务作为服务的后端,这时可以通过创建一个无 Label Selector
的Service实现
1 2 3 4 5 6 7 8 9 apiVersion : v1 kind: Service metadata: name: my-service spec: ports: - protocol : TCP port : 80 targetPort: 80
通过该定义创建的是一个不带标签选择器的 Service 即无法选择后端的 Pod, 系统不会自动创建 Endpoint
,因此需要手动创建一个和该 Service 同名的 Endpoint ,用于指向实际的后端访问地址。
1 2 3 4 5 6 7 8 9 kind: Endpoints apiVersion : v1 metadata: name: my-service subsets: - addresses: - IP: 1.2 .3 .4 ports: - port: 80
访问没有标签选择器的 Service 带有标签选择器的 Service 请求将会被路由到由用户手动定义的后端 Endpoint。
三、Headless Service 在某些应用场景中,开发人员希望自己控制负载均衡策略 不使用 Service 提供的默认负载均衡的功能,或者应用程序希望知道属于同组服务的其他实例 。
Kubernetes 提供了 Headless Service (无头服务)
来实现这种功能,即不为 Service 设置 ClusterIP 入口地址 ,仅通Label Selector
将后端的 Pod 列表返回给调用的客户端。
1 2 3 4 5 6 7 8 9 10 11 12 apiVersion : v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 clusterIP : None selector: app: nginx
Service 就不再具有一个特定 ClusterIP
对其进行访问将获得包含 Labelapp=nginx
的 全部 Pod 然后客户端程序自行决定如何处理这个 Pod 列表。 如, StatefulSet
就是使用 Headless Service 为客户端返回多个服务地址
对于去中心化类 集群 Headless Service 将非常有用。
四、集群外部访问 Pod Service 由于 Pod和Service是Kubernetes 集群范围内的虚 概念,所以集群外的客户端系统无法通过Pod的IP地址或者 Service 的虚拟IP地址和虚拟端口号访问到它们。为了让外部客户端可以访问这些服务,可以将 Pod Service 的端口号映射到宿主机,以使得客户端应用能够通过物理机访问容器应用。
1.容器端口好映射到物理机
通过设置容器级别的 hostPort
将容器应用的端口号映射到物理机上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 apiVersion: v1 kind : Pod metadata: name: webapp labels : app : webapp spec : containers: - name : webapp image : tomcat ports: - containerPort: 8080 hostPort: 8081
通过物理机的 地址和 8081 端口号访问 Pod 内的容器服务
通过设置 Pod 级别的 hostNetwork=true
,该 Pod 中所有容器的端口号都将被直接映射到物理机上。需要注意 ,在容器的 ports 定义部分如果不指定 hostPort
,则默认 hostPort
等于 containerPort
,如果指定了 hostPort
,则 hostPort
必须等于 containerPort
的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 apiVersion: v1 kind : Pod metadata: name: webapp labels : app : webapp spec : hostNetwork : true containers: - name : webapp image : tomcat imagePullPolicy : Never ports: - containerPort: 8080
通过物理 地址 8080 端口号访问 Pod 的容器服务
2.将Service的端口号映射到物理机
通过设置 nodePort
映射到物理机 ,同时设置 Service 的类型为 NodePort
:
1 2 3 4 5 6 7 8 9 10 11 12 apiVersion : v1 kind: Service metadata: name: webapp spec: type: NodePort ports: - port: 8080 targetPort : 8080 nodePort: 8081 selector: app: webapp
通过物理机的 地址和 nodePort 8081 端口号访问服务
五、DNS服务搭建指南 在集群内需要能够通过服务名对服务进行访问,这就需要一个集群范围的 DNS 服务来完成服务名到 ClusterIP
的解析
Kubernetes 提供的虚拟 DNS 服务名为 sky ,由 个组件组成。
etcd
: DNS 存储
kube2sky
:将 Kubernetes Master 中的 Service (服务)注册到 etcd
skyDNS
:提供 DNS 域名解析服务
healthz
:提供对skydns 服务的健康检查 功能
六、自定义 DNS 和上游 DNS 服务器 七、Ingress: HTTP 层路由机制 Service 的表现形式为 IP:Port,即工作在 TCP/IP层。而对于基于 HTTP 的服务来说,不同的 URL 地址经常对应到不同的后端服务或者虚拟服务器( Virtual Host ) ,这些应用层的转发机制仅通过 Kubernetes Service 机制是无法实现的。
Ingress 资源对象,用于将不同 URL 的访问请求转发到后端不同Service ,以实现 HTTP 层的业务路由机制。 Kubernetes 使用一个 Ingress 策略定义和一个具体Ingress Controller
,两者结合并实现了 一个完整 Ingress 负载均衡器。
使用 Ingress 进行负载分发时, Ingress Controller
将基于 Ingress 规则将客户端请求直接转发到 Service 对应的后端 Endpoint (即 Pod )上,这样会跳过 kube-proxy
的转发功能 , kube-proxy不再起作用。
Ingress
:Service
的统一网关入口
http://mywebsite.com/api
的访问将被路由到后端名为“api”的 Service
http://mywebsite.com/web
的访问将被路由到后端名为“web”的 Service
http://mywebsite.com/doc
的访问将被路由到后端名为“doc”的 Service
为使用 Ingress ,需要创建 Ingress Controller (带一个默认 backend 服务)和 Ingress 策略设置来共同完成 。
1.创建 Ingress Controller 和默认的 backend 服务 先部署 Ingress Controller
,以实现为所有后端 Service 提供一个统一的入口。 Ingress Controller
需要实现基于不同 HTTP URL 向后转发的负载分发规则,并可以灵活设置7层的负载分发策略。如果公有云服务商能够提供该类型 Hπp 路由 Load.Balancer,则也可设置其为 Ingress Controller.
Ingress Controller
将以 Pod 的形式运行 ,监控 apiserver 的/ingress
接口后端的 backend services
,如果 service 发生变化,则 Ingress Controller 应自动更新其转发规则
使用ingress实现一个Ingress Controller
监昕 apiserver ,获取全部 ingress 的定义。
基于 ingress 的定义, 生成 Nginx 需的配置文件/etc/nginx/nginx.conf
执行 nginx -s reload
命令, 重新加载 nginx.conf
配置文件的内容,
Go语言核心代码
1 2 3 4 5 6 7 8 9 10 11 12 13 for { rateLimiter.Accept() ingresses, err : = ingClient.List(labels.Everything(), fields.Everything()) if err != nil || reflect.DeepEqual(ingresses.Items, known.Items) { continue } if w, err :=os.Create ("/etc/nginx/nginx.conf" ); err != nil { log.Fatalf ("Failed to open %v: %v" , nginxConf, err) } else if err := tmpl.Execute (w, ingresses); err != nil { log.Fatalf ("Failed to write template" , err) } shellOut ("nginx -s reload" ) }
使用谷歌提供的 nginx-ingress-controller
镜像来创建 Ingress Controller
以daemonset 的形式进行创建,在每个 Node 上都将启动一个 Nginx 服务。
安装
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 27 28 29 30 31 32 33 34 35 wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.47.0/deploy/static/provider/baremetal/deploy.yaml vi deploy.yaml registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/ingress-nginx-controller:v0.46.0 kubectl apply -f ingress-deploy.yaml namespace/ingress-nginx created serviceaccount/ingress-nginx created configmap/ingress-nginx-controller created clusterrole.rbac.authorization.k8s.io/ingress-nginx created clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created role.rbac.authorization.k8s.io/ingress-nginx created rolebinding.rbac.authorization.k8s.io/ingress-nginx created service/ingress-nginx-controller-admission created service/ingress-nginx-controller created deployment.apps/ingress-nginx-controller created validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created serviceaccount/ingress-nginx-admission created clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created role.rbac.authorization.k8s.io/ingress-nginx-admission created rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created job.batch/ingress-nginx-admission-create created job.batch/ingress-nginx-admission-patch created kubectl get pod,svc -n ingress-nginx ingress-nginx ingress-nginx-controller NodePort 10.96.218.45 <none> 80:31993/TCP,443:31515/TCP 85m ingress-nginx ingress-nginx-controller-admission ClusterIP 10.96.194.114 <none> 443/TCP 85m
测试
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 apiVersion: apps/v1 kind: Deployment metadata: name: hello-server spec: replicas: 2 selector: matchLabels: app: hello-server template: metadata: labels: app: hello-server spec: containers: - name: hello-server image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/hello-server ports: - containerPort: 9000 --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: nginx-demo name: nginx-demo spec: replicas: 2 selector: matchLabels: app: nginx-demo template: metadata: labels: app: nginx-demo spec: containers: - image: nginx name: nginx --- apiVersion: v1 kind: Service metadata: labels: app: nginx-demo name: nginx-demo spec: selector: app: nginx-demo ports: - port: 8000 protocol: TCP targetPort: 80 --- apiVersion: v1 kind: Service metadata: labels: app: hello-server name: hello-server spec: selector: app: hello-server ports: - port: 8000 protocol: TCP targetPort: 9000
1 kubectl apply -f test.yaml
域名访问 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 27 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-host-bar spec: ingressClassName: nginx rules: - host: "hello.lq.com" http: paths: - pathType: Prefix path: "/" backend: service: name: hello-server port: number: 8000 - host: "demo.lq.com" http: paths: - pathType: Prefix path: "/nginx" backend: service: name: nginx-demo port: number: 8000
对浏览器配置域名映射
1 2 192.168.0.100 hello.lq.com 192.168.0.100 demo.lq.com
浏览器访问
1 2 http://hello.lq.com:31993/ http://demo.lq.com:31993/
路径重写 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 27 28 29 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/rewrite-target: /$2 name: ingress-host-bar spec: ingressClassName: nginx rules: - host: "hello.lq.com" http: paths: - pathType: Prefix path: "/" backend: service: name: hello-server port: number: 8000 - host: "demo.lq.com" http: paths: - pathType: Prefix path: "/nginx(/|$)(.*)" backend: service: name: nginx-demo port: number: 8000
流量限制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-limit-rate annotations: nginx.ingress.kubernetes.io/limit-rps: "1" spec: ingressClassName: nginx rules: - host: "haha.lq.com" http: paths: - pathType: Exact path: "/" backend: service: name: nginx-demo port: number: 8000