IT/기초 지식

[Docker] Docker 기초(2)

개발자 두더지 2021. 4. 5. 15:42
728x90

일본의 블로그 글을 번역한 포스팅입니다. 오역과 직역이 있을 수 있으며 내용 오류가 있다면 지적해주시면 감사합니다.

 

이 포스팅을 통해 알 수 있는 것


 지난 포스팅에서는 Docker이란 무엇인가에 대한 내용부터 간략하게 대략적인 구조에 대한 이야기를 했다. 이번 포스팅을 통해서 데이터마운트(volume), Docker Network, Docker Compose에 대해서 설명하도록 하겠다.

 

 

Docker에서의 데이터 관리


 작동하는 컨테이너 내에서 다루는 동적 데이터를 읽고 쓰는 것이 가능한 최상의 레이어(컨테이너 레이어)에 두는 것이 가능하지만, 다음과 같은 세 가지 디메리트가 있다.  

- 컨테이너가 삭제되는 시점에서는 그 컨테이너 내의 데이터도 삭제된다.

- 컨테이너간에 데이터를 공유하는 것은 불가능하다.

-  컨테이너 레이어에의 데이터 읽기는 보통의 파일 시스템과 다른 유니온 파일 시스템이 사용되기 때문에 읽기 속도가 느리다.

   그러므로 Docekr에서는 호스트 머신상에 데이터를 관리하고 그것을 컨테이너에 마운트하는 방법이 사용된다. 방법은 크게 세 가지가 있다. 

 

1) volume

 호스트 머신상에 자동생성된 지정 디렉터리(/var/lib/docker/volumes)를 컨테이너에 마운트하는 방법

 호스트 머신상에 아래와 같은 커맨드로 volume(/var/lib/docker/volumes디렉토리)를 만들어 컨테이너를 기동할 때는 

docker volume create volume의이름

 아래와 같이, --mount옵션(-v 옵션도 가능)을 붙이는 것으로 지정한 volume을 마운트할 수 있다.

docker run -itd --name 만들컨테이너명 --mount source=[마운트할volume명],target=[컨테이너상의 마운트 소스 디렉토리] 이미지명

구체적인 예:
docker run -itd --name mount-test --mount source=volume1,target=/app nginx

 마운트한 volumes 디렉토리는 마운트 소스가 있는 호스트상의 디렉터리로부터 직접 조작해야하는 것은 아니라는 점에 주의해야한다.

 또한, 같은 호스트내에 만들어진 다른 컨테이너에서도 각각 같은 volume을 마운트하여 파일을 공유할 수 있다. 더욱이 volume을 여러 개의 컨테이너에서 공유하는 경우에는 컨테이너마다 편집 권한을 설정하는 것도 가능하다.

docker run -itd —name mount-c4 —mount source=copy-vol, destination=/etc/nginx,readonly nginx

 이와 같이 구분하여 편집 권한을 지정해줄 수 있다.

 

2) 볼륨 관리 커맨드

(1) volume의 목록을 확인

docker volume ls

(2) volume의 상세 정보를 확인

docker volume inspect volume명

(3) volume의 삭제

docker volume rm volume명

 컨테이너를 삭제해도 volume은 남아 있으므로, docker volume rm volume명으로 삭제해야할 필요성이 있다는 점을 주의하자.

 

3) bind mount

 bind mount는 호스트 머신상의 임의의 디렉토리를 마운트할 수 있어, 호스트쪽의 디렉토리를 직접 조작해도 괜찮은 volume과는 다른 마운트 방법이다.

 volume과 같이 사전에 설정할 필요는 없으며, 아래의 커맨드로 컨테이너의 동작시에 옵션을 지정하여 마운트한다.

docker run -itd --name [컨테이너명] --mount type=bind,source=[마운트소스디렉터리],target=[마운트타켓디렉토리] イメージ名

구체적인 예:
docker run -itd —-name bind-mount-test —-mount type=bind,source=“$(pwd)”/mount,target=/app nginx

 소스 디렉터리가 존재하지 않는 경우는 에러가 발생하므로 사전에 작성해두자. (-v옵션을 사용한 경우에는 자동 생성된다) 

 bind mount에서는 혹시 호스트 상에 비어있는 리렉토리를 컨테이너상의 /user등에 마운트한 경우에는 컨테이너상의 데이터가 삭제되어 컨테이너가 작동하지 않게 되어버리는 경우가 있으므로 주의가 필요하다.

 volume과 같은 방법으로 컨테이너마다 편집 권한을 설정하는 것도 가능하다.

 

4) tmpfs(임시fs)

 호스트 머신의 메모리 영역을 컨테이너에 마운트하는 방법.

 호스트 머신이 종료된 경우에도 컨테이너가 종료된 경우에도 보유하고 있던 데이터는 해방된다. 

docker run -itd --name [컨테이너명] --mount type=tmpfs,destination=[마운트타켓디렉토리] 이미지명

