Chapter 02

Docker 入門

まちゅけん
まちゅけん
2023.12.16に更新

この章ではコンテナ技術である Docker をデプロイで利用する為に基礎の学習を行っていきます。

Docker とは

Docker は簡単にいうとプログラムを「コンテナ」と呼ばれるものにまとめて、Docker がインストールされている環境であればどこでも実行できるようにする技術です。

例えば、Python のプログラムをコンテナ化したら、Python 自体が入っていない、または対応していない別バージョンの Python がインストールされている OS でも Docker があるだけでコンテナ化したバージョンの Python でプログラムを実行することが可能です。

また Docker はコンテナの実行管理をしてくれる機能もあります。

プログラムが落ちてしまった時にコンテナを自動で再起動したり、実行中のコンテナを停止する機能、コンテナをバックグラウンドで実行する機能があります。 バックグラウンドで起動できるということは、nohuptmux などを利用する必要がありません。

botter 観点からすると Docker を利用すると特に以下の点で嬉しいです。

  • コンテナ化
    • 様々なリージョンのインスタンスに素早く bot を展開できる。
    • TA-Lib などインストールが複雑なライブラリでも環境構築が容易になる。
  • コンテナの実行管理
    • bot の実行停止や一覧表示が容易になる。
    • コンテナにログが紐付けされているので、ログ監視が容易になる。

また Docker は API を提供しているので外部からコンテナをデプロイすることができます。 これにより後の章で説明するデプロイの自動化も容易になります。

はじめての Docker

インストール

まずはローカル PC に Docker Desktop をインストールして、Docker を利用できるようにしましょう! インストールするのが面倒で、一時的に試したい場合は GitHub Codespaces を利用しましょう (GitHub アカウントが必要です)。 ブラウザ上の VS Code 上で Docker がインストールされた環境を試すことができます。

Docker Desktop
https://docs.docker.com/get-docker/

GitHub Codespaces

Blank のテンプレートを利用することで Codespaces の環境を試すことができます。

https://github.com/codespaces

Docker のインストールができたら、はじめてのコンテナ化を行って bot コンテナを実行してみましょう!

サンプルコード

任意のディレクトリ (例: try-deploy-docker) を作成して、そこに以下の main.pyrequirements.txt のサンプルを張り付けてください。

main.py
import time

import requests


def main():
    while True:
        r = requests.get("https://api.bitflyer.com/v1/getticker?product_code=BTC_JPY")
        data = r.json()
        print(data)

        time.sleep(1.0)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        pass
requirements.txt
requests

このサンプルは requests ライブラリをインストールして bitFlyer から BTC_JPY のティッカー情報を毎秒取得して表示するだけの bot です。 注文等は行いませんが、ここでは Python プログラムを実行したいだけなのでこのようなサンプルにしています。

Build & Run!

この bot をコンテナ化するには Dockerfile を作成します。 拡張子も何もなしで Dockerfile です。

Dockerfile
FROM python:3.12

ENV PYTHONUNBUFFERED=1

WORKDIR /app

COPY main.py requirements.txt ./

RUN pip install -r requirements.txt

CMD [ "python" , "main.py" ]

これで必要なファイルは揃いました!

docker build コマンドで bot のコンテナイメージを作成します。

$
docker build -t bot-demo .

ターミナルにはこのように FINISHED といったログが表示されていれば成功です。

[+] Building 7.9s (9/9) FINISHED 

それでは docker run コマンドでコンテナ化された bot を実行してみましょう!

$
docker run --rm bot-demo

このコマンドを実行すると毎秒以下のようにティッカー情報が表示されます。

{'product_code': 'BTC_JPY', 'state': 'RUNNING', 'timestamp': '2023-12-15T05:44:18', 'tick_id': 93039265, 'best_bid': 6061996.0, 'best_ask': 6062184.0, 'best_bid_size': 0.96996, 'best_ask_size': 0.02992205, 'total_bid_depth': 329.26893232, 'total_ask_depth': 508.98319124, 'market_bid_size': 0.0, 'market_ask_size': 0.0, 'ltp': 6062184.0, 'volume': 2324.91788788, 'volume_by_product': 1869.75000709}

