kubernetes 集群外部若是想要访问集群内部,须要经过 NodePort 类型的 service。而 service 的三种调度模型都工做在四层,对于 https 这样的七层协议无从下手,证书、私钥啥的你根本无处配置。kubernetes 的解决方案是,加一个中间层。用户请求不会直接到达 service,而是会先到达一个 pod,而因为 pod 和 pod 之间处于同一个网络,因此这个 pod 直接对后端 pod 进行负载均衡,而再也不通过 service。前端
可是 pod 如何接入外部流量?因此它的前面还须要 service,且必须是 NodePort 类型。而为了高可用,每一个节点上都会启动这个端口,因此前端还须要一个负载均衡器,用于将请求调度到每一个节点。可是这样调度 n 次性能损耗十分严重。前面提到过,pod 能够共享节点的网络名称空间,所以,pod 能够直接监听节点的端口,用于接收外部请求,而后负载均衡到后端 pod 之上。为了高可用,这个 pod 能够运行为 DaemonSet,且能够只运行在有限的节点上。node
这个 pod 在 kubernetes 被称为 ingress controller。ingress 做为一个 pod,运行的容器能够是 nginx、traefik、envoy 等。可是 pod ip 和数量是随时变化的,nginx 这样的产品如何可以知道呢?这还得借助于 service,一个 nginx 中的 upstream 对应一个 service。而这个 service 并不进行负载均衡,仍是用来筛选的,筛选出的 pod 会做为 nginx 的配置文件。可是 service 监控到的 pod 的变化如何反应到 nginx 呢?nginx
这就须要 ingress 资源,kubernetes 存在一种 ingress 的资源,它不一样于 ingress controller。ingress 资源能够得到 service 的结果,而且反应到 nginx 这样的负载均衡器的配置文件中,而且还能通知其进行重载配置文件。这就体现出 nginx 的局限性了,由于它配置文件一更改就得重载配置文件,因此这里我选择使用 traefik,它就是为 docker 而生。web
咱们的作法是,首先部署一个 ingress controller 类型的 pod,而后选择是经过基于不一样的域名或者不一样的 URL,关联到不一样的 service。然后经过 ingress 来监控 service 的变化,最终造成相应的配置文件。docker
为了简单,咱们会将 traefik 安装到 default 名称空间。vim
经过该系列的前面文章,相信你如今也有了泛域名证书,所以 traefik 会开启 https 支持。固然,你也能够选择 http,跟着往下走也不会有问题。后端
若是不须要为 traefik 开启 https,这一节能够跳过。api
因为咱们要提供 https,所以 traefik 须要监听 443 端口,而且须要将咱们的证书提供给它。这样一来,咱们须要将 https 证书挂载进 traefik 容器中,并在配置文件中指定它的位置。浏览器
这里的解决方法是,将证书建立为 secret,挂载进容器中。同时建立 traefik 配置文件,在其中指定证书的位置、开启 443,并将对 80 端口的访问直接转发到 443,而后将该配置文件建立为 configMap,将其挂载到容器中。网络
首先建立 secret:
kubectl create secret tls ntpstat.com --cert=/etc/cert/ntpstat.com.crt --key=/etc/cert/ntpstat.com.key
复制代码
接着建立 traefik 配置文件:
mkdir -p /usr/local/kuberneters/manifests/trafik
cd /usr/local/kuberneters/manifests/trafik
vim traefik.toml
复制代码
如下是文件的内容:
defaultEntryPoints = ["http","https"]
[kubernetes]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[[entryPoints.https.tls.certificates]]
CertFile = "/ssl/tls.crt"
KeyFile = "/ssl/tls.key"
复制代码
将其建立为 configMap:
kubectl create configmap traefik-config --from-file=traefik.toml
复制代码
部署以前先建立 clusterRoleBinding,它的做用是将下面列出的权限授予给 traefik-ingress-controller 这个 ServiceAccount,而后 pod 就由这个用户启动。这样一来,pod 就拥有这些权限了。
# cd /usr/local/kuberneters/manifests/trafik
# vim clusterRoleBinding.yml
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
name: traefik-ingress-controller
namespace: default
复制代码
你若是以为 ClusterRoleBinding 范围太广,也可使用 RoleBinding,1.5 版本后,traefik 支持每一个名称空间级 RoleBinding。由于咱们都在 default 名称空间使用,其实可使用 RoleBinding 的,这里就不纠结了。
kubectl apply -f clusterRoleBinding.yml
复制代码
traefik 能够部署为 Deployment 和 DaemonSet 两种模式,若是使用官方提供的 deployment 的安装方式,traefik pod 的 80/443/8080 端口会经过 NodePort 的方式暴露出来,也就是说你没法经过节点的 ip + 80 端口进行访问,所以你前面还得给它加上一个负载均衡器,这种作法就有些反人类了,访问 traefik 这个负载均衡器还得在前面加上一个负载均衡器不是扯淡么。
而官方的 DaemonSet 就不存在这样的问题了,它使用的是 NET_BIND_SERVICE
这样一个 capabilities,意思是能够直接使用宿主机网络名称空间的端口。使用它以后,你在宿主机上看不到它监听了 80 端口,可是你却能够直接访问,而不是 Deployment 这样的 NodePort 方式。可是 DaemonSet 的缺点也很明显,你的 node 节点数量越多,就越消耗资源。
有没有一箭双鵰的方法呢?那确定是有的,不管你使用 Deployment 仍是 DaemonSet,只要都使用 NET_BIND_SERVICE
,而后定义好节点标签就能让 pod 只运行在特定的节点上,而后域名解析指向这些节点就行。
固然前提是这些节点的 80/443 都没有被占用。不过这里就不指定亲和性了,我会使用 Deployment + NET_BIND_SERVICE 进行部署,之因此不使用 DaemonSet,是由于官方的 DaemonSet 已经可以知足需求了。
在部署以前,咱们先建立一个名为 nexus-pull
的 secret,这个用于 nexus pull 镜像时的认证。关于 nexus 的搭建,能够查看个人上一篇文章:
kubectl create secret docker-registry nexus-pull --docker-username=admin --docker-password="admin123" --docker-server="registry.ntpstat.com:2222"
复制代码
建立 deployment.yml:
# vim /usr/local/kuberneters/manifests/traefik/deployment.yml
---
# 建立一个 serviceaccount 用于启动 pod,而且拥有 clusterrole 的权限
apiVersion: v1
kind: ServiceAccount
metadata:
name: traefik-ingress-controller
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: traefik-ingress-controller
labels:
k8s-app: traefik-ingress-lb
spec:
# 两副本
replicas: 2
selector:
matchLabels:
k8s-app: traefik-ingress-lb
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
serviceAccountName: traefik-ingress-controller
terminationGracePeriodSeconds: 60
# 证书和配置文件
volumes:
- name: ssl
secret:
secretName: ntpstat.com
- name: conf
configMap:
name: traefik-config
imagePullSecrets:
- name: nexus-pull
containers:
- image: registry.ntpstat.com:2222/traefik
name: traefik-ingress-lb
volumeMounts:
- mountPath: /ssl
name: ssl
- mountPath: /conf
name: conf
ports:
- name: http
containerPort: 80
# 须要指定 hostPort
hostPort: 80
- name: https
containerPort: 443
hostPort: 443
- name: admin
containerPort: 8080
hostPort: 8080
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
args:
- --api
- --kubernetes
- --logLevel=INFO
# 指定配置文件位置
- --configFile=/conf/traefik.toml
---
kind: Service
apiVersion: v1
metadata:
name: traefik-ingress-service
spec:
selector:
k8s-app: traefik-ingress-lb
ports:
- protocol: TCP
port: 80
name: web
- protocol: TCP
port: 8080
name: admin
- protocol: TCP
port: 443
name: https
复制代码
部署:
# kubectl apply -f deployment.yml
serviceaccount/traefik-ingress-controller created
deployment.extensions/traefik-ingress-controller created
service/traefik-ingress-service created
复制代码
查看 pod 是否运行:
kubectl get pod -o wide
复制代码
运行 ok 以后,你能够直接使用 curl 来访问节点的 ip:
# curl NODE_IP
Found
复制代码
出现 Found 表示部署成功了。
若是你以为使用 curl 命令不够直观的话,你还能够经过部署 traefik ui 来验证 traefik 已经部署成功。traefik ui 是 traefik 内部的功能,咱们如今只须要添加规则开启它而已。
咱们准备经过主机名的方式来访问它,因此咱们接下来作的就是定义基于域名的虚拟主机。
# vim ui.yml
---
apiVersion: v1
kind: Service
metadata:
name: traefik-web-ui
spec:
selector:
k8s-app: traefik-ingress-lb
ports:
- name: web
port: 80
targetPort: 8080
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: traefik-web-ui
spec:
rules:
- host: traefik.ntpstat.com
http:
paths:
- path: /
backend:
serviceName: traefik-web-ui
servicePort: web
tls:
- hosts:
- traefik.ntpstat.com
secretName: ntpstat.com
复制代码
而后你能够在添加 hosts 以后在浏览器上直接访问 traefik.ntpstat.com 就能够看到它的 web 界面了,而且会自动跳转到 https。