구체적인 예:
docker run -itd --name tmpfs-test --mount type=tmpfs,destination=/app nginx

 위 커맨드와 같이 컨테이너를 작동시킬 때에 --mount옵션의 type에 tmpfs를 지정하는 것으로 마운트가 가능하다.

 또한 호스트상의 메모리를 제한으로 사용해버릴 가능성을 사전에 제거하기 위해서는 

docker run -itd --name tmpfs-test --mount type=tmpfs,destination=/app,tmpfs-size=800000000,tmpfs-mode=800 nginx

 이와 같은 옵션을 사용 가능 메모리 사이즈를 제한하는 것도 가능하다.

 

5) 여기까지 설명을 반영한 그림을 통해 보는 구조

 

Docker 네트워크


 지금까지 다양한 이미지로 컨테이너라고 불리는 어플리케이션을 실행환경을 작동시켜 환경 구축을 간단히 할 수 있다는 내용에 대해 살펴보았다. 여기서 부터는 여러 개의 컨테이너간 통신하는 방법에 대해서 설명하고자 한다.

 예를 들어, Web 어플리케이션 서버의 운영을 실시하는 경우, API 서버 컨테이너와 MySQL컨테이너를 구축하면 API 서버 컨테이너는 MySQL 컨테이너와 통신할 필요가 있다.

 여기서 컨테이너간 통신 방법으로써, 여러 개의 컨테이너를 연결하여 통신하는 것이 Docker 네트워크이다. Docker network에는 디폴트로 존재하는 세 가지 네트워크와 독립적으로 정의하는 네트워크가 있다.

 

1) Bridge 네트워크

 기본적으로 있는 네트워크로, 작성된 컨테이너가 기본적으로 연결되어 있는 것이 바로 이 bridge network이다. dridge driver을 사용하고 있다.

 Bridge 네트워크에는 같은 네트워크내에 존재하는 컨테이너와 IP주소를 지정하여 통신하도록 할 수 있다. 다만, Bridge 네트워크에는 DNS가 지정되어 있지 않으므로 컨테이너 이름으로는 다른 컨테이너와 통신할 수 없다.

 즉, Network지정하지않고 작동한 컨테이너끼리는 IP주소를 지정하여 통신할 수 있지만, 컨테이너명을 지정하여 통신하는 것은 불가능하다.

 또한, 기본적으로 외부에 공개되어 있지 않는 네트워크이지만 -p옵션으로 포트를 지정해 개방하여 외부로부터 컨테이너에 접근할 수 있도록 할 수도 있다.

 

2) Host 네트워크

 Host 네트워크는 host driver을 사용한 기존에 존재하는 네트워크이다. 연결한 컨테이너는 docker host와 같은 네트워크 설정이 된다.

 예를 들어, host네트워크에 연결된 컨테이너에 nginx컨테너를 실행한 경우, host머신의 IP의 80번에서 listen하고 있는 것과 동일한 것이 된다.

 그러므로 bridge 네트워크와 같이 컨테이너 작동시에 -p 옵션을 설정하여 외부에 포트를 공개하지 않아도 컨테이너를 작동시키는 것만으로도 Docker host의 IP의 80번에 접속하면 그 컨테이너에 연결될 수 있게 된다.

 

3) None 네트워크

 none 네트워크 또한 기본으로 존재하는 네트워크로 연결된 컨테이너는 네트워크 인터페이스가 없어진다. none 네트워크에 접속하는 경우에는 다른 네트워크의 연결은 모두 끊어야한다는 점을 주의하자.

 

4) 독립 네트워크

 독립 네트워크를 작성하여 컨테이너명으로 컨테이너간 통신을 가능하게 할 수 있다.

docker network create 네트워크명

 위의 커맨드로 새로운 네트워크를 작성(디폴트의 drive는 bridge가 된다)하여, 

docker network connect 접속할네트워크명 컨테이너명

 이 커맨드로 지정 컨테이너를 작성한 네트워크에 연결할 수 있다.

 유저 정의의 독립 네트워크에서는 Docker Daemon의 내장 DNS가 작동하여 컨테이너명으로 이름을 결정하여 IP와 연결해주므로 컨테이명으로 다른 컨테이너에 접속할 수 있게 되는 것이다.

 

5) Docker 네트워크 ( 컨테이너간의 통신)의 이미지

 

6) 네트워크 관리 커맨드

(1) 네트워크의 목록을 확인

docker network ls

호스트 머신상에 실행하면 그 호스트 머신의 네트워크 목록이 표시된다.

(2) 네트워크의 상세정보를 확인

docker network inspect 네트워크명

(3) 새로운 네트워크를 작성

docker network create 네트워크명

디폴트로 driver은 bridge가 된다.

(4) 컨테이너를 네트워크에 연결

docker network connect 연결할네트워크명 컨테이너명

(5) 네트워크 연결 끊기

docker network disconnect 네트워크명 컨테이너명

 

7) 지금까지의 설명을 반영한 그림으로 보는 구조

 

 

 

Docker Compose


 Docker Compose는 컨테이너의 Docker 어플리케이션을 사전 정의하여 실행하기 위한 툴이다.

 컨테이너 1개를 작동시키기 위해서 volume등의 마운트 설정이나 소속된 Docker 네트워크의 설정등을 명확히 아래와 같이 작성한다.

