PythonとDockerで簡単に始める！仮想通貨自動売買の全手順：①

## 背景
`Docker`や`Python`といった技術を単体で勉強してるけど実際に動くシステムを作ったことはない、という方もいるのではないでしょうか。自分もその一人です。
というわけで実際に動くシステムを作りつつ記事としてアウトプットしていきます。
とりあえず何かシステムを作ってみたい方の助けになれば幸いです。

:::note warn
+ 本記事は素人が趣味で作成しているものです
+ 何か間違いがありましたらやさしく教えてくださると嬉しいです
+ 特にセキュリティ面は一切考慮されていません
+ 本記事を参考にして何か不利益を被っても一切責任を取れません
:::


## この記事で何をするのか？
+ `Docker`を使用してシステムのインフラを作成します
+ `Python`による仮想通貨自動売買プログラムを構築します
+ 3部構成のうちの第1弾です
    1\. `Dockerによるインフラ構築`
    2\. APIによる仮想通貨価格取得とDBへの格納
    3\. 売買判断プログラム作成

#### 環境
```terminal
$ uname -a
Linux vm-037af314-77 6.8.0-36-generic #36-Ubuntu SMP PREEMPT_DYNAMIC Mon Jun 10 10:49:14 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

$ docker --version
Docker version 27.5.1, build 9f9e405
```

## 作成するシステムの概要
Host OS上にPythonコンテナとMysqlコンテナで構成します。
Pythonコンテナで仮想通貨データの取得し、MysqlコンテナのDBへ格納します。定期的に売買判断を行い、実際にAPIによって自動注文します。
![システム.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2087704/878f6f9c-0cae-6266-c03c-0edd5e655a5f.png)



## 構築
### ディレクトリ構成
今回作成するプロジェクトのディレクトリ構成は下記のようになっています。
- `app`
売買判断を行うプログラムを格納します。
- `db`
db関係のデータを格納します。
- `docker-compose.yml`
コンテナの設計図のようなイメージです。
- `mysql_context`
Mysqlコンテナの設定です。
- `python_context`
Pythonコンテナの設定です。

```terminal:ディレクトリ構成
todotodo@vm:~$ tree BTC_Trading/
BTC_Trading/
├── app
│   └── app.py
├── db
├── docker-compose.yml
├── mysql_context
│   ├── my.conf
│   └── sql
│       └── 001-create-table.sql
└── python_context
    ├── Dockerfile
    └── requirements.txt
```
### コンテナを作成する
今回のシステムでは複数のコンテナを使用します。
複数のコンテナを一斉に設定して起動するためのファイルはComposeファイルと呼ばれます。今回のComposeファイルは`docker-compose.yml`です。
```yaml:docker-compose.yml
version: '3'

networks:                           #コンテナが所属するネットワークを定義
  BTC_Traiding_Network:
    ipam:
      driver: default
      config:
        - subnet: 10.10.10.0/24       #コンテナが所属するサブネット

services:
  db:
    image: mysql:5.7                  #使用するイメージ 
    container_name: mysql_host        #コンテナの名前 
    environment:                      #DBの環境変数を設定
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: BTC
      MYSQL_USER: docker
      MYSQL_PASSWORD: docker
      TZ: 'Asia/Tokyo'
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
    - ./db:/var/lib/mysql             #ホストOSの./db/data配下にマウント
    - ./mysql_context/my.conf:/etc/mysql/conf.d/my.cnf #mysql設定ファイル関係を読み込む
    - ./mysql_context/sql:/docker-entrypoint-initdb.d #Docker起動時に使用するスクリプトを読み込む
    networks:
      BTC_Traiding_Network:           #コンテナが所属するネットワークを指定
        ipv4_address: 10.10.10.2      #コンテナのIPアドレスを指定
  python: 
    image: python:latest
    container_name: python_host       #コンテナの名前
    build: ./python_context           #コンテナを作成する際にやることが書いてある
    volumes: 
      - ./app:/app                    #ホストOSの./app の中身をコンテナの/appにマウントする       
    depends_on:           
      - db                            #dbを先に起動してほしい
    tty: true
    networks:
      BTC_Traiding_Network:           #コンテナが所属するネットワークを指定
        ipv4_address: 10.10.10.3      #コンテナのIPアドレスを指定
```
:::note
#### コンテナは家畜
仮想化の進展によってサーバ上に複数のコンテナを構成してその上でアプリケーションを実行するようになっています。なんらかのトラブルがあるとコンテナがあっさりと削除され再作成されます。
つまりコンテナはいつ消えるか分からないので重要なデータはコンテナ外に設置する必要があります。
例えばデータベースのデータやインフラ上で動かすアプリケーションは`volume`設定によってコンテナ外に置いています。
:::

Pythonコンテナを作成する際に実行することは`Dockerfile`に記載します。
`RUN`にはコンテナ作成後に実行するコマンドを記載します。ここでは必要なコマンドのinstallを行っています。
```Dockerfile:Dockerfile
FROM python:latest
RUN apt-get update && \
    apt-get install -y \
    build-essential \
    cmake \
    git \
    sudo \
    wget \
    vim \
    mariadb-client \         #mysqlコマンドを使えるようにする
    iputils-ping             #pingを使えるようにする
RUN pip install --upgrade pip                
COPY ./requirements.txt /requirements.txt    
RUN pip install -r /requirements.txt         
WORKDIR /app
CMD ["/bin/bash"]
```
`pip`でinstallしたいものリストです。
アプリケーションを開発するタイミングで必要になったものはここに追加していきます。
```:requirement.txt
mysql-connector-python
requests
```

