部署 Node 节点

kubernetes Node 节点包含如下组件:

  • flanneld
  • docker
  • kubelet
  • kube-proxy

使用的变量

本文档用到的变量定义如下:

  1. $ # 替换为 kubernetes master 集群任一机器 IP
  2. $ export MASTER_IP=10.64.3.7
  3. $ export KUBE_APISERVER="https://${MASTER_IP}:6443"
  4. $ # 当前部署的节点 IP
  5. $ export NODE_IP=10.64.3.7
  6. $ # 导入用到的其它全局变量:ETCD_ENDPOINTS、FLANNEL_ETCD_PREFIX、CLUSTER_CIDR、CLUSTER_DNS_SVC_IP、CLUSTER_DNS_DOMAIN、SERVICE_CIDR
  7. $ source /root/local/bin/environment.sh
  8. $

安装和配置 flanneld

参考 05-部署Flannel网络.md

安装和配置 docker

下载最新的 docker 二进制文件

  1. $ wget https://get.docker.com/builds/Linux/x86_64/docker-17.04.0-ce.tgz
  2. $ tar -xvf docker-17.04.0-ce.tgz
  3. $ cp docker/docker* /root/local/bin
  4. $ cp docker/completion/bash/docker /etc/bash_completion.d/
  5. $

创建 docker 的 systemd unit 文件

  1. $ cat docker.service
  2. [Unit]
  3. Description=Docker Application Container Engine
  4. Documentation=http://docs.docker.io
  5. [Service]
  6. Environment="PATH=/root/local/bin:/bin:/sbin:/usr/bin:/usr/sbin"
  7. EnvironmentFile=-/run/flannel/docker
  8. ExecStart=/root/local/bin/dockerd --log-level=error $DOCKER_NETWORK_OPTIONS
  9. ExecReload=/bin/kill -s HUP $MAINPID
  10. Restart=on-failure
  11. RestartSec=5
  12. LimitNOFILE=infinity
  13. LimitNPROC=infinity
  14. LimitCORE=infinity
  15. Delegate=yes
  16. KillMode=process
  17. [Install]
  18. WantedBy=multi-user.target
  • dockerd 运行时会调用其它 docker 命令,如 docker-proxy,所以需要将 docker 命令所在的目录加到 PATH 环境变量中;
  • flanneld 启动时将网络配置写入到 /run/flannel/docker 文件中的变量 DOCKER_NETWORK_OPTIONS,dockerd 命令行上指定该变量值来设置 docker0 网桥参数;
  • 如果指定了多个 EnvironmentFile 选项,则必须将 /run/flannel/docker 放在最后(确保 docker0 使用 flanneld 生成的 bip 参数);
  • 不能关闭默认开启的 --iptables--ip-masq 选项;
  • 如果内核版本比较新,建议使用 overlay 存储驱动;
  • docker 从 1.13 版本开始,可能将 iptables FORWARD chain的默认策略设置为DROP,从而导致 ping 其它 Node 上的 Pod IP 失败,遇到这种情况时,需要手动设置策略为 ACCEPT

    1. $ sudo iptables -P FORWARD ACCEPT
    2. $

    并且把以下命令写入/etc/rc.local文件中,防止节点重启iptables FORWARD chain的默认策略又还原为DROP

    1. sleep 60 && /sbin/iptables -P FORWARD ACCEPT
  • 为了加快 pull image 的速度,可以使用国内的仓库镜像服务器,同时增加下载的并发数。(如果 dockerd 已经运行,则需要重启 dockerd 生效。)

    1. $ cat /etc/docker/daemon.json
    2. {
    3. "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn", "hub-mirror.c.163.com"],
    4. "max-concurrent-downloads": 10
    5. }

完整 unit 见 docker.service

