도커(docker) 등장배경과 docker-compose, Dockerfile 이해하기

Lily·2024년 8월 18일
16
post-thumbnail

들어가며

그동안은 팀 프로젝트를 진행하면서 작업을 완료하면 main 브랜치에 PR 을 날리고 머지하는 식이었습니다.

그러니까 따로 dev 브랜치도 없고, 개발서버와 운영서버 분리도 되어있지 않은 상황이었습니다.

그런데 이번에 개발서버와 운영서버를 분리해야 하는 필요성이 생기면서, 도커를 도입하게 되었습니다.

그 과정에서 제가 도커에 대해 공부 한 내용을 담아보려고 합니다.

도커는 왜 쓰는가?

지겹도록 들어온 .. 💡 기술은 문제를 해결하기 위해 등장한다 라는 말을 떠올리며 도커를 왜 쓰는 지부터 알아보겠습니다.

옛날옛날에

초기에는 소프트웨어를 서버에 배포하는 과정이 상당히 번거로웠습니다.

이해를 돕기 위해, 현재 저의 상황을 예시를 들어볼게요. 저희 팀은 스프링 부트를 사용해서 웹 애플리케이션을 개발했습니다.

이 애플리케이션을 서버에 배포하려면 먼저 해당 서버에 필요한 여러 가지 소프트웨어와 설정을 맞춰야 합니다.

  • 어떤 게 필요하지?
    • 웹 애플리케이션이 사용자에게 보여지기 위한 웹 서버 Nginx
    • 애플리케이션 로직을 처리하기 위한 SpringBoot
    • 애플리케이션을 작성한 JAVA 21 버전
    • 데이터 처리를 위한 MySQL
    • 등등

이처럼 위와 같은 소프트웨어들이 모두 서버에 설치되어있고, 각 소프트웨어가 제대로 설정되어야 애플리케이션이 정상적으로 작동하게 됩니다.

하지만 문제는 이 과정이 꽤나 번거롭고 오류가 발생하기 쉽다는 점입니다.

서버마다 환경이 조금이라도 다르면, 하나의 서버에서는 잘 작동하던 프로그램이 다른 서버에서는 작동하지 않을 수 있었어요. 개발 환경과 실제 서버 환경이 달라서 생기는 문제가 많았습니다.

이런 문제들을 해결하기 위해 사람들은 "가상화(Virtualization)" 라는 개념을 도입하게 됩니다.

가상화(Virtualization)

가상화는 말 그대로 '가상의 컴퓨터' 를 만들어주는 기술입니다. 이 기술을 이용하면 한 대의 서버 안에서 여러 개의 가상 컴퓨터(VM)를 만들 수 있어요. 각 가상 컴퓨터는 독립된 운영체제를 가지고 있고, 마치 실제 컴퓨터 처럼 작동합니다.

예를 들어, 하나의 물리 서버에서 Ubuntu와 Windows 같은 서로 다른 운영체제를 동시에 실행할 수 있게 됩니다. 이렇게 되면, 서로 다른 환경에서의 충돌 문제를 해결할 수 있습니다.

하지만 가상화에도 단점이 있었어요. 가상 컴퓨터를 만들기 위해서는 각각의 운영체제를 설치해야 했으며, 가상 컴퓨터를 여러 개 만들면 그만큼 CPU, 메모리 등의 하드웨어 자원을 많이 차지해서 너무 비효율적이었습니다. 그만큼 관리도 까다로웠고요.

이런 문제를 해결하기 위해 '컨테이너(Container)'라는 개념이 등장하게 되었습니다.

컨테이너(Container)

컨테이너는 가상 컴퓨터랑 비슷한 개념인데, 훨씬 가볍고 효율적이에요.

가상 컴퓨터는 각자 운영체제를 가지고 있지만, 컨테이너는 단일 운영체제의 커널을 공유하면서도 각 컨테이너가 독립된 환경을 제공합니다.

⍢ 커널은 OS 의 핵심 영역!

이를 통해, 하나의 서버에서 여러 컨테이너를 실행하더라도 이전의 가상화 방법들에 비해 가볍고 훨씬 빠릅니다.

