본문 바로가기
Study/Docker

[Docker_CI/CD_스터디] 2. GitLab 세팅 & Docker Image 빌드 및 푸시

by Lpromotion 2025. 1. 31.

목차
 

1. GitLab 프로젝트 생성

GitLab은 설치형과 인터넷 서비스형이 있는데, 이 실습에서는 인터넷 서비스형을 사용한다.

 

Get free trial

 

그룹 생성 후 프로젝트 생성

 

Plan > Issue boards

칸반 보드

티켓을 할당하고 티켓을 처리하는 과정을 모니터링하여 전체 업무를 관리하는 기능.

open → 작업 → Review or QA → closed

 

Build > Pipelines

현재 프로젝트가 CI/CD의 어느 단계에 있는지, 어느 상태에 있는지를 모니터링할 수 있는 기능.

 

Build > Pipeline editor

파이프라인을 설계하는 기능.

Build, Test, Deploy 등의 과정을 설계한다. (회사, 업무 마다 다름)

 

 

2. SSH Key 설정하기

프로젝트를 Private로 생성했기 때문에 “Clone with SSH”를 하려면 SSH Key가 있어야 한다.

 

터미널을 실행한다.

`ssh-keygen` 명령어로 SSH Key를 만들 수 있다.

ssh-keygen -t rsa
  • `-t rsa`: 타입은 rsa

 

 

위의 출력된 디렉토리에서 확인할 수 있다.

private key와 public key가 생성되었다.

공개 키를 프로젝트에 등록하면 된다.

 

cat id_rsa.pub

key를 복사하여 “Add SSH Key”로 들어간다.

 

Add SSH Key > Add new key

이 계정에서 GitLab에 CLone을 하게 되면 권한을 확인해야 한다. 권한을 Private Key를 통해 확인하고 Clone이나 여러가지 Git 작업을 수행할 수 있는 권한을 획득하게 된다.

 

 

3. 개발 환경 세팅하기

“flask-app” 프로젝트를 Clone한다.

“Clone with SSH”를 복사한다.

 

터미널에서 /home/workspace에 Clone 한다.

git clone {git URL}

 

vi test.txt

테스트 파일을 추가한 후,

git status

 

git add test.txt
git commit -m "example code"
git push

Git 저장소가 성공적으로 세팅되었다.

 

 

4. 프로젝트 코드 작업

 

GitLab > Groups > New groupn > Create group

 

TeamJoinc > New project > Creat blank project

 

터미널 실행 > workspace 디렉터리로 이동

git clone {git URL}

test-app 디렉터리로 이동

`code .` 를 통해 vs code 사용

 

app.py

from flask import Flask

app = Flask(__name__)

@app.route('/') # Route URL 호출
def home():
    return 'Hello, Flask World!'

if __name__ == '__main__':
    app.run(debug=True)

 

requirements.txt

flask

 

의존성 설치

pip install -r requirements.txt

 

오류 발생

Ubuntu에서는 시스템 Python 환경을 보호하기 위해 `pip install` 을 막고 있으며, 공식적으로 가상환경을 만들어서 패키지를 설치하는 방법을 권장한다.

 

sudo apt install python3.12-venv
python3 -m venv venv // 가상환경 생성
source venv/bin/activate // 가상환경 활성화
pip install -r requirements.txt

 

작업 마친 후, 가상환경 비활성화

deactivate

 

실행

python3 app.py

http://127.0.0.1:5000 로 실행된 것을 확인할 수 있다.

웹 브라우저로 접속해보면,

 

Flask 애플리케이션 개발까지 완료되었다.

 

 

5. Docker Image 만들기

Dockerfile 생성

FROM python:3.9-slim
WORKDIR /opt/app
COPY . /opt/app
RUN pip install -r requirements.txt
CMD ["python3", "app.py"]
  • `FROM`: 어떤 이미지를 바탕으로 만들지. (Base Image)
  • `WORKDIR`: 컨테이너가 실행되고 나서 작업 디렉터리
  • `COPY`: 현재 모든 파일을 WORKDIR로 복사
  • `RUN`: 컨테이너 안에서 해당 명령을 실행
  • `CMD`: 컨테이너가 실행되었을 때 실행할 명령어

 

app.py

