Blow Up by Black Swan

docker composeを利用して、python(jupyter)+MySQL環境を構築

docker composeを利用して、Python(jupyter notebookを活用)+MySQL環境を構築したので、今回はそれについてまとめようと思います。まずは、手作業で作ってみた上で、それをdocker-composeで落とし込んでいます。以前、まとめ記事を書いたようにdocker composeについて学習したのですが、何がポイントかがよく掴みきれておらず、今回かなり苦戦しました。

しかし、今回、手作業で一からコンテナ間をリンクさせた上で、それをdocker composeファイルに落とし込んだことで、docker composeについての理解がかなり進んだように思います。

どなたか一人でも参考になれば幸いです。

1.それぞれ個別にコンテナから構築

まずは、手作業でのdockerコンテナとコンテナ間通信環境の構築です。完成後のイメージは以下のようなものです。

Docker-----------------------------+
 |                                 |
 | Con--------+       Con--------+ |
 |  | jupyter | 1===>> |         | |
 |  |(python) |        |  MySQL  | |
 |  |         | <<===2 |         | |
 |  +---------+        +---------+ |
 |                                 |
 +---------------------------------+
 * Con...コンテナ

非常に単純な図ではあるんですが、ポイントとすればpython側がクラインアントとなって、サーバであるMySQLに指令を送り、MySQLが対応するというところです。MySQLを理解する時に様々な言葉が出てきて混乱しましたが、HTTPと同じ、MySQLのこのクライアント・サーバ型の構造を理解するだけで、その他の理解もしやすくなりました。

ちなみに今回のコンテナ間通信では、runコマンド実行時に--linkオプションは使っていませんので、どちらのコンテナから作成しても問題ありません。使っていない理由については後述しています。

1-1. MySQLコンテナの作成

ここではMySQLコンテナを作ります。

1. mysqlコンテナの立ち上げ

以下のコマンドを実行し、MySQLコンテナを作成します。

$ docker run -d --name t-mysql -v /Users/hoge/:/var/lib/mysql mysql/mysql-server

ポイントは以下になります。

  • “t-mysql” -> test-mysqlの意味です
  • MySQLコンテナはバックグランドで起動する必要があります(-d)
  • DBの永続化のために、ホストとボリュームをマウントしています(“-v”)
  • MySQL公式が出しているmysql/mysql-serverイメージを利用しています

2. パスワードを確認しmysql serverのターミナルに入る

mysql/mysql-serverイメージでは、初めてコンテナを起動し、設定ファイルとリンクされていない場合は、パスワードが自動生成されるようになっています。このパスワードは起動時に表示されるようになっているので、ログからこのパスワードを確認します。

$ docker logs t-mysql

「GENERATED ROOT PASSWORD」という欄に自動生成されたパスワードが記述されています。

そして、次にこのパスワードを利用してmysqlのターミナルに入ります。

$ docker exec -it t-mysql mysql -u root -p

上記コマンドを実行するとパスワード入力を促す画面が出てくるため、先ほど確認したパスワードを入力します。mysql serverのターミナルに入るとプロンプトが”mysql>”に変わるので、ターミナルに入れたことが確認できると思います。

3. rootユーザのパスワードを変更する

パスワードが自動生成された場合、MySQLを利用し始めるには、この初期パスワードを変更する必要があります。

mysql> alter user root@localhost identified by ‘password’;

‘password’欄に新しいパスワードを入力します。

4. 新しいユーザの作成と権限の付与

次に新しいユーザを利用します。これは、rootユーザだと他のホストなどからアクセスができないようにされているからのようです。実際に、私もjupyter notebookから接続する際にrootユーザからだとエラーが出て接続できませんでした。

mysql> create user ‘username’@‘hostname’ identified by ‘password’;

ここでの‘username’と’password’は任意の名前です。‘hostname’はアクセスしたいホストのIPアドレスなどを記載します(自分のIPアドレスを指定すると他のホストからのアクセスはできない)。hostはネットワーク上の他の端末になります。

docker内で構築される世界では、コンテナ間の関係は、同じネットワークにいる他の端末との関係と同じになり、それぞれIPアドレスで識別されるようになっています。このhostnameで指定するIPアドレスは、使いたいホストのIPアドレスを指定する点に注意が必要で、自分のIPアドレスを指定すると他の端末からはアクセスできません。今回は、ホストを限定しない’%’を使用し、「’sipper’@‘%’」としています。

