Trên gitlab.local đã có username: diendt và group: beobeo
Bước 1. Tạo dự án
Vào Admin Area -> Overview -> Project -> New Project

Create New Blank Project -> Nhập tên Projet -> Chọn Group hoặc User -> Chọn Visibility Level -> Tick hoặc Untick vào Initialize repository with a README -> Create Project

Bước 2. Clone dự án từ github và khởi tạo dự án
cd <thư mục chứa các dự án>
git clone https://gitlab.com/nhanfu/core2 corejs2
cd corejs2
git config --local user.name "Dien Do"
git config --local user.email "[email protected]"
git remote rename origin old-origin
git remote add origin http://gitlab.local/beobeo/corejs.git
git push --set-upstream origin --all
git push --set-upstream origin --tags

Bước 3. Đăng ký Gitlab-Runner
Đăng nhập vào GitLab và đi đến: Admin Area > CI/CD > Runners.

Click vào Create Instance Runner

Tại đây, bạn nhập tags, Descriptions và bấm Create Runner

Chọn Platform và gõ lệnh theo hướng dẫn vào máy cài gitlab-runner

Tuy nhên gitlab-runner trong bài lab này được cài từ docker nên lệnh có sự thay đổi một chút như sau:
cd <thu muc chua file docker-compose>
docker-compose run --rm gitlab-runner register --url http://gitlab.local --token TOKEN_DUOC_TAO_RA

Đến đây là đã thành công đăng ký gitlab-runner

Đây là đăng ký Runner ở mức Instance gitlab. Ngoài ra ta có thể đăng ký runner ở mức dự án. Bằng cách, vào Settings -> CI/CD

Chọn mục Runner -> Create project runner

Sau đó làm tương tự như phần tạo runner cho Instance
Khi Runner đã được đăng ký, mỗi khi push code lên repository thì ci sẽ chạy qua các lệnh được viết trong file .gitlab-ci.yml
#gitlab.local
stages:
- build
- deploy
variables:
# Disable Docker TLS for dind
DOCKER_TLS_CERTDIR: ""
DOCKER_HOST: "tcp://docker:2375" # <- ensure docker client talks to dind service
DOCKER_BUILDKIT: "1" # <- use BuildKit for buildx pushes
# Build CoreAPI image using buildx and push to Docker Hub (registry cache enabled)
build_coreapi:
stage: build
image: docker:24
services:
- name: docker:24-dind
alias: docker
variables:
DOCKER_DRIVER: overlay2
before_script:
- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- docker buildx create --use || true
- docker info | sed -n '1,5p' # quick sanity check (no secrets printed)
script:
- |
DATE=$(date +%Y%m%d)
SHA=${CI_COMMIT_SHORT_SHA}
IMAGE_TAG="$DATE-$SHA"
REPO="$DOCKER_USERNAME/corejs-coreapi"
docker buildx build --progress=plain --platform linux/amd64 --push \
--tag "$REPO:latest" --tag "$REPO:$IMAGE_TAG" \
--cache-from=type=registry,ref="$REPO:buildcache" \
--cache-to=type=registry,ref="$REPO:buildcache",mode=max \
-f CoreAPI/Dockerfile CoreAPI
rules:
- if: '$CI_COMMIT_BRANCH == "nodejs"'
# Build Frontend image using buildx and push to Docker Hub (registry cache enabled)
build_frontend:
stage: build
image: docker:24
services:
- name: docker:24-dind
alias: docker
variables:
DOCKER_DRIVER: overlay2
before_script:
- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- docker buildx create --use || true
- docker info | sed -n '1,5p'
script:
- |
DATE=$(date +%Y%m%d)
SHA=${CI_COMMIT_SHORT_SHA}
IMAGE_TAG="$DATE-$SHA"
REPO="$DOCKER_USERNAME/corejs-frontend"
docker buildx build --progress=plain --platform linux/amd64 --push \
--tag "$REPO:latest" --tag "$REPO:$IMAGE_TAG" \
--cache-from=type=registry,ref="$REPO:buildcache" \
--cache-to=type=registry,ref="$REPO:buildcache",mode=max \
--build-arg NODE_ENV=production \
-f frontend/Dockerfile frontend
rules:
- if: '$CI_COMMIT_BRANCH == "nodejs"'
# Deploy to remote server by SSH (uses SSH_PRIVATE_KEY variable)
deploy:
stage: deploy
image: alpine:3.18
before_script:
- apk add --no-cache openssh-client bash
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa || true
- chmod 600 ~/.ssh/id_rsa || true
- |
# add known host only for remote SSH deployments
if [ -n "$SERVER" ] && [ "$SERVER" != "localhost" ] && [ "$SERVER" != "127.0.0.1" ]; then
if [ -n "$PORT" ]; then
ssh-keyscan -p $PORT $SERVER >> ~/.ssh/known_hosts
else
ssh-keyscan $SERVER >> ~/.ssh/known_hosts
fi
fi
script:
- DESTINATION_PATH="/home/$USERNAME/deployments/corejs"
- DATE=$(date +%Y%m%d)
- SHA=${CI_COMMIT_SHORT_SHA}
- IMAGE_TAG="$DATE-$SHA"
- |
if [ -z "$SERVER" ] || [ "$SERVER" = "localhost" ] || [ "$SERVER" = "127.0.0.1" ]; then
echo "Running local deploy (no SSH)..."
mkdir -p "$DESTINATION_PATH"
cp -r scripts "$DESTINATION_PATH/"
chmod +x "$DESTINATION_PATH/scripts/deploy.sh"
DOCKER_USERNAME='${DOCKER_USERNAME}' DOCKER_PASSWORD='${DOCKER_PASSWORD}' IMAGE_TAG='${IMAGE_TAG}' ALLOWED_HOSTS='${ALLOWED_HOSTS}' bash "$DESTINATION_PATH/scripts/deploy.sh"
else
echo "Running remote deploy via SSH..."
ssh -p ${PORT} ${USERNAME}@${SERVER} "mkdir -p $DESTINATION_PATH"
scp -P ${PORT} scripts/deploy.sh ${USERNAME}@${SERVER}:$DESTINATION_PATH/
ssh -p ${PORT} ${USERNAME}@${SERVER} "cd $DESTINATION_PATH && \
DOCKER_USERNAME='${DOCKER_USERNAME}' DOCKER_PASSWORD='${DOCKER_PASSWORD}' IMAGE_TAG='${IMAGE_TAG}' ALLOWED_HOSTS='${ALLOWED_HOSTS}' bash ./deploy.sh"
fi
dependencies:
- build_coreapi
- build_frontend
rules:
- if: '$CI_COMMIT_BRANCH == "nodejs"'
Bước 4. Kiểm tra và sửa lỗi
Vào Build -> Pipeline


