도커 (Docker)

김소희·2025년 11월 12일

일반적으로 개발은 각 개발자의 개인 PC(주로 Windows) 에서 진행되고, 개발이 완료되면 다음 단계를 거친다.

  • 개발 → 테스트 → 웹서버에 배포 → 운영

하지만 이 과정에서 개발 PC의 환경과 실제 서버 환경이 서로 다르기 때문에 다음과 같은 문제가 자주 발생한다.

  • 라이브러리 버전 불일치
  • OS 환경 차이
  • 설정 파일 위치(경로), 권한 차이

이런 환경 차이로 인해 배포 과정에서 많은 장애와 시간이 발생하게 된다.

삼성이나 네이버가 배포를 해도 많은 문제가 발생할 것이고, 야근을 하게 된다.
일본에서는 배포 테스트만 한달씩 한다고도 한다.


도커(Docker)의 등장 배경

개발 PC와 배포 서버가 동일한 환경을 사용하도록 보장한다.

도커는 개발자가 로컬에서 쓰는 환경 그대로를 이미지(Image) 로 만들어서
서버에서도 동일한 세팅을 가진 컨테이너(Container) 위에서 실행할 수 있게 하는 기술이다.
도커 허브에는 사람들이 올려둔 이미지들을 찾거나 직접 올릴 수도 있다.

도커의 볼륨(Volume)은 호스트 PC의 저장소(C드라이브, D드라이브 등)를 사용하는 기능으로, 컨테이너를 종료하거나 삭제하더라도 데이터가 사라지지 않도록 보존하기 위해 존재한다. 즉, 도커를 껐다 켜도 필요한 데이터가 그대로 남아 있도록 하는 영속적 저장소 역할을 한다.

도커 사용시 내 PC 와 서버에 도커가 설치되어 있는 상태에서 내 PC 의 개발환경이 세팅된 도커파일을 생성하여 (도커 이미지를 생성) 만들어진 git 또는 도커허브에 push 하고 서버PC 에서 해당하는 도커 이미지를 Pull 받아 도커 컨테이너를 생성하고 실행하면 개발 환경과 동일한 환경을 가지고 서비스를 수행할 수 있다.

실무에서는 CI/CD 파이프라인(Fastlane, GitHub Actions, Jenkins 등)과 함께 사용하여 자동 배포 환경을 구축한다.

컨테이너란?

Docker에서 컨테이너(Container) 는 중요한 개념이다.
컨테이너는 하나의 컴퓨터 환경 안에 독립적인 또 하나의 컴퓨터 환경을 구성하여,
각각 별도의 프로그램을 설치하고 실행할 수 있도록 해준다.
즉, 하나의 호스트(Host) 컴퓨터 안에 여러 개의 미니 컴퓨터를 만들어 두는 개념이라고 보면 된다.

특히 Docker에서는 이미지(Image) 라는 설치 패키지를 실행하면 그 결과물로 컨테이너가 생성된다.
이미지는 실행에 필요한 라이브러리, 설정 파일 등을 포함한 정적(Static) 객체이고,
이미지가 실행되면 동적(Dynamic) 객체인 컨테이너가 만들어지는 것이다.

  • 컨테이너(Container)의 독립성
    컨테이너는 ‘독립적인 컴퓨터 환경’이다.
  • 디스크 (저장 공간)
    각 컨테이너마다 서로 각자의 저장 공간을 가지고 있다.
    일반적으로 A 컨테이너 내부에서 B 컨테이너 내부에 있는 파일에 접근할 수 없다.
  • 네트워크 (IP, Port)
    각 컨테이너마다 고유의 네트워크를 가지고 있다. 컨테이너는 각자의 IP 주소를 가지고 있다

컨테이너에 저장되는 데이터는 컨테이너가 삭제되면 함께 사라질 수 있다.
그래서 필요한 경우 호스트 컴퓨터의 저장 공간을 컨테이너에 연결한 ‘볼륨(Volume)’을 사용한다.
볼륨을 사용하면 컨테이너가 변경되거나 삭제되어도 데이터를 안정적으로 유지할 수 있다.


이미지 생성 및 실행 예시

-- MySQL 컨테이너 실행 -> 자동으로 이미지 다운로드
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=1234 -p 3305:3306 mysql:8

-- 실행 중인 컨테이너 안으로 들어가서 MySQL 접속
docker exec -it mysql mysql -u root -p 

-- 나가기
exit

도커의 장점

환경 일관성

  • “로컬에서는 되는데 서버에서 안 돼요” 문제를 극적으로 줄인다.

확장 용이성(Scalability)

  • 동일한 이미지를 여러 개 복사해서
    여러 컨테이너를 수평 확장(Scale-out) 방식으로 띄울 수 있다.

