惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Hugging Face - Blog
Hugging Face - Blog
IT之家
IT之家
Jina AI
Jina AI
Google DeepMind News
Google DeepMind News
大猫的无限游戏
大猫的无限游戏
The GitHub Blog
The GitHub Blog
腾讯CDC
L
LangChain Blog
P
Proofpoint News Feed
M
MIT News - Artificial intelligence
F
Fortinet All Blogs
量子位
Apple Machine Learning Research
Apple Machine Learning Research
Cloudbric
Cloudbric
B
Blog RSS Feed
B
Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
Vercel News
Vercel News
S
Schneier on Security
Project Zero
Project Zero
宝玉的分享
宝玉的分享
美团技术团队
MyScale Blog
MyScale Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Spread Privacy
Spread Privacy
T
Threatpost
A
Arctic Wolf
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hacker News - Newest:
Hacker News - Newest: "LLM"
爱范儿
爱范儿
Recorded Future
Recorded Future
C
CERT Recently Published Vulnerability Notes
T
Threat Research - Cisco Blogs
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
SecWiki News
SecWiki News
H
Hacker News: Front Page
AWS News Blog
AWS News Blog
博客园_首页
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tor Project blog
C
Check Point Blog
L
Lohrmann on Cybersecurity
F
Full Disclosure
TaoSecurity Blog
TaoSecurity Blog
Google Online Security Blog
Google Online Security Blog
云风的 BLOG
云风的 BLOG
The Last Watchdog
The Last Watchdog
Martin Fowler
Martin Fowler

元视角

