原創(chuàng):陳斌 張少華
本期導(dǎo)讀
1.為提升部署效率持續(xù)集成測試環(huán)境從胖docker向深度容器化的改進;
2.架構(gòu)采用Rancher + Harbor,網(wǎng)絡(luò)采用pipework為容器配置固定的IP地址;
物理架構(gòu)
在測試環(huán)境建設(shè)過程中由于模塊數(shù)量快速增加,為了提高部署效率和穩(wěn)定性我們在容器化的基礎(chǔ)上進行了深度容器化的探索,深度容器化采用了Rancher + Harbor的架構(gòu),該架構(gòu)有非常優(yōu)良的性能,非常適合以pod為單位進行子系統(tǒng)級別測試環(huán)境建設(shè)。
網(wǎng)絡(luò)配置
如上圖所示,每一個Node節(jié)點對應(yīng)現(xiàn)在的一套測試環(huán)境,擁有獨立的ip。
其實現(xiàn)原理為Node節(jié)點是Dind生成容器,創(chuàng)建后使用pipework為容器配置固定的IP地址,之后可在Node中啟動多個Pod,分享使用Node的IP地址。
對比Cattle和K8S使用過程中的差異
由于我們測試需求的特殊性,目前Rancher上面的服務(wù)群并不需要頻繁的做升級,而是會頻繁的創(chuàng)建和釋放。Cattle的穩(wěn)定性和易用性要優(yōu)于K8S,但是K8S提供了更加強大的周邊功能包括資源管理、監(jiān)控、配置管理等。
Cattle:
(1)多服務(wù)應(yīng)用,每個項目都作為獨立的服務(wù)受Rancher管理,升級比較方便。缺點是需要以應(yīng)用(stack)為單位進行擴展和調(diào)度,因為我們其實是把一個應(yīng)用里的多個容器包在一起對外提供服務(wù)。
擴展方式 :stack-copy、或者通過模板創(chuàng)建(效率略低于主從容器方式)。
(2)主從容器,一個容器作為主容器,添加多個從容器,耦合關(guān)系更緊密,優(yōu)點是以服務(wù)(service)為單位進行擴展和調(diào)度。
擴展方式:由于資源對象的編號,直接scale即可。
缺點:
當(dāng)需要對該服務(wù)做升級時,會重啟所有容器,從容器越多,升級越慢。因為升級的最小操作單元就是服務(wù)。
當(dāng)在服務(wù)中使用負(fù)載均衡時,而該服務(wù)又擁有從服務(wù)的時候,你需要使用主服務(wù)作為負(fù)載均衡器的目標(biāo)。從服務(wù)不能成為目標(biāo)。
從服務(wù)不能使用links/external_links來創(chuàng)建服務(wù)別名。
內(nèi)部訪問:Cattle提供了內(nèi)部DNS解析,直接以容器名訪問。
K8S:
多容器Pod
以Pod為單位進行調(diào)度,一個Pod里面啟動多個容器,類似于Cattle的主從容器方式,但是差異在于網(wǎng)絡(luò)。
擴展方式:scale(效率不高)。
內(nèi)部訪問:Pod內(nèi)的所有容器共享網(wǎng)絡(luò)namespace,其實就是都通過-net=container(Pod創(chuàng)建時有一個sleep容器) 共享了同一個網(wǎng)絡(luò),所以端口不能沖突,容器直接以localhost(或者127.0.0.1)訪問。
缺點:
擴展效率低下。
容器之間的相互通信不是十分穩(wěn)定。
優(yōu)點:有一系列的強大的利于編排的功能,比如 initContainer、HPA、PV等都是非常好的特性。
CI 工作流
數(shù)據(jù)庫組件容器化
Mysql容器化
我們測試環(huán)境使用的mysql版本是5.6.41,所以我們制作mysql-5.6.41的鏡像,制作過程比較簡單
(1)拉取官方鏡像啟動,環(huán)境變量必須要設(shè)置。
docker run -p 3300:3306 --name mysql ?-e MYSQL_ROOT_PASSWORD=root123 -d docker.io/mysql:5.6.41
(2)利用docker cp 將我們的配置文件復(fù)制到該容器中
docker cp ${PWD}/my.cnf mysql:/etc/mysql/
(3)利用docker commit命令 將修改部分提交,
docker commit mysql 192.168.1.251:7003/qa/mysql:5.6.41-private
(4)推送到Harbor
docker push?192.168.1.251:7003/qa/mysql:5.6.41-private
(5)啟動試一下:
docker run -p3301:3306 -e MYSQL_ROOT_PASSWORD=root@123?-e TZ=Asia/Shanghai --name mysql-p -d -v /home/demo/data2:/var/lib/mysql 192.168.1.251:7003/qa/mysql:5.6.41-private
(6)客戶端連接,使用我們啟動時指定的root的密碼,然后查看一下配置是否正確,找一個我們配置文件里的自定義選型比對一下:
(7)沒問題,完成。注意數(shù)據(jù)卷的使用,不要把數(shù)據(jù)放在容器內(nèi)部,這部分,具體使用還要再做規(guī)劃。
Mongo容器化(3.6.4)
先看下咱們的配置文件,沒有什么特殊配置,可以直接拉取官方鏡像啟動,然后看下mongo鏡像的啟動參數(shù):
環(huán)境變量:

配置文件默認(rèn)在 /etc下,啟動時?使用–config /etc/mongo/mongo.conf 來指定自定義配置,這里不需要。我們只需要直接啟動官方鏡像,把數(shù)據(jù)文件映射出來即可。
docker run -p 27000:27017 -v /home/demo/mongodata:/data --name mongo-p -d -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=admin?-e TZ=Asia/Shanghai?192.168.1.251:7003/qa/mongo:3.6.4
客戶端連接一下試試:
Redis容器化(3.2.12)
其實原理跟mysql類似,這里簡單寫了個Dockerfile,因為redis啟動必須指定配置文件
FROM docker.io/redis:3.2.12
MAINTAINER xxx@xxx.com
COPY redis.conf /usr/redis.conf
CMD [ "redis-server", "/usr/redis.conf" ]
對配置文件做了一點修改,把日志文件去掉了,在容器里,我們一般都是使用docker logs 查看日志,需要把日志輸出出來,而不是寫入文件里。
docker build -t 192.168.1.251:7003/qa/redis:3.2.12 .
docker run -p 6300:6379 -v /home/demo/redisdata2:/data -e TZ=Asia/Shanghai --name redis-p -d 192.168.1.251:7003/qa/redis:3.2.12
連接成功,注意這里沒設(shè)置密碼驗證。如果想要加上密碼,也非常簡單,redis支持一系列的command line,使用方式跟在配置文件里面的key值完全一樣,比如這里我們要加上密碼驗證,只需要在docker 啟動命令最后加上?

(注意docker run的啟動參數(shù)是有順序的,在鏡像名字后面的內(nèi)容都認(rèn)為是command,平時啟動鏡像,一般的參數(shù)都寫在鏡像名字之前)
應(yīng)用容器化
S2I 對接生產(chǎn)
由于生產(chǎn)使用OpenShift,原有的鏡像構(gòu)建流水線做出的鏡像雖然比較簡單易用,但無法與OpenShift兼容,所以借助OpenShift提供的鏡像構(gòu)建工具?s2i?去做鏡像。缺陷就是目前這個基礎(chǔ)鏡像太大了。
本地構(gòu)建方式:
(1)安裝s2i,并拉取基礎(chǔ)鏡像到本地
(2)拉取代碼,并切換到項目根目錄,例如:/home/work/dev/projects/hydra
(3)執(zhí)行s2i構(gòu)建:s2i build . 192.168.1.251:7003/qa/python:2.7-oc?hydra?-e GIT_REPO_NAME=hydra?(必填:通過這個環(huán)境變量來指定項目名稱,并且會影響到代碼在容器中的路徑)
(4)啟動鏡像:通過上面的命令,構(gòu)建出了一個名字為 hydra,tag為latest的鏡像,要啟動這個鏡像,也非常簡單,需要指定幾個環(huán)境變量
- env:
- name:?GIT_REPO_NAME
value:?hydra
- name:?INI_CMD
value:?(測試環(huán)境一般不需要這個變量)
- name:?RUN_CMD
value:?make start_test
- name:?RUN_CMD_DIR
value:?/data/home/work/hydra/
應(yīng)用修改:
(1)測試時同一個Pod里面啟動多個容器,所以nginx 不能啟動會有端口沖突。uwsgi啟動需要將socket替換為http-socket并且端口不能沖突,由于日志管理不便,暫時舍棄。
(2)Makefile 添加用于測試環(huán)境的啟動和初始化的命令
啟動命令(RUN_CMD):start_test,先做make local_config操作,然后做啟動操作,直接runserver啟動即可
初始化命令(INI_CMD):測試一般不需要,線上一般會復(fù)制nginx配置
(3)以hydra 的修改為例子:
修改tools/config_templates/gen_config.py? 直接vim編輯,將class LocalSettings(object) 這個類里的localhost全部替換為127.0.0.1,因為localhost不走物理網(wǎng)卡,一些數(shù)據(jù)庫連接會直接在容器內(nèi)部找socket,導(dǎo)致連不上。
Makefile 根據(jù)需要啟動的服務(wù)類型添加了兩個target:
start_test:local_config
????????DJANGO_SETTINGS_MODULE="hydra.settings"?python manage.py runserver?0.0.0.0:19010?--noreload
start_celery-hydra_upload-1_test:local_config
????????DJANGO_SETTINGS_MODULE="hydra.settings"?PYTHONPATH="."?/data/home/work/hydra/.venv/bin/celery worker -A async_tasks -Q hydra,hydra_loan_notify,hydra_repay_notify -P eventlet -c?200?-l info --
logfile=/tmp/celery.log --without-heart
啟動hydra:分別傳入不同的RUN_CMD來啟動兩個服務(wù),例如:make start_test
應(yīng)用接入方式
配置修改
總體方式是——根據(jù)該項目依賴的外部服務(wù),將配置修改為通過環(huán)境變量讀取,由于之前的不規(guī)范,大部分項目都涉及如下三個文件的修改:
(1)Makefile 增加 docker_config,以hydra為例:
?(2)修改tools/config_templates/gen_config.py?
?(3)修改tools/config_templates/local_settings.py.tmpl?配置模板里面全局替換一下
項目根目錄添加啟動腳本:
entrypoint.sh 原因:部分項目需要在啟動之前先生成配置文件
根據(jù)不同項目框架 操作不同:
django項目:
#!/bin/bash
make docker_config
python manage.py runserver 0.0.0.0:8080 --noreload
flask項目:
#!/bin/bash
make docker_config
gunicorn -k gevent -c gunicorn_dev.py yin.app:app
容器化現(xiàn)存問題
日志采集
需求:
(1)持久化存儲
將日志文件持久化存儲,按hostname區(qū)分。一個pod里面通常會有小到幾個大到幾十個容器,統(tǒng)一把日志掛載到其中的stubs容器中,然后再掛載的pv上面。
缺點:
1.查看不方便,動態(tài)pv不能根據(jù)名字區(qū)分出來是哪個deploy的日志。
2.多個replicaSet副本的日志都綁到了同一個目錄下,文件眾多,查找不便。
備選方案一:阿里云logtail(收費)
備選方案二:efk?
備選方案三:graylog
當(dāng)前采取的方式是:
在stubs容器中安裝了ssh服務(wù),然后通過rsync去同步case日志。
在釋放資源之前,利用kubectl cp 持久化存儲服務(wù)日志。
去除依賴
目前自動化體系,對基礎(chǔ)庫的每日構(gòu)建、stubs更新等依賴嚴(yán)重。