之前按照和我一步步部署 kubernetes 集群的步骤一步一步的成功的使用二进制的方式安装了kubernetes集群,在该文档的基础上重新部署了最新的v1.8.2版本,实现了kube-apiserver的高可用、traefik ingress 的部署、在kubernetes上安装docker的私有仓库harbor、容器化kubernetes部分组建、使用阿里云日志服务收集日志。

部署完成后,你将理解系统各组件的交互原理,进而能快速解决实际问题,所以本文档主要适合于那些有一定kubernetes基础,想通过一步步部署的方式来学习和了解系统配置、运行原理的人。

本系列系文档适用于 CentOS 7Ubuntu 16.04 及以上版本系统,由于启用了 TLS 双向认证、RBAC 授权等严格的安全机制,建议从头开始部署,否则可能会认证、授权等失败!

有人问我为什么这么长的文章不分拆成几篇文章啊?这样阅读起来也方便啊,然而在我自己学习的过程中,这种整个一篇文章把一件事情从头到尾讲清楚的形式是最好的,能给读者提供一种沉浸式的学习体验,阅读完整个文章后有种酣畅淋漓的感觉,所以我选择这种一篇文章的形式。

扫描下面的二维码(或微信搜索k8s技术圈)关注我们的微信公众帐号,在微信公众帐号中回复 kube100.com 即可加入到我们的 kubernetes 讨论群里面共同学习。

目录

  1. 组件版本 && 集群环境
  2. 创建CA 证书和密钥
  3. 部署高可用etcd 集群
  4. 配置kubectl 命令行工具
  5. 部署Flannel 网络
  6. 部署master 节点
  7. kube-apiserver 高可用
  8. 部署node 节点
  9. 部署kubedns 插件
  10. 部署Dashboard 插件
  11. 部署Heapster 插件
  12. 部署Ingress
  13. 日志收集
  14. 私有仓库harbor 搭建
  15. 问题汇总
  16. 参考资料

1. 组件版本 && 集群环境

组件版本

  • Kubernetes 1.8.2
  • Docker 17.10.0-ce
  • Etcd 3.2.9
  • Flanneld
  • TLS 认证通信(所有组件,如etcd、kubernetes master 和node)
  • RBAC 授权
  • kubelet TLS Bootstrapping
  • kubedns、dashboard、heapster等插件
  • harbor,使用nfs后端存储

etcd 集群 && k8s master 机器 && k8s node 机器

  • master01:192.168.1.137
  • master02:192.168.1.138
  • master03/node03:192.168.1.170
  • 由于机器有限,所以我们将master03 也作为node 节点,后续有新的机器增加即可
  • node01: 192.168.1.161
  • node02: 192.168.1.162

集群环境变量

后面的嗯部署将会使用到的全局变量,定义如下(根据自己的机器、网络修改):

  1. # TLS Bootstrapping 使用的Token,可以使用命令 head -c 16 /dev/urandom | od -An -t x | tr -d ' ' 生成
  2. BOOTSTRAP_TOKEN="8981b594122ebed7596f1d3b69c78223"
  3. # 建议使用未用的网段来定义服务网段和Pod 网段
  4. # 服务网段(Service CIDR),部署前路由不可达,部署后集群内部使用IP:Port可达
  5. SERVICE_CIDR="10.254.0.0/16"
  6. # Pod 网段(Cluster CIDR),部署前路由不可达,部署后路由可达(flanneld 保证)
  7. CLUSTER_CIDR="172.30.0.0/16"
  8. # 服务端口范围(NodePort Range)
  9. NODE_PORT_RANGE="30000-32766"
  10. # etcd集群服务地址列表
  11. ETCD_ENDPOINTS="https://192.168.1.137:2379,https://192.168.1.138:2379,https://192.168.1.170:2379"
  12. # flanneld 网络配置前缀
  13. FLANNEL_ETCD_PREFIX="/kubernetes/network"
  14. # kubernetes 服务IP(预先分配,一般为SERVICE_CIDR中的第一个IP)
  15. CLUSTER_KUBERNETES_SVC_IP="10.254.0.1"
  16. # 集群 DNS 服务IP(从SERVICE_CIDR 中预先分配)
  17. CLUSTER_DNS_SVC_IP="10.254.0.2"
  18. # 集群 DNS 域名
  19. CLUSTER_DNS_DOMAIN="cluster.local."
  20. # MASTER API Server 地址
  21. MASTER_URL="k8s-api.virtual.local"

将上面变量保存为: env.sh,然后将脚本拷贝到所有机器的/usr/k8s/bin目录。

为方便后面迁移,我们在集群内定义一个域名用于访问apiserver,在每个节点的/etc/hosts文件中添加记录:192.168.1.137 k8s-api.virtual.local k8s-api

其中192.168.1.137为master01 的IP,暂时使用该IP 来做apiserver 的负载地址

如果你使用的是阿里云的ECS 服务,强烈建议你先将上述节点的安全组配置成允许所有访问,不然在安装过程中会遇到各种访问不了的问题,待集群配置成功以后再根据需要添加安全限制。

2. 创建CA 证书和密钥

kubernetes 系统各个组件需要使用TLS证书对通信进行加密,这里我们使用CloudFlare的PKI 工具集cfssl 来生成Certificate Authority(CA) 证书和密钥文件, CA 是自签名的证书,用来签名后续创建的其他TLS 证书。

安装 CFSSL

  1. $ wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
  2. $ chmod +x cfssl_linux-amd64
  3. $ sudo mv cfssl_linux-amd64 /usr/k8s/bin/cfssl
  4. $ wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
  5. $ chmod +x cfssljson_linux-amd64
  6. $ sudo mv cfssljson_linux-amd64 /usr/k8s/bin/cfssljson
  7. $ wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
  8. $ chmod +x cfssl-certinfo_linux-amd64
  9. $ sudo mv cfssl-certinfo_linux-amd64 /usr/k8s/bin/cfssl-certinfo
  10. $ export PATH=/usr/k8s/bin:$PATH
  11. $ mkdir ssl && cd ssl
  12. $ cfssl print-defaults config > config.json
  13. $ cfssl print-defaults csr > csr.json

为了方便,将/usr/k8s/bin设置成环境变量,为了重启也有效,可以将上面的export PATH=/usr/k8s/bin:$PATH添加到/etc/rc.local文件中。

创建CA

修改上面创建的config.json文件为ca-config.json

  1. $ cat ca-config.json
  2. {
  3. "signing": {
  4. "default": {
  5. "expiry": "87600h"
  6. },
  7. "profiles": {
  8. "kubernetes": {
  9. "expiry": "87600h",
  10. "usages": [
  11. "signing",
  12. "key encipherment",
  13. "server auth",
  14. "client auth"
  15. ]
  16. }
  17. }
  18. }
  19. }
  • config.json:可以定义多个profiles,分别指定不同的过期时间、使用场景等参数;后续在签名证书时使用某个profile;
  • signing: 表示该证书可用于签名其它证书;生成的ca.pem 证书中CA=TRUE
  • server auth: 表示client 可以用该CA 对server 提供的证书进行校验;
  • client auth: 表示server 可以用该CA 对client 提供的证书进行验证。

修改CA 证书签名请求为ca-csr.json

  1. $ cat ca-csr.json
  2. {
  3. "CN": "kubernetes",
  4. "key": {
  5. "algo": "rsa",
  6. "size": 2048
  7. },
  8. "names": [
  9. {
  10. "C": "CN",
  11. "L": "BeiJing",
  12. "ST": "BeiJing",
  13. "O": "k8s",
  14. "OU": "System"
  15. }
  16. ]
  17. }
  • CNCommon Name,kube-apiserver 从证书中提取该字段作为请求的用户名(User Name);浏览器使用该字段验证网站是否合法;
  • OOrganization,kube-apiserver 从证书中提取该字段作为请求用户所属的组(Group);

生成CA 证书和私钥:

  1. $ cfssl gencert -initca ca-csr.json | cfssljson -bare ca
  2. $ ls ca*
  3. $ ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem

分发证书

将生成的CA 证书、密钥文件、配置文件拷贝到所有机器的/etc/kubernetes/ssl目录下面:

  1. $ sudo mkdir -p /etc/kubernetes/ssl
  2. $ sudo cp ca* /etc/kubernetes/ssl

3. 部署高可用etcd 集群

kubernetes 系统使用etcd存储所有的数据,我们这里部署3个节点的etcd 集群,这3个节点直接复用kubernetes master的3个节点,分别命名为etcd01etcd02etcd03:

  • etcd01:192.168.1.137
  • etcd02:192.168.1.138
  • etcd03:192.168.1.170

定义环境变量

使用到的变量如下:

  1. $ export NODE_NAME=etcd01 # 当前部署的机器名称(随便定义,只要能区分不同机器即可)
  2. $ export NODE_IP=192.168.1.137 # 当前部署的机器IP
  3. $ export NODE_IPS="192.168.1.137 192.168.1.138 192.168.1.170" # etcd 集群所有机器 IP
  4. $ # etcd 集群间通信的IP和端口
  5. $ export ETCD_NODES=etcd01=https://192.168.1.137:2380,etcd02=https://192.168.1.138:2380,etcd03=https://192.168.1.170:2380
  6. $ # 导入用到的其它全局变量:ETCD_ENDPOINTS、FLANNEL_ETCD_PREFIX、CLUSTER_CIDR
  7. $ source /usr/k8s/bin/env.sh

下载etcd 二进制文件