Mysqlコンテナ作成後に実行されるコマンドです。仮想通貨の価格を格納するためのテーブルを作成します。とりあえず作ってみたもののアプリケーション側の仕様によってテーブル構成が変更になる可能性が高いです。
```sql:001-create-table.sql
create table IF not exists btc_table
(
    timestamp DATETIME,
    ask FLOAT,
    bid FLOAT,
    high FLOAT,
    low FLOAT,
    volume FLOAT
)DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
```
:::note
#### あとからインフラを変更する
dockerではインフラの設定がコードとして書かれて設定ファイルにまとまっています。
仮にインフラに設定変更が生じても設定ファイルの変更によって比較的簡単にインフラ側を変更できます。
:::

### コンテナの起動
`docker-compose.yml`をもとにコンテナをビルドし起動します。
```terminal
todotodo@vm:~/BTC_Trading$ docker compose build
todotodo@vm:~/BTC_Trading$ docker compose up -d
```
コンテナが起動していることが確認できます。
```terminal
stodo@vm:~/BTC_Trading$ docker ps
CONTAINER ID   IMAGE           COMMAND                  CREATED         STATUS         PORTS                 NAMES
db4d3e1729c0   python:latest   "python3"                5 seconds ago   Up 4 seconds                         python_host
5b74272b657b   mysql:5.7       "docker-entrypoint.s…"   5 seconds ago   Up 4 seconds   3306/tcp, 33060/tcp   mysql_host
```

## 動作確認
### ネットワーク確認
今回作成した2つのコンテナのIPアドレスを確認します。コンテナ`mysql_host`のIPアドレスは`docker-compose.yml` で指定したとおり`10.10.10.2`だとわかります。
```json:docker network inspect btc_trading_BTC_Traiding_Network
[
    {
        "Name": "btc_trading_BTC_Traiding_Network",
        "Id": "03bdc3e60ba3d470bc90590c0be40b4c6f295a3cc481f479f14740d21f8bc3bd",
        "Created": "2025-02-06T23:00:19.428822351+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.10.10.0/24"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "01be9a352ce494b6c16d7dcc8f85e9521bed0ff2cf77d1f309ef43b015fadd6d": {
                "Name": "python_host",
                "EndpointID": "0d0bb89c8bbc0335d4448b12d5236e49f8d5fa1da82e4448d4f53f2f3a55a099",
                "MacAddress": "02:42:0a:0a:0a:03",
                "IPv4Address": "10.10.10.3/24",
                "IPv6Address": ""
            },
            "5f7edcd817829ed861d3c13d98fc17b366c9c1db3045889d2de574cccf4b5db4": {
                "Name": "mysql_host",
                "EndpointID": "e6f391895adc8e3cee4e628a6debbc8b50c2b820d0a73e202e7bdda4594c8b36",
                "MacAddress": "02:42:0a:0a:0a:02",
                "IPv4Address": "10.10.10.2/24",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.config-hash": "66d67ed1fe79288979954d544346d2f682b2f42d8d3c3893b5846e38e6fc9a6a",
            "com.docker.compose.network": "BTC_Traiding_Network",
            "com.docker.compose.project": "btc_trading",
            "com.docker.compose.version": "2.32.4"
        }
    }
]
```
### テーブルが作成されていることを確認
`python_host`から`mysql_host`のmysqlに接続します。
```terminal
todotodo@vm:~/BTC_Trading$ docker compose exec python bash
root@661d031ce120:/app# mysql -u root -h 10.10.10.2 -p
```

データベースとテーブルが意図したとおりに作成されています。
```sql
MySQL [(none)]> SHOW COLUMNS FROM BTC.btc_table;
+-----------+----------+------+-----+---------+-------+
| Field     | Type     | Null | Key | Default | Extra |
+-----------+----------+------+-----+---------+-------+
| timestamp | datetime | YES  |     | NULL    |       |
| ask       | float    | YES  |     | NULL    |       |
| bid       | float    | YES  |     | NULL    |       |
| high      | float    | YES  |     | NULL    |       |
| low       | float    | YES  |     | NULL    |       |
| volume    | float    | YES  |     | NULL    |       |
+-----------+----------+------+-----+---------+-------+
6 rows in set (0.008 sec)
```
### インターネットに疎通できることを確認
Pythonコンテナからインターネットに疎通できることを確認します。
Pythonコンテナに入って`Google Public DNS` 8.8.8.8 へpingが通るか確認します。
```terminal
root@9b0fe47aa47a:/app# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=111 time=8.80 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=111 time=8.42 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=111 time=8.37 ms
^C
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 8.368/8.527/8.797/0.191 ms
```
:::note
#### 8.8.8.8
8.8.8.8はGoogleが提供する`DNS`のIPアドレスです。
覚えやすい`Global IP Address`としてインターネットへ出られるか確認する際によく使われます。
:::

## 次の記事
https://qiita.com/todotodo/items/0e4008093e6a84d2851a

## まとめ
今回は仮想通貨自動売買システムのインフラ部分を作成しました。
DBの作成とインターネットへの疎通が確認できたので次回はアプリケーションの作成に入ります。


### 参考資料
DBのdocker作成
https://qiita.com/A-Kira/items/f401aea261693c395966
https://qiita.com/taqm/items/8b6b896ec4a9a0b84886

Docker Compose のネットワーク指定
https://zaki-hmkc.hatenablog.com/entry/2021/02/26/234357#Docker-Compose