CHHB stroy
Docker Desktop, 설치부터 실무 활용까지 — 내가 처음에 알았으면 좋았을 것들 본문
Docker 처음 접했을 때 솔직히 멘붕이었다. 가상머신이랑 뭐가 다른 건지, 이미지랑 컨테이너가 뭔 차인지, 왜 다들 도커 도커 하는 건지. 근데 한번 제대로 잡고 나니까 이제는 도커 없이 개발하는 게 상상이 안 된다. "내 컴에서는 되는데요?"를 안 듣게 된 것만으로도 충분한 가치가 있다.
오늘은 Docker Desktop 기준으로, 설치부터 실무에서 자주 쓰는 패턴까지 한번에 정리해보려고 한다.
Docker가 뭔데, 왜 써야 하는 건데
한 줄로 말하면, 앱을 실행하는 데 필요한 모든 것을 하나의 패키지로 묶어서 어디서든 똑같이 돌리는 도구다.
예를 들어 PHP + MySQL + Redis 조합의 프로젝트를 한다고 치자. 팀원 A는 Mac이고, B는 Windows고, C는 Ubuntu다. 셋 다 PHP 버전이 다르고, MySQL 설정이 다르고, 뭔가 하나씩 안 된다. 이걸 Docker로 묶어놓으면 세 사람 다 docker compose up 한 방이면 똑같은 환경에서 개발할 수 있다.
가상머신(VM)이랑 뭐가 다르냐는 질문을 많이 받는데, VM은 운영체제 전체를 올리는 거고 Docker는 필요한 프로세스만 격리해서 올린다. 그래서 훨씬 가볍고 빠르다. 내 맥북에서 VM으로 Ubuntu 3개 띄우면 팬이 미쳐 돌아가는데, 컨테이너 10개 띄워도 별로 티가 안 난다.
Docker Desktop 설치
macOS
두 가지 방법이 있다.
# Homebrew로 설치 (내가 선호하는 방법)
brew install --cask docker
# 또는 공식 사이트에서 .dmg 다운로드
# https://www.docker.com/products/docker-desktop/
설치하고 실행하면 상단 메뉴바에 고래 아이콘이 뜬다. 이게 떠 있어야 Docker 명령어가 동작한다. 가끔 터미널에서 docker 쳤는데 안 되면 십중팔구 Docker Desktop이 안 켜져 있는 거다.
Apple Silicon(M1/M2/M3) 맥이면 ARM 버전이 자동으로 설치된다. 근데 가끔 x86 전용 이미지를 돌려야 할 때가 있는데, 이때는 Rosetta 에뮬레이션이 알아서 처리해준다. 느리긴 하지만 동작은 한다.
Windows
1. Docker Desktop 공식 사이트에서 설치 파일 다운로드
2. 설치 진행 — WSL 2 백엔드 사용 옵션 체크 (기본값)
3. 재부팅
4. WSL 2 커널 업데이트가 필요하다고 뜨면 안내대로 설치Windows는 WSL 2(Windows Subsystem for Linux 2)가 핵심이다. 예전에는 Hyper-V를 썼는데 요즘은 WSL 2가 기본이고 성능도 훨씬 좋다. WSL 2가 없으면 Docker Desktop 설치 과정에서 같이 설치해준다.
⚠️ 주의: Windows Home 에디션도 WSL 2 방식이면 Docker Desktop 사용 가능하다. 예전에는 Pro 이상만 됐는데 그건 Hyper-V 시절 얘기다.
Linux
# Ubuntu 기준
sudo apt-get update
sudo apt-get install ./docker-desktop-<version>-amd64.deb
솔직히 Linux에서는 Docker Desktop 안 쓰고 Docker Engine만 깔아도 된다. Desktop은 GUI가 주된 장점인데, Linux에서 Docker 쓸 정도면 CLI가 더 편한 경우가 많으니까. 근데 요즘 Docker Desktop에 Kubernetes 원클릭 활성화, Extensions 같은 기능이 있어서 쓰는 사람도 꽤 있다.
핵심 개념 정리 — 이것만 알면 된다
이미지 vs 컨테이너
이게 처음에 제일 헷갈리는 건데, 비유하면 이렇다.
- 이미지 = 붕어빵 틀. 설계도. 실행에 필요한 모든 게 들어있는 스냅샷.
- 컨테이너 = 그 틀로 찍어낸 붕어빵. 실제로 돌아가는 인스턴스.
하나의 이미지로 컨테이너를 여러 개 만들 수 있다. Node.js 이미지 하나로 컨테이너 10개 띄울 수 있는 거다.
# 이미지 다운로드
docker pull nginx
# 이미지로 컨테이너 생성 + 실행
docker run -d -p 8080:80 nginx
# 같은 이미지로 하나 더
docker run -d -p 8081:80 nginx
볼륨 (Volume)
컨테이너는 기본적으로 일회용이다. 컨테이너 지우면 안의 데이터도 날아간다. DB 컨테이너 재시작했는데 데이터가 다 사라져서 멘탈 나간 적이 있다면 (나도 그랬다), 볼륨을 안 붙여서 그런 거다.
# 볼륨 생성
docker volume create mydata
# 볼륨 붙여서 컨테이너 실행
docker run -d \
-v mydata:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
mysql:8
이렇게 하면 컨테이너를 지우고 다시 만들어도 데이터가 유지된다.
네트워크
컨테이너끼리 통신하려면 같은 네트워크에 있어야 한다. docker compose를 쓰면 자동으로 해주지만, 수동으로 할 때는 이렇게 한다.
# 네트워크 생성
docker network create mynet
# 같은 네트워크에 컨테이너 두 개 띄우기
docker run -d --name db --network mynet mysql:8
docker run -d --name app --network mynet node:20
같은 네트워크 안에서는 컨테이너 이름으로 서로 접근할 수 있다. app 컨테이너에서 db:3306으로 접속하면 된다. IP 주소 외울 필요 없다.
Dockerfile — 이미지 만드는 레시피
남이 만든 이미지만 쓸 수는 없다. 내 앱을 이미지로 만들려면 Dockerfile을 작성해야 한다.
기본 구조
# 베이스 이미지 선택
FROM node:20-alpine
# 작업 디렉토리 설정
WORKDIR /app
# 의존성 파일 먼저 복사 (캐시 활용)
COPY package*.json ./
# 의존성 설치
RUN npm ci --production
# 소스코드 복사
COPY . .
# 포트 명시 (문서화 목적, 실제 열리지는 않음)
EXPOSE 3000
# 실행 명령
CMD ["node", "server.js"]
레이어 캐시 — 이거 모르면 빌드가 느리다
Dockerfile의 각 명령어는 하나의 레이어가 된다. 변경된 레이어부터 아래는 전부 다시 빌드된다. 그래서 자주 바뀌는 것은 아래에, 안 바뀌는 것은 위에 두는 게 중요하다.
# ❌ 나쁜 예 — 소스 바꿀 때마다 npm install도 다시 함
COPY . .
RUN npm ci
# ✅ 좋은 예 — package.json 안 바뀌면 npm install 캐시 적중
COPY package*.json ./
RUN npm ci
COPY . .
이 차이를 모르면 소스 한 줄 고칠 때마다 npm ci가 돌아간다. 프로젝트 크면 빌드에 몇 분씩 잡아먹는다.
멀티스테이지 빌드 — 이미지 크기 줄이기
프론트엔드 프로젝트에서 특히 유용하다.
# 1단계: 빌드
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 2단계: 실행 (빌드 결과물만 가져옴)
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
빌드에 필요한 Node.js, node_modules 같은 건 최종 이미지에 안 들어간다. 이미지 크기가 1GB에서 50MB로 줄어드는 경우도 있다.
Docker Compose — 여러 컨테이너 한 방에 관리
실무에서는 앱 하나에 컨테이너 여러 개를 쓰는 경우가 대부분이다. 웹서버, DB, 캐시, 큐 워커... 이걸 하나씩 docker run으로 띄우면 미친다. Docker Compose가 이걸 해결해준다.
실전 예제 — 웹 앱 + DB + Redis
# docker-compose.yml (또는 compose.yaml)
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
volumes:
- .:/app
- /app/node_modules
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:
# 전부 띄우기
docker compose up -d
# 로그 보기
docker compose logs -f app
# 전부 내리기
docker compose down
# 볼륨까지 삭제 (DB 데이터도 날아감!)
docker compose down -v
depends_on과 healthcheck
depends_on만 쓰면 컨테이너 시작 순서만 보장하지, 실제로 서비스가 준비됐는지는 확인 안 한다. DB 컨테이너가 뜨긴 했는데 아직 커넥션 안 받는 상태에서 앱이 접속 시도하면 에러가 뜬다.
위 예제처럼 healthcheck + condition: service_healthy 조합을 쓰면, DB가 진짜 준비된 다음에 앱이 뜬다. 이거 안 하면 앱 시작할 때 DB 접속 실패로 크래시 나는 일이 생긴다.
자주 쓰는 Docker 명령어
매일 치는 것들 위주로 정리.
컨테이너 관리
# 실행 중인 컨테이너 목록
docker ps
# 전체 (중지된 것 포함)
docker ps -a
# 컨테이너 중지
docker stop <컨테이너ID 또는 이름>
# 컨테이너 삭제
docker rm <컨테이너ID>
# 중지된 컨테이너 일괄 삭제
docker container prune
# 컨테이너 안에 들어가기 (디버깅할 때 필수)
docker exec -it <컨테이너> /bin/sh
# 또는
docker exec -it <컨테이너> bash
docker exec -it은 정말 많이 쓴다. 컨테이너 안에서 뭐가 어떻게 돌아가는지 직접 확인하고 싶을 때, DB에 직접 접속하고 싶을 때, 로그 파일 확인하고 싶을 때.
이미지 관리
# 로컬 이미지 목록
docker images
# 이미지 삭제
docker rmi <이미지ID>
# 사용 안 하는 이미지 일괄 삭제
docker image prune
# 빌드
docker build -t myapp:latest .
# 태그 붙이기
docker tag myapp:latest myregistry.com/myapp:v1.0
디스크 정리 — 이거 안 하면 디스크 꽉 찬다
Docker 오래 쓰면 이미지, 컨테이너, 볼륨, 빌드 캐시가 쌓여서 디스크를 엄청 먹는다. 나는 한 번은 맥북 용량 50GB가 Docker한테 먹혀있던 적도 있다.
# Docker가 먹고 있는 디스크 확인
docker system df
# 안 쓰는 것 전부 정리 (컨테이너, 네트워크, 이미지, 빌드 캐시)
docker system prune
# 볼륨까지 정리 (주의! 데이터 날아감)
docker system prune --volumes
# 진짜 다 날리기 (핵폭탄 옵션)
docker system prune -a --volumes
⚠️
prune -a는 사용 중이 아닌 모든 이미지를 삭제한다. 다시 pull 받아야 하니까 네트워크 느린 환경에서는 신중하게.
Docker Desktop GUI 활용
CLI만 써도 되지만, Docker Desktop GUI가 은근 편한 것들이 있다.
Containers 탭
실행 중인 컨테이너 목록, 로그, 터미널 접속, 환경 변수 확인을 한 화면에서 할 수 있다. docker logs + docker exec + docker inspect를 GUI로 하는 거라고 보면 된다. 로그를 실시간으로 보면서 필터링하는 건 CLI보다 GUI가 편하다.
Images 탭
로컬 이미지 관리. 이미지 크기 확인하고, 안 쓰는 이미지 정리할 때 좋다. 각 이미지의 레이어 구조도 볼 수 있는데, Dockerfile 최적화할 때 참고가 된다.
Volumes 탭
볼륨 목록이랑 사용량 확인. 어떤 볼륨이 어떤 컨테이너에 붙어있는지 한눈에 보인다.
Dev Environments (개발 환경)
Git 레포 URL 넣으면 자동으로 개발 환경을 만들어주는 기능인데, 솔직히 나는 잘 안 쓴다. docker compose가 더 익숙해서. 근데 팀에 Docker 처음 접하는 사람 있으면 온보딩할 때 괜찮긴 하다.
Kubernetes 원클릭
Settings > Kubernetes > Enable Kubernetes 체크하면 끝. 로컬에서 K8s 환경을 바로 쓸 수 있다. minikube 같은 거 따로 설치 안 해도 된다. K8s 학습하거나 매니페스트 테스트할 때 편하다.
실무 팁 모음
.dockerignore 필수
.gitignore처럼 빌드 컨텍스트에서 제외할 파일을 지정한다. 이거 안 하면 node_modules나 .git 폴더가 이미지에 들어가서 이미지가 쓸데없이 커진다.
# .dockerignore
node_modules
.git
.env
.env.local
*.log
dist
.DS_Store환경변수 관리
# docker-compose.yml에서 .env 파일 사용
services:
app:
image: myapp
env_file:
- .env
# .env
DATABASE_URL=postgresql://user:pass@db:5432/myapp
JWT_SECRET=my-super-secret-key
REDIS_URL=redis://cache:6379
.env 파일은 .gitignore에 넣고, .env.example만 커밋하자. 비밀번호가 깃 히스토리에 남으면 골치 아프다.
로그 관리
컨테이너 로그가 무한으로 쌓이면 디스크를 다 먹는다. 운영 환경이면 로그 드라이버를 설정하자.
services:
app:
image: myapp
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
이러면 로그 파일이 10MB씩 최대 3개까지만 유지된다.
빌드 속도 올리기
# BuildKit 활성화 (Docker Desktop은 기본 활성화)
# 환경변수로도 강제 가능
# DOCKER_BUILDKIT=1 docker build .
# 캐시 마운트로 패키지 매니저 캐시 재활용
RUN --mount=type=cache,target=/root/.npm \
npm ci --production
BuildKit의 캐시 마운트를 쓰면 npm ci나 pip install 같은 패키지 설치가 캐시 덕분에 빨라진다.
흔한 트러블슈팅
포트 충돌
Error: Bind for 0.0.0.0:3000 failed: port is already allocated로컬에서 이미 3000번 포트를 쓰고 있는 거다. 다른 컨테이너가 쓰고 있거나, 로컬에 직접 띄운 앱이 있거나.
# 어떤 프로세스가 포트 쓰는지 확인 (macOS/Linux)
lsof -i :3000
# 다른 포트로 매핑
docker run -p 3001:3000 myapp
권한 문제 (Linux)
Permission deniedLinux에서 Docker 명령어에 sudo를 붙여야 하는 경우. docker 그룹에 사용자를 추가하면 해결된다.
sudo usermod -aG docker $USER
# 로그아웃 후 다시 로그인
컨테이너가 바로 종료됨
# 로그 확인
docker logs <컨테이너ID>
# 대화형으로 띄워서 디버깅
docker run -it myapp /bin/sh
CMD나 ENTRYPOINT가 잘못됐거나, 앱이 크래시 나는 경우가 대부분이다. 로그를 먼저 보자.
Docker Desktop이 느릴 때
macOS에서 Docker Desktop이 CPU나 메모리를 너무 잡아먹으면 Settings > Resources에서 조절할 수 있다.
기본 추천 설정:
- CPUs: 전체 코어의 절반
- Memory: 전체 RAM의 25~50%
- Disk image size: 필요한 만큼 (기본 60GB)특히 파일 I/O가 느린 문제가 있다면 (macOS에서 흔함), docker-compose.yml에서 볼륨 마운트에 캐시 옵션을 추가해볼 수 있다.
volumes:
- .:/app:cached
Docker Desktop 라이선스 얘기
이건 한번 짚고 넘어가야 한다. 2021년부터 Docker Desktop은 대기업(직원 250명 이상 또는 연매출 $10M 이상)에서 유료다. 개인, 소규모 기업, 교육, 오픈소스 프로젝트는 무료.
회사에서 쓰는데 라이선스가 걸린다면 대안도 있다.
- Rancher Desktop — 무료, K8s 통합이 강점
- Podman Desktop — Red Hat에서 만든 거, Docker CLI 호환
- Colima — macOS에서 CLI 기반으로 Docker 돌리기
근데 솔직히 Docker Desktop의 편의성을 따라오는 건 아직 없다고 느낀다. 라이선스 문제가 없다면 Docker Desktop이 제일 낫다.
마무리
Docker Desktop은 단순히 Docker를 GUI로 쓰는 것 이상이다. 로컬 개발 환경 통일, Kubernetes 테스트, 빠른 프로토타이핑까지. 한번 익숙해지면 없이 개발하는 게 오히려 불편해진다.
핵심 정리:
- 이미지는 설계도, 컨테이너는 실행 인스턴스. 이 구분만 확실히 하자
- Dockerfile은 레이어 캐시를 고려해서 작성하자 (자주 바뀌는 건 아래에)
- Docker Compose는 실무에서 거의 필수다. 단일 컨테이너만 쓸 일은 별로 없다
- 볼륨 안 붙이면 데이터 날아간다. DB는 반드시 볼륨 설정하자
docker system prune으로 주기적으로 디스크 정리하자.dockerignore랑 멀티스테이지 빌드로 이미지 크기를 줄이자
다음에는 Docker + CI/CD 파이프라인 구성이나, 프로덕션 배포 전략(Blue-Green, Rolling Update) 같은 주제도 다뤄볼 생각이다.
'기타' 카테고리의 다른 글
| .htaccess로 특정 URL 차단하는 방법 — 서버 털리기 전에 읽어야 할 글 (0) | 2026.05.18 |
|---|---|
| Docker + CI/CD 파이프라인 구성과 프로덕션 배포 전략 — 삽질 끝에 정리한 실전 가이드 (0) | 2026.05.18 |