.NET 生态下的 Agent 框架选型:从 ReAct 到原生推理 - 元视角 从「能用」到「好用」:LLM 流式响应实现方式的探索之路 - 元视角 当我用 2000 条聊天记录,让 AI 为我画一幅自画像 - 元视角 基于 Supabase 的 AI 应用开发探索 - 元视角 微博 × MCP:社交媒体新玩法解锁 - 元视角 四点钟海棠花未眠 - 元视角 Semantic Kernel × MCP:智能体的上下文增强探索 - 元视角 基于 K-Means 聚类分析实现人脸照片的快速分类 - 元视角 容器技术驱动下的代码沙箱实践与思考 - 元视角 温故而知新:后端通用查询方案的再思考 - 元视角 浅议 CancellationToken 在前后端协同取消场景中的应用 - 元视角 Semantic Kernel 视角下的 Text2SQL 实践与思考 - 元视角 关于 ChatGPT 的流式传输,你需要知道的一切 - 元视角 RAG 的是与非、Rewrite 和 Rerank - 元视角 使用 EFCore 和 PostgreSQL 实现向量存储及检索 - 元视角 基于 LLaMA 和 LangChain 实践本地 AI 知识库 - 元视角 使用 llama.cpp 在本地部署 AI 大模型的一次尝试 - 元视角 如何为 Git 配置多个 SSH Key - 元视角 C# 使用 LibUsbDotNet 实现 USB 设备检测 - 元视角 基于 C# 实现样式与数据分离的打印方案 - 元视角 基于 SVG 的图形交互方案实践 - 元视角 前端视频播放技术概览 - 元视角 温故而知新,再话 Python 动态导入 - 元视角 后 GPT 时代,NLP 不存在了? - 元视角 视频是不能 P 的系列:使用 Milvus 实现海量人脸快速检索 - 元视角 GDI+下字体大小自适应方案初探 - 元视角 小爱音箱集成 ChatGPT 的不完全教程 - 元视角 程序员视角下的三体世界随想 - 元视角 关于 Docker 容器配置信息的渐进式思考 - 元视角 为你的服务器集成 LDAP 认证 - 元视角 似花还似非花 - 元视角 视频是不能 P 的系列:使用 Dlib 实现人脸识别 - 元视角 浅议分布式链路追踪与日志的整合 - 元视角 关于 Git 大文件上传这件小事 - 元视角 .NET 进程内队列 Channel 的入门与应用 - 元视角 使用 Fody 实现 .NET 的静态编织 - 元视角 .NET Core + ELK 搭建可视化日志分析平台(下) - 元视角 聊一聊前端图片懒加载背后的故事 - 元视角 支持外部链接跳转的 Vue Router 扩展实现 - 元视角 视频是不能 P 的系列:OpenCV 和 Dlib 实现表情包 - 元视角 不得不说的 ASP.NET Core 集成测试 - 元视角 再议 DDD 视角下的 EFCore 与 领域事件 - 元视角 Vue.js 前端项目容器化部署实践极简教程 - 元视角 再见,人间四月天 - 元视角 Python 图像风格化迁移助力画家梦想 - 元视角 利用 ASP.NET Core 中的标头传播实现分布式链路追踪 - 元视角 利用 gRPC 实现文件的上传与下载 - 元视角 七种武器:延迟队列的原理和实现总结 - 元视角 gRPC 流式传输极简入门指南 - 元视角 Envoy 集成 Jaeger 实现分布式链路追踪 - 元视角 浅议非典型 Web 应用场景下的身份认证 - 元视角 gRPC 借助 Any 类型实现接口的泛化调用 - 元视角 分布式丛林探险系列之 Redis 集群模式 - 元视角 分布式丛林探险系列之 Redis 主从复制模式 - 元视角 通过 Python 预测 2021 年双十一交易额 - 元视角 gRPC 搭配 Swagger 实现微服务文档化 - 元视角 SSL/TLS 加密传输与数字证书的前世今生 - 元视角 使用 Python 自动识别防疫健康码 - 元视角 你不可不知的容器编排进阶技巧 - 元视角 ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理 - 元视角 再话 AOP,从简化缓存操作说起 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT) - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的监控预警 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的反向代理 - 元视角 ASP.NET Core gRPC 打通前端世界的尝试 - 元视角 EFCore 实体命名约定库:EFCore.NamingConventions - 元视角 ASP.NET Core gRPC 集成 Polly 实现优雅重试 - 元视角 ASP.NET Core gRPC 健康检查的探索与实现 - 元视角 ASP.NET Core gRPC 拦截器的使用技巧分享 - 元视角 SnowNLP 使用自定义语料进行模型训练 - 元视角 使用 HttpMessageHandler 实现 HttpClient 请求管道自定义 - 元视角 ABP vNext 的实体与服务扩展技巧分享 - 元视角 ABP vNext 对接 Ant Design Vue 实现分页查询 - 元视角 源代码探案系列之 .NET Core 跨域中间件 CORS - 元视角 源代码探案系列之 .NET Core 限流中间件 AspNetCoreRateLimit - 元视角 源代码探案系列之 .NET Core 并发限制中间件 ConcurrencyLimiter - 元视角 通过 EmbededFileProvider 实现 Blazor 的静态文件访问 - 元视角 低代码,想说爱你不容易 - 元视角 记一次失败的 ThoughtWorks 面试经历 - 元视角 从 C# 1.0 到 C# 9.0,历代 C# 语言特性一览 - 元视角 通过 Python 分析 2020 年全年微博热搜数据 - 元视角 基于 Python 和 Selenium 实现 CSDN 一键三连自动化 - 元视角 使用多线程为你的 Python 爬虫提速的 N 种姿势,你会几种? - 元视角 实现网页长截图的常见思路总结 - 元视角 温故而知新,由 ADO.NET 与 Dapper 所联想到的 - 元视角 视频是不能 P 的系列:OpenCV 人脸检测 - 元视角 作为技术宅的我,是这样追鬼滅の刃的 - 元视角 使用 Python 抽取《半泽直树》原著小说人物关系 - 元视角 厉害了!打工人用 Python 分析西安市职位信息 - 元视角 使用 dotTrace 对 .NET 应用进行性能分析与优化 - 元视角 一道 HashSet 面试题引发的蝴蝶效应 - 元视角 基于选项模式实现.NET Core 的配置热更新 - 元视角 Dapper.Contrib 在 Oracle 环境下引发 ORA-00928 异常问题的解决 - 元视角 .NET Core 中对象池(Object Pool)的使用 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(下):EventBus 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(中):RabbitMQ 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(上):基础篇 - 元视角 记一次从已损坏的 Git 仓库中找回代码的经历 - 元视角 .NET Core 原生 DI 扩展之属性注入实现 - 元视角 .NET Core 原生 DI 扩展之基于名称的注入实现 - 元视角
在 Docker 容器内集成 Crontab 定时任务 - 元视角
飞鸿踏雪 · 2022-11-24 · via 元视角

有时候,我们需要在容器内执行某种定时任务。譬如,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"

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

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

*    *    *    *    *
-    -    -    -    -
|    |    |    |    |
|    |    |    |    +----- 星期(0 - 6)
|    |    |    +---------- 月份(1 - 12) 
|    |    +--------------- 日期(1 - 31)
|    +-------------------- 小时(0 - 23)
+------------------------- 分钟(0 - 23)

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

# 每周六晚上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

现在,回到我们一开始的问题,如何通过定时任务来解决 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

接下来,我们只需要在真正的 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

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

# 生成定时任务
crontab <-u user> file
# 编辑定时任务
crontab <-u user> -e
# 删除定时任务
crontab <-u user> -r
# 列举定时任务
crontab <-u user> -l

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

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

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

通过命令行交互式编辑定时任务 通过命令行交互式编辑定时任务

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

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

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

显示所有的定时任务 显示所有的定时任务

Crontab 日志问题

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

tail -f /var/log/cron.log

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

无法查看定时任务日志 无法查看定时任务日志

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

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

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

查看定时任务日志 查看定时任务日志

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

echo [$(date)] 'Hello' > /proc/1/fd/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

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

定时任务中引用环境变量-A 定时任务中引用环境变量-A

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

#!/bin/sh
. /etc/profile
. ~/.bash_profile

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

env >> /etc/default/locale

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

定时任务中引用环境变量-B 定时任务中引用环境变量-B

参考链接