研究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

发现定时任务配置已经顺利同步:

◆★◆★◆思考◆★◆★◆

为什么一定要去撤销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在最后执行即可;

就像本例,我需要在启动进程前将挂载存储路径里面的配置文件更新到相应的目录,来达到重起容器即更新配置的目的,那么非脚本不可。


学会了吗?试试看,打造你的私有镜像并且使用启动脚本来更新容器,可能更有妙用。