일반적으로 개발은 각 개발자의 개인 PC(주로 Windows) 에서 진행되고, 개발이 완료되면 다음 단계를 거친다.
하지만 이 과정에서 개발 PC의 환경과 실제 서버 환경이 서로 다르기 때문에 다음과 같은 문제가 자주 발생한다.

이런 환경 차이로 인해 배포 과정에서 많은 장애와 시간이 발생하게 된다.
삼성이나 네이버가 배포를 해도 많은 문제가 발생할 것이고, 야근을 하게 된다.
일본에서는 배포 테스트만 한달씩 한다고도 한다.
개발 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

■ 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/
개발 작업은 윈도우에서 진행하더라도, 서버 운영을 위해서는 리눅스 환경에 익숙하고 vim이나 nano와 같은 터미널 기반 편집기로 파일을 직접 편집할 수 있어야 한다.
| 명령어 | 설명 |
|---|---|
ssh -i my-key.pem ec2-user@<EC2_PUBLIC_IP> | EC2 SSH 접속 |
mkdir app | 디렉토리 생성 |
cd app | 디렉토리 이동 |
ls -al | 현재 디렉토리 상세 목록 출력 |
java -jar demo.jar | JAR 실행 |
nohup java -jar demo.jar > app.log 2>&1 & | 백그라운드 실행 (서버 종료해도 계속 동작) |
tail -f app.log | 로그 실시간 모니터링 |
ps -ef \| grep java | 실행 중인 프로세스 확인 |
kill -9 <PID> | 특정 프로세스 강제 종료 |
| 명령어 | 설명 |
|---|---|
sudo apt update | 저장소의 패키지 목록 업데이트 (실제 설치 없음) |
sudo apt upgrade | 기존 설치된 패키지를 최신 버전으로 업그레이드 |
sudo apt full-upgrade | 필요한 패키지 제거/변경 포함 전체 업그레이드 |
sudo apt dist-upgrade | full-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



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