공부/DevOps

[Docker] Docker란? Docker와 Docker Compose 알아보기 (실습)

린구 2024. 8. 8. 23:55
반응형

 

📌 Docker

`Docker`는 어플리케이션을 쉽게 만들고 테스트하고 배포할 수 있게 도와주는 소프트웨어 플랫폼을 말한다.

어플리케이션을 `컨테이너`라는 가볍고 이식성 있는 `패키지`로 실행할 수 있다.

`Docker 이미지`는 어플리케이션 실행을 위한 모든 것을 포함한다.

 

Docker의 주요 특징 `컨테이너화` `경량` `이식성` `확장성`

 

`이미지` 어플리케이션 실행에 필요한 파일을 포함한 읽기 전용 템플릿`컨테이너` 실행중인 이미지 인스턴스`Dockerfile` 이미지를 생성하기 위한 명령어가 담긴 스크립트 파일`볼륨` 컨테이너 데이터를 지속적으로 저장하는 메커니즘`네트워크` 컨테이너 간 통신을 관리하는 방식

 

정리하자면 로컬에서 `Dockerfile`을 통해 `이미지`를 생성하고 이미지를 실행하면 `컨테이너`가 된다.그리고 이 `컨테이너`들은 `네트워크`를 통해 통신한다.

 

도커 이미지를 로컬에서 만든 후, 이를 `Docker Hub`에 `push`하여 다른 컴퓨터에서도 동일한 환경에서 작업할 수 있다.이 과정을 통해 애플리케이션의 일관성을 유지하고, 쉽게 배포할 수 있는 것이다 !!

 

 

📌 Docker 의 장점과 단점

💡 장점

  • 빠른 시작 시간과 낮은 오버헤드
  • 높은 이식성과 확장성
    • 도커 컨테이너는 한 번 만들면 어디서든 동일하게 실행

💡 단점

  • 보안 격리가 가상 머신보다 약함 (동일한 운영 체제 커널을 공유하므로)
  • 운영 체제 종속성 존재 (Docker가 리눅스 커널로 작동하므로 리눅스가 가장 호환이 잘됨)

 

📌 Docker는 언제 사용할까?

  • 일관된 개발 환경이 필요할 때
  • 애플리케이션을 빠르게 배포하고 싶을 때
  • 마이크로서비스 아키텍처를 도입할 때
  • CI/CD 파이프라인을 구축할 때
  • 애플리케이션 격리가 필요할 때

 

📌 Docker 명령어

💡 Docker 이미지 관련 명령어

`이미지 빌드`

docker build -t myapp:latest .

 

현재 디렉토리의 `Dockerfile`을 기반으로 myapp 이미지 생성

-t 옵션으로 이름과 태그 설정

 

`이미지 목록 보기`

docker images

 

`이미지 삭제`

docker rmi myapp:latest

이미지를 로컬 저장소에서 삭제

 

💡 Docker 컨테이너 관련 명령어

- 컨테이너 아이디는 식별 가능한 자릿수까지만 입력해도 된다

`컨테이너 실행`

docker run -d -p 8080:80 myapp:latest

myapp 이미지를 사용하여 컨테이너를 실행

`-d` 백그라운드에서 실행하는 옵션

`-p` 호스트의 8080 포트를 컨테이너의 80 포트에 매핑

 

`컨테이너 내부 접속`

docker exec -it 컨테이너_아이디 /bin/bash

 

`컨테이너 중지 / 컨테이너 삭제`

docker stop container_id
docker rm 컨테이너_아이디

컨테이너가 삭제돼도 이미지는 삭제되지 않는다!

 

📌 Docker Compose

`Docker Compose`는 다중 컨테이너 `Docker` 어플리케이션을 정의하고 실행하기 위한 도구이다.

`docker-compose.yml` 파일 하나로 어플리케이션의 서비스, 네트워크, 볼륨 등을 정의한다.

 

version: '3'
services:
  web:
    image: nginx
    ports:
      - "8080:80"
  app:
    build: .
    ports:
      - "8081:8080"
    depends_on:
      - db
  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: example

이런 식으로 `docker-compose.yml` 파일을 작성하고 명령어를 사용하여 빌드하면 된다.

 

`파일에 정의된 서비스를 빌드하고 시작`

docker compose up -d

 

이러한 방식으로 여러 컨테이너로 구성된 어플리케이션을 쉽게 관리하고 배포할 수 있다.

`docker-compose.yml` 파일 하나로 모든 서비스의 설정을 관리할 수 있어 일관된 환경을 제공할 수 있다.

 

📌 실습

💡 Docker 실습

2개의 스프링 컨테이너를 생성하고 서로의 서비스를 호출해볼 것이다.

