[TOC]
0x00 Ingress-Nginx 快速安装配置实践
描述: 此节,作为上一章的扩展补充,主要因为ingress-nginx迭代较快,加入了很多新得特性导致原来某些配置被弃用,当前时间节点【2022年3月8日 17:24:28】针对现有Ingress-nginx版本(v1.1.1)进行快速安装配置,与上一章中的安装是存在一定的不同,安装时都可以作为参考。
知识复习 Q: 什么是Ingress? A: Ingress 是管理对集群中服务的提供外部访问的 API 对象,Ingress 控制器负责实现 Ingress,通常使用负载均衡器,但它也可以配置边缘路由器或其他前端来帮助处理流量,它可以将来自集群外部的 HTTP 和 HTTPS 路由转发到集群内部的 Service 中。
Ingress 只是一个统称,其由 Ingress 和 Ingress Controller 两部分组成。
- Ingress 用作将原来需要手动配置的规则抽象成一个 Ingress 对象,使用 YAML 格式的文件来创建和管理。
- Ingress Controller 用作通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化。
使用 Ingress 控制器可以轻松实现外部URL访问集群内部服务、负载均衡、代理转发、支持配置SSL/TLS并提供基于名称的虚拟主机,值得注意的是 Ingress 不会暴露任意端口或协议,通过使用 Service.Type=NodePort
或 Service.Type=LoadBalancer
类型的服务向向 Internet 公开 HTTP 和 HTTPS 的访问服务
Q: 常用 Ingress 控制器有那些? 其它更多适用于Kubernetes的ingress控制器可以参考地址[https://kubernetes.io/zh/docs/concepts/services-networking/ingress-controllers/#其他控制器]
- ingress-Nginx : 用于 Nginx Kubernetes Ingress 控制器能够与 NGINX Web 服务器(作为代理)一起使用 (推荐)
- ingress-Traefik :由 Traefik Kubernetes Ingress 提供程序是一个用于 Traefik 代理的Ingress控制器。
- ingress-istio : Istio Ingress 是一个基于Istio的Ingress控制器。
温馨提示: 理想情况下所有 Ingress 控制器都应符合参考规范。实际上各种 Ingress 控制器的操作略有不同,请参考相应Ingress的控制器官方文档。
如下图所示的一个简单的示例,客户端请求访问外部URL地址, Ingress 将其所有流量发送到一个Service中, 后端 Pod 提供服务端响应通过路由进行返回给客户端。
WeiyiGeek.Ingress
Q: Ingress 规则有哪些?
- host : 虚拟主机名称, 主机名通配符主机可以是精确匹配(例如”foo.bar.com”)或通配符(例如“ *.foo.com”)
- paths : URL访问路径。
- pathType : Ingress 中的每个路径都需要有对应的路径类型(Path Type)
- backend : 是 Service 文档中所述的服务和端口名称的组合与规则的 host 和 path 匹配的对 Ingress 的 HTTP(和 HTTPS )请求将发送到列出的 backend, 一般情况可以单独为路径设置Backend以及未匹配的url默认访问的后端defaultBackend。
Ingress 中的每个路径都需要有对应的路径类型(Path Type),未明确设置 pathType 的路径无法通过合法性检查,当前支持的路径类型有三种:
- Exact:精确匹配 URL 路径,且区分大小写。
- Prefix:基于以
/
分隔的URL路径前缀匹配, 且区分大小写,并且对路径中的元素逐个完成。 - ImplementationSpecific:此路径类型匹配方法取决于 IngressClass, 具体实现可以将其作为单独的 pathType 处理或者与 Prefix 、 Exact 类型作相同处理。
说明: 如果路径的最后一个元素是请求路径中最后一个元素的子字符串,则不会匹配 (例如:/foo/bar 匹配 /foo/bar/baz, 但不匹配 /foo/barbaz)。
温馨提示: defaultBackend 通常在 Ingress 控制器中配置,以服务与规范中的路径不匹配的任何请求。
代码语言:javascript复制tee > test.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
spec:
defaultBackend:
service:
name: test
port:
number: 80
EOF
kubectl apply -f
注意, 入口控制器和负载平衡器可能需要一两分钟才能分配 IP 地址。 在此之前,你通常会看到地址字段的值被设定为<pending>
。
1.快速安装
代码语言:javascript复制# 1.下载 v1.1.1 版本的ingress YAML资源清单到本地。
$ wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.1/deploy/static/provider/cloud/deploy.yaml
# 2.为了加快国内环境拉取镜像的速度,此处将k8s.gcr.io替换为阿里云提供的镜像源。
$ sed -i -r 's#k8s.gcr.io/ingress-nginx/controller:(.*)@.*$#registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:1#g' ingress-nginx-deploy.yaml
$ sed -i -r 's#k8s.gcr.io/ingress-nginx/(.*)@.*$#registry.cn-hangzhou.aliyuncs.com/google_containers/1#g' ingress-nginx-deploy.yaml
# 或者使用skopeo同步到私有仓库
# ./skopeo copy --insecure-policy --src-tls-verify=false --dest-tls-verify=false --dest-authfile /root/.docker/config.json docker://registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.2.0 docker://harbor.weiyigeek.top/devops/nginx-ingress-controller:v1.2.0
# 3.在ingress-nginx命名空间中,部署 YAML 清单中ingress相关的控制器资源
$ kubectl apply -f ingress-nginx-deploy.yaml
# 4.指定条件等待Ingress相关资源部署完成以及查看namespace中部署ingress的相关Pod及其状态
$ kubectl wait --namespace ingress-nginx
--for=condition=ready pod
--selector=app.kubernetes.io/component=controller
--timeout=120s
$ kubectl get pods --namespace=ingress-nginx
# 5.创建的默认后端的资源名称一定要与ingress-nginx-controller中的 --default-backend-service 字段值相对应
# 使用如下命令利用deployment 资源控制器快速创建default-backend 对象
$ kubectl create deployment default-backend --image=nginx:latest --namespace ingress-nginx
# 为default-backend Pod 创建clusterip 类型的服务
$ kubectl create service clusterip default-backend --namespace ingress-nginx --tcp=80
# 或者资源清单的方式
tee default-backend.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
annotations:
prometheus.io/path: /metrics
prometheus.io/port: "80"
prometheus.io/scheme: http
labels:
app: weiyigeek.top
ref: default-backend
name: default-backend
namespace: ingress-nginx
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: weiyigeek.top
ref: default-backend
type: ClusterIP
EOF
# 6.查看Ingress的ClassName,此处名称为nginx
$ kubectl get ingressclasses.networking.k8s.io
# NAME CONTROLLER PARAMETERS AGE
# nginx k8s.io/ingress-nginx <none> 10d
# 将ingress controler服务Pod运行在所有Work节点上,有几台机器就可以设置几个副本,当然master要去除污点后使用
~$ kubectl scale deployment -n ingress-nginx ingress-nginx-controller --replicas=4
2.服务验证
描述: 创建使用指定的名称Ingress入口,此处可以采用两种方式进行创建,第一种方式是通过命令行方式,第二种是通过Ingress资源清单方式。
代码语言:javascript复制# 方式1,创建一个名为demo-myweb-blog的入口,控制器名称为nginx,规则是将访问demo.weiyigeek.top请求转发到后端myweb-blog:80 服务之上
kubectl create ingress demo-myweb-blog --class=nginx --rule="demo.weiyigeek.top/=web-blog:80" --namespace devtest
# kubectl create ingress test-app -n devtest --rule="test.app.weiyigeek.top/oa(/|$)(.*)=weiyigeek-oa-prod:8080" --annotation nginx.ingress.kubernetes.io/rewrite-target=/$2
# 方式2.Ingress资源清单如下所示,其中重点是ingressClassName与rules字段
cat > www-myweb-blog.ingress <<'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: www-blog
namespace: devtest
annotations:
kubernetes.io/ingress.class: "nginx" # 方式1.根据该注解指定发现 Ingress
ingressclass.kubernetes.io/is-default-class: true # 如未设置IngressClassName名称则采用默认Ingress
nginx.ingress.kubernetes.io/ssl-redirect : false # 禁用强制跳转
nginx.ingress.kubernetes.io/rewrite-target: /
labels:
app: blog
spec:
ingressClassName: nginx # 方式2.根据该字段指定发现 Ingress
defaultBackend:
service:
name: default-http
port:
number: 80
rules:
- host: "www.weiyigeek.top"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: www-blog
port:
number: 80
- host: "web.weiyigeek.top"
http:
paths:
- pathType: ImplementationSpecific
path: "/"
backend:
service:
name: web-blog
port:
number: 80
tls:
- hosts:
- www.weiyigeek.top
secretName: testsecret-tls
EOF
# 部署资源清单
kubectl apply -f www-myweb-blog.ingress
# 查看上面部署的ingress入口信息及其规则默认后端信息。
kubectl get ingress -n devtest
kubectl describe ingress -n devtest
# 调用修改后的 Ingress yaml 文件。
kubectl replace -f www-myweb-blog.ingress
# 通过本机的hosts文件,手动将www.weiyigeek.top、demo.weiyigeek.top域名绑定到K8S中Node节点IP地址上,即可通过浏览器访问。
192.168.12.222 www.weiyigeek.top demo.weiyigeek.top
WeiyiGeek.describe ingress
3.配置虚拟主机证书
描述: 通过设定包含 TLS 私钥和证书的 Secret 来保护 Ingress, TLS Secret 必须包含名为 tls.crt (证书)
和 tls.key (私钥)
的键名
# 方式1.资源清单方式
apiVersion: v1
kind: Secret
metadata:
name: www.weiyigeek.top
namespace: devtest
data:
tls.crt: `base64 编码的 cert`
tls.key: `base64 编码的 key`
type: kubernetes.io/tls
# 方式2.kubectl 命令行方式
kubectl create secret tls www.weiyigeek.top --cert=server.crt --key=server.key -n devtest
# 使用示例:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- www.weiyigeek.top
secretName: testsecret-tls
温馨提示: 在 Ingress 中引用此 Secret 将会告诉 Ingress 控制器使用 TLS 加密从客户端到负载均衡器的通道。
4.使用hostNetwork网络方式
描述: 定义后Ingress-controller
的IP就与宿主机IP一样。
步骤 01.编辑 Ingress-controller 的 deploy 资源控制器内容,添加hostNetWork为true
的键值对,。
$ kubectl edit deployments.apps -n ingress-nginx ingress-nginx-controller
spec:
hostNetwork: true
containers:
- args:
- /nginx-ingress-controller
步骤 02.查看Pod的IP地址,此时你会发现其地址为节点主机地址
。
~$ kubectl get pod -n ingress-nginx -o wide | grep "ingress-nginx-controller"
ingress-nginx-controller-6b8bd48548-dfnvc 1/1 Running 0 4m26s 192.168.12.224 weiyigeek-224
ingress-nginx-controller-6b8bd48548-r9kpl 1/1 Running 0 5m 192.168.12.226 weiyigeek-226
ingress-nginx-controller-6b8bd48548-x9w9j 1/1 Running 0 5m33s 192.168.12.225 weiyigeek-225
ingress-nginx-controller-6b8bd48548-xqpcc 1/1 Running 0 5m33s 192.168.12.223 weiyigeek-223
~$ curl 192.168.12.225
Hello ingress-Nginx , Index /
步骤 03.让Pod均衡的分别运行在work节点,我们需要执行以下两步,
代码语言:javascript复制# 设置工作节点标签
~$ kubectl label nodes weiyigeek-223 weiyigeek-224 weiyigeek-225 weiyigeek-226 node=work
# node/weiyigeek-223 labeled
# node/weiyigeek-224 labeled
# node/weiyigeek-225 labeled
# node/weiyigeek-226 labeled
# 设置节点硬亲和、pod软亲和
~$ kubectl edit deployments.apps -n ingress-nginx ingress-nginx-controller
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node
operator: In
values:
- work
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: ingress
operator: In
values:
- nginx
topologyKey: kubernetes.io/hostname
weight: 100
5.使用externalIP网络方式
描述: 前面我们使用了 hostNetwork 方式,知道了可以使用 controller.hostNetwork=true
参数进行设置, 此处我们采用采用 externalIP
的方式进行服务暴露。
温馨提示:默认的控制器类型是Deployment,不过为其稳定性建议使用 DaemonSet 类型的控制器, 设置 hostNetwork 为 true、设置 dnsPolicy 为 ClusterFirstWithHostNet,也建议为要部署到的节点上打上ingress标签, 然后使用NodeSelector
添加ingress: "true"
部署至指定节点。
安装部署
代码语言:javascript复制# 启用 RBAC 支持
$ helm install --name nginx-ingress --set "rbac.create=true,controller.service.externalIPs[0]=192.168.12.211,controller.service.externalIPs[1]=192.168.12.212,controller.service.externalIPs[2]=192.168.12.213" stable/nginx-ingress
部署完成后我们可以看到 Kubernetes 服务中增加了 nginx-ingress-controller
和 nginx-ingress-default-backend
两个服务。nginx-ingress-controller 为 Ingress Controller,主要做为一个七层的负载均衡器来提供 HTTP 路由、粘性会话、SSL 终止、SSL直通、TCP 和 UDP 负载平衡等功能。nginx-ingress-default-backend 为默认的后端,当集群外部的请求通过 Ingress 进入到集群内部时,如果无法负载到相应后端的 Service 上时,这种未知的请求将会被负载到这个默认的后端上。
由于我们采用了 externalIP 方式对外暴露服务, 所以 nginx-ingress-controller 会在 192.168.12.211、192.168.12.212、192.168.12.213 三台节点宿主机上的 暴露 80/443 端口。
代码语言:javascript复制$ kubectl get svc | grep "nginx-ingress-controller"
nginx-ingress-controller LoadBalancer 10.254.84.72 192.168.12.211,192.168.12.212,192.168.12.213 80:8410/TCP,443:8948/TCP 46s
访问 Nginx Ingress Controller , 我们可以使用以下命令来获取 Nginx 的 HTTP 和 HTTPS 地址。
代码语言:javascript复制$ kubectl --namespace default get services -o wide -w nginx-ingress-controller
访问验证:
代码语言:javascript复制# 返回 200
$ curl -I http://192.168.12.211
$ curl -I --insecure http://192.168.12.211
在几台节点宿主机上查看,我们可以看到 ExternalIP 的 Service 是通过 Kube-Proxy对外暴露的,这里的 192.168.12.211、192.168.12.212、192.168.12.213 是三个内网 IP。 实际生产应用中是需要通过边缘路由器或全局统一接入层的负载均衡器将到达公网 IP 的外网流量转发到这几个内网 IP 上,外部用户再通过域名访问集群中以 Ingress 暴露的所有服务。
代码语言:javascript复制$ sudo netstat -tlunp|grep kube-proxy|grep -E '80|443'
tcp 0 0 192.168.12.211:80 0.0.0.0:* LISTEN 714/kube-proxy
tcp 0 0 192.168.12.211:443 0.0.0.0:* LISTEN 714/kube-proxy
$ sudo netstat -tlunp|grep kube-proxy|grep -E '80|443'
tcp 0 0 192.168.12.212:80 0.0.0.0:* LISTEN 690/kube-proxy
tcp 0 0 192.168.12.212:443 0.0.0.0:* LISTEN 690/kube-proxy
$ sudo netstat -tlunp|grep kube-proxy|grep -E '80|443'
tcp 0 0 192.168.12.213:80 0.0.0.0:* LISTEN 748/kube-proxy
tcp 0 0 192.168.12.213:443 0.0.0.0:* LISTEN 748/kube-proxy
6.对外暴露端口配置
描述: 默认的 Ingress 配置是暴露 http(80) 、https(443) 端口, 但需要注意的是创建的 Ingress 必须要和对外暴露的 Service 在同一命名空间下!
除此之外其实还可通过 Ingress controller 来实现TCP 和 UDP 服务端口的暴露,下面进行一一讲解。
1) http/https端口 描述: 缺少ingress支持http与https协议的后端应用。
代码语言:javascript复制cat '
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: rewrite
namespace: default
spec:
ingressClassName: nginx
rules:
- host: rewrite.weiyigeek.top
http:
paths:
- path: /something(/|$)(.*)
pathType: Prefix
backend:
service:
name: http-svc
port:
number: 80
' | kubectl create -f -
2) 暴露TCP/UDP端口 描述: 我们可以通过 tcp-services-configmap.yaml 设置映射tcp, 通过 udp-services-configmap.yaml 映射udp
代码语言:javascript复制# tcp-services-configmap.yaml
cat > tcp-services-configmap.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: tcp-services
namespace: ingress-nginx
data:
2181: "kafka/kafka-zookeeper:2181"
50000: "devops/jenkins:50000"
EOF
cat tcp-services-configmap.yaml | kubectl create -f -
# udp-services-configmap.yaml
tee udp-services-configmap.yaml <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: udp-services
namespace: ingress-nginx
data:
53: "kube-system/kube-dns:53"
EOF
cat udp-services-configmap.yaml | kubectl create -f -
查看 ConfigMap 资源
代码语言:javascript复制~$ kubectl get cm -n ingress-nginx | grep "services"
tcp-services 2 15h
udp-services 1 15h
验证暴露的 TCP/UDP 服务:
代码语言:javascript复制~$ telnet 192.168.12.225 50000
Trying 192.168.12.225...
Connected to 192.168.12.225.
Escape character is '^]'.
~$ dig -x jenkins.devops.svc @192.168.12.225
3) 通过helm更新公开端口
描述: 通过helm chart
图表中的deployment-tcp-udp-values.yaml
或者deployment-tcp-udp-configMapNamespace-values.yaml文件
进行暴露TCP/UDP (如果你使用采用helm方式部署的ingress,此种方式是推荐的。), 注意此种方式
# ngress-nginx/ci/deployment-tcp-udp-values.yaml
controller:
image:
repository: ingress-controller/controller
tag: 1.0.0-dev
digest: null
admissionWebhooks:
enabled: false
service:
type: ClusterIP
tcp:
9000: "default/test:8080"
udp:
9001: "default/test:8080"
# 执行更新
helm upgrade nginx-ingress stable/nginx-ingress
-f ./ci/deployment-tcp-udp-values.yaml
7.节点与Pod亲和性设置
描述: 为 deployments.apps 资源控制器部署的 ingress 设置节点亲和。
代码语言:javascript复制sepc:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node
operator: In
values:
- app
- ali
- www
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
topologyKey: kubernetes.io/hostname
weight: 100
nodeSelector:
k8s.io/ingress-nginx: "true"
8.基于 auth-url 模块认证方式实践
1.使用auth-url模块配置ldap登录
描述: 在 K8s 中我们可以使用 nginx Ingress
的 auth-url
模块配置ldap登录验证。
前置需求已经有部署自己的ldap server
, 然后还要在要配置ldap验证的服务ingress 上添加以下annotation:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/auth-url: https://{server_domain}/auth/ldap/$remote_user/$http_authorization
温馨提示: 此处 {server_domain}
是你的 ldap-auth-server
域名或IP地址, 其实现原理也是非常简单的即用你的程序去ldap server校验用户名密码是否正确即可。
# app.py
# -*- coding: utf-8 -*-
# description: ldap 服务用户账号密码验证
from ldap3 import Server, Connection, ALL, SUBTREE, ALL_ATTRIBUTES, MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE
from ldap3.utils.hashed import hashed
from ldap3 import (
HASHED_SALTED_SHA, MODIFY_REPLACE
)
import json,os
import base64
from flask import (
Flask,
Response,
request,
render_template,
redirect,
jsonify,
make_response,
url_for,
abort,
)
from helpers import (
status_code
)
app = Flask(__name__)
class LdapUtils(object):
def __init__(self, ldap_host=None, port=None, base_dn=None, user=None, password=None):
self.base_dn = base_dn
try:
server = Server(ldap_host, port, get_info=ALL)
self.ldapconn = Connection(server, user=None, password=None,
auto_bind='NONE', version=3, authentication='SIMPLE',
client_strategy='SYNC',
auto_referrals=True, check_names=True, read_only=False, lazy=False,
raise_exceptions=False)
self.ldapconn.rebind(user=user, password=password)
except Exception as e:
print(e)
def ldap_search_dn(self, uid=None):
obj = self.ldapconn
search_base = self.base_dn
search_scope = SUBTREE
search_filter = "(cn={0})".format(uid)
try:
obj.search(search_base, search_filter, search_scope, attributes=['cn'], paged_size=1)
if len(obj.response) == 1:
return obj.response[0]['dn']
else:
return None
except Exception as e:
print(e)
def ldap_update_pass(self, uid=None, oldpass=None, newpass=None):
target_cn = self.ldap_search_dn(uid)
try:
hashed_password = hashed(HASHED_SALTED_SHA, newpass)
print("password:" hashed_password)
changes = {
'userPassword': [(MODIFY_REPLACE, [hashed_password])]
}
return self.ldapconn.modify(target_cn, changes=changes)
except Exception as e:
print(e)
return False
def ldap_get_vaild(self, uid=None, passwd=None):
if not uid or not passwd:
return False
obj = self.ldapconn
# 这里注意修改成自己ldap上定义的user dn
dn = "uid={0},ou=Users,dc=xxxxx,dc=com".format(uid)
try:
if obj.rebind(dn, passwd):
return True
else:
return False
except Exception as e:
print('e:' str(e))
# 这里我是通过docker部署服务,这些配置在环境变量传入,你可以根据需要选择直接填写对应值
ldap_host = os.getenv("host")
port = os.getenv("port")
base_dn = os.getenv("base_dn")
user = os.getenv("admin_user")
password = os.getenv("password")
ldap = LdapUtils(ldap_host, int(port), base_dn, user,password)
@app.route('/auth/<user>/<passwd>')
def auth_user(user="user", passwd="passwd"):
if not ldap.ldap_get_vaild(uid=user,passwd=passwd):
return status_code(401)
return jsonify(authenticated=True, user=user)
@app.route('/auth/<qop>/<user>/<passwd>')
def auth_qop_user(qop=None, user="user", passwd="passwd"):
temp = base64.b64decode(passwd.split(" ")[1]).decode()
if not ldap.ldap_get_vaild(uid=user,passwd=temp.split(":")[1]):
return status_code(401)
return jsonify(authenticated=True, user=user)
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
auth = request.headers.get('Authorization')
if auth is not None:
uid = base64.b64decode(auth.split(" ")[1]).decode().split(":")[0]
passwd = base64.b64decode(auth.split(" ")[1]).decode().split(":")[1]
if not ldap.ldap_get_vaild(uid=uid,passwd=passwd):
return status_code(401)
return render_template('password.html',uid=uid)
return status_code(401)
if __name__ == '__main__':
app.run(host = '0.0.0.0')
0x01 入坑出坑
问题1.K8s Nginx Ingress Controller 转发 X-Forwarded-Proto 请求头的问题
问题说明: 在 Kubernetes 集群上部署了 Nginx Ingress Controller 最前端用的是阿里云七层负载均衡,部署后发现不能正确转发 X-Forwarded-Proto 请求头,造成 http 重定向到 https 无法正常工作,请问如何解决?
代码语言:javascript复制# 问题补充:
# 用下面的命令进入 nginx-ingress 容器
kubectl exec -it daemonset/nginx-ingress -n nginx-ingress cat /etc/nginx/conf.d/production-cnblogs-ingress.conf
# 发现问题是下面的配置引起的
proxy_set_header X-Forwarded-Proto $scheme;
问题解决: 终于在 Nginx Ingress Controller 的官方帮助文档 Advanced Configuration with Annotations 中找到一个注解(annotation)解决了这个问题,它就是 nginx.org/redirect-to-https: "true"
Annotation: nginx.org/redirect-to-https
ConfigMap Key redirect-to-https
Description Sets the 301 redirect rule based on the value of the http_x_forwarded_proto header on the server block to force incoming traffic to be over HTTPS. Useful when terminating SSL in a load balancer in front of the Ingress controller
# 根据服务器块上http_x_forwarded_proto报头的值设置301重定向规则,以强制传入的流量使用HTTPS。在入口控制器前的负载均衡器中终止SSL时非常有用;
操作步骤: 1)在 cnblogs-ingress.yaml 中 annotations 下面添加 nginx.org/redirect-to-https: “true”
代码语言:javascript复制apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: cnblogs-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.org/redirect-to-https: "true"
spec:
rules:
- host: q.cnblogs.com
http:
paths:
- backend:
serviceName: q-web
servicePort: 80
2) 更新 ingress 配置
代码语言:javascript复制kubectl apply -f cnblogs-ingress.yaml
3) 更新 nginx-ingress
代码语言:javascript复制kubectl rollout restart daemonset/nginx-ingress -n nginx-ingress &&
kubectl rollout status daemonset/nginx-ingress -n nginx-ingress
4)查看 inginx 容器中的配置
代码语言:javascript复制kubectl exec -it daemonset/nginx-ingress -n nginx-ingress cat /etc/nginx/conf.d/production-cnblogs-ingress.conf
# 发现 proxy_set_header X-Forwarded-Proto $scheme; 变成了 proxy_set_header X-Forwarded-Proto https; ,并且增加了下面的 http 重定向 https 的配置。
if ($http_x_forwarded_proto = 'http') {
return 301 https://$host$request_uri;
}
nginx-ingress 自己完成了基于X-Forwarded-Proto
的 http 重定向到 https 的操作,应用都不需要自己处理了。
Tips :建议采用 kubernetes/ingress-nginx 而非 nginxinc/kubernetes-ingress : https://github.com/kubernetes/ingress-nginx
问题2:extensions/v1beta1 Ingress is deprecated in v1.14 , unavailable in v1.22 ; use networking.k8s.io/v1 Ingress
解决办法:根据当前 Kubernetes 版本中资源组与版本进行选择即可;
代码语言:javascript复制~/K8s/Day7/demo1$ kubectl api-resources | grep "ingresses"
ingresses ing extensions true Ingress
ingresses ing networking.k8s.io true Ingress
# Warning: extensions/v1beta1 Ingress is deprecated in v1.14 , unavailable in v1.22 ; use networking.k8s.io/v1 Ingress
apiVersion: networking.k8s.io/v1 # 注意点否则将报上面的预警
问题3.Error from server (InternalError): error when applying patch:
错误信息:
代码语言:javascript复制$ kubectl apply -f demo-ingress.yaml
# 错误1
{"metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{"apiVersion":"networking.k8s.io/v1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx"},"name":"nginx","namespace":"default"},"spec":{"rules":[{"host":"nginx.ieasou.cn","http":{"paths":[{"backend":{"service":{"name":"nginx","port":{"number":80}}},"path":"/","pathType":"Prefix"}]}}]}}n","kubernetes.io/ingress.class":"nginx"}},"spec":{"rules":[{"host":"nginx.ieasou.cn","http":{"paths":[{"backend":{"service":{"name":"nginx","port":{"number":80}}},"path":"/","pathType":"Prefix"}]}}]}}
to:
Resource: "networking.k8s.io/v1, Resource=ingresses", GroupVersionKind: "networking.k8s.io/v1, Kind=Ingress"
Name: "nginx", Namespace: "default"
for: "demo-ingress.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": Post "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/extensions/v1beta1/ingresses?timeout=30s": x509: certificate is valid for k8s-master002, kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster.local, not
ingress-nginx-controller-admission.ingress-nginx.svc
# 错误2
kubectl create -f ingress-nginx-http-v1.yaml
# Error from server (InternalError): error when creating "ingress-nginx-http-v1.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": an error on the server ("") has prevented the request from succeeding
解决办法: Webhook 删除后重新构建 ingress-nginx-http-v1.yaml 资源清单即可
代码语言:javascript复制weiyigeek@ubuntu:~/K8s/Day7/demo2$ kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
# validatingwebhookconfiguration.admissionregistration.k8s.io "ingress-nginx-admission" deleted
weiyigeek@ubuntu:~/K8s/Day7/demo2$ kubectl create -f ingress-nginx-http-v1.yaml
# ingress.networking.k8s.io/nginx-ingress-http created # 未其他报错正常创建
问题4.查看ingress规则时提示kubernetes之ingress error: endpoints "default-http-backend" not found
警告
- 问题信息:
$ kubectl describe ing
Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
- 问题原因: 注意:根据您正在使用的Ingress控制器,您可能需要创建一个default-http-backend服务。没有规则的入口将所有流量发送到一个默认后端。默认后端通常是Ingress控制器的一个配置选项,在您的Ingress资源中没有指定。如果Ingress对象中的主机或路径都不匹配HTTP请求,则流量将被路由到默认后端。
- 解决办法:
# Ingress 控制器
spec:
defaultbackend:
service:
name: nginx-ingress-v1-svc
port:
number: 80
问题5.设置了ingress无法正常显示页面,由于没有在资源清单中指定设置ingressclass名称
问题描述: 由于没有给ingress规则设置默认的ClassName,此时带有虚拟主机头的响应为默认default-http后端。
代码语言:javascript复制# 1.查看部署的ingress-nginx控制器名称k8s.io/ingress-nginx。
~$ kubectl get deployments.apps -n ingress-nginx ingress-nginx-controller -o yaml | grep " --controller-class"
- --controller-class=k8s.io/ingress-nginx
# 2.查看ingressclass的Name信息
~$ kubectl get ingressclasses.networking.k8s.io
# NAME CONTROLLER PARAMETERS AGE
# nginx k8s.io/ingress-nginx <none> 12d
# 3.编辑ingressclass
~$ kubectl get ingressclasses.networking.k8s.io
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/version: 1.1.1
helm.sh/chart: ingress-nginx-4.0.15
spec:
controller: k8s.io/ingress-nginx
parameters:
apiGroup: k8s.example.com
kind: IngressParameters
name: external-lb
namespace: external-configuration
scope: Namespace
# 4.为ingress规则指定默认的ingresclass的名称。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog
namespace: devtest
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
# kubernetes.io/ingress.class: nginx # 当使用多个 Ingress 控制器时进行配置,如果不定义 ingress.class,云提供商可能使用默认的 Ingress 控制器。
ingressclass.kubernetes.io/is-default-class: true
spec:
ingressClassName: nginx # 推荐
rules:
- host: "www.weiyigeek.top"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: myweb-blog
port:
number: 80
温馨提示: ingressclass API 资源对象中, 在 sepc.parameters 字段中有一个 scope 和 namespace 字段,可用来引用特定于名字空间的资源,对 Ingress 类进行配置。(FEATURE STATE: Kubernetes v1.22 [beta]
)
- scope 字段默认为 Cluster,表示默认是集群作用域的资源。
- scope 设置为 Namespace 并设置 namespace 字段就可以引用某特定名字空间中的参数资源。
温馨提示: 当前 ingress 1.1.1 版本中使用 ingressClassName
字段来替代kubernetes.io/ingress.class
注解, 两则区别在于该注解通常用于引用实现该 Ingress 的控制器的名称, 而这个新的字段则是对一个包含额外 Ingress 配置的 IngressClass 资源的引用, 包括 Ingress 控制器的名称。
温馨提示: 我们也可以将一个 IngressClass 资源的 ingressclass.kubernetes.io/is-default-class
注解设置为 true ,以确保在未指定 ingressClassName 字段的情况下,Ingress 也能够分配为这个默认的 IngressClass.