• 用 k8s+Ingress+Traefik 搭建一个外网可以访问的 Web 服务


    小伙伴们好呀,最近给自己安排了很多事情做,文章得很有空再写了🐖

    这篇文章记录了本次的小实验:用 k8s+Ingress+Traefik 搭建一个外网可以访问的 Web 服务

    阅读的话,可以重点看看 3个 流程解析 以及文末的收获

    k8s 搭建

    国外镜像太慢了,特别是 google 和 docker 有竞争,不把镜像弄到 docker hub 上,下载真的特别麻烦。

    用阿里云服务

    minikube start --image-mirror-country='cn'
    
    • 1

    创建 ns,deployment,svc

    ns.yaml
    apiVersion: v1
    kind: Namespace
    metadata:
      name: test-ns
    
    • 1
    • 2
    • 3
    • 4
    deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: my-dep
      name: my-dep
      namespace: test-ns
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: my-dep
      template:
        metadata:
          labels:
            app: my-dep
        spec:
          containers:
          - image: nginx
            name: nginx
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    svc.yaml
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: my-dep
      name: my-dep
      namespace: test-ns
    spec:
      selector:
        app: my-dep
      ports:
      - port: 8000
        protocol: TCP
        targetPort: 80
      type: NodePort
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    按照顺序创建

    进入 pod,修改 nginx 默认页面

    查看 pod,svc 情况

    进入 minikube 访问 svc

    在 minikube 节点中,还可以直接访问 pod 的 ip 地址,因为这里通过 docker 做了桥接。

    而在宿主机,通过 ip a 可以看到下面的信息,网卡是一对的(25和26),会将 192.168.49.2 转发到 minikube 上

    从外部访问 minikube 中的 service。

    流程解析1

    ingress

    之前一直卡在这里,就是这个镜像的问题,现在速度非常快!

    minikube addons enable ingress
    
    • 1

    默认使用的是 ingress-nginx

    ingress-demo.yaml
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: example-ingress
      annotations:
        nginx.ingress.kubernetes.io/rewrite-target: /$1
      namespace: test-ns
    spec:
      rules:
        - host: java4ye.test
          http:
            paths:
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: my-dep
                    port:
                      number: 8000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这里我卡了很久,最后不知道怎么的,这个 address 终于有了,一开始一直是空的。

    期间的操作包括重新创建 ingress ,以及重启 minikube 等。

    果然搭建环境真的很折磨人,有时都不知道这个官方文档到底有没有用的🙃

    反正我跟着下面的链接操作了没有效果,最后还是得手动往宿主机的 /etc/hosts 中配置。

    https://minikube.sigs.k8s.io/docs/handbook/addons/ingress-dns/

    通过 describe 命令查看 ingress

    可以看到其中的路由规则。

    配置完成后,我们只需要访问 java4ye.test 即可访问到后端的 service,不用再访问 API

    再进一步,我们创建一个 my-dep2 标签的 pod,svc,然后修改下 ingress ,同时配置 /etc/hosts 文件,

    这时候便可以发现 ingress 的另一个好处了。

    在微服务下,可以方便的将各个服务间的请求划分开,代码里也不用写死这个 service 的地址了,用域名就好了。

    流程解析2

    底层

    Ingress 创建成功后,会在 pod 中的 nginx.conf 文件中创建 Server :java4ye.test 和 java4ye2.test,并配置相应的路由规则

    k8s dashboard

    接下来就可以通过 UI 直接操作这些 POD 了,比如查看 logs 或者 exec 等命令,方便多了

    暴露到外网

    我是在云服务器上搭建的,所以我先得修改服务器的防火墙,放开 8001 端口

    接着利用 kubectl proxy 命令,在 8001 端口上启动代理服务,将请求转发到 10.0.8.8 上的 k8s 的 API Server

    注意这里的 address 要改成内网的 IP 地址。不能用 127.0.0.1 的,也不能写 minikube 的地址。

    kubectl proxy --port=8001 --address='10.0.8.8' --accept-hosts='^.*'
    
    • 1

    最后,直接访问云服务器公网 IP + 8001 端口,即可。

    kubectl proxy 的更多解释可以看这里 👇

    https://loft.sh/blog/when-and-how-to-use-kubectl-proxy/

    那怎么将 minikube 中的 service 暴露到公网上呢

    其实,通过上面的 kubectl proxy ,我们可以有这么一个思路,监听公网某个端口,将它转发到 k8s 的某个service。

    比如,利用 nginx,监听某个端口,再根据访问路径的不同进行转发。

    这里可选的有 Nginx,HAproxy, Traefik 等等。

    听过这个 Traefik 是后起之秀,所以这次我打算用它来实现。(折磨之旅🐷)

    https://docs.traefik.cn/

    Traefik

    https://doc.traefik.io/traefik/

    Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience. It receives requests on behalf of your system and finds out which components are responsible for handling them.

    Traefik是一个开源的边缘路由器,它可以让发布你的服务成为一种有趣而简单的体验。它代表您的系统接收请求,并找出哪些组件负责处理它们。

    这个上手也比较简单,所以基本的用法我就不多介绍了。

    docker compose 文件
    version: '3'
    
    services:
      traefik:
        # The official v2 Traefik docker image
        image: traefik:v2.9
        # Enables the web UI and tells Traefik to listen to docker
        command: --api.insecure=true --providers.docker --providers.file.directory=/etc/traefik/conf --api.insecure=true --providers.docker --providers.file.directory=/etc/traefik/conf --log.filePath=/var/log/traefik/traefik.log --log.format=json --log.level=DEBUG --accesslog=true --accesslog.filepath=/var/log/traefik/access.log --accesslog.format=json
        ports:
          # The HTTP port
          - "8002:80"
          # The Web UI (enabled by --api.insecure=true)
          - "8080:8080"
        volumes:
          # So that Traefik can listen to the Docker events
          - /var/run/docker.sock:/var/run/docker.sock
          - /home/yang4ye/traefik-docker/conf:/etc/traefik/conf
          - /home/yang4ye/logs/traefik:/var/log/traefik
          - /etc/localtime:/etc/localtime:ro
          - /etc/timezone:/etc/timezone:ro
    
        networks:
          - minikube
        extra_hosts:
          - "java4ye.test:192.168.49.2"
          - "java4ye2.test:192.168.49.2"
    
    networks:
      minikube:
        external: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    细节注意,这里我们的 minikube 运行在宿主机的 docker 容器中,我们新起的这个 traefik 容器也要和 minikube 容器连接在同一个网络才行,才能互相通信

    yaml 配置文件
    http:
      # Add the router
      routers:
        router0:
          middlewares:
          - my-basic-auth
          - my-dep-stripPrefixRegex
          - myHeader1
          service: my-dep
          rule: PathPrefix(`/my-dep/`) || PathPrefix(`/my-dep{a:/*$}`)
        router1:
          middlewares:
           - my-dep-stripPrefixRegex
           - my-basic-auth
           - myHeader2
          service: my-dep2
          rule: PathPrefix(`/my-dep2/`) || PathPrefix(`/my-dep2{b:/*$}`)
    
      # Add the middleware
      middlewares:
        my-basic-auth:
          basicAuth:
            users:
            - xxx:$apr1$/bZ/nSB0$xxxxxxxxx/
        my-dep-stripPrefixRegex:
          stripPrefixRegex:
            regex: "/my-dep\\d*"
        myHeader1:
          headers:
            customRequestHeaders:
              host: "java4ye.test" # Adds
              #X-Custom-Request-Header: "" # Removes
              # customResponseHeaders:
              # X-Custom-Response-Header: "" # Removes
        myHeader2:
          headers:
            customRequestHeaders:
               host: "java4ye2.test"
    
    
      services:
        my-dep:
          loadBalancer:
            servers:
            - url: "http://java4ye.test"
              #- url: "http://192.168.49.2:30181"
        my-dep2:
          loadBalancer:
            servers:
              # - url: "http://192.168.49.2:30571"
               - url: "http://java4ye2.test"
    
              # log 这些是静态的,不能放在动态配置里面,不会有效果
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    这个配置文件也是不断地修修改改,折磨了两天后,终于成功了!🐖

    bug 折磨我的过程

    一开始搭建成功后,出现了一个很神奇的问题。

    就是上面配置文件中 service 的 url。

    当我用自定义的 域名 时,它一直返回 404 给我 👇

    但是如果改成 IP 地址的话,就能正常访问到我们 k8s 中的 service。

    此时此刻,我已经把 traefik 的日志文件翻烂了,对比了一遍又一遍,都没看出啥问题。

    没办法,我把目光转移到 nginx 容器上,到上面翻看日志时,我又产生了 N 多疑惑,这 404 日志去哪了!

    为啥就记录这些 200 的,404 你就不记录了!!(内心在咆哮!)

    无奈之下,我把 url 换成 ip 的那种,再多次尝试下,我发现了一个疑点

    可以看到这里 404 的 nginx 是有版本号的!

    这时我发现 nginx 容器的 log 也正常记录了这个 404 日志。

    我开始怀疑这个请求根本没打到 k8s 容器上。

    于是,我脑瓜子一转,不会是 traefik 中用到了 nginx 了吧,我得翻翻它的源码去(佩服自己的脑瓜子🙄,此时此刻我还以为这个 traefik 就是个组装怪,底层用了 nginx,然后加了些 dashboard,集成 k8s ,docker 等等)

    在 GitHub 上搜查一番后,发现人家就是用 go 写的,哪有什么 nginx 的影子 🐖

    此时,我已经很懵圈了,想不出哪里还有啥问题,甚至把官网和它的论坛翻了又翻,把有关 404 的问题都仔细看了一遍又一遍,差点就在 GitHub 上提 issue 了(要不是步骤有点麻烦🐖)

    终于,在刚刚吃根冰棍降降火后,我想到了 ingress

    好家伙,这里也是用到了 nginx 的,差点忘了!

    我火速来进入到这个容器中,迫不及待的执行了 curl 命令测试下,结果它居然正常返回了。(我不能接受!🐖)

    只能去翻翻 ingress 的文档了😮

    思路再次中断。

    没办法,再吃点零食好了。

    突然,我看到了 ingress 的配置文件。

    这里指定了这个 host !!

    这一刻,我恍然大悟,肯定是 traefik 转发的时候,header 中没有这个 host。

    在中间件中把这个 host 补上去

    结果终于正常了!😄

    这里还有点不顺畅的体验,就是正则表达式的使用(写法比较奇怪),以及路由匹配(没有好例子参考。。而且 Path 和 PathPrefix 的匹配范围不太明确)

    正则表达式:

    https://regex101.com/r/58sIgx/2

    流程解析3

    收获

    做项目果然是最快的学习方式,有点当年做毕设的感觉 哈哈。(目前打算用之前买的服务器搭建一些项目玩玩,这是小项目的开端,实验成功~)😝

    这次实验为:用 k8s+Ingress+Traefik 搭建一个外网可以访问的 Web 服务

    通过它很好地熟悉了 k8s 中 Deloyment,Service,Pod,Namespace ,Ingress 这些概念。

    比如 为啥要用到 Ingress 呢?Service 不就可以访问到 Pod 吗

    这其实涉及到 四层代理和七层代理 的问题,Service 只能代理到 4 层,而这个在我们日常开发中,往往会不够用了,毕竟我们经常得去到应用层 http

    Service 通过标签去匹配要 Pod

    关于 Service 的更多知识可以看官网

    https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies

    最重要的收获,网络知识

    细心的小伙伴会发现,这里不仅仅有 容器间通信 ,还有 pod 间通信 以及 Service 到 Pod 通信

    在文中我也简单提到过 桥接,网卡配对,网段 这些东西,但是我感触最大的还是 iptables

    了解到它的 四表五链 ,也稍微知道了他们之间通过 nat 表做了目标地址转换等操作

    那为啥用 Traefik 呢

    这个也很简单,Nginx 配置起来嫌麻烦,openresty 还没用过。

    这个 Traefik 用 GO 写的(快~),而且还集成了很多,如 k8s,docker 等等,还有个 dashboard 可以看看,更改一些配置也不用自己重启,挺轻松的用起来,但是后续怎样还得再看看了…… (感觉服务器,网关好多选择)

    最痛苦的感悟,看了那么多次日志,无数次看到 host 是空的,但是楞是没想到这个点,这可能就是对这些工具比较陌生,以及没有仔细思考这些配置后面的原理,就跟着傻瓜式CV了,确实有点不应该,浪费了很多时间在这🐖,但是好处也有一点,,就是对官网文档熟悉些了😂 还有顺便把 nginx 的官网也翻熟了🙃

    云原生进度+1 🐷

  • 相关阅读:
    Spring使用注解进行注入
    SpringBoot中xml映射文件
    如何使用 chrome Timeline 工具
    Unity加速编译小技巧
    ARM_day9 按钮控制LED灯、蜂鸣器、风扇实验
    一文详解 Okio 输入输出流
    分布式定时调度-xxl-job
    Qt(Python+Qt)QMainWindow的splitDockWidget方法将QDockWidget停靠窗分割排列
    2022“杭电杯”中国大学生算法设计超级联赛(9)签到题4题
    【考研数学】概率论与数理统计 —— 第七章 | 参数估计(1,基本概念及点估计法)
  • 原文地址:https://blog.csdn.net/weixin_40251892/article/details/127484893