Categorias
Tecnologia

Aumentando espaço no Macbook Air 2012-2017

Quem utiliza o Macbook Air sabe como é (ou melhor, era) um bom custo-benefício para desenvolvimento e trabalho de escritório. A CPU é razoável, a bateria é excelente, a quantidade de memória é aceitável e o armazenamento é rápido.

Seria perfeito se pudéssemos melhorar a memória RAM e o armazenamento com o tempo. Infelizmente, só o segundo é possível. E não é barato.

Depois de pesquisar por muito tempo e a firma patrocinar o upgrade, estou redigindo isso após uma bem sucedida cirurgia de substituição do SSD PCI-e de 120GB por um de 480GB e o dobro da taxa de escrita/leitura.

A receita é simples:

Depois de ter o pendrive bootável, faça a troca do SSD (não tem mistério, só desparafusar, desencaixar, colocar o novo, parafusar novamente), insira o pendrive na porta USB e ligue o Macbook Air.

Siga os passos da instalação e pronto.

Categorias
CakePHP

Dica Rápida: Migrando Aplicação CakePHP 3.x para 4.x

Embora tenha havido um grande esforço em manter o máximo de compatibilidade entre as duas recentes versões do framework, ao migrar uma aplicação de complexidade razoável encontrei várias dificuldades não tão bem documentadas.

Pra me poupar passar por esses problemas no futuro, e talvez te poupar de dor de cabeça também, aqui vai um resumo do que observar:

  • Rotas e links: revisite todas as suas rotas e criação de links/redirects. Nomes de prefixos, plugins e controladores devem ser escritos como são, sem alteração para caixa baixa (lowercase) e underscore. O que antes seria my_products agora deve ser MyProducts, como no nome da classe MyProductsController. No caso dos links, se antes ao usar o UrlHelper não havia problema de uma rota não ter sido mapeada, agora você terá uma exceção.
  • Plugins: não defina ou carregue suas rotas no bootstrap.php do plugin, isso fará com que a aplicação tenha qualquer outra rota ignorada.
  • Mocks: com a nova versão, veio também o uso do phpunit 8.5, que por sua vez, introduz algumas quebras de compatibilidade. Isso afeta em especial a criação de mocks em modelos ligados a behaviors (se você tentar usar o mock em um método do behavior). No pull-request que abri para o futuro CakePHP 4.1, é resolvida o problema de warnings gerados, porém, em alguns testes meus, é obrigatório incluir algum método concreto da Table/Model que está sendo mockada junto com o método de behavior na lista de métodos no mock – caso contrário, o warning volta a aparecer (esse problema acontecia por uma falha no pull-request que criei, mas fiz um segundo com uma implementação melhor e não há mais necessidade disso).
  • Tests: se em algum teste você faz um $this->loadPlugin('MyPlugin'); no seu setUp(), se esse plugin possuir rotas, as rotas da sua aplicação não serão carregadas. Houve uma mudança na verificação de inicialização das rotas e necessidade de carregamento – antes, havia uma variável de controle que só era definida após carregar as rotas da aplicação, agora, só é checado se já existe alguma rota declarada, e se houver, supõe-se que todo procedimento já foi realizado com sucesso.

Destes problemas, o mais trabalhoso de corrigir é o com as rotas/links – os outros três a dificuldade foi realmente entender o que estava acontecendo.

Categorias
Ócio

Coisas de Quarentena

Como todos sabem, estamos no meio de uma pandemia. Todos os países com algum governante sério tem enfrentado essa emergência sanitária incentivando, ou até mesmo, impondo quarentena entre sua população. Infelizmente, não é esse o nosso caso aqui no Brasil, onde o líder máximo da nação boicota diariamente qualquer tentativa de barrar o avanço do novo coronavírus.

Felizmente, não dependemos só do chefe do executivo nacional para tentar conter a pandemia, então estados, municípios e a própria sociedade civil tem tido papel fundamental no incentivo e viabilização da quarentena, daqueles que podem faze-la.

Tenho o privilégio de trabalhar com TI, numa função que me permite trabalhar 100% de casa, e que, por uma peça do destino, tinha sido exatamente essa a forma que passei a trabalhar 20 dias antes da chegada do coronavírus no país.

O que esse isolamento acabou mudando na minha rotina que já era de trabalhar em casa? Não posso mais caminhar na orla da praia (pois é, e me mudei pro litoral pra isso); não posso sair para almoçar ou jantar fora, nem fazer passeios pela cidade (que nem tive tempo de conhecer direito). Sobra, pra ocupar o tempo e manter a cabeça boa, aprender coisas novas, mas cuidando sempre pra não ficar o tempo todo pró-trabalho.

Nesse meio tempo, procurei materiais e cursos sobre ciência de dados, aprendi a fazer pão, fiz exercícios de estatística para desenferrujar a cabeça, joguei xadrez contra meu celular (não fazia isso há uns 10 anos), li 3 livros (também não fazia isso há muito tempo), cuido de um quintal algumas vezes maior do que tinha anteriormente e passei mais tempo com minha família (esposa e 3 cachorros). Tudo isso, alternando com o horário de expediente.

Se o animo não acabar, quero fazer uma série de posts exatamente sobre cada uma dessas ocupações, tentando compartilhar o que me motivou e o que aprendi ou tirei de proveito.

Categorias
DevOps

Gitlab 12.1 e Dind 19.03: Resolvendo problema com conexão TLS entre CI e Docker Registry

Até a versão 12, o Gitlab CI conectava ao docker registry próprio usando conexão não segura (sem TLS) por padrão.

Com a versão 19.03 do Dind (Docker-in-Docker), a conexão com o repositório passou a utilizar TLS por padrão. Do nada seus jobs começarão a falhar com alguma das mensagens abaixo:

Error response from daemon: Client sent an HTTP request to an HTTPS server.
<code>time="2019-08-06T19:24:46Z" level=error msg="failed to dial gRPC: cannot connect to the Docker daemon. Is 'docker daemon' running on this host?: dial tcp <some address>:2375: connect: connection refused"</code>
Cannot connect to the Docker daemon at tcp://docker:2375/. Is the docker daemon running?

Para resolver isso, podemos desabilitar explicitamente o TLS (o que não é recomendado e não entrarei em detalhes) ou configurar o nosso executor (runner) para utilizar TLS e é isso que faremos.

[[runners]]
  name = "nome-do-seu-runner"
  url = "https://<seu dominio do gitlab>"
  token = "<SEU TOKEN>"
  executor = "docker"
  environment = ["DOCKER_HOST=tcp://docker:2376", "DOCKER_DRIVER=overlay2", "DOCKER_TLS_VERIFY=1", "DOCKER_CERT_PATH=/certs/${CI_JOB_ID}/client", "DOCKER_TLS_CERTDIR=/certs/${CI_JOB_ID}"]
  [runners.docker]
    pull_policy = "if-not-present"
    privileged = true
    disable_cache = false
    volumes = ["/tmp/gitlab/docker/certs:/certs", "/cache"]

Explicando:

  • DOCKER_HOST=tcp://docker:2376 : define o host interno do daemon e a porta no qual está conectado. A porta não-TLS (antiga padrão) é 2375. É ela que precisa ser alterada para 2376.
  • DOCKER_TLS_VERIFY=1 : define que o daemon deve verificar o certificado utilizado na comunicação.
  • DOCKER_TLS_CERTDIR=/certs/${CI_JOB_ID} : aqui definimos onde o serviço do docker irá gerar seus certificados. É um absoluto interno do container. Utilizamos a variável ${CI_JOB_ID} para que não haja conflito com execuções paralelas.
  • DOCKER_CERT_PATH=/certs/${CI_JOB_ID}/client : define o caminho onde o serviço deve procurar o certificado de cliente para conexão TLS. É um subdiretório gerado pela configuração anterior.
  • volumes = [“/tmp/gitlab/docker/certs:/certs”, “/cache”] : definimos um diretório no nosso host (máquina que possui o gitlab-runner instalado) que será compartilhado com os containers em execução para armazenar os certificados gerados.

Outra configuração importante que deve ser removida (e por isso não consta no meu exemplo) é a tls_verify = false .

E é isso, agora só reiniciar seu runner por garantia – embora ele já atualize as configurações automaticamente: sudo systemctl restart gitlab-runner

Referências:

Categorias
Containers

Usando valores de ARG e ENV na construção de imagens Docker

Esse é só um lembrete pro meu eu do futuro.

Para que uma variável definida no seu docker-compose.yml como args esteja disponível para seu Dockerfile e respectivos comandos (RUN/ENV), ele PRECISA ser enunciado no Dockerfile com o comando ARG.

