专栏首页Liusy01k8s之HTTP请求负载分发

k8s之HTTP请求负载分发

导读

对于基于HTTP的服务来说,不同的URL地址经常对应不同的后端服务或者虚拟服务器,通常的做法是在应用前添加一个反向代理服务器Nginx,进行请求的负载转发,在Spring Cloud这个微服务框架中,使用zuul网关实现此功能。

而对于k8s集群来说,当然也是可以用Nginx实现请求的转发,但对于一个成熟的容器编排工具,k8s内置了一个HTTP请求负载分发的组件,就是Ingress Controll。另外,k8s的Service也是具有负载均衡能力的组件。

用法

在定义Ingress之前,需要先部署Ingress Controller,以实现所有后端Service提供一个统一的入口。Ingress Controller需要实现基于不同Http URL向后转发的负载分发规则 。

在K8s中,Ingress Controller将以Pod的形式运行,监控apiserver的/ingress接口后端的backend services,如果service发生变化,则Ingress Controller应自动更新其转发规则。

1、创建Ingress Controller

其实Ingress底层就可以用Nginx实现,Ingress Controller会监听ApiServer,获取全部的Ingress定义,然后根据定义生成Nginx的配置文件。

下面使用nginx-ingress-controller镜像来创建Ingress Controller。

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx-ingress
  namespace: ingress
  labels:
    app: nginx-ingress
spec:
  replicas: 1
  template:
    metadata:
      name: nginx-ingress
      labels:
        app: nginx-ingress
    spec:
      serviceAccountName: ingress-sc
      containers:
      - name: nginx-ingress
        image: imagia/nginx-ingress-controller:0.32.0
        imagePullPolicy: IfNotPresent
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        ports:
        - containerPort: 80
          hostPort: 80
        - containerPort: 443
          hostPort: 443

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

2、创建Ingress

下面的Ingress定义了将/user的请求转发至user-svc的Service上,将/order的请求转发至order-svc的Service上。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: myweb-ingress
  namespace: ingress
spec:
  rules:
  - host: myweb.com
    http:
      paths:
      - path: /api/user
        backend:
          serviceName: user-svc
          servicePort: 8081
      - path: /api/order
        backend:
          serviceName: order-svc
          servicePort: 8082

Ingress的策略配置:

(1)所有请求都转发到单个Service上

此时不用配置rules

spec:
  backend:
    serviceName: user
    servicePort: 8080

(2)同一域名,不同url转发到不同的服务上

比如域名都是myweb.com,/api/user转发到user服务,/api/order转发到 order服务。

spec:
  rules:
  - host: myweb.com
    http:
      paths:
      - path: /api/user
        backend:
          serviceName: user-svc
          servicePort: 8081
      - path: /api/order
        backend:
          serviceName: order-svc
          servicePort: 8082

(3)不同域名

域名为myweb1.com的请求转发到user服务,域名为myweb2.com的请求转发到order服务

spec:
  rules:
  - host: myweb1.com
    http:
      paths:
      - backend:
          serviceName: user-svc
          servicePort: 8081
  - host: myweb2.com
    http:
      paths:
      - backend:
          serviceName: order-svc
          servicePort: 8082

(4)不使用域名

这种配置用于一个网站不使用域名直接提供服务的场景,此时通过 任意一台运行ingress-controller的Node都能访问到后端的服务。

spec:
  rules:
  - http:
      paths:
      - path: /api/user
        backend:
          serviceName: user-svc
          servicePort: 8081

【注】使用无域名的Ingress转发规则时,将默认禁用非安全 HTTP,强制启用HTTPS。

案例

一、简介

创建一个命名空间:ingress,启动两个服务,一个是user,一个是order,利用Ingress-controller将请求/api/user转发到user服务,将请求/api/order转发到order服务。

二、创建服务

(1)创建简单springboot应用

只有两个api接口,分别是/api/order和/api/user,然后打成jar包上传至服务器

(2)将上述jar包创建为镜像

将jar包、jdk安装包和Dockerfile放在同一个目录

Dockerfile文件内容如下:

FROM centos:7

LABEL author=lsy

ENV path=/usr/soft

RUN mkdir ${path}

WORKDIR ${path}

ADD jdk-8u191-linux-x64.tar.gz ${path}

ENV JAVA_HOME=${path}/jdk1.8.0_191
ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH=$JAVA_HOME/bin:$PATH

COPY service-1.0.jar ${path}

EXPOSE 8080

CMD  java -jar service-1.0.jar

使用命令创建镜像:

docker build ./ -t  cnode-1:5000/ingress-service:v1.0

镜像创建完成后记得上传到docker私有仓库

(3)在ingress命名空间启动相应的ReplicationController和Service

user-rc.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: user-rc
  namespace: ingress
  labels:
    name: user-rc
spec:
  replicas: 1
  selector:
    name: user-rc
  template:
    metadata:
      name: user-rc
      labels:
        name: user-rc
    spec:
      containers:
      - name: user-rc
        image: cnode-1:5000/ingress-service:v1.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080

user-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: user-svc
  namespace: ingress
spec:
  selector:
    name: user-rc
  ports:
  - port: 8081
    targetPort: 8080

order-rc.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: order-rc
  namespace: ingress
  labels:
    name: order-rc
spec:
  replicas: 1
  selector:
    name: order-rc
  template:
    metadata:
      name: order-rc
      labels:
        name: order-rc
    spec:
      containers:
      - name: order-rc
        image: cnode-1:5000/ingress-service:v1.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080