from flask import Flask

app = Flask(__name__)

@app.route('/') # Route URL 호출
def home():
    return 'Hello, Flask World!'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
  • `host='0.0.0.0'`: 외부에서 작동하도록 설정

 

Docker Image 빌드

docker build -t teamjoinc/test-app:0.1 .
  • `docker build`: Docker 이미지를 생성하는 명령어.
  • `-t teamjoinc/test-app:0.1`
    • t 옵션은 태그(tag) 지정을 의미.
    • teamjoinc/test-app → 이미지 이름
    • 0.1 → 이미지 버전(tag)
    • 따라서, teamjoinc/test-app이라는 이름으로 0.1 버전의 이미지를 생성.
  • `.` (현재 디렉토리)
    • Dockerfile이 위치한 현재 디렉토리에서 이미지를 빌드함.
    • 즉, .는 Dockerfile과 필요한 파일들이 있는 위치를 나타냄.

⇒ 현재 디렉토리(.)에 있는 Dockerfile을 기반으로 Docker 이미지를 빌드하는 명령어

 

ubuntu@ubuntu-VirtualBox:~/workspace/test-app$ docker build -t teamjoinc/test-app:0.1 .
[+] Building 5.9s (9/9) FINISHED                                                                                                         docker:default
 => [internal] load build definition from Dockerfile                                                                                               0.0s
 => => transferring dockerfile: 152B                                                                                                               0.0s
 => [internal] load metadata for docker.io/library/python:3.9-slim                                                                                 1.5s
 => [internal] load .dockerignore                                                                                                                  0.0s
 => => transferring context: 2B                                                                                                                    0.0s
 => [1/4] FROM docker.io/library/python:3.9-slim@sha256:bb8009c87ab69e751a1dd2c6c7f8abaae3d9fce8e072802d4a23c95594d16d84                           0.0s
 => [internal] load build context                                                                                                                  0.1s
 => => transferring context: 143.56kB                                                                                                              0.1s
 => CACHED [2/4] WORKDIR /opt/app                                                                                                                  0.0s
 => [3/4] COPY . /opt/app                                                                                                                          0.2s
 => [4/4] RUN pip install -r requirements.txt                                                                                                      3.5s
 => exporting to image                                                                                                                             0.3s 
 => => exporting layers                                                                                                                            0.3s 
 => => writing image sha256:5db84badb501abd8551cf8effcd0999a012fbb2ac015ac6f30889ded626fc977                                                       0.0s 
 => => naming to docker.io/teamjoinc/test-app:0.1

 

Docker Image 확인

docker images

 

컨테이너 만들기

docker run --name test-app -p 5000:5000 teamjoinc/test-app:0.1
  • `docker run`: 새 컨테이너를 생성하고 실행
  • `--name test-app`: 컨테이너의 이름을 test-app으로 지정
  • `-p 5000:5000`: 호스트의 5000번 포트를 컨테이너의 5000번 포트에 매핑
  • `teamjoinc/test-app:0.1`: 실행할 Docker 이미지 이름과 태그(버전)

 

확인하는 방법

curl localhost:5000

 

Docker Image 만들기 완료.

 

현재 상태

1단계까지 완료했다.

 

 

6. Docker Image를 ECR에 push하기

AWS > Elastic Container Registry > 리포지토리 생성 > 생성

 

접근 권한 설정

푸시 명령을 수행하기 전에 ECR에 대한 접근 권한 설정이 필요하다.

AWS Cloud에서는 IAM이라는 서비스를 이용해 권한을 관리할 수 있다.

IAM을 이용해 Access Key와 Secret Key를 발급하고, 이 키를 가지고 있는 사용자만 ECR에 접근할 수 있도록 설정한다.

 

IAM > 사용자 > 사용자 생성

모든 권한을 가지는 admin 권한을 선택한다.

 

 

다음은 Secret Key를 만든다.

ecr-user > 보안 자격 증명> 액세스 키 > 액세스 키 만들기

터미널을 사용하기 때문에 CLI 선택

 

“.csv 파일 다운로드”하여 키를 받는다.

파일을 열어보면 Access Key와 Secret Key 두 개로 이루어져 있다. 이 키를 사용하여 접근 권한을 설정한다.

 

