CI/CD pipeline GitLab CE self-host trên VPS đầy đủ

Chia sẻ bài viết

Mục lục
TL;DR
  • GitLab CE (Community Edition) là full DevOps platform open-source: Git hosting + CI/CD + Container Registry + Issue tracker.
  • Self-host trên VPS 8GB RAM, 80GB SSD. Setup bằng Omnibus package (1 lệnh apt) hoặc Docker.
  • CI/CD runner riêng có thể chạy trên VPS khác để không nặng GitLab server. Mỗi pipeline có executors: shell, docker, kubernetes.
  • Tiết kiệm: GitLab.com Premium 29 USD/user/tháng. Self-host CE free unlimited user (chỉ cần VPS ~50 USD).
  • Bài này hướng dẫn full: cài, config, tạo project, viết .gitlab-ci.yml multi-stage, register runner, deploy automation.

GitHub là chuẩn cho code hosting public, nhưng cho code private nội bộ công ty Việt Nam, GitLab Community Edition đang là lựa chọn phổ biến: free unlimited user, full DevOps stack từ Git hosting tới CI/CD tới Container Registry, data hoàn toàn nội bộ. Bài này hướng dẫn dựng GitLab CE production trên VPS, viết pipeline CI/CD đầy đủ build-test-deploy, register runner riêng để tiết kiệm tài nguyên server chính.

Test trên Cloud VPS 80 (8GB RAM, 80GB SSD CEPH) chạy Ubuntu 24.04. Workload thử nghiệm: 5 dev, 20 repository, 100 pipeline/ngày, vài Docker image build/push. Sau bài này bạn có một "GitHub nội bộ" + CI/CD chạy unlimited, không phụ thuộc GitHub Actions quota.

GitLab CE so với GitHub Enterprise, Gitea

Tiêu chíGitLab CEGitHub Enterprise (self)Gitea
LicenseMIT (CE)CommercialMIT
GiáFree21 USD/user/thángFree
CI/CD built-inGitHub ActionsGitea Actions (mới)
Container RegistryCó (mới)
Issue trackerĐầy đủĐầy đủCơ bản
RAM yêu cầu8 GB16+ GB512 MB
Phù hợpTeam 5-100Enterprise lớnCá nhân/team rất nhỏ

GitLab CE đầy đủ tính năng nhất trong nhóm free. Gitea nhẹ hơn nhưng CI/CD chưa stable bằng. GitHub Enterprise đắt.

Bước 1: Yêu cầu VPS

  • Tối thiểu 4 vCPU, 8 GB RAM, 80 GB SSD cho 5-20 dev.
  • Khuyến nghị 8 vCPU, 16 GB RAM cho 50+ dev và nhiều pipeline.
  • OS: Ubuntu 22.04/24.04 LTS hoặc AlmaLinux 9.
  • Domain để đặt subdomain gitlab.your-domain.com.
  • Tách runner ra VPS khác để không nặng GitLab server (khuyến nghị).

Bước 2: Cài GitLab CE bằng Omnibus

curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
sudo EXTERNAL_URL="https://gitlab.your-domain.com" apt install -y gitlab-ce

Quá trình cài mất 5-10 phút. Omnibus package gồm GitLab Rails, Sidekiq, Postgres, Redis, Nginx, Puma, Gitaly, Workhorse, Mattermost (optional), Container Registry. Tất cả trong 1 deb, dễ update.

Reconfigure (chạy mỗi khi sửa /etc/gitlab/gitlab.rb):

sudo gitlab-ctl reconfigure

Lấy root password ban đầu:

sudo cat /etc/gitlab/initial_root_password

Truy cập https://gitlab.your-domain.com, login user "root" + password. Đổi password ngay trong Profile.

Bước 3: Cấu hình HTTPS với Let's Encrypt

GitLab Omnibus có built-in Let's Encrypt. Sửa /etc/gitlab/gitlab.rb:

