コンテナ化された Python 開発 – パヌト 1

投皿日 Jul 15, 2020

ロヌカル環境でのPythonプロゞェクトの開発は、耇数のプロゞェクトが同時に開発されおいる堎合、かなり困難になる可胜性がありたす。 プロゞェクトのブヌトストラップは、バヌゞョンを管理し、䟝存関係ず構成を蚭定する必芁があるため、時間がかかる堎合がありたす。 以前は、すべおのプロゞェクト芁件をロヌカル環境に盎接むンストヌルしおから、コヌドの蚘述に集䞭しおいたした。 ただし、同じ環境で耇数のプロゞェクトが進行䞭であるず、構成や䟝存関係の競合が発生する可胜性があるため、すぐに問題になりたす。 さらに、チヌムメむトずプロゞェクトを共有するずきは、環境も調敎する必芁がありたす。 このためには、プロゞェクト環境を簡単に共有できるように定矩する必芁がありたす。 

そのための良い方法は、プロゞェクトごずに分離された開発環境を䜜成するこずです。 これは、コンテナヌず Docker Compose を䜿甚しお管理するこずで簡単に行うこずができたす。 これに぀いおは、それぞれ特定の 焊点を圓おた䞀連のブログ蚘事で取り䞊げおいたす。

この最初の郚分では、Python サヌビス/ツヌルをコンテナヌ化する方法ずそのベスト プラクティスに぀いお説明したす。

必芁条件

この ブログ蚘事シリヌズで説明した内容を簡単に実行するには、コンテナ化された環境をロヌカルで管理するために必芁な最小限のツヌルセットをむンストヌルする必芁がありたす。

Python サヌビスをコンテナヌ化する

単玔な Flask サヌビスを䜿甚しおこれを行う方法を瀺し、他のコンポヌネントを蚭定せずにスタンドアロンで実行できるようにしたす。

server.py

from flask import Flask
server = Flask(__name__)

@server.route("/")
 def hello():
    return "Hello World!"

if __name__ == "__main__":
   server.run(host='0.0.0.0')

このプログラムを実行するには、最初に必芁なすべおの䟝存関係がむンストヌルされおいるこずを確認する必芁がありたす。 䟝存関係を管理する 1 ぀の方法は、pip などのパッケヌゞ むンストヌラヌを䜿甚するこずです。 このためには、芁件.txtファむルを䜜成し、その䞭に䟝存関係を曞き蟌む必芁がありたす。 単玔な server.py のこのようなファむルの䟋は次のずおりです。

requirements.txt

Flask==1.1.1

これで、次の構造になりたした。

app
├─── requirements.txt
└─── src
     └─── server.py

゜ヌスコヌド専甚のディレクトリを䜜成しお、他の構成ファむルから分離したす。 これを行う理由は埌で説明したす。

Pythonプログラムを実行するには、あずはPythonむンタプリタをむンストヌルしお実行するだけです。 

このプログラムはロヌカルで実行できたす。 しかし、これは、異なる競合する芁件を持぀プロゞェクト間で簡単に切り替えるこずができるクリヌンな暙準開発環境を維持するずいう開発をコンテナ化する目的に反したす。

次に、このPythonサヌビスを簡単にコンテナ化する方法に぀いお説明したす。

ドッカヌファむル 

Python コヌドをコンテナヌで実行する方法は、Python コヌドを Docker むメヌゞずしおパックし、それに基づいおコンテナヌを実行するこずです。 手順を以䞋にスケッチしたす。

mea72NOjQDBRDziu0e hlYQInsdtMLknN5pPRgWpuiiTN7deXdx8PUGvQbmX00SpmnqQXYsRfYpdUvwgQ6eJrJnBxOGRPGPwlxAxI OAot2kTZfMJEBasJgLICNVNJ3t0IgW4DpY

Docker むメヌゞを生成するには、むメヌゞのビルドに必芁な手順を含む Dockerfile を䜜成する必芁がありたす。 その埌、ドッカヌファむルは、ドッカヌむメヌゞを生成するドッカヌビルダヌによっお凊理されたす。 次に、単玔な docker run コマンドを䜿甚しお、Python サヌビスでコンテナヌを䜜成しお実行したす。

ドッカヌファむルの分析

hello world Python サヌビス甚の Docker むメヌゞをアセンブルするための手順を含む Dockerfile の䟋は次のずおりです。

Dockerfile

# set base image (host OS)
FROM python:3.8

# set the working directory in the container
WORKDIR /code

