[Docker] Docker A to Z

예름·2025년 4월 19일

Docker

목록 보기
8/8
post-thumbnail

비전공자도 이해할 수 있는 Docker 입문/실전 강의를 바탕으로 작성했습니다.

실전에서 사용하는 거의 모든 내용을 담았으므로 목차에서 필요한 부분만 보시길 권장드립니다.

📄 목차 바로가기

📍 개요

💡 Docker를 왜 배울까?

현업에서 Docker를 왜 많이 사용할까요?
Docker를 쓰는 이유에는 여러 가지 장점이 있지만 가장 핵심적인 장점은 이식성 입니다.

이식성이란?
특정 프로그램을 다른 곳으로 쉽게 옮겨서 설치 및 실행할 수 있는 특성

예를 들어 봅시다.

A의 컴퓨터에 MySQL을 깔고 정상 작동하는 것을 확인했습니다. 그런데 B의 컴퓨터에 MySQL을 깔았더니 에러가 발생했습니다. 왜 이런 상황이 발생할까요?

B의 컴퓨터에 에러가 발생하는 이유는 다양합니다. A와 다른 버전을 설치했거나, 운영체제가 다르거나, B의 컴퓨터에 깔려있는 다른 프로그램과 충돌이 일어났거나와 같은 다양한 이유로 프로그램이 정상적으로 설치되지 않을 수 있습니다. 에러를 해결하기 위해서는 복잡한 과정을 거쳐야 합니다.

이러한 상황을 해결해주는 도구가 Docker 입니다. Docker를 사용하면 명령어 한 줄로 어떤 컴퓨터에든 MySQL을 에러 없이 설치하고 실행할 수 있습니다.

💡 Docker의 장점

Docker의 장점으로는 이식성 말고도 또 무엇이 있을까요?

  • 매번 귀찮은 설치 과정을 일일이 거치지 않아도 됩니다.
  • 항상 일관되게 프로그램을 설치할 수 있습니다.(버전, 환경 설정, 옵션, 운영 체제 등)
  • 각 프로그램이 독립적인 환경에서 실행되기 때문에 프로그램 간에 서로 충돌이 일어나지 않습니다.

🔎 Docker의 기본 개념

💡 Docker란?

컨테이너를 사용하여 각각의 프로그램을 분리된 환경에서 실행 및 관리할 수 있는 툴입니다.

💡 컨테이너(Container)란?

위에서 말한 분리된 환경을 컨테이너라고 부릅니다.

Docker를 통해 하나의 컴퓨터 환경에서 독립적인 컴퓨터 환경을 구성해서 각 환경에 프로그램을 별도로 설치할 수 있습니다.

'컨테이너''컨테이너를 포함하고 있는 컴퓨터'를 구분하기 위해 컨테이너를 포함하고 있는 컴퓨터를 '호스트 컴퓨터'라고 부릅니다.

💡 이미지(Image)란?

이미지는 컨테이너를 정의하는 읽기 전용 템플릿입니다.

이미지는 프로그램을 실행하는 데 필요한 설치 과정, 설정, 버전 정보 등 프로그램을 실행하는 데 필요한 모든 것을 포함하고 있습니다.


🔎 Docker 명령어

자주 사용하는 명령어만 작성했습니다.
도커의 전체적인 개념 및 흐름을 보고 싶다면 명령어는 나중에 보시고, 밑에 있는 Dockerfile, 볼륨, 컴포즈 부터 보시는 것을 추천드립니다.

💡 이미지(Image) 다운로드

이미지를 다운로드 할 때 Dockerhub라는 곳에서 이미지를 다운받습니다.
Dockerhub은 Github처럼 이미지를 저장 및 다운받을 수 있는 저장소 역할을 합니다.

최신 버전(latest) 이미지 다운로드

# docker pull 이미지명
$ docker pull nginx # docker pull nginx:latest와 동일하게 작동

특정 버전 이미지 다운로드

특정 버전을 나타내는 이름태그명이라고 합니다. 태그명은 Dockerhub에서 확인할 수 있습니다.

# docker pull 이미지명:태그명
$ docker pull nginx:stable-perl

💡 이미지(Image) 조회

다운받은 모든 이미지 조회

$ docker image ls
  • ls : list의 약자

💡 이미지(Image) 삭제

특정 이미지 삭제

$ docker image rm [이미지 ID 또는 이미지명]
  • rm : remove의 약자
  • 이미지 ID를 입력할 때 전체 ID를 다 입력하지 않고 ID의 일부만 입력해도 됩니다.
  • 컨테이너에서 사용하고 있지 않은 이미지만 삭제가 가능합니다.

