.NET遇上Docker - Docker集成Cron定時運行.NETCore(ConsoleApp)程序.md


配置項目的Docker支持

對於VS中Docker的配置,依舊重復一些廢話。
給項目添加Docker支持,VS2015可以直接使用Docker for VS插件,VS2017在安裝時選擇容器支持。VS配置好容器支持后,右鍵點擊項目,添加菜單中就可看到Docker Support選項。
VS2015的Docker for VS插件會把Dockerfile加入到package.json的publishOptions中。這樣一次dotnet publish操作就可以獲得含有Dockerfile可以直接用於生成Image的輸出。
而VS2017中,使用“添加-Docker支持”菜單添加的Dockerfile文件不會自動配置為輸出到輸出目錄,可以選擇Dockerfile並右擊屬性,在屬性選項卡中,選擇“如果較新則復制”。

在Linux使用cron定時運行.NETCore App

一個坑

如果直接在crontab -e中通過下面的方法添加任務:

*/2 * * * * dotnet /publish/your.app.dll #2分鍾運行一次,用於測試

肯定是沒有辦法執行的,通過tail -f /var/log/cron可以看到如下錯誤輸出(CentOS):

(root) CMDOUT (/bin/sh: dotnet: 未找到命令)

爬坑

我們需要使用如下腳本來幫助運行.NetCore應用:

#!/bin/sh
export  PATH=$PATH:/publish:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
cd /publish
dotnet your.app.dll >> /var/log/cron.log 2>&1 #將應用的輸出重定向(可選),默認輸出到`/var/log/cron`
exit;
EOF

一些已知限制,應用程序目錄中不能含有.

為避免在cron輸出日志時遇到No MTA installed, discarding output這樣的錯誤,建議重定向應用的輸出到指定的文件

將這個文件保存為rundotnet.sh並設置輸出到發布文件中。這個文件將被cron調用定時來運行.NetCore程序。
將使用crontab -e修改cron任務如下(添加后可使用crontab -l驗證添加結果):

*/2 * * * * /publish/rundotnet.sh

注意修改rundotnet.sh的權限使其具有執行權限

另一種方式是直接將應用發布為可執行程序。
親測使用VS2017發布.NetCore App(.NETCore版本1.0.4),可以在安裝有.NetCore SDK 1.0.3的Linux上使用下面的cron配置正常執行。

*/2 * * * * /publish/your.app

同樣需要賦予your.app文件執行權限

而VS2015發布.NetCore App(.NETCore版本1.0.0),在安裝有.NetCore SDK 1.0.1的Linux上就不能正常執行。

整合到Docker中

創建一個名為crontab的文件用於保存cron命令,內容就是我們之前使用crontab -e添加的任務:

*/2 * * * root /publish/rundotnet.sh # 用戶名root在這里不可少,根據需要修改定時執行的時間
# 合法的cron文件需要留一行空行

可能有眼尖的園友發現為什么這里有root這個用戶名而前文沒有,前文的測試是在一台RedHat系(CentOS7)上進行的。而微軟的.NETCore Docker Iamge基於Debian系系統,這里必須有用戶名,不然會報類似“crontab文件格式不合法“類似的錯誤。

創建好文件后將其配置為發布到輸出目錄,以便可以在dotnet pulish后被docker build所使用

然后添加一個文件runcron.sh,這將作為docker container的執行入口

rsyslogd
cron
touch /var/log/cron.log
tail -F /var/log/syslog /var/log/cron.log

最后一行分別輸出系統日志(cron執行情況,在syslog中)和dotnet app的輸出(在cron.log中,前提是前面添加了輸出的重定向)

tail命令將保證container一致執行,不會退出
也可以用docker的--restart=always參數控制容器的運行

修改VS創建的Dockerfile文件中的內容為類似如下的樣子:

FROM microsoft/dotnet:1.0-runtime
COPY . /publish
WORKDIR /publish
# 開始配置cron

