系列专栏声明:比较流水,主要是写一些踩坑的点,和实践中与文档差距较大的地方的思考。这个专栏的典型特征可能是 次佳实践
,争取能在大量的最佳实践中生存。
一、怎么在墙内安装 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"
几个要注意的点是:
- minikube 和 kubernetes 都显式指明了版本,尽管这与拥抱云原生的理念略有悖,但确实是一个成本相对划算的策略;目前考虑的版本升级策略是完全新建镜像,等应用迁移完成后把流量转过去
minikube addons enable ingress
阶段也需要访问源,所以也要在香港区完成- 不确定 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
几个需要注意的点是:
- 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
?
观察得到的网络拓扑情况如下(非官方文档):
ClusterIP
指的是pod
的 ip;cluster
和profile
看上去是同一个东西,一个cluster
可以有多个node
,minikube 默认是单node
- minikube 是跑在 host docker 里的,所以 minikube 和 node 使用的是 host docker 的网络,即
192.168.49.2
;我试了一下新建第二个profile
,它的 ip 会是192.168.58.2
;所以似乎profile
之间是不会共享网络的 ingress
的规则写的是service
,但实际上似乎是指向pod
,所以service
仅仅是提供了注册中心效果,但流量没有从这里做负载均衡吗- 没有找到 host.minikube.internal,由于下面的方案不需要这个,所以没有继续研究
以下才是重点:
pod
之间可以通过ClusterIP
互相访问,即172.17.0.4
可以访问172.17.0.5
,既然它叫做Cluster
IP,那我猜是在整个 cluster 范围内生效的;但感觉实际意义不大,因为 ip 是动态变化的,写在代码里的应该还是用httpbin.default.svc.cluster.local
互相访问pod
可以访问192.168.49.1
,这很重要,比如可以在host
上安装MySQL
,并将MySQL container
的port
和host
的port
绑定起来,这样就可以在pod
里访问192.168.49.1:3306
pod
也可以访问192.168.49.3
即 host docker 上的traefik
,细节后面会讲,这是因为我们将traefik
放在了prd
这个网络里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
模式,非常奇怪,看下来感觉是一个非常差的实践,决定用这个方式来做上手演示的人应该被拖出去打死。
这里选用了 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"
这个方案不一定是最佳实践,但是实现/演示了两个重要的观点:
- 拆掉 https 证书的工作应该在网关上完成,不应该交给 k8s,已经进入 k8s 的流量应该是信任的;在 k8s 内部做流量的权限控制,应该使用 role 等策略,不应该使用暴露给用户的那张证书,那张证书是有品牌意义的,有可能是另外一些非运维部门负责的
- 目前 traefik 是在 host docker 上用 docker-compose 维护的,这是把之前的用法直接复制过来了,而且直接用同一个网络方便反问。如果也要使用 k8s 管理的话,应该是再起一个独立的 cluster,里面只有这个 traefik,然后做集群间流量。即整个入口网关都不应该和应用放在同一个集群里,这样的设计另一个好处是,应用集群天然是分布式的,天然是支持两地三中心的;单点故障控制在网关集群这里
参考文献: