Docker para iniciantes: Criando Containers de Bancos de Dados
Tags: docker, database, beginners, tutorial
Published: 2024-02-17 16:28:32

Nesse artigo, vou fornecer alguns exemplos de como criar containers de bancos de dados utilizando Docker.

O foco deste artigo é auxiliar iniciantes no uso do Docker em seus projetos, abordando dúvidas sobre a criação do `docker-compose` ou `Dockerfile`.

Como referência para os bancos de dados mais populares, utilizei o top 9 da pesquisa do [Stack Overflow](https://survey.stackoverflow.co/2023/#technology) de 2023. 

## Sumário

- [PostgreSQL](#postgresql)
- [MySQL](#mysql)
- [SQLite](#sqlite)
- [MongoDB](#mongodb)
- [Microsoft SQL Server](#microsoft-sql-server)
- [Redis](#redis)
- [MariaDB](#mariadb)
- [Elasticsearch](#elasticsearch)
- [Oracle](#oracle)

## PostgreSQL

No nosso Dockerfile, será bastante simples. Vamos utilizar a imagem do PostgreSQL na versão 16 e expor a porta 5432 para que seja acessível pelo nosso container.

```dockerfile
FROM postgres:16

EXPOSE 5432
```

Vamos adicionar ao nosso services o container do banco de dados e atribuir um nome a ele, neste caso, `postgres_db`. 

```yml
services:
  database:
    container_name: postgres_db
```

Na tag build, informaremos onde se encontra nosso `Dockerfile` que será usado para configurar o nosso container do Postgres.

```yml
    build:
      context: .
      dockerfile: Dockerfile.postgres
```

No context utilizamos o ponto para indicar que o Dockerfile se encontra no mesmo diretório que o docker-compose.

Como PostgreSQL utiliza o usuário padrão `postgres`, adicionaremos apenas uma senha para o nosso usuário na seção environment.

```yml
    environment:
      POSTGRES_PASSWORD: 12345678
```

Agora precisamos adicionar as portas de comunicação entre a máquina host e o container, isso irá fazer com que tenha uma ligação entre os dois permitindo com que seja possível acessar o banco de dados que foi criado no container.

```yml
    ports:
      - "5432:5432"
```

A criação de volumes é opcional, mas é algo interessante de se fazer quando estamos utilizando banco de dados, pois permite que ocorra a persistência de dados do nosso container e mesmo que ele seja destruído podemos recriar o container com os dados intactos a partir do volume.

```yml
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
    postgres_data:
```

Por último iremos criar uma Network. Elas são usadas para facilitar a comunicação dos nossos services. Por exemplo, digamos que você deseja criar um Compose que tenha o Postgres e o pgAdmin, para facilitar a interação entre eles, adicionamos ambos na mesma network para ajudar na comunicação (Exemplo [aqui](https://github.com/JoaoVitorBernardino/docker-for-beginners/blob/main/1%20-%20PostgreSQL/pgAdmin/docker-compose.yml)).

```yml
    networks:
      - mynet

networks:
  mynet:
    driver: bridge
```

### Docker-compose
```yml
version: '3.8'

services:
  database:
    container_name: postgres_db
    build:
      context: .
      dockerfile: Dockerfile.postgres
    environment:
      POSTGRES_PASSWORD: 12345678
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - mynet

volumes:
  postgres_data:

networks:
  mynet:
    driver: bridge
```

## MySQL

Para o Dockerfile do MySQL, faremos a mesma configuração do PostgreSQL, com a diferença de que vamos expor a porta padrão do MySQL.

```dockerfile
FROM mysql:8.3

EXPOSE 3306
```

O docker-compose seguirá a mesma estrutura do compose do Postgres, com a diferença de que na seção environment serão adicionados mais alguns campos além da senha do usuário.

```yml
    environment:
      MYSQL_ROOT_PASSWORD: 12345678
      MYSQL_DATABASE: mydb
      MYSQL_USER: admin
      MYSQL_PASSWORD: admin123
```

O campo `MYSQL_ROOT_PASSWORD`, como o nome sugere, será a senha do usuário root. Não confunda com o campo `MYSQL_PASSWORD`, que é a senha do usuário defininda no campo `MYSQL_USER`. O campo `MYSQL_DATABASE` é o nome do banco de dados que será criado ao construir o container. Você pode criar outros bancos de dados após a criação do container usando comandos do MySQL ou um cliente de banco de dados, como o DBeaver (Exemplo [aqui](https://github.com/JoaoVitorBernardino/docker-for-beginners/blob/main/2%20-%20MySQL/Dbeaver/docker-compose.yml)).

### Docker-compose

```yml
version: '3.8'

services:
  database:
    container_name: mysql_db
    build:
      context: .
      dockerfile: Dockerfile.mysql
    environment:
      MYSQL_ROOT_PASSWORD: 12345678
      MYSQL_DATABASE: mydb
      MYSQL_USER: admin
      MYSQL_PASSWORD: admin123
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - mynet2

volumes:
  mysql_data:

networks:
  mynet2:
    driver: bridge
```

## SQLite

O SQLite é diferente dos outros bancos de dados mencionados aqui. Ele é um banco de dados `disk-based`, o que significa que armazena os dados gerados em um arquivo no próprio sistema e não depende de um servidor, como os outros bancos de dados apresentados. Geralmente, os arquivos são salvos com a extensão `.db`.

É amplamente utilizado em aplicações móveis, principalmente no Android, devido à sua presença em muitos dispositivos.

### Dockerfile

Para o Dockerfile do SQLite, usaremos uma imagem de uma distribuição Linux chamada [Alpine](https://hub.docker.com/_/alpine), conhecida por sua leveza, o que a torna uma ótima opção para containers Docker.

```dockerfile
FROM alpine:3.19

RUN apk add --no-cache sqlite

WORKDIR /database
```

Neste Dockerfile, instalamos o SQLite em nosso container e criamos um diretório chamado "database", onde executaremos nossos comandos do SQLite e salvaremos os bancos de dados criados.

### Docker-compose

O Compose do SQLite seguirá uma estrutura semelhante ao do Postgres e do MySQL, mas com algumas diferenças.

A primeira diferença está nos volumes, onde vamos explicitar o diretório em que o volume será montado, permitindo uma manipulação mais eficiente dos dados do container.

```yml
    volumes:
      - ./sqlite_data:/database
```

A segunda diferença é o comando `tail -f /dev/null`. Como o SQLite é um banco de dados `disk-based` e não possui um servidor para manter o container ativo, usamos este comando para manter o container em execução.

```yml
command: ["tail", "-f", "/dev/null"]
```
O comando `tail` é usado para exibir as últimas linhas de um arquivo, enquanto o argumento `-f` serve para especificar o arquivo que o comando deverá seguir esperando novas linhas. Ao especificarmos `/dev/null`, que não contêm nada, o comando não exibirá nenhum conteúdo no terminal, mantendo assim nosso container ativo para que possamos utilizar os comandos do SQLite no terminal.

```yml
version: '3.8'

services:
  database:
    container_name: sqlite_db
    build:
      context: .
      dockerfile: Dockerfile.sqlite
    volumes:
      - ./sqlite_data:/database
    networks:
      - mynet3
    command: ["tail", "-f", "/dev/null"]

volumes:
  sqlite_data:

networks:
  mynet3:
    driver: bridge
```

## MongoDB

O MongoDB é um banco de dados NoSQL, diferentemente dos outros apresentados até agora, que eram SQL, ou seja, bancos relacionais. O Mongo é um banco de dados orientado a documentos e é amplamente utilizado por sua flexibilidade, escalabilidade e facilidade de manipulação.

### Dockerfile

Nosso Dockerfile seguirá o mesmo padrão dos outros. Utilizaremos a versão latest e iremos expor a porta padrão do MongoDB, que é `27017`.

```dockerfile
FROM mongo:latest

EXPOSE 27017
```

### Docker-compose

No docker-compose do MongoDB, forneceremos duas variáveis de ambiente. A priemira é `MONGO_INITDB_ROOT_USERNAME`, para definirmos o usuário root, e a segunda é `MONGO_INITDB_ROOT_PASSWORD`, onde adicionaremos a senha deste usuário.

```yml
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: 12345678
```

Também utilizaremos o `mongo-express`, que é uma interface web para facilitar a manipulação do Mongo, semelhante ao PgAdmin do PostgreSQL.

```yml
mongo-express:
    image: mongo-express
```

Para funcionar corretamente, precisamos informar nas variáveis do mongo-express o usuário e senha que criamos para o MongoDB, além da URL do banco de dados.

```yml
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: admin
      ME_CONFIG_MONGODB_ADMINPASSWORD: 12345678
      ME_CONFIG_MONGODB_URL: mongodb://admin:12345678@database:27017/
```

A estrutura da URL é da seguinte forma `<Driver>://<user>:<password>@<host>:<port>/`. Como estamos utilizando a URL externamente, usaremos o nome do nosso service do MongoDB, que é `database`, Caso vá fazer a conexão dentro do container do Mongo utilizando o [mongosh](https://www.mongodb.com/docs/mongodb-shell/), você terá que substituir o host para `localhost`.

Para poder acessar mongo-express, será necessário criar um usuário e senha. Isso será feito através das variáveis `ME_CONFIG_BASICAUTH_USERNAME` e `ME_CONFIG_BASICAUTH_PASSWORD`.

```yml
      ME_CONFIG_BASICAUTH_USERNAME: mongo
      ME_CONFIG_BASICAUTH_PASSWORD: 123456
```

É necessário que os services utilizem a mesma network para facilitar a comunicação entre eles.

```yml
version: '3.8'

services:
  database:
    container_name: mongo_db
    build:
      context: .
      dockerfile: Dockerfile.mongo
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: 12345678
    ports:
      - "27017:27017"
    volumes:
      - mongo_data:/data/db
    networks:
      - mynet4
  
  mongo-express:
    image: mongo-express 
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: admin
      ME_CONFIG_MONGODB_ADMINPASSWORD: 12345678
      ME_CONFIG_MONGODB_URL: mongodb://admin:12345678@database:27017/
      ME_CONFIG_BASICAUTH_USERNAME: mongo
      ME_CONFIG_BASICAUTH_PASSWORD: 123456
    depends_on:
      - database
    ports:
      - "8081:8081"
    networks:
      - mynet4

volumes:
  mongo_data:
  
networks:
  mynet4:
    driver: bridge
```

## Microsoft SQL Server

O SQL Server, diferentemente dos outros bancos de dados, tem sua imagem é mantida pelo `Registro de Contêiner da Microsoft`, em vez do Docker Hub.

### Dockerfile

A imagem utilizada é a versão Linux do SQL Server. Para mais informações ou para utilizar uma imagem específica do banco de dados, acesse [aqui](https://mcr.microsoft.com/product/mssql/server/about).

```dockerfile
FROM mcr.microsoft.com/mssql/server:2022-latest

EXPOSE 1433
```

### Docker-compose

Diferente dos outros bancos, é necessário utilizar a variável `ACCEPT_EULA` para confirmar que aceita os temos do Contrato de Licença do Usuário Final. E na variável `MSSQL_SA_PASSWORD`, é necessário seguir a política de senha da Microsoft (veja [aqui](https://learn.microsoft.com/pt-br/sql/relational-databases/security/password-policy?view=sql-server-ver16)).

```yml
version: '3.8'

services:
  database:
    container_name: sqlserver_db
    build: 
      context: .
      dockerfile: Dockerfile.sqlserver
    environment:
      ACCEPT_EULA: Y
      MSSQL_SA_PASSWORD: Admin123#
    ports:
      - "1455:1433"
    networks:
      - mynet5

networks:
  mynet5:
    driver: bridge
```

Para mais informações sobre a imagem, instruções e como utilizar os comando do SQL Server, acesse a documentação oficial da Microsoft [aqui](https://learn.microsoft.com/pt-br/sql/linux/quickstart-install-connect-docker?view=sql-server-ver16&pivots=cs1-bash).

## Redis

O Redis, assim como o MongoBD, também é um banco de dados NoSQL. É um banco de dados em memória que utiliza uma estrutura de dados chave-valor e é amplamente utilizado para `cache`.

### Docker-compose

O Compose do Redis será mais simples. Utilizaremos a tag `image` para indicar qual será a imagem que nosso container irá fazer o pull e também exporemos a porta padrão do Redis, que é a `6379`.

```yml
    image: redis
    ports:
      - "6379:6379"
```

No Redis, também podemos personalizar sua configuração através de um arquivo chamado `redis.conf`, onde podemos definir, por exemplo, quais endereços ele poderá se conectar, quanto de memória será utilizado, especificar o protocolo que pode ser usado e muitas outras configurações. Veja mais sobre as configurações do Redis [aqui](https://redis.io/docs/management/config-file/).

```yml
version: '3.8'

services:
  database:
    image: redis
    container_name: redis_db
    ports:
      - "6379:6379"
    networks:
      - mynet6
    
networks:
  mynet6:
    driver: bridge
```

## MariaDB

Iremos utilizar a versão `11.2.3` da MariaDB e, assim como o MySQL, iremos expor a porta 3306.

```dockerfile
FROM mariadb:11.2.3

EXPOSE 3306
```

### Docker-compose

O docker-compose da MariaDB será semelhante ao do MySQL, tendo como única diferença na envirenment que trocaremos somente o prefixo `MYSQL` por `MARIADB`.

```yml
    environment:
      MARIADB_ROOT_PASSWORD: 13246578
      MARIADB_DATABASE: mydb
      MARIADB_USER: admin
      MARIADB_PASSWORD: admin123
```

Também teremos um exemplo de docker-compose utilizando o DBeaver [aqui](https://github.com/JoaoVitorBernardino/docker-for-beginners/blob/main/7%20-%20MariaDB/Dbeaver/docker-compose.yml).

```yml
version: '3.8'

services:
  database:
    build:
      context: .
      dockerfile: Dockerfile.mariadb
    environment:
      MARIADB_ROOT_PASSWORD: 13246578
      MARIADB_DATABASE: mydb
      MARIADB_USER: admin
      MARIADB_PASSWORD: admin123
    ports:
      - "3306:3306"
    volumes:
      - mariadb_data:/var/lib/mysql
    networks:
      - mynet7

volumes:
  mariadb_data:

networks:
  mynet7:
    driver: bridge
```

## Elasticsearch

O Elasticsearch é mais um banco de dados NoSQL da nossa lista. Ele é muito usado como mecanismo de pesquisa, para análise de logs, análises de negócios e entre outros objetivos.

Para uma melhor experiência, irei criar um container utilizando o Elasticsearch e o `Kibana`, que é um plugin de visualização de dados utilizado com o Elastic. Ambos fazem parte da Elastic Stack (ou ELK Stack), que inclui outras ferramentas como o Beats e o Logstash.

### Dockerfile

No nosso Dockerfile, iremos utilizar a versão `8.12.1` do Elasticsearch. Também criaremos um diretório onde vamos copiar um script para executar dois comandos necessários para utilizar o Elastic com o Kibana. Por fim, iremos expor a porta padrão do Elasticsearch, que é a 9200. 

```dockerfile
FROM docker.elastic.co/elasticsearch/elasticsearch:8.12.1

WORKDIR /elastic

COPY setup_elasticsearch.sh /elastic

EXPOSE 9200
```

Nesse script, iremos executar dois comandos que são necessários. O primeiro é para conseguirmos a senha do usuário `elastic`, e o segundo comando será para gerarmos o token do kibana. Basicamente, ambos os comandos estão sendo executados e respondendo que queremos resetar a senha e gerar o token.

```bash
#!/bin/bash

echo "y" | /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic

echo "y" | /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana
```

> A utilização do script é opcional; você pode executar os comandos manualmente pelo terminal do container. Mas, caso deseje utilizar, basta entrar no terminal do container e executá-lo escrevendo `./setup_elasticsearch.sh`.


### Docker-compose

O Compose terá algumas variáveis de ambiente diferentes até agora. Vamos utilizar apenas duas para este exemplo, mas vale lembrar que na documentação do Elasticsearch e do Kibana existe várias outras.

Estamos utilizando a variável `discovery.type` como "single-node". Por padrão, o Elasticsearch permite que o cluster seja "multi-node", o que permite conectar e juntar vários nós no mesmo cluster.

E a segunda variável de ambiente que vamos utilizar é a `ES_JAVA_OPTS`, que define o tamanho inicial da memória `heap` que o Elasticsearch pode usar. Neste caso, estamos definindo o tamanho como 1GB.

```yml
version: '3.8'

services:
  elasticsearch:
    build:
      context: .
      dockerfile: Dockerfile.elastic
    container_name: Elasticsearch
    environment:
      discovery.type: "single-node"
      ES_JAVA_OPTS: "-Xms1g -Xmx1g"
    ports:
      - "9200:9200"
    networks:
      - mynet8

  kibana:
    image: docker.elastic.co/kibana/kibana:8.12.1
    container_name: kibana
    ports:
      - "5601:5601"
    networks:
      - mynet8

networks:
  mynet8:
    driver: bridge
```

## Oracle

Voltamos aos bancos de dados SQL, e para este exemplo, irei utilizar a versão `23c Free` do banco de dados da Oracle. Encontrar a imagem da Oracle pode ser um pouco mais complicado, pois assim como a Microsoft com o SQL Server, a Oracle também mantêm suas próprias imagens, o que torna um pouco mais difícil de achar e utilizar no Docker. A versão que irei utilizar pode ser encontrada [aqui](https://container-registry.oracle.com/ords/f?p=113:4:5674987054298:::4:P4_REPOSITORY,AI_REPOSITORY,AI_REPOSITORY_NAME,P4_REPOSITORY_NAME,P4_EULA_ID,P4_BUSINESS_AREA_ID:1863,1863,Oracle%20Database%20Free,Oracle%20Database%20Free,1,0&cs=3aUoFW2rXVkvlvAzDeuZi6P_iZvD9lcYmYGXWDBRN2eFu2ZZ13ezdTy9qX6C4f5agbKRr9B68yO_yzyxz87V5bA).

## Docker-compose

Nosso Compose será simples, sendo necessário informar apenas a senha do nosso usuário nas variáveis de ambiente.

```yml
     environment:
      ORACLE_PWD: 12345678
```

Você também pode modificar o conjunto de caracteres do banco de dados utilizando a variável `ORACLE_CHARACTERSET`, mas é opcional. O valor padrão é definido como `AL32UTF8`.

```yml
version: '3.8'

services:
  database:
    image: container-registry.oracle.com/database/free:latest
    container_name: oracle
    environment:
      ORACLE_PWD: 12345678
    ports:
      - "1521:1521"
    volumes:
      - "oracle_data:/opt/oracle/oradata"
    networks:
      - mynet9

volumes:
  oracle_data:

networks:
  mynet9:
    driver: bridge
```

Para conhecer melhor esse banco de dados, gostaria de deixar alguns links úteis. O [Get Started](https://www.oracle.com/database/free/get-started/) ensina a instalar o Oracle de outras formas e também ensina como se conectar no SQL pelo terminal e também em várias linguagens. O [Oracle Database 23c](https://docs.oracle.com/en/database/oracle/oracle-database/23/index.html) para se aprofundar mais nessa versão. E também o [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:10::::::) caso queira obter as imagens de outros produtos da Oracle ou até mesmo utilizar outras versões do banco de dados.
 
## Conclusão

Espero que este artigo ajude alguém que está começando e tire algumas dificuldades ao criar seu container Docker, pois criar container de banco de dados é algo que utilizamos muito no nosso dia a dia. Vou deixar [aqui](https://github.com/JoaoVitorBernardino/docker-for-beginners) o link do repositório com todos os exemplos.