k8s 二进制部署 十 (kube-proxy)

大番茄 2019年12月12日 1,149次浏览

v1.16

介绍:

service 资源实现了kubenetes中用来自动查找后端pod的功能, 在集群私有网络中的任何节点都可以通过访问service ip来访问后端的pod。但是service只是逻辑上的,相当于是配置。 而kube-proxy就是真正实现功能的组件。
有兴趣可以看看这里:
https://www.yxingxing.net/archives/kubernetes-20191104-service-network


kube-proxy 有三种模式,userspace, iptables, ipvs。

userspace:

是老版本的模式了, 在这个模式下kube-proxy就是一个代理,类似haproxy,通过iptables把流量转发到kube-proxy用户进程,由Kube-proxy再发送到目的pod。

iptables:

是现在最稳定的模式, 也是添加iptables规则,只不过是直接通过DNAT发送到后端pod。 而负载均衡的实现是通过添加多条规则,内容是iptables的statistic模块的random产生的不同随机值做运算来随机匹配规则。如:

-A KUBE-SVC-NPX46M4PTMTKRN6Y -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-LKAO6JA5PQWHK647
-A KUBE-SVC-NPX46M4PTMTKRN6Y -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-6AATE2IAMBDY74K4
-A KUBE-SVC-NPX46M4PTMTKRN6Y -j KUBE-SEP-LGKM7LHKYURGBIAK

因为iptables是为防火墙设计的, 是线性的数据结构,规则是一条一条的匹配, 所以在service很多的情况下,很影响性能, 并且在一些极端的情况下可能还会出现规则丢失的情况。

ipvs:

是新的模式, 在v1.11 进入GA版。 ipvs 与iptables的数据机构不同,在大量的service情况下,影响微乎其微。
通过添加ipvs规则实现转发与负载均衡,不过iptables也还是要用的,需要添加一些辅助的规则,如外部访问NodePort的service需要SNAT还要DNAT。还会添加一个kube-ipvs网卡,设置所有service的地址到上面, 同时local route也会更新。 我想是因为ipvs是添加在网络input上的钩子实现的,所以本机想要访问service需要进入input, 通过进入另外一个网卡来实现。
有兴趣可以看看这个:
https://www.yxingxing.net/archives/kubernetes-20191108-kubeproxy-network


kube-proxy通过watch apiserver的service, endpoint, node资源,实时监控资源的变化,来近乎实时的更新本机的相关规则。

这里使用的参数很少,大部分都是默认的,不过还是建议看一下其他参数,特别是conntrack-max-per-core参数:
https://www.yxingxing.net/archives/kubernetes-20191213-proxy-parameter

两种安装方式

安装方式一般是两种,一种是二进制在宿主机上安装,使用证书认证,这种方式已经被官方网站称为旧方法了:
https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/#other-authenticating-components
还有就是用DaemonSet启动,使用serviceaccount认证,这种方式更加的简单点。

kube-proxy如何区分的

就是以有没有提供kubeconfig文件, 或者是连接apiserver的变量。

[root@k8snode1 bin]# ./kube-proxy --cluster-cidr=10.1.0.0/16   --logtostderr=false --log-dir=/var/log  --proxy-mode=ipvs
F1220 09:24:51.783346    3066 server.go:495] unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined

如果提供变量,kube-proxy就会自动去找ServiceAccount

[root@k8snode1 bin]# export  KUBERNETES_SERVICE_HOST=https://192.168.1.231
[root@k8snode1 bin]# export KUBERNETES_SERVICE_PORT=6443
[root@k8snode1 bin]# ./kube-proxy --cluster-cidr=10.1.0.0/16   --logtostderr=false --log-dir=/var/log  --proxy-mode=ipvs
F1220 09:36:46.057337    6764 server.go:495] open /var/run/secrets/kubernetes.io/serviceaccount/token: no such file or directory

反过来,如果提供了kubeconfig文件, 自然是按文件的设置了。

二进制方式

1、生成证书

可以使用网上的一些工具,如: cfssl。 我这里使用自己的脚本。
https://www.yxingxing.net/archives/kubernetes-20191025-ca
主要就是注意证书的common name, 因为跟授权有关系。
kubernetes专门内置了kube-proxy 使用的 clusterrolebinding, 名字叫做system:node-proxier

[root@k8s-master1 ~]# kubectl describe clusterrolebinding/system:node-proxier
Name:         system:node-proxier
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
Role:
  Kind:  ClusterRole
  Name:  system:node-proxier
Subjects:
  Kind  Name               Namespace
  ----  ----               ---------
  User  system:kube-proxy 

绑定了用户system:kube-proxy。所以证书的common name使用system:kube-proxy就可以

