🐳 Docker 란?
Docker는 2013년에 dotCloud의 Solomon Hykes와 그의 팀에 의해 2013년 PyCon에서 처음 데모 되었다.
이 당시에는 클라우드 환경에서 배포하는 것이 어려운 문제였고, 기존 가상화 기술을 통해서는 관리가 어려웠다.
따라서 전체 운영 체제를 가상화하는 것이 아닌, 해당 종속성만 가상화하는 것이 효율적이라는 것을 알게 되었고, Docker의 컨테이너 개념이 탄생하게 되었다.
📦 컨테이너와 이미지
컨테이너화는 기본 운영 체제(OS) 커널을 통일한 시스템의 다른 컨테이너와 공유하는 격리된 환경에서 애플리케이션을 실행하는 방법이다.
컨테이너는 종속성, 필요한 라이브러리 및 바이너리와 함께 존재하는 애플리케이션을 컨테이너 이미지라고 하는 독립적인 패키지로 패키지화하여 여러 컴퓨팅 환경에서 쉽게 배포할 수 있다.
위 그림과 같이 기존 Virtualized Deployment 방법은 하이퍼바이저 위에서 여러 개의 Guest OS를 실행한다.
하이퍼바이저(Hypervisor)
하이퍼바이저는 하나의 물리적인 호스트 시스템에서 여러 개의 가상 머신(VM, Virtual Machine)을 실행할 수 있도록 해주는 가상화 소프트웨어이다. 이를 통해 하나의 컴퓨터에서 여러 운영 체제를 동시에 실행할 수 있다. 하이퍼바이저는 가상 머신을 생성하고, 각 VM에 CPU, 메모리, 스토리지, 네트워크 등의 자원을 할당하여 운영한다. VM들을 독립적으로 실행되며, 서로 영향을 주지 않는다. 이러한 가상화 기술을 통해 서버 통합, 테스트 환경 구축, 보안 격리등의 이점을 얻을 수 있다.
Container Deployment 환경은 Guest OS를 사용하지 않고 Host OS에 컨테이너형 가상화 소프트웨어를 설치한다.
즉, 컨테이너는 논리적으로 프로세스를 격리하는 기술이고, 컨테이너는 가상 머신처럼 하드웨어를 전부 구현하지 않기 때문에 매우 빠른 실행이 가능해진다.
앱 간 간섭 받지 않고 독립적인 환경으로 격리되어 운영이 가능하며, 컨테이너는 OS가 없다.
VM은 Host OS 커널에 비종속적인 반면, 컨테이너는 Host OS 커널에 종속적이다.
📸 이미지 구조
컨테이너 이미지 명세는 Dockerfile로 작성한다.
Docker는 Image를 만들기 위해 Dockerfile에 DSL(Domain-Specific Language)로 이미지를 생성한다.
Dockerfile은 무엇을 이미지화해야 하는지를 선언적으로 정의한다.
컨테이너로 서버 띄워보기
실습은 Express.js를 통해 간단한 웹 서버를 만든 후, 이를 컨테이너를 통해 띄워볼 것이다.
그리고, Dockerfile을 통해 빌드된 이미지를 뜯어보며 이미지가 어떻게 구성되어 있는지를 확인해 볼 것이다.
먼저 디렉토리를 생성한 후, express
라이브러리를 설치해 보자
mkdir docker-express-app && cd docker-express-app
npm init -y
npm install express
위와 같이 프로젝트가 잘 생성된 것을 볼 수 있다.
서버가 정상적으로 띄워진 것을 확인하기 위해 간단한 index.html
을 작성해 준다.
<!-- index.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Docker Express App</title>
</head>
<body>
<h1>🚀 Docker + Express.js 서버 실행 성공! 🎉</h1>
</body>
</html>
그리고 express
를 통해 만든 HTML을 간단하게 보여주는 서버 코드를 작성한다.
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.static('public'));
app.listen(PORT,() => {
console.log(`Server is running on port ${PORT}`);
})
이후 node server.js
를 통해 서버를 켜주면 Server is running on port 3000
이라는 문구와 함께 local의 3000번 포트로 접근하면 내가 만든 HTML을 서빙해 주는 것을 확인할 수 있다.
이제 Dockerfile을 작성해 보자
# Dockerfile
# Node.js 베이스 이미지 사용
FROM node:18
# 작업 디렉토리 설정
WORKDIR /app
# 필요한 파일 복사 및 의존성 설치
COPY package.json package-lock.json ./
RUN npm install
# 소스 코드 복사
COPY . .
# 컨테이너가 실행할 명령어 설정
CMD ["node", "server.js"]
# 컨테이너가 사용할 포트 설정
EXPOSE 3000
이후 Mac 환경에서 이미지 빌드를 위해 Docker Desktop을 실행한 후 빌드해 준다.
이때 Mac에서 빌드하려면 Docker Hub에 로그인하여 권한을 얻어야 하고, 나는 node 18 이미지가 계속 다운로드되지 않아서, 직접 node 이미지를 다운로드하였다.
docker run -p 3000:3000 my-express-app
위의 명령어를 통해 이미지를 실행하면 다음과 같이 내가 만든 Express 서버가 잘 뜬것을 확인할 수 있다.
Docker 이미지 분해하기
Docker에서 .tar 파일은 Docker 이미지를 파일 형태로 저장한 것이다. 이 파일은 Docker 이미지의 모든 레이어와 메타데이터를 포함하고 있으며, 다른 시스템에 이미지를 배포하거나 백업할 때 유용하게 사용된다.
Docker 이미지와 레이어
Docker 이미지는 여러 개의 레이어로 구성되어 있다. 각 레이어는 파일 시스템의 변경 사항을 나타내며, 이미지를 빌드할 때마다 새로운 레이어가 추가된다. 이 레이어들은 read-only 상태로 저장되며, Docker는 이미지를 관리할 때 레이어를 효율적으로 재사용한다.
Docker 이미지의
.tar
파일
.tar
파일은 Docker 이미지의 전체 파일 시스템을 아카이브한 것이다. 이 파일은 Docker 이미지가 어떻게 구성되어 있는지, 즉 이미지가 가진 파일 시스템과 설정을 정확하게 저장한다.여러 개의 파일과 디렉토리를 하나의 파일로 묶거나 풀 때 사용하는 명령
sudo docker save my-express-app -o my-express-app.tar
위 명령어를 통해 빌드한 이미지를 .tar
파일의 형태로 가져온다.
그러면 현재의 워크 디렉토리에 my-express-app.tar
파일이 생긴 것을 확인할 수 있다.
이제 .tar
파일을 풀어보자
# container-image 디렉토리 생성 후 tar 파일 옮기기
mkdir container-image
mv my-express-app.tar ./container-image
cd ./container-image
tar tvf my-express-app.tar
tar xvf my-express-app.tar
위의 명령어를 실행하면 아래와 같이 .tar
파일이 풀린 것을 볼 수 있다.
cat manifest.json | jq
명령어를 통해 manifest.json을
확인해보면 다음과 같다.
위처럼 이미지의 계층을 확인할 수 있다.
가장 아래 레이어를 풀어서 확인해보면 아래 사진과 같이 Dockerfile
에서 워크 디렉토리로 정해준 /app 디렉토리가 만들어진 것을 확인할 수 있고, 그 안에 내가 만들었던 Express.js
서버 코드들을 확인할 수 있다.
tar xvf ./60afb0dfb46b8e72f338726fd682de1d35457b803c2a04d2e466906b5325d042/layer.tar
tar xvf
를 통해 그 위의 레이어를 풀어보니 root
디렉토리가 생성되었다.
이후 컨테이너와 이미지를 삭제해준다.
# 컨테이너 삭제
sudo docker rm {id}
# 이미지 삭제
sudo docker rmi {id}
이를 통해 내가 만든 코드들이 Dockerfile을 통해 어떻게 빌드되는지 확인할 수 있었고, 이미지가 여러 레이어로 .tar 파일을 통해 압축된다는 것을 확인할 수 있었다.
또한 .tar 파일이 어떻게 구성되어 있는지도 확인할 수 있었다.