1、前言
我们不应该期望Kubernetes Pod是健壮的,而是要假设Pod中的容器很可能因为各种原因发生故障而死掉。Deployment等Controller会通过动态创建和销毁Pod来保证应用整体的健壮性。换句话说,Pod是脆弱的,但应用是健壮的。每个Pod都有自己的IP地址。当Controller用新Pod替代发生故障的Pod时,新Pod会分配到新的IP地址。这样就产生了一个问题:如果一组Pod对外提供服务(比如HTTP),它们的IP很有可能发生变化,那么客户端如何找到并访问这个服务呢?Kubernetes给出的解决方案是Service。
2、基本语法
2.1 Service yaml
yaml
apiVersion: v1
kind: Service
metadata:
name: httpd-demo-1
namespace: test
labels:
app: httpd-demo
spec:
selector:
app: httpd-demo
ports:
- port: 80
protocol: TCP
2.2 关键字段
- selector:选择器。selector是一个键值对,用于指定Service要代理的Pod的标签。Service会自动检测满足selector条件的Pod,并将流量转发给它们。
- ports:用于定义Service所使用的端口。这个字段是一个对象,其中包含多个字段,最常用的两个字段是port和targetPort
2.3 port、nodePort、targetPort、containerPort字段说明
port是暴露在cluster ip上的端口,:port提供了集群内部客户端访问service的入口,即clusterIP:port。
nodePort提供了集群外部客户端访问service的一种方式,:nodePort提供了集群外部客户端访问service的端口,即nodeIP:nodePort提供了外部流量访问k8s集群中service的入口。
targetPort是pod上的端口,从port/nodePort上来的数据,经过kube-proxy流入到后端pod的targetPort上,最后进入容器。
containerPort是在pod控制器中定义的、pod中的容器需要暴露的端口。
3、Service 类型
3.1 ClusterIP
Service通过Cluster内部的IP对外提供服务,只有Cluster内的节点和Pod可访问,这是默认的Service类型。
3.2 NodePort
将Service通过指定的Node上的端口暴露给外部,访问任意一个 NodeIP:nodePort都将路由到ClusterIP。
端口范围:30000-32767,可以不指定,k8s会生成一个nodePort
3.3 LoadBalancer
使用云平台的负载均衡器向外部公开 Service。Kubernetes 不直接提供负载均衡组件; 你必须提供一个,或者将你的 Kubernetes 集群与某个云平台集成。
3.4 ExternalName
将服务映射到 externalName 字段的内容(例如,映射到主机名 api.foo.bar.example)。 该映射将集群的 DNS 服务器配置为返回具有该外部主机名值的 CNAME 记录。 集群不会为之创建任何类型代理。
4、无头服务(Headless Services)
service 有一个特别的形态就是 Headless Service。service 创建的时候可以指定 clusterIP:None,告诉 K8s 说我不需要 clusterIP(就是刚才所说的集群里面的一个虚拟 IP),然后 K8s 就不会分配给这个 service 一个虚拟 IP 地址,它没有虚拟 IP 地址怎么做到负载均衡以及统一的访问入口呢?
它是这样来操作的:pod 可以直接通过 service_name 用 DNS 的方式解析到所有后端 pod 的 IP 地址,通过 DNS 的 A 记录的方式会解析到所有后端的 Pod 的地址,由客户端选择一个后端的 IP 地址,这个 A 记录会随着 pod 的生命周期变化,返回的 A 记录列表也发生变化,这样就要求客户端应用要从 A 记录把所有 DNS 返回到 A 记录的列表里面 IP 地址中,客户端自己去选择一个合适的地址去访问 pod。
无头 Service 不会获得集群 IP,kube-proxy 不会处理这类 Service, 而且平台也不会为它们提供负载均衡或路由支持。你可以使用无头 Service 与其他服务发现机制交互,而不必绑定到 Kubernetes 的实现。
5、访问service
启动demo
yaml
apiVersion: v1
kind: Service
metadata:
name: httpd-demo-1
namespace: test
labels:
app: httpd-demo
spec:
ports:
- port: 80
protocol: TCP
selector:
app: httpd-demo
---
apiVersion: v1
kind: Service
metadata:
name: httpd-demo-2
namespace: test
labels:
app: httpd-demo
spec:
type: NodePort
ports:
- nodePort: 32181
port: 80
targetPort: 80
protocol: TCP
selector:
app: httpd-demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpd-demo
namespace: test
labels:
app: httpd-demo
spec:
replicas: 2
selector:
matchLabels:
app: httpd-demo
template:
metadata:
labels:
app: httpd-demo
spec:
containers:
- name: httpd-demo
image: httpd:latest
ini
kubectl --kubeconfig=xxx apply -f service.yaml
启动一个busybox镜像的pod,方便连接service进行测试
yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox-test
namespace: test
labels:
app: busybox
spec:
containers:
- name: busybox-test
image: busybox:latest
command: ["/bin/sh", "-c"]
args:
- |
sleep 100000
查看当前service
scss
kubectl --kubeconfig=xxx get svc -n test
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
httpd-demo-1 ClusterIP 10.30.99.106 <none> 80/TCP 1h
httpd-demo-2 NodePort 10.30.167.74 <none> 80:32181/TCP 1h
通过nodeIp:nodePort访问 http://xx.xx.xx.xx:32181/
集群内访问 curl 10.30.99.106:80
其他pod访问
bash
kubectl --kubeconfig=xxx exec -it -n test busybox-test sh
# 通过clusterIp访问service
wget -O- 10.30.99.106
# 通过环境变量访问service
wget -O- $HTTPD_DEMO_1_SERVICE_HOST
# 通过服务名访问service,如果是不同namespace,加上.namaspace
wget -O- httpd-demo-1
wget -O- httpd-demo-1.test
# 查看环境变量,可以看到写入了很多环境变量,如xxx_SERVICE_HOST xxx_PORT
env |grep HTTPD_DEMO