Kubernetes入门 十一、网络之Service

概述

在 Kubernetes 中,Pod 是应用程序的载体,我们可以通过 Pod 的 IP 来访问应用程序,但是 Pod 的 IP 地址不是固定的,这就意味着不方便直接采用 Pod 的 IP 对服务进行访问。

为了解决这个问题,Kubernetes 提供了 Service 资源,Service 会对提供同一个服务的多个 Pod 进行聚合,并且提供一个统一的入口地址,通过访问 Service 的入口地址就能访问到后面的 Pod 服务。

在这里插入图片描述

Service不仅提供了服务发现的功能,还有负载均衡的能力。

Service 原理

Service 在很多情况下只是一个概念,真正起作用的其实是 kube-proxy 服务进程,每个 Node 节点上都运行了一个 kube-proxy 的服务进程。当创建 Service 的时候会通过 API Server 向 etcd 写入创建的 Service 的信息,而 kube-proxy 会基于监听的机制发现这种 Service 的变化,然后它会将最新的 Service 信息转换为对应的访问规则(其实,就是 EndPoint,后面讲)。

Service整体的网络架构如下:
在这里插入图片描述

步骤如下:

  1. 创建Service时,会通过标签选择器,来选择为哪些pod维护网络,内部维护一个Endpoint,用来找到对应的pod。
  2. 当我们在集群内部访问其他pod,先找到service
  3. 在通过service里的Endpoint找到对应pod
  4. 通过iptables(或其他方式)转发到kube-proxy
  5. kube-proxy在找到对应的pod容器

Service 四种类型

  • ClusterIP:默认类型,自动分配一个仅 cluster 内部可以访问的虚拟 IP。选择该值,服务只能够在集群内部访问,这也是默认的 ServiceType。
  • NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 <NodeIP>:NodePort 来访问该服务。如果 kube-proxy 设置了 --nodeport-addresses=10.240.0.0/16(v1.10 支持),那么仅该 NodePort 仅对设置在范围内的 IP 有效。
  • LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部的负载均衡器,并将请求转发到 <NodeIP>:NodePort
  • ExternalName:将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定)。需要 kube-dns 版本在 1.7 以上。

另外,也可以将已有的服务以 Service 的形式加入到 Kubernetes 集群中来,只需要在创建 Service 的时候不指定 Label selector,而是在 Service 创建好后手动为其添加 endpoint。

创建 Service

创建文件nginx-svc.yaml,内容如下:

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc  # Service名字
  labels:
    app: nginx-svc  # Service自己的标签
spec:
  selector: # 选中当前 service 匹配哪些 pod,对哪些 pod 的东西流量进行代理
    app: nginx-deploy
  ports:
  - name: http # service 端口配置的名称
    protocol: TCP # 端口绑定的协议,支持 TCP、UDP、SCTP,默认为 TCP
    port: 80 # service 自己的端口
    targetPort: 80 # 目标 pod 的端口
  type: NodePort  # 随机启动一个端口(30000-32767),映射到ports中的端口,该端口直接绑定到node上,集群中每个node都绑定这个端口

创建Service管理的pod,使用前面章节用过的nginx-deploy.yaml,内容如下:

apiVersion: apps/v1  # deployment api 版本
kind: Deployment  # 资源类型为deployment
metadata:  # 元信息
  labels:  # 标签
    app: nginx-deploy
  name: nginx-deploy  # deployment的名字
  namespace: default  # 所在命名空间
spec:
  replicas: 3  # 期望副本数
  revisionHistoryLimit: 10  # 进行滚动更新后,保留的历史版本数
  selector:  # 选择器,用于找到匹配的RS,管理指定标签的Rs
    matchLabels:  # 按照标签匹配
      app: nginx-deploy  # 匹配的标签
  strategy:  # 更新策略
    rollingUpdate:  # 滚动更新配置
      maxSurge: 25%  # 进行滚动更新时,更新的个数超过期望副本数的比例
      maxUnavailable: 25%  # 进行滚动更新时,最大不可用更新比例,也就是更新不成功最多能有多少个
    type: RollingUpdate  # 更新策略采用滚动更新
  template:  # pod模板
    metadata:  # pod的元信息
      labels:  # pod的标签
        app: nginx-deploy
    spec:  # pod的描述信息
      containers: # pod的描述信息
      - image: nginx:1.7.9   # pod使用镜像
        imagePullPolicy: IfNotPresent   # 镜像拉取策略
        name: nginx  # 容器名称
      restartPolicy: Always  # 重启策略
      terminationGracePeriodSeconds: 30  # 容器删除等待时间

部署deployment:

kubectl apply -f nginx-deploy.yaml
# deployment.apps/nginx-deploy created

