도커 기본 사용법

Seokbin·2022년 2월 27일
3
post-thumbnail

1. Overview


저희 팀의 경우 쿠버네티스에서 배포되는 플랫폼을 개발하기 때문에 개발자가 쿠버네티스에 대한 지식은 어느정도 있어야 됩니다. 하지만 신입분들의 학부생 출신이기 때문에 쿠버네티스 뿐아니라 이에 기본이 되는 도커, 컨테이너 환경에 대해 접하지 못한 분들이 대부분입니다. 저 또한 입사 할 당시 도커를 사용해 본 적이 없었기 때문에 이에 대한 많은 공부를 병행하며 업무를 수행했습니다. 현재 신입분들에게 도커 기본 사용방법을 알려주면서 기록한 내용을 공유합니다. 쿠버네티스와 도커 또한 제대로 알고 사용하기에는 많은 공부가 필요합니다만 여기서는 아주 기본적인 사용법만 다루겠습니다.

2. Docker ?


많은 개발자 분들이 도커에 대한 이야기를 많이 들어보았을 겁니다. 현재 도커와 컨테이너라는 단어가 동일 시되면서 사용되는 경향이 있지만 도커는 컨테이너를 쉽게 다루게 도와주는 일종의 플랫폼입니다. 그럼 컨테이너화 한다는 것은 무엇일까요? 어플리케이션을 개발 후 사용자가 실행할 때 필요한 환경들은 한번에 패키징 하여 별 다른 설치 없이 한번에 실행시켜주도록 하는 것입니다.
Dependency 까지 한번에 포함되어있기 때문에 사용자의 환경에 관계없이 실행이 가능합니다. 예를 들어 Postgres DB를 설치 하기 위해서 docker run -p 5432:5432 --name postgres postgres 한 줄로 실행이 가능합니다.
사실 이러한 장점은 개발환경에서도 큰 편의를 볼 수 있습니다. 예를 들어 파이썬 기반으로 개발을 진행 할 때 필요한 라이브러리들을 설치 하기 위해서 모든 개발자가 똑같은 환경을 셋팅해야됩니다. 하지만 각자 설치되어있는 파이썬 버전이나 OS 등 모두 같은 수 없기 때문에 이를 맞추는 일도 쉽지 않습니다. 하지만 컨테이너로 이러한 환경을 구성한 후 이미지를 만들어 배하면 개발자들 이미를 기반으로 컨테이너를 생성하여 사용하기만 하면 됩니다.
어플리케이션의 컨테이너화가 왜이렇게 주목 받는지에 이해하기 위해 발전 과정을 알아볼 필요가 있습니다. 전통적인 방식의 경우 한 서버에서 여러 어플리케이션을 같이 실행시켰습니다. 하지만 이 방식의 경우 어플리케이션 간에 서로 영향을 끼칠 수 있기 때문에 위험했습니다. 한 어플리케이션이 모든 자원(CPU,RAM 등)을 점유할 수 있기 때문에 이에 대한 분리가 필요했습니다.

이에 대한 해결책으로 나온 것이 VM 입니다. VM 은 한 물리적인 서버에 자원을 할당해 여러개의 Virtual 서버로 나눌 수 있었습니다. 이렇게 분리를 한 후 관련있는 어플리케이션끼리 묶어서 사용을 했습니다. 이렇게 전통적인 방식의 단점은 해결하였지만 VM의 경우 OS를 포함하고 있고 이를 연결하기 위한 Hypervisor가 필요하기 때문에 사용하기에는 무거웠습니다.

컨테이너 방식은 VM 과 유사하지만 OS를 포함하지 않고 Host의 OS를 사용하면서 경량화 하였습니다.이러한 방식 때문에 보다 가볍게 사용할 수 있게 되면서 어플리케이션 생성과 배포에 훨씬 효율적으로 사용 할 수 있게 되었습니다.

현재는 VM과 컨테이너 환경을 혼합해서 많이 사용하기도 합니다.(물리적인 서버를 VM으로 나누어서 Kubernetes 클러스터로 구성한 후 어플리케이션을 배포하는 방법) 주로 VM은 서버 가상화에, 컨테이너는 어플리케이션 가상화에 사용합니다.

이제 도커 기본 사용방법에 대한 내용을 알아보겠습니다. 실습의 경우 Docker Desktop을 사용하였습니다.

3. 이미지 PULL and Push