먼저 AWS CLI를 설치한다.

참고: https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html

 

최신 버전의 AWS CLI 설치 또는 업데이트 - AWS Command Line Interface

이전 버전에서 업데이트하는 경우 unzip 명령을 실행하면 기존 파일을 덮어쓸지 묻는 메시지가 표시됩니다. 스크립트 자동화와 같은 경우에 이러한 프롬프트를 건너뛰려면 unzip에 대한 -u 업데이

docs.aws.amazon.com

 

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

설치 완료

 

`aws configure` → Access Key 입력 → Secret Key 입력 → Region(서울) 입력 → 출력 포멧(JSON) 입력

키 설정 완료

 

확인 명령어 (/home 에서)

cd .aws/
cat config
cat credeintials

 

사용자는 credeintial을 여러 개 가질 수 있다.

예시) `vi credeintials`

[default]
aws_access_key_id = {aws_access_key_id}
aws_secret_access_key = {aws_secret_access_key}

[devops]
aws_access_key_id = XXXXXXXXX
aws_secret_access_key = YYYYYYYYYYYY

프로파일 이름(예시: devops)을 지정하여 작업을 수행할 수 있다.

 

푸시 명령 실행

1. AWS ECR 로그인

Elastic Container Registry > 리포지토리 > 푸시 명령 보기

 

> 1. AWS CLI 사용

aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 277707140590.dkr.ecr.ap-northeast-2.amazonaws.com

AWS ECR 로그인 명령어이다.

  • `aws ecr get-login-password --region ap-northeast-2`: AWS ECR에서 로그인 비밀번호를 가져옴
  • `docker login --username AWS --password-stdin <ECR_URL>`: AWS ECR 레지스트리에 Docker 로그인

권한이 있기 때문에 로그인 성공.

 

> 푸시 명령 보기 > 2. 이미지 빌드

이미 이미지 빌드는 위에서 수행했기 때문에 넘어간다.

 

2. 이미지에 태그를 지정

푸시 명령 보기 > 3. 이미지에 태그를 지정

docker tag teamjoinc/test-app:0.1 277707140590.dkr.ecr.ap-northeast-2.amazonaws.com/teamjoinc/test-app:0.1

기본 명령어에서 태그만 lastest → 0.1 로 변경했다.

 

3. 푸시

> 푸시 명령 보기 > 4. AWS 리포지토리로 푸시

docker push 277707140590.dkr.ecr.ap-northeast-2.amazonaws.com/teamjoinc/test-app:0.1

기본 명령어에서 태그만 lastest → 0.1 로 변경했다.

 

푸시 완료.

 

 

ECR에 잘 들어가 있는지 확인한다.

ECR > 리포지토리

0.1 버전이 잘 들어가 있는 것을 확인할 수 있다.

 

테스트

실제 저장소에 있는 이미지를 가지고 와서 컨테이너를 실행할 수 있는지 테스트한다.

 

먼저 기존의 image를 강제로 지워준다.

docker rmi {IMAGE_ID} -f

 

이미지 URL을 복사한다.

docker run --name ecr-test -p 5000:5000 277707140590.dkr.ecr.ap-northeast-2.amazonaws.com/teamjoinc/test-app:0.1

잘 작동하는 것을 확인했다.

 

프로세스 확인

docker ps

 

ECR에 Image를 Push하고, Image를 Pull해서 실행하는 것까지 완료했다.

이제 자동화 단계로 넘어간다.

 

 

7. GitLab을 이용한 Build Push 자동화

GitLab이 사람이 하는 일을 코드로 자동화한다.

build, log,tagging, push 작업들을 GitLab이 스크립트로 관리하고, 스트립트를 실행하여 전 과정을 자동화한다.

 

GitLab Runner 설치

https://docs.gitlab.com/runner/install/linux-manually.html

 

Install GitLab Runner manually on GNU/Linux | GitLab

GitLab product documentation.

docs.gitlab.com

 

패키지를 직접 다운로드하면 된다.

