云原生 05:安装 Minikube 并使用 Traefik 做为网关暴露到外网

理斯特

系列专栏声明:比较流水,主要是写一些踩坑的点,和实践中与文档差距较大的地方的思考。这个专栏的典型特征可能是 次佳实践,争取能在大量的最佳实践中生存。

一、怎么在墙内安装 minikube

minikube 默认使用的是源是 gcr.io,所以很多组件是装不上的。官方自某版本之后提供了指定镜像源的方式,可以指定到阿里云的源,但社区对这个源的风评不好,所以没有研究这个方案。

选定的方案是在阿里云香港区用 Packer 制作镜像,在制作镜像的过程中把可能用到的组件都装好,然后将镜像复制回杭州区。关键脚本如下:

$ curl -LO https://github.com/kubernetes/minikube/releases/download/v1.24.0/minikube-linux-amd64
$ install minikube-linux-amd64 /usr/local/bin/minikube

$ su core -c "minikube config set WantUpdateNotification false"

$ su core -c "minikube start --kubernetes-version=1.22.3 --profile=prd"
$ su core -c "minikube profile prd"
$ su core -c "minikube addons enable ingress"
$ su core -c "minikube stop"

几个要注意的点是:

  1. minikube 和 kubernetes 都显式指明了版本,尽管这与拥抱云原生的理念略有悖,但确实是一个成本相对划算的策略;目前考虑的版本升级策略是完全新建镜像,等应用迁移完成后把流量转过去
  2. minikube addons enable ingress 阶段也需要访问源,所以也要在香港区完成
  3. 不确定 profile 的实践意义,实践中应该是物理隔离的多个集群吧,应该不会用同一套 k8s 的不同 profile 做软隔离吧?但是 minikube delete 删掉的是 profile,包括所有基础镜像,和 ingress,所以不要使用 delete,否则要去香港区重装

二、怎样使用自有镜像

官方有提供配置项可以指定私有源,不过也暂时不研究这个方案。大体的思路仍然是去香港区下载需要的镜像,然后用 tar 传到杭州区,然后使用 docker load。需要注意的是,minikube 使用的是 host 上的 docker,但是独立的工作区,所以要从 host 往 minikube 内部再 load 一次。大体的脚本如下:

$ wget http://path/to/httpbin.latest.tar
$ docker load < httpbin.latest.tar
$ docker tag kennethreitz/httpbin:latest kennethreitz/httpbin:1.0
$ docker rmi kennethreitz/httpbin:latest
$ minikube image load kennethreitz/httpbin:1.0

几个需要注意的点是:

  1. k8s 对 :latest 这类 tag 的启动时的默认拉镜像策略是 Always,所以这里把它改成 :1.0 使得默认拉镜像策略变为 IfNotPresent,也就是优先使用本地镜像,不要尝试获取更新,因为连不上源

三、启动一个最简单的示例

$ kubectl create deployment httpbin --image=docker.io/kennethreitz/httpbin:1.0
$ kubectl describe deployment httpbin

$ kubectl expose deployment httpbin --type=ClusterIP --port=80
$ kubectl describe service httpbin

这个命令为什么不是 kubectl create service httpbin

观察得到的网络拓扑情况如下(非官方文档):

minikube-network.png

  1. ClusterIP 指的是 pod 的 ip;clusterprofile 看上去是同一个东西,一个 cluster 可以有多个 node,minikube 默认是单 node
  2. minikube 是跑在 host docker 里的,所以 minikube 和 node 使用的是 host docker 的网络,即 192.168.49.2;我试了一下新建第二个 profile,它的 ip 会是 192.168.58.2;所以似乎 profile 之间是不会共享网络的
  3. ingress 的规则写的是 service,但实际上似乎是指向 pod,所以 service 仅仅是提供了注册中心效果,但流量没有从这里做负载均衡吗
  4. pod 可以访问 host.minikube.internal,解析出来的 ip 是 192.168.49.1

以下才是重点:

  1. pod 之间可以通过 ClusterIP 互相访问,即 172.17.0.4 可以访问 172.17.0.5,既然它叫做 ClusterIP,那我猜是在整个 cluster 范围内生效的;但感觉实际意义不大,因为 ip 是动态变化的,写在代码里的应该还是用 httpbin.default.svc.cluster.local 互相访问
  2. pod 可以访问 192.168.49.1,这很重要,比如可以在 host 上安装 MySQL,并将 MySQL containerporthostport 绑定起来,这样就可以在 pod 里访问 192.168.49.1:3306
  3. pod 也可以访问 192.168.49.3 即 host docker 上的 traefik,细节后面会讲,这是因为我们将 traefik 放在了 prd 这个网络里
  4. pod 可以访问 10.1.x.x,即可以在整个 VPC 内畅通无阻(当然我猜需要对 IP 端做一些规划,不要冲突),所以 pod 可以访问到另一台虚机上的任何产品,和托管的产品如 RDS,只要纳入到 VPC 范围里