Pipeline đang lỗi, cần kiểm tra và sửa lỗi, Bấm vào Failed để kiểm tra

Cả 2 pipeline đang lỗi,

Lỗi này là do trong docker không phân dải được gitlab.local. Sửa file /etc/gitlab-runner/config.toml trong docker gitlab-runner file này được mount ra file /opt/gitlab-runner/config/config.toml
[[runners]]
name = "gitlab-runer-local"
url = "http://gitlab.local"
id = 2
token = "glrt-Gs66iShXuQI0W0KQjp4j8286MQp0OjEKdToxCw.01.120274ez3"
token_obtained_at = 2025-10-01T12:46:25Z
token_expires_at = 0001-01-01T00:00:00Z
executor = "docker"
[runners.cache]
MaxUploadedArchiveSize = 0
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
[runners.docker]
tls_verify = false
image = "docker:latest"
privileged = true
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
network_mtu = 0
network_mode = "gitlab-network"
Sửa file xong thì restart gitlab-runner
docker compose restart gitlab-runner
Bấm vào Retry để chạy lại

Nguyên nhân lỗi là login vào docker hub không được. Ta cần thêm biến môi trường để có đủ thôgn tin
Thêm biến môi trường: Settings -> CI/CD chọn mục Variables


Add variable
Nhập tên biến và giá trị

Add thêm biến DOCKER_USERNAME và DOCKER_PASSWORDDOCKER_PASSWORD được tạo ra từ Token Generate trên docker hub.
Vào docker hub, Setting -> Personal access tokens -> Generate new token


Nhập tên Access token description -> Chọn Expiratioin Date -> Chọn Access permission (Read, Write, Delete) -> Generate

Copy token và lưu lại (Docker chỉ hiển thi token 1 lần duy nhất)

Nhập token và DOCKER_PASSWORD rồi kiểm tra lại pipeline

Như vậy đã build thành công.