# Replace ${arch} with any of the supported architectures, e.g. amd64, arm, arm64
# A full list of architectures can be found here https://s3.dualstack.us-east-1.amazonaws.com/gitlab-runner-downloads/latest/index.html
curl -LJO "https://s3.dualstack.us-east-1.amazonaws.com/gitlab-runner-downloads/latest/deb/gitlab-runner-helper-images.deb"
curl -LJO "https://s3.dualstack.us-east-1.amazonaws.com/gitlab-runner-downloads/latest/deb/gitlab-runner_${arch}.deb"

Architecture은 직접 입력해줘야 함. `amd64` 로 입력.

 

의존 패키지 먼저 설치한다.

sudo dpkg -i gitlab-runner-helper-images.deb

다음으로 gitlab-runner 설치한다.

sudo dpkg -i gitlab-runner_amd64.deb

 

그리고 ps 명령어로 확인해보면

GitLab Runner가 실행 중인 것을 확인할 수 있다.

 

이제 GitLab CI에서 GitLab Runner를 등록시켜 주면 된다.

 

GitLab Runner 등록

GitLab > Project > Settings > CI/CD > Runner

> Project runners > New project runner

"Run untagged jobs" 체크

 

GitLab Runner는 GitLab CI에서 발급한 token을 가지로 등록 요청을 하게 된다. GitLab CI는 자신이 발급한 token인지 확인하여 GitLab Runner가 프로젝트에 접근할 수 있는 권한을 준다.

이후 GitLab은 주기적으로 변동 사항을 확인하여 코드를 읽어와서 CI/CD 작업을 수행한다.

 

Step1의 명렁어를 GitLab이 설치면 호스트에 실행해주면 된다.

ubuntu@ubuntu-VirtualBox:~$ gitlab-runner register  --url https://gitlab.com  --token glrt-t3_NxNLh1KSFd2zr4Fi9sV1
Runtime platform                                    arch=amd64 os=linux pid=4873 revision=690ce25c version=17.8.3
WARNING: Running in user-mode.                     
WARNING: The user-mode requires you to manually start builds processing: 
WARNING: $ gitlab-runner run                       
WARNING: Use sudo for system-mode:                 
WARNING: $ sudo gitlab-runner...                   
                                                   