No caso de enunciar antes da expressão FROM, você PRECISA repetir o enunciado após o mesmo – porque a imagem que você está estendendo pode ter removido/limpado as variáveis anteriores.
Segue um exemplo:

ARG PHP_VERSION
FROM phusion/baseimage:latest

ARG PHP_VERSION
RUN apt-get install php${PHP_VERSION}-cli
Categorias
Containers DevOps

Usando Docker para testar versões diferentes do MySQL

Recentemente assumi a tarefa de avaliar e otimizar algumas tabelas e queries que apresentavam perfomance muito diferente no PostgreSQL e no MySQL (versão bem antiga).

Minha primeira suspeita foi justamente o fato do MySQL estar muito desatualizado. Para descartar ou confirmar essa suspeita, eu precisava rodar no mesmo hardware versões diferentes do MySQL – e com containers, isso é fácil e indolor.

A minha instância padrão (versão antiga) compartilha com o host (minha máquina) a porta padrão do MySQL, então, do próprio macOS eu consigo conectar nela (depois de estar em execução) com o comando:

mysql -h 127.0.0.1 -P 3306 -u root -p

Agora, para executar uma versão específica, primeiro precisamos iniciar o container:

docker run --name mysql-atual -e MYSQL_ROOT_PASSWORD=senha -p 3307:3306 -d mysql:latest

Isso executará a versão atual (latest) da imagem mysql, definindo a senha senha para o usuário root, mapeando a porta 3306 do container com a 3307 do host (meu macOS) em modo daemon, com o nome mysql-atual.

Pronto, temos duas versões diferentes no mesmo hardware para fazer comparações. Podemos conectar nesse novo container da mesma forma que o anterior, alterando apenas a porta:

mysql -h 127.0.0.1 -P 3307 -u root -p
Categorias
DevOps

Qual o saldo de usar Docker no lugar do Vagrant para desenvolvimento?

Nos dois últimos posts falei um pouco do porquê e como fiz para gerar as imagens docker. Agora gostaria de compartilhar um pouco das vantagens que tenho visto nessa abordagem em relação ao Vagrant + VirtualBox.

Reprodutibilidade

Com o passar do tempo, a tendência é que as dependências de nossos projetos mudem, algumas coisas são adicionadas, umas removidas e outras atualizadas. Em um ambiente tradicional, acabamos por seguir o caminho mais rápido no dia a dia para adequar o ambiente, geralmente fazendo essa adaptação manualmente.

Dessa forma, se em algum momento você precisar recriar seu ambiente de desenvolvimento, ele provavelmente não funcionará como você esperava.

Como os containers Dockers são criados a partir da imagem a cada vez que executamos, qualquer alteração feita durante uma execução não é persistida – excluindo disso, obviamente, os volumes persistentes. Cada dia de trabalho, cada vez que você inicia seus containers é como o primeiro dia – quase poético.

As atualizações precisam necessariamente ser definidas no Dockerfile ou script correspondente para atualizar a imagem e tudo fica registrado.

Performance

Esse foi o ponto que me fez pensar em uma alternativa ao setup antigo – a escrita em disco era sofrível pelo VirtualBox. Além disso, como uso o mesmo ambiente para trabalhar em diversos projetos, há diversos serviços em execução ao mesmo tempo: MySQL Server, Postgresql Server, Elasticsearch, Redis Server, PHP-FPM (duas versões), Nginx, dentre outros.

Ficar alternando entre serviços ativos e inativos para cada projeto seria tedioso, e o preço era um consumo alto de memória.

Com o Docker e o docker-compose, tenho aliases no terminal para iniciar os serviços (leia-se containers) para cada projeto. Se mais de um projeto usa o mesmo serviço, ele é iniciado uma única vez e compartilhado. A escrita em disco com o recente Docker for Mac é muito mais que aceitável, especialmente quando comparada com o Vagrant.

Conclusão

De inicio eu nem enxerguei a vantagem inerente do uso de containers, mas é por si só um grande benefício dessa abordagem. A questão da performance é mais perceptível em relação a escrita de dados – banco de dados, processamento batch e etc. O consumo de memória vai depender muito da quantidade de serviços/containers rodando simultaneamente.

Categorias
DevOps

Como fiz o Gitlab gerar minhas imagens do Laradock