到[https://github.com/coreos/etcd/releases]()页面下载最新版本的二进制文件:

  1. $ wget https://github.com/coreos/etcd/releases/download/v3.2.9/etcd-v3.2.9-linux-amd64.tar.gz
  2. $ tar -xvf etcd-v3.2.9-linux-amd64.tar.gz
  3. $ sudo mv etcd-v3.2.9-linux-amd64/etcd* /usr/k8s/bin/

创建TLS 密钥和证书

为了保证通信安全,客户端(如etcdctl)与etcd 集群、etcd 集群之间的通信需要使用TLS 加密。

创建etcd 证书签名请求:

  1. $ cat > etcd-csr.json <<EOF
  2. {
  3. "CN": "etcd",
  4. "hosts": [
  5. "127.0.0.1",
  6. "${NODE_IP}"
  7. ],
  8. "key": {
  9. "algo": "rsa",
  10. "size": 2048
  11. },
  12. "names": [
  13. {
  14. "C": "CN",
  15. "ST": "BeiJing",
  16. "L": "BeiJing",
  17. "O": "k8s",
  18. "OU": "System"
  19. }
  20. ]
  21. }
  22. EOF
  • hosts 字段指定授权使用该证书的etcd节点IP

生成etcd证书和私钥:

  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 etcd-csr.json | cfssljson -bare etcd
  5. $ ls etcd*
  6. etcd.csr etcd-csr.json etcd-key.pem etcd.pem
  7. $ sudo mkdir -p /etc/etcd/ssl
  8. $ sudo mv etcd*.pem /etc/etcd/ssl/

创建etcd 的systemd unit 文件

  1. $ sudo mkdir -p /var/lib/etcd # 必须要先创建工作目录
  2. $ cat > etcd.service <<EOF
  3. [Unit]
  4. Description=Etcd Server
  5. After=network.target
  6. After=network-online.target
  7. Wants=network-online.target
  8. Documentation=https://github.com/coreos
  9. [Service]
  10. Type=notify
  11. WorkingDirectory=/var/lib/etcd/
  12. ExecStart=/usr/k8s/bin/etcd \\
  13. --name=${NODE_NAME} \\
  14. --cert-file=/etc/etcd/ssl/etcd.pem \\
  15. --key-file=/etc/etcd/ssl/etcd-key.pem \\
  16. --peer-cert-file=/etc/etcd/ssl/etcd.pem \\
  17. --peer-key-file=/etc/etcd/ssl/etcd-key.pem \\
  18. --trusted-ca-file=/etc/kubernetes/ssl/ca.pem \\
  19. --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \\
  20. --initial-advertise-peer-urls=https://${NODE_IP}:2380 \\
  21. --listen-peer-urls=https://${NODE_IP}:2380 \\
  22. --listen-client-urls=https://${NODE_IP}:2379,http://127.0.0.1:2379 \\
  23. --advertise-client-urls=https://${NODE_IP}:2379 \\
  24. --initial-cluster-token=etcd-cluster-0 \\
  25. --initial-cluster=${ETCD_NODES} \\
  26. --initial-cluster-state=new \\
  27. --data-dir=/var/lib/etcd
  28. Restart=on-failure
  29. RestartSec=5
  30. LimitNOFILE=65536
  31. [Install]
  32. WantedBy=multi-user.target
  33. EOF
  • 指定etcd的工作目录和数据目录为/var/lib/etcd,需要在启动服务前创建这个目录;
  • 为了保证通信安全,需要指定etcd 的公私钥(cert-file和key-file)、Peers通信的公私钥和CA 证书(peer-cert-file、peer-key-file、peer-trusted-ca-file)、客户端的CA 证书(trusted-ca-file);
  • --initial-cluster-state值为new时,--name的参数值必须位于--initial-cluster列表中;

启动etcd 服务

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

最先启动的etcd 进程会卡住一段时间,等待其他节点启动加入集群,在所有的etcd 节点重复上面的步骤,直到所有的机器etcd 服务都已经启动。

验证服务

部署完etcd 集群后,在任一etcd 节点上执行下面命令:

  1. for ip in ${NODE_IPS}; do
  2. ETCDCTL_API=3 /usr/k8s/bin/etcdctl \
  3. --endpoints=https://${ip}:2379 \
  4. --cacert=/etc/kubernetes/ssl/ca.pem \
  5. --cert=/etc/etcd/ssl/etcd.pem \
  6. --key=/etc/etcd/ssl/etcd-key.pem \
  7. endpoint health; done

输出如下结果:

  1. https://192.168.1.137:2379 is healthy: successfully committed proposal: took = 1.509032ms
  2. https://192.168.1.138:2379 is healthy: successfully committed proposal: took = 1.639228ms
  3. https://192.168.1.170:2379 is healthy: successfully committed proposal: took = 1.4152ms

可以看到上面的信息3个节点上的etcd 均为healthy,则表示集群服务正常。

4. 配置kubectl 命令行工具

kubectl默认从~/.kube/config配置文件中获取访问kube-apiserver 地址、证书、用户名等信息,需要正确配置该文件才能正常使用kubectl命令。

需要将下载的kubectl 二进制文件和生产的~/.kube/config配置文件拷贝到需要使用kubectl 命令的机器上。

很多童鞋说这个地方不知道在哪个节点上执行,kubectl只是一个和kube-apiserver进行交互的一个命令行工具,所以你想安装到那个节点都想,master或者node任意节点都可以,比如你先在master节点上安装,这样你就可以在master节点使用kubectl命令行工具了,如果你想在node节点上使用(当然安装的过程肯定会用到的),你就把master上面的kubectl二进制文件和~/.kube/config文件拷贝到对应的node节点上就行了。

环境变量

  1. $ source /usr/k8s/bin/env.sh
  2. $ export KUBE_APISERVER="https://${MASTER_URL}:6443"

注意这里的KUBE_APISERVER地址,因为我们还没有安装haproxy,所以暂时需要手动指定使用apiserver的6443端口,等haproxy安装完成后就可以用使用443端口转发到6443端口去了。

  • 变量KUBE_APISERVER 指定kubelet 访问的kube-apiserver 的地址,后续被写入~/.kube/config配置文件

下载kubectl

  1. $ wget https://dl.k8s.io/v1.8.2/kubernetes-client-linux-amd64.tar.gz # 如果服务器上下载不下来,可以想办法下载到本地,然后scp上去即可
  2. $ tar -xzvf kubernetes-client-linux-amd64.tar.gz
  3. $ sudo cp kubernetes/client/bin/kube* /usr/k8s/bin/
  4. $ sudo chmod a+x /usr/k8s/bin/kube*
  5. $ export PATH=/usr/k8s/bin:$PATH

创建admin 证书

kubectl 与kube-apiserver 的安全端口通信,需要为安全通信提供TLS 证书和密钥。创建admin 证书签名请求:

  1. $ cat > admin-csr.json <<EOF
  2. {
  3. "CN": "admin",
  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": "system:masters",
  15. "OU": "System"
  16. }
  17. ]
  18. }
  19. EOF
  • 后续kube-apiserver使用RBAC 对客户端(如kubelet、kube-proxy、Pod)请求进行授权
  • kube-apiserver 预定义了一些RBAC 使用的RoleBindings,如cluster-admin 将Group system:masters与Role cluster-admin绑定,该Role 授予了调用kube-apiserver所有API 的权限
  • O 指定了该证书的Group 为system:masters,kubectl使用该证书访问kube-apiserver时,由于证书被CA 签名,所以认证通过,同时由于证书用户组为经过预授权的system:masters,所以被授予访问所有API 的劝降
  • hosts 属性值为空列表

生成admin 证书和私钥:

  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 admin-csr.json | cfssljson -bare admin
  5. $ ls admin
  6. admin.csr admin-csr.json admin-key.pem admin.pem
  7. $ sudo mv admin*.pem /etc/kubernetes/ssl/

创建kubectl 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. # 设置客户端认证参数
  7. $ kubectl config set-credentials admin \
  8. --client-certificate=/etc/kubernetes/ssl/admin.pem \
  9. --embed-certs=true \
  10. --client-key=/etc/kubernetes/ssl/admin-key.pem \
  11. --token=${BOOTSTRAP_TOKEN}
  12. # 设置上下文参数
  13. $ kubectl config set-context kubernetes \
  14. --cluster=kubernetes \
  15. --user=admin
  16. # 设置默认上下文
  17. $ kubectl config use-context kubernetes
  • admin.pem证书O 字段值为system:masterskube-apiserver 预定义的 RoleBinding cluster-admin 将 Group system:masters 与 Role cluster-admin 绑定,该 Role 授予了调用kube-apiserver 相关 API 的权限
  • 生成的kubeconfig 被保存到 ~/.kube/config 文件

分发kubeconfig 文件

~/.kube/config文件拷贝到运行kubectl命令的机器的~/.kube/目录下去。

5. 部署Flannel 网络

kubernetes 要求集群内各节点能通过Pod 网段互联互通,下面我们来使用Flannel 在所有节点上创建互联互通的Pod 网段的步骤。

需要在所有的Node节点安装。

环境变量

  1. $ export NODE_IP=192.168.1.137 # 当前部署节点的IP
  2. # 导入全局变量
  3. $ source /usr/k8s/bin/env.sh

创建TLS 密钥和证书

etcd 集群启用了双向TLS 认证,所以需要为flanneld 指定与etcd 集群通信的CA 和密钥。

创建flanneld 证书签名请求:

  1. $ cat > flanneld-csr.json <<EOF
  2. {
  3. "CN": "flanneld",
  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. }
  19. EOF

生成flanneld 证书和私钥:

  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 flanneld-csr.json | cfssljson -bare flanneld
  5. $ ls flanneld*
  6. flanneld.csr flanneld-csr.json flanneld-key.pem flanneld.pem
  7. $ sudo mkdir -p /etc/flanneld/ssl
  8. $ sudo mv flanneld*.pem /etc/flanneld/ssl

向etcd 写入集群Pod 网段信息

该步骤只需在第一次部署Flannel 网络时执行,后续在其他节点上部署Flanneld 时无需再写入该信息

  1. $ etcdctl \
  2. --endpoints=${ETCD_ENDPOINTS} \
  3. --ca-file=/etc/kubernetes/ssl/ca.pem \
  4. --cert-file=/etc/flanneld/ssl/flanneld.pem \
  5. --key-file=/etc/flanneld/ssl/flanneld-key.pem \
  6. set ${FLANNEL_ETCD_PREFIX}/config '{"Network":"'${CLUSTER_CIDR}'", "SubnetLen": 24, "Backend": {"Type": "vxlan"}}'
  7. # 得到如下反馈信息
  8. {"Network":"172.30.0.0/16", "SubnetLen": 24, "Backend": {"Type": "vxlan"}}
  • 写入的 Pod 网段(${CLUSTER_CIDR},172.30.0.0/16) 必须与kube-controller-manager 的 --cluster-cidr 选项值一致;

安装和配置flanneld

前往flanneld release页面下载最新版的flanneld 二进制文件:

  1. $ mkdir flannel
  2. $ wget https://github.com/coreos/flannel/releases/download/v0.9.0/flannel-v0.9.0-linux-amd64.tar.gz
  3. $ tar -xzvf flannel-v0.9.0-linux-amd64.tar.gz -C flannel
  4. $ sudo cp flannel/{flanneld,mk-docker-opts.sh} /usr/k8s/bin

