0-前言
此教程仅适合学习和开发环境部署,强烈不建议上生产
网上有很多关于ingress controller部署的文章,部署方式也各种各样,结合实际环境,找到一个适合自己的方式,记录一下,给未来的自己。
安装部署 个人理解ingress controller的安装部署是分为两步:
- 第一步是--安装,即在k8s集群里安装这个组件;
- 第二步是--部署,即如何把这个集群里的组件暴露到外部以供调用 经过第一步和第二步,就在k8s集群的内外交界处搭起了一座沟通的桥梁。
1-Ingress Controller的实现方案
Ingress Controller只是一个抽象的统称,其实现的组件有如下:
- Ingress NGINX: Kubernetes 官方维护的方案,也是本次安装使用的 Controller。
- F5 BIG-IP Controller: F5 所开发的 Controller,它能够管理员通过 CLI 或 API 让 Kubernetes 与 OpenShift 管理 F5 BIG-IP 设备。
- Ingress Kong: 著名的开源 API Gateway 方案所维护的 Kubernetes Ingress Controller。
- Traefik: 是一套开源的 HTTP 反向代理与负载均衡器,而它也支援了 Ingress。
- Voyager: 一套以 HAProxy 为底的 Ingress Controller。 Source
NOTE:Ingress NGINX 是本文中安装部署的对象,以下所有的Ingress Controller均指Ingress NGINX方案
2-安装
一行命令(已在K8S v19.2上验证通过):
kubectl apply -f mandatory.yml
这是一份官方提供的mandatory.yml文件,好多ingress controller的部署教程都不提供完整的mandatory.yml文件,只会提供一个URL,但是这个URL往往会有两个问题:
- 1.URL点进去找不到文件
- 2.提供的版本太老了 所以我直接贴出来一份完整版的mandatory.yml文件,给大家快速安装验证功能的同时也给自己做了一个备份(如果是要上生产,强烈不建议直接使用该yml文件)。
修改了一个地方:
kind.deployment.spec.template.spec.containers.imagePullPolicy=IfNotPresent
之所以修改,是因为直接使用kubectl apply -f mandatory.yml部署的时候,发现:始终拉不下来镜像:quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.31.1。所以我在node节点上,先把docker image下载到本地(docker pull quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.31.1),然后再部署(kubectl apply -f mandatory.yml)。
知识点:
默认的镜像拉取策略是 IfNotPresent:在镜像已经存在的情况下, kubelet 将不再去拉取镜像。 如果希望强制总是拉取镜像,你可以执行以下操作之一:
- 设置容器的 imagePullPolicy 为 Always。
- 省略 imagePullPolicy,并使用 :latest 作为要使用的镜像的标签。
- 省略 imagePullPolicy 和要使用的镜像标签。
- 启用 AlwaysPullImages 准入控制器(Admission Controller)。 如果 imagePullPolicy 未被定义为特定的值,也会被设置为 Always
3-部署
部署ingress controller有很多方式,以下举例了三种常见的方式:
- Deployment+Service(NodePort)
- DaemonSet+HostNetwork+nodeSelector
- Deployment+Service(LoadBalancer)
这里,我主要还是针对前两种部署方式做展开。
3.1-Deployment+Service(NodePort)
nodePort的部署思路:
- 首先在所有的节点之上提供一个nodeport service,
- 然后在每个节点上开辟nodePort的端口(该端口为宿主机的端口),
- 通过nodeport service将流量引入进来到任意一个节点上,如果该节点没有部署ingress controller容器,那么就通过iptables将流量转发到ingress-controller容器中(图中的nginx容器),
- 而后由nginx根据ingress的规则进行判断,将其转发到对应的应用web容器中。
需要注意的是,ingress controller容器可以部署在其中一个node节点或者master节点上(不建议),也可以部署到所有的node节点和master节点上。后者的好处是,可以减少一次NAT的网络流量转发,提高效率;缺点也显而易见,在资源有限的情况下无法在每个节点都部署一个ingress controller容器。
该部署方式的svc yaml文件ingress-controller-nodeport-svc.yml。在node节点上开一个30080端口作为nodeport,暴露给集群外部。
外部访问方式:
http://nodeport_ip:30080/url
3.2-DaemonSet+HostNetwork+nodeSelector
HostNetwork的部署思路:
- 在每个节点创建一个ingress controller容器,
- 将这些容器的网络模式设为hostNetwork(这会导致每个节点的80和443端口都会被ingress controller容器占用)
- 当外部流量通过节点的80/443端口进来后,都会进入到ingress controller容器里,而后nginx根据ingress规则再将流量转发到对应的web应用容器中。
相比较与nodePort模式,hostNetwork模式不需要创建一个svc来统一收口流量,而是让各个节点自行转发流量。
该方案部署,需要修改mandatory.yml:
- 删除
kind.Deployment.spec.replicas: 1 - 修改
kind.Deployment为kind.DeamonSet - 添加
kind.DeamonSet.spec.template.spec.hostNetwork: true
外部访问方式:
http://nodeport_ip/url
知识点:什么是DeamonSet部署?
DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。 Source
知识点:为什么要用DeamonSet部署?
结合DS的定义和hostNetwork的部署思路,我们可以看到,利用DS可以很方便的实现:在所有的节点上都部署一个ingress controller容器的副本。当然,也可以利用K8S的标签选择器,在指定的一个或多个节点上部署ingress controller容器的副本。操作如下:
- 首先给需要部署ingress controller容器的节点(如node-1)打上标签
$ kubectl label node node-1 isIngressController="true" - 修改mandatory.yml:
kind.DeamonSet.spec.template.spec.NodeSelector: kubernetes.io/os: linux改为kind.DeamonSet.spec.template.spec.NodeSelector: isIngressController="true"
3.3-Nodeport和HostNetwork方案的比较
-
容器资源消耗:
- nodePort部署模式中需要部署的ingress-controller容器较少。一个集群可以部署几个就可以了。
- 而hostNetwork模式需要在每个节点部署一个ingress-controller容器,因此总起来消耗资源较多。
-
对宿主机端口的占用:
- nodePort模式主要占用的是svc的nodePort端口。
- 而hostNetwork则需要占用物理机的80和443端口。
-
网络流量的流转:
- 通过hostNetwork访问时,每个node节点都部署了ingress controller容器,因此不需要对进入的流量进行转发,提高了效率
-
source ip追踪:
- 通过nodePort访问时,nginx接收到的http请求中的source ip将会被转换为接受该请求的node节点的ip,而非真正的client端ip。
- 而使用hostNetwork的方式,ingress-controller将会使用的是物理机的DNS域名解析(即物理机的/etc/resolv.conf)。而无法使用内部的比如coredns的域名解析。
I-Reference
- Nginx Ingress Controller Installation Guide
- k8s ingress原理及ingress-nginx部署测试
- 更新镜像
- Kubernetes Ingress Controller的使用介绍及高可用落地
- Kubernetes NodePort vs LoadBalancer vs Ingress? When should I use what?
II-附录
ingress-controller-nodeport-svc.yml
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:
- port: 80
targetPort: 80
nodePort: 30080
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
mandatory.yml
apiVersion: v1
kind: Namespace
metadata:
name: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: tcp-services
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: udp-services
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nginx-ingress-clusterrole
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
rules:
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- nodes
- pods
- secrets
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- "extensions"
- "networking.k8s.io"
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- "extensions"
- "networking.k8s.io"
resources:
- ingresses/status
verbs:
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: nginx-ingress-role
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
rules:
- apiGroups:
- ""
resources:
- configmaps
- pods
- secrets
- namespaces
verbs:
- get
- apiGroups:
- ""
resources:
- configmaps
resourceNames:
# Defaults to "<election-id>-<ingress-class>"
# Here: "<ingress-controller-leader>-<nginx>"
# This has to be adapted if you change either parameter
# when launching the nginx-ingress-controller.
- "ingress-controller-leader-nginx"
verbs:
- get
- update
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: nginx-ingress-role-nisa-binding
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: nginx-ingress-role
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: nginx-ingress-clusterrole-nisa-binding
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: nginx-ingress-clusterrole
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
annotations:
prometheus.io/port: "10254"
prometheus.io/scrape: "true"
spec:
# wait up to five minutes for the drain of connections
terminationGracePeriodSeconds: 300
serviceAccountName: nginx-ingress-serviceaccount
nodeSelector:
kubernetes.io/os: linux
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.31.1
imagePullPolicy: IfNotPresent
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/nginx-configuration
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- --publish-service=$(POD_NAMESPACE)/ingress-nginx
- --annotations-prefix=nginx.ingress.kubernetes.io
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
# www-data -> 101
runAsUser: 101
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: http
containerPort: 80
protocol: TCP
- name: https
containerPort: 443
protocol: TCP
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
---
apiVersion: v1
kind: LimitRange
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
limits:
- min:
memory: 90Mi
cpu: 100m
type: Container