启动 dockerd

  1. $ sudo cp docker.service /etc/systemd/system/docker.service
  2. $ sudo systemctl daemon-reload
  3. $ sudo systemctl stop firewalld
  4. $ sudo systemctl disable firewalld
  5. $ sudo iptables -F && sudo iptables -X && sudo iptables -F -t nat && sudo iptables -X -t nat
  6. $ sudo systemctl enable docker
  7. $ sudo systemctl start docker
  8. $
  • 需要关闭 firewalld(centos7)/ufw(ubuntu16.04),否则可能会重复创建的 iptables 规则;
  • 最好清理旧的 iptables rules 和 chains 规则;

检查 docker 服务

  1. $ docker version
  2. $

安装和配置 kubelet

kubelet 启动时向 kube-apiserver 发送 TLS bootstrapping 请求,需要先将 bootstrap token 文件中的 kubelet-bootstrap 用户赋予 system:node-bootstrapper 角色,然后 kubelet 才有权限创建认证请求(certificatesigningrequests):

  1. $ kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --user=kubelet-bootstrap
  2. $
  • --user=kubelet-bootstrap 是文件 /etc/kubernetes/token.csv 中指定的用户名,同时也写入了文件 /etc/kubernetes/bootstrap.kubeconfig

下载最新的 kubelet 和 kube-proxy 二进制文件

  1. $ wget https://dl.k8s.io/v1.6.2/kubernetes-server-linux-amd64.tar.gz
  2. $ tar -xzvf kubernetes-server-linux-amd64.tar.gz
  3. $ cd kubernetes
  4. $ tar -xzvf kubernetes-src.tar.gz
  5. $ sudo cp -r ./server/bin/{kube-proxy,kubelet} /root/local/bin/
  6. $

创建 kubelet bootstrapping kubeconfig 文件

  1. $ # 设置集群参数
  2. $ kubectl config set-cluster kubernetes \
  3. --certificate-authority=/etc/kubernetes/ssl/ca.pem \
  4. --embed-certs=true \
  5. --server=${KUBE_APISERVER} \
  6. --kubeconfig=bootstrap.kubeconfig
  7. $ # 设置客户端认证参数
  8. $ kubectl config set-credentials kubelet-bootstrap \
  9. --token=${BOOTSTRAP_TOKEN} \
  10. --kubeconfig=bootstrap.kubeconfig
  11. $ # 设置上下文参数
  12. $ kubectl config set-context default \
  13. --cluster=kubernetes \
  14. --user=kubelet-bootstrap \
  15. --kubeconfig=bootstrap.kubeconfig
  16. $ # 设置默认上下文
  17. $ kubectl config use-context default --kubeconfig=bootstrap.kubeconfig
  18. $ mv bootstrap.kubeconfig /etc/kubernetes/
  • --embed-certstrue 时表示将 certificate-authority 证书写入到生成的 bootstrap.kubeconfig 文件中;
  • 设置 kubelet 客户端认证参数时没有指定秘钥和证书,后续由 kube-apiserver 自动生成;