创建flanneld的systemd unit 文件

  1. $ cat > flanneld.service << EOF
  2. [Unit]
  3. Description=Flanneld overlay address etcd agent
  4. After=network.target
  5. After=network-online.target
  6. Wants=network-online.target
  7. After=etcd.service
  8. Before=docker.service
  9. [Service]
  10. Type=notify
  11. ExecStart=/usr/k8s/bin/flanneld \\
  12. -etcd-cafile=/etc/kubernetes/ssl/ca.pem \\
  13. -etcd-certfile=/etc/flanneld/ssl/flanneld.pem \\
  14. -etcd-keyfile=/etc/flanneld/ssl/flanneld-key.pem \\
  15. -etcd-endpoints=${ETCD_ENDPOINTS} \\
  16. -etcd-prefix=${FLANNEL_ETCD_PREFIX}
  17. ExecStartPost=/usr/k8s/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker
  18. Restart=on-failure
  19. [Install]
  20. WantedBy=multi-user.target
  21. RequiredBy=docker.service
  22. EOF
  • mk-docker-opts.sh脚本将分配给flanneld 的Pod 子网网段信息写入到/run/flannel/docker 文件中,后续docker 启动时使用这个文件中的参数值为 docker0 网桥
  • flanneld 使用系统缺省路由所在的接口和其他节点通信,对于有多个网络接口的机器(内网和公网),可以用 --iface 选项值指定通信接口(上面的 systemd unit 文件没指定这个选项)

启动flanneld

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

检查flanneld 服务

  1. ifconfig flannel.1

检查分配给各flanneld 的Pod 网段信息

  1. $ # 查看集群 Pod 网段(/16)
  2. $ etcdctl \
  3. --endpoints=${ETCD_ENDPOINTS} \
  4. --ca-file=/etc/kubernetes/ssl/ca.pem \
  5. --cert-file=/etc/flanneld/ssl/flanneld.pem \
  6. --key-file=/etc/flanneld/ssl/flanneld-key.pem \
  7. get ${FLANNEL_ETCD_PREFIX}/config
  8. { "Network": "172.30.0.0/16", "SubnetLen": 24, "Backend": { "Type": "vxlan" } }
  9. $ # 查看已分配的 Pod 子网段列表(/24)
  10. $ etcdctl \
  11. --endpoints=${ETCD_ENDPOINTS} \
  12. --ca-file=/etc/kubernetes/ssl/ca.pem \
  13. --cert-file=/etc/flanneld/ssl/flanneld.pem \
  14. --key-file=/etc/flanneld/ssl/flanneld-key.pem \
  15. ls ${FLANNEL_ETCD_PREFIX}/subnets
  16. /kubernetes/network/subnets/172.30.77.0-24
  17. $ # 查看某一 Pod 网段对应的 flanneld 进程监听的 IP 和网络参数
  18. $ etcdctl \
  19. --endpoints=${ETCD_ENDPOINTS} \
  20. --ca-file=/etc/kubernetes/ssl/ca.pem \
  21. --cert-file=/etc/flanneld/ssl/flanneld.pem \
  22. --key-file=/etc/flanneld/ssl/flanneld-key.pem \
  23. get ${FLANNEL_ETCD_PREFIX}/subnets/172.30.77.0-24
  24. {"PublicIP":"192.168.1.137","BackendType":"vxlan","BackendData":{"VtepMAC":"62:fc:03:83:1b:2b"}}

确保各节点间Pod 网段能互联互通

在各个节点部署完Flanneld 后,查看已分配的Pod 子网段列表:

  1. $ etcdctl \
  2. --endpoints=${ETCD_ENDPOINTS} \
  3. --ca-file=/etc/kubernetes/ssl/ca.pem \
  4. --cert-file=/etc/flanneld/ssl/flanneld.pem \
  5. --key-file=/etc/flanneld/ssl/flanneld-key.pem \
  6. ls ${FLANNEL_ETCD_PREFIX}/subnets
  7. /kubernetes/network/subnets/172.30.19.0-24
  8. /kubernetes/network/subnets/172.30.30.0-24
  9. /kubernetes/network/subnets/172.30.77.0-24
  10. /kubernetes/network/subnets/172.30.41.0-24
  11. /kubernetes/network/subnets/172.30.83.0-24

当前五个节点分配的 Pod 网段分别是:172.30.77.0-24、172.30.30.0-24、172.30.19.0-24、172.30.41.0-24、172.30.83.0-24。

6. 部署master 节点

kubernetes master 节点包含的组件有:

  • kube-apiserver
  • kube-scheduler
  • kube-controller-manager

目前这3个组件需要部署到同一台机器上:(后面再部署高可用的master)

  • kube-schedulerkube-controller-manager 和 kube-apiserver 三者的功能紧密相关;
  • 同时只能有一个 kube-schedulerkube-controller-manager 进程处于工作状态,如果运行多个,则需要通过选举产生一个 leader;

master 节点与node 节点上的Pods 通过Pod 网络通信,所以需要在master 节点上部署Flannel 网络。

环境变量

  1. $ export NODE_IP=192.168.1.137 # 当前部署的master 机器IP
  2. $ source /usr/k8s/bin/env.sh

下载最新版本的二进制文件

kubernetes changelog 页面下载最新版本的文件:

  1. $ wget https://dl.k8s.io/v1.8.2/kubernetes-server-linux-amd64.tar.gz
  2. $ tar -xzvf kubernetes-server-linux-amd64.tar.gz

将二进制文件拷贝到/usr/k8s/bin目录

  1. $ sudo cp -r server/bin/{kube-apiserver,kube-controller-manager,kube-scheduler} /usr/k8s/bin/

创建kubernetes 证书

创建kubernetes 证书签名请求:

  1. $ cat > kubernetes-csr.json <<EOF
  2. {
  3. "CN": "kubernetes",
  4. "hosts": [
  5. "127.0.0.1",
  6. "${NODE_IP}",
  7. "${MASTER_URL}",
  8. "${CLUSTER_KUBERNETES_SVC_IP}",
  9. "kubernetes",
  10. "kubernetes.default",
  11. "kubernetes.default.svc",
  12. "kubernetes.default.svc.cluster",
  13. "kubernetes.default.svc.cluster.local"
  14. ],
  15. "key": {
  16. "algo": "rsa",
  17. "size": 2048
  18. },
  19. "names": [
  20. {
  21. "C": "CN",
  22. "ST": "BeiJing",
  23. "L": "BeiJing",
  24. "O": "k8s",
  25. "OU": "System"
  26. }
  27. ]
  28. }
  29. EOF
  • 如果 hosts 字段不为空则需要指定授权使用该证书的 IP 或域名列表,所以上面分别指定了当前部署的 master 节点主机 IP 以及apiserver 负载的内部域名
  • 还需要添加 kube-apiserver 注册的名为 kubernetes 的服务 IP (Service Cluster IP),一般是 kube-apiserver --service-cluster-ip-range 选项值指定的网段的第一个IP,如 “10.254.0.1”

生成kubernetes 证书和私钥:

  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 kubernetes-csr.json | cfssljson -bare kubernetes
  5. $ ls kubernetes*
  6. kubernetes.csr kubernetes-csr.json kubernetes-key.pem kubernetes.pem
  7. $ sudo mkdir -p /etc/kubernetes/ssl/
  8. $ sudo mv kubernetes*.pem /etc/kubernetes/ssl/

6.1 配置和启动kube-apiserver

创建kube-apiserver 使用的客户端token 文件