Enter the GitLab instance URL (for example, https://gitlab.com/):
[https://gitlab.com]: 
Verifying runner... is valid                        runner=t3_NxNLh1
Enter a name for the runner. This is stored only in the local config.toml file:
[ubuntu-VirtualBox]: test-app
Enter an executor: custom, shell, parallels, kubernetes, docker-autoscaler, ssh, virtualbox, docker, docker-windows, docker+machine, instance:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
 
Configuration (with the authentication token) was saved in "/home/ubuntu/.gitlab-runner/config.toml"
  • GitLab 서버 URL을 입력하라고 요청: [https://gitlab.com]은 기본값(Default), 그냥 Enter를 누르면 그대로 적용된다.
  • Runner의 이름을 지정: test-app으로 입력. (이 이름은 로컬 파일에만 저장되고, GitLab에는 저장되지 않는다.)
  • Runner가 빌드를 실행할 방식(Executor) 선택: shell 입력 (Runner가 빌드를 로컬 셸 환경에서 실행하도록 설정)

 

만들어진 설정파일로 이동.

cd .gitlab-runner/
vi config.toml

name, url, token 등이 모두 설정된 것을 확인할 수 있다.

 

GitLab Ci는 token 값이 일치하면 등록된 runner라는 것을 인증하고 실행할 수 있도록 권한을 준다.

 

GitLabl Runner 실행

gitlab-runner run

"View runners"로 이동.

프로젝트를 위한 runner가 활성화된 것을 확인할 수 있다.

 

Instance runner는 비활성화 시켜준다.

 

GitLab Runner 자동 실행 설정

systemctl 설정을 변경한다.

cd /etc/systemd/system
vi gitlab-runner.service

config와 working-directory, user를 변경했다.

 

cd ~
ls -al .bash_logout
vi .bash_logout

가장 밑의 3줄 주석 처리.

 

서비스 재시작.

systemctl daemon-reload
sudo systemctl restart gitlab-runner

 

부팅할 때 매번 자동으로 실행하도록 해야한다.

sudo systemctl enable gitlab-runner

 

 

8. GitLab PIPLINE 구성

project > Build > Pipline editor > Configure pipline

main 브랜치에 commit

 

예시

doc-job:
    stage: build
    script:
        - echo "Create API document..."
        - echo "Create complete."

위처럼 job을 추가하거나, stage를 추가하여 사용할 수 있다.


"Visualize" 탭에서도 확인할 수 있다.

 

> Piplines

 

실패하는 예시도 확인한다.

unit-test-job:   # This job runs in the test stage.
  stage: test    # It only starts when the job in the build stage completes successfully.
  script:
    - echo "Running unit tests... This will take about 60 seconds."
    - sleep 20
    - exit 1

`exit`에서 0이 아닌 값은 실패했다고 출력된다.

 

특정 stage에서 실패하면 다음 stage로 넘어가지 않는다.

 

> project

yml 파일이 main 브랜치에 들어가 있는 것을 확인할 수 있다.

 

 

9. Makefile 푸시 및 GitLab CI/CD 실행

Makefile 빌드 및 푸시

`git pull`을 통해 GitLab CI에서 작업했던 내용을 가져온다.

 

`vi Makefile`로 Makefile을 추가한다.

PRJ_NAME=YOUR_PROJECT_NAME
ECR_URI=YOUR_ECR_URL
VERSION:=$(shell git rev-parse --short HEAD)


build:
	docker build -t $(PRJ_NAME):$(VERSION) .
	
push:
	aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin $(ECR_URI)
	docker tag $(PRJ_NAME):$(VERSION) $(ECR_URI)/$(PRJ_NAME):$(VERSION)
	docker tag $(PRJ_NAME):$(VERSION) $(ECR_URI)/$(PRJ_NAME):latest
	docker push $(ECR_URI)/$(PRJ_NAME):$(VERSION)
	docker push $(ECR_URI)/$(PRJ_NAME):latest
  • `git rev-parse --short HEAD`: 현재 버전에 대한 hash 값을 출력한다. 이 값을 이용해 버전을 설정할 수 있다. 결과적으로 build 명령이 `docker build -t teamjoinc/test-app: {버전값}`이 된다.

 

다음으로 `make build`를 하게 되면, 변수가 치환되어 이미지가 만들어 지는 것을 확인할 수 있다.

`docker images`를 통해 확인할 수 있다.

 

`make push`

login, tag, push 까지 자동으로 실행된다.

 

ECR에서 잘 푸시되었는지 확인한다.

ECR > 리포지토리

 

Make Script를 GitLab에서 호출

GitLab > project > Build > Pipline editor

실제 작동하는 스크립트를 작성한다.

stages:          # List of stages for jobs, and their order of execution
  - build
  - test
  - deploy

build-job:       # This job runs in the build stage, which runs first.
  stage: build
  script:
    - echo "Docker build start"
    - make build
    - echo "Docker build complete."

push-job:       # This job runs in the build stage, which runs first.
  stage: build 
  script:
    - echo "Docker push start"
    - make push
    - echo "Docker push complete."

unit-test-job:   # This job runs in the test stage.
  stage: test    # It only starts when the job in the build stage completes successfully.
  script:
    - echo "Running unit tests."
    - sleep 5 
    - echo "Unit tests complete."

lint-test-job:   # This job also runs in the test stage.
  stage: test    # It can run at the same time as unit-test-job (in parallel).
  script:
    - echo "Linting code... This will take about 10 seconds."
    - sleep 5 
    - echo "No lint issues found."

deploy-job:      # This job runs in the deploy stage.
  stage: deploy  # It only runs when *both* jobs in the test stage complete successfully.
  environment: production
  script:
    - echo "Deploying application..."
    - echo "Application successfully deployed."
  • build는 두 개의 job을 가진다. 
    • `build-job`: 이미지를 build 하는 작업을 수행한다.
    • `push-job`: build된 이미지를 push 하는 작업을 수행한다.
  • test는 두 개의 job을 가진다.
    • 현재는 test 작업을 하기 않기 때문에 echo만 사용한다.
  • deploy는 다음에서 다룬다.

 

현재는 Makefile과 코드들을 올리지 않았기 때문에 실패한다.

 

코드들을 올린다.

git pull

git add .
git commit -m "my source code"
git push

 

GitLab > project > Build > Pipelines

3개의 stage가 잘 돌아가는 것을 확인할 수 있다.

 

반응형

댓글