Introduction
今までVSCodeを使ってPython環境をローカルPCに構築しデータ分析を主に行ってきた.
Dockerというものがあることは前々から知っており,必要なツールはインストールした.
色々な解説記事を見るとDockerfile
やdocker-compose.yml
やdevcontainer.json
などのファイルがいくつも出てきて何が必要なのかよくわからなかった.
そんな自分が5つのステップで学習を進めたらDockerの理解ができたので備忘録的にこの記事にまとめる.
- Step1:DockerHubから取得したDockerイメージを使用する
- Step2 Dockerfileを使用する
- Step3 DockerComposeを使用する
- Step4 devcontainer.jsonを使用する
- Step5 複数のコンテナを使用する
VSCodeとその拡張機能,DockderDesktop,WindowsであればWSLといった,Dockerを使うために必要なツール類のインストールに関しては他の記事を参照されたし.
自分が本記事を作成した時のスタート地点とゴール
- スタート
- VSCodeを使ってローカルでPythonを動かしたりライブラリをインストールしたことはある.
- VScodeを使ってDocker環境を作るために必要なツールはインストール済み.
ゴール
- DockerとVSCodeを使ってPythonの環境構築ができる.
成果物はGitHubにあげておく.
Step1 DockerHubから取得したDockerイメージを使用する
イメージを取得する
DockerHubにアクセスしpythonと検索すると使用可能なイメージの一覧が見れる.
DockerHubからDockerイメージを取得する.
$ docker image pull python:3.10-slim
取得したイメージ一覧を確認する.
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
python 3.10-slim b4447fd198e3 4 weeks ago 147MB
コンテナの作成と起動
いくつかのオプションを指定しコンテナの作成と起動をおこなう.
-
docker container run
:コンテナを作成し起動する基本コマンド -
-it
:対話型モードで起動するオプション -
--rm
:コンテナ終了時にコンテナを削除するオプション. -
python:3.10-slim
:イメージを先ほど確認したREPOSITORY:TAG
の形式で指定する(IMAGE ID
でも可) -
/bin/bash
:コンテナ起動と同時にシェルを起動するオプション.これを指定しない場合はpythonインタプリンタが起動.
$ docker container run -it --rm python:3.10-slim /bin/bash
root@d5197617f85c:/#
コンテナ起動後はLinuxの基本コマンドやpythonコマンドが使える.
root@d5197617f85c:/# ls
bin dev home lib32 libx32 mnt proc run srv tmp var
boot etc lib lib64 media opt root sbin sys usr
root@d5197617f85c:/# python --version
Python 3.10.12
別のターミナルを開いて起動中のコンテナを確認する.
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f0cf0db8e076 b4447fd198e3 "/bin/bash" About a minute ago Up About a minute wizardly_yalow
VScodeでコンテナにアクセスしpythonを実行する
VSCodeの拡張機能Remote Developmentはインストールしておく.
- VScodeの画面左下隅にある
><
のような青いアイコンをタップ - Attach to Running Containerをクリック
- 起動中のコンテナ名(先ほど確認した
NAMES
の値)をクリック
ここまでの内容をおこなえば,コンテナに接続したVSCodeのウィンドウが立ち上がる.
コンテナ内には既にいくつかのフォルダが存在するが,optフォルダの中身は空なのでまずはそこに移動するとよい.
あとは普段と同じように拡張機能をインストールしたり,ディレクトリを作成したり,pythonファイルを作成して実行すればよい.
コンテナを終了
コンテナを終了する.
※作成したディレクトリやpythonファイルはコンテナとともに消えてしまう.
$ exit
ローカルPCの内のディレクトリをコンテナ内にマウントする.
マウントとはローカルPC内のディレクトリとコンテナ内のディレクトリを同期すること.
マウントすることでコンテナ内で作成したファイルはローカルPCにも保存されるため,コンテナを消してもファイルは残り続ける.
まずローカル内に以下のようなディレクトリ構成を作る.
step1/
workspace/
hello.py
コンテナの作成と起動を行うコマンドを実行する.
-
--mount
:マウントするためのオプション -
type=bind
:マウントの種類を指定 -
source=<絶対パス>/workspace
:ローカルPC内のマウントしたいディレクトリを絶対パスで指定 -
target=/workspace
:コンテナ内のディレクトリを指定
$ docker container run -it --rm --mount type=bind,source=<絶対パス>/workspace,target=/workspace python:3.10-slim /bin/bash
root@e9783105d4bf:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var workspace
root@e9783105d4bf:/# ls workspace
hello.py
コンテナ内にworkspaceディレクトリとhello.pyが既に存在することが確認できる.
コンテナ内のworkspace内に新しいディレクトリやファイルを作成するとローカルPCのworkspaceにも同じものが作成される.
※workspace外で作成したものはコンテナとともに消えてしまう.
Step2 Dockerfileを使用する
Dockerfileを作成する
Dockerfileを使用することで,DockerHubから取得したベースイメージをカスタマイズできる.
まず以下のようなディレクトリ構成を作る.
step2/
workspace/
/src
hello.ipynb
Dockerfile
requirements.txt
Dockerfileを作成する.
- FROM:ベースイメージを指定
- WORKDIR:作業ディレクトリを指定
- COPY:ローカルPCからコンテナにコピーするディレクトリやファイルを指定
- RUN:イメージ作成時に実行するコマンドを指定
- CMD:コンテナ起動時に1度だけ実行するコマンドを指定
# DockerHubのベースとなるイメージを指定
FROM python:3.11-slim
# 作業ディレクトリを変更
WORKDIR /workspace
# ローカルPCのファイルをコンテナのカレントディレクトリにコピー
COPY requirements.txt ${pwd}
# pipのアップデート
RUN pip install --upgrade pip
# pythonパッケージをインストール
RUN pip install -r requirements.txt
# コンテナ起動時に実行するコマンドを指定
CMD ["/bin/bash"]
Dockerfileと同じ階層にrequirements.txtを作成する.
jupyter
numpy
pandas
イメージを作成する
step2フォルダに移動し以下のコマンドでDockerfileからイメージを作成する.
-
-t python:step2
:-t REPOSITORY名:TAG名
の形式で指定 -
workspace/.
:Dockerfileの場所をカレントディレクトリからの相対パスで指定
$ docker image build -t python:step2 ./workspace
[+] Building 33.6s (10/10) FINISHED
# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
python step2 ebf33e38b104 43 hours ago 534MB
python 3.10-slim b4447fd198e3 4 weeks ago 147MB
コンテナの作成と起動
あとはStep1と同じようにコンテナの作成と起動をおこなえばよい.
Step1と異なる点
- /bin/bashを指定しなくてもよい(DockerfileのCMDで指定しているため)
- シェルのカレントディレクトリがルートではなくworkspaceとなっている(DockerfileのWORKDIRで指定しているため)
- pythonライブラリがインストールされている(DockerfileのRUNでインストールを実行しているため)
$ docker container run -it --rm --mount type=bind,source=<絶対パス>/workspace/src,target=/workspace/src python:step2
root@3e1b626c3542:/workspace# ls
requirements.txt src
root@3e1b626c3542:/workspace# pip list
Package Version
------------------------- --------
...
jupyter 1.0.0
numpy 1.25.1
pandas 2.0.3
pip 23.2
...
Step3 DockerComposeを使用する
Docker Composeを使用することで,コンテナの作成や起動を簡潔におこなうことができる.
以下のディレクトリ構成を作成
step3/
workspace/
.devcontainer/
Dockerfile
requirements.txt
docker-compose.yml
src/
hello.ipynb
docker-compose.ymlを作成する
docker-compose.ymlはコンテナの作成や起動を管理する設定ファイル.
Docker関連のファイルが増えてきたので.devcontainerフォルダににまとめる.
docker-compose.yml以外はStep2と同じものを使用する.
docker-compose.ymlを作成する
services: # サービスを定義しますという決まり文句
python: # pythonという名前でサービスを定義
image: python:step3 # イメージのREPOSITORY名:TAG名を指定
build: . # Dockerfileを相対パスで指定
container_name: python-step3 # 作成されるコンテナ名を指定
working_dir: /workspace/src # 作業ディレクトリを指定
volumes: # マウントするファイルを指定する
- ../src:/workspace/src # ローカルPCのsrc:コンテナのworkspace/src
tty: true # コンテナを起動し続ける
コンテナを起動する
docker-compose.ymlがあるディレクトリに移動し以下のコマンドを実行する.
イメージの作成,コンテナの作成,起動が一発で完了する.
$ docker compose up -d
[+] Running 0/1
- python Warning 2.4s
[+] Building 1.6s (10/10) FINISHED
...
[+] Running 1/1
- Container python-step3 Started
起動中のコンテナに接続する
まずは起動しているコンテナを確認する.
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
python-step3 python:step3 "/bin/bash" python 21 seconds ago Up 20 seconds
Step2までとは異なりローカルPCのターミナルがコンテナに接続していない状態で起動される.
上で表示したSERVICE名を指定してコンテナのシェルを起動する.
$ docker compose exec python /bin/bash
root@eeaa12bfb21a:/workspace/src#
コンテナの終了
Step2までとは異なりシェルにexit
を入力してもコンテナは動き続ける.
以下のコマンドでコンテナの終了と削除をおこなう.
$ docker compose down
- Container python-step3 Removed
- Network devcontainer_default Removed
Step4 devcontainer.jsonを使用する
Step3までの内容でDockerを使用した開発環境構築は十分だといえる.
このステップではコンテナの起動や停止をコマンドを使わずVScode上で完結させる.
ディレクトリ構成は以下の通り.
今回追加するdevcontainer.json以外はStep3と同じ.
step4/
workspace/
.devcontainer/
Dockerfile
requirements.txt
docker-compose.yml
devcontainer.json
src/
hello.ipynb
devcontainer.jsonを作成する
-
"extensions"
の名前はVScode上で拡張機能を右クリックしCopy Extension ID
をクリックすることでコピーできる. - VScodeの設定はSettings画面でGUI操作で設定した内容がJSONファイルに記載されている.
設定内容が記載されたJSONファイルはコマンドパレットを開き>open user settings(JSON)
を開くと見ることができるため,その内容を"settings"
に書けばよい.
{
"name": "Python VScode", // VScodeがコンテナに接続したときのタイトルを自由に設定
"dockerComposeFile": "docker-compose.yml", // docker-conpose.ymlファイルを指定
"service": "python", // docker-conpose.ymlファイル内に記載したサービス名を指定
"workspaceFolder": "/workspace/src", // VScode接続時に開くフォルダを指定
"customizations": {
"vscode": {
"extensions": [ // コンテナ作成時にインストールする拡張機能を記載
"ms-python.python", // python拡張機能
"ms-toolsai.jupyter" // jupyter拡張機能
],
"settings": { // VScodeの設定を記載
"files.autoSave": "afterDelay" // ファイルのオートセーブ
}
}
}
}
コンテナの起動と終了
- workspaceディレクトリをVScodeで開く
- VScodeの画面左下隅にある
><
のような青いアイコンをタップ - Reopen in Containerをクリックするとコンテナが起動しリモート接続した状態になる
- VScodeを閉じればコンテナが終了する
Step5 複数のコンテナを使用する
このステップではWEBサーバーとDBサーバーなど複数のコンテナを利用する方法について紹介する.
ディレクトリ構成は以下の通り.
step5/
workspace/
.devcontainer/
db/
Dockerfile
web/
Dockerfile
requirements.txt
devcontainer.json
docker-compose.yml
db/
init/
01_create_tables.sql
02_insert_data.sql
web/
src/
app.py
Docker構築用のファイルを用意
データベース用のDockerfile
# ベースイメージを指定
FROM mysql:5.7
# タイムゾーンを指定
ENV TZ=Asia/Tokyo
webサーバー用のDockerfile
# DockerHubにあるpythonのイメージ指定する
FROM python:3.11-slim
# 作業ディレクトリをworkspaceに変更
WORKDIR /workspace/.devcontainer/web
# ローカルPCのrequirements.txtをコンテナのカレントディレクトリ(上で指定した場所)にコピー
COPY requirements.txt ${pwd}
# pipのアップデート
RUN pip install --upgrade pip
# pythonパッケージをインストール
RUN pip install -r requirements.txt
flaskとpythonでmysqlに接続するためのライブラリを定義
flask
mysql-connector-python
コンテナに接続するVScodeの設定を記述
{
"name": "flask mysql", // VScodeがコンテナに接続したときのタイトルを自由に設定
"dockerComposeFile": "docker-compose.yml", // docker-conpose.ymlファイルを指定
"service": "web", // docker-conpose.ymlファイル内に記載したサービス名(webサーバー)を指定
"workspaceFolder": "/workspace/web/src", // VScode接続時に開くフォルダを指定
"customizations": {
"vscode": {
"extensions": [ // コンテナに接続するVScodeにインストールする拡張機能を記載
"ms-python.python" // python
],
"settings": { // VScodeの設定を記載
"files.autoSave": "afterDelay" // ファイルのオートセーブ
}
}
}
}
db用とwebサーバー用の2つのコンテナを作成するよう定義
services: # サービスを定義するための決まり文句
db: # DB用のコンテナ(名前は自由に定義できる)
image: mysql:step5 # イメージのREPOSITORY名:TAG名
build: ./db # Dockerfileの相対パス
container_name: mysql-step5 # コンテナ名
expose: # webサーバー側のコンテナからアクセスするために必要
- "3306" # webサーバー側のコンテナに公開するポート番号
ports: # ローカルPCからもアクセスするために必要
- "3307:3306" # ローカルPCのポート番号:コンテナのポート番号
environment:
- MYSQL_ROOT_PASSWORD=root # rootユーザのパスワード
- MYSQL_DATABASE=step5 # データベース名
- MYSQL_USER=step5 # ユーザー名
- MYSQL_PASSWORD=step5 # ユーザーのパスワード
volumes:
- ../db/database:/var/lib/mysql # データベースの情報を保持するために定義
- ../db/init:/docker-entrypoint-initdb.d # コンテナ初回作成時に実行するsqlファイルをマウントする
web: # webサーバー用のコンテナを定義
image: python:step5 # イメージのREPOSITORY名:TAG名を指定
build: ./web # Dockerfileの相対パス
container_name: python-step5 # コンテナ名
depends_on: # サービスの依存関係を指定する
- db # webがdbに依存するよう指定
working_dir: /workspace/web/ # 作業ディレクトリを指定
volumes: # マウントするファイルを指定する
- ../web:/workspace/web # ローカルPCのwebをコンテナのworkspace/webにマウント
tty: true # コンテナを起動し続けるための定義
コンテナ初回作成時にテーブル作成やレコードの挿入などを行うクエリをsqlファイル内に定義する
ファイル名の順番でsqlファイルが実行されるため先頭に数字などつけるとよい
create table users(id int, name varchar(10), create_date datetime);
INSERT INTO users(id, name, create_date) VALUES
(1, 'sena', now()),
(2, 'taro', now());
flaskでWebAPIを作成するソース
from flask import Flask, jsonify
import mysql.connector
app = Flask(__name__)
db = mysql.connector.connect(
host="mysql-step5", # hostはdocker-compose.ymlファイルで定義したDBのコンテナ名を指定
user="step5",
password="step5",
database="step5"
)
@app.route('/')
def get_data():
cursor = db.cursor()
cursor.execute("SELECT * FROM users")
data = cursor.fetchall()
cursor.close()
return jsonify( data)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=500)
コンテナの起動からAPIの動作確認まで
step4と同様の手順でVSCodeからReopen in Containerでコンテナの作成および起動をおこなう.
app.pyファイルを実行.
/usr/local/bin/python /workspace/web/src/app.py
root@ea6494b0cbad:/workspace/web/src# /usr/local/bin/python /workspace/web/src/app.py
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:500
* Running on http://172.21.0.3:500
Press CTRL+C to quit
http://127.0.0.1:500にアクセスし以下のように表示されれば成功!
[[1,"sena","Mon, 17 Jul 2023 13:08:23 GMT"],[2,"taro","Mon, 17 Jul 2023 13:08:23 GMT"]]
データベースの確認
データベースの中身を確認する方法として2つ紹介する.
- mysqlコンテナのシェルに接続して確認
- ローカルPCのターミナルから直接確認
1. mysqlコンテナのシェルに接続して確認
まずは起動中のコンテナを確認してみる.
$docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ea6494b0cbad python:step5 "/bin/sh -c 'echo Co…" 7 minutes ago Up 7 minutes python-step5
9244be7034ad mysql:step5 "docker-entrypoint.s…" 7 minutes ago Up 7 minutes 33060/tcp, 0.0.0.0:3307->3306/tcp mysql-step5
mysqlコンテナのシェルに接続する.
$ docker exec -it mysql-step5 /bin/bash
あとは普段通りsqlコマンドを実行していけばよい.
$ mysql -u root -proot
mysql> select * from step5.users;
+------+------+---------------------+
| id | name | create_date |
+------+------+---------------------+
| 1 | sena | 2023-07-17 13:32:10 |
| 2 | taro | 2023-07-17 13:32:10 |
+------+------+---------------------+
2 rows in set (0.02 sec)
2. ローカルPCのターミナルから直接確認
docker-compose.yml内でローカルPCの3307
ポートを経由して,コンテナ内のMySQLサーバーのポート3306
にアクセスできるよう定義している.
なので以下のように3307
ポートを指定することでコンテナのmysqlに直接アクセスできる.
$ mysql -h 127.0.0.1 --port 3307 -u root -proot
MySQL [(none)]> INSERT INTO step5.users(id, name, create_date) VALUES (3, 'ken', now());
Query OK, 1 row affected (0.007 sec)
dbフォルダの中身について
コンテナを起動するとdbフォルダにはdatabaseフォルダが作成されているはず.
databaseフォルダにコンテナ内のDBの内容を保存しておくことで情報が失われない.
一度コンテナを停止し再度起動すると先ほどinsertしたデータが追加されているはず.
$ mysql -h 127.0.0.1 --port 3307 -u root -proot
MySQL [(none)]> select * from step5.users;
+------+------+---------------------+
| id | name | create_date |
+------+------+---------------------+
| 1 | sena | 2023-07-17 13:32:10 |
| 2 | taro | 2023-07-17 13:32:10 |
| 3 | ken | 2023-07-17 13:45:41 |
+------+------+---------------------+
initフォルダ内のsqlはいくら書き換えてもdatabaseフォルダが存在すると反映されない.
反映させるにはローカルのdatabaseフォルダを一度削除してからコンテナを起動すればよい.
Appendix
便利なコマンド
停止中のコンテナを一括削除
$ docker container prune
不要なイメージを一括削除
$ docker image prune -a
Conclusion
Dockerを使うことで,ローカル環境を汚すことなく様々な言語やライブラリ,DBを使用した開発が気軽に試せる点が魅力的に感じた.
データ分析やアプリ開発などにどんどん活用していきたい.