kubelet 首次启动时向kube-apiserver 发送TLS Bootstrapping 请求,kube-apiserver 验证请求中的token 是否与它配置的token.csv 一致,如果一致则自动为kubelet 生成证书和密钥。

  1. $ # 导入的 environment.sh 文件定义了 BOOTSTRAP_TOKEN 变量
  2. $ cat > token.csv <<EOF
  3. ${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"
  4. EOF
  5. $ sudo mv token.csv /etc/kubernetes/

创建kube-apiserver 的systemd unit文件

  1. $ cat > kube-apiserver.service <<EOF
  2. [Unit]
  3. Description=Kubernetes API Server
  4. Documentation=https://github.com/GoogleCloudPlatform/kubernetes
  5. After=network.target
  6. [Service]
  7. ExecStart=/usr/k8s/bin/kube-apiserver \\
  8. --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
  9. --advertise-address=${NODE_IP} \\
  10. --bind-address=0.0.0.0 \\
  11. --insecure-bind-address=${NODE_IP} \\
  12. --authorization-mode=Node,RBAC \\
  13. --runtime-config=rbac.authorization.k8s.io/v1alpha1 \\
  14. --kubelet-https=true \\
  15. --experimental-bootstrap-token-auth \\
  16. --token-auth-file=/etc/kubernetes/token.csv \\
  17. --service-cluster-ip-range=${SERVICE_CIDR} \\
  18. --service-node-port-range=${NODE_PORT_RANGE} \\
  19. --tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem \\
  20. --tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \\
  21. --client-ca-file=/etc/kubernetes/ssl/ca.pem \\
  22. --service-account-key-file=/etc/kubernetes/ssl/ca-key.pem \\
  23. --etcd-cafile=/etc/kubernetes/ssl/ca.pem \\
  24. --etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem \\
  25. --etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem \\
  26. --etcd-servers=${ETCD_ENDPOINTS} \\
  27. --enable-swagger-ui=true \\
  28. --allow-privileged=true \\
  29. --apiserver-count=2 \\
  30. --audit-log-maxage=30 \\
  31. --audit-log-maxbackup=3 \\
  32. --audit-log-maxsize=100 \\
  33. --audit-log-path=/var/lib/audit.log \\
  34. --audit-policy-file=/etc/kubernetes/audit-policy.yaml \\
  35. --event-ttl=1h \\
  36. --logtostderr=true \\
  37. --v=6
  38. Restart=on-failure
  39. RestartSec=5
  40. Type=notify
  41. LimitNOFILE=65536
  42. [Install]
  43. WantedBy=multi-user.target
  44. EOF
  • kube-apiserver 1.6 版本开始使用 etcd v3 API 和存储格式
  • --authorization-mode=RBAC 指定在安全端口使用RBAC 授权模式,拒绝未通过授权的请求
  • kube-scheduler、kube-controller-manager 一般和 kube-apiserver 部署在同一台机器上,它们使用非安全端口和 kube-apiserver通信
  • kubelet、kube-proxy、kubectl 部署在其它 Node 节点上,如果通过安全端口访问 kube-apiserver,则必须先通过 TLS 证书认证,再通过 RBAC 授权
  • kube-proxy、kubectl 通过使用证书里指定相关的 User、Group 来达到通过 RBAC 授权的目的
  • 如果使用了 kubelet TLS Boostrap 机制,则不能再指定 --kubelet-certificate-authority--kubelet-client-certificate 和 --kubelet-client-key 选项,否则后续 kube-apiserver 校验 kubelet 证书时出现 ”x509: certificate signed by unknown authority“ 错误
  • --admission-control 值必须包含 ServiceAccount,否则部署集群插件时会失败
  • --bind-address 不能为 127.0.0.1
  • --service-cluster-ip-range 指定 Service Cluster IP 地址段,该地址段不能路由可达
  • --service-node-port-range=${NODE_PORT_RANGE} 指定 NodePort 的端口范围
  • 缺省情况下 kubernetes 对象保存在etcd/registry 路径下,可以通过 --etcd-prefix 参数进行调整
  • kube-apiserver 1.8版本后需要在--authorization-mode参数中添加Node,即:--authorization-mode=Node,RBAC,否则Node 节点无法注册
  • 注意要开启审查日志功能,指定--audit-log-path参数是不够的,这只是指定了日志的路径,还需要指定一个审查日志策略文件:--audit-policy-file,我们也可以使用日志收集工具收集相关的日志进行分析。

审查日志策略文件内容如下:(/etc/kubernetes/audit-policy.yaml)

  1. apiVersion: audit.k8s.io/v1beta1 # This is required.
  2. kind: Policy
  3. # Don't generate audit events for all requests in RequestReceived stage.
  4. omitStages:
  5. - "RequestReceived"
  6. rules:
  7. # Log pod changes at RequestResponse level
  8. - level: RequestResponse
  9. resources:
  10. - group: ""
  11. # Resource "pods" doesn't match requests to any subresource of pods,
  12. # which is consistent with the RBAC policy.
  13. resources: ["pods"]
  14. # Log "pods/log", "pods/status" at Metadata level
  15. - level: Metadata
  16. resources:
  17. - group: ""
  18. resources: ["pods/log", "pods/status"]
  19. # Don't log requests to a configmap called "controller-leader"
  20. - level: None
  21. resources:
  22. - group: ""
  23. resources: ["configmaps"]
  24. resourceNames: ["controller-leader"]
  25. # Don't log watch requests by the "system:kube-proxy" on endpoints or services
  26. - level: None
  27. users: ["system:kube-proxy"]
  28. verbs: ["watch"]
  29. resources:
  30. - group: "" # core API group
  31. resources: ["endpoints", "services"]
  32. # Don't log authenticated requests to certain non-resource URL paths.
  33. - level: None
  34. userGroups: ["system:authenticated"]
  35. nonResourceURLs:
  36. - "/api*" # Wildcard matching.
  37. - "/version"
  38. # Log the request body of configmap changes in kube-system.
  39. - level: Request
  40. resources:
  41. - group: "" # core API group
  42. resources: ["configmaps"]
  43. # This rule only applies to resources in the "kube-system" namespace.
  44. # The empty string "" can be used to select non-namespaced resources.
  45. namespaces: ["kube-system"]
  46. # Log configmap and secret changes in all other namespaces at the Metadata level.
  47. - level: Metadata
  48. resources:
  49. - group: "" # core API group
  50. resources: ["secrets", "configmaps"]
  51. # Log all other resources in core and extensions at the Request level.
  52. - level: Request
  53. resources:
  54. - group: "" # core API group
  55. - group: "extensions" # Version of group should NOT be included.
  56. # A catch-all rule to log all other requests at the Metadata level.
  57. - level: Metadata
  58. # Long-running requests like watches that fall under this rule will not
  59. # generate an audit event in RequestReceived.
  60. omitStages:
  61. - "RequestReceived"

审查日志的相关配置可以查看文档了解:https://kubernetes.io/docs/tasks/debug-application-cluster/audit/

启动kube-apiserver

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

6.2 配置和启动kube-controller-manager

创建kube-controller-manager 的systemd unit 文件

  1. $ cat > kube-controller-manager.service <<EOF
  2. [Unit]
  3. Description=Kubernetes Controller Manager
  4. Documentation=https://github.com/GoogleCloudPlatform/kubernetes
  5. [Service]
  6. ExecStart=/usr/k8s/bin/kube-controller-manager \\
  7. --address=127.0.0.1 \\
  8. --master=http://${MASTER_URL}:8080 \\
  9. --allocate-node-cidrs=true \\
  10. --service-cluster-ip-range=${SERVICE_CIDR} \\
  11. --cluster-cidr=${CLUSTER_CIDR} \\
  12. --cluster-name=kubernetes \\
  13. --cluster-signing-cert-file=/etc/kubernetes/ssl/ca.pem \\
  14. --cluster-signing-key-file=/etc/kubernetes/ssl/ca-key.pem \\
  15. --service-account-private-key-file=/etc/kubernetes/ssl/ca-key.pem \\
  16. --root-ca-file=/etc/kubernetes/ssl/ca.pem \\
  17. --leader-elect=true \\
  18. --v=2
  19. Restart=on-failure
  20. RestartSec=5
  21. [Install]
  22. WantedBy=multi-user.target
  23. EOF
  • --address 值必须为 127.0.0.1,因为当前 kube-apiserver 期望 scheduler 和 controller-manager 在同一台机器

  • --master=http://${MASTER_URL}:8080:使用http(非安全端口)与 kube-apiserver 通信,需要下面的haproxy安装成功后才能去掉8080端口。

  • --cluster-cidr 指定 Cluster 中 Pod 的 CIDR 范围,该网段在各 Node 间必须路由可达(flanneld保证)

  • --service-cluster-ip-range 参数指定 Cluster 中 Service 的CIDR范围,该网络在各 Node 间必须路由不可达,必须和 kube-apiserver 中的参数一致

  • --cluster-signing-* 指定的证书和私钥文件用来签名为 TLS BootStrap 创建的证书和私钥

  • --root-ca-file 用来对 kube-apiserver 证书进行校验,指定该参数后,才会在Pod 容器的 ServiceAccount 中放置该 CA 证书文件

  • --leader-elect=true 部署多台机器组成的 master 集群时选举产生一处于工作状态的 kube-controller-manager 进程

启动kube-controller-manager

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

6.3 配置和启动kube-scheduler

创建kube-scheduler 的systemd unit文件

  1. $ cat > kube-scheduler.service <<EOF
  2. [Unit]
  3. Description=Kubernetes Scheduler
  4. Documentation=https://github.com/GoogleCloudPlatform/kubernetes
  5. [Service]
  6. ExecStart=/usr/k8s/bin/kube-scheduler \\
  7. --address=127.0.0.1 \\
  8. --master=http://${MASTER_URL}:8080 \\
  9. --leader-elect=true \\
  10. --v=2
  11. Restart=on-failure
  12. RestartSec=5
  13. [Install]
  14. WantedBy=multi-user.target
  15. EOF
  • --address 值必须为 127.0.0.1,因为当前 kube-apiserver 期望 scheduler 和 controller-manager 在同一台机器
  • --master=http://${MASTER_URL}:8080:使用http(非安全端口)与 kube-apiserver 通信,需要下面的haproxy启动成功后才能去掉8080端口
  • --leader-elect=true 部署多台机器组成的 master 集群时选举产生一处于工作状态的 kube-controller-manager 进程

启动kube-scheduler

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

6.4 验证master 节点

  1. $ kubectl get componentstatuses
  2. NAME STATUS MESSAGE ERROR
  3. scheduler Healthy ok
  4. controller-manager Healthy ok
  5. etcd-1 Healthy {"health": "true"}
  6. etcd-2 Healthy {"health": "true"}
  7. etcd-0 Healthy {"health": "true"}

7. kube-apiserver 高可用

按照上面的方式在master01master02机器上安装kube-apiserverkube-controller-managerkube-scheduler,但是现在我们还是手动指定访问的6443和8080端口的,因为我们的域名k8s-api.virtual.local对应的master01节点直接通过http 和https 还不能访问,这里我们使用haproxy 来代替请求。

明白什么意思吗?就是我们需要将http默认的80端口请求转发到apiserver的8080端口,将https默认的443端口请求转发到apiserver的6443端口,所以我们这里使用haproxy来做请求转发。

安装haproxy

  1. $ yum install -y haproxy

配置haproxy

由于集群内部有的组建是通过非安全端口访问apiserver 的,有的是通过安全端口访问apiserver 的,所以我们要配置http 和https 两种代理方式,配置文件 /etc/haproxy/haproxy.cfg

  1. listen stats
  2. bind *:9000
  3. mode http
  4. stats enable
  5. stats hide-version
  6. stats uri /stats
  7. stats refresh 30s
  8. stats realm Haproxy\ Statistics
  9. stats auth Admin:Password
  10. frontend k8s-api
  11. bind 192.168.1.137:443
  12. mode tcp
  13. option tcplog
  14. tcp-request inspect-delay 5s
  15. tcp-request content accept if { req.ssl_hello_type 1 }
  16. default_backend k8s-api
  17. backend k8s-api
  18. mode tcp
  19. option tcplog
  20. option tcp-check
  21. balance roundrobin
  22. default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
  23. server k8s-api-1 192.168.1.137:6443 check
  24. server k8s-api-2 192.168.1.138:6443 check
  25. frontend k8s-http-api
  26. bind 192.168.1.137:80
  27. mode tcp
  28. option tcplog
  29. default_backend k8s-http-api
  30. backend k8s-http-api
  31. mode tcp
  32. option tcplog
  33. option tcp-check
  34. balance roundrobin
  35. default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
  36. server k8s-http-api-1 192.168.1.137:8080 check
  37. server k8s-http-api-2 192.168.1.138:8080 check

通过上面的配置文件我们可以看出通过https的访问将请求转发给apiserver 的6443端口了,http的请求转发到了apiserver 的8080端口。

启动haproxy

  1. $ sudo systemctl start haproxy
  2. $ sudo systemctl enable haproxy
  3. $ sudo systemctl status haproxy

然后我们可以通过上面9000端口监控我们的haproxy的运行状态(192.168.1.137:9000/stats):

问题

上面我们的haproxy的确可以代理我们的两个master 上的apiserver 了,但是还不是高可用的,如果master01 这个节点down 掉了,那么我们haproxy 就不能正常提供服务了。这里我们可以使用两种方法来实现高可用

方式1:使用阿里云SLB

这种方式实际上是最省心的,在阿里云上建一个内网的SLB,将master01 与master02 添加到SLB 机器组中,转发80(http)和443(https)端口即可(注意下面的提示)

注意:阿里云的负载均衡是四层TCP负责,不支持后端ECS实例既作为Real Server又作为客户端向所在的负载均衡实例发送请求。因为返回的数据包只在云服务器内部转发,不经过负载均衡,所以在后端ECS实例上去访问负载均衡的服务地址是不通的。什么意思?就是如果你要使用阿里云的SLB的话,那么你不能在apiserver节点上使用SLB(比如在apiserver 上安装kubectl,然后将apiserver的地址设置为SLB的负载地址使用),因为这样的话就可能造成回环了,所以简单的做法是另外用两个新的节点做HA实例,然后将这两个实例添加到SLB 机器组中。

方式2:使用keepalived

KeepAlived 是一个高可用方案,通过 VIP(即虚拟 IP)和心跳检测来实现高可用。其原理是存在一组(两台)服务器,分别赋予 Master、Backup 两个角色,默认情况下Master 会绑定VIP 到自己的网卡上,对外提供服务。Master、Backup 会在一定的时间间隔向对方发送心跳数据包来检测对方的状态,这个时间间隔一般为 2 秒钟,如果Backup 发现Master 宕机,那么Backup 会发送ARP 包到网关,把VIP 绑定到自己的网卡,此时Backup 对外提供服务,实现自动化的故障转移,当Master 恢复的时候会重新接管服务。非常类似于路由器中的虚拟路由器冗余协议(VRRP)

开启路由转发,这里我们定义虚拟IP为:192.168.1.139

  1. $ vi /etc/sysctl.conf
  2. # 添加以下内容
  3. net.ipv4.ip_forward = 1
  4. net.ipv4.ip_nonlocal_bind = 1
  5. # 验证并生效
  6. $ sysctl -p
  7. # 验证是否生效
  8. $ cat /proc/sys/net/ipv4/ip_forward
  9. 1

安装keepalived:

  1. $ yum install -y keepalived

我们这里将master01 设置为Master,master02 设置为Backup,修改配置:

  1. $ vi /etc/keepalived/keepalived.conf
  2. ! Configuration File for keepalived
  3. global_defs {
  4. notification_email {
  5. }
  6. router_id kube_api
  7. }
  8. vrrp_script check_k8s {
  9. # 自身状态检测
  10. script "/etc/keepalived/chk_k8s_master.sh"
  11. interval 3
  12. weight 5
  13. }
  14. vrrp_instance haproxy-vip {
  15. # 使用单播通信,默认是组播通信
  16. unicast_src_ip 192.168.1.137
  17. unicast_peer {
  18. 192.168.1.138
  19. }
  20. # 初始化状态
  21. state MASTER
  22. # 虚拟ip 绑定的网卡 (这里根据你自己的实际情况选择网卡)
  23. interface eth0
  24. # 此ID 要与Backup 配置一致
  25. virtual_router_id 51
  26. # 默认启动优先级,要比Backup 大点,但要控制量,保证自身状态检测生效
  27. priority 100
  28. advert_int 1
  29. authentication {
  30. auth_type PASS
  31. auth_pass 1111
  32. }
  33. virtual_ipaddress {
  34. # 虚拟ip 地址
  35. 192.168.1.139
  36. }
  37. track_script {
  38. check_k8s
  39. }
  40. }
  41. # 自身状态监测脚本,这里通过 api-server 是否在监听端口(6433 端口)来判断状态。
  42. $ vi /etc/keepalived/chk_k8s_master.sh
  43. #!/bin/bash
  44. count=`ss -tnl | grep 6433 | wc -l`
  45. if [ $count = 0 ]; then
  46. exit 1
  47. else
  48. exit 0
  49. fi

统一的方式在master02 节点上安装keepalived,修改配置,只需要将state 更改成BACKUP,priority更改成99,unicast_src_ip 与unicast_peer 地址修改即可。

启动keepalived:

  1. $ systemctl start keepalived
  2. $ systemctl enable keepalived
  3. # 查看日志
  4. $ journalctl -f -u keepalived

验证虚拟IP:

  1. # 使用ifconfig -a 命令查看不到,要使用ip addr
  2. $ ip addr
  3. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
  4. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  5. inet 127.0.0.1/8 scope host lo
  6. valid_lft forever preferred_lft forever
  7. 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
  8. link/ether 00:16:3e:00:55:c1 brd ff:ff:ff:ff:ff:ff
  9. inet 192.168.1.137/24 brd 192.168.1.255 scope global dynamic eth0
  10. valid_lft 31447746sec preferred_lft 31447746sec
  11. inet 192.168.1.139/24 brd 192.168.1.255 scope global secondary eth0-vip
  12. valid_lft forever preferred_lft forever

到这里,我们就可以将上面的6443端口和8080端口去掉了,可以手动将kubectl生成的config文件(~/.kube/config)中的server 地址6443端口去掉,另外kube-controller-managerkube-scheduler的–master参数中的8080端口去掉了,然后分别重启这两个组件即可。

验证apiserver:关闭master01 节点上的kube-apiserver 进程,然后查看虚拟ip是否漂移到了master02 节点。

然后我们就可以将第一步在/etc/hosts里面设置的域名对应的IP 更改为我们的虚拟IP了

master01 与master 02 节点都需要安装keepalived 和haproxy,实际上我们虚拟IP的自身检测应该是检测haproxy,脚本大家可以自行更改

这样我们就实现了接入层apiserver 的高可用了,一个部分是多活的apiserver 服务,另一个部分是一主一备的haproxy 服务。

kube-controller-manager 和kube-scheduler 的高可用

Kubernetes 的管理层服务包括kube-schedulerkube-controller-manager。kube-scheduler和kube-controller-manager使用一主多从的高可用方案,在同一时刻只允许一个服务处以具体的任务。Kubernetes中实现了一套简单的选主逻辑,依赖Etcd实现scheduler和controller-manager的选主功能。如果scheduler和controller-manager在启动的时候设置了leader-elect参数,它们在启动后会先尝试获取leader节点身份,只有在获取leader节点身份后才可以执行具体的业务逻辑。它们分别会在Etcd中创建kube-scheduler和kube-controller-manager的endpoint,endpoint的信息中记录了当前的leader节点信息,以及记录的上次更新时间。leader节点会定期更新endpoint的信息,维护自己的leader身份。每个从节点的服务都会定期检查endpoint的信息,如果endpoint的信息在时间范围内没有更新,它们会尝试更新自己为leader节点。scheduler服务以及controller-manager服务之间不会进行通信,利用Etcd的强一致性,能够保证在分布式高并发情况下leader节点的全局唯一性。整体方案如下图所示:

当集群中的leader节点服务异常后,其它节点的服务会尝试更新自身为leader节点,当有多个节点同时更新endpoint时,由Etcd保证只有一个服务的更新请求能够成功。通过这种机制sheduler和controller-manager可以保证在leader节点宕机后其它的节点可以顺利选主,保证服务故障后快速恢复。当集群中的网络出现故障时对服务的选主影响不是很大,因为scheduler和controller-manager是依赖Etcd进行选主的,在网络故障后,可以和Etcd通信的主机依然可以按照之前的逻辑进行选主,就算集群被切分,Etcd也可以保证同一时刻只有一个节点的服务处于leader状态。

8. 部署Node 节点

kubernetes Node 节点包含如下组件:

  • flanneld
  • docker
  • kubelet
  • kube-proxy

环境变量

  1. $ source /usr/k8s/bin/env.sh
  2. $ export KUBE_APISERVER="https://${MASTER_URL}" // 如果你没有安装`haproxy`的话,还是需要使用6443端口的哦
  3. $ export NODE_IP=192.168.1.170 # 当前部署的节点 IP

按照上面的步骤安装配置好flanneld

开启路由转发

修改/etc/sysctl.conf文件,添加下面的规则:

  1. net.ipv4.ip_forward=1
  2. net.bridge.bridge-nf-call-iptables=1
  3. net.bridge.bridge-nf-call-ip6tables=1

执行下面的命令立即生效:

  1. $ sysctl -p

配置docker

你可以用二进制或yum install 的方式来安装docker,然后修改docker 的systemd unit 文件:

  1. $ cat /usr/lib/systemd/system/docker.service # 用systemctl status docker 命令可查看unit 文件路径
  2. [Unit]
  3. Description=Docker Application Container Engine
  4. Documentation=https://docs.docker.com
  5. After=network-online.target firewalld.service
  6. Wants=network-online.target
  7. [Service]
  8. Type=notify
  9. # the default is not to use systemd for cgroups because the delegate issues still
  10. # exists and systemd currently does not support the cgroup feature set required
  11. # for containers run by docker
  12. EnvironmentFile=-/run/flannel/docker
  13. ExecStart=/usr/bin/dockerd --log-level=info $DOCKER_NETWORK_OPTIONS
  14. ExecReload=/bin/kill -s HUP $MAINPID
  15. # Having non-zero Limit*s causes performance problems due to accounting overhead
  16. # in the kernel. We recommend using cgroups to do container-local accounting.
  17. LimitNOFILE=infinity
  18. LimitNPROC=infinity
  19. LimitCORE=infinity
  20. # Uncomment TasksMax if your systemd version supports it.
  21. # Only systemd 226 and above support this version.
  22. #TasksMax=infinity
  23. TimeoutStartSec=0
  24. # set delegate yes so that systemd does not reset the cgroups of docker containers
  25. Delegate=yes
  26. # kill only the docker process, not all processes in the cgroup
  27. KillMode=process
  28. # restart the docker process if it exits prematurely
  29. Restart=on-failure
  30. StartLimitBurst=3
  31. StartLimitInterval=60s
  32. [Install]
  33. 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

如果没有开启上面的路由转发(net.ipv4.ip_forward=1),则需要把以下命令写入/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. "max-concurrent-downloads": 10
  4. }