order-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: order-svc
  namespace: ingress
spec:
  selector:
    name: order-rc
  ports:
  - port: 8082
    targetPort: 8080

分别使用kubectl create 命令创建上述资源

(4)在ingress命名空间中创建ServiceAccount

创建ServiceAccount和ClusterRoleBinding,如果不创建的话,Ingress-controller会使用默认名为default的ServiceAccount,default权限很弱,获取不到相应的资源信息,这样Ingress-controller会启动失败。

---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    name: ingress-sc
  name: ingress-sc
  namespace: ingress

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: ingress
  labels:
    name: ingress-crb
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: ingress-sc
  namespace: ingress

(5)创建Ingress Controller

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx-ingress
  namespace: ingress
  labels:
    app: nginx-ingress
spec:
  replicas: 1
  template:
    metadata:
      name: nginx-ingress
      labels:
        app: nginx-ingress
    spec:
      serviceAccountName: ingress-sc
      containers:
      - name: nginx-ingress
        image: imagia/nginx-ingress-controller:0.32.0
        imagePullPolicy: IfNotPresent
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        ports:
        - containerPort: 80
          hostPort: 80
        - containerPort: 443
          hostPort: 443

创建完后查看:

【注】环境变量POD_NAME和POD_NAMESPACE是必须要设置的,不然会报错。

(6)创建Ingress

也就是规则设置,将请求地址为/api/user转发至user服务,请求地址为/api/order的转发至order服务。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: myweb-ingress
  namespace: ingress
spec:
  rules:
  - host: myweb.com
    http:
      paths:
      - path: /api/user
        backend:
          serviceName: user-svc
          servicePort: 8081
      - path: /api/order
        backend:
          serviceName: order-svc
          servicePort: 8082

创建完后查看:

可以看到,它的Hosts是myweb.com,物理机地址是192.168.197.120,端口为80.

如果ADDRESS列为空, 则通常说明Nginx未能正确连接到后端Service,需要排错。

【注】为什么只有一个ip,是因为这个RC只有一个Pod,调度到cnode-2这台机运行,如果想要每台机器都有一个,建议使用DaemonSet类型的Controller

此时查看ingress-controller的Pod的日志:

先获取Pod的名字

然后查看日志:

从上图可以看到,当ingress创建之后,ingress-controller会自动去加载,然后生成对应的nginx的conf文件。

进入容器查看nginx的配置文件:文件是/etc/nginx/nginx.conf

(7)测试效果

由于并没有上述设置的myweb.com这个域名,所以需要进行解析。

curl --resolve myweb.com:80:192.168.197.120 http://myweb.com:80/api/order

访问order服务:

查看日志:可以看到是转发到order-svc这个Service上了

192.168.197.100 - - [31/Oct/2020:07:40:46 +0000] "GET /api/order HTTP/1.1" 200 21 "-" "curl/7.29.0" 82 0.004 [ingress-order-svc-8082] [] 10.36.0.4:8080 21 0.004 200 0f7dfb1134644ee2ceb7a7d364ddfe45

访问user服务:

查看日志:可以看到是转发到user-svc这个Service上了

192.168.197.100 - - [31/Oct/2020:07:42:16 +0000] "GET /api/user HTTP/1.1" 200 20 "-" "curl/7.29.0" 81 0.011 [ingress-user-svc-8081] [] 10.44.0.3:8080 20 0.010 200 46f66ab540c41590a74bbea66bd65ea8

结尾

秋风吹落叶,天气微凉,注意保暖!

本文分享自微信公众号 - Liusy01(Liusy_01),作者:Liusy01

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-11-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ZK实现分布式锁

    上一篇说了ZK是什么以及能干什么,今儿这篇就来用ZK实现分布式锁,分别用java原生的zookeeper客户端、ZKClient实现。

    Liusy
  • 【设计模式-工厂相关模式】

    定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。

    Liusy
  • 【设计模式-享元模式】

    【导读】程序设计有时会面临需要创建大量相同对象或相似对象,创建大量的对象会耗费大量的内存,此时就需要一个创建之后可以重复使用的设计,这就是享元模式。

    Liusy
  • TKE容器服务​创建ingress

    ingress:通俗的理解是:通过7层负载均衡转发对应url到对应的path中,实现准确转发流量目的。

    马凌鑫
  • Hexo搭建 --- 3、Hexo发布文章详解

    一份执着✘
  • 语音识别内容

    A1:但是你传过来的音频,必须是双通道的。是你音频文件生成好的。是一个实时音频流的概念。

    算法发
  • MySQL统计信息相关表介绍

    以前给大家介绍过MySQL中的统计信息,相信大家也都了解了。那么统计信息是存放在哪里呢?我们怎么去查看? 在MySQL中提供了两个表记录统计信息的相关内容,分别...

    沃趣科技
  • URL短网址生成算法原理

    短网址(Short URL),是在形式上比较短的网址,通过映射关系跳转到原有的长网址。

    阳光岛主
  • 为什么要重写equals和hashCode方法

    葆宁
  • Java后端技术从0到1技术路线,一步步走向大神!

    对于很多在校生只需要掌握初级阶段然后好好复习基础知识,好好准备笔试和面试试题,基本上可以到差不多的工作了。如果学有余力可以看一下高级阶段,毕竟现在很多公司都是需...

    Java技术江湖

扫码关注云+社区

领取腾讯云代金券