그동안은 팀 프로젝트를 진행하면서 작업을 완료하면 main 브랜치에 PR 을 날리고 머지하는 식이었습니다.
그러니까 따로 dev 브랜치도 없고, 개발서버와 운영서버 분리도 되어있지 않은 상황이었습니다.
그런데 이번에 개발서버와 운영서버를 분리해야 하는 필요성이 생기면서, 도커를 도입하게 되었습니다.
그 과정에서 제가 도커에 대해 공부 한 내용을 담아보려고 합니다.
지겹도록 들어온 .. 💡 기술은 문제를 해결하기 위해 등장한다 라는 말을 떠올리며 도커를 왜 쓰는 지부터 알아보겠습니다.
초기에는 소프트웨어를 서버에 배포하는 과정이 상당히 번거로웠습니다.
이해를 돕기 위해, 현재 저의 상황을 예시를 들어볼게요. 저희 팀은 스프링 부트를 사용해서 웹 애플리케이션을 개발했습니다.
이 애플리케이션을 서버에 배포하려면 먼저 해당 서버에 필요한 여러 가지 소프트웨어와 설정을 맞춰야 합니다.
이처럼 위와 같은 소프트웨어들이 모두 서버에 설치되어있고, 각 소프트웨어가 제대로 설정되어야 애플리케이션이 정상적으로 작동하게 됩니다.
하지만 문제는 이 과정이 꽤나 번거롭고 오류가 발생하기 쉽다는 점입니다.
서버마다 환경이 조금이라도 다르면, 하나의 서버에서는 잘 작동하던 프로그램이 다른 서버에서는 작동하지 않을 수 있었어요. 개발 환경과 실제 서버 환경이 달라서 생기는 문제가 많았습니다.
이런 문제들을 해결하기 위해 사람들은 "가상화(Virtualization)" 라는 개념을 도입하게 됩니다.
가상화는 말 그대로 '가상의 컴퓨터' 를 만들어주는 기술입니다. 이 기술을 이용하면 한 대의 서버 안에서 여러 개의 가상 컴퓨터(VM)를 만들 수 있어요. 각 가상 컴퓨터는 독립된 운영체제를 가지고 있고, 마치 실제 컴퓨터 처럼 작동합니다.
예를 들어, 하나의 물리 서버에서 Ubuntu와 Windows 같은 서로 다른 운영체제를 동시에 실행할 수 있게 됩니다. 이렇게 되면, 서로 다른 환경에서의 충돌 문제를 해결할 수 있습니다.
하지만 가상화에도 단점이 있었어요. 가상 컴퓨터를 만들기 위해서는 각각의 운영체제를 설치해야 했으며, 가상 컴퓨터를 여러 개 만들면 그만큼 CPU, 메모리 등의 하드웨어 자원을 많이 차지해서 너무 비효율적이었습니다. 그만큼 관리도 까다로웠고요.
이런 문제를 해결하기 위해 '컨테이너(Container)'라는 개념이 등장하게 되었습니다.
컨테이너는 가상 컴퓨터랑 비슷한 개념인데, 훨씬 가볍고 효율적이에요.
가상 컴퓨터는 각자 운영체제를 가지고 있지만, 컨테이너는 단일 운영체제의 커널을 공유하면서도 각 컨테이너가 독립된 환경을 제공합니다.
⍢ 커널은 OS 의 핵심 영역!
이를 통해, 하나의 서버에서 여러 컨테이너를 실행하더라도 이전의 가상화 방법들에 비해 가볍고 훨씬 빠릅니다.
또한, 컨테이너는 이미지(Image)라는 형태로 패키징되기 때문에 한 번 만든 이미지를 어디서나 동일하게 실행할 수 있어요.
→ 개발 환경에서 잘 작동한 프로그램이 운영서버에서도 동일하게 작동하는 것!
이렇게 컨테이너라는 개념이 등장하면서, 이를 쉽게 사용할 수 있는 도구들이 필요하게 되었는데 그 중 가장 대표적인 것이 도커(Docker) 입니다!
도커를 사용해서 아까 예시로 든 프로그램에 필요한 모든 환경을 하나의 이미지로 만들 수 있어요.
이제 이 컨테이너만 서버에 배포하면, 개발 환경과 동일한 조건에서 애플리케이션이 실행될 수 있게 됩니다.
도커를 잘 아는 멋진😎 팀원이 작업한 내용을 뜯어보며 공부했는데, 이 과정에서 많은 학습이 된 것 같습니다. 복습 겸 그때 학습했던 흐름을 그대로 기록해보겠습니다.
(이때 무작정 같은 팀 크루에게 찾아가 질문했는데, 친절히 설명해주어서 감사했어요❥)
일단 PR 의 files changed 는 docker-compose.yml
파일과 Dockerfile
입니다. 해당 파일들을 중심으로 설명이 될 것 같습니다.
docker-compose.yml
(= 위 compose.dev.yml) 파일은 여러 개의 컨테이너를 정의하고, 이들을 한 번에 관리할 수 있게 해줍니다.
📌 아래 compose 파일에선 Nginx 와 Spring Boot 를 각각 하나의 서비스로 설정
services:
nginx:
image: nginx
depends_on:
- application
networks:
- nginx-app-net
ports:
- "<포트포워딩할 주소>:<컨테이너 내부 포트>"
volumes:
- "<호스트 경로>:<컨테이너 경로>"
application:
image: ${BACKEND_APP_IMAGE_NAME}
networks:
- nginx-app-net
ports:
- "<포트포워딩할 주소>:<컨테이너 내부 포트>"
environment:
TZ: "Asia/Seoul"
SPRING_PROFILE: dev
restart: always
container_name: develup-app
networks:
nginx-app-net:
추가적인 부분만 설명하겠습니다.
TZ: "Asia/Seoul"
- 컨테이너의 타임존을 설정SPRING_PROFILE: dev
- Spring Boot가 개발 환경에서 실행develup-app
으로 지정Dockerfile
은 애플리케이션을 어떻게 컨테이너화할지 정의하는 파일입니다.
📌 아래의 Dockerfile은 Spring Boot 애플리케이션을 도커 이미지로 빌드하기 위한 과정 (Spring Boot를 이용해 직접 개발한 애플리케이션이므로 공식 도커 이미지가 없으니까)
FROM openjdk:21
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-Dspring.profiles.active=${SPRING_PROFILE}", \
"-jar", "/app.jar"]
openjdk 21
이미지를 기반으로 컨테이너를 생성JAR_FILE
이라는 빌드 아티팩트 경로를 정의JAR_FILE
변수를 통해 정의된 .jar 파일을 컨테이너 내부의 app.jar
로 복사추가적으로 CD(Continuous Deployment) 를 통해 도커 컨테이너를 실행(docker compose up) 시키는 과정만 해주면 끄읏 입니다.
마지막으로.. 화이트보드에 적어가며 열정적으로 도커 강의를 해준 크루에게 감사하며 그 흔적을 남깁니다 ^__^ 🏃