Utilizar o docker¶
O que é o Docker? Citando-os
O Docker é um conjunto de produtos de plataforma como serviço que utilizam virtualização ao nível do sistema operativo para fornecer software em pacotes chamados containers.
A forma convencional¶
Quando se faz um deployment, geralmente precisa-se de:
- Decidir quantos ambientes vai se implementar (teste, staging, produção...)
- Preparar os requisitos.
- Preparar possíveis variáveis de ambiente.
- Preparar secrets para serem passados para a aplicação.
- Possivelmente, preparar os acessos à base de dados através dessas mesmas variáveis de ambiente.
- Orquestração.
- ...
E no final, muita esperança de que tudo funcione perfeitamente em cada ambiente, desde que sejam exatamente iguais.
Isto é muito bom mas susceptível a erros humanos.
A abordagem do Docker¶
Ao utilizar o Docker, ainda é necessário pensar na infraestrutura e nos recursos para a aplicação, mas reduz a necessidade de instalar os mesmos binários em cada ambiente, uma vez que eles serão geridos por um container.
Imagine um container como um ficheiro zip. Simplesmente reúne tudo o que é necessário para que o Lilya funcione num único lugar e "zipa" isso, o que neste caso significa "dockerizar" a aplicação. Isto significa que em cada ambiente os binários serão exatamente os mesmos e não dependerão de seres humanos, reduzindo a complexidade.
Exemplo do Lilya e Docker¶
Vamos supor que queremos implantar uma aplicação simples do Lilya utilizando o Docker. Assumindo que os recursos externos já estão a ser tratados e geridos.
Vamos utilizar:
- Configuração do Nginx - Servidor web.
- Supervisor - Gestor de processos.
-
Aplicação Lilya dockerizada. Suposições:
-
Todas as configurações serão colocadas numa pasta chamada
/deployment
. -
A aplicação terá uma estrutura de pastas simples
. ├── app │ ├── __init__.py │ └── main.py ├── Dockerfile ├── deployment/ │ ├── nginx.conf │ └── supervisor.conf └── requirements.txt
-
O ficheiro de requisitos
lilya uvicorn nginx supervisor
Como mencionado nestes documentos, estaremos a utilizar o uvicorn nos exemplos, mas é livre de usar o que quiser
A aplicação¶
Vamos começar com uma aplicação simples, de um único ficheiro, apenas para enviar um hello word.
from __future__ import annotations
from lilya.apps import Lilya
from lilya.routing import Path
def home():
return {"Hello": "World"}
def read_user(user_id: int, q: str | None = None):
return {"item_id": user_id, "q": q}
app = Lilya(
routes=[
Path("/", handler=home),
Path("/users/{user_id}", handler=read_user),
]
)
Nginx¶
O Nginx é um servidor web que também pode ser usado como um reverse proxy, balanceador de carga, proxy de email e cache HTTP.
Encontrará mais detalhes sobre o Nginx na a documentação oficial e como utilizá-lo.
Vamos começar a construir a nossa simples configuração nginx.
events {
worker_connections 1024;
}
http {
server {
listen 80;
client_max_body_size 4G;
server_name example.com;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-CSS-Protection "1; mode=block";
proxy_set_header X-Content-Type-Options "nosniff";
proxy_set_header Cache-Control "public,max-age=120,must-revalidate,s-maxage=120";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self';" always;
add_header X-Frame-Options "SAMEORIGIN";
proxy_redirect off;
proxy_buffering off;
proxy_pass http://uvicorn;
}
location /static {
# path for static files
root /path/to/app/static;
}
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream uvicorn {
server unix:/tmp/uvicorn.sock;
}
}
nginx
com algum nível de segurança para garantir que protegemos a aplicação em todos os níveis.
Supervisor¶
O Supervisor é um gestor de processos simples, mas poderoso, que permite monitorizar e controlar vários processos em sistemas operativos semelhantes ao UNIX.
A documentação deles irá ajudá-lo a entender melhor como utilizá-lo.
Agora é hora de criar uma configuração para o supervisor.
[unix_http_server]
file = /run/supervisor.sock
chown = root:root
chmod = 0700
username = username
password = passwd
[supervisord]
nodaemon = true
nocleanup = true
logfile =/var/log/supervisord.log
loglevel = warn
childlogdir =/var/log
user = root
[supervisord]
nodaemon = true
nocleanup = true
logfile =/var/log/supervisord.log
loglevel = warn
childlogdir =/var/log
user = root
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl = unix:///run/supervisor.sock
username = username
password = passwd
[program:nginx]
command = nginx -g "daemon off;"
autostart = true
autorestart = true
priority = 200
stopwaitsecs = 60
stdout_logfile = /dev/stdout
stdout_logfile_maxbytes = 0
stderr_logfile = /dev/stderr
stderr_logfile_maxbytes = 0
[fcgi-program:uvicorn]
socket = tcp://localhost:8000
command = uvicorn --fd 0 app.main:app
numprocs = 4
priority = 14
startsecs = 10
autostart = true
autorestart = true
process_name = uvicorn-%(process_num)d
stdout_logfile = /dev/stdout
stdout_logfile_maxbytes = 0
redirect_stderr = true
Parece complexo e extenso, mas vamos traduzir o que esta configuração está realmente a fazer.
- Cria as configurações iniciais para o
supervisor
esupervisord
. - Declara as instruções de como iniciar o nginx.
- Declara as instruções de como iniciar o
uvicorn
e a aplicação lilya.
Dockefile¶
O ficheiro Dockerfile é onde se coloca todas as instruções necessárias para iniciar a aplicação assim que for construída, por exemplo, iniciar o supervisor que irá então iniciar todos os processos declarados na sua configuração.
# (1)
FROM python:3.9
# (2)
RUN apt-get update && \
apt-get install -y --no-install-recommends \
libatlas-base-dev gfortran nginx supervisor nginx-extras
# (3)
WORKDIR /src
# (4)
COPY ./requirements.txt /src/requirements.txt
# (5)
RUN pip install --no-cache-dir --upgrade -r /src/requirements.txt
# (6)
COPY ./app /src/app
COPY deployment/nginx.conf /etc/nginx/
COPY deployment/nginx.conf /etc/nginx/sites-enabled/default
COPY deployment/supervisord.conf /etc/
# (7)
CMD ["/usr/bin/supervisord"]
- Comece a partir de uma imagem base oficial do Python.
- Instale os requisitos mínimos para executar o Nginx e o Supervisor.
-
Defina a directoria atual como
/src
.É aqui que irá colocar o
requirements.txt
e a directoriaapp
. -
Copie os requisitos para o seu projecto.
Você deve copiar apenas os requisitos e não o restante do código, e a razão para isso é o cache do Docker. Se o arquivo não mudar com muita frequência, ele será armazenado em cache e na próxima vez que você precisar reconstruir a imagem, ele não repetirá as mesmas etapas o tempo todo.
-
Instale os requisitos.
O
--no-cache-dir
é opcional. Pode simplesmente adicioná-lo para informar o pip para não armazenar em cache os pacotes, localmente.O
--upgrade
é para garantir que o pip atualiza os pacotes instalados para a versão mais recente. -
Copie o
./app
para a directoria/src
.Também copie os ficheiros
nginx.conf
esupervisor.conf
previamente criados para as respectivas pastas do sistema. -
Indique ao
supervisor
para começar a ser executado. O sistema usará o ficheirosupervisor.conf
criado e acionará as instruções declaradas, como iniciar o Nginx e o Uvicorn.
Construir a imagem Docker¶
Com o Dockerfile criado, agora é hora de construir a imagem.
$ docker build -t myapp-image .
Testar a imagem localmente¶
Pode testar a sua imagem localmente antes de implementar e verificar se funciona como desejado.
$ docker run -d --name mycontainer -p 80:80 myapp-image
Verificar¶
Após construir a imagem e iniciar localmente, pode então verificar se ela funciona como desejado.
Examplo:
Importante¶
Foi fornecido um exemplo de como construir alguns ficheiros semelhantes aos necessários para uma determinada implementação.
Deve sempre verificar e alterar qualquer um dos exemplos para se adequar às suas necessidades e garantir que funcione para si