查看po信息如下:

在这里插入图片描述

重点看下3个pod的IP和lables。

然后部署Service:

kubectl apply -f nginx-svc.yaml
# service/nginx-svc created

查看信息:

kubectl get svc
# 结果如下
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
nginx-svc    NodePort    10.108.244.247   <none>        80:30087/TCP   2m45s

查看具体信息,可以看到NodePort绑定的端口信息和Endpoints信息:

kubectl describe svc nginx-svc
# 结果如下
Name:                     nginx-svc
Namespace:                default
Labels:                   app=nginx-svc
Annotations:              <none>
Selector:                 app=nginx-deploy
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.108.244.247
IPs:                      10.108.244.247
LoadBalancer Ingress:     localhost
Port:                     http  80/TCP
TargetPort:               80/TCP
NodePort:                 http  30087/TCP  # NodePort绑定的端口信息
Endpoints:                10.1.0.112:80,10.1.0.113:80,10.1.0.114:80  # Endpoints信息
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

可以看到Endpoints对应的3个IP分别是3个Pod的IP。

下面我们就创建另外一个Pod来访问前面创建的Pod。

还创建我们前面用过的busybox:

kubectl run -it --image busybox dns-test --restart=Never --rm /bin/sh

创建完成后我们使用通过服务名来访问:

# 使用wget命令访问
wget http://nginx-svc
# 结果如下,成功下载到了index.html
Connecting to nginx-svc (10.108.244.247:80)  # 请求到了svc地址,然后svc找到pod访问
saving to 'index.html'
index.html           100% |*******************************************************************|   612  0:00:00 ETA
'index.html' saved

也可以在后面加上命名空间实现跨命名空间访问:

 wget http://nginx-svc.defaul

代理 k8s 外部服务

虽然Service是用来负责集群内部服务互相访问,但是有时我们想要Service代理外部服务也是可以的,也就是当我们访问Service时,它可以把请求转发到集群外部去。

例如,当我们的服务部署时,分为开发环境和测试环境,那一个服务部署请求另一个服务时,希望是开发环境的服务请求另一个服务时,也请求到开发环境,同理测试环境也是。

再比如,当我们把一部分服务迁移到K8s中时,还有一部分没有迁移,这时候也需要能够通过Service调用即可。

要实现这个效果,需要额外做以下两件事:

  • 编写 service 配置文件时,不指定 selector 属性(这时不会自动创建endpoint)
  • 自己创建 endpoint

下面就来看看如何实现。

  1. 创建Service

创建文件pgsql-svc-external.yaml,去掉selector,内容如下:

apiVersion: v1
kind: Service
metadata:
  name: pgsql-svc-external  # Service名字
  labels:
    app: pgsql  # Service自己的标签
spec:
  ports:
  - name: pgsql # service 端口配置的名称
    port: 80 # service 自己的端口
    targetPort: 5432 # 目标端口
  type: ClusterIP

创建Service:

kubectl apply -f pgsql-svc-external.yaml
# service/nginx-svc-external created

查看endpoint:

kubectl get ep
# 发现没有为我们自动创建endpoint即可
  1. 创建endpoint

创建资源文件pgsql-ep.yaml,内容如下:

apiVersion: v1
kind: Endpoints
metadata:
  labels:
    app: pgsql # 与 service 一致
  name: pgsql-svc-external # 与 service 一致
  namespace: default # 与 service 一致
subsets:
- addresses:
  - ip: 10.2.8.79
  ports: 
  - port: 5432

假如要在集群内部访问集群外部的pg数据库。

创建endpoint:

kubectl apply -f pgsql-ep.yaml
# endpoints/nginx-svc-external created

查看创建的:

kubectl get ep
# 结果如下
NAME                 ENDPOINTS           AGE
pgsql-svc-external   10.2.8.79:5432      5s

kubectl describe ep pgsql-svc-external
# 结果如下
Name:         pgsql-svc-external
Namespace:    default
Labels:       app=pgsql
Annotations:  <none>
Subsets:
  Addresses:          10.2.8.79
  NotReadyAddresses:  <none>
  Ports:
    Name     Port  Protocol
    ----     ----  --------
    <unset>  5432  TCP

Events:  <none>

以上就是将外部 IP 地址和服务引入到 k8s 集群内部(其他节点),由 service 作为一个代理来达到能够访问外部服务的目的。

没实操实现,有时间再看看。

反向代理外部域名

使用ExternalName类型的Service即可,配置如下:

apiVersion: v1
kind: Service
metadata:
  name: baidu-svc  # Service名字
  labels:
    app: baidu  # Service自己的标签
spec:
  type: ExternalName
  externalName: www.baidu.com

不再演示。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ethan-running

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值