중지된 컨테이너에서 사용하고 있는 이미지 강제 삭제

$ docker image rm -f [이미지 ID 또는 이미지명]
  • 실행 중인 컨테이너에서 사용하고 있는 이미지는 강제로 삭제할 수 없습니다. → 따라서 반드시 컨테이너를 중지 시킨 후에 이미지를 삭제해야 합니다.

전체 이미지 삭제

# 컨테이너에서 사용하고 있지 않은 이미지만 전체 삭제
$ docker image rm $(docker images -q)

# 컨테이너에서 사용하고 있는 이미지를 포함해서 전체 이미지 삭제
$ docker image rm -f $(docker images -q)
  • docker images -q : 시스템에 있는 모든 이미지의 ID를 반환합니다. 여기서 -q 옵션은 quite를 의미하며, 상세 정보 대신에 각 이미지의 고유한 ID만 표시하도록 지시합니다.

💡 컨테이너(Container) 생성/실행

컨테이너 생성

이미지를 바탕으로 컨테이너를 생성합니다. 다음 명령어는 컨테이너를 생성만 하고 실행시키지는 않습니다.

# docker create 이미지명[:태그명]
$ docker create nginx

$ docker ps -a # 모든 컨테이너 조회
  • 로컬 환경에 다운받은 이미지가 없다면 Dockerhub으로부터 이미지를 다운(docker pull)받아서 컨테이너를 생성합니다.

컨테이너 실행

# docker start 컨테이너명[또는 컨테이너 ID]
$ docker start 컨테이너명[또는 컨테이너 ID]

$ docker ps # 실행중인 컨테이너 조회

# Nginx 컨테이너 중단 후 삭제하기
$ docker ps # 실행 중인 컨테이너 조회
$ docker stop {nginx를 실행시킨 Contnainer ID} # 컨테이너 중단
$ docker rm {nginx를 실행시킨 Contnainer ID} # 컨테이너 삭제
$ docker image rm nginx # Nginx 이미지 삭제

컨테이너 생성 + 실행

다음 명령어는 이미지를 바탕으로 컨테이너를 생성한 뒤, 컨테이너를 실행까지 시킨킵니다. (처음에 이미지를 바탕으로 컨테이너를 실행시키고 싶을 때, 이 명령어를 자주 사용합니다.)

# docker run 이미지명[:태그명]
$ docker run nginx # 포그라운드에서 실행 (추가적인 명령어 조작을 할 수가 없음)

# Ctrl + C로 종료할 수 있음
  • 로컬 환경에 다운받은 이미지가 없다면 Dockerhub으로부터 이미지를 다운(docker pull)받아서 실행시킵니다.
  • Dockerhub으로부터 새롭게 갱신된 이미지를 다운 받고 싶다면 docker pull 명령어를 활용해야 합니다.

컨테이너를 백그라운드에서 실행시키기

# docker run -d 이미지명[:태그명]
$ docker run -d nginx

# Nginx 컨테이너 중단 후 삭제하기
$ docker ps # 실행 중인 컨테이너 조회
$ docker stop {nginx를 실행시킨 Contnainer ID} # 컨테이너 중단
$ docker rm {nginx를 실행시킨 Contnainer ID} # 컨테이너 삭제
$ docker image rm nginx # Nginx 이미지 삭제

포그라운드(foreground) vs 백그라운드(background)

포그라운드는 내가 실행시킨 프로그램의 내용이 화면에서 실행되고 출력되는 상태를 뜻합니다. 그러다보니 포그라운드 상태에서는 다른 프로그램을 조작할 수가 없습니다.

백그라운드는 내가 실행시킨 프로그램이 컴퓨터 내부적으로 실행되는 상태를 의미합니다. 그래서 프로그램이 어떻게 실행되고 있는 지에 대한 정보를 화면에서 확인할 수 없습니다. 이런 특성 때문에 다른 명령어를 추가로 입력할 수도 있고, 새로운 프로그램을 조작할 수도 있습니다.

컨테이너에 이름 붙여서 생성 및 실행하기

# docker run -d --name [컨테이너 이름] 이미지명[:태그명]
$ docker run -d --name my-web-server nginx

# Nginx 컨테이너 중단 후 삭제하기
$ docker ps # 실행 중인 컨테이너 조회
$ docker stop {nginx를 실행시킨 Contnainer ID} # 컨테이너 중단
$ docker rm {nginx를 실행시킨 Contnainer ID} # 컨테이너 삭제
$ docker image rm nginx # Nginx 이미지 삭제