완전한 격리(독립 환경)

  • 하나의 PC에서도 서로 완전히 독립된 여러 환경(컨테이너)을 동시에 운영 가능
  • 테스트, 개발, 운영 환경을 분리할 수 있어 MSA(Microservice Architecture) 구현에 매우 유리하다.

빠른 배포

  • 이미지만 있으면 서버 어디서든 동일하게 실행 가능
  • 새로운 버전 배포도 이미지 교체만으로 해결됨

도커 사용 흐름

  1. 로컬 PC와 서버 모두 도커가 설치되어 있어야 함
  2. 개발자가 Dockerfile 작성 → 이미지 생성
  3. 이미지를 GitHub 또는 Docker Hub에 push
  4. 서버에서 이미지를 pull
  5. 서버에서 컨테이너 실행 → 로컬과 동일한 환경으로 서비스 가능

도커 · JDK · 톰캣 설치

WSL(우분투) 설치
가이드: https://jincchan.tistory.com/33

Docker Desktop 설치
공식 다운로드: https://docs.docker.com/desktop/setup/install/windows-install/
설치 후 WSL에서 다음으로 확인:

docker version

JDK 설치 + Tomcat 설정(우분투)
가이드:
https://cafe.naver.com/f-e/cafes/31543511/articles/363

파일 전송(FileZilla)
Windows: https://filezilla-project.org/download.php?platform=win64
Mac: https://filezilla-project.org/download.php?platform=macos-arm64

SSH 클라이언트(PuTTY)
(필요한 경우만) https://www.putty.org/


EC2 관련 필수 명령어

개발 작업은 윈도우에서 진행하더라도, 서버 운영을 위해서는 리눅스 환경에 익숙하고 vim이나 nano와 같은 터미널 기반 편집기로 파일을 직접 편집할 수 있어야 한다.

EC2 접속 & 기본 리눅스 명령어

명령어설명
ssh -i my-key.pem ec2-user@<EC2_PUBLIC_IP>EC2 SSH 접속
mkdir app디렉토리 생성
cd app디렉토리 이동
ls -al현재 디렉토리 상세 목록 출력
java -jar demo.jarJAR 실행
nohup java -jar demo.jar > app.log 2>&1 &백그라운드 실행 (서버 종료해도 계속 동작)
tail -f app.log로그 실시간 모니터링
ps -ef \| grep java실행 중인 프로세스 확인
kill -9 <PID>특정 프로세스 강제 종료

APT 패키지 목록/업데이트 명령어

패키지 업데이트 관련

명령어설명
sudo apt update저장소의 패키지 목록 업데이트 (실제 설치 없음)
sudo apt upgrade기존 설치된 패키지를 최신 버전으로 업그레이드
sudo apt full-upgrade필요한 패키지 제거/변경 포함 전체 업그레이드
sudo apt dist-upgradefull-upgrade와 동일한 명령 (구버전 이름)
sudo apt autoremove더 이상 필요 없는 패키지 자동 제거
sudo apt autoclean오래된 패키지 캐시 삭제
sudo apt clean모든 캐시 파일 완전 삭제

패키지 설치/삭제 명령어

명령어설명
sudo apt install <패키지명>패키지 설치
sudo apt install <패키지1> <패키지2>여러 패키지 동시 설치
sudo apt install -y <패키지명>사용자 확인 없이 자동 설치
sudo apt remove <패키지명>패키지 삭제 (설정 파일 유지)
sudo apt purge <패키지명>패키지 + 설정 파일까지 삭제
sudo apt reinstall <패키지명>패키지 재설치

패키지 검색/정보 확인 명령어

명령어설명
apt search <키워드>패키지 검색
apt show <패키지명>패키지 상세 정보
dpkg -l설치된 모든 패키지 목록
dpkg -l \| grep <키워드>특정 키워드가 포함된 패키지 찾기
dpkg -s <패키지명>해당 패키지 상태 확인
dpkg -L <패키지명>패키지가 설치한 파일 목록 확인

실행 예시

C:\Users\KOSA>docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
266626526d42: Pull complete
52bc359bcbd7: Pull complete
e2f8e296d9df: Pull complete
d7ecded7702a: Pull complete
320b0949be89: Pull complete
d921c57c6a81: Pull complete
9def903993e4: Pull complete
Digest: sha256:1beed3ca46acebe9d3fb62e9067f03d05d5bfa97a00f30938a0a3580563272ad
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

C:\Users\KOSA>docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
nginx        latest    1beed3ca46ac   12 days ago   225MB

C:\Users\KOSA>docker run --name soheeserver -d -p 80:80 nginx
8b6276fb59daff2abc1d6f6f522e9f504eff8ba7fc291e78156f9081a762549b

