이미지 출처는 링크 or 아이펠 교육 자료입니다.
개발 환경
서비스 환경
보통은 이러한 차이로 인해 배포가 쉽지 않을 것(최악의 경우, 서비스 데스 발생)
- 라이브러리 충돌
- 버전 충돌
- 데이터베이스 등의 충돌...
단점
- 서버 시스템에 또다른 오퍼레이션 시스템을 올리게 되다보니, 용량 이슈 발생
- 오퍼레이팅 시스템을 RAM 위에 올리고 구동을 해야 하기 때문에 막대한 자원 사용량이 요구됨
- 오퍼레이팅 시스템 내부의 커널, 스케줄러 등이 독립적으로 모두 동작해야 하기 때문에 CPU 오버헤드 발생
기존 설치 완료 -> 실습 바로 진행
$ docker help
$ docker ps -a
$ docker images
이미지 받아오기
컨테이너 생성
# hello-word 이미지 pull
$ docker pull hello-world
# 다운 확인
$ docker images | grep hello-world
hello-world
출처: Docker Hubdocker pull {url/image:tag}
- url : 저장소(설정하지 않을 경우, Docker Hub에서 다운)
- image : 이름
- tag : 지정 안할 경우 latest로 자동 지정
# hello-world 이미지 제거
$ docker rmi hello-world
hello-world:latest
가 사라졌음을 알 수 있음# hello-world pull & run
$ docker run -dit --name test hello-world
# 현재 도커의 컨테이너 리스트 확인(hello-world)
docker ps -a | grep hello-world
docker rmi
실행 시, 동작 안함컨테이너 삭제 후 ➡️ 이미지 삭제 가능!
- rm의 기본 동작: 잘 삭제 된 경우, 해당 값을 repeat해줌
$ docker rm {컨데이너 아이디}
--name
옵션 사용$ docker run --name test hello-world
$ docker rm test
같은 이름을 부여하게 되면, 나중에 받으려는 이미지의 경우, 에러가 발생할 것
$ docker logs {name or ID}
-dit
-i
옵션과 함꼐 사용참고: - 와 --
-
: 단어 가장 처음 글자 하나씩 사용 가능
--
: 단어 단위 옵션(fully)
$ docker run -dit --name nginx -p 8000:80 nginx
logs 옵션 -f
사용
프로세스 계속 진행 중인 상태
$ docker stop nginx
$ docker rm nginx # 강제 삭제 시 docker rm -f nginx
포트 포워드 명령 없이 바로 실행
내부에서 접근
$ docker rm -f nginx
$ docker rmi nginx
$ cd /tmp
$ git clone https://github.com/KennethanCeyer/mlops-quicklab.git
$ ll | grep mlops-quicklab
$ cd mlops-quicklab
$ ll
$ tree -L 2 .
$ cd basic
Dockerfile
$ cat Dockerfile
FROM
WORKDIR
COPY
RUN apt-get update
RUN pip install ...
CMD ...
FROM python:3.11
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
COPY ./app /code/app
RUN apt-get update
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
RUN : 이미지를 만드는 타이밍에 동작
CMD : 이미지를 실행시키는 타이밍에 동작
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
$ conda create -n quicklab-modu python=3.11
$ conda activate quicklab-modu
$ pip3 install -r requirements.txt # 로컬에 설치됨
$ uvicorn app.main:app --host 0.0.0.0 --port 8000
.
: 현재 폴더 위치를 의미-t
: tag$ docker build . -t fastapi-app:latest #latest는 생략 가능
docker login 옵션이 뜨는 경우 -> 로그인 진행 후 사용
$ docker run --name fastapi-app -p 8000:80 fastapi-app
컨테이너 한번에 종료
$ docker rm -f $(docker ps -qa)
이미지 한번에 삭제
$ docker rmi $(docker images -a)
필자 노트북의 경우, 기존에 사용하던 삭제하면 안되는 컨테이너가 있어 컨테이너 ID 지정하여 특정 컨테이너만 삭제함.
docker push <IMAGE>:<TAG>
)docker run <IMAGE>:<TAG>
)명령어 | 설명 |
---|---|
FROM | base image 지정(모든 Dockerfile은 FROM으로 시작) |
ENV | 환경 변수 |
USER | 명령어 실행 사용자 계정 설정 |
COPY | 파일 or 디렉터리를 이미지에 복사하는 명령어 |
WORKDIR | 작업 디렉터리 설정 |
RUN | 컨테이너 내 명령어 실행(패키지 설치 등) |
CMD | 컨테이너 시작 시 실행 명령어 |
ARG | Dockerfile에 쓰는 변수 정의(build할 때 끌어옴) |
- 파일 변경이 빈번한 커맨드가 위에 있다 ➡️ 비효율성이 아주 커진다는 의미이기도 함
- 커맨드 개수가 많아지는 것 또한 오버헤드를 만드는 것임..
- 즉, 최대한 1개의 커맨드로 표현될 수 있도록 잘 줄여줘야 함
$ brew install dive # 설치
$ dive fastapi-app
[소스 코드] ➡️ [FROM xxx: 24초] ➡️ [Add Codes: 1초] ➡️ [Install Deps: 4분] ➡️ [CMD xxx: 1초] ➡️ Docker Image
소스코드 업데이트 시마다, 4분이 넘는 시간동안 다시 기다려야한다는 것이 매우 비효율적인 것
[소스 코드] ➡️ [FROM xxx: 캐싱] ➡️ [Add Codes: 1초] ➡️ [Install Deps: 4분] ➡️ [CMD xxx: 1초] ➡️ Docker Image
$ cd optimization
$ docker build -f Dockerfile.base -t fastapi-app:base .
Dockerfile의 FROM절의 로컬 환경의 fastapi-app:base로 변경
빌드 ➡️ 속도가 빨라짐을 체감할 수 있음
docker build. -t fastapi-app:latest
$ vi app/main.py # Hello word 출력 부분 수정 후 저장
$ docker build -t fastapi-app:latest .
$ docker run -dit --name fastapi-app -p 8000:80 fastapi-app:lastest
DockerHub에 Repository 생성하여 원격 실행 테스트(base로 배포 예정)
유저 이름이 앞에 있어야, 해당 Docker Repository를 사용하는 것
$ docker tag fastapi-app:base hayannn/fastapi-app:base
$ docker push hayannn/fastapi-app:base
로그인이 안되어 있는 경우, 에러 발생하니 로그인 후 재실행하면 됨
Scale-up
⭐️ Scale-out ⭐️
현재 디렉터리 : /tmp/mlops-quicklab/dockers/swarm
is_prime
: 계산량이 많이 필요한 함수를 서비스에서 돌린다고 가정하기 위해 만듦find_primes
: 모든 소수를 찾는 로직$ python3 app/main.py
0.0.0.0:8000/docs
로 접속ModuleNotFoundError: No module named 'pydantic_settings'
- 해결
$ pip3 install -r requirements.txt
locustfile.py
from locust import HttpUser, between, task
import random
class WebsiteUser(HttpUser):
host = "http://localhost:8000"
wait_time = between(1, 5)
@task
def get_primes(self):
end = random.randint(1000, 50000)
start = random.randint(1, end // 2)
self.client.get(f"/primes?start={start}&end={end}")
$ pip install locust
$ locust -f locustfile.py
서버 실행 & locust 재구동
실패가 발생하지 않고, RPS가 15 ~ 18 선을 유지
유저 200
유저 1,000 ➡️ failures 발생 시작
유저 2000
$ docker run -dit --name fastapi-app -p 8000:8000 kennethan/fastapi-app-prime
$ docker ps -a | grep fastapi-app
Google Cloud 이용
이전에 사용하던 계정이 있다면, 새 계정으로 진행할 것
compute engine
검색 및 사용 클릭
Create instance
클릭
설정
docker-swam-worker1
asia-northeast3(서울)
e2-micro
변경
클릭 -> 운영체제 ubuntu
, 크기 40으로 변경인스턴스 생성 완료
copy&paste
해주면 됨!# 1번
sudo groupadd docker
# 2번
sudo usermod -aG docker $USER
여기까지 진행 후, 창을 모두 닫고 다시 SSH 창 열기
$ docker run -dit --name fastapi -p 80:8000 kennethan/fastapi-app-prime
$ docker ps -a
$ docker swarm init
$ docker swarm join --token {토큰내용}
docker service ls
docker node ls
: 노드 내용 확인docker-compose.yml
을 이용한 deploy
내용 copy
cat docker-compose.yml | pbcopy
1번 SSL 콘솔
$ cd /tmp
$ vi docker-compose.yml
$ docker stack deploy -c docker-compose.yml fastapi
$ docker service ls
1,2, 3번에 나눠서 뜬 걸 확인할 수 있음
1 -> 1개
2 -> 2개
3 -> 1개
docker rm -f $(docker ps -qa)
docker service ls
로 서비스 확인디렉터리 : /tmp/mlops-quicklab/dockers/compose
$ docker compose up
$ docker compose down --volumes
Volume?
- "컨테이너가 죽더라도, DB에 있는 데이터 등의 민감한 데이터들이 날아가지 않아야 하며" + "여러 컨테이너가 데이터를 공유해야하기 때문"에 Host에 내용을 저장하고, 그걸 링크로 불러오는 것
$ docker compose up -d
컨테이너의 실행 여부와 관련없이, docker compose 동작 가능
터미널에 복사해 설치
curl -LO https://github.com/kubernetes/minikube/releases/latest/download/minikube-darwin-arm64
sudo install minikube-darwin-arm64 /usr/local/bin/minikube
$ minikube start --cpus 4 --memory 7844 --disk-size=40g
kubectl
이용$ kubectl create deployment hello-minikube --image=kicbase/echo-server:1.0
$ kubectl expose deployment hello-minikube --type=NodePort --port=8080
$
$ kubectl get services
$ kubectl get services hello-minikube
$ minikube service hello-minikube
$ kubectl port-forward service/hello-minikube 7080:8080
$ kubectl delete service hello-minikube
$ kubectl delete deployment hello-minikube