Publicado em
Na página https://docs.gitlab.com/omnibus/docker/README.html tem várias informações a cerca
das imagens Docker oficiais do GitLab (ferramenta web para versionamento usando o Git).
Neste tutorial, o Gitlab será executado usando um único conteiner para fornecer todo o ambiente
necessário para executá-lo, o que deixa a instalação bem simples e rapidamente você tem o serviço
funcionando.
O GitLab requer um host com, pelo menos, 4 GB de memória RAM. A lista completa de
requisitos de hardware e software está na
página: https://docs.gitlab.com/ce/install/requirements.html#hardware-requirements.
Se você não sabe o que é Docker, recomendo começar lendo os links desta página. É um longo
caminho, mas vale a pena conhecer essa tecnologia.
http://blog.aeciopires.com/palestra-transportando-as-aplicacoes-entre-varios-ambientes-com-
docker/
http://blog.aeciopires.com/primeiros-passos-com-docker/
Abaixo tem o tutorial customizado para iniciar o conteiner GitLab persistindo os dados.
1) Crie o diretório de dados, log e configuração do GitLab.
mkdir -p /opt/docker/gitlab/config
mkdir -p /opt/docker/gitlab/logs
mkdir -p /opt/docker/gitlab/data
Altere o nome gitlab.example.com pelo nome DNS do conteiner adequado para a sua rede.
4) O log pode ser visualizado com os comandos abaixo.
docker logs gitlab
O serviço pode demorar até 5 minutos para ser iniciado e configurado da primeira vez. Ele estará
pronto para ser acessado quando aparecer a mensagem abaixo no log.
Thank you for using GitLab Docker Image!
5) Acesse o GitLab na URL http://IP-Servidor (com HTTP). Será solicitado que crie uma senha
com no mínimo 8 caracteres para o usuário root. Depois disso é só acessar com a conta root e a
senha recém criada.
6) Se quiser parar o conteiner, é só executar o comando abaixo.
docker stop gitlab
2) OPCIONAL: Para usar o grafana com HTTPS, crie o diretório abaixo e em seguida copie a chave
pública (EX.: certificado.crt) e privada (EX.: certificado.key) do certificado a ser usado.
mkdir -p /docker/grafana/certs
chown -R 472:472 /docker/grafana/certs
A permissão dos diretórios foram ajustadas para o usuário e grupo 472, porque esse é o UID e GID
do usuário do Grafana executado dentro do conteiner e que precisa ter permissão nos diretórios que
receberão os dados a serem persistidos fora do conteiner.
3) OPCIONAL: Inicie o conteiner docker do Grafana com suporte a HTTPS.
docker run -d --name=grafana \
--restart always \
-p 3000:3000 \
-e "GF_INSTALL_PLUGINS=grafana-clock-panel,briangann-gauge-
panel,alexanderzobnin-zabbix-app" \
-e "GF_SERVER_PROTOCOL=https" \
-e "GF_SERVER_HTTP_PORT=3000" \
-e "GF_SERVER_CERT_FILE=/etc/certs/server.crt" \
-e "GF_SERVER_CERT_KEY=/etc/certs/server.key" \
-v /docker/grafana/data:/var/lib/grafana \
-v /docker/grafana/certs/certificado.crt:/etc/certs/server.crt \
-v /docker/grafana/certs/certificado.key:/etc/certs/server.key \
grafana/grafana
Lembre-se de alterar o nome certificado.key e certificador.crt pelo nomes dos arquivos dos arquivos
de certificado que você copiou no passo 2.
Ou, inicie o conteiner docker do Grafana com HTTP.
docker run -d --name=grafana \
--restart always \
-p 3000:3000 \
-e "GF_INSTALL_PLUGINS=grafana-clock-panel,briangann-gauge-
panel,alexanderzobnin-zabbix-app" \
-e "GF_SERVER_PROTOCOL=http" \
-e "GF_SERVER_HTTP_PORT=3000" \
-v /docker/grafana/data:/var/lib/grafana \
grafana/grafana
Aviso inicial
Todo o código e estrutura que vou mostrar aqui está nesse repositório do GitHub.
Agora vamos supor que a tabela que criamos não precise de uma coluna idade, então criamos a
migração 002_alter_table_up.sql para nos livrarmos dela:
ALTER TABLE clientes DROP COLUMN idade;
Além de não precisar da coluna nós vamos querer uma outra coluna chamada de endereco
também com tamanho máximo de 250 caracteres, e foi o que fizemos na migração
003_alter_table_up.sql:
ALTER TABLE clientes ADD COLUMN endereco VARCHAR(250) NOT NULL;
E agora que temos uma estrutura pronta para armazenar dados vamos fazer umas inserções nessa
linda tabela com a migração 004_data_migration_up.sql, essa migração cria três registros
na tabela clientes fazendo INSERTS:
INSERT INTO clientes (nome, endereco) VALUES ('Umbrella Corporation', '545 S
Birdneck RD STE 202B Virginia Beach, VA 23451');
INSERT INTO clientes (nome, endereco) VALUES ('OCP Omni Consumer Products',
'Delta City (formerly Detroit)');
INSERT INTO clientes (nome, endereco) VALUES ('Weyland-Yutani Corporation',
'Weyland-Yutani Corporation HQ, Tokyo');
Note que os nomes de todas as migrações terminam em up, isso por que, cada uma das migrações
que você viu aqui, tem uma migração irmã que desfaz o que essa migração fez. Isso é uma boa
prática de software ;)
Mas agora que nós temos essas migrações como aplicá-las ao nosso banco?
Primeiro vamos copiar as migrações pro lugar certo e depois “rodá-las” dentro do container:
sudo cp migrations/*up.sql data/postgres/
docker-compose exec tad psql -U postgres -d tadpgweb -1 -f
/var/lib/postgresql/data/001_create_table_up.sql
docker-compose exec tad psql -U postgres -d tadpgweb -1 -f
/var/lib/postgresql/data/002_alter_table_up.sql
docker-compose exec tad psql -U postgres -d tadpgweb -1 -f
/var/lib/postgresql/data/003_alter_table_up.sql
docker-compose exec tad psql -U postgres -d tadpgweb -1 -f
/var/lib/postgresql/data/004_data_migration_up.sql
A sintaxe SQL não é muito misteriosa, mas se você estiver um pouco enferrujada(o) existem vários
materiais disponíveis por aí, dentre eles o material em português do Criar Web, o curso de SQL do
codecademy e o tutorial de SQL do GeekForGeeks ambos em inglês.
O Servidor
Vou imaginar que você não tem um servidor, não tem uma conta na AWS, nem DigitalOcean, nem
Linode, nada… Você pode criar uma conta em um deles, lançar uma máquina com as configurações
que quiser (Na AWS é tudo mais complicado pra quem não está acostumado, se for sua primeira
vez, prefira outro).
Pra esse tutorial estou falando de Ubuntu 16.04, que é o servidor que você mais vai ver por aí nesse
momento nesses serviços. Você pode escolher um Debian qualquer também.
Configuração inicial
Configure o timezone do servidor
sudo locale-gen --no-purge --lang pt_BR
sudo dpkg-reconfigure tzdata
Atualize os pacotes:
sudo apt-get update
sudo apt-get -y upgrade
Crie seu VirtualEnv apontando qual python aquele virtualenv irá usar
mkvirtualenv nome_venv --python=python3
O VirtualEnvWrapper é muito fácil, para entrar em um Virtualenv que você criou, você pode usar:
workon nome_venv
Depois entre no seu github, em Settings > SSH and GPG Keys. Clique em New SSH Key, dê um
nome pra essa chave, como (“chaves do servidor teste”) e em Key cole o conteúdo da chave pública
id_rsa.pub
Agora faça as alterações que forem necessárias para o deploy django, como criar um arquivo de
settings_local, banco de dados, ou qualquer outra coisa específica do seu projeto.
Depois de tudo certo, você ainda precisa rodar as migrações e gerar os arquivos estáticos (se estiver
usando)
python manage.py migrate
python manage.py collectstatic
Configurando o NGINX
Nginx, assim como o Apache, tem um mundo inteiro só deles, nesse momento você precisa
conhecer o básico.
Existe um diretório /etc/nginx/sites-available/ onde ficão os arquivos de configuração de sites
disponíveis para o nginx servir e existe o diretório /etc/nginx/sites-enabled/ que é a mesmíssima
coisa, mas o que estiver aqui o Nginx estará servindo mesmo.
Normalmente você cria o arquivo de configuração no sites-available/ e cria um link simbólico para
o sites-enabled/
Nós vamos fazer isso. Primeiramente, vou excluir o site default do nginx
sudo rm /etc/nginx/sites-enabled/default
Agora crie o arquivo de configuração para o seu site. (Se você não está acostumado com o VIM, use
troque vi por nano)
sudo vi /etc/nginx/sites-available/meusite
server_name nome-site.com.br;
location / {
proxy_pass http://127.0.0.1:8000;
#As proximas linhas passam o IP real para o gunicorn nao achar que sao acessos
locais
proxy_pass_header Server;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
location /static {
alias /home/usuario/caminho_projeto/static/;
Reinicie o Nginx:
sudo service nginx restart
(Se você configurou tudo direitinho até aqui, ao acessar o site você verá uma página com um erro
502 Bad Gateway do próprio nginx)
Isso acontece porque ainda não tem nada aqui http://127.0.0.1:8000
Vamos colocar o site pra rodar nessa porta pelo gunicorn
Configurando o Gunicorn
Alguém vivo até essa parte? Cansa não, falta pouco.
No seu virtualenv (lembra workon nome_env?) instale o gunicorn
pip install gunicorn
Agora se você rodar o gunicorn, você vai ver seu site rodando:
/home/usuario/Envs/nome_venv/bin/gunicorn projeto.wsgi:application -c
gunicorn_conf
Mas o que você pretende fazer? Rodar esse comando dentro de um screen e ir embora? Não dá né!
Então, você vai usar o Supervisor pra controlar o funcionamento do gunicorn.
Configurando o Supervisor
Crie o seguinte arquivo de configuração
sudo vi /etc/supervisor/conf.d/gunicorn.conf
Depois é só avisar o supervisor que existe um novo processo que ele precisa controlar da seguinte
forma:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart gunicorn
Conclusão
Existem muito mais coisas envolvidas no processo de um deploy. Você precisa configurar um
firewall, provavelmente precisará servir mais pastas estáticas, etc, etc, etc… Mas precisa começar
por algum lugar.
Não acredito que fiz um post inteiro sem colocar nenhum gif no meio, então só pra terminar,
PRESTE ATENÇÃO em TODOS os caminhos que eu coloquei acima, você vai ter que usar os
seus próprios paths corretamente
Criando um container Docker para um projeto
Django Existente
Neste post, vou mostrar como criar um container Docker para um projeto Django já existente.
Como exemplo, resolvi buscar por uma issue aberta no github que estivesse pedindo para ser
dockerizada. Criei um PR para a Issue e usei como exemplo aqui.
Por quê você vai querer dockerizar uma aplicação web django que já existe? Bom, existem muitas
razões, se você acha que não tem uma, faça pela diversão!
Eu decidi usar o docker em uma das minhas aplicações porque ela estava ficando muito difícil de
instalar. Muitos requisitos do sistema, vários bancos de dados, celery, rabbitmq e por aí vai. Sem
dockerizar cada vez que uma nova pessoa entra no time é um inferno pra setar tudo porque levava
tempo demais.
O mundo ideal é que o programador tenha em seu ambiente de desenvolvimento o mais próximo
que puder do ambiente de produção. Se você usa SQLite na sua máquina mas Postgres no servidor
pode ser que tenha problemas de dados que são simplesmente truncados localmente mas que vão
levantar erros na base de produção. Só para ter ideia de um exemplo.
Se você não sabe o que é o docker, imagine que é um imenso virtualenv que no lugar de ter apenas
pacotes python tem o sistema operacional “inteiro”. Isso consegue isolar seu app de tudo que está
no seu SO, bancos de dados, workers, etc.
Mão na massa
Ok, falar é fácil, vamos codar um pouco.
Primeiro de tudo, instale o Docker. Fiz isso no Ubuntu e no Mac sem nenhum problema. Já no
Windows Home não consegui fazer funcionar.
Para dizer ao docker que sua aplicação é um container você precisa criar um arquivo Dockerfile:
1 FROM python:3.6
2 ENV PYTHONUNBUFFERED 1
3 RUN mkdir /webapps
4 WORKDIR /webapps
5 # Installing OS Dependencies
6 RUN apt-get update && apt-get upgrade -y && apt-get install -y \
7 libsqlite3-dev
8 RUN pip install -U pip setuptools
9 COPY requirements.txt /webapps/
10 COPY requirements-opt.txt /webapps/
11 RUN pip install -r /webapps/requirements.txt
12 RUN pip install -r /webapps/requirements-opt.txt
13 ADD . /webapps/
14 # Django service
15 EXPOSE 8000
Docker Images
FROM python:3.6
Aqui estamos usando uma imagem do docker hub. Isto, é um container pré-formatado do docker
que permite que você monte sua máquina a partir daquela configuração inicial. Nesse caso, Python
3.6 é um container de um Ubuntu que tem o Python 3.6 instalado nele. Você pode procurar por
containers no docker hub.
Por exemplo, se você usa variáveis de ambiente para guardar sua secret key do Django é só fazer
assim:
ENV DJANGO_SECRET_KEY abcde0s&&$uyc)hf_3rv@!a95nasd22e-dxt^9k^7!f+$jxkk+$k-
Run Commands
Docker Run Commands tem um nome meio óbvio. Você pode rodar um comando “dentro” do seu
container. Estou colocando dentro entre aspas porque o docker na verdade cria algo como sub
containers para que não precise rodar os mesmos comandos novamente no caso de precisar dar um
rebuild do container.
RUN mkdir /webapps
WORKDIR /webapps
# Installing OS Dependencies
RUN apt-get update && apt-get upgrade -y && apt-get install -y \
libsqlite3-dev
ADD . /webapps/
COPY e ADD
Copy e ADD são basicamente a mesma cosia. Ambos copiam um arquivo do seu computador (o
Host) dentro do container (o Guest). No meu exemplo, estou apenas copiando o requirements para
dentro do docker, para que eu possa dar pip install nos pacotes.
EXPOSE
Expose é para mapear uma porta do Guest (o Container) para o Host (seu computador)
# Django service
EXPOSE 8000
Ok, e agora? Como podemos adicionar mais containers para rodá-los juntos? E se precisarmos
colocar um postgresql para rodar em um container também? Não se preocupe, vamos usar o docker-
compose.
Docker-Compose
O compose é uma ferramenta para rodar múltiplos containers do docker. Só precisa criar um
arquivo yml na pasta do seu projeto com o nome docker-compose.yml:
version: '3.3'
services:
# Postgres
db:
image: postgres
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=postgres
web:
build: .
command: ["./run_web.sh"]
volumes:
- .:/webapps
ports:
- "8000:8000"
links:
- db
depends_on:
- db
Aqui estou usando uma imagem do Postgres que também peguei no Docker Hub.
Agora vamos mudar o settings.py para poder usar o Postgres do container como banco de dados.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': 'postgres',
'HOST': 'db',
'PORT': '5432',
}
}
VOLUMES
Lembra do vagrant?
Era uma vez o Vagrant. Ele era uma forma de rodar um projeto dentro de uma Máquina Virtual que
permitia configurar e mapear portas fácilmente, provisionar requisitos do sistema e compartilhar
volumes. Seu computador (o Host) podia compartilhar um volume com a máquina virtual (o Guest,
ou convidado). No docker, o volume é exatamente a mesma coisa. Quando você escreve um arquivo
em um volume que está compartilhado o arquivo também está sendo escrito dentro do container.
volumes:
- .:/webapps
Nesse o caso, o diretório em que nos encontramos (.) é o que está sendo compartilhado com o
container.
LINKS
links:
- db
Você pode se referir a outro container que pertence ao seu arquivo docker-compose utilizando o
nome dele. Como criamos um container com o nome db para o Postgres nós podemos criar um link
para ele no nosso container chamado web. Pode ver que no settings.py nós colocamos ‘db‘ como
host.
DEPENDS_ON
Para rodar sua aplicação, seu banco de dados precisa estar pronto antes do container web, senão vai
dar algum pau!
depends_on:
- db
Command
Command é o comando padrão que o docker vai rodar logo depois que você subir, ou seja, colocar
os containeres para funcionar.
No nosso exemplo, eu criei um run_web.sh que vai rodar as migrações, coletar o static files e
iniciar o servidor de desenvolvimento.
#!/usr/bin/env bash
cd django-boards/
python manage.py migrate
python manage.py collectstatic --noinput
python manage.py runserver 0.0.0.0:8000
Alguém pode argumentar que migrar assim automaticamente toda vez que subir o container pode
não ser uma boa prática. Eu concordo. Você pode (e deve) rodar o migrate direto na máquina
web. Você pode acessar seu container para rodar comandos assim (como no bom e velho vagrant
ssh):
docker-compose exec web bash
Se você quiser , você pode rodar o comando sem acessar o container mesmo, apenas mudando o
último argumento do comando acima:
docker-compose exec web python manage.py migrate
Rodando o Docker
Com nosso Dockerfile, docker-compose.yml e o run_web.sh no lugar, vamos rodar tudo junto:
docker-compose up
**EDITADO**
Antes eu estava usando run no lugar de exec. Mas o Bruno FS me mostrou que o exec é melhor
pois está acessando exatamente o container que já está rodando e que o run na verdade está criando
um novo container.