[250813수1844H] Docker 명령어, Docker Compose

윤승호·2025년 8월 13일

멘붕

학습시간 09:00~22:00(당일13H/누적1844H)


◆ 학습내용

1. Docker CLI

(1) 이미지 관련 명령어

  • build: Dockerfile로 이미지 생성 (docker build -t 이름:태그 .)
  • images: 로컬에 저장된 이미지 목록 보기 (docker images)
  • pull: 원격 저장소(Docker Hub)에서 이미지 가져오기 (docker pull nginx:latest)
  • push: 내가 만든 이미지를 원격 저장소에 올리기 (docker push 내이름/내앱:태그)
  • rmi: 로컬 이미지 삭제 (docker rmi 이미지ID)
  • tag: 이미지에 새로운 태그(이름) 붙이기 (docker tag 원본이미지:태그 새이미지:태그)
  • history: 이미지의 히스토리(만들어진 과정) 보기 (docker history 이미지이름)

(2) 컨테이너 관련 명령어

  • run: 이미지로 컨테이너를 생성하고 실행 (docker run -d -p 80:80 --name 웹서버 nginx)
  • ps: 실행 중인 컨테이너 목록 보기 (docker ps, a 옵션은 멈춘 것까지 모두)
  • stop: 실행 중인 컨테이너 중지 (docker stop 컨테이너이름)
  • start: 멈춘 컨테이너 다시 시작 (docker start 컨테이너이름)
  • restart: 컨테이너 재시작 (docker restart 컨테이너이름)
  • rm: 컨테이너 삭제 (docker rm 컨테이너이름)
  • logs: 컨테이너의 로그(출력 기록) 확인 (docker logs -f 컨테이너이름)
  • exec: 실행 중인 컨테이너에 접속하거나 명령어 전달 (docker exec -it 컨테이너이름 /bin/bash)
  • cp: 호스트와 컨테이너 간 파일 복사 (docker cp 파일경로 컨테이너이름:파일경로)
  • inspect: 컨테이너나 이미지의 모든 세부 정보 확인 (docker inspect 컨테이너이름)

(3) 정리 명령어

  • prune: 한 방에 정리하는 강력한 명령어
  • docker system prune: 사용하지 않는 컨테이너, 이미지, 네트워크, 빌드캐시 모두 삭제 (a 옵션은 볼륨까지)
  • docker container prune: 멈춰있는 모든 컨테이너 삭제
  • docker image prune: 사용되지 않는 모든 이미지(댕글링 이미지) 삭제
  • docker volume prune: 어떤 컨테이너도 사용하지 않는 모든 볼륨 삭제

2. Dockerfile 작성법

(1) FROM

  • 만들 이미지의 기반(OS/런타임)을 설정하는 단계
  • 내 앱을 실행할 환경의 가장 기초가 되는 베이스 이미지를 지정하는, 모든 Dockerfile의 시작점
  • node:18-alpine처럼 가볍고 특정 버전이 명시된 공식 이미지를 사용하는 것이 좋음
FROM node:18-alpine

(2) LABEL

  • 이미지에 이름표를 붙이는 단계
  • 이미지 제작자, 버전, 설명 등 키-값 형태의 다양한 정보를 이미지 자체에 심어두는 역할
  • docker inspect 명령어로 나중에 확인할 수 있어 관리에 용이함
LABEL version="1.0" maintainer="Gildong Hong <gd.hong@example.com>"

(3) ARG

  • 빌드 타임(Build-Time) 변수를 정의하는 단계
  • docker build 명령어를 실행하는 시점에만 사용될 임시 변수를 선언
  • -build-arg 옵션으로 빌드 시점에 값을 동적으로 전달할 수 있어 유연함
ARG NODE_VERSION=18
FROM node:${NODE_VERSION}-alpine

(4) ENV

  • 컨테이너 실행 환경을 구성하는 단계
  • 이미지 자체에 환경 변수를 설정하여, 컨테이너가 실행될 때 기본값으로 사용됨
  • DB 정보, API 키 등 코드와 설정을 분리하여 보안과 유연성을 높임
ENV PORT="3000"
ENV NODE_ENV="production"

