시작하기 전에
그동안 프로젝트를 진행하며 docker-compose.yml은 AI를 사용해서 작성하거나 인프라 팀에게 받아쓰기만 했다. 인프라쪽을 잘 몰랐기 때문에, 팀을 만들 때도 이런 부분을 채워줄 수 있는 팀원을 구했는데 다시 생각해보면 인프라를 하기가 무서웠다. 멋진 비즈니스 로직을 작성하는게 아닌 데이터베이스 백업, 스키마 변경, 서버 세팅 등은 잘하면 안정적으로 서비스가 돌아가지만 실수하면 서비스 장애로 이어지는 부담스러운 작업이기 때문이다. (다시 생각해보면 그만큼 중요한 작업이다.)
하지만 개인이 하고 싶은 일 보다 팀에 도움이 되는 개발을 해야 한다고 생각한다. 따라서, SSAFY의 공식 일정이 끝난 지금부터 DevOps에 대한 공부와 팀을 위한 플랫폼 엔지니어링을 하기 위한 공부를 시작하려고 한다.
1. Docker
Docker(도커)는 애플리케이션을 구축, 테스트 및 배포할 수 있는 컨테이너 기반의 오픈 소스 가상화 플랫폼이다.
쉽게 말해, 소프트웨어가 동작하는 데 필요한 모든 실행 환경(코드, 런타임, 시스템 도구, 라이브러리 등)을 '컨테이너'라는 표준화된 단위로 패키징하여, 어떤 환경에서든 동일하게 실행되도록 보장해 주는 도구이다.
"제 컴퓨터에선 돌아갔는데, 서버에선 안돼요..."라는 문제를 해결하기 위한 도구라고 생각하자.
컨테이너(Docker) vs 가상머신
흔한 질문이다. "컨테이너와 가상머신의 차이가 뭔가요?"

