Varnish Cache - Primeiros passos

Última atualização: 7/23/2013

O Varnish Cache é categorizado como um proxy HTTP reverso, que na prática opera como um cache entre a aplicação cliente e o servidor HTTP. Guardando em memória elementos estáticos de uma página que na prática o servidor HTTP não precisaria se preocupar em servir a cada acesso do cliente.

Para entender como o Varnish funciona e como ele "acelera" as páginas é importante entender um pouco sobre cache em navegadores. Ao acessar páginas na Internet seu navegador precisa carregar informações dinâmicas (por exemplo uma marcação HTML gerada por um script PHP) e estáticas (por exemplo imagens, arquivos Javascript e estilos CSS). Note que só faz sentido guardar informações estáticas em um cache, pois na teoria cada acesso uma página dinâmica é único, a página diferente a cada acesso e esta precisa ser processada pelo servidor HTTP todas as vezes. Enquanto páginas/imagens estáticas podem ser guardadas no cache e não precisam ser solicitadas todas as vezes.

Navegadores fazem cache, e proxies tradicionais podem guardar informações em cache também. Ambos costumam ficar na infraestrutura do cliente (quem solicita a página) e funcionam de modo geral para todas a páginas que o cliente acessa, evitando com que o cliente precise conectar no servidor HTTP para requisitar arquivos estáticos. Economizando tempo e conexão de rede. Enquanto um proxy reverso fica na mesma infraestrutura de quem serve o conteúdo. Este armazena em cache apenas as informações servidas por este servidor e tem como foco economizar processamento do servidor HTTP real.

Servir páginas estáticas é um trabalho muito simples para qualquer servidor HTTP, mas o Varnish consegue auxiliar o servidor HTTP com um consumo menor de recursos. Um servidor como o Apache precisa manter em memória vários módulos, configurações e processos de controle, e precisa acessar o disco rígido para conferir se houve mudanças no arquivo estático. Enquanto o Varnish, por ser um proxy reverso especializado, consiste de um conjunto de processos leves otimizados para analisar, buscar e responder ao cliente com a maior velocidade possível, armazenando ainda os conteúdos estáticos em memória.

O Varnish ainda pode atuar como um load balancer, quando operado com mais de um backend. Pode filtrar requisições por IP de origem ou URL e encaminhar a um servidor HTTP específico. E você pode até montar sua própria CDN (Content Delivery Network). A grande diferença do Varnish é que ele é desenvolvido desde sua base para ser um proxy HTTP reverso, e por isso é muito otimizado a ponto dos desenvolvedores se preocuparem até com a quantidade de requisições realizadas para o sistema operacional. É uma ideia um pouco diferente do Nginx que realiza função inclusive de proxy IMAP/POP3 e servidor HTTP (sua função inicial).

Como funciona o Varnish Cache?

O servidor Varnish pode ser instalado no mesmo hardware que o servidor HTTP, porém não faz muito sentido este tipo de instalação para um ambiente de alto desempenho. Assim recomenda-se instalar o Varnish Cache em um servidor dedicado, onde o cache poderá de fato aliviar a carga no hardware do servidor HTTP.

Quando o usuário realiza uma requisição para a página, o cliente conecta ao proxy reverso na porta 80. E o Varnish então interpreta a requisição e verifica se o conteúdo requisitado pelo usuário está no cache em memória. Se estiver, o conteúdo é disponibilizado de imediato para o usuário. Caso contrário, o Varnish cria uma conexão com o servidor HTTP redirecionando a requisição.

Após a requisição, o Varnish confere se o conteúdo pode ser guardado no cache através dos cabeçalhos retornados pelo servidor HTTP. Se o servidor HTTP informar que o arquivo pode ser guardado por uma hora, o Varnish o manterá no cache por uma hora. Evitando que o servidor HTTP seja consultado por outras requisições de outros clientes a este mesmo arquivo.

Limitações de um proxy reverso

O proxy reverso possui algumas limitações e pode causar problemas em alguns casos. O cache de conteúdo HTTPS não é possível. Isto porque na teoria a encriptação é feita no servidor HTTP e decodificada no navegador, e o Varnish não pode ler a requisição do cliente no meio do caminho, seria uma falha no conceito do HTTPS.

O cache também pode causar alguns efeitos indesejados na página caso os desenvolvedores não estejam cientes da existência do proxy reverso. É muito comum que desenvolvedores ou clientes façam modificações na página, em arquivos Javascript/CSS ou imagens e estes arquivos não sejam retornados para o cliente mesmo este limpando o cache do navegador. Você precisará criar um meio para estes usuários limpem o cache ou respeitem o tempo de cache do Varnish.

Instalando o Varnish Cache

Para instalar a aplicação basta um apt-get install varnish como super usuário, em versões baseadas no Debian. Não há Varnish para Windows pois a aplicação foi projetada para alto desempenho em sistema operacional Linux. As instruções aqui correspondem ao pacote de versão 3.0 disponível no Debian 7. Em outras distribuições é possível que os arquivos sejam outros.

Após instalar o pacote do Varnish é necessário verificar o arquivo /etc/default/varnish. Garantindo que a opção START esteja com o valor yes. Neste mesmo arquivo você poderá observar vários exemplos de parametrizações. Normalmente a alternativa 2 oferece uma configuração baseada em memória. Ficando conforme o exemplo abaixo.

