关于 IPVS 以及部署Kubernetes 1.9.2 并启用IPVS和CoreDNS

IPVS简介

IPVS 即IP Virtual Server. 是基于Netfilter,用于IP层(三层)的负载均衡. 它是专门为负载均衡设计的,其底层使用哈希表数据结构,数据处理高效,并且允许无限扩容.

IPVS 的架构主要有IP包处理, 负载均衡算法, 参数配置管理这三个模块以及虚拟服务器与真实服务器链表(iptables)组成.可以说 IPVS 它依赖 iptables. 但 IPVS 模式承诺比 iptables 模式有更加高效的性能,也能支持更加庞大的集群.

在 Kubernetes 1.8 的时候,官方加入了 IPVS的支持(alpha). 上周发布的 1.9 中,已经将该特性置为 beta,但默认是 false,需要手动开启.

部署

整体部署的流程和之前写过的 创建基于Fannel网络的Kubernetes 1.7.6 类似,依旧是三个 Master,同时也做 Node 用.

只不过由于需要启用 IPVS ,所以 Node 节点的kubelet, kube-proxy 组件的配置参数有些微的改动.

目前 Calico 还未完美支持 IPVS 模式,所以如果尝试 IPVS ,推荐用 Flannel 网络.

证书的颁发与 etcd 的配置已经一些认证流程参考之前的文章,这里主要记录下 Kubernetes 几个组件的配置.

首先开启 IPVS modprobe ip_vs ip_vs_sh ip_vs_rr ip_vs_wrr. 手动管理 IPVS 可用 ipvsadm工具, RedHat 系的系统使用yum install -y ipvsadm安装 ,Debian 系的系统使用apt install -y ipvsadm安装.

每个系统上都设置环境变量:

# master1
# hostnamectl --static set-hostname master1
# master2
# hostnamectl --static set-hostname master2
# master3
# hostnamectl --static set-hostname master3

cat << EOF >>/etc/hosts
192.168.85.141 master1
192.168.85.142 master2
192.168.85.143 master3
EOF

export master1IP="192.168.85.141"
export master2IP="192.168.85.142"
export master3IP="192.168.85.143"
export hostname=`hostname`
eval tmp_ip=\\${hostname}IP # 第二个 前没有反斜杠,此添加是因为博客编辑器会将双 识别为 Latex 标记符而不显示
export hostIP=tmp_ip

然后下载 Kubernetes 1.9.2:

curl -SL https://dl.k8s.io/v1.9.2/kubernetes-server-linux-amd64.tar.gz -o kubernetes.tgz
tar -zxf kubernetes.tgz
mv kubernetes/server/bin/{kube-apiserver,kube-controller-manager,kubectl,kube-proxy,kube-scheduler,kubelet} /usr/local/bin

kube-apiserver

由于 Kubernetes 1.8 中 kube-apiserver 已经将AdvancedAuditing置为 Beta,并默认开启,因此必须要创建审计的文件,这里参考官方给的格式:

cat << EOF > audit-policy.yaml
apiVersion: audit.k8s.io/v1beta1
kind: Policy
rules:
- level: Metadata
EOF

scp audit-policy.yaml {master1IP}:/etc/kubernetes/
scp audit-policy.yaml{master2IP}:/etc/kubernetes/
scp audit-policy.yaml ${master3IP}:/etc/kubernetes/

kube-apiserver 的启动配置:

# 先创建 Kubernetes 日志目录
mkdir -p /var/log/kubernetes

cat << EOF > /usr/lib/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
Restart=on-failure
RestartSec=5
LimitNOFILE=infinity

ExecStart=/usr/local/bin/kube-apiserver 
  --admission-control=DefaultStorageClass,LimitRanger,NamespaceLifecycle,NodeRestriction,ResourceQuota,ServiceAccount 
  --advertise-address={hostIP}  --allow-privileged=true  --apiserver-count=3  --audit-log-maxage=10  --audit-log-maxbackup=3  --audit-log-maxsize=100  --audit-log-path=/var/log/kubernetes/audit.log  --audit-policy-file=/etc/kubernetes/audit-policy.yaml  --authorization-mode=Node,RBAC  --bind-address=0.0.0.0  --client-ca-file=/etc/kubernetes/ssl/ca.pem  --etcd-cafile=/etc/kubernetes/ssl/ca.pem  --etcd-certfile=/etc/kubernetes/ssl/etcd.pem  --etcd-keyfile=/etc/kubernetes/ssl/etcd-key.pem  --etcd-servers=https://{master1IP}:2379,https://{master2IP}:2379,https://{master3IP}:2379 
  --enable-bootstrap-token-auth 
  --insecure-bind-address=${hostIP} 
  --kubelet-https=true 
  --secure-port=6443 
  --service-account-key-file=/etc/kubernetes/ssl/ca-key.pem 
  --service-cluster-ip-range=10.254.0.0/16 
  --service-node-port-range=80-32000 
  --tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem 
  --tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem 
  --token-auth-file=/etc/kubernetes/token.csv

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable kube-apiserver
systemctl start kube-apiserver
systemctl status kube-apiserver
#出现错误使用 journalctl -u kube-apiserver 排错

