Cron容器部署
cron容器部署
1. 方案一:使用shell语言部署
#说明:
#不能使用cron,不适合在容器运行,包括权限等。
#使用supercronic,版本v0.2.33
#supercronic 官网 https://github.com/aptible/supercronic
#第一次执行
sudo -i
#准备工作copy supercronic 到Dockerfile同级目录
#设置权限
#chown root:root ./supercronic
#第二次执行
#crontab_filename
crontab_filename="crontab"
script_hello_world_filename="hello_world.sh"
#host
#/docker-data/dockerfiles/cron 存放Dockerfile文件
host_dockerfiles_path="/docker-data/dockerfiles/cron"
host_dockerfile_filename=${host_dockerfiles_path}/Dockerfile
host_dockerfiles_path_scripts_path=${host_dockerfiles_path}/scripts
host_dockerfiles_etc_conf_d_path=${host_dockerfiles_path}/etc/cron.d
host_dockerfiles_etc_conf_d_crontab_filename=${host_dockerfiles_path}/etc/cron.d/${crontab_filename}
host_relative_dockerfiles_etc_conf_d_crontab_filename=/etc/cron.d/${crontab_filename}
#/docker-data/cron 存放容器挂载目录
host_data_cron_path="/docker-data/cron"
host_crontab_filename=${host_data_cron_path}/${crontab_filename}
host_scripts_path=${host_data_cron_path}/scripts
host_script_filename=${host_scripts_path}/${script_filename}
host_log_path=${host_data_cron_path}/log
host_script_hello_world_log_filename=${host_log_path}/hello-world.log
#映像
#映像名称
image_name="supercronic:alpine"
#crontab_path
image_cron_d_path=/etc/cron.d
#crontab_filename
image_cron_d_crontab_filename=${image_cron_d_path}/${crontab_filename}
#scripts_path
image_scripts_path="/scripts"
#log_path
image_log_path=/var/log
#hello-world.log
image_hello_world_log_filename=${image_log_path}/hello-world.log
#container
#容器名称
docker_container_name="supercronic"
#创建目录
#host
if [ ! -d ${host_dockerfiles_path} ]; then
mkdir -p ${host_dockerfiles_path}
fi
if [ ! -d ${host_dockerfiles_path_scripts_path} ]; then
mkdir -p ${host_dockerfiles_path_scripts_path}
fi
if [ ! -d ${host_dockerfiles_etc_conf_d_path} ]; then
mkdir -p ${host_dockerfiles_etc_conf_d_path}
fi
if [ ! -d ${host_log_path} ]; then
mkdir -p ${host_log_path}
fi
if [ ! -d ${host_scripts_path} ]; then
mkdir -p ${host_scripts_path}
fi
#crontabl
echo "* * * * * echo hello-world >> ${image_hello_world_log_filename} " > \
${host_dockerfiles_etc_conf_d_crontab_filename}
chmod 644 ${host_dockerfiles_etc_conf_d_crontab_filename}
cp ${host_dockerfiles_etc_conf_d_crontab_filename} ${host_crontab_filename}
#第三次执行
#创建Dockerfile文件
cat > ${host_dockerfile_filename} << eof
# 使用 Alpine 作为基础镜像
FROM alpine:latest
# 安装必要的包,及supercronic
# curl openssl ca-certificates jq 是acmesh需要的
#tzdata 时区相关包
RUN apk add --no-cache curl bash openssl ca-certificates jq tzdata
RUN rm -rf /var/cache/apk/*
#设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo "Asia/Shanghai" > /etc/timezone
ENV TZ=Asia/Shanghai
#RUN curl -Lo /usr/local/bin/supercronic https://github.com/aptible/supercronic/releases/latest/download/supercronic-linux-amd64
COPY ./supercronic /usr/local/bin/supercronic
RUN chmod +x /usr/local/bin/supercronic
# 创建一个目录用于存放脚本和配置文件
RUN mkdir -p ${image_cron_d_path} ${image_scripts_path}
# 将定时任务配置文件和脚本复制到容器中
COPY ./etc/cron.d/crontab ${image_cron_d_crontab_filename}
#COPY my_script.sh /scripts/my_script.sh
# 设置执行权限
#RUN chmod +x /scripts/my_script.sh
RUN chmod 0644 ${image_cron_d_crontab_filename}
#acme需要的配置
#创建网站ssl相关目录
#RUN mkdir -p /nginx/config/conf.d/ssl
#RUN mkdir -p /nginx/html/ssl
#创建docker映射目录
#RUN mkdir -p /var/run
# 启动 Supercronic 并读取 cron 配置文件
CMD ["/usr/local/bin/supercronic", "${image_cron_d_crontab_filename}"]
eof
#第四次执行
#生成映像,最后一个参数是设置命令执行的当前目录
docker build -t ${image_name} -f ${host_dockerfile_filename} ${host_dockerfiles_path}
#第五次执行
#运行容器
#里面包含了:acme 需要的增加nginx的映射目录 和 docker的映射
docker run -d \
-v ${host_crontab_filename}:${image_cron_d_crontab_filename} \
-v ${host_scripts_path}:${image_scripts_path} \
-v ${host_log_path}:${image_log_path} \
-v /docker-data/nginx/config/conf.d/ssl:/nginx/config/conf.d/ssl \
-v /docker-data/nginx/html/ssl:/nginx/html/ssl \
-v /var/run/docker.sock:/var/run/docker.sock \
--name ${docker_container_name} \
--restart always \
${image_name}
#测试
#查看是否每分钟会多出一个 hello-world 的内容
cat $host_script_hello_world_log_filename
#使用
#修改配置文件,可以注释掉不用的hello-world样例
vim $host_dockerfiles_etc_conf_d_crontab_filename
#重启容器
docker restart $docker_container_name
#查看日志
docker logs $docker_container_name2.方案二「推荐」:docker-compose
准备工作
crontab 文件
Dockerfile
docker-compose.yaml文件
执行docker-compose.yaml
#pwd /docker-data/docker-composes/cron/
docker compose up -d查看docker-ps supercronic 是否没有重启
查看docker logs supercronic 是否没有错误日志
查看宿主机:/docker-data/cron/log 日志文件是否持续输出hello-world
一切正常:注释掉/docker-data/cron/crontab:/etc/cron.d/crontab 计划任务,重启容器:docker restart supercronic
3.在cron下配置acmesh
acme官网:
https://github.com/acmesh-official/acme.shacme.sh -h 显示帮助信息
映射目录检查:
检查容器是否包含映射目录:
docker inspect supercronic"Mounts": [ { "Type": "bind", "Source": "/docker-data/cron/log", "Destination": "/var/log", "Mode": "rw", "RW": true, "Propagation": "rprivate" }, { "Type": "bind", "Source": "/docker-data/nginx/config/conf.d/ssl", "Destination": "/nginx/config/conf.d/ssl", "Mode": "rw", "RW": true, "Propagation": "rprivate" }, { "Type": "bind", "Source": "/docker-data/nginx/html/ssl", "Destination": "/nginx/html/ssl", "Mode": "rw", "RW": true, "Propagation": "rprivate" }, { "Type": "bind", "Source": "/var/run/docker.sock", "Destination": "/var/run/docker.sock", "Mode": "rw", "RW": true, "Propagation": "rprivate" }, { "Type": "bind", "Source": "/docker-data/cron/crontab", "Destination": "/etc/cron.d/crontab", "Mode": "rw", "RW": true, "Propagation": "rprivate" }, { "Type": "bind", "Source": "/docker-data/cron/scripts", "Destination": "/scripts", "Mode": "rw", "RW": true, "Propagation": "rprivate" } ]
宿主机文件准备:
/docker-data/cron/scripts/ ├── acme └── acme-sh #此文件夹需要拷贝过来,chown -R root:root acme-sh └── container-nginx-reload.sh #此文件需要拷贝过来,核对容器中的nginx容器名称acme-sh 目录内容:https://github.com/acmesh-official/acme.sh 中的内容
container-nginx-reload.sh
#!/bin/bash # 定义变量 CONTAINER_NAME="nginx" # 替换为你的容器名称 COMMAND='["nginx", "-s", "reload"]' # 创建 exec 实例 EXEC_ID=$(curl -s --unix-socket /var/run/docker.sock -X POST \ -H "Content-Type: application/json" \ -d "{ \"AttachStdout\": true, \"AttachStderr\": true, \"Cmd\": $COMMAND }" \ http://localhost/containers/$CONTAINER_NAME/exec | jq -r '.Id') # 检查是否成功获取 EXEC_ID if [ -z "$EXEC_ID" ]; then echo "Failed to create exec instance." exit 1 fi # 启动 exec 实例 curl -s --unix-socket /var/run/docker.sock -X POST \ -H "Content-Type: application/json" \ -d '{}' \ http://localhost/exec/$EXEC_ID/start安装acmesh
docker exec -it supercronic bash #进入容器 #以下命令在容器内部执行 accountemai="lidongzhang@rstone.com.cn" web_site_name="api.rstone.net.cn" cd /scripts/acme/acme-sh ./acme.sh --install \ --home /scripts/acme/home \ --accountemail ${accountemai} \ --log /scripts/acme/acme.sh.log \ --nocron安装时可能会提示:
It is recommended to install socat first.
We use socat for the standalone server, which is used for standalone mode.
If you don't want to use standalone mode, you may ignore this warning.
#这个提示是建议安装socat,来提供standalone server,
#我们不需要使用这种申请证书,因为我们使用的是nginxacme.sh 运行参数说明
# --home is a customized dir to install acme.sh in. By default, it installs into ~/.acme.sh
# --config-home is a writable folder, acme.sh will write all the files(including cert/keys, configs) there. By default, it's in --home
# --cert-home is a customized dir to save the certs you issue. By default, it's saved in --config-home.
# --accountemail is the email used to register an account to Let's Encrypt, you will receive a renewal notice email here.
# --accountkey is the file saving your account private key. By default, it's saved in --config-home.
# --useragent is the user-agent header value used to send to Let's Encrypt.
# --nocron install acme.sh without cronjob
# --issue Issue a cert.生成证书
mkdir -p /nginx/html/ssl/${web_site_name}
cd /scripts/acme/home
#设置联系人邮箱
./acme.sh --register-account -m lidongzhang@rstone.com.cn --home /scripts/acme/home
#更改默认 CA, 否则生成证书的时候会报错
./acme.sh --set-default-ca --server letsencrypt --home /scripts/acme/home
#申请证书
./acme.sh --issue -d ${web_site_name} -w /nginx/html/ssl/${web_site_name} --home /scripts/acme/home
安装证书
mkdir -p /nginx/config/conf.d/ssl/${web_site_name}
cd /scripts/acme/home
./acme.sh --install-cert -d ${web_site_name} \
--key-file /nginx/config/conf.d/ssl/${web_site_name}/key.pem \
--fullchain-file /nginx/config/conf.d/ssl/${web_site_name}/cert.pem \
--home /scripts/acme/home \
--reloadcmd "/scripts/acme/container-nginx-reload.sh"给网站配置ssl,完善网站的nginx配置文件的https「ssl」部分
配置crontab文件
#每天夜间 23:25 分执行, All the certs will be renewed automatically every 60 days.
echo "25 23 * * * /scripts/acme/home/acme.sh --cron --home /scripts/acme/home > /dev/null" >> /etc/cron.d/crontab退出容器
重启cron容器
docker restart supercronic测试
在浏览器中打开网站 https://${web_site_name} 看证书的日期是否是最新的
使用curl查看证书
执行:
curl -vI https://www.rstone.com.cn --ssl参数说明:
-v:显示详细输出(verbose)。
-I:只请求 HEAD 信息(避免下载完整内容)。
--ssl:强制使用 SSL(默认已启用)。
执行结果:
Warning: --ssl is an insecure option, consider --ssl-reqd instead * Host www.rstone.com.cn:443 was resolved. * IPv6: (none) * IPv4: 1.94.249.37 * Trying 1.94.249.37:443... * ALPN: curl offers h2,http/1.1 * TLSv1.3 (OUT), TLS handshake, Client hello (1): * CAfile: /etc/ssl/cert.pem * CApath: /etc/ssl/certs * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS handshake, Certificate (11): * TLSv1.2 (IN), TLS handshake, Server key exchange (12): * TLSv1.2 (IN), TLS handshake, Server finished (14): * TLSv1.2 (OUT), TLS handshake, Client key exchange (16): * TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.2 (OUT), TLS handshake, Finished (20): * TLSv1.2 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384 / x25519 / id-ecPublicKey * ALPN: server accepted http/1.1 * Server certificate: * subject: CN=www.rstone.com.cn * start date: Jul 20 08:11:07 2025 GMT * expire date: Oct 18 08:11:06 2025 GMT * subjectAltName: host "www.rstone.com.cn" matched cert's "www.rstone.com.cn" * issuer: C=US; O=Let's Encrypt; CN=E5 * SSL certificate verify ok. * Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384 * Certificate level 1: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using sha256WithRSAEncryption * Certificate level 2: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption * Connected to www.rstone.com.cn (1.94.249.37) port 443 * using HTTP/1.x > HEAD / HTTP/1.1 > Host: www.rstone.com.cn > User-Agent: curl/8.12.1 > Accept: */* > * Request completely sent off < HTTP/1.1 200 OK HTTP/1.1 200 OK < Server: nginx/1.22.0 Server: nginx/1.22.0 < Date: Sun, 20 Jul 2025 09:44:41 GMT Date: Sun, 20 Jul 2025 09:44:41 GMT < Content-Type: text/html;charset=utf-8 Content-Type: text/html;charset=utf-8 < Connection: keep-alive Connection: keep-alive < x-powered-by: Nuxt x-powered-by: Nuxt < * Connection #0 to host www.rstone.com.cn left intact重点内容
* Server certificate: #域名 * subject: CN=www.rstone.com.cn #证书开始时间 * start date: Jul 20 08:11:07 2025 GMT #证书到期时间 * expire date: Oct 18 08:11:06 2025 GMT * subjectAltName: host "www.rstone.com.cn" matched cert's "www.rstone.com.cn" * issuer: C=US; O=Let's Encrypt; CN=E5 #ssl证书验证ok * SSL certificate verify ok.在supercronic内部测试
#执行 看是否正确「在容器内部」 /scripts/acme/home/acme.sh --cron --home /scripts/acme/home #输出内容,如果是不需要更新: #[Wed Mar 26 13:08:09 CST 2025] ===Starting cron=== #[Wed Mar 26 13:08:09 CST 2025] Renewing: 'tx.rstone.com.cn' #[Wed Mar 26 13:08:09 CST 2025] Renewing using Le_API=https://acme-v02.api.letsencrypt.org/directory #[Wed Mar 26 13:08:09 CST 2025] Skipping. Next renewal time is: 2025-05-24T04:51:39Z #[Wed Mar 26 13:08:09 CST 2025] Add '--force' to force renewal. #[Wed Mar 26 13:08:09 CST 2025] Skipped tx.rstone.com.cn_ecc #[Wed Mar 26 13:08:09 CST 2025] Renewing: 'www.rstone.com.cn' #[Wed Mar 26 13:08:09 CST 2025] Renewing using Le_API=https://acme.zerossl.com/v2/DV90 #[Wed Mar 26 13:08:09 CST 2025] Skipping. Next renewal time is: 2025-05-24T04:49:20Z #[Wed Mar 26 13:08:09 CST 2025] Add '--force' to force renewal. #[Wed Mar 26 13:08:09 CST 2025] Skipped www.rstone.com.cn_ecc #[Wed Mar 26 13:08:09 CST 2025] Renewing: 'www.rstone.net.cn' #[Wed Mar 26 13:08:09 CST 2025] Renewing using Le_API=https://acme-v02.api.letsencrypt.org/directory #[Wed Mar 26 13:08:09 CST 2025] Skipping. Next renewal time is: 2025-05-24T04:51:57Z #[Wed Mar 26 13:08:09 CST 2025] Add '--force' to force renewal. #[Wed Mar 26 13:08:10 CST 2025] Skipped www.rstone.net.cn_ecc #[Wed Mar 26 13:08:10 CST 2025] ===End cron=== #如果需要更新里面会有更新的内容信息 #如果出现问题,可以在后面加 --debug 参数,查看问题出在了哪里。 #手动重新生成证书: 此过程包括:申请证书,安装证书,执行reloadcmd /scripts/acme/home/acme.sh --renew -d ${web_site_name} --force --home /scripts/acme/home #原理解释: #acme 的home目录「安装的时候指定的」下面: #文件和目录列表: account.conf ca deploy notify www.rstone.net.cn_ecc acme.sh certs dnsapi tx.rstone.com.cn_ecc acme.sh.env data http.header www.rstone.com.cn_ecc #里面保存着配置信息和所有申请域名的目录,一个域名一个目录,里面有证书和要部署到的路径 #在我们执行证书申请和安装证书命令的时候必须指定 --home 路径,这样才可以生成域名的目录保存相关证书和配置信息在supercronic内部
/scripts/acme/home 里面每个配置过的域名会有一个 「域名_ecc」 的目录