[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"content-perform-scheduled-tasks-using-gitLab-pipelines":3,"$f3s6s879g0uqrk":22},{"id":4,"type":5,"slug":6,"title":7,"date":8,"category":9,"tags":10,"body_markdown":14,"permalink":15,"excerpt_src":15,"media_type":15,"media_title":15,"media_author":15,"media_url":15,"rating":15,"layout":15,"pv":16,"admin_only":16,"created_at":17,"updated_at":17,"deleted_at":15,"status":18,"html":19,"excerpt":20,"cover":21},135,"article","perform-scheduled-tasks-using-gitLab-pipelines","使用Gitlab Pipelines执行定时任务","2023-06-16 12:00","tech",[11,12,13],"定时任务","Linux","Gitlab","\n## 背景\n\n在Linux服务器下执行定时任务是一个非常常见的需求，我们可能需要定时备份数据库、定时执行程序脚本、定时清理日志等等。在旧版本Linux下，我们通常会使用`crontab`来执行定时任务，而新版本的Linux系统中则更推荐用systemd的`timer`来执行定时任务。\n\n但是systemd的配置麻烦又罗嗦，需要先定义一个`service`，再定义一个`timer`来调用`service`，而且`timer`的配置文件中还需要指定`OnCalendar`来指定定时任务的执行时间，这个时间格式又不是很好记。\n\n此外即使定义好了，管理起来也并不容易，更不要说排查问题了。\n\n而如果机器上使用Docker等容器环境，定时任务就更麻烦了：为了应用的可移植性，希望尽可能将定时任务放到容器中运行，但容器又不能直接以宿主机的身份执行各种脚本（例如重启其它容器，可以通过一些手段实现，但更麻烦）。\n\n此外，定制一个跑定时任务的容器也不是一件容易的事，首先需要有对应环境的基础镜像，然后基于这个镜像安装好`crond`之类的工具，再将定时任务的脚本刷到`crond`中，最后将这个容器部署到机器上。这个容器还必须有网络权限等。\n\n总之，定时任务的配置和管理是一个非常麻烦的事情，在容器环境下尤其如此。\n\n\u003C!-- more -->\n\n## Gitlab Pipelines\n\nGitlab提供了CI\u002FCD功能，可以自己定义Pipelines，然后在代码推送、MR合并等事件触发时执行Pipelines。利用这个功能可以很好地执行CI\u002FCD任务。\n\n最近发现Gitlab还有一个Pipeline Schedules的功能，可以在指定的时间点执行Pipelines。利用这个功能可以很好地解决定时任务的问题。\n\n### 使用方法\n\n首先在项目的CI\u002FCD设置中新建一个Pipelines：\n\n![screenshot-1](\u002Fassets\u002Ftech\u002F2023\u002Fperform-scheduled-tasks-using-gitLab-pipelines\u002F01.png)\n\n可以看到，这个界面能可视化地管理Pipelines的执行时间，非常方便。\n\n添加好之后，就可以在管理界面看到刚添加的定时任务了：\n\n![screenshot-2](\u002Fassets\u002Ftech\u002F2023\u002Fperform-scheduled-tasks-using-gitLab-pipelines\u002F02.png)\n\n但这里马上就会面临一个新问题：我们在Gitlab CI中定义的是CI\u002FCD的任务，但定时任务却希望是另外的脚本，怎么办呢？事实上如果此时点击手工运行的话，会发现Gitlab CI会执行我们定义的CI\u002FCD任务，而不是我们希望的定时任务。\n\n这个问题的解决方法是，我们可以在CI\u002FCD的任务中单独定义需要执行的定时任务，并通过条件来指定只有在定时任务触发时才执行。例如：\n\n```yaml\nci-task:\n    script:\n        - echo \"This is a CI task\"\n    rules:\n        - if: $CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_BRANCH == \"master\"\n\ncron-job:\n    script:\n        - echo \"This is a cron job\"\n    rules:\n        - if: $CI_PIPELINE_SOURCE == \"schedule\"\n```\n\n上述配置中，我们定义了两个任务，一个是CI任务，一个是定时任务。其中CI任务只有在`push`到`master`分支时才会执行，而定时任务只有在定时任务触发时才会执行。这样就可以很好地解决定时任务的执行的问题了。\n\n> 详细说明可参考官方文档：\u003Chttps:\u002F\u002Fdocs.gitlab.com\u002Fee\u002Fci\u002Fjobs\u002Fjob_control.html#run-jobs-for-scheduled-pipelines>\n\n接下来的问题是：不是说好的要解决Linux系统下的定时任务问题吗？这里的任务是在Gitlab CI中执行的，怎么执行自己服务器上的任务呢？\n\n这就需要我们从Gitlab CI环境使用SSH连接到自己的服务器上，然后执行对应的任务脚本来实现了。\n\n### SSH连接\n\n首先需要在Gitlab CI\u002FCD的设置中将SSH Key作为变量添加进去，这样才能在Gitlab CI环境中获取到对应的Key，并使用SSH连接到自己的服务器上。\n\n![screenshot-3](\u002Fassets\u002Ftech\u002F2023\u002Fperform-scheduled-tasks-using-gitLab-pipelines\u002F03.png)\n\n截图中`SSH_CONFIG`的内容如下：\n\n```\nHost *\n    KexAlgorithms +diffie-hellman-group1-sha1\n    StrictHostKeyChecking no\n```\n\n这里是为了解决SSH客户端与服务端使用的加密方案不同造成无法连接的情况，可视情况修改或者添加对应的配置。\n\n对应的CI任务脚本如下：\n\n```yaml\nscript:\n    - apk update&&apk add openssh\n    - mkdir ~\u002F.ssh\n    - echo \"$SSH_CONFIG\">~\u002F.ssh\u002Fconfig\n    - echo \"$SSH_KEY_PRIVATE\">~\u002F.ssh\u002Fid_rsa\n    - echo \"$SSH_KEY_PUBLIC\">~\u002F.ssh\u002Fid_rsa.pub\n    - chmod 400 ~\u002F.ssh\u002Fid_rsa\n    - ssh root@100.1.2.3 \"sh \u002Fdata\u002Fscripts\u002Fdb-bak.sh&&docker restart example\"\n```\n\n首先安装SSH客户端，然后将SSH Key写入到对应的文件中，最后连接到服务器执行对应的脚本。\n\n## 小结和可改进点\n\n总的来说，这个方法可以让我们不用花太多心思去配置“定时”这件事情，只需要在Gitlab中添加一个定时任务，然后通过SSH连接到服务器上执行对应的脚本即可。\n\n但这个方法依然不是特别“干净”，因为最终还是需要留一个脚本在服务器上，并不完全符合容器可移植的要求。但这个问题也有一些可改进的方向：\n\n1. 将脚本放到容器中，通过SSH连接到服务器后执行`docker`相关命令进行调用\n2. 将需要定时任务的任务通过HTTP接口等形式暴露出来，然后在Gitlab CI中通过`curl`等工具调用\n3. 将定时任务的脚本放到Gitlab CI中，通过SSH连接到服务器后将脚本写入到服务器上，然后执行\n",null,0,"2026-08-28 04:37:17","published","\u003Ch2>背景\u003C\u002Fh2>\n\u003Cp>在Linux服务器下执行定时任务是一个非常常见的需求，我们可能需要定时备份数据库、定时执行程序脚本、定时清理日志等等。在旧版本Linux下，我们通常会使用\u003Ccode>crontab\u003C\u002Fcode>来执行定时任务，而新版本的Linux系统中则更推荐用systemd的\u003Ccode>timer\u003C\u002Fcode>来执行定时任务。\u003C\u002Fp>\n\u003Cp>但是systemd的配置麻烦又罗嗦，需要先定义一个\u003Ccode>service\u003C\u002Fcode>，再定义一个\u003Ccode>timer\u003C\u002Fcode>来调用\u003Ccode>service\u003C\u002Fcode>，而且\u003Ccode>timer\u003C\u002Fcode>的配置文件中还需要指定\u003Ccode>OnCalendar\u003C\u002Fcode>来指定定时任务的执行时间，这个时间格式又不是很好记。\u003C\u002Fp>\n\u003Cp>此外即使定义好了，管理起来也并不容易，更不要说排查问题了。\u003C\u002Fp>\n\u003Cp>而如果机器上使用Docker等容器环境，定时任务就更麻烦了：为了应用的可移植性，希望尽可能将定时任务放到容器中运行，但容器又不能直接以宿主机的身份执行各种脚本（例如重启其它容器，可以通过一些手段实现，但更麻烦）。\u003C\u002Fp>\n\u003Cp>此外，定制一个跑定时任务的容器也不是一件容易的事，首先需要有对应环境的基础镜像，然后基于这个镜像安装好\u003Ccode>crond\u003C\u002Fcode>之类的工具，再将定时任务的脚本刷到\u003Ccode>crond\u003C\u002Fcode>中，最后将这个容器部署到机器上。这个容器还必须有网络权限等。\u003C\u002Fp>\n\u003Cp>总之，定时任务的配置和管理是一个非常麻烦的事情，在容器环境下尤其如此。\u003C\u002Fp>\n\u003Ch2>Gitlab Pipelines\u003C\u002Fh2>\n\u003Cp>Gitlab提供了CI\u002FCD功能，可以自己定义Pipelines，然后在代码推送、MR合并等事件触发时执行Pipelines。利用这个功能可以很好地执行CI\u002FCD任务。\u003C\u002Fp>\n\u003Cp>最近发现Gitlab还有一个Pipeline Schedules的功能，可以在指定的时间点执行Pipelines。利用这个功能可以很好地解决定时任务的问题。\u003C\u002Fp>\n\u003Ch3>使用方法\u003C\u002Fh3>\n\u003Cp>首先在项目的CI\u002FCD设置中新建一个Pipelines：\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fassets.toobug.net\u002Fassets\u002Ftech\u002F2023\u002Fperform-scheduled-tasks-using-gitLab-pipelines\u002F01.png\" alt=\"screenshot-1\">\u003C\u002Fp>\n\u003Cp>可以看到，这个界面能可视化地管理Pipelines的执行时间，非常方便。\u003C\u002Fp>\n\u003Cp>添加好之后，就可以在管理界面看到刚添加的定时任务了：\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fassets.toobug.net\u002Fassets\u002Ftech\u002F2023\u002Fperform-scheduled-tasks-using-gitLab-pipelines\u002F02.png\" alt=\"screenshot-2\">\u003C\u002Fp>\n\u003Cp>但这里马上就会面临一个新问题：我们在Gitlab CI中定义的是CI\u002FCD的任务，但定时任务却希望是另外的脚本，怎么办呢？事实上如果此时点击手工运行的话，会发现Gitlab CI会执行我们定义的CI\u002FCD任务，而不是我们希望的定时任务。\u003C\u002Fp>\n\u003Cp>这个问题的解决方法是，我们可以在CI\u002FCD的任务中单独定义需要执行的定时任务，并通过条件来指定只有在定时任务触发时才执行。例如：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>ci-task:\n    script:\n        - echo &quot;This is a CI task&quot;\n    rules:\n        - if: $CI_PIPELINE_SOURCE == &quot;push&quot; &amp;&amp; $CI_COMMIT_BRANCH == &quot;master&quot;\n\ncron-job:\n    script:\n        - echo &quot;This is a cron job&quot;\n    rules:\n        - if: $CI_PIPELINE_SOURCE == &quot;schedule&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>上述配置中，我们定义了两个任务，一个是CI任务，一个是定时任务。其中CI任务只有在\u003Ccode>push\u003C\u002Fcode>到\u003Ccode>master\u003C\u002Fcode>分支时才会执行，而定时任务只有在定时任务触发时才会执行。这样就可以很好地解决定时任务的执行的问题了。\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>详细说明可参考官方文档：\u003Ca href=\"https:\u002F\u002Fdocs.gitlab.com\u002Fee\u002Fci\u002Fjobs\u002Fjob_control.html#run-jobs-for-scheduled-pipelines\">https:\u002F\u002Fdocs.gitlab.com\u002Fee\u002Fci\u002Fjobs\u002Fjob_control.html#run-jobs-for-scheduled-pipelines\u003C\u002Fa>\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>接下来的问题是：不是说好的要解决Linux系统下的定时任务问题吗？这里的任务是在Gitlab CI中执行的，怎么执行自己服务器上的任务呢？\u003C\u002Fp>\n\u003Cp>这就需要我们从Gitlab CI环境使用SSH连接到自己的服务器上，然后执行对应的任务脚本来实现了。\u003C\u002Fp>\n\u003Ch3>SSH连接\u003C\u002Fh3>\n\u003Cp>首先需要在Gitlab CI\u002FCD的设置中将SSH Key作为变量添加进去，这样才能在Gitlab CI环境中获取到对应的Key，并使用SSH连接到自己的服务器上。\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fassets.toobug.net\u002Fassets\u002Ftech\u002F2023\u002Fperform-scheduled-tasks-using-gitLab-pipelines\u002F03.png\" alt=\"screenshot-3\">\u003C\u002Fp>\n\u003Cp>截图中\u003Ccode>SSH_CONFIG\u003C\u002Fcode>的内容如下：\u003C\u002Fp>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>Host *\n    KexAlgorithms +diffie-hellman-group1-sha1\n    StrictHostKeyChecking no\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>这里是为了解决SSH客户端与服务端使用的加密方案不同造成无法连接的情况，可视情况修改或者添加对应的配置。\u003C\u002Fp>\n\u003Cp>对应的CI任务脚本如下：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>script:\n    - apk update&amp;&amp;apk add openssh\n    - mkdir ~\u002F.ssh\n    - echo &quot;$SSH_CONFIG&quot;&gt;~\u002F.ssh\u002Fconfig\n    - echo &quot;$SSH_KEY_PRIVATE&quot;&gt;~\u002F.ssh\u002Fid_rsa\n    - echo &quot;$SSH_KEY_PUBLIC&quot;&gt;~\u002F.ssh\u002Fid_rsa.pub\n    - chmod 400 ~\u002F.ssh\u002Fid_rsa\n    - ssh root@100.1.2.3 &quot;sh \u002Fdata\u002Fscripts\u002Fdb-bak.sh&amp;&amp;docker restart example&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>首先安装SSH客户端，然后将SSH Key写入到对应的文件中，最后连接到服务器执行对应的脚本。\u003C\u002Fp>\n\u003Ch2>小结和可改进点\u003C\u002Fh2>\n\u003Cp>总的来说，这个方法可以让我们不用花太多心思去配置“定时”这件事情，只需要在Gitlab中添加一个定时任务，然后通过SSH连接到服务器上执行对应的脚本即可。\u003C\u002Fp>\n\u003Cp>但这个方法依然不是特别“干净”，因为最终还是需要留一个脚本在服务器上，并不完全符合容器可移植的要求。但这个问题也有一些可改进的方向：\u003C\u002Fp>\n\u003Col>\n\u003Cli>将脚本放到容器中，通过SSH连接到服务器后执行\u003Ccode>docker\u003C\u002Fcode>相关命令进行调用\u003C\u002Fli>\n\u003Cli>将需要定时任务的任务通过HTTP接口等形式暴露出来，然后在Gitlab CI中通过\u003Ccode>curl\u003C\u002Fcode>等工具调用\u003C\u002Fli>\n\u003Cli>将定时任务的脚本放到Gitlab CI中，通过SSH连接到服务器后将脚本写入到服务器上，然后执行\u003C\u002Fli>\n\u003C\u002Fol>\n","\u003Ch2>背景\u003C\u002Fh2>\n\u003Cp>在Linux服务器下执行定时任务是一个非常常见的需求，我们可能需要定时备份数据库、定时执行程序脚本、定时清理日志等等。在旧版本Linux下，我们通常会使用\u003Ccode>crontab\u003C\u002Fcode>来执行定时任务，而新版本的Linux系统中则更推荐用systemd的\u003Ccode>timer\u003C\u002Fcode>来执行定时任务。\u003C\u002Fp>\n\u003Cp>但是systemd的配置麻烦又罗嗦，需要先定义一个\u003Ccode>service\u003C\u002Fcode>，再定义一个\u003Ccode>timer\u003C\u002Fcode>来调用\u003Ccode>service\u003C\u002Fcode>，而且\u003Ccode>timer\u003C\u002Fcode>的配置文件中还需要指定\u003Ccode>OnCalendar\u003C\u002Fcode>来指定定时任务的执行时间，这个时间格式又不是很好记。\u003C\u002Fp>\n\u003Cp>此外即使定义好了，管理起来也并不容易，更不要说排查问题了。\u003C\u002Fp>\n\u003Cp>而如果机器上使用Docker等容器环境，定时任务就更麻烦了：为了应用的可移植性，希望尽可能将定时任务放到容器中运行，但容器又不能直接以宿主机的身份执行各种脚本（例如重启其它容器，可以通过一些手段实现，但更麻烦）。\u003C\u002Fp>\n\u003Cp>此外，定制一个跑定时任务的容器也不是一件容易的事，首先需要有对应环境的基础镜像，然后基于这个镜像安装好\u003Ccode>crond\u003C\u002Fcode>之类的工具，再将定时任务的脚本刷到\u003Ccode>crond\u003C\u002Fcode>中，最后将这个容器部署到机器上。这个容器还必须有网络权限等。\u003C\u002Fp>\n\u003Cp>总之，定时任务的配置和管理是一个非常麻烦的事情，在容器环境下尤其如此。\u003C\u002Fp>\n","https:\u002F\u002Fassets.toobug.net\u002Fassets\u002Ftech\u002F2023\u002Fperform-scheduled-tasks-using-gitLab-pipelines\u002F01.png",{"total":16,"totalRoots":16,"comments":23,"pv":16},[]]