bot は無限ループで動き続けるので動作確認ができたら Ctrl+C を押して bot を終了しましょう。

これで bot をコンテナとして実行する体験ができました!

Dockerfile

Dockerfile はプログラムの実行環境を 1 つにまとめて定義するファイルです。 Docker のビルドエンジンは Dockerfile を読み込んでビルドを行い、コンテナイメージという形式で内部に保存します。

Dockerfile では FROM, COPY, RUN などの命令を利用して環境を定義します。先ほどの Dockerfile の意味をコメント付きで解説します。

Dockerfile
FROM python:3.12
# 利用する「ベースイメージ」を指定する。 ここでは Docker Hub 公式の Python 3.12 イメージを利用する。
# `python` が「イメージ名」で、コロンのあとの `3.12` が「タグ名」と呼ばれる。
# このイメージには Python 3.12 の実行環境が含まれている。
# 初めてのビルド時にこのイメージがダウンロードされる。
# `python` のイメージは以下の Docker Hub のページに存在するもので、
# `3.12` タグ以外にも利用可能なタグを確認することができる。
# https://hub.docker.com/_/python

ENV PYTHONUNBUFFERED=1
# 環境変数 PYTHONUNBUFFERED に 1 を設定する。
# この変数は標準出力のバッファリングに関わる Python の設定で、
# Docker で Python を利用する際のお作法的な変数。
# これを設定しないとコンテナ実行中にログが表示されず、終了と同時に一気に表示される。

WORKDIR /app
# 作業を行う為のディレクトリ。
# WORKDIR 何も設定しないとルート `/` にファイルがコピーされるので移動することを推奨する。

COPY main.py requirements.txt ./
# ローカル側にある main.py と requirements.txt をコンテナ側のディレクトリにコピーする。
# 相対パスを指定しているので WORKDIR 直下にコピーされる。

RUN pip install -r requirements.txt
# コンテナ内にて、`pip install` コマンドを実行して必要ライブラリをインストールしておく。

CMD [ "python" , "main.py" ]
# 実行コマンド。 `docker run` 時に実行される。

docker build / run

docker builddocker run コマンドのオプションの意味を解説します。

$
docker build -t bot-demo .

docker build は Dockerfile をビルドしてコンテナイメージを作成するコマンドです。

  • -t bot-demo
    • イメージ名:タグ名 を定義するオプションです。
    • この例では bot-demo というイメージ名を付けました。
    • タグ名は省略しています。 省略すると latest というタグ名から利用できます。
  • .
    • 末尾のドット . はビルドする場所を表しています。
    • 現在のディレクトリでビルドしたいので、現在のディレクトリを表す . を指定しています。
    • Docker は指定されたディレクトリ直下にある Dockerfile を読み込んでビルドを行います。
$
docker run --rm bot-demo

docker run コンテナイメージを実行するためのコマンドです。

  • --rm :
    • コンテナの実行終了後、そのコンテナを削除します。
    • このオプションを使用しないと、コンテナは終了後 Exited という状態になります。 docker run を何度も行うと Exited 状態のコンテナがいくつも作成されてしまいます。
    • コンテナを削除するとログなども消えてしまいますが、この演習では一旦実行したかっただけなのでこのオプションを利用しています。
  • bot-demo
    • ビルド時に定義したイメージ名を指定しています。
    • タグ名を省略しているので暗黙的に latest が実行されます。

バックグラウンドで実行してみる

先ほどの run コマンドはフォアグラウンドで実行されていました。 次は bot をバックグラウンドモードで起動してみましょう。

$
docker run -d --name mybot bot-demo
  • -d:
    • バックグラウンドで実行するオプションです。
  • --name mybot
    • バックグラウンドで実行したコンテナを識別するためにコンテナに名前を付けます。
    • --name を指定しないとランダムな名前のコンテナが作成されます。
    • 同じ名前のコンテナは重複して作成することができません。 そのため --name を指定しておくことで同じ bot を複数個バックグラウンドで実行してしまうことを防ぎます。

実際にコンテナが立ち上がっているか確認しましょう。 実行中のコンテナの確認は docker ps コマンドで行う事ができます。

$
docker ps