왼쪽이 가상머신이고 오른쪽은 도커의 컨테이너이다. 가상머신은 OS를 포함하고 있고, 컨테이너에는 OS가 없다는 사실을 알 수 있다. 이 둘은 가상화 방식에 가장 큰 차이가 있다. 가상머신은 하드웨어를 가상화하고, 컨테이너는 운영체제 수준에서 가상화한다.
💡가상화란?
가상화는 물리적인 하드웨어 자원을 논리적인 객체로 추상화는 기술이다. 말이 너무 어렵다. 조금 더 쉽게 설명하면 물리적으로 하나인 컴퓨터 자원(CPU, Memory, Disk 등)을 소프트웨어를 통해 논리적으로 쪼개거나 합쳐서, 마치 여러 개의 자원이 있는 것 처럼 혹은 하나의 큰 자원이 있는 것 처럼 보이게 만드는 기술이다.
💡가상화는 왜 필요할까?
1. 자원의 효율적 사용가상화가 없던 시절에는 서버 한 대에 OS를 설치해서 사용했다. 성능이 좋은 서버를 샀다면, 해당 자원을 모두 사용하지 않는 '유휴 자원'이 발생한다. 이를 해결하기 위해 필요하다.
2. 환경 격리 및 보안한 컴퓨터에서 여러 프로그램들 돌리고 있다고 가정해보자. 그 중 하나의 프로그램이 잘못돼어 OS가 다운된다면 다른 중요한 프로그램도 마비된다. 위험한 작업이나 서로 충돌할 수 있는 프로그램은 아예 '가상머신/컨테이너'를 따로 써서 실행하면 안전하다.
가상머신은 각각 완전한 OS를 설치해야 해서 무겁고 시작 속도가 느리다. 컴퓨터 안에 또 다른 컴퓨터를 만드는 것과 비슷하다.
반면 컨테이너는 호스트 OS의 커널을 공유하여 훨씬 가볍고 빠르다.
격리 수준에서도 차이가 있다. 가상머신은 완전히 독립적이어서 보안이 강력하지만, 컨테이너는 커널을 공유하므로 상대적으로 격리 수준이 낮다. 대신 컨테이너는 이미지 기반으로 어디서나 동일하게 실행되어 이식성이 뛰어나다.
| 비교 항목 | 높은 격리 수준 (가상머신, VM) | 낮은 격리 수준 (컨테이너) |
|---|---|---|
| 핵심 특징 | 하드웨어 전체를 가상화 (Guest OS 포함) | 운영체제 커널을 공유 (프로세스 격리) |
| 보안성 (장점) | 매우 강력함. 커널이 분리되어 있어 해킹 시에도 옆방(다른 VM)이나 집주인(Host)에게 피해가 거의 안 감. |
기본적인 격리 제공. cgroups, namespaces로 분리되나, 커널 공유로 인해 취약점 발생 시 전체 시스템 위협 가능. |
| 성능/속도 (장점) | 상대적 느림. OS 부팅 과정이 필요해 시작에 수 분 소요. 오버헤드(Overhead)가 큼. |
매우 빠름. OS 부팅 없이 프로세스만 실행하면 되므로 수 초 내 실행. 네이티브에 가까운 성능. |
| 자원 효율성 | 낮음. 사용하지 않아도 미리 할당된 자원(RAM 등)을 점유함. |
높음. 필요한 만큼만 자원을 쓰고, 여러 컨테이너를 고밀도로 실행 가능. |
| OS 호환성 | 자유로움. Linux 위에 Windows, Windows 위에 Linux 등 서로 다른 OS 실행 가능. |
제한적. Host OS의 커널을 공유하므로, Linux Host에서는 Linux 컨테이너만 구동 가능 (기본적으로). |
| 파일 크기 | 무거움 (GB 단위). OS 전체 파일이 포함됨. |
가벼움 (MB 단위). 애플리케이션과 라이브러리만 포함됨. |
| 주요 용도 | 서로 완전히 다른 환경이 필요하거나, 강력한 보안이 요구되는 레거시 시스템. | MSA(마이크로서비스), CI/CD, 빠른 배포와 확장이 필요한 웹 서버 개발. |
2. Docker의 4대 구성 요소
Docker는 Dockerfile, Docker Image, Docker Container, Docker Volume로 구성되어 있다.
Dockerfile
애플리케이션을 어떻게 패키징할지 정의한 설정 파일이다. "어떤 OS를 베이스로 쓸지", "어떤 언어를 설치할지", "어떤 포트를 열지" 등의 과정을 스크립트로 작성한다.
Docker Image
Dockerfile을 빌드하여 만들어진 불변(Read-only)의 파일이다. 컨테이너를 실행하기 위한 모든 정보를 포함하고 있으며, 이 이미지만 있으면 어디서든 똑같은 환경을 만들 수 있다.
Docker Container
Docker Image를 실행한 상태이다. 이미지에 쓰기 가능한 레이어를 얹어 메모리에 올린 인스턴스로, 하나의 이미지로 여러 개의 독립된 컨테이너를 동시에 실행할 수 있다.
Docker Volume
Docker Volume은 컨테이너 내부의 특정 폴더를 호스트 컴퓨터의 저장 공간과 연결(마운트)하는 기술이다. 기본적으로 Docker Container는 '쓰고 버리는' 개념으로 설계되었다. 따라서, Container를 삭제하는 순간 Container 내부에서 생성된 데이터는 함께 삭제된다. 데이터베이스를 Container로 띄웠는데, 컨테이너를 재시작했더니 모든 데이터가 날아간다면 끔찍할 것이다. Volume은 이 문제를 해결해준다.
3. 직접 해보기
Dockerfile을 직접 생성해서 html을 띄워보고, docker-compose로 DB와 웹 서버를 한 번에 실행해보려고 한다.
목표
- Nginx를 실행하고 'Welcome to nginx!' 확인하기
- 1번의 내용 바꿔보기
- docker compose로 Nginx와 Mysql을 동시에 실행해보기
Nginx를 실행하기
도커에서 Nginx를 실행하는 것은 매우 쉬웠다. 누군가 만들어 놓은 이미지를 다음과 같은 명령어로 간단하게 실행할 수 있었다.
docker run -d -p 8080:80 nginx
- `docker run`: 이미지를 기반으로 컨테이너를 생성하고 시작하라는 명령어
- `-d`: Detached Mode(분리 모드), 컨테이너를 백그라운드에서 실행
- `-p 8080:80`: 포트 포워딩 설정, `[호스트 포트]:[컨테이너 포트]`
- 호스트의 해당 포트로 들어오는 모든 요청을 컨테이너 내부의 해당 포트로 연결하라는 명령어
- `nginx`: 실행할 이미지 이름


Nginx의 내용 바꾸기
'Welcome to nginx!'말고 다른 내용으로 바꾸는 법을 알고 싶었다. 바꿀 내용을 `index.html`파일로 만들었다. (`index.html` 파일명이 중요했다.)

그 다음 같은 폴더에 `Dockerfile`이라는 이름으로 파일을 만들었다.