启动docker

  1. $ sudo systemctl daemon-reload
  2. $ sudo systemctl stop firewalld
  3. $ sudo systemctl disable firewalld
  4. $ sudo iptables -F && sudo iptables -X && sudo iptables -F -t nat && sudo iptables -X -t nat
  5. $ sudo systemctl enable docker
  6. $ sudo systemctl start docker
  • 需要关闭 firewalld(centos7)/ufw(ubuntu16.04),否则可能会重复创建 iptables 规则
  • 最好清理旧的 iptables rules 和 chains 规则
  • 执行命令:docker version,检查docker服务是否正常

注意如果

vim /etc/sysctl.conf

net.ipv4.ip_forward=1 net.bridge.bridge-nf-call-iptables=1 net.bridge.bridge-nf-call-ip6tables=1

sysctl -p

安装和配置kubelet

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

kubelet就是运行在Node节点上的,所以这一步安装是在所有的Node节点上,如果你想把你的Master也当做Node节点的话,当然也可以在Master节点上安装的。

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

另外1.8 版本中还需要为Node 请求创建一个RBAC 授权规则:

  1. $ kubectl create clusterrolebinding kubelet-nodes --clusterrole=system:node --group=system:nodes

然后下载最新的kubelet 和kube-proxy 二进制文件(前面下载kubernetes 目录下面其实也有):

  1. $ wget https://dl.k8s.io/v1.8.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} /usr/k8s/bin/

