목차
개요
본 실습에서는 Gitlab과 Jenkins를 사용해 CI/CD를 구축하였습니다. CI/CD는 지속적인 통합 및 배포를 의미하며, 소프트웨어 개발 및 배포 과정을 자동화하여 개발자들이 더욱 효율적으로 작업할 수 있도록 돕습니다. CI/CD를 통해 개발자들은 코드 변경 사항을 지속적으로 통합하고, 자동화된 빌드 및 테스트를 통해 품질을 확인할 수 있습니다. 이를 통해 배포 과정에서 발생할 수 있는 오류를 사전에 감지하고, 안정적인 소프트웨어 배포를 실현할 수 있습니다. CI/CD는 개발 생산성과 품질을 향상시키는 핵심 요소로서, 현대적인 소프트웨어 개발 프로세스에서 필수적입니다.
본 실습에서는 CI/CD 중 CD를 구축하는 과정을 겪었습니다. CI/CD 실습을 통해 GitLab과 Jenkins를 서버에서 실행하고 웹훅과 플러그인을 통해 서로 연동되도록 실시했으며, 연동을 통해 GitLab Project에 이벤트가 Push되면 Jenkins는 자동으로 빌드함으로써 CI가 진행되고 있음을 확인할 수 있었습니다.
이번 실습에서의 CD 구현은 Jenkins와 WAS를 연동하여 Jenkins가 빌드를 마치고 Push하여 WAS가 지속적으로 배포가 되도록 구축하는 것이었습니다.
<그림 1> 실습 목표 아키텍처
이에 지난 실습인 “Docker 컨테이너 환경 기반 웹 시스템 구현(Navitve)”을 Jenkins와 연동하여 배포되도록 하는 것을 목표로 했습니다. CD를 위해서는 Jenkins가 배포할 VM을 따로 두어 Jenkins에서 도커 이미지를 build & push하여 배포가 되는 서버에서 해당 이미지로 컨테이너를 실행하도록 했습니다.
구현 단계
1) 설계
CI/CD를 구축하기 위해 <그림 2>과 같은 아키텍처를 구성했습니다.
<그림 2> CI/CD 아키텍처
Gitlab의 프로젝트에 .war file, dockerfile 및 소스코드를 push하면 웹훅 설정으로 인해 Jenkins의 프로젝트가 빌드됩니다. Jenkins는 push된 .war 파일과 dockerfile로 이미지를 빌드하고 해당 이미지를 dockerhub에 push함으로써 다른 VM에서도 이미지를 pull할 수 있도록 구성했습니다. Jenkins에서 빌드한 이미지를 통해 배포를 담당하는 서버에서는 배포가 이뤄집니다.
CI/CD를 구축하기 위해 Gitlab, Jenkins, Deployment에 대한 VM을 구성했으며 각 VM의 스펙 및 구축환경은 다음과 같습니다.
GitLab
•
OS: CentOS 7
•
CPU: 2vCPU
•
RAM: 8GB
•
IP: 172.16.213.25
Jenkins
•
OS: CentOS 7
•
CPU: 1vCPU
•
RAM: 4GB
•
IP: 172.16.213.42
Deployment
•
OS: CentOS 7
•
CPU: 1vCPU
•
RAM: 4GB
•
IP: 172.16.213.43
2) 구현
CD를 구현하기 위해 Jenkins 프로젝트를 두 개를 생성했습니다. 먼저 Jenkins에서 docker 이미지를 빌드하고 dockerhub에 push하는 과정을 가지는 프로젝트를 생성했고, build/push의 과정을 갖는 프로젝트가 끝나고 난후 배포 서버에서 이미지를 pull하여 컨테이너를 생성하도록 하는 프로젝트를 생성해 CD를 구현했습니다.
2.1) Build/Push images in Jenkins
실습을 통해 GitLab과 연동되어 GitLab에 Event가 Push되면 Build가 되도록 Build Trigger를 설정했지만 Build Steps 섹션에서의 Execute shell의 Command가 비어있었습니다. 때문에 연결은 진행되었지만 아무 작업이 이뤄지지 않아 Command 스크립트를 작성했습니다.
<그림 3> Script 작성
먼저 도커 이미지를 registry에 Build 및 Push하기 위해 dockerhub 로그인을 진행했습니다. docker-compose.yaml 파일은 deploy 디렉토리에 복제해놓았는데, deploy는 Jenkins가 다른 VM에 접근해 배포를 하도록 하는 프로젝트 명입니다. 해당 스크립트를 작성하기 전 “pwd” 명령어를 통해 GitLab에 Push했던 파일 및 폴더가 Jenkins 컨테이너의 bash의 /var/jenkins_home/workspace/{project명}에 저장되는 것과 이 디렉토리에서 Command가 이뤄지는 것을 알 수 있었고, 이 때문에 docker-compose.yaml 파일을 deploy에 복제했습니다.
다음으로는 이전 실습에 사용되었던 petclinic을 빌드하고 registry에 push했습니다. 이 과정으로 배포가 이뤄지는 VM에서 Jenkins가 빌드한 이미지를 사용해서 컨테이너를 생성되도록 구축했습니다.
2.2) Deployment
GitLab에 Event가 있을 때 새롭게 이미지를 빌드하고 푸쉬하는 프로젝트 이외에 프로젝트를 추가로 생성했습니다. 해당 프로젝트는 타 VM에 배포하는 과정을 갖는 프로젝트입니다.
2.2.1) 사전 설정
타 VM에 접근해서 배포하기 때문에 Jenkins가 SSH 접근이 가능하도록 설정해주었습니다.
<그림 4> Plugin 설치
<그림 5> SSH Server 추가
SSH에 접근해 Command를 실행하는 Plugin인 “Publish Over SSH”를 설치하고 어느 SSH에 접근할 것인지 설정을 추가했습니다.
2.2.2) 프로젝트 생성
2.1 에서 생성한 프로젝트(sa_cicd)의 빌드가 완료되면 Deployment VM에 WAS를 배포하는 프로젝트(deploy)를 생성했습니다. 이전 프로젝트의 빌드가 stable할 때만 build가 실행되도록 하는 배포 프로젝트를 생성했는데, 실패했을 때는 배포가 이뤄지지 않아 안정성을 유지했습니다.
<그림 6> Build Trigger 설정
빌드 환경으로는 “Publish Over SSH” 플러그인을 통해 실행되는 설정을 활성화해 배포 VM에서 Command가 실행되도록 설정함으로써 컨테이너가 생성되며 배포가 완료되도록 구축했습니다.
<그림 7> Build Environment 설정
2.1 에서 deploy 디렉토리로 복제해놓은 docker-compose.yaml을 원격 서버로 전송해 해당 VM에 직접 접근해 파일을 관리하지 않도록 했고 compose 파일을 실행시킴으로써 이전 실습이었던 “Docker 컨테이너 환경 기반 웹 시스템 구현(Navitve)”의 petclinic의 WS와 WAS에 대한 Container가 생성되도록 구축했습니다. 이전 실습 과정에서 컨테이너를 생성시켰을 때 바로 접근이 되지 않는 문제가 있어서 WS와 WAS 컨테이너는 한번씩 재실행의 절차를 가졌습니다.
3) 결과 (배포)
마지막으로, 설계했던 아키텍처 및 파이프라인대로 작동하는지 확인하기 위해 GitLab에 소스코드를 Push하여 배포되는 것을 검증했습니다.
3.1) Git Push
GitLab과 Jenkins는 웹훅으로 연결되어 GitLab에 Push가 발생하면 Jenkins의 빌드가 이뤄지는 과정을 갖고 있어 GitLab에 작업했던 petclinic의 소스코드를 push했습니다.
<그림 8> GitLab Project 화면
3.2) Jenkins Build
3.2.1) 소스파일 빌드
<그림 9> sa_cicd 프로젝트 빌드 대기
<그림 10> sa_cicd 프로젝트 빌드
Git Push가 이뤄지면 Jenkins는 이를 감지하고 프로젝트 build를 진행했습니다.
3.2.2) 배포 프로젝트 빌드
<그림 11> deploy 프로젝트 빌드
3.2.1 의 sa_cicd 프로젝트가 빌드가 완료되고 stable해 deploy 프로젝트가 실행되었습니다.
3.3) 결과 확인
deploy 프로젝트의 결과로 172.16.213.43 VM에는 이전에 존재하지 않았던 docker-compose.yaml이 생성되었고, docker 컨테이너가 생성되어 실행에 성공했으며, URL을 통해 브라우저에서 접속할 수 있음을 확인했습니다.
<그림 12> docker container 생성 확인
<그림 13> URL 접속 화면
Trouble Shooting
실습을 진행하면서 가장 시간을 많이 소비했던 것은 Jenkins가 docker를 실행하도록 하는 것이었습니다. petclinic 프로젝트 실습 환경상 .war 파일을 만들고 dockerfile을 통해 이미지를 빌드하는 과정에서 docker build . 과 같은 Docker 명령어 사용이 필요한데 Jenkins는 Docker 위에 실행되고 있기 때문에 Docker 명령어를 사용할 수 없었습니다. 해당 문제를 해결하기 위해서는 Docker in Docker나 Docker out of Docker를 사용해야 했습니다.
Docker out of Docker (DooD)는 Docker 컨테이너 내에서 다른 Docker 컨테이너를 실행하는 개념입니다. 일반적으로 Docker 컨테이너는 호스트 시스템의 Docker 데몬을 사용하여 다른 컨테이너를 실행합니다. 그러나 DooD를 사용하면 호스트 시스템의 Docker 데몬 대신 외부 Docker 데몬을 사용하여 컨테이너를 실행할 수 있습니다.
DooD를 사용하는 가장 일반적인 시나리오는 Jenkins와 같은 CI/CD 도구에서 Docker 컨테이너를 실행해야 하는 경우입니다. 호스트 시스템의 Docker 데몬을 사용하면 보안 및 권한 문제가 발생할 수 있으며, DooD를 사용하면 이러한 문제를 우회할 수 있습니다.
DooD를 구현하는 방법은 여러 가지가 있지만, 일반적으로는 호스트 시스템의 Docker 소켓을 Docker 컨테이너 내부로 마운트하여 사용합니다. 이를 통해 Docker 컨테이너 내에서 호스트 시스템의 Docker 데몬을 사용할 수 있게 됩니다. DooD는 Docker 컨테이너 내에서 Docker를 사용해야 하는 경우 유용한 기술이며, CI/CD 파이프라인에서 Docker 이미지를 빌드하거나 실행해야 할 때 많이 활용됩니다.
따라서 Jenkins를 실행할 이미지를 새로 빌드하고 볼륨설정을 통해 Jenkins container에서도 docker 명령어를 사용할 수 있었습니다.
<그림 14> Jenkins 이미지 생성 스크립트
docker run -d -p 8181:8080 —restart=always —name jenkins \
-v /var/run/docker.sock:/var/run/docker.sock my_jenkins:latest
Bash
복사
결론 및 회고
CI/CD 구축 결과
GitLab과 Jenkins를 사용하여 CI/CD를 구축한 결과, 소스코드 변경 시 자동으로 빌드 및 배포되는 과정을 경험할 수 있었습니다. GitLab과 Jenkins의 연결을 통해 소스코드를 Push하면 Jenkins가 자동으로 빌드를 시작하고, 안정적인 배포를 위해 컨테이너를 생성하고 실행했습니다.
또한, Jenkins를 사용하는 과정에서 Docker out of Docker(DooD)를 구현하여 Docker 컨테이너 내에서 Docker 명령어를 실행할 수 있도록 설정했습니다. 이를 통해 CI/CD 파이프라인에서 Docker 이미지를 빌드하고 실행하는 작업을 신속하게 처리할 수 있었습니다.
구축과정에서 현재까지 구축한 것에 대한 개선을 생각해보았습니다. GitLab에서 push된 소스코드 중에서 .war 파일과 Dockerfile만을 Jenkins에서 가져와 빌드하게 된다면, 이는 필요한 파일들만 Jenkins의 워크스페이스에 쌓아둘 수 있게 하여, 워크스페이스의 공간을 효율적으로 사용할 수 있게 될 것입니다. 이런 세부적인 최적화 방안은 CI/CD 구축 과정에서 더욱 고려해볼 만한 부분이라고 생각합니다.
느낀점
CI/CD를 구축하면 소프트웨어 개발 및 배포 과정에서 생산성을 크게 향상시킬 수 있다는 것을 느꼈습니다. 자동화된 빌드 및 배포 과정을 통해 개발자는 코드 변경 사항을 더 빠르게 테스트하고 배포할 수 있으며, 안정적인 배포 환경을 유지할 수 있습니다.
또한, CI/CD를 통해 개발자들은 지속적인 통합과 배포를 위한 작업에 더 많은 시간과 에너지를 집중할 수 있습니다. 이는 개발자들이 자신의 업무에 집중하고 더 나은 코드를 작성할 수 있도록 도와줍니다.
CI/CD를 통해 소프트웨어 개발 프로세스를 자동화하고 효율성을 높일 수 있었던 경험은 매우 유익했습니다. 앞으로도 CI/CD를 적극적으로 활용하여 개발 프로세스를 개선하고 효율적인 소프트웨어 개발을 지속적으로 추구하고자 합니다.
.png&blockId=14dadc51-d61a-8061-b14f-e09aa66f37a9)
.png&blockId=14dadc51-d61a-8061-b14f-e09aa66f37a9&width=256)





.png&blockId=32cd7b0f-1ef0-46cc-b322-854f8891d783)


.png&blockId=2238e59d-ff8b-4362-bf93-2493e46d6032)



