内容简介:本次采用二进制文件方式部署和之前的步骤差不多都是和kubeadm步骤一样,不过这次所有kubelet全部走bootstrap不会像之前master上的kubelet生成证书,另外证书换成openssl生成不建议使用secureCRT这个ssh软件复制本篇博客内容的命令,因为它的部分版本对包含多条命令的处理结果并不完美,可能很多命令不是预期结果
本次采用二进制文件方式部署
和之前的步骤差不多都是和kubeadm步骤一样,不过这次所有kubelet全部走bootstrap不会像之前master上的kubelet生成证书,另外证书换成openssl生成
不建议使用secureCRT这个ssh软件复制本篇博客内容的命令,因为它的部分版本对包含多条命令的处理结果并不完美,可能很多命令不是预期结果
本文命令里有些是输出,不要乱粘贴输入(虽然也没影响)
本文命令全部是在 k8s-m1
上执行
本文很多步骤是选择其一,别啥都不看一路往下复制粘贴
如果某些步骤理解不了可以上下内容一起看来理解
本文后面的几个svc用了externalIPs,上生产的话必须用VIP或者LB代替
本文的HA是vip,生产和云上可以用LB和SLB,不过阿里的SLB四层有问题(不支持回源),可以每个node上代理127.0.0.1的某个port分摊在所有apiserver的port上,aws的SLB正常
master节点一定要kube-proxy和calico或者flannel,kube-proxy是维持svc的ip到pod的ip的负载均衡,而你流量想到pod的ip需要calico或者flannel组件的overlay网络下才可以,后续学到APIService和CRD的时候,APIService如果选中了svc,kube-apiserver会把这个APISerivce的请求代理到选中的svc上,最后流量流到svc选中的pod,该pod要处理请求然后回应,这个时候就是kube-apiserver解析svc的名字得到svc的ip,然后kube-proxy定向到pod的ip,calico或者flannel把包发到目标机器上,这个时候如果kube-proxy和calico或者flannel没有那你创建的APISerivce就没用了。apiserver的路由聚合没试过不知道可行不可行,本文中的metrics-server就是这样的工作流程,所以建议master也跑pod,不然后续某些CRD用不了
本次安装的版本:
- Kubernetes v1.13.4
- CNI v0.7.4
- Etcd v3.3.12
- Flannel v0.11.0 或者 Calico v3.4
- Docker CE 18.06.03
在官方的支持版本里好像没说1.13.4支持18.09,保险起见我这里使用的是18.06.03
本次部署的网络信息:
- Cluster IP CIDR: 10.244.0.0/16
- Service Cluster IP CIDR: 10.96.0.0/12
- Service DNS IP: 10.96.0.10
- DNS DN: cluster.local
上面不建议改
- Kubernetes API VIP: 10.0.6.155
- Kubernetes Ingress VIP: 10.0.6.156
Kubernetes API VIP
节点信息
本教学将以下列节点数与规格来进行部署Kubernetes集群,系统可采用 Ubuntu 16.x
与 CentOS 7.4+
IP | Hostname | CPU | Memory |
---|---|---|---|
10.0.6.166 | K8S-M1 | 4 | 8G |
10.0.6.167 | K8S-M2 | 4 | 8G |
10.0.6.168 | K8S-M3 | 4 | 8G |
10.0.6.169 | K8S-N1 | 2 | 4G |
10.0.6.170 | K8S-N2 | 2 | 4G |
另外VIP为 10.0.6.155
,由所有 master节点
的 keepalived+haproxy
来选择VIP的归属保持高可用
master
事前准备
-
所有机器
彼此网路互通,并且k8s-m1
SSH登入其他节点为passwdless(如果不互信可以后面的操作)。 -
所有防火墙与SELinux 已关闭。如CentOS:
否则后续 K8S 挂载目录时可能报错 Permission denied
systemctl disable --now firewalld NetworkManager setenforce 0 sed -ri '/^[^#]*SELINUX=/s#=.+$#=disabled#' /etc/selinux/config
-
关闭 dnsmasq (可选)
linux 系统开启了 dnsmasq 后(如 GUI 环境),将系统 DNS Server 设置为 127.0.0.1,这会导致 docker 容器无法解析域名,需要关闭它
systemctl disable --now dnsmasq
-
Kubernetes v1.8+要求关闭系统Swap,若不关闭则需要修改kubelet设定参数( –fail-swap-on 设置为 false 来忽略 swap on),在
所有机器
使用以下指令关闭swap并注释掉/etc/fstab
中swap的行:swapoff -a && sysctl -w vm.swappiness=0 sed -ri '/^[^#]*swap/s@^@#@' /etc/fstab
-
如果是centos的话此时建议升级下系统(后面会升级内核,如果想偷懒直接升级到新内核可以此处的yum update -y后面加上
--exclude=kernel*
后不会安装保守内核,可以不重启直接往后直接跳到最新内核步骤)yum install epel-release -y yum install wget git jq psmisc socat -y yum update -y
-
升级后建议重启下,因为后面会安装最新版内核,上面安装的是保守的内核,先加载看看
reboot
因为目前市面上包管理下内核版本会很低,安装docker后无论centos还是ubuntu会有如下bug,4.15的内核依然存在
kernel:unregister_netdevice: waiting for lo to become free. Usage count = 1
所以建议先升级内核
perl是内核的依赖包,如果没有就安装下
[ ! -f /usr/bin/perl ] && yum install perl -y
-
升级内核需要使用 elrepo 的yum 源,首先我们导入 elrepo 的 key并安装 elrepo 源
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
-
查看可用的内核
yum --disablerepo="*" --enablerepo="elrepo-kernel" list available --showduplicates
- 在yum的ELRepo源中,mainline 为最新版本的内核,安装kernel
ipvs依赖于 nf_conntrack_ipv4
内核模块, 4.19
包括之后内核里改名为 nf_conntrack
,1.13.1之前的kube-proxy的代码里没有加判断一直用的 nf_conntrack_ipv4
,好像是1.13.1后的kube-proxy代码里增加了判断,我测试了是会去load nf_conntrack
使用ipvs正常
下面链接可以下载到其他归档版本的
- ubuntu http://kernel.ubuntu.com/~kernel-ppa/mainline/
- RHEL http://mirror.rc.usf.edu/compute_lock/elrepo/kernel/el7/x86_64/RPMS/
下面是ml的内核和上面归档内核版本任选其一的安装方法
-
自选版本内核安装方法
export Kernel_Version=4.18.9-1 wget http://mirror.rc.usf.edu/compute_lock/elrepo/kernel/el7/x86_64/RPMS/kernel-ml{,-devel}-${Kernel_Version}.el7.elrepo.x86_64.rpm yum localinstall -y kernel-ml*
-
最新内核安装(也是我使用的)
yum --disablerepo="*" --enablerepo="elrepo-kernel" list available --showduplicates | grep -Po '^kernel-ml.x86_64\s+\K\S+(?=.el7)' yum --disablerepo="*" --enablerepo=elrepo-kernel install -y kernel-ml{,-devel}
- 修改内核启动顺序,默认启动的顺序应该为1,升级以后内核是往前面插入,为0(如果每次启动时需要手动选择哪个内核,该步骤可以省略)
grub2-set-default 0 && grub2-mkconfig -o /etc/grub2.cfg
- 修改内核启动顺序,默认启动的顺序应该为1,升级以后内核是往前面插入,为0(如果每次启动时需要手动选择哪个内核,该步骤可以省略)
使用下面命令看看确认下是否启动默认内核指向上面安装的内核
grubby --default-kernel
-
docker官方的内核检查脚本建议
(RHEL7/CentOS7: User namespaces disabled; add 'user_namespace.enable=1' to boot command line)
,使用下面命令开启grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"
-
重启加载新内核
reboot
-
所有机器
安装ipvs(ipvs性能甩iptables几条街并且排错更直观)在每台机器上安装依赖包:
CentOS:
yum install ipvsadm ipset sysstat conntrack libseccomp -y
Ubuntu:
sudo apt-get install -y wget git conntrack ipvsadm ipset jq sysstat curl iptables libseccomp
-
所有机器
选择需要开机加载的内核模块,以下是 ipvs 模式需要加载的模块并设置开机自动加载:> /etc/modules-load.d/ipvs.conf module=( ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack br_netfilter ) for kernel_module in ${module[@]};do /sbin/modinfo -F filename $kernel_module |& grep -qv ERROR && echo $kernel_module >> /etc/modules-load.d/ipvs.conf || : done systemctl enable --now systemd-modules-load.service
上面如果 systemctl enable
命令报错可以 systemctl status -l systemd-modules-load.service
看看哪个内核模块加载不了,在 /etc/modules-load.d/ipvs.conf
里注释掉它再enable试试
-
所有机器
需要设定/etc/sysctl.d/k8s.conf
的系统参数。cat <<EOF > /etc/sysctl.d/k8s.conf # https://github.com/moby/moby/issues/31208 # ipvsadm -l --timout # 修复ipvs模式下长连接timeout问题 小于900即可 net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_intvl = 30 net.ipv4.tcp_keepalive_probes = 10 net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1 net.ipv6.conf.lo.disable_ipv6 = 1 net.ipv4.neigh.default.gc_stale_time = 120 net.ipv4.conf.all.rp_filter = 0 net.ipv4.conf.default.rp_filter = 0 net.ipv4.conf.default.arp_announce = 2 net.ipv4.conf.lo.arp_announce = 2 net.ipv4.conf.all.arp_announce = 2 net.ipv4.ip_forward = 1 net.ipv4.tcp_max_tw_buckets = 5000 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn_backlog = 1024 net.ipv4.tcp_synack_retries = 2 net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 net.netfilter.nf_conntrack_max = 2310720 fs.inotify.max_user_watches=89100 fs.may_detach_mounts = 1 fs.file-max = 52706963 fs.nr_open = 52706963 net.bridge.bridge-nf-call-arptables = 1 vm.swappiness = 0 vm.overcommit_memory=1 vm.panic_on_oom=0 EOF sysctl --system
- 检查系统内核和模块是否适合运行 docker (仅适用于 linux 系统)
curl https://raw.githubusercontent.com/docker/docker/master/contrib/check-config.sh > check-config.sh bash ./check-config.sh
-
所有机器
需要安装Docker CE 版本的容器引擎,推荐使用年份命名版本的docker ce: - 在官方查看K8s支持的docker版本 https://github.com/kubernetes/kubernetes 里进对应版本的changelog里搜
The list of validated docker versions remain
-
这里利用docker的官方安装脚本来安装一次来添加repo,然后查询可用的docker版本,选择你要安装的k8s版本支持的docker版本即可,这里我使用的是
18.06.03
curl -fsSL "https://get.docker.com/" | bash -s -- --mirror Aliyun && yum autoremove docker-ce -y yum list docker-ce --showduplicates | sort -r yum install -y docker-ce-18.06.3.ce-3.el7
-
所有机器
配置加速源并配置docker的启动参数使用systemd,使用systemd是官方的建议,详见 https://kubernetes.io/docs/setup/cri/mkdir -p /etc/docker/ cat>/etc/docker/daemon.json<<EOF { "exec-opts": ["native.cgroupdriver=systemd"], "registry-mirrors": ["https://fz5yth0r.mirror.aliyuncs.com"], "storage-driver": "overlay2", "storage-opts": [ "overlay2.override_kernel_check=true" ], "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } } EOF
- 设置docker开机启动,CentOS安装完成后docker需要手动设置docker命令补全:
yum install -y epel-release bash-completion && cp /usr/share/bash-completion/completions/docker /etc/bash_completion.d/ systemctl enable --now docker
- 切记
所有机器
需要自行设定ntp
,否则不只HA下apiserver通信有问题,各种千奇百怪的问题 。
此时可以关机做个快照
- 在
k8s-m1
上声明集群信息使用环境变量声明集群信息
根据自己环境声明用到的变量,后续操作依赖于环境变量,所以断开了ssh后要重新声明下(主要是ip和一些信息,路径最好别改)
下面键是主机的 hostname
,而的值是主机的IP。默认Kubelet向集群注册是带hostname上去的,或者选项 --hostname-override
去设置注册的名字。后面没有用 --hostname-override
,我是偷懒直接使用hostname,生产力建议用 --hostname-override
固定。所有机器同理
haproxy每台上占据 8443
端口去负载到每台master上的 api-server
的 6443
端口
然后 keepalived
会保证 vip
飘在可用的 master
上
管理组件
和
kubelet
都会去访问
vip:8443
确保了即使down掉一台master也能访问到
apiserver
云上的话选择熟练的LB来代替掉haproxy和keepalived即可
VIP
和 INGRESS_VIP
选同一个局域网的没用过IP来使用即可
建议下面写进一个文件,这样断开了ssh也能source一下加载
# 声明集群成员信息 declare -A MasterArray otherMaster NodeArray AllNode Other MasterArray=(['k8s-m1']=10.0.6.166 ['k8s-m2']=10.0.6.167 ['k8s-m3']=10.0.6.168) otherMaster=(['k8s-m2']=10.0.6.167 ['k8s-m3']=10.0.6.168) NodeArray=(['k8s-n1']=10.0.6.169 ['k8s-n2']=10.0.6.170) # 下面复制上面的信息粘贴即可 AllNode=(['k8s-m1']=10.0.6.166 ['k8s-m2']=10.0.6.167 ['k8s-m3']=10.0.6.168 ['k8s-n1']=10.0.6.169 ['k8s-n2']=10.0.6.170) Other=(['k8s-m2']=10.0.6.167 ['k8s-m3']=10.0.6.168 ['k8s-n1']=10.0.6.169 ['k8s-n2']=10.0.6.170) export VIP=10.0.6.155 export INGRESS_VIP=10.0.6.156 [ "${#MasterArray[@]}" -eq 1 ] && export VIP=${MasterArray[@]} || export API_PORT=8443 export KUBE_APISERVER=https://${VIP}:${API_PORT:-6443} #声明需要安装的的k8s版本 export KUBE_VERSION=v1.13.4 # 网卡名 export interface=eth0 # cni export CNI_URL="https://github.com/containernetworking/plugins/releases/download" export CNI_VERSION=v0.7.4 # etcd export ETCD_version=v3.3.12
k8s-m1
登陆其他机器要免密(不然就后面文章手动输入)或者在 k8s-m1
安装 sshpass
后使用别名来让ssh和scp不输入密码, zhangguanzhang
为所有机器密码
yum install sshpass -y alias ssh='sshpass -p zhangguanzhang ssh -o StrictHostKeyChecking=no' alias scp='sshpass -p zhangguanzhang scp -o StrictHostKeyChecking=no'
- 设置所有机器的
hostname
,有些人喜欢用master1
就自己改,我的是下面的k8s-m1
,所有机器都要设置for name in ${!AllNode[@]};do echo "--- $name ${AllNode[$name]} ---" ssh ${AllNode[$name]} "hostnamectl set-hostname $name" done
首先在 k8s-m1
通过git获取部署要用到的二进制配置文件和yml
git clone https://github.com/zhangguanzhang/k8s-manual-files.git ~/k8s-manual-files -b v1.13.4 cd ~/k8s-manual-files/
- 在
k8s-m1
下载Kubernetes二进制文件后分发到其他机器
可通过下面命令查询所有stable版本(耐心等待,请确保能访问到github)
$ curl -s https://zhangguanzhang.github.io/bash/pull.sh | bash -s search gcr.io/google_containers/kube-apiserver-amd64/ | grep -P 'v[\d.]+$' | sort -t '.' -n -k 2
无越墙 工具 的,我已把所有二进制文件上传到dockerhub了,详情见 k8s_bin-docker_cp
-
使用下面命令可以不越墙下载
cd ~/k8s-manual-files/ docker pull zhangguanzhang/k8s_bin:$KUBE_VERSION-full docker run --rm -d --name temp zhangguanzhang/k8s_bin:$KUBE_VERSION-full sleep 10 docker cp temp:/kubernetes-server-linux-amd64.tar.gz . tar -zxvf kubernetes-server-linux-amd64.tar.gz --strip-components=3 -C /usr/local/bin kubernetes/server/bin/kube{let,ctl,-apiserver,-controller-manager,-scheduler,-proxy}
-
有越墙工具的,官网下载地址使用下面命令
curl https://storage.googleapis.com/kubernetes-release/release/${KUBE_VERSION}/kubernetes-server-linux-amd64.tar.gz > kubernetes-server-linux-amd64.tar.gz tar -zxvf kubernetes-server-linux-amd64.tar.gz --strip-components=3 -C /usr/local/bin kubernetes/server/bin/kube{let,ctl,-apiserver,-controller-manager,-scheduler,-proxy}
分发master相关组件二进制文件到其他master上
for NODE in "${!otherMaster[@]}"; do echo "--- $NODE ${otherMaster[$NODE]} ---" scp /usr/local/bin/kube{let,ctl,-apiserver,-controller-manager,-scheduler,-proxy} ${otherMaster[$NODE]}:/usr/local/bin/ done
分发node的kubernetes二进制文件
for NODE in "${!NodeArray[@]}"; do echo "--- $NODE ${NodeArray[$NODE]} ---" scp /usr/local/bin/kube{let,-proxy} ${NodeArray[$NODE]}:/usr/local/bin/ done
- 在
k81-m1
下载Kubernetes CNI 二进制文件并分发
分发cni文件到other
mkdir -p /opt/cni/bin wget "${CNI_URL}/${CNI_VERSION}/cni-plugins-amd64-${CNI_VERSION}.tgz" tar -zxf cni-plugins-amd64-${CNI_VERSION}.tgz -C /opt/cni/bin # 分发cni文件 for NODE in "${!Other[@]}"; do echo "--- $NODE ${Other[$NODE]} ---" ssh ${Other[$NODE]} 'mkdir -p /opt/cni/bin' scp /opt/cni/bin/* ${Other[$NODE]}:/opt/cni/bin/ done
建立集群CA keys 与Certificates
在这个部分,将需要产生多个元件的Certificates,这包含Etcd、Kubernetes 元件等,并且每个集群都会有一个根数位凭证认证机构(Root Certificate Authority)被用在认证API Server 与Kubelet 端的凭证。
- PS这边要注意CA JSON档的
CN(Common Name)
与O(Organization)
等内容是会影响Kubernetes元件认证的。
CN
Common Name, apiserver 会从证书中提取该字段作为请求的用户名 (User Name)
O
Organization, apiserver 会从证书中提取该字段作为请求用户所属的组 (Group) - CA (Certificate Authority) 是自签名的根证书,用来签名后续创建的其它证书。
- 本文copensl创建所有证书。
准备openssl 证书配置文件
注入ip信息
mkdir -p /etc/kubernetes/pki/etcd sed -i "/IP.2/a IP.3 = $VIP" ~/k8s-manual-files/pki/openssl.cnf sed -ri '/IP.3/r '<( paste -d '' <(seq -f 'IP.%g = ' 4 $[${#AllNode[@]}+3]) <(xargs -n1<<<${AllNode[@]} | sort) ) ~/k8s-manual-files/pki/openssl.cnf sed -ri '$r '<( paste -d '' <(seq -f 'IP.%g = ' 2 $[${#MasterArray[@]}+1]) <(xargs -n1<<<${MasterArray[@]} | sort) ) ~/k8s-manual-files/pki/openssl.cnf cp ~/k8s-manual-files/pki/openssl.cnf /etc/kubernetes/pki/ cd /etc/kubernetes/pki
生成证书
path | Default CN | description |
---|---|---|
ca.crt,key | kubernetes-ca | Kubernetes general CA |
etcd/ca.crt,key | etcd-ca | For all etcd-related functions |
front-proxy-ca.crt,key | kubernetes-front-proxy-ca | For the front-end proxy |
kubernetes-ca
openssl genrsa -out ca.key 2048 openssl req -x509 -new -nodes -key ca.key -config openssl.cnf -subj "/CN=kubernetes-ca" -extensions v3_ca -out ca.crt -days 10000
etcd-ca
openssl genrsa -out etcd/ca.key 2048 openssl req -x509 -new -nodes -key etcd/ca.key -config openssl.cnf -subj "/CN=etcd-ca" -extensions v3_ca -out etcd/ca.crt -days 10000
front-proxy-ca
openssl genrsa -out front-proxy-ca.key 2048 openssl req -x509 -new -nodes -key front-proxy-ca.key -config openssl.cnf -subj "/CN=kubernetes-ca" -extensions v3_ca -out front-proxy-ca.crt -days 10000
生成所有的证书信息
Default CN | Parent CA | O (in Subject) | kind |
---|---|---|---|
kube-etcd | etcd-ca | server, client | |
kube-etcd-peer | etcd-ca | server, client | |
kube-etcd-healthcheck-client | etcd-ca | client | |
kube-apiserver-etcd-client | etcd-ca | system:masters | client |
kube-apiserver | kubernetes-ca | server | |
kube-apiserver-kubelet-client | kubernetes-ca | system:masters | client |
front-proxy-client | kubernetes-front-proxy-ca | client |
证书路径
Default CN | recommend key path | recommended cert path | command | key argument | cert argument |
---|---|---|---|---|---|
etcd-ca | etcd/ca.crt | kube-apiserver | –etcd-cafile | ||
etcd-client | apiserver-etcd-client.key | apiserver-etcd-client.crt | kube-apiserver | –etcd-keyfile | –etcd-certfile |
kubernetes-ca | ca.crt | kube-apiserver | –client-ca-file | ||
kube-apiserver | apiserver.key | apiserver.crt | kube-apiserver | –tls-private-key-file | –tls-cert-file |
apiserver-kubelet-client | apiserver-kubelet-client.crt | kube-apiserver | –kubelet-client-certificate | ||
front-proxy-ca | front-proxy-ca.crt | kube-apiserver | –requestheader-client-ca-file | ||
front-proxy-client | front-proxy-client.key | front-proxy-client.crt | kube-apiserver | –proxy-client-key-file | –proxy-client-cert-file |
etcd-ca | etcd/ca.crt | etcd | –trusted-ca-file, –peer-trusted-ca-file | ||
kube-etcd | etcd/server.key | etcd/server.crt | etcd | –key-file | –cert-file |
kube-etcd-peer | etcd/peer.key | etcd/peer.crt | etcd | –peer-key-file | –peer-cert-file |
etcd-ca | etcd/ca.crt | etcdctl | –cacert | ||
kube-etcd-healthcheck-client | etcd/healthcheck-client.key | etcd/healthcheck-client.crt | etcdctl | –key | –cert |
生成证书
apiserver-etcd-client
openssl genrsa -out apiserver-etcd-client.key 2048 openssl req -new -key apiserver-etcd-client.key -subj "/CN=apiserver-etcd-client/O=system:masters" -out apiserver-etcd-client.csr openssl x509 -in apiserver-etcd-client.csr -req -CA etcd/ca.crt -CAkey etcd/ca.key -CAcreateserial -extensions v3_req_etcd -extfile openssl.cnf -out apiserver-etcd-client.crt -days 10000
kube-etcd
openssl genrsa -out etcd/server.key 2048 openssl req -new -key etcd/server.key -subj "/CN=etcd-server" -out etcd/server.csr openssl x509 -in etcd/server.csr -req -CA etcd/ca.crt -CAkey etcd/ca.key -CAcreateserial -extensions v3_req_etcd -extfile openssl.cnf -out etcd/server.crt -days 10000
kube-etcd-peer
openssl genrsa -out etcd/peer.key 2048 openssl req -new -key etcd/peer.key -subj "/CN=etcd-peer" -out etcd/peer.csr openssl x509 -in etcd/peer.csr -req -CA etcd/ca.crt -CAkey etcd/ca.key -CAcreateserial -extensions v3_req_etcd -extfile openssl.cnf -out etcd/peer.crt -days 10000
kube-etcd-healthcheck-client
openssl genrsa -out etcd/healthcheck-client.key 2048 openssl req -new -key etcd/healthcheck-client.key -subj "/CN=etcd-client" -out etcd/healthcheck-client.csr openssl x509 -in etcd/healthcheck-client.csr -req -CA etcd/ca.crt -CAkey etcd/ca.key -CAcreateserial -extensions v3_req_etcd -extfile openssl.cnf -out etcd/healthcheck-client.crt -days 10000
kube-apiserver
openssl genrsa -out apiserver.key 2048 openssl req -new -key apiserver.key -subj "/CN=kube-apiserver" -config openssl.cnf -out apiserver.csr openssl x509 -req -in apiserver.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 10000 -extensions v3_req_apiserver -extfile openssl.cnf -out apiserver.crt
apiserver-kubelet-client
openssl genrsa -out apiserver-kubelet-client.key 2048 openssl req -new -key apiserver-kubelet-client.key -subj "/CN=apiserver-kubelet-client/O=system:masters" -out apiserver-kubelet-client.csr openssl x509 -req -in apiserver-kubelet-client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 10000 -extensions v3_req_client -extfile openssl.cnf -out apiserver-kubelet-client.crt
front-proxy-client
openssl genrsa -out front-proxy-client.key 2048 openssl req -new -key front-proxy-client.key -subj "/CN=front-proxy-client" -out front-proxy-client.csr openssl x509 -req -in front-proxy-client.csr -CA front-proxy-ca.crt -CAkey front-proxy-ca.key -CAcreateserial -days 10000 -extensions v3_req_client -extfile openssl.cnf -out front-proxy-client.crt
kube-scheduler 证书
openssl genrsa -out kube-scheduler.key 2048 openssl req -new -key kube-scheduler.key -subj "/CN=system:kube-scheduler" -out kube-scheduler.csr openssl x509 -req -in kube-scheduler.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 10000 -extensions v3_req_client -extfile openssl.cnf -out kube-scheduler.crt
sa.pub sa.key
openssl genrsa -out sa.key 2048 openssl ecparam -name secp521r1 -genkey -noout -out sa.key openssl ec -in sa.key -outform PEM -pubout -out sa.pub openssl req -new -sha256 -key sa.key -subj "/CN=system:kube-controller-manager" -out sa.csr openssl x509 -req -in sa.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 10000 -extensions v3_req_client -extfile openssl.cnf -out sa.crt
admin 证书
openssl genrsa -out admin.key 2048 openssl req -new -key admin.key -subj "/CN=kubernetes-admin/O=system:masters" -out admin.csr openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 10000 -extensions v3_req_client -extfile openssl.cnf -out admin.crt
清理 csr srl
find . -name "*.csr" -o -name "*.srl"|xargs rm -f
利用证书生成组件的kubeconfig
filename | credential name | Default CN | O (in Subject) |
---|---|---|---|
admin.kubeconfig | default-admin | kubernetes-admin | system:masters |
controller-manager.kubeconfig | default-controller-manager | system:kube-controller-manager | |
scheduler.kubeconfig | default-manager | system:kube-scheduler |
kube-controller-manager
CLUSTER_NAME="kubernetes" KUBE_USER="system:kube-controller-manager" KUBE_CERT="sa" KUBE_CONFIG="controller-manager.kubeconfig" # 设置集群参数 kubectl config set-cluster ${CLUSTER_NAME} \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置客户端认证参数 kubectl config set-credentials ${KUBE_USER} \ --client-certificate=/etc/kubernetes/pki/${KUBE_CERT}.crt \ --client-key=/etc/kubernetes/pki/${KUBE_CERT}.key \ --embed-certs=true \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置上下文参数 kubectl config set-context ${KUBE_USER}@${CLUSTER_NAME} \ --cluster=${CLUSTER_NAME} \ --user=${KUBE_USER} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置当前使用的上下文 kubectl config use-context ${KUBE_USER}@${CLUSTER_NAME} --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 查看生成的配置文件 kubectl config view --kubeconfig=/etc/kubernetes/${KUBE_CONFIG}
kube-scheduler
CLUSTER_NAME="kubernetes" KUBE_USER="system:kube-scheduler" KUBE_CERT="kube-scheduler" KUBE_CONFIG="scheduler.kubeconfig" # 设置集群参数 kubectl config set-cluster ${CLUSTER_NAME} \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置客户端认证参数 kubectl config set-credentials ${KUBE_USER} \ --client-certificate=/etc/kubernetes/pki/${KUBE_CERT}.crt \ --client-key=/etc/kubernetes/pki/${KUBE_CERT}.key \ --embed-certs=true \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置上下文参数 kubectl config set-context ${KUBE_USER}@${CLUSTER_NAME} \ --cluster=${CLUSTER_NAME} \ --user=${KUBE_USER} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置当前使用的上下文 kubectl config use-context ${KUBE_USER}@${CLUSTER_NAME} --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 查看生成的配置文件 kubectl config view --kubeconfig=/etc/kubernetes/${KUBE_CONFIG}
admin(kubectl)
CLUSTER_NAME="kubernetes" KUBE_USER="kubernetes-admin" KUBE_CERT="admin" KUBE_CONFIG="admin.kubeconfig" # 设置集群参数 kubectl config set-cluster ${CLUSTER_NAME} \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置客户端认证参数 kubectl config set-credentials ${KUBE_USER} \ --client-certificate=/etc/kubernetes/pki/${KUBE_CERT}.crt \ --client-key=/etc/kubernetes/pki/${KUBE_CERT}.key \ --embed-certs=true \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置上下文参数 kubectl config set-context ${KUBE_USER}@${CLUSTER_NAME} \ --cluster=${CLUSTER_NAME} \ --user=${KUBE_USER} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置当前使用的上下文 kubectl config use-context ${KUBE_USER}@${CLUSTER_NAME} --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 查看生成的配置文件 kubectl config view --kubeconfig=/etc/kubernetes/${KUBE_CONFIG}
分发证书
分发到 kubeconfig 及证书其他 master 节点
for NODE in "${!otherMaster[@]}"; do echo "--- $NODE ${otherMaster[$NODE]} ---" scp -r /etc/kubernetes ${otherMaster[$NODE]}:/etc done
配置ETCD
Etcd 二进制文件
- Etcd:用来保存集群所有状态的 Key/Value 存储系统,所有 Kubernetes 组件会通过 API Server 来跟 Etcd 进行沟通从而保存或读取资源状态。
- 这边etcd跑在master上,有条件的可以单独几台机器跑,不过得会配置apiserver指向etcd集群
etcd所有标准版本可以在下面url查看
https://github.com/etcd-io/etcd/releases
在 k8s-m1
上下载etcd的二进制文件, 单台的话建议使用v3.1.9因为有bug详情见我github
[ "${#MasterArray[@]}" -eq 1 ] && ETCD_version=v3.1.9 || : cd ~/k8s-manual-files
如果下面直接下载失败的话一样使用骚套路:docker拉镜像后cp出来
wget https://github.com/etcd-io/etcd/releases/download/${ETCD_version}/etcd-${ETCD_version}-linux-amd64.tar.gz tar -zxvf etcd-${ETCD_version}-linux-amd64.tar.gz --strip-components=1 -C /usr/local/bin etcd-${ETCD_version}-linux-amd64/etcd{,ctl} #------- #上面被墙了可以使用骚套路 docker pull quay.io/coreos/etcd:$ETCD_version docker run --rm -d --name temp quay.io/coreos/etcd:$ETCD_version sleep 10 docker cp temp:/usr/local/bin/etcd /usr/local/bin docker cp temp:/usr/local/bin/etcdctl /usr/local/bin
在 k8s-m1
上分发etcd的二进制文件到其他master上
for NODE in "${!otherMaster[@]}"; do echo "--- $NODE ${otherMaster[$NODE]} ---" scp /usr/local/bin/etcd* ${otherMaster[$NODE]}:/usr/local/bin/ done
在 k8s-m1
上配置etcd配置文件并分发相关文件
/etc/etcd/etcd.config.yml
注入基础变量
cd ~/k8s-manual-files/master/ etcd_servers=$( xargs -n1<<<${MasterArray[@]} | sort | sed 's#^#https://#;s#$#:2379#;$s#\n##' | paste -d, -s - ) etcd_initial_cluster=$( for i in ${!MasterArray[@]};do echo $i=https://${MasterArray[$i]}:2380; done | sort | paste -d, -s - ) sed -ri "/initial-cluster:/s#'.+'#'${etcd_initial_cluster}'#" etc/etcd/config.yml
分发systemd和配置文件
for NODE in "${!MasterArray[@]}"; do echo "--- $NODE ${MasterArray[$NODE]} ---" ssh ${MasterArray[$NODE]} "mkdir -p $MANIFESTS_DIR /etc/etcd /var/lib/etcd" scp systemd/etcd.service ${MasterArray[$NODE]}:/usr/lib/systemd/system/etcd.service scp etc/etcd/config.yml ${MasterArray[$NODE]}:/etc/etcd/etcd.config.yml ssh ${MasterArray[$NODE]} "sed -i "s/{HOSTNAME}/$NODE/g" /etc/etcd/etcd.config.yml" ssh ${MasterArray[$NODE]} "sed -i "s/{PUBLIC_IP}/${MasterArray[$NODE]}/g" /etc/etcd/etcd.config.yml" ssh ${MasterArray[$NODE]} 'systemctl daemon-reload' done
在 k8s-m1
上启动所有etcd
etcd 进程首次启动时会等待其它节点的 etcd 加入集群,命令 systemctl start etcd 会卡住一段时间,为正常现象
可以全部启动后后面的 etcdctl
命令查看状态确认正常否
for NODE in "${!MasterArray[@]}"; do echo "--- $NODE ${MasterArray[$NODE]} ---" ssh ${MasterArray[$NODE]} 'systemctl enable --now etcd' & done wait
然后输出到终端了的时候多按几下回车直到等光标回到终端状态
k8s-m1
上执行下面命令验证 ETCD 集群状态,下面第二个是使用3的api去查询集群的键值
etcdctl \ --cert-file /etc/kubernetes/pki/etcd/healthcheck-client.crt \ --key-file /etc/kubernetes/pki/etcd/healthcheck-client.key \ --ca-file /etc/kubernetes/pki/etcd/ca.crt \ --endpoints $etcd_servers cluster-health ...下面是输出 member 4f15324b6756581c is healthy: got healthy result from https://10.0.6.166:2379 member cce1303a6b6dd443 is healthy: got healthy result from https://10.0.6.167:2379 member ead42f3e6c9bb295 is healthy: got healthy result from https://10.0.6.168:2379 cluster is healthy ETCDCTL_API=3 \ etcdctl \ --cert /etc/kubernetes/pki/etcd/healthcheck-client.crt \ --key /etc/kubernetes/pki/etcd/healthcheck-client.key \ --cacert /etc/kubernetes/pki/etcd/ca.crt \ --endpoints $etcd_servers get / --prefix --keys-only
- 如果想了解更多etcdctl操作可以去官网 etcdctl command 文章。
Kubernetes Masters
本部分将说明如何建立与设定Kubernetes Master 角色,过程中会部署以下元件:
kubelet :
API Server
kube-apiserver :
- 以 REST APIs 提供 Kubernetes 资源的 CRUD,如授权、认证、存取控制与 API 注册等机制。
- 关闭非安全端口,在安全端口 6443 接收 https 请求
- 严格的认证和授权策略 (x509、token、RBAC)
- 开启 bootstrap token 认证,支持 kubelet TLS bootstrapping
- 使用 https 访问 kubelet、etcd,加密通信
kube-controller-manager :
- 通过核心控制循环(Core Control Loop)监听 Kubernetes API 的资源来维护集群的状态,这些资源会被不同的控制器所管理,如 Replication Controller、Namespace Controller 等等。而这些控制器会处理着自动扩展、滚动更新等等功能。
- 关闭非安全端口,在安全端口 10252 接收 https 请求
- 使用 kubeconfig 访问 apiserver 的安全端口
kube-scheduler :
- 负责将一個(或多个)容器依据调度策略分配到对应节点上让容器引擎(如 Docker)执行。而调度受到 QoS 要求、软硬性约束、亲和性(Affinity)等等因素影响。
HAProxy :
- 提供多个 API Server 的负载均衡(Load Balance),确保haproxy的端口负载到所有的apiserver的6443端口
Keepalived :
提供虚拟IP位址(VIP),来让vip落在可用的master主机上供所有组件都能访问到可用的master,结合haproxy能访问到master上的apiserver的6443端口
部署与设定
- 信息啥的按照自己实际填写,文件改了后如果不懂我下面写的 shell 估计是改不回了
- 网卡名改为各自宿主机的网卡名,下面用
export interface=eth0
后续的所有文件同理 - 若
cluster dns
或domain
有改变的话,需要修改kubelet-conf.yml
。
HA(haproxy+keepalived) 单台master就不要用HA了
首先所有 master
安装 haproxy+keepalived
,多按几次回车如果没输出的话
for NODE in "${!MasterArray[@]}"; do echo "--- $NODE ${MasterArray[$NODE]} ---" ssh ${MasterArray[$NODE]} 'yum install haproxy keepalived -y' & done wait
在 k8s-m1
节点下把相关配置文件配置后再分发
cd ~/k8s-manual-files/master/etc # 修改haproxy.cfg配置文件 sed -i '$r '<(paste <( seq -f' server k8s-api-%g' ${#MasterArray[@]} ) <( xargs -n1<<<${MasterArray[@]} | sort | sed 's#$#:6443 check#')) haproxy/haproxy.cfg # 修改keepalived(网卡和VIP写进去,使用下面命令) sed -ri "s#\{\{ VIP \}\}#${VIP}#" keepalived/* sed -ri "s#\{\{ interface \}\}#${interface}#" keepalived/keepalived.conf sed -i '/unicast_peer/r '<(xargs -n1<<<${MasterArray[@]} | sort | sed 's#^#\t#') keepalived/keepalived.conf # 分发文件 for NODE in "${!MasterArray[@]}"; do echo "--- $NODE ${MasterArray[$NODE]} ---" scp -r haproxy/ ${MasterArray[$NODE]}:/etc scp -r keepalived/ ${MasterArray[$NODE]}:/etc ssh ${MasterArray[$NODE]} 'systemctl enable --now haproxy keepalived' done
ping下vip看看能通否,先等待大概四五秒等keepalived和haproxy起来
ping $VIP
如果vip没起来就是keepalived没起来就每个节点上去restart下keepalived或者确认下配置文件 /etc/keepalived/keepalived.conf
里网卡名和ip是否注入成功
for NODE in "${!MasterArray[@]}"; do echo "--- $NODE ${MasterArray[$NODE]} ---" ssh ${MasterArray[$NODE]} 'systemctl restart haproxy keepalived' done
Master组件
在 k8s-m1
节点下把相关配置文件配置后再分发
cd ~/k8s-manual-files/master/ etcd_servers=$( xargs -n1<<<${MasterArray[@]} | sort | sed 's#^#https://#;s#$#:2379#;$s#\n##' | paste -d, -s - ) # 注入VIP和etcd_servers,apiserver数量 sed -ri '/--advertise-address/s#=.+#='"$VIP"' \\#' systemd/kube-apiserver.service sed -ri '/--etcd-servers/s#=.+#='"$etcd_servers"' \\#' systemd/kube-apiserver.service sed -ri '/apiserver-count/s#=[^\]+#='"${#MasterArray[@]}"' #' systemd/kube-apiserver.service # 分发文件 for NODE in "${!MasterArray[@]}"; do echo "--- $NODE ${MasterArray[$NODE]} ---" ssh ${MasterArray[$NODE]} 'mkdir -p /etc/kubernetes/manifests /etc/systemd/system/kubelet.service.d /var/lib/kubelet /var/log/kubernetes' scp systemd/kube-*.service ${MasterArray[$NODE]}:/usr/lib/systemd/system/ #注入网卡ip ssh ${MasterArray[$NODE]} "sed -ri '/bind-address/s#=[^\]+#=${MasterArray[$NODE]} #' /usr/lib/systemd/system/kube-apiserver.service" done
在 k8s-m1
上给所有master机器启动kubelet 服务并设置kubectl补全脚本:
for NODE in "${!MasterArray[@]}"; do echo "--- $NODE ${MasterArray[$NODE]} ---" ssh ${MasterArray[$NODE]} 'systemctl enable --now kube-apiserver kube-controller-manager kube-scheduler; mkdir -p ~/.kube/ cp /etc/kubernetes/admin.kubeconfig ~/.kube/config; kubectl completion bash > /etc/bash_completion.d/kubectl' done
验证组件
完成后,在任意一台 master
节点通过简单指令验证:
$ kubectl get cs NAME STATUS MESSAGE ERROR scheduler Healthy ok controller-manager Healthy ok etcd-2 Healthy {"health": "true"} etcd-0 Healthy {"health": "true"} etcd-1 Healthy {"health": "true"} $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36s
配置 bootstrap
由于本次安装启用了TLS认证,因此每个节点的kubelet都必须使用kube-apiserver的CA的凭证后,才能与kube-apiserver进行沟通,而该过程需要手动针对每台节点单独签署凭证是一件繁琐的事情,且一旦节点增加会延伸出管理不易问题;而TLS bootstrapping目标就是解决该问题,通过让kubelet先使用一个预定低权限使用者连接到kube-apiserver,然后在对kube-apiserver申请凭证签署,当授权Token一致时,Node节点的kubelet凭证将由kube-apiserver动态签署提供。具体作法可以参考 TLS Bootstrapping 与 Authenticating with Bootstrap Tokens 。
后面 kubectl
命令只需要在任何一台 master
执行就行了
首先在 k8s-m1
建立一个变数来产生 BOOTSTRAP_TOKEN
,并建立 bootstrap
的kubeconfig文件:
接着 在k8s-m1
建立TLS bootstrap secret来提供自动签证使用:
TOKEN_PUB=$(openssl rand -hex 3) TOKEN_SECRET=$(openssl rand -hex 8) BOOTSTRAP_TOKEN="${TOKEN_PUB}.${TOKEN_SECRET}" kubectl -n kube-system create secret generic bootstrap-token-${TOKEN_PUB} \ --type 'bootstrap.kubernetes.io/token' \ --from-literal description="cluster bootstrap token" \ --from-literal token-id=${TOKEN_PUB} \ --from-literal token-secret=${TOKEN_SECRET} \ --from-literal usage-bootstrap-authentication=true \ --from-literal usage-bootstrap-signing=true
建立 bootstrap
的kubeconfig文件
CLUSTER_NAME="kubernetes" KUBE_USER="kubelet-bootstrap" KUBE_CONFIG="bootstrap.kubeconfig" # 设置集群参数 kubectl config set-cluster ${CLUSTER_NAME} \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置上下文参数 kubectl config set-context ${KUBE_USER}@${CLUSTER_NAME} \ --cluster=${CLUSTER_NAME} \ --user=${KUBE_USER} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置客户端认证参数 kubectl config set-credentials ${KUBE_USER} \ --token=${BOOTSTRAP_TOKEN} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 设置当前使用的上下文 kubectl config use-context ${KUBE_USER}@${CLUSTER_NAME} --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} # 查看生成的配置文件 kubectl config view --kubeconfig=/etc/kubernetes/${KUBE_CONFIG}
- 若想要用手动签署凭证来进行授权的话,可以参考 Certificate 。
授权 kubelet 可以创建 csr
kubectl create clusterrolebinding kubeadm:kubelet-bootstrap \ --clusterrole system:node-bootstrapper --group system:bootstrappers
批准 csr 请求
- 允许 system:bootstrappers 组的所有 csr
cat <<EOF | kubectl apply -f - # Approve all CSRs for the group "system:bootstrappers" kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: auto-approve-csrs-for-group subjects: - kind: Group name: system:bootstrappers apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: system:certificates.k8s.io:certificatesigningrequests:nodeclient apiGroup: rbac.authorization.k8s.io EOF
允许 kubelet 能够更新自己的证书
cat <<EOF | kubectl apply -f - # Approve renewal CSRs for the group "system:nodes" kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: auto-approve-renewals-for-nodes subjects: - kind: Group name: system:nodes apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient apiGroup: rbac.authorization.k8s.io EOF
创建所需的 clusterrole
cat <<EOF | kubectl apply -f - # A ClusterRole which instructs the CSR approver to approve a user requesting # node client credentials. kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: system:certificates.k8s.io:certificatesigningrequests:nodeclient rules: - apiGroups: ["certificates.k8s.io"] resources: ["certificatesigningrequests/nodeclient"] verbs: ["create"] --- # A ClusterRole which instructs the CSR approver to approve a node renewing its # own client credentials. kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient rules: - apiGroups: ["certificates.k8s.io"] resources: ["certificatesigningrequests/selfnodeclient"] verbs: ["create"] EOF
All Kubernetes Nodes
本部分将说明如何建立与设定Kubernetes Node 角色,Node 是主要执行容器实例(Pod)的工作节点。
在开始部署前,先在 k8-m1
将需要用到的文件复制到所有其他节点上:
for NODE in "${!Other[@]}"; do echo "--- $NODE ${Other[$NODE]} ---" ssh ${Other[$NODE]} "mkdir -p /etc/kubernetes/pki /etc/kubernetes/manifests /var/lib/kubelet/" for FILE in /etc/kubernetes/pki/ca.crt /etc/kubernetes/bootstrap.kubeconfig; do scp ${FILE} ${Other[$NODE]}:${FILE} done done
部署与设定
在 k8s-m1
节点分发 kubelet.service
文件和配置文件到每台上去管理kubelet:
cd ~/k8s-manual-files/ for NODE in "${!AllNode[@]}"; do echo "--- $NODE ${AllNode[$NODE]} ---" scp master/systemd/kubelet.service ${AllNode[$NODE]}:/lib/systemd/system/kubelet.service scp master/etc/kubelet/kubelet-conf.yml ${AllNode[$NODE]}:/etc/kubernetes/kubelet-conf.yml ssh ${AllNode[$NODE]} "sed -ri '/0.0.0.0/s#\S+\$#${MasterArray[$NODE]}#' /etc/kubernetes/kubelet-conf.yml" done
最后在 k8s-m1
上去启动每个 node
节点的kubelet 服务:
for NODE in "${!AllNode[@]}"; do echo "--- $NODE ${AllNode[$NODE]} ---" ssh ${AllNode[$NODE]} 'systemctl enable --now kubelet.service' done
验证集群
完成后,在任意一台 master
节点并通过简单指令验证:
$ kubectl get nodes NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k8s-m1 NotReady <none> 22s v1.13.4 10.0.6.166 <none> CentOS Linux 7 (Core) 4.20.13-1.el7.elrepo.x86_64 docker://18.6.3 k8s-m2 NotReady <none> 24s v1.13.4 10.0.6.167 <none> CentOS Linux 7 (Core) 4.20.13-1.el7.elrepo.x86_64 docker://18.6.3 k8s-m3 NotReady <none> 21s v1.13.4 10.0.6.168 <none> CentOS Linux 7 (Core) 4.20.13-1.el7.elrepo.x86_64 docker://18.6.3 k8s-n1 NotReady <none> 22s v1.13.4 10.0.6.169 <none> CentOS Linux 7 (Core) 4.20.13-1.el7.elrepo.x86_64 docker://18.6.3 k8s-n2 NotReady <none> 22s v1.13.4 10.0.6.170 <none> CentOS Linux 7 (Core) 4.20.13-1.el7.elrepo.x86_64 docker://18.6.3 # csr自动被授权 $ kubectl get csr NAME AGE REQUESTOR CONDITION node-csr-4fCDrNulc_btdBiRgev0JO4EorZ0rMuyJ756wrn9NqQ 27s system:bootstrap:e860ec Approved,Issued node-csr-P3Y_knryQNaQWDDYFObtcdfXB4XAl9IB2Be2YJ-b-dA 27s system:bootstrap:e860ec Approved,Issued node-csr-r_4ZDnanqBw2HPTSn6bSL50r-kJkTPjix6SY1n9UmjY 28s system:bootstrap:e860ec Approved,Issued node-csr-vy-6tgMI9vUiIzR3Ogv6bPVGA2_gZrd7aMIWMSuHrME 27s system:bootstrap:e860ec Approved,Issued node-csr-zOvVxSaY1iMco2LnOHmwqiBDwPUaLix7cSqUfZWTGFo 26s system:bootstrap:e860ec Approved,Issued
设定 master
节点加上污点Taint不让(没有声明容忍该污点的)pod跑在master节点上:
kubectl taint nodes ${!MasterArray[@]} node-role.kubernetes.io/master="":NoSchedule # 下面是输出 node "k8s-m1" tainted node "k8s-m2" tainted node "k8s-m3" tainted
node打标签声明role
kubectl label node ${!MasterArray[@]} node-role.kubernetes.io/master="" kubectl label node ${!NodeArray[@]} node-role.kubernetes.io/worker=worker
Kubernetes Core Addons部署
当完成上面所有步骤后,接着需要部署一些插件,其中如 Kubernetes DNS
与 Kubernetes Proxy
等这种Addons是非常重要的。
Kubernetes Proxy(二进制和ds选择一种方式)
Kube-proxy 是实现Service的关键插件,kube-proxy会在每台节点上执行,然后监听API Server的Service与Endpoint资源物件的改变,然后来依据变化执行iptables来实现网路的转发。这边我们会需要建议一个DaemonSet来执行,并且建立一些需要的Certificates。
二进制部署方式(ds比二进制更好扩展,后面有ds部署)
在 k8s-m1
配置 kube-proxy:
创建一个 kube-proxy 的 service account
:
kubectl -n kube-system create serviceaccount kube-proxy
将 kube-proxy 的 serviceaccount
绑定到 clusterrole system:node-proxier
以允许 RBAC:
kubectl create clusterrolebinding kubeadm:kube-proxy \ --clusterrole system:node-proxier \ --serviceaccount kube-system:kube-proxy
创建 kube-proxy
的 kubeconfig
:
CLUSTER_NAME="kubernetes" KUBE_CONFIG="kube-proxy.kubeconfig" SECRET=$(kubectl -n kube-system get sa/kube-proxy \ --output=jsonpath='{.secrets[0].name}') JWT_TOKEN=$(kubectl -n kube-system get secret/$SECRET \ --output=jsonpath='{.data.token}' | base64 -d) kubectl config set-cluster ${CLUSTER_NAME} \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} kubectl config set-context ${CLUSTER_NAME} \ --cluster=${CLUSTER_NAME} \ --user=${CLUSTER_NAME} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} kubectl config set-credentials ${CLUSTER_NAME} \ --token=${JWT_TOKEN} \ --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} kubectl config use-context ${CLUSTER_NAME} --kubeconfig=/etc/kubernetes/${KUBE_CONFIG} kubectl config view --kubeconfig=/etc/kubernetes/${KUBE_CONFIG}
在 k8s-m1
分发 kube-proxy
的 相关文件到所有节点
cd ~/k8s-manual-files/ for NODE in "${!Other[@]}"; do echo "--- $NODE ${Other[$NODE]} ---" scp /etc/kubernetes/kube-proxy.kubeconfig ${Other[$NODE]}:/etc/kubernetes/kube-proxy.kubeconfig done for NODE in "${!AllNode[@]}"; do echo "--- $NODE ${AllNode[$NODE]} ---" scp addons/kube-proxy/kube-proxy.conf ${AllNode[$NODE]}:/etc/kubernetes/kube-proxy.conf scp addons/kube-proxy/kube-proxy.service ${AllNode[$NODE]}:/usr/lib/systemd/system/kube-proxy.service ssh ${AllNode[$NODE]} "sed -ri '/0.0.0.0/s#\S+\$#${MasterArray[$NODE]}#' /etc/kubernetes/kube-proxy.conf" done
然后在 k8s-m1
上启动所有节点的kube-proxy 服务:
for NODE in "${!AllNode[@]}"; do echo "--- $NODE ${AllNode[$NODE]} ---" ssh ${AllNode[$NODE]} 'systemctl enable --now kube-proxy' done
daemonSet方式部署
cd ~/k8s-manual-files # 注入变量 sed -ri "/server:/s#(: ).+#\1${KUBE_APISERVER}#" addons/kube-proxy/kube-proxy.yml sed -ri "/image:.+kube-proxy/s#:[^:]+\$#:$KUBE_VERSION#" addons/kube-proxy/kube-proxy.yml kubectl apply -f addons/kube-proxy/kube-proxy.yml # 下面是输出 serviceaccount "kube-proxy" created clusterrolebinding.rbac.authorization.k8s.io "system:kube-proxy" created configmap "kube-proxy" created daemonset.apps "kube-proxy" created
正常是下面状态,如果有问题可以看看docker拉到了镜像否和kubelet的日志输出
- 正常了可以直接翻到下面
ipvsadm -ln
那
$ kubectl -n kube-system get po -l k8s-app=kube-proxy NAME READY STATUS RESTARTS AGE kube-proxy-dd2m7 1/1 Running 0 8m kube-proxy-fwgx8 1/1 Running 0 8m kube-proxy-kjn57 1/1 Running 0 8m kube-proxy-vp47w 1/1 Running 0 8m
通过ipvsadm查看 proxy 规则
$ ipvsadm -ln IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 10.96.0.1:443 rr -> 10.0.6.155:6443 Masq 1 0 0
确认使用ipvs模式
$ curl localhost:10249/proxyMode ipvs
集群网络
Kubernetes 在默认情況下与 Docker 的网络有所不同。在 Kubernetes 中有四个问题是需要被解決的,分別为:
- 高耦合的容器到容器通信:通过 Pods 内 localhost 的來解決。
- Pod 到 Pod 的通信:通过实现网络模型来解决。
- Pod 到 Service 通信:由 Service objects 结合 kube-proxy 解決。
- 外部到 Service 通信:一样由 Service objects 结合 kube-proxy 解決。
而 Kubernetes 对于任何网络的实现都需要满足以下基本要求(除非是有意调整的网络分段策略):
- 所有容器能够在沒有 NAT 的情況下与其他容器通信。
- 所有节点能夠在沒有 NAT 情況下与所有容器通信(反之亦然)。
- 容器看到的 IP 与其他人看到的 IP 是一样的。
庆幸的是 Kubernetes 已经有非常多种的 网络模型 作为 网络插件(Network Plugins) 方式被实现,因此可以选用满足自己需求的网络功能来使用。另外 Kubernetes 中的网络插件有以下两种形式:
- CNI plugins:以 appc/CNI 标准规范所实现的网络,详细可以阅读 CNI Specification 。
- Kubenet plugin:使用 CNI plugins 的 bridge 与 host-local 来实现基本的 cbr0。这通常被用在公有云服务上的 Kubernetes 集群网络。
- 如果想了解如何选择可以如阅读 Chris Love 的 Choosing a CNI Network Provider for Kubernetes 文章。
网络部署与设定(flannel或者calico任选其一)
如果是公有云不在一个vpc里建议用flannel,因为公有云是SDN,只有vxlan才能到达目标,每个node上的flannel.1充当了vtep身份.另外完成到集群可以使用后会发现只有pod所在的node能访问到它这台上面的clusterIP,是因为kubelet上报的节点的node public IP是取网卡的ip,公有云网卡ip都是内网ip,所以当flannel包要发到目标机器的flannel上的时候会发到目标机器的内网ip上,根本发不出去,解决方法可以私聊我我帮你解答
flannel
flannel 使用 vxlan 技术为各节点创建一个可以互通的 Pod 网络,使用的端口为 UDP 8472,需要开放该端口(如公有云 AWS 等)。
flannel 第一次启动时,从 etcd 获取 Pod 网段信息,为本节点分配一个未使用的 /24 段地址,然后创建 flannel.1(也可能是其它名称,如 flannel1 等) 接口。
这边镜像因为是quay.io域名仓库会拉取很慢,所有节点可以提前拉取下,否则就等。镜像名根据输出来,可能我博客部分使用镜像版本更新了
$ grep -Pom1 'image:\s+\K\S+' addons/flannel/kube-flannel.yml quay.io/coreos/flannel:v0.11.0-amd64 $ curl -s https://zhangguanzhang.github.io/bash/pull.sh | bash -s -- quay.io/coreos/flannel:v0.11.0-amd64
创建flannel,这边使用ds来创建( 这边建议先别创建,因为有个坑 )
sed -ri "s#\{\{ interface \}\}#${interface}#" addons/flannel/kube-flannel.yml kubectl apply -f addons/flannel/kube-flannel.yml ...
这边会发现一个问题,flannel的pod报错
E0304 07:49:51.171741 1 main.go:241] Failed to create SubnetManager: error retrieving pod spec for 'kube-system/kube-flannel-ds-amd64-4lpqx': Get https://10.96.0.1:443/api/v1/namespaces/kube-system/pods/kube-flannel-ds-amd64-4lpqx: dial tcp 10.96.0.1:443: connect: connection refused
手动发下请求看看
$ curl -v -sk https://10.96.0.1:443/healthz * About to connect() to 10.96.0.1 port 443 (#0) * Trying 10.96.0.1... * Connection refused * Failed connect to 10.96.0.1:443; Connection refused * Closing connection 0
查看下kubernetes的svc规则
$ ipvsadm -ln IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 10.96.0.1:443 rr -> 10.0.6.155:6443 Masq 1 0 0
手动请求下它的ep发现请求也拒绝了
$ curl -v -sk https://$VIP:6443/healthz * About to connect() to 10.96.0.1 port 443 (#0) * Trying 10.96.0.1... * Connection refused * Failed connect to 10.0.6.155:6443; Connection refused * Closing connection 0
手动直接请求backend绕过ha试试
$ curl -v -sk https://${MasterArray[@]:1:1}:6443/healthz * About to connect() to 10.0.6.166 port 6443 (#0) * Trying 10.0.6.166... * Connected to 10.0.6.166 (10.0.6.166) port 6443 (#0) * Initializing NSS with certpath: sql:/etc/pki/nssdb * skipping SSL peer certificate verification * NSS: client certificate not found (nickname not specified) * SSL connection using TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 * Server certificate: * subject: CN=kube-apiserver * start date: Mar 04 07:22:05 2019 GMT * expire date: Jul 20 07:22:05 2046 GMT * common name: kube-apiserver * issuer: CN=kubernetes-ca > GET /healthz HTTP/1.1 > User-Agent: curl/7.29.0 > Host: 10.0.6.166:6443 > Accept: */* > < HTTP/1.1 200 OK < Date: Mon, 04 Mar 2019 07:57:01 GMT < Content-Length: 2 < Content-Type: text/plain; charset=utf-8 < * Connection #0 to host 10.0.6.166 left intact ok
测下ha + ha port发现可以
$ curl -v -sk https://$VIP:8443/healthz ok
所以我们要修改默认kubernetes的svc的ep里的port(在apiserver起来后到这期间都可以修改ep)
kubectl patch ep kubernetes -p '{"subsets": [{"addresses":[{"ip":"'$VIP'"}],"ports":[{"name":"https","port":8443,"protocol":"TCP"}]}] }'
检查是否启动
$ kubectl -n kube-system get po -l k8s-app=flannel NAME READY STATUS RESTARTS AGE kube-flannel-ds-27jwl 2/2 Running 0 59s kube-flannel-ds-4fgv6 2/2 Running 0 59s kube-flannel-ds-mvrt7 2/2 Running 0 59s kube-flannel-ds-p2q9g 2/2 Running 0 59s kube-flannel-ds-zchsz 2/2 Running 0 59s
Calico
Calico 是一款纯 Layer 3 的网络,其好处是它整合了各种云原生平台(Docker、Mesos 与 OpenStack 等),且 Calico 不采用 vSwitch,而是在每个 Kubernetes 节点使用 vRouter 功能,并通过 Linux Kernel 既有的 L3 forwarding 功能,而当资料中心复杂度增加时,Calico 也可以利用 BGP route reflector 來达成。
- 想了解 Calico 与传统 overlay networks 的差异,可以阅读 Difficulties with traditional overlay networks 文章。
由于 Calico 提供了 Kubernetes resources YAML 文件来快速以容器方式部署网络插件至所有节点上,因此只需要在k8s-m1使用 kubeclt 执行下面指令來建立:
这边镜像因为是quay.io域名仓库会拉取很慢,所有节点可以提前拉取下,否则就等。镜像名根据输出来,可能我博客部分使用镜像版本更新了
$ grep -Po 'image:\s+\K\S+' addons/calico/v3.1/calico.yml quay.io/calico/typha:v0.7.4 quay.io/calico/node:v3.1.3 quay.io/calico/cni:v3.1.3
- 另外当节点超过 50 台,可以使用 Calico 的 Typha 模式来减少通过 Kubernetes datastore 造成 API Server 的负担。
包含上面三个镜像,拉取两个即可
curl -s https://zhangguanzhang.github.io/bash/pull.sh | bash -s -- quay.io/calico/node:v3.1.3 curl -s https://zhangguanzhang.github.io/bash/pull.sh | bash -s -- quay.io/calico/cni:v3.1.3
sed -ri "s#\{\{ interface \}\}#${interface}#" addons/calico/v3.1/calico.yml kubectl apply -f addons/calico/v3.1
$ kubectl -n kube-system get pod --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system calico-node-2hdqf 0/2 ContainerCreating 0 4m kube-system calico-node-456fh 0/2 ContainerCreating 0 4m kube-system calico-node-jh6vd 0/2 ContainerCreating 0 4m kube-system calico-node-sp6w9 0/2 ContainerCreating 0 4m kube-system calicoctl-6dfc585667-24s9h 0/1 Pending 0 4m kube-system kube-proxy-46hr5 1/1 Running 0 7m kube-system kube-proxy-l42sk 1/1 Running 0 7m kube-system kube-proxy-p2nbf 1/1 Running 0 7m kube-system kube-proxy-q6qn9 1/1 Running 0 7m
calico正常是下面状态
$ kubectl get pod --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system calico-node-2hdqf 2/2 Running 0 4m kube-system calico-node-456fh 2/2 Running 2 4m kube-system calico-node-jh6vd 2/2 Running 0 4m kube-system calico-node-sp6w9 2/2 Running 0 4m kube-system calicoctl-6dfc585667-24s9h 1/1 Running 0 4m kube-system kube-proxy-46hr5 1/1 Running 0 8m kube-system kube-proxy-l42sk 1/1 Running 0 8m kube-system kube-proxy-p2nbf 1/1 Running 0 8m kube-system kube-proxy-q6qn9 1/1 Running 0 8m
部署后通过下面查看状态即使正常
kubectl -n kube-system get po -l k8s-app=calico-node NAME READY STATUS RESTARTS AGE calico-node-bv7r9 2/2 Running 4 5m calico-node-cmh2w 2/2 Running 3 5m calico-node-klzrz 2/2 Running 4 5m calico-node-n4c9j 2/2 Running 4 5m
查找calicoctl的pod名字
kubectl -n kube-system get po -l k8s-app=calicoctl NAME READY STATUS RESTARTS AGE calicoctl-6b5bf7cb74-d9gv8 1/1 Running 0 5m
通过 kubectl exec calicoctl pod 执行命令来检查功能是否正常
$ kubectl -n kube-system exec calicoctl-6b5bf7cb74-d9gv8 -- calicoctl get profiles -o wide NAME LABELS kns.default map[] kns.kube-public map[] kns.kube-system map[] $ kubectl -n kube-system exec calicoctl-6b5bf7cb74-d9gv8 -- calicoctl get node -o wide NAME ASN IPV4 IPV6 k8s-m1 (unknown) 192.168.88.111/24 k8s-m2 (unknown) 192.168.88.112/24 k8s-m3 (unknown) 192.168.88.113/24 k8s-n1 (unknown) 10.244.3.1/24
完成后,通过检查节点是否不再是NotReady,以及 Pod 是否不再是Pending:
CoreDNS
1.11后CoreDNS 已取代 Kube DNS 作为集群服务发现元件,由于 Kubernetes 需要让 Pod 与 Pod 之间能夠互相通信,然而要能够通信需要知道彼此的 IP 才行,而这种做法通常是通过 Kubernetes API 来获取,但是 Pod IP 会因为生命周期变化而改变,因此这种做法无法弹性使用,且还会增加 API Server 负担,基于此问题 Kubernetes 提供了 DNS 服务来作为查询,让 Pod 能夠以 Service 名称作为域名来查询 IP 位址,因此使用者就再不需要关心实际 Pod IP,而 DNS 也会根据 Pod 变化更新资源记录(Record resources)。
CoreDNS 是由 CNCF 维护的开源 DNS 方案,该方案前身是 SkyDNS,其采用了 Caddy 的一部分来开发伺服器框架,使其能够建立一套快速灵活的 DNS,而 CoreDNS 每个功能都可以被当作成一個插件的中介软体,如 Log、Cache、Kubernetes 等功能,甚至能够将源记录存储在 Redis 、Etcd 中。
这里节点使用的是hostname,所以建议把hosts关系写到Coredns的解析里
写成下面这种格式也就是使用 Coredns的hosts插件
data: Corefile: | .:53 { errors health kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure upstream fallthrough in-addr.arpa ip6.arpa } hosts { 192.168.88.111 k8s-m1 192.168.88.112 k8s-m2 192.168.88.113 k8s-m3 192.168.88.114 k8s-n1 } prometheus :9153 proxy . /etc/resolv.conf cache 30 reload loadbalance }
如果偷懒可以我命令修改文件注入(顺序乱没关系,强迫症的话自己去改)
sed -i '57r '<(echo ' hosts {';for NODE in "${!MasterArray[@]}";do echo " ${MasterArray[$NODE]} $NODE"; done;for NODE in "${!NodeArray[@]}";do echo " ${NodeArray[$NODE]} $NODE";done;echo ' }';) addons/coredns/coredns.yml
如果后期增加类似解析记录的话可以改cm后(注意cm是yaml格式写的,所以不要使用tab必须用空格)用kill信号让coredns去reload,因为主进程是前台也就是PID为1,找到对应的pod执行即可,也可以利用deploy的更新机制去伪更新实现重启
kubectl exec coredns-xxxxxx -- kill -SIGUSR1 1
在 k8s-m1
通过 kubeclt 执行下面命令來创建,并检查是否部署成功:
kubectl apply -f addons/coredns/coredns.yml # 下面是输出 serviceaccount/coredns created clusterrole.rbac.authorization.k8s.io/system:coredns created clusterrolebinding.rbac.authorization.k8s.io/system:coredns created configmap/coredns created deployment.extensions/coredns created service/kube-dns created $ kubectl -n kube-system get po -l k8s-app=kube-dns NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-6975654877-jjqkg 1/1 Running 0 1m kube-system coredns-6975654877-ztqjh 1/1 Running 0 1m
完成后,通过检查节点是否不再是NotReady,以及 Pod 是否不再是Pending:
$ kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-m1 Ready master 17m v1.11.1 k8s-m2 Ready master 16m v1.11.1 k8s-m3 Ready master 16m v1.11.1 k8s-n1 Ready node 6m v1.11.1
这里似乎有个官方bug https://github.com/coredns/coredns/issues/2289
coredns正常否看脸,可以下面创建pod来测试
先创建一个dnstool的pod
$ cat<<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: busybox namespace: default spec: containers: - name: busybox image: busybox:1.28 command: - sleep - "3600" imagePullPolicy: IfNotPresent restartPolicy: Always EOF
nslookup下看看能返回地址不
$ kubectl exec -ti busybox -- nslookup kubernetes Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: kubernetes Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local
下面则是遇到了,这个现象是官方bug,如果想看log的话在 Corefile
加一行log则开启log打印查看,上面的issue里官方目前也无解
Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local nslookup: can't resolve 'kubernetes' command terminated with exit code 1
KubeDNS(如果遇到上面的CoreDNS的bug的话使用它)
Kube DNS 是Kubernetes集群内部Pod之间互相沟通的重要Addon,它允许Pod可以通过Domain Name方式来连接Service,其主要由Kube DNS与Sky DNS组合而成,通过Kube DNS监听Service与Endpoint变化,来提供给Sky DNS资讯,已更新解析位址。
如果CoreDNS工作不正常,先删掉它,删掉后确保coredns的pod和svc不存在
$ kubectl delete -f addons/coredns/coredns.yml $ kubectl -n kube-system get pod,svc -l k8s-app=kube-dns No resources found.
创建KubeDNS
$ kubectl apply -f addons/Kubedns/kubedns.yml serviceaccount/kube-dns created service/kube-dns created deployment.extensions/kube-dns create
查看pod状态
$ kubectl -n kube-system get pod,svc -l k8s-app=kube-dns NAME READY STATUS RESTARTS AGE pod/kube-dns-59c677cb95-pxcbc 3/3 Running 0 3m pod/kube-dns-59c677cb95-wlprb 3/3 Running 0 3m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 3m
检查集群dns正常否
$ kubectl exec -ti busybox -- nslookup kubernetes Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: kubernetes Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local
等待官方修复的话可以后期先创建出coredns的deploy,svc会负载到coredns之后再删掉kubedns的副本控制器和pod
Metrics Server(1.8+的k8s)
Metrics Server 是实现了 Metrics API 的元件,其目标是取代 Heapster 作位 Pod 与 Node 提供资源的 Usage metrics,该元件会从每个 Kubernetes 节点上的 Kubelet 所公开的 Summary API 中收集 Metrics
- Horizontal Pod Autoscaler(HPA)控制器用于实现基于CPU使用率进行自动Pod伸缩的功能。
- HPA控制器基于Master的kube-controller-manager服务启动参数–horizontal-pod-autoscaler-sync-period定义是时长(默认30秒),周期性监控目标Pod的CPU使用率,并在满足条件时对ReplicationController或Deployment中的Pod副本数进行调整,以符合用户定义的平均Pod CPU使用率。
- 在新版本的kubernetes中 Pod CPU使用率不在来源于heapster,而是来自于metrics-server
- 官网原话是 The –horizontal-pod-autoscaler-use-rest-clients is true or unset. Setting this to false switches to Heapster-based autoscaling, which is deprecated.
- yml 文件来自于github https://github.com/kubernetes-incubator/metrics-server/tree/master/deploy/1.8+
- /etc/kubernetes/pki/front-proxy-ca.pem 文件来自于部署kubernetes集群
- 需要对yml文件进行修改才可使用 改动自行见文件
- 配置不够不要硬上,我笔记本配置不够结果apiserver不正常工作了
设置apiserver相关参数 --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem \ --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.pem \ --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client-key.pem \ --requestheader-allowed-names=aggregator \ --requestheader-group-headers=X-Remote-Group \ --requestheader-extra-headers-prefix=X-Remote-Extra- \ --requestheader-username-headers=X-Remote-User \
这里为啥我说master最好跑pod,是因为用户定义资源,也就是 kind:xxxx
和 apiVersion:xxxx
现在都是用自带的,后续开发和接触到CRD的时候会创建APIService,如果APIService里选中了svc,那么请求kube-apiserver的web路由的时候kube-apiserver会把请求转发到你选中的svc上,这里可以看官方文件里一部分内容。当我们使用 kubectl top node
的时候实际是请求kube-apiserver的url路径 /apis/metrics.k8s.io/v1beta1/nodes
,由于创建了metrics-server的APIService,请求会被转发到svc的pod,pod工作流程是获取node列表,然后去请求node上的kubelet的metrics端口获取metrics信息收集起来,信息包括了node的基本cpu和内存以及上面跑的pod的cpu和内存。这之前流量是kube-apiserver到pod上中间经过svc的ip,如果没有kube-proxy和网络组件就无法通信
apiVersion: apiregistration.k8s.io/v1beta1 kind: APIService metadata: name: v1beta1.metrics.k8s.io spec: service: name: metrics-server namespace: kube-system group: metrics.k8s.io version: v1beta1 insecureSkipTLSVerify: true groupPriorityMinimum: 100 versionPriority: 10
这里我们使用tls证书,证书前面的 Front Proxy Certificate
已经生成了
首先在 k8s-m1
测试一下 kubectl top 指令:
$ kubectl top node Error from server (NotFound): the server could not find the requested resource (get services http:heapster:)
发现 top 命令无法取得 Metrics,这表示 Kubernetes 集群没有安装 Heapster 或着 Metrics Server 来提供 Metrics API 给 top 指令取得资源使用量。
由于上述问题,我們要在 k8s-m1
部署 Metrics Server 元件来解決:
镜像v0.2.1的话直接使用下面的(v1.12+版本建议使用后面的yml)
kubectl create -f addons/metric-server/metrics-server.yml
v0.3.1镜像的yml相对于之前变化了如下:
-
多了一个 ClusterRole 并且选项有部分变化,可以 自行比对
-
kubelet的metrics端口为10250是授权端口,10255是readonly的metrics端口,这里使用10250端口
喜欢追求新镜像(个人建议1.12+k8s)可以使用下面的yml
kubectl create -f addons/metric-server/metrics-server-1.12+.yml
查看pod状态
kubectl -n kube-system get po -l k8s-app=metrics-server NAME READY STATUS RESTARTS AGE pod/metrics-server-86bd9d7667-5hbn6 1/1 Running 0 1m
完成后,等待一段时间(约 30s - 1m)收集 Metrics,再次执行 kubectl top 指令查看:
$ kubectl get --raw /apis/metrics.k8s.io/v1beta1 {"kind":"APIResourceList","apiVersion":"v1","groupVersion":"metrics.k8s.io/v1beta1","resources":[{"name":"nodes","singularName":"","namespaced":false,"kind":"NodeMetrics","verbs":["get","list"]},{"name":"pods","singularName":"","namespaced":true,"kind":"PodMetrics","verbs":["get","list"]}]} $ kubectl get apiservice|grep metrics v1beta1.metrics.k8s.io kube-system/metrics-server True 2m $ kubectl top node NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% k8s-m1 113m 2% 1080Mi 14% k8s-m2 133m 3% 1086Mi 14% k8s-m3 100m 2% 1029Mi 13% k8s-n1 146m 3% 403Mi 5%
而这时若有使用 HPA 的話,就能够正确抓到 Pod 的 CPU 与 Memory 使用量了。
若想让 HPA 使用 Prometheus 的 Metrics 的话,可以阅读 Custom Metrics Server 来了解。
测试是否可以建立 Pod(到此步集群即可使用)
$ kubectl run nginx --image nginx --restart=Never --port 80 $ kubectl get po NAME READY STATUS RESTARTS AGE nginx 1/1 Running 0 22s
Kubernets Extra Addons部署
本节说明如何部署一些官方常用的额外 Addons,如 Dashboard、Ingress Controller、External DNS 与 Prometheus等等。
下面基本都是kubectl命令,apply文件后会有输出耐心等待拉取docker镜像,命令输出不要粘贴到终端上(虽然没影响)
所有 Addons 部署文件已放至k8s-manual-files中,因此在k8s-m1进入该目录,按照以下顺序建立:
$ cd ~/k8s-manual-files
Dashboard
Dashboard 是Kubernetes社区官方开发的仪表板,有了仪表板后管理者就能够通过Web-based方式来管理Kubernetes集群,除了提升管理方便,也让资源视觉化,让人更直觉看见系统资讯的呈现结果。
在 k8s-m1
通过kubectl来建立kubernetes dashboard即可:
$ kubectl apply -f ExtraAddons/dashboard $ kubectl -n kube-system get po,svc -l k8s-app=kubernetes-dashboard NAME READY STATUS RESTARTS AGE kubernetes-dashboard-7d5dcdb6d9-j492l 1/1 Running 0 12s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes-dashboard ClusterIP 10.111.22.111 <none> 443/TCP 12s
这边会额外建立一个名称为 anonymous-dashboard-proxy
的 Cluster Role(Binding) 来让 system:anonymous
这个匿名使用者能够通过 API Server 来 proxy 到 Kubernetes Dashboard,而这个 RBAC 规则仅能够存取services/proxy资源,以及https:kubernetes-dashboard:资源名称同时在 1.7 版本以后的 Dashboard 将不再提供所有权限,因此需要建立一个 service account 来绑定 cluster-admin role(这系列已经写在 dashboard/anonymous-proxy-rbac.yml
里)
完成后,就可以通过浏览器存取Dashboard https://{YOUR_VIP}:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/ 。
:
$ kubectl -n kube-system describe secrets | sed -rn '/\sdashboard-token-/,/^token/{/^token/s#\S+\s+##p}' eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtdG9rZW4tdzVocmgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiYWJmMTFjYzMtZjRlYi0xMWU3LTgzYWUtMDgwMDI3NjdkOWI5Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZCJ9.Xuyq34ci7Mk8bI97o4IldDyKySOOqRXRsxVWIJkPNiVUxKT4wpQZtikNJe2mfUBBD-JvoXTzwqyeSSTsAy2CiKQhekW8QgPLYelkBPBibySjBhJpiCD38J1u7yru4P0Pww2ZQJDjIxY4vqT46ywBklReGVqY3ogtUQg-eXueBmz-o7lJYMjw8L14692OJuhBjzTRSaKW8U2MPluBVnD7M2SOekDff7KpSxgOwXHsLVQoMrVNbspUCvtIiEI1EiXkyCNRGwfnd2my3uzUABIHFhm0_RZSmGwExPbxflr8Fc6bxmuz-_jSdOtUidYkFIzvEWw2vRovPgs3MXTv59RwUw
- 复制
token
,然后贴到Kubernetes dashboard。注意这边一般来说要针对不同User开启特定存取权限。
Ingress Controller
Ingress 是 Kubernetes 中的一个抽象资源,其功能是通过 Web Server 的 Virtual Host 概念以域名(Domain Name)方式转发到內部 Service,这避免了使用 Service 中的 NodePort 与 LoadBalancer 类型所带來的限制(如 Port 数量上限),而实现 Ingress 功能则是通过 Ingress Controller 来达成,它会负责监听 Kubernetes API 中的 Ingress 与 Service 资源物件,并在发生资源变化时,根据资源预期的结果来设置 Web Server。另外 Ingress Controller 有许多实现可以选择:
- Ingress NGINX : Kubernetes 官方维护的方案,也是本次安装使用的 Controller。
- F5 BIG-IP Controller : F5 所开发的 Controller,它能够让管理员通过 CLI 或 API 让 Kubernetes 与 OpenShift 管理 F5 BIG-IP 设备。
- Ingress Kong : 著名的开源 API Gateway 方案所维护的 Kubernetes Ingress Controller。
- Traefik : 是一套开源的 HTTP 反向代理与负载均衡器,而它也支援了 Ingress。
- Voyager : 一套以 HAProxy 为底的 Ingress Controller。
- 而 Ingress Controller 的实现不只上面这些方案,还有很多可以在网络上找到这里不一一列出来了
首先在k8s-m1执行下列命令来建立 Ingress Controller,并检查是否部署正常:
sed -ri 's#\{\{ INGRESS_VIP \}\}#'"${INGRESS_VIP}"'#' ExtraAddons/ingress-controller/ingress-controller-svc.yml kubectl create ns ingress-nginx kubectl apply -f ExtraAddons/ingress-controller/ kubectl -n ingress-nginx get po,svc # 下面是输出 NAME READY STATUS RESTARTS AGE pod/default-http-backend-846b65fb5f-l5hrc 1/1 Running 0 2m pod/nginx-ingress-controller-5db8d65fb-z2lf9 1/1 Running 0 2m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/default-http-backend ClusterIP 10.99.105.112 <none> 80/TCP 2m service/ingress-nginx LoadBalancer 10.106.18.106 192.168.88.109 80:31197/TCP 2m
完成后使用curl发起http请求访问http://{your ingress vip}}:80来查看是否能连接,若可以会有以下结果。
$ curl ${INGRESS_VIP}:80 default backend - 404
确认上面步骤都沒问题后,就可以通过 kubeclt 建立简单 NGINX 来测试功能:
kubectl apply -f apps/nginx/ # 下面是输出 deployment.extensions/nginx created ingress.extensions/nginx-ingress created service/nginx created $ kubectl get po,svc,ing NAME READY STATUS RESTARTS AGE pod/nginx-966857787-78kth 1/1 Running 0 32s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d service/nginx ClusterIP 10.104.180.119 <none> 80/TCP 32s NAME HOSTS ADDRESS PORTS AGE ingress.extensions/nginx-ingress nginx.k8s.local 192.168.88.109 80 33s
- P.S. Ingress 规则也支持不同 Path 的服务转发,可以参考上面提供的官方文件来设置。
完成后通过 curl 命令来测试功能是否正常:
$ curl ${INGRESS_VIP} -H 'Host: nginx.k8s.local' <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ... # 测试其他 domain name 是否会回返回 404 $ curl ${INGRESS_VIP} -H 'Host: nginx1.k8s.local' default backend - 404
虽然 Ingress 能够让我們通过域名方式访问 Kubernetes 內部服务,但是若域名无法被用户解析的话,将会显示 default backend - 404
结果,而这经常发送在內部自建环境上,虽然可以通过修改主机 /etc/hosts
来解决,但不能弹性扩展,因此下节将说明如何建立一个 External DNS 与 DNS 服务器来提供自动解析 Ingress 域名。
External DNS(非公有云下且内网没DNS下很实用)
用户部署了ingress controller后创建的ing资源对象的hosts需要访问的时候访问者一般是设置hosts文件来访问的
例如创建了上面的nginx.k8s.local的ing,用户访问它需要本机设置一条hosts 192.168.88.109 nginx.k8s.local
在浏览打开 nginx.k8s.local
才能访问
这样如果办公室或者内网下ing创建多了很麻烦,如果办公室或者内网自建了dns可以加解析记录就不用每个人去设置hosts了
如果没有,可以部署它然后所有机器的dns的ip指向它即可访问到ing
本部分说明如何通过 CoreDNS 自建一套DNS 服务,并利用 Kubernetes ExternalDNS 同步Kubernetes 的Ingress 与Service API object 中的域名(Domain Name)来产生资源纪录(Record Resources),让使用者能够通过自建DNS 服务来导向到Kubernetes 集群里的服务
External DNS 是 Kubernetes 社区的孵化方案,被用于定期同步 Kubernetes Service 与 Ingress 资源,并依据资源內容来自动设定公有云 DNS 服务的资源记录(Record resources)。而由于部署不是公有云环境,因此需要通过 CoreDNS 提供一個內部 DNS 服务器,再由 ExternalDNS 与这个 CoreDNS 做串接。
- CoreDNS:用来提供使用者的 DNS 解析以处理服务导向,并利用 Etcd 插件来存储与查询 DNS 资源记录(Record resources)。CoreDNS 是由 CNCF 维护的开源 DNS 方案,该方案前身是 SkyDNS,其采用了 Caddy 的一部分来开发私有服务器框架,使其能够构建一套快速灵活的 DNS,而 CoreDNS 每个功能都可以被当作成一个插件的中介软体,如 Log、Cache 等功能,甚至能够将资源存储存至 Redis、Etcd 中。另外 CoreDNS 目前也被 Kubernetes 作为一个內部服务查询的核心元件,并慢慢取代 KubeDNS 来提供使用。
- 由于市面上大多以 Bind9 作为 DNS,但是 Bind9 并不支持插件与 REST API 功能,虽然效率高又稳定,但是在一些场景並不灵活。
- Etcd:用来储存 CoreDNS 资源纪录,并提供给整合的元件查询与储存使用。 Etcd 是一套分散式键值(Key/Value)储存系统,其功能类似ZooKeeper,而Etcd 在一致性演算法采用了Raft 来处理多节点高可靠性问题,Etcd 好处是支援了REST API、JSON 格式、SSL 与高效能等,而目前Etcd 被应用在Kubernetes 与Cloud Foundry 等方案中。
-
ExternalDNS:用于定期同步Kubernetes Service 与Ingress 资源,并根据Kubernetes 资源内容产生DNS 资源纪录来设定CoreDNS,架构中采用Etcd 作为两者沟通中介,一旦有资源纪录产生就储存至Etcd 中,以提供给CoreDNS作为资源纪录来确保服务辨识导向。 ExternalDNS 是 Kubernetes 社区的专案,目前被用于同步 Kubernetes 自动设定公有云 DNS 服务的资源纪录。
-
Ingress Controller:提供 Kubernetes Service 能够以 Domain Name 方式提供外部的存取。 Ingress Controller 会监听Kubernetes API Server 的Ingress 与Service 抽象资源,并依据对应资讯产生配置文件来设置到一个以NGINX 为引擎的后端,当使用者存取对应服务时,会通过NGINX 后端进入,这时会依据设定档的Domain Name 来转送给对应Kubernetes Service。
- Kubernetes API Server:ExternalDNS 会定期抓取来自 API Server 的 Ingress 与 Service 抽象资源,并根据资源內容产生资源记录。
首先当使用者建立了一个Kubernetes Service 或Ingress(实作以同步Ingress 为主)时,会通过与API Server 与Kubernetes 集群沟通,一旦Service 或Ingress 建立完成,并正确分配Service external IP 或是Ingress address 后, ExternalDNS
会在同步期间抓取所有Namespace(或指定)中的Service 与Ingress 资源,并从Service 的 metadata.annotations
取出 external-dns.alpha.kubernetes.io/hostname
键的值,以及从Ingress 中的 spec.rules
取出host 值来产生DNS 资源纪录(如A record),当完成产生资源纪录后,再通过Etcd 储存该纪录来让CoreDNS 在收到查询请求时,能够依据Etcd 的纪录来辨识导向
拆解不同流程步驟如下:
- 使用者建立一个带有 annotations 的 Service 或是 Ingress。
apiVersion: v1 kind: Service metadata: name: nginx annotations: external-dns.alpha.kubernetes.io/hostname: nginx.k8s.local # 将被自动注册 domain name. spec: type: NodePort selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: nginx-ingress spec: rules: - host: nginx.k8s.local # 将被自动注册的 domain name. http: paths: - backend: serviceName: nginx servicePort: 80
- 该示例中,若使用 Ingress 的话则不需要在 Service 塞入
external-dns.alpha.kubernetes.io/hostname
,且不需要使用 NodePort 与 LoadBalancer。
-
ExternalDNS 接收到 Service 与 Ingress 抽象资源,取出将被用来注册 Domain Name 的资讯,并依据上述信息产生 DNS 资源纪录(Record resources),然后储存到 Etcd。
-
当使用者访问
nginx.k8s.local
时,将对 CoreDNS 提供的 DNS 服务器发送查询请求,这时 CoreDNS 会到 Etcd 找寻资源纪录来进行辨识重导向功能,若找到资源纪录回复解析结果给使用者。 -
这时使用者正确地被导向地址。其中若使用 Service 则要额外输入对应 Port,用 Ingress 则能够透过 DN 存取到服务,这是由于 Ingress controller 提供了一个 NGINX Proxy 后端来转至对应的内部服务。
首先在 k8s-m1
执行下面命令来建立 CoreDNS Server,并检查是否部署正常:
sed -ri 's#\{\{ INGRESS_VIP \}\}#'"${INGRESS_VIP}"'#' ExtraAddons/external-dns/coredns/coredns-svc-tcp.yml sed -ri 's#\{\{ INGRESS_VIP \}\}#'"${INGRESS_VIP}"'#' ExtraAddons/external-dns/coredns/coredns-svc-udp.yml kubectl create ns external-dns kubectl create -f ExtraAddons/external-dns/coredns/ kubectl -n external-dns get po,svc # 下面是输出 NAME READY STATUS RESTARTS AGE pod/coredns-54bcfcbd5b-5grb5 1/1 Running 0 2m pod/coredns-etcd-6c9c68fd76-n8rhj 1/1 Running 0 2m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/coredns-etcd ClusterIP 10.110.186.83 <none> 2379/TCP,2380/TCP 2m service/coredns-tcp LoadBalancer 10.109.105.166 192.168.88.109 53:32169/TCP,9153:32150/TCP 2m service/coredns-udp LoadBalancer 10.110.242.185 192.168.88.109 53:31210/UDP
- 这边域名为k8s.local,可以修改文件中的coredns-cm.yml來改变。
完成后,通过 dig 工具来检查 DNS 是否正常:
$ dig @${INGRESS_VIP} SOA nginx.k8s.local +noall +answer +time=2 +tries=1 ... ; (1 server found) ;; global options: +cmd k8s.local. 300 IN SOA ns.dns.k8s.local. hostmaster.k8s.local. 1531299150 7200 1800 86400 30
接着部署 ExternalDNS 来与 CoreDNS 同步资源记录:
kubectl apply -f ExtraAddons/external-dns/external-dns/ kubectl -n external-dns get po -l k8s-app=external-dns NAME READY STATUS RESTARTS AGE external-dns-86f67f6df8-ljnhj 1/1 Running 0 1m
完成后,通过 dig 与 nslookup 工具检查前面测试 Ingress 的 nginx 服务:
$ dig @${INGRESS_VIP} A nginx.k8s.local +noall +answer +time=2 +tries=1 ... ; (1 server found) ;; global options: +cmd nginx.k8s.local. 300 IN A 192.168.88.109 $ nslookup nginx.k8s.local Server: 192.168.88.109 Address: 192.168.88.109#53 ** server can't find nginx.k8s.local: NXDOMAIN
这时会无法通过 nslookup 解析域名,这是因为测试机器并没有使用这个 DNS 服务器,可以通过修改 /etc/resolv.conf
来加入(不同 OS 有差异,不过都可以设置)。
设置后再次通过 nslookup 检查,会发现可以解析了,这时也就能通过 curl 来测试结果:
$ nslookup nginx.k8s.local Server: 192.168.88.109 Address: 192.168.88.109#53 Name: nginx.k8s.local Address: 192.168.88.109 $ nslookup www.baidu.com Server: 192.168.88.110 Address: 192.168.88.110#53 Non-authoritative answer: www.baidu.com canonical name = www.a.shifen.com. Name: www.a.shifen.com Address: 61.135.169.125 Name: www.a.shifen.com Address: 61.135.169.121 $ curl nginx.k8s.local <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...
Prometheus Operator
由于 Heapster 将要被移除,因此这里选用 Prometheus 作为第三方的集群监控方案。而本次安装采用 CoreOS 开发的 Prometheus Operator 用来管理在 Kubernetes 上的 Prometheus 集群资源,目标就是简化部署与维护 Prometheus 上的事情,其架构如下所示:
Prometheus Operator相关CRD介绍(建议先进行后面的部署再回过头看这里)
架构中的每一个部分都是运行在 Kubernetes 集群上的资源,这些资源分別负责不同作用与意义:
- Operator : Operator 是整个系统的主要控制器,会以 Deployment 方式运行在 Kubernetes 集群上,并根据自定义的资源(Custom Resource Definition,CRDs)来负责管理与部署Prometheus Server 。而 Operator 会透过监听这些自定义资源的事件变化来做对应处理。
-
Prometheus Server : 例如下面的,由 Operator 依据一个自定义资源
kind: Prometheus
类型中,所描述的内容而部署的 Prometheus Server 集群,可以将这个自定义资源看作是一种特别用来管理Prometheus Server的StatefulSets资源。- 可以通过文件查看
$ cat ExtraAddons/prometheus/prometheus/prometheus-main.yml apiVersion: monitoring.coreos.com/v1 kind: Prometheus metadata: labels: prometheus: k8s name: k8s namespace: monitoring spec: baseImage: quay.io/prometheus/prometheus replicas: 2 version: v2.3.1 resources: requests: memory: 400Mi ruleSelector: ...
- 可以通过文件查看
-
ServiceMonitor : 一个Kubernetes自定义资源(和
kind: Prometheus
一样是CRD),该资源描述了Prometheus Server的Target列表,Operator 会监听这个资源的变化来动态的更新Prometheus Server的Scrape targets。而该资源主要通过Selector来依据 Labels 选取对应的Service Endpoint,并让 Prometheus Server 通过 Service 进行拉取(拉)指标资料(也就是metrics信息),metrics信息要在http的url输出符合metrics格式的信息,ServiceMonitor也可以定义目标的metrics的url.- 查看kubelet的servicemonitor如下
cat ExtraAddons/prometheus/servicemonitor/kubelet-sm.yml apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: k8s-app: kubelet name: kubelet namespace: monitoring spec: endpoints: - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token honorLabels: true interval: 30s port: https-metrics scheme: https tlsConfig: insecureSkipVerify: true - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token honorLabels: true interval: 30s path: /metrics/cadvisor port: https-metrics scheme: https tlsConfig: insecureSkipVerify: true jobLabel: k8s-app namespaceSelector: matchNames: - kube-system selector: matchLabels: k8s-app: kubelet
- 查看kubelet的servicemonitor如下
- 而operator创建了一个svc,改svc的ep为各个node节点
$ kubectl -n kube-system get svc -l k8s-app=kubelet NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubelet ClusterIP None <none> 10250/TCP 35h $ kubectl -n kube-system describe svc kubelet Name: kubelet Namespace: kube-system Labels: k8s-app=kubelet Annotations: <none> Selector: <none> Type: ClusterIP IP: None Port: https-metrics 10250/TCP TargetPort: 10250/TCP Endpoints: 192.168.88.111:10250,192.168.88.112:10250,192.168.88.113:10250 + 1 more... Session Affinity: None Events: <none>
- 10250是kubelet的cadvisor端口,可以看到上面的ServiceMonitor注明了拉取metrics的port和metrics的web的path以及使用tls
$ curl -sk https://127.0.0.1:10250/metrics/cadvisor Unauthorized
- 总结: ServiceMonitor就是要收集一个svc对应的所有pod的metrics信息的话则创建一个ServiceMonitor会让prometheus去拉取metrics
- Alertmanager :Prometheus Operator 不只是提供 Prometheus Server 管理与部署,也包含了 AlertManager,并且一样通过一个
kind: Alertmanager
自定义资源来描述信息,再由 Operator 依据描述内容部署 Alertmanager 集群。apiVersion: monitoring.coreos.com/v1 kind: Alertmanager metadata: name: main labels: alertmanager: main spec: replicas: 3 ...
参考文档: https://github.com/coreos/prometheus-operator/tree/release-0.26/contrib/kube-prometheus/manifests
部署
在 k8s-m1
通过kubectl来建立Prometheus 需要的元件:
kubectl apply -f ExtraAddons/prometheus/ kubectl apply -f ExtraAddons/prometheus/operator/ # 这边要等 operator 起來并建立好 CRDs 才能进行 kubectl apply -f ExtraAddons/prometheus/alertmanater/ kubectl apply -f ExtraAddons/prometheus/node-exporter/ kubectl apply -f ExtraAddons/prometheus/kube-state-metrics/ kubectl apply -f ExtraAddons/prometheus/grafana/ kubectl apply -f ExtraAddons/prometheus/kube-service-discovery/
这里建议先执行后面的补全ep信息部分的操作后再回来
kubectl apply -f ExtraAddons/prometheus/prometheus/ kubectl apply -f ExtraAddons/prometheus/servicemonitor/
完成后,通过 kubectl 检查服务是否正常运行:
$ kubectl -n monitoring get po,svc,ing NAME READY STATUS RESTARTS AGE pod/alertmanager-main-0 1/2 Running 0 1m pod/grafana-6d495c46d5-jpf6r 1/1 Running 0 43s pod/kube-state-metrics-b84cfb86-4b8qg 4/4 Running 0 37s pod/node-exporter-2f4lh 2/2 Running 0 59s pod/node-exporter-7cz5s 2/2 Running 0 59s pod/node-exporter-djdtk 2/2 Running 0 59s pod/node-exporter-kfpzt 2/2 Running 0 59s pod/node-exporter-qp2jf 2/2 Running 0 59s pod/prometheus-k8s-0 3/3 Running 0 28s pod/prometheus-k8s-1 3/3 Running 0 15s pod/prometheus-operator-9ffd6bdd9-rvqsz 1/1 Running 0 1m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/alertmanager-main ClusterIP 10.110.188.2 <none> 9093/TCP 1m service/alertmanager-operated ClusterIP None <none> 9093/TCP,6783/TCP 1m service/grafana ClusterIP 10.104.147.154 <none> 3000/TCP 43s service/kube-state-metrics ClusterIP None <none> 8443/TCP,9443/TCP 51s service/node-exporter ClusterIP None <none> 9100/TCP 1m service/prometheus-k8s ClusterIP 10.96.78.58 <none> 9090/TCP 28s service/prometheus-operated ClusterIP None <none> 9090/TCP 33s service/prometheus-operator ClusterIP 10.99.251.16 <none> 8080/TCP NAME HOSTS ADDRESS PORTS AGE ingress.extensions/grafana-ing grafana.monitoring.k8s.local 192.168.88.109 80 45s ingress.extensions/prometheus-ing prometheus.monitoring.k8s.local 192.168.88.109 80 34s
补全ep信息
prometheus会收集存储metrics,grafana来视图输出
k8s的管理组件都会有metrics信息输出的端口,例如访问 curl localhost:10252/metrics
可以看到controller的metrics信息
上面我们知道如果prometheus监控管理组件的metrics需要(创建)ServiceMonitor选中一个svc,operator会查询到svc的ep(不需要svc有clusterip),根据配置的path从目标服务http的metrics页面拉取metrics信息
查看 ServiceMonitor
的yaml目录
cd ExtraAddons/prometheus ll servicemonitor/kube-* ...下面是输出 -rw-r--r-- 1 root root 567 Dec 23 20:20 servicemonitor/kube-apiserver-sm.yml -rw-r--r-- 1 root root 374 Dec 23 20:20 servicemonitor/kube-controller-manager-sm.yml -rw-r--r-- 1 root root 347 Dec 23 20:20 servicemonitor/kube-scheduler-sm.yml
apiserver这个默认会在集群创建svc和生成ep(在default这个namespace里),无需理会
查看上面俩servicemonitor的yaml发现是选的kube-system(namespacs下)的标签为 k8s-app: kube-scheduler
和 k8s-app: kube-controller-manager
的svc
在下面目录找到官方单独创建了他俩的svc
$ ll kube-service-discovery/ total 8 -rw-r--r-- 1 root root 343 Dec 23 20:20 kube-controller-manager-svc.yml -rw-r--r-- 1 root root 316 Dec 23 20:20 kube-scheduler-svc.yml
但是二进制跑的话由于不是pod不具有label属性,不会被上面俩svc选中,也就是俩svc的ep没创建需要我们手动创建
这种在需要把集群外的服务映射进来的时候就是创建一个同名的svc和ep,上面俩svc名字为
$ grep -P '^\s+name:' kube-service-discovery/kube-* kube-service-discovery/kube-controller-manager-svc.yml: name: kube-controller-manager-prometheus-discovery kube-service-discovery/kube-scheduler-svc.yml: name: kube-scheduler-prometheus-discovery
实际上默认集群带了组件同名的ep,但是operator为了不干预原有的,所以才用的上面的svc名字,同理operator也创建出了同名的ep,但是这俩ep是空的,需要我们手动修改
$ kubectl -n kube-system get ep NAME ENDPOINTS AGE kube-controller-manager <none> 5m kube-controller-manager-prometheus-discovery <none> 5m kube-dns 10.244.1.2:53,10.244.8.2:53,10.244.1.2:53 + 1 more... 5m kube-scheduler <none> 5m kube-scheduler-prometheus-discovery <none> 5m kubelet 192.168.88.111:10255,192.168.88.112:10255,192.168.88.113:10255 + 1 more... 5m metrics-server 10.244.7.3:443 5m
可以看到为none,这里需要我们手动修改,下面为controller的信息注入,port是10252,servicemonitor里port名字为 https-metrics
,所以不要修改下面的port的name
kubectl -n kube-system patch ep kube-controller-manager-prometheus-discovery -p "$(cat<<EOF {"subsets": [ { "addresses": [ $(for i in ${!MasterArray[@]};do echo '{"ip": "'${MasterArray[$i]}'"}'; done | paste -d',' -s -) ], "ports": [ { "name": "http-metrics", "port": 10252, "protocol": "TCP" } ] } ] } EOF )"
同理注入kube-scheduler-prometheus-discovery的ep
kubectl -n kube-system patch ep kube-scheduler-prometheus-discovery -p "$(cat<<EOF {"subsets": [ { "addresses": [ $(for i in ${!MasterArray[@]};do echo '{"ip": "'${MasterArray[$i]}'"}'; done | paste -d',' -s -) ], "ports": [ { "name": "http-metrics", "port": 10251, "protocol": "TCP" } ] } ] } EOF )"
确认没问题后,通过浏览器查看 prometheus.monitoring.k8s.local 与 grafana.monitoring.k8s.local 是否正常,若沒问题就可以看到下图结果。
Weave Scope
另外这边也推荐用 Weave Scope 来监控容器的网络 Flow 拓扑图。
yml来源于官方 https://www.weave.works/docs/scope/latest/installing/#k8s 这边增加了一个ingress方便访问
注入信息并且部署
sed -ri '/^\s+"date"/s#"[^"]+"\s*$#"'"$(date "+%a %b %d %Y %T GMT%z (%Z)")"'"#;/^\s+"url"/s#=[^"]+#='$(kubectl version | base64 | tr -d '\n')'#' ExtraAddons/WeaveScope/scope.yml kubectl apply -f ExtraAddons/WeaveScope/scope.yml
查看状态
[root@k8s-m1 weave-scope]# kubectl -n weave get pod,svc,ing NAME READY STATUS RESTARTS AGE pod/weave-scope-agent-2srz9 1/1 Running 0 111s pod/weave-scope-agent-9t847 1/1 Running 0 111s pod/weave-scope-agent-dmjkp 1/1 Running 0 111s pod/weave-scope-agent-j26rb 1/1 Running 0 111s pod/weave-scope-app-5989b9c6d7-t28fs 1/1 Running 0 112s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/weave-scope-app ClusterIP 10.103.161.235 <none> 80/TCP 112s NAME HOSTS ADDRESS PORTS AGE ingress.extensions/weave-ingress scope.weave.k8s.local 192.168.88.109 80 111s
完成后访问 scope.weave.k8s.local
Helm Tiller Server
Helm 是Kubernetes Chart的管理工具,Kubernetes Chart是一套预先组态的Kubernetes资源套件。其中 Tiller Server
主要负责接收来至Client的指令,并通过kube-apiserver与Kubernetes集群做沟通,根据Chart定义的内容,来产生与管理各种对应API物件的Kubernetes部署文件(又称为 Release
)。
首先在 k8s-m1
安装Helm tool:
$ wget -qO- https://kubernetes-helm.storage.googleapis.com/helm-v2.9.1-linux-amd64.tar.gz | tar -zx $ sudo mv linux-amd64/helm /usr/local/bin/
另外在所有 node
机器安裝 socat(用于端口转发):
yum install -y socat
$ sudo apt-get install -y socat
接着初始化 Helm(这边会安装 Tiller Server):
$ kubectl -n kube-system create sa tiller $ kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller $ helm init --service-account tiller ... Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster. Happy Helming!
这边默认helm的部署的镜像是 gcr.io/kubernetes-helm/tiller:v2.9.1
,如果拉取不了可以使用命令修改成国内能拉取到的镜像
kubectl -n kube-system patch deploy tiller-deploy -p '{"spec":{"template":{"spec":{"containers":[{"name":"tiller","image":"zhangguanzhang/gcr.io.kubernetes-helm.tiller:v2.9.1"}]}}}}'
查看tiller的pod
$ kubectl -n kube-system get po -l app=helm NAME READY STATUS RESTARTS AGE tiller-deploy-5f789bd9f7-tzss6 1/1 Running 0 29s $ helm version Client: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"} Server: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}
测试Helm 功能
这边部署简单Jenkins 来进行功能测试:
$ helm install --name demo --set Persistence.Enabled=false stable/jenkins
查看状态
$ kubectl get po,svc -l app=demo-jenkins NAME READY STATUS RESTARTS AGE demo-jenkins-7bf4bfcff-q74nt 1/1 Running 0 2m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE demo-jenkins LoadBalancer 10.103.15.129 <pending> 8080:31161/TCP 2m demo-jenkins-agent ClusterIP 10.103.160.88 <none> 50000/TCP 2m
取得 admin 账号的密码
$ printf $(kubectl get secret --namespace default demo-jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo r6y9FMuF2u
可以上面状态看到nodeport的端口为 31161
完成后,就可以通过浏览器访问Jenkins Web http://192.168.88.110:31161 。
测试完成后,即可删除:
$ helm ls NAME REVISION UPDATED STATUS CHART NAMESPACE demo 1 Tue Apr 10 07:29:51 2018 DEPLOYED jenkins-0.14.4 default $ helm delete demo --purge release "demo" deleted
更多Helm Apps可以到 Kubeapps Hub 寻找。
测试集群高可用
SSH进入 k8s-m1
节点,然后关闭该节点:
$ sudo poweroff
接着进入到 k8s-m2
节点,通过kubectl来检查集群是否能够正常执行:
# 先检查 etcd 状态,可以发现 etcd-0 因為关机而中断 $ kubectl get cs NAME STATUS MESSAGE ERROR scheduler Healthy ok controller-manager Healthy ok etcd-1 Healthy {"health": "true"} etcd-2 Healthy {"health": "true"} etcd-0 Unhealthy Get https://192.168.88.111:2379/health: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Kubernetes v1.12 二进制部署集群
- Kubernetes v1.12 二进制部署集群(HTTPS+RBAC)
- CentOS7.5 Kubernetes V1.13(最新版)二进制部署集群
- 自动化部署工具 Syncd v1.1.0 发布,提供二进制安装包
- 二进制手表
- 二进制状态码
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。