컨테이너 격리된 코드와 환경

작성일: Sun Apr 14 2024

들어가며

내 컴퓨터에서는 잘 동작하는 애플리케이션이, 배포만 하면 의도대로 동작하지 않는 경험을 한 적이 있으신가요?

이 글에서는 도커와 컨테이너를 통해 이러한 문제를 어떻게 해결할 수 있는지 알아봅니다.


TL:DR

  • 컨테이너는 애플리케이션을 구성하는 소스코드 뿐만 아니라, 애플리케이션이 실행될 환경까지 함께 격리합니다.
  • 이를 통해 해당 애플리케이션이 어느 환경에서든 같은 동작을 하도록 보장합니다.

컨테이너: 격리된 코드와 환경

소스코드를 빌드해본 적이 있으신가요?

빌드 결과물인 애플리케이션은 애플리케이션 작동에 필요한 소스코드를 외부로부터 격리한 결과물입니다.

따라서 개발자가 소스코드를 수정해도, 다시 빌드를 하지 않는 이상 애플리케이션이 수정되지 않으리라는 것을 알 수 있습니다.

문제는 같은 소스코드로 개발된 애플리케이션이라고 하더라도, 어느 환경에서 실행하느냐에 따라 서로 다르게 동작할 수 있다는 것입니다.

일부 애플리케이션은 개발자가 직접 개발한 소스코드 외에도, 애플리케이션이 실행되는 외부 환경에 종속성을 갖기 때문입니다.

이러한 문제를 해결하기 위해, 소스코드 뿐만 아니라 소스코드가 실행되는 환경도 함께 격리시키는 아이디어가 제시되었으니, 이를 컨테이너 라고 합니다.

컨테이너는 외부로부터 격리된 코드와 환경을 갖고 있는 패키지이므로, 언제 어디서 누가 실행하든 같은 동작을 기대할 수 있습니다.

도커는 이러한 컨테이너의 생성과 관리를 도와주는 툴입니다.

컨테이너의 대체재로는 가상 머신(Virtual Machine) 이 있지만, 둘의 비교는 여기서 다루고자 하는 내용이 아닙니다.

다만, 둘의 차이에 대해 AWS 문서에서 언급한 비유가 흥미로워 간단히 언급해보자면,

우주비행사가 다른 행성을 탐사할 때, 가상 머신은 지구의 대기를 재현하는 것이고, 컨테이너는 우주복을 입는 것과 비슷하다고 하네요.

자세한 비교가 궁금하신 분은 AWS 에서 제공하는 컨테이너와 가상 머신의 비교를 읽어보시기를 권합니다.


이미지: 컨테이너를 만드는 블루프린트

이미지는 위에서 소개한 컨테이너를 생성하는 데에 쓰이는 블루프린트입니다.

마치 클래스를 활용하여 인스턴스를 생성하는 것과 비슷합니다.

다만 컨테이너는 클래스 대신 Dockerfile 이라는 파일을 통해 빌드할 이미지 스펙 및 컨테이너의 스펙을 정의합니다.

즉, Dockerfile 에 적힌 명세를 통해 이미지를 빌드하고, 빌드된 이미지를 통해 컨테이너를 생성, 실행할 수 있습니다.


첫 Dockerfile 작성해보기

아래 코드는, 프로젝트 루트에 server.jsDockerfile 이라는 파일이 있다는 전제하에 작성되었습니다.

# Dockerfile

# node 라는 환경을 컨테이너에 포함시킴
FROM node

# 이미지 내부의 /app 이라는 디렉토리를 작업 디렉토리로 사용함.
WORKDIR /app

# Dockerfile 이 위치한 곳의 모든 디렉토리, 파일들을 이미지 내부의 /app 이라는 디렉토리 내부로 복사함
COPY . /app

# 이미지를 빌드할 때 실행할 명령어
RUN npm install

