GitLab Runner 部署核心指南:Executor选型、安全配置与dotnet8实战

发布时间:2026/9/13 6:19:22
GitLab Runner 部署核心指南:Executor选型、安全配置与dotnet8实战
1. 这不是装个软件那么简单GitLab Runner 的真实角色与部署动机很多人第一次看到“部署 GitLab Runner”这个标题下意识觉得就是下载一个二进制、注册一下、启动服务——完事。我刚接触 CI/CD 时也这么想直到被线上环境连续三天的 pipeline 卡在 “pending” 状态逼到凌晨三点翻日志才发现自己连 runner 是什么、为什么需要它、它和 GitLab 实例之间到底在“聊”什么都没搞清楚。GitLab Runner 不是 GitLab 的附属插件它是独立运行的执行代理Execution Agent是整个 CI/CD 流水线真正干活的“手”和“脚”。GitLab 本身只负责调度、编排、展示结果而所有代码拉取、依赖安装、编译、测试、打包、镜像构建、甚至推送到生产服务器的操作全由 Runner 在你指定的机器上完成。它不跑在 GitLab 服务器上而是部署在你自己的基础设施里——可能是开发机、测试服务器、云主机甚至是树莓派。这就决定了它的部署不是“一键安装”而是一次基础设施级的权限、网络、资源与安全策略的协同配置。核心关键词gitlab-runner、CI/CD、pipeline、gitlab-ci.yml、executor每一个都指向一个关键环节gitlab-runner是载体CI/CD是目标范式pipeline是可视化的工作流gitlab-ci.yml是声明式指令集而executor则是 Runner 的“肌肉类型”——它决定这双手用什么方式干活是直接调用宿主机 shellshell executor还是拉起一个干净隔离的 Docker 容器docker executor或是连接 Kubernetes 集群kubernetes executor。当前热词里反复出现的 “gitlab-runner 自动化部署 dotnet8”、“docker 镜像构建与自动化部署实践”本质上都是在选择并配置合适的 executor再围绕它设计gitlab-ci.yml的 job 流程。比如 dotnet8 项目若用 shell executor就得在 runner 主机上预装 .NET SDK 8.x、dotnet cli、nuget 源配置而用 docker executor你只需在.yml里指定image: mcr.microsoft.com/dotnet/sdk:8.0Runner 会自动拉镜像、启动容器、执行命令——环境完全隔离版本精准可控这才是现代 CI/CD 的底层逻辑。所以“部署 GitLab Runner” 的第一课从来不是curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash这条命令而是先问自己我的 pipeline 要跑什么环境依赖有多重是否需要多环境隔离团队协作时权限怎么划这些决策直接决定了后续 executor 选型、注册方式、并发策略、缓存机制等所有细节。它不是运维的收尾工作而是整个自动化交付链路的起点锚点。2. 部署前必须厘清的四大核心设计维度部署 GitLab Runner 绝非机械执行安装脚本。它是一次面向业务交付能力的架构设计需从四个相互耦合的维度系统性规划。我见过太多团队在 pipeline 稳定运行半年后因突然要支持 Java Python DotNet 三栈并行才发现当初用 shell executor 部署的单台 runner 已成瓶颈重装又怕影响线上发布——根源就在于部署前没做这四维推演。2.1 Executor 类型选型不是“能用就行”而是“长期可维护”Executor 是 Runner 的执行引擎选错类型后期改造成本极高。目前主流有五种但实际生产中 90% 场景集中在三种Docker Executor这是当前最推荐、最主流的选择。Runner 启动一个 Docker 容器作为 job 执行环境容器镜像由gitlab-ci.yml中的image字段指定如mcr.microsoft.com/dotnet/sdk:8.0或node:18-alpine。优势极其明显环境完全隔离、依赖版本精确锁定、无需在 runner 主机预装任何语言 SDK、不同 job 互不干扰。劣势是 runner 主机必须安装 Docker daemon且需赋予 runner 用户对/var/run/docker.sock的读写权限安全风险需评估。适用于绝大多数 Web 应用、微服务、前后端分离项目。Shell ExecutorRunner 直接在宿主机的 shellbash/zsh中执行命令。部署最简单无需额外依赖。但致命缺陷是所有 job 共享同一套系统环境A 项目的npm install可能污染 B 项目的 node_modulesPython 3.9 和 3.11 无法共存更别说 dotnet8 和 dotnet6 的全局 SDK 冲突。仅适合极简场景如纯静态页构建、轻量脚本触发或作为临时调试用。Kubernetes ExecutorRunner 本身运行在 K8s 集群外但每个 job 会动态创建一个 Pod 来执行。环境隔离性比 Docker 更强Pod 级别资源调度更精细CPU/Memory Request/Limit天然支持多租户隔离。但要求你已有稳定运行的 K8s 集群并需配置 ServiceAccount、RBAC 权限、StorageClass 等。适合大型企业、多团队共享 CI 资源的场景。提示不要被“Kubernetes 很酷”带偏。我曾帮一家 50 人规模的 SaaS 公司评估过他们当时只有 3 台云主机硬上 K8s executor 导致运维复杂度飙升最终退回 Docker executor 多 runner 实例方案稳定性反而提升。选型核心标准是你的团队是否有能力持续维护该 executor 的底层依赖2.2 注册模式Shared、Group 还是 Specific权限与复用的平衡术Runner 注册后需绑定到 GitLab 的某个作用域这决定了谁的 pipeline 能调用它Shared Runner注册时选择 “Run untagged jobs” 并勾选 “Run for all projects”它将出现在 GitLab 实例首页的 “Shared Runners” 列表中所有项目只要未禁用 shared runners均可使用。优点是资源复用率高运维成本低缺点是 job 随机调度不同项目可能抢占同一 runner 的 CPU/内存导致构建超时或失败。适用于内部工具链、文档生成等低优先级任务。Group Runner注册时绑定到特定 Group如backend-team该 Group 下所有子项目自动继承此 runner。权限边界清晰资源归属明确适合按业务线或技术栈划分的团队。例如为frontendGroup 配置 Node.js 优化的 runner为dotnetGroup 配置预装 SDK 8.0 的 runner。Specific Runner注册时绑定到单一 Project如payment-service仅该项目的 pipeline 可调用。隔离性最强配置最灵活可为每个项目定制专属 executor、tags、并发数但管理成本最高。适用于核心支付、风控等对构建环境、安全性、稳定性要求极高的关键项目。注意Shared Runner 的 “untracked jobs” 并非指无 tag 的 job而是指.gitlab-ci.yml中未声明tags的 job。一旦你在 job 中写了tags: [dotnet]它就只会匹配带有dotnettag 的 runner。因此tag 是实现精细化调度的核心钥匙而非注册模式本身。2.3 并发与资源策略别让一台 runner 成为流水线的“堵车点”Runner 的concurrent参数全局并发数和每个 job 的resource_limits容器资源限制共同决定吞吐能力。常见误区是认为 “并发数越大越好”。实测数据表明一台 4C8G 的云主机若用 Docker executor 运行 dotnet8 构建单个 job 平均占用 2.5G 内存、1.8 核 CPU。若设concurrent 4当 4 个 job 同时启动内存极易爆满OOM Killer 会杀掉进程导致 pipeline 失败。正确做法是压测基线用gitlab-runner verify或手动触发多个相同 job观察htop中 CPU/内存峰值留出余量设定concurrent时按floor(可用内存 / 单 job 内存峰值 * 0.7)计算0.7 是安全系数容器限流在config.toml的[[runners.docker]]段落中强制设置memory 3g、cpus 2防止单个失控 job 吃光资源。此外limit参数单 runner 最大 job 数和output_limit单 job 日志最大行数是防止单个长时 job 占用全部资源的保险丝。我们曾遇到一个遗留 Python 脚本因死循环打印日志撑爆 runner 磁盘导致所有 pipeline 挂起——启用output_limit 4000后该 job 自动被 kill其他 job 不受影响。2.4 安全与隔离边界你的 runner 是“透明玻璃房”还是“上锁保险柜”Runner 主机是代码执行的“物理沙盒”其安全等级直接决定 pipeline 的可信度。关键防线有三Docker Socket 权限若用 Docker executorRunner 必须能访问/var/run/docker.sock。直接chmod 666是重大风险应创建专用用户组docker-runner将gitlab-runner用户加入该组并设置 socket 文件组权限为grw。这样既满足功能又避免 root 权限滥用。作业目录隔离默认 runner 在/home/gitlab-runner/builds下创建工作目录。务必确保该路径所在磁盘有充足空间建议单独挂载 SSD 分区并设置builds_dir /data/gitlab-runner/builds指向大容量盘。同时在config.toml中启用clone_url https://your-gitlab.com而非http://localhost避免 runner 主机通过内网直连 GitLab暴露内部网络拓扑。凭证管理Pipeline 中常需访问私有 Docker Registry、云厂商 API、数据库等。绝不可将密码明文写入.gitlab-ci.yml。必须使用 GitLab 的CI/CD Variables项目级或 group 级勾选 “Mask variable” 并设置 “Protected”仅在 protected branches 上暴露。Runner 在执行时会自动注入环境变量安全且可审计。这四大维度不是孤立选项而是交织的决策网络。例如选 Docker executor 就必然涉及 Docker Socket 权限安全维度选 Group Runner 就需规划 tags 体系注册模式维度而并发数设定又依赖于你为 dotnet8 job 压测出的资源基线资源维度。部署前花两小时画一张四维决策表远胜于部署后花两天排查诡异超时。3. 从零开始Docker Executor 的完整部署与深度配置实录以当前最主流的 Docker Executor 为例下面是我在线上环境反复验证过的、可直接抄作业的部署流程。全程基于 Ubuntu 22.04 LTSGitLab 版本 16.10目标是部署一个专用于 dotnet8 项目的 Group Runner并支持 Docker 镜像构建与推送。所有命令均附带原理说明拒绝黑盒操作。3.1 环境准备与基础依赖安装首先确保主机已安装 Docker Engine 并正常运行。这不是 Runner 的依赖而是 Docker Executor 的运行基石。执行以下命令验证# 检查 Docker 是否安装及版本需 20.10 sudo docker --version # 启动 Docker 服务并设为开机自启 sudo systemctl enable docker sudo systemctl start docker # 验证 Docker daemon 正常响应 sudo docker info | grep Server Version接着创建专用用户组与用户避免 runner 以 root 权限运行# 创建 docker-runner 组用于管理 docker socket 权限 sudo groupadd docker-runner # 创建 gitlab-runner 用户禁止登录 shell主目录设为 /home/gitlab-runner sudo useradd --create-home --shell /bin/bash --groups docker-runner gitlab-runner # 设置 gitlab-runner 用户密码仅用于 sudo 操作非必需 sudo passwd gitlab-runner关键原理--shell /bin/bash是为了方便后续调试时sudo -u gitlab-runner bash进入用户环境--groups docker-runner将用户加入新组为后续 socket 权限控制铺路。这一步看似繁琐却是安全隔离的第一道墙。3.2 下载、安装与服务初始化GitLab 官方提供多种安装方式强烈推荐使用官方 APT 仓库因其更新及时、签名验证严格避免手动下载二进制带来的校验风险# 添加 GitLab 官方 GPG key curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash # 更新 apt 缓存 sudo apt-get update # 安装 gitlab-runner会自动安装 systemd service sudo apt-get install gitlab-runner # 验证安装版本确保 16.10以支持 dotnet8 的最新特性 gitlab-runner --version安装完成后Runner 服务已注册为gitlab-runner.service但尚未启动。此时需先完成注册再启用服务。3.3 注册 Runner绑定 Group、打 Tag、设权限注册是 Runner 与 GitLab 建立信任关系的关键步骤。你需要从 GitLab 项目或 Group 的 Settings CI/CD Runners 页面获取Registration Token注意不是 Project ID也不是 Personal Access Token。执行注册命令sudo gitlab-runner register \ --url https://your-gitlab.com/ \ --registration-token GR1348941zxxxxxxxxxxxxxxxxxxxxxx \ --description dotnet8-builder-prod \ --tag-list dotnet8,prod,linux \ --run-untaggedfalse \ --lockedfalse \ --access-levelnot_protected \ --executor docker \ --docker-image alpine:latest \ --docker-volumes /cache \ --docker-privilegedfalse参数详解--url你的 GitLab 实例地址必须与浏览器访问地址一致含 https--registration-token从 GitLab UI 复制的 token有效期 24 小时务必及时使用--descriptionRunner 描述建议包含用途dotnet8、环境prod、OSlinux便于后续识别--tag-list核心调度标签用英文逗号分隔。此处dotnet8表示只运行声明了tags: [dotnet8]的 jobprod表示可用于生产环境相关 joblinux是 OS 标签便于跨平台调度--run-untaggedfalse关闭 untagged job强制所有 job 必须显式声明 tags提升可追溯性--executor docker明确指定 executor 类型--docker-image alpine:latest为 runner 自身非 job指定基础镜像alpine 轻量安全--docker-volumes /cache挂载主机/cache目录到容器内用于 job 级缓存如 nuget packages--docker-privilegedfalse严禁开启privileged 模式等于给 job root 权限是严重安全漏洞。注册成功后会在/etc/gitlab-runner/config.toml生成配置。此时不要启动服务先进行关键配置优化。3.4 深度配置config.toml超越默认的性能与安全加固config.toml是 Runner 的心脏。默认配置仅满足基本运行生产环境必须调整。以下是经过压测验证的核心修改项修改前请备份原文件concurrent 3 # 全局并发数根据 4C8G 主机压测设定为 3 check_interval 30 # 每 30 秒轮询 GitLab 获取新 job平衡延迟与负载 [session_server] session_timeout 1800 # Session 会话超时 30 分钟避免长连接堆积 [[runners]] name dotnet8-builder-prod url https://your-gitlab.com/ token xxx # 自动生成勿修改 executor docker clone_url https://your-gitlab.com/ # 强制使用 HTTPS 克隆避免内网暴露 limit 10 # 单 runner 最大处理 10 个 job防止单点过载 output_limit 4000 # 单 job 日志上限 4000 行防日志爆炸 [runners.cache] Type s3 # 启用 S3 缓存替代默认本地缓存提升跨 runner 一致性 Shared true [runners.cache.s3] ServerAddress s3.your-company.com AccessKey YOUR_S3_ACCESS_KEY SecretKey YOUR_S3_SECRET_KEY BucketName gitlab-runner-cache BucketLocation us-east-1 [runners.docker] tls_verify false image alpine:latest privileged false disable_entrypoint_overwrite false oom_kill_disable false disable_cache false volumes [/cache:/cache:rw, /data/gitlab-runner/builds:/builds:rw] # 显式挂载 builds 目录到大容量盘 shm_size 1024000000 # /dev/shm 大小设为 1GB解决 dotnet build 的 tmpfs 不足问题 memory 3g # 单 job 容器内存上限 3GB cpus 2 # 单 job 容器 CPU 上限 2 核 network_mode bridge # 使用 bridge 网络隔离性优于 host 模式 [runners.custom_build_dir] enabled true # 启用自定义构建目录配合上面的 /builds 挂载实操心得shm_size是 dotnet8 构建的隐形杀手。.NET 的 MSBuild 在编译大型解决方案时会大量使用/dev/shmtmpfs默认 Docker 容器只有 64MB极易触发System.IO.IOException: No space left on device。将shm_size设为 1GB 后该错误归零。这个参数在官方文档里藏得很深但却是 dotnet8 项目部署的必填项。3.5 启动服务与权限固化配置完成后启动服务并检查状态# 启用服务开机自启 sudo systemctl enable gitlab-runner # 启动服务 sudo systemctl start gitlab-runner # 查看服务状态重点关注 Active: active (running) sudo systemctl status gitlab-runner # 查看实时日志确认无报错 sudo journalctl -u gitlab-runner -f最关键的一步是固化 Docker Socket 权限确保 runner 用户能安全访问# 将 gitlab-runner 用户加入 docker 组如果之前没加 sudo usermod -aG docker gitlab-runner # 重启 docker 服务使组变更生效 sudo systemctl restart docker # 验证权限切换到 gitlab-runner 用户尝试 list containers sudo -u gitlab-runner docker ps -q /dev/null 21 echo Permission OK || echo Permission Denied注意usermod -aG docker中的-aappend至关重要。漏掉-a会导致用户被移出其他组如docker-runner引发权限丢失。这是新手最常踩的坑。3.6 验证与上线用一个 dotnet8 pipeline 实战检验最后用真实的.gitlab-ci.yml验证 runner 是否就绪。创建一个极简的 dotnet8 构建 job# .gitlab-ci.yml stages: - build build-dotnet8: stage: build image: mcr.microsoft.com/dotnet/sdk:8.0 tags: - dotnet8 script: - dotnet --version # 输出 8.0.x证明环境正确 - dotnet restore src/MyApp.sln - dotnet build src/MyApp.sln --configuration Release --no-restore artifacts: paths: - bin/Release/ cache: key: $CI_COMMIT_REF_SLUG paths: - **/*.csproj - **/obj/** - **/bin/**提交该文件到项目主分支触发 pipeline。在 GitLab UI 的 CI/CD Pipelines 页面观察 job 状态若显示running并在日志中看到dotnet --version输出8.0.x说明 runner 已成功拉起容器、执行命令若卡在pending检查 runner 状态Settings CI/CD Runners确认其为绿色active且Run for this project已勾选若报错permission denied on /var/run/docker.sock回溯权限固化步骤重点检查usermod和systemctl restart docker是否执行。至此一个安全、高效、专用于 dotnet8 的 Docker Executor Runner 已部署完成。整个过程约 25 分钟但背后是数次失败后沉淀的配置逻辑。4. Pipeline 实战从 dotnet8 构建到 Docker 镜像自动化部署Runner 部署只是基础设施就绪真正的价值在于 pipeline 的编排。结合当前热词 “gitlab ci/cd中docker镜像构建与自动化部署实践”下面给出一个完整的、生产可用的 dotnet8 项目流水线覆盖构建、测试、镜像打包、推送、K8s 部署全流程。所有步骤均基于上一节部署的 runner无需额外配置。4.1 完整.gitlab-ci.yml解析模块化、可复用、易维护# .gitlab-ci.yml # 定义全局变量避免重复 variables: DOTNET_VERSION: 8.0 APP_NAME: myapp IMAGE_REGISTRY: registry.your-company.com IMAGE_TAG: $CI_COMMIT_SHORT_SHA K8S_NAMESPACE: prod # 定义可复用的 job 模板 .default-job: default-job image: mcr.microsoft.com/dotnet/sdk:$DOTNET_VERSION tags: - dotnet8 before_script: - export PATH$PATH:/root/.dotnet/tools - dotnet tool restore # 构建阶段编译、测试、生成发布包 build-and-test: : *default-job stage: build script: - dotnet restore src/$APP_NAME.sln - dotnet build src/$APP_NAME.sln --configuration Release --no-restore - dotnet test tests/$APP_NAME.Tests.csproj --no-build --logger trx;LogFileNametest-results.xml artifacts: paths: - src/$APP_NAME/bin/Release/net8.0/publish/ expire_in: 1 week coverage: /^Total.*?([0-9]{1,3})\%$/ # 镜像构建阶段基于 publish 输出构建轻量 Alpine 镜像 build-docker-image: stage: build image: docker:stable services: - docker:dind # 启用 Docker-in-Docker 服务 tags: - dotnet8 variables: DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: /certs before_script: - docker info - docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $IMAGE_REGISTRY script: - | # 构建多阶段 Dockerfile cat Dockerfile EOF FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS base WORKDIR /app EXPOSE 80 FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build WORKDIR /src COPY . . RUN dotnet restore src/$APP_NAME.sln RUN dotnet publish src/$APP_NAME.sln -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --frombuild /app/publish . ENTRYPOINT [dotnet, $APP_NAME.dll] EOF - docker build -t $IMAGE_REGISTRY/$APP_NAME:$IMAGE_TAG . - docker push $IMAGE_REGISTRY/$APP_NAME:$IMAGE_TAG after_script: - docker logout $IMAGE_REGISTRY # 部署阶段更新 K8s Deployment deploy-to-k8s: stage: deploy image: bitnami/kubectl:latest tags: - dotnet8 variables: KUBECONFIG: /root/.kube/config before_script: - mkdir -p /root/.kube - echo $KUBE_CONFIG | base64 -d /root/.kube/config script: - kubectl config current-context - kubectl set image deployment/$APP_NAME $APP_NAME$IMAGE_REGISTRY/$APP_NAME:$IMAGE_TAG -n $K8S_NAMESPACE - kubectl rollout status deployment/$APP_NAME -n $K8S_NAMESPACE --timeout300s only: - main # 仅 main 分支触发部署4.2 关键环节深度拆解为什么这样写services: - docker:dindDocker-in-Docker 是在 runner 容器内启动一个嵌套的 Docker daemon用于构建镜像。这是安全的因为 dind 容器与宿主机 Docker daemon 隔离。DOCKER_DRIVER: overlay2是推荐的存储驱动性能优于 aufs。多阶段构建Multi-stage BuildDockerfile 中FROM ... AS build和FROM ... AS final将构建环境含 SDK与运行环境仅 ASP.NET Runtime分离。最终镜像大小从 800MB 降至 120MB极大提升推送与拉取速度减少攻击面。kubectl set image这是 K8s 部署的原子操作直接更新 Deployment 的 container image 字段触发滚动更新。相比kubectl apply -f它无需维护 YAML 文件更简洁可靠。only: - main严格限定部署仅在 main 分支发生避免 feature 分支误触发生产变更。GitLab 的rules语法更强大但only对简单场景足够清晰。4.3 CI/CD Variables 配置安全注入敏感信息上述 pipeline 中的$REGISTRY_USER、$REGISTRY_PASSWORD、$KUBE_CONFIG均来自 GitLab 的 CI/CD Variables。在项目 Settings CI/CD Variables 中配置KeyValueMaskedProtectedDescriptionREGISTRY_USERyour-registry-username✅✅私有镜像仓库用户名REGISTRY_PASSWORDbase64 -w0 ~/.docker/config.json | jq -r .auths.registry.your-company.com.auth解码后的密码✅✅镜像仓库密码Masked 防止日志泄露KUBE_CONFIGcat ~/.kube/config | base64 -w0✅✅K8s 集群 kubeconfig 文件 Base64 编码Protected 确保仅在 main 分支暴露实操心得KUBE_CONFIG的 Base64 编码是必须的因为 YAML 文件中不能直接嵌入多行文本。kubectl会自动解码KUBECONFIG指向的文件。将 kubeconfig 存储在 Variables 中比挂载 secret volume 更简单且 GitLab 会自动加密存储。4.4 效果验证与可观测性Pipeline 运行后可在 GitLab UI 直观看到Jobs 面板每个 job 的执行时长、日志、状态passed/failedArtifactsbuild-and-test生成的 publish 文件可下载用于手动验证Environmentsdeploy-to-k8sjob 会自动创建 Environment如prod点击可跳转到 K8s Dashboard 或查看部署详情Pipelines Graph可视化展示各 stage 依赖关系一目了然。更重要的是kubectl get pods -n prod应能看到新 pod 处于Running状态且kubectl describe pod中的Image字段已更新为registry.your-company.com/myapp:abc123。至此从代码提交到生产环境更新全程无人工干预真正实现自动化闭环。5. 常见问题排查与独家避坑指南部署和运行 GitLab Runner 的过程中90% 的问题都集中在几个高频场景。下面是我整理的实战问题速查表每一条都来自真实故障现场附带根因分析与一招解决法。5.1 Pipeline 卡在 “pending”不是 runner 挂了而是调度失灵现象根因分析排查命令解决方案所有 job 长期 pendingRunner 未激活或 token 失效sudo gitlab-runner verify进入 GitLab UISettings CI/CD Runners检查 runner 状态是否为灰色inactive点击 “Enable for this project”若 token 过期重新注册部分 job pending部分 runningjob 的tags与 runner 的tag-list不匹配gitlab-runner list检查.gitlab-ci.yml中 job 的tags字段如tags: [dotnet8]是否与 runner 注册时的--tag-list完全一致区分大小写runner 的--run-untagged是否为falserunner 状态 green但 job 仍 pendingrunner 的limit已达上限sudo gitlab-runner statussudo journalctl -u gitlab-runner | grep job limit在config.toml中增大limit值或增加 runner 实例数检查是否有 long-running job 占用 slot独家技巧gitlab-runner list命令会显示每个 runner 的当前 job 数如busy2这是判断资源是否耗尽的最快方式。比翻日志高效十倍。5.2 Docker Executor 报错权限、资源、网络三座大山错误日志片段根因解决方案ERROR: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?gitlab-runner用户未加入docker组或docker服务未重启sudo usermod -aG docker gitlab-runner→sudo systemctl restart docker→sudo -u gitlab-runner docker ps验证ERROR: failed to dial gRPC: unable to upgrade to tcp, received 404docker:dind服务启动失败常见于DOCKER_DRIVER不匹配在build-docker-imagejob 的variables中显式添加DOCKER_DRIVER: overlay2检查宿主机docker info | grep Storage DriverERROR: Job failed: failed to pull image ... no basic auth credentialsDocker registry 认证失败确认REGISTRY_USER/REGISTRY_PASSWORDVariables 已正确配置且Masked检查docker login命令中的 registry 地址是否与IMAGE_REGISTRY一致ERROR: Job failed: command terminated with exit code 137OOM Killer 杀死了进程内存不足在config.toml的[runners.docker]段落中增加memory 3g检查shm_size是否已设为10240000001GB注意exit code 137 是 Linux OOM Killer 的标志性信号意味着进程因内存超限被强制终止。此时dmesg -T \| grep -i killed process会输出具体被杀进程名。5.3 dotnet8 特有问题SDK、Runtime、NuGet 的三重陷阱问题现象根因解决方案dotnet --version输出7.0.x而非8.0.xjob 使用了错误的image如mcr.microsoft.com/dotnet/sdk:7.0在.gitlab-ci.yml的 job 中显式指定image: mcr.microsoft.com/dotnet/sdk:8.0检查config.toml中[[runners]]的image是否覆盖了 job 的imageerror NU1102: Unable to find package Microsoft.NETCore.App.Runtime...NuGet 源未配置或网络无法访问api.nuget.org在before_script中添加dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org或使用公司私有 NuGet 源The type or namespace name AspNetCore does not exist in the namespace Microsoft项目文件.csproj中TargetFramework未设为net8.0检查src/MyApp/MyApp.csproj确认TargetFrameworknet8.0/TargetFramework若为多框架需指定 net6.0;net