ちなみに、create userなどは大体のサイトでは大文字で記載していますが、小文字でも問題ありません。さらに、このユーザがMySQL上でできることを指定する許可を与えます。

mysql> grant all on *.* to ‘sipper’@‘%’

上記はすべての権限を付与するようにしていますが、アクセスの範囲を指定することもできます。その場合、allや *.*の部分を変更します。mysql> show grants for ’username’@‘hostname’で権限の範囲を確認することができます。

ユーザに関連する権限については、ホスト部分の許可、実行できるコマンド部分の許可(上記のallに該当する部分)、対象とするデータベースやテーブルの範囲に関する許可(上記の*.*の部分)をそれぞれする指定する必要があります。

以上でmysqlコンテナの準備が完了しました。

1-2. Python(jupyter)コンテナの作成

次は、Python(jupyter)のコンテナを作成します。

  • 使用するimageは、私が作成したubuntu/jupyter/mysqlになります。

以下がそのdockerfileになります。

RUN apt-add-repository -y ppa:git-core/ppa && \
    apt-get update && \
    apt-get install -y git \
                       vim

RUN git clone https://github.com/pyenv/pyenv.git ~/.pyenv
RUN echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc && \
    echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc

RUN echo 'if command -v pyenv 1>/dev/null 2>&1; then\n  eval "$(pyenv init -)"\nfi' >> ~/.bashrc

RUN apt-get install -y make build-essential libssl-dev zlib1g-dev \
                       libbz2-dev libreadline-dev libsqlite3-dev wget \
                       curl llvm libncurses5-dev xz-utils tk-dev \
                       libxml2-dev libxmlsec1-dev libffi-dev vim

ENV PATH $PATH:/root/.pyenv/bin

RUN pyenv install 3.6.5 && \
    pyenv global 3.6.5 && \
    pyenv rehash

ENV PATH $PATH:/root/.pyenv/shims

# install mysql connector
RUN pip install --upgrade pip && \
    pip install jupyter && \
    pip install mysql-connector-python

RUN mkdir workbook \
          ~/.jupyter && \
    touch ~/.jupyter/jupyter_notebook_config.py && \
    echo -e "c = get_config()\n" \
            "c.NotebookApp.ip = '*'\n" \
            "c.NotebookApp.open_browser = False\n" \
            "c.NotebookApp.port = 8888\n" \
            "c.NotebookApp.password = u" > ~/.jupyter/jupyter_notebook_config.py

EXPOSE 8888

上記のdockerfileの作成については、以前の投稿を参考にしていただければと思います。

1. jupyterコンテナの立ち上げ

$ docker run -it --name t-jupyter -p 8888:8888 ubuntu/jupyter/mysql

jupyter notebookは、ホストのブラウザで使うためポートの公開が必要です(“-p”)。--linkコマンドは、使用していません。公式ドキュメントによると、今後廃止される予定であり、また後ほどIPアドレスの確認と接続で行うように、各コンテナはコンテナ生成時点でIPアドレスが割り当てられているため、--linkオプションを使わずともそのIPアドレスにアクセスすればコンテナ間通信が可能となるためです。

2. jupyterの設定

次にjupyterの設定を行います。対象ファイルやある程度の記述は、dockerfileでimage生成時に構築されるようになっているため、ここではパスワードの設定を行います。以下の流れに添って、パスワードのハッシュ値を生成し、設定ファイルに書き込みます。

> ipython
In [1] from notebook.auth import passwd
In [2] passwd()
Enter password:’password’
Varify password: ‘password’
Out [2] ‘passwordのハッシュ値
In [3] exit()
> vim ./jupyter/jupyter_notebook_config.py

上記コマンドを実行し、ファイルの最後の行に先ほど設定したパスワードのハッシュ値をペースとして設定完了です。

3. jupyterの立ち上げ

最後にjupyterの立ち上げを行います。

> jupyternotebook --allow-root --port=8888 --ip=0.0.0.0

上記コマンドを実行後にホストのブラウザでhttp://127.0.0.1:8888/にアクセスすることで接続できます。以上でjupyterの準備が完了しました。

1-3. コンテナ間の接続

最後にコンテナ間の接続をかくにんするためにjupyterからpythonコマンドを使ってSQL文をMySQLコンテナに送ります。

1. 接続