四、暴露服务到外网

deployment 有 3 种方式:ClusterIP,NodePort,Load Balancer。粗粗看了一下,主观感觉最云原生的方式应该是 Load Balancer,即直接使用云厂商提供的云原生网关,直接将流量打到 service 甚至是 pod,连 ingress 都不要走。k8s 只是一个容器管理工具,可能恰好提供了 ingress 等能力,但是在 网关 方面,感觉 k8s 并不是一个最佳实践,将网关包成容器部署在 k8s 内也不是一个最佳实践。

上手教程推荐的是 NodePort 模式,非常奇怪,看下来感觉是一个非常差的实践,决定用这个方式来做上手演示的人应该被拖出去打死。同样的,也不要学习 kubectl port-forward

这里选用了 ingress 的方式,所以 deployment 使用了默认的 ClusterIP 模式。

$ kubectl apply -f ingress.yaml
$ kubectl describe ingress app-ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
spec:
  rules:
    - host: httpbin.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: httpbin
                port:
                  number: 80
关于默认的 nginx ingress 文档有个很奇怪的坑,示例代码里开启了 annotation,会把所有的请求都 rewrite 到首页,即访问 http://httpbin.example.com/a.js 会被 rewrite 到 http://httpbin.example.com,不知道为什么要在新手文档里这么干。

minikube 会在 host docker 上创建一个和 profile 同名的 network,即 prd,并启动自己做为第一个 instance;同时使用了默认的单 Node 模式,所以这个 Node 的 ip 一定是 192.168.49.2;多 Node 会怎样我没有尝试,但是猜测可以指定。

version: '3.8'

services:
  traefik:
    container_name: traefik
    image: traefik:v2.2.11
    networks:
      - prd
    ports:
      - 80:80
      - 443:443
    labels:
      - "traefik.enable=false"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # 省略其它配置文件 
    restart: always

networks:
  prd:
    external: true

/path/to/traefik/config/default.toml

[http.services]
  [http.services.noop.loadBalancer]
     [[http.services.noop.loadBalancer.servers]]
        url = ""

  [http.services.minikube.loadBalancer]
    [[http.services.minikube.loadBalancer.servers]]
      url = "http://192.168.49.2:80/"

[http.routers]
  [http.routers.https-redirect]
    entryPoints = ["http"]
    rule = "HostRegexp(`{app:[0-9a-z-]+}.example.com`)"
    middlewares = ["https-redirect"]
    service = "noop"

  [http.routers.example]
    entryPoints = ["https"]
    rule = "HostRegexp(`{app:[0-9a-z-]+}.example.com`)"
    service = "minikube"
  [http.routers.example.tls]

[http.middlewares.https-redirect.redirectScheme]
  scheme = "https"

这个方案不一定是最佳实践,但是实现/演示了两个重要的观点:

  1. 拆掉 https 证书的工作应该在网关上完成,不应该交给 k8s,已经进入 k8s 的流量应该是信任的;在 k8s 内部做流量的权限控制,应该使用 role 等策略,不应该使用暴露给用户的那张证书,那张证书是有品牌意义的,有可能是另外一些非运维部门负责的
  2. 目前 traefik 是在 host docker 上用 docker-compose 维护的,这是把之前的用法直接复制过来了,而且直接用同一个网络方便反问。如果也要使用 k8s 管理的话,应该是再起一个独立的 cluster,里面只有这个 traefik,然后做集群间流量。即整个入口网关都不应该和应用放在同一个集群里,这样的设计另一个好处是,应用集群天然是分布式的,天然是支持两地三中心的;单点故障控制在网关集群这里

参考文献:

  1. Minikube Start
  2. Kubernetes 的三种外部访问方式:NodePort、LoadBalancer 和 Ingress
  3. Set up Ingress on Minikube with the NGINX Ingress Controller
  4. Ingress Nginx Controller Troubleshooting
  5. Kompose
  6. kubectl for Docker Users
  7. 在 Kubernetes 集群中使用 MetalLB 作为 Load Balancer(上)(下)
阅读 459

小鲜
治大国,若烹小鲜,以道莅天下,其鬼不神。非其鬼不神,其神不伤人。非其神不伤人,圣人亦不伤人。夫两...

web/mobile/iot, front/back, js/java

15 声望
7 粉丝
0 条评论

web/mobile/iot, front/back, js/java

15 声望
7 粉丝
文章目录
宣传栏