第五章 Service

  • Service 是什么
  • 使用 Service
  • 服务类型
  • 综合案例

1 Service 是什么

1.1 定义

在 Kubernetes (k8s) 中,Service 是一种重要的抽象,用于定义一组逻辑上的 Pod 以及访问这些 Pod 的策略。它在集群中提供了一种稳定的访问方式,即使 Pod 的 IP 地址发生变化,Service 仍然能够保证稳定的访问。

1.2 Service 的必要性

Service 是一种重要的资源,用于解决 Pod 动态性带来的访问问题。具体来说,Service 的必要性主要体现在以下几个方面:

  1. Pod 的动态性

    ip地址变化: Pod 在 Kubernetes 中是短暂的。它们可能因为扩展、缩减、更新或失败而被重新创建,从而导致其 IP 地址发生变化。如果应用程序直接使用 Pod 的 IP 地址进行通信,那么每次 Pod 变化时都需要更新这些地址,这显然是不切实际的。

    生命周期管理: Pod 的生命周期是不可预测的,可能会因为各种原因(如资源限制、节点故障等)而被删除或重新调度。

  2. 负载均衡

    负载分发: Service 提供了一个统一的访问入口,并能够将流量均匀分发到后端的多个 Pod,从而实现负载均衡。

    自动化: Kubernetes 自动监控 Pod 的健康状态,并更新 Service 的端点(Endpoints),确保流量只会被路由到健康的 Pod。

  3. 服务发现

    DNS 集成: Kubernetes 中的 Service 会自动注册一个 DNS 名称,允许其他 Pod 通过这个 DNS 名称来访问服务。这大大简化了服务发现的过程,不需要手动配置或维护服务地址。

  4. 安全性和访问控制

    网络隔离: 通过定义不同类型的 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
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