docker run -itd —name mount-test —mount source=volume-test, destination=/etc/nginx,readonly nginx

 따라서 복잡한 옵션을 붙인 커맨드를 타자로 칠 수 밖에 없는데, 이것은 매우 귀찮은 일이고 잘못하여 연결이나 환경에 문제가 생기는 경우가 있다. 따라서 이러한 것을 자동화하기 위한 것이 Docekr Compose이다.

 어떤 Web서비스의 실행 환경을 Docekr에 구축하는 경우, Web 서버, DB 서버, Cache 서버 등의 정의를 하나의 docker-compose.yml파일에 기재해 그 파일을 실행하는 것으로 필요한 컨테이너를 한 번에 작동·설정하는 것이 가능하다.

 방법은 아래와 같다.

① Dockerfile을 준비한다 혹은 Docker Hub등에 사용하는 이미지를 준비한다.

② docker-compose.yml를 정의한다.

③ yml파일이 있는 디렉토리에서 docker-compose up을 실행한다.

 

1) Docekr Compose를 사용하지 않는 경우

각 컨테이너 하나씩 하나씩 옵션을 설정하여 작동시킬 수 밖에 없다.

 

2) Docker Compose를 이용하는 경우

docker-compose.yml를 정의하여, docker-compose up 커맨트 하나로 여러개의 컨테이럴 기동할 수 있다.

 

3) docker-compose관리 커맨드 목록

(1) docker-compose로 기동한 컨테이너 목록을 표시

docker-compose ps

(2) docker-compose.yml에 기재한 설정으로 컨테이너를 작동

docker-compose up

기존의 컨테이너가 작동중이라도 다시 재시작해준다.

(3) docker-compose로 만들어진 컨테이너나 네트워크의 삭제

docker-compose down
# docker-compose down -v

-v옵션으로 volume도 같이 삭제된다.

(4) 지정한 서비스 컨텍스트내에 커맨드를 실행

docker-compose run [컨테이너명]  [커맨드]

(5) 일련의 컨테이너를 정지

docker-compose stop

(6) 일련의 컨테이너를 실행

docker-compose start

 

4) docker-compose를 이용한 어플리케이션 실행환경 구축 예

Python의 WAF인 Django를 사용한 Web어플리케이션 환경 구축의 예를 구체적으로 살펴보자.

(1) image를 준비

작업 디렉토리를 작성하여 그 안에 Dockerfile을 만든다.

Dockerfile은 다음과 같이 작성하였다.

FROM python:3  # image명에python3실행환경의 이미지를 지정
ENV PYTHONUNBUFFERED 1 # python의 표준입력 내용이 버퍼에 쌓이지 않도록 환경변수를 설정
RUN mkdir /service  # service디렉토리를 작성
WORKDIR /service # service디렉토리를 이동
COPY requirements.txt /service/  # requirements.txt(사전에작성)을service디렉토리에 위치
RUN pip install -r requirements.txt # pip  install로 패키지를 설치
COPY . /service/ # build컨텍스트의 내용을 전부 /service내에 위치

(2) docker-compose.yml을 작성

 아래와 같이 docker-compose.yml파일을 작성한다.

version: '3' # docker-compose의 version지정
services: # 실행하는 서비스 컨테이너를 작성해나간다.
  db: # db서버 컨테이너를 실행
   image: postgres # 이미지는 dockerhub상의 postgres:latest를 사용
  web: # web서버 컨테이너를 실행
   build: . # 현재 디렉토리인 dockerfile로부터 image작성
   command: python3 manage.py runserver 0.0.0.0:8000  # 컨테이너 기동시에 실행되는 커맨드를 지정
   volumes:
    - .:/app # current디렉터리를 /app디렉터리에 bind mount
   ports:
    - "8000:8000" # 컨테이너의 8000번을 공개
   depends_on: # web서버 컨테이너를 구축하기 전에 db서버 컨테이너가 구축되도록 한다.
   - db

 depend_on은 구축 순서를 정하는 커맨드일뿐이며, 여기서 "db가 실행완료되면 web을 실행한다"라는 의미이다. 이러한 순서를 정해주지 않아 docker-compose up커맨드를 실행해도 잘 되지 않는 경우가 있는데, 그럴 경우 entypoint:로 실행대기의 shell script를 실행하는 등의 처리를 할 수 있다.

 자세한 내용은 여기를 참고하길 바란다. 

(3) docker-compose의 실행

docker-compose run web django-admin.py startproject test .

 커맨드로 docker-compose:yml로 정의한 web서비스 컨테이너를 일시 실행하고, django 프로젝트를 작성한다.

docker-compose up -d

 이 커맨드로 분리 모드(-d : 백그라운드)로 일련의 컨테이너를 기동한다. 이 docker-compose.yml파일을 공유하는 것으로 다른 머신에서도 docker-compose up 커맨드 1개로 일련의 컨테이너를 실행할 수 있다.

 

5) 지금까지의 설명을 반영한 그림으로 보는 구조


참고자료

qiita.com/etaroid/items/88ec3a0e2d80d7cdf87a

728x90