또한, 컨테이너는 이미지(Image)라는 형태로 패키징되기 때문에 한 번 만든 이미지를 어디서나 동일하게 실행할 수 있어요.

→ 개발 환경에서 잘 작동한 프로그램이 운영서버에서도 동일하게 작동하는 것!

도커(Docker)

이렇게 컨테이너라는 개념이 등장하면서, 이를 쉽게 사용할 수 있는 도구들이 필요하게 되었는데 그 중 가장 대표적인 것이 도커(Docker) 입니다!

도커를 사용해서 아까 예시로 든 프로그램에 필요한 모든 환경을 하나의 이미지로 만들 수 있어요.

이제 이 컨테이너만 서버에 배포하면, 개발 환경과 동일한 조건에서 애플리케이션이 실행될 수 있게 됩니다.

도커로 애플리케이션 배포하기

도커를 잘 아는 멋진😎 팀원이 작업한 내용을 뜯어보며 공부했는데, 이 과정에서 많은 학습이 된 것 같습니다. 복습 겸 그때 학습했던 흐름을 그대로 기록해보겠습니다.

(이때 무작정 같은 팀 크루에게 찾아가 질문했는데, 친절히 설명해주어서 감사했어요❥)

일단 PR 의 files changed 는 docker-compose.yml 파일과 Dockerfile 입니다. 해당 파일들을 중심으로 설명이 될 것 같습니다.

Docker Compose 파일 (docker-compose.yml)

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:
  

service1) nginx

  • image: nginx : Nginx 를 실행하기 위한 공식 도커 이미지.
    • 이미 널리 사용되고 있는 소프트웨어(Nginx, MySQL 등)는 공식 도커 이미지가 제공이 되기 때문에, 직접 이미지를 만들 필요가 없습니다.
  • depends_on: Nginx 는 applicaion 서비스가 먼저 시작되어야 실행됨.
    • Nginx가 애플리케이션을 프록시하기위함
  • networks: nginx-app-net 네트워크를 사용하여 application 과 연결됨.
    • 네트워크를 명시적으로 지정하지 않아도 해당 컴포즈 파일 내의 모든 서비스들은 default 네트워크로 묶입니다.
  • ports: 호스트의 특정 포트를 컨테이너 내부의 특정 포트에 매핑함.
    • 외부에서 들어오는 요청이 컨테이너 내의 해당 포트로 전달됩니다.
    • HTTP와 HTTPS 요청을 처리
  • volumes
    • 호스트에서 Nginx의 설정 파일을 컨테이너 내부의 설정 파일로 덮어씀.
    • SSL 인증서와 개인 키를 호스트에서 컨테이너로 전달하여, 컨테이너 내의 서비스가 HTTPS 연결을 처리할 수 있게 설정
    • 등등

service2) application

추가적인 부분만 설명하겠습니다.

  • environment
    • TZ: "Asia/Seoul" - 컨테이너의 타임존을 설정
    • SPRING_PROFILE: dev - Spring Boot가 개발 환경에서 실행
  • restart: always: 컨테이너가 종료되더라도 자동으로 다시 시작되도록 설정
  • container_name: develup-app: 컨테이너의 이름을 develup-app으로 지정

Dockerfile

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"]                
  • FROM openjdk:21: openjdk 21 이미지를 기반으로 컨테이너를 생성
  • ARG JAR_FILE=build/libs/*.jar: JAR_FILE이라는 빌드 아티팩트 경로를 정의
  • COPY ${JAR_FILE} app.jar: JAR_FILE 변수를 통해 정의된 .jar 파일을 컨테이너 내부의 app.jar로 복사
  • EXPOSE 8080: 컨테이너가 8080번 포트를 외부에 노출
  • ENTRYPOINT: 컨테이너가 시작될 때 실행될 기본 명령어를 설정

정리

추가적으로 CD(Continuous Deployment) 를 통해 도커 컨테이너를 실행(docker compose up) 시키는 과정만 해주면 끄읏 입니다.

마지막으로.. 화이트보드에 적어가며 열정적으로 도커 강의를 해준 크루에게 감사하며 그 흔적을 남깁니다 ^__^ 🏃

profile
내가 하고 싶은 거

0개의 댓글

관련 채용 정보