news 2026/10/15 14:20:31

GitLab Docker单节点部署:原理、挂载与生产避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GitLab Docker单节点部署:原理、挂载与生产避坑指南

1. 为什么选择 Docker 部署 GitLab?这不只是“图省事”那么简单

GitLab Docker 方式部署,这个标题背后藏着的不是一句简单的“用容器跑 GitLab”,而是一整套现代软件交付逻辑的落地实践。我从2017年开始在多个团队主导 DevOps 基础设施搭建,亲手部署过不下12套 GitLab 环境——有直接装在物理机上的,有基于 LXC 的,有 Ansible 全自动编排的,也有纯 Kubernetes 上 Helm 部署的。但直到2020年之后,只要不是超大规模(日活用户破万、CI 并发超200)、且没有强合规审计要求的场景,我给所有新项目写的第一个技术决策文档里,第一行永远是:“GitLab 采用官方 Docker 镜像单节点部署”。这不是跟风,而是踩过坑、算过账、压过测之后的理性选择。

核心关键词就三个:Docker、GitLab、部署。它们组合在一起,解决的是一个非常具体又高频的问题:如何在一台干净的 Linux 服务器上,5分钟内获得一个功能完整、版本可控、升级路径清晰、数据可迁移、故障可回滚的代码托管与 CI/CD 平台?不是“能跑就行”,而是“跑得稳、管得住、扩得开、退得掉”。

它适合谁?三类人最该认真读完这篇:第一类是中小团队的运维或全栈工程师,手头只有一台 4C8G 的云服务器,要快速搭起内部代码平台;第二类是高校实验室或开源项目维护者,需要一个轻量、免维护、不依赖系统包管理器的 GitLab 实例;第三类是正在学习 DevOps 工程实践的开发者,想通过 GitLab 这个典型应用,真正理解容器化部署中卷挂载、端口映射、环境变量控制、配置持久化等核心机制——它比写个 Hello World 容器有价值得多,又比啃 Kubernetes 文档门槛低得多。

很多人误以为“Docker 部署 = 一键拉镜像 run 起来”,结果第二天就发现仓库 clone 失败、CI job 卡住、Web 页面 502、上传大文件超时……这些不是 Docker 的锅,而是没搞懂 GitLab 本身对网络、存储、权限、反向代理的隐性依赖。这篇内容,就是把那些藏在docker run命令背后的“为什么必须这样配”、“为什么不能那样挂”、“为什么改了配置文件重启就失效”全部摊开讲透。不讲虚的原理,只讲你执行每一步时,系统底层到底在发生什么,以及我当年在某公司生产环境凌晨三点排查 502 错误时,是怎么一步步定位到/var/opt/gitlab目录属主被意外重置的。

2. 整体设计思路:为什么是单容器 + 外部卷,而不是多容器拆分?

2.1 官方镜像的设计哲学:All-in-One 不是偷懒,而是权衡

GitLab 官方提供的gitlab/gitlab-ce镜像,是一个典型的 All-in-One 架构容器:Nginx、GitLab Rails 应用、Sidekiq 后台任务、Unicorn/Puma Web 服务、PostgreSQL、Redis、Logrotate、甚至 GitLab Shell 和 Gitaly —— 全部打包进一个镜像,运行在一个容器进程里。初学者常问:“这不符合微服务理念啊?为什么不把数据库、缓存、Web 层拆成独立容器?”这个问题问得非常好,答案也很实在:因为 GitLab 本身不是为容器原生设计的单体应用,强行拆分带来的运维复杂度和稳定性风险,远超其理论收益。

我做过对比测试:在同等 4C8G 服务器上,用官方单容器部署,启动时间 92 秒,内存常驻 1.8GB;而用社区维护的“GitLab 分布式部署方案”(PostgreSQL 单独容器 + Redis 单独容器 + GitLab CE 容器),启动时间 210 秒,内存常驻 2.3GB,且 Sidekiq 与 Rails 进程间通信延迟增加 37%,导致 Merge Request 页面加载慢 1.8 秒。更关键的是,当 PostgreSQL 容器因磁盘满而崩溃时,GitLab 容器不会自动降级,而是整个 Web 界面返回 500,且恢复流程需手动介入检查 pg 数据一致性。

官方选择 All-in-One,本质是做了三重权衡:

  • 部署确定性:所有组件版本严格绑定,避免“PostgreSQL 13 兼容 GitLab 15.0,但不兼容 15.1”的跨容器版本错配;
  • 调试便捷性:所有日志集中在/var/log/gitlab/下,gitlab-ctl tail一条命令看全链路,不用kubectl logs -f gitlab-web && kubectl logs -f gitlab-sidekiq切来切去;
  • 资源效率:共享同一套内核调度、同一块宿主机磁盘 I/O 缓存,避免多容器间 IPC 开销和网络栈冗余。

所以,我们的整体设计思路非常明确:拥抱官方镜像的 All-in-One 特性,用 Docker 的卷(Volume)和绑定挂载(Bind Mount)机制,将状态数据(配置、代码仓库、数据库、日志)彻底剥离出容器层,实现“一次构建,处处运行;一次配置,永久生效”。这不是妥协,而是把容器技术用在了它最擅长的地方——隔离运行时环境,而非强行解耦本就不该解耦的系统。

2.2 核心架构图:三层分离,各司其职

整个部署结构可以清晰划分为三个逻辑层:

层级组成部分存储位置生命周期管理关键特性
容器层(易变)gitlab/gitlab-ce:16.9.0镜像、运行时进程、临时文件宿主机内存 & 容器可写层每次docker run重建,docker stop后消失无状态、可丢弃、版本可精确控制
数据层(持久)/etc/gitlab(配置)、/var/opt/gitlab(代码库+DB+Redis)、/var/log/gitlab(日志)宿主机目录(如/srv/gitlab/config)或命名卷(gitlab-config)手动创建,长期保留,升级时复用有状态、不可丢弃、需定期备份
网络层(抽象)Docker 网桥(gitlab-network)、端口映射(80→8080, 443→8443, 22→2222)宿主机网络命名空间docker network create一次,长期有效可配置、可隔离、支持自定义 DNS

这个三层模型,直接决定了我们后续所有操作的顺序和逻辑。比如,为什么第一次启动必须先mkdir -p /srv/gitlab/{config,data,logs}?因为这是在为“数据层”打地基,确保容器启动时,-v /srv/gitlab/config:/etc/gitlab这个挂载点下有正确的目录结构和权限。为什么不能用docker commit保存修改后的容器?因为那只会固化“容器层”,而真正的配置和数据还在外部卷里,commit 后的镜像毫无意义。这些细节,都是从架构设计里自然推导出来的,不是凭空记住的命令。

2.3 为什么拒绝 Compose?YAML 不是银弹

网上大量教程推荐用docker-compose.yml部署 GitLab,但我在线上环境已三年未使用。原因很实际:Compose 是为本地开发编排多服务设计的,不是为生产级单应用部署准备的。我们来算一笔账。

一个典型的gitlab-compose.yml文件,至少包含 20 行 YAML,涉及version、services.gitlab.image、volumes、ports、environment、restart、networks、healthcheck等八九个字段。每次升级 GitLab 版本,你需要手动修改image:行,然后docker-compose pull && docker-compose up -d。看似方便,但问题藏在细节里:

  • 当docker-compose.yml中environment与/srv/gitlab/config/gitlab.rb冲突时(比如都设置了external_url),以哪个为准?答案是gitlab.rb优先,但 Compose 的环境变量会覆盖容器内默认值,导致配置解析异常;
  • docker-compose down会删除所有关联卷,除非你显式声明volumes: [config, data, logs]并加external: true,否则一不小心就删库跑路;
  • 更隐蔽的是,Compose 默认创建的匿名卷,其元数据存储在/var/lib/docker/volumes/下,一旦宿主机磁盘损坏,这些卷的归属关系极难恢复。

而纯docker run命令,虽然长一点,但优势极其明显:

  • 完全透明:所有参数一目了然,没有 YAML 解析层的黑盒;
  • 原子操作:docker stop gitlab && docker rm gitlab清理干净,docker run重新创建,无残留;
  • 脚本友好:可轻松嵌入 Bash 脚本做版本校验、磁盘空间预检、备份前停服等自动化逻辑;
  • 故障定位快:docker inspect gitlab | grep -A 10 Mounts一眼看到挂载点是否正确,不用docker-compose config转换再查。

所以,我的实操原则是:开发测试用 Compose 快速启停,生产部署只用docker run命令 + Shell 脚本封装。后文所有步骤,均基于此原则展开,这也是保障稳定性的第一道防线。

3. 核心细节解析:挂载点、权限、端口、SSL,一个都不能错

3.1 四大挂载点详解:为什么必须用 Bind Mount,而不是 Named Volume?

GitLab 官方文档明确建议使用绑定挂载(Bind Mount),而非命名卷(Named Volume)。这不是随意指定,而是由 GitLab 的内部文件系统访问模式决定的。我们逐个拆解四个必须挂载的目录:

1./etc/gitlab—— 配置中枢,权限敏感
这是 GitLab 的“大脑”,存放gitlab.rb主配置文件。官方镜像启动时,会运行/assets/setup.sh脚本,读取此目录下的gitlab.rb,生成 Nginx、PostgreSQL、Redis 等所有子服务的实际配置。关键点在于:此目录在容器内必须由root:root所有,且权限为755。如果你用docker volume create gitlab-config创建命名卷,Docker 默认会以root用户创建目录,但卷的挂载行为可能导致权限继承异常。而mkdir -p /srv/gitlab/config && chown -R root:root /srv/gitlab/config后再挂载,能 100% 保证权限一致。我曾遇到过一次诡异问题:gitlab.rb里写了nginx['enable'] = false,但 Nginx 依然启动,最后发现是命名卷挂载后,容器内/etc/gitlab的stat显示uid=0 gid=0但st_uid != 0,导致 setup.sh 权限校验失败,跳过了配置加载。

2./var/opt/gitlab—— 数据心脏,IO 密集
这里存放着所有“值钱”的东西:Git 仓库(repositories/)、PostgreSQL 数据(postgresql/data/)、Redis 数据(redis/dump.rdb)、CI Runner 缓存(gitlab-ci/builds/)。它对磁盘 I/O 性能极度敏感。绑定挂载到宿主机 SSD 目录(如/srv/gitlab/data),能直接利用宿主机文件系统缓存和 IO 调度器。而命名卷走的是 Docker 的 overlay2 存储驱动,多了一层元数据映射,在高并发 Git push 场景下,IOPS 损失可达 15%-20%。更重要的是,/var/opt/gitlab下的子目录(如postgresql/data)在首次启动时由gitlab-ctl reconfigure自动创建,其属主必须是git:git(UID 998/GID 998),这个 UID/GID 是官方镜像内置的,与宿主机无关。绑定挂载能确保容器内进程以正确 UID 写入文件,命名卷则可能因宿主机 UID 冲突导致权限错误。

3./var/log/gitlab—— 故障线索,轮转关键
日志不是随便写写就完事。GitLab 内置 Logrotate,会按天切割nginx,gitlab-rails,sidekiq等日志,并压缩归档。这些操作依赖于容器内logrotate进程对目录的写权限。如果挂载为只读,Logrotate 会静默失败,日志文件无限增长。绑定挂载到宿主机目录,我们还能用宿主机的logrotate做二次归档(如compress+maxage 90),而命名卷的日志文件无法被宿主机工具直接管理。

4./opt/gitlab/embedded/service/gitlab-shell/.gitconfig—— 小细节,大影响
这个文件常被忽略,但它控制着 GitLab Shell 的全局 Git 配置,比如core.autocrlf、user.email。GitLab 在处理 Web IDE 提交、API 创建 Commit 等操作时,会读取此文件。如果不挂载,容器重启后此文件会重置为默认值,导致某些 Git 操作行为不一致。它必须作为单独的 Bind Mount 挂载,且文件需提前创建并设置644权限。

提示:所有挂载点必须使用绝对路径,且宿主机目录需提前mkdir -p创建。Docker 不会自动创建父目录,-v /srv/gitlab/config:/etc/gitlab若/srv/gitlab不存在,会创建一个空目录而非报错,导致后续权限混乱。

3.2 端口映射的底层逻辑:为什么 80/443/22 不能直接映射?

新手最常犯的错误,就是写docker run -p 80:80 -p 443:443 -p 22:22 ...。这在绝大多数情况下会失败,原因在于:宿主机的 80、443、22 端口,默认被 systemd-resolved、sshd 或其他服务占用,且 Docker 的-p参数要求宿主机端口必须空闲。更深层的问题是,GitLab 容器内的 Nginx 默认监听localhost:8080和localhost:8443,SSH 服务监听localhost:22,它并不期望直接暴露在0.0.0.0:80上。

官方镜像的设计是:容器内服务只监听127.0.0.1(即 localhost),通过 Docker 的端口映射机制,将宿主机的0.0.0.0:8080流量转发到容器的127.0.0.1:8080。这样做的安全优势极大:容器内服务无法被其他容器直接访问(因为只绑定了 localhost),只能通过 Docker 网络栈转发,天然隔离。

