平台应用安全
- 1 平台应用安全
- 1.1 敏感数据
- 1.1.1 应用数据
- 1.1.2 Secret基础
- 1.1.3 Secret案例1
- 1.1.4 Secret案例2
- 1.2 数据访问
- 1.2.1 网络体系解读
- 1.2.2 Service实践
- 1.2.3 Service进阶
- 1.2.4 Service解读
- 1.3 应用流量
- 1.3.1 Ingress基础
- 1.3.2 Ingress实践
- 1.3.3 Ingress进阶
- 1.3.4 Ingress认证
- 1.3.5 Ingress高阶
- 1.3.6 Ingress扩展
1 平台应用安全
1.1 敏感数据
1.1.1 应用数据
学习目标
这一节,我们从 基础知识、资源对象、小结 三个方面来学习
基础知识
Docker存储
Docker的文件系统 与Docker容器具有相同的生命周期,但是Docker容器肯定会遇到同时运行到多节点场景中,这个时候,会因为节点崩溃、服务崩溃、网络原因,导致容器异常退出,所以一旦我们将数据存储到容器内部,肯定会导致数据丢失。
- Docker镜像是只读的文件,Docker容器可读可写,但是不能够数据持久化。
所以为了避免这种数据异常丢失的情况,我们一般会将 容器的数据(程序状态数据、应用存储数据等),存储在容器的文件系统之外。
- 通过引入外部的存储空间来实现 数据的可持久化效果。
存储方式
Docker的两种文件存储机制:
host存储方式
- 通过数据卷 或者 数据卷容器,将当前宿主机上面的文件系统目录与容器里面的工作目录进行一一的关联。
- 即使我们对本地是磁盘做 raid等冗余功能,但是这种等级的安全很鸡肋。
网络存储方式
- 通过网络的方式,将外部的存储空间挂载到当前宿主机,然后借助于host机制实现容器数据的可持久化。
资源对象
资源对象体系
k8s集群中的容器数据存储
k8s集群中的多pod中多容器间的数据存储机制,有两个关键点:
1 容器引擎对共享式存储设备的支持类型:
- 多路并行读写 - 多个容器内可以同时对同一个存储设备进行读写操作
- 多路只读 - 多个容器内可以同时对同一个存储设备进行只读操作
- 单路读写 - 多个容器内可以通过同一个中间容器对同一个存储设备进行读写操作
2 pod内部容器使用存储卷有两步:
- 在Pod上定义存储卷,并关联至目标存储服务上
- 在需要用到存储卷的容器上,挂载其所属的Pod的存储卷
资源对象属性
spec:
volumes:
- name <string> # 存储卷名称标识,仅可使用DNS标签格式的字符,在当前Pod中必须唯一
VOL_TYPE <Object> # 存储卷插件及具体的目标存储供给方的相关配置
containers:
- name: …
image: …
volumeMounts:
- name <string> # 要挂载的存储卷的名称,必须匹配存储卷列表中某项的定义
mountPath <string> # 容器文件系统上的挂载点路径
readOnly <boolean> # 是否挂载为只读模式,默认为“否”
subPath <string> # 挂载存储卷上的一个子目录至指定的挂载点
subPathExpr <string> # 挂载由指定的模式匹配到的存储卷的文件或目录至挂载点
小结
1.1.2 Secret基础
学习目标
这一节,我们从 属性解读、简单实践、小结 三个方面来学习
属性解读
如何为容器化应用提供配置信息?
1 启动容器时,直接向应用程序传递参数,args: []
2 将定义好的配置文件焙进镜像之中;
3 通过环境变量向容器传递配置数据:有个前提要求,应用得支持从环境变量加载配置信息;
4 制作镜像时,使用entrypoint脚本来预处理变量,常见的做法就是使用非交互式编辑工具,将环境变量的值替换到应用的配置文件中;
5 基于存储卷向容器传递配置文件;
注意:
对于运行容器中的配置改变,需要通过应用程序重载相关配置
k8s的配置管理
kubernetes提供了对pod中容器应用的集中配置管理组件:ConfigMap、Secret等。通过这些组件来实现向pod中的容器中注入配置信息的机制。
在k8s集群中,有一些核心的配置属性信息是非常敏感的,所以这些信息在传递的过程中,是不希望外人能够看到的,所以,k8s提供了一种加密场景中的配置管理资源对象 -- secret。
Secret的对象需要单独定义并创建,然后以数据卷的形式挂载到Pod中,Secret的数据将以文件的形式保存,容器通过读取文件可以获取需要的数据。
secret类型
类型 | 解析 |
---|---|
generic | 通用类型,基于base64编码的,长用来存储密码,公钥之类的。常见的子类型有: kubernetes.io/basic-auth、kubernetes.io/rbd、kubernetes.io/ssh-auth(比较重要) |
docker-registry | 专用于让kubelet启动Pod时从私有镜像仓库pull镜像时,首先认证到Registry时使用 |
tls | 专门用于保存tls/ssl用到证书和配对儿的私钥 |
命令解析
查看命令帮助
# kubectl create secret --help
Create a secret using specified subcommand.
Available Commands:
docker-registry 创建一个给 Docker registry 使用的 secret
generic Create a secret from a local file, directory, or literal value
tls 创建一个 TLS secret
Usage:
kubectl create secret [flags] [options]
Use "kubectl <command> --help" for more information about a given command.
结果显示:
secret 主要有三种类型 tls、generic、docker-registry
创建generic类型的secret
kubectl create secret generic --help
参数详解:
--from-file=[] 以配置文件的方式创建配置数据
--from-literal=[] 以命令行设置键值对的方式配置数据
创建tls类型的secret
kubectl create secret tls --help
参数详解:
--cert=path/to/cert/file 指定证书文件
--key=path/to/key/file 指定秘钥文件
创建docker-registry类型的secret
kubectl create secret docker-registry --help
参数详解:
--docker-username=user 指定登录用户
--docker-password=password 指定登录密码
--docker-server=string 指定docker仓库
--from-file=[key=]source 指定秘钥文件
简单实践
命令创建secret实践
kubectl create secret generic my-secret --from-literal=key1=supersecret --from-literal=key2=topsecret
资源对象方式创建secret
我们以用户名和密码的加密方式进行认证,首先对用户名和密码进行base64加密
]# echo -n 'admin' | base64
YWRtaW4=
]# echo -n 'password' | base64
cGFzc3dvcmQ=
注意:
-n的作用是仅仅传递字符串内容,而不包括字符串末尾的换行符
secret资源定义文件 01_kubernetes_application_secure_secret_data.yaml
apiVersion: v1
kind: Secret
metadata:
name: superopsmsb-secret
type: kubernetes.io/basic-auth
data:
username: YWRtaW4=
password: cGFzc3dvcmQ=
生成新的对象
kubectl apply -f 01_kubernetes_application_secure_secret_data.yaml
pod应用资源对象
创建资源对象 02_kubernetes_application_secure_secret_nginx_pod.yml
apiVersion: v1
kind: Pod
metadata:
name: superopsmsb-secret
spec:
volumes:
- name: secret-1
secret:
secretName: superopsmsb-secret
- name: secret-2
secret:
secretName: my-secret
containers:
- name: nginx-web
image: kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1
volumeMounts:
- name: secret-1
mountPath: /secret-1/
- name: secret-2
mountPath: /secret-2/
创建资源对象
kubectl apply -f 02_kubernetes_application_secure_secret_nginx_pod.yml
查看效果
[root@kubernetes-master1 /data/kubernetes/storage]# kubectl describe pod superopsmsb-secret
Name: superopsmsb-secret
...
Containers:
nginx-web:
...
Mounts:
/secret-1/ from secret-1 (rw)
/secret-2/ from secret-2 (rw)
...
Volumes:
secret-1:
Type: Secret (a volume populated by a Secret)
SecretName: superopsmsb-secret
Optional: false
secret-2:
Type: Secret (a volume populated by a Secret)
SecretName: my-secret
Optional: false
查看文件内容效果
]# kubectl exec -it superopsmsb-secret -- /bin/bash
root@superopsmsb-secret:/# ls /secret-1/
password username
root@superopsmsb-secret:/# ls /secret-2/
key1 key2
root@superopsmsb-secret:/# echo $(cat /secret-1/username)
admin
root@superopsmsb-secret:/# echo $(cat /secret-2/key1)
supersecret
小结
1.1.3 Secret案例1
学习目标
这一节,我们从 案例解读、简单实践、小结 三个方面来学习。
案例解读
需求
实践一个基于https来访问的nginx的web服务
准备工作
准备nginx容器的配置目录
[root@kubernetes-master1 /data/kubernetes/app_secure]# mkdir nginx-conf-tls
创建主配置文件
[root@kubernetes-master1 /data/kubernetes/app_secure]# vim nginx-conf-tls/superopsmsb.conf
server {
listen 443 ssl;
server_name www.superopsmsb.com;
ssl_certificate /etc/nginx/certs/tls.crt;
ssl_certificate_key /etc/nginx/certs/tls.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
include /etc/nginx/conf.d/myserver-*.cfg;
location / {
root /usr/share/nginx/html;
}
}
server {
listen 80;
server_name www.superopsmsb.com;
return 301 https://$host$request_uri;
}
创建证书
准备nginx容器的配置目录
[root@kubernetes-master1 /data/kubernetes/app_secure]# mkdir tls-key
做证书
[root@kubernetes-master1 /data/kubernetes/app_secure]# openssl genrsa -out tls-key/tls.key 2048
做成自签证书
[root@kubernetes-master1 /data/kubernetes/app_secure]# openssl req -new -x509 -key tls-key/tls.key -out tls-key/tls.crt -subj /C=CN/ST=Beijing/L=Beijing/O=DevOps/CN=www.superopsmsb.com
注意:
域名必须是我们使用的域名信息
简单实践
创建资源对象文件
创建cm资源对象
kubectl create configmap nginx-ssl-conf --from-file=nginx-conf-tls/
创建secret资源对象
kubectl create secret tls nginx-ssl-secret --cert=tls-key/tls.crt --key=tls-key/tls.key
创建应用资源对象
创建资源配置文件 03_kubernetes_application_secure_secret_cm_nginx_pod.yml
apiVersion: v1
kind: Pod
metadata:
name: superops-nginx-ssl
spec:
containers:
- image: kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1
name: nginx-web
volumeMounts:
- name: nginxcerts
mountPath: /etc/nginx/certs/
readOnly: true
- name: nginxconfs
mountPath: /etc/nginx/conf.d/
readOnly: true
volumes:
- name: nginxcerts
secret:
secretName: nginx-ssl-secret
- name: nginxconfs
configMap:
name: nginx-ssl-conf
optional: false
应用资源配置文件
kubectl apply -f 03_kubernetes_application_secure_secret_cm_nginx_pod.yml
检查效果
]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE ...
superops-nginx-ssl 1/1 Running 0 79s 10.244.2.6 node2
使用https测试
]# curl -H "Host:www.superopsmsb.com" https://10.244.2.6
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
结果提示:
由于是自签证书,所以默认不允许通过,我们可以通过 -k的方式,让它忽略警告。
]# curl -k -H "Host:www.superopsmsb.com" https://10.244.2.6
Hello Nginx, superops-nginx-ssl-1.23.0
使用http访问
]# curl http://10.244.2.6
<html>
<head><title>301 Moved Permanently</title></head>
...
结果显式:
这个 301 就是我们定制的返回信息。
小结
1.1.4 Secret案例2
学习目标
这一节,我们从 案例解读、简单实践、小结 三个方面来学习
案例解读
简介
截止目前,我们的镜像仓库是以http的方式来存在的,为了方便所有位置都可以正常的下载仓库镜像,所以我们将镜像仓库设置为了公开的。而对于一些私密性的镜像或者说敏感性的镜像来说,我们需要建立一个私有的仓库,在获取镜像的时候,需要通过密码方式才可以下载。
准备工作
在docker harbor中创建一个私有的仓库 register。
在A主机上提交镜像到私有仓库
docker tag kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1 kubernetes-register.superopsmsb.com/register/nginx_web:v0.1
docker push kubernetes-register.superopsmsb.com/register/nginx_web:v0.1
在B主机上下载私有仓库镜像
[root@kubernetes-node2 ~]# docker pull kubernetes-register.superopsmsb.com/register/nginx_web:v0.1
Error response from daemon: unauthorized: unauthorized to access repository: register/nginx_web, action: pull: unauthorized to access repository: register/nginx_web, action: pull
结果显示:
无法从私有镜像仓库中获取镜像
简单实践
命令实践
创建docker-registry类型的secret
kubectl create secret docker-registry --help
参数详解:
--docker-username=user 指定登录用户
--docker-password=password 指定登录密码
--docker-server=string 指定docker仓库
--from-file=[key=]source 指定秘钥文件
创建secret
创建secret对象
kubectl create secret docker-registry docker-secret --docker-server=kubernetes-register.superopsmsb.com --docker-username=shuji --docker-password=A12345678a
应用secret
创建资源对象 04_kubernetes_application_secure_secret_docker_pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: superops-docker
spec:
containers:
- image: kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1
name: nginx-web
imagePullSecrets:
- name: docker-secret
使用资源对象
kubectl apply -f 04_kubernetes_application_secure_secret_docker_pod.yaml
查看效果
[root@kubernetes-master1 /data/kubernetes/app_secure]# kubectl describe pod superops-docker
Name: superops-docker
...
Containers:
nginx-web:
Container ID: docker://6cb4b5486a23b5441372579ea2775ce21308007ac9b1dbd19e6d9d92aa65dd5a
Image: kubernetes-register.superopsmsb.com/register/nginx_web:v0.1
Image ID: docker-pullable://kubernetes-register.superopsmsb.com/register/nginx_web@sha256:4ad6f78ef6996fb7f239b9cfa00891ec8a14a23fb981cc9064c830890143b02c
结果显示:
可以从通过secret的方式从私有仓库中获取镜像
小结
1.2 数据访问
1.2.1 网络体系解读
学习目标
这一节,我们从 应用流程、细节解读、小结 三个方面来学习
应用流程
入口
Kubernetes 的 Service定义了一个服务的访问入口地址,前端的应用Pod通过Service访问其背后一组有Pod副本组成的集群示例,Service通过Label Selector访问指定的后端Pod,RC保证Service的服务能力和服务质量处于预期状态。
Service是Kubernetes中最高一级的抽象资源对象,每个Service提供一个独立的服务,集群Service彼此间使用TCP/IP进行通信,将不同的服务组合在一起运行起来,就行了我们所谓的"系统",效果如下图
service
service是Kubernetes里最核心的资源对象之一,每一个Service都是一个完整的业务服务,我们之前学到的Pod、RC、Deployment等资源对象都是为Service服务的。他们之间的关系如下图:
细节解读
service模式解析
service类型
对于k8s来说,内部服务的自由通信可以满足我们环境的稳定运行,但是我们作为一个平台,其核心功能还是将平台内部的服务发布到外部环境,那么在k8s环境平台上,Service主要有四种样式来满足我们的需求,种类如下:
ClusterIP
这是service默认的服务暴露模式,主要针对的对象是集群内部。
NodePort
在ClusterIP的基础上,以<NodeIP>:<NodePort>方式对外提供服务,默认端口范围沿用Docker初期的随机端口范围 30000~32767,但是NodePort设定的时候,会在集群所有节点上实现相同的端口。
LoadBalancer
基于NodePort之上,使用运营商负载均衡器方式实现对外提供服务底层是基于IaaS云创建一个k8s云,同时该平台也支持LBaaS产品服务。
ExternalName
当前k8s集群依赖集群外部的服务,那么通过externalName将外部主机引入到k8s集群内部外部主机名以 DNS方式解析为一个 CNAME记录给k8s集群的其他主机来使用这种Service既不会有ClusterIP,也不会有NodePort.而且依赖于内部的CoreDNS功能
资源属性
apiVersion: v1
kind: Service
metadata:
name: …
namespace: …
labels:
key1: value1
key2: value2
spec:
type <string> # Service类型,默认为ClusterIP
selector <map[string]string> # 等值类型的标签选择器,内含“与”逻辑
ports: # Service的端口对象列表
- name <string> # 端口名称
protocol <string> # 协议,目前仅支持TCP、UDP和SCTP,默认为TCP
port <integer> # Service的端口号
targetPort <string> # 后端目标进程的端口号或名称,名称需由Pod规范定义
nodePort <integer> # 节点端口号,仅适用于NodePort和LoadBalancer类型
clusterIP <string> # Service的集群IP,建议由系统自动分配
externalTrafficPolicy <string> # 外部流量策略处理方式,Local表示由当前节点处理,Cluster表示向集群范围调度
loadBalancerIP <string> # 外部负载均衡器使用的IP地址,仅适用于LoadBlancer
externalName <string> # 外部服务名称,该名称将作为Service的DNS CNAME值
小结
1.2.2 Service实践
学习目标
这一节,我们从 资源解读、NodePort实践、小结 三个方面来学习
资源解读
准备工作
创建部署资源对象
kubectl create deployment nginx --image=kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1 --replicas=3
创建资源对象
资源对象文件 05_kubernetes_application_secure_svc_clusterIP.yml
apiVersion: v1
kind: Service
metadata:
name: superopsmsb-nginx-service
spec:
selector:
app: nginx
ports:
- name: http
port: 80
应用资源清单文件
kubectl apply -f 05_kubernetes_application_secure_svc_clusterIP.yml
确认资源
kubectl get svc superopsmsb-nginx-service
kubectl describe svc superopsmsb-nginx-service
访问效果
]# curl 10.100.79.167
Hello Nginx, nginx-756889d9f-fq5nh-1.23.1
清理环境
kubectl delete -f 05_kubernetes_application_secure_svc_clusterIP.yml
NodePort实践
简介
NodePort会在所有的节点主机上,暴露一个指定或者随机的端口,供外部的服务能够正常的访问pod内部的资源。
简单实践
资源对象文件 06_kubernetes_application_secure_svc_nodePort.yml
apiVersion: v1
kind: Service
metadata:
name: superopsmsb-nginx-nodeport
spec:
selector:
app: nginx
type: NodePort
ports:
- name: http
port: 80
nodePort: 30080
应用资源清单文件
kubectl apply -f 06_kubernetes_application_secure_svc_nodePort.yml
确认资源
kubectl get svc superopsmsb-nginx-nodeport
kubectl describe svc superopsmsb-nginx-nodeport
访问效果
]# curl 10.0.0.12:30080
Hello Nginx, nginx-756889d9f-xjtrb-1.23.1
清理环境
kubectl delete -f 06_kubernetes_application_secure_svc_nodePort.yml
小结
1.2.3 Service进阶
学习目标
这一节,我们从 本地流量、会话粘滞、小结 三个方面来学习
本地流量
场景
默认情况下,当一个svc的模式为NodePort类型的时候,他会在所有的集群节点上开放一个相同的端口,但是因为svc默认访问的效果是轮训,所以,当我们在指定一个具体node节点来进行访问的话,它也会自动跳转到其他主机。
查看pod对象
]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-756889d9f-fq5nh 1/1 Running 0 13m
nginx-756889d9f-kcn5c 1/1 Running 0 13m
nginx-756889d9f-xjtrb 1/1 Running 0 13m
查看svc对象
]# kubectl get svc superopsmsb-nginx-nodeport
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
superopsmsb-nginx-nodeport NodePort 10.110.220.50 <none> 80:30080/TCP 2m13s
测试效果
]# > /tmp/svc.txt
]# for i in {1..100}; do curl -s 10.0.0.16:30080 >> /tmp/svc.txt; done
]# for i in fq5nh kcn5c xjtrb; do grep $i /tmp/svc.txt | wc -l; done
40
28
32
结果显示:
可以看到,svc代理的多个pod是以均匀的方式进行代理的。
需求
如果指定的节点上有对应的pod服务,那么就没有必要来回跳转了,直接用本地流量就可以很好的提高业务的访问效率。service 的提供了一个属性可以定制流量的使用模式:
externalTrafficPolicy <string> # 外部流量策略处理方式
- Local表示由当前节点处理,如果当前节点出现问题,则导致访问异常
- Cluster表示向集群范围调度
默认情况下,svc的流量调度采用的是集群范围调度
]# kubectl get svc superopsmsb-nginx-nodeport -o yaml | grep TrafficPolicy
externalTrafficPolicy: Cluster
internalTrafficPolicy: Cluster
调整为本地流量调度
资源对象文件 07_kubernetes_application_secure_svc_local.yml
apiVersion: v1
kind: Service
metadata:
name: superopsmsb-nginx-local
spec:
selector:
app: nginx
type: NodePort
externalTrafficPolicy: Local
ports:
- name: http
port: 80
nodePort: 30080
应用资源清单文件
kubectl apply -f 07_kubernetes_application_secure_svc_local.yml
确认资源
kubectl get svc superopsmsb-nginx-local
kubectl describe svc superopsmsb-nginx-local
访问效果
]# > /tmp/svc.txt
]# for i in {1..100}; do curl -s 10.0.0.16:30080 >> /tmp/svc.txt; done
]# for i in fq5nh kcn5c xjtrb; do grep $i /tmp/svc.txt | wc -l; done
100
0
0
结果显示:
使用的是本地流量了
清理环境
kubectl delete -f 07_kubernetes_application_secure_svc_local.yml
会话粘滞
场景
kubernetes的Service功能可以通过多种方式将pod对象提供的服务对外开放,但是当pod多的时候,用户访问service的时候,service默认是按照轮训的情况进行转发,如果我们的用户请求由一定的会话要求,即希望同一个客户能访问一个pod的时候,我们可以使用service的affinity机制来实现,它能将同一个客户端的请求始终转发至同一个后端Pod对象,它是由kube-proxy的ipvs机制来实现的。
默认情况下,service所提供的会话粘性效果默认在10800s(3小时)后会重新调度,而且仅能基于客户端IP进行识别,调度粒度较粗,不推荐使用
属性解析
属性信息
spec.sessionAffinity,定义粘性会话的类型,可为 None 和 ClientIP
spec.sessionAffinityConfig.clientIP,配置会话保持时长,默认10800s 范围 1-86400
配置样式
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
调整为本地流量调度
资源对象文件 08_kubernetes_application_secure_svc_session.yml
apiVersion: v1
kind: Service
metadata:
name: superopsmsb-nginx-session
spec:
selector:
app: nginx
sessionAffinity: ClientIP
ports:
- name: http
port: 80
nodePort: 30080
应用资源清单文件
kubectl apply -f 08_kubernetes_application_secure_svc_session.yml
确认资源
kubectl get svc superopsmsb-nginx-session
kubectl describe svc superopsmsb-nginx-session
访问效果
]# > /tmp/svc.txt
]# for i in {1..100}; do curl -s 10.105.65.217 >> /tmp/svc.txt; done
]# for i in fq5nh kcn5c xjtrb; do grep $i /tmp/svc.txt | wc -l; done
0
100
0
结果显示:
应用层面使用的是会话粘滞效果了
清理环境
kubectl delete -f 08_kubernetes_application_secure_svc_session.yml
小结
1.2.4 Service解读
学习目标
这一节,我们从 ClusterIP原理、NodePort原理、小结 三个方面来学习
ClusterIP原理
简介
ClusterIP 是service默认的服务暴露模式,主要针对的对象是集群内部。是集群外部流量引入集群的首要方式。
这一节,我们好好的说一下ClusterIP的访问原理,我们主要以 iptables的模式来进行解析。
iptables代理模式下的ClusterIP,每个Service在每个节点上(由kube-proxy负责生成)都会生成相应的iptables规则
iptables规则解析
规则名称 | 规则解析 |
---|---|
KUBE-SERVICES | 包含所有ClusterIP类型的Service的流量匹配规则,由PREROUTING和OUTPUT两个内置链直接调用; 每个Service对象包含两条规则定义: 对于所有发往Service(目标IP为Service_IP且目标端口为Service_Port)的请求报文 前一条用于为那些非源自Pod网络(! -s 10.244.0.0/16)中请求报文借助于KUBE-MARQ-MASK自定义链中的规则打上特有的防火墙标记 后一条负责将所有报文转至专用的以KUBE-SVC为名称前缀的自定义链,后缀是Service信息hash值。 |
KUBE-MARK-MASQ | 专用目的自定义链,所有转至该自定义链的报文都将被置入防火墙标记(0x4000)以便于将特定的类型的报文定义为单独的分类 目的在将该类报文转发到目标端点之前由POSTROUTING规则链进行源地址转换。 |
KUBE-SVC- | 定义一个服务的流量调度规则,它通过随机调度算法(RANDOM)将请求分发给该Service的所有后端端点 每个后端端点定义在以KUBE-SEP为前缀名称的自定链上,后缀是端点信息的hash值。 |
KUBE-SEP- | 定义一个端点相关的流量处理规则,它通常包含两条规则 前一条用于为那些源自该端点自身(-s ep_ip)的请求流量调用自定义链KUBE-MARQ-MASK打上防火墙标记 后一条负责将发往该端点的所有流量进行目标IP地址和端口转换,新目标为该端点的IP和端口(-j DNAT --to-destination ep_ip:ep_port)。 |
KUBE-POSTROUTING | 专用的自定义链,由内置链POSTROUTING无条件调用,负责将拥有特有防火墙标记0x4000的请求报文进行源地址转换(Target为实现地址伪装的MASQUERADE),新的源地址为报文离开协议栈时流经接口的主IP(primary ip)地址。 |
KUBE-NODEPORTS | 当采用基于ClusterIP的NodePort模型的时候,KUBE-SERVICES会把请求转发给KUBE-NODEPORTS,由KUBE-NODEPORTS将信息转交给KUBE-SVC-规则 |
注意:关于iptables的防火墙规则顺序,几乎上每个版本都会发生一些改动
环境准备
准备默认的环境资源对象
kubectl create deployment my-nginx --image=kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1 --replicas=3
kubectl expose deployment my-nginx --port 80
查看svc和pod的基本效果
]# kubectl get svc my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx ClusterIP 10.110.9.95 <none> 80/TCP 3m30s
[root@kubernetes-master1 /data/kubernetes/auth_secure]# kubectl get pod -o wide
NAME READY ... IP NODE ...
my-nginx-d46c77b9b-c98zg 1/1 ... 10.244.4.14 kubernetes-node2 ...
my-nginx-d46c77b9b-scwgm 1/1 ... 10.244.5.6 kubernetes-node3 ...
my-nginx-d46c77b9b-z67sw 1/1 ... 10.244.3.10 kubernetes-node1 ...
在node节点上分析iptables的流程
第一条 OUTPUT链 和 PREROUTING
[root@kubernetes-node2 ~]# iptables -t nat -S OUTPUT
-P OUTPUT ACCEPT
-A OUTPUT -m comment --comment "cali:tVnHkvAo15HuiPy0" -j cali-OUTPUT
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
[root@kubernetes-node2 ~]# iptables -t nat -S PREROUTING
-P PREROUTING ACCEPT
-A PREROUTING -m comment --comment "cali:6gwbT8clXdHdC1b1" -j cali-PREROUTING
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
结果显式:
所有的数据都交给 KUBE-SERVICES 链处理
- 本机的数据来源 :独立的docker容器、Pod、Process等
- 其他主机的数据来源,任意对象
第二条 KUBE-SERVICES链 (自定义链)
[root@kubernetes-node2 ~]# iptables -t nat -S KUBE-SERVICES
确定与测试svc有关系的防火墙规则
[root@kubernetes-node2 ~]# iptables -t nat -S KUBE-SERVICES | grep my-nginx
-A KUBE-SERVICES -d 10.110.9.95/32 -p tcp -m comment --comment "default/my-nginx cluster IP" -m tcp --dport 80 -j KUBE-SVC-L65ENXXZWWSAPRCR
结果显示:
这是一条自定义规则链
只要是发往 service网址(10.110.9.95)的数据包都转交给 KUBE-SVC-L65ENXXZWWSAPRCR 链
第三条 KUBE-SVC-<HASH>链(针对service与后端endpoint的负载均衡法则)
[root@kubernetes-node2 ~]# iptables -t nat -S KUBE-SVC-L65ENXXZWWSAPRCR
-N KUBE-SVC-L65ENXXZWWSAPRCR
-A KUBE-SVC-L65ENXXZWWSAPRCR ! -s 10.244.0.0/16 -d 10.110.9.95/32 -p tcp -m comment --comment "default/my-nginx cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SVC-L65ENXXZWWSAPRCR -m comment --comment "default/my-nginx -> 10.244.3.10:80" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-5U6QNRMHL7SGMPSD
-A KUBE-SVC-L65ENXXZWWSAPRCR -m comment --comment "default/my-nginx -> 10.244.4.14:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-IW6DUZ27KH7RMJIV
-A KUBE-SVC-L65ENXXZWWSAPRCR -m comment --comment "default/my-nginx -> 10.244.5.6:80" -j KUBE-SEP-XLZEZNQVHC5A6ZHD
结果显示:
对于源地址不是k8s集群内部的网段(! -s 10.244.0.0/16),同时目标地址是service地址(-d 10.110.9.95/32)的请求进行转发(-j KUBE-MARK-MASQ)
对于通过第一条规则的正常请求进行转发,模式是随机(--mode random)
对于不同后端endpoint的转发,第一个节点的流量是1/3, 对于第二个后端是剩余流量的1/2,最终可以保证所有节点流量负载是1:1
由于第三个节点没有在当前节点,在其他主机节点,所以需要进行转发(-j KUBE-SEP-XLZEZNQVHC5A6ZHD)
注意:
这三条负载均衡规则是按照 node1-node2-node3 的顺序排列的
第四条 KUBE-MARK-MASQ 链
[root@kubernetes-node2 ~]# iptables -t nat -S KUBE-MARK-MASQ
-N KUBE-MARK-MASQ
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
[root@kubernetes-node2 ~]# iptables -t nat -L KUBE-MARK-MASQ
Chain KUBE-MARK-MASQ (22 references)
target prot opt source destination
MARK all -- anywhere anywhere MARK or 0x4000
结果显示:
这是一条自定义规则链
该规则的目的就是为当前规则的请求,添加一个特殊的标签,然后运行所有数据通过
第五条 KUBE-SEP-<HASH> 链 (后端endpoint地址的请求处理)
本机endpoint转发规则
[root@kubernetes-node2 ~]# iptables -t nat -S KUBE-SEP-IW6DUZ27KH7RMJIV
-N KUBE-SEP-IW6DUZ27KH7RMJIV
-A KUBE-SEP-IW6DUZ27KH7RMJIV -s 10.244.4.14/32 -m comment --comment "default/my-nginx" -j KUBE-MARK-MASQ
-A KUBE-SEP-IW6DUZ27KH7RMJIV -p tcp -m comment --comment "default/my-nginx" -m tcp -j DNAT --to-destination 10.244.4.14:80
其他主机endpoint转发规则
[root@kubernetes-node2 ~]# iptables -t nat -S KUBE-SEP-5U6QNRMHL7SGMPSD
-N KUBE-SEP-5U6QNRMHL7SGMPSD
-A KUBE-SEP-5U6QNRMHL7SGMPSD -s 10.244.3.10/32 -m comment --comment "default/my-nginx" -j KUBE-MARK-MASQ
-A KUBE-SEP-5U6QNRMHL7SGMPSD -p tcp -m comment --comment "default/my-nginx" -m tcp -j DNAT --to-destination 10.244.3.10:80
结果显示:
这些规则都是自定义规则链,规则中主要做两件事情:
1 如果源地址(-s 10.244.4.14/32)是当前节点自己,那么就交给KUBE-MARK-MASQ链处理,
2 如果是源地址是其他节点对象,那么就做DNAT处理,由当前资源对象(pod)来处理
第六条 POSTROUTING + KUBE-POSTROUTING链 (信息的处理)
[root@kubernetes-node2 ~]# iptables -t nat -S POSTROUTING
-P POSTROUTING ACCEPT
-A POSTROUTING -m comment --comment "cali:O3lYWMrLQYEMJtB5" -j cali-POSTROUTING
-A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
[root@kubernetes-node2 ~]# iptables -t nat -S KUBE-POSTROUTING
-N KUBE-POSTROUTING
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
结果显式:
POSTROUTING
所有请求交给KUBE-POSTROUTING自定义链来处理
KUBE-POSTROUTING
对所有不携带mark标识的数据,直接RETURN(因为是子链,所以是上级规则的下一条,即POSTROUTING的第二条规则)
如果请求携带mark特殊标识,先将当前的mark标识置为0,然后对源地址进行转换的地址伪装,便于请求的后续处理
流程小结
一个 Service会在任何一台主机上生成 至少14条规则,后端pod多的话,规则更多,所以大量service场景下,iptabels规则还是相当多的,性能可能会收到一定程度的影响。
默认的调度算法是均衡的随机调度算法。
NodePort原理
环境准备
切换成nodeport类型
kubectl delete svc my-nginx
kubectl expose deployment my-nginx --port=80 --type=NodePort
确认效果
]# kubectl get svc my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx NodePort 10.107.70.222 <none> 80:31519/TCP 37s
注意:
不要使用本地流量externalTrafficPolicy: Local,不然的话转发规则你懂得
在node节点上分析iptables的流程
第一条 OUTPUT链 和 PREROUTING
[root@kubernetes-node2 ~]# iptables -t nat -S OUTPUT
-P OUTPUT ACCEPT
-A OUTPUT -m comment --comment "cali:tVnHkvAo15HuiPy0" -j cali-OUTPUT
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
[root@kubernetes-node2 ~]# iptables -t nat -S PREROUTING
-P PREROUTING ACCEPT
-A PREROUTING -m comment --comment "cali:6gwbT8clXdHdC1b1" -j cali-PREROUTING
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
结果显式:
所有的数据都交给 KUBE-SERVICES 链处理
- 本机的数据来源 :独立的docker容器、Pod、Process等
- 其他主机的数据来源,任意对象
第二条 KUBE-SERVICES链 (自定义链)
[root@kubernetes-node2 ~]# iptables -t nat -S KUBE-SERVICES
-N KUBE-SERVICES
...
-A KUBE-SERVICES -d 10.107.70.222/32 -p tcp -m comment --comment "default/my-nginx cluster IP" -m tcp --dport 80 -j KUBE-SVC-L65ENXXZWWSAPRCR
...
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
结果显示:
如果需要走 ClusterIP 的话,直接走第一条规则,由于我们这里准备走的是 NodePort类型,所以走的是最后一条,另外如果所有的kube-service规则都没有匹配到的话,也会走最后一条,跳转到 KUBE-NODEPORTS 链进行处理
注意:
虽然我们故意将最佳使用本地流量的 externalTrafficPolicy: Local 属性取消了,但是默认值仍然是它。因为没有特殊的指定externalTrafficPolicy,所以基于cluster全局的调度策略进行流量的指定,一旦设定了这条属性,则会在KUBE-NODEPORTS内部和 KUBE-SVC-XXX 之间,增加一个 KUBE-XLB-XXX 的本地调度策略
第三条 KUBE-NODEPORTS链(针对service与后端endpoint的负载均衡法则)
[root@kubernetes-node2 ~]# iptables -t nat -S KUBE-NODEPORTS
-N KUBE-NODEPORTS
-A KUBE-NODEPORTS -p tcp -m comment --comment "kubernetes-dashboard/kubernetes-dashboard" -m tcp --dport 30443 -j KUBE-EXT-CEZPIJSAUFW5MYPQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx" -m tcp --dport 31519 -j KUBE-EXT-L65ENXXZWWSAPRCR
结果显示:
每一个nodeport类型的service都会在这里生成一条规则
第四条 KUBE-SEP-<HASH> 链 (后端endpoint地址的请求处理)
[root@kubernetes-node2 ~]# iptables -t nat -S KUBE-EXT-L65ENXXZWWSAPRCR
-N KUBE-EXT-L65ENXXZWWSAPRCR
-A KUBE-EXT-L65ENXXZWWSAPRCR -m comment --comment "masquerade traffic for default/my-nginx external destinations" -j KUBE-MARK-MASQ
-A KUBE-EXT-L65ENXXZWWSAPRCR -j KUBE-SVC-L65ENXXZWWSAPRCR
结果显示:
将相关流量转发给了KUBE-MARK-MASQ 或者 转发给 KUBE-SEP-XXX了
后续的 KUBE-MARK-MASQ 链、POSTROUTING链、KUBE-POSTROUTING链的操作与clusterIP的效果就一致了
一个 Service会在任何一台主机上生成 至少 15条规则,后端pod多的话,规则更多,所以大量service场景下,iptabels规则还是相当多的,性能可能会收到一定程度的影响。
默认的调度算法是均衡的随机调度算法。
关于 NodePort(携带externalTrafficPolicy属性) 的规则 和 ExternalIP 等模型的出口方式,大家自己来梳理
小结
1.3 应用流量
1.3.1 Ingress基础
学习目标
这一节,我们从 场景需求、方案解读、小结 三个方面来学习。
场景需求
简介
在实际的应用中,kubenetes接受的不仅仅有内部的流量,还有外部流量,我们可以通过两种方式实现将集群外部的流量引入到集群的内部中来,从而实现外部客户的正常访问。
service方式:
nodePort、externalIP 等service对象方式,借助于 namespace、iptables、ipvs 等代理模式实现流量的转发。
Host方式:
通过node节点的 hostNetwork 与 hostPort 及配套的port映射,以间接的方式实现service的效果
k8s网络
在业内有一个不成文的说法,就是:
将集群内部 pod之类对象 之间的网络通信称为 东西向流量;
将集群外部应用 和 集群内部之间对象 之间的网络通信 称为 南北向流量。
南入↓ -- ingress
北出↑ -- egress
问题
1 在实际的应用中,虽然我们可以基于service或host等多种方式实现集群内外的网络通信,但是这些内容都是基于四层协议来进行调度的,而且服务级别的健康检查功能很难实现。
2 而对于外部流量的接入,一般都是http(s)协议的数据,四层协议是无法实现的。尤其是涉及到各种ssl会话的管理,由于其位于osi七层模型的会话层,要高于传输层的四层,service和host是无法正常实现的。
3 虽然service能够实现将外部流量引入到集群内部,但是其本质上,是通过网络规则方式来进行转发的,形象的说法就是在墙上打洞,将集群内部的服务暴露到外部。甚至这些洞和洞之间根本没有关联关系。
方案
为了解决上述的问题,就引入了一种新的解决方法ingress,简单来说,它可以解决上面提出的三个问题:
1 可以基于https的方式,将集群外部的流量统一的引入到集群内部
2 通过一个统一的流量入口,避免将集群内部大量的"洞"暴露给外部。
ingress这种利用应用层协议来进行流量的负载均衡效果,它可以实现让用户通过域名来访问相应的service就可以了,无需关心Node IP及Port是什么,避免了信息的泄露。
ingress 是k8s上的一种标准化资源格式,它为了剥离与特定负载均衡功能实现软件的相关性,而抽象出来的一种公共的统一的声明式配置格式。然后借助于特定的ingress controller功能负责将其加载并转换成k8s集群内部的配置信息。
ingress的特点符合我们之前对于CRD的学习 - ingress + controller,所以,其具备了动态更新并加载新配置的特性。而且ingress本身是不具备实现集群内外流量通信的功能的,这个功能是通过 controller来实现的。
方案解读
实现
Ingress由任何具有反向代理功能的程序实现,如Nginx、Traefik、Envoy、HAProxy、Vulcand等,Ingress本身是运行于集群中的Pod资源对象。
ingress 主要包含两个组件Ingress Controller和Ingress。
组件 | 解析 |
---|---|
Ingress | 将Nginx的配置抽象成一个Ingress对象,每个服务对应一个ingress的yaml配置文件 |
Ingress Controller | 将新加入的Ingress转化成Nginx的配置文件并使之生效 |
问题梳理
如果我们将ingress 以pod的方式部署到k8s集群内部,那么就会遇到多个问题:
1 ingress的pod如何引入外部流量
- 通过一个专用的service就可以实现了
2 ingress的pod如何把流量负载均衡到其他pod
- 关于pod负载均衡的流量,直接通过controller转发给后端pod即可。
3 后端应用的pod很多,如何知道谁是我们要转发的目标?
- 通过k8s的server对所有的pod进行分组管理,借助于controller内部的负载均衡配置,找到对应的目标。
原理解析
Ingress是授权入站连接到达集群服务的规则集合。
从外部流量调度到nodeport上的service
从service调度到ingress-controller
ingress-controller根据ingress[Pod]中的定义(虚拟主机或者后端的url)
根据虚拟主机名直接调度到后端的一组应用pod中
注意:
整个流程中涉及到了两处service内容
service ingress-nginx 是帮ingress controller接入外部流量的
虚线表示service对后端的应用进行分组,实现是ingress实际的访问流程
常见的解决方案
对于ingress controller的软件实现,其实没有特殊的要求,只要能够实现七层的负载均衡功能效果即可,各种组织对于ingress的controller虽然实现方式不同,但是功能基本上一样,常见的实现方式有:
Ingress-Nginx: Kong
HAProxy
性能相对于nginx来说,较稳定
Envoy:
基于C++语言开发,本身就为动态环境设计的,支持配置动态加载的XDS API
不能像ingress-nginx一样,直接加载配置文件并动态加载转换成对应的应用配置,所以需要借助于其他控制平面工具来实现。
常见解决方案:Contour, Gloo
Traefik
注意:ingress 自从 1.6版本引入后,在1.19 正式进入了一个稳定版本状态。
小结
1.3.2 Ingress实践
学习目标
这一节,我们从 环境部署、简单实践、小结 三个方面来学习。
环境部署
参考资料:https://github.com/kubernetes/ingress-nginx
安装文档:https://kubernetes.github.io/ingress-nginx/deploy/
配置文件解析
apiVersion: networking.k8s.io/v1 # 资源所属的API群组和版本
kind: Ingress # 资源类型标识符
metadata: # 元数据
name <string> # 资源名称
annotations: # 资源注解,v1beta1使用下面的注解来指定要解析该资源的控制器类型
kubernetes.io/ingress.class: <string> # 适配的Ingress控制器类别,便于多ingress组件场景下,挑选针对的类型
namespace <string> # 名称空间
spec:
rules <[]Object> # Ingress规则列表,也就是http转发时候用到的 url关键字
- host <string> # 虚拟主机的FQDN,支持“*”前缀通配,不支持IP,不支持指定端口
http <Object>
paths <[]Object> # 虚拟主机PATH定义的列表,由path和backend组成
- path <string> # 流量匹配的HTTP PATH,必须以/开头
pathType <string> # 支持Exact、Prefix和ImplementationSpecific,必选
backend <Object> # 匹配到的流量转发到的目标后端
resource <Object> # 引用的同一名称空间下的资源,与下面两个字段互斥
service <object> # 关联的后端Service对象
name <string> # 后端Service的名称
port <object> # 后端Service上的端口对象
name <string> # 端口名称
number <integer> # 端口号
tls <[]Object> # TLS配置,用于指定上rules中定义的哪些host需要工作HTTPS模式
- hosts <[]string> # 使用同一组证书的主机名称列表
secretName <string> # 保存于数字证书和私钥信息的secret资源名称,用于主机认证
backend <Object> # 默认backend的定义,可嵌套字段及使用格式跟rules字段中的相同
ingressClassName <string> # ingress类名称,用于指定适配的控制器,类似于注解的功能
环境部署
获取配置文件
cd /data/kubernetes/app_secure
mkdir ingress ; cd ingress
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.1/deploy/static/provider/baremetal/deploy.yaml
mv deploy.yaml ingress-deploy.yaml
cp ingress-deploy.yaml{,.bak}
默认镜像
]# grep image: ingress-deploy.yaml | awk -F '/|@' '{print $(NF-1)}' | uniq
controller:v1.3.1
kube-webhook-certgen:v1.3.0
获取镜像
for i in nginx-ingress-controller:v1.3.1 kube-webhook-certgen:v1.3.0
do
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$i
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$i kubernetes-register.superopsmsb.com/google_containers/$i
docker push kubernetes-register.superopsmsb.com/google_containers/$i
docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/$i
done
注意:
controller的名称是需要更改一下,阿里云的镜像名称多了一个标识
修改基础镜像
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# grep image: ingress-deploy.yaml
image: kubernetes-register.superopsmsb.com/google_containers/nginx-ingress-controller:v1.3.1
image: kubernetes-register.superopsmsb.com/google_containers/kube-webhook-certgen:v1.3.0
image: kubernetes-register.superopsmsb.com/google_containers/kube-webhook-certgen:v1.3.0
开放访问入口地址
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# vim ingress-deploy.yaml
...
334 apiVersion: v1
335 kind: Service
...
344 namespace: ingress-nginx
345 spec:
...
348 ipFamilyPolicy: SingleStack
349 externalIPs: ['10.0.0.12'] # 限制集群外部访问的入口ip
350 ports:
351 - appProtocol: http
352 name: http
353 port: 80
...
628 failurePolicy: Ignore # 为了避免默认的准入控制限制,改为Ignore
...
应用资源配置文件
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# kubectl apply -f ingress-deploy.yaml
确认效果
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# kubectl get all -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-admission-create-s5p7h 0/1 Completed 0 105s
pod/ingress-nginx-admission-patch-qnjmv 0/1 Completed 0 105s
pod/ingress-nginx-controller-6cc467dfd9-c2dfg 1/1 Running 0 105s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller NodePort 10.109.163.145 10.0.0.12 80:30439/TCP,443:31912/TCP 105s
service/ingress-nginx-controller-admission ClusterIP 10.96.223.121 <none> 443/TCP 105s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 105s
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-6cc467dfd9 1 1 1 105s
NAME COMPLETIONS DURATION AGE
job.batch/ingress-nginx-admission-create 1/1 8s 105s
job.batch/ingress-nginx-admission-patch 1/1 7s 105s
测试访问页面
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# curl 10.0.0.12:30439
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
简单实践
准备工作
定义nginx服务
kubectl create deployment superopsmsb-nginx --image kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1
kubectl expose deployment superopsmsb-nginx --port 80
定义tomcat服务
kubectl create deployment superopsmsb-tomcat --image kubernetes-register.superopsmsb.com/superopsmsb/tomcat_web:v0.1
kubectl expose deployment superopsmsb-tomcat --port 8080
创建ingress
定义资源文件 09_kubernetes_application_secure_ingress_test.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: superopsmsb-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: nginx.superopsmsb.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: superopsmsb-nginx
port:
number: 80
属性解析:
注释信息,对于不同的ingress的内容是不一样的
对于ingress来说,最终要的就是rules了,这里的hosts是一个自定义的域名
应用资源文件
kubectl apply -f 09_kubernetes_application_secure_ingress_test.yaml
查看效果
[root@kubernetes-master1 /data/kubernetes/app_secure]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
superopsmsb-ingress <none> nginx.superopsmsb.com 80 17s
查看详细信息
[root@kubernetes-master1 /data/kubernetes/app_secure]# kubectl describe ingress superopsmsb-ingress
Name: superopsmsb-ingress
...
Rules:
Host Path Backends
---- ---- --------
nginx.superopsmsb.com
/ superopsmsb-nginx:80 (10.244.5.15:80)
Annotations: kubernetes.io/ingress.class: nginx
...
结果显式:
ingress 将 后端的superopsmsb-nginx-service:80 的样式自动与 ingress 的 url(/) 关联在一起了
访问效果
]# echo '10.0.0.12 nginx.superopsmsb.com' >> /etc/hosts
确认效果
]# curl nginx.superopsmsb.com
Hello Nginx, superopsmsb-nginx-deployment-64fc5c5f6c-dt4hx-1.23.0
]# curl 10.0.0.12 -H "Host: nginx.superopsmsb.com"
Hello Nginx, superopsmsb-nginx-74bf9646c5-bqcld-1.23.1
结果显式:
通过域名可以正常的访问后端的服务,而非域名访问方式受到了限制
小结
1.3.3 Ingress进阶
学习目标
这一节,我们从 多host、多url、小结 三个方面来学习。
多host
需求
访问 tomcat.superopsmsb.com/的时候,返回tomcat的结果
访问 nginx.superopsmsb.com/的时候,返回nginx的结果
资源配置文件
编辑资源定义文件 10_kubernetes_application_secure_ingress_mulhost.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: superopsmsb-ingress-mulhost
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: nginx.superopsmsb.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: superopsmsb-nginx
port:
number: 80
- host: tomcat.superopsmsb.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: superopsmsb-tomcat
port:
number: 8080
配置解析:
这里面有两个主机访问的地址是同一个后端端口,如果还用number的话,就无法正常的识别导致同一个地址访问不同的后端效果
应用资源文件
kubectl apply -f 10_kubernetes_application_secure_ingress_mulhost.yaml
查看ingress效果
kubectl get ingress
kubectl describe ingress superopsmsb-ingress-mulhost
访问效果
[root@kubernetes-master1 /data/kubernetes/app_secure]# curl 10.0.0.12 -H "Host: nginx.superopsmsb.com"
Hello Nginx, superopsmsb-nginx-74bf9646c5-bqcld-1.23.1
[root@kubernetes-master1 /data/kubernetes/app_secure]# curl 10.0.0.12 -H "Host: tomcat.superopsmsb.com"
Hello Tomcat, superopsmsb-tomcat-5bc5cc946d-jnbhc-10.0.23
环境收尾
kubectl delete -f 10_kubernetes_application_secure_ingress_mulhost.yaml
- 单域名多url访问
需求
访问 shuji.superopsmsb.com/tomcat的时候,返回tomcat的结果
访问 shuji.superopsmsb.com/nginx的时候,返回nginx的结果
注意事项:
由于这里涉及到了域名的url转发,而后端服务有可能不存在,随意我们需要在后端url转发的时候,取消转发关键字。
方法就是,在annotation中添加一个重写的规则
nginx.ingress.kubernetes.io/rewrite-target: /
即,所有的请求,把ingress匹配到的url关键字清除掉
测试效果
编辑资源定义文件 11_kubernetes_application_secure_ingress_mulurl.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: superopsmsb-ingress-mulurl
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: shuji.superopsmsb.com
http:
paths:
- path: /nginx
pathType: Prefix
backend:
service:
name: superopsmsb-nginx
port:
number: 80
- path: /tomcat
pathType: Prefix
backend:
service:
name: superopsmsb-tomcat
port:
number: 8080
应用资源文件
kubectl apply -f 11_kubernetes_application_secure_ingress_mulurl.yaml
查看效果
kubectl get ingress
kubectl describe ingress superopsmsb-ingress-mulurl
浏览器访问效果
[root@kubernetes-master1 /data/kubernetes/app_secure]# curl 10.0.0.12/nginx -H "Host: shuji.superopsmsb.com" Hello Nginx, superopsmsb-nginx-74bf9646c5-bqcld-1.23.1
[root@kubernetes-master1 /data/kubernetes/app_secure]# curl 10.0.0.12/tomcat -H "Host: shuji.superopsmsb.com"
Hello Tomcat, superopsmsb-tomcat-5bc5cc946d-jnbhc-10.0.23
环境收尾
kubectl delete -f 11_kubernetes_application_secure_ingress_mulurl.yaml
小结
1.3.4 Ingress认证
学习目标
这一节,我们从 web认证、属性认证、小结 三个方面来学习。
web认证
需求
我们准备对基础的nginx应用进行https方式来进行访问
方法:
ingress的tls规则就可以帮助我们实现对http应用进行https升级
简单实践
定制secret文件
cd /data/kubernetes/app_secure
mkdir ingress-tls && cd ingress-tls
(umask 077; openssl genrsa -out tls.key 2048)
openssl req -new -x509 -key tls.key -out tls.crt -subj "/CN=nginx.superopsmsb.com" -days 365
kubectl create secret tls superopsmsb-ingress-tls --cert=./tls.crt --key=./tls.key
确认效果
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress-tls]# ls
tls.crt tls.key
编辑资源定义文件 12_kubernetes_application_secure_ingress_tls.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: superopsmsb-ingress-tls
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: nginx.superopsmsb.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: superopsmsb-nginx
port:
number: 80
tls:
- hosts:
- shuji.superopsmsb.com
secretName: superopsmsb-ingress-tls
应用资源文件
kubectl apply -f 12_kubernetes_application_secure_ingress_tls.yaml
查看效果
kubectl get ingress
kubectl describe ingress superopsmsb-ingress-tls
访问测试效果
[root@kubernetes-master1 /data/kubernetes/app_secure]# curl 10.0.0.12 -H "Host: nginx.superopsmsb.com"
<html>
<head><title>308 Permanent Redirect</title></head>
<body>
<center><h1>308 Permanent Redirect</h1></center>
<hr><center>nginx</center>
</body>
</html>
[root@kubernetes-master1 /data/kubernetes/app_secure]# curl https://10.0.0.12 -H "Host: nginx.superopsmsb.com" -k
Hello Nginx, superopsmsb-nginx-74bf9646c5-bqcld-1.23.1
属性认证
简介
如果我们的容器应用本身就已经实现了https的功能,关于对集群内部的ingress的tls访问方式,可以借助于ingress controller的annotation的方式向nginx中传递一些属性,从而上nginx支持相应的功能。
1 通过两个annotation中的规则
ingress.kubernetes.io/ssl-passthrough: "true"
- 以为后面的dashboard本身就做了tls功能,所以这里进行tls代理即可
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
- 对后端直接启动https协议访问即可。
参考资料:
https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
简单实践
构建nginx镜像 - 开放443端口
cd /data/images/web/nginx
编辑Dockerfile增加如下内容
EXPOSE 80 443
构建镜像
docker build -t kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.2 .
提交镜像
docker push kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.2
清理deployment和svc
kubectl delete deployments.apps superopsmsb-nginx
kubectl delete svc superopsmsb-nginx
创建https格式的业务应用
cd /data/kubernetes/app_secure
kubectl create configmap nginx-ssl-conf --from-file=nginx-conf-tls/
kubectl create secret tls nginx-ssl-secret --cert=tls-key/tls.crt --key=tls-key/tls.key
资源清单文件 13_kubernetes_application_secure_ingress_tls_deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: superopsmsb-nginx
spec:
replicas: 1
selector:
matchLabels:
app: superopsmsb-nginx
template:
metadata:
labels:
app: superopsmsb-nginx
spec:
containers:
- name: nginx
image: kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.2
volumeMounts:
- name: nginxcerts
mountPath: /etc/nginx/certs/
readOnly: true
- name: nginxconfs
mountPath: /etc/nginx/conf.d/
readOnly: true
volumes:
- name: nginxcerts
secret:
secretName: nginx-ssl-secret
- name: nginxconfs
configMap:
name: nginx-ssl-conf
optional: false
---
kind: Service
apiVersion: v1
metadata:
name: superopsmsb-nginx
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
- name: https
port: 443
protocol: TCP
targetPort: 443
selector:
app: superopsmsb-nginx
应用资源配置文件
kubectl apply -f 13_kubernetes_application_secure_ingress_tls_deployment.yaml
测试效果
kubectl get svc superopsmsb-nginx
kubectl describe svc superopsmsb-nginx
访问Pod
[root@kubernetes-master1 /data/kubernetes/app_secure]# curl -k -H "Host:www.superopsmsb.com" https://10.244.5.17
Hello Nginx, superopsmsb-nginx-7b89fbfd4-bzgkd-1.23.1
访问svc
[root@kubernetes-master1 /data/kubernetes/app_secure]# curl https://10.103.23.88 -k
Hello Nginx, superopsmsb-nginx-fb8c87f7f-vp22q-1.23.1
ingress定制
资源对象文件 14_kubernetes_application_secure_ingress_tls.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: superopsmsb-ingress-tls
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
rules:
- host: www.superopsmsb.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: superopsmsb-nginx
port:
number: 443 # 转发的时候,使用443端口
应用资源对象
kubectl apply -f 14_kubernetes_application_secure_ingress_tls.yaml
检查效果
kubectl get ingress
kubectl describe ingress superopsmsb-ingress-tls
确认效果
[root@kubernetes-master1 /data/kubernetes/app_secure]# curl -k -H "Host:www.superopsmsb.com" https://10.0.0.12 Hello Nginx, superopsmsb-nginx-fb8c87f7f-vp22q-1.23.1
1.3.5 Ingress高阶
学习目标
这一节,我们从 动态URL、简单实践、小结 三个方面来学习。
动态URL
需求
我们知道,对于k8s的dashboard来说,它所实现的功能还是比较多的,而且原则上要求必须是https方式来进行访问。我们之前是通过service的方式实现正常的请求访问。有了ingress,我们也可以自由的访问dashboard了。比如我们访问k8s的dashboard的首页地址是
https://10.0.0.12:nodeport/#/login
思路
我们可以通过annotation中的规则来实现url的动态格式重写
nginx.ingress.kubernetes.io/rewrite-target: /$2
参考资料:
https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
简单实践
资源对象
定制ingress资源配置文件 15_kubernetes_application_secure_ingress_tls_dashboard.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: superopsmsb-ingress-dashboard
namespace: kubernetes-dashboard
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: shuji.superopsmsb.com
http:
paths:
- path: /dashboard(/|$)(.*)
pathType: Prefix
backend:
service:
name: kubernetes-dashboard
port:
number: 443
注意:
dashboard的应用是在kubernetes-dashboard命名空间里面
应用资源文件
kubectl apply -f 15_kubernetes_application_secure_ingress_tls_dashboard.yaml
查看效果
kubectl get ingress -n kubernetes-dashboard
kubectl describe ingress -n kubernetes-dashboard
浏览器访问 https://shuji.superopsmsb.com/dashboard/ 查看效果
结果显示:
借助于nginx-controller的tls功能可以正常的实现相关的https的效果。
token准备
cd ../secure
kubectl apply -f 10_kubernetes_platform_secure_lisi_config.yaml
kubectl describe secrets lisi-secret -n superopsmsb
小结
1.3.6 Ingress扩展
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
简介
默认情况下,Ingress是通过 Deployment + nodePort的方式将集群内部的服务暴露出去的,而且默认情况下,整个kubernetes集群中只有一个ingress-nginx实例。
[root@kubernetes-master1 /data/kubernetes/app_secure]# kubectl get pod -n ingress-nginx | grep controll
ingress-nginx-controller-6cc467dfd9-c2dfg 1/1 Running 0 52m
思路:
如果集群中唯一的ingress-nginx出现了故障,将导致整个kubernetes集群不可用。所以我们需要将这个集群业务的唯一入口进行高可用处理,根据我们之前的kubernetes资源对象知识,我们可以按照以下思路来进行:
1 借助于Daemonset的方式实现ingress的多节点部署
2 为了限制ingress-controller仅仅部署到master节点上,我们这里采用nodeSeler的方式来筛选master节点
3 为了避免NodePort的劣势凸显,可以直接使用宿主机的80和443端口,我们可以采用HostNetwork的方式实现Cluster和node的网络互通,让Cluster直接监听node的端口。前提是宿主机的这两个端口不被占用
所以最终我们可以确定 DaemonSet + HostNetwork + nodeSelector方式来部署ingress-nginx高可用集群。
准备工作
文件备份
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# cp ingress-deploy.yaml ingress-daemonset.yaml
检查主机环境
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# for i in {12..14}; do ssh root@10.0.0.$i "netstat -tnulp | egrep '443|80'"; done
tcp 0 0 10.0.0.12:2380 0.0.0.0:* LISTEN 2146/etcd
tcp6 0 0 :::6443 :::* LISTEN 31072/kube-apiserve
tcp 0 0 10.0.0.13:2380 0.0.0.0:* LISTEN 2234/etcd
tcp6 0 0 :::6443 :::* LISTEN 28944/kube-apiserve
tcp 0 0 10.0.0.14:2380 0.0.0.0:* LISTEN 2186/etcd
tcp6 0 0 :::6443 :::* LISTEN 29045/kube-apiserve
简单实践
修改文件
修改资源清单文件
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# vim ingress-daemonset.yaml
...
390 apiVersion: apps/v1
391 kind: DaemonSet # 将Deployment修改为 DaemonSet
...
491 dnsPolicy: ClusterFirst
492 hostNetwork: true # 增加cluster和node网络的互通
493 tolerations:
494 - operator: Exists
495 nodeSelector:
496 ingressSelect: "true" # 专属的节点部署标签,而且必须加上双引号
497 kubernetes.io/os: linux
所有master节点添加标识
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# for i in 1 2 3
do
kubectl label node kubernetes-master$i ingressSelect=true
done
应用资源清单文件
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# kubectl apply -f ingress-daemonset.yaml
查看效果
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# kubectl get pod -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ingress-nginx-admission-create-mn4tl 0/1 Completed 0 116s 10.244.2.25 kubernetes-node2 <none> <none>
ingress-nginx-admission-patch-rbtl5 0/1 Completed 0 116s 10.244.3.31 kubernetes-node3 <none> <none>
ingress-nginx-controller-85c26 1/1 Running 0 116s 10.0.0.14 kubernetes-master3 <none> <none>
ingress-nginx-controller-88w6j 1/1 Running 0 116s 10.0.0.12 kubernetes-master1 <none> <none>
ingress-nginx-controller-v2k7z 1/1 Running 0 116s 10.0.0.13 kubernetes-master2 <none> <none>
[root@kubernetes-mas
端口检查效果
[root@kubernetes-master1 /data/kubernetes/app_secure/ingress]# for i in {12..14}; do ssh root@10.0.0.$i "netstat -tnulp | egrep ':(443|80)'"; done
tcp 0 0 0.0.0.0:443 ...78308/nginx: master
tcp 0 0 0.0.0.0:80 ...78308/nginx: master
tcp6 0 0 :::443 ...78308/nginx: master
tcp6 0 0 :::80 ...78308/nginx: master
tcp 0 0 0.0.0.0:80 ...72502/nginx: master
tcp 0 0 0.0.0.0:443 ...72502/nginx: master
tcp6 0 0 :::80 ...72502/nginx: master
tcp6 0 0 :::443 ...72502/nginx: master
tcp 0 0 0.0.0.0:80 ...72415/nginx: master
tcp 0 0 0.0.0.0:443 ...72415/nginx: master
tcp6 0 0 :::80 ...72415/nginx: master
tcp6 0 0 :::443 ...72415/nginx: master
确认效果
创建资源对象
[root@kubernetes-master1 /data/kubernetes/app_secure]# kubectl apply -f 14_kubernetes_application_secure_ingress_tls.yaml
查看效果
[root@kubernetes-master1 /data/kubernetes/app_secure]# kubectl describe ingress superopsmsb-ingress-tls
Name: superopsmsb-ingress-tls
Labels: <none>
Namespace: default
Address: 10.0.0.12,10.0.0.13,10.0.0.14
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 17s (x2 over 40s) nginx-ingress-controller Scheduled for sync
Normal Sync 17s (x2 over 40s) nginx-ingress-controller Scheduled for sync
Normal Sync 17s (x2 over 40s) nginx-ingress-controller Scheduled for sync
查看访问效果
[root@kubernetes-master1 /data/kubernetes/app_secure]# curl -k -H "Host: www.superopsmsb.com" https://10.0.0.12
Hello Nginx, superopsmsb-nginx-fb8c87f7f-vp22q-1.23.1