도커를 개발하는 프로젝트에 적용해보기위해 이론적인 개념들을 선행학습 하였습니다.
이 글은 이러한 학습을 정리한 글입니다.
1. 왜 도커인가
개발 환경과 운영 환경 사이의 불일치 오류는 프로젝트에서 빈번하게 발생합니다.
내 로컬에서는 특정 버전 라이브러리가 설치되어 있는데, 서버에는 다른 버전이 설치되어 있을 경우 런타임 오류가 생길 수 있습니다.
이런 문제를 완화하기 위해 VM을 사용하는 방법이 있지만
VM은 운영체제 전체를 복제하기 때문에 시작 속도가 느리고 자원 소모가 큽니다.
이런 맥락에서 리눅스 커널이 제공하는 네임스페이스 와 컨트롤 그룹기반의 OS 수준 격리가 주목받았고
Docker는 이 기술 위에 이미지 / 컨테이너 개념과 UX를 더해 실제로 사용하는 플랫폼으로 탄생했습니다.
Docker는 동일한 환경을 여러 시스템에서 표준화된 이미지로 재현할 수 있게 하고
배포는 단순히 이미지를 전달하고 컨테이너로 실행하는 방식으로 단순해집니다.
결과적으로 도커를 사용하면 환경 불일치로 인한 오류를 줄이고 배포 속도와 비용을 개선할 수 있습니다.
실무 팁
처음부터 전면 도커화하기보다는
환경 오류가 자주 일어나는 부분부터 도커 컨테이너로 감싸는 방식으로 점진적으로 도입하는 것이 효과적입니다.
또한 서비스별로 도커를 적용해 보고 VM과 컨테이너를 섞어 운영하는 전략도 고려해볼 만합니다.
2. 이미지, 컨테이너, 레이어와 캐시의 의미
Docker에서는 이미지와 컨테이너를 구분해서 사용합니다.
이미지는 읽기 전용 계층들의 조합이며, 컨테이너는 그 이미지를 기반으로 읽기/쓰기 계층을 덧붙인 실행 환경입니다.
이 방식 덕분에 이미지는 불변하게 유지되고 컨테이너 내에서의 변화는 독립적으로 남습니다.
이미지를 구성하는 각 레이어는 Dockerfile의 지시어(RUN, COPY, ADD 등)마다 생성됩니다.
빌드 시 이전 레이어가 변경되지 않았으면 캐시를 재사용할 수 있으므로, 이미지 빌드 속도가 상당히 빨라집니다.
따라서 Dockerfile을 작성할 때는 자주 바뀌는 부분은 아래쪽, 안 바뀌는 부분은 위쪽에 배치해두는 것이 좋습니다.
배포 측면에서는 태그와 다이제스트개념이 중요합니다.
태그는 사람이 읽는 버전을 의미하고 다이제스트는 이미지 내용을 해시한 불변 식별자입니다.
태그만 사용하는 경우
“같은 태그지만 내부 내용이 바뀌었다면?”이라는 위험이 생기므로
다이제스트를 함께 고정해서 재현 가능 배포를 만드는 것이 좋습니다.
실무 팁
.dockerignore 파일을 잘 관리해 빌드 컨텍스트를 최소화하면 전송/빌드 비용을 줄일 수 있습니다.
또한 멀티스테이지 빌드를 활용하면 빌드를 위한 도구들을 최종 이미지에서 제거해 이미지 크기를 줄일 수 있습니다.
베이스 이미지는 슬림한 버전, 보안 패치가 최신인 버전을 선택하는 것이 좋습니다.
3. 커널 수준 격리: 네임스페이스와 cgroups
Docker의 핵심은 리눅스 커널 기능을 활용한 격리와 제한입니다.
네임스페이스는 프로세스가 볼 수 있는 자원을 격리하고
cgroups는 자원 사용량을 제한하거나 계측하는 기능을 제공합니다.
네임스페이스의 예로,
PID 네임스페이스는 컨테이너 내부 프로세스가 PID 1부터 시작하게 만들고
네트워크 네임스페이스는 각 컨테이너가 독립된 네트워크 스택과 인터페이스를 갖게 합니다.
그리고 마운트 네임스페이스는 컨테이너만의 파일 시스템 뷰를 제공합니다.
이렇게 격리하면 컨테이너끼리는 서로 다른 리소스를 보지 못하고 독립적으로 동작합니다.
cgroups는 CPU, 메모리, I/O 등 자원 사용량을 제한하면서
컨테이너가 시스템 전체에 과도한 영향을 주는 것을 방지합니다.
도커는 컨테이너를 실행할 때 내부적으로 네임스페이스와 cgroups를 설정하여
격리와 자원 제어를 함께 적용합니다.
실무 팁
컨테이너 실행 시 --cpus, --memory 옵션을 설정해 리소스 상한을 두는 것이 기본입니다.
호스트 커널 버전이 cgroup v2를 지원하는지 확인해보는 것도 필요합니다.
권한 분리를 위해 user namespace remapping 기능을 고려할 수 있습니다.
그리고 cgroup 사용량을 모니터링하는 지표를 수집해 병목 구간을 파악하는 것도 중요합니다.
4. 이미지 빌드 흐름: 컨텍스트, 지시어, 멀티스테이지
이미지를 빌드할 때 Docker는 전체 디렉토리를 빌드 컨텍스트로 전송합니다.
이때 .dockerignore 파일을 통해 제외할 파일들을 지정하면
클라이언트 → Docker 데몬 간 전송 비용을 줄일 수 있습니다.
Dockerfile의 각 명령은 레이어를 생성하며
이전 레이어가 변경되지 않았을 경우 캐시 재사용이 가능해집니다.
이 덕분에 전체 이미지를 매번 다시 빌드할 필요 없이 변경된 부분만 재빌드할 수 있습니다.
멀티스테이지 빌드는 가장 강력한 최적화 수단입니다.
첫 스테이지에서 컴파일이나 빌드 툴을 사용하고
두 번째 스테이지에서는 실행 파일만 복사해서 불필요한 파일을 제거한 작은 이미지를 만들 수 있습니다.
이 방식으로 최종 이미지를 최소화하고 보안성을 높일 수 있습니다.
실무 팁
명령을 가능하면 합쳐서 레이어 수를 줄이는 것이 좋습니다.
멀티스테이지를 활용해 빌드 도구를 최종 이미지에서 제거하면 크기가 크게 줄어듭니다.
이미지 내 불필요한 패키지나 파일이 있는지 점검을 주기적으로 해야 합니다.
5. 실행 흐름: 프로세스 / 네트워크 / 데이터 / 로그
컨테이너는 create → start → run → stop → remove 순서로 수명 주기를 가집니다.
이때 애플리케이션 로그는 stdout / stderr 로 출력되며
외부 로깅 시스템이 이를 수집합니다.
네트워크는 기본적으로 bridge 드라이버를 사용하며
호스트와 포트를 매핑하는 방식(-p)으로 외부 접근을 허용합니다.
Compose 환경에서는 서비스 이름으로 DNS 통신도 가능합니다.
컨테이너 내부 파일 시스템은 읽기 전용 이미지 레이어 + 쓰기 계층 구조이며,
컨테이너 제거 시 변경된 데이터는 사라집니다.
상태 있는 서비스(DB, 파일 저장소 등)는 볼륨(volume) 또는 bind mount를 사용해 데이터를 영속화해야 합니다.
실무 팁
상태 저장 서비스는 반드시 볼륨으로 저장해야 데이터가 사라지지 않습니다.
애플리케이션 로그는 stdout / stderr 위주로 출력하고 파일 기반 로깅보다는 중앙 로깅 시스템을 사용하세요.
그리고 네트워크 포트는 최소한만 노출하고, 내부 서비스는 내부 네트워크 통신을 활용하세요.
컨테이너 재시작 정책과 헬스체크를 설정해서 자동 복구 구조를 만드는 것이 좋습니다.
이어서 다음 글에서는 도커 컴포즈를 활용한 프로젝트 환경 세팅을 한 내용을 다뤄보겠습니다.
긴 글 읽어주셔서 감사합니다.
'프로젝트 > 웹 성능 테스트' 카테고리의 다른 글
| DB 마이그레이션 (0) | 2025.10.25 |
|---|---|
| Git Flow 전략 (1) | 2025.10.24 |
| 도커: 이론2(용어 정리) (0) | 2025.10.18 |
| Docker 적용: 실습 (0) | 2025.10.12 |
| GHCR로 백엔드 도커 이미지 자동 빌드 & 푸시 (2) | 2025.09.07 |