您好,登录后才能下订单哦!
研究K8S有一阵子了,这个东西很好用,但是也有很多坑,个人觉得很多地方还有待改进,K8S的静态Pod是一个利器,但也必须对它有全面的了解才能运作。
今天给大家示例一个从头到尾自己研究出来的项目,需要一定的基础,抛砖引玉,大家学会了可以自己去发挥。
K8S最大作用是面向集群业务型的和面向研发CI/CD,我这个例子偏向CI/CD。
步入正题:
环境:
1,首先,你得至少已经搭建了单节点的K8S平台;
2,会熟悉的部署Dockerfile;
3, 会K8S基本的命令操作;
4,对K8S基本运作有比较清晰的认识,不然排错是一个很大的障碍;
项目说明:
在上K8S之前各种计划任务,比如,定时备份,定时重启,定时检索日志发送邮件等等都是通过一台CentOS虚拟机的crontab来执行各种shell,python脚本来完成。
但是虚拟机也有因为停电,硬件故障导致系统崩溃的风险,所以,将这台虚拟机容器化并迁移至K8S内就健壮多了。
只要镜像在,并且K8S编排脚本在,那么一切都在,并且健康的运行,非常可靠。
最重要的,我需要将所有的管理脚本外置于外部NAS存储,并且挂载到K8S容器内,来达到数据持久化和代码集中化管理,
并且,我不需要登入容器,只需要在外部修改好crontab文件,删除掉当前pod,K8S会很乖的重新起一个容器,顺便加载了最新的crontab配置,巧妙利用K8S的特性来简化管理。
不废话,上干货,首先,需要把一切需要的东西封装到centos镜像中:
1,我需要在容器内执行python脚本,而且需要3以上的版本;
2,我需要shell执行远程ssh,那么需要安装sshpass;(当然你也可以选择其他方式)
3,我需要expect工具免交互脚本,那么需要安装expect;
4, 因为容器默认是没有安装crontab的,要做任务计划怎么能少了这个;
5,最重要的是安装supervisor进程守护管理工具,以免K8S的容器循环重启;(原理不作解释)
可能有一些不必要的插件,自己去优化吧,这里达到实验目的即可,没有做镜像大小优化,
贴上Dockerfile源码:
FROM centos:centos7.6.1810 MAINTAINER gavin.guo<379783667@qq.com> ENV TZ "Asia/Shanghai" ENV TERM xterm ENV LANG en_US.utf8 ADD aliyun-mirror.repo /etc/yum.repos.d/CentOS-Base.repo ADD aliyun-epel.repo /etc/yum.repos.d/epel.repo RUN yum install -y zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap -devel xz-devel && \ yum install -y curl wget tar bzip2 unzip vim-enhanced passwd yum-utils hostname net-tools rsync man && \ yum install -y gcc gcc-c++ git make automake cmake patch logrotate python-devel libpng-devel libjpeg-devel && \ yum clean all ADD Python-3.6.2.tgz /root RUN cd /root/Python-3.6.2/ && \ mkdir /usr/local/python3 ; ./configure --prefix=/usr/local/python3 && \ make && make install && \ ln -s /usr/local/python3/bin/python3 /usr/bin/python3 ; ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3 && \ cd /root ; rm -rf Python-3.6.2 && \ yum -y install net-snmp-utils crontabs sshpass expect ; sed -i 's/required pam_loginuid.so/sufficient pam_loginuid.so/' /etc/pam.d/crond && \ pip3 install supervisor && \ mkdir -p /etc/supervisor.conf.d && \ mkdir -p /var/log/supervisor COPY supervisord.conf /etc/supervisord.conf COPY supervisor_crontab.conf /etc/supervisor.conf.d/crontab.conf EXPOSE 22 COPY start.sh /root/start.sh RUN chmod +x /root/start.sh ENTRYPOINT ["/root/start.sh"]
Dockefile 同目录下,需要放置python3.6的二进制源码安装包,防止版本漂移和加快构建速度,和启动脚本start.sh
#! /bin/bash cp /root/script/root -f /var/spool/cron/ /usr/local/python3/bin/supervisord -n -c /etc/supervisord.conf
这个小小的脚本一行都不能少,少了第一行,那么K8S在启动Pod的时候会报错,因为找不到shell的执行路径;
第二行用来每次启动新的Pod的时候加载最新的crontab文件,而不需要操作pod容器;
第三行,启动supervisord,保证pod健康运行而不退出;
crontab的supervisord的启动配置文件贴一下:
[program:crond] directory=/ command=/usr/sbin/crond -n user=root autostart=true autorestart=true stdout_logfile=/var/log/supervisor/%(program_name)s.log stderr_logfile=/var/log/supervisor/%(program_name)s.log
使用命令开始封装镜像:
docker build -t gavin/mycron:v1 .
封装完成后,开始编写K8S编排文件:
创建一个mycron.yaml文件:
apiVersion: v1 kind: Pod metadata: name: mycron spec: containers: - name: mycron image: gavin/mycron:v1 ports: - containerPort: 22 volumeMounts: - name: script mountPath: /root/script volumes: - name: script hostPath: path: /root/K8S_PV/script type: DirectoryOrCreate
代码很简单,但我们重点看主机磁盘卷的挂载,这里虽然用的是hostPath,但是/root/K8S_PV/Script并不是普通的本地路径,而是我挂载到本地的NAS共享文件夹:
注意:
使用命令将配置好的NAS共享文件挂载到K8S宿主机内,并用hostPath的方式将其映射到master节点的Pod内,这个是官方教程和权威指南里面都没有说过的,不是亲身实验是无从得知的,但理论和实际上都是可行的;
讲操作:
挂载NAS共享文件夹:
mount -o username=XXX,password=XXX //NAS服务器的IP/K8S_PV /root/K8S_PV
K8S允许挂载NFS的PV,但是遇上NAS是有权限验证的,有权限验证的NFS如何引入PV, 官方和网上都没有资料,这也是我说它为什么需要改进的,不能都要求用户去单独搭一个NFS服务器吧!
而且对于小型存储需求来讲,PV和PVI这一套显得繁琐,没有本地直接挂载然后hostPath简单粗暴好用。
挂载完毕后,然后在K8S_PV里面新建一个目录script,将所有脚本丢进去即可;
将mycron.yaml文件复制到目录/etc/kubernetes/manifests/, K8S会自动启动静态Pod;
那么我们进入容器内看看是否一切正常:
kubectl exec -it mycron-master /bin/bash 运行crontab -e
发现定时任务配置已经顺利同步:
并且脚本路径顺利加载至pod:
以后我们需要修改定时任务或者脚本,只需要在NAS服务器上修改下文件,删除一下Pod即可,非常快捷方便,并且做到高可用,高可靠,集中化管理。
◆★◆★◆排错◆★◆★◆
在我后面修改了root账户下的crontab的配置文件,在K8S里面删除这个pod,异想天开以为美好的事情会按照预期的发生,却发现问题来了,crontab并没有更新到容器内,反复试了多次依然如故。
分析:
我们知道静态Pod是不受kubelet直接干预的特殊pod,K8S内置一套检查和恢复机制去完成它,删除k8s内创建的静态Pod实例(我们目的是为了得到新的更新实例)有三种途径,:
1,釜底抽薪,直接干掉或者移除/etc/kubernetes/manifests/内的yaml编排文件,K8S会自我销毁创建的静态Pod,完毕再复制yaml文件回去,它又将自我重建;
2,在K8S-dashboard的web页面操作删除:
3,使用命令删除:
目前第一种过于繁琐,我采取的是第二种和第三种方式,发现没有效果,只要当第一种方式容器内才更新成功,何故?
好,我们来使用docker命令来查看每种删除方式后的容器发生了什么变化,来找出问题在哪里:
我们发现第二种和第三种方法对容器没有变化,因为它的容器创建时间和容器名称没有发生改变,你大爷还是你大爷。
当使用第一种方式删除的时候,奇迹出现了,该发生的都发生了,我们可以看到虽然容器注册名没有发生改变,但是创建时间改变了,它已经不是从前的它了:
◆★◆★◆思考◆★◆★◆
为什么一定要去撤销yaml编排文件,容器才能得到彻底销毁呢?个人觉得,这要从K8S的机制说起,我们知道,K8S系统在容器的基础上又抽象出一个沙箱概念:Pod,这个豆荚是我们在k8S系统内所能操作的最小单位,但实际上容器实例才是最小颗粒单位。我们删除pod,并不代表删除了容器,恰恰相反,由于K8S的各种检查和恢复,销毁机制,如果不销毁yaml文件,那么K8S检测到容器依然是活跃并健康的,根本不会销毁和置换一个新的容器给你,而是重新套上一个pod包装箱,事实上容器连停止和重启的动作都不会进行。这也是为什么删除pod和启动新的pod为什么速度那么快,因为K8S只是换了个Pod而没有换容器。不要感觉被骗了,因为K8S觉得没有那个必要,这就是它自作聪明之处。
知道这个道理后,那么我们知道如何去应对,有几种方式去重建容器:
1,K8S只有当容器出现崩溃或者说不健康了,才会销毁和重建容器(这种是自然发生,不可控)
2,销毁或者说改动yaml编排文件,那么意味着新的部署,容器才能得以彻底销毁重建;
3,使用docker rm -f 容器名,强制删除容器,那么也会得到新的容器;
4,如果你能分析K8S源码,能欺骗健康检查机制去销毁和重建容器也是可以的,但是一般人做不到;
个人认为非常经典的一个实验,实践了以下内容:
1,静态Pod的创建;
2,hostPath存储的挂载,外部NAS存储的巧妙引用;
3,测试了静态Pod的特性;
4,Docker镜像的封装, 关键点还在于入口脚本的设置;
5,了解了pod和容器之间的关系和行为;
一般来说,通常Dockerfile的最后一行为:
ENTRYPOINT ["/usr/local/python3/bin/supervisord" ,"-n","-c" "/etc/supervisord.conf"]
这个是supervisord的启动进程命令,但是很多时候并不是这么简单,可能在容器启动的时候有很多初始化的复杂操作,那么全部放入一个start脚本里面去执行,并且保证supervisord在最后执行即可;
就像本例,我需要在启动进程前将挂载存储路径里面的配置文件更新到相应的目录,来达到重起容器即更新配置的目的,那么非脚本不可。
学会了吗?试试看,打造你的私有镜像并且使用启动脚本来更新容器,可能更有妙用。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。