研究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<[email protected]> 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在最後執行即可;
就像本例,我需要在啓動進程前將掛載存儲路徑裏面的配置文件更新到相應的目錄,來達到重起容器即更新配置的目的,那麼非腳本不可。
學會了嗎?試試看,打造你的私有鏡像並且使用啓動腳本來更新容器,可能更有妙用。