Docker소개
-- 가상화란?
-- Docker 등장하기 전
-- Docker 소개
-- Docker로 할 수 있는 일
Docker 실습하며 배워보기
-- 설치하고 실행하기
-- Docker Image 만들기
-- Registry에 Docker Image Push
Docker 이미지로 배포하기
-- Serverless Cloud 서비스(Cloud Run)
-- Compute Engine에 Docker Image배포하기 (Streamlit)
-- Docker Compose
개발할 때, 서비스 운영에 사용하는 서버에 직접 들어가서 개발하지 않음
Local환경에서 개발하고, 완료되면 Staging서버, Production서버에 배포
개발을 진행한 Local환경과 Production서버 환경이 다른 경우
예)
Local환경은 윈도우
서버 환경은 Linux
OS가 다르기 때문에 라이브러리, 파이썬 등 설치할 때 다르게 진행해야 함.
Local환경과 서버가 같은 OS를 사용해도, 서버에서 올바르게 작동하지 않을 수 있음
예)
Local의 환경 변수
Production서버의 환경 변수(Env)
Production 서버의 사용자 그룹, Permission
예)
Jupyter Notebook 서버를 만들기 위해 클라우드에서 클릭-이름-클릭 만들기 => 인스턴스로 접속해서 필요한 패키지 설치하는 과정
운영하고 있는 Server가 100대라면?
이부분에서 생기는 고민: 서버 환경까지도 모두 한번에 소프트웨어화 할 수 없을까?
밀키트처럼 만들어서 집에서도 사용하고 레스토랑에서도 사용하는 방법은?
이런 고민을 해결하기 위해 나온 개념이 가상화
(엄밀하게는 하드웨어 가상화 등 더 넓은 개념이지만, 여기선 소프트웨어 가상화로 한정)
Research/Production 환경에서 공통적으로 사용하는 일종의 템플릿
특정 소프트웨어 환경을 만들고, Local, Production 서버에서 그대로 활용
가상화 기술로 주로 VM(Virtual Machine)을 사용
예)
호스트머신은 Window인데 Window에서 Linux를 실행
호스트머신은 Mac인데, Mac에서 Window를 실행
GCP의 Compute Engine 또는 AWS EC2가 이런 개념을 활용
그러나 OS위에 OS를 하나 더 실행시키는 점에서 VM은 굉장히 리소스를 많이 사용
Container: VM의 무거움을 크게 덜어주면서, 가상화를 좀 더 경량화된 프로세스의 개념으로 만든 기술
Container 기술을 쉽게 사용할 수 있도록 나온 도구가 바로 Docker
도커 Image
다른 사람이 만든 소프트웨어를 가져와서 바로 사용할 수 있음
-- MySQL을 Docker로 실행
-- Jupyter Notebook을 Docker로 실행
다른 사람이 만든 소프트웨어 : Docker Image
-- OS, 설정을 포함한 실행 환경
-- Linux, Window, Mac 어디에서나 동일하게 실행할 수 있음
자신만의 이미지를 만들면 다른 사람에게 공유할 수 있음
-- 원격저장소에 저장하면 어디서나 사용할 수 있음
원격 저장소 : Contatiner Registry
-- 회사에서 서비스를 배포할 때는 원격 저장소에 이미지를 업로드하고, 서버에서 받아서 실행하는 식으로 진행
도커 공식 홈페이지에서 자신의 운영체제에 맞는 Docker Desktop
설치 후, 터미널에서 Docker 커맨드가 동작하는지 확인
docker pull "이미지 이름:태그"
docker pull mysql:8로 mysql 8 버전의 이미지를 다운
docker images
다운받은 이미지 확인
docker run "이미지 이름:태그"
다운받은 MySQL 이미지 기반으로 Docker Container를 만들고 실행
docker run --name mysql-tutorial -e MYSQL_ROOT_PASSWORD=1234 -d -p 3306:3306 mysql:8
name mysql-tutorial :
-- 컨테이너 이름 : 지정하지 않으면 랜덤으로 생성, 나중에 확인하기 위해 이름 꼭 붙이기
-e MYSQL_ROOT_PASSWORD=1234 :
-- 환경변수 설정 : 사용하는 이미지에 따라 설정이 다름
-- MySQL: 환경변수를 통해 root계정의 비밀번호를 설정
-d : 데몬(백그라운드)모드
-- 컨테이너를 백그라운드 형태로 실행
-- 이 설정을 하지 않으면, 현재 실행하는 셀 위에서 컨테이너가 실행
-- 컨테이너의 로그를 바로 볼 수 있으나, 컨테이너를 나가면 실행 종료
-p 3306:3306 포트 지정
-- 로컬 호스트 포트: 컨테이너 포트 형태로, 로컬포트 3306으로 접근 시 컨테이너 포트 3306으로 연결되도록 설정
-- mysql은 기본적으로 3306 포트를 통해 통신
-- 로컬호스트: 우리의 컴퓨터
-- 컨테이너 : 컨테이너 이미지 내부
mysql:8 아까 pull해온 mysql:8을 가져오겠다
실행한 컨테이너는 docker ps 명령어로 확인할 수 있음
docker exec -it "컨테이너 이름(혹은 ID)" /bin/bash
mysql -u root -p
MySQL 프로세스로 들어가면 MySQL쉘 화면이 보임 -> MySQL이 실행됨 !
작동을 멈춘 컨테이너는 docker ps -a 명령어로만 확인할 수 있음
(docker ps는 실행중인 컨테이너 목록만 보여줌)
docker rm "컨테이너 이름(ID)"
멈춘 컨테이너를 삭제
(멈춘 컨테이너만 삭제할 수 있지만 docker rm "컨테이너 이름(ID)" -f 로 실행중인 컨테이너도 삭제 가능)
그 외
docker run 할 때 파일을 공유하는 방법
Volume Mount
-- Docker Container 내부는 특별한 설정이 없으면 컨테이너를 삭제할 때 파일이 사라짐
-- (=Host와 Container와 파일 공유가 되지 않음)
-- 만약 파일을 유지하고 싶다면 Host(우리의 컴퓨터)와 Container의 저장소를 공유해야함
Volume Mount를 진행하면 Host와 Container의 폴더가 공유됨
-v옵션을 사용하며, -p(Port)처럼 사용함. -v Host_Folder:Container_Folder
예)
docker run -it -p 8888:8888 -v /some/host/folder/for/work:/homejovyan/workspace/jupyter/minimal-notebook
Dockerhub에 공개된 모든 이미지를 다운받을 수 있음
MySQL도 Dockerhub에서 다운로드
Dockerhub에 왠만한 오픈소스들이 공개되어 있고, 우리는 필요한 이미지를 찾아 실행
이미지를 다운받아 사용하는 법을 알았으니, 이제 직접 Docker Image 만들기
간단한 FastAPI 애플리케이션을 실행하는 서버를 Docker Image 생성
먼저 폴더를 하나 만들고, 여기에 가상환경 세팅과 FastAPI패키지를 설치
GET /hello로 요청하면 메시지를 전달하는 간단한 코드를 작성
pip freeze: 설치한 라이브러리를 모두 보여줌
또는 pip list --not-required --format=freeze : 의존성에 따라 설치된 라이브러리는 보이지 않음
pip로 설치한 라이브러리를 모두 requirements.txt에 저장
pip freeze > requirements.txt
Dockerfile라는 파일을 만들어 아래처럼 작성 (Docker Image를 빌드하기 위한 정보가 담김)
FROM python:3.8.7-slim-buster : FROM 이미지 이름:태그
COPY . /app
WORKDIR /app
ENV PYTHONPATH = /app
ENV PYTHONBUFFERED=1
RUN pip install pip==21.2.4 &&\ pip install -r requirements.txt
CMD ["python", "main.py"]
RUN vs CMD
차이점분석 (feat. ENTRYPOINT)
RUN
CMD
docker build "Dockerfile이 위치한 경로"
docker bult . -t my-fastapi -app
docker run "이미지 이름:태그"
방금 만든 이미지를 실행
-- 태그가 "latest"인 경우 생략 가능
다른 터미널을 열어 curl로 애플리케이션이 잘 작동하는지 확인할 수 있음
파이썬 환경 및 애플리케이션 코드를 작성
Dockerfile 작성
-- FROM으로 베이스 이미지를 지정
-- COPY로 로컬 내 디렉토리 및 파일을 컨테이너 내부로 복사
-- WORKDIR로 RUN, CMD등을 실행할 컨테이너 내 디렉토리 지정
-- RUN으로 애플리케이션 실행에 필요한 여러 리눅스 명령어들을 실행
-- CMD로 이미지 실행 시 바로 실행할 명령어를 지정
docker build "Dockerfile이 위치한 경로" -t "이미지 이름:태그" 으로 이미지 빌드
docker run "이미지 이름 : 태그" 로 빌드한 이미지를 실행
그외에 Dockerfile에서 사용하는것
-- EXPOSE : 컨테이너 외부에 노출할 포트 지정
-- ENTRYPOINT : 이미지 컨테이너로 띄울 때 항상 실행하는 커맨드
이제 우리가 만든 이미지를 인터넷에 업로드하기
이를 위해 이미지 저장소인 Container Registry에 Docker Image Push
Container Registry : Dockerhub, GCP GCR, AWS ECR 등
별도로 지정하지 않으면 기본적으로 Dockerhub를 사용. 우리는 GCP의 GCR을 사용
보통 어떤 클라우드 서비스로 배포할 지에 따라 어떤 레지스트리 서비스를 사용할지 결정
(ex. GCP에서 배포한다면 레지스트리도 역시 GCP서비스인 GCR을 사용하는 식)
GCR 설정하기
1. GCP웹 콘솔에서 GCR 확인 (= Container Registry )
2. (처음이라면) GCR화면에 들어가 API 활성화
3. Registry에 아무것도 존재하지 않음
4. 로컬에서 GCR로 이미지를 Push할 준비
먼저 gcloud를 로그인하고, 프로젝트 세팅
(gcloud: 구글 클라우드 플랫폼 제품을 CLI로 쉽게 사용할 수 있도록 만든 도구)
Cloud SDK Install에서 OS를 확인한 후 다운로드 및 실행
gcloud설정
gcloud auth login
gcloud config set project <PROJECT_ID>
gcloud auth configure-docker
(docker 설정)
Tag 설정
docker tag "기존 이미지:태그" "새 이미지 이름:태그"
gcr에 올릴 이미지 이름은 gcr.io/GCP 프로젝트 이름/ 이미지 이름형태여야 함
docker push "이미지 이름:태그"
기다리면 Push
Registry 확인
이미지 이름은 Repository로 태그는 이름 형태로 저장
푸시를 여러번하면 여러가지 파일이 저장됨.
버전1,버전2 처럼 github의 hashing과 유사하다.
Docker Image Pull
Push한 이미지는 "docker pull" 명령어로 어디서든 받을 수 있음
도커 이미지를 서버에 배포하는 가장 간단한 방법: Cloud 서비스 활용
Cloud RUN
GCP Cloud Run을 사용하는 경우, 다음처럼 GCR에 올린 이미지 경로만 입력하면 끝 !
Cloud Run 인스턴스가 만들어지면, 다음처럼 URL이 자동으로 생성
이 URL로 요청을 날리면 서버가 잘 작동하는 것을 알 수 있음
다른 방식으로 Docker Image 배포
서비스계정 생성- 서비스계정 대시보드
IAM 및 관리자- 서비스계정
IAM(Identity Access Management)
서비스 계정(Service Account)
6.IAM에서 방금 생성한 서비스 계정에 권한 부여
참고 : 서비스 계정을 Python 스크립트에서 사용할 경우
from google.cloud import storage
storage_client = strage.Client.from_service_account_json('service_account.json')
서비스 계정 없이 실행하면 서비스 계정을 명시적으로 입력하거나, 환경 변수를 등록하라는 메시지가 출력
-> 서비스 계정의 Key값 복사
-> Github Repository의 Secret에 추가(띄어쓰기, 오타 조심) - SERVICE_ACCOUNT_KEY
-> Github Repository의 Secret에 추가(띄어쓰기, 오타 조심) - GCP_PROJECT_ID
->-> GCP대시보드에서 프로젝트 ID복사해서 추가 (프로젝트 정보/프로젝트 ID)
-> Github Repository의 Secret에 추가(띄어쓰기, 오타 조심) - GCE_INSTANCE
->-> 생성한 인스턴스의 이름
-> Github Repository의 Secret에 추가(띄어쓰기, 오타 조심) - GCE_INSTANCE_ZONE
->-> 생성한 인스턴스의 ZONE
github repo Boostcamp-AI-Tech-Product-Serving의 part2/04-cicd로 이동
Dockerfile
FROM python:3.8.7-slim-buster
COPY requirements.txt ./requirements.txt
RUN pip install -r ./requirements.txt
COPY . /app
WORKDIR /app
EXPOSE 8501
ENTRYPOINT ["streamlit","run","app.py","--server.port","8501"]
프로젝트 구조상 assets의 모델파일을 복사해야 해서 copy_asset.sh를 실행
bash copy_asset.sh
먼저 Local에서 Docker Image Build
docker build . -t "streamlit"
해당 이미지가 존재하는지 확인
docker images | grep "streamlit"
Tag 설정
docker tag streamlit gcr.io/{각자 project id}/streamlit
GCR에 Push
docker push gcr.io/{각자 project id}/streamlit
시간이 소요되고 영겁의 기다림
이미지를 작게쓰려고 성능을 버릴순 없다 타협점을 잘 찾자 :)
GCR에서 Docker Image 확인
Compute Engine - 인스턴스 만들기 (부팅 디스크 용량 50GB !)
아까 GCR Push한 컨테이너 이미지 지정하고 확인
gcr.io/{각자 project id}/streamlit:latest
최신 이미지를 사용!
이번엔 네트워크 태그를 바로 추가하며 인스턴스 생성
인스턴스가 생성되면 외부 IP로 접근
만약 IP주소에 접근할 수 없는 경우, 원인 파악 하나씩 (실무에서도 문제는 매번 발생 )
Compute Engine에 SSH 접속 후
sudo journalctl -u konlet-startup
입력하면 Container 실행하는 로그가 나옴 -> 로그 메세지 확인하며 다시 문제 해결
이제 Github Action Workflow 생성하기
작업흐름 - gitflow
1. Feature/xxx Branch에서 작업
2. Main Branch로 Pull Request
3. Review 후 Merge <- gitflow
Boostcamp-AI-Tech-Product-Serving/.github/workflows
github 위의 파일로 이동
코드 읽어보기
deploy_docker.yml
Pull Request - Merge !!
(5분~10분정도 걸림...)
앞서 진행한 내용: Docker Image
이런 경우 활용할 수 있는 것이 Docker Compose!!
단일이미지가 아닌 여러이미지를 띄우려면 Docker Compose !
Docker Image 일괄 실행
docker-compose up
docekr-compose up -d: 백그라운드에서 실행하기 ( docker run -d와 동일 )
docekr-compose down: 서비스 중단(컨테이너, 볼륨 등 삭제)
docekr-compose logs <서비스명>: 로그 확인
docker-compose.yml파일을 수정하고 docker-compose up을 하면 컨테이너를 재생성하고, 서비스를 재시작함.
docker-compose up이 완료되면 다음처럼 docker ps 명령어나 docker-compose ps 명령어로 현재 실행되고 있는 컨테이너를 확인할 수 있음
이제 curl로 요청을 보내면 잘 작동하는지 확인할 수 있음
앞에 도커 커맨드에 대한 공부를 많이 하다보면 컴포즈를 써야되는 시점이 온다.