GitLab ๋ด์ฅ CI/CD ํ์ดํ๋ผ์ธ ์๋ฒฝ ๊ฐ์ด๋: GitLab Runner ์ค์น ๋ฐ ๋ฑ๋ก, .gitlab-ci.yml ๋ฌธ๋ฒ ๊ตฌ์กฐ, StagesยทJobs ์ํคํ
์ฒ, Artifacts vs Cache ์ฐจ์ด์ , Rules ์กฐ๊ฑด๋ถ ์คํ, Docker-in-Docker(dind) ์ปจํ
์ด๋ ๋น๋, ์๋ ์น์ธ(Manual) ๋ฐฐํฌ, ํ
ํ๋ฆฟ ์์(extends/include) ๋ฐ ํ๋ก๋์
์ด์ ์ฒดํฌ๋ฆฌ์คํธ
## 1. ํ์ ์ฌ์ ์ค์น ์๊ฑด & ๊ฐ๋ฐ ํ๊ฒฝ ๊ตฌ์ฑ (Prerequisites)
GitLab CI/CD๋ GitLab ์ ์ฅ์์ ๊ธฐ๋ณธ ๋ด์ฅ๋ ๊ฐ๋ ฅํ ์ง์์ ํตํฉ/์ง์์ ๋ฐฐํฌ ์์คํ
์
๋๋ค. ๋ณ๋์ CI ์๋ฒ(Jenkins ๋ฑ)๋ฅผ ์ค์นํ ํ์ ์์ด, ์ ์ฅ์ ๋ฃจํธ์ `.gitlab-ci.yml` ํ์ผ์ ์์ฑํ๊ณ **GitLab Runner**๋ฅผ ์ฐ๊ฒฐํ๋ ๊ฒ๋ง์ผ๋ก ๋์ํฉ๋๋ค.
### ๐ฆ 1. GitLab ํธ์คํ
ํ๊ฒฝ
- **GitLab.com (SaaS)**: ๊ฐ์
์ฆ์ ๋ฌด๋ฃ ๊ณต์ ๋ฌ๋(Shared Runner)๊ฐ ์ ๊ณต๋๋ฏ๋ก ๋ณ๋ ๋ฌ๋ ์ค์น ์์ด ์ฆ์ ํ์ดํ๋ผ์ธ์ ์คํํ ์ ์์ต๋๋ค.
- **GitLab Self-Managed (์จํ๋ ๋ฏธ์ค CE/EE)**: ํ์ฌ ์ฌ๋ด๋ง ์๋ฒ์ ์ค์น๋ GitLab ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ ์ฉ GitLab Runner ์ค์น๊ฐ ํ์์
๋๋ค.
### ๐ 2. GitLab Runner ์ค์น & ๋ฑ๋ก (Docker Executor ๊ถ์ฅ)
์ฌ๋ด ์๋ฒ๋ ๋ก์ปฌ PC๋ฅผ ๋น๋ ์คํ๊ธฐ(Runner)๋ก ๋ฑ๋กํฉ๋๋ค:
```bash
# 1. Docker ํ๊ฒฝ์์ GitLab Runner ์ปจํ
์ด๋ ์คํ
docker run -d \
--name gitlab-runner \
--restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v gitlab-runner-config:/etc/gitlab-runner \
gitlab/gitlab-runner:latest
# 2. GitLab ์ธ์คํด์ค์ Runner ๋ฑ๋ก (Registration)
# โป GitLab ํ๋ก์ ํธ -> Settings -> CI/CD -> Runners -> Registration Token ํ์ธ
docker run --rm -it \
-v gitlab-runner-config:/etc/gitlab-runner \
gitlab/gitlab-runner:latest register \
--non-interactive \
--url "https://gitlab.com/" \
--registration-token "GLRT-xxxxxxxxxxxxxxxxxxxx" \
--executor "docker" \
--docker-image "alpine:latest" \
--description "my-docker-runner" \
--tag-list "docker,linux" \
--run-untagged="true" \
--locked="false"
```
### โ๏ธ 3. ํ์ผ ๊ท๊ฒฉ ๋ฐ ํ๊ฒฝ๋ณ์(Variables) ์ค์
1. **ํ์ผ ์์น**: ์ ์ฅ์ ์ต์์ ๋ฃจํธ์ `.gitlab-ci.yml` ์ด๋ฆ์ผ๋ก ์์ฑํฉ๋๋ค.
2. **๋น๋ฐ ํ๊ฒฝ๋ณ์ ์ค์ **:
- ๊ฒฝ๋ก: `Settings` โ `CI/CD` โ `Variables` โ **Add variable**
- **Type**: `Variable` (์ผ๋ฐ ๋ฌธ์์ด) ๋๋ `File` (์ธ์ฆ์, pem ํค)
- **Flags**:
- **Protect variable**: `Protected` ๋ธ๋์น/ํ๊ทธ์์๋ง ๋
ธ์ถ
- **Mask variable**: ์์
์ฝ์ ๋ก๊ทธ์ ํ๋ฌธ ๋
ธ์ถ ๋ฐฉ์ง (`[MASKED]` ์นํ)
---
## 2. GitLab CI/CD ํต์ฌ ์ํคํ
์ฒ & ๋ฉํ ๋ชจ๋ธ (Mental Model)
GitLab CI/CD๋ ์ค์ ์ฝ๋๋ค์ดํฐ(GitLab Server)์ ๋ถ์ฐ ์์
์คํ๊ธฐ(GitLab Runner) ๊ฐ์ ํ์
์ผ๋ก ๋์ํฉ๋๋ค.
### ๐๏ธ GitLab CI/CD ์ํคํ
์ฒ
```text
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ GitLab Coordinator (Server) โ
โ - Git ์ปค๋ฐ / MR ์ด๋ฒคํธ ๊ฐ์ง ๋ฐ .gitlab-ci.yml ํ์ฑ โ
โ - Pipeline ์์ฑ, Stage ์ค์ผ์ค๋ง, Job ์ํ ๊ด๋ฆฌ โ
โ - ์น ๋์๋ณด๋ ๊ทธ๋ํ UI, Artifacts & Cache ์คํ ๋ฆฌ์ง ์ ์ฅโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ (HTTPS REST API / gRPC ํด๋ง)
โโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโ
โผ โผ
โโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโ
โ GitLab Runner #1 โ โ GitLab Runner #2 โ
โ (Docker Executor) โ โ (Kubernetes Executor) โ
โ - Maven / Gradle ๋น๋ โ โ - ๋๊ท๋ชจ ๋ณ๋ ฌ ํ
์คํธ โ
โ - ๋จ์ ํ
์คํธ ์คํ โ โ - ์ด์ ํด๋ฌ์คํฐ ๋ฐฐํฌ โ
โโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโ
```
### ๐ ํ์ดํ๋ผ์ธ ๊ณ์ธต ๊ตฌ์กฐ (Pipeline Hierarchy)
```text
[Pipeline]
โ
โโโบ [Stage: build]
โ โโ Job: compile-frontend (๋์ ๋ณ๋ ฌ ์คํ)
โ โโ Job: compile-backend (๋์ ๋ณ๋ ฌ ์คํ)
โ
โโโบ [Stage: test] (build ๋จ๊ณ์ ๋ชจ๋ Job์ด ์ฑ๊ณตํด์ผ ์ง์
)
โ โโ Job: unit-tests
โ โโ Job: security-scan
โ
โโโบ [Stage: package]
โ โโ Job: build-docker-image
โ
โโโบ [Stage: deploy]
โโ Job: deploy-dev (์๋ ์คํ)
โโ Job: deploy-prod (์๋ ์น์ธ ๋ฒํผ ๋๊ธฐ: when: manual)
```
---
## 3. [์ด๋ณด์ 1๋จ๊ณ] ํฌ๋ก์๋: ์ต์ ๋์ ์์
์ ์ฅ์ ๋ฃจํธ์ `.gitlab-ci.yml` ํ์ผ์ ๋ง๋ค๊ณ ์ฆ์ ๋์์ ๊ฒ์ฆํ ์ ์๋ ์ต์ ํ์ดํ๋ผ์ธ์
๋๋ค.
### ๐ ํ์ผ ์์ฑ: `.gitlab-ci.yml`
```yaml
# 1. ๊ธฐ๋ณธ ์คํ ๋์ปค ์ด๋ฏธ์ง ์ ์
default:
image: alpine:latest
# 2. ํ์ดํ๋ผ์ธ ๋จ๊ณ ์์ ์ ์
stages:
- build
- test
- deploy
# 3. ์ฒซ ๋ฒ์งธ ๋จ๊ณ Job: ๋น๋ ์๋ฎฌ๋ ์ด์
job_build:
stage: build
script:
- echo "=== [Stage 1] ์ ํ๋ฆฌ์ผ์ด์
๋น๋ ์์ ==="
- uname -a
- mkdir -p dist
- echo "LuckyTechWorld App v1.0.0" > dist/app.txt
- echo "๋น๋ ์ฐ์ถ๋ฌผ ์์ฑ ์๋ฃ: dist/app.txt"
artifacts:
paths:
- dist/
expire_in: 1 hour
# 4. ๋ ๋ฒ์งธ ๋จ๊ณ Job: ํ
์คํธ ์๋ฎฌ๋ ์ด์
job_test:
stage: test
script:
- echo "=== [Stage 2] ํ
์คํธ ๋จ๊ณ ์ํ ์ค ==="
- cat dist/app.txt
- echo "๋ชจ๋ ํ
์คํธ ์ผ์ด์ค ํต๊ณผ!"
# 5. ์ธ ๋ฒ์งธ ๋จ๊ณ Job: ๋ฐฐํฌ ์๋ฎฌ๋ ์ด์
job_deploy:
stage: deploy
script:
- echo "=== [Stage 3] ํฌ๋ก์๋ ๋ฐฐํฌ ์๋ฃ ==="
- echo "๋ฐฐํฌ ์ฑ๊ณต: ์๋น์ค ์ ์ ์ด์ ์ค"
```
### ๐ป ์คํ ๋ฐ ์น ์ฝ์ ์ถ๋ ฅ ํ์ธ
์ฝ๋๋ฅผ Git ์ปค๋ฐํ๊ณ ์๊ฒฉ ์ ์ฅ์๋ก `git push`ํ๋ฉด ํ์ดํ๋ผ์ธ์ด ์๋ ํธ๋ฆฌ๊ฑฐ๋ฉ๋๋ค:
1. GitLab ์น UI ์ข์ธก ๋ฉ๋ด โ **Build** โ **Pipelines** ์ด๋
2. ์์ฑ๋ ์ต์ ํ์ดํ๋ผ์ธ ํด๋ฆญ โ `job_build` ํด๋ฆญํ์ฌ ์ฝ์ ๋ก๊ทธ ํ์ธ:
```text
Running with gitlab-runner 16.8.0 (xxxxxxxx)
on my-docker-runner xxxxxxxx
Preparing the "docker" executor
Using Docker executor with image alpine:latest ...
Pulling docker image alpine:latest ...
Using docker image sha256:xxxxxxxx for alpine:latest ...
$ echo "=== [Stage 1] ์ ํ๋ฆฌ์ผ์ด์
๋น๋ ์์ ==="
=== [Stage 1] ์ ํ๋ฆฌ์ผ์ด์
๋น๋ ์์ ===
$ uname -a
Linux runner-xxxx 5.15.0 #1 SMP Linux
$ mkdir -p dist
$ echo "LuckyTechWorld App v1.0.0" > dist/app.txt
$ echo "๋น๋ ์ฐ์ถ๋ฌผ ์์ฑ ์๋ฃ: dist/app.txt"
๋น๋ ์ฐ์ถ๋ฌผ ์์ฑ ์๋ฃ: dist/app.txt
Uploading artifacts...
dist/: found 1 matching files and directories
Uploading artifacts as "archive" to coordinator... 201 Created
Job succeeded
```
---
## 4. [์ด๋ณด์ 2๋จ๊ณ] ํต์ฌ ๊ธฐ๋ฅ & ํ์ดํ๋ผ์ธ ๋ฌธ๋ฒ
์ค๋ฌด ํ๋ก์ ํธ์์ ๋งค์ผ ์ฌ์ฉํ๋ ์บ์ฑ, ์ํฐํฉํธ, ์กฐ๊ฑด ์ ์ด(`rules`), ์ฌ์ ์ ์ ํ๊ฒฝ๋ณ์ ๋ฌธ๋ฒ์
๋๋ค.
### ๐ฆ 1. Artifacts(์ฐ์ถ๋ฌผ) vs Cache(์บ์) ์๋ฒฝ ๋น๊ต
| ๊ตฌ๋ถ | Artifacts (์ฐ์ถ๋ฌผ) | Cache (์บ์) |
| :--- | :--- | :--- |
| **์ฃผ์ ๋ชฉ์ ** | ๋ค์ Stage์ Job์ผ๋ก ์ปดํ์ผ ๊ฒฐ๊ณผ๋ฌผ(๋ฐ์ด๋๋ฆฌ, jar, dist) ์ ๋ฌ | ํจํค์ง ์์กด์ฑ(`node_modules`, `.m2`, `.gradle`)์ ์ฌ์ฌ์ฉํ์ฌ ๋น๋ ๊ฐ์ |
| **๋ณด์ฅ์ฑ** | ๋์ผ ํ์ดํ๋ผ์ธ ๋ด์์ ๋ค์ด์คํธ๋ฆผ Job์ **100% ์ ๋ฌ ๋ณด์ฅ** | ๋ค์ด๋ก๋ ์คํจ ์์๋ ๋น๋๋ ๊ณ์ ์งํ (Best-effort ๋ณด์ฅ) |
| **GitLab UI** | ์น ๋ธ๋ผ์ฐ์ ์์ zip ํ์ผ๋ก ์ง์ ๋ค์ด๋ก๋ ๊ฐ๋ฅ | UI ๋ค์ด๋ก๋ ๋ถ๊ฐ, ๋ฌ๋ ๊ฐ S3/๋ก์ปฌ ๋์คํฌ์ ๊ณต์ ๋ณด๊ด |
```yaml
build_app:
stage: build
image: node:20-alpine
# ํจํค์ง ๋งค๋์ ์บ์ ์ค์ (์ฌ๋ค์ด๋ก๋ ๋ฐฉ์ง)
cache:
key:
files:
- package-lock.json
paths:
- .npm/
script:
- npm ci --cache .npm --prefer-offline
- npm run build
# ๋น๋ ๊ฒฐ๊ณผ๋ฌผ ์ ๋ฌ ์ค์
artifacts:
name: "build-$CI_COMMIT_SHORT_SHA"
paths:
- dist/
expire_in: 1 week
```
### ๐ฆ 2. Rules: ๋ชจ๋ ํ์ดํ๋ผ์ธ ์คํ ์กฐ๊ฑด ์ ์ด
๋ ๊ฑฐ์ `only/except`๋ฅผ ๋์ฒดํ๋ ํ๋์ ์ธ ์กฐ๊ฑด๋ฌธ ์์คํ
์
๋๋ค.
```yaml
deploy_staging:
stage: deploy
script:
- ./deploy-staging.sh
rules:
# 1. develop ๋ธ๋์น๋ก ํธ์๋ ๋๋ง ์๋ ์คํ
- if: '$CI_COMMIT_BRANCH == "develop"'
when: on_success
deploy_production:
stage: deploy
script:
- ./deploy-prod.sh
rules:
# 2. main ๋ธ๋์น์ผ ๋๋ ์น UI์์ ์ฌ๋์ด ์๋ ์น์ธ ๋ฒํผ์ ๋๋ฌ์ผ ์คํ
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual
allow_failure: false # ์น์ธ ์์ด ๋ค์ ๋จ๊ณ๋ก ๋์ด๊ฐ์ง ๋ชปํ๋๋ก ๋ฐฉ์ง
# 3. ๊ทธ ์ธ ๋ค๋ฅธ ๋ธ๋์น๋ ์ด Job์ ์์ ์์ฑํ์ง ์์
- when: never
```
### ๐ 3. ์ ์ฉํ ์ฌ์ ์ ์ ํ๊ฒฝ๋ณ์ (Predefined CI Variables)
GitLab์ด ํ์ดํ๋ผ์ธ ๋ฐํ์์ ์๋์ผ๋ก ์ ๊ณตํ๋ ํต์ฌ ๋ณ์๋ค์
๋๋ค:
| ํ๊ฒฝ ๋ณ์๋ช
| ๊ฐ ์์ | ์ค๋ช
|
| :--- | :--- | :--- |
| `$CI_COMMIT_BRANCH` | `main`, `feat/login` | ํ์ฌ ์ปค๋ฐ์ด ์์นํ ๋ธ๋์น ์ด๋ฆ |
| `$CI_COMMIT_SHORT_SHA` | `7b3f12a` | ์ปค๋ฐ ํด์ ์ 8์๋ฆฌ (๋์ปค ์ด๋ฏธ์ง ํ๊ทธ์ ์ต์ ) |
| `$CI_PIPELINE_IID` | `42` | ํ์ฌ ํ๋ก์ ํธ ๋ด ํ์ดํ๋ผ์ธ ์์ฐจ ์ฆ๊ฐ ๋ฒํธ |
| `$CI_REGISTRY_IMAGE` | `registry.gitlab.com/group/project` | GitLab ํ๋ก์ ํธ ๋ด์ฅ ๋์ปค ๋ ์ง์คํธ๋ฆฌ URL |
| `$CI_REGISTRY_USER` | `gitlab-ci-token` | GitLab ๋ ์ง์คํธ๋ฆฌ ๋ก๊ทธ์ธ์ฉ ์์ ์์คํ
์ ์ |
| `$CI_JOB_TOKEN` | `[MASKED]` | ๋ฌ๋๊ฐ GitLab API ๋ฐ ํจํค์ง ๋ ์ง์คํธ๋ฆฌ์ ์ธ์ฆํ๋ ์์ ํ ํฐ |
### ๐ 4. ๋ณ๋ ฌ ๋งคํธ๋ฆญ์ค ๋น๋ (Parallel Matrix)
์ฌ๋ฌ Node/Java ๋ฒ์ ์ด๋ OS ํ๊ฒฝ์์ ํ
์คํธ๋ฅผ ๋์์ ์ํํฉ๋๋ค.
```yaml
matrix_tests:
stage: test
image: node:${NODE_VERSION}-alpine
parallel:
matrix:
- NODE_VERSION: ['18', '20', '22']
script:
- node -v
- npm test
```
---
## 5. [์ด๋ณด์ 3๋จ๊ณ] ์ค๋ฌด ์์ฉ ํจํด & ํ๋ก๋์
ํ์ดํ๋ผ์ธ
์ค๋ฌด ํ๋ก๋์
ํ๊ฒฝ์์ ์ฌ์ฉํ๋ Docker ์ด๋ฏธ์ง ๋น๋(dind), GitLab ๋ ์ง์คํธ๋ฆฌ ํธ์, K8s ๋ฐฐํฌ ๋ฐ ์ฌ์ฌ์ฉ ํ
ํ๋ฆฟ์ ํฌํจํ ์์ ํ `.gitlab-ci.yml` ์์ ์
๋๋ค.
### ๐ ์ค์ ํ๋ก๋์
`.gitlab-ci.yml` (๋ณต์ฌํด์ ๋ฐ๋ก ์ฌ์ฉ ๊ฐ๋ฅ)
```yaml
# ==========================================
# ๐ฆ LuckyTech Enterprise Production Pipeline
# ==========================================
# ์ ์ญ ์ค์
stages:
- lint
- build
- test
- package
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
IMAGE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
LATEST_TAG: "$CI_REGISTRY_IMAGE:latest"
# ๐งฉ 1. ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๊ณตํต ์ก ํ
ํ๋ฆฟ (Hidden Job)
.node_template:
image: node:20-alpine
cache:
key:
files:
- package-lock.json
paths:
- .npm/
before_script:
- npm ci --cache .npm --prefer-offline
# ------------------------------------------
# Stage 1: ์ฝ๋ ์ ์ ๋ถ์
# ------------------------------------------
lint_code:
extends: .node_template
stage: lint
script:
- npm run lint
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop"'
# ------------------------------------------
# Stage 2: ์ ํ๋ฆฌ์ผ์ด์
๋น๋
# ------------------------------------------
compile_app:
extends: .node_template
stage: build
script:
- npm run build
artifacts:
name: "dist-$CI_COMMIT_SHORT_SHA"
paths:
- dist/
expire_in: 2 days
# ------------------------------------------
# Stage 3: ๋จ์ ํ
์คํธ & ์ปค๋ฒ๋ฆฌ์ง ์์ง
# ------------------------------------------
unit_test:
extends: .node_template
stage: test
script:
- npm test -- --coverage
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
artifacts:
reports:
junit: junit.xml
# ------------------------------------------
# Stage 4: Docker ์ด๋ฏธ์ง ๋น๋ ๋ฐ Registry ํธ์
# ------------------------------------------
docker_build_push:
stage: package
image: docker:24.0.5
services:
- docker:24.0.5-dind # Docker-in-Docker ๋ฐ๋ชฌ ์๋น์ค ์คํ
before_script:
# GitLab ๋ด์ฅ ์ปจํ
์ด๋ ๋ ์ง์คํธ๋ฆฌ ์๋ ๋ก๊ทธ์ธ
- docker login -u "$CI_REGISTRY_USER" -p "$CI_JOB_TOKEN" "$CI_REGISTRY"
script:
- echo "๋์ปค ์ด๋ฏธ์ง ๋น๋ ์ค: $IMAGE_TAG"
- docker build -t "$IMAGE_TAG" -t "$LATEST_TAG" .
- docker push "$IMAGE_TAG"
- docker push "$LATEST_TAG"
rules:
- if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop"'
# ------------------------------------------
# Stage 5-1: ๊ฐ๋ฐ ํ๊ฒฝ(Dev) ์๋ ๋ฐฐํฌ
# ------------------------------------------
deploy_development:
stage: deploy
image: bitnami/kubectl:latest
environment:
name: development
url: https://dev.luckytech.world
script:
- kubectl config set-cluster k8s --server="$KUBE_DEV_SERVER" --insecure-skip-tls-verify=true
- kubectl config set-credentials admin --token="$KUBE_DEV_TOKEN"
- kubectl config set-context default --cluster=k8s --user=admin
- kubectl config use-context default
- kubectl set image deployment/lucky-app lucky-app="$IMAGE_TAG" -n dev
- kubectl rollout status deployment/lucky-app -n dev --timeout=3m
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'
# ------------------------------------------
# Stage 5-2: ์ด์ ํ๊ฒฝ(Prod) ๊ด๋ฆฌ์ ์๋ ์น์ธ ๋ฐฐํฌ
# ------------------------------------------
deploy_production:
stage: deploy
image: bitnami/kubectl:latest
environment:
name: production
url: https://luckytech.world
script:
- kubectl config set-cluster k8s --server="$KUBE_PROD_SERVER" --insecure-skip-tls-verify=true
- kubectl config set-credentials admin --token="$KUBE_PROD_TOKEN"
- kubectl config set-context default --cluster=k8s --user=admin
- kubectl config use-context default
- kubectl set image deployment/lucky-app lucky-app="$IMAGE_TAG" -n prod
- kubectl rollout status deployment/lucky-app -n prod --timeout=5m
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual # ์น ๋์๋ณด๋์์ ์๋ [Play ๋ฒํผ] ํด๋ฆญ ์ ์คํ
allow_failure: false
```
---
## 6. ์ค๋ฌด ์ฃผ์์ฌํญ & ํ๋ก๋์
์ฒดํฌ๋ฆฌ์คํธ
### โ ๏ธ 1. ์ด๋ณด์๊ฐ ๊ฐ์ฅ ํํ ๊ฒช๋ ์ค์ & ํด๊ฒฐ๋ฒ
| ๋ฒํธ | ํํ ์๋ฌ ๋ฐ ์ฆ์ | ๊ทผ๋ณธ ์์ธ | ํด๊ฒฐ ๋ฐฉ๋ฒ |
| :---: | :--- | :--- | :--- |
| **1** | `docker: command not found` ๋๋ ๋ฐ๋ชฌ ์ฐ๊ฒฐ ์คํจ | `image: docker`๋ฅผ ์ฌ์ฉํ๋ฉด์ `services: [docker:dind]`๋ฅผ ๋๋ฝํ๊ฑฐ๋ Runner์ `privileged = true` ์ค์ ์์ | `.gitlab-ci.yml`์ `services: [docker:dind]` ์ถ๊ฐ ๋ฐ ๋ฌ๋ ์ค์ ํ์ผ(`/etc/gitlab-runner/config.toml`)์ `privileged = true` ๋ถ์ฌ |
| **2** | ์ด์ Stage์์ ๋ง๋ ํ์ผ์ด ๋ค์ Stage์์ ์ฌ๋ผ์ง | `artifacts: paths` ์ง์์ด๋ฅผ ์ง์ ํ์ง ์์ (๊ฐ Job์ ๊ฒฉ๋ฆฌ๋ ์ปจํ
์ด๋์์ ์๋ก ์์๋จ) | ์์ฑ๋ ํ์ผ์ด ๋ด๊ธด ๋๋ ํ ๋ฆฌ๋ฅผ `artifacts: paths: [dist/]`๋ก ๋ฑ๋ก |
| **3** | Masked ๋ณ์ ๋ฑ๋ก ์ ์๋ฌ (`is invalid: regex match`) | ๋ง์คํนํ ๋น๋ฐํค์ ์ค๋ฐ๊ฟ(์ํฐ)์ด๋ ํน์๋ฌธ์๊ฐ ํฌํจ๋จ | Base64๋ก ์ธ์ฝ๋ฉํ์ฌ ๋ณ์์ ์ ์ฅ ํ(`echo -n "secret" \| base64`), ์คํฌ๋ฆฝํธ ์คํ ์ `echo "$VAR" \| base64 -d` ๋์ฝ๋ฉํ์ฌ ์ฌ์ฉ |
| **4** | ๋ ๊ฑฐ์ `only/except`์ ๋ชจ๋ `rules` ํผ์ฉ | ํ ํ์ดํ๋ผ์ธ ๋ด์์ ๋ ๋ฐฉ์์ ์์ด ์ฐ๋ฉด ์์์น ๋ชปํ ๋์ ๋ฐ์ | ๋ชจ๋ Job์ ๋ชจ๋ ํ์ค์ธ `rules:` ๋ธ๋ก์ผ๋ก ์ผ๊ด๋๊ฒ ํต์ผ |
### ๐ 2. ํ๋ก๋์
ํ์ดํ๋ผ์ธ ์ฑ๋ฅ ์ต์ ํ ํ
1. **Interruptible ํ๋๊ทธ ํ์ฑํ**:
๋์ผ ๋ธ๋์น์ ์ ์ปค๋ฐ์ด ํธ์๋์์ ๋ ์ด์ ์ ๋ถํ์ํ ์งํ ์ค ํ์ดํ๋ผ์ธ์ ์๋ ์ทจ์ํ์ฌ ๋ฌ๋ ์์์ ์ ์ฝํฉ๋๋ค:
```yaml
default:
interruptible: true
```
2. **Docker ๋น๋ ์บ์ฑ (`--cache-from`)**:
`docker build --cache-from $LATEST_TAG` ์ต์
์ ์ฌ์ฉํ๋ฉด ์์ค์ฝ๋๊ฐ ๋ณ๊ฒฝ๋์ง ์์ ๋ ์ด์ด์ ์ฌ๋น๋๋ฅผ ๊ฑด๋๋๋๋ค.
3. **๋ฃจํธ๋ฆฌ์ค(Rootless) Kaniko ๋์
**:
Kubernetes ํ๊ฒฝ์์ ๋ณด์ ์ํ์ด ์๋ privileged dind ๋์ ๊ตฌ๊ธ์ **Kaniko**๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ๋ชฌ ์์ด ์์ ํ๊ฒ ๋์ปค ์ด๋ฏธ์ง๋ฅผ ๋น๋ํ ์ ์์ต๋๋ค.
### ๐ 3. `.gitlab-ci.yml` ์ต์์ ํต์ฌ ์ง์์ด ๋น ๋ฅธ ์์ฝ
```yaml
default: # ๋ชจ๋ Job์ ์ ์ฉ๋ ๊ธฐ๋ณธ ์ค์ (image, cache, before_script)
variables: # ์ ์ญ ํ๊ฒฝ๋ณ์ ์ ์ธ
stages: # ํ์ดํ๋ผ์ธ ์คํ ๋จ๊ณ ์์ ์ ์ธ
include: # ์ธ๋ถ/๊ณตํต YAML ํ
ํ๋ฆฟ ํฌํจ (local, project, remote)
<job_name>: # ๊ฐ๋ณ ์์
์ ์ธ
stage: # ์์๋ stage ์ง์
image: # ์์
์คํ ๋์ปค ์ด๋ฏธ์ง
services: # ๋ณด์กฐ ์ปจํ
์ด๋ (postgres, redis, dind ๋ฑ)
before_script: # ๋ฉ์ธ ์คํฌ๋ฆฝํธ ์คํ ์ ์คํํ ๋ช
๋ น์ด
script: # ์คํํ ๋ฉ์ธ ์
ธ ๋ช
๋ น์ด ๋ฆฌ์คํธ (ํ์)
after_script: # ์์
์ฑ๊ณต/์คํจ ๋ฌด๊ดํ๊ฒ ์ข
๋ฃ ํ ์คํ
artifacts: # ๋ณด๊ด ๋ฐ ๋ค์ด์คํธ๋ฆผ ์ ๋ฌ ํ์ผ
cache: # ๋น๋ ๊ฐ์์ฉ ์ข
์์ฑ ์บ์
rules: # ์คํ ์ฌ๋ถ ์กฐ๊ฑด๋ฌธ
tags: # ์คํํ ๋ฌ๋ ํ๊ทธ ํํฐ๋ง
```
์๊ฒฌ ๋ฐ ์ง๋ฌธ
0์์ง ๋ฑ๋ก๋ ์๊ฒฌ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋๊ธ์ ๋จ๊ฒจ๋ณด์ธ์!
๋๊ธ ์์
๋๊ธ ์ญ์