-
k8s安装目录
- 1. 组件版本 && 集群环境
- 组件版本
- etcd 集群 && k8s master 机器 && k8s node 机器
- 集群环境变量
- 2. 创建CA 证书和密钥
- 安装 CFSSL
- 创建CA
- 分发证书
- 3. 部署高可用etcd 集群
- 定义环境变量
- 下载etcd 二进制文件
- 创建TLS 密钥和证书
- 创建etcd 的systemd unit 文件
- 启动etcd 服务
- 验证服务
- 4. 配置kubectl 命令行工具
- 环境变量
- 下载kubectl
- 创建admin 证书
- 创建kubectl kubeconfig 文件
- 分发kubeconfig 文件
- 5. 部署Flannel 网络
- 环境变量
- 创建TLS 密钥和证书
- 向etcd 写入集群Pod 网段信息
- 安装和配置flanneld
- 启动flanneld
- 检查flanneld 服务
- 检查分配给各flanneld 的Pod 网段信息
- 确保各节点间Pod 网段能互联互通
- 6. 部署master 节点
- 环境变量
- 下载最新版本的二进制文件
- 创建kubernetes 证书
- 6.1 配置和启动kube-apiserver
- 创建kube-apiserver 使用的客户端token 文件
- 创建kube-apiserver 的systemd unit文件
- 启动kube-apiserver
- 6.2 配置和启动kube-controller-manager
- 创建kube-controller-manager 的systemd unit 文件
- 启动kube-controller-manager
- 6.3 配置和启动kube-scheduler
- 创建kube-scheduler 的systemd unit文件
- 启动kube-scheduler
- 6.4 验证master 节点
- 7. kube-apiserver 高可用
- 安装haproxy
- 配置haproxy
- 启动haproxy
- 问题
- 方式1:使用阿里云SLB
- 方式2:使用keepalived
- kube-controller-manager 和kube-scheduler 的高可用
- 8. 部署Node 节点
- 环境变量
- 开启路由转发
- 配置docker
- 启动docker
- 安装和配置kubelet
- 创建kubelet bootstapping kubeconfig 文件
- 创建kubelet 的systemd unit 文件
- 启动kubelet
- 通过kubelet 的TLS 证书请求
- 配置kube-proxy
- 创建kube-proxy 证书签名请求:
- 生成kube-proxy 客户端证书和私钥
- 创建kube-proxy kubeconfig 文件
- 创建kube-proxy 的systemd unit 文件
- 启动kube-proxy
- 验证集群功能
- 9. 部署kubedns 插件
- 系统预定义的RoleBinding
- 配置kube-dns ServiceAccount
- 配置kube-dns 服务
- 配置kube-dns Deployment
- 执行所有定义文件
- 检查kubedns 功能
- 10. 部署Dashboard 插件
- 配置dashboard-controller
- 配置dashboard-service
- 执行所有定义文件
- 检查执行结果
- 访问dashboard
- 11. 部署Heapster 插件
- 执行所有文件
- 检查执行结果
- 访问 grafana
- 12. 安装Ingress
- 部署traefik
- 创建rbac
- DaemonSet 形式部署traefik
- 创建ingress
- Traefik UI
- 测试
- 部署traefik
- 13. 日志收集
- 14. 私有仓库harbor 搭建
- 15. 问题汇总
-
- 15.1 dashboard无法显示监控图
- 15.2 kube-proxy报错kube-proxy[2241]: E0502 15:55:13.889842 2241 conntrack.go:42] conntrack returned error: error looking for path of conntrack: exec: “conntrack”: executable file not found in $PATH
- 15.3 Unable to access kubernetes services: no route to host
- 15.4 使用NodePort 类型的服务,只能在POD 所在节点进行访问
-
- 参考资料
- 1. 组件版本 && 集群环境
在该文档的基础上重新部署了最新的v1.8.2版本,实现了kube-apiserver的高可用、traefik ingress 的部署、在kubernetes上安装docker的私有仓库harbor、容器化kubernetes部分组建、使用阿里云日志服务收集日志。
部署完成后,你将理解系统各组件的交互原理,进而能快速解决实际问题,所以本文档主要适合于那些有一定kubernetes基础,想通过一步步部署的方式来学习和了解系统配置、运行原理的人。
本系列系文档适用于 CentOS 7、Ubuntu 16.04 及以上版本系统,由于启用了 TLS 双向认证、RBAC 授权等严格的安全机制,建议从头开始部署,否则可能会认证、授权等失败!
有人问我为什么这么长的文章不分拆成几篇文章啊?这样阅读起来也方便啊,然而在我自己学习的过程中,这种整个一篇文章把一件事情从头到尾讲清楚的形式是最好的,能给读者提供一种
沉浸式的学习体验,阅读完整个文章后有种酣畅淋漓的感觉,所以我选择这种一篇文章的形式。
1. 组件版本 && 集群环境
组件版本
- Kubernetes 1.8.2(1.9.x版本也可以,只有细微的差别)
- 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 ' ' 生成 2BOOTSTRAP_TOKEN="8981b594122ebed7596f1d3b69c78223" 3 4# 建议使用未用的网段来定义服务网段和Pod 网段 5# 服务网段(Service CIDR),部署前路由不可达,部署后集群内部使用IP:Port可达 6SERVICE_CIDR="10.254.0.0/16" 7# Pod 网段(Cluster CIDR),部署前路由不可达,部署后路由可达(flanneld 保证) 8CLUSTER_CIDR="172.30.0.0/16" 9 10# 服务端口范围(NodePort Range) 11NODE_PORT_RANGE="30000-32766" 12 13# etcd集群服务地址列表 14ETCD_ENDPOINTS="https://192.168.1.137:2379,https://192.168.1.138:2379,https://192.168.1.170:2379" 15 16# flanneld 网络配置前缀 17FLANNEL_ETCD_PREFIX="/kubernetes/network" 18 19# kubernetes 服务IP(预先分配,一般为SERVICE_CIDR中的第一个IP) 20CLUSTER_KUBERNETES_SVC_IP="10.254.0.1" 21 22# 集群 DNS 服务IP(从SERVICE_CIDR 中预先分配) 23CLUSTER_DNS_SVC_IP="10.254.0.2" 24 25# 集群 DNS 域名 26CLUSTER_DNS_DOMAIN="cluster.local." 27 28# MASTER API Server 地址 29MASTER_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 5$ wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 6$ chmod +x cfssljson_linux-amd64 7$ sudo mv cfssljson_linux-amd64 /usr/k8s/bin/cfssljson 8 9$ wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 10$ chmod +x cfssl-certinfo_linux-amd64 11$ sudo mv cfssl-certinfo_linux-amd64 /usr/k8s/bin/cfssl-certinfo 12 13$ export PATH=/usr/k8s/bin:$PATH 14$ mkdir ssl && cd ssl 15$ cfssl print-defaults config > config.json 16$ 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}
CN:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名(User Name);浏览器使用该字段验证网站是否合法;O:Organization,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个节点,分别命名为etcd01、etcd02、etcd03:
- 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} 22EOF
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* 6etcd.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] 4Description=Etcd Server 5After=network.target 6After=network-online.target 7Wants=network-online.target 8Documentation=https://github.com/coreos 9[Service] 10Type=notify 11WorkingDirectory=/var/lib/etcd/ 12ExecStart=/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 28Restart=on-failure 29RestartSec=5 30LimitNOFILE=65536 31[Install] 32WantedBy=multi-user.target 33EOF
- 指定
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 节点上执行下面命令:
1for 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
输出如下结果:
1https://192.168.1.137:2379 is healthy: successfully committed proposal: took = 1.509032ms 2https://192.168.1.138:2379 is healthy: successfully committed proposal: took = 1.639228ms 3https://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} 19EOF
- 后续
kube-apiserver使用RBAC 对客户端(如kubelet、kube-proxy、Pod)请求进行授权 kube-apiserver预定义了一些RBAC 使用的RoleBindings,如cluster-admin 将Groupsystem:masters与Rolecluster-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 6admin.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:masters,kube-apiserver预定义的 RoleBindingcluster-admin将 Groupsystem:masters与 Rolecluster-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} 19EOF
生成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* 6flanneld.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] 3Description=Flanneld overlay address etcd agent 4After=network.target 5After=network-online.target 6Wants=network-online.target 7After=etcd.service 8Before=docker.service 9[Service] 10Type=notify 11ExecStart=/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} 17ExecStartPost=/usr/k8s/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker 18Restart=on-failure 19[Install] 20WantedBy=multi-user.target 21RequiredBy=docker.service 22EOF
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 服务
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 8/kubernetes/network/subnets/172.30.19.0-24 9/kubernetes/network/subnets/172.30.30.0-24 10/kubernetes/network/subnets/172.30.77.0-24 11/kubernetes/network/subnets/172.30.41.0-24 12/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-scheduler、kube-controller-manager和kube-apiserver三者的功能紧密相关;- 同时只能有一个
kube-scheduler、kube-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目录
$ 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} 29EOF
- 如果 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* 6kubernetes.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" 4EOF 5$ sudo mv token.csv /etc/kubernetes/
创建kube-apiserver 的systemd unit文件
1$ cat > kube-apiserver.service <<EOF 2[Unit] 3Description=Kubernetes API Server 4Documentation=https://github.com/GoogleCloudPlatform/kubernetes 5After=network.target 6[Service] 7ExecStart=/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 38Restart=on-failure 39RestartSec=5 40Type=notify 41LimitNOFILE=65536 42[Install] 43WantedBy=multi-user.target 44EOF
- 如果你安装的是1.9.x版本的,一定要记住上面的参数
experimental-bootstrap-token-auth,需要替换成enable-bootstrap-token-auth,因为这个参数在1.9.x里面已经废弃掉了 - 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)
1apiVersion: audit.k8s.io/v1beta1 # This is required. 2kind: Policy 3# Don't generate audit events for all requests in RequestReceived stage. 4omitStages: 5 - "RequestReceived" 6rules: 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 20 # Don't log requests to a configmap called "controller-leader" 21 - level: None 22 resources: 23 - group: "" 24 resources: ["configmaps"] 25 resourceNames: ["controller-leader"] 26 27 # Don't log watch requests by the "system:kube-proxy" on endpoints or services 28 - level: None 29 users: ["system:kube-proxy"] 30 verbs: ["watch"] 31 resources: 32 - group: "" # core API group 33 resources: ["endpoints", "services"] 34 35 # Don't log authenticated requests to certain non-resource URL paths. 36 - level: None 37 userGroups: ["system:authenticated"] 38 nonResourceURLs: 39 - "/api*" # Wildcard matching. 40 - "/version" 41 42 # Log the request body of configmap changes in kube-system. 43 - level: Request 44 resources: 45 - group: "" # core API group 46 resources: ["configmaps"] 47 # This rule only applies to resources in the "kube-system" namespace. 48 # The empty string "" can be used to select non-namespaced resources. 49 namespaces: ["kube-system"] 50 51 # Log configmap and secret changes in all other namespaces at the Metadata level. 52 - level: Metadata 53 resources: 54 - group: "" # core API group 55 resources: ["secrets", "configmaps"] 56 57 # Log all other resources in core and extensions at the Request level. 58 - level: Request 59 resources: 60 - group: "" # core API group 61 - group: "extensions" # Version of group should NOT be included. 62 63 # A catch-all rule to log all other requests at the Metadata level. 64 - level: Metadata 65 # Long-running requests like watches that fall under this rule will not 66 # generate an audit event in RequestReceived. 67 omitStages: 68 - "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] 3Description=Kubernetes Controller Manager 4Documentation=https://github.com/GoogleCloudPlatform/kubernetes 5[Service] 6ExecStart=/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 19Restart=on-failure 20RestartSec=5 21[Install] 22WantedBy=multi-user.target 23EOF
-
--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] 3Description=Kubernetes Scheduler 4Documentation=https://github.com/GoogleCloudPlatform/kubernetes 5[Service] 6ExecStart=/usr/k8s/bin/kube-scheduler \\ 7 --address=127.0.0.1 \\ 8 --master=http://${MASTER_URL}:8080 \\ 9 --leader-elect=true \\ 10 --v=2 11Restart=on-failure 12RestartSec=5 13[Install] 14WantedBy=multi-user.target 15EOF
--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 2NAME STATUS MESSAGE ERROR 3scheduler Healthy ok 4controller-manager Healthy ok 5etcd-1 Healthy {"health": "true"} 6etcd-2 Healthy {"health": "true"} 7etcd-0 Healthy {"health": "true"}
7. kube-apiserver 高可用
按照上面的方式在master01与master02机器上安装kube-apiserver、kube-controller-manager、kube-scheduler,但是现在我们还是手动指定访问的6443和8080端口的,因为我们的域名k8s-api.virtual.local对应的master01节点直接通过http 和https 还不能访问,这里我们使用haproxy 来代替请求。
明白什么意思吗?就是我们需要将http默认的80端口请求转发到
apiserver的8080端口,将https默认的443端口请求转发到apiserver的6443端口,所以我们这里使用haproxy来做请求转发。
安装haproxy
$ yum install -y haproxy
配置haproxy
由于集群内部有的组建是通过非安全端口访问apiserver 的,有的是通过安全端口访问apiserver 的,所以我们要配置http 和https 两种代理方式,配置文件 /etc/haproxy/haproxy.cfg:
1listen 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 11frontend k8s-api 12 bind 192.168.1.137:443 13 mode tcp 14 option tcplog 15 tcp-request inspect-delay 5s 16 tcp-request content accept if { req.ssl_hello_type 1 } 17 default_backend k8s-api 18 19backend k8s-api 20 mode tcp 21 option tcplog 22 option tcp-check 23 balance roundrobin 24 default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100 25 server k8s-api-1 192.168.1.137:6443 check 26 server k8s-api-2 192.168.1.138:6443 check 27 28frontend k8s-http-api 29 bind 192.168.1.137:80 30 mode tcp 31 option tcplog 32 default_backend k8s-http-api 33 34backend k8s-http-api 35 mode tcp 36 option tcplog 37 option tcp-check 38 balance roundrobin 39 default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100 40 server k8s-http-api-1 192.168.1.137:8080 check 41 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 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# 添加以下内容 3net.ipv4.ip_forward = 1 4net.ipv4.ip_nonlocal_bind = 1 5 6# 验证并生效 7$ sysctl -p 8# 验证是否生效 9$ cat /proc/sys/net/ipv4/ip_forward 101
安装keepalived:
$ yum install -y keepalived
我们这里将master01 设置为Master,master02 设置为Backup,修改配置:
1$ vi /etc/keepalived/keepalived.conf 2! Configuration File for keepalived 3 4global_defs { 5 notification_email { 6 } 7 router_id kube_api 8} 9 10vrrp_script check_haproxy { 11 # 自身状态检测 12 script "killall -0 haproxy" 13 interval 3 14 weight 5 15} 16 17vrrp_instance haproxy-vip { 18 # 使用单播通信,默认是组播通信 19 unicast_src_ip 192.168.1.137 20 unicast_peer { 21 192.168.1.138 22 } 23 # 初始化状态 24 state MASTER 25 # 虚拟ip 绑定的网卡 (这里根据你自己的实际情况选择网卡) 26 interface eth0 27 # 此ID 要与Backup 配置一致 28 virtual_router_id 51 29 # 默认启动优先级,要比Backup 大点,但要控制量,保证自身状态检测生效 30 priority 100 31 advert_int 1 32 authentication { 33 auth_type PASS 34 auth_pass 1111 35 } 36 virtual_ipaddress { 37 # 虚拟ip 地址 38 192.168.1.139 39 } 40 track_script { 41 check_haproxy 42 } 43} 44 45virtual_server 192.168.1.139 80 { 46 delay_loop 5 47 lvs_sched wlc 48 lvs_method NAT 49 persistence_timeout 1800 50 protocol TCP 51 52 real_server 192.168.1.137 80 { 53 weight 1 54 TCP_CHECK { 55 connect_port 80 56 connect_timeout 3 57 } 58 } 59} 60 61virtual_server 192.168.1.139 443 { 62 delay_loop 5 63 lvs_sched wlc 64 lvs_method NAT 65 persistence_timeout 1800 66 protocol TCP 67 68 real_server 192.168.1.137 443 { 69 weight 1 70 TCP_CHECK { 71 connect_port 443 72 connect_timeout 3 73 } 74 } 75}
统一的方式在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 31: 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 72: 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-manager和kube-scheduler的**–master**参数中的8080端口去掉了,然后分别重启这两个组件即可。
验证apiserver:关闭master01 节点上的kube-apiserver 进程,然后查看虚拟ip是否漂移到了master02 节点。
然后我们就可以将第一步在/etc/hosts里面设置的域名对应的IP 更改为我们的虚拟IP了
master01 与master 02 节点都需要安装keepalived 和haproxy,实际上我们虚拟IP的自身检测应该是检测haproxy,脚本大家可以自行更改
kube-apiserver ha
这样我们就实现了接入层apiserver 的高可用了,一个部分是多活的apiserver 服务,另一个部分是一主一备的haproxy 服务。
kube-controller-manager 和kube-scheduler 的高可用
Kubernetes 的管理层服务包括kube-scheduler和kube-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节点的全局唯一性。整体方案如下图所示:
img
当集群中的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文件,添加下面的规则:
1net.ipv4.ip_forward=1 2net.bridge.bridge-nf-call-iptables=1 3net.bridge.bridge-nf-call-ip6tables=1
执行下面的命令立即生效:
$ sysctl -p
配置docker
你可以用二进制或yum install 的方式来安装docker,然后修改docker 的systemd unit 文件:
1$ cat /usr/lib/systemd/system/docker.service # 用systemctl status docker 命令可查看unit 文件路径 2[Unit] 3Description=Docker Application Container Engine 4Documentation=https://docs.docker.com 5After=network-online.target firewalld.service 6Wants=network-online.target 7 8[Service] 9Type=notify 10# the default is not to use systemd for cgroups because the delegate issues still 11# exists and systemd currently does not support the cgroup feature set required 12# for containers run by docker 13EnvironmentFile=-/run/flannel/docker 14ExecStart=/usr/bin/dockerd --log-level=info $DOCKER_NETWORK_OPTIONS 15ExecReload=/bin/kill -s HUP $MAINPID 16# Having non-zero Limit*s causes performance problems due to accounting overhead 17# in the kernel. We recommend using cgroups to do container-local accounting. 18LimitNOFILE=infinity 19LimitNPROC=infinity 20LimitCORE=infinity 21# Uncomment TasksMax if your systemd version supports it. 22# Only systemd 226 and above support this version. 23#TasksMax=infinity 24TimeoutStartSec=0 25# set delegate yes so that systemd does not reset the cgroups of docker containers 26Delegate=yes 27# kill only the docker process, not all processes in the cgroup 28KillMode=process 29# restart the docker process if it exits prematurely 30Restart=on-failure 31StartLimitBurst=3 32StartLimitInterval=60s 33 34[Install] 35WantedBy=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:$ sudo iptables -P FORWARD ACCEPT
如果没有开启上面的路由转发(net.ipv4.ip_forward=1),则需要把以下命令写入/etc/rc.local文件中,防止节点重启iptables FORWARD chain的默认策略又还原为DROP(下面的开机脚本我测试了几次都没生效,不知道是不是方法有误,所以最好的方式还是开启上面的路由转发功能,一劳永逸)
sleep 60 && /sbin/iptables -P FORWARD ACCEPT
-
为了加快 pull image 的速度,可以使用国内的仓库镜像服务器,同时增加下载的并发数。(如果 dockerd 已经运行,则需要重启 dockerd 生效。)
$ cat /etc/docker/daemon.json { "max-concurrent-downloads": 10 }
启动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服务是否正常
安装和配置kubelet
kubelet 启动时向kube-apiserver 发送TLS bootstrapping 请求,需要先将bootstrap token 文件中的kubelet-bootstrap 用户赋予system:node-bootstrapper 角色,然后kubelet 才有权限创建认证请求(certificatesigningrequests):
kubelet就是运行在Node节点上的,所以这一步安装是在所有的Node节点上,如果你想把你的Master也当做Node节点的话,当然也可以在Master节点上安装的。
$ 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 授权规则:
$ 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] 4Description=Kubernetes Kubelet 5Documentation=https://github.com/GoogleCloudPlatform/kubernetes 6After=docker.service 7Requires=docker.service 8[Service] 9WorkingDirectory=/var/lib/kubelet 10ExecStart=/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 26Restart=on-failure 27RestartSec=5 28[Install] 29WantedBy=multi-user.target 30EOF
请仔细阅读下面的注意事项,不然可能会启动失败。
--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 2NAME AGE REQUESTOR CONDITION 3node-csr--k3G2G1EoM4h9w1FuJRjJjfbIPNxa551A8TZfW9dG-g 2m kubelet-bootstrap Pending 4$ kubectl get nodes 5No resources found.
通过CSR 请求:
1$ kubectl certificate approve node-csr--k3G2G1EoM4h9w1FuJRjJjfbIPNxa551A8TZfW9dG-g 2certificatesigningrequest "node-csr--k3G2G1EoM4h9w1FuJRjJjfbIPNxa551A8TZfW9dG-g" approved 3$ kubectl get nodes 4NAME STATUS ROLES AGE VERSION 5192.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} 19EOF
- CN 指定该证书的 User 为
system:kube-proxy kube-apiserver预定义的 RoleBindingsystem:node-proxier将Usersystem:kube-proxy与 Rolesystem:node-proxier绑定,该 Role 授予了调用kube-apiserverProxy 相关 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* 6kube-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-authority、client-certificate和client-key指向的证书文件内容写入到生成的kube-proxy.kubeconfig文件中 kube-proxy.pem证书中 CN 为system:kube-proxy,kube-apiserver预定义的 RoleBindingcluster-admin将Usersystem:kube-proxy与 Rolesystem:node-proxier绑定,该 Role 授予了调用kube-apiserverProxy 相关 API 的权限
创建kube-proxy 的systemd unit 文件
1$ sudo mkdir -p /var/lib/kube-proxy # 必须先创建工作目录 2$ cat > kube-proxy.service <<EOF 3[Unit] 4Description=Kubernetes Kube-Proxy Server 5Documentation=https://github.com/GoogleCloudPlatform/kubernetes 6After=network.target 7[Service] 8WorkingDirectory=/var/lib/kube-proxy 9ExecStart=/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 16Restart=on-failure 17RestartSec=5 18LimitNOFILE=65536 19[Install] 20WantedBy=multi-user.target 21EOF
--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将Usersystem:kube-proxy与 Rolesystem:node-proxier绑定,该 Role 授予了调用kube-apiserverProxy 相关 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)
1apiVersion: v1 2kind: Service 3metadata: 4 name: nginx-ds 5 labels: 6 app: nginx-ds 7spec: 8 type: NodePort 9 selector: 10 app: nginx-ds 11 ports: 12 - name: http 13 port: 80 14 targetPort: 80 15--- 16apiVersion: extensions/v1beta1 17kind: DaemonSet 18metadata: 19 name: nginx-ds 20 labels: 21 addonmanager.kubernetes.io/mode: Reconcile 22spec: 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 2service "nginx-ds" created 3daemonset "nginx-ds" created
执行下面的命令查看Pod 和SVC:
1$ kubectl get pods -o wide 2NAME READY STATUS RESTARTS AGE IP NODE 3nginx-ds-f29zt 1/1 Running 0 23m 172.17.0.2 192.168.1.170 4$ kubectl get svc 5NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 6nginx-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 2kubedns-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 2apiVersion: rbac.authorization.k8s.io/v1 3kind: ClusterRoleBinding 4metadata: 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 14roleRef: 15 apiGroup: rbac.authorization.k8s.io 16 kind: ClusterRole 17 name: system:kube-dns 18subjects: 19- kind: ServiceAccount 20 name: kube-dns 21 namespace: kube-system
kubedns-controller.yaml中定义的 Pods 时使用了kubedns-sa.yaml文件定义的kube-dnsServiceAccount,所以具有访问 kube-apiserver DNS 相关 API 的权限;
配置kube-dns ServiceAccount
无需更改
配置kube-dns 服务
1$ diff kubedns-svc.yaml.base kubedns-svc.yaml 230c30 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 288c88 3< - --domain=__PILLAR__DNS__DOMAIN__. 4--- 5> - --domain=cluster.local 6128c128 7< - --server=/__PILLAR__DNS__DOMAIN__/127.0.0.1#10053 8--- 9> - --server=/cluster.local/127.0.0.1#10053 10160,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-dnsServiceAccount,该账户具有访问 kube-apiserver DNS 相关 API 的权限
执行所有定义文件
1$ pwd 2/home/ych/k8s-repo/kube-dns 3$ ls *.yaml 4kubedns-cm.yaml kubedns-controller.yaml kubedns-sa.yaml kubedns-svc.yaml 5$ kubectl create -f .
检查kubedns 功能
新建一个Deployment
1$ cat > my-nginx.yaml<<EOF 2apiVersion: extensions/v1beta1 3kind: Deployment 4metadata: 5 name: my-nginx 6spec: 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 18EOF 19$ kubectl create -f my-nginx.yaml 20deployment "my-nginx" created
Expose 该Deployment,生成my-nginx 服务
1$ kubectl expose deploy my-nginx 2$ kubectl get services 3NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 4kubernetes ClusterIP 10.254.0.1 <none> 443/TCP 1d 5my-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 2apiVersion: v1 3kind: Pod 4metadata: 5 name: nginx 6spec: 7 containers: 8 - name: nginx 9 image: nginx:1.7.9 10 ports: 11 - containerPort: 80 12EOF 13$ kubectl create -f pod-nginx.yaml 14pod "nginx" created 15$ kubectl exec nginx -i -t -- /bin/bash 16root@nginx:/# cat /etc/resolv.conf 17nameserver 10.254.0.2 18search default.svc.cluster.local. svc.cluster.local. cluster.local. 19options ndots:5 20root@nginx:/# ping my-nginx 21PING my-nginx.default.svc.cluster.local (10.254.32.162): 48 data bytes 22^C--- my-nginx.default.svc.cluster.local ping statistics --- 2314 packets transmitted, 0 packets received, 100% packet loss 24 25root@nginx:/# ping kubernetes 26PING kubernetes.default.svc.cluster.local (10.254.0.1): 48 data bytes 27^C--- kubernetes.default.svc.cluster.local ping statistics --- 286 packets transmitted, 0 packets received, 100% packet loss 29 30root@nginx:/# ping kube-dns.kube-system.svc.cluster.local 31PING kube-dns.kube-system.svc.cluster.local (10.254.0.2): 48 data bytes 32^C--- kube-dns.kube-system.svc.cluster.local ping statistics --- 332 packets transmitted, 0 packets received, 100% packet loss
10. 部署Dashboard 插件
官方文件目录:kubernetes/cluster/addons/dashboard
使用的文件如下:
1$ ls *.yaml 2dashboard-controller.yaml dashboard-rbac.yaml dashboard-service.yaml
- 新加了
dashboard-rbac.yaml文件,定义 dashboard 使用的 RoleBinding。
由于 kube-apiserver 启用了 RBAC 授权,而官方源码目录的 dashboard-controller.yaml 没有定义授权的 ServiceAccount,所以后续访问 kube-apiserver 的 API 时会被拒绝,前端界面提示:
403
解决办法是:定义一个名为dashboard 的ServiceAccount,然后将它和Cluster Role view 绑定:
1$ cat > dashboard-rbac.yaml<<EOF 2apiVersion: v1 3kind: ServiceAccount 4metadata: 5 name: dashboard 6 namespace: kube-system 7--- 8kind: ClusterRoleBinding 9apiVersion: rbac.authorization.k8s.io/v1alpha1 10metadata: 11 name: dashboard 12subjects: 13 - kind: ServiceAccount 14 name: dashboard 15 namespace: kube-system 16roleRef: 17 kind: ClusterRole 18 name: cluster-admin 19 apiGroup: rbac.authorization.k8s.io 20EOF
配置dashboard-controller
120a21 2> serviceAccountName: dashboard
- 使用名为 dashboard 的自定义 ServiceAccount
配置dashboard-service
1$ diff dashboard-service.yaml.orig dashboard-service.yaml 210a11 3> type: NodePort
- 指定端口类型为 NodePort,这样外界可以通过地址 nodeIP:nodePort 访问 dashboard
执行所有定义文件
1$ pwd 2/home/ych/k8s-repo/dashboard 3$ ls *.yaml 4dashboard-controller.yaml dashboard-rbac.yaml dashboard-service.yaml 5$ kubectl create -f .
检查执行结果
查看分配的 NodePort
1$ kubectl get services kubernetes-dashboard -n kube-system 2NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 3kubernetes-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 2NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 3kubernetes-dashboard 1 1 1 1 3m 4 5$ kubectl get pods -n kube-system | grep dashboard 6kubernetes-dashboard-6667f9b4c-4xbpz 1/1 Running 0 3m
访问dashboard
- kubernetes-dashboard 服务暴露了 NodePort,可以使用
http://NodeIP:nodePort地址访问 dashboard - 通过 kube-apiserver 访问 dashboard
- 通过 kubectl proxy 访问 dashboard
dashboard ui
由于缺少 Heapster 插件,当前 dashboard 不能展示 Pod、Nodes 的 CPU、内存等 metric 图形
注意:如果你的后端
apiserver是高可用的集群模式的话,那么Dashboard的apiserver-host最好手动指定,不然,当你apiserver某个节点挂了的时候,Dashboard可能不能正常访问,如下配置
1image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.7.1 2ports: 3- containerPort: 9090 4 protocol: TCP 5args: 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/ 2grafana.yaml heapster.yaml influxdb.yaml 3heapster-rbac.yaml
为方便测试访问,将grafana.yaml下面的服务类型设置为type=NodePort
执行所有文件
1$ kubectl create -f rbac/heapster-rbac.yaml 2clusterrolebinding "heapster" created 3$ kubectl create -f influxdb 4deployment "monitoring-grafana" created 5service "monitoring-grafana" created 6serviceaccount "heapster" created 7deployment "heapster" created 8service "heapster" created 9deployment "monitoring-influxdb" created 10service "monitoring-influxdb" created
检查执行结果
检查 Deployment
1$ kubectl get deployments -n kube-system | grep -E 'heapster|monitoring' 2heapster 1 1 1 1 2m 3monitoring-grafana 1 1 1 0 2m 4monitoring-influxdb 1 1 1 1 2m
检查 Pods
1$ kubectl get pods -n kube-system | grep -E 'heapster|monitoring' 2heapster-7cf895f48f-p98tk 1/1 Running 0 2m 3monitoring-grafana-c9d5cd98d-gb9xn 0/1 CrashLoopBackOff 4 2m 4monitoring-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 2NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 3monitoring-grafana NodePort 10.254.34.89 <none> 80:30191/TCP 28m
则我们就可以通过任意一个节点加上上面的30191端口就可以访问grafana 了。
grafana ui
heapster 正确安装后,我们便可以回去看我们的dashboard 是否有图表出现了:
dashboard
12. 安装Ingress
Ingress其实就是从kuberenets集群外部访问集群的一个入口,将外部的请求转发到集群内不同的Service 上,其实就相当于nginx、apache 等负载均衡代理服务器,再加上一个规则定义,路由信息的刷新需要靠Ingress controller来提供
Ingress controller可以理解为一个监听器,通过不断地与kube-apiserver打交道,实时的感知后端service、pod 等的变化,当得到这些变化信息后,Ingress controller再结合Ingress的配置,更新反向代理负载均衡器,达到服务发现的作用。其实这点和服务发现工具consul的consul-template非常类似。
部署traefik
Traefik是一款开源的反向代理与负载均衡工具。它最大的优点是能够与常见的微服务系统直接整合,可以实现自动化动态配置。目前支持Docker、Swarm、Mesos/Marathon、 Mesos、Kubernetes、Consul、Etcd、Zookeeper、BoltDB、Rest API等等后端模型。
traefik
创建rbac
创建文件:ingress-rbac.yaml,用于service account验证
1apiVersion: v1 2kind: ServiceAccount 3metadata: 4 name: ingress 5 namespace: kube-system 6--- 7kind: ClusterRoleBinding 8apiVersion: rbac.authorization.k8s.io/v1beta1 9metadata: 10 name: ingress 11subjects: 12 - kind: ServiceAccount 13 name: ingress 14 namespace: kube-system 15roleRef: 16 kind: ClusterRole 17 name: cluster-admin 18 apiGroup: rbac.authorization.k8s.io
DaemonSet 形式部署traefik
创建文件:traefik-daemonset.yaml,为保证traefik 总能提供服务,在每个节点上都部署一个traefik,所以这里使用DaemonSet 的形式
1kind: ConfigMap 2apiVersion: v1 3metadata: 4 name: traefik-conf 5 namespace: kube-system 6data: 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--- 22kind: DaemonSet 23apiVersion: extensions/v1beta1 24metadata: 25 name: traefik-ingress 26 namespace: kube-system 27 labels: 28 k8s-app: traefik-ingress 29spec: 30 template: 31 metadata: 32 labels: 33 k8s-app: traefik-ingress 34 name: traefik-ingress 35 spec: 36 terminationGracePeriodSeconds: 60 37 restartPolicy: Always 38 serviceAccountName: ingress 39 containers: 40 - image: traefik:latest 41 name: traefik-ingress 42 ports: 43 - name: http 44 containerPort: 80 45 hostPort: 80 46 - name: https 47 containerPort: 443 48 hostPort: 443 49 - name: admin 50 containerPort: 8080 51 args: 52 - --configFile=/etc/traefik/traefik.toml 53 - -d 54 - --web 55 - --kubernetes 56 - --logLevel=DEBUG 57 volumeMounts: 58 - name: traefik-config-volume 59 mountPath: /etc/traefik 60 - name: traefik-ssl-volume 61 mountPath: /ssl 62 volumes: 63 - name: traefik-config-volume 64 configMap: 65 name: traefik-conf 66 items: 67 - key: traefik-config 68 path: traefik.toml 69 - name: traefik-ssl-volume 70 secret: 71 secretName: traefik-ssl
注意上面的yaml 文件中我们添加了一个名为traefik-conf的ConfigMap,该配置是用来将http 请求强制跳转成https,并指定https 所需CA 文件地址,这里我们使用secret的形式来指定CA 文件的路径:
1$ ls 2ssl.crt ssl.key 3$ kubectl create secret generic traefik-ssl --from-file=ssl.crt --from-file=ssl.key --namespace=kube-system 4secret "traefik-ssl" created
创建ingress
创建文件:traefik-ingress.yaml,现在可以通过创建ingress文件来定义请求规则了,根据自己集群中的service 自己修改相应的serviceName 和servicePort
1apiVersion: extensions/v1beta1 2kind: Ingress 3metadata: 4 name: traefik-ingress 5spec: 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 2serviceaccount "ingress" created 3clusterrolebinding "ingress" created 4$ kubectl create -f traefik-daemonset.yaml 5configmap "traefik-conf" created 6daemonset "traefik-ingress" created 7$ kubectl create -f traefik-ingress.yaml 8ingress "traefik-ingress" created
Traefik UI
创建文件:traefik-ui.yaml,
1apiVersion: v1 2kind: Service 3metadata: 4 name: traefik-ui 5 namespace: kube-system 6spec: 7 selector: 8 k8s-app: traefik-ingress 9 ports: 10 - name: web 11 port: 80 12 targetPort: 8080 13--- 14apiVersion: extensions/v1beta1 15kind: Ingress 16metadata: 17 name: traefik-ui 18 namespace: kube-system 19spec: 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 3xx.xx.xx.xx master03 traefik.nginx.io traefik-ui.local
配置完成后,在本地访问:traefik-ui.local,则可以访问到traefik的dashboard页面:
traefik dashboard
同样的可以访问traefik.nginx.io,得到正确的结果页面:
WX20171110-140306
上面配置完成后,就可以将我们的所有节点加入到一个SLB中,然后配置相应的域名解析到SLB即可。
13. 日志收集
参考文章kubernetes 日志收集方案
14. 私有仓库harbor 搭建
参考文章在kubernetes 上搭建docker 私有仓库Harbor
15. 问题汇总
15.1 dashboard无法显示监控图
dashboard 和heapster influxdb都部署完成后 dashboard依旧无法显示监控图 通过排查 heapster log有超时错误
1` 2 3$ kubectl logs -f pods/heapster-2882613285-58d9r -n kube-system 4 5E0630 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💯 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:55.014414 1 influxdb.go:150] Failed to create infuxdb: failed to ping InfluxDB server at "monitoring-influxdb:8086" - Get http://monitoring-influxdb:8086/ping: dial tcp: lookup monitoring-influxdb on 10.254.0.2:53: read udp 172.30.45.4:48955->10.254.0.2:53: i/o timeout
我是docker的systemd Unit文件忘记添加
ExecStart=/root/local/bin/dockerd --log-level=error $DOCKER_NETWORK_OPTIONS
后边的$DOCKER_NETWORK_OPTIONS,导致docker0的网段跟flannel.1不一致。
15.2 kube-proxy报错kube-proxy[2241]: E0502 15:55:13.889842 2241 conntrack.go:42] conntrack returned error: error looking for path of conntrack: exec: “conntrack”: executable file not found in $PATH
导致现象:kubedns启动成功,运行正常,但是service 之间无法解析,kubernetes中的DNS解析异常
解决方法:CentOS中安装conntrack-tools包后重启kubernetes 集群即可。
15.3 Unable to access kubernetes services: no route to host
导致现象: 在POD 内访问集群的某个服务的时候出现no route to host
1$ curl my-nginx.nx.svc.cluster.local 2curl: (7) Failed connect to my-nginx.nx.svc.cluster.local:80; No route to host
解决方法:清除所有的防火墙规则,然后重启docker 服务
1$ iptables --flush && iptables -tnat --flush 2$ systemctl restart docker
15.4 使用NodePort 类型的服务,只能在POD 所在节点进行访问
导致现象: 使用NodePort 类型的服务,只能在POD 所在节点进行访问,其他节点通过NodePort 不能正常访问
解决方法: kube-proxy 默认使用的是proxy_model就是iptables,正常情况下是所有节点都可以通过NodePort 进行访问的,我这里将阿里云的安全组限制全部去掉即可,然后根据需要进