【Kubernetes基础--Service深入理解】--查阅笔记4

目录

  • [Service 的用法](#Service 的用法)
    • [docker 对外提供服务](#docker 对外提供服务)
    • [service 对外提供服务](#service 对外提供服务)
  • [从集群外部访问 Pod 或 Service](#从集群外部访问 Pod 或 Service)
  • [Ingress:HTTP 7层路由机制](#Ingress:HTTP 7层路由机制)
    • [创建Ingress Controller和默认的backend服务](#创建Ingress Controller和默认的backend服务)

k8s 通过创建 Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上。

Service 的用法

docker 对外提供服务

一般来说,对外提供服务的应用程序,对于容器应用最简便的方式就是通过 TCP/IP 机制及监听 IP 和端口号来实现。例如,定义一个提供Web 服务的 RC,由两个 Tomcat 容器副本组成,每个容器都通过 containerPort 设置提供服务的端口号为 8080:

yaml 复制代码
# webapp-rc.yaml

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

创建 RC,获取 IP:

powershell 复制代码
kubectl create -f webapp-rc.yaml

kubectl get pods -l app=webapp -o yaml | grep podIP
# podIP: 172.17.1.3
# podIP: 172.17.1.4

可以通过这两个 Pod 的 IP 地址和端口号访问 Tomcat 服务:

powershell 复制代码
curl 172.17.1.3:8080

curl 172.17.1.4:8080

但是,这种方式是不可靠的。例如当 Pod 所在的 Node 发生故障,Pod 将被 k8s 重新调度到另一个 Node,Pod 的 IP 地址将发生变化。我们着重考虑的是,如果容器应用本身是分布式的部署方式,通过多个实例共同提供服务,就需要在这些实例的前端设置一个负载均衡器来实现请求的分发。k8s 中的 Service 就是用于解决这些问题的核心组件。

service 对外提供服务

以上面的 webapp 为例,为了访问 Tomcat Pod 实例,我们来创建一个 Service 提供服务:

powershell 复制代码
kubectl expose rc webapp
# service "webapp" exposed
# 查看新创建的 Service,可以看到系统为它分配了一个虚拟的IP地址(ClusterIP)​,Service 所需的端口号则从 Pod 中的 containerPort 复制而来:
kubectl get svc
# NAME         CLUSTER-IP         EXTERNAL-IP    PORT(S)     AGE
# webapp      169.169.235.79    <none>          8080/TCP    3s

curl 169.169.235.79:8080

这里,对 Service 地址 169.169.235.79:8080 的访问被自动负载分发到了后端两个 Pod 之一:172.17.1.3:8080或172.17.1.4:8080。

除了使用 kubectl expose 命令创建 Service,也可以通过配置文件定义 Service,再通过 kubectl create 命令进行创建:

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  ports:
  - port: 8081
    targetPort: 8080
  selector:
    app: webapp

Service 定义中的关键字段是 ports 和 selector。上面 ports 定义部分指定了 Service 所需的虚拟端口号为 8081,由于与 Pod 容器端口号8080 不一样,所以需要再通过 targetPort 来指定后端 Pod 的端口号。selector 定义部分设置的是后端 Pod所拥有的 label:app=webapp。

创建该Service并查看其 ClusterIP 地址:

powershell 复制代码
kubectl create -f webapp-svc.yaml
# service "webapp" created
kubectl get svc
# NAME         CLUSTER-IP         EXTERNAL-IP    PORT(S)     AGE
# webapp      169.169.28.190    <none>          8081/TCP    3s

curl 169.169.28.190:8081

目前 k8s 提供了两种负载分发策略:RoundRobin 和 SessionAffinity:

  • RoundRobin:轮询模式(默认),即轮询将请求转发到后端的各个 Pod 上
  • SessionAffinity:基于客户端 IP 地址进行会话保持的模式,即第1次将某个客户端发起的请求转发到后端的某个 Pod 上,之后从相同的客户端发起的请求都将被转发到后端相同的 Pod 上

在默认情况下,k8s 采用 RoundRobin 模式对客户端请求进行负载分发,也可以通过设置 service.spec.sessionAffinity=ClientIP 来启用SessionAffinity 策略。这样,同一个客户端 IP 发来的请求就会被转发到后端固定的某个 Pod 上了。通过 Service 的定义,k8s 实现了一种分布式应用统一入口的定义和负载均衡机制。

从集群外部访问 Pod 或 Service

Pod 和 Service 都是 k8s 的虚拟概念,集群外的客户端系统无法通过 Pod 的 IP 地址或者 Service 的虚拟 IP 地址和虚拟端口号访问他们。我们需要将 Pod 或 Service 的端口号映射到宿主机,以使客户端应用能够通过物理机访问容器应用。

将容器应用的端口号映射到物理机

  1. 通过设置容器级别的 hostPort,将容器应用的端口号映射到物理机上:
yaml 复制代码
# pod-hostport.yaml
apiVersion: v1
kind: Pod
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  containers:
  - name: webapp
    image: tomcat
    ports:
    - containerPort: 8080
      hostPort: 8081
powershell 复制代码
kubectl create -f pod-hostport.yaml # pod "webapp" created

curl 192.168.18.3:8081 # 宿主机ip
  1. 通过设置 Pod 级别的 hostNetwork=true,该 Pod 中所有容器的端口号都将被直接映射到物理机上。在设置 hostNetwork=true 时需要注意,在容器的 ports 定义部分如果不指定 hostPort,则默认 hostPort 等于 containerPort,如果指定了 hostPort,则 hostPort 必须等于containerPort 的值:
yaml 复制代码
# pod-hostnetwork.yaml
apiVersion: v1
kind: Pod
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  hostNetwork: true  # 设置宿主机网络
  containers:
  - name: webapp
    image: tomcat
    imagePullPolicy: Never
    ports:
    - containerPort: 8080
powershell 复制代码
kubectl create -f pod-hostnetwork.yaml  # pod "webapp" created

curl 192.168.18.4:8080

将 Service 的端口号映射到物理机

  1. 通过设置 nodePort 映射到物理机,同时设置 Service 的类型为 NodePort:
yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  type: NodePort # service 类型设置为 NodePort
  ports:
  - port: 8080
    targetPort: 8080
    nodePort: 8081 # 指定 nodePort 端口号
  selector:
    app: webapp
powershell 复制代码
kubectl create -f webapp-svc-nodeport.yaml
# service "webapp" created
# 注意,这里系统提示:由于要使用物理机的端口号,所以需要在防火墙上做好相应的配置,以使外部客户端能够访问到该端口
curl 192.168.18.4:8081        
  1. 通过设置 LoadBalancer 映射到云服务商提供的 LoadBalancer 地址。这种用法仅用于在公有云服务提供商的云平台上设置 Service 的场景。
    示例中,status.loadBalancer.ingress.ip 设置的 146.148.47.155 为云服务商提供的负载均衡器的 IP 地址。对该 Service 的访问请求将会通过 LoadBalancer 转发到后端 Pod 上,负载分发的实现方式则依赖于云服务商提供的 LoadBalancer 的实现机制:
yaml 复制代码
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
    nodePort: 30061
  clusterIP: 10.0.171.239
  loadBalancerIP: 78.11.24.19
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 146.148.47.155

Ingress:HTTP 7层路由机制

如前所述,Service 的表现形式为 IP:Port,即工作在 TCP/IP 层。而对于基于 HTTP 的服务来说,不同的 URL 地址经常对应到不同的后端服务或者虚拟服务器(Virtual Host)​,这些应用层的转发机制仅通过 k8s 的 Service 机制是无法实现的。

Ingress 资源对象,可以将不同 URL 的访问请求转发到后端不同的 Service,以实现 HTTP 层的业务路由机制。k8s 使用了一个 Ingress 策略定义和一个具体的 Ingress Controller,两者结合并实现了一个完整的 Ingress 负载均衡器。代理不同后端 Service 而设置的负载均衡服务,就是 k8s 里的 Ingress 服务。

可以这样理解:Ingress 就是 Service 的 "Service",是"反向代理"的一种抽象。

使用 Ingress 进行负载分发时,Ingress Controller 基于 Ingress 规则将客户端请求直接转发到 Service 对应的后端 Endpoint(Pod)上,这样会跳过 kube-proxy 的转发功能,kube-proxy 不再起作用。如果 Ingress Controller 提供的是对外服务,则实际上实现的是边缘路由器的功能。

为使用 Ingress,需要创建 Ingress Controller(带一个默认 backend 服务)和 Ingress 策略设置来共同完成。

创建Ingress Controller和默认的backend服务

在定义 Ingress 策略前,先部署 Ingress Controller,来实现为所有后端 Service 提供统一入口。Ingress Controller 需要实现基于不同 HTTP URL 向后转发的负载分发规则,并可以灵活设置7层负载分发策略。如果公有云服务商能够提供该类型的 HTTP 路由 LoadBalancer,则也可设置其为 Ingress Controller。

下面示例中,使用谷歌提供的 nginx-ingress-controller 镜像来创建 Ingress Controller。该 Ingress Controller 以 daemonset 的形式进行创建,在每个 Node 上都将启动一个Nginx服务。

Nginx 容器设置了 hostPort,将容器应用监听的 80 和 443 端口号映射到物理机上,使得客户端应用可以通过 URL 地址"http://物理机IP:80"或"https://物理机IP:443"来访问该 Ingress Controller。这使得 Nginx 类似于通过 NodePort 映射到物理机的 Service,成为代替 kube-proxy的 HTTP 层的 Load Balancer:

yaml 复制代码
# nginx-ingress-daemonset.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
 name: nginx-ingress-lb
 labels:
   name: nginx-ingress-lb
 namespace: kube-system
spec:
 template:
   metadata:
     labels:
       name: nginx-ingress-lb
   spec:
     terminationGracePeriodSeconds: 60
     containers:
     - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
       name: nginx-ingress-lb
       readinessProbe:
         httpGet:
           path: /healthz
           port: 10254
           scheme: HTTP
       livenessProbe:
         httpGet:
           path: /healthz
           port: 10254
           scheme: HTTP
           initialDelaySeconds: 10
           timeoutSeconds: 1
         ports:
         - containerPort: 80
           hostPort: 80  # 映射到主机80端口
         - containerPort: 443
           hostPort: 443
         env:
           - name: POD_NAME
             valueFrom:  # Downward API,将Pod信息注入为环境变量
               fieldRef:
                 fieldPath: metadata.name
           - name: POD_NAMESPACE
             valueFrom:
               fieldRef:
                 fieldPath: metadata.namespace
         args:
         - /nginx-ingress-controller
         - --default-backend-service=$(POD_NAMESPACE)/default-http-backend

为了让 Ingress Controller 正常启动,我们配置一个默认的 backend,用于在客户端访问的 URL 地址不存在时,返回404。这个 backend 服务用任何应用实现都可以,只要满足对根路径"/"的访问返回404应答,并且提供 /healthz 路径以使 kubelet 完成对它的健康检查。另外,由于 Nginx 通过 default-backend-service 的服务名称(Service Name)去访问它,所以需要 DNS 服务正确运行:

yaml 复制代码
# default-backend.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: default-http-backend
  labels:
    k8s-app: default-http-backend
  namespace: kube-system
spec:
  replicas: 1
  template:
    metadata:
      labels:
      k8s-app: default-http-backend
    spec:
terminationGracePeriodSeconds: 60
containers:
- name: default-http-backend
  image: gcr.io/google_containers/defaultbackend:1.0
  livenessProbe:
    httpGet:
      path: /healthz
      port: 8080
      scheme: HTTP
    initialDelaySeconds: 30
    timeoutSeconds: 5
  ports:
  - containerPort: 8080
  resources:
    limits:
      cpu: 10m
      memory: 20Mi
    requests:
      cpu: 10m
      memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
  name: default-http-backend
  namespace: kube-system
  labels:
    k8s-app: default-http-backend
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    k8s-app: default-http-backend

创建 backend 服务,并且创建 nginx-ingress-controller:

powershell 复制代码
kubectl create -f default-backend.yaml
# deployment "default-http-backend" created
# service "default-http-backend" created

kubectl create -f nginx-ingress-daemonset.yaml # daemonset "nginx-ingress-lb" created

# 查看 default-http-backend 和 nginx-ingress-controller 容器是否正确运行
kubectl get pod --namespace=kube-system
# NAME                                     READY     STATUS    RESTARTS   AGE
# default-http-backend-1132503640-84lnv   1/1       Running   0          3m
# kube-dns-v11-z3cb0                      4/4       Running   0         10m
# nginx-ingress-lb-5jbwv                 1/1       Running   0          3m
# nginx-ingress-lb-60j7h                 1/1       Running   0          3m
# nginx-ingress-lb-dttr9                 1/1       Running   0          3m

# 用 curl 访问任意 Node 的 80 端口号,验证 nginx-ingress-controller 和 default-http-backend 服务正常工作:
curl k8s-node-2 # default backend -404
相关推荐
代码老y1 小时前
Docker:容器化技术的基石与实践指南
运维·docker·容器
巴伦是只猫1 小时前
【机器学习笔记Ⅰ】13 正则化代价函数
人工智能·笔记·机器学习
X_StarX7 小时前
【Unity笔记02】订阅事件-自动开门
笔记·学习·unity·游戏引擎·游戏开发·大学生
MingYue_SSS7 小时前
开关电源抄板学习
经验分享·笔记·嵌入式硬件·学习
巴伦是只猫7 小时前
【机器学习笔记 Ⅱ】1 神经网络
笔记·神经网络·机器学习
weixin_437398218 小时前
转Go学习笔记(2)进阶
服务器·笔记·后端·学习·架构·golang
teeeeeeemo9 小时前
回调函数 vs Promise vs async/await区别
开发语言·前端·javascript·笔记
致***锌12 小时前
期权标准化合约是什么?
笔记
Wilber的技术分享13 小时前
【机器学习实战笔记 14】集成学习:XGBoost算法(一) 原理简介与快速应用
人工智能·笔记·算法·随机森林·机器学习·集成学习·xgboost
V我五十买鸡腿15 小时前
顺序栈和链式栈
c语言·数据结构·笔记·算法