Upgrade to Pro — share decks privately, control downloads, hide ads and more …

機械学習開発のためのコンテナ入門 / Container for ML

chck
December 03, 2021

機械学習開発のためのコンテナ入門 / Container for ML

CA 1Day Youth Boot Campでの発表資料です

chck

December 03, 2021
Tweet

More Decks by chck

Other Decks in Technology

Transcript

  1. Docker Docker is an open platform for developing, shipping, and

    running applications. Docker enables you to separate your applications from your infrastructure so you can deliver software quickly. With Docker, you can manage your infrastructure in the same ways you manage your applications. By taking advantage of Docker’s methodologies for shipping, testing, and deploying code quickly, you can significantly reduce the delay between writing code and running it in production. https://docs.docker.com/get-started/overview/ 9
  2. Why Docker? • コードを動かすための依存をDockerさえ入っていれば動く状態にまでPackagingできる ◦ CondaやPoetry, RStudioではだめ? ▪ 自分のPCでだけ動かすならOK 。移行や共有時どうする?

    ▪ OSへの直Installや別途Downloadが必要な依存がある場合は? • 実験コードを動かすための長く複雑な手順を Dockerで隠蔽できる ◦ READMEにはDocker commandを書くだけ • Kubernetesを始めとする強力なContainer ServiceにML AppをDeployできる ◦ 2021年現在、一定規模以上のAppの運用を考えると業界標準に 10
  3. LiveDemo ➔ git clone https://github.com/chck/container4ml.git ➔ cd container4ml ➔ ls

    .git .gitignore 1-simple 2-jupyter 3-fastapi README.md 12
  4. Story 1 • 既にあるPython ScriptをDocker化したい • Docker化のProcess ◦ Python Scriptを動かすための依存を含んだDocker

    Imageを作成 ◦ Docker Containerとして実行し、挙動を確認 ◦ DockerfileのCommitやDocker RegistryにImageをUploadして完了 13
  5. Docker Image An image is a read-only template with instructions

    for creating a Docker container. Often, an image is based on another image, with some additional customization. https://docs.docker.com/get-started/overview/ 14
  6. Docker Container A container is a runnable instance of an

    image. You can create, start, stop, move, or delete a container using the Docker CLI. It is a standard unit of software that packages up code and all its dependencies. https://www.docker.com/resources/what-container 15
  7. Docker Container A container is a runnable instance of an

    image. You can create, start, stop, move, or delete a container using the Docker CLI. It is a standard unit of software that packages up code and all its dependencies. https://www.docker.com/resources/what-container MacBook, ThinkPad, EC2, GCE macOS, Windows10, Ubuntu JupyterLab, Flask, MySQL, Original App 16
  8. Story 1: 再掲 • 既にあるPython ScriptをDocker化したい • Docker化のProcess ◦ Python

    Scriptを動かすための依存を含んだDocker Imageを作成 ◦ Docker Containerとして実行し、挙動を確認 ◦ DockerfileのCommitやDocker RegistryにImageをUploadして完了 17
  9. 2. Dockerfileを書く ➔ cd 1-simple ➔ ls .dockerignore Dockerfile Makefile

    pyproject.toml docker-compose.yml main.py poetry.lock ➔ vi Dockerfile -------------------------- 19
  10. 2. Dockerfileを書く ➔ vi Dockerfile -------------------------- FROM python:3.8-slim ENV APP_HOME

    /app RUN apt update && apt install -y --no-install-recommends curl \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR ${APP_HOME} COPY main.py . CMD ["python", "main.py"] 20
  11. 2. Dockerfileを書く ➔ vi Dockerfile -------------------------- FROM python:3.8-slim ENV APP_HOME

    /app RUN apt update && apt install -y --no-install-recommends curl \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR ${APP_HOME} COPY main.py . CMD ["python", "main.py"] 21
  12. 2. Dockerfileを書く ➔ vi Dockerfile -------------------------- FROM python:3.8-slim ENV APP_HOME

    /app RUN apt update && apt install -y --no-install-recommends curl \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR ${APP_HOME} COPY main.py . CMD ["python", "main.py"] Base Imageを指定 22
  13. 2. Dockerfileを書く ➔ vi Dockerfile -------------------------- FROM python:3.8-slim ENV APP_HOME

    /app RUN apt update && apt install -y --no-install-recommends curl \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR ${APP_HOME} COPY main.py . CMD ["python", "main.py"] 環境変数の定義 23
  14. 2. Dockerfileを書く ➔ vi Dockerfile -------------------------- FROM python:3.8-slim ENV APP_HOME

    /app RUN apt update && apt install -y --no-install-recommends curl \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR ${APP_HOME} COPY main.py . CMD ["python", "main.py"] 環境構築の実行コマンドを記述 24
  15. 2. Dockerfileを書く ➔ vi Dockerfile -------------------------- FROM python:3.8-slim ENV APP_HOME

    /app RUN apt update && apt install -y --no-install-recommends curl \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR ${APP_HOME} COPY main.py . CMD ["python", "main.py"] Image内でのDirectory移動 25
  16. 2. Dockerfileを書く ➔ vi Dockerfile -------------------------- FROM python:3.8-slim ENV APP_HOME

    /app RUN apt update && apt install -y --no-install-recommends curl \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR ${APP_HOME} COPY main.py . CMD ["python", "main.py"] local://${pwd}/main.py docker://app/main.py COPY main.py . 26
  17. 2. Dockerfileを書く ➔ vi Dockerfile -------------------------- FROM python:3.8-slim ENV APP_HOME

    /app RUN apt update && apt install -y --no-install-recommends curl \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR ${APP_HOME} COPY main.py . CMD ["python", "main.py"] 起動時に実行されるコマンド 27
  18. 3. Docker Imageの作成 ➔ docker build . -t container4ml-simple:1.0 コンテキストの指定.

    Dockerに見てほしいpath. COPY句でどこを起点にするか . 基本的にDockerfileのある場所でOK イメージ名. お作法的にはowner/image_name イメージタグ. イメージは更新されゆくのでいわゆる versioning 29
  19. 3. Docker Imageの作成 ➔ docker build . -t container4ml-simple:1.0 ➔

    docker images REPOSITORY TAG IMAGE ID CREATED SIZE container4ml-simple 1.0 be5c53cd3ad6 7 seconds ago 437MB Image sizeはなるべく小さくする 重いとImageのupload/downloadに時間がかかりML Lifecycleの効率も落ちる 30
  20. 4. Docker Containerの起動 ➔ docker run container4ml-simple:1.0 scikit-learn: 1.0.1 ➔

    docker ps -a CONTAINER ID IMAGE COMMAND ... NAMES 30f773835fbc container4ml-simple:1.0 "python main.py" $(CONTAINER_NAME) ➔ cat main.py import sklearn print(f"scikit-learn: {sklearn.__version__}") 31
  21. 4. Docker Containerの実行上書き ➔ docker run --rm container4ml-simple:1.0 ls -lh

    total 4.0K -rw-r--r-- 1 root root 62 Dec 2 14:54 main.py 実行コマンドは上書きできる 同Imageで挙動だけ変えたい時等で活用 例えばTraining/Servingを同Imageにするとか 32
  22. 5. Docker ImageのUpload/Download ➔ docker tag container4ml-simple:1.0 chck/container4ml-simple:1.0 ➔ docker

    images REPOSITORY TAG IMAGE ID CREATED SIZE chck/container4ml-simple 1.0 c1575db34e5d 2 hours ago 437MB container4ml-simple 1.0 c1575db34e5d 2 hours ago 437MB ➔ docker push chck/container4ml-simple:1.0 ➔ open https://hub.docker.com/repository/docker/chck/container4ml-simple ➔ docker rmi chck/container4ml-simple:1.0 ➔ docker pull chck/container4ml-simple:1.0 ➔ docker images 33
  23. 5. Docker ImageのUpload/Download ➔ docker tag container4ml-simple:1.0 chck/container4ml-simple:1.0 ➔ docker

    images REPOSITORY TAG IMAGE ID CREATED SIZE chck/container4ml-simple 1.0 c1575db34e5d 2 hours ago 437MB container4ml-simple 1.0 c1575db34e5d 2 hours ago 437MB ➔ docker push chck/container4ml-simple:1.0 ➔ open https://hub.docker.com/repository/docker/chck/container4ml-simple ➔ docker rmi chck/container4ml-simple:1.0 ➔ docker pull chck/container4ml-simple:1.0 ➔ docker images tagはAliasとして働くので同じImage IDを持ち, Diskも重複消費しない 指定registryにimageがuploadされる 34
  24. 5. Docker ImageのUpload/Download ➔ docker tag container4ml-simple:1.0 chck/container4ml-simple:1.0 ➔ docker

    images REPOSITORY TAG IMAGE ID CREATED SIZE chck/container4ml-simple 1.0 c1575db34e5d 2 hours ago 437MB container4ml-simple 1.0 c1575db34e5d 2 hours ago 437MB ➔ docker push chck/container4ml-simple:1.0 ➔ open https://hub.docker.com/repository/docker/chck/container4ml-simple ➔ docker rmi chck/container4ml-simple:1.0 ➔ docker pull chck/container4ml-simple:1.0 ➔ docker images 指定したDocker Imageを削除. Registryに上げていれば安心 指定RegistryからImageをDownload これはDocker Hubの参照例 35
  25. Tips: Image Tagは複数付与できる ➔ docker tag container4ml-simple:1.0 chck/container4ml-simple:1.0 ➔ docker

    tag container4ml-simple:1.0 chck/container4ml-simple:latest ➔ docker push chck/container4ml-simple:1.0 ➔ docker push chck/container4ml-simple:latest 36
  26. Tips: slim? alpine? ➔ docker pull python:3.8-alpine ➔ docker pull

    python:3.8-slim ➔ docker pull python:3.8 ➔ docker images REPOSITORY TAG IMAGE ID CREATED SIZE python 3.8-alpine d314e28e240c 2 days ago 43.2MB python 3.8-slim 64458f531a7e 2 weeks ago 122MB python 3.8 67ec76d9f73b 2 weeks ago 909MB Debian baseのfull image (無印) > 軽量版 (slim) > 最軽量版 (alpine) 依存も少なくなっていくのでトレードオフ 最初はslimがおすすめ 37
  27. Docker Architecture 42 こっちは Remote Server Docker Hubや GCR, ECRが相当

    https://docs.docker.com/get-started/overview/#docker-architecture
  28. Docker Compose Compose is a tool for defining and running

    multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. 44 https://docs.docker.com/compose/
  29. Docker Compose Docker: Docker Compose: 45 ➔ docker build .

    -t container4ml ➔ docker run --rm -p 8888:8888 -v ${PWD}:/app container4ml (docker-compose.ymlがある状態で) ➔ docker compose up
  30. 1. Dockerfileを書く ➔ cd 2-jupyter ➔ ls .dockerignore docker-compose.yml Makefile

    pyproject.toml .gitignore Dockerfile poetry.lock train.ipynb ➔ vi Dockerfile -------------------------- 46
  31. 1. Dockerfileを書く 47 ➔ vi Dockerfile -------------------------- FROM python:3.8-slim ENV

    APP_HOME /app RUN apt update && apt install -y --no-install-recommends git build-essential \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR ${APP_HOME} COPY *.bin . EXPOSE 8888 CMD ["jupyter", "lab", "--allow-root", "--ip=0.0.0.0", "--no-browser", "--ServerApp.allow_origin=*", "--ServerApp.token=", "--ServerApp.password="]
  32. 1-simpleのDockerfileと比較 ➔ vi Dockerfile -------------------------- FROM python:3.8-slim ENV APP_HOME /app

    RUN apt update && apt install -y --no-install-recommends curl \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR ${APP_HOME} COPY main.py . CMD ["python", "main.py"] 48
  33. 1. Dockerfileを書く 49 ➔ vi Dockerfile -------------------------- FROM python:3.8-slim ENV

    APP_HOME /app RUN apt update && apt install -y --no-install-recommends git build-essential \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR ${APP_HOME} COPY *.bin . EXPOSE 8888 CMD ["jupyter", "lab", "--allow-root", "--ip=0.0.0.0", "--no-browser", "--ServerApp.allow_origin=*", "--ServerApp.token=", "--ServerApp.password="]
  34. 2. docker-compose.ymlを書く ➔ vi docker-compose.yml -------------------------- services: jupyter: image: chck/container4ml-jupyter:1.0

    build: . ports: - "8888:8888" volumes: - ${PWD}:/app 51 任意のService名 Dockerfileを参照しながら 指定Image名でdocker build portやvolume optionを付与してdocker runされ jupyter containerが起動
  35. 3. Docker Containerの起動 (via docker compose) ➔ docker compose up

    Jupyter Server 1.12.1 is running at:... ➔ docker images ➔ docker ps CONTAINER ID IMAGE ... PORTS ... 47ac8f57c169 chck/container4ml-jupyter:1.0 ... 0.0.0.0:8888->8888/tcp … ➔ open http://localhost:8888 # train.ipynbを実行、model.binが作られたことを確認後、 docker compose build (次ページ) ➔ Ctrl-C Gracefully stopping... 52
  36. 1. Dockerfileを書く ➔ cd 3-fastapi ➔ ls .env Dockerfile Makefile

    test-scale.sh .envrc infra poetry.lock test-switch.sh docker-compose.yml main.py pyproject.toml ➔ vi Dockerfile -------------------------- 56
  37. 1. Dockerfileを書く 57 ➔ vi Dockerfile -------------------------- FROM chck/container4ml-jupyter:1.0 AS

    trainer FROM python:3.8-slim ENV APP_HOME /app RUN apt update && apt install -y --no-install-recommends git build-essential \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR /models COPY --from=trainer /app/*.bin . WORKDIR ${APP_HOME} COPY main.py . EXPOSE 80 CMD ["gunicorn", "main:app", "--bind=0.0.0.0:80", "--workers=1", "--threads=8", "--timeout=0", "--worker-class=uvicorn.workers.UvicornWorker"]
  38. 1. Dockerfileを書く 58 ➔ vi Dockerfile -------------------------- FROM chck/container4ml-jupyter:1.0 AS

    trainer FROM python:3.8-slim ENV APP_HOME /app RUN apt update && apt install -y --no-install-recommends git build-essential \ && apt clean && rm -rf /var/lib/apt/lists/* \ && pip install -U pip && pip install --no-cache-dir poetry COPY pyproject.toml poetry.lock ./ RUN poetry export --without-hashes -f requirements.txt -o requirements.txt \ && pip install -r requirements.txt --no-cache-dir WORKDIR /models COPY --from=trainer /app/*.bin . WORKDIR ${APP_HOME} COPY main.py . EXPOSE 80 CMD ["gunicorn", "main:app", "--bind=0.0.0.0:80", "--workers=1", "--threads=8", "--timeout=0", "--worker-class=uvicorn.workers.UvicornWorker"]
  39. 2. docker-compose.ymlを書く ➔ vi docker-compose.yml -------------------------- services: myapp: image: chck/container4ml-fastapi:1.0

    build: . ports: - "2222:80" environment: - PYTHONUNBUFFERED=1 - DEBUG=true - REDISHOST=myredis - MODEL_PATH=/models/ua_classifier.bin volumes: - ${PWD}:/app depends_on: - myredis myredis: image: redis:alpine 59
  40. 2. docker-compose.ymlを書く ➔ vi docker-compose.yml -------------------------- services: myapp: image: chck/container4ml-fastapi:1.0

    build: . ports: - "2222:80" environment: - PYTHONUNBUFFERED=1 - DEBUG=true - REDISHOST=myredis - MODEL_PATH=/models/ua_classifier.bin volumes: - ${PWD}:/app depends_on: - myredis myredis: image: redis:alpine 60 任意のService名 任意のService名 Dockerfileを参照しながら 指定Image名でdocker build 指定Imageをdocker pullで用意 portやvolume, env optionを付与してdocker runされ jupyter containerが起動 myredis containerの起動を待ってから myappを起動
  41. 2. docker-compose.ymlを書く ➔ vi docker-compose.yml -------------------------- services: myapp: image: chck/container4ml-fastapi:1.0

    build: . ports: - "2222:80" environment: - PYTHONUNBUFFERED=1 - DEBUG=true - REDISHOST=myredis - MODEL_PATH=/models/ua_classifier.bin volumes: - ${PWD}:/app depends_on: - myredis myredis: image: redis:alpine 61 w/o docker compose ➔ docker network create mynwk ➔ docker run -p 2222:80 -e PYTHONUNBUFFERED=1 -e DEBUG=true -e REDISHOST=myredis -e MODEL_PATH=/models/ua_classifier.bin -v ${PWD}:/app --net mynwk chck/container4ml-fastapi:1.0 ➔ docker run --net mynwk redis:alpine w/ docker compose ➔ docker compose up Container間通信(mynwk) myapp myredis
  42. 3. Docker Containerの起動 (via docker compose) ➔ docker compose up

    -d ➔ docker compose logs -f ➔ docker ps CONTAINER ID IMAGE ... PORTS ... NAMES b36811e9f286 chck/container4ml-fastapi:1.0 ... 0.0.0.0:2222->80/tcp 3-fastapi-myapp-1 7304fab5c081 redis:alpine ... 6379/tcp 3-fastapi-myredis-1 ➔ open http://localhost:2222 ➔ open http://localhost:2222/stats ➔ cat main.py ➔ docker compose down ➔ # docker-compose.ymlのenvironmentsにMODEL_NAME=Bを追加して docker compose up --build -d 62
  43. 時間が余った時用 • docker system prune -a • 1 Image: 1

    Appの理由 • Docker化が嬉しい場面 • Dataなどの重たいファイルはvolumeでsyncしておく話 • ModelをStorageに持つかImageに持つか • Docker container のDebug方法 • 変数によってbuildの振る舞いを変えたい時 • Training/Servingは同じImageか分けるか • Securityの話 63
  44. Container Orchestration • Containerの展開や状態の管理を 担うService • Docker ImageをどうServingする かの部分 •

    Kubernetesを基とする Managed Serviceが複数展開 https://www.datadoghq.com/container-report-2020/ 69
  45. Cloud Run • GCPが提供する Managed Container Service • k8sの細かい所をwrapしてApp のCodingに集中できるようにし

    たもの • Managing Level ◦ GCE < GKE < Cloud Run https://www.idkrtm.com/5-kinds-of-compute-on-google-cloud/ 74
  46. Story 3-2 • LocalのDocker Composeで動作確認のできたUA Estimatorを Cloud RunにDeployしたい • DeployのProcess

    ◦ Docker ImageをRegistryにUpload ◦ Cloud Run Serviceを構築・起動 ◦ Cloud Run ServiceにUploadしたImageをApply 76
  47. 1. RegistryにImageをUpload ➔ docker tag chck/container4ml-fastapi:1.0 gcr.io/$(GCP_PROJECT)/container4ml/fastapi:1.0 ➔ docker tag

    gcr.io/$(GCP_PROJECT)/container4ml/fastapi:1.0 gcr.io/$(GCP_PROJECT)/container4ml/fastapi:latest ➔ docker push gcr.io/$(GCP_PROJECT)/container4ml/fastapi:1.0 ➔ docker push gcr.io/$(GCP_PROJECT)/container4ml/fastapi:latest ➔ open https://console.cloud.google.com/gcr/images 77
  48. Autoscaling https://github.com/rakyll/hey WebAppの負荷試験ができるheyでbenchmarkを取ってみる ➔ brew install hey ➔ sh ./test-scale.sh

    ➔ sh ./test-scale.sh https://${CLOUDRUN_DOMAIN} Cloud Runのmetricsやheyのbenchmarkを観察 81
  49. Blue-green deployment 既存のApp (blue) 稼働中の裏に 新版のApp (green) をTrafficが来ない状態で Deployし、Standbyになったタイミングで Router内部の向き先をgreenに変えることで

    無停止Deployを行う仕組み Cloud RunではRevisionによってこれを達成 ➔ sh ./test-switch.sh https://${CLOUDRUN_DOMAIN} ➔ # Deploy New Revision in Cloud Run 82 https://candost.blog/the-blue-green-deployment-strategy/
  50. Today’s Goal • Portableな実験環境 ◦ -> Docker, Docker-Compose • Scalableな推論App

    ◦ -> Cloud Run • まずは研究コードのDocker化から始めてみる 86