创建kubelet bootstapping 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-certs 为 true 时表示将 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=/usr/k8s/bin/kubelet \\
  11. --fail-swap-on=false \\
  12. --cgroup-driver=cgroupfs \\
  13. --address=${NODE_IP} \\
  14. --hostname-override=${NODE_IP} \\
  15. --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig \\
  16. --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\
  17. --require-kubeconfig \\
  18. --cert-dir=/etc/kubernetes/ssl \\
  19. --cluster-dns=${CLUSTER_DNS_SVC_IP} \\
  20. --cluster-domain=${CLUSTER_DNS_DOMAIN} \\
  21. --hairpin-mode promiscuous-bridge \\
  22. --allow-privileged=true \\
  23. --serialize-image-pulls=false \\
  24. --logtostderr=true \\
  25. --v=2
  26. ExecStartPost=/sbin/iptables -A INPUT -s 10.0.0.0/8 -p tcp --dport 4194 -j ACCEPT
  27. ExecStartPost=/sbin/iptables -A INPUT -s 172.17.0.0/12 -p tcp --dport 4194 -j ACCEPT
  28. ExecStartPost=/sbin/iptables -A INPUT -s 192.168.1.0/16 -p tcp --dport 4194 -j ACCEPT
  29. ExecStartPost=/sbin/iptables -A INPUT -p tcp --dport 4194 -j DROP
  30. Restart=on-failure
  31. RestartSec=5
  32. [Install]
  33. WantedBy=multi-user.target
  34. EOF

请仔细阅读下面的注意事项,不然可能会启动失败。

  • --fail-swap-on参数,这个一定要注意,Kubernetes 1.8开始要求关闭系统的Swap,如果不关闭,默认配置下kubelet将无法启动,也可以通过kubelet的启动参数–fail-swap-on=false来避免该问题
  • --cgroup-driver参数,kubelet 用来维护主机的的 cgroups 的,默认是cgroupfs,但是这个地方的值需要你根据docker 的配置来确定(docker info |grep cgroup
  • -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.crt 和 kubelet-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

  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

通过kubelet 的TLS 证书请求

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

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

通过CSR 请求:

  1. $ kubectl certificate approve node-csr--k3G2G1EoM4h9w1FuJRjJjfbIPNxa551A8TZfW9dG-g
  2. certificatesigningrequest "node-csr--k3G2G1EoM4h9w1FuJRjJjfbIPNxa551A8TZfW9dG-g" approved
  3. $ kubectl get nodes
  4. NAME STATUS ROLES AGE VERSION
  5. 192.168.1.170 Ready <none> 48s v1.8.1

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

  1. $ ls -l /etc/kubernetes/kubelet.kubeconfig
  2. -rw------- 1 root root 2280 Nov 7 10:26 /etc/kubernetes/kubelet.kubeconfig
  3. $ ls -l /etc/kubernetes/ssl/kubelet*
  4. -rw-r--r-- 1 root root 1046 Nov 7 10:26 /etc/kubernetes/ssl/kubelet-client.crt
  5. -rw------- 1 root root 227 Nov 7 10:22 /etc/kubernetes/ssl/kubelet-client.key
  6. -rw-r--r-- 1 root root 1115 Nov 7 10:16 /etc/kubernetes/ssl/kubelet.crt
  7. -rw------- 1 root root 1675 Nov 7 10:16 /etc/kubernetes/ssl/kubelet.key

配置kube-proxy

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

  1. $ cat > kube-proxy-csr.json <<EOF
  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. }
  19. EOF
  • 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/

