컨테이너화는 앱을 모든 의존성과 함께 격리된 단위로 패키징해 어떤 호스트에서도 동일하게 실행되도록 하는 방식입니다. 소프트웨어에서 가장 오래된 버그 리포트인 “내 컴퓨터에서는 잘 되는데”를 끝내는 방법으로, 코드와 함께 머신의 관련 부분까지 실어 보내되 어떤 인프라에서도 실행할 수 있을 만큼 표준화된 상자에 담습니다.
핵심 요약
| 질문 | 답변 |
|---|---|
| 무엇인가 | 앱 + 의존성을 하나의 이식 가능하고 격리된 단위로 묶은 것 |
| vs. 가상 머신 | VM은 하드웨어를 가상화(GB 단위, 분 단위); 컨테이너는 OS 커널을 공유(MB 단위, 밀리초 단위) |
| 내부 원리 | 커널 네임스페이스(격리) + cgroups(제한) + 계층형 파일시스템 |
| 이식성의 근거 | OCI 오픈 표준. 호환되는 엔진이라면 어떤 이미지든 실행 |
| 다음 단계 | 대규모에서는 오케스트레이션(Kubernetes), 그 위에는 서버리스 |
여섯 줄로 보는 핵심 아이디어
컨테이너 이미지는 평범한 빌드 파일로 정의됩니다. 아래 파일은 Node.js API 하나를 완전하게 패키징합니다.
FROM node:22-slim # 베이스 이미지에서 시작(캐시된 레이어)
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev # 명령 하나가 읽기 전용 레이어 하나를 추가
COPY . .
CMD ["node", "server.js"] # 컨테이너 시작 시 실행할 명령
$ docker build -t api:1.0 . # 이미지 빌드
$ docker run -p 8080:8080 api:1.0 # 실행. 어디서든 동일하게 동작
물론 클라이언트는 어떤 컨테이너가 응답하는지 알지도 못하고 신경 쓰지도 않습니다. 컨테이너화된(또는 완전 관리형) 백엔드를 안정적인 API 계약과 짝짓는 이유가 바로 이것입니다.
// JavaScript / Node.js — Back4app JS SDK
// Clients never know (or care) what container runs the backend
const query = new Parse.Query('Build');
query.equalTo('status', 'passing');
const builds = await query.find();
console.log(`${builds.length} green builds`); // Flutter / Dart — Back4app Flutter SDK
final query = QueryBuilder<ParseObject>(ParseObject('Build'))
..whereEqualTo('status', 'passing');
final response = await query.query();
if (response.success) {
print('${response.results?.length} green builds');
} // iOS / Swift — Back4app Swift SDK
let query = Build.query("status" == "passing")
query.find { result in
if case .success(let builds) = result {
print("\(builds.count) green builds")
}
} // Android / Kotlin — Back4app Android SDK
val query = ParseQuery.getQuery<ParseObject>("Build")
query.whereEqualTo("status", "passing")
query.findInBackground { builds, e ->
if (e == null) Log.d("CI", "${builds.size} green builds")
} 컨테이너 vs. 가상 머신
| 항목 | 가상 머신 | 컨테이너 |
|---|---|---|
| 가상화 대상 | 하드웨어(하이퍼바이저 경유) | 운영체제 |
| 커널 | VM마다 하나 | 호스트와 공유 |
| 크기 | 기가바이트 | 메가바이트 |
| 시작 시간 | 분 단위 | 밀리초–초 단위 |
| 호스트당 밀도 | 수십 개 | 수백 개 |
| 격리 강도 | 하드웨어 경계, 더 강함 | 커널 경계, 더 약함 |
| 적합한 경우 | 신뢰할 수 없는/멀티테넌트 워크로드, 완전한 OS가 필요한 경우 | 패키징, 밀도, CI/CD, 마이크로서비스 |
둘은 경쟁하는 것이 아니라 결합됩니다. 프로덕션 컨테이너의 압도적 다수는 VM 안에서 실행됩니다. VM은 클라우드 제공업체를 위해 테넌트를 격리하고, 컨테이너는 팀을 위해 앱을 패키징합니다. 신뢰할 수 없는 코드에는 마이크로 VM이 컨테이너에 가까운 시작 속도로 하드웨어 격리를 제공하며 그 중간을 메웁니다.
내부 원리를 쉽게 풀면
컨테이너는 특별한 커널 객체가 아닙니다. 커널이 제공하는 세 벌의 옷을 입은 평범한 프로세스입니다. 네임스페이스는 자체 프로세스 목록, 네트워크 스택, 마운트 테이블, 사용자 ID 등 세상을 보는 독립된 시야를 제공합니다. cgroups는 CPU, 메모리, I/O에 엄격한 상한을 두어 예산을 정합니다. 계층형 파일시스템은 이미지의 읽기 전용 레이어들과 그 위의 쓰기 가능 레이어 하나로 디스크를 조립합니다. 이미지 하나에서 띄운 컨테이너 열 개가 하나일 때보다 디스크를 거의 더 쓰지 않는 이유입니다. 도구를 걷어 내면 chroot에 리소스 제한을 더한 것이 언제나 뼈대였습니다. 2013년의 혁명은 그 능력을 누구나 빌드하고, 공유하고, 실행할 수 있는 이미지 형식으로 패키징한 데 있었고, 2015년부터는 OCI의 세 가지 명세(이미지, 런타임, 배포)가 이를 표준화해 컨테이너화가 어느 한 벤더의 것이 아님을 보장합니다. 계보는 chroot(1979년) → BSD jail과 OS zone(2000년대) → 커널 cgroups(2006년) → LXC(2008년) → 현대 컨테이너 시대(2013년)로 이어집니다.
일반적인 사용 사례
- 재현 가능한 환경. 개발, CI, 프로덕션이 같은 이미지를 실행하므로 환경 차이에서 비롯되는 버그가 원천적으로 사라집니다.
- CI/CD 파이프라인. 파이프라인에서 한 번 빌드한 이미지가 모든 단계로 승격되는 아티팩트입니다. 한 번 빌드해 어디서나 배포하는 일을 현실로 만든 것이 컨테이너입니다.
- 마이크로서비스. 서비스당 컨테이너 하나가 자연스러운 패키징 단위이며, 이후 서비스 집합의 관리는 오케스트레이션이 맡습니다.
- 레거시 애플리케이션 패키징. 의존성 스택이 취약한 오래된 앱을 이미지로 동결해 현대 인프라 위에서 안전하게 운영합니다.
- 밀도와 비용. VM이 수십 개 들어가는 호스트에 컨테이너는 수백 개가 들어갑니다. 이 빈 패킹(bin-packing)은 곧바로 더 적은 청구액으로 이어집니다.
컨테이너화를 선택해야 할까? 결정 매트릭스
| 컨테이너화를 선택해야 할 때… | 다른 길을 찾아야 할 때… |
|---|---|
| 환경 차이 때문에 계속 문제를 겪을 때 | 워크로드가 신뢰할 수 없는 멀티테넌트 코드일 때(VM/마이크로 VM 사용) |
| CI/CD 파이프라인으로 배포할 때 | 제품이 데스크톱 앱이나 커널 수준 소프트웨어일 때 |
| 서비스가 개발/프로덕션에서 동일하게 동작해야 할 때 | 팀이 프론트엔드 전담이고 백엔드는 표준적일 때 |
| 오케스트레이션으로 나아가고 있을 때 | 관리형 백엔드가 이미 요구를 충족해 패키징할 것이 없을 때 |
| 의존성이 복잡하거나 서로 충돌할 때 | 정적 바이너리 하나면 충분할 때(컨테이너가 더해 주는 것이 적음) |
마지막 두 행은 검색 상위 글들이 다루지 않는 솔직한 이야기입니다. 컨테이너화는 패키징이고, 패키징은 패키징할 자기 것이 있을 때만 가치가 있습니다. 서비스로 소비하는 표준 백엔드는 그 아티팩트 자체를 없앱니다.
한계와 트레이드오프
- 공유 커널 보안. 격리 경계는 커널입니다. 악의적인 워크로드에는 이것으로 부족하며, 마이크로 VM과 강화된 런타임이 존재하는 이유입니다.
- 이미지는 낡아 갑니다. 컨테이너는 취약점까지 포함해 의존성을 동결합니다. 이미지 스캔과 주기적인 재빌드는 영원히 끝나지 않는 일상 업무가 됩니다.
- 상태 저장 워크로드는 가장 까다롭습니다. 컨테이너는 일회용이기를 원하지만 데이터베이스는 그렇지 않습니다. 영구 볼륨과 상태 저장 오케스트레이션은 여전히 가장 까다로운 부분입니다.
- 레지스트리는 핵심 인프라입니다. 이미지를 호스팅하는 주체가 배포를 망가뜨릴 수 있으므로 프로덕션 시스템과 같은 수준으로 관리해야 합니다.
- 패키징은 운영이 아닙니다. 완벽한 이미지라도 스케줄링, 네트워킹, 확장, 모니터링이 필요합니다. 팀이 때로는 너무 이르게 Kubernetes에 도달하는 경로가 바로 이것입니다.
Back4app에서의 컨테이너화
Back4app은 관리형 데이터베이스, 자동 생성되는 REST 및 GraphQL API, 인증, 파일 스토리지, Cloud Code 서버리스 함수를 결합한 오픈소스 Backend as a Service(BaaS) 플랫폼입니다. 위 결정 매트릭스의 양쪽 끝에서 컨테이너화와 만납니다. 직접 패키징해야 하는 자체 워크로드라면 Back4app Containers가 Git 저장소에서 바로 어떤 이미지든 배포합니다. 빌드, 실행, 확장을 관리형 서비스로 제공합니다. 그 옆의 표준 백엔드에는 의도적으로 컨테이너화할 것이 없습니다. 데이터베이스, 인증, API가 미리 빌드된 상태로 제공되며, 이는 “어떤 머신에서든 동작한다”의 가장 강력한 형태입니다. 사용자의 머신에 의존하는 일이 아예 없기 때문입니다.
자주 묻는 질문
컨테이너화를 쉽게 설명하면 무엇입니까?
애플리케이션을 코드, 런타임, 라이브러리, 설정 등 필요한 모든 것과 함께 하나의 격리된 이식 가능한 단위로 패키징하는 것입니다. 이 단위는 노트북, 서버, 어떤 클라우드에서도 똑같이 실행됩니다. 이름은 해운 컨테이너의 비유를 그대로 옮긴 것으로, 내용물이 무엇이든 어떤 배, 기차, 트럭이든 실어 나를 수 있는 표준화된 상자를 뜻합니다.
컨테이너와 가상 머신의 차이는 무엇입니까?
가상 머신은 하드웨어를 가상화합니다. 각 VM은 완전한 게스트 운영체제와 자체 커널을 포함하므로 크기는 기가바이트 단위이고 부팅에 몇 분이 걸립니다. 컨테이너는 운영체제 수준에서 가상화하며 호스트 커널을 공유하므로 크기는 메가바이트 단위이고 시작에 몇 밀리초면 충분합니다. 널리 통용되는 한 줄 요약은 이렇습니다. VM은 하드웨어를 추상화하고, 컨테이너는 OS를 추상화합니다.
Docker와 컨테이너화는 같은 것입니까?
아닙니다. Docker는 2013년에 컨테이너화를 주류로 만든 도구이지 개념 그 자체가 아닙니다. 기반이 되는 커널 기술은 그보다 수십 년 앞서 존재했고, 대체 엔진과 런타임(Podman, containerd, CRI-O, LXC)도 같은 이미지를 빌드하고 실행합니다. 이미지가 특정 벤더의 형식이 아니라 OCI 오픈 표준을 따르기 때문입니다.
컨테이너 이미지란 무엇입니까?
컨테이너를 시작하는 데 쓰이는 불변 템플릿으로, 컨테이너가 인스턴스라면 이미지는 클래스에 해당합니다. 이미지는 읽기 전용 레이어의 스택으로 빌드되며(빌드 명령 하나가 레이어 하나를 추가), 레이어는 캐시되어 이미지 간에 공유됩니다. 런타임에는 그 위에 얇은 쓰기 가능 레이어가 올라갑니다. 이미지는 레지스트리에 저장되고, 어떤 호스트든 레지스트리에서 이미지를 가져와 실행할 수 있습니다.
컨테이너는 내부적으로 어떻게 동작합니까?
세 가지 커널 기능이 실제 작업을 담당합니다. 네임스페이스는 각 컨테이너에 프로세스 ID, 네트워크 인터페이스, 파일시스템, 사용자 등 시스템에 대한 독립된 시야를 제공합니다. 컨트롤 그룹(cgroups)은 컨테이너가 사용할 수 있는 CPU와 메모리의 상한을 정합니다. 그리고 계층형 유니온 파일시스템이 이미지를 효율적으로 조립합니다. 컨테이너는 커널 안의 어떤 객체가 아니라, 격리라는 옷을 입은 하나의 프로세스입니다.
OCI란 무엇입니까?
Open Container Initiative, 즉 컨테이너의 이식성을 지키는 표준화 기구로 2015년 Linux Foundation 산하에 설립되었습니다. 세 가지 명세가 이미지 형식, 런타임 동작, 이미지 배포를 각각 규정하며, 한 도구로 빌드한 이미지가 호환되는 어떤 엔진, 레지스트리, 오케스트레이터에서도 실행되는 이유가 여기에 있습니다. 컨테이너화가 단일 벤더 종속에서 벗어난 것은 OCI 덕분입니다.
컨테이너가 가상 머신보다 안전합니까?
VM이 더 강한 경계를 제공합니다. 하이퍼바이저 뒤에 커널이 분리되어 있기 때문입니다. 컨테이너는 호스트 커널을 공유하므로 이론상 커널 취약점 공격이 컨테이너 사이를 넘나들 수 있으며, 신뢰할 수 없는 코드나 멀티테넌트 코드를 실행할 때는 이 점이 중요합니다. 중간 지대는 마이크로 VM입니다. 컨테이너에 가까운 시작 시간으로 하드웨어 격리를 제공하며, 신뢰할 수 없는 워크로드에는 이제 표준 관행이 되었습니다. 직접 개발한 신뢰할 수 있는 앱이라면 컨테이너 격리로 대체로 충분합니다.
컨테이너가 가상 머신을 대체합니까?
아닙니다. 프로덕션에서 컨테이너는 압도적으로 VM 안에서 실행됩니다. 업계 분석은 컨테이너 대다수가 VM 기반 인프라에 배포된다는 결과를 일관되게 보여 줍니다. VM은 클라우드 제공업체에 강력한 멀티테넌트 격리를 제공하고, 컨테이너는 애플리케이션 팀에 패키징과 밀도를 제공합니다. 둘은 경쟁자가 아니라 상호 보완적인 계층입니다.