创建 kubelet 的 systemd unit 文件

  1. $ sudo mkdir /var/lib/kubelet # 必须先创建工作目录
  2. $ cat > kubelet.service <<EOF
  3. [Unit]
  4. Description=Kubernetes Kubelet
  5. Documentation=https://github.com/GoogleCloudPlatform/kubernetes
  6. After=docker.service
  7. Requires=docker.service
  8. [Service]
  9. WorkingDirectory=/var/lib/kubelet
  10. ExecStart=/root/local/bin/kubelet \\
  11. --address=${NODE_IP} \\
  12. --hostname-override=${NODE_IP} \\
  13. --pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure:latest \\
  14. --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig \\
  15. --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\
  16. --require-kubeconfig \\
  17. --cert-dir=/etc/kubernetes/ssl \\
  18. --cluster-dns=${CLUSTER_DNS_SVC_IP} \\
  19. --cluster-domain=${CLUSTER_DNS_DOMAIN} \\
  20. --hairpin-mode promiscuous-bridge \\
  21. --allow-privileged=true \\
  22. --serialize-image-pulls=false \\
  23. --logtostderr=true \\
  24. --v=2
  25. ExecStartPost=/sbin/iptables -A INPUT -s 10.0.0.0/8 -p tcp --dport 4194 -j ACCEPT
  26. ExecStartPost=/sbin/iptables -A INPUT -s 172.16.0.0/12 -p tcp --dport 4194 -j ACCEPT
  27. ExecStartPost=/sbin/iptables -A INPUT -s 192.168.0.0/16 -p tcp --dport 4194 -j ACCEPT
  28. ExecStartPost=/sbin/iptables -A INPUT -p tcp --dport 4194 -j DROP
  29. Restart=on-failure
  30. RestartSec=5
  31. [Install]
  32. WantedBy=multi-user.target
  33. EOF
  • --address 不能设置为 127.0.0.1,否则后续 Pods 访问 kubelet 的 API 接口时会失败,因为 Pods 访问的 127.0.0.1 指向自己而不是 kubelet;
  • 如果设置了 --hostname-override 选项,则 kube-proxy 也需要设置该选项,否则会出现找不到 Node 的情况;
  • --experimental-bootstrap-kubeconfig 指向 bootstrap kubeconfig 文件,kubelet 使用该文件中的用户名和 token 向 kube-apiserver 发送 TLS Bootstrapping 请求;
  • 管理员通过了 CSR 请求后,kubelet 自动在 --cert-dir 目录创建证书和私钥文件(kubelet-client.crtkubelet-client.key),然后写入 --kubeconfig 文件(自动创建 --kubeconfig 指定的文件);
  • 建议在 --kubeconfig 配置文件中指定 kube-apiserver 地址,如果未指定 --api-servers 选项,则必须指定 --require-kubeconfig 选项后才从配置文件中读取 kue-apiserver 的地址,否则 kubelet 启动后将找不到 kube-apiserver (日志中提示未找到 API Server),kubectl get nodes 不会返回对应的 Node 信息;
  • --cluster-dns 指定 kubedns 的 Service IP(可以先分配,后续创建 kubedns 服务时指定该 IP),--cluster-domain 指定域名后缀,这两个参数同时指定后才会生效;
  • kubelet cAdvisor 默认在所有接口监听 4194 端口的请求,对于有外网的机器来说不安全,ExecStartPost 选项指定的 iptables 规则只允许内网机器访问 4194 端口;

完整 unit 见 kubelet.service

启动 kubelet

  1. $ sudo cp kubelet.service /etc/systemd/system/kubelet.service
  2. $ sudo systemctl daemon-reload
  3. $ sudo systemctl enable kubelet
  4. $ sudo systemctl start kubelet
  5. $ systemctl status kubelet
  6. $

通过 kubelet 的 TLS 证书请求

kubelet 首次启动时向 kube-apiserver 发送证书签名请求,必须通过后 kubernetes 系统才会将该 Node 加入到集群。

查看未授权的 CSR 请求:

  1. $ kubectl get csr
  2. NAME AGE REQUESTOR CONDITION
  3. csr-2b308 4m kubelet-bootstrap Pending
  4. $ kubectl get nodes
  5. No resources found.

通过 CSR 请求:

  1. $ kubectl certificate approve csr-2b308
  2. certificatesigningrequest "csr-2b308" approved
  3. $ kubectl get nodes
  4. NAME STATUS AGE VERSION
  5. 10.64.3.7 Ready 49m v1.6.2

自动生成了 kubelet kubeconfig 文件和公私钥:

  1. $ ls -l /etc/kubernetes/kubelet.kubeconfig
  2. -rw------- 1 root root 2284 Apr 7 02:07 /etc/kubernetes/kubelet.kubeconfig
  3. $ ls -l /etc/kubernetes/ssl/kubelet*
  4. -rw-r--r-- 1 root root 1046 Apr 7 02:07 /etc/kubernetes/ssl/kubelet-client.crt
  5. -rw------- 1 root root 227 Apr 7 02:04 /etc/kubernetes/ssl/kubelet-client.key
  6. -rw-r--r-- 1 root root 1103 Apr 7 02:07 /etc/kubernetes/ssl/kubelet.crt
  7. -rw------- 1 root root 1675 Apr 7 02:07 /etc/kubernetes/ssl/kubelet.key

