フラスコ、gunicornをパッケージ化する方法
目次
1 )無重力概念
What are distroless containers ?
Think in slim down linux distribution, where are removed all unused things.
Don't have shell or package manager, come without: apt, apk, pip, bash, etc..
When remove all unnecessary things get a bonus of size saving, halving or even more the final size.So is more faster to upload it. From a security concern we obtain a small surface attack area.
コンセプトは明らかです:ちょうどアプリケーションランタイムとアクセシビリティライブラリを追加します.
2 )どうやって動くか
The gcr.io/distroless/python3
is a batteries not included toy.
The classic recipe resolve all in one saucepan:
FROM python:3.6
ADD . /app
WORKDIR /app
RUN pip install flask gunicorn
EXPOSE 8000
CMD ["gunicorn", "-b", "0.0.0.0:8000", "app"]
Now the build process change, is more like a crispy home fries recipe, first boil the potatoes in water, then drain and cool, add spices and finally send to oven.
First we will take a base system python:3.6-slim
and call build-env
, over this it will be added the needed packages. Second move the packages and our application to the distroless image gcr.io/distroless/python3
.
2.2 )パッケージインストール
インストールするパッケージ
#file:requirements.txt
flask==1.1.4
gunicorn>19
イメージを構築するには、次の2つの段階が必要です.# file: flask_gunicorn_distroless.dockerfile
FROM python:3.6-slim AS build-env
WORKDIR /app
# First install packages to avoid re-run pip install
COPY requirements.txt ./
RUN pip install --disable-pip-version-check -r requirements.txt --target /packages
COPY ./versions.py /app
COPY ./app.py /app
COPY ./standalone.py /app
FROM gcr.io/distroless/python3
WORKDIR /app
COPY --from=build-env /app /app
COPY --from=build-env /packages /packages
#instead of COPY --from=build-env /packages /usr/local/lib/python/site-packages
ENV PYTHONPATH=/packages
#instead of ENV PYTHONPATH=/usr/local/lib/python/site-packages
CMD ["standalone.py"]
いくつかのヒント
distexlessでは、nextsコマンドは動作しません.
RUN mkdir /app
RUN cp ./source /app
代わりにWORKDIR /app
mkdirとcdを暗黙的に実行します.COPY ./app /app
ターゲットディレクトリを作成する2.2 )内部は何か
イメージ
gcr.io/distroless/python3
いくつかのプリインストールされたパッケージが付属しているだけで、その中に小さなPythonファイルが書かれています.# file version.py
import sys
import os
import argparse
from pathlib import Path
print("Versions:------------------")
parser = argparse.ArgumentParser()
parser.add_argument('folder', type=str, help='The folder to list.', nargs='?', default=os.getcwd())
#--------------------------
import flask
print("Flask Version:",flask.__version__)
import gunicorn
print("Gunicorn Version:",gunicorn.__version__)
#--------------------------
print("Getcwd: ",os.getcwd())
print("Sys.path count:",str(len(sys.path)))
print('\n'.join(sys.path))
def listFolders(str_path: str):
dir_path=Path(str_path).expanduser()
print(" Packages:------------------")
try:
for path in dir_path.iterdir():
if path.is_dir():
print(path)
except PermissionError:
pass
def main(args):
print('Folder:'+args.folder)
listFolders(args.folder)
if __name__ == "__main__":
main(parser.parse_args())
#listFolders('/packages')
dockfileで最後の行を変更します.CMD ["versions.py", "/usr/lib/python3.9"]
画像を再構築しますdocker build --tag flask_gunicorn_distroless --file flask_gunicorn_distroless.dockerfile .
実行しますdocker run flask_gunicorn_distroless
コンソールでは、パスとあらかじめインストールされたパッケージが表示されます.:docker run flask_gunicorn_distroless
Versions:------------------
Flask Version: 1.1.4
Gunicorn Version: 20.1.0
Getcwd: /app
Sys.path count: 5
/app
/packages
/usr/lib/python39.zip
/usr/lib/python3.9
/usr/lib/python3.9/lib-dynload
Folder:/usr/lib/python3.9
Folders:------------------
/usr/lib/python3.9/multiprocessing
/usr/lib/python3.9/zoneinfo
/usr/lib/python3.9/sqlite3
/usr/lib/python3.9/distutils
/usr/lib/python3.9/venv
/usr/lib/python3.9/http
/usr/lib/python3.9/xml
/usr/lib/python3.9/asyncio
/usr/lib/python3.9/collections
/usr/lib/python3.9/wsgiref
/usr/lib/python3.9/encodings
/usr/lib/python3.9/pydoc_data
/usr/lib/python3.9/ctypes
/usr/lib/python3.9/xmlrpc
/usr/lib/python3.9/test
/usr/lib/python3.9/email
/usr/lib/python3.9/importlib
/usr/lib/python3.9/curses
/usr/lib/python3.9/html
/usr/lib/python3.9/json
/usr/lib/python3.9/logging
/usr/lib/python3.9/dbm
/usr/lib/python3.9/lib-dynload
/usr/lib/python3.9/urllib
/usr/lib/python3.9/concurrent
/usr/lib/python3.9/unittest
/usr/lib/python3.9/__pycache__
2.3 )実行フラスコ
ちょうどSharityチェックのために、我々のフラスコアプリケーションが動くことができるかどうか見てください:
#file:app.py
from time import gmtime, strftime
from flask import Flask
application = Flask(__name__)
@application.route("/")
def index():
return {'status':'Available','time':strftime("%H:%M:%S", gmtime()) }
if __name__ == "__main__":
application.run(host="0.0.0.0", port=5000,debug=True)
dockfileで最後の行を変更します.CMD ["app.py"]
画像を再構築しますdocker build --tag flask_gunicorn_distroless --file flask_gunicorn_distroless.dockerfile .
実行しますdocker run -p 80:5000 flask_gunicorn_distroless
好みのwebブラウザーでアドレーションを開きます.http://localhost
, すべてがOKならば、JSONは示されます.2.4 . gunicornの実行
古典的なレシピでは、gunicornを実行する通常の方法は以下の通りです.
CMD ["gunicorn", "-b", "0.0.0.0:8000", "app"]
でも今はENTRYPOINT
is /usr/bin/python3.9
ですから、Pythonの内部からgunicornを実行する必要があります.Gunicorn Custom Application
アイデアはサブクラス
gunicorn.app.base.BaseApplication
と過負荷load_config
and load
.# file:standalone.py
from app import application
import multiprocessing
import gunicorn.app.base
def number_of_workers():
return (multiprocessing.cpu_count() * 2) + 1
class StandaloneApplication(gunicorn.app.base.BaseApplication):
def __init__(self, app, options=None):
self.options = options or {}
self.application = app
super().__init__()
def load_config(self):
config = {key: value for key, value in self.options.items()
if key in self.cfg.settings and value is not None}
for key, value in config.items():
self.cfg.set(key.lower(), value)
def load(self):
return self.application
if __name__ == '__main__':
options = {
'bind': '%s:%s' % ('0.0.0.0', '8000'),
'workers': number_of_workers(),
}
StandaloneApplication(application, options).run()
dockfileで最後の行を変更します.CMD ["standalone.py"]
画像を再構築しますdocker build --tag flask_gunicorn_distroless --file flask_gunicorn_distroless.dockerfile .
実行しますdocker run -p 80:8000 flask_gunicorn_distroless
好みのwebブラウザーでアドレーションを開きます.http://localhost
, すべてがOKならば、JSONは示されます.脆弱性走査
We will use Clair to perform a static analysis of vulnerabilities.
1) Run the vulnerabilities database:
docker run -d --name db arminc/clair-db:latest
2) Run the service:
docker run -p 6060:6060 --link db:postgres -d --name clair arminc/clair-local-scan
3) Run the client:
Download from: Clair Scanner画像IDを取得します.
docker image ls flask_gunicorn_distroless
REPOSITORY TAG IMAGE ID CREATED SIZE
flask_gunicorn_distroless latest fa824d3ab0a6 3 minutes ago 70.8MB
スキャンバック、私たち自身のIPとイメージIDを通過します../clair-scanner_linux_386 --ip=127.0.0.1 fa824d3ab0a6 | grep Unapproved | wc -l
脆弱性を得ると比較する
python:3.9-slim
画像IDを取得します.docker image ls python:3.9-slim
REPOSITORY TAG IMAGE ID CREATED SIZE
python 3.9-slim 5e714d33137a 5 days ago 122MB
スキャンバック、私たち自身のIPとイメージIDを通過します../clair-scanner_linux_386 --ip=127.0.0.1 5e714d33137a | grep Unapproved | wc -l
脆弱性を得るReference
この問題について(フラスコ、gunicornをパッケージ化する方法), 我々は、より多くの情報をここで見つけました https://dev.to/lionelmarco/how-to-add-flask-gunicorn-packages-to-a-distroless-docker-container-2ml2テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol