Kubernetes容器集群管理环境 - 完整部署(上篇)
<p> </p><p>Kubernetes(通常称为"K8S")是Google开源的容器集群管理系统。其设计目标是在主机集群之间提供一个能够自动化部署、可拓展、应用容器可运营的平台。Kubernetes通常结合docker容器工具工作,并且整合多个运行着docker容器的主机集群,Kubernetes不仅仅支持Docker,还支持Rocket,这是另一种容器技术。Kubernetes是一个用于容器集群的自动化部署、扩容以及运维的开源平台。<span style="color: rgba(0, 0, 255, 1); font-size: 15px"><strong>通过Kubernetes, 可以快速有效地响应用户需求:</strong></span><br><span style="color: rgba(255, 0, 255, 1)"><strong>-></strong></span> 快速而有预期地部署应用;<br><strong><span style="color: rgba(255, 0, 255, 1)">-></span></strong> 极速地扩展你的应用;<br><strong><span style="color: rgba(255, 0, 255, 1)">-> </span></strong> 无缝对接新的应用功能;<br><strong><span style="color: rgba(255, 0, 255, 1)">-></span></strong> 节省资源,优化硬件资源的使用;</p>
<p><span style="color: rgba(0, 0, 255, 1); font-size: 15px"><strong>Kubernetes功能特性:</strong></span><br><strong><span style="color: rgba(255, 0, 255, 1)">-> </span></strong> 自动化容器部署与复制<br><strong><span style="color: rgba(255, 0, 255, 1)">-></span></strong> 随时扩展或收缩容器规模<br><strong><span style="color: rgba(255, 0, 255, 1)">-></span></strong> 组织容器成组,提供容器间的负载均衡<br><strong><span style="color: rgba(255, 0, 255, 1)">-> </span></strong> 快速更新及回滚容器版本<br><strong><span style="color: rgba(255, 0, 255, 1)">-> </span></strong> 提供弹性伸缩,如果某个容器失效就进行替换</p>
<p><span style="color: rgba(0, 0, 255, 1)"><strong><span style="font-size: 15px">Kubernetes重要组件:</span></strong></span><br><span style="background-color: rgba(255, 255, 153, 1)"><strong>1)Master组件</strong></span><br>Master节点上面主要由四个模块组成:APIServer、scheduler、controller manager、etcd<br>-><strong><span style="color: rgba(255, 102, 0, 1)"> APIServer</span></strong>: 负责对外提供RESTful的Kubernetes API服务,它是系统管理指令的统一入口,任何对资源进行增删改查的操作都要交给APIServer处理后再提交给etcd。kubectl(k8s提供的客户端工具,该工具内部就是对Kubernetes API的调用)是直接和APIServer交互的。<br>-><strong><span style="color: rgba(255, 102, 0, 1)"> schedule</span></strong>: 它的职责很明确,就是负责调度pod到合适的Node上。如果把scheduler看成一个黑匣子,那么它的输入是pod和由多个Node组成的列表,输出是Pod和一个Node的绑定,即将这个pod部署到这个Node上。Kubernetes目前提供了调度算法,但是同样也保留了接口,用户可以根据自己的需求定义自己的调度算法。<br>-> <strong><span style="color: rgba(255, 102, 0, 1)"> controller manager</span></strong>: 如果说APIServer做的是“前台”的工作的话,那controller manager就是负责“后台”的。每个资源一般都对应有一个控制器,而controller manager就是负责管理这些控制器的。比如我们通过APIServer创建一个pod,当这个pod创建成功后,APIServer的任务就算完成了。而后面保证Pod的状态始终和我们预期的一样的重任就由controller manager去保证了。<br>-> <strong><span style="color: rgba(255, 102, 0, 1)">etcd</span></strong>: 它是一个高可用的键值存储系统,Kubernetes使用它来存储各个资源的状态,从而实现了Restful的API。</p>
<p><strong><span style="background-color: rgba(255, 255, 153, 1)">2)Node组件</span></strong><br>每个Node节点主要由三个模块组成:kubelet、kube-proxy、runtime。<br>runtime。runtime指的是容器运行环境,目前Kubernetes支持docker和rkt两种容器。<br>-> kubelet:Kubelet是Master在每个Node节点上面的agent,是Node节点上面最重要的模块,它负责维护和管理该Node上面的所有容器,但是如果容器不是通过Kubernetes创建的,它并不会管理。本质上,它负责使Pod得运行状态与期望的状态一致。<br>-> kube-proxy:该模块实现了Kubernetes中的服务发现和反向代理功能。反向代理方面:kube-proxy支持TCP和UDP连接转发,默认基于Round Robin算法将客户端流量转发到与service对应的一组后端pod。服务发现方面,kube-proxy使用etcd的watch机制,监控集群中service和endpoint对象数据的动态变化,并且维护一个service到endpoint的映射关系,从而保证了后端pod的IP变化不会对访问者造成影响。另外kube-proxy还支持session affinity。</p>
<p><strong><span style="background-color: rgba(255, 255, 153, 1)">3)Pod</span></strong><br>Pod是k8s进行资源调度的最小单位,每个Pod中运行着一个或多个密切相关的业务容器,这些业务容器共享这个Pause容器的IP和Volume,我们以这个不易死亡的Pause容器作为Pod的根容器,以它的状态表示整个容器组的状态。一个Pod一旦被创建就会放到Etcd中存储,然后由Master调度到一个Node绑定,由这个Node上的Kubelet进行实例化。每个Pod会被分配一个单独的Pod IP,Pod IP + ContainerPort 组成了一个Endpoint。</p>
<p><strong><span style="background-color: rgba(255, 255, 153, 1)">4)Service</span></strong><br>Service其功能使应用暴露,Pods 是有生命周期的,也有独立的 IP 地址,随着 Pods 的创建与销毁,一个必不可少的工作就是保证各个应用能够感知这种变化。这就要提到 Service 了,Service 是 YAML 或 JSON 定义的由 Pods 通过某种策略的逻辑组合。更重要的是,Pods 的独立 IP 需要通过 Service 暴露到网络中。</p>
<p>K8s集群可以帮助培育出一个组件及工具的生态,帮助减轻在公有云及私有云上运行应用的负担。之前已经详细介绍了Kubernetes的概念和原理, 对Kubernetes集群部署做一整理和记录,方便后续作为手册来用(参考来源)。</p>
<p><span style="font-size: 16px; font-family: arial, helvetica, sans-serif"><strong><span style="color: rgba(0, 0, 255, 1)">搭建Kubernetes集群环境有以下三种方式:</span></strong></span></p>
<p><strong><span style="color: rgba(0, 0, 0, 1)">1.</span> </strong><span style="color: rgba(255, 0, 255, 1)"><strong>Minikube安装方式</strong></span><br>Minikube是一个工具,可以在本地快速运行一个单点的Kubernetes,尝试Kubernetes或日常开发的用户使用。但是这种方式仅可用于学习和测试部署,<strong><span style="color: rgba(255, 102, 0, 1)">不能用于生产环境</span></strong>。</p>
<p><strong>2. <span style="color: rgba(255, 0, 255, 1)">K</span><span style="color: rgba(255, 0, 255, 1)">ubeadm安装方式</span></strong><br>kubeadm是一个kubernetes官方提供的快速安装和初始化拥有最佳实践(best practice)的kubernetes集群的工具,提供kubeadm init和kubeadm join,用于快速部署Kubernetes集群。目前kubeadm还处于beta 和alpha状态,<strong><span style="color: rgba(255, 102, 0, 1)">不推荐</span><span style="color: rgba(255, 102, 0, 1)">用在生产环境</span></strong>,但是可以通过学习这种部署方法来体会一些官方推荐的kubernetes最佳实践的设计和思想。</p>
<p>kubeadm的目标是提供一个最小可用的可以通过Kubernetes一致性测试的集群,所以并不会安装任何除此之外的非必须的addon。kubeadm默认情况下并不会安装一个网络解决方案,所以用kubeadm安装完之后,需要自己来安装一个网络的插件。所以说,目前的kubeadm是不能用于生产环境的</p>
<p><strong>3.</strong> <span style="color: rgba(255, 0, 255, 1)"><strong>二进制包安装方式</strong></span><strong><span style="color: rgba(51, 102, 255, 1)">(生产部署的推荐方式)</span></strong><br>从官方下载发行版的二进制包,手动部署每个组件,组成Kubernetes集群,这种方式符合企业生产环境标准的Kubernetes集群环境的安装,<strong><span style="color: rgba(255, 102, 0, 1)">可用于生产方式部署</span></strong>。</p>
<p><span style="font-size: 14pt; background-color: rgba(255, 255, 255, 1); color: rgba(153, 51, 102, 1)"><strong>一、基础信息</strong></span><br>使用Kubernetes1.14.2,所有节点机操作系统是Centos7.5。本文档部署中所需kubernetes相关安装包和镜像可提前在FQ服务器上下载,然后同步到k8s部署机器上。具体信息如下:</p>
<table border="0">
<tbody>
<tr>
<td>ip地址</td>
<td>主机名</td>
<td>角色</td>
</tr>
<tr>
<td>172.16.60.241</td>
<td>k8s-master01</td>
<td>主节点1、etc节点1</td>
</tr>
<tr>
<td>172.16.60.242</td>
<td>k8s-master02</td>
<td>主节点2、etc节点2</td>
</tr>
<tr>
<td>172.16.60.243</td>
<td>k8s-master03</td>
<td>主节点3、etc节点3</td>
</tr>
<tr>
<td>172.16.60.244 </td>
<td>k8s-node01</td>
<td>工作节点1</td>
</tr>
<tr>
<td>172.16.60.245</td>
<td>k8s-node02</td>
<td>工作节点2</td>
</tr>
<tr>
<td>172.16.60.246</td>
<td>k8s-node03</td>
<td>工作节点3</td>
</tr>
<tr>
<td>172.16.60.247</td>
<td>k8s-ha01</td>
<td>nginx节点1、harbor节点1</td>
</tr>
<tr>
<td>172.16.60.248</td>
<td>k8s-ha02</td>
<td>nginx节点2、harbor节点2</td>
</tr>
</tbody>
</table>
<p>本套Kubernetes集群环境版本<br>- Kubernetes 1.14.2<br>- Docker 18.09.6-ce<br>- Etcd 3.3.13<br>- Flanneld 0.11.0<br>插件:<br>- Coredns<br>- Dashboard<br>- Metrics-server<br>镜像仓库:<br>- harbor(两个仓库相互同步,对外提供统一入口VIP地址)</p>
<p><span style="font-size: 16px; color: rgba(51, 102, 255, 1)"><strong>主要配置策略</strong></span><br>kube-apiserver高可用(Nginx负载层):<br>- 使用Nginx+Keepalived实现高可用, <span style="color: rgba(0, 128, 0, 1)">VIP1:</span><span style="color: rgba(255, 102, 0, 1)">172.16.60.250</span>;<br>- 关闭非安全端口 8080 和匿名访问;<br>- 在安全端口 6443 接收 https 请求;<br>- 严格的认证和授权策略 (x509、token、RBAC);<br>- 开启 bootstrap token 认证,支持 kubelet TLS bootstrapping;<br>- 使用 https 访问 kubelet、etcd,加密通信;</p>
<p>kube-controller-manager高可用:<br>- 3节点高可用;<br>- 关闭非安全端口,在安全端口 10252 接收 https 请求;<br>- 使用 kubeconfig 访问 apiserver 的安全端口;<br>- 自动 approve kubelet 证书签名请求 (CSR),证书过期后自动轮转;<br>- 各controller 使用自己的 ServiceAccount 访问 apiserver;</p>
<p>kube-scheduler高可用:<br>- 3节点高可用;<br>- 使用 kubeconfig 访问 apiserver 的安全端口;</p>
<p>kubelet:<br>- 使用 kubeadm 动态创建 bootstrap token,而不是在 apiserver 中静态配置;<br>- 使用TLS bootstrap机制自动生成 client 和 server 证书,过期后自动轮转;<br>- 在 kubeletConfiguration 类型的 JSON 文件配置主要参数;<br>- 关闭只读端口,在安全端口 10250 接收 https 请求,对请求进行认证和授权,拒绝匿名访问和非授权访问;<br>- 使用 kubeconfig 访问 apiserver 的安全端口;</p>
<p>kube-proxy:<br>- 使用kubeconfig 访问 apiserver 的安全端口;<br>- 在KubeProxyConfiguration 类型的 JSON 文件配置主要参数;<br>- 使用ipvs代理模式;</p>
<p>集群插件:<br>- DNS:使用功能、性能更好的 coredns;<br>- Dashboard:支持登录认证;<br>- Metric:metrics-server,使用 https 访问 kubelet 安全端口;<br>- Log:Elasticsearch、Fluend、Kibana;<br>- Registry 镜像库:Harbor私有仓库,两个节点相互同步;</p>
<p><span style="background-color: rgba(255, 255, 153, 1)"> </span><br>kubernetes集群部署中生成的证书文件如下:<br>ca-key.pem 根私钥(controller-manager配置的时候,跟上--service-account-private-key-file)<br>ca.pem 根证书(apiserver配置的时候,跟上--service-account-key-file)<br>kubernetes-key.pem 集群私钥<br>kubernetes.pem 集群证书<br>kube-proxy.pem proxy证书-node节点进行认证<br>kube-proxy-key.pem proxy私钥-node节点进行认证<br>admin.pem 管理员证书-主要用于kubectl认证<br>admin-key.pem 管理员私钥-主要用于kubectl认证</p>
<p><span style="background-color: rgba(255, 255, 153, 1)"> </span><br><strong><span style="color: rgba(255, 0, 0, 1)">TLS</span><span style="color: rgba(255, 0, 0, 1)">作用:</span></strong>就是对通讯加密,防止中间人窃听;同时如果证书不信任的话根本就无法与 apiserver 建立连接,更不用提有没有权限向 apiserver 请求指定内容。<br><strong><span style="color: rgba(255, 0, 0, 1)">RBAC作用:</span></strong>RBAC 中规定了一个用户或者用户组(subject)具有请求哪些 api 的权限;在配合 TLS 加密的时候,实际上 apiserver 读取客户端证书的 CN 字段作为用户名,读取 O 字段作为用户组。</p>
<p>总之想要与apiserver通讯就必须采用由apiserver CA签发的证书,这样才能形成信任关系,建立TLS连接;另外可通过证书的CN、O字段来提供RBAC所需用户与用户组。</p>
<p><span style="background-color: rgba(255, 255, 153, 1)"> </span><br>kubernetes集群会默认开启RABC(角色访问控制机制),这里提前了解几个重要概念:<br>- <span style="color: rgba(255, 0, 255, 1)"><strong>DRBC</strong></span><br>K8S 1.6引进,是让用户能够访问K8S API资源的授权方式(不授权就没有资格访问K8S的资源)<br>- <span style="color: rgba(255, 0, 255, 1)"><strong>用户</strong></span><br>K8S有两种用户:User 和 Service Account。其中,User给用户使用,Service Account给进程使用,让进程有相关权限。如Dashboard就是一个进程,可以创建一个Service Account给它使用。<br>- <span style="color: rgba(255, 0, 255, 1)"><strong>角色</strong></span><br>Role是一系列权限的集合,例如一个Role可包含读取和列出Pod的权限(ClusterRole和Role类似,其权限范围是整个集群)<br>- <span style="color: rgba(255, 0, 255, 1)"><strong>角色绑定</strong></span><br>RoleBinding把角色映射到用户,从而让这些用户拥有该角色的权限(ClusterRoleBinding和RoleBinding类似,可让用户拥有ClusteRole的权限)<br>- <span style="color: rgba(255, 0, 255, 1)"><strong>Secret</strong></span><br>Secret是一个包含少量敏感信息如密码,令牌或密钥的对象。把这些信息保存在Secret对象中,可以在这些信息被使用时加以控制,并可以减低信息泄露的风险。</p>
<p><span style="font-size: 14pt; color: rgba(153, 51, 102, 1)"><strong>二、环境初始化准备</strong></span><br>Kubernetes集群部署过程均需要使用root账号操作,下面初始化操作在k8s的master和node节点上操作。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">这里先以k8s-master01节点为例,其他节点类似操作。
1)主机名修改
# hostnamectl set-hostname k8s-master01
如果DNS不支持解析主机名称,则需要修改/etc/hosts文件,添加主机名和IP的对应关系:
# cat >> /etc/hosts <<EOF
172.16.60.241 k8s-master01
172.16.60.242 k8s-master02
172.16.60.243 k8s-master03
172.16.60.241 k8s-etcd01
172.16.60.242 k8s-etcd02
172.16.60.243 k8s-etcd03
172.16.60.244 k8s-node01
172.16.60.245 k8s-node02
172.16.60.246 k8s-node03
EOF
2) 添加docker账户
# useradd -m docker
3) 无密码ssh信任关系
本篇部署文档有很有操作都是在k8s-master01节点上执行,然后远程分发文件到其他节点机器上并远程执行命令,所以需要添加该节点到其它节点的ssh信任关系。
# ssh-keygen -t rsa
# cp /root/.ssh/id_rsa.pub /root/.ssh/authorized_keys
# ssh-copy-id -i /root/.ssh/id_rsa.pub -p22 root@k8s-master01
# ssh-copy-id -i /root/.ssh/id_rsa.pub -p22 root@k8s-master02
# ssh-copy-id -i /root/.ssh/id_rsa.pub -p22 root@k8s-master03
# ssh-copy-id -i /root/.ssh/id_rsa.pub -p22 root@k8s-node01
# ssh-copy-id -i /root/.ssh/id_rsa.pub -p22 root@k8s-node02
# ssh-copy-id -i /root/.ssh/id_rsa.pub -p22 root@k8s-node03
以上信任关系设置后,最好手动验证下本节点登陆到其他节点的ssh无密码信任关系
4) 更新PATH变量,将可执行文件目录添加到PATH环境变量中
将可执行文件目录添加到PATH环境变量中
# echo 'PATH=/opt/k8s/bin:$PATH' >>/root/.bashrc
# source /root/.bashrc
5) 安装依赖包
# yum install -y epel-release
# yum install -y conntrack ntpdate ntp ipvsadm ipset jq iptables curl sysstat libseccomp wget lsof telnet
关闭无关的服务
# systemctl stop postfix && systemctl disable postfix
6)关闭防火墙
在每台机器上关闭防火墙,清理防火墙规则,设置默认转发策略:
# systemctl stop firewalld
# systemctl disable firewalld
# iptables -F && iptables -X && iptables -F -t nat && iptables -X -t nat
# iptables -P FORWARD ACCEPT
# firewall-cmd --state
not running
7) 关闭SELinux
关闭SELinux,否则后续K8S挂载目录时可能报错 Permission denied:
# setenforce 0
# sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
8) 关闭swap分区
如果开启了swap分区,kubelet会启动失败(可以通过将参数 --fail-swap-on 设置为false来忽略swap on),故需要在每个node节点机器上关闭swap分区。
这里索性将所有节点的swap分区都关闭,同时注释/etc/fstab中相应的条目,防止开机自动挂载swap分区:
# swapoff -a
# sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
9) 关闭dnsmasq
linux系统开启了dnsmasq后(如 GUI 环境),将系统DNS Server设置为 127.0.0.1,这会导致docker容器无法解析域名,需要关闭它 (centos7系统可能默认没有安装这个服务)
# systemctl stop dnsmasq
# systemctl disable dnsmasq
10)加载内核模块
# modprobe ip_vs_rr
# modprobe br_netfilter
11)优化内核参数
# cat > kubernetes.conf <<EOF
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward=1
net.ipv4.tcp_tw_recycle=0#由于tcp_tw_recycle与kubernetes的NAT冲突,必须关闭!否则会导致服务不通。
vm.swappiness=0 #禁止使用 swap 空间,只有当系统 OOM 时才允许使用它
vm.overcommit_memory=1 #不检查物理内存是否够用
vm.panic_on_oom=0 #开启 OOM
fs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=1048576
fs.file-max=52706963
fs.nr_open=52706963
net.ipv6.conf.all.disable_ipv6=1#关闭不使用的ipv6协议栈,防止触发docker BUG.
net.netfilter.nf_conntrack_max=2310720
EOF
# cp kubernetes.conf/etc/sysctl.d/kubernetes.conf
# sysctl -p /etc/sysctl.d/kubernetes.conf
这里需要注意:
必须关闭 tcp_tw_recycle,否则和 NAT 冲突,会导致服务不通;
关闭 IPV6,防止触发 docker BUG;
12)设置系统时区
# 调整系统 TimeZone
# timedatectl set-timezone Asia/Shanghai
# 将当前的 UTC 时间写入硬件时钟
# timedatectl set-local-rtc 0
# 重启依赖于系统时间的服务
# systemctl restart rsyslog
# systemctl restart crond
13)设置rsyslogd 和systemd journald (每台节点机都要操作)
systemd 的 journald 是 Centos 7 缺省的日志记录工具,它记录了所有系统、内核、Service Unit 的日志。相比 systemd,journald 记录的日志有如下优势:
-> 可以记录到内存或文件系统;(默认记录到内存,对应的位置为 /run/log/jounal);
-> 可以限制占用的磁盘空间、保证磁盘剩余空间;
-> 可以限制日志文件大小、保存的时间;
-> journald 默认将日志转发给 rsyslog,这会导致日志写了多份,/var/log/messages 中包含了太多无关日志,不方便后续查看,同时也影响系统性能。
# mkdir /var/log/journal #持久化保存日志的目录
# mkdir /etc/systemd/journald.conf.d
# cat > /etc/systemd/journald.conf.d/99-prophet.conf <<EOF
# 持久化保存到磁盘
Storage=persistent
# 压缩历史日志
Compress=yes
SyncIntervalSec=5m
RateLimitInterval=30s
RateLimitBurst=1000
# 最大占用空间 10G
SystemMaxUse=10G
# 单日志文件最大 200M
SystemMaxFileSize=200M
# 日志保存时间 2 周
MaxRetentionSec=2week
# 不将日志转发到 syslog
ForwardToSyslog=no
EOF
# systemctl restart systemd-journald
14) 创建k8s相关目录 (每台节点机都要操作)
# mkdir -p /opt/k8s/{bin,work} /etc/{kubernetes,etcd}/cert
15) 升级内核 (每台节点机都要操作)
CentOS 7.x系统自带的3.10.x内核存在一些Bugs,导致运行的Docker、Kubernetes不稳定,例如:
-> 高版本的 docker(1.13 以后) 启用了3.10 kernel实验支持的kernel memory account功能(无法关闭),当节点压力大如频繁启动和停止容器时会导致 cgroup memory leak;
-> 网络设备引用计数泄漏,会导致类似于报错:"kernel:unregister_netdevice: waiting for eth0 to become free. Usage count = 1";
解决方案如下:
-> 升级内核到 4.4.X 以上;
-> 或者,手动编译内核,disable CONFIG_MEMCG_KMEM 特性;
-> 或者安装修复了该问题的 Docker 18.09.1 及以上的版本。但由于 kubelet 也会设置 kmem(它 vendor 了 runc),所以需要重新编译 kubelet 并指定 GOFLAGS="-tags=nokmem";
这里升级内核方法:
# uname-r
3.10.0-862.el7.x86_64
# rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
安装完成后检查 /boot/grub2/grub.cfg 中对应内核 menuentry 中是否包含 initrd16 配置,如果没有,再安装一次!
# yum --enablerepo=elrepo-kernel install -y kernel-lt
设置开机从新内核启动
# grub2-set-default 0
重启机器
# init 6
安装内核源文件(在升级完内核并重启机器后执行,也可以不用执行这一步。可选):
# yum --enablerepo=elrepo-kernel install kernel-lt-devel-$(uname -r) kernel-lt-headers-$(uname -r)
# uname -r
4.4.180-2.el7.elrepo.x86_64
====================================================================================================================================
或者也可以采用下面升级内核的方法:
# git clone --branch v1.14.1 --single-branch --depth 1 https://github.com/kubernetes/kubernetes
# cd kubernetes
# KUBE_GIT_VERSION=v1.14.1 ./build/run.sh make kubelet GOFLAGS="-tags=nokmem"
# init 6
====================================================================================================================================
16) 关闭NUMA
# cp /etc/default/grub{,.bak}
# vim /etc/default/grub
.........
GRUB_CMDLINE_LINUX="...... numa=off" # 即添加"numa=0ff"内容
重新生成 grub2 配置文件:
# cp /boot/grub2/grub.cfg{,.bak}
# grub2-mkconfig -o /boot/grub2/grub.cfg
17) 变量脚本文件 (这一步很关键)
# vim /opt/k8s/bin/environment.sh
#!/usr/bin/bash
# 生成 EncryptionConfig 所需的加密 key
export ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
# 集群中所有节点机器IP数组(master,node,etcd节点)
export NODE_ALL_IPS=(172.16.60.241 172.16.60.242 172.16.60.243 172.16.60.244 172.16.60.245 172.16.60.246)
# 集群中所有节点IP对应的主机名数组
export NODE_ALL_NAMES=(k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02 k8s-node03)
# 集群中所有master节点集群IP数组
export NODE_MASTER_IPS=(172.16.60.241 172.16.60.242 172.16.60.243)
# 集群中master节点IP对应的主机名数组
export NODE_MASTER_NAMES=(k8s-master01 k8s-master02 k8s-master03)
# 集群中所有node节点集群IP数组
export NODE_NODE_IPS=(172.16.60.244 172.16.60.245 172.16.60.246)
# 集群中node节点IP对应的主机名数组
export NODE_NODE_NAMES=(k8s-node01 k8s-node02 k8s-node03)
# 集群中所有etcd节点集群IP数组
export NODE_ETCD_IPS=(172.16.60.241 172.16.60.242 172.16.60.243)
# 集群中etcd节点IP对应的主机名数组(这里是和master三节点机器共用)
export NODE_ETCD_NAMES=(k8s-etcd01 k8s-etcd02 k8s-etcd03)
# etcd 集群服务地址列表
export ETCD_ENDPOINTS="https://172.16.60.241:2379,https://172.16.60.242:2379,https://172.16.60.243:2379"
# etcd 集群间通信的 IP 和端口
export ETCD_NODES="k8s-etcd01=https://172.16.60.241:2380,k8s-etcd02=https://172.16.60.242:2380,k8s-etcd03=https://172.16.60.243:2380"
# kube-apiserver 的反向代理(地址端口.这里也就是nginx代理层的VIP地址
export KUBE_APISERVER="https://172.16.60.250:8443"
# 节点间互联网络接口名称. 这里我所有的centos7节点机的网卡设备是ens192,而不是eth0
export IFACE="ens192"
# etcd 数据目录
export ETCD_DATA_DIR="/data/k8s/etcd/data"
# etcd WAL 目录,建议是 SSD 磁盘分区,或者和 ETCD_DATA_DIR 不同的磁盘分区
export ETCD_WAL_DIR="/data/k8s/etcd/wal"
# k8s 各组件数据目录
export K8S_DIR="/data/k8s/k8s"
# docker 数据目录
export DOCKER_DIR="/data/k8s/docker"
## 以下参数一般不需要修改
# TLS Bootstrapping 使用的 Token,可以使用命令 head -c 16 /dev/urandom | od -An -t x | tr -d ' ' 生成
BOOTSTRAP_TOKEN="41f7e4ba8b7be874fcff18bf5cf41a7c"
# 最好使用 当前未用的网段 来定义服务网段和 Pod 网段
# 服务网段,部署前路由不可达,部署后集群内路由可达(kube-proxy 保证)
SERVICE_CIDR="10.254.0.0/16"
# Pod 网段,建议 /16 段地址,部署前路由不可达,部署后集群内路由可达(flanneld 保证)
CLUSTER_CIDR="172.30.0.0/16"
# 服务端口范围 (NodePort Range)
export NODE_PORT_RANGE="30000-32767"
# flanneld 网络配置前缀
export FLANNEL_ETCD_PREFIX="/kubernetes/network"
# kubernetes 服务 IP (一般是 SERVICE_CIDR 中第一个IP)
export CLUSTER_KUBERNETES_SVC_IP="10.254.0.1"
# 集群 DNS 服务 IP (从 SERVICE_CIDR 中预分配)
export CLUSTER_DNS_SVC_IP="10.254.0.2"
# 集群 DNS 域名(末尾不带点号)
export CLUSTER_DNS_DOMAIN="cluster.local"
# 将二进制目录 /opt/k8s/bin 加到 PATH 中
export PATH=/opt/k8s/bin:$PATH</pre>
</div>
<p><span style="font-size: 14pt; color: rgba(153, 51, 102, 1)"><strong>三、创建集群中需要的CA证书和秘钥</strong></span><br>为确保安全,kubernetes 系统各组件需要使用 x509 证书对通信进行加密和认证。CA (Certificate Authority) 是自签名的根证书,用来签名后续创建的其它证书。这里使用 CloudFlare 的 PKI 工具集 cfssl 创建所有证书。<span style="background-color: rgba(255, 204, 153, 1)">下面部署命令均在k8s-master01节点上执行,然后远程分发文件和执行命令。</span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">1)安装cfssl工具集
# mkdir -p /opt/k8s/work && cd /opt/k8s/work
# wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
# mv cfssl_linux-amd64 /opt/k8s/bin/cfssl
# wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
# mv cfssljson_linux-amd64 /opt/k8s/bin/cfssljson
# wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
# mv cfssl-certinfo_linux-amd64 /opt/k8s/bin/cfssl-certinfo
# chmod +x /opt/k8s/bin/*
# export PATH=/opt/k8s/bin:$PATH
2)创建根证书 (CA)
CA 证书是集群所有节点共享的,只需要创建一个 CA 证书,后续创建的所有证书都由它签名。
2.1)创建配置文件
CA 配置文件用于配置根证书的使用场景 (profile) 和具体参数 (usage,过期时间、服务端认证、客户端认证、加密等),后续在签名其它证书时需要指定特定场景。
# cd /opt/k8s/work
# cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "87600h"
}
}
}
}
EOF
配置说明:
signing:表示该证书可用于签名其它证书,生成的 ca.pem 证书中 CA=TRUE;
server auth:表示 client 可以用该该证书对 server 提供的证书进行验证;
client auth:表示 server 可以用该该证书对 client 提供的证书进行验证;
2.2)创建证书签名请求文件
# cd /opt/k8s/work
# cat > ca-csr.json <<EOF
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "4Paradigm"
}
]
}
EOF
配置说明:
CN:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name),浏览器使用该字段验证网站是否合法;
O:Organization,kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group);
kube-apiserver 将提取的 User、Group 作为 RBAC 授权的用户标识;
2.3)生成 CA 证书和私钥
# cd /opt/k8s/work
# cfssl gencert -initca ca-csr.json | cfssljson -bare ca
# ls ca*
ca-config.jsonca.csrca-csr.jsonca-key.pemca.pem
#
3)分发证书文件
将生成的 CA 证书、秘钥文件、配置文件拷贝到所有节点的 /etc/kubernetes/cert 目录下:
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_all_ip in ${NODE_ALL_IPS[@]}
do
echo ">>> ${node_all_ip}"
ssh root@${node_all_ip} "mkdir -p /etc/kubernetes/cert"
scp ca*.pem ca-config.json root@${node_all_ip}:/etc/kubernetes/cert
done
</pre>
</div>
<p><span style="color: rgba(153, 51, 102, 1); font-size: 14pt"><strong>四、部署kubectl命令行工具</strong></span><br>kubectl 是 kubernetes 集群的命令行管理工具. kubectl 默认从 ~/.kube/config 文件读取kube-apiserver地址和认证信息,如果没有配置,执行kubectl命令时就会报错!kubectl只需要部署一次,生成的kubeconfig文件是通用的,可以拷贝到需要执行kubectl命令的节点机器,重命名为 ~/.kube/config;这里我将kubectl节点只部署到三个master节点机器上,其他节点不部署kubectl命令。也就是说后续进行kubectl命令管理就只能在master节点上操作。<span style="background-color: rgba(255, 204, 153, 1)">下面部署命令均在k8s-master01节点上执行,然后远程分发文件和执行命令。</span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">如果没有部署kubectl工具,则执行时会报错说没有该命令:
# kubectl get pods
-bash: kubectl: command not found
1)下载和分发kubectl二进制文件
二进制包下载地址:https://pan.baidu.com/s/1HUWFqKVLyxIzoX2LDQSEBg
提取密码:7kaf
# cd /opt/k8s/work
# wget https://dl.k8s.io/v1.14.2/kubernetes-client-linux-amd64.tar.gz
# tar -xzvf kubernetes-client-linux-amd64.tar.gz
分发到所有使用kubectl的节点,这里只分发到三个master节点
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_master_ip in ${NODE_MASTER_IPS[@]}
do
echo ">>> ${node_master_ip}"
scp kubernetes/client/bin/kubectl root@${node_master_ip}:/opt/k8s/bin/
ssh root@${node_master_ip} "chmod +x /opt/k8s/bin/*"
done
2) 创建admin证书和私钥
kubectl与apiserver https安全端口通信,apiserver 对提供的证书进行认证和授权。
kubectl作为集群的管理工具,需要被授予最高权限,这里创建具有最高权限的 admin 证书。
创建证书签名请求:
# cd /opt/k8s/work
# cat > admin-csr.json <<EOF
{
"CN": "admin",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "system:masters",
"OU": "4Paradigm"
}
]
}
EOF
配置说明:
O为system:masters,kube-apiserver 收到该证书后将请求的 Group 设置为 system:masters;
预定义的 ClusterRoleBinding cluster-admin 将Group system:masters 与 Role cluster-admin 绑定,该 Role 授予所有 API的权限;
该证书只会被kubectl当做client证书使用,所以hosts字段为空;
生成证书和私钥:
# cd /opt/k8s/work
# cfssl gencert -ca=/opt/k8s/work/ca.pem \
-ca-key=/opt/k8s/work/ca-key.pem \
-config=/opt/k8s/work/ca-config.json \
-profile=kubernetes admin-csr.json | cfssljson -bare admin
# ls admin*
admin.csradmin-csr.jsonadmin-key.pemadmin.pem
3)创建 kubeconfig 文件
kubeconfig 为 kubectl 的配置文件,包含访问 apiserver 的所有信息,如 apiserver 地址、CA 证书和自身使用的证书;
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
设置集群参数
# kubectl config set-cluster kubernetes \
--certificate-authority=/opt/k8s/work/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=kubectl.kubeconfig
设置客户端认证参数
# kubectl config set-credentials admin \
--client-certificate=/opt/k8s/work/admin.pem \
--client-key=/opt/k8s/work/admin-key.pem \
--embed-certs=true \
--kubeconfig=kubectl.kubeconfig
设置上下文参数
# kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=admin \
--kubeconfig=kubectl.kubeconfig
设置默认上下文
# kubectl config use-context kubernetes --kubeconfig=kubectl.kubeconfig
配置说明:
--certificate-authority:验证 kube-apiserver 证书的根证书;
--client-certificate、--client-key:刚生成的 admin 证书和私钥,连接 kube-apiserver 时使用;
--embed-certs=true:将 ca.pem 和 admin.pem 证书内容嵌入到生成的 kubectl.kubeconfig 文件中(不加时,写入的是证书文件路径,
后续拷贝 kubeconfig 到其它机器时,还需要单独拷贝证书文件,这就很不方便了)
4)分发 kubeconfig 文件, 保存的文件名为 ~/.kube/config;
分发到所有使用 kubectl 命令的节点,即分发到三个master节点上
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_master_ip in ${NODE_MASTER_IPS[@]}
do
echo ">>> ${node_master_ip}"
ssh root@${node_master_ip} "mkdir -p ~/.kube"
scp kubectl.kubeconfig root@${node_master_ip}:~/.kube/config
done
</pre>
</div>
<p><span style="color: rgba(153, 51, 102, 1); font-size: 14pt"><strong>五、部署etcd集群</strong></span><br>etcd是基于Raft的分布式key-value存储系统,由CoreOS开发,常用于服务发现、共享配置以及并发控制(如leader选举、分布式锁等)。kubernetes使用etcd存储所有运行数据。<span style="color: rgba(0, 128, 128, 1)"><strong>需要注意的是:</strong>由于etcd是负责存储,所以<strong>不建议搭建单点集群</strong>,如zookeeper一样,由于存在选举策略,所以一般<strong>推荐奇数个集群</strong>,如3,5,7。只要集群半数以上的结点存活,那么集群就可以正常运行,否则集群可能无法正常使用。</span><span style="background-color: rgba(255, 204, 153, 1)">下面部署命令均在k8s-master01节点上执行,然后远程分发文件和执行命令。</span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">1)下载和分发etcd二进制文件
# cd /opt/k8s/work
# wget https://github.com/coreos/etcd/releases/download/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz
# tar -xvf etcd-v3.3.13-linux-amd64.tar.gz
分发二进制文件到etcd集群所有节点:
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_etcd_ip in ${NODE_ETCD_IPS[@]}
do
echo ">>> ${node_etcd_ip}"
scp etcd-v3.3.13-linux-amd64/etcd* root@${node_etcd_ip}:/opt/k8s/bin
ssh root@${node_etcd_ip} "chmod +x /opt/k8s/bin/*"
done
2) 创建etcd证书和私钥
创建证书签名请求:
# cd /opt/k8s/work
# cat > etcd-csr.json <<EOF
{
"CN": "etcd",
"hosts": [
"127.0.0.1",
"172.16.60.241",
"172.16.60.242",
"172.16.60.243"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "4Paradigm"
}
]
}
EOF
配置说明:
hosts 字段指定授权使用该证书的 etcd 节点 IP 或域名列表,需要将 etcd 集群的三个节点 IP 都列在其中;
生成证书和私钥
# cd /opt/k8s/work
# cfssl gencert -ca=/opt/k8s/work/ca.pem \
-ca-key=/opt/k8s/work/ca-key.pem \
-config=/opt/k8s/work/ca-config.json \
-profile=kubernetes etcd-csr.json | cfssljson -bare etcd
# ls etcd*pem
etcd-key.pemetcd.pem
分发生成的证书和私钥到各etcd节点
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_etcd_ip in ${NODE_ETCD_IPS[@]}
do
echo ">>> ${node_etcd_ip}"
ssh root@${node_etcd_ip} "mkdir -p /etc/etcd/cert"
scp etcd*.pem root@${node_etcd_ip}:/etc/etcd/cert/
done
3) 创建etcd的systemd unit模板文件
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# cat > etcd.service.template <<EOF
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/coreos
Type=notify
WorkingDirectory=${ETCD_DATA_DIR}
ExecStart=/opt/k8s/bin/etcd \\
--data-dir=${ETCD_DATA_DIR} \\
--wal-dir=${ETCD_WAL_DIR} \\
--name=##NODE_ETCD_NAME## \\
--cert-file=/etc/etcd/cert/etcd.pem \\
--key-file=/etc/etcd/cert/etcd-key.pem \\
--trusted-ca-file=/etc/kubernetes/cert/ca.pem \\
--peer-cert-file=/etc/etcd/cert/etcd.pem \\
--peer-key-file=/etc/etcd/cert/etcd-key.pem \\
--peer-trusted-ca-file=/etc/kubernetes/cert/ca.pem \\
--peer-client-cert-auth \\
--client-cert-auth \\
--listen-peer-urls=https://##NODE_ETCD_IP##:2380 \\
--initial-advertise-peer-urls=https://##NODE_ETCD_IP##:2380 \\
--listen-client-urls=https://##NODE_ETCD_IP##:2379,http://127.0.0.1:2379 \\
--advertise-client-urls=https://##NODE_ETCD_IP##:2379 \\
--initial-cluster-token=etcd-cluster-0 \\
--initial-cluster=${ETCD_NODES} \\
--initial-cluster-state=new \\
--auto-compaction-mode=periodic \\
--auto-compaction-retention=1 \\
--max-request-bytes=33554432 \\
--quota-backend-bytes=6442450944 \\
--heartbeat-interval=250 \\
--election-timeout=2000
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
WantedBy=multi-user.target
EOF
配置说明:
WorkingDirectory、--data-dir:指定工作目录和数据目录为 ${ETCD_DATA_DIR},需在启动服务前创建这个目录;
--wal-dir:指定 wal 目录,为了提高性能,一般使用 SSD 或者和 --data-dir 不同的磁盘;
--name:指定节点名称,当 --initial-cluster-state 值为 new 时,--name 的参数值必须位于 --initial-cluster 列表中;
--cert-file、--key-file:etcd server 与 client 通信时使用的证书和私钥;
--trusted-ca-file:签名 client 证书的 CA 证书,用于验证 client 证书;
--peer-cert-file、--peer-key-file:etcd 与 peer 通信使用的证书和私钥;
--peer-trusted-ca-file:签名 peer 证书的 CA 证书,用于验证 peer 证书;
4)为各etcd节点创建和分发 etcd systemd unit 文件
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for (( i=0; i < 3; i++ ))
do
sed -e "s/##NODE_ETCD_NAME##/${NODE_ETCD_NAMES}/" -e "s/##NODE_ETCD_IP##/${NODE_ETCD_IPS}/" etcd.service.template > etcd-${NODE_ETCD_IPS}.service
done
# ls *.service
etcd-172.16.60.241.serviceetcd-172.16.60.242.serviceetcd-172.16.60.243.service
最好手动查看其中一个etcd节点的启动文件里的--name名称和ip是否都已修改过来了
# cat etcd-172.16.60.241.service
.......
--name=k8s-etcd01 \
.......
--listen-peer-urls=https://172.16.60.241:2380 \
--initial-advertise-peer-urls=https://172.16.60.241:2380 \
--listen-client-urls=https://172.16.60.241:2379,http://127.0.0.1:2379 \
--advertise-client-urls=https://172.16.60.241:2379 \
--initial-cluster-token=etcd-cluster-0 \
--initial-cluster=k8s-etcd01=https://172.16.60.241:2380,k8s-etcd02=https://172.16.60.242:2380,k8s-etcd03=https://172.16.60.243:2380 \
.......
配置说明:
NODE_ETCD_NAMES 和 NODE_ETCD_IPS 为相同长度的bash数组,分别为etcd集群节点名称和对应的IP;
分发生成的 systemd unit 文件:
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_etcd_ip in ${NODE_ETCD_IPS[@]}
do
echo ">>> ${node_etcd_ip}"
scp etcd-${node_etcd_ip}.service root@${node_etcd_ip}:/etc/systemd/system/etcd.service
done
配置说明: 文件重命名为 etcd.service;
5)启动 etcd 服务
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_etcd_ip in ${NODE_ETCD_IPS[@]}
do
echo ">>> ${node_etcd_ip}"
ssh root@${node_etcd_ip} "mkdir -p ${ETCD_DATA_DIR} ${ETCD_WAL_DIR}"
ssh root@${node_etcd_ip} "systemctl daemon-reload && systemctl enable etcd && systemctl restart etcd " &
done
配置说明:
必须先创建 etcd 数据目录和工作目录;
etcd 进程首次启动时会等待其它节点的 etcd 加入集群,命令 systemctl start etcd 会卡住一段时间,为正常现象;
6)检查etcd服务启动结果
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_etcd_ip in ${NODE_ETCD_IPS[@]}
do
echo ">>> ${node_etcd_ip}"
ssh root@${node_etcd_ip} "systemctl status etcd|grep Active"
done
预期输出结果为:
>>> 172.16.60.241
Active: active (running) since Tue 2019-06-04 19:55:32 CST; 7min ago
>>> 172.16.60.242
Active: active (running) since Tue 2019-06-04 19:55:32 CST; 7min ago
>>> 172.16.60.243
Active: active (running) since Tue 2019-06-04 19:55:32 CST; 7min ago
确保状态均为为active (running),否则查看日志,确认原因 (可以执行"journalctl -u etcd"命令查看启动失败原因)
6)验证服务状态
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_etcd_ip in ${NODE_ETCD_IPS[@]}
do
echo ">>> ${node_etcd_ip}"
ssh root@${node_etcd_ip} "
ETCDCTL_API=3 /opt/k8s/bin/etcdctl \
--endpoints=https://${node_etcd_ip}:2379 \
--cacert=/etc/kubernetes/cert/ca.pem \
--cert=/etc/etcd/cert/etcd.pem \
--key=/etc/etcd/cert/etcd-key.pem endpoint health "
done
预期输出结果为:
https://172.16.60.241:2379 is healthy: successfully committed proposal: took = 2.44394ms
>>> 172.16.60.242
https://172.16.60.242:2379 is healthy: successfully committed proposal: took = 7.044349ms
>>> 172.16.60.243
https://172.16.60.243:2379 is healthy: successfully committed proposal: took = 1.865713ms
输出均为 healthy 时表示集群服务正常。
7)查看当前etcd集群中的leader
在三台etcd节点中的任意一个节点机器上执行下面命令:
# source /opt/k8s/bin/environment.sh
# ETCDCTL_API=3 /opt/k8s/bin/etcdctl \
-w table --cacert=/etc/kubernetes/cert/ca.pem \
--cert=/etc/etcd/cert/etcd.pem \
--key=/etc/etcd/cert/etcd-key.pem \
--endpoints=${ETCD_ENDPOINTS} endpoint status
预期输出结果为:
+----------------------------+------------------+---------+---------+-----------+-----------+------------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+----------------------------+------------------+---------+---------+-----------+-----------+------------+
| https://172.16.60.241:2379 | 577381f5de0f4495 |3.3.13 | 16 kB | false | 2 | 8 |
| https://172.16.60.242:2379 | bf4ce221cdf39fb0 |3.3.13 | 16 kB | false | 2 | 8 |
| https://172.16.60.243:2379 |3bc2e49bc639590 |3.3.13 | 16 kB | true | 2 | 8 |
+----------------------------+------------------+---------+---------+-----------+-----------+------------+
由上面结果可见,当前的leader节点为172.16.60.243
</pre>
</div>
<p><span style="font-size: 14pt"><strong><span style="color: rgba(153, 51, 102, 1)">六、Flannel容器网络方案部署</span></strong></span><br>kubernetes要求集群内各节点(这里指master和node节点)能通过Pod网段互联互通。flannel使用vxlan技术为各节点创建一个可以互通的Pod网络,使用的端口为UDP 8472(需要开放该端口,如公有云AWS等)。flanneld第一次启动时,从etcd获取配置的Pod网段信息,为本节点分配一个未使用的地址段,然后创建flannedl.1网络接口(也可能是其它名称,如flannel1等)。flannel将分配给自己的Pod网段信息写入/run/flannel/docker文件,docker后续使用这个文件中的环境变量设置docker0网桥,从而从这个地址段为本节点的所有Pod容器分配IP。<span style="background-color: rgba(255, 204, 153, 1)">下面部署命令均在k8s-master01节点上执行,然后远程分发文件和执行命令。</span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">1) 下载和分发 flanneld 二进制文件
从flannel的release页面(https://github.com/coreos/flannel/releases)下载最新版本的安装包:
# cd /opt/k8s/work
# mkdir flannel
# wget https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz
# tar -zvxf flannel-v0.11.0-linux-amd64.tar.gz -C flannel
分发二进制文件到集群所有节点:
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_all_ip in ${NODE_ALL_IPS[@]}
do
echo ">>> ${node_all_ip}"
scp flannel/{flanneld,mk-docker-opts.sh} root@${node_all_ip}:/opt/k8s/bin/
ssh root@${node_all_ip} "chmod +x /opt/k8s/bin/*"
done
2) 创建 flannel 证书和私钥
flanneld 从 etcd 集群存取网段分配信息,而 etcd 集群启用了双向 x509 证书认证,所以需要为 flanneld 生成证书和私钥。
创建证书签名请求:
# cd /opt/k8s/work
# cat > flanneld-csr.json <<EOF
{
"CN": "flanneld",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "4Paradigm"
}
]
}
EOF
该证书只会被 kubectl 当做 client 证书使用,所以 hosts 字段为空;
生成证书和私钥:
# cfssl gencert -ca=/opt/k8s/work/ca.pem \
-ca-key=/opt/k8s/work/ca-key.pem \
-config=/opt/k8s/work/ca-config.json \
-profile=kubernetes flanneld-csr.json | cfssljson -bare flanneld
将生成的证书和私钥分发到所有节点(master 和 node):
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_all_ip in ${NODE_ALL_IPS[@]}
do
echo ">>> ${node_all_ip}"
ssh root@${node_all_ip} "mkdir -p /etc/flanneld/cert"
scp flanneld*.pem root@${node_all_ip}:/etc/flanneld/cert
done
3)向 etcd 写入集群 Pod 网段信息 (注意:本步骤只需执行一次)
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# etcdctl \
--endpoints=${ETCD_ENDPOINTS} \
--ca-file=/opt/k8s/work/ca.pem \
--cert-file=/opt/k8s/work/flanneld.pem \
--key-file=/opt/k8s/work/flanneld-key.pem \
mk ${FLANNEL_ETCD_PREFIX}/config '{"Network":"'${CLUSTER_CIDR}'", "SubnetLen": 21, "Backend": {"Type": "vxlan"}}'
解决说明:
flanneld 当前版本 (v0.11.0) 不支持 etcd v3,故使用 etcd v2 API 写入配置 key 和网段数据;
写入的 Pod 网段 ${CLUSTER_CIDR} 地址段(如 /16)必须小于 SubnetLen,必须与 kube-controller-manager 的 --cluster-cidr 参数值一致;
4)创建 flanneld 的 systemd unit 文件
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# cat > flanneld.service << EOF
Description=Flanneld overlay address etcd agent
After=network.target
After=network-online.target
Wants=network-online.target
After=etcd.service
Before=docker.service
Type=notify
ExecStart=/opt/k8s/bin/flanneld \\
-etcd-cafile=/etc/kubernetes/cert/ca.pem \\
-etcd-certfile=/etc/flanneld/cert/flanneld.pem \\
-etcd-keyfile=/etc/flanneld/cert/flanneld-key.pem \\
-etcd-endpoints=${ETCD_ENDPOINTS} \\
-etcd-prefix=${FLANNEL_ETCD_PREFIX} \\
-iface=${IFACE} \\
-ip-masq
ExecStartPost=/opt/k8s/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker
Restart=always
RestartSec=5
StartLimitInterval=0
WantedBy=multi-user.target
RequiredBy=docker.service
EOF
解决说明:
mk-docker-opts.sh 脚本将分配给 flanneld 的 Pod 子网段信息写入 /run/flannel/docker 文件,后续 docker 启动时使用这个文件中的环境变量配置 docker0 网桥;
flanneld 使用系统缺省路由所在的接口与其它节点通信,对于有多个网络接口(如内网和公网)的节点,可以用 -iface 参数指定通信接口;
flanneld 运行时需要 root 权限;
-ip-masq: flanneld 为访问 Pod 网络外的流量设置 SNAT 规则,同时将传递给 Docker 的变量 --ip-masq(/run/flannel/docker 文件中)设置为 false,这样 Docker 将不再创建 SNAT 规则; Docker 的 --ip-masq 为 true 时,创建的 SNAT 规则比较“暴力”:将所有本节点 Pod 发起的、访问非 docker0 接口的请求做 SNAT,这样访问其他节点 Pod 的请求来源 IP 会被设置为 flannel.1 接口的 IP,导致目的 Pod 看不到真实的来源 Pod IP。 flanneld 创建的 SNAT 规则比较温和,只对访问非 Pod 网段的请求做 SNAT。
5)分发 flanneld systemd unit 文件到所有节点
# cd /opt/k8s/work
# source /opt/k8s/bin/environment.sh
# for node_all_ip in ${NODE_ALL_IPS[@]}
do
echo ">>> ${node_all_ip}"
scp flanneld.service root@${node_all_ip}:/etc/systemd/system/
done
6)启动 flanneld 服务
# source /opt/k8s/bin/environment.sh
# for node_all_ip in ${NODE_ALL_IPS[@]}
do
echo ">>> ${node_all_ip}"
ssh root@${node_all_ip} "systemctl daemon-reload && systemctl enable flanneld && systemctl restart flanneld"
done
6)检查启动结果
# source /opt/k8s/bin/environment.sh
# for node_all_ip in ${NODE_ALL_IPS[@]}
do
echo ">>> ${node_all_ip}"
ssh root@${node_all_ip} "systemctl status flanneld|grep Active"
done
确保状态为 active (running),否则查看日志,确认原因"journalctl -u flanneld"
7) 检查分配给各 flanneld 的 Pod 网段信息
查看集群 Pod 网段(/16):
# source /opt/k8s/bin/environment.sh
# etcdctl \
--endpoints=${ETCD_ENDPOINTS} \
--ca-file=/etc/kubernetes/cert/ca.pem \
--cert-file=/etc/flanneld/cert/flanneld.pem \
--key-file=/etc/flanneld/cert/flanneld-key.pem \
get ${FLANNEL_ETCD_PREFIX}/config
预期输出: {"Network":"172.30.0.0/16", "SubnetLen": 21, "Backend": {"Type": "vxlan"}}
查看已分配的 Pod 子网段列表(/24):
# source /opt/k8s/bin/environment.sh
# etcdctl \
--endpoints=${ETCD_ENDPOINTS} \
--ca-file=/etc/kubernetes/cert/ca.pem \
--cert-file=/etc/flanneld/cert/flanneld.pem \
--key-file=/etc/flanneld/cert/flanneld-key.pem \
ls ${FLANNEL_ETCD_PREFIX}/subnets
预期输出:
/kubernetes/network/subnets/172.30.40.0-21
/kubernetes/network/subnets/172.30.88.0-21
/kubernetes/network/subnets/172.30.56.0-21
/kubernetes/network/subnets/172.30.72.0-21
/kubernetes/network/subnets/172.30.232.0-21
/kubernetes/network/subnets/172.30.152.0-21
查看某一 Pod 网段对应的节点 IP 和 flannel 接口地址:
# source /opt/k8s/bin/environment.sh
# etcdctl \
--endpoints=${ETCD_ENDPOINTS} \
--ca-file=/etc/kubernetes/cert/ca.pem \
--cert-file=/etc/flanneld/cert/flanneld.pem \
--key-file=/etc/flanneld/cert/flanneld-key.pem \
get ${FLANNEL_ETCD_PREFIX}/subnets/172.30.40.0-21
预期输出:{"PublicIP":"172.16.60.243","BackendType":"vxlan","BackendData":{"VtepMAC":"f2:de:47:06:4b:d3"}}
解决说明:
172.30.40.0/21 被分配给节点k8s-master03(172.16.60.243);
VtepMAC 为k8s-master03节点的 flannel.1 网卡 MAC 地址;
8)检查节点 flannel 网络信息 (比如k8s-master01节点)
# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:ac:7c:81 brd ff:ff:ff:ff:ff:ff
inet 172.16.60.241/24 brd 172.16.60.255 scope global ens192
valid_lft forever preferred_lft forever
3: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether 7a:2a:36:99:75:5f brd ff:ff:ff:ff:ff:ff
inet 172.30.232.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
注意: flannel.1 网卡的地址为分配的 Pod 子网段的第一个 IP(.0),且是 /32 的地址;
# ip route show |grep flannel.1
172.30.40.0/21 via 172.30.40.0 dev flannel.1 onlink
172.30.56.0/21 via 172.30.56.0 dev flannel.1 onlink
172.30.72.0/21 via 172.30.72.0 dev flannel.1 onlink
172.30.88.0/21 via 172.30.88.0 dev flannel.1 onlink
172.30.152.0/21 via 172.30.152.0 dev flannel.1 onlink
到其它节点 Pod 网段请求都被转发到 flannel.1 网卡;
flanneld 根据 etcd 中子网段的信息,如 ${FLANNEL_ETCD_PREFIX}/subnets/172.30.232.0-21 ,来决定进请求发送给哪个节点的互联 IP;
9)验证各节点能通过 Pod 网段互通
在各节点上部署 flannel 后,检查是否创建了 flannel 接口(名称可能为 flannel0、flannel.0、flannel.1 等):
# source /opt/k8s/bin/environment.sh
# for node_all_ip in ${NODE_ALL_IPS[@]}
do
echo ">>> ${node_all_ip}"
ssh ${node_all_ip} "/usr/sbin/ip addr show flannel.1|grep -w inet"
done
预期输出:
>>> 172.16.60.241
inet 172.30.232.0/32 scope global flannel.1
>>> 172.16.60.242
inet 172.30.152.0/32 scope global flannel.1
>>> 172.16.60.243
inet 172.30.40.0/32 scope global flannel.1
>>> 172.16.60.244
inet 172.30.88.0/32 scope global flannel.1
>>> 172.16.60.245
inet 172.30.56.0/32 scope global flannel.1
>>> 172.16.60.246
inet 172.30.72.0/32 scope global flannel.1
在各节点上 ping 所有 flannel 接口 IP,确保能通:
# source /opt/k8s/bin/environment.sh
# for node_all_ip in ${NODE_ALL_IPS[@]}
do
echo ">>> ${node_all_ip}"
ssh ${node_all_ip} "ping -c 1 172.30.232.0"
ssh ${node_all_ip} "ping -c 1 172.30.152.0"
ssh ${node_all_ip} "ping -c 1 172.30.40.0"
ssh ${node_all_ip} "ping -c 1 172.30.88.0"
ssh ${node_all_ip} "ping -c 1 172.30.56.0"
ssh ${node_all_ip} "ping -c 1 172.30.72.0"
done
</pre>
</div>
<p><span style="font-size: 14pt"><strong><span style="color: rgba(153, 51, 102, 1)">七、基于nginx 四层代理环境</span></strong></span><br>这里采用nginx 4 层透明代理功能实现 K8S 节点( master 节点和 worker 节点)高可用访问 kube-apiserver。控制节点的 kube-controller-manager、kube-scheduler 是多实例(3个)部署,所以只要有一个实例正常,就可以保证高可用;搭建nginx+keepalived环境,对外提供一个统一的vip地址,后端对接多个 apiserver 实例,nginx 对它们做健康检查和负载均衡;kubelet、kube-proxy、controller-manager、scheduler 通过vip地址访问 kube-apiserver,从而实现 kube-apiserver 的高可用;</p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">一、安装和配置nginx,下面操作在172.16.60.247、172.16.60.247两个节点机器上操作
1)下载和编译 nginx
# yum -y install gcc pcre-devel zlib-devel openssl-devel wget lsof
# cd /opt/k8s/work
# wget http://nginx.org/download/nginx-1.15.3.tar.gz
# tar -xzvf nginx-1.15.3.tar.gz
# cd nginx-1.15.3
# mkdir nginx-prefix
# ./configure --with-stream --without-http --prefix=$(pwd)/nginx-prefix --without-http_uwsgi_module --without-http_scgi_module --without-http_fastcgi_module
解决说明:
--with-stream:开启 4 层透明转发(TCP Proxy)功能;
--without-xxx:关闭所有其他功能,这样生成的动态链接二进制程序依赖最小;
预期输出:
Configuration summary
+ PCRE library is not used
+ OpenSSL library is not used
+ zlib library is not used
nginx path prefix: "/root/tmp/nginx-1.15.3/nginx-prefix"
nginx binary file: "/root/tmp/nginx-1.15.3/nginx-prefix/sbin/nginx"
nginx modules path: "/root/tmp/nginx-1.15.3/nginx-prefix/modules"
nginx configuration prefix: "/root/tmp/nginx-1.15.3/nginx-prefix/conf"
nginx configuration file: "/root/tmp/nginx-1.15.3/nginx-prefix/conf/nginx.conf"
nginx pid file: "/root/tmp/nginx-1.15.3/nginx-prefix/logs/nginx.pid"
nginx error log file: "/root/tmp/nginx-1.15.3/nginx-prefix/logs/error.log"
nginx http access log file: "/root/tmp/nginx-1.15.3/nginx-prefix/logs/access.log"
nginx http client request body temporary files: "client_body_temp"
nginx http proxy temporary files: "proxy_temp"
继续编译和安装:
# make && make install
2)验证编译的 nginx
# ./nginx-prefix/sbin/nginx -v
nginx version: nginx/1.15.3
查看 nginx 动态链接的库:
# ldd ./nginx-prefix/sbin/nginx
linux-vdso.so.1 =>(0x00007ffc7e0ef000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f00b5c2d000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f00b5a11000)
libc.so.6 => /lib64/libc.so.6 (0x00007f00b5644000)
/lib64/ld-linux-x86-64.so.2 (0x00007f00b5e31000)
由于只开启了 4 层透明转发功能,所以除了依赖 libc 等操作系统核心 lib 库外,没有对其它 lib 的依赖(如 libz、libssl 等),这样可以方便部署到各版本操作系统中;
3)安装和部署 nginx
# cp /opt/k8s/work/nginx-1.15.3/nginx-prefix/sbin/nginx /opt/k8s/kube-nginx/sbin/kube-nginx
# chmod a+x /opt/k8s/kube-nginx/sbin/*
# mkdir -p /opt/k8s/kube-nginx/{conf,logs,sbin}
配置 nginx,开启 4 层透明转发功能:
# vim /opt/k8s/kube-nginx/conf/kube-nginx.conf
worker_processes 2;
events {
worker_connections65525;
}
stream {
upstream backend {
hash $remote_addr consistent;
server 172.16.60.241:6443 max_fails=3 fail_timeout=30s;
server 172.16.60.242:6443 max_fails=3 fail_timeout=30s;
server 172.16.60.243:6443 max_fails=3 fail_timeout=30s;
}
server {
listen 8443;
proxy_connect_timeout 1s;
proxy_pass backend;
}
}
# ulimit -n 65525
# vim /etc/security/limits.conf # 文件底部添加下面四行内容
* soft nofile 65525
* hard nofile 65525
* soft nproc 65525
* hard nproc 65525
4) 配置 systemd unit 文件,启动服务
# vim /etc/systemd/system/kube-nginx.service
Description=kube-apiserver nginx proxy
After=network.target
After=network-online.target
Wants=network-online.target
Type=forking
ExecStartPre=/opt/k8s/kube-nginx/sbin/kube-nginx -c /opt/k8s/kube-nginx/conf/kube-nginx.conf -p /opt/k8s/kube-nginx -t
ExecStart=/opt/k8s/kube-nginx/sbin/kube-nginx -c /opt/k8s/kube-nginx/conf/kube-nginx.conf -p /opt/k8s/kube-nginx
ExecReload=/opt/k8s/kube-nginx/sbin/kube-nginx -c /opt/k8s/kube-nginx/conf/kube-nginx.conf -p /opt/k8s/kube-nginx -s reload
PrivateTmp=true
Restart=always
RestartSec=5
StartLimitInterval=0
LimitNOFILE=65536
WantedBy=multi-user.target
# systemctl daemon-reload && systemctl enable kube-nginx && systemctl restart kube-nginx
# lsof -i:8443
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
kube-ngin 31980 root 5uIPv4 145789 0t0TCP localhost:pcsync-https (LISTEN)
kube-ngin 31981 nobody 5uIPv4 145789 0t0TCP localhost:pcsync-https (LISTEN)
kube-ngin 31982 nobody 5uIPv4 145789 0t0TCP localhost:pcsync-https (LISTEN)
测试下8443代理端口连通性
# telnet 172.16.60.250 8443
Trying 172.16.60.250...
Connected to 172.16.60.250.
Escape character is '^]'.
Connection closed by foreign host.
这是因为三个kube-apiserver服务还没有部署,即后端三个apiserver实例的6443端口还没有起来。
二、安装和配置keepalived
1)编译安装keepalived (两个节点上同样操作)
# cd /opt/k8s/work/
# wget https://www.keepalived.org/software/keepalived-2.0.16.tar.gz
# tar -zvxf keepalived-2.0.16.tar.gz
# cd keepalived-2.0.16
# ./configure
# make && make install
# cp keepalived/etc/init.d/keepalived /etc/rc.d/init.d/
# cp /usr/local/etc/sysconfig/keepalived /etc/sysconfig/
# mkdir /etc/keepalived
# cp /usr/local/etc/keepalived/keepalived.conf /etc/keepalived/
# cp /usr/local/sbin/keepalived /usr/sbin/
# echo "/etc/init.d/keepalived start" >> /etc/rc.local
2) 配置keepalived
172.16.60.207节点上的keepalived配置内容
# cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak
# >/etc/keepalived/keepalived.conf
# vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
notification_email {
ops@wangshibo.cn
tech@wangshibo.cn
}
notification_email_from ops@wangshibo.cn
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id master-node
}
vrrp_script chk_http_port {
script "/opt/chk_nginx.sh"
interval 2
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state MASTER
interface ens192
mcast_src_ip 172.16.60.247
virtual_router_id 51
priority 101
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
172.16.60.250
}
track_script {
chk_http_port
}
}
另一个节点172.16.60.248上的keepalived配置内容为:
# cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak
# >/etc/keepalived/keepalived.conf
# vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
notification_email {
ops@wangshibo.cn
tech@wangshibo.cn
}
notification_email_from ops@wangshibo.cn
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id slave-node
}
vrrp_script chk_http_port {
script "/opt/chk_nginx.sh"
interval 2
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state MASTER
interface ens192
mcast_src_ip 172.16.60.248
virtual_router_id 51
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
172.16.60.250
}
track_script {
chk_http_port
}
}
2) 配置两个节点的nginx监控脚本(该脚本会在keepalived.conf配置中被引用)
# vim /opt/chk_nginx.sh
#!/bin/bash
counter=$(ps -ef|grep -w kube-nginx|grep -v grep|wc -l)
if [ "${counter}" = "0" ]; then
systemctl start kube-nginx
sleep 2
counter=$(ps -ef|grep kube-nginx|grep -v grep|wc -l)
if [ "${counter}" = "0" ]; then
/etc/init.d/keepalived stop
fi
fi
# chmod 755 /opt/chk_nginx.sh
3) 启动两个节点的keepalived服务
# /etc/init.d/keepalived start
Starting keepalived (via systemctl):
# ps -ef|grep keepalived
root 5358 10 00:32 ? 00:00:00 /usr/local/sbin/keepalived -D
root 535953580 00:32 ? 00:00:00 /usr/local/sbin/keepalived -D
root 5391 296060 00:32 pts/0 00:00:00 grep --color=auto keepalived
查看vip情况. 发现vip默认起初会在master节点上
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:ac:3a:a6 brd ff:ff:ff:ff:ff:ff
inet 172.16.60.247/24 brd 172.16.60.255 scope global ens192
valid_lft forever preferred_lft forever
inet 172.16.60.250/32 scope global ens192
valid_lft forever preferred_lft forever
inet6 fe80::250:56ff:feac:3aa6/64 scope link
valid_lft forever preferred_lft forever
4) 测试vip故障转移
参考:https://www.cnblogs.com/kevingrace/p/6138185.html
当master节点的keepalived服务挂掉,vip会自动漂移到slave节点上
当master节点的keepliaved服务恢复后,从将vip资源从slave节点重新抢占回来(keepalived配置文件中的priority优先级决定的)
当两个节点的nginx挂掉后,keepaived会引用nginx监控脚本自启动nginx服务,如启动失败,则强杀keepalived服务,从而实现vip转移。</pre>
</div>
</div>
<div id="MySignature" role="contentinfo">
*************** 当你发现自己的才华撑不起野心时,就请安静下来学习吧!***************<br><br>
来源:https://www.cnblogs.com/kevingrace/p/10961264.html
頁:
[1]