第五章 Service
- Service 是什么
- 使用 Service
- 服务类型
- 综合案例
1 Service 是什么
1.1 定义
在 Kubernetes (k8s) 中,Service 是一种重要的抽象,用于定义一组逻辑上的 Pod 以及访问这些 Pod 的策略。它在集群中提供了一种稳定的访问方式,即使 Pod 的 IP 地址发生变化,Service 仍然能够保证稳定的访问。
1.2 Service 的必要性
Service 是一种重要的资源,用于解决 Pod 动态性带来的访问问题。具体来说,Service 的必要性主要体现在以下几个方面:
-
Pod 的动态性
ip地址变化: Pod 在 Kubernetes 中是短暂的。它们可能因为扩展、缩减、更新或失败而被重新创建,从而导致其 IP 地址发生变化。如果应用程序直接使用 Pod 的 IP 地址进行通信,那么每次 Pod 变化时都需要更新这些地址,这显然是不切实际的。
生命周期管理: Pod 的生命周期是不可预测的,可能会因为各种原因(如资源限制、节点故障等)而被删除或重新调度。
-
负载均衡
负载分发: Service 提供了一个统一的访问入口,并能够将流量均匀分发到后端的多个 Pod,从而实现负载均衡。
自动化: Kubernetes 自动监控 Pod 的健康状态,并更新 Service 的端点(Endpoints),确保流量只会被路由到健康的 Pod。
-
服务发现
DNS 集成: Kubernetes 中的 Service 会自动注册一个 DNS 名称,允许其他 Pod 通过这个 DNS 名称来访问服务。这大大简化了服务发现的过程,不需要手动配置或维护服务地址。
-
安全性和访问控制
网络隔离: 通过定义不同类型的 Service(如 ClusterIP、NodePort、LoadBalancer),可以灵活地控制服务的访问范围,确保服务仅在需要的范围内暴露。
防火墙规则: 配合 Network Policy,可以定义细粒度的网络策略,控制哪些 Pod 或外部流量可以访问某个 Service,提高集群的安全性。
Kubernetes 的 Service 提供了一种抽象层,使得应用程序不需要关心底层 Pod 的动态变化,并提供了负载均衡、服务发现、安全控制等重要功能。这使得在动态和分布式环境中,应用程序能够更加稳定、高效地运行。
1.3 Service 如何与 Pod 产生联系
标签选择器与 Pod 关联
- 标签选择器: Service 使用标签选择器(Label Selector)来选择哪些 Pod 应该包含在这个服务的范围内。通过在 Pod 定义中设置相应的标签,并在 Service 定义中使用匹配这些标签的选择器,Kubernetes 可以自动将流量路由到相应的 Pod。
- 自动管理: 当新的 Pod 被创建并符合标签选择器时,Kubernetes 会自动将它们加入到 Service 中;当 Pod 被删除或不再符合标签选择器时,它们会被自动移出 Service。
2 使用 Service
2.1 创建 Service
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
2.2 查看 Service
# 查看service状态
$ kubectl get service
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d18h
my-service ClusterIP 10.108.122.215 <none> 80/TCP 11s
# 获取特定Service的详细信息
$ kubectl describe service my-service
Name: my-service
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app.kubernetes.io/name=MyApp
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.108.122.215
IPs: 10.108.122.215
Port: http 80/TCP
TargetPort: 9376/TCP
Endpoints: <none>
Session Affinity: None
Events: <none>
2.3 端口定义
Pod 中的端口定义是有名字的,你可以在 Service 的 targetPort
属性中引用这些名字。
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app.kubernetes.io/name: proxy
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
name: http-web-svc
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app.kubernetes.io/name: proxy
ports:
- name: name-of-service-port
protocol: TCP
port: 80
# 跟上面 pod.spec.containers.ports.name 相对应
targetPort: http-web-svc
2.4 多端口定义
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
template:
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
restartPolicy: Always
selector:
matchLabels:
app: nginx
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: nginx
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
- name: https
protocol: TCP
port: 443
targetPort: 81
运行并测试
# 创建 通过deployment 方式创建了pod 创建一个暴露多端口service
$ kubectl apply -f my-service.yml
deployment.apps/nginx created
service/my-service created
# 查看 Service
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ClusterIP 10.96.196.236 <none> 80/TCP,443/TCP 115s
# 验证 及集群内部 访问
$ curl http://10.96.196.236:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
3 服务类型
-
ClusterIP:
默认类型
仅在集群内部暴露服务,服务可以通过集群内部的ip访问
不允许外部流量直接访问
-
NodePort:
暴露在每个节点的某个端口上。NodePort Service 在每个节点的 IP 和指定的端口上暴露服务,可以通过
NodeIP:NodePort
访问。暴露的端口在 30000-32767 范围内(默认)。这种方式一般用在开发和测试环境。 -
LoadBalancer:
使用云平台的负载均衡器向外部公开 Service。Kubernetes 不直接提供负载均衡组件; 你必须提供一个,或者将你的 Kubernetes 集群与某个云平台集成。
-
ExternalName:
将服务映射到
externalName
字段的内容,例如foo.bar.example.com
。适用于将 Kubernetes 内部服务指向外部 DNS 名称。
-
Headless Service:
没有 ClusterIP 的服务。Headless Service 不分配 ClusterIP,直接返回与服务相关的 Pod 的 IP 地址。
3.1 ClusterIP
这种方式之前有案例了 创建验证可以翻下前面的
apiVersion: v1
kind: Service
metadata:
name: my-clusterip-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
type: ClusterIP
3.2 NodePort
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30007
type: NodePort
运行并验证
# 启动
$ kubectl apply -f node-pod-service.yml
deployment.apps/my-app created
service/my-nodeport-service created
# 确定服务是否成功启动并绑定到节点的端口
$ kubectl get services my-nodeport-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nodeport-service NodePort 10.109.23.122 <none> 80:30007/TCP 105s
# 获取节点的ip地址
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
controlplane Ready control-plane 2d19h v1.30.0 172.30.1.2 <none> Ubuntu 20.04.5 LTS 5.4.0-131-generic containerd://1.7.13
node01 Ready <none> 2d18h v1.30.0 172.30.2.2 <none> Ubuntu 20.04.5 LTS 5.4.0-131-generic containerd://1.7.13
# 访问 NodePort 服务
$ curl http://172.30.1.2:30007 或 curl http://172.30.2.2:30007
能够看到 Nginx 默认的欢迎页面 HTML 内容,确认服务正常工作。
3.3 LoadBalancer
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
创建并验证
# 启动
$ kubectl apply -f loadbalancer-service.yml
deployment.apps/my-app created
service/my-loadbalancer-service created
# 命令检查服务的状态:
$ kubectl get svc my-loadbalancer-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-loadbalancer-service LoadBalancer 10.109.210.214 <pending> 80:30802/TCP 111s
在一段时间后,EXTERNAL-IP 字段应显示一个外部 IP 地址。如果它仍然显示 <pending>,则可能是云提供商的问题,或者 Kubernetes 控制器未能分配外部 IP。 我这这边是没有的
# 验证 若有获得外部ip后 使用 curl 或浏览器访问 就能够看到nginx欢迎页面
$ curl http://<EXTERNAL-IP>
3.4 ExternalName
ExternalName 服务类型在 Kubernetes 中用于将服务映射到外部 DNS 名称。这种服务类型不会分配 ClusterIP,而是将请求转发到指定的外部 DNS 名称。
apiVersion: v1
kind: Service
metadata:
name: my-externalname-service
spec:
type: ExternalName
externalName: example.com
---
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: busybox
image: busybox
command:
- sleep
- "3600"
创建并验证
# 创建
$ kubectl apply -f externalname-service.yml
service/my-externalname-service created
pod/test-pod created
# 进入 pod 测试
$ kubectl exec -it test-pod -- sh
# 验证
$ nslookup my-externalname-service
4 综合案例
通过搭建nginx 和 mysql,相互访问
4.1 搭建 nginx
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-app
spec:
replicas: 3
selector:
matchLabels:
app: nginx-app
template:
metadata:
labels:
app: nginx-app
spec:
containers:
- name: nginx-container
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx-app
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
4.2 搭建mysql
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
labels:
app: mysql
spec:
selector:
matchLabels:
app: mysql
replicas: 3
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql/mysql-server:8.0
ports:
- name: mysql
containerPort: 3306
---
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
selector:
app: mysql
ports:
- name: mysql
port: 3306
targetPort: 3306
type: ClusterIP
4.3 运行并验证
# 分别创建nginx和mysql
$ kubectl apply -f nginx.yml
$ kubectl apply -f mysql.yml
# 查看pods
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-65d6686747-7wh85 1/1 Running 0 4m50s
mysql-65d6686747-rwv5k 1/1 Running 0 4m50s
mysql-65d6686747-svbzl 1/1 Running 0 4m50s
nginx-app-6b7bf698-rrw6k 1/1 Running 0 5m26s
nginx-app-6b7bf698-vdph6 1/1 Running 0 5m26s
nginx-app-6b7bf698-zl5br 1/1 Running 0 5m26s
# 查看service
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql-service ClusterIP 10.102.253.65 <none> 3306/TCP 92s
nginx-service ClusterIP 10.110.209.43 <none> 80/TCP 5m1s
# 进入 mysql 访问nginx能够看到欢迎页面
$ kubectl exec -it mysql-65d6686747-7wh85 -- sh
$ curl http://nginx-service:80