Já comentei diversas vezes que sou um usuário adito do Gitlab – como dizem por aí, desde quando tudo aquilo ali era mato. E um dos recursos que foram adicionados ao longo dos anos e eu mais utilizo é o de CI. Com ele consigo definir processos automáticos para testes, builds e deploy (esse, infelizmente, fora da minha realidade por enquanto) que são executados em containers ou máquinas especificamente designadas.

Se você olhar o repositório do Laradock, encontrará um arquivo com nome .gitlab-ci.yml. Esse arquivo é o responsável por dizer ao CI do Gitlab o que fazer quando uma alteração no repositório é enviada – ou quando o CI é disparado manualmente. Infelizmente ele não funcionará – a versão ali definida para o docker, dind e docker-compose estão defasadas, além disso, se você, como eu, utilizar o container registry do próprio Gitlab, essa informação deve ser adicionada ao arquivo.

O .gitlab-ci.yml atual do laradock começa desta forma:

# image: docker:latest
# services:
#   - docker:dind
image: jonaskello/docker-and-compose:1.12.1-1.8.0
services:
  - docker:1.12.1-dind

before_script:
  - docker info
  - docker-compose version
  - cp env-example .env
  - sed -i -- "s/=false/=true/g" .env
  - cat .env
  - env | sort

build:5.6:php-fpm:
  variables:
    PHP_VERSION: "5.6"
  script:
- docker-compose build php-fpm

Precisamos corrigir então:

  • Atualizar a imagem usada por: jonaskello/docker-and-compose:17.03.0-1.18.0
  • Atualizar a versão do service para: docker:18-dind
  • Incluir a variável DOCKER_HOST com valor tcp://docker:2375/
  • Incluir junto ao before_script a autenticação no seu registry para que seja possível fazer o push da imagem ao fim da geração. No meu caso, a linha fica: docker login -u gitlab-ci-token –password $CI_JOB_TOKEN registry.meudominio.com.br

Além disso, dentro do arquivo docker-compose.yml é preciso definir o caminho de cada imagem que você quer enviar ao container registry.

Por exemplo, no caso da imagem workspace as configurações devem ficar:

php-fpm:
      image: registry.meudominio.com.br/php-fpm:${PHP_VERSION}"
      build:
        context: ./php-fpm

Voltando ao .gitlab-ci.yml, em cada imagem que iremos construir devemos incluir o comando para fazer o push da imagem, todo o bloco de construção de uma imagem deve ficar parecido com o abaixo:

build:7.2:php-fpm:
  variables:
    PHP_VERSION: "7.2"
  script:
    - docker login -u gitlab-ci-token --password $CI_JOB_TOKEN registry.meudominio.com.br
    - docker pull "registry.meudominio.com.br/php-fpm:${PHP_VERSION}" || true
    - docker-compose build php-fpm
    - docker login -u gitlab-ci-token --password $CI_JOB_TOKEN registry.meudominio.com.br
    - docker-compose push php-fpm

Talvez você esteja perguntando por que tem duas chamadas ao docker login no processo de construção. A resposta é bem simples: garantir que não haja problema com sessão expirada. No meu setup, algumas imagens levam um bom tempo para serem construídas (culpa dos sistemas monolíticos) e no inicio eu tinha problema de ao tentar fazer o push da imagem, receber como resposta um Unauthorized.

Recapitulando, nosso .gitlab-ci.yml deve ficar parecido com:

image: jonaskello/docker-and-compose:17.03.0-1.18.0
services:
  - docker:18-dind

variables:
  DOCKER_HOST: tcp://docker:2375/
  DOCKER_DRIVER: overlay2

before_script:
  - docker login -u gitlab-ci-token --password $CI_JOB_TOKEN registry.meudominio.com.br
  - docker info
  - docker-compose version
  - cp env-example .env
  - cat .env
  - env | sort

build:7.2:php-fpm:
  variables:
    PHP_VERSION: "7.2"
  script:
    - docker login -u gitlab-ci-token --password $CI_JOB_TOKEN registry.meudominio.com.br
    - docker pull "registry.meudominio.com.br/php-fpm:${PHP_VERSION}" || true
    - docker-compose build php-fpm
    - docker login -u gitlab-ci-token --password $CI_JOB_TOKEN registry.meudominio.com.br
    - docker-compose push php-fpm

Último detalhe importante: o runner que vai executar esse build precisa ser do tipo Docker e deve ser executado em modo privilegiado (root), o que é não-recomendado em ambientes compartilhados – use uma máquina exclusivamente para CI, minimizando os riscos dessa configuração.
Agora você só precisa fazer o envio do seu código para instância do Gitlab que deseja usar e aguardar o build.