# 이미지를 기반으로 컨테이너가 시작될 때, 컨테이너가 노출했으면 하는 포트를 명시 
# ( 이는 단순히 문서화 목적으로 작성하는 것으로, 아래 구문을 추가해도 실제로 컨테이너의 80 포트가 expose 되지는 않음.
# 실제로 80 포트를 expose 하기 위해서는 컨테이너를 시작할 때 -p 옵션을 명시해야 함 자세한 실행방법은 후술)
EXPOSE 80

# 이미지를 기반으로 컨테이너가 시작될때 실행할 명령어는 CMD 키워드를 사용함
# CMD 구문이 없는 경우, 베이스 이미지가 실행되며, 베이스 이미지가 없는 경우 에러가 발생함
CMD ["node", "server.js"]

갑자기 많은 내용이 나왔는데, 위 Dockerfile 의 내용을 간단히 요약하면 다음과 같습니다.

로컬 개발 환경의 모든 소스코드를 이미지 내부의 /app 이라는 디렉토리로 옮기고, 이 이미지를 기반으로 컨테이너를 실행할 때에는 node server.js 라는 명령어를 실행한다.

중요한 부분이니 한가지 짚고 넘어가자면,

컨테이너를 소개하는 부분에서 컨테이너는 소스코드가 실행되는 환경 또한 격리시킬 수 있다고 언급했습니다.

위 Dockerfile 의 첫줄에 작성된 FROM node 가 바로 그부분입니다.

컨테이너에 소스코드가 실행될 환경 ( 여기서는 node ) 를 함께 격리시키는 것이죠.

이를통해 이 애플리케이션은 node가 설치되지 않은 컴퓨터에서도 node 와 함께 실행될 것임을 확신할 수 있습니다.


작성한 Dockerfile로 이미지 빌드, 컨테이너 실행해보기

Dockerfile 과 같은 경로에서, 터미널을 열고 아래와 같이 작성해봅시다.

(아직 도커를 설치하지 않았다면, 도커 공식 사이트 설치 링크를 참고해주세요)

docker build .

빌드된 이미지는 아래 명령어를 통해 확인할 수 있습니다.

docker images

위 명령어를 입력하면 방금 빌드한 이미지의 IMAGE ID 가 터미널에 보일텐데요. 이 IMAGE ID 를 기반으로 컨테이너를 실행할 수 있습니다.

docker run -p 3000:80 YOUR_IMAGE_ID

중간의 -p 3000:80 옵션은 컨테이너를 실행하는 로컬 컴퓨터와, 컨테이너 내부 환경을 매핑해주는 역할을 합니다.

로컬 컴퓨터의 3000 포트로 컨테이너 내부 환경의 80포트에 접근할 수 있게 된다고 보면 됩니다.


요약 및 추가로 학습할 부분

지금까지 컨테이너가 해결하려고 하는 문제와, 간단한 실행 예시를 알아보았습니다.

컨테이너는 빌드된 소스코드만으로는 서로 다른 환경에서 실행되었을 때 같은 동작을 보장하지 못하는 문제로부터 출발했습니다.

이에 대해 제시된 해결방법은, 애플리케이션이 실행되는 환경까지 함께 격리함으로써 환경과 상관없이 동일한 동작을 보장하는 것입니다.

이렇게 생성된 컨테이너는 사실 그 자체만으로 활용도나 DX가 그렇게 좋지는 않고, AWS, Azure 등의 클라우드 프로바이더나, Kubernetes 와 함께 사용했을 때 훨씬 더 편리하게 애플리케이션을 관리하고 실행할 수 있습니다.

그렇기 때문에 이 글에서 소개한 컨테이너에 대한 컨셉이 마음에 드신 독자분이라면, 해당 내용을 추가로 학습해보시기를 권합니다.


레퍼런스

https://www.udemy.com/course/docker-kubernetes-2022

https://aws.amazon.com/ko/compare/the-difference-between-containers-and-virtual-machines

https://aws.amazon.com/ko/blogs/korea/how-to-choose-aws-container-services/

https://azure.microsoft.com/ko-kr/products/category/containers

https://kubernetes.io