O Que São Contêineres na Computação em Nuvem?
Desde que o Docker entrou em cena em 2013, os contêineres explodiram em popularidade. Muitas empresas já integraram contêineres em seus fluxos de trabalho porque isso permite que elas implantem, distribuam, gerenciem e dimensionem facilmente seus softwares.
Neste artigo, explicaremos o que são contêineres na computação em nuvem. Falaremos sobre os benefícios de usar contêineres, seus casos de uso, vamos compará-los com máquinas virtuais e analisar Docker e Kubernetes. Por fim, ensinaremos como codificar, dockerizar e implantar um aplicativo da web para Contêineres Back4app – de graça!
Contents
- 1 Definição de contêiner
- 2 Benefícios do uso de contêineres
- 3 Casos de uso de contêineres
- 4 Contêineres vs. Máquinas Virtuais
- 5 Docker e Kubernetes
- 6 Desenvolvendo um aplicativo usando arquitetura baseada em contêiner
- 7 Conclusão
- 8 FAQ
- 9 O que é um contêiner?
- 10 Quais são as vantagens de usar contêineres?
- 11 Qual é a diferença entre contêineres e máquinas virtuais?
- 12 Qual é a diferença entre Docker e Kubernetes?
- 13 Como desenvolver um aplicativo usando arquitetura baseada em contêiner?
Definição de contêiner
Um contêiner é um pacote executável autônomo que inclui tudo o que é necessário para executar um aplicativo: código, tempo de execução, bibliotecas, variáveis de ambiente e arquivos de configuração. O incrível dos aplicativos em contêineres é que eles podem ser executados em qualquer lugar, desde seu ambiente de desenvolvimento local até nuvens públicas e assim por diante. Os contêineres são pequenos em tamanho, eficientes e permitem um isolamento eficaz.
Benefícios do uso de contêineres
O uso de contêineres tem vários benefícios. Vejamos alguns deles.
Eficiência
Os contêineres exigem menos recursos do sistema do que servidores ou MVs tradicionais porque não incluem uma imagem do sistema operacional. Isso os torna extremamente eficientes, pequenos em tamanho (geralmente medidos em MBs) e permitem que você execute um número significativo de aplicativos em um servidor.
Isolamento de aplicações
Os contêineres isolam o aplicativo e suas dependências do sistema host. Ao mesmo tempo, eles podem compartilhar o kernel do sistema operacional e os recursos do sistema, como CPU, memória, armazenamento e rede.
Portabilidade
O software em contêiner pode ser executado e se comportar da mesma maneira em praticamente qualquer máquina que tenha o mecanismo de contêiner instalado. Isso facilita a implantação e movimentação de aplicativos entre diferentes ambientes e elimina o problema “funciona na minha máquina”.
Separação de responsabilidade
Os contêineres permitem a separação de responsabilidades dividindo tarefas e responsabilidades entre desenvolvedores e equipes de operações de TI. Os desenvolvedores são responsáveis por criar e manter o código e as dependências do aplicativo, enquanto as equipes de operações de TI se concentram na implantação e no gerenciamento dos contêineres e da infraestrutura subjacente.
Desenvolvimento de aplicativos mais rápido
A conteinerização facilita o desenvolvimento, teste, gerenciamento e distribuição de software. Os contêineres podem ser facilmente integrados a sistemas de CI/CD, o que pode acelerar bastante o desenvolvimento de software e o processo de envio.
Fácil Escala
Aplicativos em contêiner em combinação com uma plataforma de orquestração como Kubernetespode escalar facilmente sob demanda. Isso permite que sua empresa acomode altas cargas de trabalho enquanto minimiza os custos.
Casos de uso de contêineres
A tecnologia de contêiner tem muitos casos de uso para desenvolvedores, bem como para equipes de operações de TI.
Desenvolvimento nativo de contêiner
O desenvolvimento nativo de contêiner é uma abordagem de desenvolvimento de software que utiliza contêineres como um bloco de construção principal. No desenvolvimento nativo de contêiner, os aplicativos são empacotados como contêineres e executados em um ambiente em contêiner. Essa abordagem de desenvolvimento oferece todas as vantagens legais que os contêineres têm a oferecer.
Integração Contínua e Entrega Contínua (CI/CD)
Em um pipeline de CI/CD, os contêineres são usados para empacotar aplicativos e executar testes automatizados, possibilitando testar e implantar aplicativos de maneira consistente e repetível. Os contêineres podem ser facilmente criados, testados e implantados como parte do pipeline de CI/CD, reduzindo o risco de erros e melhorando a eficiência geral do processo de desenvolvimento de software.
Microsserviços
Os contêineres podem ser usados para desenvolver aplicativos que seguem uma arquitetura de microsserviço. Com os contêineres, você pode facilmente dividir seu aplicativo monolítico em uma coleção de serviços de baixa granularidade fracamente acoplados que são executados em diferentes contêineres.
Ambiente de desenvolvimento
Os contêineres facilitam para as equipes de desenvolvedores configurar rapidamente seus ambientes de desenvolvimento. Eles fornecem ambientes de desenvolvimento consistentes, independentemente do sistema operacional do host e das bibliotecas do host.
Processos em Lote
Os processos em lote podem ser facilmente colocados em contêineres e implantados na nuvem. Cada tarefa é empacotada como uma imagem de contêiner individual e executada como uma instância de contêiner separada. Isso permite a utilização eficiente de recursos, pois cada tarefa é executada em seu próprio ambiente e não interfere em outras tarefas.
Contêineres vs. Máquinas Virtuais
Contêineres e máquinas virtuais são duas abordagens diferentes para a virtualização. Apesar de terem algumas semelhanças, são bem diferentes.
As máquinas virtuais (MVs) são uma abstração do hardware físico. Elas nos permitem transformar um servidor em vários servidores. Cada uma das MVs possui seu próprio sistema operacional e geralmente é gerenciada por um hypervisor. As MVs são adequadas para executar vários aplicativos (no mesmo servidor), aplicativos monolíticos e aplicativos que exigem alto grau de isolamento e segurança. Sua desvantagem é que elas tendem a ocupar muito espaço e podem ser bastante lentas para inicializar.
Os contêineres, por outro lado, são virtualizados no nível do sistema operacional. Eles ocupam menos espaço, pois compartilham o mesmo kernel do Linux, são mais eficientes, inicializam mais rápido, são altamente escaláveis e podem lidar com mais aplicativos. Os contêineres são gerenciados por um Motor Contêiner. Seus principais casos de uso, em contraste com as MVs, são microsserviços e aplicativos que precisam ser portáteis, leves e escaláveis.
Também é possível combinar contêineres e MVs para obter os benefícios de ambos.
Docker e Kubernetes
Duas das ferramentas mais populares para trabalhar com contêineres são Docker e Kubernetes. Vamos explicar como eles funcionam e ver suas diferenças.
Docker é um projeto de código aberto baseado em Linux usado para automatizar a implantação e o gerenciamento de aplicativos em contêineres leves. Isso permite que os aplicativos conteinerizados funcionem com eficiência em diferentes ambientes. Atualmente, o Docker pode ser encontrado em quase qualquer lugar, desde máquinas Linux até grandes provedores de nuvem e assim por diante.
As alternativas mais populares do Docker são Podman, LXD e contêiner.
Kubernetes (K8s) é um sistema de orquestração de contêineres de código aberto para automatizar a implantação, dimensionamento e gerenciamento de aplicativos em contêineres. Desde seu lançamento em 2014, tornou-se o padrão de fato para implantação e operação de aplicativos em contêineres em ambientes de nuvem. Os benefícios do Kubernetes incluem escalabilidade, alta disponibilidade, operações automatizadas, abstração de infraestrutura e monitoramento de integridade.
Outras plataformas de orquestração incluem: AWS ECS, Nomad e Red Hat OpenShift.
Então, qual é a diferença entre Docker e Kubernetes? Bem, em termos simples, o Docker nos permite empacotar e distribuir aplicativos dentro de contêineres, enquanto o Kubernetes facilita o trabalho harmonioso de vários contêineres.
Desenvolvendo um aplicativo usando arquitetura baseada em contêiner
Nesta seção do tutorial, criaremos, encaixaremos e implantaremos uma API REST simples para Contêineres Back4app.
O que é o Back4app Containers?
O Back4app Containers é uma plataforma gratuita de código aberto para implantação e dimensionamento de aplicativos em contêineres distribuídos globalmente em uma infraestrutura de nuvem.
Ele permite que você se concentre em seu software e o envie mais rapidamente, sem precisar se preocupar com o DevOps. A plataforma está intimamente integrada ao GitHub, possui um sistema integrado de CI/CD e permite que você coloque seu aplicativo em funcionamento em minutos!
Por que usar contêineres Back4app?
- Integra-se bem com o GitHub
- Implantações sem tempo de inatividade
- Fácil de usar e tem um nível gratuito
- Excelente suporte ao cliente
Introdução do projeto
Estaremos construindo uma API REST simples que servirá como uma lista de observação de filmes. O aplicativo da web permitirá operações CRUD básicas, como adicionar um filme, excluir um filme e assim por diante. Para criar a API usaremos o framework Flask. Por fim, vamos Dockerizar o projeto e demonstrar como é fácil implantá-lo em Back4app Containers.
Pré-requisitos
- Experiência com o framework Flask
- Compreensão básica do Docker e contêineres
- Capacidade de usar Gite GitHub
Aplicação do código
As etapas a seguir exigirão que você tenha o Python instalado. Se você ainda não tem o Python instalado, vá em frente e baixe-o.
Inicialização do projeto
Primeiro, crie um diretório dedicado para seu aplicativo e navegue até ele:
$ mkdir flask-watchlist
$ cd flask-watchlist
Em seguida, crie um novo ambiente virtual e ative-o:
$ python3 -m venv venv && source venv/bin/activate
Já que vamos usar Flask como nossa estrutura, temos que instalá-lo:
$ (venv) pip install Flask==2.2.2
Crie app.py com o seguinte conteúdo:
# app.py
from flask import Flask
app = Flask(__name__)
app.config['JSON_SORT_KEYS'] = False
@app.route('/')
def index_view():
return {
'detail': 'Hello world!'
}
Esse código inicializa o Flask e cria um endpoint simples que retorna uma mensagem.
Execute o servidor com:
$ flask run
Navegue para http://localhost:5000/ e você deve ver uma mensagem dizendo Hello World!.
Base de dados
Para o banco de dados, vamos usar SQLite. O SQLite é um sistema de gerenciamento de banco de dados relacional incorporado e sem servidor. Para simplificar o trabalho com o banco de dados vamos instalar Flask-SQLAlquimia — uma extensão para Flask que adiciona suporte para SQLAlchemy ao seu aplicativo.
Vá em frente e instale-o executando:
$ (venv) pip install Flask-SQLAlchemy==3.0.3
Em seguida, navegue até o topo de app.py e altere-o para inicializar o banco de dados:
# app.py
db = SQLAlchemy()
app = Flask(__name__)
app.config['JSON_SORT_KEYS'] = False
app.config['SECRET_KEY'] = '5b3cd5b80eb8b217c20fb37074ff4a33'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///default.db"
db.init_app(app)
Não se esqueça da importação:
from flask_sqlalchemy import SQLAlchemy
Em seguida, vamos definir nossos modelos de banco de dados.
Como estamos criando um aplicativo simples de lista de observação de filmes, precisaremos apenas de um modelo. Defina o modelo do Filme assim:
# app.py
class Movie(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(128), nullable=False)
release_date = db.Column(db.Date(), nullable=False)
is_watched = db.Column(db.Boolean, default=False)
watched_at = db.Column(db.DateTime, default=None, nullable=True)
def as_dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
def __repr__(self):
return '<Movie %r>' % self.title
Para inicializar e preencher nosso banco de dados, criaremos um script Python simples. Navegue até a raiz do seu projeto e crie um novo arquivo chamado init_db.py com o seguinte conteúdo:
# init_db.py
from datetime import date
from app import db, app
from app import Movie
with app.app_context():
db.create_all()
if Movie.query.count() == 0:
movies = [
Movie(title='Fight Club', release_date=date(1999, 9, 15)),
Movie(title='The Matrix', release_date=date(1999, 3, 31)),
Movie(title='Donnie Darko', release_date=date(2001, 1, 19)),
Movie(title='Inception', release_date=date(2010, 7, 16)),
]
for movie in movies:
db.session.add(movie)
db.session.commit()
A última coisa que precisamos fazer é executar o script:
$ (venv) python init_db.py
Isso cria o banco de dados, as tabelas do banco de dados e as preenche. O arquivo de banco de dados será colocado na pasta da instância.
Pontos de extremidade da API
Nosso aplicativo da web terá os seguintes endpoints:
/
retorna informações básicas da API/api/
retorna a lista dos filmes/api/create/
adiciona um novo filme à lista de observação/api/<movie_id>/
retorna detalhes de um filme específico/api/watch/<movie_id>/
marca o filme como visto
Vá em frente e defina os endpoints na parte inferior de app.py:
# app.py
@app.route('/')
def index_view():
return {
'name': 'flask-watchlist',
'description': 'a simple app for tracking the movies you want to watch',
'version': 1.1,
}
@app.route('/api/')
def list_view():
json = [movie.as_dict() for movie in Movie.query.all()]
return jsonify(json)
@app.route('/api/<int:movie_id>/', methods=['GET', 'DELETE'])
def detail_view(movie_id):
movie = db.get_or_404(Movie, movie_id)
if request.method == 'DELETE':
db.session.delete(movie)
db.session.commit()
return {
'detail': 'Movie has been successfully deleted.'
}
else:
return movie.as_dict()
@app.route('/api/create/', methods=['POST'])
def create_view():
title = request.form.get('title')
release_date = request.form.get('release_date', type=float)
if title is None or release_date is None:
return {
'detail': 'Please provide the title and release_date.'
}, 400
movie = Movie(title=title, release_date=datetime.fromtimestamp(release_date))
db.session.add(movie)
db.session.commit()
return movie.as_dict()
@app.route('/api/watch/<int:movie_id>/')
def watch_view(movie_id):
movie = db.get_or_404(Movie, movie_id)
if movie.is_watched:
return {
'detail': 'Movie has already been watched.'
}, 400
movie.is_watched = True
movie.watched_at = datetime.now()
db.session.commit()
return movie.as_dict()
Não se esqueça das importações:
from datetime import datetime
from flask import request, jsonify
Ótimo, nosso aplicativo agora está mais ou menos completo. Execute o servidor de desenvolvimento:
$ (venv) flask run
Teste se você pode buscar a lista de filmes:
$ (venv) curl http://localhost:5000/api/ | jq '.'
[
{
"id": 1,
"title": "Fight Club",
"release_date": "Wed, 15 Sep 1999 00:00:00 GMT",
"is_watched": false,
"watched_at": null
},
{
"id": 2,
"title": "The Matrix",
"release_date": "Wed, 31 Mar 1999 00:00:00 GMT",
"is_watched": false,
"watched_at": null
},
...
]
Gunicorn
O servidor de desenvolvimento Flask não é apropriado para produção, então vamos trocá-lo por Gunicorn. Gunicorn ou “Green Unicorn” é um Python WSGI HTTP Server pronto para produção para Unix.
Instale-o executando:
$ (venv) pip install gunicorn==20.1.0
Após a instalação do pacote, você pode iniciar seu servidor WSGI da seguinte forma:
$ (venv) gunicorn -w 2 -b 0.0.0.0:5000 app:app
[INFO] Starting gunicorn 20.1.0
[INFO] Listening at: http://0.0.0.0:5000 (1)
[INFO] Using worker: sync
[INFO] Booting worker with pid: 7
[INFO] Booting worker with pid: 8
Lembre-se de que esse comando funciona apenas em sistemas operacionais baseados em UNIX.
Isso iniciará dois trabalhadores do Gunicorn e irá expor seu aplicativo à Internet. Para acessar o aplicativo, abra seu navegador da Web favorito e navegue até http://localhost:5000.
requirements.txt
A última coisa que precisamos fazer antes de dockerizar nosso aplicativo é criar um arquivo requirements.txt. O arquivo requirements.txt é usado para especificar as dependências do projeto.
A maneira mais fácil de gerá-lo é executando:
$ (venv) pip freeze > requirements.txt
Dockerizar Aplicativo
As etapas a seguir exigirão que você tenha o Docker instalado. A maneira mais fácil de instalar o Docker é baixando Docker Desktop.
Para verificar se você tem o Docker instalado, execute:
$ docker --version
Docker version 20.10.22, build 3a2c30b
Dockerfile
Para dockerizar nosso aplicativo, usaremos um Dockerfile. Um Dockerfile é um arquivo de texto simples que nos permite definir a imagem base, ambiente, variáveis ambientais, comandos, configurações de rede, volumes e assim por diante.
Crie um Dockerfile na raiz do seu projeto com o seguinte conteúdo:
# syntax=docker/dockerfile:1.4
FROM --platform=$BUILDPLATFORM python:3.10-alpine
# set the working directory
WORKDIR /app
# set environmental variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install the requirements
COPY requirements.txt /app
RUN --mount=type=cache,target=/root/.cache/pip \
pip3 install -r requirements.txt
# copy the code to the container
COPY . .
# initialize the database (create DB, tables, populate)
RUN python init_db.py
# expose
EXPOSE 5000/tcp
# entrypoint command
CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "app:app"]
- Nós usamos python:3.10-alpine como a imagem base.
- Contexto PYTHONDONWRITEBYTECODE para 1 faz com que o Python não grave mais arquivos .pyc no disco.
- Contexto PYTHONUNBUFFERED para 1 garante que os fluxos de saída do Python sejam enviados diretamente para o terminal.
Para mais informações sobre como escrever Dockerfiles, dê uma olhada na Referência do Dockerfile.
.dockerignore
Antes de o Docker criar uma imagem, ele procura um arquivo .dockerignore. Um arquivo .dockerignore nos permite definir quais arquivos não queremos que sejam incluídos na imagem. Isso pode reduzir bastante o tamanho da imagem. Ele funciona de forma semelhante a um arquivo .gitignore.
Crie um arquivo .dockerignore na raiz do projeto com o seguinte conteúdo:
# .dockerignore
.git/
instance/
__pycache__/
.idea/
Certifique-se de adicionar quaisquer diretórios ou arquivos adicionais que gostaria de excluir.
Crie e execute a imagem
Seguindo em frente, vamos criar e marcar nossa imagem do Docker.
$ docker build -t flask-watchlist:1.0 .
[+] Building 11.1s (15/15) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 32B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 34B 0.0s
=> resolve image config for docker.io/docker/dockerfile:1.4 0.5s
=> CACHED docker-image://docker.io/docker/dockerfile:1.4@sha256:9ba7531a0dbc 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> [internal] load .dockerignore 0.0s
=> [internal] load metadata for docker.io/library/python:3.10-alpine 0.5s
=> [stage-0 1/6] FROM docker.io/library/python:3.10-alpine@sha256:da5ab5e911253dfb 0.0s
=> [internal] load build context 0.3s
=> => transferring context: 182.45kB 0.2s
=> CACHED [stage-0 2/6] WORKDIR /app 0.0s
=> [stage-0 3/6] COPY requirements.txt /app 0.0s
=> [stage-0 4/6] RUN --mount=type=cache,target=/root/.cache/pip
pip3 install -r requirements.txt 7.2s
=> [stage-0 5/6] COPY . . 0.3s
=> [stage-0 6/6] RUN python init_db.py 1.5s
=> exporting to image 0.3s
=> => exporting layers 0.3s
=> => writing image sha256:2671ccb7546a0594807c721a0600a 0.0s
=> => naming to docker.io/library/flask-watchlist:1.0
Se você listar as imagens, deverá ver nossa nova imagem:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
flask-watchlist 1.0 7bce66230eb1 8 hours ago 110MB
Por fim, use a imagem para criar um novo contêiner do Docker:
$ docker run -it -p 5000:5000 flask-watchlist:1.0
[2023-02-02 20:08:57 +0000] [1] [INFO] Starting gunicorn 20.1.0
[2023-02-02 20:08:57 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1)
[2023-02-02 20:08:57 +0000] [1] [INFO] Using worker: sync
[2023-02-02 20:08:57 +0000] [7] [INFO] Booting worker with pid: 7
[2023-02-02 20:08:57 +0000] [8] [INFO] Booting worker with pid: 8
Você pode usar -d para iniciar o contêiner do Docker no modo desanexado. O que significa que o contêiner é executado em segundo plano no seu terminal e não recebe entrada ou exibe a saída.
Muito bem, seu aplicativo agora está sendo executado em um contêiner! Navegue para http://localhost:5000 e você deve obter a seguinte resposta:
{
"name": "flask-watchlist",
"description": "a simple app for tracking the movies you want to watch",
"version": 1
}
GitHub
Para implantar o aplicativo no Back4app Containers, você terá que carregar seu código-fonte em um repositório GitHub. Vá em frente e crie um novo repositório no GitHub, adicione o controle remoto, adicione .gitignore e confirme seu código. Assim que seu código estiver no GitHub, vá para a próxima etapa.
Implantar aplicativo em contêineres Back4app
As etapas a seguir exigirão que você tenha uma conta Back4app. Se você já tem Conecte-se, caso contrário, vá em frente e inscreva-se para a conta gratuita.
Para trabalhar com Back4app, primeiro precisamos criar um aplicativo. Ao fazer login no seu painel, você verá a lista de seus aplicativos. Clique em “Criar um novo aplicativo” para criar um novo aplicativo.
Em seguida, selecione “Contêineres como serviço”.
Se você ainda não o fez, conecte seu GitHub ao Back4app e importe os repositórios que deseja implantar. Depois que seu GitHub estiver conectado, seus repositórios serão exibidos na tabela.
Escolha o repositório que deseja implantar clicando em “Selecionar”.
Em seguida, o Back4app solicitará que você configure o ambiente. Escolha um nome de aplicativo, eu irei com flask-watchlist. Sinta-se à vontade para deixar todo o resto como padrão.
Por fim, clique em “Criar aplicativo” para criar automaticamente o aplicativo e implantá-lo.
Você será redirecionado para os detalhes do aplicativo, onde poderá ver os logs de implantação.
Aguarde alguns minutos para que o aplicativo seja implantado e pronto! Seu aplicativo agora está ativo no Back4app Containers. Para ver seu aplicativo em ação, clique no URL verde exibido à esquerda.
Conclusão
Ao longo do artigo, explicamos o que são contêineres, seus benefícios e demonstramos como você pode implementar contêineres em seu fluxo de trabalho. Até agora você deve ser capaz de construir sua própria API REST simples, encaixá-la e implantá-la em Back4app Containers.
Pegue o código-fonte final do repositório GitHub.
Etapas futuras
- Você não deve armazenar o banco de dados em sua imagem. No momento, cada reimplantação vai redefinir o banco de dados. Considere mudar para uma instância gerenciada de PostgreSQL ou MySQL.
- Aprenda sobre compilações de vários estágios para otimizar seus Dockerfiles.
- Leia o artigo Como implantar contêineres do Docker para um tutorial passo a passo
FAQ
O que é um contêiner?
Um contêiner é um pacote executável autônomo que inclui tudo o que é necessário para executar o aplicativo. Isso é código, tempo de execução, bibliotecas, variáveis de ambiente e arquivos de configuração.
Quais são as vantagens de usar contêineres?
– Eficiência
– Isolamento de aplicativos
– Separação de responsabilidades
– Desenvolvimento mais rápido de aplicativos
Qual é a diferença entre contêineres e máquinas virtuais?
As MVs são uma abstração do hardware físico, enquanto os contêineres são virtualizados no nível do sistema operacional. As MVs oferecem maior isolamento e segurança, enquanto os contêineres não ocupam muito espaço, são eficientes e escaláveis.
Qual é a diferença entre Docker e Kubernetes?
O Docker nos permite empacotar e distribuir aplicativos dentro de contêineres, enquanto o Kubernetes facilita o trabalho conjunto de vários contêineres.
Como desenvolver um aplicativo usando arquitetura baseada em contêiner?
1. Escolha uma linguagem de programação e codifique seu aplicativo.
2. Dockerize seu aplicativo com um Dockerfile ou Docker Compose.
3. Crie uma imagem do Docker e teste-a localmente.
4. Escolha um CaaS como Contêineres Back4app e envie seu código para ele. Aguarde até que o serviço seja implantado e pronto!