• 在 Docker 容器内集成 Crontab 定时任务


    有时候,我们需要在容器内执行某种定时任务。譬如,Kerberos 客户端从 KDC 中获取到的 TGT 默认有效期为 10 个小时,一旦这个票据失效,我们将无法使用单点登录功能。此时,我们就需要一个定时任务来定时刷新票据。此前,博主为大家介绍过 QuartzHangfire 这样的定时任务系统,而对于 Linux 来说,其内置的 crontab 是比以上两种方案更加轻量级的一种方案,它可以定时地去执行 Linux 中的命令或者是脚本。对应到 Kerberos 的这个例子里面,从 KDC 申请一个新的票据,我们只需要使用 kinit 这个命令即可。因此,在今天这篇博客里,我想和大家分享一下,如何在 Docker 容器内集成 Crontab 定时任务,姑且算是在探索 Kerberos 过程中的无心插柳,Kerberos 认证这个话题博主还需要再消化一下,请大家拭目以待,哈哈!

    Crontab 基础知识

    众所周知,Linux 中的所有内容都是以文件的形式保存和管理的,即:一切皆为文件。那么,自然而然的地,Linux 中的定时任务同样遵循这套丛林法则,因此,当我们谈论到在 Linux 中执行定时任务这个话题的时候,本质上依然是在谈论某种特定格式的文件。事实上,这类文件通常被称为 crontab 文件,这是一个来源于希腊语 chronos 的词汇,其含义是时间。Linux 会定时(每分钟)读取 crontab 文件中的指令,检查是否有预定任务需要执行。下面是一个 crontab 文件的示例:

    # 每分钟执行一次 ls 命令
    * * * * * /bin/ls
    # 周一到周五的下午5点发邮件
    0 17 * * 1-5 mail -s "hi" alex@162.com
    # 每月1号和15号执行脚本
    0 0 1,15 * * /var/www/newbee/check.sh
    # 00:20、02:20、04:20...执行 echo 命令
    20 0-23/2 * * * echo "hello"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以注意到,crontab 文件就像是一份写给 Linux 的日程表,它会告诉 Linux 每个时刻应该做什么样的事情。虽然这些事情看起来都显得琐碎繁复,甚至都精确到了每一分钟,可如果我们观察得足够仔细的话,就会发现这些定时任务都可以用下面的形式来表示,即:

    * * * * * <program> 
    
    • 1

    其中, 可以是一个命令或者脚本,而对于 前面的这部分,我们通常将其称为 cron 表达式,其含义定义如下:

    *    *    *    *    *
    -    -    -    -    -
    |    |    |    |    |
    |    |    |    |    +----- 星期(0 - 6)
    |    |    |    +---------- 月份(1 - 12) 
    |    |    +--------------- 日期(1 - 31)
    |    +-------------------- 小时(0 - 23)
    +------------------------- 分钟(0 - 23)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    例如,49 19 24 11 * 这串神秘代码表示的是:11 月 24 日 19 时 49 分,在此基础上,当第一位为 * 时,表示每分钟都执行 ;当第一位为 a-b 时,表示在 a 分钟到 b 分钟这段时间内执行 ;当第一位为 */n 时,表示每隔 n 分钟执行一次 ;当第一位为 a,b,c… 时表示第 a、b、c…分钟时执行一次 。依次类推,我们可以在不同的时间单位上使用这些表达式。例如:

    # 每周六晚上00:00
    0 0 * * 6
    # 每周六和周天晚上00:00
    0 0 * * 0,6
    # 每周六和周天晚上00:00 ~ 04:00
    0 0-4 * * 0,6
    # 每周六和周天晚上00:00 和 01:00 执行
    0 0,1 * * 0,6
    # 每周六和周天晚上每隔两个小时执行一次
    0 */2 * * 0,6
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    现在,回到我们一开始的问题,如何通过定时任务来解决 Kerberos 票据过期的问题呢?首先,我们准备一个名为 renew.sh 的脚本,这是一个非常简单的脚本,无论你是否接触过 Kerberos,是否了解 Principal 或是 SPN 这些概念,你都可以轻而易举地上手:

    echo [$(date)] 'request a new ticket for HTTP/web.your-domain.com@YOUR-DOMAIN.COM'
    kinit -kt /etc/apache2/krb-container.keytab HTTP/web.your-domain.com@YOUR-DOMAIN.COM
    
    • 1
    • 2

    接下来,我们只需要在真正的 crontab 文件里引用这个脚本即可,从上面的表达式定义出发,我们可以知道,这个定时任务每隔 10 分钟执行一次:

    cronfile='/usr/crontab/cron.conf'
    renewTicket='*/10 * * * * /usr/crontab/renew.sh'
    echo "$renewTicket" | tee -a $cronfile
    crontab $cronfile
    /etc/init.d/cron reload
    /etc/init.d/cron restart
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这里运用到的第一个技巧是 crontab 命令,它可以生成、编辑、删除、列举定时任务:

    # 生成定时任务
    crontab <-u user> file
    # 编辑定时任务
    crontab <-u user> -e
    # 删除定时任务
    crontab <-u user> -r
    # 列举定时任务
    crontab <-u user> -l
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在本文的示例中,博主没有指定 -u 这个参数,这是因为博主使用的是 root 用户。事实上,在指定了编辑器的情况下,你可以使用 -e 参数通过交互式的命令行来编写定时任务:

    # 使用 vim 作为 crontab 的编辑器
    export EDITOR="/usr/bin/vim"
    crontab -e 
    
    • 1
    • 2
    • 3

    此时,我们就可以通过 vim 修改这份 crontab 文件。相信此时此刻,你会有一种感觉,这个 crontab 文件不就是一份由 cron 表达式组成的日程表吗?事实上,Linux 系统会维护一份 crontab 文件,我们自己编写的 crontab 文件会在执行 crontab 命令后合入到这份 crontab 文件里:

    在这里插入图片描述

    需要注意到的是,当我们在 Docker 容器内执行定时任务时,需要确保生成定时任务的这部分脚本,在容器的入口位置被执行,简而言之,我们应该有一个名为 entrypoint.sh 的脚本,并用它来替代容器的默认入口:

    CMD ["sh", "/usr/docker/entrypoint.sh"]
    
    • 1

    一旦定时任务被创建出来,我们总是可以使用 crontab -l 命令列举出当前用户下有哪些定时任务:

    在这里插入图片描述

    Crontab 日志问题

    在这个过程中,博主发现一个问题,那就是看不到这些定时任务的执行日志。通常,这些日志位于以下路径:/var/log/cron.log,我们可以使用下面的命令:

    tail -f /var/log/cron.log
    
    • 1

    此时,我们会得到下面的错误,显然,这是日志没有写入的缘故:

    在这里插入图片描述

    在尝试安装 rsyslog 以及修改配置启用定时任务日志后,该问题依然存在:

    sudo apt-get install -y rsyslog;
    sudo vim /etc/rsyslog.d/50-default.conf;
    
    • 1
    • 2

    有趣的是,这个做法在 Ubuntu 最新版本中是生效的,而两者的区别是:前者是容器,后者是虚拟机。

    在这里插入图片描述

    吾上下求索而不得,最终采用下面的方案来解决,即直接重定向脚本输出到容器的标准输出:

    echo [$(date)] 'Hello' > /proc/1/fd/1
    
    • 1

    此时,我们就可以在容器中看到对应的日志,虽然这只是一个小问题,可个人感觉还是挺折腾人的:

    在这里插入图片描述

    Crontab 环境变量

    如果你希望在定时任务脚本中引用环境变量,例如:

    echo [$(date)] 'request a new ticket for HTTP/'$NEXTCLOUD_SERVER_NAME'@'$DOMAIN_SERVER_NAME > /proc/1/fd/1
    kinit -kt /etc/apache2/krb-container.keytab HTTP/$NEXTCLOUD_SERVER_NAME@$DOMAIN_SERVER_NAME
    
    • 1
    • 2

    此时,你会注意到容器输出的日志中,这些环境变量的值都是缺失的,虽然这些环境变量确实存在:

    在这里插入图片描述

    如果你求助于搜索引擎,大概虑会得到下面的答案:

    #!/bin/sh
    . /etc/profile
    . ~/.bash_profile
    
    • 1
    • 2
    • 3

    事实上,这个方案在主机环境下没有问题。如果是在容器环境下,建议使用下面的方案:

    env >> /etc/default/locale
    
    • 1

    此时,就可以达成我们预期的效果:

    在这里插入图片描述

    参考链接

  • 相关阅读:
    【小型物体测速仪】只有原理,无代码
    【大数据】Flink SQL 语法篇(八):集合、Order By、Limit、TopN
    Collections.addAll()和list.addAll()不同之处说明
    再谈微服务负载均衡器:Ribbon均衡器和SpringCloud自带LoadBalancer均衡器
    网站分享~~
    敏捷开发和瀑布开发的区别及相关工具推荐
    Java开发快递物流项目
    Java IO包之序列化与ObjectInputStream、ObjectOutputStream的简介说明
    DevEco Studio如何在真机设备上运行HarmonyOS应用之必备的签名文件怎么做
    【操作系统-存储】存储的分配和管理方式
  • 原文地址:https://blog.csdn.net/qinyuanpei/article/details/128091119