컨테이너는 이미지를 기반으로 생성됩니다. 실행에 필요한 요소(환경, 코드, 실행파일 등)를 이미지로 만들어 컨테이너를 실행 할 수 있도록 도와줍니다.이러한 이미지는 레이어(층) 구조로 만들어집니다. 가장 베이스가 되는 이미지(주로 OS 관련 이미지를 사용)를 가장 아래에 쌓고 그 위에 필요한 패키지들을 설치 하는 등 어플리케이션에 필요한 구조를 쌓아갑니다. 아래는 nginx 이미지를 inspect Layer를 확인한 내용입니다. 이러한 이미지는 도커 허브를 통해 다운받을 수 있습니다. 도커 허브에서 검색한 이미지를 docker pull [image name]을 통해 다운 받아 사용 할 수 있습니다. 아래와 같이 이미지를 다운받으면 로컬 리포지토리로 이미지가 다운 받게 됩니다. 컨테이너를 실행 할 때 우선적으로 로컬 리포지토리에 해당 이미지가 있는지 검색하고 없으면 docker hub에서 검색해 해당 이미지를 찾게 됩니다.(도커 허브와 같이 모두 접근 할 수 있는 리포지토리 외에 사내에서 nexus와 같은 private repository도 사용합니다) 로컬 리포지토리는 docker images로 확인 할 수 있습니다. 이러한 이미지로 작업을 진행하고 해당 이미지를 repository에 Push 또한 할 수 있습니다. docker hub에서 저의 repository를 생성하고 이미지를 repository의 주소로 tag하여 push 할 수 있습니다. 보통 my-repository 가 어플리케이션의 이름이 되고 ':' 뒤로 버전에 붙습니다. 도커 허브에서 무료로 이용할 수 있는 private repository가 1개 이기 때문에 저는 버전 대신 어플리케이션 이름을 통해 구분 했습니다. docker hub에서 아래와 같이 정상적으로 Push 되었음을 확인 할 수 있습니다.

4. 컨테이너 실행하기


컨테이너를 실행 할 때는 docker run [option] 커맨드를 사용합니다. 필수적인 옵션은 이미지 항목입니다. 이미지는 어떤 컨테이너를 실행시킬 지 기본이 됩니다. Python 이미지를 선택 할 경우 파이썬이 설치되어있는 컨테이너가 실행 됩니다. 이미지의 경우 docker hub를 통해 사람들이 올려 놓은 이미지를 사용할 수 있습니다. docker pull 명령어를 통해 로컬 리포지토리에 다운받고 컨테이너를 실행시킵니다. 그 외에 자주 사용하는 옵션의 경우 다음과 같습니다.
1. port
port 옵션의 경우 컨테이너에서 사용하는 포트와 호스트의 포트를 연결해 호스트를 통해 컨테이너에 접속 할 수 있도록 합니다.
다음과 같이 [host port : container port] 로 옵션을 설정하면 host의 IP를 통해서 컨테이너에 접속 할 수 있습니다.

2. Volume
volume은 컨테이너안의 파일과 컨테이너 안의 파일을 공유하는데 사용합니다. 컨테이너 안에서 작업한 내용을 호스트에서 관리하거나 컨테이너가 삭제되어도 데이터를 보존하기 위해 사용할 수 있습니다. -v [host_path:container_path]를 통해 마운트 합니다.

3. env
env 옵션은 컨테이너에 환경변수를 설정합니다. 보통 이미지 관리자가 컨테이너 실행에 필요한 환경변수를 전달합니다. 예를 들어 Postgres에서는 다음과 같이 비밀번호를 설정 할 수 있습니다.
docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres
4. 그 외의 옵션
-d 의 옵션은 백그라운드 실행, --name 컨테이너 이름 지정 , -it 컨테이너 내부에서 명령어를 실행합니다. 위의 경우 /bin/bash 명령어를 통해 쉘로 접속해 실행한 화면입니다.

5. 컨테이너 접속하기


컨테이너에 접속하는 방법은 두가지가 있습니다. 처음은 위에서 사용한 방식으로 기존에 없는 컨테이너를 실행시키면서 접속하는 방식입니다. -it 옵션 후 쉘에 접속하는 명령어를 통해 접속합니다. 다음과 같이 실행시키면서 접속 후 exit 명령어를 통해 빠져나왔습니다. 다음과 같이 빠져나올 경우 컨테이너가 stop 상태로 변합니다. 실행상태를 유지하면서 빠져나올 때는 ctrl + p + q 를 통해 나올 수 있습니다.