[root@k8s-master ca]# ./openssl.sh build kube-proxy
Generating RSA private key, 2048 bit long modulus
.............................................................................................+++
...........................................................................................+++
e is 65537 (0x10001)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [CN]:
State or Province Name (full name) [BJ]:
Locality Name (eg, city) [BJ]:
Organization Name (eg, company) [atest.pub]:
Organizational Unit Name (eg, section) [op]:
Common Name (eg, your name or your server's hostname) [kube-proxy]:system:kube-proxy
Email Address [op@atest.pub]:

......

创建出来的证书:
image-976497a1

2、生成kubeconfig文件。

生成用于kube-proxy访问apiserver的kubeconfig文件。
需要kubectl命令生成,还需要ca证书,这些master节点都有, 把kube-proxy证书与key传到master节点任意目录就可以了。 在其他节点也可以,生成配置文件只是kubectl自身的功能,不需要权限。

[root@k8s-master kube-proxy]# tree
.
├── kube-proxy.crt
└── kube-proxy.key

0 directories, 2 files
[root@k8s-master kube-proxy]# kubectl config set-cluster mycluster --server=https://172.100.102.70:6443 --certificate-authority=/usr/local/k8s/ssl/cacert.pem --embed-certs --kubeconfig=kube-proxy.kubeconfig.yaml
Cluster "mycluster" set.
[root@k8s-master kube-proxy]# kubectl config set-credentials myuser --client-certificate=kube-proxy.crt --client-key=kube-proxy.key --embed-certs --kubeconfig=kube-proxy.kubeconfig.yaml 
User "myuser" set.
[root@k8s-master kube-proxy]# kubectl config set-context mycontext --cluster=mycluster --user=myuser --kubeconfig=kube-proxy.kubeconfig.yaml 
Context "mycontext" created.
[root@k8s-master kube-proxy]# kubectl config use-context mycontext --kubeconfig=kube-proxy.kubeconfig.yaml 
Switched to context "mycontext".

1、创建连接集群的配置,指定apiserver地址与集群ca证书(在连接的时候验证apiserver的真实性), --embed-certs表示把证书写到文件里, --kubeconfig表示把配置写到指定的文件,而不是默认的kubectl配置文件的路径。
2、创建用户信息,指定用户证书与key。
3、创建配置的上下文。 配置文件里可以写多套集群与用户的配置, 这个上下文就是把指定的集群信息与用户信息组合起来。
4、指定当前使用的上下文。


接下来把kube-proxy.kubeconfig.yaml文件传到kube-proxy的节点就行了,这里的证书不用,已经写到文件里了。

3、创建Unit文件

现在的目录结构:

[root@k8s-node1 k8s]# tree
.
├── bin
│   ├── kubelet
│   └── kube-proxy
├── conf
│   ├── bootstrp-kubeconfig
│   ├── kubeconfig
│   └── kube-proxy.kubeconfig.yaml
├── logs
└── ssl
    ├── kubelet-client-2019-11-24-02-03-44.pem
    ├── kubelet-client-current.pem -> /usr/local/k8s/ssl/kubelet-client-2019-11-24-02-03-44.pem
    ├── kubelet.crt
    └── kubelet.key

4 directories, 9 files

kubelet是之前安装kubelet的时候生成的,因为kube-proxy启动以后会去获取节点信息,如果本节点不是集群节点,会报错, 所以kubelet需要先运行。
flannel 也要先运行,kube-proxy有个参数需要获取本机的pod网段信息,用于生成相关的iptables规则。flannel可以提供。

[root@k8s-node1 k8s]# cat /lib/systemd/system/kube-proxy.service
[Unit]
Description=kubelet
After=network.target

[Service]
EnvironmentFile=/var/run/flannel/subnet.env
ExecStart=/usr/local/k8s/bin/kube-proxy\
--kubeconfig=/usr/local/k8s/conf/kube-proxy.kubeconfig.yaml \
--proxy-mode=ipvs \
--log-dir=/usr/local/k8s/logs \
--logtostderr=false \
--metrics-bind-address=0.0.0.0 \
--v=4 \
--cluster-cidr=${FLANNEL_NETWORK}
Restart=on-failure

[Install]
WantedBy=multi-user.target

--cluster-cidr 也可以直接写集群的POD范围,一般也不会修改,是整个集群的pod范围, 不是当前节点。

[root@k8s-node1 k8s]# cat /var/run/flannel/subnet.env
FLANNEL_NETWORK=10.1.0.0/16
FLANNEL_SUBNET=10.1.44.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
4、启动
[root@k8s-node1 k8s]# systemctl start kube-proxy

查看日志发现:

W1214 03:55:21.883814   30619 hostport_manager.go:68] The binary conntrack is not installed, this can cause failures in network connection cleanup.

直接yum安装conntrack就可以, 只是暂时还搞不懂具体的作用。

查看infor日志可以发现修改内核参数的信息:

image-0b3821e8

warning日志:

Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
W1214 03:56:04.673371   31011 server.go:208] WARNING: all flags other than --config, --write-config-to, and --cleanup are deprecated. Please begin using a config file ASAP.
W1214 03:56:04.751364   31011 proxier.go:420] IPVS scheduler not specified, use rr by default

ipvs默认rr调度,这个可以通过kube-proxy参数修改。


ipvs模块是否加载:

