2054 kube-proxy

我们知道容器的特点是快速创建、快速销毁,Kubernetes Pod和容器一样只具有临时的生命周期,一个Pod随时有可能被终止或者漂移,随着集群的状态变化而变化,一旦Pod变化,则该Pod提供的服务也就无法访问,如果直接访问Pod则无法实现服务的连续性和高可用性,因此显然不能使用Pod地址作为服务暴露端口。

解决这个问题的办法和传统数据中心解决无状态服务高可用的思路完全一样,通过负载均衡和VIP实现后端真实服务的自动转发、故障转移。

这个负载均衡在Kubernetes中称为Service,VIP即Service ClusterIP,因此可以认为Kubernetes的Service就是一个四层负载均衡,Kubernetes对应的还有七层负载均衡Ingress,本文仅介绍Kubernetes Service。

这个Service就是由kube-proxy实现的,ClusterIP不会因为Pod状态改变而变,需要注意的是VIP即ClusterIP是个假的IP,这个IP在整个集群中根本不存在,当然也就无法通过IP协议栈无法路由,底层underlay设备更无法感知这个IP的存在,因此ClusterIP只能是单主机(Host Only)作用域可见,这个IP在其他节点以及集群外均无法访问。

Kubernetes为了实现在集群所有的节点都能够访问Service,kube-proxy默认会在所有的Node节点都创建这个VIP并且实现负载

部署策略:

  • 使用 kubeconfig 访问 apiserver 的安全端口;

  • 在 KubeProxyConfiguration 类型的 yaml 文件配置主要参数;

  • 使用 ipvs 代理模式;

01.创建Kube-proxy证书和私钥

cat > kube-proxy-csr.json <<EOF
{
  "CN": "system:kube-proxy",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "China",
      "L": "Beijing",
      "O": "Kubernetes",
      "OU": "Kubernetes",
      "ST": "Beijing"
    }
  ]
}
EOF

解释: CN:指定该证书的 User 为 system:kube-proxy; 预定义的 RoleBinding system:node-proxier 将User system:kube-proxy 与Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限; 该证书只会被 kube-proxy 当做 client 证书使用,所以 hosts 字段为空。

创建 kube-proxy 凭证与私钥:

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-proxy-csr.json | cfssljson -bare kube-proxy

结果将产生以下两个文件:

kube-proxy-key.pem
kube-proxy.pem

02.创建 kube-proxy kubeconfig 文件

export KUBE_APISERVER="https://apiserver-p001.svc.gxd88.cn:6443"
  • 设置集群参数

      kubectl config set-cluster kubernetes \
        --certificate-authority=ca.pem \
        --embed-certs=true \
        --server=${KUBE_APISERVER} \
        --kubeconfig=kube-proxy.kubeconfig
  • 设置客户端认证参数

      kubectl config set-credentials kube-proxy \
        --client-certificate=kube-proxy.pem \
        --client-key=kube-proxy-key.pem \
        --embed-certs=true \
        --kubeconfig=kube-proxy.kubeconfig
  • 设置上下文参数

      kubectl config set-context default \
        --cluster=kubernetes \
        --user=kube-proxy \
        --kubeconfig=kube-proxy.kubeconfig
  • 设置默认上下文

      kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
  • 设置集群参数和客户端认证参数时 --embed-certs 都为 true,这会将 certificate-authority、client-certificate 和 client-key 指向的证书文件内容写入到生成的 kube-proxy.kubeconfig 文件中;

  • kube-proxy.pem 证书中 CN 为 system:kube-proxy,kube-apiserver 预定义的 RoleBinding cluster-admin 将User system:kube-proxy 与 Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限;

03.维护kube-proxy配置文件

同kubelet一样, kube-proxy中的很多参数已经废弃需要单独的配置文件支持, 你可以使用 --write-config-to 选项生成该配置文件

注意: 如果kubelet中配置了hostnameOverride那么kube-proxy也需要相同的配置

