在企业环境或防火墙后部署Kubernetes时,通常需要配置代理服务器以访问外部资源。这对于下载容器镜像、更新软件包和与外部API交互至关重要。在本指南中,我们将讨论Kubernetes中代理的所有配置级别——从节点配置到单个Pods。
Kubernetes集群中为什么需要代理
企业环境中的Kubernetes集群通常在隔离的网络中运行,访问互联网的权限有限。代理服务器解决了几个关键问题:
- 下载容器镜像 — Docker Hub、Google Container Registry、私有注册表需要外部访问
- 更新软件包 — 在容器内通过apt、yum、pip安装依赖项
- 访问外部API — 与云服务、监控、日志记录集成
- 安全性 — 流量控制、域过滤、请求日志记录
- 缓存 — 加速对相同资源的重复请求
如果没有正确配置代理,您可能会在尝试部署应用程序时遇到“image pull failed”、“connection timeout”或“network unreachable”等错误。这对于自动化CI/CD管道尤其关键,因为每一秒的停机都会造成经济损失。
重要: 对于企业集群,建议使用具有高带宽和连接稳定性的数据中心代理,因为它们关系到整个基础设施的可用性。
Kubernetes中代理的配置级别
Kubernetes具有多层架构,代理需要根据任务在每个级别进行配置:
| 级别 | 配置内容 | 用途 |
|---|---|---|
| 操作系统 | 系统环境变量 | 访问工具(curl、wget、apt) |
| Container Runtime (Docker/containerd) | 守护进程配置 | 下载容器镜像 |
| kubelet | kubelet启动参数 | 与API服务器交互 |
| Pods | 清单中的环境变量 | 应用程序访问外部API |
| kubectl | 客户端环境变量 | 通过代理管理集群 |
每个级别都需要单独配置,遗漏任何一个都可能导致问题。例如,如果只为Docker配置了代理,但没有为Pods配置,则镜像会被下载,但容器内的应用程序无法访问外部API。
为Docker和containerd配置代理
Container runtime是需要配置的第一个组件,因为它负责从外部注册表下载容器镜像。我们将讨论这两个流行的runtime的配置。
为Docker配置代理
对于Docker,需要创建systemd drop-in文件,将环境变量添加到Docker服务中:
# 创建配置目录
sudo mkdir -p /etc/systemd/system/docker.service.d
# 创建代理设置文件
sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf <<EOF
[Service]
Environment="HTTP_PROXY=http://proxy.company.com:8080"
Environment="HTTPS_PROXY=http://proxy.company.com:8080"
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.cluster.local,.svc"
EOF
# 重新加载systemd配置
sudo systemctl daemon-reload
# 重启Docker
sudo systemctl restart docker
# 检查设置是否生效
sudo systemctl show --property=Environment docker
之后,Docker将能够通过代理服务器下载镜像。可以使用以下命令检查其工作:
docker pull nginx:latest
为containerd配置代理
在现代Kubernetes集群中,containerd被用作主要的container runtime。为其配置代理稍有不同:
# 创建配置目录
sudo mkdir -p /etc/systemd/system/containerd.service.d
# 创建代理设置文件
sudo tee /etc/systemd/system/containerd.service.d/http-proxy.conf <<EOF
[Service]
Environment="HTTP_PROXY=http://proxy.company.com:8080"
Environment="HTTPS_PROXY=http://proxy.company.com:8080"
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.cluster.local,.svc"
EOF
# 重新加载配置
sudo systemctl daemon-reload
sudo systemctl restart containerd
# 检查状态
sudo systemctl status containerd
建议: 如果您使用私有容器注册表,请将其域名添加到NO_PROXY中,以避免不必要的延迟和SSL证书问题。
为kubelet配置代理
Kubelet是Kubernetes的代理,运行在集群的每个节点上。它也需要访问API服务器和外部资源。配置取决于Kubernetes的安装方式。
对于kubeadm集群
如果您使用kubeadm,请通过systemd配置代理:
# 创建kubelet配置目录
sudo mkdir -p /etc/systemd/system/kubelet.service.d
# 创建代理设置文件
sudo tee /etc/systemd/system/kubelet.service.d/http-proxy.conf <<EOF
[Service]
Environment="HTTP_PROXY=http://proxy.company.com:8080"
Environment="HTTPS_PROXY=http://proxy.company.com:8080"
Environment="NO_PROXY=localhost,127.0.0.1,10.96.0.0/12,10.244.0.0/16,.cluster.local,.svc"
EOF
# 重新加载kubelet
sudo systemctl daemon-reload
sudo systemctl restart kubelet
对于托管Kubernetes(EKS、GKE、AKS)
在托管的Kubernetes服务中,kubelet的配置通常通过节点启动参数或用户数据脚本进行。例如,对于AWS EKS:
#!/bin/bash
# EKS工作节点的用户数据脚本
# 为系统配置代理
cat <<EOF >> /etc/environment
HTTP_PROXY=http://proxy.company.com:8080
HTTPS_PROXY=http://proxy.company.com:8080
NO_PROXY=localhost,127.0.0.1,169.254.169.254,.ec2.internal,.cluster.local
EOF
# 为kubelet配置
mkdir -p /etc/systemd/system/kubelet.service.d
cat <<EOF > /etc/systemd/system/kubelet.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.company.com:8080"
Environment="HTTPS_PROXY=http://proxy.company.com:8080"
Environment="NO_PROXY=localhost,127.0.0.1,169.254.169.254,.ec2.internal,.cluster.local"
EOF
systemctl daemon-reload
systemctl restart kubelet
请注意将169.254.169.254添加到NO_PROXY中——这是AWS元数据服务的地址,应该在没有代理的情况下访问。
在Pods级别配置代理
即使您为Docker和kubelet配置了代理,Pods内部的应用程序也不会自动使用代理。需要在Kubernetes清单中明确指定环境变量。
通过Deployment清单配置
最简单的方法是将环境变量添加到容器的规格中:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-company/my-app:latest
env:
- name: HTTP_PROXY
value: "http://proxy.company.com:8080"
- name: HTTPS_PROXY
value: "http://proxy.company.com:8080"
- name: NO_PROXY
value: "localhost,127.0.0.1,.cluster.local,.svc,10.0.0.0/8"
ports:
- containerPort: 8080
使用ConfigMap进行集中配置
为了避免在每个Deployment中重复配置代理,请创建ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: proxy-config
namespace: default
data:
HTTP_PROXY: "http://proxy.company.com:8080"
HTTPS_PROXY: "http://proxy.company.com:8080"
NO_PROXY: "localhost,127.0.0.1,.cluster.local,.svc,10.0.0.0/8"
然后在Deployment中使用这个ConfigMap:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: app
image: my-company/my-app:latest
envFrom:
- configMapRef:
name: proxy-config
这种方法简化了管理:当代理地址更改时,只需更新ConfigMap,Pods重启后新设置将自动应用。
通过MutatingWebhook自动注入
为了自动将代理变量添加到所有Pods中,可以使用MutatingAdmissionWebhook。这是一种高级方法,需要开发自己的webhook服务,但它允许集中管理设置,而无需更改应用程序的清单。
正确配置NO_PROXY
NO_PROXY变量定义了哪些地址和域名应该绕过代理服务器。NO_PROXY配置不正确是Kubernetes集群中问题的最常见原因。
Kubernetes的必需排除项
以下地址和范围必须始终在NO_PROXY中:
| 地址/范围 | 用途 |
|---|---|
localhost, 127.0.0.1 |
本地连接 |
.cluster.local |
集群内部DNS |
.svc |
Kubernetes服务 |
10.0.0.0/8 |
Pod网络(取决于CNI) |
10.96.0.0/12 |
服务网络(默认) |
172.16.0.0/12 |
Docker私有网络 |
192.168.0.0/16 |
私有局域网 |
NO_PROXY的完整配置示例
NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,10.96.0.0/12,.cluster.local,.svc,.default.svc,.default.svc.cluster.local,kubernetes.default.svc,kubernetes.default.svc.cluster.local
注意: 一些应用程序不支持NO_PROXY中的CIDR表示法。在这种情况下,请使用通配符:10.*代替10.0.0.0/8。
为kubectl配置代理
如果您通过位于代理后面的工作站管理集群,请为kubectl配置环境变量:
# 对于Linux/macOS - 添加到~/.bashrc或~/.zshrc
export HTTP_PROXY=http://proxy.company.com:8080
export HTTPS_PROXY=http://proxy.company.com:8080
export NO_PROXY=localhost,127.0.0.1,kubernetes.default.svc,.cluster.local
# 对于Windows PowerShell
$env:HTTP_PROXY="http://proxy.company.com:8080"
$env:HTTPS_PROXY="http://proxy.company.com:8080"
$env:NO_PROXY="localhost,127.0.0.1,kubernetes.default.svc,.cluster.local"
之后,kubectl将能够通过代理连接到集群的API服务器。检查其工作:
kubectl cluster-info
kubectl get nodes
带身份验证的代理配置
如果代理服务器需要身份验证,请将凭据添加到URL中:
export HTTP_PROXY=http://username:password@proxy.company.com:8080
export HTTPS_PROXY=http://username:password@proxy.company.com:8080
安全性: 不要在配置文件中以明文形式存储密码。使用环境变量或Kubernetes秘密来存储代理凭据。
诊断和解决常见问题
即使在正确配置的情况下,也可能会出现问题。让我们看看最常见的错误及其解决方法。
下载镜像时出现“ImagePullBackOff”错误
症状: Pods无法启动,事件中显示“Failed to pull image”或“connection timeout”错误。
诊断:
# 检查Pod事件
kubectl describe pod <pod-name>
# 检查Docker/containerd中的代理设置
sudo systemctl show --property=Environment docker
sudo systemctl show --property=Environment containerd
# 尝试在节点上手动下载镜像
sudo docker pull nginx:latest
sudo crictl pull nginx:latest
解决方案: 确保为container runtime配置了代理,并且镜像注册表的域名不在NO_PROXY中。
集群内部的DNS解析问题
症状: Pods无法通过DNS名称相互访问(例如,service-name.namespace.svc.cluster.local)。
诊断:
# 检查Pod中的DNS
kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup kubernetes.default
# 检查Pod中的代理变量
kubectl exec -it <pod-name> -- env | grep PROXY
解决方案: 将.cluster.local和.svc添加到NO_PROXY中。
访问外部API时的慢速或超时问题
症状: 应用程序运行缓慢或在请求外部服务时出现超时。
诊断:
# 检查Pod中的代理可用性
kubectl exec -it <pod-name> -- curl -v -x http://proxy.company.com:8080 https://www.google.com
# 测量响应时间
kubectl exec -it <pod-name> -- time curl -x http://proxy.company.com:8080 https://api.example.com
解决方案: 问题可能出在代理服务器的性能上。考虑使用地理位置接近的住宅代理以减少延迟。
通过代理时的SSL/TLS错误
症状: 出现“certificate verify failed”或“SSL handshake failed”等错误。
原因: 一些代理服务器执行SSL检查(解密HTTPS流量),这需要安装代理的根证书。
解决方案:
# 创建包含代理证书的ConfigMap
kubectl create configmap proxy-ca-cert --from-file=ca.crt=/path/to/proxy-ca.crt
# 将证书挂载到Pod并添加到系统存储中
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: app
volumeMounts:
- name: proxy-ca
mountPath: /usr/local/share/ca-certificates/proxy-ca.crt
subPath: ca.crt
volumes:
- name: proxy-ca
configMap:
name: proxy-ca-cert
生产环境中代理的最佳实践
根据在企业环境中运行Kubernetes集群的经验,以下是与代理可靠运行的建议:
1. 使用高可用的代理服务器
代理成为整个集群的单点故障。配置多个代理服务器通过负载均衡器:
HTTP_PROXY=http://proxy-lb.company.com:8080
其中proxy-lb.company.com是位于多个代理服务器前面的负载均衡器。
2. 集中管理配置
使用ConfigMap或Secret存储代理设置,而不是在每个清单中硬编码:
apiVersion: v1
kind: ConfigMap
metadata:
name: cluster-proxy-config
namespace: kube-system
data:
HTTP_PROXY: "http://proxy-lb.company.com:8080"
HTTPS_PROXY: "http://proxy-lb.company.com:8080"
NO_PROXY: "localhost,127.0.0.1,.cluster.local,.svc,10.0.0.0/8"
3. 监控和警报
配置代理服务器的可用性监控和问题警报:
- 代理的响应时间(应小于100ms对于本地代理)
- 连接代理的错误数量
- 集群中ImagePullBackOff事件的数量
- 代理服务器的CPU和网络负载
4. 记录NO_PROXY的排除项
记录哪些域名和IP地址被添加到NO_PROXY中及其原因。这将有助于故障排除和安全审计。
5. 在开发环境中测试更改
在生产中更改代理设置之前,请始终在开发/暂存集群中进行测试:
# 测试Pod以检查代理
apiVersion: v1
kind: Pod
metadata:
name: proxy-test
spec:
containers:
- name: test
image: curlimages/curl:latest
command: ["sleep", "3600"]
env:
- name: HTTP_PROXY
value: "http://new-proxy.company.com:8080"
- name: HTTPS_PROXY
value: "http://new-proxy.company.com:8080"
# 检查外部资源的可用性
kubectl exec -it proxy-test -- curl -v https://registry.k8s.io
kubectl exec -it proxy-test -- curl -v https://docker.io
6. 针对不同任务使用不同类型的代理
对于关键组件(下载镜像、集群API),使用快速的数据中心代理,而对于需要地理多样性的IP的应用程序——使用住宅或移动代理。
7. 定期更新NO_PROXY列表
在添加新服务或更改网络拓扑时,更新NO_PROXY。通过Helm图表或Kustomize自动化此过程:
# Helm图表的values.yaml
proxy:
enabled: true
http: "http://proxy.company.com:8080"
https: "http://proxy.company.com:8080"
noProxy:
- localhost
- 127.0.0.1
- .cluster.local
- .svc
- 10.0.0.0/8
- internal-service.company.com
结论
在Kubernetes集群中配置代理是一项多层任务,需要在每个级别上关注细节:从操作系统和container runtime到单个Pods。正确的配置确保集群的持续运行、安全访问外部资源以及符合企业安全政策。
需要记住的关键点:
- 在所有级别配置代理:操作系统、container runtime、kubelet、Pods
- 正确配置NO_PROXY,包括集群的所有内部网络
- 通过ConfigMap使用集中管理
- 监控代理服务器的可用性和性能
- 在生产中应用更改之前进行测试
对于关键的Kubernetes集群,建议使用可靠的数据中心代理,以确保高可用性和低延迟。这将确保基础设施的稳定运行,并最小化由于网络访问问题而导致的停机风险。
在出现问题时,使用系统的方法进行诊断:检查每个级别的设置,分析日志和事件,手动测试连接。大多数Kubernetes中与代理相关的问题都可以通过正确配置环境变量和NO_PROXY来解决。