配置 kube-proxy

创建 kube-proxy 证书

创建 kube-proxy 证书签名请求:

  1. $ cat kube-proxy-csr.json
  2. {
  3. "CN": "system:kube-proxy",
  4. "hosts": [],
  5. "key": {
  6. "algo": "rsa",
  7. "size": 2048
  8. },
  9. "names": [
  10. {
  11. "C": "CN",
  12. "ST": "BeiJing",
  13. "L": "BeiJing",
  14. "O": "k8s",
  15. "OU": "System"
  16. }
  17. ]
  18. }
  • CN 指定该证书的 User 为 system:kube-proxy
  • kube-apiserver 预定义的 RoleBinding system:node-proxier 将User system:kube-proxy 与 Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限;
  • hosts 属性值为空列表;

生成 kube-proxy 客户端证书和私钥:

  1. $ cfssl gencert -ca=/etc/kubernetes/ssl/ca.pem \
  2. -ca-key=/etc/kubernetes/ssl/ca-key.pem \
  3. -config=/etc/kubernetes/ssl/ca-config.json \
  4. -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy
  5. $ ls kube-proxy*
  6. kube-proxy.csr kube-proxy-csr.json kube-proxy-key.pem kube-proxy.pem
  7. $ sudo mv kube-proxy*.pem /etc/kubernetes/ssl/
  8. $ rm kube-proxy.csr kube-proxy-csr.json
  9. $

创建 kube-proxy kubeconfig 文件

  1. $ # 设置集群参数
  2. $ kubectl config set-cluster kubernetes \
  3. --certificate-authority=/etc/kubernetes/ssl/ca.pem \
  4. --embed-certs=true \
  5. --server=${KUBE_APISERVER} \
  6. --kubeconfig=kube-proxy.kubeconfig
  7. $ # 设置客户端认证参数
  8. $ kubectl config set-credentials kube-proxy \
  9. --client-certificate=/etc/kubernetes/ssl/kube-proxy.pem \
  10. --client-key=/etc/kubernetes/ssl/kube-proxy-key.pem \
  11. --embed-certs=true \
  12. --kubeconfig=kube-proxy.kubeconfig
  13. $ # 设置上下文参数
  14. $ kubectl config set-context default \
  15. --cluster=kubernetes \
  16. --user=kube-proxy \
  17. --kubeconfig=kube-proxy.kubeconfig
  18. $ # 设置默认上下文
  19. $ kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
  20. $ mv kube-proxy.kubeconfig /etc/kubernetes/
  • 设置集群参数和客户端认证参数时 --embed-certs 都为 true,这会将 certificate-authorityclient-certificateclient-key 指向的证书文件内容写入到生成的 kube-proxy.kubeconfig 文件中;
  • kube-proxy.pem 证书中 CN 为 system:kube-proxykube-apiserver 预定义的 RoleBinding cluster-admin 将User system:kube-proxy 与 Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限;