호스트의 포트와 컨테이너의 포트를 연결하기

# docker run -d -p [호스트 포트]:[컨테이너 포트] 이미지명[:태그명]
$ docker run -d -p 4000:80 nginx

  • docker run -p 4000:80 라고 명령어를 입력하게 되면, 도커를 실행하는 호스트의 4000번 포트를 컨테이너의 80번 포트로 연결하도록 설정합니다.

💡 컨테이너(Container) 조회/중지/삭제

컨테이너 조회

$ docker ps # 실행 중인 컨테이너들만 조회

$ docker ps -a # 모든 컨테이너 조회(작동 중인 컨테이너 + 작동을 멈춘 컨테이너)
  • ps : process status의 약자
  • a: all의 약자

컨테이너 중지

$ docker stop 컨테이너명[또는 컨테이너 ID]
$ docker kill 컨테이너명[또는 컨테이너 ID]
  • 집에 있는 컴퓨터로 비유하자면 stop은 시스템 종료 버튼을 통해 정상적으로 컴퓨터를 종료하는 걸 의미하고, kill은 본체 버튼을 눌러 무식하게 종료하는 걸 의미합니다.

컨테이너 삭제

$ docker rm 컨테이너명[또는 컨테이너 ID] # 중지되어 있는 특정 컨테이너 삭제

$ docker rm -f 컨테이너명[또는 컨테이너 ID] # 실행되고 있는 특정 컨테이너 삭제

$ docker rm $(docker ps -qa) # 중지되어 있는 모든 컨테이너 삭제

$ docker rm **-f** $(docker ps -qa) # 실행되고 있는 모든 컨테이너 삭제

💡 컨테이너(Container) 로그 조회

컨테이너를 실행시키고나서 실행시킨 컨테이너가 잘 실행되고 있는 지, 에러가 발생한 건 아닌 지 로그를 확인할 수 있어야 합니다. 디버깅할 때 필수로 확인해야 하는 게 로그입니다. 지금부터 컨테이너에서 발생한 로그는 어떻게 확인하는 지 알아봅시다.

특정 컨테이너의 모든 로그 조회

# docker logs [컨테이너 ID 또는 컨테이너명]

$ docker run -d nginx
$ docker logs [nginx가 실행되고 있는 컨테이너 ID]

최근 로그 10줄만 조회

# dokcer logs --tail [로그 끝부터 표시할 줄 수] [컨테이너 ID 또는 컨테이너명]
$ dokcer logs --tail 10 [컨테이너 ID 또는 컨테이너명]

기존 로그 조회 + 생성되는 로그를 실시간으로 보고 싶은 경우

# docker logs -f [컨테이너 ID 또는 컨테이너명]

# Nginx의 컨테이너에 실시간으로 쌓이는 로그 확인하기
$ docker run -d -p 80:80 nginx
$ docker logs -f
  • f : follow의 약어

기존 로그는 조회하지 않기 + 생성되는 로그를 실시간으로 보고 싶은 경우

$ docker logs **--tail 0 -f** [컨테이너 ID 또는 컨테이너명]

💡 실행중인 컨테이너 내부에 접속하기 (exec -it)

실행 중인 컨테이너 내부에 접속하기

# docker exec -it 컨테이너명[또는 컨테이너 ID] bash

$ docker run -d nginx
$ docker exec -it [Nginx가 실행되고 있는 컨테이너 ID] bash
$ ls # 컨테이너 내부 파일 조회
$ cd /etc/nginx 
$ cat nginx.conf
  • 컨테이너 내부에서 나오려면 Ctrl + D 또는 exit을 입력하면 됩니다.
  • bash : 쉘(Shell)의 일종
  • -it : -it옵션을 사용해야 명령어를 입력하고 결과를 확인할 수 있습니다. -it옵션을 적지 않으면 명령어를 1번만 실행시키고 종료되어 버립니다. 즉, -it 옵션을 적어야 계속해서 명령어를 입력할 수 있습니다.

🔎 Docker 볼륨(Docker Volume)

💡 Docker 볼륨이란?

Docker 볼륨이란 도커 컨테이너에서 데이터를 영속적으로 저장하기 위한 방법입니다.
볼륨(Volume)은 컨테이너 자체의 저장 공간을 사용하지 않고, 호스트 자체의 저장 공간을 공유해서 사용하는 형태입니다.

💡 볼륨(Volume)을 사용하는 명령어

$ docker run -v [호스트의 디렉토리 절대경로]:[컨테이너의 디렉토리 절대경로] [이미지명]:[태그명]

⚠️ 볼륨을 사용할 때는 주의해야할 점이 있습니다.