기존에 생성된 컨테이너는exec 명령어로 컨테이너에 접속 할 수 있습니다. docker ps -a 를 통해 현재 생성된 컨테이너를 확인 한 후 stop 상태인 python-container를 실행시킨 후 exec를 통해 접속했습니다.

6. 이미지 생성하기 & 저장하기


Docker hub에서 다운받아서 컨테이너를 실행하는 경우 외에도 이미지 생성이 필요한 경우가 있습니다. 이는 Dockerfile을 통해 이미지에 필요한 내용을 작성하고 이미지를 생성할 수 있습니다. Dockerfile 작성하는 내용도 다양한 옵션이 있으므로 기본적인 내용만 테스트하겠습니다. 예를 들어 아래와 같이 hello world 를 출력하는 flask API 파일이 있습니다.

#main.py
from flask import Flask
from flask_restx import Api, Resource

app = Flask(__name__)
api = Api(app)


@api.route('/hello')
class HelloWorld(Resource):
    def get(self):
        return {"hello": "world!"}

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=5000)

이를 실행하는 이미지를 만들기 위해 Dockerfile을 작성합니다.

#DOCKERFILE
FROM python                     #기본이 되는 Base 이미지 선택
WORKDIR /code                   # 컨테이너의 작업환경 생성
COPY main.py /code              # code 복사
RUN pip install flask-restx     # code를 실행하기 위한 라이브러리 설치
EXPOSE 5000                     # container에서 사용하기 위한 포트 expose
CMD ["python3","main.py"]       # 커맨드 실행

이미지는 레이어형식으로 구성됩니다. 현재 저는 python을 기본 이미지로 그 위에 코드를 복사해서 새로운 레이어를 쌓음으로서 저만의 이미지를 만들었습니다. 도커파일의 명령어도 다양하기 때문에 실제 이미지를 만들 때는 더 많은 학습이 필요합니다. 위에서 만든 이미지를 docker build 명령어를 통해 생성합니다. -t [image_name:version] 옵션을 통해 이미지 이름과 버젼을 지정해 생성하였습니다. 이후 이미지 리스트를 검색한 결과 정상적으로 생성된 것을 확인할 수 있습니다. 해당 이미지를 실행하면 정상적으로 flask app 이 실행되는 것을 확인 할 수 있습니다. 하지만 아래와 같이 localhost:5000번으로 접속시 오류가 발생합니다. 이는 위에서 설명한 Port 옵션을 따로 설정하지 않았기 때문에 컨테이너 내부에서만 접근이 가능합니다. 컨테이너 안에서 접속하면 정상적으로 실행 된 것을 확인할 수 있습니다.

하지만 항상 이미지를 생성할 때 Dockerfile을 작성해야하는 것은 아닙니다. 작업중인 컨테이너에서 저장하는 방식도 존재합니다. 예를 들어 현재 존재 하는 python_container에서 위의 flask 코드를 복사해 이미지를 저장해 보겠습니다.
docker cp 명령어를 통해 호스트에 존재하는 main.py 코드를 컨테이너 안으로 복사합니다. code를 실행하기 위한 library를 설치 하면 실행하기 위한 환경은 완료되었습니다. 이를 docker commit 명령어를 통해 저장합니다이후 해당 이미지로 컨테이너 생성시 코드가 존재하고 실행이 가능합니다. 하지만 이 경우 Dockerfile의 CMD옵션 처럼 컨테이너 생성시 자동으로 실행하는 설정 등을 할 수 없습니다. 이 이미지를 base이미지로 도커파일을 생성하는 방법도 있을 수 있을거 같습니다.

7. 마무리

실무에서는 network,private repository와 연동, docker config,docker-compose 등 학습할 내용이 굉장히 많습니다.위에서 작성한 내용은 도커의 아주 기본적인 사용방법만 작성한 것입니다. 컨테이너 환경으로 사용하다 보면 편의성은 확실하게 느낄 수 있습니다. 아직 컨테이너 환경을 도입하지 않은 회사도 많지만 앞으로 이에 대한 관심을 더욱 커질 것으로 느껴집니다. 도커를 학습하며 도움이 되는 글이였으면 좋겠습니다.

0개의 댓글