* 목차
- Docker Engine 설치
- Docker Image 제작 (flask)
- Docker Image 실행
- Docker Image 배포
* 환경은 EC2의 ubuntu으로 진행합니다. 아래 링크를 참조해 주세요
Docker Engine 설치
1. Docker Engine 설치 문서 접속
먼저 Docker의 공식문서 접속합니다.
Docker Engine을 설치하기 위해 상단의 Manuals에 들어갑니다.
Docker Engine메뉴를 왼쪽에서 찾아볼 수 있습니다. 클릭하고 페이지 가운데에 Install Docker Engine을 클릭합니다.
클릭하면 Docker Engine 페이지가 나옵니다.
필자는 Desktop이 아닌 Server를 설치할 것이기 때문에 아래쪽의 Ubuntu를 클릭합니다.
이제 여기서 안내하는 것들을 차례대로 진행하면 됩니다.
2. 구버전 삭제
먼저 충돌 방지 목적을 위해 구버전을 삭제해 달라고 합니다.
- docker.io
- docker-compose
- docker-doc
- podman-docker
이런 구버전을 삭제하기 위한 명령어는 아래와 같습니다.
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done
docker를 깔아본 적 없는 server인 경우 아래와 같은 문구를 확인할 수 있습니다.
3. apt 저장소 설정
- HTTPS를 통해 repository를 사용할 수 있도록 package index를 update 하고 아래와 같은 package를 설치합니다.
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
- Docker의 공식 GPG key를 추가합니다. 배포파일이 신뢰할 수 있는지를 확인하기 위함입니다.
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
apt repository source list에 docker gpg key를 추가하기 위해 아래와 같은 명령어를 입력합니다.
echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
이제 docker engine 설치 준비가 끝났습니다.
4. docker engine 설치
apt-get update를 진행합니다. repository 설정이 제대로 되었다면 다음과 같은 get이 추가로 들어오게 됩니다.
sudo apt-get update
최신 version을 받도록 합시다. 설치할 package는 다음과 같습니다.
- docker-ce : docker engine Community Edition
- docker-ce-cli : docker 명령어 interface
- containerd.io : OCI 표준의 container runtime.
- docker-buildx-plugin : multi architecture build 등 다양한 build option을 지원하는 CLI 플러그인
- docker-compose-plugin : 여러 컨테이너를 띄우는 Docker application을 정의하고 실행하는 플러그인
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
이렇게 되면 Docker 설치가 끝났습니다. 한번 설치가 제대로 되었는지 hello-world를 실행해보겠습니다.
sudo docker run hello-world
실행하면 다음과 같이 동작하게 됩니다.
이제 설치가 끝났으니 image를 만들어 보도록 합시다.
Docker image 생성 (flask)
이전에 helloworld를 실행했을 때 다음과 같은 동작이 일어났습니다.
- helloworld image 가 local에 있는지 확인
- image가 local에 없었으므로 최신 version image를 hub.docker.io에서 pulling
- image를 build 해서 container로 만듦
- Process가 Hello from docker! 를 출력
현재 docker process현황을 확인하면 helloworld container가 종료된 상태로 존재하는 것을 볼 수 있습니다. 여기서 ps는 process status를 의미합니다. Docker가 OS가 아닌 process라는 것을 한번 더 각인시켜 줍니다.
sudo docker ps -a
Image도 한번 확인해 보겠습니다.
sudo docker image list
이 container화 할 수 있는 image를 직접 만들어 보겠습니다.
1. dockerfile 생성
dockerfile은 지침서 같은 것입니다. 여러분이 만약에 새로운 Server에서 ubuntu os환경 구성 후 python flask로 web server를 실행한다고 하면 다음과 같은 작업을 하게 될 것입니다.
- ubuntu os 설치
- python 가상환경 만들기(virtual env)
- venv python에서 pip install flask를 진행 (requirements.txt로 더 많은 library를 설치할 수 있다)
- 기존에 작성된 flask app 복사
- flask 실행
이렇게 6단계를 진행하게 될 것입니다. 그런데 이 작업을 본인의 컴퓨터에 한 번 하면 전혀 문제가 없겠지만 한 10대 정도 서버에 환경구축을 해야 한다고 합시다. 그러면 매우 반복적이고 귀찮은 작업이 될 것입니다. 누군가가 마법처럼 저 작업을 자동으로 해줄 수는 없을까...라는 생각을 하셨다면 그 마법 같은 작업을 docker로 너무 쉽게 할 수 있습니다. dockerfile을 작성하고 docker build를 실행하면 원하는 환경이 설정된 image가 생성되게 됩니다. image를 서버 10대에 배포하고 각각 서버에 docker run을 하면 원하는 환경이 갖춰진 container가 실행되는 것입니다. 서버 일일이 할 일을 한번에 해결할 수 있게 됩니다 :)
Dockerfile에는 위의 작업을 다음과 같이 작성할 수 있습니다.
# 1.ubuntu os image base. 공식 image는 {iamge}:{version}으로 명명한다.
FROM ubuntu:22.04
# 2.python install. docker 자체가 container니까 virtual environment는 필요 없다.
RUN apt-get update && apt-get install -y python3 python3-pip
# 3. python library 설치
RUN pip install flask==2.1.*
# 4. copy flask code (to /flask_app/app.py in container)
WORKDIR /flask_app
COPY app.py .
# 5. run flask
EXPOSE 8000
CMD flask run --host 0.0.0.0 --port 8000
어떤가요? 이렇게 작성하고 build 하면 image가 생성됩니다. 그리고 그 image는 Windows환경, Mac 환경이건 docker만 깔려있으면 해당 image를 container로 소환하여 8000 port의 flask web server로 사용할 수 있게 됩니다.
Dockerfile의 주요한 명령어는 다음과 같습니다.
명령어 | 쉬운 설명 | 예시 |
FROM | 기반이 될 image를 불러온다. | FROM python:3.7-alpine |
RUN | image를 꾸미고 commit한다. | RUN pip install -r requirements.txt |
CMD | docker run에서 실행될 default 명령어 | CMD echo "This is a test." | wc - |
LABEL | key-value형태의 metadata 추가 | LABEL multi.label1="value1" multi.label2="value2" other="value3" |
EXPOSE | container가 수신대기할 port | EXPOSE 80 |
ENV | 환경 변수 설정 | ENV FLASK_APP=app.py |
ADD | container에 새로운 파일 추가 | ADD test.txt relativeDir/ |
COPY | 파일을 container에 복사 | COPY test.txt relativeDir/ |
ENTRYPOINT | container 생성시 실행할 명령어 | ENTRYPOINT exec top -b |
이 Dockerfile을 Tutorial에서 실제로 작성하고 build 해보겠습니다.
2. docker build
docker build 명령어를 쓰면 dockerfile을 읽고 build를 수행 후 image를 만들게 됩니다.
docker build의 명령어는 다음과 같은 형식을 가집니다.
docker build [OPTIONS] PATH | URL | -
PATH나 URL(git url도 됩니다)에 있는 지침서(Dockerfile과 같은)를 읽고 docker는 image를 build 하게 됩니다. build를 진행하기 전에 copy 할 flask app을 작성해 봅시다.
app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World"
Dockerfile. port를 저는 5000번으로 바꿨습니다. AWS의 server에서도 5000을 접속 가능하도록 보안처리 했습니다.
# 1.ubuntu os 설치
FROM ubuntu:22.04
# 2.python install. docker 자체가 container니까 virtual environment는 필요 없다.
RUN apt-get update && apt-get install -y python3 python3-pip
# 3. python library 설치
RUN pip install flask==2.1.*
# 4. copy flask code (to /flask_app/app.py in container)
WORKDIR /flask_app
COPY app.py .
# 5. run flask
EXPOSE 5000
CMD flask run --host 0.0.0.0 --port 5000
file tree
flask run command
flask run --host 0.0.0.0 --port 5000
결과
flask app이 잘 작동하는 것을 확인했습니다. 이 상태로 바로 Dockerfile을 build 해 봅시다.
PATH는 현재 폴더이니 "."을 입력했습니다. PATH에 있는 Dockerfile이란 file이 존재하면 자동으로 docker가 인식해서 실행합니다.
docker build .
1분 동안 build작업 후 완료되었습니다.
이제 image가 생성되었는지 확인해 보겠습니다.
image는 생성이 되었지만 <none>이란 이름으로 적힌 것을 확인해 볼 수 있습니다. 왜 그런 것일까요? 이유는 Repository와 Tag를 설정하지 않았기 때문입니다. 이렇게 <none>으로 적히는 경우는 여러 가지 이유로 인해 생기는데 이런 image를 dangling image라고 합니다.
<none>으로 생성된 image 긴 하지만 container(process)로 만들어 보겠습니다. 정상작동할까요?
Docker image 실행
1. docker run
build 한 image를 container로 만들기 위해서는 run이라는 명령어를 사용해야 합니다.
docker run 명령어 사용법은 다음과 같습니다.
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
위에서 새로 만든 flask image의 이름은 <none>이고 Image ID는 a0966...으로 시작합니다. 이 ID를 가지고 실행해 보도록 하겠습니다. 다행히도 모든 ID의 길이를 입력하지 않고 docker가 구분할 수 있을 만큼 입력하면 알아서 인식합니다.
sudo docker run a0966
그럼 아래와 같은 결과를 확인할 수 있습니다.
그럼 접속해 볼까요? 안타깝게도 이 상태로는 연결이 되지 않습니다.
container는 OS에 docker engine이 얹어진 상태입니다. 그러니 port로 접속할 때 OS에서 docker engine과 연결이 되고, docker engine은 실행 중인 container에 port가 연결이 되어 있어야 container가 비로소 요청을 받을 수 있습니다.
저 빨간색 물음표 부분이 이어져야 비로소 Client의 5000 port 요청이 Container까지 이어지게 됩니다.
docker run에 저 빨간색 물음표 부분을 이어줍시다.
2. port option
우선 기존에 container 된 것을 지워줍시다.
이 container를 지우려면 먼저 멈추고 삭제해야 합니다. Container id로 지정해서 지워줍시다.
sudo docker stop f24
sudo docker rm f24
그리고 다시 docker run을 해보도록 합시다. 이번에는 port도 연결해 봅니다.
sudo docker run -p 5000:5000 a0966
이제는 제대로 접속이 되는 것을 확인해 볼 수 있습니다.
이제 모든 port가 연결이 되었음을 확인할 수 있습니다. 저 중에 하나라도 연결이 안 되어 있다면 service를 하기 어렵습니다.
만약 이런 형태에서도 접속이 안된다면 Server의 port 방화벽을 Check 해보시기 바랍니다.
-p option에서 port를 2가지 적었었습니다. 그 의미를 살펴봅시다. 5000:5000에서 앞에는 client의 요청 port이고, 뒤에는 docker 내의 expose 했던 port로 넘겨줄 port입니다.
만약에 5173:5000으로 작성한다면 127.0.0.1:5173으로 접속해야 docker container들 중에 5000 port로 service 하고 있는 곳으로 request를 넘겨줄 것입니다.
Docker image 배포
1. 배포할 image build
*혹시 hub.docker.com에 계정이 없으시면 회원가입을 먼저 진행하시길 바랍니다.
이렇게 잘 만들어진(?) image를 이제 누군가한테 전달하면 그 사람은 이제 app.py service를 바로 실행할 수 있을 것입니다. image를 hub.docker.com에 업로드를 해보고 확인해 봅시다.
우선 image의 이름이 아직 <none>이므로 이름을 제대로 정해줍시다. docker build 단계에서 --name option을 통해 repository 이름을 정할 수 있습니다. tag는 version1.0으로 정합니다.
sudo docker tag a0966 tyoon9781/flask_practice:version1.0
a0966은 tag가 없던 image의 id입니다. tyoon9781/flask_practice:version1.0은 {배포기관}/{repository이름}:version을 따릅니다. 배포기관의 이름이 hub.docker.com에서 가입한 user profile과 일치해야 정상적으로 배포가 가능합니다.
사실 image를 build 할 때 tag option을 사용하면 이렇게 따로 붙이지 않아도 됩니다.
sudo docker -t tyoon9781/flask_practice:version1.0 build .
이제 이 image를 가지고 hub.docker.com에 업로드해보겠습니다.
2. Docker push
docker push를 하려면 login부터 해야 합니다.
sudo docker login
로그인에 성공하면 비밀번호가 평서문으로 저장되었다고 하면서 credential 설정을 하라고 경고합니다. 이것이 찜찜하다면 2단계 인증을 진행하셔서 token을 발급하시길 바랍니다.
docker push은 마치 git처럼 개발 코드를 commit 하고 push 하는 것과 같습니다. 명령어는 다음과 같은 형식을 가집니다.
docker push [OPTIONS] NAME[:TAG]
제가 만든 docker image를 push 해보겠습니다.
sudo docker push tyoon9781/flask_practice:version1.0
Push가 정상적으로 진행된 것을 확인해 볼 수 있습니다. docker허브 페이지에서 확인해 보겠습니다.
3. docker pull
이제 이 docker를 pull 해서 가져와 봅시다.
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
먼저 기존에 있던 image를 삭제해 보겠습니다.
sudo docker stop {container}
sudo docker rm {container}
sudo docker rmi {image}
전부 진행하면 다음과 같이 됩니다.
이제 docker image를 pull 해보겠습니다.
sudo docker pull tyoon9781/flask_practice:version1.0
image 목록을 보면 image가 pull 되었음을 확인해 볼 수 있다.
마무리...
사실 Docker는 여러분들에게 필요없을지도 모릅니다. 서버 한대에서 개발하고 운영하고 배포하면 사실 환경 변화로 인한 스트레스를 체험해 보시지 않았을 것이고, 그러면 왜 가상환경, 미리 Setting된 환경이 필요한지 모르기 때문입니다.
하지만 여러분이 하드웨어, CUDA version, OS version, Library version에 따라 다르게 동작하는 Software, framework를 만나서 힘들었던 경험이 있으셨다면 docker는 최고의 프로그램입니다. 기존의 환경을 똑같이 맞춰주기 위한 Virtual Machine(VM)은 환경마다 개별 OS가 필요해서 매우 무거웠지만 docker는 Host OS에 자원관리를 맞기기 때문에 훨씬 가볍고 사용하기 편리합니다. 다양한 하드웨어와 OS, library version관리에 지치셨다면 Docker를 꼭 써보시길 바랍니다.
* reference
https://docs.docker.com/engine/reference/builder/
https://docs.docker.com/engine/reference/commandline/pull/
https://docs.docker.com/engine/reference/commandline/push/
https://docs.docker.com/engine/reference/commandline/tag/
https://docs.docker.com/engine/reference/commandline/build/
'Backend > Docker, k8s' 카테고리의 다른 글
Docker + Frontend(Nginx, React) + Backend(Nginx, Gunicorn, Django, PostgreSQL) (7) | 2023.06.20 |
---|---|
Docker Compose - tutorial (0) | 2023.06.18 |
k8s - Service (0) | 2023.04.28 |
k8s - Pod (0) | 2023.04.27 |
Docker - ubuntu에 Docker 설치 (0) | 2023.04.26 |