# microsoft/dotnet:1.0-runtime鏡像不包含cron,先安裝,順便安裝rsyslog輸出日志
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
               cron \
               rsyslog \
    && rm -rf /var/lib/apt/lists/*

# 將我們編輯的crontab任務文件添加到cron配置目錄
ADD crontab /etc/cron.d/dotnet-cron
# 賦予crontab文件讀權限(rw/r/r)
RUN chmod 0644 /etc/cron.d/dotnet-cron
# 創建日志文件,以便執行tail命令
RUN touch /var/log/cron.log
# 賦予runcron.sh和rundotnet.sh執行權限
RUN chmod +x /publish/rundotnet.sh
RUN chmod +x /publish/runcron.sh
# 執行入口腳本runcron.sh
CMD ["bash","/publish/runcron.sh"]

在Dockerfile中我們向基礎容器添加了cron和rsyslog,在debian/ubuntu上(dotnet官方鏡像的基礎系統)cron需要靠rsyslog來輸出日志

最終輸出目錄要包含下列文件:

"appsettings.json",
"Dockerfile",
"crontab",
"rundotnet.sh",
"runcron.sh"

使用如下命令publish項目

dotnet publish --framework netcoreapp1.0 --configuration release --output publish

將publish中文件傳到安裝有docker的環境中准備生成docker image

生成Docker映像,運行Container

我們將生成運行(包含清理舊映像)的腳本整合為一個buildhelper.sh:

#!/bin/bash
contName=$1
contName=${contName:="containerName"}
imagName=$2
imagName=${imagName:="orgname/projname:tag"}
docker stop ${contName}
docker rm -f ${contName}
docker rmi ${imagName}
docker ps -a
docker images
sudo docker build --rm -t orgname/projname:tag .
sudo docker run --name containerName -it orgname/projname:tag

依然配置此文件發布到輸出到發布目錄(在Linux上要賦予執行權限)。

容器運行后可以連接到container的bash查看運行情況

docker exec -it containerName /bin/bash

如果感覺測試時,每次Docker Image都會進行apt-get安裝cron很麻煩,可以先生成一個包含cron以及rsyslog的鏡像,並以此作為這個例子的基礎鏡像。(按照博主提供的這些基本可以一次成功)

提示

所有shell腳本文件(xxx.sh),如果是UTF8格式,都要使用無BOM的UTF8編碼。或者使用ASCII。不然bash無法正常運行。
一鍵去BOM的方法: grep -r -I -l $'^\xEF\xBB\xBF' /path | xargs sed -i 's/^\xEF\xBB\xBF//g'


如果在Windows里編輯過sh文件,在linux種可能出現錯誤:“/bin/bash^M:損壞的解釋器: 沒有那個文件或目錄”
這是由於兩種系統換行符不同,Windows為\n\r,Linux為\n,而\r會被顯示為^M。
一鍵替換 sed -i 's/\r$//g' filename
大致有如下文件需要處理:

sed -i 's/\r$//g' crontab
sed -i 's/\r$//g' buildhelper.sh
sed -i 's/\r$//g' rundotnet.sh
sed -i 's/\r$//g' runcron.sh

crontab處理換行很重要,不然在linux會導致必須存在的空行無法被識別而無法正茬加載。


rsyslogd的如下錯誤提示可以安全忽略:
rsyslogd: imklog: cannot open kernel log(/proc/kmsg): Operation not permitted.
而錯誤:
Could not open output pipe '/dev/xconsole': No such file or directory
暫時不知道有無影響(實測不會對rsyslog的工作產生影響)


定時任務時間 對於定時任務,要注意如之前使用的基礎鏡像microsoft/dotnet:1.0.1-core的時區都是UTC時間,對於我們UTC+8的地方要自行在配置cron的時間時加上8個小時。
好辦法 在生成鏡像的時候直接修改好時區,Dockerfile中加入 RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime


可能會有小朋友質疑,用Docker運行這種定時任務有必要嗎,樓主感覺Docker最主要的還是屏蔽不同底層系統的差異。順便可以讓不同的應用的定時任務可以分離。本文也是提供一種方法,供有需要的童鞋參考。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM