“์ฝ๋๋ฅผ pushํ๋ฉด ์์์ ํ ์คํธํ๊ณ ๋ฐฐํฌ๊น์ง ํด์คฌ์ผ๋ฉด ์ข๊ฒ ๋ค.”
์ด ๋ฐ๋์ ํ์ค๋ก ๋ง๋ค์ด์ฃผ๋ ๊ฒ์ด CI/CD ํ์ดํ๋ผ์ธ์ ๋๋ค. ์ด๋ฒ ๊ณผ์ ์์๋ Docker๋ก GitLab ์๋ฒ๋ฅผ ์ง์ ๋์ฐ๊ณ , GitLab Runner๋ฅผ ์ฐ๊ฒฐํด ์ฝ๋๋ฅผ pushํ๋ ์๊ฐ ์๋์ผ๋ก ํ์ดํ๋ผ์ธ์ด ์คํ๋๋ ํ๊ฒฝ์ ์ฒ์๋ถํฐ ๊ตฌ์ถํด๋ดค์ต๋๋ค.
๐ ์ ์ฒด ๊ตฌ์ฑ ๊ฐ์
| ๊ตฌ์ฑ ์์ | ์ญํ |
|---|---|
| Docker + Docker Compose | ์ปจํ ์ด๋ ๊ธฐ๋ฐ ์คํ ํ๊ฒฝ |
| GitLab | ์ฝ๋ ์ ์ฅ์ + CI/CD ์ค์ผ์คํธ๋ ์ดํฐ |
| GitLab Runner | ์ค์ ํ์ดํ๋ผ์ธ ์์ ์ ์คํํ๋ ์์ด์ ํธ |
.gitlab-ci.yml | ํ์ดํ๋ผ์ธ ์ ์ ํ์ผ |
์ ์ฒด ํ๋ฆ์ ํ ์ค๋ก ์์ฝํ๋ฉด:
์ฝ๋ Push โ GitLab ๊ฐ์ง โ Runner์๊ฒ ์์ ํ ๋น โ ์ปจํ ์ด๋ ์์์ ๋น๋/ํ ์คํธ ์คํ โ ๊ฒฐ๊ณผ ๋ณด๊ณ
1. Docker & Docker Compose ์ค์น ๐ณ
GPG ํค ๋ฑ๋ก ๋ฐ ๊ณต์ ์ ์ฅ์ ์ถ๊ฐ
Docker๋ฅผ apt๋ก ์ค์นํ๋ ค๋ฉด ๋จผ์ ๊ณต์ ์ ์ฅ์๋ฅผ ๋ฑ๋กํด์ผ ํฉ๋๋ค. GPG ํค๋ ๋ค์ด๋ก๋ํ๋ ํจํค์ง๊ฐ Docker์์ ๊ณต์ ๋ฐฐํฌํ ๊ฒ์ธ์ง ๊ฒ์ฆํ๋ ์ญํ ์ ํฉ๋๋ค. ๋ณ์กฐ๋ ํจํค์ง๋ฅผ ๋ง๋ ์ผ์ข ์ ๋์งํธ ์๋ช ํ์ธ์ ๋๋ค.
์์คํ
์ํคํ
์ฒ(dpkg --print-architecture)์ OS ๋ฒ์ (/etc/os-release)์ ์๋์ผ๋ก ๊ฐ์งํด ํ๊ฒฝ์ ๋ง๋ ์ ์ฅ์๋ฅผ ์ถ๊ฐํฉ๋๋ค.
GPG ํค ๋ฑ๋ก ํ๋ฉด

์ ์ฅ์ ์ถ๊ฐ ํ๋ฉด

Docker ์์ง + Compose ํ๋ฌ๊ทธ์ธ ์ค์น
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
๊ฐ ํจํค์ง์ ์ญํ :
docker-ce: Docker ์์ง ๋ณธ์ฒดdocker-ce-cli: ํฐ๋ฏธ๋์์docker๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ CLI ๋๊ตฌcontainerd.io: ์ปจํ ์ด๋ ์คํ์ ๋ด๋นํ๋ ๋ฐํ์docker-compose-plugin:docker compose๋ช ๋ น์ ์ฌ์ฉํ๊ธฐ ์ํ ํ๋ฌ๊ทธ์ธ
WSL ํ๊ฒฝ์์ Docker ๋ฐ๋ชฌ์ ์์ํ๊ณ , ํ์ฌ ์ฌ์ฉ์๋ฅผ docker ๊ทธ๋ฃน์ ์ถ๊ฐํด sudo ์์ด Docker ๋ช
๋ น์ ์ฌ์ฉํ ์ ์๋๋ก ๊ถํ์ ์ค์ ํฉ๋๋ค.
Docker ๋ฐ๋ชฌ ์์ ๋ฐ ๊ทธ๋ฃน ์ถ๊ฐ


์ค์น ๋ฒ์ ํ์ธ
docker version๋ฐdocker compose version์ถ๋ ฅ

Docker Engine: 28.1.1
Docker Compose: v2.35.1
2. GitLab ์๋ฒ ๊ตฌ์ฑ ๐ฆ
๋ฐ์ดํฐ ๋๋ ํฐ๋ฆฌ ์ค๋น
์ปจํ ์ด๋๊ฐ ์ญ์ ๋์ด๋ ๋ฐ์ดํฐ๊ฐ ๋จ๋๋ก ๋ณผ๋ฅจ ๋ง์ดํธ๋ฅผ ์ํ ๋๋ ํฐ๋ฆฌ๋ฅผ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋ก๋๋ค.
sudo mkdir -p /data/gitlab/{data,logs,config}
๋๋ ํฐ๋ฆฌ ์์ฑ ๋ฐ ๊ถํ ์ค์

docker-compose.yaml ์์ฑ
GitLab Enterprise Edition 16.1.0 ์ด๋ฏธ์ง๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋น์ค๋ฅผ ์ ์ํฉ๋๋ค.
services:
gitlab:
image: gitlab/gitlab-ee:16.1.0-ee.0
container_name: gitlab
restart: always
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "8022:22" # SSH (ํธ์คํธ 8022 โ ์ปจํ
์ด๋ 22)
volumes:
- /data/gitlab/config:/etc/gitlab
- /data/gitlab/logs:/var/log/gitlab
- /data/gitlab/data:/var/opt/gitlab
docker-compose.yaml ์์ฑ ํ๋ฉด

ํฌํธ๋ฅผ 3๊ฐ๋ก ๋ถ๋ฆฌํ ์ด์ :
80/443: ์น ๋ธ๋ผ์ฐ์ ์์ GitLab UI ์ ์8022: SSH๋ก git clone/push ์ ์ฌ์ฉ (ํธ์คํธ์ 22๋ฒ์ ์ด๋ฏธ ์์คํ SSH๊ฐ ์ฌ์ฉ ์ค)
GitLab ์ปจํ ์ด๋ ์คํ ๋ฐ ์ด๊ธฐ ๋น๋ฐ๋ฒํธ ํ์ธ
docker compose up -d
์ปจํ
์ด๋๊ฐ healthy ์ํ๊ฐ ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ ํ ์ด๊ธฐ root ๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํฉ๋๋ค. GitLab์ ์ฒ์ ์คํ ์ ์๋์ผ๋ก ์์ ๋น๋ฐ๋ฒํธ๋ฅผ ์์ฑํด ํ์ผ๋ก ์ ์ฅํฉ๋๋ค.
docker exec gitlab cat /etc/gitlab/initial_root_password
์ปจํ ์ด๋ ์คํ ๋ฐ healthy ์ํ ํ์ธ


3. GitLab Runner ์ค์น ๋ฐ ๋ฑ๋ก ๐
Runner๋?
GitLab ์๋ฒ๋ “์ด๋ฐ ์์ ์ ํด์ผ ํด”๋ผ๊ณ ์ง์๋ง ํฉ๋๋ค. ์ค์ ๋ก ์ฝ๋๋ฅผ ๋ฐ์์ ๋น๋ํ๊ณ ํ ์คํธํ๋ ์คํ์๊ฐ ๋ฐ๋ก GitLab Runner์ ๋๋ค. Runner๋ฅผ ๋ณ๋ ์ปจํ ์ด๋๋ก ๋์ GitLab ์๋ฒ์ ์ฐ๊ฒฐํฉ๋๋ค.
Runner ๋๋ ํฐ๋ฆฌ ๋ฐ docker-compose.yml ์์ฑ
sudo mkdir -p /data/gitlab-runner/config
Runner ์ปจํ
์ด๋๋ Docker-in-Docker(DinD) ๋ฐฉ์์ผ๋ก ๋์ํฉ๋๋ค. ํธ์คํธ์ Docker ์์ผ(/var/run/docker.sock)์ ๊ณต์ ํด Runner๊ฐ ์ปจํ
์ด๋ ์์์๋ Docker๋ฅผ ์คํํ ์ ์๋๋ก ํฉ๋๋ค.
services:
gitlab-runner:
image: gitlab/gitlab-runner:v16.0.2
container_name: gitlab-runner
restart: always
volumes:
- /data/gitlab-runner/config:/etc/gitlab-runner
- /var/run/docker.sock:/var/run/docker.sock # DinD ํต์ฌ


Runner ๋ฑ๋ก
GitLab ์น UI์์ Admin Area โ CI/CD โ Runners ๋ฉ๋ด๋ก ์ด๋ํด ์๋ก์ด Instance Runner๋ฅผ ์์ฑํฉ๋๋ค.
- Tags:
docker-test - “Run untagged jobs” ์ฒดํฌ โ ํ๊ทธ ์๋ ์์ ๋ ์คํ ํ์ฉ
์์ฑ๋ Registration Token์ ๋ณต์ฌํด Runner ๋ฑ๋ก ๋ช ๋ น์ด๋ฅผ ์คํํฉ๋๋ค.
docker exec -it gitlab-runner gitlab-runner register \
--url http://localhost \
--executor docker \
--docker-image alpine:latest
--executor docker: ๊ฐ ํ์ดํ๋ผ์ธ ์์ ์ ๋ณ๋ ์ปจํ ์ด๋์์ ์คํ--docker-image alpine:latest: ๊ธฐ๋ณธ ๋น๋ ์ด๋ฏธ์ง๋ก ๊ฒฝ๋ Alpine Linux ์ฌ์ฉ

4. CI/CD ํ์ดํ๋ผ์ธ ์ค์ ๐
ํ๋ก์ ํธ Clone
VS Code๋ฅผ WSL ๋ชจ๋๋ก ์คํํ๊ณ , GitLab์์ ๋ง๋ ํ๋ก์ ํธ๋ฅผ ๋ก์ปฌ๋ก Cloneํฉ๋๋ค.
.gitlab-ci.yml ์์ฑ
ํ๋ก์ ํธ ๋ฃจํธ์ .gitlab-ci.yml ํ์ผ์ ์์ฑํฉ๋๋ค. ์ด ํ์ผ์ด CI/CD ํ์ดํ๋ผ์ธ์ ์ค๊ณ๋์
๋๋ค. pushํ ๋๋ง๋ค GitLab์ด ์ด ํ์ผ์ ์ฝ์ด Runner์๊ฒ ์์
์ ์ง์ํฉ๋๋ค.
stages:
- test
run-test:
stage: test
image: alpine:latest
script:
- echo "Hello from CI/CD Pipeline!"
- echo "Build started at $(date)"
- echo "All tests passed!"
Commit & Push
git add .gitlab-ci.yml
git commit -m "Add CI/CD pipeline"
git push origin main
5. ์คํ ๊ฒฐ๊ณผ ๋ฐ ํธ๋ฌ๋ธ์ํ ๐ง
YAML ๋ฌธ๋ฒ ์ค๋ฅ ์์
์ด๊ธฐ ํ์ดํ๋ผ์ธ์ด ์คํจํ์ต๋๋ค. script ํญ๋ชฉ์ ๋จ์ผ ๋ฌธ์์ด๋ก ์์ฑํ๋๋ YAML ํ์๊ฐ ์ค๋ฅ๋ฅผ ๋ฐ์์์ผฐ์ต๋๋ค. ๋ฐฐ์ด ํ์์ผ๋ก ์์ ํด ํด๊ฒฐํ์ต๋๋ค.
# โ ์ค๋ฅ: ๋จ์ผ ๋ฌธ์์ด
script: echo "Hello"
# โ
์์ : ๋ฐฐ์ด ํ์
script:
- echo "Hello"
๐ ๊ฒฐ๋ก ๋ฐ ๋ฐฐ์ด ์
์ด๋ฒ ๊ณผ์ ๋ฅผ ํตํด DevOps ํ๊ฒฝ์ ์ ์ฒด ๊ทธ๋ฆผ์ ์ง์ ๊ตฌ์ถํด๋ดค์ต๋๋ค.
| ๋จ๊ณ | ๊ฒฐ๊ณผ |
|---|---|
| Docker + Compose ์ค์น | โ ์๋ฃ |
| GitLab ์๋ฒ ๊ตฌ๋ | โ ์๋ฃ |
| GitLab Runner ๋ฑ๋ก | โ Online ์ํ ํ์ธ |
.gitlab-ci.yml ์์ฑ ๋ฐ Push | โ ์๋ฃ |
| ํ์ดํ๋ผ์ธ ์คํ ์ฑ๊ณต | โ ์๋ฃ |
YAML ๋ฌธ๋ฒ์ ์ค์์ฑ: ๋ค์ฌ์ฐ๊ธฐ ํ๋, ๋ฐ์ดํ ํ๋๊ฐ ์ ์ฒด ํ์ดํ๋ผ์ธ์ ๋ฉ์ถฅ๋๋ค. ์ ์ธํ ์ค์ ํ์ผ์์ ๋ฌธ๋ฒ์ ์ ํ์ฑ์ด ์ผ๋ง๋ ์ค์ํ์ง ์ง์ ๋๊ผ์ต๋๋ค.
์ปจํ
์ด๋ ๊ธฐ๋ฐ ์ธํ๋ผ์ ์ฅ์ : GitLab ์๋ฒ, Runner, ๋น๋ ํ๊ฒฝ ๋ชจ๋๋ฅผ ์ปจํ
์ด๋๋ก ๊ด๋ฆฌํ๋ ์ค์น/์ญ์ /๋ฒ์ ๋ณ๊ฒฝ์ด ๋งค์ฐ ๊ฐํธํ์ต๋๋ค. docker compose up -d ํ ์ค๋ก ๋ณต์กํ ์๋ฒ๊ฐ ๋จ๋ ๊ฒฝํ์ ๊ฝค ์ธ์์ ์ด์์ต๋๋ค.
DinD(Docker-in-Docker)์ ๊ฐ๋ : Runner๊ฐ ์ปจํ ์ด๋ ์์์ ๋ ๋ค๋ฅธ ์ปจํ ์ด๋๋ฅผ ์์ฑํด ๋น๋๋ฅผ ์คํํ๋ ๊ตฌ์กฐ๋ฅผ ์ดํดํ์ต๋๋ค. ํธ์คํธ Docker ์์ผ์ ๊ณต์ ํ๋ ๋ฐฉ์์ด ๋ณด์๊ณผ ์ฑ๋ฅ ์ธก๋ฉด์์ ์ด๋ค ํธ๋ ์ด๋์คํ๋ฅผ ๊ฐ๋์ง๋ ์ถ๊ฐ๋ก ๊ณต๋ถํ๊ฒ ๋์ต๋๋ค.