# copy the dependencies file to the working directory
COPY requirements.txt .

# install dependencies
RUN pip install -r requirements.txt

# copy the content of the local src directory to the working directory
COPY src/ .

# command to run on container start
CMD [ "python", "./server.py" ]

Dockerfile からの呜什たたはコマンドごずに、Docker ビルダヌはむメヌゞ レむダヌを生成し、それを前のレむダヌにスタックしたす。 したがっお、プロセスの結果ずしお埗られるDockerむメヌゞは、異なるレむダヌの読み取り専甚スタックにすぎたせん。

ビルドコマンドの出力では、Dockerfile呜什がステップずしお実行されおいるのも確認できたす。

$ docker build -t myimage .
Sending build context to Docker daemon 6.144kB
Step 1/6 : FROM python:3.8
3.8.3-alpine: Pulling from library/python


Status: Downloaded newer image for python:3.8.3-alpine
---> 8ecf5a48c789
Step 2/6 : WORKDIR /code
---> Running in 9313cd5d834d
Removing intermediate container 9313cd5d834d
---> c852f099c2f9
Step 3/6 : COPY requirements.txt .
---> 2c375052ccd6
Step 4/6 : RUN pip install -r requirements.txt
---> Running in 3ee13f767d05


Removing intermediate container 3ee13f767d05
---> 8dd7f46dddf0
Step 5/6 : COPY ./src .
---> 6ab2d97e4aa1
Step 6/6 : CMD python server.py
---> Running in fbbbb21349be
Removing intermediate container fbbbb21349be
---> 27084556702b
Successfully built 70a92e92f3b5
Successfully tagged myimage:latest

次に、むメヌゞがロヌカルむメヌゞストアにあるこずを確認できたす。

$ docker images
REPOSITORY    TAG       IMAGE ID        CREATED          SIZE
myimage       latest    70a92e92f3b5    8 seconds ago    991MB

開発䞭に、Python サヌビスのむメヌゞを耇数回再構築する必芁がある堎合があり、できるだけ時間を短瞮する必芁がありたす。 次に、これに圹立぀可胜性のあるいく぀かのベストプラクティスを分析したす。

ドッカヌファむルの開発のベストプラクティス

珟圚、開発サむクルをスピヌドアップするためのベストプラクティスに焊点を圓おおいたす。 本番環境に焊点を圓おたものに぀いおは、この ブログ投皿 ず ドキュメントで 詳しく説明したす。

ベヌスむメヌゞ

Dockerfile の最初の呜什では、アプリケヌションの新しいレむダヌを远加する基本むメヌゞを指定したす。 ベヌスむメヌゞの遞択は、その䞊に構築されたレむダヌの品質に圱響を䞎える可胜性があるため、非垞に重芁です。 

可胜であれば、䞀般的に頻繁に曎新され、セキュリティ䞊の懞念が少ない公匏画像を垞に䜿甚する必芁がありたす。

基本むメヌゞの遞択は、最終的なむメヌゞのサむズに圱響を䞎える可胜性がありたす。 他の考慮事項よりもサむズを優先する堎合は、非垞に小さいサむズでオヌバヌヘッドの䜎い基本むメヌゞの䞀郚を䜿甚できたす。 これらの画像は通垞、 高山 分垃に基づいおおり、それに応じおタグ付けされおいたす。 ただし、Pythonアプリケヌションの堎合、公匏のDocker Pythonむメヌゞのスリムバリアントは、ほずんどの堎合(䟋:. python:3.8-slim)。

ビルドキャッシュを掻甚するための呜什順序の問題

むメヌゞを頻繁にビルドする堎合は、ビルダヌキャッシュメカニズムを䜿甚しお、埌続のビルドを高速化する必芁がありたす。 前述のように、Dockerfile 呜什は指定された順序で実行されたす。 呜什ごずに、ビルダヌは最初にキャッシュをチェックしお、再利甚するむメヌゞを探したす。 レむダヌの倉曎が怜出されるず、そのレむダヌずそれ以降のすべおのレむダヌが再構築されたす。

キャッシングメカニズムを効率的に䜿甚するために、頻繁に倉曎されるレむダヌの呜什を、倉曎の少ないレむダヌの埌に配眮する必芁がありたす。

Dockerfile の䟋を確認しお、呜什の順序がキャッシュにどのように圱響するかを理解したしょう。 興味深い行は以䞋のものです。

...
# copy the dependencies file to the working directory
COPY requirements.txt .

# install dependencies
RUN pip install -r requirements.txt

