剛開始打算用Jenkins+shell 部署鏡像到K8S,無意間看到網上推薦的drone,用了之后覺得drone和docker、K8S非常般配,Jenkins更像上一代產品。在這里分享和總結一下drone的使用過程。
通過docker-compose安裝drone,和官網版本差不多,分享一個我的
# cat drone/docker-compose.yaml
version: '2'
services:
drone-server:
image: drone/drone:0.8
ports:
- 8078:8000
- 9000
volumes:
- /var/lib/drone:/var/lib/drone/
restart: always
environment:
- DRONE_OPEN=true
- DRONE_ADMIN=[git user name]
- DRONE_HOST=[http url]
- DRONE_GITEA=true
- DRONE_GITEA_URL=[git url]
- DRONE_SECRET=aHVubGlqaWRyb25lMTAw
# - DRONE_DATABASE_DRIVER=mysql
# - DRONE_DATABASE_DATASOURCE=root:123456@tcp(ip:3306)/drone?parseTime=true
drone-agent:
image: drone/agent:0.8
command: agent
restart: always
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_DEBUG=true
- DRONE_SERVER=drone-server:9000
- DRONE_SECRET=aHVubGlqaWRyb25lMTAw
歷史原因
- MVC中V被拆出來,作一個單獨的git庫。我們的項目框架是ThinkPHP3,經典的MVC模式,前端是.tpl文件,里面會用到controller傳遞的變量,所以部署上無法分離。
- 接著前端加入了webpack/react/vue等等技術棧,生出了10多個不同功能的git庫。drone對合并git倉庫并不友好,這對打包鏡像帶來難度。
- 我的解決方法是
- 把必要的2個代碼庫(php和tpl)通過volume cache插件,互相掛載。當php有更新時,把最新的tpl掛載過來(是最近一次tpl倉庫更新完成才掛載出去的)。反之tpl更新時亦然。
- 對于其他git庫,通過nginx反向代理到原服務器上, 后期再剝離部署。
Dockerfile
FROM registry.cn-hangzhou.aliyuncs.com/ns/alpine-php-fpm-nginx
COPY souce_code /server_root/.
php git庫里的.drone.yml
#設置工作目錄
workspace:
base: /www
path: php
pipeline:
build: #集成前后端代碼,打包鏡像
image: registry.cn-hangzhou.aliyuncs.com/ns/alpine-php-fpm-nginx
secrets: [ docker_username, docker_password ]
volumes:
- /tmp/cache/tpl:/www/tpl
commands:
- cp -rf * /www/tpl/
cache: #把最新的php文件掛載到宿主機
image: drillster/drone-volume-cache
rebuild: true
mount:
- /www/php
volumes:
- /tmp/cache:/cache
publish-image: #調用目錄下Dockerfile打包鏡像,并push到遠程倉庫
image: plugins/docker
registry: registry.cn-hangzhou.aliyuncs.com
repo: registry.cn-hangzhou.aliyuncs.com/namespace/project #遠程倉庫地址
secrets: [ docker_username, docker_password ]
tags: latest
k8s-deploy: #調用k8s部署鏡像
image: registry.cn-hangzhou.aliyuncs.com/namespace/deploy #自己做的一個部署鏡像,執行sh命令
secrets: [ docker_username, docker_password ]
commands:
- deploy.sh ${DRONE_REPO_NAME} ${DRONE_BUILD_NUMBER} ingress_name project:latest #把項目名、構建序號傳遞過去,便于管理版本
notice: #企業微信通知一下
image: clem109/drone-wechat
secrets: [plugin_corpid, plugin_corp_secret, plugin_agent_id]
to_user: "user token"
title: "${DRONE_REPO_NAME} build No.${DRONE_BUILD_NUMBER} ${DRONE_PREV_BUILD_STATUS} !"
description: "Author: ${DRONE_COMMIT_AUTHOR} \nBranch: ${DRONE_COMMIT_BRANCH} \n\n點擊查看drone自動化部署報告->"
msg_url: ${DRONE_BUILD_LINK}
branches: [ master ] #只對master代碼觸發部署
接著,介紹下k8s master上的相關腳本
目錄結構
project
│ deployment.yaml #部署模板
│ upgrade.sh #執行部署任務,藍綠發布
│ keepone.sh #php服務占用內存太高,只希望保持最新版本的服務,不考慮版本回退
│
└───deploys #deploys yaml logs
│ │ project.drone_num.yaml
│ │ ...
│
└───ingress #ingress yaml
deployment.yaml 定義了部署容器組和服務
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: <deploy_name> #定義個變量,通過文本替換的方式賦值
labels:
app: <deploy_name>
spec:
replicas: 1
selector:
matchLabels:
app: <deploy_name>
template:
metadata:
labels:
app: <deploy_name>
spec:
containers:
- name: <deploy_name>
image: registry.cn-hangzhou.aliyuncs.com/namespace/<image_name>
imagePullPolicy: Always #總是拉取最新的
livenessProbe:#存活檢查
failureThreshold: 3
httpGet:
path: path_to_api
port: <image_port>
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 3
ports:
- containerPort: <image_port>
imagePullSecrets:
- name: <image_secret> #替換為自己的保密字典
---
apiVersion: v1
kind: Service #開個服務
metadata:
labels:
run: <deploy_name>
name: <deploy_name>-svc
spec:
ports:
- port: <image_port>
protocol: TCP
targetPort: <image_port>
selector:
app: <deploy_name>
sessionAffinity: None
type: NodePort
upgrade.sh
# 調用方式deploy.sh ${DRONE_REPO_NAME} ${DRONE_BUILD_NUMBER} ingress_name docker_image:tag
#!/bin/bash
#只保證最新的服務存活
keep_one_live() {
sleep 300 #給老版本的容器時間處理正在執行的進程
wait
sh keepone.sh $1
}
if [ ! $1 ]; then
echo -e "\033[41;37m param1 enter a deploy name \033[0m \n"
exit
fi
if [ ! $2 ]; then
echo -e "\033[41;37m param2 enter a deploy version \033[0m \n"
exit
fi
if [ ! $3 ]; then
echo -e "\033[41;37m param3 enter ingress name or string 'none' \033[0m \n"
exit
fi
if [ ! $4 ]; then
echo -e "\033[41;37m param4 enter docker image name:tag \033[0m \n"
exit
fi
LABEL=$1
VERSION=$2
INGRESS=$3
DOCKERIMAGE=$4
if [ ! $5 ]; then
IMAGEPORT=80
else
IMAGEPORT=$5
fi
DEPLOYMENTNAME=$1-$2
SERVICE=`echo $1-$2-svc`
DEPLOYMENTFILE="deploys/$1.$2.yaml"
INGRESSFILE="ingress/$3.yaml"
DEMO="deployment.yaml"
echo -e "\033[47;32m 1. Start to deployment:$DEPLOYMENTNAME \033[0m \n" #加個騷氣的顏色 參考 https://blog.csdn.net/David_Dai_1108/article/details/70478826
cp -f $DEMO $DEPLOYMENTFILE #拷貝一份到deploys目錄
sed -i "s/<deploy_name>/$DEPLOYMENTNAME/g" $DEPLOYMENTFILE #替換變量
sed -i "s/<image_name>/$DOCKERIMAGE/g" $DEPLOYMENTFILE
sed -i "s/<image_port>/$IMAGEPORT/g" $DEPLOYMENTFILE
echo -e "\033[47;32m 2. Create new service:$SERVICE \033[0m \n"
cat $DEPLOYMENTFILE
kubectl apply -f $DEPLOYMENTFILE #按模板創建容器組和服務
#此時會運行V1 V2 兩個版本的容器,等V2啟動成功,再切換路由,就是藍綠發布
echo -e "\033[47;32m 3. Wait until the deployment:$DEPLOYMENTNAME is ready \033[0m \n";
READY=$(kubectl get deploy $DEPLOYMENTNAME -o json | jq '.status.conditions[] | select(.reason == "MinimumReplicasAvailable") | .status' | tr -d '"')
while [[ "$READY" != "True" ]]; do
READY=$(kubectl get deploy $DEPLOYMENTNAME -o json | jq '.status.conditions[] | select(.reason == "MinimumReplicasAvailable") | .status' | tr -d '"')
sleep 5
done
if [ $INGRESS !== "none" ]; then
echo -e "\033[47;32m 4. Update ingress with service:$SERVICE \033[0m \n"
LASTSVC=$(kubectl get ing $INGRESS -o json | jq '.spec|.rules|.[0]|.http|.paths|. [0]|.backend|.serviceName' | grep -oP "\w+(\-\w+)+")
kubectl get ing $INGRESS -o yaml > $INGRESSFILE #把當前路由配置做個備份
sed -i "s/$LASTSVC/$SERVICE/g" $INGRESSFILE #把v1服務替換v2服務
kubectl replace -f $INGRESSFILE #應用當前路由配置
fi
echo -e "\033[47;32m 5. Delete old versions \033[0m \n"
keep_one_live $LABEL & #利用shell子進程,異步刪除V1的容器組和服務
echo -e "\033[47;32m 6. Deployment:$DEPLOYMENTNAME job done. \033[0m \n"
keepone.sh
path="deploys"
files=$(ls $path -t) #按時間排序部署過的yaml文件
i=0
j=0
for filename in $files
do
dp=`echo $filename | cut -d "." -f1`
if [ $dp == $1 ];then #只匹配相同項目名的文件
((i++))
if [ $i -gt 1 ];then #排除最新的一個,保留2個版本就改為 `gt 2`
((j++))
if [ $j -lt 2 ];then #這里有點繞,是為了避免刪除已刪除的容器組和服務
echo "$filename deleted"
kubectl delete -f "$path/$filename" #找到次新的yaml,刪除其容器組和服務
fi
fi
fi
done
通過以上腳本,就實現了自動化部署,drone的功能很簡潔,沒有一絲多余,最后放一張部署成功的截圖
drone.png