cat  > /srv/kubernetes/conf/kube-proxy-config.yml <<EOF
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 10.40.61.116
clientConnection:
  acceptContentTypes: ""
  burst: 10
  contentType: application/vnd.kubernetes.protobuf
  kubeconfig: "/srv/kubernetes/kubeconfig/kube-proxy.kubeconfig"
  qps: 5
clusterCIDR: "10.243.0.0/16"
configSyncPeriod: 15m0s
conntrack:
  maxPerCore: 32768
  min: 131072
  tcpCloseWaitTimeout: 1h0m0s
  tcpEstablishedTimeout: 24h0m0s
detectLocalMode: ""
enableProfiling: false
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: "py-modelo2o08cn-p005.pek3.example.net"
iptables:
  masqueradeAll: false
  masqueradeBit: 14
  minSyncPeriod: 0s
  syncPeriod: 30s
ipvs:
  excludeCIDRs: null
  minSyncPeriod: 0s
  scheduler: ""
  strictARP: false
  syncPeriod: 30s
  tcpFinTimeout: 0s
  tcpTimeout: 0s
  udpTimeout: 0s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: "ipvs"
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
showHiddenMetricsForVersion: ""
udpIdleTimeout: 250ms
winkernel:
  enableDSR: false
  networkName: ""
  sourceVip: ""
EOF

参数说明:

bindAddress: 监听地址;

clientConnection.kubeconfig: 连接 apiserver 的 kubeconfig 文件

clusterCIDR: kube-proxy 根据 --cluster-cidr 判断集群内部和外部流量,指定 --cluster-cidr 或 --masquerade-all 选项后 kube-proxy 才会对访问 Service IP 的请求做 SNAT;

hostnameOverride: 参数值必须与 kubelet 的值一致,否则 kube-proxy 启动后会找不到该 Node,从而不会创建任何 ipvs 规则;

mode: 使用 ipvs 模式。

04.使用systemd管理kube-proxy

根据实际情况替换--hostname-override

cat > /etc/systemd/system/kube-proxy.service <<EOF
[Unit]
Description=Kubernetes Kube Proxy
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/srv/kubernetes/bin/kube-proxy \
  --config=/srv/kubernetes/conf/kube-proxy-config.yml \
  --v=2 \
  --logtostderr=false \
  --log-dir=/srv/kubernetes/log
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

05.启动/停止 kube-proxy

sudo /bin/systemctl daemon-reload
sudo /bin/systemctl enable kube-proxy.service

kube-proxy can be started and stopped as follows:

sudo systemctl start kube-proxy.service
sudo systemctl stop  kube-proxy.service

06.验证

检查服务:

systemctl status kube-proxy|grep Active
   Active: active (running) since 四 2020-04-16 15:46:21 CST; 1 day 22h ago

检查端口:

kube-proxy 监听 10249 和 10256 端口:

  • 10249:对外提供 /metrics;

  • 10256:对外提供 /healthz 的访问。

$ netstat -lnpt|grep kube-prox
tcp        0      0 127.0.0.1:10249         0.0.0.0:*               LISTEN      11562/kube-proxy
tcp6       0      0 :::10256                :::*                    LISTEN      11562/kube-proxy

07.测试

创建pod测试

kubectl run --image=nginx:1.17 nginx-app --port=80

创建service

kubectl  expose  deployment  nginx-app --port=80  --name nginx-app

查看ipvs规则

$ ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.244.0.1:443 rr
  -> 10.40.58.153:6443            Masq    1      0          0
  -> 10.40.58.154:6443            Masq    1      0          0
  -> 10.40.61.116:6443            Masq    1      0          0
TCP  10.244.155.238:80 rr
  -> 10.243.104.2:80              Masq    1      0          0

Q&A

Q: 启动kube-proxy 日志文件一直报如下错误

proxier.go:1950] Failed to list IPVS destinations, error: parseIP Error ip=[10 40 61 116 0 0 0 0 0 0 0 0 0 0 0 0]
proxier.go:1192] Failed to sync endpoint for service: 10.244.0.1:443/TCP, err: parseIP Error ip=[10 40 61 116 0 0 0 0 0 0 0 0 0 0 0 0]

最后更新于