본문 바로가기

Backend/Docker, k8s

Docker - Tutorial

* 목차

  - Docker Engine 설치

  - Docker Image 제작 (flask)

  - Docker Image 실행

  - Docker Image 배포

 

* 환경은 EC2의 ubuntu으로 진행합니다. 아래 링크를 참조해 주세요

 

AWS - EC2 초 간단 생성 + vscode 원격연결 (2023년 version)

독립된 서버가 급히 필요한데 주변에 아무것도 없다면? Linux OS가 필요한 상황인데 집에 Windows만 있는 상황이라면? Docker를 windows 환경에서 또 새로 구축하긴 귀찮죠... 그럴 때는 EC2를 사용해 봅시

tyoon9781.tistory.com

 

 


Docker Engine 설치

1. Docker Engine 설치 문서 접속

먼저 Docker의 공식문서 접속합니다.

 

Docker Docs: How to build, share, and run applications

 

docs.docker.com

 

Docker Engine을 설치하기 위해 상단의 Manuals에 들어갑니다.

상단에 Manuals에 들어간다.

 

Docker Engine메뉴를 왼쪽에서 찾아볼 수 있습니다. 클릭하고 페이지 가운데에 Install Docker Engine을 클릭합니다.

 

클릭하면 Docker Engine 페이지가 나옵니다.

필자는 Desktop이 아닌 Server를 설치할 것이기 때문에 아래쪽의 Ubuntu를 클릭합니다.

Desktop이 아닌 Server를 설치진행하도록 한다.

 

이제 여기서 안내하는 것들을 차례대로 진행하면 됩니다.

 

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인 경우 아래와 같은 문구를 확인할 수 있습니다.

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

docker 관련 get이 진행되었다.

 최신 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

 

실행하면 다음과 같이 동작하게 됩니다.

hello world image가 local에 없어 최신image를 받고 실행하는 상황이다.

 

이제 설치가 끝났으니 image를 만들어 보도록 합시다.

 


 

Docker image 생성 (flask)

이전에 helloworld를 실행했을 때 다음과 같은 동작이 일어났습니다.

  1. helloworld image 가 local에 있는지 확인
  2. image가 local에 없었으므로 최신 version image를 hub.docker.io에서 pulling
  3. image를 build 해서 container로 만듦
  4. Process가 Hello from docker! 를 출력

현재 docker process현황을 확인하면 helloworld container가 종료된 상태로 존재하는 것을 볼 수 있습니다. 여기서 ps는 process status를 의미합니다. Docker가 OS가 아닌 process라는 것을 한번 더 각인시켜 줍니다.

sudo docker ps -a

hello-world라는 image로부터 build된 container가 존재함을 확인할 수 있다.

 

Image도 한번 확인해 보겠습니다.

sudo docker image list

hello-world image를 확인할 수 있다.

 

이 container화 할 수 있는 image를 직접 만들어 보겠습니다.

 

1. dockerfile 생성

dockerfile은 지침서 같은 것입니다. 여러분이 만약에 새로운 Server에서 ubuntu os환경 구성 후 python flask로 web server를 실행한다고 하면 다음과 같은 작업을 하게 될 것입니다.

  1. ubuntu os 설치
  2. python 가상환경 만들기(virtual env)
  3. venv python에서 pip install flask를 진행 (requirements.txt로 더 많은 library를 설치할 수 있다)
  4. 기존에 작성된 flask app 복사
  5. 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

aws EC2의 보안규칙을 설정. 이걸 까먹고 Web server가 안되는 경우 원인을 찾기가 매우 곤란하다.

 

file tree

 

flask run command

flask run --host 0.0.0.0 --port 5000

 

결과

실제로는 public ip로도 접속 가능

 

flask app이 잘 작동하는 것을 확인했습니다. 이 상태로 바로 Dockerfile을 build 해 봅시다.

PATH는 현재 폴더이니 "."을 입력했습니다. PATH에 있는 Dockerfile이란 file이 존재하면 자동으로 docker가 인식해서 실행합니다.

docker build .

 

1분 동안 build작업 후 완료되었습니다.

[n/5] 로 dockerfile에 명령한 실제 작업이 잘 진행되는 것을 확인해 볼 수 있다.

 

이제 image가 생성되었는지 확인해 보겠습니다.

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

 

그럼 아래와 같은 결과를 확인할 수 있습니다.

 

그럼 접속해 볼까요? 안타깝게도 이 상태로는 연결이 되지 않습니다.

실제로는 public ip로 진행했습니다만 마찬가지로 연결이 안됩니다.

container는 OS에 docker engine이 얹어진 상태입니다. 그러니 port로 접속할 때 OS에서 docker engine과 연결이 되고, docker engine은 실행 중인 container에 port가 연결이 되어 있어야 container가 비로소 요청을 받을 수 있습니다.

 

저 빨간색 물음표 부분이 이어져야 비로소 Client의 5000 port 요청이 Container까지 이어지게 됩니다.

docker run에 저 빨간색 물음표 부분을 이어줍시다.

 

2. port option

우선 기존에 container 된 것을 지워줍시다.

Image가 a0966으로 되어 있으며 실행중이다.

 

이 container를 지우려면 먼저 멈추고 삭제해야 합니다. Container id로 지정해서 지워줍시다.

sudo docker stop f24
sudo docker rm f24

 

그리고 다시 docker run을 해보도록 합시다. 이번에는 port도 연결해 봅니다.

sudo docker run -p 5000:5000 a0966

 

이제는 제대로 접속이 되는 것을 확인해 볼 수 있습니다.

public ip로도 접속 됩니다.

 

이제 모든 port가 연결이 되었음을 확인할 수 있습니다. 저 중에 하나라도 연결이 안 되어 있다면 service를 하기 어렵습니다.

모든 port가 연결되었다.

 만약 이런 형태에서도 접속이 안된다면 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를 넘겨줄 것입니다.

port binding이 잘 동작함을 확인했다.

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

 

none이었던 image에 정보가 생겼다.

 

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을 발급하시길 바랍니다.

hub.docker.com에서 계정의 Account Settings > Security에서 확인할 수 있다.

 

 

docker push은 마치 git처럼 개발 코드를 commit 하고 push 하는 것과 같습니다. 명령어는 다음과 같은 형식을 가집니다.

docker push [OPTIONS] NAME[:TAG]

 

제가 만든 docker image를 push 해보겠습니다.

sudo docker push tyoon9781/flask_practice:version1.0

실제 test는 좀 다른 계정에서 진행했었습니다...ㅎㅎ

 

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}

 

전부 진행하면 다음과 같이 됩니다.

flask practice image가 다 사라진 모습이다.

 

이제 docker image를 pull 해보겠습니다.

sudo docker pull tyoon9781/flask_practice:version1.0

이렇게 검열할게 많을 줄 알았으면 그냥 tyoon9781계정으로 docker hub를 가입할 걸 그랬다...ㅠㅠ

 

image 목록을 보면 image가 pull 되었음을 확인해 볼 수 있다.

Image id는 그대로 유지되는게 신기합니다...그래서 Create도 그대로 로그기 유지되네요

 


마무리...

사실 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/

https://docs.docker.com/build/building/packaging/

https://docs.docker.com/

https://aws.amazon.com/ko/docker/

'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