Docker
- 간단히, App을 container 기반으로 패키징 할 수 있는 가상화 도구이다.
(VM과 container의 차이에 대해서 간단히 살펴보자.)
- VM: 하나의 물리적 컴퓨터(host OS) 상에서, 여러 VM이 각자의 OS(guest)를 가질 수 있다.
- 각 OS는 해당 컴퓨터의 물리적 리소스를 분할해서 사용한다.
- container: 하나의 물리적 컴퓨터 상에서, 여러 container는 각자의 OS를 갖지 않는다.
- 각 container는 해당 컴퓨터의 물리적 리소스를 함께 사용한다.
- 따라서 docker의 경우, host OS상에 engine이 존재하는 형태로,
container마다 독립적인 실행 환경을 갖게 되는 것일 뿐이다.
- App에 대한 실행 환경을 특정함으로써,
다른 machine에서도 항상 동일한 환경으로 App을 실행할 수 있게 해준다.
- 팀원 각자 개발함에 있어서 local PC에서 App을 실행할 때,
통일된 환경(버전 등)을 이용할 수 있다는 점에서 용이하다.
- docker container OS로는 기본적으로 Linux 기반 OS을 지원한다.
따라서, host OS도 Linux 기반 OS여야 한다.
(container는 host OS의 kernel을 공유하는 것이기 때문이다.)
- 하지만 Docker Desktop이, Windows에서도 linux container를 사용할 수 있게 해준다.
(container를 만들 때, LinuxKit(linux VM)을 이용한다고 한다.)
이에 따라, WSL2 혹은 Hyper-V를 이용한 별도의 가상화를 이용한다.
- WSL2는 Linux kernel을 내장시킨 것으로, Linux 명령어들을 사용할 수 있게 해준다.
- (기존에 git bash를 이용하고 있었다면, 터미널에 'wsl'를 입력함으로써
완전한 Linux 명령어를 이용할 수 있게 된다.)
- (Docker Desktop을 통하지 않고, docker를 직접 설치하여 사용할 수도 있지만
Docker Desktop을 이용하여 docker를 설치하면,
해당 machine에서 terminal에 관계없이(WSL2던 아니던),
같은 환경을 유지하면서 docker를 사용할 수 있다는 등의 편리한 점이 있다.)
- (WSL2도 Hyper-V를 기반으로 가상화한 것이라고 한다.)
- (예전엔 Docker Toolbox로써,
VMware, VirtualBox와 같은 VM을 이용하여 docker를 사용하기도 했다고 한다.
(헷갈릴 수 있어서 덧붙이자면, 여기서 VM을 이용한다는 것은
docker가 VM과 대비되는 container 기반이라는 개념과는 다른 얘기이다.
단지, host OS가 Windows 등일 때, Linux인 VM에서 docker를 사용한다는 말이다.)
하지만, 이 방법으로 docker를 사용하는 것은 더 이상 불가능하다.)
- (Windows container도 사용 가능하지만, 잘 쓰이지 않는다.)
- AWS EC2 서버 등에서 docker를 이용하기 위해서는,
’Install Docker Engine on Ubuntu’ 를 검색한 결과를 따라한다.
concepts
dockerfile
- docker container를 만들기 위한 첫걸음이다.
- 하나의 설계도로써, docker image를 build할 수 있다.
- 하나의 App으로써 실행될 때 필요한 것들을 세팅한다.
- 프로그래밍 언어로 작성된 소스 코드 파일
- 각종 dependency 설치: framework, library
- config: env variables
- system tool: ubuntu, git
- setup script: App 실행 관련 명령어
- DB server와 web server 등은 App과 격리하여, 다른 container에서 실행하곤 한다.
- container가 아닌 host OS 상에서 실행할 목적으로,
따로 image를 만들지 않고 사용하기도 한다.
- docker-compose를 통해, 작성된 여러 dockerfile들로 각 image를 만들고,
여러 container로써 함께 연결해서 동작하도록 할 수 있다.
- 주로, 하나의 서비스를 위해 필요한 App, DB, web server 등을 함께 실행하고자 사용한다.
- 기존에 각 dockerfile 마다 직접 image를 build하고 container를 run했던 것에 비해,
docker-compose up
으로 간편하게 build 및 run 할 수 있다.
- 확장자는 .yaml이다.
docker image
- 앞서 dockerfile에서 세팅된 것을 토대로 실행되는 App의 snapshot으로 볼 수 있다.
- 최초 만들어진 후, 이를 수정할 수는 없다.
- docker container를 만들 수 있다.
docker container
- 독립된 환경(개별적인 filesystem)에서, 앞서 생성된 docker image를 실행할 수 있는 것을 말한다.
- 즉, container 안에서 App이 동작한다.
- docker container들은 서로 간에 모두 분리되어 있고, 독립적이다.
- 이로 인해, 하나의 서버에 여러 container를 띄울 수 있다.
- docker swarm이나 k8s와 같은 orchestration을 함께 사용하여
cluster나 pod 단위로 container(s)를 효율적으로 관리할 수 있다.
- container 안에서 동작하는 App은, 수정이 가능하다.
usage
- local PC와 App을 실행할 다른 machine(linux 서버 or PC)에 docker를 설치한다.
- local에서 dockerfile을 작성하고, image를 build한다.
- 해당 image를 Container Registry에 push한다.
- 해당 image를 다른 machine에서 pull한다.
- 해당 machine에서는, image를 토대로 container를 생성하여 App을 실행한다.
- Container Registry은 Cloud로써, public(Docker Hub, etc.) 또는 private(AWS, etc.)을 사용한다.
- (docker 실습을 위해 간단한 App을 준비하자.)
from flask import Flask
def create_app():
app = Flask(__name__)
return app
app = create_app()
@app.route('/')
def get_home_page():
return 'hello world!'
if __name__ == "__main__":
app.run(host='0.0.0.0')
- docker container를 통해 배포된 서버에 접속할 수 있게 하기 위해서는
host를 '0.0.0.0'으로 두어, 외부에 open 되도록 한다.
install docker
- OS에 맞게 Docker Desktop 설치.
- Windows의 경우 WSL2와 관련하여 Linux kernel 설치에 대한 팝업이 뜬다면,
안내에 따라 절차를 밟으면 된다.
docker --version
: Docker 설치 완료를 확인한다.
make dockerfile
- 앞서 WSL2를 설치한 것과 별개로, dockerfile 작성은 local에서 진행한다.
- dockerfile에 작성된 각 line은 layer system으로 구성된다고 한다.
- 빈번하게 변경될 수 있는 line일수록 아래에 작성하는 것이 좋다고 한다.
- 아래에 작성될수록 바깥 layer에 존재하며, 변경된 layer의 하위 것들은 cache를 사용한다.
- 따로 확장자는 없다.
ENV {env_variable_name} {value}
로, 환경 변수 등을 설정할 수도 있다.
FROM python:3.8-slim
- baseImage로 python이 기본적으로 설치된 것을 사용한다.
RUN pip3 install flask
- image 생성 과정에서 실행되는 명령어이다.
- shell 명령어도 가능하므로, pip install -r requirements.txt 형태로 주로 사용한다.
COPY . /app
- RUN과 같이, image 생성 과정에서 실행되는 명령어이다.
- 현재 dockerfile이 위치한 디렉터리(docker_practice/app)에 있는 것들을,
container 구동 전에 image 내부의 /app 디렉터리로 미리 복사해 놓는다는 의미이다.
WORKDIR /app
- image를 통해 container를 구동할 때, 아래의 명령어들을 실행할 디렉터리를 지정한다.
CMD ["python3", "app.py"]
- container가 구동된 후 실행할 명령어이다.
- 큰 따옴표로 감싸줘야한다.
build docker image
docker images
: build된 image를 확인할 수 있다.
docker rmi <image_id>
: image를 삭제한다.
- 단, 종료된 container 중에서도 해당 image를 사용하는 경우 삭제할 수 없다.
따라서, 해당 container를 종료 후 삭제하고 image를 삭제할 수 있다.
- id가 동일한 경우, image 이름(REPOSITORY)로 특정하여 제거할 수 있다.
docker tag {REPOSITORY_name}:{TAG_name} {~}:{~}
: 생성한 image의 이름과 tag를 변경한다.
- local에서 만든 image를 remote repository로 push하기 위해서 알맞게 작성해야 한다.
- 변경할 REPOSITORY_name은, {user_name}/{repo_name}으로 맞춰줘야 한다.
- 한 번 만든 image는 수정이 불가능하므로, 실제로는 새로운 이름으로 복사하는 것이다.
$ docker build -t flask-app .
- -t로, flask-app이라는 이름으로 image를 만든다.
- 맨 마지막 '.'은 build context로,
현재 디렉터리에 있는 dockerfile을 기준으로 image를 만든다는 의미이다.
- 이러면, local에 image를 build한 것이다.
run docker container in local
docker ps
: container를 확인할 수 있다.
docker logs <container_id>
: 실행 중인 container의 log를 확인할 수 있다.
docker kill <container_id>
: 실행 중인 container를 종료한다.
docker rm <container_id>
: container를 삭제한다.
- Docker Desktop 앱을 통해, GUI로 쉽게 container 관리를 할 수도 있다.
- 각 container의 terminal을 열 수도 있다.
$ docker run -d -p 5001:5000 flask-app
- build된 image로, container를 실행한다.
- -d로, container를 background에서 실행한다.
- -p로, port를 지정해준다.
- Flask 서버는 기본적으로 5000번 포트에서 실행된다.
- local machine(host)의 port인 5001번과 container port 5000번을 mapping한다.
- container에서 실행되는 Flask의 log를 보면, IP 주소가 172.X.X.X 형태이다.
- local machine에 5001번으로 들어오는 요청을,
5000번 포트인 container, 즉 Flask 서버가 실행되고 있는 곳으로 보낸다는 의미이다.
- 해당 웹 페이지는 localhost:5001으로 접근 가능하다.
push docker image into Docker Hub
- (Docker Hub에 sign up한다.)
- Create repository (repository name: docker-practice)
$ docker login
- push를 하기 위해선, login이 선행되어야 한다.
- Docker Hub repository에 해당 docker image를 저장할 계정 정보를 입력한다.
$ docker push {user_name}/{repo_name}:{tagname}
- 해당 docker image를, 로그인한 계정의 해당 repo_name으로 push한다.
- push할 image의 이름(REPOSITORY)은, {user_name}/{repo_name}이어야 한다.
pull docker image & run docker container with WSL2
- install docker와 함께 WSL2을 설치했다면, Linux에 docker가 설치되어 있을 것이다.
- (host가 동일하기 때문에 WSL2도 terminal이 local인데
좀 더 바람직한 실습은, AWS EC2 등 다른 host에서 pull하는 것이다.)
$ docker login
$ docker pull {user_name}/{repo_name}:{tag_name}
$ docker run -d -p 5001:5000 {user_name}/{repo_name}