寫在前面
我們以前windows跑.net Framework程序的時候,發布,自己乖乖的替換程序;備份,也是自己一個一個的重命名備份;回滾,發布遇到問題的回滾更是不用說了;運維很是怕我們 這些用windows的啊;
那全面擁抱linux的一一.net core 時代 ,是如何處理這些個問題的呢?
噔噔蹬蹬~請往下看。
運行環境
centos:7.2
cpu:1核 2G內存 1M帶寬
一台安裝jenkins的機器。
(本文例子不一定要安裝jenkins,但實際項目是要用jenkins的)
背景
我們目前的應用部署環境是這樣划分的(暫定):
開發環境
環境變量:Development
開發環境就是我們平時的開發用的機器,錯誤、異常盡可能多的報出來這種。css、js、頁面文件等各種靜態資源也不做壓縮處理,連接測試庫;
開發環境的部署:開發人員按自己習慣自己部署;
測試環境
環境變量:Staging
測試環境也就是測試同學測試用的環境,為了貼合生產環境的多機器部署,我們測試機器也有多台,目前我們搭建了jenkins可由測試同學自己部署;錯誤信息已做捕捉處理,靜態文件同樣不壓縮,連接測試庫;
測試環境的部署:docker+docker-compose部署,我們在項目里面編寫好了Staging.Dockerfile、docker-compose.yml還有對應的測試環境發布的shell腳本,借助jenkins來進行參數化的構建。參數包括程序運行的端口、綁定的ip,consul配置等等。哦對了,我們目前的構建步驟大概是:
-
去gitlab拉取最新程序代碼;
-
執行單元測試和集成測試,只有通過單元測試和集成測試才能繼續步驟3,否則部署失敗終止:
-
dotnet restore->build->publish,將生成產品打包成一個鏡像;
-
使用docker-compose down 停止、移除上次的構建;
-
使用docker-compose up 這個強大的命令,構建新的鏡像、啟動容器;
-
清除臨時鏡像,構建完成;
單元測試用dotnet test 命令;
這里我們還可以看到,配置文件也一並被打包到鏡像里面了,修改配置文件也需要重新構建的;
預生產環境
環境變量:Staging
預生產環境是相對於測試環境來說,無論數據、配置還是架構都是更加接近生產環境的存在了。一般還是連接的數據庫是預生產環境的數據庫(同步了生產環境的數據的),甚至有的使用會直接連接生產環境的庫(一般不練、只讀賬號等控制);不過我們公司還是連接的測試庫
然后靜態文件壓縮啊、什么的這些,生產環境怎么處理,這里也怎么處理;
通過測試環境測試的程序才可以部署到這里,這里測試通過后,才可以部署到生產環境;
預生產環境的部署:由項目負責人或者運維部署,需要比較大權限才可以;
生產環境
環境變量:Production
生產環境一般應配置為最大限度地提高安全性、性能和應用可靠性,包括但不限於以下舉措:
- 全面啟用分布式緩存
- 客戶端資源被捆綁和縮小,並可能從 CDN (網絡分發)提供。
- 必須禁用診斷錯誤頁。
- 啟用友好錯誤頁、一致的錯誤響應。
- 啟用生產記錄和監視。
生產環境的部署:運維部署,我們開發沒有權限了;
部署的背景我們的條件等等大概講完了,下面我們說說生產環境我們怎么設計容器的。
生產環境的容器設計
由於生產環境經常需要修改配置、保留日志信息、需考慮程序的備份與回滾等等,我們不能像上面的測試環境一樣,把整個發布的產品打包成一個鏡像了,我們需要做特殊的處理;
熟悉docker的同學,肯定會想到:掛載
對的,我們就這么處理,我們用docker -v 處理這頭痛的問題;
程序的目錄結構
我們程序的目錄結構是這樣的:
backs:放歷史版本的程序文件,按備份日期壓縮命名;
logs:程序的運行日志文件;
program:當前運行的程序;
logs 和 program 目錄,使用 docker -v 掛載;
backs目錄截圖:
發布
發布步驟
-
同步通過測試的預生產環境的程序文件;
-
壓縮、備份上一版本的程序文件;
-
通過更改文件夾名稱的方式,當前運行程序替換為最新的;
-
重啟程序;
-
心跳檢測:通過輸出部署成功,未通過執行回滾操作。
發布腳本(Production.Publish.sh)
#!/bin/bash
function success()
{
echo -e "\033[32m $1 \033[0m"
}
function error()
{
echo -e "\033[31m\033[01m $1 \033[0m"
}
echo "publish beging。。。。。。"
remotePath=$1
healthCheckUrl=$2
defaulPaht= $3;
bashPath=${defaulPaht:=`pwd`}
if [ ! $remotePath ]; then
echo "warn:remotePath should't be empty!"
exit
fi
if [ ! $bashPath ]; then
error "error:bashPath should't be empty!"
exit
fi
echo "bashpath is ${bashPath}"
programPath="${bashPath}/program"
logPath="${bashPath}/logs"
backPath="${bashPath}/backs"
publisTemp="${bashPath}/publisTemp"
mkdir -p $programPath
mkdir -p $logPath
mkdir -p $backPath
mkdir -p $publisTemp
#remote git or scp
#這里同步預生產環境的程序文件,這里寫死了ip只是示例,scp也只是示例
#大家可以采用更安全,更有效率的同步文件方式
scp -r root@139.199.196.67:${remotePath}"/.*" ${publisTemp}
if [ $? ]; then
echo "info:copy successful!"
#壓縮、備份當前運行程序到backs文件夾
backFileName=`date +%Y%m%d%H%M%S`".tar.gz"
`cd ${programPath} && tar -zcPf ${backPath}/${backFileName} *`
#replace
#替換程序
if [ $? ]; then
mv $programPath ${programPath}"Old"
mv $publisTemp ${programPath}
rm -r ${programPath}"Old"
#publis fail ,then Production.Rollback
if [ $healthCheckUrl ]; then
curl $healthCheckUrl
if [ $? -ne 0 ]; then
error "error:public failed!"
#心跳檢測失敗,執行回滾
if [ -f "Production.Rollback.sh" ];then
echo "************************************ exec Rollbacking...... ************************************"
./Production.Rollback.sh ${backPath}
else
error "error:Production.Rollback.sh is not existing!";
fi
exit
fi
fi
success "publish Successful!"
else
error "error:file tar failed!"
fi
else
error "error:remote files copy failed,Maybe you should checkout your ssh auth!"
fi
Dockerfile
Dockerfile比較簡單
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
ARG RUN_PORT=${RUN_PORT:-""}
ARG CONSUL_TO_NETCOREHOST=${CONSUL_TO_NETCOREHOST:-""}
ARG ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT:-""}
ENV RUN_PORT=${RUN_PORT} CONSUL_TO_NETCOREHOST=${CONSUL_TO_NETCOREHOST} ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT}
ENTRYPOINT ["dotnet", "Member.WebApi.dll"]
docker-compose
version: '3.4'
services:
member.webapi:
image: memberwebapi${RUN_PORT}
build:
context: .
dockerfile: ${ASPNETCORE_ENVIRONMENT}.Dockerfile
network_mode: "host"
restart: always
environment:
- RUN_PORT=${RUN_PORT}
- ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT}
- CONSUL_TO_NETCOREHOST=${CONSUL_TO_NETCOREHOST}
command:
- "--port"
- "${RUN_PORT}"
#就是這里掛載
volumes:
- ../program/:/app/
- ../logs/:/app/logs
回滾
回滾其實就是發布的逆操作;
發布是:同步最新程序->備份當前運行程序->替換;
回滾是->找到上一次的備份->刪掉的當前運行程序->替換;
Production.Rollback.sh
#!/bin/bash
echo "rollback beging。。。。。。"
defaulPaht= $1;
bashPath=${defaulPaht:=`pwd`}
programPath="${bashPath}/program"
backPath="${bashPath}/backs"
lastFile=`cd ${backPath} &&ls -t |head -n1|awk '{print $0}'`
if [ ! $lastFile ];then
echo "error:none backup program!"
fi
lastFilePath="${backPath}/${lastFile}"
echo $lastFilePath
if [ -f $lastFilePath ];then
echo "rollback program:${lastFilePath}"
programOldPath="${programPath}Old"
mkdir -p ${programOldPath}
tar zxvf ${lastFilePath} -C ${programOldPath}
#replace
if [ $? ]; then
rm -r ${programPath}
mv ${programOldPath} ${programPath}
echo "rollback Successful!"
else
echo "error:backup program is not existing!"
fi
else
echo "error:backup program is not existing!"
fi
最后貼一個運行截圖:
總結
毫不誇張地說,Jenkins + Dockor 讓.net 完全從一個刀耕火種的原始人一下子穿越到了全自動化的現代;
文章的思路可以借鑒,腳本改改也可以用,但需理解思路;
有的同學可能會問,為什么生產環境的部署,不能像測試環境一樣直接拉取master的代碼構建,我這里的回答是涉及到配置的權限問題、devops的學習到位問題。歷史原因等,我們暫定這樣,后面實踐,我樂於分享;
本文的實踐都有很大的局限性,比如有現成的工具、有更強大的插件等等可以更簡單的去解決這個問題之類的,我可能還不知道;比如我的shell寫的一塌糊塗等等。。歡迎溝通,不理賜教。
晚安~