创建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-certificate 和 client-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=/usr/k8s/bin/kube-proxy \\
  10. --bind-address=${NODE_IP} \\
  11. --hostname-override=${NODE_IP} \\
  12. --cluster-cidr=${SERVICE_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-apiserver 的 --service-cluster-ip-range 选项值一致
  • 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 的权限

启动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

验证集群功能

定义yaml 文件:(将下面内容保存为:nginx-ds.yaml)

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

创建 Pod 和服务:

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

执行下面的命令查看Pod 和SVC:

  1. $ kubectl get pods -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE
  3. nginx-ds-f29zt 1/1 Running 0 23m 172.17.0.2 192.168.1.170
  4. $ kubectl get svc
  5. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  6. nginx-ds NodePort 10.254.6.249 <none> 80:30813/TCP 24m

可以看到:

  • 服务IP:10.254.6.249
  • 服务端口:80
  • NodePort端口:30813

在所有 Node 上执行:

  1. $ curl 10.254.6.249
  2. $ curl 192.168.1.170:30813

执行上面的命令预期都会输出nginx 欢迎页面内容,表示我们的Node 节点正常运行了。

9. 部署kubedns 插件

官方文件目录:kubernetes/cluster/addons/dns

使用的文件:

  1. $ ls *.yaml *.base
  2. kubedns-cm.yaml kubedns-sa.yaml kubedns-controller.yaml.base kubedns-svc.yaml.base

系统预定义的RoleBinding

预定义的RoleBinding system:kube-dns将kube-system 命名空间的kube-dnsServiceAccount 与 system:kube-dns Role 绑定,该Role 具有访问kube-apiserver DNS 相关的API 权限:

  1. $ kubectl get clusterrolebindings system:kube-dns -o yaml
  2. apiVersion: rbac.authorization.k8s.io/v1
  3. kind: ClusterRoleBinding
  4. metadata:
  5. annotations:
  6. rbac.authorization.kubernetes.io/autoupdate: "true"
  7. creationTimestamp: 2017-11-06T10:51:59Z
  8. labels:
  9. kubernetes.io/bootstrapping: rbac-defaults
  10. name: system:kube-dns
  11. resourceVersion: "78"
  12. selfLink: /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/system%3Akube-dns
  13. uid: 83a25fd9-c2e0-11e7-9646-00163e0055c1
  14. roleRef:
  15. apiGroup: rbac.authorization.k8s.io
  16. kind: ClusterRole
  17. name: system:kube-dns
  18. subjects:
  19. - kind: ServiceAccount
  20. name: kube-dns
  21. namespace: kube-system
  • kubedns-controller.yaml 中定义的 Pods 时使用了 kubedns-sa.yaml 文件定义的 kube-dns ServiceAccount,所以具有访问 kube-apiserver DNS 相关 API 的权限;

配置kube-dns ServiceAccount

无需更改

配置kube-dns 服务

  1. $ diff kubedns-svc.yaml.base kubedns-svc.yaml
  2. 30c30
  3. < clusterIP: __PILLAR__DNS__SERVER__
  4. ---
  5. > clusterIP: 10.254.0.2
  • 需要将 spec.clusterIP 设置为集群环境变量中变量 CLUSTER_DNS_SVC_IP 值,这个IP 需要和 kubelet 的 —cluster-dns 参数值一致

配置kube-dns Deployment

  1. $ diff kubedns-controller.yaml.base kubedns-controller.yaml
  2. 88c88
  3. < - --domain=__PILLAR__DNS__DOMAIN__.
  4. ---
  5. > - --domain=cluster.local
  6. 128c128
  7. < - --server=/__PILLAR__DNS__DOMAIN__/127.0.0.1#10053
  8. ---
  9. > - --server=/cluster.local/127.0.0.1#10053
  10. 160,161c160,161
  11. < - --probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.__PILLAR__DNS__DOMAIN__,5,A
  12. < - --probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.__PILLAR__DNS__DOMAIN__,5,A
  13. ---
  14. > - --probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.cluster.local,5,A
  15. > - --probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.cluster.local,5,A
  • --domain 为集群环境变量CLUSTER_DNS_DOMAIN 的值
  • 使用系统已经做了 RoleBinding 的 kube-dns ServiceAccount,该账户具有访问 kube-apiserver DNS 相关 API 的权限

执行所有定义文件

  1. $ pwd
  2. /home/ych/k8s-repo/kube-dns
  3. $ ls *.yaml
  4. kubedns-cm.yaml kubedns-controller.yaml kubedns-sa.yaml kubedns-svc.yaml
  5. $ kubectl create -f .

检查kubedns 功能

新建一个Deployment

  1. $ cat > my-nginx.yaml<<EOF
  2. apiVersion: extensions/v1beta1
  3. kind: Deployment
  4. metadata:
  5. name: my-nginx
  6. spec:
  7. replicas: 2
  8. template:
  9. metadata:
  10. labels:
  11. run: my-nginx
  12. spec:
  13. containers:
  14. - name: my-nginx
  15. image: nginx:1.7.9
  16. ports:
  17. - containerPort: 80
  18. EOF
  19. $ kubectl create -f my-nginx.yaml
  20. deployment "my-nginx" created

Expose 该Deployment,生成my-nginx 服务

  1. $ kubectl expose deploy my-nginx
  2. $ kubectl get services
  3. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  4. kubernetes ClusterIP 10.254.0.1 <none> 443/TCP 1d
  5. my-nginx ClusterIP 10.254.32.162 <none> 80/TCP 56s

然后创建另外一个Pod,查看/etc/resolv.conf是否包含kubelet配置的--cluster-dns 和--cluster-domain,是否能够将服务my-nginx 解析到上面显示的CLUSTER-IP 10.254.32.162

  1. $ cat > pod-nginx.yaml<<EOF
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: nginx
  6. spec:
  7. containers:
  8. - name: nginx
  9. image: nginx:1.7.9
  10. ports:
  11. - containerPort: 80
  12. EOF
  13. $ kubectl create -f pod-nginx.yaml
  14. pod "nginx" created
  15. $ kubectl exec nginx -i -t -- /bin/bash
  16. root@nginx:/# cat /etc/resolv.conf
  17. nameserver 10.254.0.2
  18. search default.svc.cluster.local. svc.cluster.local. cluster.local.
  19. options ndots:5
  20. root@nginx:/# ping my-nginx
  21. PING my-nginx.default.svc.cluster.local (10.254.32.162): 48 data bytes
  22. ^C--- my-nginx.default.svc.cluster.local ping statistics ---
  23. 14 packets transmitted, 0 packets received, 100% packet loss
  24. root@nginx:/# ping kubernetes
  25. PING kubernetes.default.svc.cluster.local (10.254.0.1): 48 data bytes
  26. ^C--- kubernetes.default.svc.cluster.local ping statistics ---
  27. 6 packets transmitted, 0 packets received, 100% packet loss
  28. root@nginx:/# ping kube-dns.kube-system.svc.cluster.local
  29. PING kube-dns.kube-system.svc.cluster.local (10.254.0.2): 48 data bytes
  30. ^C--- kube-dns.kube-system.svc.cluster.local ping statistics ---
  31. 2 packets transmitted, 0 packets received, 100% packet loss

10. 部署Dashboard 插件

官方文件目录:kubernetes/cluster/addons/dashboard

使用的文件如下:

  1. $ ls *.yaml
  2. dashboard-controller.yaml dashboard-rbac.yaml dashboard-service.yaml
  • 新加了 dashboard-rbac.yaml 文件,定义 dashboard 使用的 RoleBinding。

由于 kube-apiserver 启用了 RBAC 授权,而官方源码目录的 dashboard-controller.yaml 没有定义授权的 ServiceAccount,所以后续访问 kube-apiserver 的 API 时会被拒绝,前端界面提示:

解决办法是:定义一个名为dashboard 的ServiceAccount,然后将它和Cluster Role view 绑定:

  1. $ cat > dashboard-rbac.yaml<<EOF
  2. apiVersion: v1
  3. kind: ServiceAccount
  4. metadata:
  5. name: dashboard
  6. namespace: kube-system
  7. ---
  8. kind: ClusterRoleBinding
  9. apiVersion: rbac.authorization.k8s.io/v1alpha1
  10. metadata:
  11. name: dashboard
  12. subjects:
  13. - kind: ServiceAccount
  14. name: dashboard
  15. namespace: kube-system
  16. roleRef:
  17. kind: ClusterRole
  18. name: cluster-admin
  19. apiGroup: rbac.authorization.k8s.io
  20. EOF

配置dashboard-controller

  1. 20a21
  2. > serviceAccountName: dashboard
  • 使用名为 dashboard 的自定义 ServiceAccount

配置dashboard-service

  1. $ diff dashboard-service.yaml.orig dashboard-service.yaml
  2. 10a11
  3. > type: NodePort
  • 指定端口类型为 NodePort,这样外界可以通过地址 nodeIP:nodePort 访问 dashboard

执行所有定义文件

  1. $ pwd
  2. /home/ych/k8s-repo/dashboard
  3. $ ls *.yaml
  4. dashboard-controller.yaml dashboard-rbac.yaml dashboard-service.yaml
  5. $ kubectl create -f .

检查执行结果

查看分配的 NodePort

  1. $ kubectl get services kubernetes-dashboard -n kube-system
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. kubernetes-dashboard NodePort 10.254.104.90 <none> 80:31202/TCP 1m
  • NodePort 31202映射到dashboard pod 80端口;

检查 controller

  1. $ kubectl get deployment kubernetes-dashboard -n kube-system
  2. NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
  3. kubernetes-dashboard 1 1 1 1 3m
  4. $ kubectl get pods -n kube-system | grep dashboard
  5. kubernetes-dashboard-6667f9b4c-4xbpz 1/1 Running 0 3m

访问dashboard

  1. kubernetes-dashboard 服务暴露了 NodePort,可以使用 http://NodeIP:nodePort 地址访问 dashboard
  2. 通过 kube-apiserver 访问 dashboard
  3. 通过 kubectl proxy 访问 dashboard

由于缺少 Heapster 插件,当前 dashboard 不能展示 Pod、Nodes 的 CPU、内存等 metric 图形

注意:如果你的后端apiserver是高可用的集群模式的话,那么Dashboardapiserver-host最好手动指定,不然,当你apiserver某个节点挂了的时候,Dashboard可能不能正常访问,如下配置

  1. image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.7.1
  2. ports:
  3. - containerPort: 9090
  4. protocol: TCP
  5. args:
  6. - --apiserver-host=http://<api_server_ha_addr>:8080

11. 部署Heapster 插件

heapster release 页面下载最新版的heapster

  1. $ wget https://github.com/kubernetes/heapster/archive/v1.4.3.tar.gz
  2. $ tar -xzvf v1.4.3.tar.gz

部署相关文件目录:/home/ych/k8s-repo/heapster-1.4.3/deploy/kube-config

  1. $ ls influxdb/ && ls rbac/
  2. grafana.yaml heapster.yaml influxdb.yaml
  3. heapster-rbac.yaml

为方便测试访问,将grafana.yaml下面的服务类型设置为type=NodePort

执行所有文件

  1. $ kubectl create -f rbac/heapster-rbac.yaml
  2. clusterrolebinding "heapster" created
  3. $ kubectl create -f influxdb
  4. deployment "monitoring-grafana" created
  5. service "monitoring-grafana" created
  6. serviceaccount "heapster" created
  7. deployment "heapster" created
  8. service "heapster" created
  9. deployment "monitoring-influxdb" created
  10. service "monitoring-influxdb" created

检查执行结果

检查 Deployment

  1. $ kubectl get deployments -n kube-system | grep -E 'heapster|monitoring'
  2. heapster 1 1 1 1 2m
  3. monitoring-grafana 1 1 1 0 2m
  4. monitoring-influxdb 1 1 1 1 2m

检查 Pods

  1. $ kubectl get pods -n kube-system | grep -E 'heapster|monitoring'
  2. heapster-7cf895f48f-p98tk 1/1 Running 0 2m
  3. monitoring-grafana-c9d5cd98d-gb9xn 0/1 CrashLoopBackOff 4 2m
  4. monitoring-influxdb-67f8d587dd-zqj6p 1/1 Running 0 2m

我们可以看到monitoring-grafana的POD 是没有执行成功的,通过查看日志可以看到下面的错误信息:

Failed to parse /etc/grafana/grafana.ini, open /etc/grafana/grafana.ini: no such file or directory

要解决这个问题(heapster issues)我们需要将grafana 的镜像版本更改成:gcr.io/google_containers/heapster-grafana-amd64:v4.0.2,然后重新执行,即可正常。

访问 grafana

上面我们修改grafana 的Service 为NodePort 类型:

  1. $ kubectl get svc -n kube-system
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. monitoring-grafana NodePort 10.254.34.89 <none> 80:30191/TCP 28m

则我们就可以通过任意一个节点加上上面的30191端口就可以访问grafana 了。

heapster 正确安装后,我们便可以回去看我们的dashboard 是否有图表出现了:

12. 安装Ingress

Ingress其实就是从kuberenets集群外部访问集群的一个入口,将外部的请求转发到集群内不同的Service 上,其实就相当于nginx、apache 等负载均衡代理服务器,再加上一个规则定义,路由信息的刷新需要靠Ingress controller来提供

Ingress controller可以理解为一个监听器,通过不断地与kube-apiserver打交道,实时的感知后端service、pod 等的变化,当得到这些变化信息后,Ingress controller再结合Ingress的配置,更新反向代理负载均衡器,达到服务发现的作用。其实这点和服务发现工具consulconsul-template非常类似。

部署traefik

Traefik是一款开源的反向代理与负载均衡工具。它最大的优点是能够与常见的微服务系统直接整合,可以实现自动化动态配置。目前支持Docker、Swarm、Mesos/Marathon、 Mesos、Kubernetes、Consul、Etcd、Zookeeper、BoltDB、Rest API等等后端模型。

创建rbac

创建文件:ingress-rbac.yaml,用于service account验证

  1. apiVersion: v1
  2. kind: ServiceAccount
  3. metadata:
  4. name: ingress
  5. namespace: kube-system
  6. ---
  7. kind: ClusterRoleBinding
  8. apiVersion: rbac.authorization.k8s.io/v1beta1
  9. metadata:
  10. name: ingress
  11. subjects:
  12. - kind: ServiceAccount
  13. name: ingress
  14. namespace: kube-system
  15. roleRef:
  16. kind: ClusterRole
  17. name: cluster-admin
  18. apiGroup: rbac.authorization.k8s.io

DaemonSet 形式部署traefik

创建文件:traefik-daemonset.yaml,为保证traefik 总能提供服务,在每个节点上都部署一个traefik,所以这里使用DaemonSet 的形式

  1. kind: ConfigMap
  2. apiVersion: v1
  3. metadata:
  4. name: traefik-conf
  5. namespace: kube-system
  6. data:
  7. traefik-config: |-
  8. defaultEntryPoints = ["http","https"]
  9. [entryPoints]
  10. [entryPoints.http]
  11. address = ":80"
  12. [entryPoints.http.redirect]
  13. entryPoint = "https"
  14. [entryPoints.https]
  15. address = ":443"
  16. [entryPoints.https.tls]
  17. [[entryPoints.https.tls.certificates]]
  18. CertFile = "/ssl/ssl.crt"
  19. KeyFile = "/ssl/ssl.key"
  20. ---
  21. kind: DaemonSet
  22. apiVersion: extensions/v1beta1
  23. metadata:
  24. name: traefik-ingress
  25. namespace: kube-system
  26. labels:
  27. k8s-app: traefik-ingress
  28. spec:
  29. template:
  30. metadata:
  31. labels:
  32. k8s-app: traefik-ingress
  33. name: traefik-ingress
  34. spec:
  35. terminationGracePeriodSeconds: 60
  36. restartPolicy: Always
  37. serviceAccountName: ingress
  38. containers:
  39. - image: traefik:latest
  40. name: traefik-ingress
  41. ports:
  42. - name: http
  43. containerPort: 80
  44. hostPort: 80
  45. - name: https
  46. containerPort: 443
  47. hostPort: 443
  48. - name: admin
  49. containerPort: 8080
  50. args:
  51. - --configFile=/etc/traefik/traefik.toml
  52. - -d
  53. - --web
  54. - --kubernetes
  55. - --logLevel=DEBUG
  56. volumeMounts:
  57. - name: traefik-config-volume
  58. mountPath: /etc/traefik
  59. - name: traefik-ssl-volume
  60. mountPath: /ssl
  61. volumes:
  62. - name: traefik-config-volume
  63. configMap:
  64. name: traefik-conf
  65. items:
  66. - key: traefik-config
  67. path: traefik.toml
  68. - name: traefik-ssl-volume
  69. secret:
  70. secretName: traefik-ssl

注意上面的yaml 文件中我们添加了一个名为traefik-confConfigMap,该配置是用来将http 请求强制跳转成https,并指定https 所需CA 文件地址,这里我们使用secret的形式来指定CA 文件的路径:

  1. $ ls
  2. ssl.crt ssl.key
  3. $ kubectl create secret generic traefik-ssl --from-file=ssl.crt --from-file=ssl.key --namespace=kube-system
  4. secret "traefik-ssl" created

创建ingress

创建文件:traefik-ingress.yaml,现在可以通过创建ingress文件来定义请求规则了,根据自己集群中的service 自己修改相应的serviceName 和servicePort

  1. apiVersion: extensions/v1beta1
  2. kind: Ingress
  3. metadata:
  4. name: traefik-ingress
  5. spec:
  6. rules:
  7. - host: traefik.nginx.io
  8. http:
  9. paths:
  10. - path: /
  11. backend:
  12. serviceName: my-nginx
  13. servicePort: 80

执行创建命令:

  1. $ kubectl create -f ingress-rbac.yaml
  2. serviceaccount "ingress" created
  3. clusterrolebinding "ingress" created
  4. $ kubectl create -f traefik-daemonset.yaml
  5. configmap "traefik-conf" created
  6. daemonset "traefik-ingress" created
  7. $ kubectl create -f traefik-ingress.yaml
  8. ingress "traefik-ingress" created

Traefik UI

创建文件:traefik-ui.yaml

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: traefik-ui
  5. namespace: kube-system
  6. spec:
  7. selector:
  8. k8s-app: traefik-ingress
  9. ports:
  10. - name: web
  11. port: 80
  12. targetPort: 8080
  13. ---
  14. apiVersion: extensions/v1beta1
  15. kind: Ingress
  16. metadata:
  17. name: traefik-ui
  18. namespace: kube-system
  19. spec:
  20. rules:
  21. - host: traefik-ui.local
  22. http:
  23. paths:
  24. - path: /
  25. backend:
  26. serviceName: traefik-ui
  27. servicePort: web

测试

部署完成后,在本地/etc/hosts添加一条配置:

  1. # 将下面的xx.xx.xx.xx替换成任意节点IP
  2. xx.xx.xx.xx master03 traefik.nginx.io traefik-ui.local

配置完成后,在本地访问:traefik-ui.local,则可以访问到traefikdashboard页面:

同样的可以访问traefik.nginx.io,得到正确的结果页面:

上面配置完成后,就可以将我们的所有节点加入到一个SLB中,然后配置相应的域名解析到SLB即可。

13. 日志收集

参考文章kubernetes 日志收集方案

14. 私有仓库harbor 搭建

参考文章在kubernetes 上搭建docker 私有仓库Harbor

15. 问题汇总

15.1 dashboard无法显示监控图

dashboard 和heapster influxdb都部署完成后 dashboard依旧无法显示监控图 通过排查 heapster log有超时错误

  1. $ kubectl logs -f pods/heapster-2882613285-58d9r -n kube-system
  2. E0630 17:23:47.339987 1 reflector.go:203] k8s.io/heapster/metrics/sources/kubelet/kubelet.go:342: Failed to list *api.Node: Get http://kubernetes.default/api/v1/nodes?resourceVersion=0: dial tcp: i/o timeout E0630 17:23:47.340274 1 reflector.go:203] k8s.io/heapster/metrics/heapster.go:319: Failed to list *api.Pod: Get http://kubernetes.default/api/v1/pods?resourceVersion=0: dial tcp: i/o timeout E0630 17:23:47.340498 1 reflector.go:203] k8s.io/heapster/metrics/processors/namespace_based_enricher.go:84: Failed to list *api.Namespace: Get http://kubernetes.default/api/v1/namespaces?resourceVersion=0: dial tcp: lookup kubernetes.default on 10.254.0.2:53: dial udp 10.254.0.2:53: i/o timeout E0630 17:23:47.340563 1 reflector.go:203] k8s.io/heapster/metrics/heapster.go:327: Failed to list *api.Node: Get http://kubernetes.default/api/v1/nodes?resourceVersion=0: dial tcp: lookup kubernetes.default on 10.254.0.2:53: dial udp 10.254.0.2:53: i/o timeout E0630 17:23:47.340623 1 reflector.go:203] k8s.io/heapster/metrics/processors/node_autoscaling_enricher.go
  3. 手动搭建高可用的kubernetes 集群的更多相关文章

      1. 企业运维实践-还不会部署高可用的kubernetes集群?使用kubeadm方式安装高可用k8s集群v1.23.7
      1. 关注「WeiyiGeek」公众号 设为「特别关注」每天带你玩转网络安全运维.应用开发.物联网IOT学习! 希望各位看友[关注.点赞.评论.收藏.投币],助力每一个梦想. 文章目录: 0x00 前言简述 ...

      1. 1.还不会部署高可用的kubernetes集群?看我手把手教你使用二进制部署v1.23.6K8S集群实践(上)
      1. 公众号关注「WeiyiGeek 设为「特别关注」,每天带你玩转网络安全运维.应用开发.物联网IOT学习! 本章目录: 0x00 前言简述 0x01 环境准备 主机规划 软件版本 网络规划 0x02 ...

      1. keepalived工作原理和配置说明 腾讯云VPC内通过keepalived搭建高可用主备集群
      1. keepalived工作原理和配置说明 腾讯云VPC内通过keepalived搭建高可用主备集群 内网路由都用mac地址 一个mac地址绑定多个ip一个网卡只能一个mac地址,而且mac地址无法改,但 ...

      1. 搭建高可用的MongoDB集群
      1. http://www.csdn.net/article/2014-04-09/2819221-build-high-avialable-mongodb-cluster-part-1/1 在大数据的时代 ...

      1. Hadoop搭建高可用的HA集群
      1. 一.工具准备 1.7台虚拟机(至少需要3台),本次搭建以7台为例,配好ip,关闭防火墙,修改主机名和IP的映射关系(/etc/hosts),关闭防火墙 2.安装JDK,配置环境变量 二.集群规划: ...

      1. 搭建高可用的rabbitmq集群 + Mirror Queue + 使用C#驱动连接
      1. 我们知道rabbitmq是一个专业的MQ产品,而且它也是一个严格遵守AMQP协议的玩意,但是要想骚,一定需要拿出高可用的东西出来,这不本篇就跟大家说 一下cluster的概念,rabbitmqerl ...

      1. Redis Cluster搭建高可用Redis服务器集群
      1. 一.Redis Cluster集群简介 Redis ClusterRedis官方提供的分布式解决方案,在3.0版本后推出的,有效地解决了Redis分布式的需求,当一个节点挂了可以快速的切换到另一个节 ...

      1. 快速掌握RabbitMQ(五)——搭建高可用的RabbitMQ集群
      1. RabbitMQ的集群是依赖erlang集群的,而erlang集群是通过.erlang.cookie文件进行通信认证的,所以我们使用RabbitMQ集群时只需要配置一下.erlang.cookie文件 ...

      1. Redis总结(八)如何搭建高可用的Redis集群
      1. 以前总结Redis 的一些基本的安装和使用,大家可以这这里查看Redis 系列文章:https://www.cnblogs.com/zhangweizhong/category/771056.html ...

    1. 随机推荐

        1. socket发送文字、图片、文件---基于python实现
        1. socket官方文档:https://docs.python.org/2/library/socket.html socket中文详细介绍:http://blog.csdn.net/rebelqsp/ ...

        1. A Bug's Life(加权并查集)
        1. Description Background  Professor Hopper is researching the sexual behavior of a rare species of bug ...

        1. 【分层最短路】Magical Girl Haze
        1. https://nanti.jisuanke.com/t/31001 有K次机会可以让一条边的权值变为0,求最短路. 在存储单源最短路的数组上多开一维状态,d[i][k]表示走到序号i的点,且让k条边 ...

        1. 软工实践-Alpha 冲刺 (8/10)
        1. 队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 已经解决登录注册等基本功能的界面. 完成非功能的主界面制作 ...

        1. HDU 5418 Victor and World 允许多次经过的TSP
        1. 题目链接: hdu: http://acm.hdu.edu.cn/showproblem.php?pid=5418 bestcoder(中文): http://bestcoder.hdu.edu.cn ...

        1. centos快速安装lamp
        1. 搭建MySQL数据库 使用 yum 安装 MySQL: yum install mysql-server -y 安装完成后,启动 MySQL 服务: service mysqld restart 设置 ...

        1. C关键字volatile总结
        1. 做嵌入式C开发的相信都使用过一个关键字volatile,特别是做底层开发的.假设一个GPIO的数据寄存器地址是0x50000004,我们一般会定义一个这样的宏: #define GDATA *((vo ...

        1. 读着读着《构建之法》(Build To Win) 越精神的白雪儿的思考
        1. 哲学家的宗旨是:我思,故我在 科学家的宗旨是:我发现,故我在 工程师的宗旨是:我构建,故我在 ——<工程学--无尽的前沿> 序言:珍惜角色“人”,注重实践“物” <构建之法>, ...

        1. HDU 2154 跳舞毯
        1. http://acm.hdu.edu.cn/showproblem.php?pid=2154 Problem Description 由于长期缺乏运动,小黑发现自己的身材臃肿了许多,于是他想健身,更准 ...

        1. RAD Studio 10.3 Rio (BCB & Dephi) 发布啦
        1. 期盼已久的RAD Studio 10.3 Rio  终于发布了: 下载链接:http://altd.embarcadero.com/download/radstudio/10.3/delphicbui ...