(5) WORKDIR

  • 명령어 실행 위치를 지정하는 단계
  • 이 명령어 이후에 실행될 모든 명령어(RUN, COPY 등)의 기본 실행 위치를 변경
  • 마치 터미널에서 cd 명령어를 실행한 것과 같은 효과를 줌
WORKDIR /usr/src/app

(6) COPY

  • 호스트 파일 → 이미지 내부로 복사하는 단계
  • 로컬 컴퓨터(호스트)의 파일이나 폴더를 이미지 파일 시스템 안으로 복사하는 가장 일반적인 방법
  • 소스 경로와 목적지 경로를 명확하게 지정
COPY ./package.json ./

(7) ADD

  • 파일 복사 + α
  • COPY와 기능은 같지만, 원격 URL에서 직접 파일을 다운로드하거나 압축 파일(tar)을 자동으로 풀어서 복사하는 기능이 추가됨
  • 기능이 복잡해서 일반적인 파일 복사는 COPY를 사용하는 것을 권장
ADD https://.../file.zip .

(8) RUN

  • 패키지 설치 및 소스코드 빌드
  • 이미지를 만드는 과정에서 필요한 스크립트나 명령어를 실행하는 핵심 명령어
  • 주로 앱 실행에 필요한 라이브러리를 설치하거나, 개발용 코드를 실행 가능한 코드로 변환(빌드)할 때 사용
RUN npm ci --only=production && npm run build

(9) USER

  • 명령어 실행 사용자 지정
  • RUN, CMD, ENTRYPOINT 명령어를 실행할 사용자 계정을 지정하여 보안을 강화
  • root가 아닌 일반 사용자 계정을 만들어 실행하는 것이 안전함
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

(10) VOLUME

  • 컨테이너 데이터를 외부에 보존
  • 컨테이너가 삭제되어도 데이터를 보존해야 할 경로를 지정하는 역할
  • DB 데이터, 로그, 사용자 업로드 파일 등을 저장하는 경로에 사용
VOLUME ["/var/log", "/usr/src/app/data"]

(11) EXPOSE

  • 컨테이너가 사용할 포트 선언
  • 컨테이너 내의 앱이 특정 포트를 사용한다는 것을 명시적으로 문서화하는 역할
  • 실제로 포트를 외부에 개방하지는 않으며, 실제 개방은 docker run -p 옵션으로 해야 함
EXPOSE 3000

(12) CMD

  • 컨테이너의 '기본' 동작 정의
  • 컨테이너가 시작될 때 실행할 기본 명령어로, docker run 시 다른 명령어를 주면 쉽게 덮어쓸 수 있음
  • ENTRYPOINT의 기본 인자(argument)로 사용되기도 함
CMD ["npm", "run", "start"]

(13) ENTRYPOINT

A. 컨테이너의 '고정' 실행 명령어

  • 컨테이너가 시작될 때 반드시 실행될 메인 명령어로, docker run에서 쉽게 덮어써지지 않음
  • 컨테이너를 하나의 실행 파일처럼 고정된 용도로 사용하고 싶을 때 적합
ENTRYPOINT ["node", "server.js"]

(14) HEALTHCHECK

  • 애플리케이션 정상 동작 확인
  • 컨테이너 안의 앱이 정말 정상적으로 동작하는지 주기적으로 체크하는 명령어를 지정
  • docker ps로 상태를 보면 (healthy) 또는 (unhealthy)로 표시되어 자동 복구 시스템과 연동 가능
HEALTHCHECK --interval=30s --timeout=5s CMD curl -f http://localhost:3000/ || exit 1

3. 이미지 빌드 및 배포 실습

(1) Dockerfile

ARG NODE_VERSION
FROM node:${NODE_VERSION}
COPY . /apps/mbti
WORKDIR /apps/mbti
RUN npm ci \
   && npm run build
ENV PORT="3000" \
    DB_HOST="localhost" \
    DB_PORT="3306" \
    DB_NAME="db_mbti" \
    DB_USERNAME="user_mbti" \
    DB_PASSWORD="pw_mbti"
ENTRYPOINT ["npm", "run", "start"]

이미지를 빌드하려면 폴더 내 Dockerfile을 생성해야 함
베이스 이미지의 태그는 이미지를 빌드할 때 지정할 수 있게 ARG를 사용

(2) .dockerignore

node_modules

.dockerignore 파일을 통해서 빌드 컨텍스트에서 제외할 수 있음

