섹션 9 - Docker 컨테이너 배포
127. 개발(Development)에서 제품 생산(Production)까지
개발(Development) 단계에서 제품 생산(Production) 단계까지 컨테이너와 도커를 활용하면, 지금까지의 모든 섹션과 프로덕션 환경에서 확인했던 격리된 환경의 이점을 개발 과정에서도 동일하게 누릴 수 있습니다.
컨테이너와 도커를 사용하면, 공유하기 쉽고 재현 가능한 환경을 손쉽게 구축할 수 있습니다. 따라서 컨테이너를 활용할 때는 로컬 환경에서 잘 작동하는 컨테이너가 리모트 환경에서도 동일하게 작동하도록 보장해야 합니다. 이번 섹션에서는 컨테이너 배포(Container Deployment) 에 대해 자세히 살펴보겠습니다. 컨테이너 배포와 관련하여 반드시 알아야 할 주요 사항은 다음과 같습니다.
주의사항
지금까지 학습한 섹션 중 일부에서는 개발 편의를 위해 바인드 마운트(Bind Mount) 를 사용했습니다. 하지만 Production 환경에서는 바인드 마운트를 절대 사용하지 않아야 합니다.
컨테이너화된 애플리케이션은 개발 환경과 프로덕션 환경 각각에 맞는 별도의 설정을 필요로 합니다.
멀티 컨테이너 프로젝트의 경우, 여러 호스트나 리모트 머신에 컨테이너를 분할하여 배포할 수 있을지 여부는 프로젝트의 구조와 컨테이너 설계에 따라 결정됩니다.
배포 플랫폼을 덜 통제하는 방식이 오히려 더 간단하고 효율적인 솔루션이 될 수도 있다는 점을 고려해야 합니다.
128. 베포 프로세스 & 프로바이더
이번 섹션에서는 Docker 컨테이너를 사용한 배포 프로세스를 다양한 시나리오를 통해 단계적으로 학습합니다.
간단한 예제를 통해 기본 개념을 익힌 후, 더 복잡한 시나리오를 다룰 예정입니다.
다음은 우리가 구현할 기본적인 배포 시나리오입니다:
Node.js 애플리케이션 준비: 데이터베이스를 사용하지 않는 간단한 Node.js 프로젝트를 준비합니다.
Docker 이미지 생성: Node.js 애플리케이션을 단일 Docker 이미지로 빌드하고, 이를 단일 컨테이너로 실행할 수 있도록 설정합니다.
리모트 서버 설정: 리모트 서버를 준비하고 SSH를 통해 안전하게 연결합니다.
Docker 이미지 푸시: 로컬 개발 환경에서 빌드한 Docker 이미지를 Docker Hub 또는 기타 Docker 레지스트리에 푸시합니다.
리모트 서버에서 이미지 실행: 리모트 서버에서 해당 Docker 이미지를 가져와(pull) 컨테이너를 실행합니다.
포트 노출: 애플리케이션이 외부(WWW)에서 접근 가능하도록 리모트 서버의 필요한 포트를 개방합니다.
시작 전 준비사항
위 시나리오를 구현하려면 리모트 서버 또는 리모트 머신이 필요합니다. 리모트 서버는 애플리케이션을 배포하고 실행할 수 있는 환경을 제공합니다.
이를 위해 다양한 클라우드 호스팅 제공업체를 사용할 수 있습니다.
저희는 3가지의 유명 호스팅 제공 서비스로 간추릴 수 있습니다.
AWS
Azure
Google Cloud 이 섹션에서는 AWS를 사용해 배포 프로세스를 진행합니다. AWS는 유연한 인프라와 다양한 도구를 제공하여 Docker 기반 배포에 적합합니다.
129. 예제로 시작하기
저희는 AWS에서 제공하는 EC2 서비스를 활용하여 리모트 환경에서 웹 서비스를 배포하고 운영할 계획입니다. 이번 과정은 총 세 가지 단계로 구성되어 있습니다.
EC2 인스턴스 생성 및 네트워크 설정 EC2 인스턴스를 생성한 후, VPC와 Security Group을 설정합니다. 이를 통해 서버 접근을 제어하고, 네트워크 환경을 구성합니다.
Security Group 포트 개방 웹 서비스가 외부에서 접근 가능하도록 Security Group의 인바운드 규칙을 수정하여, 모든 포트를 WWW 환경에 노출되도록 설정합니다.
SSH 연결 및 Docker 환경 구성 EC2 인스턴스에 SSH를 통해 접속한 뒤, Docker를 설치하고 실행 환경을 구축합니다.
강의에서 제공된 파일(deployment-01-starting-setup.zip)을 사용해 Node.js 기반 웹 애플리케이션을 배포합니다. 아래는 제공된 Dockerfile입니다:
여기서 node:14-alpine 은 경량화된 Alpine 기반 Node.js 14 이미지를 사용합니다. 이는 이미지 크기를 줄이고, 빌드 및 배포 속도를 향상시킵니다.
이제 강의에서 주어진 dockerFile 을 Iamge화 시키도록 하겠습니다.
docker build -t node-dep-example .
명령어를 통해 컨테이너를 실행 시킵니다
docker run -d --rm --name node-dep -p 80:80 node-dep-example
이것이 현재 저희의 데모 웹애플리케이션입니다.
프로덕션에서 바인드 마운트
현재 이 웹 애플리케이션은 프로덕션 환경에서 바인드 마운트를 사용하지 않습니다. 그 이유는 개발 환경과 운영 환경 사이에 컨테이너 실행 목적과 구조적인 차이가 존재하기 때문입니다.
개발 단계에서는 컨테이너가 런타임 환경을 캡슐화해야 하지만, 소스 코드는 캡슐화할 필요가 없습니다. 애플리케이션을 작업하는 동안에는 컨테이너가 실행 환경을 제공하고, 소스 코드는 로컬 시스템에서 즉시 수정 가능하도록 바인드 마운트를 활용하는 것이 효율적입니다.
반면, 프로덕션 환경에서는 컨테이너가 독립적으로 실행될 수 있어야 하며, 원격 머신에 소스 코드가 노출되어서는 안 됩니다. 이를 위해 Dockerfile 내부에서 COPY 명령을 사용해, 소스 코드와 필요한 파일들을 컨테이너 이미지에 포함한 상태로 배포합니다. 이 방식은 애플리케이션이 언제 어디서든 동일한 환경에서 실행될 수 있도록 보장해 줍니다.
정리하면 다음과 같습니다.
"In Development" (개발 환경):
컨테이너는 런타임 환경을 캡슐화해야 하지만, 반드시 코드를 포함할 필요는 없습니다
로컬 호스트 프로젝트 파일을 실행 중인 컨테이너에 제공하기 위해 'Bind Mounts'를 사용하세요
컨테이너를 재시작하지 않고도 즉시 업데이트가 가능합니다
"In Production" (운영 환경):
컨테이너는 독립적으로 실행되어야 하며, 원격 머신에 소스 코드가 있어서는 안 됩니다
이미지에 코드 스냅샷을 복사하기 위해 COPY를 사용하세요
모든 이미지가 추가적인 구성이나 코드 없이도 실행되도록 보장합니다
131. AWS & EC2 소개
현재 로컬에서 만들어진 이미지파일을 리모트 서버에 올려 컨테이너에 실행 시켜야 합니다. 그리하여 저희는 AWS에서 제공하는 EC2 서버를 사용할 것입니다.
로그인 이후 해당 URL 에서 인스턴스를 실행하면 됩니다.
https://us-east-1.console.aws.amazon.com/ec2/home?region=us-east-1#Home:
132. EC2 인스턴스에 연결하기
134. 가상 머신에 Docker 설치하기
sudo yum update -y
해당 명령어를 통해서 리모트 머신의 필요한 모든 필수 패키지가 업데이트 됩니다.
sudo yum -y install docker
위의 명령어를 통해서 docker 와 관련된 필수 패키지를 다운받을 수 있습니다.
sudo service docker start
위의 명령어를 통해서 docker 서비스를 실행시킬 수 있습니다.
136. 로컬 이미지를 클라우드로 푸시(push)하기
현재 리모트 환경에서 docker 를 설치하였기 떄문에 해당 로컬 이미지를 리모트 환경에 다운받아야합니다.
여기서 여러가지 옵션이 존재합니다.
옵션1: 소스 코드 배포 (Deploy Source)
원격 머신(서버)에서 이미지 빌드
원격 머신으로 소스 코드 전송 후, docker build 실행하고 이어서 docker run 실행
불필요한 복잡성: 과정이 여러 단계이고 서버에서 해야 할 작업이 많아 복잡합니다.
옵션2: 빌드된 이미지 배포 (Deploy Built Image)
배포 전 (예: 로컬 개발 머신에서) 이미지 빌드 완료
(원격 머신에서는) docker run 명령어만 실행
원격 서버의 불필요한 작업(빌드) 방지: 서버에서 직접 빌드하는 과정을 생략하여 서버 부하를 줄이고 작업을 단순화합니다.
저희는 여기서 옵션2 를 쳬택하여 적용해보도록 하겠습니다
먼저 .dockerignore 파일을 만들어 현재 올리지말아야 하는것들을 선정해줍니다.
비밀키는 올라가면 안되기떄문에 이그노어 파일에 추가해줍니다
그 다음 도커허브에 접속하여 Repository 를 만들어 줍니다.
다음으로 docker 이미지를 빌드해줍니다
docker build -t node-dep-example-1 .
해당 이미지는 이제 Local 환경에 존재합니다.
docker tag node-dep-example-1 {사용자이름}/node-example-1
해당 명렁어를 통해 태그를 등록해줍니다.
docker login
해당 명령어를 통해서 도커를 로그인 한 이후
docker push {사용자이름}/node-example-1
해당 명령어를 사용하여 repository에 업로드 해줍니다
137. 앱 실행 & 게시하기 (EC2에서)
다시 리모트 환경으로 돌아가 해당 환경에서 명령어를 쳐줍니다
sudo docker run -d --rm -p 80:80 jongminhong844/node-example-1
해당 명령어를 통해서 Repository에 존재하는 이미지를 다운받아 컨테이너를 실행할 수 있게 됩니다.
그리하여 현재 Insatance 의 IP 주소로 URL을 통해서 들어가게 되면 요청이 거절 될 것입니다.
왜냐하면 보안그룹에서 InBound 규칙이 SSH를 제외한 모든 포트의 요청을 막기 때문에 HTTP의 대한 요청을 허가 해줌으로써 Instance의 IP 주소를 통해 확인을 할 수 있습니다.
위와 같은 설정들을 통해서 리모트 환경에서 도커를 실행할 수 있다는것을 확인할 수 있었습니다.
138.컨테이너/이미지 관리 & 업데이트
현재 리모트 환경에서는 정상적으로 컨테이너가 작동중입니다 이제 로컬 환경에 존재하는 원시 코드파일을 수정하여 리모트 환경을 업데이트 하는 과정을 진행해보도록 하겠습니다.
기존의 존재하는 welcome.html 파일의 일부를 수정하였습니다. 이 변경사항은 로컬과 리모트 환경 둘다 자동으로 반영되지 않습니다.
이러한 변경사항을 리모트 서버에 업데이트 하는 방법은 매우 간단합니다 이미지를 다시 빌드하고 도커 허브에 올린 후 다시 업데이트 된 이미지를 사용하면 되는것입니다.
docker build -t node-dep-example-1 .
명령어를 통해 이미지를 다시 빌드해줍니다
docker tag node-dep-example-1 {사용자 이름}/node-example-1
태그를 다시 지정해줍니다
docker push {사용자 이름}/node-example-1
해당 업데이트 된 파일을 도커 허브에 올려줍니다.
이제 다시 EC2 서버에 접속하여 기존에 존재하던 컨테이너를 정지하고 최신 파일을 다운받아줍니다.
sudo docker pull jongminhong844/node-example-1
다시 컨테이너를 실행시켜 줍니다.
sudo docker run -d --rm -p 80:80 {사용자 이름}/node-example-1
그렇게 하면 이제 변경된 사항이 적용된 사이트를 확인 할 수 있습니다.
139. 현재 접근 방식의 문제점
현재 우리는 로컬에서 도커 이미지를 빌드한 후, 도커 허브를 통해 리모트 환경에 배포하는 방식을 사용하고 있습니다. 이 과정 덕분에 리모트 환경은 로컬과 동일한 앱과 환경을 보장받을 수 있습니다.
하지만 이 방법은 몇 가지 단점을 가지고 있습니다.
인스턴스 생성, 설정, 네트워크 연결, 도커 설치를 모두 수동으로 처리해야 합니다.
이 방식은 리모트 머신이 전적으로 우리 소유여야 가능하며, 서버의 OS 환경 업데이트와 보안 그룹, 방화벽 설정을 직접 관리해야 합니다.
원격 머신에 접근하기 위해 SSH 사용법 등 기본적인 서버 관리 지식을 익혀야 합니다.
결론적으로, 이 방식은 개발보다는 인프라 관리에 많은 시간을 소비하게 만듭니다.
우리의 궁극적인 목표는 몇 가지 명령어만으로 로컬 환경에서 자동으로 배포가 완료되는 시스템을 구축하는 것입니다. 그렇게 되면 개발자는 오직 소스 코드 작성과 애플리케이션 품질 향상에만 집중할 수 있습니다.
Last updated