文章目录
- 1、Service 定义
- 1.1、无选择符的服务
- 1.2、Endpoints
- 2、服务发布类型
- 2.1、ClusterIP
- 2.2、NodePort
- 2.3、ExternalName
- 2.4、loadbalancer
- 3、无头服务
- 3.1、有选择符的服务
- 3.2、无选择符的服务
- 4、服务发现
- 4.1、环境变量
- 4.2、DNS
- 5、Service TLS
Service:将运行在一组 Pods 上的应用程序公开为网络服务(微服务)的抽象方法。
k8s 以 Pod 应用部署的最小单位。k8s 根据调度算法为 Pod 分配运行节点,并随机分配 ip 地址。因此 Pod 的 IP 地址不固定,不方便通过 Pod 的 IP 地址访问服务。因此,k8s 提供了 Service,对后端提供相同服务的一组 Pod 的聚合。
一个 Service 提供一个虚拟的 Cluster IP,后端对应一个或者多个提供服务的 Pod。在集群中访问该 Service 时,采用 Cluster IP 即可,Kube-proxy 负责将发送到 Cluster IP 的请求转发到后端的Pod上。
1、Service 定义
来看一个微服务的定义
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
# service 发布类型
type: ClusterIP
# 标签选择器:选择 pod
selector:
app: myapp
# 指定端口数组
ports:
# service 端口
- port: 443
# 协议
protocol: TCP
# 容器端口
targetPort: 443
# 端口名称
name: https
- port: 80
protocol: TCP
targetPort: 80
name: http
- port: 6379
protocol: TCP
targetPort: 6379
name: redis-tcp
微服务暴露端口:443, 80, 6379,k8s 为其分配一个集群 ip: ClusterIP。通过标签选择器选择与其匹配的 Pod,然后将所有更新发布到也称为 myapp-svc 的 Endpoint 对象。
1.1、无选择符的服务
没有选择符的 service 需要手动配置 EndpointSlice(旧版 Endpoints),将服务映射到集群外部的服务或者是集群内其他命名空间下的服务。
例:定义没有选择符的 Service
apiVersion: v1
kind: Service
metadata:
name: notselector-svc
spec:
ports:
- protocol: TCP
port: 8080
targetPort: 80
服务没有选择算符,因此不会自动创建相应的 Endpoint 对象。 需要手动添加 Endpoint 对象,将服务手动映射到运行该服务的网络地址和端口:
apiVersion: v1
kind: Endpoints
metadata:
name: notselector-svc
subsets:
- addresses:
- ip: 10.244.1.60
ports:
- port: 80
- addresses:
# 非集群节点的机器:集群外部服务
- ip: 192.168.88.131
ports:
- port: 80
应用资源
kubectl apply -f notselector-svc.yaml
kubectl describe -f notselector-svc.yaml
kubectl apply -f notselector-points.yaml
kubectl describe -f notselector-points.yaml
在集群节点访问外部服务
curl http://192.168.88.131:80/ping
1.2、Endpoints
Endpoints 定义了网络端点的列表,通常由 Service 引用,定义可以将流量发送到哪些 Pod。推荐用 EndpointSlice API 替换 Endpoints。
2、服务发布类型
type
的四种类型
ClusterIP
:默认值,通过集群内部 ip 暴露服务。该服务只能在集群内部访问。NodePort
:通过每个节点上的 IP 和静态端口(NodePort
)暴露服务。除了集群内部访问,集群外部可以通过IP:NodePort
访问该服务。ExternalName
:将服务映射到 DNS 名称,把集群外部的服务引入到集群内部来使用LoadBalancer
:使用云提供商的负载均衡器向外部暴露服务。 将来自外部负载均衡器的流量路由到后端的 pod 上,不再需要内部的负载均衡。
2.1、ClusterIP
定义 clusterIP 类型的服务
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
type: ClusterIP
ports:
- port: 443
protocol: TCP
targetPort: 443
name: https
- port: 80
protocol: TCP
targetPort: 80
name: http
- port: 6379
protocol: TCP
targetPort: 6379
name: redis-tcp
应用 | 查看服务
kubectl apply -f myapp-svc.yaml
kubectl get -f myapp-svc.yaml
kubectl describe -f myapp-svc.yaml
kubectl get endpoints
集群 ip 范围在 master 节点初始化配置文件 init.default.yaml 中
networking:
# 域名
dnsDomain: cluster.local
# 服务 ip
serviceSubnet: 10.96.0.0/12
# pod ip
podSubnet: 10.244.0.0/16
测试服务
集群内部创建 pod
# 创建一个 redis pod
kubectl run redis --image=redis
# 创建一个带有 curl 工具的 pod
kubectl run curl --image=radial/busyboxplus:curl -it
测试:集群内部访问服务。
# 访问 curl 容器
kubectl exec -it curl -- sh
# 访问集群 service ip
curl http://10.109.36.83:80/ping
# 访问集群服务域名
# 域名形式:服务名.命名空间.服务.dns域名(cluster.local)
curl http://myapp-svc.default.svc.cluster.local:80/ping
# 访问 redis 容器
kubectl exec -it redis -- bash
# 通过集群IP访问
redis-cli -h <clusterIP> -p 6379
# 通过域名访问
redis-cli -h myapp-svc.default.svc.cluster.local -p 6379
2.2、NodePort
定义 service
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
type: NodePort
ports:
- port: 443
protocol: TCP
targetPort: 443
name: https
nodePort: 30001
- port: 80
protocol: TCP
targetPort: 80
name: http
- port: 6379
protocol: TCP
targetPort: 6379
name: redis-tcp
nodePort: 30002
selector:
app: myapp
查看 | 应用服务
kubectl delete svc myapp-svc
kubectl apply -f myapp-svc.yaml
kubectl get -f myapp-svc.yaml
kubectl describe -f myapp-svc.yaml
# 显示 svcPort: nodePort。不指定 nodePort,则随机生成
# 443:30001/TCP, 80:32326/TCP, 6379:30002/TCP
测试服务
- 通过
serviceIP:port
可以集群节点,集群内部容器内访问服务。 - 通过 DNS 域名,可以在集群内部容器访问服务。
- 通过
nodeIP:nodePort
可以在集群节点、集群内部容器、集群外部访问服务。
# serviceIP:port
curl http://<clusterIP>:80/ping
curl http://<podIP>:32326/ping
# nodeIP:nodePort
curl http://<nodeIP>:32326ping
# DNS 域名
curl http://myapp-svc.default.svc.cluster.local:80/ping
2.3、ExternalName
将服务映射到 DNS 名称,相当于将集群内的服务域名映射到外部域名。使用 CNAME 重定向,无法实现端口映射。
定义服务
apiVersion: v1
kind: Service
metadata:
name: externalname-svc
spec:
type: ExternalName
externalName:
应用服务
kubectl apply -f externalname-svc.yaml
kubectl get svc
# 显示:CLUSTER-IP 和 PORT(S) 均为 <none>
测试服务
# 进入集群 curl pod
kubectl exec -it curl -- sh
ping externalname-svc.default.svc.cluster.local
ping www.baidu.com
2.4、loadbalancer
在使用支持外部负载均衡器的云提供商的服务时,云提供商的服务会自动的为 type 值为 LoadBalancer 的 service 创建负载均衡。负载均衡是异步创建的,关于被提供的负载均衡器的信息将会通过 service 的 status.loadBalancer
字段发布出去。
使用云厂商提供的外部负载均衡器,来自外部负载均衡器的流量将直接重定向到后端 Pod 上。不再需要内部负载均衡。
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-svc
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80
clusterIP: 10.0.171.239
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 192.0.2.127
3、无头服务
对于无状态的应用,客户端并不在意其连接的是哪一个 Pod,采用 Service 是没有问题的。但是对于有状态的应用,有时需要客户端根据某种算法选择对应的某一个后端 Pod 提供服务,或是需要连接所有的后端 Pod,例如 redis 服务,这时不需要负载均衡和单独。此时,可以通过指定 clusterIP: None
,来创建无头服务 Headless Service
。
无头 Service 不会分配 ClusterIP,kube-proxy 不会处理它们, 不会进行负载均衡和路由。既不能通过集群 IP 访问,也不能通过 DNS 域名访问。其 DNS 记录返回该服务下的所有 Endpoint。
3.1、有选择符的服务
对于有选择符的无头服务,创建 EndpointSlice 对象,返回 DNS 记录是服务的所有 EndPoint。
例:定义有选择符的无头服务
apiVersion: v1
kind: Service
metadata:
name: myapp-headless-svc
spec:
clusterIP: None
type: ClusterIP
selector:
app: myapp
ports:
- protocol: TCP
name: http
port: 8080
targetPort: 80
应用服务
kubectl apply -f myapp-headless-svc.yaml
kubectl describe -f myapp-headless-svc.yaml
测试
# 进入 curl pod
kubectl exec -it curl -- sh
# 查询 DNS 记录
# serviceName.namespace
# 列举出所有的端点
nslookup myapp-headless-svc.default.svc.cluster.local
3.2、无选择符的服务
不会创建 EndpointSlice 对象。 返回 DNS 记录是服务的所有 EndPoint。
例:定义无头无选择服务
apiVersion: v1
kind: Service
metadata:
name: notselector-headless-svc
spec:
clusterIP: None
ports:
- protocol: TCP
port: 8080
targetPort: 80
应用资源
kubectl apply -f notselector-headless-svc.yaml
kubectl describe svc notselector-headless-svc
# 显示:Endpoints:<none>
手动创建 Endpoint 对象。
apiVersion: v1
kind: Endpoints
metadata:
name: notselector-headless-svc
subsets:
- addresses:
- ip: 10.244.1.60
ports:
- port: 80
- addresses:
- ip: 192.168.88.131
ports:
- port: 80
测试服务
# 应用资源
kubectl apply -f notselector-points.yaml
kubectl describe -f notselector-headless-svc.yaml
# 查看 dns 记录
kubectl exec -it curl -- sh
nslookup notselector-headless-svc.default.svc.cluster.local
4、服务发现
4.1、环境变量
必须先创建 service,才能在创建 Pod 时设定环境变量。
当 Pod 运行在节点上,kubelet 会为每个活跃的 Service 添加一组环境变量。
{SVCNAME}_SERVICE_HOST
{SVCNAME}_SERVICE_PORT
# 查看 pod 的环境变量
kubectl exec myapp-deploy-59f8994d89-7gjpd -- printenv | grep SERVICE
4.2、DNS
支持集群的 DNS 服务器监视 Kubernetes API 中的新服务,并为每个服务创建一组 DNS 记录。 如果在整个集群中都启用了 DNS,则所有 Pod 都应该能够通过其 DNS 名称自动解析服务。
# 进入 curl pod
kubectl exec -it curl -- sh
# 查询DNS serviceName.namespace
nslookup myapp-svc.default
5、Service TLS
# 1、自签证书
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/tls.key -out /tmp/tls.crt -subj "/CN=myapp-svc.default.svc.cluster.local/O=my-hello"
# 或
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/tls.key -out /tmp/tls.crt -subj "/CN=10.109.36.83/O=my-hello"
# 2、创建 secret
kubectl create secret tls myhellotls --cert=/tmp/tls.crt --key=/tmp/tls.key
# 查看 secret
kubectl get secrets myhellotls -o yaml
# 部署应用程序,修改 myapp-deployment.yaml
# spec.template.spec 创建
volumes:
- name: myhello-tls-volume
secret:
secretName: myhellotls
# spec.template.containers 创建
volumeMounts:
- mountPath: /app/cert
name: myhello-tls-volume
# https 访问
curl https://10.109.36.83/ping --cacert /tmp/tls.crt
# 结束测试,删除证书
rm /tmp/tls.*