前言
在 Kubernetes 中, 服务和 Pod 的 IP 地址仅可以在集群网络内部使用, 对于集群外的应用是不可见的. 为了使外部的应用能够访问集群内的服务, 在 Kubernetes 中目前提供了以下几种方案:
- NodePort
- LoadBalancer
- Ingress
在之前的博文中介绍过 NodePort, 简单来说, 就是通过 service 这种资源对象, 为后端 pod 提供一个统一的访问接口, 然后将 service 的统一访问接口映射到群集节点上, 最终实现 client 通过映射到群集节点上的端口访问到后端 pod 提供的服务.
但是, 这种方式有一个弊端, 就是当新生成一个 pod 服务就需要创建对应的 service 将其映射到节点端口, 当运行的 pod 过多时, 我们节点暴露给 client 端的端口也会随之增加, 这样我们整个 k8s 群集的危险系数就会增加, 因为我们在搭建群集之处, 官方明确指出, 必须关闭 firewalld 防火墙及清空 iptables 规则, 现在我们又暴露了那么多端口给 client, 安全系数可想而知.
有没有更安全又简便的一种方法呢? 答案是肯定的, 就是来利用 Ingress 这种资源对象来实现.
博文大纲:
一, Ingress-nginx 介绍
二, Ingress-nginx 配置示例
一, Ingress-nginx 介绍
1,Ingress-nginx 组成
ingress-nginx-controller: 根据用户编写的 ingress 规则 (创建的 ingress 的 YAML 文件), 动态的去更改 nginx 服务的配置文件, 并且 reload 重载使其生效 (是自动化的, 通过 lua 脚本来实现);
ingress 资源对象: 将 Nginx 的配置抽象成一个 Ingress 对象, 每添加一个新的 Service 资源对象只需写一个新的 Ingress 规则的 YAML 文件即可 (或修改已存在的 ingress 规则的 YAML 文件)
1,Ingress-nginx 可以解决什么问题?
1) 动态配置服务
如果按照传统方式, 当新增加一个服务时, 我们可能需要在流量入口加一个反向代理指向我们新的 k8s 服务. 而如果用了 Ingress-nginx, 只需要配置好这个服务, 当服务启动时, 会自动注册到 Ingress 的中, 不需要而外的操作.
2) 减少不必要的端口映射
配置过 k8s 的都清楚, 第一步是要关闭防火墙的, 主要原因是 k8s 的很多服务会以 NodePort 方式映射出去, 这样就相当于给宿主机打了很多孔, 既不安全也不优雅. 而 Ingress 可以避免这个问题, 除了 Ingress 自身服务可能需要映射出去, 其他服务都不要用 NodePort 方式
2,Ingress-nginx 工作原理
1)ingress controller 通过和 kubernetes API 交互, 动态的去感知集群中 ingress 规则变化,
2) 然后读取它, 按照自定义的规则, 规则就是写明了哪个域名对应哪个 service, 生成一段 nginx 配置,
3) 再写到 nginx-ingress-controller 的 pod 里, 这个 Ingress controller 的 pod 里运行着一个 Nginx 服务, 控制器会把生成的 nginx 配置写入 / etc/nginx.conf 文件中,
4) 然后 reload 一下使配置生效. 以此达到域名分别配置和动态更新的问题.
二, Ingress-nginx 配置示例
注: 文中所有的 YAML 文件及镜像都可以通过我的网盘链接下载 https://pan.baidu.com/s/1-dPOqfxXqitSRU4OPHwd2w (httpd 和 tomcat 除外).
1, 搭建 registry 私有仓库 (可跳过配置私有仓库, 选择手动导入所需镜像到相应节点)
- # 运行 registry 私有仓库
- [[email protected] ~]# docker run -tid --name registry -p 5000:5000 --restart always registry
- [[email protected] ~]# VIM /usr/lib/systemd/system/docker.service #修改配置文件, 指定私有仓库
- ExecStart=/usr/bin/dockerd -H unix:// --insecure-registry 192.168.20.6:5000
- # 将修改后的文件发送到 k8s 群集中的其他节点
- [[email protected] ~]# scp /usr/lib/systemd/system/docker.service [email protected]:/usr/lib/systemd/system/
- docker.service 100% 1637 1.6KB/s 00:00
- [[email protected] ~]# scp /usr/lib/systemd/system/docker.service [email protected]:/usr/lib/systemd/system/
- # 在各个节点包括 master 上执行以下命令, 重启 docker, 使更改生效
- [[email protected] ~]# systemctl daemon-reload
- [[email protected] ~]# systemctl restart docker
- # 上传测试所需的镜像到私有仓库
- [[email protected] ~]# docker push 192.168.20.6:5000/httpd:v1
- [[email protected] ~]# docker push 192.168.20.6:5000/tomcat:v1
2, 创建 namespace(也可跳过, 使用默认的 default 名称空间也可以, 但需要删除下面所有 YAML 文件中关于自定义的名称空间的配置字段)
- [[email protected] ~]# kubectl create ns test-ns #创建名称空间 test-ns
- [[email protected] ~]# kubectl get ns #确认创建成功
3, 创建 Deployment,Service 资源对象
1) 创建 httpd 服务及其 service
- [[email protected] test]# VIM httpd-01.YAML #编写基于 httpd 服务的资源对象
- kind: Deployment
- apiVersion: extensions/v1beta1
- metadata:
- name: web01
- namespace: test-ns
- spec:
- replicas: 3
- template:
- metadata:
- labels:
- App: httpd01
- spec:
- containers:
- - name: httpd
- image: 192.168.20.6:5000/httpd:v1
- ---
- apiVersion: v1
- kind: Service
- metadata:
- name: httpd-svc
- namespace: test-ns
- spec:
- selector:
- App: httpd01
- ports:
- - protocol: TCP
- port: 80
- targetPort: 80
- [[email protected] test]# kubectl apply -f httpd-01.YAML #执行 YAML 文件
2) 创建 tomcat 服务及其 service
- [[email protected] test]# VIM tomcat-01.YAML #编写 YAML 文件如下
- kind: Deployment
- apiVersion: extensions/v1beta1
- metadata:
- name: web02
- namespace: test-ns
- spec:
- replicas: 3
- template:
- metadata:
- labels:
- App: tomcat01
- spec:
- containers:
- - name: tomcat
- image: 192.168.20.6:5000/tomcat:v1
- ---
- apiVersion: v1
- kind: Service
- metadata:
- name: tomcat-svc
- namespace: test-ns
- spec:
- selector:
- App: tomcat01
- ports:
- - protocol: TCP
- port: 8080
- targetPort: 8080
- [[email protected] test]# kubectl apply -f tomcat-01.YAML #执行 YAML 文件
3) 确认成功创建上述资源对象
- [[email protected] test]# kubectl get po -n test-ns #确定 pod 是正常运行状态
- NAME READY STATUS RESTARTS AGE
- web01-757cfc547d-fmjnt 1/1 Running 0 8m24s
- web01-757cfc547d-pjrrt 1/1 Running 0 9m30s
- web01-757cfc547d-v7tdb 1/1 Running 0 8m24s
- web02-57c46c759d-l9qzx 1/1 Running 0 4m9s
- web02-57c46c759d-vs6mg 1/1 Running 0 4m9s
- web02-57c46c759d-zknrw 1/1 Running 0 4m9s
- [[email protected] test]# kubectl get svc -n test-ns #确认 SVC 创建成功
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- httpd-svc ClusterIP 10.107.211.219 <none> 80/TCP 10m
- tomcat-svc ClusterIP 10.101.159.1 <none> 8080/TCP 5m8s
- # 访问 SVC 的 clusterIP + 端口, 确定可以访问到后端 Pod
- [[email protected] test]# curl -I 10.101.159.1:8080 #访问 tomcat
- HTTP/1.1 200 #返回状态码为 200
- Content-Type: text/HTML;charset=UTF-8
- Transfer-Encoding: chunked
- Date: Fri, 22 Nov 2019 12:34:32 GMT
- [[email protected] test]# curl -I 10.107.211.219:80 #访问 httpd
- HTTP/1.1 200 OK #状态码为 200
- Date: Fri, 22 Nov 2019 12:34:39 GMT
- Server: Apache/2.4.41 (Unix) #版本号也有
- Last-Modified: Sat, 16 Nov 2019 10:00:39 GMT
- ETag: "1a-59773c95e7fc0"
- Accept-Ranges: bytes
- Content-Length: 26
- Content-Type: text/HTML
- # 如果在上述访问测试中, 没有访问到相应的 pod, 建议使用 "kubectl describe svc" 命令,
- # 查看相应的 service 中的 Endpoints 列中有没有关联后端 pod.
4, 创建 Ingress-nginx 资源对象
下载 https://pan.baidu.com/s/1nS3ox4esAvfWHx5uC8M5yQ 我提供的镜像并导入到需要运行 Ingress-nginx 的节点上, 也可以选择自行下载其他镜像.
方法 1: 去 GitLab 搜索 Ingress-nginx, 点击 "deploy", 再点击页面下的跳转链接, 即可看到如下命令:
- # 不要直接复制命令到终端执行, 先将 YAML 文件下载下来
- [[email protected] test]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
- [[email protected] test]# VIM mandatory.YAML #修改其 YAML 文件
- # 修改以下内容
- spec: #这是在 212 行的 spec 字段
- hostNetwork: true #添加这行, 表示使用主机网络
- # wait up to five minutes for the drain of connections
- terminationGracePeriodSeconds: 300
- serviceAccountName: nginx-ingress-serviceaccount
- nodeSelector:
- Ingress: nginx #设置节点的标签选择器, 指定在哪台节点上运行
- containers:
- - name: nginx-ingress-controller
- image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.26.1
- # 上面是指定使用什么镜像, 若需要更改镜像名称, 改这里即可, 我保持默认.
- # 修改后保存退出即可
- [[email protected] test]# kubectl label nodes node01 Ingress=nginx
- # 对 node01 节点打相应的标签, 以便指定 Ingress-nginx 运行在 node01
- # 可以执行下面的命令, 查看 node01 的标签是否存在
- [[email protected] test]# kubectl get nodes node01 --show-labels
- # 在 node01 节点执行下面命令, 导入 Ingress-nginx 镜像 (自行上传这个包, 我提供的网盘链接中有)
- [[email protected] ~]# docker load <nginx-ingress-controller.0.26.1.tar
- # 手动将 ingress-nginx 镜像导入到 node01 节点
- # 回到 master 节点, 执行 YAML 文件
- [[email protected] test]# kubectl apply -f mandatory.YAML #执行 ingress-nginx 的 YAML 文件
关于上面 YAML 文件中写入的 "hostNetwork: true" 具体解释: 如果使用此网络参数, 那么 pod 中运行的应用程序可以直接使用 Node 节点端口, 这样 node 节点主机所在的网络的其他主机, 都可以通过访问到此应用程序.
确定 Ingress-nginx 的容器运行正常:
- [[email protected] test]# kubectl get pod -n ingress-nginx -o wide
- NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
- nginx-ingress-controller-77c8f6577b-6shdc 1/1 Running 0 107s 192.168.20.7 node01 <none> <none>
5, 定义 Ingress 规则 (编写 ingress 的 YAML 文件)
- [[email protected] test]# VIM ingress.YAML #编写 YAML 文件如下
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: test-ingress
- namespace: test-ns
- annotations:
- nginx.ingress.kubernetes.io/rewrite-target: /
- spec:
- rules:
- - host: www.test01.com
- http:
- paths:
- - path: /
- backend:
- serviceName: httpd-svc
- servicePort: 80
- - path: /tomcat
- backend:
- serviceName: tomcat-svc
- servicePort: 8080
- [[email protected] test]# kubectl apply -f ingress.YAML #执行 ingress 规则的 YAML 文件
- [[email protected] test]# kubectl get ingresses -n test-ns #查看 ingresses 规则资源对象
- NAME HOSTS ADDRESS PORTS AGE
- test-ingress www.test01.com 80 28s
其实, 至此已经实现了我们想要的功能, 现在就可以通过 www.test01.com 来访问到我们后端 httpd 容器提供的服务, 通过 www.test01.com/tomcat 来访问我们后端 tomcat 提供的服务, 当然, 前提是自行配置 DNS 解析, 或者直接修改 client 的 hosts 文件. 访问页面如下 (注意: 一定要自己解决域名解析的问题, 若不知道域名对应的是哪个 IP, 请跳过这两个图, 看下面的文字解释):
访问 httpd 服务 (首页内容是我自定义的):
访问 tomcat 服务:
在上面的访问测试中, 虽然访问到了对应的服务, 但是有一个弊端, 就是在做 DNS 解析的时候, 只能指定 Ingress-nginx 容器所在的节点 IP. 而指定 k8s 集群内部的其他节点 IP(包括 master) 都是不可以访问到的, 如果这个节点一旦宕机, Ingress-nginx 容器被转移到其他节点上运行 (不考虑节点标签的问题, 其实保持 Ingress-nginx 的 YAML 文件中默认的标签的话, 那么每个节点都是有那个标签的). 随之还要我们手动去更改 DNS 解析的 IP(要更改为 Ingress-nginx 容器所在节点的 IP, 通过命令 "kubectl get pod -n ingress-nginx -o wide" 可以查看到其所在节点), 很是麻烦.
有没有更简单的一种方法呢? 答案是肯定的, 就是我们为 Ingress-nginx 规则再创建一个类型为 nodePort 的 Service, 这样, 在配置 DNS 解析时, 就可以使用 www.test01.com 绑定所有 node 节点, 包括 master 节点的 IP 了, 很是灵活.
6, 为 Ingress 规则创建一个 Service
就在刚才找到 Ingress-nginx 的 YAML 文件的页面, 然后下拉页面, 即可看到以下, 可以根据 k8s 集群环境来选择适合自己的 YAML 文件, 假如自己是在 Azure 云平台搭建的 K8s 集群, 则选择复制 Azure 下面的命令即可, 我这里是自己的测试环境, 所以选择 Bare-metal 下面的 YAML 文件:
创建这个 Service 有两种方法, 一是直接复制其 Web 页面的命令到 master 节点上执行, 二是将其链接地址复制到终端使用 wget 下载下来再执行, 我选择第二种方法, 因为我想看看里面的内容:
- # 将其下载到本地
- [[email protected] test]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml
- [[email protected] test]# cat service-nodeport.YAML #仅仅是查看一下内容, 并不需要修改
- apiVersion: v1
- kind: Service
- metadata:
- name: ingress-nginx
- namespace: ingress-nginx
- labels:
- App.kubernetes.io/name: ingress-nginx
- App.kubernetes.io/part-of: ingress-nginx
- spec:
- type: NodePort
- ports:
- - name: http
- port: 80
- targetPort: 80
- protocol: TCP
- - name: https
- port: 443
- targetPort: 443
- protocol: TCP
- selector:
- App.kubernetes.io/name: ingress-nginx
- App.kubernetes.io/part-of: ingress-nginx
- ---
- [[email protected] test]# kubectl apply -f service-nodeport.YAML #执行下载下来的 YAML 文件
- [[email protected] test]# kubectl get svc -n ingress-nginx #查看运行的 service
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- ingress-nginx NodePort 10.109.106.246 <none> 80:30465/TCP,443:32432/TCP 56s
- # 可以看到 service 分别将 80 和 443 端口映射到了节点的 30645 和 32432 端口 (随机映射的, 也可以修改 YAML 文件指定端口).
至此, 这个 www.test01.com 的域名即可和群集中任意节点的 30465/32432 端口进行绑定了.
测试如下 (域名解析对应的 IP 可以是 k8s 群集内的任意节点 IP):
至此, 就实现了最初的需求.
基于虚拟主机的 Ingress 规则
如果现在是另一种需求, 我需要将 www.test01.com 和 www.test02.com 都对应上我后端的 httpd 容器提供的服务, 那么此时应该怎么配置?
配置如下 (基于上面的环境进行配置):
- [[email protected] test]# VIM ingress.YAML #修改 ingress 规则的 YAML 文件如下
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: test-ingress
- namespace: test-ns
- annotations:
- nginx.ingress.kubernetes.io/rewrite-target: /
- spec:
- rules:
- - host: www.test02.com #增加这一段 host 配置
- http:
- paths:
- - path: /
- backend:
- serviceName: httpd-svc #绑定和 www.test01 相同的 service 名字即可
- servicePort: 80
- - host: www.test01.com
- http:
- paths:
- - path: /
- backend:
- serviceName: httpd-svc
- servicePort: 80
- - path: /tomcat
- backend:
- serviceName: tomcat-svc
- servicePort: 8080
- # 增加完上述的 host 字段保存退出即可
- [[email protected] test]# kubectl apply -f ingress.YAML #重新执行 YAML 文件
至此, 即可实现访问 www.test01.com 和 www.test02.com 都可以访问到后端的 httpd 提供的页面 (自行解决域名解析问题), 如下:
访问 www.test01.com:30465
访问 www.test02.com:30465
总结上述示例的 pod 是如何一步一步可以使 client 访问到的, 总结如下:
后端 pod===》service====》ingress 规则 ====》写入 Ingress-nginx-controller 配置文件并自动重载使更改生效 ===》对 Ingress-nginx 创建 service====》实现 client 无论通过哪个 K8 节点的 IP + 端口都可以访问到后端 pod
三, 配置 HTTPS
在上面的操作中, 实现了使用 ingress-nginx 为后端所有 pod 提供一个统一的入口, 那么, 有一个非常严肃的问题需要考虑, 就是如何为我们的 pod 配置 CA 证书来实现 HTTPS 访问? 在 pod 中直接配置 CA 么? 那需要进行多少重复性的操作? 而且, pod 是随时可能被 kubelet 杀死再创建的. 当然这些问题有很多解决方法, 比如直接将 CA 配置到镜像中, 但是这样又需要很多个 CA 证书.
这里有更简便的一种方法, 就拿上面的情况来说, 后端有多个 pod,pod 与 service 进行关联, service 又被 ingress 规则发现并动态写入到 ingress-nginx-controller 容器中, 然后又为 ingress-nginx-controller 创建了一个 Service 映射到群集节点上的端口, 来供 client 来访问.
在上面的一系列流程中, 关键的点就在于 ingress 规则, 我们只需要在 ingress 的 YAML 文件中, 为域名配置 CA 证书即可, 只要可以通过 HTTPS 访问到域名, 至于这个域名是怎么关联到后端提供服务的 pod, 这就是属于 k8s 群集内部的通信了, 即便是使用 http 来通信, 也无伤大雅.
配置如下:
接下来的配置与上面的配置基本没什么关系, 但是由于上面已经运行了 Ingress-nginx-controller 容器, 所以这里就没有必要再运行了. 只需要配置 pod,service,ingress 规则即可.
- # 创建 CA 证书 (测试环境, 自己创建吧)
- [[email protected] https]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
- # 当前目录下会生成两个文件, 如下:
- [[email protected] https]# ls #确定当前目录下有这两个文件
- tls.crt tls.key
- # 将生成的 CA 证书存储到 etcd
- [[email protected] https]# kubectl create secret tls tls-secret --key=tls.key --cert tls.crt
- # 创建 deploy,service,ingress 资源对象
- [[email protected] https]# VIM httpd03.YAML #编写 YAML 文件
- kind: Deployment
- apiVersion: extensions/v1beta1
- metadata:
- name: web03
- spec:
- replicas: 2
- template:
- metadata:
- labels:
- App: httpd03
- spec:
- containers:
- - name: httpd3
- image: 192.168.20.6:5000/httpd:v1
- ---
- apiVersion: v1
- kind: Service
- metadata:
- name: httpd-svc3
- spec:
- selector:
- App: httpd03
- ports:
- - protocol: TCP
- port: 80
- targetPort: 80
- ---
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: test-ingress3
- spec:
- tls:
- - hosts:
- - www.test03.com
- secretName: tls-secret #这里是指定的是 etcd 存储的 CA 证书名称
- rules:
- - host: www.test03.com
- http:
- paths:
- - path: /
- backend:
- serviceName: httpd-svc3
- servicePort: 80
- [[email protected] https]# kubectl apply -f httpd03.YAML #执行 YAML 文件
确认创建的资源对象正常运行:
- [[email protected] https]# kubectl get svc #查看 svc
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- httpd-svc3 ClusterIP 10.98.180.104 <none> 80/TCP 31s
- kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19d
- [[email protected] https]# kubectl get pod #查看 pod
- NAME READY STATUS RESTARTS AGE
- web03-66dfbc8cf-w6vvp 1/1 Running 0 34s
- web03-66dfbc8cf-zgxd7 1/1 Running 0 34s
- [[email protected] https]# kubectl describe ingresses #查看 ingress 规则
- Name: test-ingress3
- Namespace: default
- Address: 10.109.106.246
- Default backend: default-http-backend:80 (<none>)
- TLS:
- tls-secret terminates www.test03.com
- Rules:
- Host Path Backends
- ---- ---- --------
- www.test03.com
- / httpd-svc3:80 (10.244.1.13:80,10.244.2.9:80)
- # 确定关联到对应的 service 及后端的 pod
https 访问测试:
使用 https://www.test03.com 进行访问 (自行解决域名解析问题)
来源: http://www.bubuko.com/infodetail-3301369.html