にほんごのれんしゅう

日本語として伝えるための訓練を兼ねたテクログ

機械学習ではじめるDocker

目次とお断り

この資料をまとめるに当たって、実際に開発したり運用したりという経験のスニペットから、できるだけ編集して、自分なりに体系化したものです  

様々な角度のデータが乱雑なっててわかりにくいかもしれませんが、ご了承いただけると幸いです  

  • "1. Dockerとは"
  • "2. Dockerを用いるメリット"
  • "3. docker.ioのインストール"
  • "4. dockerでコンテナの起動"
  • "5. 基本的な操作"
  • "6. Dockerコンテナにsshdなどの必須ソフトウェアをインストールする"
  • "7. dockerコンテナのexportとimport"
  • "8. 機械学習ように調整したコンテナの利用"
  • "9. 実際に使用している例"
  • "10. Docker Hub連係"
  • "11. Docker Compose"
  • "12. Dockerのコンテナとホストマシン間でファイルの共有をする"
  • "13. 便利なチートシート"
  • "14. まとめ"

1. Dockerとは

  • 仮想化コンテナで、VMWareVirtualBoxのようなsystemdやシステムのサービス管理サービスが稼働するような重いものではない
  • chrootみたいなものだが、immutable architectureを前提とするので、docker commitするまで状態は保存されない
  • vmware, virtualboxみたいな完全なOSを乗っけるイメージよりアプリレベルの簡単な仮想化を想定(指定したバイナリしか動かないのでsystemd通常動作させない)

2. Dockerを用いるメリット

開発と分析するいう側面  

  • 依存の多い分析ライブラリと開発環境を簡単にどこでも好きな場所で再現できる
  • XGBoost, LightGBM, LibSVM, RedisやAerospikeのような微妙な環境じゃないと動かなかったりするものを封じ込めて、安定運用版のスナップショットとしていつでも利用できる

運用という側面

  • Dockerで構築したアプリをそのままデプロイ用に加工して、DevOpsに渡すことで、運用に回してもらう
  • DevOpsは開発レイヤーが強いので、なんなら機械学習とエンジニアリング専門の私でも、運用ルールを策定できる

3. docker.ioのインストール

手法1. dockerのrepositoryからのインストール

$ sudo apt install docker.io
$ sudo usermod -aG docker $USER
LOGOUTしてLOGINか、再起動

手法2. docker公式が提供する公式のインストールスクリプトからインストール

$ curl -fsSL get.docker.com -o get-docker.sh
$ sh get-docker.sh
$ sudo usermod -aG docker $USER
LOGOUTして、LOGINか再起動

4. dockerでコンテナの起動

最小サイズのubuntuをインストール

$ docker pull ubuntu
$ docker run -it ubuntu bash

5. 基本的な操作

dockerにはなぜか名前がつかないことがあって、docker tagで名前をつける

$ docker tag ${ID} ${NAME}

run(内容が保存されない)

$ docker run -it ${NAME} /bin/bash

すでに起動済みのDockerにCONTAINER IDでログイン  

$ docker exec -it ${CONTAINER_ID} /bin/bash

恒久化(Commit)  

作業した内容は保存されないないので、commitすることで、状態が差分として保存される

$ docker commit ${CONTAINER_ID} ${NAME}/${TAG}

Dockerでportをホストのportとバインドして起動

docker内でportを起動して、ホストマシンにつなげてサービスを公開するときなど、利用できる

$ docker run -it -p ${PORT}:${PORT} bash

6. Dockerコンテナにsshdなどの必須ソフトウェアをインストールする

DockerコンテナにSSHでアクセスする

sshdなどはdockerの性質上、systemdなどで管理なので、dockerにbashなどでログインして、手動やdocker-composeなどで起動する必要があります

sshdのインストール(Ubuntu, Debian)

$ apt install openssh-server

sshdのインストール(ArchLinux)

$ sudo pacman -S openssh

sshdサーバの起動

$ /usr/bin/sshd

sshへのアクセス
IPアドレスなどをnet-toolsのifconfigなどで確認して、ホストマシンからclientのマシンに接続する

$ ssh ${USER}@172.17.0.2

.bashrcのロード
通常の環境変数のローディングと異なるので、.bashrcから読み取る  

$ source .bashrc

これをしないとlocaleがja_JP.UTF-8にならずにtmuxなどが使えないっぽい

7. dockerコンテナのexportとimport

諸々をインストールして、安定して運用できるようになったらば、tar形式にexportできます

export
tarファイルで出力することができます

$ docker export ${PID} > alice-bob.tar

import

$ docker import ./hogehoge.tar

インポートした状態では、最初は

REPOSITORY:<none> TAG:<none>

となるので、tagコマンドで適切に名前をつける

$ docker tag ${NAME}/${TAG} ${NEWTAGNAME}

8. 機械学習ように調整したコンテナの利用

ArchLinux(Official)

$ docker pull archlinux/base

Debian(Official)

$ docker pull debian

私のDocker Hubのレポジトリ

機械学習専用のコンテナを作りました!

sshd, python3, pip3, mecab, neologd, lightgbm, xgboost, neovim, tmux, git, JVM, Kotlin, leveldb, rocksdbなどをインストールしてすぐ使えるようにしたイメージです。docker hubで公開しているので、docker pullでダウンロードしてすぐ利用できます  

すぐに機械学習できますね!!