external_url 'https://gitlab.your-domain.com'
letsencrypt['enable'] = true
letsencrypt['contact_emails'] = ['[email protected]']
letsencrypt['auto_renew'] = true
letsencrypt['auto_renew_hour'] = 3
sudo gitlab-ctl reconfigure

GitLab tự xin cert, configure Nginx HTTPS. Auto renew 3 giờ sáng.

Bước 4: Disable signup public

Tránh bot register spam. Vào Admin Area > Settings > General > Sign-up restrictions:

  • Uncheck "Sign-up enabled".
  • Hoặc giữ enabled + "Require admin approval" + "Domain restriction" (chỉ email @your-domain.com).

Bạn invite user qua Members trong Project hoặc Admin Area.

Bước 5: Tạo project và clone

  1. Sidebar > New project > Create blank project.
  2. Project name "myapp", Visibility Private.
  3. Copy git URL: [email protected]:root/myapp.git.
  4. Add SSH key vào Profile > SSH Keys.
  5. Local: git clone [email protected]:root/myapp.git.

Bước 6: Viết .gitlab-ci.yml đầu tiên

stages:
  - build
  - test
  - deploy

variables:
  NODE_VERSION: "20"

build:
  stage: build
  image: node:20-alpine
  cache:
    key: $CI_COMMIT_REF_SLUG
    paths:
      - node_modules/
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 week

test:
  stage: test
  image: node:20-alpine
  cache:
    key: $CI_COMMIT_REF_SLUG
    paths:
      - node_modules/
    policy: pull
  script:
    - npm test
  coverage: '/Liness*:s*(d+.d+)/'

deploy:
  stage: deploy
  image: alpine:latest
  only:
    - main
  before_script:
    - apk add --no-cache openssh-client rsync
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | ssh-add -
    - mkdir -p ~/.ssh && chmod 700 ~/.ssh
    - ssh-keyscan $DEPLOY_HOST >> ~/.ssh/known_hosts
  script:
    - rsync -avz --delete dist/ deploy@$DEPLOY_HOST:/var/www/myapp/
    - ssh deploy@$DEPLOY_HOST "pm2 reload myapp"

Commit và push. Pipeline tự chạy: build > test > deploy. Vào Project > Pipelines xem progress.

Variable SSH_PRIVATE_KEY và DEPLOY_HOST set trong Settings > CI/CD > Variables (marked as Protected, Masked).

Bước 7: Register GitLab Runner trên VPS khác

Mặc định GitLab dùng runner shared (chạy trên cùng server). Tách runner ra giảm load:

Trên VPS runner (Cloud VPS 50):

curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt install -y gitlab-runner
sudo gitlab-runner register

Wizard hỏi:

  • URL: https://gitlab.your-domain.com
  • Token: lấy từ Admin Area > CI/CD > Runners > Register an instance runner
  • Description: "vps-runner-01"
  • Tags: linux,docker,vn
  • Executor: docker (recommended)
  • Default image: alpine:latest

Runner đăng ký xong, hiện online trong GitLab UI. Pipeline mới sẽ chạy trên runner này.

Bước 8: Container Registry

GitLab CE có Container Registry built-in. Bật trong /etc/gitlab/gitlab.rb:

registry_external_url 'https://registry.your-domain.com'
sudo gitlab-ctl reconfigure

Trong .gitlab-ci.yml build và push image:

build-image:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest

Image lưu trong registry GitLab, pull về production qua:

docker login registry.your-domain.com
docker pull registry.your-domain.com/root/myapp:latest

Bước 9: Merge Request workflow

  1. Dev tạo branch feature/xxx.
  2. Push branch, GitLab gợi ý "Create merge request".
  3. Tạo MR, gán reviewer.
  4. Pipeline tự chạy trên branch.
  5. Reviewer comment, request change.
  6. Dev push fix, pipeline rerun.
  7. Approved + pipeline pass > Merge.
  8. Pipeline trên main tự chạy deploy stage.