우선 FeignClient를 사용하여 서비스 A가 B를 호출하는 형태로 프로젝트를 생성한다.

 

Docker에서 서로 다른 컨테이너를 사용할 경우, 각 컨테이너가 독립된 네임스페이스 내에서 실행되므로, 동일한 포트 번호를 사용할 수 있다.

이는 각 컨테이너가 격리된 환경에서 실행되기 때문에 가능하며, 같은 호스트 머신에서 서로 다른 컨테이너가 동일한 포트 번호를 사용하더라도 충돌이 발생하지 않는다. 

 

따라서 A, B 서비스 모두 포트번호를 8080으로 설정한다.

 

spring.application.name=service-a

server.port=8080

service.b.url=http://service-b:8080

 

서비스 A에서 B를 호출하기 위해 `service.b.url`을 지정하는데 두 서비스 포트가 같은 것을 볼 수 있다.

 

FROM openjdk:17-jdk-slim

VOLUME /tmp

ARG JAR_FILE=build/libs/*.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-jar","/app.jar"]

 

위와 같이 `Dockerfile`을 작성하여 각 서비스 디렉토리에 넣는다.

 

도커끼리 컨테이너 이름으로 호출하기 위해서는 기본 브리지 네트워크 대신 사용자 정의 네트워크에서 진행해야 한다.

docker network create my-network

`사용자 정의 네트워크 생성`

 

./gradlew clean bootJar

서비스 B 프로젝트의 루트 폴더에서 프로젝트를 빌드하고

 docker build -t img-service-b .

`이미지를 생성`한 뒤,

(`.`은 `Dockerfile` 위치)

docker run -d --name service-b \
 --network my-network \
 -p 18081:8080 \
 img-service-b

`컨테이너를 생성`한다!

 

18081 호스트 포트에서 8080 컨테이너 포트를 매핑한다.

`호스트 포트`란 Docker 컨테이너가 실행되는 컴퓨터의 네트워크 포트를 의미한다. 즉, 우리 컴퓨터의 포트 번호인 것!

컨테이너 내부에서는 자체적으로 네트워크 환경이 격리되어 있어, 컨테이너 내부의 애플리케이션이 사용하는 포트는 외부에서 직접 접근할 수 없다.

그래서 Docker에서는 호스트 포트와 컨테이너 포트를 매핑하는 방법으로 외부에서 컨테이너에 접근할 수 있도록 한다.

 

docker ps

`컨테이너 확인` 명령어를 통해 컨테이너가 실행됐는지 확인한다.

 

docker run -d --name service-a \
 --network my-network \
 -p 18080:8080 \
 -e SERVICE_B_URL=http://service-b:8080 \
img-service-a

서비스 A도 같은 방식으로 컨테이너로 실행한다.

 

이렇게 다 실행하고 나면 서비스 A가 서비스 B를 정상적으로 호출하는 것을 볼 수 있다!

 

💡 Docker Compose 실습

version: '3.8'

services:
  service-a:
    image: img-service-a
    ports:
      - "18080:8080"
    environment:
      - SERVICE_B_URL=http://service-b:8080
    depends_on:
      - service-b

  service-b:
    image: img-service-b
    ports:
      - "18081:8080"

networks:
  default:
    driver: bridge

서비스 A 프로젝트와 서비스 B 프로젝트의 상위 폴더에서 `docker-compose.yml` 파일을 생성한다.

`environment`는 컨테이너의 환경 변수를 설정한다.

서비스 A에서 B를 호출할때 사용하는 변수를 설정해주었다.

 

`depends_on`은 먼저 실행되어야 하는 서비스를 설정한다!

 

docker compose up -d

그리고 도커 컴포즈를 실행하면 위의 실습과 같은 결과를 확인할 수 있다!

 

* 도커 실습시에는 사용자 정의 네트워크를 따로 생성하였는데 도커 컴포즈에서는 생성 없이 어떻게 된걸까?

💡 Docker Compose를 사용하여 서비스를 실행하면, Docker Compose는 `docker-compose.yml`이 있는 디렉토리의 이름을 기반으로 하여 기본적으로 새 브리지 네트워크를 생성하고 각 서비스 컨테이너를 그 네트워크에 연결한다.

이 네트워크는 docker-compose.yml 파일에 정의된 모든 서비스가 서로 통신할 수 있도록 한다!

 


 

드디어 도커 강의를 들었다!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

그동안 도커가 너무 막연하게 느껴졌는데 ..

이렇게 강의를 듣고 나서 정리까지 해보니 조금이나마~ 이해가 되는 것 같다!!

더 연습해봐야지

 

 

 

반응형