class ImageV1: # docker build -> image:v1
...
container1_v1 = ImageV1() # docker run image:v1
container2_v1 = ImageV1() # docker run image:v1
...
# some time later
class ImageV2: # docker build -> image:v2
...
container1_v2 = ImageV2() # docker run image:v2
container2_v2 = ImageV2() # docker run image:v2
...
python3 -c 'print("Hello, world!")'
FROM ubuntu:16.04
RUN apt update -y && \
apt install -y python3
CMD ["python3", "-c", "print('Hello, world!')"]
ubuntu
apt
cmd
2fe221ce
4c583ab5
5ecf05ce
FROM ubuntu:16.04
RUN apt update -y && \
apt install -y python3
CMD ["python3", \
"-m", \
"http.server"]
ubuntu
apt
cmd
2fe221ce
4c583ab5
5ecf05ce
ubuntu
apt
cmd
90d13b6a
FROM ubuntu:16.04
RUN apt update -y && \
apt install -y python3
CMD ["python3", \
"-c", \
"print('Hello')"]
FROM ubuntu:16.04
RUN apt update -y && \
apt install -y python3
COPY run.py /opt/run.py
CMD ["python3", "/opt/run.py"]
HOST
CONTAINER
some_directory
dockerfile
run.py
...
opt
run.py
/
...
...
...
FROM ubuntu:16.04
RUN apt update -y && \
apt install -y python3
COPY run.py /opt/run.py
CMD ["python3", "/opt/run.py"]
AAA
BBB
ubuntu
apt
copy
cmd
CCC
DDD
FROM ubuntu:16.04
RUN apt update -y && \
apt install -y python3
COPY run.py /tmp/run.py
CMD ["python3", "/tmp/run.py"]
AAA
BBB
ubuntu
apt
copy
cmd
CCC
DDD
ubuntu
apt
copy
cmd
EEE
FFF
from flask import Flask
app = Flask('example-server')
@app.route('/')
def home():
return 'ok'
apt install ...
pip install ...
source code
?
?
?
apt install ...
pip install ...
source code
FROM alpine
RUN apk update && \
apk add --update python3-dev py3-pip && \
pip3 install --upgrade pip
COPY requirements.txt /tmp/requirements.txt
RUN pip3 install -r /tmp/requirements.txt
WORKDIR /server
COPY ./server /server
CMD ["gunicorn", "app:app", "-b", "0.0.0.0:8000"]
HOST
CONTAINER
project
dockerfile
app.py
...
server
/
requirements.txt
server
__init__.py
app.py
__init__.py
tmp
requirements.txt
...
(1) git push
(2) git pull
CI
(3) docker push <image>:<tag>
(4) deploy
<image>:<tag>
(5) docker pull <image>:<tag>
TeamCity Server
build agent
build agent
build agent
build agent
<...>
Шаги сборки:
RUN apk install ...
COPY req.txt /r.txt
RUN pip install ...
COPY ./code /code
awesome-service:2019-09-25-b7ed60f
FROM alpine
RUN apk install ...
COPY req.txt /r.txt
RUN pip install ...
COPY ./code /code
awesome-service:2019-09-25-b7ed60f
FROM alpine
FROM awesome-base:v1
awesome-base:v1
FROM alpine
RUN apk update && \
apk add --update python3-dev py3-pip && \
pip3 install --upgrade pip
COPY poetry.lock /tmp/poetry.lock
COPY pyproject.toml /tmp/pyproject.toml
RUN pip install poetry && poetry install
ARG BASE_TAG
FROM awesome-base:${BASE_TAG}
WORKDIR /server
COPY ./server /server
CMD ["gunicorn", "app:app", "-b", "0.0.0.0:8000"]
awesome-base.dockerfile
awesome.dockerfile
certifi==2018.4.16
chardet==3.0.4
Django==1.9
idna==2.6
requests==2.18.4
urllib3==1.22
certifi
chardet
Django
idna
requests
urllib3
md5sum requirements.txt
...
[tool.poetry.dependencies]
python = "^3.7"
fastapi = "^0.38.1"
uvicorn = "^0.9.0"
python-multipart = "^0.0.5"
[tool.poetry.dev-dependencies]
pytest = "^5.1"
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
grep 'content-hash' poetry.lock
pyproject.toml
poetry.lock
...
content-hash = "de0b7dd2286198c11fce59..."
...
RUN apk install ...
COPY req.txt /r.txt
RUN pip install ...
FROM alpine
awesome-base:<poetry-content-hash>
Вычисляем тэг базового образа:
grep 'content-hash' poetry.lock
Пулим базовый образ:
docker pull awesome-base:de0b7dd2
Получилось?
Собираем и пушим базовый образ:
docker build ...
docker push awesome-base:de0b7dd2
Собираем основной образ
awesome:<date>-<commit-hash>
на основе базового
POETRY_HASH=$(grep 'content-hash' poetry.lock)
DOCKERFILE_HASH=$(md5sum awesome-base.dockerfile)
TAG="$POETRY_HASH-$DOCKERFILE_HASH"
FROM alpine
RUN apk update && \
apk add --update python3-dev py3-pip && \
pip3 install --upgrade pip
WORKDIR /awesome
COPY poetry.lock poetry.lock
COPY pyproject.toml pyproject.toml
RUN pip install poetry && poetry install
awesome-base.dockerfile