Setup Branch Protection: Settings > Repository > Protected branches > main. Tick "Require approval from code owners". Đảm bảo không ai push thẳng vào main.

Backup GitLab

sudo gitlab-backup create STRATEGY=copy
# File trong /var/opt/gitlab/backups/
sudo cp /etc/gitlab/gitlab-secrets.json /backup/  # CRITICAL
sudo cp /etc/gitlab/gitlab.rb /backup/

Cron daily:

0 3 * * * /opt/gitlab/bin/gitlab-backup create CRON=1
0 4 * * * rclone copy /var/opt/gitlab/backups/ b2:gitlab-backup/

QUAN TRỌNG: gitlab-secrets.json chứa key encrypt. Mất file này = không restore được dù có data backup.

Update GitLab

sudo apt update
sudo apt install gitlab-ce
sudo gitlab-ctl reconfigure

GitLab có policy update theo major version path (15.x > 16.0.x > 16.x latest > 17.0.x). Đọc upgrade docs khi nhảy major. Backup trước.

Monitor và performance

GitLab có Prometheus + Grafana built-in. Bật trong gitlab.rb:

prometheus_monitoring['enable'] = true
grafana['enable'] = true

Truy cập https://gitlab.your-domain.com/-/grafana xem dashboard built-in. Quan trọng: theo dõi RAM Sidekiq (job queue), Postgres connection, Nginx request rate.

Lỗi phổ biến

  • 502 Bad Gateway: GitLab chưa start xong (mất 60-120 giây sau reboot). Đợi rồi reload.
  • "Repository is empty" sau clone: chưa tạo branch main. Initialize repo bằng button trong UI.
  • Pipeline stuck "pending": không có runner online với tag tương ứng. Kiểm tra Admin > Runners.
  • Out of memory: Sidekiq ngốn RAM khi nhiều job. Tăng VPS RAM hoặc giảm sidekiq['max_concurrency'].
  • Disk full /var/opt/gitlab: log Postgres và Sidekiq lớn. Đặt logrotate hoặc giảm log level.

FAQ

GitLab CE thiếu tính năng gì so với Premium/Ultimate?

Premium thêm: code owner enforcement nâng cao, multi-level approval, merge train, audit log mở rộng. Ultimate thêm security scanning (SAST, DAST), license compliance, value stream analytics. Cho 95 phần trăm team SaaS Việt Nam, CE đủ.

GitLab CE có hỗ trợ SSO không?

Có. OAuth Google, GitHub, Microsoft built-in. SAML cũng có nhưng nhiều cấu hình hơn. LDAP/AD cho enterprise. Config trong /etc/gitlab/gitlab.rb section omniauth_providers.

Chạy GitLab + nhiều runner trên 1 VPS được không?

Được nếu VPS đủ RAM (16GB+). Nhưng khi pipeline build nặng (Docker, multi-arch), runner ngốn CPU/disk, ảnh hưởng response GitLab. Tách runner ra VPS riêng là best practice cho team 5+ dev.

Có thể import từ GitHub sang GitLab không?

Có. Settings > Import project > GitHub. Cần GitHub Personal Access Token. Tool tự import code, branch, issue, PR (thành MR), label, milestone. Mất 5-30 phút tùy repo lớn.

GitLab có CI cache giữa các pipeline không?

Có. Cache: key + paths trong .gitlab-ci.yml. Runner lưu cache vào volume riêng (Docker), reuse cho pipeline tiếp theo. Tăng tốc đáng kể cho npm install, pip install.

Cloud VPS cho vibe coder

Cloud VPS đủ RAM cho GitLab CE + runner dedicated

Cloud VPS TND sẵn AlmaLinux 9, Ubuntu 22/24, Debian 12/13. SSD CEPH, snapshot 1-click, backup hằng ngày, network 200Mbps trong nước. Combo: VPS 80 chạy GitLab + VPS 50 chạy runner, tiết kiệm chi phí tổng so với GitLab.com Premium.

Xem 8 cấu hình Cloud VPS →