# Define que o serviço está ativo
START=yes
# Define principalmente qual é a porta que o Varnish deve usar
# e qual é a quantidade máxima de memória usada
DAEMON_OPTS="-a :80 \
             -T localhost:6082 \
             -S /etc/varnish/secret \
             -f /etc/varnish/default.vcl \
             -s malloc,256m"

Na linha 2 ativamos o serviço. Sem esta configuração o Varnish não irá iniciar pelo /etc/init.d/varnish start ou service varnish start. A porta que o servidor deverá ouvir está na linha 5. A linha 6 cria uma interface administrativa, assim é possível limpar o cache, reiniciar o serviço e também ler e monitorar o serviço pelas demais ferramentas por linha de comando. Para aumentar a segurança da porta administrativa, a linha 7 define o arquivo que contêm a chave de acesso.

O Varnish precisa de um arquivo .vcl indicando o que deve ser feito a cada recebimento de requisição. Este arquivo é indicado pela linha 8. Na linha 9 indicamos onde as informações armazenadas no cache são mantidas durante a operação do aplicativo. É possível definir um caminho de um arquivo físico, no nosso caso indicamos o uso da memória (uso da instrução malloc) e limitar o uso em 256Mb de memória RAM dedicada a isso.

Criando um arquivo .vcl

O arquivo .vcl permite além ler informações da requisição, alterar as informações de cada requisição/resposta, indicando assim como o Varnish deve processar cada uma delas e também passar dados adicionais ao servidor HTTP. O Varnish utiliza funções distintas para cada situação da requisição, desde o recebimento até a resposta para o cliente seja proveniente do cache ou do servidor backend.

Na image abaixo consta todo o fluxograma de processamento de requisição. As funções mais importantes e que normalmente são ajustadas no .vcl são:

  • vcl_recv - Que trata a requisição assim que é recebida;

  • vcl_fetch - Que trata a resposta do servidor HTTP antes do objeto ser retornado ao cliente ou gravado no cache;

  • vcl_error - Que define o que o Varnish deve fazer quando ocorrer um erro na requisição;

  • vcl_deliver - Que trata a resposta a ser enviada ao usuário após todo o processamento.

Fluxograma de processamento de requisição do Varnish (Fonte: Varnish-cache.org)

No exemplo é possível observar a declaração de duas funções, a responsável pelo recebimento da requisição e a de tratamento do retorno para o cliente. Note que a sintaxe é muito parecida a da linguagem C. E tem um motivo, na prática este arquivo é compilado e linkado ao binário original para garantir máxima performance.

# Aqui definimos o backend (servidor HTTP real)
backend default {  .host = "127.0.0.1";  .port = "8080"; }
# Criamos uma rotina adicional para o recebimento da requisição
sub vcl_recv {
	if (req.restarts == 0) {
		if (req.http.x-forwarded-for) {
			set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
		} else {
			set req.http.X-Forwarded-For = client.ip;
		}
	}
	if (req.request != "GET" && req.request != "HEAD") {
		return (pass);
	}
	if (req.http.Authorization || req.http.Cookie) {
		return (pass);
	}
	return (lookup);
}
# E aqui tratamos a resposta ao cliente
sub vcl_deliver {
	set resp.http.X-Cache-Hits = "MeuServidor";
	if (obj.hits > 0) {
		set resp.http.X-Cache-Hits = resp.http.X-Org + "-" + obj.hits;
	}
	remove resp.http.X-Varnish;
	remove resp.http.Via;
	remove resp.http.Age;
	remove resp.http.Server;
	remove resp.http.X-Powered-By;
}

Na linha 2 definimos qual é o servidor HTTP real. Você pode indicar um nome de domínio se não quiser usar um IP. Na prática aqui configuramos que o servidor HTTP real está na mesma máquina, mas respondendo na porta 8080. Entre as linhas 5 e 11 acrescentamos um cabeçalho na requisição do cliente, que corresponde ao IP real do cliente. Sem isso você será incapaz de saber qual é o IP real do seu cliente.

Entre a linha 12 e 17 indicamos situações onde o cache deve ser ignorado, como caso o usuário tenha Cookies ou esteja autenticado por HTTP, ou se ele realizar um envio de dados por POST. Através do return (pass) indicamos que o Varnish deve fazer a requisição para o servidor HTTP real independente de existir no cache ou não. Na linha 18 indicamos ao Varnish que ele deve buscar no cache a informação e se não enconstrar, buscar no servidor HTTP real.

Entre a linha 22 e 25 adicionamos a informação de quantas vezes o objeto foi "resgatado do cache". Assim você poderá identificar por um cabeçalho HTTP no seu inspecionador do navegador se o objeto é cacheado ou não. Entre as linhas 26 e 30 limpamos cabeçalhos que poderiam identificar nosso servidor HTTP real.

Não se esqueça de ao final de tudo reiniciar o Varnish com service varnish restart.

As versões 2.x e 3.x do Varnish possuem sintaxe de arquivo .vcl distintas. Se houver na concatenação de strings o "+" significa que está utilizando a versão 3 (a usada nestes exemplos)

Leitura complementar