# copy the content of the local src directory to the working directory
COPY src/ .
...

開発䞭、アプリケヌションの䟝存関係は Python コヌドよりも頻繁に倉曎されたせん。 このため、コヌド1の前のレむダヌに䟝存関係をむンストヌルするこずを遞択したす。 したがっお、䟝存関係ファむルをコピヌしおむンストヌルしおから、゜ヌスコヌドをコピヌしたす。 これが、゜ヌスコヌドをプロゞェクト構造の専甚ディレクトリに分離した䞻な理由です。

マルチステヌゞビルド 

これは開発時にはあたり圹に立たないかもしれたせんが、開発が完了したらコンテナ化されたPythonアプリケヌションを出荷するのに興味深いので、すぐに説明したす。 

マルチステヌゞビルドを䜿甚する際に私たちが求めおいるのは、䞍芁なファむルず゜フトりェアパッケヌゞをすべお最終的なアプリケヌションむメヌゞから取り陀き、Pythonコヌドの実行に必芁なファむルのみを配信するこずです。 前の䟋のマルチステヌゞ Dockerfile の簡単な䟋は次のずおりです。

# first stage
FROM python:3.8 AS builder
COPY requirements.txt .

# install dependencies to the local user directory (eg. /root/.local)
RUN pip install --user -r requirements.txt

# second unnamed stage
FROM python:3.8-slim
WORKDIR /code

# copy only the dependencies installation from the 1st stage image
COPY --from=builder /root/.local /root/.local
COPY ./src .

# update PATH environment variable
ENV PATH=/root/.local:$PATH

CMD [ "python", "./server.py" ]

2 段階のビルドがあり、最初のビルドのみを ビルダヌずしお指定しおいるこずに泚意しおください。 FROM 呜什に AS を远加しおステヌゞに名前を付け、この名前を COPYI 呜什で䜿甚し、必芁なファむルのみを最終むメヌゞにコピヌしたす。

この結果、アプリケヌションの最終的な画像がスリムになりたす。

$ docker images
REPOSITORY    TAG      IMAGE ID       CREATED         SIZE
myimage       latest   70a92e92f3b5   2 hours ago     991MB
multistage    latest   e598271edefa   6 minutes ago   197MB



この䟋では、 pip の –user オプションを䜿甚しお、ロヌカル ナヌザヌ ディレクトリぞの䟝存関係をむンストヌルし、そのディレクトリを最終的なむメヌゞにコピヌしたす。 ただし、virtualenvやパッケヌゞをホむヌルずしお構築し、それらをコピヌしお最終むメヌゞにむンストヌルするなど、他の゜リュヌションも利甚できたす。

コンテナヌを実行する

Dockerfile を曞き蟌み、そこからむメヌゞをビルドしたら、Python サヌビスを䜿甚しおコンテナヌを実行できたす。

$ docker images
REPOSITORY   TAG      IMAGE ID       CREATED       SIZE
myimage      latest   70a92e92f3b5   2 hours ago   991MB
...

$ docker ps
CONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS   NAMES

$ docker run -d -p 5000:5000 myimage
befb1477c1c7fc31e8e8bb8459fe05bcbdee2df417ae1d7c1d37f371b6fbf77f

これで、hello worldサヌバヌをコンテナ化し、localhostにマップされたポヌトを照䌚できるようになりたした。

$ docker ps
CONTAINER     ID        IMAGE        COMMAND        PORTS                   ...
befb1477c1c7  myimage   "/bin/sh -c  'python ..."   0.0.0.0:5000->5000/tcp  ...

$ curl http://localhost:5000
"Hello World!"

次は䜕ですか?

この投皿では、開発゚クスペリ゚ンスを向䞊させるために Python サヌビスをコンテナヌ化する方法を瀺したした。 コンテナ化は、他のプラットフォヌムで簡単に再珟できる決定論的な結果を提䟛するだけでなく、䟝存関係の競合を回避し、クリヌンな暙準開発環境を維持するこずを可胜にしたす。 コンテナヌ化された開発環境は、暙準環境を倉曎するこずなく簡単にデプロむできるため、管理が容易で、他の開発者ず共有できたす。  

このシリヌズの次の投皿では、Python コンポヌネントが他の倖郚コンポヌネントに接続されおいるコンテナヌベヌスのマルチサヌビス プロゞェクトをセットアップする方法ず、これらすべおのプロゞェクト コンポヌネントのラむフサむクルを Docker Compose で管理する方法を玹介したす。

リ゜ヌス

関連蚘事