- `FROM nginx:latest`: 베이스 이미지는 nginx를 쓰겠다
- `COPY index.html /usr/share/nginx/html/index.html`: 내가 만든 html 파일을 컨테이너 안의 해당 경로 파일 덮어씌워라
이 바꾼 `index.html`을 또 다른 이미지로 만들어보자.
docker build -t my-custom-nginx .
- `-t my-custom-nginx`: 이미지 이름을 `my-custom-nginx`라고 짓겠다.
내가 만든 Dockerfile을 Docker image로 만든 것이다.


8080포트는 이미 사용하고 있어서 8081포트를 사용했다.
이번엔 기존 컨테이너를 지우고, 그리고 다음과 같은 명령어를 실행했다.
docker run -d -p 8081:80 -v .:/usr/share/nginx/html nginx
그리고 `index.html`의 값을 바꾸고 저장했다.



실시간으로 내가 변경한 값으로 바뀐 것을 확인할 수 있었다. 왜 바뀔 수 있는걸까? 내가 앞서 설정했던 `COPY index.html /usr/share/nginx/html/index.html`는 `my-custom-nginx` 이미지에만 설정되어 있고 nginx에는 설정되지 않았을 건데 말이다.
그 이유는 `-v`에 있다. ` -v .:/usr/share/nginx/html`를 해석해보자.
- `-v`(Volume): 저장소에 연결
- `.`(Host 경로):
- "내 컴퓨터의 현재 터미널이 위치한 폴더"를 의미
- `:`(구분자): 연결할 경로
- `/usr/share/nginx/html`(Container 경로):
- Nginx가 기본적으로 `index.html` 파일을 찾아서 보여주는 약속된 내부 폴더
원래 Nginx 이미지 안에는 'Welcome to nginx!'라고 적힌 기본 index.html 파일이 들어있다. 하지만 위 명령어를 치는 순간, 컨테이너 내부의 그 폴더가 내 컴퓨터의 현재 폴더로 덮어씌워진다. Docker는 컨테이너를 재시작할 필요 없이, 브라우저에서 새로고침만 하면 바로 반영이 된다.
💡 사실 `my-custom-nginx`를 실행하고 `index.html`을 바꾸어도 반영되지 않는다. 이는 `COPY`와 `Volume(-v)`의 결정적인 차이다. `COPY`는 이미지를 빌드하는 순간에 파일을 복사해서 이미지에 넣는 것이기 때문에 스냅샷의 개념이고, `Volume(-v)`은 내 폴더를 직접 컨테이너에 연결하기 때문에 실시간으로 변경이 가능하다.
docker compose로 Nginx와 Mysql을 동시에 실행해보기
docker compose는 여러 개의 컨테이너를 정의하고 실행하기 위한 도구이다. docker compose가 없었다면, 앞에서 했던 `docker run`을 매번 하나씩 해야한다. docker compose는 이런 과정을 `docker-compose.yml`이라는 파일에 적어두고, 한 번의 명령어로 실행한다.
docker-compose up -d



docker compose로 nginx와 mysql이 잘 올라가 있는걸 확인할 수 있었고, mysql workbench와도 연결한 걸 확인할 수 있었다.
💡`-1`은 왜 붙을까?
docker compose는 기본적으로 컨테이너가 여러 개 늘어날 수도 있다고 가정하고 이름을 짓기 때문에, docker가 알아서 index를 붙힌다.
'직접 해보기'를 통해서 docker의 기본적인 구성 요소를 이해할 수 있었다. 특히 `COPY`와 `Volume(-v)`의 차이가 인상 깊었고, 앞으로 소스 코드를 수정하면 바로 반영되는 `Volume(-v)`을 잘 사용해 개발 생산성을 올려야겠다. 다음엔 이 Image들을 AWS EC2에 올려서 public하게 접근이 가능하도록 해볼 예정이다.
References
https://geekflare.com/devops/docker-vs-virtual-machine/
Docker vs Virtual Machine (VM) - Understanding the Differences
Learn the differences between the key features of Docker containers and virtual machines to make informed decisions for your infrastructure.
geekflare.com
'Infra' 카테고리의 다른 글
| [Infra] GitHub Actions와 Docker로 CI/CD 파이프라인 구축하기 (1) | 2025.12.24 |
|---|---|
| [Infra] AWS 클라우드 배포 (Docker에서 AWS EC2까지-2) (0) | 2025.12.09 |