TIL - Docker | 프로젝트 배포하기

한성봉·2021년 7월 18일
0

Docker

Docker는 가상화 기술이다. Docker 라는 회사가 container virtualization을 개발하였다. Docker는 container 가상화 실행 환경 위에 application 배포 엔진을 더함으로서 사용자의 코드를 어디서든 빠르고 가볍게 실행시킬수 있는 기술을 제공한다.

Docker를 더욱 자세히 알고 싶다면 다음 홈페이지를 참조해보자.

Docker 공식 홈페이지

  • Container 가상화
    Docker 같은 컨테이너 가상화 기술은 hypervisor 가상화와 틀리게 OS의 커널 위의 유저 공간(user space)에서 실행된다. 즉 완전히 독립적인 운영체제를 가상화 하는 것이 아니라 독립적인 user space를 가상화 한다고 생각하면 쉽다. 즉 하나의 호스트 서버에서 여러 독립적인 user space 인스탄스들을 가상적으로 실행할 수 있게 되는 것이다. 이러한 가상화 기술의 장점은 hypervisor 가상화 보다 훨씬 가볍기 때문에 빠르고 쉽게 독립적인 가상 환경을 실행시킬수 있다 (또한 hypervisor는 base OS와 가상화 OS 사이에 커널 시스템 호출을 연결 시켜주는 emulation layer가 필요한데 docker는 hypervisor 처럼 emulator가 필요없이 그냥 일반적인 시스템 API interface를 사용한다).

Docker의 구조

  • Docker client, server(engine)
    Docker는 클라이언트 와 서버 구조로 이루어저 있다. 클라이언트가 서버에 명령을 전달하고 서버가 실행시키는 구조이다. docker binrary 커맨드가 docker 클라이언트 이고 dockerd 가 docker daemon 혹은 docker engine이다. Docker engine과 interact하기 위한 Restful API도 제공된다. 클라이언트와 서버는 동일한 호스트 안에서 운영 될수도 있으며 서로 다른 호스트에서 운영 될수도 있다.

  • Docker image
    Docker의 life cycle에서 docker 이미지는 “build”의 부분에 해당된다. Docker container에서 실행시키고 싶은 application을 docker 이미지로 빌드해서 실행시키게 된다.

  • Docker registries
    Docker registires는 docker 이미지를 저장하는 repository라고 보면 된다. Source code를 github에 저장하여 관리하듯 docker 이미지는 dockerhub 같은 docker registries에 저장한다고 생각하면 된다. Github가 마찬가지로 public registry 가 있고 private registry가 있다.

  • Docker containers
    Docker container에서 docker 이미지가 실행된다. 즉 docker 이미지를 실행시키는 가상화 공간 이다. Docker container는 하나 혹은 그 이상의 프로세스를 실행 시킬수 있다 (하지만 하나의 프로세스만 실행시키는 것을 권장).

  • Docker Compose And Swarm
    Docker compose는 복수의 docker container들을 모아서 종합적인 application stack을 정의 하고 운영할수 있도록 해주는 서비스이다. Compose 파일을 사용하여 전체적인 application 서비스를 설정한후, application을 이루고 있는 각각의 컨테이너들 (예를 들어, web 서버 컨테이너, api 서버 컨테이너 등등)을 따로 실행시킬 필요 없이 한번에 생성하고 실행할 수 있도록 해준다. Docker swarm은 docker containers 들로 이러우진 cluster를 관리할수 있도록 해주는 서비스이다. 즉 docker container를 위한 clustering tool 이다.

Docker Image

Docker image는 파일시스템들의 layer로 만들어져 있다. 가장 base layer는 boot filesystem bootfs로 일반적인 Linux boot 파일시스템으로 되어있다. Docker 유저가 직접적으로 boot filesystem을 사용할 일은 없으며, 실제로 container가 부팅이 되면 메모리로 옴겨지고 boot 파일시스템은 unmount된다.

Boot layer 다음에는 rootfs 라고 하는 root 파일 시스템 layer이다. Root 파일 시스템은 실제 OS 가 설치된다(예를 들어 Debian 이나 Ubuntu). 원래 리눅스에서는 root filesystem은 처음 mount될때는 read-only로 mount가 된후 integrity check후 read-write으로 바뀐다. 하지만 Docker에서는 계속해서 read-only 모드이다. Read-write 모드로 변환하지 않는 이유는 Docker는 union mount를 사용해서 read-only 파일 시스템들을 root 파일 시스템 위에 덮는 구조로 이루어져 있기 때문이다. 참고로 union mount는 여러게의 파일 시스템을 mount하되 하나의 파일 시스템만이 mount된것 처럼 사용하는 방법이다.