Kubernetes 1.8 中,--experimental-bootstrap-token-auth已经被替换成了--enable-bootstrap-token-auth,并在1.9中--experimental-bootstrap-token-auth已被移除.

kube-controller-manager

kube-controller-manager 的启动配置:

cat << EOF >/usr/lib/systemd/system/kube-controller-manager.service
[Unit]
Description=kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
After=network-online.target
Wants=network-online.target

[Service]
Restart=on-failure
RestartSec=5
LimitNOFILE=infinity
ExecStart=/usr/local/bin/kube-controller-manager 
  --allocate-node-cidrs=true 
  --cluster-cidr=10.244.0.0/16 
  --cluster-signing-cert-file=/etc/kubernetes/ssl/ca.pem 
  --cluster-signing-key-file=/etc/kubernetes/ssl/ca-key.pem 
  --leader-elect=true 
  --master=http://${hostIP}:8080 
  --service-account-private-key-file=/etc/kubernetes/ssl/ca-key.pem 
  --service-cluster-ip-range=10.254.0.0/16 
  --root-ca-file=/etc/kubernetes/ssl/ca.pem

# server-cluster-ip-range 的值要与 kube-apiserver 中的一致,它是分配给 Service 的 IP,不能与cluster-cidr 的IP 段重合; 而 cluster-cidr 则要与 flannel 配置的一致,它是分配给 Pod 的IP.

[Install]
WantedBy=multi-user.target
EOF

# 启动 kube-controller-manager
systemctl daemon-reload
systemctl enable kube-controller-manager
systemctl start kube-controller-manager
systemctl status kube-controller-manager
# 出现错误使用journalctl -u kube-controller-manager 排错

kube-scheduler

kube-scheduler的启动配置:

cat << EOF> /usr/lib/systemd/system/kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
After=network-online.target
Wants=network-online.target

[Service]
Restart=on-failure
RestartSec=5
LimitNOFILE=infinity

ExecStart=/usr/local/bin/kube-scheduler 
  --master=http://${hostIP}:8080

[Install]
WantedBy=multi-user.target
EOF

# 启动 kube-scheduler
systemctl daemon-reload
systemctl enable kube-scheduler
systemctl start kube-scheduler
systemctl status kube-scheduler
# 出现错误使用journalctl -u kube-scheduler排错

kubelet

docker 的 Cgroup Driver 驱动方式使用docker info命令查看,此参数要保持和 kubelet 中--cgroup-driver参数的一致,kubelet 默认值为cgroupfs. 使用官方源 curl -SL https://get.docker.com | sh - 安装的 docker 默认驱动也为 cgroufs .

之后的 flannel 使用 DaemonSet 部署,需要使用 CNI ,因此每个 Node 节点上都需要存在 CNI 可执行文件,kubelet 中使用--cni-bin-dir 指定可执行文件路径,默认/opt/cni/bin:

curl -SL https://github.com/containernetworking/plugins/releases/download/v0.6.0/cni-plugins-amd64-v0.6.0.tgz -o cni.tgz
mkdir -p /opt/cni/bin
tar -zxf cni.tgz -C /opt/cni/bin/

要启用 IPVS,则 kubelet 中的参数 --hairpin-mode 要为默认的混杂模式 promiscuous-bridge,并在 --feature-gates 中指定 SupportIPVSProxyMode=true.

kubelet的启动配置:

# 先创建 kubelet 工作目录
mkdir -p /var/lib/kubelet

cat << EOF > /usr/lib/systemd/system/kubelet.service
[Unit]
Description=kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=docker.service
Requires=docker.service

[Service]
Restart=on-failure
RestartSec=5
LimitNOFILE=infinity
WorkingDirectory=/var/lib/kubelet