ここでは、pythonコードについて主に記述します。juypter notebookで新しいファイルを作成してください。ちなみに、今回、pythonのmysqlコネクターとして”mysql-connector-python”を利用しています(上記のdockerfileでインストールされています)。下記コードを記載し、実行してください。

import mysql.connector

cnx = mysql.connector.connect(user='sipper', password='sql', host='172.17.0.2’)

エラーが立たなければ接続が成功したことを意味します。ここでのポイントは以下になります。

  • hostnameはmysqlのIPアドレスを指定する
  • mysqlでユーザを作成時の’hostname’がこのjupyterコンテナのホストに対応していないと接続できない
    • 今回はすべてのIPアドレスを受け入れる’%’を利用したため、接続可能

IPアドレスが不明な場合の接続方法は以下になります。

  1. ターミナルに接続
  • $ docker exec -it t-mysql bashでコンテナのターミナルに接続
  • mysqlのターミナルでない点に注意
  1. ’hostname’コマンドのインストール
  • > yum install -y hostnameでhostnameコマンドをインストール
  1. ホストネームの確認
  • > hostname -iでIPアドレスを確認

2. mysql serverにSQLコマンドを実行

次にmysql serverにSQLコマンド実行します。

cursor = cnx.cursor()
query1 = "create database if not exists visit;”
query2 = """create table if not exists visit.activity (id int, date date, place varchar(20), note varchar(30));"""
query3 = """insert into visit.activity (id, date, place, note) values (1, '2018-10-27', ‘moscow’, "nothing");"""
cursor.execute(query1)
cursor.execute(query2)
cursor.execute(query3)
cnx.commit()

上記では、新規のデータベースとテーブルを作成し、テーブルに新しいデータを入力しています。こちらもエラーが出なければうまく言っています。ちなみに、MySQLデータベースへの書き込みを完了させるには、commit()関数の実行が必要になりますので、その点に注意が必要です。

3. MySQLからデータを取得

次は、先ほど書き込んだデータを取得してみます。

query = "select * from visit.activity"
cursor2 = cnx.cursor()
cursor2.execute(query)

for a, b, c, d in cursor2:
    print(str(a) + ', ' + str(b) + ', ' + str(c) + ', ' + str(d))
print(cursor)
cursor.close()
cnx.close()

上記コマンドを実行すると、戻り値が先ほど登録したデータになっていることがわかると思います。

4. mysqlターミナルで確認

さらなる確認として、mysqlターミナルからも確認してみます。まず、mysqlのターミナルに接続します。

$ docker exec -it t-mysql mysql -u root -p

そして、パスワードを入力し、mysqlターミナルに接続します。下記の確認コマンドを実行します。

mysql> select * from visit.activity

これで、先ほどのデータを反映したテーブルが表示されると思います。以上が手作業でコンテナ間をリンクさせる場合の方法です。

2. docker-composeファイルで構築

上記で手作業でコンテナ間をリンクさせる方法がわかりました。それをdocker composeファイルに落とし込んだ内容が以下になります。

version: '3'
services:

  jupyter:
    image: ubuntu/jupyter/mysql
    container_name: t-jupyter
    ports:
     - "8888:8888"
    depends_on:
     - mysql
    tty: true

  mysql:
    image: mysql/mysql-server
    container_name: t-mysql
    volumes:
     - /Users/nero/docker_test/sqlfile:/var/lib/mysql

ファイル構成については、以下になります。

/test---+/python--dockerfile         
        +/mysql
        +docker-compose.yml

そしてdocker composeをビルドし、コンテナを作成します。

$ docker-compose up -d

MySQlがあるため、バックグラウンドで実行しています。これで2つのコンテナが立ち上がりました。あとは、jupyterの設定、MySQLの新しいユーザの設定、jupyterからのSQL文の実行については、上記と全く同じ方法になるため、割愛します。

3. まとめ

以上がdocker-composeでpython(jupyter)+MySQL環境を構築する方法です。一通りやってみて感じることは、docker環境の構築はdockerfileやdocker composeファイルの利用のどちらにおいても、基本的な流れを知らなければその便利な使い方はわからないということであり、逆に言えば、dockerコンテナを構築するという一番基本的な部分がわかれば、dockerfileやdocker composeといった便利な機能も使えるようになる、ということです。

かなり苦戦する部分もありましたが、その文かなり勉強になりました。どなたか一人でも参考になりましたら、幸いです。

読んでくださった方、どうもありがとうございました。

4. 参考サイト

参考サイトは以下になります。