[{"data":1,"prerenderedAt":72},["ShallowReactive",2],{"content:\u002Farticles:gitea-actions-setup":3},{"item":4,"md":15,"html":16,"menu":17,"excerpt":43,"related":44},{"title":5,"len":6,"tags":7,"id":10,"customSlug":11,"time":12,"modifyTime":12,"showComments":13,"encrypt":14},"上手gitea actions记录",6059,[8,9],"运维","nas",7909,"gitea-actions-setup",1770992638940,true,false,null,"\u003Ch1 data-marked-line=\"1\">\u003Csup class=\"fake-head\" id=\"Gitea%20Actions%20CI\u002FCD%20%E5%AE%9E%E6%88%98%EF%BC%9A%E4%BB%8E%E6%9E%84%E5%BB%BA%E5%88%B0%E9%83%A8%E7%BD%B2\">\u003C\u002Fsup>\u003Ca class=\"header-link\" href=\"#Gitea%20Actions%20CI\u002FCD%20%E5%AE%9E%E6%88%98%EF%BC%9A%E4%BB%8E%E6%9E%84%E5%BB%BA%E5%88%B0%E9%83%A8%E7%BD%B2\">Gitea Actions CI\u002FCD 实战：从构建到部署\u003C\u002Fa>\u003C\u002Fh1>\u003Cp data-marked-line=\"3\">&emsp;&emsp;不久前发现我部署的 \u003Ca href=\"https:\u002F\u002Fblog.yunyuyuan.net\u002Farticles\u002F8346\" target=\"_blank\">drone CI\u002FCD\u003C\u002Fa> 报错了 docker api 版本过低，找到一个相关的 \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fharness\u002Fharness\u002Fissues\u002F3650\" target=\"_blank\">issue\u003C\u002Fa>，但是没有解决，另外感觉 drone 这一套貌似维护不太活跃。于是寻找一个替代方案，正好发现 Gitea 支持了 \u003Ca href=\"https:\u002F\u002Fdocs.gitea.com\u002Fusage\u002Factions\u002Foverview\" target=\"_blank\">actions\u003C\u002Fa>，于是尝试把本地部署迁移到 gitea actions，因为我本地已经部署了一个 gitea。\u003C\u002Fp>\n\u003Ch2 data-marked-line=\"5\">\u003Csup class=\"fake-head\" id=\"%E4%B8%8A%E6%89%8B\">\u003C\u002Fsup>\u003Ca class=\"header-link\" href=\"#%E4%B8%8A%E6%89%8B\">上手\u003C\u002Fa>\u003C\u002Fh2>\u003Cp data-marked-line=\"7\">&emsp;&emsp;首先需要一个 self-hosted 的 Gitea，这部分略过。\u003C\u002Fp>\n\u003Cp data-marked-line=\"9\">&emsp;&emsp;接下来，我们需要配置 Gitea Actions 的执行环境，也就是 Runner。Gitea 的 Runner 叫做 \u003Ccode>act_runner\u003C\u002Fcode>，它负责执行在 workflow 文件中定义的任务。\u003C\u002Fp>\n\u003Ch2 data-marked-line=\"11\">\u003Csup class=\"fake-head\" id=\"Runner%20%E9%85%8D%E7%BD%AE%E8%A7%A3%E6%9E%90\">\u003C\u002Fsup>\u003Ca class=\"header-link\" href=\"#Runner%20%E9%85%8D%E7%BD%AE%E8%A7%A3%E6%9E%90\">Runner 配置解析\u003C\u002Fa>\u003C\u002Fh2>\u003Cp data-marked-line=\"13\">&emsp;&emsp;为了让我们的 CI\u002FCD 流程顺利跑起来，一个配置合理的 Runner 是关键。我的部署环境是基于 Docker 的，所以 Runner 也是通过 Docker Compose 来管理的。\u003C\u002Fp>\n\u003Ch3 data-marked-line=\"15\">\u003Csup class=\"fake-head\" id=\"docker-compose.yml\">\u003C\u002Fsup>\u003Ca class=\"header-link\" href=\"#docker-compose.yml\">docker-compose.yml\u003C\u002Fa>\u003C\u002Fh3>\u003Cp data-marked-line=\"17\">&emsp;&emsp;这是用于启动 \u003Ccode>act_runner\u003C\u002Fcode> 的 \u003Ccode>docker-compose.yml\u003C\u002Fcode> 文件。它定义了 Runner 容器的运行参数。\u003C\u002Fp>\n\u003Cpre data-marked-line=\"19\" data-lang=\"yaml\" data-shiki>\u003Ccode class=\"language-yaml\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">services\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">  runner\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">    image\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">docker.io\u002Fgitea\u002Fact_runner:latest\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">    privileged\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#D19A66\">true\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">    environment\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">      CONFIG_FILE\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">\u002Fconfig.yaml\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">      GITEA_INSTANCE_URL\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">http:\u002F\u002F127.0.0.1:16004\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">      GITEA_RUNNER_REGISTRATION_TOKEN\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">loPCJ0jM7zeArrE6QukefYFxRGUbwF6JFGSz7ips\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">      GITEA_RUNNER_NAME\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">local_runner\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">      GITEA_RUNNER_LABELS\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">\"runner-host,node18:docker:\u002F\u002Fnode:18,node20:docker:\u002F\u002Fnode:20,node22:docker:\u002F\u002Fnode:22\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">    volumes\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">      - \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">\u002Fdata\u002Fgitea-runner\u002Fconfig.yaml:\u002Fconfig.yaml\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">      - \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">\u002Fdata\u002Fgitea-runner:\u002Fdata\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">      - \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">\u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">      - \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">\u002Fdata\u002Fnext-cloud\u002Fdata\u002Fyunyuyuan\u002Ffiles\u002Fnuxt3-blog-imgs:\u002Fdata\u002Fnext-cloud\u002Fdata\u002Fyunyuyuan\u002Ffiles\u002Fnuxt3-blog-imgs\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">    network_mode\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">\"host\"\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp data-marked-line=\"38\">&emsp;&emsp;这里有几个关键配置需要特别说明：\u003C\u002Fp>\n\u003Cul data-marked-line=\"40\">\n\u003Cli>\u003Cstrong>\u003Ccode>privileged: true\u003C\u002Fcode>\u003C\u002Fstrong>: 赋予容器特权模式，这对于在容器内操作 Docker 是必要的。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u003Ccode>GITEA_RUNNER_LABELS\u003C\u002Fcode>\u003C\u002Fstrong>: 这是 Runner 的标签，是 Gitea Actions 用来决定哪个 Runner 执行哪个 Job 的依据。这里的配置非常关键：\u003Cul data-marked-line=\"42\">\n\u003Cli>\u003Ccode>runner-host\u003C\u002Fcode>: 这是一个自定义标签，我用它来指定在 Runner 宿主环境（也就是 \u003Ccode>act_runner\u003C\u002Fcode> 容器本身）中执行任务。\u003C\u002Fli>\n\u003Cli>\u003Ccode>node18:docker:\u002F\u002Fnode:18\u003C\u002Fcode>, \u003Ccode>node20:docker:\u002F\u002Fnode:20\u003C\u002Fcode>, \u003Ccode>node22:docker:\u002F\u002Fnode:22\u003C\u002Fcode>: 这些标签定义了当 \u003Ccode>runs-on\u003C\u002Fcode> 指定为 \u003Ccode>node18\u003C\u002Fcode>、\u003Ccode>node20\u003C\u002Fcode> 或 \u003Ccode>node22\u003C\u002Fcode> 时，\u003Ccode>act_runner\u003C\u002Fcode> 会启动一个对应的 Node.js 官方 Docker 镜像作为 Job 的执行环境。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u003Ccode>volumes\u003C\u002Fcode>\u003C\u002Fstrong>: \u003Cul data-marked-line=\"45\">\n\u003Cli>\u003Ccode>- \u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock\u003C\u002Fcode>: 这是实现 “Docker in Docker” 的核心。通过将宿主机的 Docker socket 文件挂载到 \u003Ccode>act_runner\u003C\u002Fcode> 容器内部，我们使得在 \u003Ccode>act_runner\u003C\u002Fcode> 容器中可以调用和控制宿主机的 Docker 服务。这是我们后续能够更新部署在宿主机上的应用容器的关键。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u003Ccode>network_mode: &quot;host&quot;\u003C\u002Fcode>\u003C\u002Fstrong>: 让 \u003Ccode>act_runner\u003C\u002Fcode> 容器直接使用宿主机的网络，简化了网络配置。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 data-marked-line=\"48\">\u003Csup class=\"fake-head\" id=\"%3Ccode%3Econfig.yaml%3C\u002Fcode%3E\">\u003C\u002Fsup>\u003Ca class=\"header-link\" href=\"#%3Ccode%3Econfig.yaml%3C\u002Fcode%3E\">\u003Ccode>config.yaml\u003C\u002Fcode>\u003C\u002Fa>\u003C\u002Fh3>\u003Cp data-marked-line=\"50\">&emsp;&emsp;这个文件是 \u003Ccode>act_runner\u003C\u002Fcode> 的配置文件，它为 Job 的执行容器提供了默认配置。\u003C\u002Fp>\n\u003Cpre data-marked-line=\"52\" data-lang=\"yaml\" data-shiki>\u003Ccode class=\"language-yaml\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">container\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">  network\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">host\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">  privileged\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#D19A66\">true\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">  volumes\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">    - \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">\u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">    - \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">\u002Fdata\u002Fnext-cloud\u002Fdata\u002Fyunyuyuan\u002Ffiles\u002Fnuxt3-blog-imgs:\u002Fdata\u002Fnext-cloud\u002Fdata\u002Fyunyuyuan\u002Ffiles\u002Fnuxt3-blog-imgs\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp data-marked-line=\"61\">&emsp;&emsp;这里的配置会被应用到由 \u003Ccode>act_runner\u003C\u002Fcode> 启动的每一个 Job 容器上。例如，当一个 Job \u003Ccode>runs-on: node22\u003C\u002Fcode> 时，\u003Ccode>act_runner\u003C\u002Fcode> 会启动一个 \u003Ccode>node:22\u003C\u002Fcode> 的容器，并且这个 \u003Ccode>config.yaml\u003C\u002Fcode> 中的配置（如 \u003Ccode>privileged: true\u003C\u002Fcode> 和 \u003Ccode>volumes\u003C\u002Fcode>）会被应用到这个 \u003Ccode>node:22\u003C\u002Fcode> 容器上。这意味着，即使是 Job 容器，也同样挂载了宿主机的 Docker socket，因此也具备了操作宿主机 Docker 的能力。\u003C\u002Fp>\n\u003Ch2 data-marked-line=\"63\">\u003Csup class=\"fake-head\" id=\"Workflow%20%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%A7%A3%E6%9E%90\">\u003C\u002Fsup>\u003Ca class=\"header-link\" href=\"#Workflow%20%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%A7%A3%E6%9E%90\">Workflow 工作流解析\u003C\u002Fa>\u003C\u002Fh2>\u003Cp data-marked-line=\"65\">&emsp;&emsp;理解了 Runner 的配置后，我们来看看实际的 CI\u002FCD 工作流文件 \u003Ccode>.gitea\u002Fworkflows\u002Fbuild.yaml\u003C\u002Fcode>。我将整个流程拆分成了两个 Job：\u003Ccode>build\u003C\u002Fcode> 和 \u003Ccode>deploy\u003C\u002Fcode>。这么做的原因我会在后面详细解释。\u003C\u002Fp>\n\u003Cpre data-marked-line=\"67\" data-lang=\"yaml\" data-shiki>\u003Ccode class=\"language-yaml\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">name\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">build\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#D19A66\">on\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">  push\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">    branches\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">      - \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">blog\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">jobs\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">  build\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">    runs-on\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">node22\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">    steps\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-light-font-style:inherit;--shiki-dark:#7F848E;--shiki-dark-font-style:italic\">      # ... (省略了分支检查、代码检出、Node.js环境设置等步骤)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">      - \u003C\u002Fspan>\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">name\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">Set up Docker Buildx\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">        uses\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">docker\u002Fsetup-buildx-action@v3\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">      - \u003C\u002Fspan>\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">name\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">Build and load Docker image\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">        uses\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">docker\u002Fbuild-push-action@v5\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">        with\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">          context\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#D19A66\">.\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">          push\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#D19A66\">false\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">          load\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#D19A66\">true\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">          tags\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">nuxt3-blog:latest\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">  deploy\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">    runs-on\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">runner-host\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">    needs\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">build\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">    steps\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">      - \u003C\u002Fspan>\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">name\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">Install Docker\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">        run\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#C678DD\">|\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">          apk update\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">          apk add docker\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">      - \u003C\u002Fspan>\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">name\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">Deploy Docker container\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#22863A;--shiki-dark:#E06C75\">        run\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#ABB2BF\">: \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#C678DD\">|\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">          # Stop and remove existing container\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">          docker ps -q --filter \"name=nuxt3-blog-app\" | xargs -r docker stop || true\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">          docker ps -aq --filter \"name=nuxt3-blog-app\" | xargs -r docker rm || true\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">          # Run new container\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">          docker run -d \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">            --name=nuxt3-blog-app \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">            --restart=always \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">            -e CLOUDFLARE_D1_TOKEN=${{ secrets.CLOUDFLARE_D1_TOKEN }} \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">            -e CLOUDFLARE_D1_ACCOUNT_ID=${{ secrets.CLOUDFLARE_D1_ACCOUNT_ID }} \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">            -e CLOUDFLARE_D1_DATABASE_ID=${{ secrets.CLOUDFLARE_D1_DATABASE_ID }} \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">            -p 127.0.0.1:8451:3000 \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#032F62;--shiki-dark:#98C379\">            nuxt3-blog:latest\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3 data-marked-line=\"112\">\u003Csup class=\"fake-head\" id=\"%E4%B8%BA%E4%BB%80%E4%B9%88%E6%8B%86%E5%88%86%E4%B8%BA%E4%B8%A4%E4%B8%AA%20Job%EF%BC%9F\">\u003C\u002Fsup>\u003Ca class=\"header-link\" href=\"#%E4%B8%BA%E4%BB%80%E4%B9%88%E6%8B%86%E5%88%86%E4%B8%BA%E4%B8%A4%E4%B8%AA%20Job%EF%BC%9F\">为什么拆分为两个 Job？\u003C\u002Fa>\u003C\u002Fh3>\u003Cp data-marked-line=\"114\">&emsp;&emsp;这是本次实践的核心设计。将构建和部署拆分为两个独立的 Job，主要是为了解决 \u003Cstrong>“在什么环境中执行什么命令”\u003C\u002Fstrong> 的问题。\u003C\u002Fp>\n\u003Col data-marked-line=\"116\">\n\u003Cli>\u003Cp data-marked-line=\"116\">\u003Cstrong>\u003Ccode>build\u003C\u002Fcode> Job：专业的构建环境\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul data-marked-line=\"117\">\n\u003Cli>\u003Ccode>runs-on: node22\u003C\u002Fcode>：我的博客项目是基于 Nuxt.js 的，构建过程需要 Node.js 环境和 \u003Ccode>pnpm\u003C\u002Fcode> 包管理器。因此，我让 \u003Ccode>build\u003C\u002Fcode> Job 运行在一个标准的 \u003Ccode>node:22\u003C\u002Fcode> 容器中，这是一个纯净且专业的 Node.js 构建环境。\u003C\u002Fli>\n\u003Cli>这个 \u003Ccode>node:22\u003C\u002Fcode> 容器默认是没有安装 Docker CLI 的。但我们依然可以构建 Docker 镜像，这得益于 \u003Ccode>docker\u002Fsetup-buildx-action@v3\u003C\u002Fcode> 和 \u003Ccode>docker\u002Fbuild-push-action@v5\u003C\u002Fcode> 这两个官方 Action。它们通过我们之前挂载的 \u003Ccode>\u002Fvar\u002Frun\u002Fdocker.sock\u003C\u002Fcode> 文件，直接与宿主机的 Docker 引擎通信来完成镜像的构建和加载 (\u003Ccode>load: true\u003C\u002Fcode>)，完全绕过了在 Job 容器中安装 Docker CLI 的需要。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Cp data-marked-line=\"120\">\u003Cstrong>\u003Ccode>deploy\u003C\u002Fcode> Job：直接的宿主机控制\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul data-marked-line=\"121\">\n\u003Cli>\u003Ccode>runs-on: runner-host\u003C\u002Fcode>：这是关键所在。这个配置告诉 Gitea，这个 Job 应该由带有 \u003Ccode>runner-host\u003C\u002Fcode> 标签的 Runner 来执行，并且是在 \u003Cstrong>host\u003C\u002Fstrong> 模式下。根据我们之前的 \u003Ccode>docker-compose.yml\u003C\u002Fcode> 配置，这意味着 \u003Ccode>deploy\u003C\u002Fcode> Job 会直接在 \u003Ccode>act_runner\u003C\u002Fcode> 容器本身的环境中执行，而不是在一个新启动的 Job 容器里。\u003C\u002Fli>\n\u003Cli>\u003Ccode>apk add docker\u003C\u002Fcode>：\u003Ccode>act_runner\u003C\u002Fcode> 的基础镜像是 Alpine Linux，它非常轻量，默认不包含 Docker 客户端。因此，第一步我们需要通过 \u003Ccode>apk\u003C\u002Fcode> 包管理器来安装 \u003Ccode>docker\u003C\u002Fcode>。\u003C\u002Fli>\n\u003Cli>执行 \u003Ccode>docker\u003C\u002Fcode> 命令：一旦 Docker CLI 安装完毕，这个 Job 就获得了直接操作宿主机 Docker 的能力——因为它本身就运行在一个挂载了 \u003Ccode>docker.sock\u003C\u002Fcode> 的容器里。接下来的 \u003Ccode>docker stop\u003C\u002Fcode>, \u003Ccode>docker rm\u003C\u002Fcode>, \u003Ccode>docker run\u003C\u002Fcode> 等命令，实际上都是在操作物理机上的 Docker 容器，从而完成了应用的停止、移除和重新部署。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3 data-marked-line=\"125\">\u003Csup class=\"fake-head\" id=\"%E6%80%BB%E7%BB%93\">\u003C\u002Fsup>\u003Ca class=\"header-link\" href=\"#%E6%80%BB%E7%BB%93\">总结\u003C\u002Fa>\u003C\u002Fh3>\u003Cp data-marked-line=\"127\">&emsp;&emsp;通过将 \u003Ccode>jobs\u003C\u002Fcode> 拆分为两个，我们实现了关注点分离：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth align=\"left\">Job\u003C\u002Fth>\n\u003Cth align=\"left\">运行环境\u003C\u002Fth>\n\u003Cth align=\"left\">核心任务\u003C\u002Fth>\n\u003Cth align=\"left\">关键技术\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd align=\"left\">\u003Cstrong>build\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd align=\"left\">\u003Ccode>node:22\u003C\u002Fcode> Docker 容器\u003C\u002Ftd>\n\u003Ctd align=\"left\">使用 Node.js 环境构建前端项目并打包 Docker 镜像\u003C\u002Ftd>\n\u003Ctd align=\"left\">通过 \u003Ccode>docker.sock\u003C\u002Fcode> 挂载，使用 \u003Ccode>build-push-action\u003C\u002Fcode> 与宿主机 Docker 通信\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd align=\"left\">\u003Cstrong>deploy\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd align=\"left\">\u003Ccode>act_runner\u003C\u002Fcode> 容器本身 (Alpine Linux)\u003C\u002Ftd>\n\u003Ctd align=\"left\">在宿主机上停止旧容器、启动新容器\u003C\u002Ftd>\n\u003Ctd align=\"left\">使用 \u003Ccode>runner-host\u003C\u002Fcode> 标签，在 Runner 容器内安装 Docker CLI，通过 \u003Ccode>docker.sock\u003C\u002Fcode> 操作宿主机\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp data-marked-line=\"134\">&emsp;&emsp;这种方法巧妙地利用了 Gitea Actions Runner 的标签和 \u003Ccode>docker.sock\u003C\u002Fcode> 挂载机制，为不同的任务阶段提供了最合适、最干净的执行环境，既保证了构建环境的标准化，又实现了对宿主机容器的直接控制，是一种非常灵活和高效的 CI\u002FCD 实践。\u003C\u002Fp>\n\u003Chr data-marked-line=\"136\" \u002F>\n\u003Cp data-marked-line=\"138\">&emsp;&emsp;AI味太浓了哈哈哈\u003Cimg src=\"\u002Fsticker\u002Faru\u002F55.png\" alt=\"sticker\"\u002F>\u003C\u002Fp>\n",[18,22,25,28,31,34,37,40],{"size":19,"text":20,"url":21},"big","Gitea Actions CI\u002FCD 实战：从构建到部署","Gitea%20Actions%20CI\u002FCD%20%E5%AE%9E%E6%88%98%EF%BC%9A%E4%BB%8E%E6%9E%84%E5%BB%BA%E5%88%B0%E9%83%A8%E7%BD%B2",{"size":19,"text":23,"url":24},"上手","%E4%B8%8A%E6%89%8B",{"size":19,"text":26,"url":27},"Runner 配置解析","Runner%20%E9%85%8D%E7%BD%AE%E8%A7%A3%E6%9E%90",{"size":29,"text":30,"url":30},"small","docker-compose.yml",{"size":29,"text":32,"url":33},"\u003Ccode>config.yaml\u003C\u002Fcode>","%3Ccode%3Econfig.yaml%3C\u002Fcode%3E",{"size":19,"text":35,"url":36},"Workflow 工作流解析","Workflow%20%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%A7%A3%E6%9E%90",{"size":29,"text":38,"url":39},"为什么拆分为两个 Job？","%E4%B8%BA%E4%BB%80%E4%B9%88%E6%8B%86%E5%88%86%E4%B8%BA%E4%B8%A4%E4%B8%AA%20Job%EF%BC%9F",{"size":29,"text":41,"url":42},"总结","%E6%80%BB%E7%BB%93","Gitea Actions CI\u002FCD 实战：从构建到部署 不久前发现我部署的 drone CI\u002FCD 报错了 docker api 版本过低，找到一个相关的 issue，但是没有解决，另外感觉 drone 这一套貌似维护不太活跃。于是寻找一个替代方案，正好发现 Gitea 支持了 actions，…",[45,55,64],{"item":46,"count":54},{"title":47,"len":48,"tags":49,"id":50,"time":51,"modifyTime":52,"encrypt":14,"showComments":13,"customSlug":53},"使用Drone+docker配置本地轻量化CI\u002FCD",7292,[8],8346,1694238608081,1694594878863,"",1,{"item":56,"count":54},{"title":57,"len":58,"tags":59,"id":61,"time":62,"modifyTime":63,"encrypt":14,"showComments":13,"customSlug":53},"树莓派bt下载器\u002F局域网NAS\u002F远程控制",4570,[60,9],"树莓派",4343,1662638618123,1679559795985,{"item":65,"count":54},{"title":66,"len":67,"tags":68,"id":69,"time":70,"modifyTime":71,"encrypt":14,"showComments":13,"customSlug":53},"jenkins+github配置CI\u002FCD",2698,[8],3300,1649186115521,1679559849202,1793351205601]