Docker 구조에서는 이러한 파일 시스템 하나 하나가 바로 image이다. 그럼으로 image들을 서로 위에 layer시키는 구조로 되어있다. Base가 되는 이미지를 부모 이미지라고하며 맨 위의 이미지 부터 가장 밑 부분의 이미지 까지 횡단 하는 구조로 되어있다. 즉 filesystem / image를 수정하는 구조가 아니라 read-only filesystem / image 들을 서로 위에 layer 시키되 union mount 기법으로 마지막에는 하나의 파일 시스템으로 보이는 구조를 가지고 있는 것이다.

모든 read-only 파일시스템들이 mount가 되고 docker 컨테이너가 이미지로부터 시작될때 docker는 마지막으로 read-write 파일 시스템을 파일 시스템 layer 맨 위에 mount한다. 마지막에는 read-only가 아니라 read-write으로 올려놓는 이유는 read-write 파일 시스템에 컨테이너가 필요한 프로세스를 생성하고 실행하기 위해서이다. 글로 설명하면 굉장히 어려울수 있지만 아래 그림을 보면 비교적 이해가 어렵지 않을것이다.

이러한 pattern을 copy on write이라고 한다. 이 copy on write 구조는 Dockerfile 을 이용해 docker image 를 빌드할때 효과적이다. Docker image를 빌드할때 Dockerfile의 각각의 instruction이 바로 filesystem / image가 되는것이다. 그러므로 빌드를 할때 처음부터 다 빌드하는 것이 아니라 바뀐 파일시스템만 mount하면 됨으로 효과적이고 빠르게 빌드 할 수 있으며 빌드가 도중에 실패하더라도 마지막으로 성공한 instruction은 빌드가 되어있는 상태이므로 그 이미지에 shell 접속을 해서 디버깅을 한다거나 하는 것이 가능해진다.

이론은 여기까지 보도록 하고 실습에 들어가보자..
Docker는 구글링을 통해 훨씬 많은 지식을 배울 수 있을 것이다.

일단 우리가 할 작업은 로컬에서 진행했던 프로젝트를 도커를 이용하여 이미지 생성, 컨테이너 생성, 푸쉬, EC2, RDS와 연동하여 배포까지 해보겠다.

Docker를 이용한 배포

지난번 AWS 배포 시간에 EC2, RDS는 생성되어있다는 전제 하에 진행하겠다.

Docker 회원가입

Docker 공식 홈페이지

다음 공식 홈페이지에서 회원가입을 해준다. 가입할 때 등록했던 ID는 잘 기억해준다.

Docker 설치

홈페이지에 Get Started 버튼을 눌러 도커를 설치해준다.

Docker 기본 명령어

docker

docker ps
#(실행중인 컨테이너를 보여주는 커맨드) 
docker ps -a
#(실행이 종료된 것을 포함해서 모든 컨테이너를 보는 커맨드 및 옵션)
docker images
#(생성된 혹은 다운로드 된 이미지를 보여주는 커맨드)
docker images -a
#(모든 이미지를 보여주는 커맨드 및 옵션)

Dockerfile (중요)

#./Dockerfile
FROM python:3 
#기반이 될 이미지

# 작업디렉토리(default)설정
WORKDIR /usr/src/app 

## Install packages
#현재 패키지 설치 정보를 도커 이미지에 복사
COPY requirements.txt ./ 
#설치정보를 읽어 들여서 패키지를 설치
RUN pip install -r requirements.txt

## Copy all src files
#현재경로에 존재하는 모든 소스파일을 이미지에 복사
COPY . . 


## Run the application on the port 8080
#8000번 포트를 외부에 개방하도록 설정
EXPOSE 8000   


#CMD ["python", "./setup.py", "runserver", "--host=0.0.0.0", "-p 8080"]
#gunicorn을 사용해서 서버를 실행
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "example.wsgi:application"]  

도커를 이용해 배포할 때는 Docker 파일이 굉장히 중요하다 모든 실행 명령어가 들어있기 때문에 잘 정의해주어야한다.

requirement.txt

EC2에 도커를 이용하여 배포하려면 requirement.txt 파일을 통해 로컬의 가상환경과 같은 환경을 만들어 주기 위해 설치 목록을 txt 파일로 만들어 주어야한다. 현재 가상환경의 install 목록을 텍스트 파일로 만들어 주자

pip freeze > requirement.txt

EC2, RDS 연동

우리의 장고 프로젝트는 현재 로컬DB와 호스트 주소가 로컬과 외부 환경에서만 들어올 수 있도록 설정 되어있는데 그것을 EC2와 RDS로 연동 해주고 Docker 이미지를 빌드해야한다.

-EC2 주소 연결

# 프로젝트/settings.py
ALLOWED_HOSTS = ['*', 'EC2 ip v4 주소', 'EC2 ip v4 주소:8000']
  • RDS 연동