C:\Users\KOSA>docker ps
CONTAINER ID   IMAGE     COMMAND                   CREATED              STATUS              PORTS                                 NAMES
8b6276fb59da   nginx     "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:80->80/tcp, [::]:80->80/tcp   soheeserver

C:\Users\KOSA>docker stop soheeserver
soheeserver

C:\Users\KOSA>docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
nginx        latest    1beed3ca46ac   12 days ago   225MB

C:\Users\KOSA>docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

C:\Users\KOSA>docker ps -a
CONTAINER ID   IMAGE     COMMAND                   CREATED         STATUS                          PORTS     NAMES
8b6276fb59da   nginx     "/docker-entrypoint.…"   5 minutes ago   Exited (0) About a minute ago             soheeserver

C:\Users\KOSA>docker rm 8b6
8b6

C:\Users\KOSA>docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

C:\Users\KOSA>docker image rm -f 1beed3ca46ac
Untagged: nginx:latest
Deleted: sha256:1beed3ca46acebe9d3fb62e9067f03d05d5bfa97a00f30938a0a3580563272ad

docker stop 결과 : 컨테이너 멈춤

docker rm 결과 : 컨테이너 삭제

docker image rm -f 결과 : 사용중인 이미지를 포함해서 전체 이미지 삭제

도커 - 레디스 실행예시

C:\Users\KOSA>docker run -d -p 6379:6379 redis
Unable to find image 'redis:latest' locally
latest: Pulling from library/redis
6c981fc7c621: Pull complete
5dec27664782: Pull complete
729797e636a7: Pull complete
4f4fb700ef54: Pull complete
2e60b18d70d8: Pull complete
1adabd6b0d6b: Pull complete
22506777a096: Pull complete
Digest: sha256:5c7c0445ed86918cb9efb96d95a6bfc03ed2059fe2c5f02b4d74f477ffe47915
Status: Downloaded newer image for redis:latest
542f1502ce0fb4fe63e78a82ce0cfb0285fad03d15b730bcac32c7dece8aa1aa

C:\Users\KOSA>docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
redis        latest    5c7c0445ed86   13 days ago   200MB

C:\Users\KOSA>docker ps
CONTAINER ID   IMAGE     COMMAND                   CREATED          STATUS          PORTS                                         NAMES
542f1502ce0f   redis     "docker-entrypoint.s…"   45 seconds ago   Up 44 seconds   0.0.0.0:6379->6379/tcp, [::]:6379->6379/tcp   romantic_aryabhata