またバックグラウンド実行なので、ログはそのまま画面に出力されません。 docker logs をコマンドで確認することができます。

$
docker logs -f mybot
  • -f:
    • ログをリアルタイムで表示するオプションです。
  • mybot:
    • コンテナ名を指定します。

ログのリアルタイム表示が確認できたら Ctrl+C で表示を終了します。

確認ができたらコンテナを停止しましょう。 コンテナの停止には docker rm コマンドを利用します。

$
docker rm -f mybot
  • -f:
    • 実行中のコンテナを強制的に削除する。
  • mybot:
    • コンテナ名を指定します。

コンテナイメージをアップデートする

ローカルの main.py を変更して bot をアップデートしてみましょう。

main.py
import time

import requests


def main():
    while True:
        r = requests.get("https://api.bitflyer.com/v1/getticker?product_code=ETH_JPY")
        data = r.json()
        print(data)

        time.sleep(1.0)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        pass

requests.get で取得する銘柄を ETH_JPY に変更しました。

更新した bot を確認する為に docker run で実行してみます。

$
docker run --rm bot-demo
{'product_code': 'BTC_JPY', 'state': 'RUNNING', 'timestamp': '2023-12-15T05:44:18', 'tick_id': 93039265, 'best_bid': 6061996.0, 'best_ask': 6062184.0, 'best_bid_size': 0.96996, 'best_ask_size': 0.02992205, 'total_bid_depth': 329.26893232, 'total_ask_depth': 508.98319124, 'market_bid_size': 0.0, 'market_ask_size': 0.0, 'ltp': 6062184.0, 'volume': 2324.91788788, 'volume_by_product': 1869.75000709}

しかしコンテナが出力するログは変更前の BTC_JPY のバージョンになっています。 確認したら Ctrl+C でコンテナを止めてください。

これは、コンテナイメージは読み取り専用のレイヤーとなっている為です。 つまり docker build を実行してコピーされた時点のファイルが実行されています。 その為、ローカルの main.py を更新してもコンテナイメージには反映されません。

コンテナイメージを更新するには再度 docker build を実行します。

$
docker build -t bot-demo .

これで再ビルドができたので docker run コマンドで再度 bot を実行してみましょう。

$
docker run --rm bot-demo
{'product_code': 'ETH_JPY', 'state': 'RUNNING', 'timestamp': '2023-12-15T08:04:29.407', 'tick_id': 57115956, 'best_bid': 323590.0, 'best_ask': 323810.0, 'best_bid_size': 0.01, 'best_ask_size': 0.09, 'total_bid_depth': 1836.8604647, 'total_ask_depth': 1711.4365051, 'market_bid_size': 0.0, 'market_ask_size': 0.0, 'ltp': 323760.0, 'volume': 8751.0728978, 'volume_by_product': 8751.0728978}

ETH_JPY のティッカー情報が表示され、無事 bot が最新の状態になりました!

ここまで docker コマンドの基礎を学んで bot を実行する方法を学びました 🐋

しかし単なる docker コマンドは指定するオプションが多く、開発時にこの今度を毎回実行するのは面倒です。 Docker Compose を利用するともっと単純なコマンドでコンテナをビルド・実行することができます。 次は docker compose コマンドを利用する方法を学んでいきます。

Docker Compose

Docker Compose は簡単にいうと docker コマンドのオプションをまとめて実行します。

プログラムの環境構築をまとめておくのが Docker で、Docker の実行方法をまとめたのが Docker Compose といったイメージです。

コマンドのオプションをまとめるファイルは compose.yaml です。

Compose Up!

では Docker Compose を利用して先ほどの bot を実行してみましょう!

まずは compose.yaml ファイルを作成います。 先ほどの節の Dockerfile と同じディレクトリに作成してください。

compose.yaml
services:
  bot-demo:
    build:
      context: .
    restart: unless-stopped

docker compose up コマンドでコンテナをビルドと実行を同時に行いましょう!

$
docker compose up --build -d

docker build コマンドの時のようなビルドのログが表示されます。 同様に FINISHED と表示されていれば成功です。 Docker Compose は自動で現在のディレクトリにある compose.yaml ファイルを読み込んでいるのです。