호스트의 디렉토리 절대 경로에 디렉토리가 존재하지 않을 경우, 호스트의 디렉터리 절대 경로에 디렉터리를 새로 만들고 컨테이너의 디렉터리에 있는 파일들을 호스트의 디렉터리로 복사해옵니다.

호스트의 디렉토리 절대 경로에 디렉토리가 이미 존재할 경우, 호스트의 디렉터리가 컨테이너의 디렉터리를 덮어씌웁니다.

💡 볼륨(Volume)을 활용해 MySQL 컨테이너 띄우기

$ cd /Users/yereumi/Documents/Develop
$ mkdir docker-mysql # MySQL 데이터를 저장하고 싶은 폴더 만들기

# docker run -e MYSQL_ROOT_PASSWORD=password123 -p 3306:3306 -v {호스트의 절대경로}/mysql_data:/var/lib/mysql -d mysql
$ docker run -e MYSQL_ROOT_PASSWORD=password123 -p 3306:3306 -v /Users/yereumi/Documents/Develop/docker-mysql/mysql_data:/var/lib/mysql -d mysql

주의) mysql_data 디렉토리를 미리 만들어 놓으면 안됩니다. 그래야 처음 이미지를 실행시킬 때 mysql 내부에 있는 /var/lib/mysql 파일들을 호스트 컴퓨터로 공유받을 수 있습니다. mysql_data 디렉토리를 미리 만들어놓을 경우, 기존 컨테이너의 /var/lib/mysql 파일들을 전부 삭제한 뒤에 mysql_data로 덮어씌워 버립니다.

DB에 관련된 데이터가 저장되는 곳이 /var/lib/mysql 인지는 Dockerhub MySQL의 공식 문서에 나와있습니다.


🔎 Dockerfile

💡 Dockerfile이란?

Dockerfile이란 Docker 이미지를 만들게 해주는 파일입니다.

# FROM - 베이스 이미지를 생성하는 역할
FROM [이미지명]
FROM [이미지명]:[태그명]

# COPY - 호스트 컴퓨터에 있는 파일을 복사해서 컨테이너로 전달
COPY [호스트 컴퓨터에 있는 복사할 파일의 경로] [컨테이너에서 파일이 위치할 경로]
  
# ENTRYPOINT - 컨테이너가 생성되고 최초로 실행할 때 수행되는 명령어
ENTRYPOINT [명령문...]

# RUN - 이미지 생성 과정에서 명령어를 실행시켜야 할 때 사용
RUN [명령문]

# WORKDIR - 작업 디렉토리를 지정
WORKDIR [작업 디렉토리로 사용할 절대 경로]

# EXPOSE - 컨테이너 내부에서 사용 중인 포트를 문서화하기
EXPOSE [포트 번호]

💡 Spring Boot 프로젝트를 Docker 이미지로 만들기

Dockerfile 작성하기

FROM openjdk:17-jdk

