• .NET遇上Docker


    配置项目的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 $'^xEFxBBxBF' /path | xargs sed -i 's/^xEFxBBxBF//g'


    如果在Windows里编辑过sh文件,在linux种可能出现错误:“/bin/bash^M:损坏的解释器: 没有那个文件或目录”
    这是由于两种系统换行符不同,Windows为 ,Linux为 ,而 会被显示为^M。
    一键替换 sed -i 's/ $//g' filename
    大致有如下文件需要处理:

    sed -i 's/
    $//g' crontab
    sed -i 's/
    $//g' buildhelper.sh
    sed -i 's/
    $//g' rundotnet.sh
    sed -i 's/
    $//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最主要的还是屏蔽不同底层系统的差异。顺便可以让不同的应用的定时任务可以分离。本文也是提供一种方法,供有需要的童鞋参考。

  • 相关阅读:
    jvm 更多链接
    JVM 内存初学 (堆(heap)、栈(stack)和方法区(method) )
    python 排序
    python 第K个语法符号
    python conf.ini 文件的使用
    python 表示数字
    window 下安装redis
    python redis相关操作
    python 矩阵乘法
    python 查找两个字符串a,b中的最长公共子串
  • 原文地址:https://www.cnblogs.com/lsxqw2004/p/6773882.html
Copyright © 2020-2023  润新知