O autor escolheu o Internet Archive para receber uma doação como parte do programa Write for DOnations.
O desenvolvimento de aplicações web pode se tornar complexo e demorado ao criar e manter várias tecnologias diferentes. A consideração de opções de menor peso projetadas para reduzir a complexidade e o tempo de produção da sua aplicação pode resultar em uma solução mais flexível e escalável. Como um micro web framework construído em Python, o Flask fornece uma maneira extensível para os desenvolvedores crescerem suas aplicações através de extensões que podem ser integradas em projetos. Para continuar a escalabilidade da pilha de tecnologia de um desenvolvedor, o MongoDB é um banco de dados NoSQL projetado para escalar e trabalhar com alterações frequentes. Os desenvolvedores podem usar o Docker para simplificar o processo de empacotamento e deploy de suas aplicações.
O Docker Compose simplificou ainda mais o ambiente de desenvolvimento, permitindo definir sua infraestrutura, incluindo serviços de aplicações, volumes de rede e montagens, em um único arquivo. O uso do Docker Compose fornece facilidade de uso ao executar vários comandos docker container run
. Ele permite definir todos os seus serviços em um único arquivo Compose e, com um único comando, você cria e inicia todos os serviços a partir da sua configuração. Isso garante que haja controle de versão em toda a infraestrutura de container. O Docker Compose usa um nome de projeto para isolar ambientes um do outro, permitindo executar vários ambientes em um único host.
Neste tutorial, você criará, empacotará e executará sua aplicação web de lista de tarefas com o Flask, Nginx e MongoDB dentro de containers Docker. Você definirá toda a configuração da pilha em um arquivo docker-compose.yml
, juntamente com os arquivos de configuração para Python, MongoDB e Nginx. O Flask requer um servidor web para atender solicitações HTTP, portanto você também usará o Gunicorn, que é um servidor HTTP Python WSGI, para atender à aplicação. O Nginx atua como um servidor de proxy reverso que encaminha solicitações ao Gunicorn para processamento.
Para seguir este tutorial, você precisará do seguinte:
sudo
configurado seguindo os passos do tutorial Configuração inicial do servidor.A construção de suas aplicações no Docker permite o versionamento da infraestrutura facilmente, dependendo das alterações de configuração feitas no Docker Compose. A infraestrutura pode ser definida em um único arquivo e criada com um único comando. Neste passo, você configurará o arquivo docker-compose.yml
para executar sua aplicação Flask.
O arquivo docker-compose.yml
permite definir sua infraestrutura de aplicações como serviços individuais. Os serviços podem ser conectados entre si e cada um pode ter um volume anexado a ele para armazenamento persistente. Os volumes são armazenados em uma parte do sistema de arquivos do host gerenciado pelo Docker (/var/lib/docker/volumes/
no Linux).
Os volumes são a melhor maneira de persistir os dados no Docker, pois os dados nos volumes podem ser exportados ou compartilhados com outras aplicações. Para obter informações adicionais sobre o compartilhamento de dados no Docker, consulte How To Share Data Between the Docker Container and the Host.
Para começar, crie um diretório para a aplicação no diretório home do seu servidor:
- mkdir flaskapp
Mova para o diretório recém-criado:
- cd flaskapp
Em seguida, crie o arquivo docker-compose.yml
:
- nano docker-compose.yml
O arquivo docker-compose.yml
começa com um número de versão que identifica a versão do arquivo Docker Compose. A versão 3
do arquivo do Docker Compose é direcionada à versão do Docker Engine 1.13.0+
, que é um pré-requisito para esta configuração. Você também adicionará a tag services
que você definirá no próximo passo:
version: '3'
services:
Agora você definirá o flask
como o primeiro serviço no seu arquivo docker-compose.yml
. Adicione o seguinte código para definir o serviço Flask:
. . .
flask:
build:
context: app
dockerfile: Dockerfile
container_name: flask
image: digitalocean.com/flask-python:3.6
restart: unless-stopped
environment:
APP_ENV: "prod"
APP_DEBUG: "False"
APP_PORT: 5000
MONGODB_DATABASE: flaskdb
MONGODB_USERNAME: flaskuser
MONGODB_PASSWORD: sua_senha_do_mongodb
MONGODB_HOSTNAME: mongodb
volumes:
- appdata:/var/www
depends_on:
- mongodb
networks:
- frontend
- backend
A propriedade build
define o contexto do build. Nesse caso, a pasta app
que conterá o Dockerfile
.
Você usa a propriedade container_name
para definir um nome para cada container. A propriedade image
especifica o nome da imagem e como a imagem do Docker será identificada ou tagueada. A propriedade restart
define como o container deve ser reiniciado — no seu caso é unless-stopped
. Isso significa que seus containers serão parados apenas quando o Docker Engine for parado/reiniciado ou quando você os interromper explicitamente. O benefício de usar a propriedade unless-stopped
é que os containers serão iniciados automaticamente assim que o Docker Engine for reiniciado ou ocorrer algum erro.
A propriedade environment
contém as variáveis de ambiente que são passadas para o container. Você precisa fornecer uma senha segura para a variável de ambiente MONGODB_PASSWORD
. A propriedade volumes
define os volumes que o serviço está usando. No seu caso, o volume appdata
é montado dentro do container no diretório /var/www
. A propriedade depends_on
define um serviço do qual o Flask depende para funcionar corretamente. Nesse caso, o serviço flask
dependerá do mongodb
, pois o serviço mongodb
atua como o banco de dados da sua aplicação. O depends_on
garante que o serviço flask
seja executado apenas se o serviço mongodb
estiver sendo executado.
A propriedade networks
especifica frontend
e backend
como as redes às quais o serviço flask
terá acesso.
Com o serviço flask
definido, você está pronto para adicionar a configuração do MongoDB ao arquivo. Neste exemplo, você usará a imagem mongo
oficial na versão 4.0.8
. Adicione o seguinte código ao seu arquivo docker-compose.yml
seguindo o flask service
:
. . .
mongodb:
image: mongo:4.0.8
container_name: mongodb
restart: unless-stopped
command: mongod --auth
environment:
MONGO_INITDB_ROOT_USERNAME: mongodbuser
MONGO_INITDB_ROOT_PASSWORD: senha_do_root_do_mongodb
MONGO_INITDB_DATABASE: flaskdb
MONGODB_DATA_DIR: /data/db
MONDODB_LOG_DIR: /dev/null
volumes:
- mongodbdata:/data/db
networks:
- backend
O container_name
para este serviço é mongodb
com uma política de reinicialização unless-stopped
. Você usa a propriedade command
para definir o comando que será executado quando o container for iniciado. O comando mongod --auth
desativará o logon no shell do MongoDB sem credenciais, o que protegerá o MongoDB exigindo autenticação.
As variáveis de ambiente MONGO_INITDB_ROOT_USERNAME
e MONGO_INITDB_ROOT_PASSWORD
criam um usuário root com as credenciais fornecidas, portanto, substitua o espaço reservado por uma senha forte.
O MongoDB armazena seus dados em /data/db
por padrão, portanto os dados na pasta /data/db
serão gravados no volume nomeado mongodbdata
para persistência. Como resultado, você não perderá seus bancos de dados em caso de reinicialização. O serviço mongoDB
não expõe nenhuma porta, portanto, o serviço estará acessível apenas através da rede backend
.
Em seguida, você definirá o servidor web para sua aplicação. Adicione o seguinte código ao seu arquivo docker-compose.yml
para configurar o Nginx:
. . .
webserver:
build:
context: nginx
dockerfile: Dockerfile
image: digitalocean.com/webserver:latest
container_name: webserver
restart: unless-stopped
environment:
APP_ENV: "prod"
APP_NAME: "webserver"
APP_DEBUG: "false"
SERVICE_NAME: "webserver"
ports:
- "80:80"
- "443:443"
volumes:
- nginxdata:/var/log/nginx
depends_on:
- flask
networks:
- frontend
Aqui você definiu o context
do build
, que é a pasta nginx
que contém o Dockerfile
. Com a propriedade image
, você especifica a imagem usada para taguear e executar o container. A propriedade ports
configurará o serviço Nginx para ser acessível publicamente através das portas :80
e :443
e volumes
montam o volume nginxdata
dentro do container no diretório /var/log/nginx
.
Você definiu o serviço do qual o serviço do servidor web depends_on
como flask
. Finalmente, a propriedade networks
define que o serviço do servidor web terá acesso à rede frontend
.
Em seguida, você criará bridge networks para permitir que os containers se comuniquem. Acrescente as seguintes linhas ao final do seu arquivo:
. . .
networks:
frontend:
driver: bridge
backend:
driver: bridge
Você definiu duas redes — frontend
e backend
— para os serviços aos quais se conectar. Os serviços de front-end, como o Nginx, se conectam à rede front-end
, pois precisa ser acessível ao público. Serviços de back-end, como o MongoDB, serão conectados à rede back-end
para impedir o acesso não autorizado ao serviço.
Em seguida, você usará os volumes para persistir os arquivos de banco de dados, aplicação e configuração. Como sua aplicação usará os bancos de dados e arquivos, é imperativo persistir as alterações feitas neles. Os volumes são gerenciados pelo Docker e armazenados no sistema de arquivos. Adicione este código ao arquivo docker-compose.yml
para configurar os volumes:
. . .
volumes:
mongodbdata:
driver: local
appdata:
driver: local
nginxdata:
driver: local
A seção volumes
declara os volumes que a aplicação usará para persistir os dados. Aqui você definiu os volumes mongodbdata
, appdata
e nginxdata
para persistir seus bancos de dados MongoDB, dados da aplicação Flask e logs do servidor web Nginx, respectivamente. Todos esses volumes usam um driver local
para armazenar os dados localmente. Os volumes são usados para persistir esses dados, para que dados como os bancos de dados MongoDB e os logs do servidor web Nginx possam ser perdidos depois que você reiniciar os containers.
Seu arquivo docker-compose.yml
completo terá a seguinte aparência:
version: '3'
services:
flask:
build:
context: app
dockerfile: Dockerfile
container_name: flask
image: digitalocean.com/flask-python:3.6
restart: unless-stopped
environment:
APP_ENV: "prod"
APP_DEBUG: "False"
APP_PORT: 5000
MONGODB_DATABASE: flaskdb
MONGODB_USERNAME: flaskuser
MONGODB_PASSWORD: sua_senha_do_mongodb
MONGODB_HOSTNAME: mongodb
volumes:
- appdata:/var/www
depends_on:
- mongodb
networks:
- frontend
- backend
mongodb:
image: mongo:4.0.8
container_name: mongodb
restart: unless-stopped
command: mongod --auth
environment:
MONGO_INITDB_ROOT_USERNAME: mongodbuser
MONGO_INITDB_ROOT_PASSWORD: senha_do_root_do_mongodb
MONGO_INITDB_DATABASE: flaskdb
MONGODB_DATA_DIR: /data/db
MONDODB_LOG_DIR: /dev/null
volumes:
- mongodbdata:/data/db
networks:
- backend
webserver:
build:
context: nginx
dockerfile: Dockerfile
image: digitalocean.com/webserver:latest
container_name: webserver
restart: unless-stopped
environment:
APP_ENV: "prod"
APP_NAME: "webserver"
APP_DEBUG: "true"
SERVICE_NAME: "webserver"
ports:
- "80:80"
- "443:443"
volumes:
- nginxdata:/var/log/nginx
depends_on:
- flask
networks:
- frontend
networks:
frontend:
driver: bridge
backend:
driver: bridge
volumes:
mongodbdata:
driver: local
appdata:
driver: local
nginxdata:
driver: local
Salve o arquivo e saia do editor após verificar sua configuração.
Você definiu a configuração do Docker para toda a pilha de aplicações no arquivo docker-compose.yml
. Agora você passará a escrever os Dockerfiles para o Flask e o servidor web.
Com o Docker, você pode criar containers para executar suas aplicações a partir de um arquivo chamado Dockerfile. O Dockerfile é uma ferramenta que permite criar imagens personalizadas que você pode usar para instalar o software exigido pela sua aplicação e configurar seus containers com base em seus requisitos. Você pode enviar as imagens personalizadas que você criou para o Docker Hub ou qualquer registro privado.
Neste passo, você escreverá os Dockerfiles para os serviços do Flask e do servidor web. Para começar, crie o diretório app
para o sua alicação Flask:
- mkdir app
Em seguida, crie o Dockerfile
para a sua aplicação Flask no diretório app
:
- nano app/Dockerfile
Adicione o seguinte código ao arquivo para personalizar seu container do Flask:
FROM python:3.6.8-alpine3.9
LABEL MAINTAINER="Nome Sobrenome <example@domain.com>"
ENV GROUP_ID=1000 \
USER_ID=1000
WORKDIR /var/www/
Neste Dockerfile
, você está criando uma imagem com base na imagem 3.6.8-alpine3.9
, que é baseada no Alpine 3.9 com Python 3.6.8 pré-instalado.
A diretiva ENV
é usada para definir as variáveis de ambiente para o nosso grupo e ID do usuário. A Linux Standard Base (LSB) especifica que UIDs e GIDs 0-99 são alocados estaticamente pelo sistema. UIDs 100-999 devem ser alocados dinamicamente para usuários e grupos do sistema. UIDs 1000-59999 devem ser alocados dinamicamente para contas de usuário. Tendo isso em mente, você pode atribuir com segurança um UID e GID de 1000
, além disso, você pode alterar o UID/GID atualizando o GROUP_ID
e o USER_ID
para atender aos seus requisitos.
A diretiva WORKDIR
define o diretório de trabalho do container. Certifique-se de substituir o campo LABEL MAINTAINER
pelo seu nome e endereço de e-mail.
Adicione o seguinte bloco de código para copiar o aplicativo Flask no container e instalar as dependências necessárias:
. . .
ADD ./requirements.txt /var/www/requirements.txt
RUN pip install -r requirements.txt
ADD . /var/www/
RUN pip install gunicorn
O código a seguir usará a diretiva ADD
para copiar arquivos do diretório local app
para o diretório /var/www
no container. Em seguida, o Dockerfile usará a diretiva RUN
para instalar o Gunicorn e os pacotes especificados no arquivo requirements.txt
, que você criará posteriormente no tutorial.
O seguinte bloco de código adiciona um novo usuário e grupo e inicializa a aplicação:
. . .
RUN addgroup -g $GROUP_ID www
RUN adduser -D -u $USER_ID -G www www -s /bin/sh
USER www
EXPOSE 5000
CMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"]
Por padrão, os containers Docker são executados como o usuário root. O usuário root tem acesso a tudo no sistema, portanto, as implicações de uma violação de segurança podem ser desastrosas. Para mitigar esse risco de segurança, isso criará um novo usuário e grupo que terá acesso apenas ao diretório /var/www
.
Este código usará primeiro o comando addgroup
para criar um novo grupo chamado www
. A flag -g
definirá o ID do grupo para a variável ENV GROUP_ID=1000
, definida anteriormente no Dockerfile
.
A linha adduser -D -u $USER_ID -G www www -s /bin/sh
cria um usuário www
com um ID de usuário 1000
, conforme definido pela variável ENV
. A flag -s
cria o diretório home do usuário se ele não existir e define o shell de login padrão como /bin/sh
. A flag -G
é usada para definir o grupo de login inicial do usuário como www
, que foi criado pelo comando anterior.
O comando USER
define que os programas executados no container usarão o usuário www
. O Gunicorn escutará na porta :5000
, então você abrirá esta porta com o comando EXPOSE
.
Finalmente a linha CMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"]
executa o comando para iniciar o servidor Gunicorn com quatro workers escutando na porta 5000
. O número geralmente deve estar entre 2 a 4 workers por núcleo no servidor. A documentação do Gunicorn recomenda (2 x $num_cores) + 1
como o número de workers para começar.
O seu Dockerfile
completo terá a seguinte aparência:
FROM python:3.6.8-alpine3.9
LABEL MAINTAINER="Nome Sobrenome <example@domain.com>"
ENV GROUP_ID=1000 \
USER_ID=1000
WORKDIR /var/www/
ADD . /var/www/
RUN pip install -r requirements.txt
RUN pip install gunicorn
RUN addgroup -g $GROUP_ID www
RUN adduser -D -u $USER_ID -G www www -s /bin/sh
USER www
EXPOSE 5000
CMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"]
Salve o arquivo e saia do editor de texto.
Em seguida, crie um novo diretório para manter sua configuração do Nginx:
- mkdir nginx
Depois, crie o Dockerfile
para o seu servidor web Nginx no diretório nginx
:
- nano nginx/Dockerfile
Adicione o seguinte código ao arquivo para criar o Dockerfile que criará a imagem para seu container Nginx:
FROM digitalocean.com/alpine:latest
LABEL MAINTAINER="Nome Sobrenome <example@domain.com>"
RUN apk --update add nginx && \
ln -sf /dev/stdout /var/log/nginx/access.log && \
ln -sf /dev/stderr /var/log/nginx/error.log && \
mkdir /etc/nginx/sites-enabled/ && \
mkdir -p /run/nginx && \
rm -rf /etc/nginx/conf.d/default.conf && \
rm -rf /var/cache/apk/*
COPY conf.d/app.conf /etc/nginx/conf.d/app.conf
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
Este Dockerfile
de Nginx usa uma imagem base alpine
, que é uma pequena distribuição Linux com uma superfície de ataque mínima criada para segurança.
Na diretiva RUN
, você está instalando o nginx
e criando links simbólicos para publicar os logs de erro e de acesso na saída padrão de erro (/dev/stderr
) e na saída padrão (/dev/stdout
). A publicação de erros na saída padrão de erro e na saída padrão é uma prática recomendada, pois os containers são efêmeros. Dessa forma, os logs são enviados para os logs do docker e, a partir daí, você pode encaminhar seus logs para um serviço de log como a pilha Elastic para persistência. Depois disso feito, são executados comandos para remover o default.conf
e /var/cache/apk/*
para reduzir o tamanho da imagem resultante. A execução de todos esses comandos em um único RUN
diminui o número de camadas na imagem, o que também reduz o tamanho da imagem resultante.
A diretiva COPY
copia a configuração app.conf
do servidor web dentro do container. A diretiva EXPOSE
garante que os containers escutem nas portas :80
e :443
, pois sua aplicação será executada na porta :80
com a :443
como porta segura.
Finalmente, a diretiva CMD
define o comando para iniciar o servidor Nginx.
Salve o arquivo e saia do editor de texto.
Agora que o Dockerfile
está pronto, você está pronto para configurar o proxy reverso do Nginx para rotear o tráfego para a aplicação Flask.
Neste passo, você configurará o Nginx como um proxy reverso para encaminhar solicitações ao Gunicorn na porta :5000
. Um servidor de proxy reverso é usado para direcionar solicitações de clientes ao servidor de back-end apropriado. Ele fornece uma camada adicional de abstração e controle para garantir o fluxo suave do tráfego de rede entre clientes e servidores.
Comece criando o diretório nginx/conf.d
:
- mkdir nginx/conf.d
Para configurar o Nginx, você precisa criar um arquivo app.conf
com a seguinte configuração na pasta nginx/conf.d/
. O arquivo app.conf
contém a configuração que o proxy reverso precisa para encaminhar as solicitações ao Gunicorn.
- nano nginx/conf.d/app.conf
Coloque o seguinte conteúdo no arquivo app.conf
:
upstream app_server {
server flask:5000;
}
server {
listen 80;
server_name _;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
client_max_body_size 64M;
location / {
try_files $uri @proxy_to_app;
}
location @proxy_to_app {
gzip_static on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_buffering off;
proxy_redirect off;
proxy_pass http://app_server;
}
}
Isso primeiro definirá o servidor upstream, que normalmente é usado para especificar um servidor web ou servidor de aplicação para roteamento ou balanceamento de carga.
Seu servidor upstream, app_server
, define o endereço do servidor com a diretiva server
, que é identificada pelo nome do container flask:5000
.
A configuração do servidor web Nginx é definida no bloco server
. A diretiva listen
define o número da porta na qual o servidor escutará as solicitações recebidas. As diretivas error_log
e access_log
definem os arquivos para gravar logs. A diretiva proxy_pass
é usada para configurar o servidor upstream para encaminhar os pedidos para http://app_server
.
Salve e feche o arquivo.
Com o servidor web Nginx configurado, é possível criar a API da lista de tarefas do Flask.
Agora que você construiu seu ambiente, você está pronto para criar sua aplicação. Neste passo, você escreverá uma aplicação de API da lista de tarefas que salvará e exibirá as notas de tarefas enviadas de uma solicitação POST.
Comece criando o arquivo requirements.txt
no diretório app
:
- nano app/requirements.txt
Este arquivo é usado para instalar as dependências para sua aplicação. A implementação deste tutorial usará o Flask
, Flask-PyMongo
, e requests
. Adicione o seguinte ao arquivo requirements.txt
:
Flask==1.0.2
Flask-PyMongo==2.2.0
requests==2.20.1
Salve o arquivo e saia do editor após inserir os requisitos.
Em seguida, crie o arquivo app.py
para conter o código da aplicação Flask no diretório app
:
- nano app/app.py
No seu novo arquivo app.py
, digite este código para importar as dependências:
import os
from flask import Flask, request, jsonify
from flask_pymongo import PyMongo
O pacote os
é usado para importar as variáveis de ambiente. Da biblioteca flask
, você importou os objetos Flask
, request
e jsonify
para instanciar a aplicação, tratar solicitações e enviar respostas JSON, respectivamente. Do flask_pymongo
, você importou o objeto PyMongo
para interagir com o MongoDB.
Em seguida, adicione o código necessário para conectar-se ao MongoDB:
. . .
application = Flask(__name__)
application.config["MONGO_URI"] = 'mongodb://' + os.environ['MONGODB_USERNAME'] + ':' + os.environ['MONGODB_PASSWORD'] + '@' + os.environ['MONGODB_HOSTNAME'] + ':27017/' + os.environ['MONGODB_DATABASE']
mongo = PyMongo(application)
db = mongo.db
O Flask (__ name __)
carrega o objeto da aplicação na variável application
. A seguir, o código cria a string de conexão ao MongoDB a partir das variáveis de ambiente usando o os.environ
. Passar o objeto application
para o método PyMongo()
fornecerá a você o objeto mongo
, que por sua vez fornece o objeto db
em mongo.db
.
Agora você adicionará o código para criar uma mensagem de index:
. . .
@application.route('/')
def index():
return jsonify(
status=True,
message='Welcome to the Dockerized Flask MongoDB app!'
)
O @application.route('/')
define a rota GET /
da sua API. Aqui sua função index()
retorna uma string JSON usando o método jsonify
.
Em seguida, adicione a rota /todo
para listar todas as tarefas a fazer:
. . .
@application.route('/todo')
def todo():
_todos = db.todo.find()
item = {}
data = []
for todo in _todos:
item = {
'id': str(todo['_id']),
'todo': todo['todo']
}
data.append(item)
return jsonify(
status=True,
data=data
)
O @application.route('/todo')
define a rota GET /todo
da sua API, que retorna as tarefas no banco de dados. O método db.todo.find()
retorna todas as tarefas a fazer no banco de dados. A seguir, você itera sobre _todos
para construir um item
que inclui apenas o id
e todo
dos objetos anexando-os a uma matriz data
e finalmente os retorna como JSON.
Em seguida, adicione o código para criar a tarefa:
. . .
@application.route('/todo', methods=['POST'])
def createTodo():
data = request.get_json(force=True)
item = {
'todo': data['todo']
}
db.todo.insert_one(item)
return jsonify(
status=True,
message='To-do saved successfully!'
), 201
O @application.route('/todo')
define a rota POST /todo
da sua API, que cria uma nota de tarefa no banco de dados. O request.get_json(force=True)
obtém o JSON que você publica na rota e o item
é usado para construir o JSON que será salvo na tarefa. O db.todo.insert_one(item)
é usado para inserir um item no banco de dados. Depois que a tarefa é salva no banco de dados, você retorna uma resposta JSON com um código de status 201 CREATED
.
Agora você adiciona o código para executar a aplicação:
. . .
if __name__ == "__main__":
ENVIRONMENT_DEBUG = os.environ.get("APP_DEBUG", True)
ENVIRONMENT_PORT = os.environ.get("APP_PORT", 5000)
application.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG)
A condição __name__ == "__main__"
é usada para verificar se a variável global, __name__
, no módulo é o entry point para o seu programa, é "__main__"
e, em seguida, executar a aplicação. Se __name__
for igual a "__main__"
, então o código dentro do bloco if
executará a aplicação usando este comando application.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG)
.
A seguir, obtemos os valores para ENVIRONMENT_DEBUG
e ENVIRONMENT_PORT
das variáveis de ambiente usando os.environ.get()
, usando a chave como primeiro parâmetro e o valor padrão como o segundo parâmetro. O application.run()
define os valores host
, port
e debug
para a aplicação.
O arquivo app.py
completo ficará assim:
import os
from flask import Flask, request, jsonify
from flask_pymongo import PyMongo
application = Flask(__name__)
application.config["MONGO_URI"] = 'mongodb://' + os.environ['MONGODB_USERNAME'] + ':' + os.environ['MONGODB_PASSWORD'] + '@' + os.environ['MONGODB_HOSTNAME'] + ':27017/' + os.environ['MONGODB_DATABASE']
mongo = PyMongo(application)
db = mongo.db
@application.route('/')
def index():
return jsonify(
status=True,
message='Welcome to the Dockerized Flask MongoDB app!'
)
@application.route('/todo')
def todo():
_todos = db.todo.find()
item = {}
data = []
for todo in _todos:
item = {
'id': str(todo['_id']),
'todo': todo['todo']
}
data.append(item)
return jsonify(
status=True,
data=data
)
@application.route('/todo', methods=['POST'])
def createTodo():
data = request.get_json(force=True)
item = {
'todo': data['todo']
}
db.todo.insert_one(item)
return jsonify(
status=True,
message='To-do saved successfully!'
), 201
if __name__ == "__main__":
ENVIRONMENT_DEBUG = os.environ.get("APP_DEBUG", True)
ENVIRONMENT_PORT = os.environ.get("APP_PORT", 5000)
application.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG)
Salve o arquivo e saia do editor.
Em seguida, crie o arquivo wsgi.py
no diretório app
.
- nano app/wsgi.py
O arquivo wsgi.py
cria um objeto de aplicação (ou que pode ser chamado) para que o servidor possa usá-lo. Cada vez que uma solicitação é recebida, o servidor usa esse objeto de aplicação para executar os manipuladores de solicitação da aplicação ao fazer o parse da URL.
Coloque o seguinte conteúdo no arquivo wsgi.py
, salve o arquivo e saia do editor de texto:
from app import application
if __name__ == "__main__":
application.run()
Este arquivo wsgi.py
importa o objeto da aplicação do arquivo app.py
e cria um objeto de aplicação para o servidor Gunicorn.
A aplicação de lista de tarefas já está em vigor, então você está pronto para começar a executar a aplicação em containers.
Agora que você definiu todos os serviços no seu arquivo docker-compose.yml
e suas configurações, você pode iniciar os containers.
Como os serviços são definidos em um único arquivo, você precisa emitir um único comando para iniciar os containers, criar os volumes e configurar as redes. Este comando também cria a imagem para a sua aplicação Flask e o servidor web Nginx. Execute o seguinte comando para criar os containers:
- docker-compose up -d
Ao executar o comando pela primeira vez, ele fará o download de todas as imagens necessárias do Docker, o que pode levar algum tempo. Depois que as imagens são baixadas e armazenadas na sua máquina local, o docker-compose
criará seus containers. A flag -d
roda o processo como um daemon, o que permite que ele seja executado como um processo em segundo plano.
Use o comando a seguir para listar os containers em execução quando o processo de compilação estiver concluído:
- docker ps
Você verá uma saída semelhante à seguinte:
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f20e9a7fd2b9 digitalocean.com/webserver:latest "nginx -g 'daemon of…" 2 weeks ago Up 2 weeks 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp webserver
3d53ea054517 digitalocean.com/flask-python:3.6 "gunicorn -w 4 --bin…" 2 weeks ago Up 2 weeks 5000/tcp flask
96f5a91fc0db mongo:4.0.8 "docker-entrypoint.s…" 2 weeks ago Up 2 weeks 27017/tcp mongodb
O CONTAINER ID
é um identificador exclusivo usado para acessar containers. IMAGE
define o nome da imagem para o container especificado. O campo NAMES
é o nome do serviço sob o qual os containers são criados. Semelhante ao CONTAINER ID
, eles podem ser usados para acessar containers. Finalmente, STATUS
fornece informações sobre o estado do container, seja ele executando, reiniciando ou parado.
Você usou o comando docker-compose
para criar seus containers a partir de seus arquivos de configuração. No próximo passo, você criará um usuário MongoDB para sua aplicação.
Por padrão, o MongoDB permite que os usuários efetuem login sem credenciais e concede privilégios ilimitados. Neste passo, você protegerá seu banco de dados MongoDB criando um usuário dedicado para acessá-lo.
Para fazer isso, você precisará do nome de usuário e senha raiz definidos nas variáveis de ambiente do arquivo docker-compose.yml
, que são MONGO_INITDB_ROOT_USERNAME
e MONGO_INITDB_ROOT_PASSWORD
para o serviço mongodb
. Em geral, é melhor evitar o uso da conta administrativa root ao interagir com o banco de dados. Em vez disso, você criará um usuário de banco de dados dedicado para sua aplicação Flask, bem como um novo banco de dados que a aplicação Flask terá permissão para acessar.
Para criar um novo usuário, primeiro inicie um shell interativo no container mongodb
:
- docker exec -it mongodb bash
Você usa o comando docker exec
para executar um comando dentro de um container em execução junto com a flag -it
para executar um shell interativo dentro do container.
Uma vez dentro do container, efetue login na conta administrativa root do MongoDB:
- mongo -u mongodbuser -p
Você será solicitado pela senha digitada como o valor da variável MONGO_INITDB_ROOT_PASSWORD
no arquivo docker-compose.yml
. A senha pode ser alterada definindo um novo valor para MONGO_INITDB_ROOT_PASSWORD
no serviço mongodb
, nesse caso você terá que executar novamente o comando docker-compose up -d
Execute o comando show dbs;
para listar todos os bancos de dados:
- show dbs;
Você verá a seguinte saída:
Outputadmin 0.000GB
config 0.000GB
local 0.000GB
5 rows in set (0.00 sec)
O banco de dados admin
é um banco de dados especial que concede permissões administrativas aos usuários. Se um usuário tiver acesso de leitura ao banco de dados admin
, ele terá permissões de leitura e gravação em todos os outros bancos de dados. Como a saída lista o banco de dados admin
, o usuário tem acesso a esse banco de dados e, portanto, pode ler e gravar em todos os outros bancos de dados.
Salvando a primeira nota de tarefa a executar automaticamente criará o banco de dados MongoDB. O MongoDB permite que você alterne para um banco de dados que não existe usando o comando use database
. Ele cria um banco de dados quando um documento é salvo em uma coleção. Portanto, o banco de dados não é criado aqui; isso acontecerá quando você salvar sua primeira nota de tarefa no banco de dados a partir da API. Execute o comando use
para alternar para o banco de dados flaskdb
:
- use flaskdb
Em seguida, crie um novo usuário com permissão para acessar este banco de dados:
- db.createUser({user: 'flaskuser', pwd: 'your password', roles: [{role: 'readWrite', db: 'flaskdb'}]})
- exit
Este comando cria um usuário chamado flaskuser com acesso readWrite
ao banco de dados flaskdb
. Certifique-se de usar uma senha segura no campo pwd
. O user
e a pwd
aqui são os valores que você definiu no arquivo docker-compose.yml
na seção de variáveis de ambiente para o serviço flask
.
Efetue login no banco de dados autenticado com o seguinte comando:
- mongo -u flaskuser -p your password --authenticationDatabase flaskdb
Agora que você adicionou o usuário, efetue logout do banco de dados.
- exit
E, finalmente, saia do container:
- exit
Agora você configurou um banco de dados dedicado e uma conta de usuário para sua aplicação Flask. Os componentes do banco de dados estão prontos, portanto, agora você pode executar a aplicação de lista de tarefas do Flask.
Agora que seus serviços estão configurados e em execução, você pode testar sua aplicação navegando até http://ip_do_seu_servidor
em um navegador. Além disso, você pode executar curl
para ver a resposta JSON do Flask:
- curl -i http://ip_do_seu_servidor
Você receberá a seguinte resposta:
Output{"message":"Welcome to the Dockerized Flask MongoDB app!","status":true}
A configuração para a aplicação Flask é passada para a aplicação a partir do arquivo docker-compose.yml
. A configuração referente à conexão com o banco de dados é definida usando as variáveis MONGODB_*
definidas na seção environment
do serviço flask
.
Para testar tudo, crie uma nota de tarefa usando a API do Flask. Você pode fazer isso com uma solicitação POST
no curl para a rota /todo
:
- curl -i -H "Content-Type: application/json" -X POST -d '{"todo": "Dockerize Flask application with MongoDB backend"}' http://ip_do_seu_servidor/todo
Essa solicitação resulta em uma resposta com um código de status 201 CREATED
quando o item de tarefa é salvo no MongoDB:
Output{"message":"To-do saved successfully!","status":true}
Você pode listar todas as notas de tarefas do MongoDB com uma solicitação GET para a rota /todo
:
- curl -i http://ip_do_seu_servidor/todo
Output{"data":[{"id":"5c9fa25591cb7b000a180b60","todo":"Dockerize Flask application with MongoDB backend"}],"status":true}
Com isso, você Dockerizou uma API do Flask executando um back-end do MongoDB com o Nginx como um proxy reverso deployado em seus servidores. Para um ambiente de produção, você pode usar o sudo systemctl enable docker
para garantir que o serviço Docker seja iniciado automaticamente em tempo de execução ou runtime.
Neste tutorial, você fez o deloy de uma aplicação Flask com Docker, MongoDB, Nginx e Gunicorn. Agora você tem uma aplicação de API stateless moderna e funcional que pode ser escalada. Embora você possa alcançar esse resultado usando um comando como docker container run
, o docker-compose.yml
simplifica seu trabalho, pois essa pilha pode ser colocada no controle de versão e atualizada conforme necessário.
A partir daqui, você também pode dar uma olhada em nossos tutoriais de Framework do Python.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!