# my_settings.py
SECRET_KEY = 'YOUR SECRET_KEY'

DATABASES = {
    'default' : {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'DB이름',
        'USER': 'root',
        'PASSWORD' : 'RDS 비밀번호',
        'HOST': 'RDS 엔드포인트 주소',
        'PORT': '3306',
    }
}

ALGORITHM = 'YOUR ALGORITHM'

다음과 같은 설정이 완료되었다면 본격적으로 Docker 이미지를 빌드 해보자.!!

Docker image build

도커 이미지를 빌드하기전에는 꼭 프로젝트에 이상이 없는지 확인하자. 서버를 켜보고 RDS 와 연동하여 서버로 요청하는 것까지 되는지 확인해보고 이미지빌드를 하자.

docker build -t '도커허브에 가입한 계정명'/'이미지명(프로젝트명 권장)':'버전' .
ex) docker build -t ssaboo99/project-gonggol:0.1.0 .

마지막에 .은 현재 해당 디렉토리 안에 파일들을 이미지로 빌드하겠다는 뜻이다. 그러니 프로젝트 디렉토리를 잘 찾아 빌드하자.

이미지를 빌드 했다면

docker images

다음 명령어를 통해 이미지 생성 여부를 확인하자.

Docker Container 실행

docker run —name docker-container -d -p 8000:8000 사용자이름/이미지이름:0.1.0
docker run -it wecode/wecodeproject:0.1.0 /bin/bash
#위의 명령어를 실행하면 이미지를 기반으로 바로 컨테이너를 실행하면서 접속한다.
#process 등 여러가지를 살펴보면서 현재 서버에 어떤 문제가 있는지 알 수 있다. 하지만 수정은 컨테이너에서 하는게 아닌 이미지를 다시 빌드 해야한다.

다음 명령을 통해 컨테이너 실행 여부를 확인하자.

docker ps
docker ps -a 중지된 컨테이너까지 확인가능하다.

이미지와 컨테이너가 생성이 잘 되었다면 우리가 홈페이지에서 받았던 app에서도 잘 보일 것이다.


지금 컨테이너 중지 상태인데 잘 실행된다면 컨테이너가 초록색일 것이다.!

그리고 Docker 실습 중 분명 안되는 과정이 있을 것이다. 그렇다면 다음 명령어를 통해 이미지와 컨테이너를지우고 다시 실행해보자.

docker rm $(docker ps -a -q)
#UBUNTU 
sudo docker rm $(sudo docker ps -a -q)
docker rm $(docker ps -a -q)
#UBUNTU 
sudo docker rm $(sudo docker ps -a -q)

만약 컨테이너가 실행중이라면 컨테이너를 중지시키고 삭제 명령어를 실행해야한다.

  • Docker container 중지
Docker stop (container ID)
  • Docker container 실행
Docker start (container ID)
Docker restart (container ID)

Docker push

로컬 환경에서 도커 이미지 빌드와 컨테이너까지 생성하였다면 Docker Repository에 Push 까지 해보자.

  • Docker Push
docker push 도커허브에 가입한 계정명'/'이미지명(프로젝트명 권장)':'버전(태그)'

그럼 Dockerhub 사이트 repository에 방금 push 했던 이미지가 잘 올라간 것을 알 수 있다.

그럼 인제 거의 다 왔다. EC2에 접속하여 도커 이미지를 pull 받아 컨테이너를 실행시키기만 하면 배포가 완료된 것이다.

EC2 접속

aws-pem 파일이 있는 디렉토리에서 EC2 접속

# 지정한파일이름pem
ssh -i 지정한파일이름.pem ubuntu@<EC2 접속 주소>

docker install

이후의 작업은 로컬에서 빌드했던 작업과 거의 동일하다.

  1. docker install
  2. sudo bash
  3. docker login
  4. docker pull
docker pull 도커허브에 가입한 계정명'/'이미지명(프로젝트명 권장)':'버전(태그)'
  1. docker container 실행
docker run —name docker-container -d -p 8000:8000 사용자이름/이미지이름:0.1.0

이 과정이 모두 완료되었다면 runserver를 실행시켜 잘 돌아가는지 확인 후 postman 으로 요청을 보내보자. 그때 주소는 EC2 주소로 보내야한다.

http://EC2주소:8000/<설정한 url>

이 때 요청이 잘 온다면 docker를 이용한 AWS EC2 배포가 완료된 것이다. docker를 사용하기 전에는 직접 EC2 서버에서 프로젝트를 클론받아 설정을 한 뒤 gunicorn으로 직접 서버를 구동했는데 도커를 사용하여 Docker 파일을 작성하여 쉽게 배포를 할 수 있었다.

0개의 댓글