[root@k8s-node1 logs]# lsmod | grep ip_vs
ip_vs_sh               12688  0 
ip_vs_wrr              12697  0 
ip_vs_rr               12600  1 
ip_vs                 145497  7 ip_vs_rr,ip_vs_sh,ip_vs_wrr
nf_conntrack          137239  7 ip_vs,nf_nat,nf_nat_ipv4,xt_conntrack,nf_nat_masquerade_ipv4,nf_conntrack_netlink,nf_conntrack_ipv4
libcrc32c              12644  4 xfs,ip_vs,nf_nat,nf_conntrack
[root@k8s-node1 logs]# 

查看有没有规则,集群有一个表示自己的service, 是service 网段的第一个ip。

[root@k8s-node1 logs]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.0.0.1:443 rr
  -> 172.100.102.70:6443          Masq    1      0          0         
[root@k8s-node1 logs]# 

DaemonSet方式

下载的二进制安装包里面有除了kubelet以外的所有组件镜像文件。
image.png

自带的yaml文件

还有kube-proxy安装用的yaml文件。

kubernetes/cluster/addons/kube-proxy

[root@k8smaster1 kube-proxy]# ls
kube-proxy-ds.yaml   kube-proxy-rbac.yaml  OWNERS

其中rbac文件包含用到的serviceaccount的授权。另一个就是部署kube-proxy的daemonset了。
image.png

注意: 这个yaml文件里有nodeSelector

这个文件可能是用于salt管理工具批量安装的,这里把这些变量改吧改吧就能用。

上面的nodeselect看情况要不要删除。下面的日志路径也是看情况修改。
我这里改完以后是这个样子:

# Please keep kube-proxy configuration in-sync with:
# cluster/saltbase/salt/kube-proxy/kube-proxy.manifest

apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    k8s-app: kube-proxy
    addonmanager.kubernetes.io/mode: Reconcile
  name: kube-proxy
  namespace: kube-system
spec:
  selector:
    matchLabels:
      k8s-app: kube-proxy
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 10%
  template:
    metadata:
      labels:
        k8s-app: kube-proxy
    spec:
      priorityClassName: system-node-critical
      hostNetwork: true
      tolerations:
      - operator: "Exists"
        effect: "NoExecute"
      - operator: "Exists"
        effect: "NoSchedule"
      containers:
      - name: kube-proxy
        image: harbor.atest.pub/k8s/kube-proxy:v1.19.5
        resources:
          requests:
            cpu: 500m
        command:
        - /bin/sh
        - -c
        - kube-proxy --cluster-cidr=10.1.0.0/16 --logtostderr=false --log-dir=/var/log  --proxy-mode=ipvs 1>>/var/log/kube-proxy.log 2>&1
        env:
        - name: KUBERNETES_SERVICE_HOST
          value: 192.168.1.221
        - name: KUBERNETES_SERVICE_PORT
          value: '6443'
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /var/log
          name: varlog
          readOnly: false
        - mountPath: /run/xtables.lock
          name: xtables-lock
          readOnly: false
        - mountPath: /lib/modules
          name: lib-modules
          readOnly: true
      volumes:
      - name: varlog
        hostPath:
          path: /opt/kubernetes/log/kube-proxy
      - name: xtables-lock
        hostPath:
          path: /run/xtables.lock
          type: FileOrCreate
      - name: lib-modules
        hostPath:
          path: /lib/modules
      serviceAccountName: kube-proxy

上面的image指定的是我这里的内网harbor。

应用yaml文件就是了:

kubectl apply -f  kube-proxy-rbac.yaml
kubectl apply -f  kube-proxy-ds.yaml

驱逐与抢占

驱逐

为了节点稳定性,在节点可用资源不多时,会驱逐一些pod。
资源限制的配置,也是看规模了。 为了避免资源紧缺被驱逐,最好把memory也加上。 并且添加limit与requests相等。 Guaranteed pod
如:

        resources:
          requests:
            cpu: 500m
            memory: 512Mi
          limits:
            cpu: 500m
            memory: 512Mi

在get pod详情里可以看到这个字样。

[root@k8smaster1 kube-proxy]# kubectl get pods/kube-proxy-mwnnp -o yaml -n kube-system
......
 qosClass: Guaranteed
......
抢占

抢占可能比驱逐只是多了一个抢,
因为资源紧缺,新的pod调度不进来,如果新的pod比节点已存在的pod优先级高,就会把低优先级的pod干掉。让高优先级的pod进来。

priorityClassName: system-node-critical

system-node-critical 是集群公共优先级类中优先级最高的。
详情:
https://kubernetes.io/zh/docs/concepts/configuration/pod-priority-preemption/

容忍设置

还要注意上面的tolerations,容忍设置。这个必须要有。

因为有一种情况是:
节点因为kube-proxy没有部署导致是NoReady状态, 只有添加容忍,kube-proxy才能部署过去,节点才能变为Ready

优化

1、conntrack表空间大小

sysctl -w net.netfilter.nf_conntrack_max=1000000
echo "net.netfilter.nf_conntrack_max=1000000" >> /etc/sysctl.conf