Kubernetes部署Django遇Disallowed Hosts错误,求LoadBalancer白名单配置方法
我明白你遇到的困扰了——Kubernetes动态生成LoadBalancer后,Django因为没把这个LB的地址加入ALLOWED_HOSTS而报错,而且因为LB是动态创建的,没法提前硬编码进去对吧?下面给你几个实用的解决方案,从测试到生产环境都覆盖到:
先修复一个关键配置问题:Service与Pod的Selector不匹配
先提个必须解决的小问题:你的service.yaml里selector是app: mgmt_reporting,但pod.yaml的label是app: my-pod,这会导致Service找不到对应的Pod,流量根本到不了Django容器。先把这个匹配上:要么把Pod的label改成app: mgmt_reporting,要么把Service的selector改成app: my-pod,这是所有方案生效的前提!
方案1:临时测试用——手动获取LB地址并注入环境变量
如果只是临时验证功能,你可以先拿到LoadBalancer的外部IP或主机名:
# 获取LB的外部IP(如果是IP类型的Ingress) kubectl get service mgmt-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}' # 如果云厂商分配的是域名(比如AWS的ELB域名),用这个命令 kubectl get service mgmt-service -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
然后修改你的pod.yaml,把这个地址作为环境变量传给Django容器:
apiVersion: v1 kind: Pod metadata: name: test.example.com labels: app: mgmt_reporting # 这里已改成和Service匹配的label spec: containers: - name: my-container image: my-image:1.0 ports: - name: my-port containerPort: 8000 env: - name: DJANGO_ALLOWED_HOSTS value: "test.example.com,xxx.xxx.xxx.xxx" # 把xxx替换成你拿到的LB地址
接着在Django的settings.py里读取这个环境变量:
import os ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",")
这个方法简单直接,但缺点是如果LB地址变化(比如重新创建Service),你得手动更新Pod的环境变量,适合临时测试用。
方案2:生产环境推荐——容器启动时自动获取LB地址
这个方法更自动化,适合生产环境。思路是在容器启动时,用脚本自动查询LoadBalancer的地址,然后注入到Django的配置里。
步骤1:编写启动脚本
创建一个start.sh脚本,放在你的Django项目目录中:
#!/bin/sh set -e # 优先尝试获取LB的主机名,若不存在则获取IP LB_HOST=$(kubectl get service mgmt-service -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') if [ -z "$LB_HOST" ]; then LB_HOST=$(kubectl get service mgmt-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}') fi # 合并原有允许的主机和LB地址 export DJANGO_ALLOWED_HOSTS="test.example.com,$LB_HOST" # 启动Django服务(如果用gunicorn就替换成对应的启动命令) exec python manage.py runserver 0.0.0.0:8000
步骤2:更新Dockerfile
把脚本加入镜像,并设置为容器的启动命令:
# 你的原有Dockerfile内容... COPY start.sh /start.sh RUN chmod +x /start.sh CMD ["/start.sh"]
步骤3:给Pod赋予查询Service的权限
因为脚本里要用kubectl查询Service信息,所以需要给Pod的ServiceAccount绑定对应的权限。创建以下RBAC配置文件(比如rbac.yaml):
apiVersion: v1 kind: ServiceAccount metadata: name: django-lb-reader namespace: default # 替换成你的Pod所在的命名空间 --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: service-reader rules: - apiGroups: [""] resources: ["services"] verbs: ["get", "list"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: django-service-reader-binding subjects: - kind: ServiceAccount name: django-lb-reader namespace: default roleRef: kind: ClusterRole name: service-reader apiGroup: rbac.authorization.k8s.io
执行kubectl apply -f rbac.yaml创建这些权限资源。
步骤4:更新Pod配置使用ServiceAccount
修改pod.yaml,指定使用刚才创建的ServiceAccount:
apiVersion: v1 kind: Pod metadata: name: test.example.com labels: app: mgmt_reporting spec: serviceAccountName: django-lb-reader # 新增这一行 containers: - name: my-container image: my-image:1.0 # 记得重新构建镜像并推送至镜像仓库 ports: - name: my-port containerPort: 8000
这样每次容器启动时,都会自动获取当前LoadBalancer的地址,加入到Django的允许主机列表里,完全不需要手动干预。
方案3:测试环境快速绕过(绝对禁止生产环境使用)
如果是本地测试或者开发环境,你可以暂时放宽Django的主机限制:
# settings.py里修改 ALLOWED_HOSTS = ['*']
但绝对不能在生产环境这么做,这会带来严重的安全风险,可能导致主机头攻击。
内容的提问来源于stack exchange,提问作者Sebastian