このコマンドではコンテナはバックグラウンドで起動します。 現在の Docker Compose プロジェクトで起動しているコンテナの確認は docker compose ps コマンドで行うことができます。

$
docker compose ps

ログを確認するには docker compose logs を利用します。

$
docker compose logs -f

docker logs コマンドの時のようにリアルタイムで表示されるので、確認ができたら Ctrl+C で表示を終了してください。

docker コマンド単体の時と比べて docker compose up のコマンドのみでコンテナのビルドと実行が同時にできるのでとても簡単になりました 🐳

最後に Docker Compose で作成したコンテナを削除してみます。 docker compose down コマンドを利用します。

$
docker compose down

compose.yaml

先ほど利用した compose.yaml ファイルの意味を説明します。

compose.yaml
services:
# トップレベルの宣言。
# Docker Compose では実行する各コンテナは「サービス」と表現されます。

  bot-demo:
  # コンテナ (サービス) の名前。 コンテナイメージ名にも使用されます。

    build:
    # コンテナイメージのビルドに関する設定レベル

      context: .
      # `Dockerfile` が存在するディレクトリの指定する。
      # ここでは現在のディレクトリにあるのでドット `.` を指定してる。

    restart: unless-stopped
    # コンテナの再起動に関する設定を指定する。
    # この設定では手動でコンテナを停止しない限り、
    # 例えばプログラムのエラー時など自動で再起動してくれます。
    # ※ `docker` コマンド自体にもあるオプションです。

docker compose up / down

$
docker compose up --build -d

docker compose up は定義した compose.yaml に従ってサービス (コンテナ) を起動するコマンドです。 コマンド単体でみると docker run に相当します。

  • --build
    • docker build に相当するフラグです。
    • こちらを指定しないとビルドなしでサービス (コンテナ) を立ち上げるだけなのでプログラムが更新されません。
  • -d
    • バックグラウンドでコンテナを起動します。 docker 単体コマンドと同じです。
$
docker compose down

docker compose up で立ち上げたサービス群のリソースを削除します。 起動中、停止中の状態に関わらず対象のコンテナは削除されます。

マルチコンテナ

この本では詳しくは説明しませんが、Docker Compose は複数のコンテナを同時に立ち上げることが可能です。

botter 的なユースケースでいうと、環境変数を使って銘柄を切り替えて bot を起動したり、bot のデータや状態を管理するデータベースコンテナを立ち上げて利用するなどです。

こちらの compose.yaml ファイルはそのユースケースに沿った例です。

compose.yaml
services:
  bot-demo-btc:
    build:
      context: .
    restart: unless-stopped
    environment:
        PRODUCT_CODE: BTC_JPY

  bot-demo-eth:
    build:
      context: .
    restart: unless-stopped
    environment:
        PRODUCT_CODE: ETH_JPY

  mongo:
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example

この章のまとめ

ここまで Docker を利用して bot を実行する為の学習を行いました。

  • Docker はプログラムの実行環境を「コンテナ」にまとめて実行することができます。
  • Docker を利用するには Dockerfile を作成します。
  • docker build コマンドで bot のプログラムをコンテナイメージへビルドができます。
  • docker run コマンドでコンテナイメージからコンテナを実行できます。
    • -d オプションを利用することでバックグラウンドで実行できます。
  • docker psdocker logs などのコマンドで実行中のコンテナを確認したりログを確認できます。
  • Docker Compose を利用するには compose.yaml ファイルを作成します。
  • docker compose up でまとめてビルドと実行ができます。

しかしここで覚えた方法でコンテナが実行される場所はローカル環境になります。 クラウド環境で実行するには何が必要でしょうか?

SSH でログインして、開発した bot のソースコードをクラウド環境にコピーして docker compose up --build -d コマンドの実行すればよいのでしょうか? 誰もデプロイするために SSH でログインしてコマンドを実行したくはありません

次の章では実践的なデプロイ方法、及び運用方法を学びます。

参考資料

この章では bot を実行する為だけの必要な Docker の機能のみ紹介しました。

Docker をより学ぶには公式ドキュメントが最も参考になります。 Guides からステップバイステップで学ぶことが可能です。

https://docs.docker.com/get-started/overview/