Se tiver alguma dica de como melhorar o setup, ou alguma dificuldade para fazer seu setup funcionar, deixe um comentário.

Categorias
Desenvolvimento Web

Ambientes de desenvolvimento

Sempre que troco de computador me vejo pensando: será que hoje tem uma forma melhor (leia-se mais moderna) de configurar um ambiente de trabalho pra programador?

No final de 2018 recebi um notebook novo para ser utilizado no trabalho e foi nesse contexto que minha saga começou.

Desde que comecei a usar macOS como sistema principal, optei por deixar o sistema o mais leve/intocado possível, facilitando atualizações e não sobrecarregando a máquina quando não estou usando a trabalho. Pra isso, utilizei algumas formas de virtualização ao longo do tempo.

No começo, optei pelo VirtualBox, gerenciando o provisionamento e configuração com o Vagrant. Por um tempo foi bom, mas toda vez que ia iniciar um projeto novo, precisava configurar DNS, VirtualHost e várias configurações dentro da vm. E isso cansou.

Depois de quebrar a cabeça com o provisionamento algumas vezes, descobri o projeto Laravel Homestead, que resolvia a maioria dos meus problemas: nele é possível configurar novos projetos usando o arquivo de configuração (incluindo criar banco de dados, VirtualHost etc), sem precisar nem logar dentro da vm. Outra grande vantagem do Homestead é fornecer uma vm com diversas versões do PHP pré-configuradas, e usáveis simultaneamente – você pode ter diversos projetos, cada um com a versão do PHP.

Mas um fator ainda me incomodava: o sistema de arquivos do VirtualBox. É extremamente lento, tarefas que depende de disco intensivo (banco de dados e testes unitários, por exemplo) são bastante penalizados. A solução mais “simples” seria abrir a carteira e gastar alguns centenas de dólares em uma solução mais robusta de virtualização – como não tenho esses dólares disponível, nem cogitei a hipótese.

Foi aí que surgiu a oportunidade de me aprofundar nos estudos sobre Docker. Coincidiu com o lançamento do Docker for Mac, que eliminou a necessidade do VirtualBox para rodar os containers. Agora eu tinha a possibilidade de virtualizar o ambiente, com ferramentas nativas do macOS, e containers específicos para cada recurso / serviço necessário – evitando desperdício de RAM/CPU.

Enquanto eu experimentava a construção da imagem perfeita, descobri o Laradock, uma espécie de Homestead voltado para Docker. Com ele, você pode definir diversas dependências pré-configuradas usando um arquivo de ambiente (.env), criar suas imagens de acordo com suas necessidades e executar apenas aquilo que precisa.

Estou usando essa solução a 2 meses e tem me atendido muito bem.

Categorias
CakePHP PHP

CakePHP 3 e Elasticsearch

Tanto o CakePHP quanto o Elasticsearch fazem parte da minha vida a alguns anos. No começo foi um pouco traumático – era preciso fazer chamadas via REST sem nenhuma abstração, utilizando curl ou streams.

Hoje contamos com diversas camadas intermediárias para facilitar a integração entre ambos, como o cliente em baixo nível oficial e o cliente em alto nível (Elastica).

Há ainda um projeto de datasource oficial que, embora nunca tenha chego em um release final, já incorpora algumas funcionalidades importantes da integração para uso do Elasticsearch como um backend para os repositórios no CakePHP.

Este projeto se chama cakephp/elastic-search e nas últimas semanas tive a oportunidade de ajudar em uma grande refatoração para torna-lo compatível com o Elasticsearch > 6 – a versão anterior do projeto suportava versões até a 2.5.

Dentre as mudanças mais importantes estão:

  • Troca da entidade Type pela Index, seguindo a diretriz do ES de remover suporte e múltiplos tipos em um mesmo índice;
  • Cada Index passa a especificar seu nome e tipo, em um mapeamento 1×1, como é obrigatório no ES desde a versão 6.0;
  • Atualização da dependência Elastica, para versão corrente, permitindo uso de todos os recursos recentes tanto do ES quanto do client.

Caso tenham alguma dúvida sobre uso e não queiram utilizar o github (por conta da língua ou outro motivo), utilizem os comentários que tentarei responder o mais rápido possível.