ExecStart=/usr/local/bin/kubelet 
  --allow-privileged=true 
  --cert-dir=/etc/kubernetes/ssl 
  --cluster_dns=10.254.0.2 
  --cluster_domain=cluster.local. 
  --cni-bin-dir=/opt/cni/bin 
  --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig 
  --fail-swap-on=false 
  --feature-gates=SupportIPVSProxyMode=true 
  --hostname-override=${hostIP} 
  --kubeconfig=/etc/kubernetes/kubelet.kubeconfig 
  --max-pods=256 
  --network-plugin=cni 
  --pod-infra-container-image=gcr.io/google_containers/pause:3.0 
  --require-kubeconfig 
  --serialize-image-pulls=false

# 参数 cluster_dns 需要预先定义 预分配的 dns 地址,然后对 kubedns 配置中的地址修改成一致即可.
# gcr.io/google_containers/pause 这个镜像可能出于GFW的原因在国内拉不到,可以先用中转的方式导入.
[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable kubelet
systemctl start kubelet
systemctl status kubelet
# 出现错误使用journalctl -u kubelet排错
# 在所有node节点都类似这样配置

kube-proxy

如果要开启 IPVS ,则 kube-proxy 的-feature-gates与 kubelet 一样,并且--masquerade-all参数必须指定,否则将不会生效.

# 创建keube-proxy 的工作目录
mkdir -p /var/lib/kube-proxy

cat <<EOF> /usr/lib/systemd/system/kube-proxy.service
[Unit]
Description=kubernetes kube-proxy
Documentation=https://github.com/kubernetes/kubernetes
After=docker.service
Requires=docker.service

[Service]
Restart=on-failure
RestartSec=5
LimitNOFILE=infinity
WorkingDirectory=/var/lib/kube-proxy
ExecStart=/usr/local/bin/kube-proxy 
  --bind-address={hostIP}  --hostname-override={hostIP} 
  --cluster-cidr=10.244.0.0/16 
  --feature-gates=SupportIPVSProxyMode=true 
  --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig 
  --masquerade-all 
  --ipvs-min-sync-period=30s 
  --ipvs-scheduler=rr 
  --ipvs-sync-period=1m


[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable kube-proxy
systemctl start kube-proxy
systemctl status kube-proxy
# 出现错误使用journalctl -u kube-proxy排错

flannel

确认已经安装 cni 组件,并且与 kubelet 中--cni-bin-dir参数的指向一致,指定参数--network-plugin=cni,参数--cluster-cidr的值与 flannel 中默认网段的要一致.

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml

CoreDNS

CoreDNS 最近也很火,原因是它比 KubeDNS 要更高效,更简洁,并且功能加强大.

KubeDNS 的配置中,可以看到其中调用了三个镜像,分别是 kubedns,dnsmasq,sidecar.其中kubedns会通过kube-apiserver监视 Kubernetes 集群提供的DNS记录,dnsmasq 负责提供 dns 服务器的功能,sidecar负责提供指标和健康检测的支持.

可以看到这样的分工其实无可厚非,但是有个很严重的问题就是 Kubernetes 中的 externalName 是不会被dnsmasq解析的,并且dnsmasq的漏洞同样会影响到 Kubernetes集群.而这些在 CoreDNS 中都将得到解决.

CoreDNS 是由Go编写的,它以 Plugin 的形式实现 KubeDNS 中的一些功能.运行后的 pod 只有一个 container.

首先下载 CoreDNS 的 daemonSet 描述文件,然后修改其中data字段的CLUSTER_DOMAIN为 kubelet 中指定的--cluster_domain值,SERVICE_CIDR改为 kube-apiserver 中--service-cluster-ip-range的值,POD_CIDR改为 kube-proxy 中--cluster-cidr的值,将CLUSTER_DNS_IP改为 kubelet 中--cluster_dns指定的值:

curl -SL -o coredns.yaml https://github.com/coredns/deployment/raw/master/kubernetes/coredns.yaml.sed 

sed -e "s/CLUSTER_DOMAIN/cluster.domain./g" -e "s/SERVICE_CIDR/10.254.0.0/16/g" -e "s/POD_CIDR/10.244.0.0./16/g" -e "s/CLUSTER_DNS_IP/10.254.0.2/g" -i coredns.yaml

# 部署coredns
kubectl apply -f coredns.yaml