因此,标准映射是:

  • 8080:8080→ 对应 HTTP 访问(http://your-domain:8080)
  • 8443:8443→ 对应 HTTPS 访问(https://your-domain:8443)
  • 2222:22→ 对应 SSH Git 操作(git@your-domain:2222/username/project.git)

但生产环境绝不能让用户访问:8080这种端口。解决方案是:在宿主机上部署 Nginx 或 Traefik 作为反向代理,监听0.0.0.0:80/443,再将请求转发到http://127.0.0.1:8080和https://127.0.0.1:8443。这样既满足用户无端口访问,又保持了容器内服务的安全绑定策略。gitlab.rb中的external_url 'https://gitlab.example.com'就是为此而设,它告诉 GitLab:“所有生成的 URL(邮件链接、API 返回地址)都用这个域名,别用http://localhost:8080”。

注意:SSH 端口映射2222:22是必须的,且gitlab.rb中必须配置gitlab_rails['git_ssh_host'] = 'gitlab.example.com'和gitlab_rails['git_ssh_port'] = 2222,否则 Web 页面显示的 Clone URL 仍是git@gitlab.example.com:username/project.git(走默认 22 端口),导致连接超时。

3.3 SSL 配置的两种路径:自签名 vs Let's Encrypt,选哪个?

GitLab 强制要求 HTTPS(尤其启用 Container Registry 或 CI/CD 时),SSL 配置是绕不开的坎。官方提供两种主流方式,适用场景截然不同:

方案一:自签名证书(适合内网/测试)
优点:零成本、秒生成、完全可控。
缺点:浏览器警告、Git CLI 需额外配置git config --global http.sslVerify false(不推荐)。
实操步骤:

# 在 /srv/gitlab/config/ 下生成 mkdir -p /srv/gitlab/config/ssl openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout /srv/gitlab/config/ssl/gitlab.example.com.key \ -out /srv/gitlab/config/ssl/gitlab.example.com.crt \ -subj "/C=CN/ST=Beijing/L=Beijing/O=GitLab/CN=gitlab.example.com" # 设置权限,必须是 root:root 且 600 chown root:root /srv/gitlab/config/ssl/gitlab.example.com.* chmod 600 /srv/gitlab/config/ssl/gitlab.example.com.*

然后在gitlab.rb中启用:

nginx['redirect_http_to_https'] = true nginx['ssl_certificate'] = "/etc/gitlab/ssl/gitlab.example.com.crt" nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/gitlab.example.com.key"

方案二:Let's Encrypt(适合公网/生产)
优点:浏览器信任、免费、自动续期。
缺点:需域名解析到宿主机、首次验证需开放 80 端口、续期脚本需定时运行。
关键点在于:Let's Encrypt 验证必须由 GitLab 容器内的 Nginx 完成,因此gitlab.rb中必须配置lets_encrypt['enable'] = true,且external_url必须是有效的公网域名。官方镜像内置了 Certbot,会在gitlab-ctl reconfigure时自动申请和续期。但要注意:/var/opt/gitlab/lets-encrypt目录必须挂载到宿主机,否则容器重启后证书丢失。我在某客户环境就因忘记挂载此目录,导致证书过期后自动续期失败,整个平台 HTTPS 失效。

实操心得:无论哪种方案,gitlab.rb中的nginx['ssl_protocols']和nginx['ssl_ciphers']务必按最新安全标准配置(如TLSv1.2 TLSv1.3,禁用SSLv3、TLSv1.0),否则会被安全扫描工具标为高危。这些参数在gitlab.rb中默认注释掉,必须手动取消注释并修改。

4. 实操过程:从零开始,一条命令一条命令带你跑通

4.1 环境准备:操作系统、Docker、资源预估,缺一不可

部署前,请务必确认宿主机满足以下硬性条件,这是后续所有步骤成功的前提:

操作系统:仅支持64位 Linux,推荐 Ubuntu 22.04 LTS 或 CentOS Stream 9。Windows 或 macOS 上的 Docker Desktop 仅用于测试,因其文件系统性能(尤其是 WSL2 的 ext4 overlay)无法满足 GitLab 的高 IO 要求。我曾用 MacBook M1 Pro 跑过 GitLab,git push一个 100MB 的二进制文件耗时 47 秒,而在同配置云服务器上仅需 3.2 秒——差距就在文件系统层。

Docker 版本:必须≥ 20.10。旧版本(如 19.03)的--cgroup-parent参数不支持,而 GitLab 容器启动时会尝试设置 cgroup 限制,版本不匹配会导致启动失败或 OOM Killer 误杀进程。检查命令:docker --version。

资源预估(最低要求):

  • CPU:4 核(GitLab 启动后常驻 3 个 Ruby 进程 + 1 个 PostgreSQL + 1 个 Redis + Nginx,单核负载极易超 100%);
  • 内存:8GB(PostgreSQL 默认分配 2GB,Redis 1GB,Rails 应用堆 2GB,剩余为系统缓存);
  • 磁盘:50GB SSD(/var/opt/gitlab是主要消耗者,每个 Git 仓库平均占用 500MB,100 个项目就是 50GB;日志和备份另计);
  • Swap:必须关闭!swapoff -a && sed -i '/swap/d' /etc/fstab。GitLab 的 PostgreSQL 对 Swap 极其敏感,一旦触发 Swap,查询延迟飙升至秒级,且gitlab-ctl restart可能卡死。

执行以下命令完成基础准备:

# 更新系统 sudo apt update && sudo apt upgrade -y # Ubuntu/Debian # 或 sudo dnf update -y # CentOS/RHEL # 安装 Docker(以 Ubuntu 为例) curl -fsSL https://get.docker.com | sh sudo systemctl enable docker sudo systemctl start docker # 添加当前用户到 docker 组(避免每次 sudo) sudo usermod -aG docker $USER # 退出终端重登,使组生效 # 关闭 Swap sudo swapoff -a sudo sed -i '/swap/d' /etc/fstab # 创建 GitLab 数据目录(SSD 目录优先) sudo mkdir -p /srv/gitlab/{config,data,logs} sudo chown -R $USER:$USER /srv/gitlab # 注意:此处 chown 是给宿主机用户,容器内权限由后续 gitlab-ctl 控制

提示:/srv/gitlab目录必须位于 SSD 分区。我见过太多案例,把数据目录放在机械硬盘或云盘(如 AWS EBS gp2)上,导致gitlab-ctl reconfigure执行时间超过 20 分钟,且首次启动后 Web 页面加载缓慢。用lsblk -d -o NAME,ROTA查看ROTA=0表示 SSD。

4.2 配置文件编写:gitlab.rb不是模板,而是你的部署契约

/srv/gitlab/config/gitlab.rb是整个部署的“宪法”,它不是一份可有可无的配置文件,而是你与 GitLab 官方镜像之间的一份契约。每一行配置,都在告诉 GitLab “你应该如何运行”。下面是我经过 12 个生产环境验证的最小可用gitlab.rb模板,已去除所有注释,只保留必需项:

# 外部访问地址(必须!决定所有生成的 URL) external_url 'https://gitlab.example.com' # SSH 配置(必须!否则 Clone URL 错误) gitlab_rails['git_ssh_host'] = 'gitlab.example.com' gitlab_rails['git_ssh_port'] = 2222 # Nginx 配置 nginx['enable'] = true nginx['redirect_http_to_https'] = true nginx['ssl_certificate'] = "/etc/gitlab/ssl/gitlab.example.com.crt" nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/gitlab.example.com.key" nginx['ssl_protocols'] = "TLSv1.2 TLSv1.3" nginx['ssl_ciphers'] = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384" # PostgreSQL 配置(优化 IO) postgresql['enable'] = true postgresql['shared_buffers'] = "256MB" postgresql['max_connections'] = 200 postgresql['checkpoint_timeout'] = "10min" postgresql['effective_cache_size'] = "1GB" # Redis 配置 redis['enable'] = true redis['maxmemory'] = "1GB" redis['maxmemory_policy'] = "allkeys-lru" # GitLab Rails 配置 gitlab_rails['enable'] = true gitlab_rails['time_zone'] = 'Asia/Shanghai' gitlab_rails['git_max_size'] = 52428800 # 50MB gitlab_rails['git_timeout'] = 60 # Sidekiq 配置(后台任务) sidekiq['enable'] = true sidekiq['concurrency'] = 25 # 日志配置 logging['logrotate_enabled'] = true logging['logrotate_frequency'] = "daily" logging['logrotate_maxsize'] = "100MB"

这份配置的关键点解析:

  • external_url必须是完整的 HTTPS URL,且域名需能被公网解析(Let's Encrypt 验证所需);
  • git_ssh_port必须与 Docker 端口映射2222:22严格对应;
  • ssl_certificate和ssl_certificate_key路径是容器内路径,因此挂载时必须确保宿主机/srv/gitlab/config/ssl/下有对应文件;
  • postgresql['shared_buffers']设为256MB是经验阈值,设太高会挤占系统缓存,设太低则频繁刷盘;
  • gitlab_rails['git_max_size'] = 52428800将单文件上传限制设为 50MB,防止用户上传超大二进制文件拖垮服务。

注意:gitlab.rb文件必须由root用户创建,且权限为644。如果权限不对,gitlab-ctl reconfigure会静默跳过加载,导致所有配置无效。执行ls -l /srv/gitlab/config/gitlab.rb确认输出为-rw-r--r-- 1 root root。

4.3 启动命令详解:为什么这条命令要写 200 字?

现在,到了最关键的一步:执行docker run。下面这条命令,是我线上环境稳定运行三年的“黄金命令”,每一个参数都有其不可替代的作用:

sudo docker run --detach \ --hostname gitlab.example.com \ --publish 8080:8080 --publish 8443:8443 --publish 2222:22 \ --name gitlab \ --restart always \ --volume /srv/gitlab/config:/etc/gitlab \ --volume /srv/gitlab/data:/var/opt/gitlab \ --volume /srv/gitlab/logs:/var/log/gitlab \ --volume /srv/gitlab/config/ssl:/etc/gitlab/ssl:ro \ --shm-size 256m \ --memory 6g \ --cpus 3.5 \ gitlab/gitlab-ce:16.9.0

逐参数解读:

  • --detach:后台运行,这是生产环境唯一合法模式;
  • --hostname gitlab.example.com:设置容器 hostname,GitLab 内部服务(如 Gitaly)依赖此值进行服务发现;
  • --publish:端口映射,注意是8080:8080而非80:80;
  • --name gitlab:固定容器名,便于后续docker exec -it gitlab bash进入调试;
  • --restart always:确保宿主机重启后 GitLab 自动恢复,这是高可用的第一步;
  • --volume:四大挂载点,其中ssl目录加了:ro(只读),防止容器内进程意外修改证书;
  • --shm-size 256m:极其关键!GitLab 的 PostgreSQL 使用/dev/shm作为共享内存,官方要求至少256MB,默认64MB会导致 PostgreSQL 启动失败或随机崩溃;
  • --memory 6g:限制容器内存上限为 6GB,防止 PostgreSQL 或 Rails 进程无节制吃内存,触发 OOM Killer;
  • --cpus 3.5:限制 CPU 使用率为 3.5 核,避免单个容器占满所有 CPU,影响宿主机其他服务;
  • gitlab/gitlab-ce:16.9.0:必须指定精确版本标签!不要用latest,否则某天docker pull可能拉到不兼容的 17.x,导致服务中断。

执行后,用sudo docker ps -a查看容器状态。首次启动会耗时 3-5 分钟,因为容器内会执行gitlab-ctl reconfigure,生成所有子服务配置并初始化数据库。此时STATUS会显示healthy或running (health: starting)。不要着急刷新页面,耐心等待。

实操心得:如果docker ps显示容器Exited (1),立刻执行sudo docker logs gitlab查看错误。90% 的问题都源于gitlab.rb配置语法错误(如少了个})、SSL 证书路径错误、或/srv/gitlab/data目录权限不对。日志里会明确提示Error executing actionrunon resource 'execute[gitlab-ctl reconfigure]',顺着这个线索查,基本都能解决。

4.4 首次访问与初始化:从登录页面到第一个项目

容器启动成功后,打开浏览器访问https://gitlab.example.com:8443(如果你用了 Let's Encrypt,则是https://gitlab.example.com)。首次访问会跳转到密码重置页面,这是 GitLab 的安全设计:初始 root 用户密码未设置,必须通过 Web 页面强制重置。

重置密码后,用root用户和新密码登录。此时你会看到一个欢迎界面,提示“Welcome to your GitLab instance”。接下来,你应该立即做三件事:

1. 创建第一个用户(非 root)
root 是超级管理员,不应直接用于日常开发。点击右上角头像 →Admin Area→Users→New user,创建一个普通用户(如dev1),并赋予Developer角色。然后用dev1登录,测试权限。

2. 创建第一个 Group 和 Project
点击Groups→New group,创建一个组(如my-team)。然后在该组下点击New project,选择Create blank project,输入名称(如hello-world)。创建成功后,你会得到一个 SSH 和 HTTPS 的 Clone URL。用dev1用户的凭据克隆测试:

git clone https://gitlab.example.com/my-team/hello-world.git cd hello-world echo "# Hello World" > README.md git add . git commit -m "init" git push origin main

如果push成功,说明整个 Git 服务链路(Web → Rails → Gitaly → Git)完全打通。

3. 配置 SMTP(邮件通知)
GitLab 的 Issue、MR 评论、注册确认都依赖邮件。编辑/srv/gitlab/config/gitlab.rb,添加 SMTP 配置(以腾讯企业邮箱为例):

gitlab_rails['smtp_enable'] = true gitlab_rails['smtp_address'] = "smtp.exmail.qq.com" gitlab_rails['smtp_port'] = 465 gitlab_rails['smtp_user_name'] = "admin@your-domain.com" gitlab_rails['smtp_password'] = "your-app-password" gitlab_rails['smtp_domain'] = "your-domain.com" gitlab_rails['smtp_authentication'] = "login" gitlab_rails['smtp_enable_starttls_auto'] = true gitlab_rails['smtp_tls'] = true

然后执行sudo docker exec -it gitlab gitlab-ctl reconfigure重新加载配置。发送测试邮件:sudo docker exec -it gitlab gitlab-rake gitlab:env:info会输出环境信息,同时触发一封测试邮件到admin@your-domain.com。

提示:SMTP 密码必须是“邮箱授权码”,而非邮箱登录密码。这是企业邮箱的安全要求,很多新手卡在这里,反复重试登录密码导致账户被锁定。

5. 常见问题与排查技巧实录:那些凌晨三点教会我的事

5.1 502 Bad Gateway:Nginx 与 Rails 的“失联”之谜

现象:浏览器访问https://gitlab.example.com显示502 Bad Gateway,但docker ps显示容器Up 10 minutes (healthy)。这是 GitLab 部署中最经典的“假健康”问题。

排查思路:502 意味着 Nginx 作为反向代理,无法连接到上游的 Rails 应用。但gitlab-ctl status显示unicorn和sidekiq都是run状态,说明服务进程活着,只是通信断了。

**

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 14:20:29

基于DRBD+Pacemaker+Corosync的MySQL高可用架构实战

1. 高可用架构选型背后的思考1.1 为什么是DRBD而不是共享存储做过MySQL高可用的朋友都知道,方案大致分两派:共享存储派和复制派。共享存储派里最常见的是SAN或者NAS挂载,但中小团队往往没有独立存储设备,预算和运维成本都扛不住。…

作者头像 李华
网站建设 2026/10/15 14:20:19

C++调用命令行程序的三种方法:从Shell重定向到Boost.Process实战

1. 项目概述:为什么命令行输出重定向不是“写个 > 就完事”的小事在 Windows 开发、自动化脚本、CI/CD 流水线或本地工具链集成中,我几乎每天都要和cmd.exe打交道——不是为了怀旧,而是因为很多老旧但关键的内部工具、硬件驱动配套程序、第…

作者头像 李华
网站建设 2026/10/15 14:19:46

Windows Server 2012 R2 内网离线安装 IIS 实战指南

简介:这份资源是面向IT运维人员与系统管理员的Windows Server 2012 R2 IIS离线安装包,用于在无外网或网络受限的服务器环境中添加、更新IIS角色与相关组件,解决内网部署Web服务时无法在线拉取源文件的问题。压缩包共1568个文件,约…

作者头像 李华
网站建设 2026/10/15 14:19:30

GESP五级贪心算法全解:从区间调度到堆优化,掌握考点与避坑技巧

我把GESP五级的真题和模拟题翻来覆去做过好几遍之后,发现一个特别明显的规律:凡是考算法思想的大题,十道里至少有六道能闻到贪心的味道。考生圈子里甚至流传着一句话——五级能不能过,很大程度上看贪心学得透不透。这个说法不算夸…

作者头像 李华
网站建设 2026/10/15 14:18:28

本地知识库语义检索实战:Docker部署PGVector与MCP服务

1. 为什么要在本地给 WorkBuddy 配一套向量库WorkBuddy 这类本地知识库工具,核心能力是把散落在本地的文档、笔记、代码片段变成可检索、可对话的知识资产。但很多人用着用着会发现一个瓶颈:纯关键词检索越来越不够用。你搜“部署流程”,它只…

作者头像 李华
网站建设 2026/10/15 14:18:22

SpringBoot2+Vue3宠物领养系统实战拆解:从架构设计到功能落地

做一个完整项目最大的痛点不是“不会敲代码”,而是“不知道整个系统该怎么串起来”。很多同学学完Java基础、SpringBoot、Vue,真到自己动手做一个带前后端的完整项目时,还是容易卡壳:表结构怎么设计、接口怎么分层、前端怎么对接、…

作者头像 李华