PostgreSQL + Docker = 💙
Tags: docker, dockercompose, postgres, pgadmin
Published: 2020-09-13 18:18:17

Assim como várias outra técnologias, o PostgreSQL também disponibiliza sua versão conteinerizada, conhecido como imagem, para ser executada usando Docker. Esta prática de conteinerização possibilita uma configuração rápida de ambientes de desenvolvimento, testes e produção.
  
Os familiarizados ao SGBD sabem que este tem uma ferramenta gráfica de administração que, por sua vez, também disponibiliza uma imagem gratuita e que pode ser utilizado em qualquer sistema operacional que suporte Docker. E os familiarizados ao Docker sabem que existe uma forma simples de executar vários contêineres, o Docker Compose, utilizando um arquivo YAML.
  
Será demonstrado neste tutorial:
  
- [:clock12: Criar uma network](#clock12-criar-uma-network )
- [:clock2: Criar um volume](#clock2-criar-um-volume )
- [:clock4: Criar contêineres a partir de imagens](#clock4-criar-contêineres-a-partir-de-imagens )
- [:clock6: Acessar o banco usando pgAdmin4](#clock6-acessar-o-banco-usando-pgadmin4 )
- [:clock8: Criar arquivo YAML](#clock8-criar-arquivo-yaml )
- [:link: Links](#link-links )
  
Portanto, é esperado que você tenha conhecimento básico das tecnologias citadas. Certifique-se de que há instalado em sua máquina o [Docker](https://docs.docker.com/get-docker/ ) e [Docker Compose](https://docs.docker.com/compose/install/ ). Ainda, se atente que todos comandos mostrados neste tutorial são executados utilizando um terminal.
  
#  :clock12: Criar uma network
  
  
Network (rede) são pontes de comunicação que possiblitam à contêineres uma conexão entre eles. Geralmente, criasse uma network quando dois ou mias contêineres têm uma relação e comunicam-se. Portanto, para este caso crie uma network chamada **postgres-network** utilizando o comando abaixo:
  
```sh
docker network create -d bridge postgres-network
```
  
Criado a network que será utilizada nesse tutorial, é possível ver quais outras networks estão sendo utilizadas em sua máquina:
  
```sh
docker network ls
```
  
| NETWORK ID   | NAME                 | DRIVER | SCOPE |
| ------------ | -------------------- | ------ | ----- |
| a8d59bf0f5bb | bridge               | bridge | local |
| c0beeb145f0d | host                 | host   | local |
| d539d7388de2 | **postgres-network** | bridge | local |
  
#  :clock2: Criar um volume
  
  
Volume é o jeito utilizado no Docker para criar uma armazenamento persistente de dados, ou seja, ao desligar o contêiner os dados continuam existindo.
Para criar um volume utilize o comando abaixo, neste caso será criando um volume chamado **postgres-data**:
  
```sh
docker volume create --name=postgres-data
```
  
Para listar todos volumes existentes em sua máquina utilize o comando:
  
```sh
docker volume ls
```
  
| DRIVER | VOLUME NAME                 |
| ------ | --------------------------- |
| local  | 2b168382f98fc0280482b760893 |
| local  | portainer_data              |
| local  | **postgres-data**           |
  
#  :clock4: Criar contêineres a partir de imagens
  
  
Antes de escolher uma imagem docker para utilizar é preciso se atentar à alguns detalhes:
  
- Imagens docker são construídas em cima de alguma imagem de sistema operacional, geralmente linux, e cada imagem linux tem um tamanho.
- Imagens também têm uma versão, geralmente estes são versões do próprio produto/serviço escolhido, e caso não seja especificado uma versão sempre será instalado a última versão estável.
  
Tendo os detalhes citados acima em mente, neste tutorial será usado a imagem do PostgreSQL versão **12.4**, baseada na imagem linux **Alpine** de **~ 5MB**, e para a imagem do pgAdmin4 você irá usar a última versão estável (mais abaixo). Siga o comando a baixo para criar um contêiner do PostgreSQL:
  
```sh
docker run --name postgres --network=postgres-network -e "POSTGRES_PASSWORD=postgresql" -v postgres-data:/var/lib/postgresql/data -p 5440:5432 -d postgres:12.4-alpine
```
  
Nesta única linha foi criado e executado um contêiner, em que:
  
- **-\-name**, especificao nome do contêiner (**postgres**)
- **-\-network**, especificaa rede (**postgres_network**)
- **-e**, especifica a variável de ambiente do contêiner
  - **POSTGRES_PASSWORD=postgresql**, é a variável de ambiente que especificaa senha necessária para ter acesso ao banco de dados.
- **-v**, especifica o volume utilizado para persistir os dados (**postgres-data**).
- **-p**, especificaqual porta será exposta (**5440:5432**), ou seja, será acessível de fora do docker (utilizar em uma aplicação não conteinerizada).
- **-d**, especificaque após criado o contêiner seu terminal se separe do contêiner e você consiga utilizando sem ter que iniciar um novo terminal, ou tenha que parar o contêiner.
- **postgres:12.4-alpine**, por fim o nome da imagem utilizada para criar o contêiner, neste caso a image **postgres**, versão **12.4** utilizando linux **alpine**.
  
É possível ver outras versões de imagens PostgreSQL acessando: [https://hub.docker.com/\_/postgres](https://hub.docker.com/_/postgres )
  
Agora, crie um contêiner utilizando a imagem do pgAdmin4 em sua última versão utilizando o seguinte comando:
  
```sh
docker run --name pgadmin --network=postgres-network -p 15432:80 -e "PGADMIN_DEFAULT_EMAIL=example@example.com" -e "PGADMIN_DEFAULT_PASSWORD=pgadmin1234" -d dpage/pgadmin4
```
  
Este comando é muito parecido com o utilizado para criar o contêiner do Postgres. Estão presentes os atributos **name**, **network**, **p**, **d**, e dessa vez 2 atributos **e** de variáveis de ambiente:
  
- **PGADMIN_DEFAULT_EMAIL**, email de login (**example@example.com**).
- **PGADMIN_DEFAULT_PASSWORD**, senha de login (**pgadmin1234**)
  
Também, por último se encontra o nome da imagem utilizada para criar o contêiner (**dpage/pgadmin4**), como pode ver, desta vez não foi passado nenhuma versão especifica, portanto, por padrão é instalado a última versão estável da imagem, outro jeito de especificar a última versão da imagem é usando **dpage/pgadmin:latest**.
  
#  :clock6: Acessar o banco usando pgAdmin4
  
  
Já que foi criado os dois contêineres é hora de testar se tudo está funcionando como esperado. Primeiro rode o comando abaixo para listar todos os contêineres em execução em sua máquina:
  
```sh
docker ps
```
  
Caso os dois contêineres ainda estejam em execução você deve ver algo similar ao mostrado abaixo:
  
| CONTAINER ID | IMAGE           | COMMAND                | CREATED       | STATUS     | PORTS                          | NAMES    |
| ------------ | --------------- | ---------------------- | ------------- | ---------- | ------------------------------ | -------- |
| cdb6a40baab2 | dpage/pgadmin4  | "/entrypoint.sh"       | 1 minute ago  | Up 2 hours | 443/tcp, 0.0.0.0:15432->80/tcp | pgadmin  |
| 0e4fbee2549d | postgres:alpine | "docker-entrypoint.s…" | 4 minutes ago | Up 2 hours | 0.0.0.0:5440->5432/tcp         | postgres |
  
Usando um navegador acesse [http://localhost:15432](http://localhost:15432 ), repare que a porta é a mesma exposta na criação do contêiner pgadmin (**15432**).
  
<img src="https://res.cloudinary.com/dmzu6cgre/image/upload/v1599955688/public/posts/postgres_docker/pgadmin_home_zlmwe0.webp" alt="Página de login do pgAdmin4" width="384px"/>
  
Adicione o email e senha passados como variáveis de ambiente na criação do contêiner e clique em "login".
  
- email: example@example.com
- senha: pgadmin1234
  
Você será redirecionado para outra página, como esta:
  
<img src="https://res.cloudinary.com/dmzu6cgre/image/upload/v1599956024/public/posts/postgres_docker/home_page_jkqkvx.webp" alt="Página inicial pgadmin" width="384px"/>
  
Porém, ainda não há nenhum servidor de banco de dados configurado. Para adicionar um novo, clique com o botão direito em cima do "Servers" no canto superior esquerdo, vá em "Create" e em seguinda em "Server..."
  
<img src="https://res.cloudinary.com/dmzu6cgre/image/upload/v1599956015/public/posts/postgres_docker/criar_server_zpdjjz.webp" alt="Criando um novo servidor" width="384px"/>
  
Um modal será aberto onde você irá inserir informações sobre o servidor. No campo "name" adicione um nome de sua preferência e clique na aba "Connection".
  
<img src="https://res.cloudinary.com/dmzu6cgre/image/upload/v1599958047/public/posts/postgres_docker/modal_step_1_vek8lp.webp" alt="Modal passo 1" width="384px"/>
  
  
Nesta dela siga os passos:
  
- No campo "Host name/address", adicione o nome do contêiner "**postgres**" (nome do contêiner Postgres).
- No campo "Port" utilize a porta interna do contêiner "**5432**".
- Deixe o campo "Maintenance database" como está.
- No campo "Username" adicione "**postgres**".
- No campo "Password" adicione a senha usada como variável de ambiente na criação do contêiner do PostgreSQL ("**postgresql**").
  
Ao final, clique no botão "Save".
  
<img src="https://res.cloudinary.com/dmzu6cgre/image/upload/v1599958290/public/posts/postgres_docker/modal_step_2_vxf4gs.webp" alt="Modal passo 2" width="384px"/>
  
Verá que o servidor foi criado e agora você tem acesso ao banco de dados do mesmo modo se não estivesse usando docker.
  
<img src="https://res.cloudinary.com/dmzu6cgre/image/upload/v1599958693/public/posts/postgres_docker/servidor_criado_tb8pxh.webp" alt="Servidor Criado" width="384px"/>
  
Lembre-se que para acesso interno o host do banco de dados é o nome do contêiner (**postgres**) e a porta é a interna (**5432**). Já para acesso externo, como em uma aplicação feita em NodeJS, o host é "**localhost**" ou "**127.0.0.1**" e a porta é a porta externa especificada na criação do contêiner (**5440**).
  
#  :clock8: Criar arquivo YAML
  
  
Todo o passo de criação dos contêineres pode ser feito utilizando um arquivo que por convenção/padrão é chamado `docker-compose.yml` e utiliza o Docker Compose como seu gerenciador, além disso adiciona o conceito de **stack**.
  
Stack nada mais é que um conjunto de contêineres que tem uma relação entre si, você pode criar quantas stacks quiser. Lembrando que sempre que criar um arquivo **docker-compose.yml**, a stack será o nome da pasta em que o arquivo está. Ao criar uma network dentro de uma stack o nome desta network terá como prefixo o nome da stack (exemplo logo abaixo).
  
Portanto, para começar, crie um arquivo com o nome **docker-compose.yml** dentro de uma pasta nomeada **postgres**, sendo assim a stack será **postgres**.
  
Em seguida, abra o arquivo em um editor de texto de sua preferência e comece adicionando o básico:
  
```yaml
version: "3"
  
networks:
  network:
    driver: bridge
  
volumes:
  postgres-data:
    external: true
```
  
Em que há:
  
- **version** - versão do Compose.
- **networks** - networks a serem criadas dentro da stack.
  - **network** - esta network, como dita antes, será nomeada **postgres-network**, pois será adicionado o prefixo da stack.
- **volumes** - volumes (armazenamentos) a serem criadas dentro da stack.
  
Rearraje o script do contêiner **postgres** ao arquivo:
  
```yaml
version: "3"
  
services:
  postgres-compose:
    image: postgres:12.4-alpine
    container_name: postgres
    environment:
      POSTGRES_PASSWORD: "postgresql"
    ports:
      - "5440:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - network
  
networks:
  network:
    driver: bridge
  
volumes:
  postgres-data:
    external: true
```
  
Como feito antes utilizando o script de uma única linha, será criado um contêiner com o nome "**postgres**", senha "**postgresql**", porta "**5440:5432**", network "**postgres-network**" (lembre do prefixo), e volume.
  
Agora é a vez de reescrever script do contêiner do **pgadmin**:
  
```yaml
version: "3"
  
services:
  postgres-compose:
    image: postgres:12.4-alpine
    container_name: postgres
    environment:
      POSTGRES_PASSWORD: "postgresql"
    ports:
      - "5440:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - network
  
  pgadmin-compose:
    image: dpage/pgadmin4
    container_name: pgadmin
    environment:
      PGADMIN_DEFAULT_EMAIL: "example@example"
      PGADMIN_DEFAULT_PASSWORD: "pgadmin1234"
    ports:
      - "15432:80"
    depends_on:
      - postgres-compose
    networks:
      - network
  
networks:
  network:
    driver: bridge
  
volumes:
  postgres-data:
    external: true
```
  
Também, como no contêiner **postgres**, está tudo bem parecido com escrito anteriormente utilizando uma única linha. O nome, email, senha, porta e network continuam o mesmo, porém também foi adicionado um atributo novo, **depends_on**, este permite que o contêiner **pgadmin** não seja criado/iniciado antes do contêiner **postgres** ser criado/iniciado.
  
Porém, antes de executar o arquivo será necessário deletar os contêineres já existente afinal não serão mais utilizados como anteriormente, agora eles pertecerão à uma stack. Primeiro pare os dois contêineres `docker stop postgres pgadmin` em seguida os remova `docker rm postgres pgadmin`.
  
Agora, rode o comando para executar o arquivo **docker-compose.yml** utilizando o Docker Compose:
  
```sh
docker-compose up -d
```
  
Este comando faz com que os contêineres configurados no arquivo sejam criandos, caso ainda não exista, e os execute. Ainda, quando algo for alterado no arquivo os contêineres afetados serão reconstruidos.
  
Em algum momento, caso você queira parar todos os contêineres do arquivo, utilize o comando:
  
```sh
docker-compose down
```
  
E para remover:
  
```sh
docker-compose rm
```
  
---
  
Pessoalmente, gosto de guardar arquivos, scripts e instaladores, pois, estou sempre testando algo novo e algumas vezes isso resulta em ter que formatar o computador. Portanto, já ter um arquivo/script de instalação econimiza bastante tempo, afinal não é preciso ter que ir na internet buscar como configurar ou ter que reconfigurar na mão.
  
> O preguiçoso sempre arruma um jeito de não ter trabalho.
  
Por fim, agradeço você por ter chegado até aqui, deixe seu comentário e sugestão para os próximos posts, e caso este post tenha te ajudado deixe seu like. :thumbsup:
  
#  :link: Links
  
  
Site pessoal: [baraus.dev](https://baraus.dev )
GitHub: [@devbaraus](https://github.com/devbaraus )
Instagram: [@devbaraus](https://instagram.com/devbaraus )
  
Tutoriais:
  
- [Como criar um servidor NodeJS - Parte 1](https://dev.to/devbaraus/criando-um-servidor-em-nodejs-parte-1-c7f )
- [Como criar um servidor em NodeJS - Parte 2](https://dev.to/devbaraus/criando-um-servidor-em-nodejs-parte-2-6g7 )