COPY build/libs/*SNAPSHOT.jar app.jar

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

Spring Boot 프로젝트 빌드하기

$ ./gradlew clean build

Dockerfile을 바탕으로 이미지 빌드하기

$ docker build -t hello-server .

이미지가 잘 생성됐는지 확인하기

$ docker image ls

생성한 이미지를 컨테이너로 실행하기

$ docker run -d -p 8080:8080 hello-server

컨테이너 잘 실행되고 있는 지 확인하기

$ docker ps

localhost:8080으로 들어가보기


🔎 Docker 컴포즈(Docker Compose)

💡 Docker 컴포즈란?

Docker Compose란 여러 개의 Docker 컨테이너들을 하나의 서비스로 정의하고 구성해 하나의 묶음으로 관리할 수 있게 도와주는 툴입니다.

💡 Docker Compose를 사용하는 이유

1. 여러 개의 컨테이너를 관리하는 데 용이

여러 개의 컨테이너로 이루어진 복잡한 애플리케이션을 한 번에 관리할 수 있게 해줍니다. 여러 컨테이너를 하나의 환경에서 실행하고 관리하는 데 도움이 됩니다.

2. 복잡한 명령어로 실행시키던 걸 간소화 시킬 수 있음

이전에 MySQL 이미지를 컨테이너로 실행시킬 때 아래와 같은 명령어를 실행시켰습니다.

$ docker run -e MYSQL_ROOT_PASSWORD=password123 -p 3306:3306 –v /Users/jaeseong/Documents/Develop/docker-mysql/mysql_data:/var/lib/mysql -d mysql

Docker Compose를 사용하면 위와 같이 컨테이너를 실행시킬 때마다 복잡한 명령어를 입력하지 않고, 단순히 docker compose up 명령어만 실행시키면 됩니다.

💡 자주 사용하는 Docker Compose CLI 명령어

compose.yml

services:
  websever:
    container_name: webserver
    image: nginx
    ports: 
      - 80:80

compose.yml에서 정의한 컨테이너 실행하기

$ docker compose up    # 포그라운드에서 실행
$ docker compose up -d # 백그라운드에서 실행

Docker Compose로 실행시킨 컨테이너 확인하기

# compose.yml에 정의된 컨테이너 중 실행 중인 컨테이너만 보여줌 
$ docker compose ps 

# compose.yml에 정의된 모든 컨테이너를 보여줌
$ docker compose ps -a

Docker Compose 로그 확인하기

# compose.yml에 정의된 모든 컨테이너의 로그를 모아서 출력
$ docker compose logs

컨테이너를 실행하기 전에 이미지 재빌드하기

$ docker compose up --build # 포그라운드에서 실행
$ docker compose up --build -d # 백그라운드에서 실행

이미지 다운받기 / 업데이트하기

$ docker compose pull

Docker Compose에서 이용한 컨테이너 종료하기

$ docker compose down

💡 Docker CLI ↔ Docker Compose 쉽게 작성하기

지금까지의 예제를 보면 Docker CLI로 작성할 수 있는 명령어는 전부 compose.yml 파일로 옮길 수 있습니다. 반대로 compose.yml에 작성한 모든 값은 Docker CLI로 나타낼 수 있습니다. 이를 편하게 변환해주는 사이트가 존재합니다.

Docker CLI → compose.yml로 변환

Composerize

compose.yml → Docker CLI로 변환

Decomposerize

💡 Spring Boot, MySQL 컨테이너 동시에 띄워보기

application.yml 작성하기

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: pwd1234
    driver-class-name: com.mysql.cj.jdbc.Driver

Dockerfile 작성하기

FROM openjdk:17-jdk

COPY build/libs/*SNAPSHOT.jar /app.jar

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

compose.yml 작성하기

services:
  my-server:
    build: .
    ports:
      - 8080:8080
		# my-db의 컨테이너가 생성되고 healthy 하다고 판단 될 때, 해당 컨테이너를 생성한다. 
    depends_on:
      my-db:
        condition: service_healthy
  my-db:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: pwd1234
      MYSQL_DATABASE: mydb # MySQL 최초 실행 시 mydb라는 데이터베이스를 생성해준다.
    volumes:
      - ./mysql_data:/var/lib/mysql
    ports:
      - 3306:3306
    healthcheck:
      test: [ "CMD", "mysqladmin", "ping" ] # MySQL이 healthy 한 지 판단할 수 있는 명령어
      interval: 5s # 5초 간격으로 체크
      retries: 10 # 10번까지 재시도

Spring Boot 프로젝트 빌드하기

$ ./gradlew clean build

compose 파일 실행시키기

$ docker compose up -d **--build**

compose 실행 현황 보기

$ docker compose ps
$ docker ps
$ docker logs [Container ID]

❓ 에러가 발생하는 이유?

위에처럼 작성하면 Spring Boot 컨테이너에서 에러가 발생합니다.

localhost:3306은 spring-boot 컨테이너 내부의 3306 포트를 가르키기 때문에 spring-boot 컨테이너 밖에 있는 mysql 컨테이너와 연결되지 못합니다.

따라서 application.yml을 다음과 같이 작성해야 합니다.

spring:
  datasource:
    url: jdbc:mysql://my-db:3306/mydb # localhost가 아니라 mysql의 컨테이너 이름인 my-db로 수정
    username: root
    password: pwd1234
    driver-class-name: com.mysql.cj.jdbc.Driver


그러면 my-db인 컨테이너를 인지하고 Spring Boot와 MySQL을 연결해줍니다.

🔮 다음 글 예고

다음 글은 도커를 활용하여 AWS EC2에 배포하는 내용을 가져오겠습니다.

profile
안정적인 쳇바퀴를 돌리는 삶

6개의 댓글

comment-user-thumbnail
2025년 4월 19일

오늘부터 제 전공책으로 삼겠습니다.... 최고의 블로그.

1개의 답글
comment-user-thumbnail
2025년 4월 19일

도커를 어떻게 사용할지 몰라 정보를 찾고 있었는데 덕분에 해결할 수 있었습니다! 감사합니다!☺️

1개의 답글
comment-user-thumbnail
2025년 4월 21일

와 전 아무것도 모르는데 찔려서 들어왔습니다

1개의 답글