创建 kube-proxy 的 systemd unit 文件

  1. $ sudo mkdir -p /var/lib/kube-proxy # 必须先创建工作目录
  2. $ cat > kube-proxy.service <<EOF
  3. [Unit]
  4. Description=Kubernetes Kube-Proxy Server
  5. Documentation=https://github.com/GoogleCloudPlatform/kubernetes
  6. After=network.target
  7. [Service]
  8. WorkingDirectory=/var/lib/kube-proxy
  9. ExecStart=/root/local/bin/kube-proxy \\
  10. --bind-address=${NODE_IP} \\
  11. --hostname-override=${NODE_IP} \\
  12. --cluster-cidr=${CLUSTER_CIDR} \\
  13. --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig \\
  14. --logtostderr=true \\
  15. --v=2
  16. Restart=on-failure
  17. RestartSec=5
  18. LimitNOFILE=65536
  19. [Install]
  20. WantedBy=multi-user.target
  21. EOF
  • --hostname-override 参数值必须与 kubelet 的值一致,否则 kube-proxy 启动后会找不到该 Node,从而不会创建任何 iptables 规则;
  • --cluster-cidr 必须与 kube-controller-manager 的 --cluster-cidr 选项值一致;
  • kube-proxy 根据 --cluster-cidr 判断集群内部和外部流量,指定 --cluster-cidr--masquerade-all 选项后 kube-proxy 才会对访问 Service IP 的请求做 SNAT;
  • --kubeconfig 指定的配置文件嵌入了 kube-apiserver 的地址、用户名、证书、秘钥等请求和认证信息;
  • 预定义的 RoleBinding cluster-admin 将User system:kube-proxy 与 Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限;

完整 unit 见 kube-proxy.service

启动 kube-proxy

  1. $ sudo cp kube-proxy.service /etc/systemd/system/
  2. $ sudo systemctl daemon-reload
  3. $ sudo systemctl enable kube-proxy
  4. $ sudo systemctl start kube-proxy
  5. $ systemctl status kube-proxy
  6. $

验证集群功能

定义文件:

  1. $ cat nginx-ds.yml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: nginx-ds
  6. labels:
  7. app: nginx-ds
  8. spec:
  9. type: NodePort
  10. selector:
  11. app: nginx-ds
  12. ports:
  13. - name: http
  14. port: 80
  15. targetPort: 80
  16. ---
  17. apiVersion: extensions/v1beta1
  18. kind: DaemonSet
  19. metadata:
  20. name: nginx-ds
  21. labels:
  22. addonmanager.kubernetes.io/mode: Reconcile
  23. spec:
  24. template:
  25. metadata:
  26. labels:
  27. app: nginx-ds
  28. spec:
  29. containers:
  30. - name: my-nginx
  31. image: nginx:1.7.9
  32. ports:
  33. - containerPort: 80

创建 Pod 和服务:

  1. $ kubectl create -f nginx-ds.yml
  2. service "nginx-ds" created
  3. daemonset "nginx-ds" created

检查节点状态

  1. $ kubectl get nodes
  2. NAME STATUS AGE VERSION
  3. 10.64.3.7 Ready 8d v1.6.2
  4. 10.64.3.8 Ready 8d v1.6.2

都为 Ready 时正常。

检查各 Node 上的 Pod IP 连通性

  1. $ kubectl get pods -o wide|grep nginx-ds
  2. nginx-ds-6ktz8 1/1 Running 0 5m 172.30.25.19 10.64.3.7
  3. nginx-ds-6ktz9 1/1 Running 0 5m 172.30.20.20 10.64.3.8

可见,nginx-ds 的 Pod IP 分别是 172.30.25.19172.30.20.20,在所有 Node 上分别 ping 这两个 IP,看是否连通。

检查服务 IP 和端口可达性

  1. $ kubectl get svc |grep nginx-ds
  2. nginx-ds 10.254.136.178 <nodes> 80:8744/TCP 11m

可见:

  • 服务IP:10.254.136.178
  • 服务端口:80
  • NodePort端口:8744

在所有 Node 上执行:

  1. $ curl 10.254.136.178 # `kubectl get svc |grep nginx-ds` 输出中的服务 IP
  2. $

预期输出 nginx 欢迎页面内容。

检查服务的 NodePort 可达性

在所有 Node 上执行:

  1. $ export NODE_IP=10.64.3.7 # 当前 Node 的 IP
  2. $ export NODE_PORT=8744 # `kubectl get svc |grep nginx-ds` 输出中 80 端口映射的 NodePort
  3. $ curl ${NODE_IP}:${NODE_PORT}
  4. $

预期输出 nginx 欢迎页面内容。