C:\Users\KOSA>docker logs 542f
Starting Redis Server
1:C 17 Nov 2025 02:02:16.525 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 17 Nov 2025 02:02:16.525 * Redis version=8.2.3, bits=64, commit=00000000, modified=1, pid=1, just started
1:C 17 Nov 2025 02:02:16.525 * Configuration loaded
1:M 17 Nov 2025 02:02:16.525 * monotonic clock: POSIX clock_gettime
1:M 17 Nov 2025 02:02:16.526 * Running mode=standalone, port=6379.
1:M 17 Nov 2025 02:02:16.526 * <bf> RedisBloom version 8.2.8 (Git=unknown)
1:M 17 Nov 2025 02:02:16.526 * <bf> Registering configuration options: [
1:M 17 Nov 2025 02:02:16.526 * <bf>     { bf-error-rate       :      0.01 }
1:M 17 Nov 2025 02:02:16.526 * <bf>     { bf-initial-size     :       100 }
1:M 17 Nov 2025 02:02:16.526 * <bf>     { bf-expansion-factor :         2 }
1:M 17 Nov 2025 02:02:16.526 * <bf>     { cf-bucket-size      :         2 }
1:M 17 Nov 2025 02:02:16.526 * <bf>     { cf-initial-size     :      1024 }
1:M 17 Nov 2025 02:02:16.526 * <bf>     { cf-max-iterations   :        20 }
1:M 17 Nov 2025 02:02:16.526 * <bf>     { cf-expansion-factor :         1 }
1:M 17 Nov 2025 02:02:16.526 * <bf>     { cf-max-expansions   :        32 }
1:M 17 Nov 2025 02:02:16.526 * <bf> ]
1:M 17 Nov 2025 02:02:16.526 * Module 'bf' loaded from /usr/local/lib/redis/modules//redisbloom.so
1:M 17 Nov 2025 02:02:16.527 * <search> Redis version found by RedisSearch : 8.2.3 - oss
1:M 17 Nov 2025 02:02:16.527 * <search> RediSearch version 8.2.5 (Git=222ad3b)
1:M 17 Nov 2025 02:02:16.527 * <search> Low level api version 1 initialized successfully
1:M 17 Nov 2025 02:02:16.527 * <search> gc: ON, prefix min length: 2, min word length to stem: 4, prefix max expansions: 200, query timeout (ms): 500, timeout policy: return, cursor read size: 1000, cursor max idle (ms): 300000, max doctable size: 1000000, max number of search results:  1000000,
1:M 17 Nov 2025 02:02:16.527 * <search> Initialized thread pools!
1:M 17 Nov 2025 02:02:16.527 * <search> Disabled workers threadpool of size 0
1:M 17 Nov 2025 02:02:16.527 * <search> Subscribe to config changes
1:M 17 Nov 2025 02:02:16.527 * <search> Enabled role change notification
1:M 17 Nov 2025 02:02:16.527 * <search> Cluster configuration: AUTO partitions, type: 0, coordinator timeout: 0ms
1:M 17 Nov 2025 02:02:16.528 * <search> Register write commands
1:M 17 Nov 2025 02:02:16.528 * Module 'search' loaded from /usr/local/lib/redis/modules//redisearch.so
1:M 17 Nov 2025 02:02:16.528 * <timeseries> RedisTimeSeries version 80200, git_sha=1439d4a439ca9c063e6ef124a510abff09a5d493
1:M 17 Nov 2025 02:02:16.528 * <timeseries> Redis version found by RedisTimeSeries : 8.2.3 - oss
1:M 17 Nov 2025 02:02:16.528 * <timeseries> Registering configuration options: [
1:M 17 Nov 2025 02:02:16.528 * <timeseries>     { ts-compaction-policy   :              }
1:M 17 Nov 2025 02:02:16.528 * <timeseries>     { ts-num-threads         :            3 }
1:M 17 Nov 2025 02:02:16.528 * <timeseries>     { ts-retention-policy    :            0 }
1:M 17 Nov 2025 02:02:16.528 * <timeseries>     { ts-duplicate-policy    :        block }
1:M 17 Nov 2025 02:02:16.528 * <timeseries>     { ts-chunk-size-bytes    :         4096 }
1:M 17 Nov 2025 02:02:16.528 * <timeseries>     { ts-encoding            :   compressed }
1:M 17 Nov 2025 02:02:16.528 * <timeseries>     { ts-ignore-max-time-diff:            0 }
1:M 17 Nov 2025 02:02:16.528 * <timeseries>     { ts-ignore-max-val-diff :     0.000000 }
1:M 17 Nov 2025 02:02:16.528 * <timeseries> ]
1:M 17 Nov 2025 02:02:16.528 * <timeseries> Detected redis oss
1:M 17 Nov 2025 02:02:16.528 * <timeseries> Enabled diskless replication
1:M 17 Nov 2025 02:02:16.528 * Module 'timeseries' loaded from /usr/local/lib/redis/modules//redistimeseries.so
1:M 17 Nov 2025 02:02:16.528 * <ReJSON> Created new data type 'ReJSON-RL'
1:M 17 Nov 2025 02:02:16.528 * <ReJSON> version: 80201 git sha: unknown branch: unknown
1:M 17 Nov 2025 02:02:16.528 * <ReJSON> Exported RedisJSON_V1 API
1:M 17 Nov 2025 02:02:16.528 * <ReJSON> Exported RedisJSON_V2 API
1:M 17 Nov 2025 02:02:16.528 * <ReJSON> Exported RedisJSON_V3 API
1:M 17 Nov 2025 02:02:16.528 * <ReJSON> Exported RedisJSON_V4 API
1:M 17 Nov 2025 02:02:16.528 * <ReJSON> Exported RedisJSON_V5 API
1:M 17 Nov 2025 02:02:16.528 * <ReJSON> Enabled diskless replication
1:M 17 Nov 2025 02:02:16.528 * <ReJSON> Initialized shared string cache, thread safe: false.
1:M 17 Nov 2025 02:02:16.528 * Module 'ReJSON' loaded from /usr/local/lib/redis/modules//rejson.so
1:M 17 Nov 2025 02:02:16.528 * <search> Acquired RedisJSON_V5 API
1:M 17 Nov 2025 02:02:16.529 * Server initialized
1:M 17 Nov 2025 02:02:16.529 * Ready to accept connections tcp

C:\Users\KOSA>docker exec -it 542f bash
root@542f1502ce0f:/data# redis-cli
127.0.0.1:6379> set 1 kim
OK
127.0.0.1:6379> get 1
"kim"
127.0.0.1:6379> exit
root@542f1502ce0f:/data# exit
exit

참고자료

윈도우에 도커 데스크탑 설치
맥OS에 도커 설치

profile
개발자 소희의 노트

0개의 댓글