(3) 이미지 빌드

docker buildx build \
    --build-arg NODE_VERSION=20.15.1 \
    -t [계정명]/mbti:mysql \
    -t [계정명]/mbti:latest \
    --pull \
.

docker image build 명령어로 이미지를 빌드할 수 있음
이때 --build-arg 옵션으로 ARG로 작성해 두었던 변수의 값을 지정

(4) 이미지 배포

docker image push [계정명]/mbti:mysql
docker image push [계정명]/mbti:latest

docker image push 명령어로 이미지를 배포할 수 있음

a 옵션을 활용하면 이미지의 여러 태그를 한 번에 배포할 수 있음

docker image push -a [계정명]/mbti

(5) 네트워크 생성

컨테이너 간 네트워크 연결이 필요 시 네트워크를 생성해야함

docker network create mbti-net

(6) 볼륨 생성

컨테이너 삭제 후에서 남겨놓을 디렉토리

docker volume create mbti-vol

(7) db 컨테이너 실행

docker container run \
    --name db \
    --rm \
    -d \
    -e MYSQL_ROOT_PASSWORD=pw_root \
    -e MYSQL_DATABASE=db_mbti \
    -e MYSQL_USER=user_mbti \
    -e MYSQL_PASSWORD=pw_mbti \
    --network mbti-net \
    -v mbti-vol:/var/lib/mysql \
mysql:8.3.0
  • 이때 db 컨테이너의 데이터가 저장되는 경로는 /var/lib/mysql
  • v 옵션으로 mbti-vol 볼륨과 연결함
  • -network 옵션으로 mbti-net 네트워크에 연결함
  • db 컨테이너는 외부에서 접속이 불가능해야 해서 p 옵션을 사용하지 않음

(8) app 컨테이너 실행

docker container run \
    --name app \
    --rm \
    -d \
    -e PORT=3000 \
    -e DB_HOST=db \
    -e DB_PORT=3306 \
    -e DB_NAME=db_mbti \
    -e DB_USERNAME=user_mbti \
    -e DB_PASSWORD=pw_mbti \
    --network mbti-net \
    -p 4000:3000 \
[계정명]/mbti:mysql
  • app 컨테이너는 db 컨테이너와 같은 네트워크(mbti-net)에 속하기 때문에 DNS 이름으로 통신할 수 있음
  • app 컨테이너는 외부에서 접속이 가능해야 하기 때문에 3000번 포트를 공개하고, 호스트의 4000번 포트로 바인딩함

(9) 테스트

[ http://localhost:4000/home ] 접속하기


4. Docker Compose

여러 컨테이너를 동시에 실행

# docker-compose.yaml
# 실행: docker compose -f ./docker-compose.yaml up -d --build
# 종료: docker compose down # 볼륨은 삭제되지 않음


name: mbti

services:
### docker buildx build에 들어가는 명령어
### 새로운 이미지를 빌드하는 것
  app:
    image: [계정명]/mbti:mysql
    build:
      args:
        - NODE_VERSION=20.15.1
      dockerfile: ./Dockerfile
      pull: true
      context: .

### docker container run에 들어가는 명령어(app)
    container_name: app
    environment:
      - PORT=3000
      - DB_HOST=db
      - DB_PORT=3306
      - DB_NAME=db_mbti
      - DB_USERNAME=user_mbti
      - DB_PASSWORD=pw_mbti
    networks:
      - mbti-net
    ports:
      - 3001:3000
    depends_on:  # db 서비스에 의존
      - db
	# 이후 condition, healthcheck 옵션을 추가하는 것이 좋음
    
### docker container run에 들어가는 명령어(db)
  db:
    image: mysql:8.3.0  # 도커허브 이미지 사용
    container_name: db
    environment:
      - MYSQL_ROOT_PASSWORD=root1234
      - MYSQL_DATABASE=db_mbti
      - MYSQL_USER=user_mbti
      - MYSQL_PASSWORD=pw_mbti
    networks:
      - mbti-net
    volumes:
      - mbti-vol:/var/lib/mysql

### 네트워크 & 볼륨 정의
networks:
  mbti-net:
    name: mbti-net
volumes:
  mbti-vol:
    name: mbti-vol
profile
나는 AI 엔지니어가 된다.

0개의 댓글