$ docker pull nardtree/oppai:latest
$ docker run -it nardtree/oppai bash
(sshd serverモードではこうして、表示されたipaddressにssh oppao@${IP_ADDR}とします)
$ docker run -p 1022:22 -it nardtree/oppai server.py

server.pyでは、ifconfigとsshdが非同期で実行されて、このDockerのsshdにアクセスできるようになります

**ここからコンテナ内部です**
# sudo su oppai #passwordはoppaiです
$ cd ~
$ source .bashrc

$ python3
Python 3.6.3 (default, Oct 24 2017, 14:48:20) 
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

$ sudo pip3 install mecab-python3
Requirement already satisfied: mecab-python3 in /usr/lib/python3.6/site-packages

$ echo "艦これ" | mecab
艦これ  名詞,固有名詞,一般,*,*,*,艦これ,カンコレ,カンコレ
EOS

$ which lightgbm
/usr/local/bin/lightgbm

$ which xgboost
/usr/bin/xgboost

$ which nvim
/usr/bin/nvim

$ which javac
/usr/bin/javac

$ which kotlin
/usr/bin/kotlin

9. 実際に使用している例

開発、分析環境として利用する例

本番に投入するサービスとして利用する例

分析機にはUbuntu, Debian or ArchLinuxを使うことが多く、本番環境ではRHELCentOSを使う運用が多い構成なのですが、バイナリや微妙なディストリビューション間の差を人間が吸収するのはやめて、Dockerの出力を利用してどこでも再現してもらえば、問題ないよね、好きな構成でアプリとして機能すればDevOpsでよろしく組み合わせるよ、というスパースな設計ができます

このように一つの機能を持ったアプリとして動作させるには、docker composeやkubernetesを用いてDockerの断片をつなぎ合わせて、巨大なシステムを編纂します。   

ローカルでserverの機能をもつアプリをDockerでラップした一つのアプリの粒度として動作させるには、このようにします。

$ docker run -p ${PORT1}:${PORT2} -it ${APPLICATION_NAME}

10. Docker Hub連係

DockerというgithubのようなコンテナのイメージのSNSみたいなのがあります
nardtree/archのようなtag名をつけるとスラッシュの先のユーザ名のレポジトリにpushできて再現性が確保で来ます

$ docker tag ${IMAGE_ID} ${USER_NAME}/${SOME_NAME}
$ docker push ${USER_NAME}/${SOME_NAME}

11. Docker Compose

Dockerの一連の複雑なオペレーションを自動化した状態で、取り扱うのが、Docker Composeです

$ git clone https://github.com/GINK03/docker-compose-templates
$ cd docker-compose-templates/minimal-machine-learning-oppai
$ docker-compose up

これをやると、一連のめんどくさいセットアップコマンド各種をすっ飛ばして、port 1022でlocalhostsshdできるArchLinuxが仕上がります

$ ssh -p 1022:22 oppai@localhost
# password:oppai

12. Dockerのコンテナとホストマシン間でファイルの共有をする

Dockerコンテナでは状態を持たないイミュータブルアーキテクチャになっており、あまり状態を持つことを期待されて居ません   しかし、RDBやKVSなどを利用する際は、当然ながら、ファイルに書き出したりして状態を保存しなければいけません

DockerfileではVOLUMEオプションをつけることで、マウントするディレクトリを指定し、作ることができます

FROM nardtree/oppai                              
EXPOSE 22                                        
EXPOSE 1022                                      
EXPOSE 4567                                      
VOLUME ["/data"]                                 
CMD ["server.py"]  

Docker-compose.ymlでホストの$HOME/dataとコンテナの/dataを結び、片方で書き込まれたらもう片方にも反映されるようになります

version: '3'                                     
services:                                        
  ml-app:                                        
    build: .                                     
    ports:                                       
     - "4567:4567"                               
     - "1022:22"                                 
    volumes:                                     
      - ~/data:/data  

13. 便利なチートシート

noneの一括削除

$ docker images | awk '/<none/{print $3}' | xargs docker rmi

肥大化するvarを別のポイントに移動
ホストマシンのデフォルトのdockerのコンテナの保存を別の場所にする
SSDでパフォーマンスが大きく変わるサービスである、KVSなどを利用する際は、こっちの方がいいよ
  方法1. /etc/default/dockerを編集します

DOCKER_OPTS="-g $HOME/sda/docker/data"

方法2. /var/lib/dockerをssdやnvmeからリンクする

$ sudo mv /var/lib/docker ${nvme_dir}
$ sudo ln -s ${nvme_dir} /var/lib/docker

docker containerのIPや詳細を調べる

$ docker inspect ${PID}

docker composeで一括アプリの実行

$ docker-compose run ml-app

14. まとめ

私の所属しているいくつかの組織では、インフラエンジニアが十分に確保できなく、慣れないインフラオペレーションにイライラしていたのですが、時代はDevOpsということでソフトウェアエンジニアや私のような機械学習エンジニアがインフラの管理をしなくてはいけないことが、まま発生するようになりました

最近はもう諦めて、Dockerや最近のDocker-Compose、より上位のKubernetesなどを勉強し始めて、依存や環境を丸めて機能のみを取り出すDockerの粒度が一つのアプリの粒度として優れていると、思うようになり、運用と開発の面で導入するようになりました。実際便利であり、オンプレでサーバを持たなくていいので楽です