NGINX e NGINX Plus fornecem imagens responsivas sem complicações

NGINX | 29 de setembro de 2016

Foto: Phil Roeder

Como usar o módulo Image‑Filter e o atributo srcset para redimensionar imagens dinamicamente

Web design responsivo tornou-se o padrão para sites e aplicações web modernos, proporcionando uma experiência consistente em uma ampla variedade de dispositivos e, ao mesmo tempo, otimizando a exibição para cada um deles. No entanto, os dispositivos modernos variam não apenas em termos de tamanho da tela, mas também de densidade de pixels. A tag HTML5 img oferece diversos recursos que permitem ao navegador selecionar o recurso mais adequado quando o servidor disponibiliza várias versões. Se o site disponibilizar a mesma imagem em vários tamanhos diferentes, o navegador poderá escolher o tamanho mais adequado ao ambiente atual.

Assim, as imagens responsivas permitem que o navegador produza uma renderização que corresponda fielmente à intenção do designer. Isso melhora a experiência do usuário, mas também impõe uma carga adicional às equipes de desenvolvimento e operações, que agora precisam criar e implantar diversas variantes dos ativos de imagem, além da imagem padrão.

Nesta publicação do blog, mostramos como usar o módulo Image‑Filter para NGINX e NGINX Plus a fim de fornecer imagens responsivas sem a complicação de criar e gerenciar uma infinidade de variantes de ativos de imagem. Em vez disso, podemos implantar uma única versão “original” de cada imagem, que o NGINX ou o NGINX Plus redimensiona dinamicamente. As informações desta publicação se aplicam tanto ao NGINX Open Source quanto ao NGINX Plus (exceto pelas instruções específicas em Instalação do módulo Image‑Filter); para simplificar, usamos NGINX Plus ao longo de todo o texto.

O atributo srcset Attribute

A principal ferramenta para fornecer imagens responsivas é o atributo srcset da tag img do HTML5. Podemos usá-lo para especificar diversas variantes de um recurso de imagem para diferentes densidades de pixels e tamanhos de viewport. Viewport é um termo genérico para o espaço de exibição disponível para o navegador, seja em uma janela no computador ou em um aplicativo em tela cheia em um dispositivo móvel.

No exemplo a seguir, o atributo src define uma imagem padrão para navegadores que não são compatíveis com o atributo srcset attribute e o atributo srcset especifica duas variantes – uma para telas com densidade de pixels padrão (1x) e outra para telas com o dobro da densidade de pixels, como as telas Apple Retina™ e alguns monitores 4K (2x).

<img src="/images/mylogo-default.png"  srcset="/images/mylogo-density1.png 1x, /images/mylogo-density2.png 2x">

O exemplo mais sofisticado a seguir define diversas variantes de um recurso de imagem a serem exibidas de acordo com a largura da janela de visualização. O atributo sizes especifica que o navegador renderiza uma imagem ocupando metade da janela de visualização se ela for mais larga que 10em; caso contrário, usa toda a janela de visualização. O navegador determina quanto espaço está disponível para a imagem e seleciona a variante do recurso de imagem que melhor se ajusta ao espaço disponível, normalmente “arredondando para cima” até a próxima largura (sufixo w) e redimensionando internamente a imagem para preencher o espaço com exatidão.

<img src="/images/racecar-default.jpg"  sizes="(min-width: 10em) 50vw, 100vw"
  srcset="/images/racecar-100px.jpg 100w, /images/racecar-225px.jpg 225w, /images/racecar-450px.jpg 450w, /images/racecar-675px.jpg 675w">

Essa abordagem para fornecer imagens responsivas por meio da especificação de diversas variantes de um recurso de imagem é fácil de implementar e altamente eficaz. No entanto, ela apresenta desafios relacionados à criação e ao gerenciamento das próprias variantes. É necessário redimensionar muitas imagens na etapa de pré-produção e implantar um número muito maior de arquivos no servidor. Ajustar com precisão a quantidade e o tamanho ideais de cada variante pode ser demorado, o que também dificulta testar se cada variante do recurso de imagem está acessível.

Para saber mais sobre o atributo srcset e outras técnicas para imagens responsivas, confira esta excelente publicação no blog.

Instalando o módulo de filtro de imagens

O procedimento para obter o módulo Image‑Filter é diferente para o NGINX Plus e o NGINX.

Instalando o módulo Image‑Filter para o NGINX Plus

O módulo Image‑Filter está disponível gratuitamente como um módulo dinâmico para assinantes do NGINX Plus.

  1. Obtenha o módulo instalando-o pelo repositório do NGINX Plus. Para sistemas Ubuntu e Debian:
$ sudo apt-get install nginx-plus-module-image-filter
  1. Para sistemas RedHat, CentOS e Oracle Linux:
$ sudo yum install nginx-plus-module-image-filter
  1. Habilite o módulo incluindo uma diretiva load_module para ele no contexto de nível superior (“main”) do arquivo de configuração nginx.conf (ou seja, não nos contextos http ou stream).
load_module modules/ngx_http_image_filter_module.so;
  1. Recarregue o NGINX Plus para carregar o módulo Image‑Filter na instância em execução.
$ sudo nginx -s reload>

Instalando o módulo de filtragem de imagens para o NGINX Open Source

A maneira mais fácil de instalar o módulo Image‑Filter é obtê-lo no repositório oficial do NGINX. Siga estas instruções para configurar seu sistema para acessar o repositório oficial do NGINX e, em seguida, instale-o usando o gerenciador de pacotes do seu sistema operacional.

Para sistemas Ubuntu e Debian:

$ sudo apt-get install nginx-module-image-filter

Para sistemas Red Hat, CentOS e Oracle Linux:

$ sudo yum install nginx-module-image-filter

Após a instalação, siga as etapas 2 e 3 em Instalação do módulo de filtragem de imagens para o NGINX Plus para configurar e recarregar o NGINX.

Também é possível compilar o módulo Image‑Filter a partir do código-fonte e carregá-lo como um módulo compilado estaticamente ou dinâmico. Para obter detalhes, consulte o Guia de Administração do NGINX Plus.

Adequação do tamanho da imagem à densidade de pixels

Com o módulo Image‑Filter, podemos criar e implantar uma única versão “original” de cada imagem e fazer com que o NGINX Plus a redimensione dinamicamente para fornecer qualquer variação de tamanho solicitada pelo navegador. Podemos ajustar com precisão páginas da Web responsivas e imagens diretamente no código-fonte HTML, sem precisar redimensionar e implantar manualmente as imagens em nosso servidor Web.

Neste arquivo HTML de exemplo, definimos quatro variantes de imagem para dispositivos com diferentes densidades de pixels.

<!DOCTYPE html><html>
<head>
<title>Responsive Logo</title>
</head>
<body>
 
<h2>Logo selection based on pixel density</h2>

<img src="/img400/mylogo.png"
  srcset="/img400/mylogo.png 1x, /img800/mylogo.png 2x, /img1200/mylogo.png 3x, /img1600/mylogo.png 4x">
 
</body>
</html>

Os diretórios /img400, /img800, /img1200 e /img1600 não existem de fato. Em vez disso, a configuração do NGINX Plus a seguir corresponde às solicitações de ativos com o prefixo /img e as transforma em solicitações para redimensionar a imagem no nome de arquivo original (por exemplo, mylogo.png no HTML anterior).

server {    listen 80;
    root /var/www/public_html;
 
    location ~ ^/img([0-9]+)(?:/(.*))?$ {
        alias /var/www/source_images/$2;
        image_filter_buffer 10M;
        image_filter resize $1 -;
    }
}

O bloco server define como o NGINX Plus processa as solicitações HTTP recebidas. A diretiva listen instrui o NGINX Plus a escutar na porta 80, a porta padrão para o tráfego HTTP. A diretiva root especifica o local deste site no disco. Neste exemplo simples, usamos um site estático hospedado pelo NGINX Plus, mas também é comum que o NGINX Plus atue como proxy reverso para conteúdo dinâmico ou como conector de aplicações, como o FastCGI. Todos esses casos de uso podem aproveitar o módulo Image‑Filter conforme descrito aqui, implantando as imagens de origem no servidor NGINX Plus.

O bloco location usa uma expressão regular para corresponder a solicitações de recursos armazenados em qualquer diretório cujo nome comece com /img seguido de um ou mais dígitos. Os dígitos são capturados na variável $1, e o nome do arquivo subsequente é capturado na variável $2. Em seguida, usamos a diretiva alias para atender a essa solicitação a partir do diretório no disco que contém nossas imagens de origem. Observe que esse diretório não está no caminho definido por root, portanto, os clientes não podem solicitar diretamente as imagens de origem.

Como é provável que nossas imagens de origem sejam muito grandes, talvez com milhares de pixels de largura, precisamos garantir que o módulo Image‑Filter aloque memória suficiente para carregá-las e redimensioná-las. Neste exemplo, usamos a diretiva image_filter_buffer para oferecer suporte a arquivos de imagem de até 10 MB.

Por fim, a diretiva image_filter instrui o módulo Image‑Filter a redimensionar a imagem de origem para a largura obtida do sufixo do nome do diretório /img. O hífen (‑) instrui o NGINX Plus a manter a proporção da imagem de origem.

Considerações para uso em produção

A configuração descrita em Correspondência do tamanho da imagem com a densidade de pixels pode fornecer uma imagem em qualquer tamanho, dependendo do nome do diretório usado para solicitá-la. No entanto, em um ambiente de produção, não queremos esperar que o servidor web redimensione as imagens a cada solicitação. Isso prejudica a latência geral e também pode aumentar significativamente o uso da CPU.

A solução mais eficaz é armazenar em cache nossas variantes de imagens redimensionadas, para que as solicitações subsequentes de cada variante sejam atendidas pelo cache, sem passar pelo módulo Image‑Filter. Podemos fazer isso por meio da configuração do NGINX Plus, definindo um servidor virtual separado que redimensiona as imagens e encaminhando solicitações a ele por proxy somente se o tamanho de imagem solicitado ainda não estiver no cache. Chamamos isso de servidor de imagens responsivas.

Figura 1. Configuração de produção de um servidor de imagens responsivas com cache habilitado no servidor web de frontend

Também devemos considerar as implicações de segurança de permitir que solicitações arbitrárias realizem operações de redimensionamento de imagens. Ter o cache configurado não ajuda se um invasor fizer solicitações rápidas de variantes exclusivas de ativos de imagem, como /img1001/mylogo.png, /img1002/mylogo.png, /img1003/mylogo.png e assim por diante. Mesmo com um volume relativamente baixo de solicitações, esses ataques podem causar negação de serviço (DoS) devido ao uso excessivo da CPU. Para resolver esse problema, aplicamos um limite de taxa ao servidor de imagens responsivas, mas não ao servidor de frontend que contém as variantes armazenadas em cache. A configuração a seguir amplia a configuração apresentada em Correspondência do tamanho da imagem com a densidade de pixels, aplicando cache e limitação de taxa ao módulo Image-Filter.

proxy_cache_path /var/www/imgcache levels=1 keys_zone=resized:1m max_size=256m;
server {
    listen 80;
    root /var/www/public_html;

    location ~ ^/img([0-9]+)(?:/(.*))?$ {
        proxy_pass        http://127.0.0.1:9001;
        proxy_cache       resized;
        proxy_cache_valid 180m;
    }
}

limit_req_zone "1" zone=2persec:32k rate=2r/s;

server {
    listen 9001;
    allow 127.0.0.1;
    deny all;
    limit_req zone=2persec burst=10;

    location ~ ^/img([0-9]+)(?:/(.*))?$ {
        alias /var/www/source_images/$2;
        image_filter_buffer 10M;
        image_filter resize $1 -;
    }
}

Começamos definindo o local das imagens armazenadas em cache com a diretiva proxy_cache_path. O parâmetro keys_zone define uma zona de memória compartilhada para o índice do cache (chamada resized) e aloca 1 MB, o que é suficiente para cerca de 8.000 imagens redimensionadas. O parâmetro max_size define o ponto em que o NGINX Plus começa a remover do cache as imagens solicitadas há mais tempo para liberar espaço para novos itens.

A diretiva location do servidor web de frontend (aquele que escuta na porta 80) usa a diretiva proxy_pass para encaminhar as solicitações com o prefixo /img ao servidor de imagens responsivas hospedado internamente (127.0.0.1:9001). A diretiva proxy_cache habilita o armazenamento em cache para esse local, especificando o nome do cache (resized) a ser usado para armazenar as respostas do servidor de imagens responsivas. A diretiva proxy_cache_valid garante que as imagens redimensionadas sejam mantidas no cache por pelo menos 180 minutos (a menos que o cache tenha excedido max_size e elas estejam entre as solicitadas há mais tempo) e que nenhuma resposta com erro do servidor de imagens responsivas seja armazenada em cache.

Para uma descrição detalhada do armazenamento em cache, consulte Guia de armazenamento em cache com o NGINX e o NGINX Plus.

Antes de definir o próprio servidor de imagens responsivas, especificamos um limite de taxa com a diretiva limit_req_zone. A diretiva em si não impõe um limite de taxa; ela define um limite de duas solicitações por segundo, que é então aplicado ao servidor de imagens responsivas incluindo a diretiva limit_req em seu bloco server (consulte o próximo parágrafo). Normalmente, um limite de taxa é associado a um atributo da solicitação, mas, neste caso, especificamos o valor de chave estático "1" para que o limite se aplique a todos os solicitantes. Definimos o tamanho da zona de memória compartilhada como o menor valor possível, 3 KB, porque nossa chave tem cardinalidade fixa igual a um.

O bloco server do servidor de imagens responsivas escuta na porta 9001. Incluímos as diretivas allow e deny para especificar que somente o localhost (o servidor web de frontend) pode se conectar ao servidor de imagens responsivas. Em seguida, aplicamos o limite de requisições definido anteriormente incluindo a diretiva limit_req; o parâmetro burst permite 10 requisições simultâneas antes que o limite seja imposto. Depois que o limite entra em vigor, as requisições excedentes são adiadas até que possam ser processadas dentro do limite.

O bloco location é idêntico ao apresentado em Como adequar o tamanho da imagem à densidade de pixels, mas agora só é acionado quando a imagem solicitada não está no cache e o limite de requisições ainda não foi excedido.

Nessa configuração básica, uma única instância do NGINX Plus atua tanto como servidor web de frontend quanto como servidor de imagens responsivas. O processamento de imagens pode exigir muitos recursos computacionais, o que pode resultar em cargas de trabalho extremamente altas e deixar o NGINX Plus sujeito a ataques de DoS. Para evitar que o servidor web de frontend não consiga aceitar novas solicitações imediatamente porque todos os processos de trabalho estão ocupados com solicitações de redimensionamento de imagens, recomendamos executar uma instância separada do NGINX Plus dedicada ao processamento de imagens. Isso isola os processos de trabalho do servidor web de frontend daqueles que realizam o processamento de imagens. Para executar uma instância separada do NGINX Plus no mesmo host, especifique outro arquivo de configuração na linha de comando:

$ sudo nginx -c /etc/nginx/resize-server.conf

Imagens responsivas em ação

A maneira mais eficaz de ver imagens responsivas em ação é observar um navegador decidindo qual variante de imagem do atributo srcset usar à medida que o tamanho da janela de visualização muda. Veja a seguir o código-fonte HTML de uma galeria de imagens simples. Observe que, para fins de demonstração, as variantes de tamanho são ligeiramente diferentes para cada imagem, criando muitos possíveis “pontos de interrupção” nos quais o navegador pode escolher uma variante diferente.

<!DOCTYPE html>
<html>
<head>
<title>Responsive Image Gallery</title>
</head>
<body>
 
<h2>Responsive Image Gallery</h2>

<img src="/img100/1-dominos.jpg" sizes="(min-width: 20em) 40vw, 100vw"
  srcset="/img110/1-dominos.jpg 110w, /img210/1-dominos.jpg 210w, /img310/1-dominos.jpg 310w, /img410/1-dominos.jpg 410w, /img510/1-dominos.jpg 510w, /img610/1-dominos.jpg 610w">

<img src="/img100/2-sign.jpg" sizes="(min-width: 20em) 40vw, 100vw"
  srcset="/img120/2-sign.jpg 120w, /img220/2-sign.jpg 220w, /img330/2-sign.jpg 330w, /img420/2-sign.jpg 420w, /img520/2-sign.jpg 520w, /img620/2-sign.jpg 620w">

<img src="/img100/3-thruppence.jpg" sizes="(min-width: 20em) 40vw, 100vw"
  srcset="/img130/3-thruppence.jpg 130w, /img230/3-thruppence.jpg 230w, /img330/3-thruppence.jpg 330w, /img440/3-thruppence.jpg 440w, /img550/3-thruppence.jpg 550w, /img660/3-thruppence.jpg 660w">

<img src="/img100/4-aces.jpg" sizes="(min-width: 20em) 40vw, 100vw"
  srcset="/img140/4-aces.jpg 140w, /img240/4-aces.jpg 240w, /img340/4-aces.jpg 340w, /img440/4-aces.jpg 440w, /img540/4-aces.jpg 540w, /img640/4-aces.jpg 640w">

</body>
</html>

As capturas de tela abaixo mostram o conteúdo desta página da web em um navegador Chrome com o Inspetor aberto na guia Rede. A coluna Nome mostra o caminho de cada variante de imagem solicitada ao servidor, o que nos permite ver o tamanho escolhido sem precisar verificar os logs do servidor web.

Com a janela de visualização estreita na Figura 2, o navegador escolheu imagens com largura entre 220 e 310 pixels (os sufixos numéricos nos nomes dos diretórios /img na coluna Nome estão dentro desse intervalo).

Figura 2. Galeria de imagens responsiva quando a janela de visualização é estreita

Quando ampliamos a janela do navegador na Figura 3, o navegador escolhe imagens com largura entre 440 e 540 pixels (as quatro últimas imagens listadas). O valor dessas imagens na coluna Iniciador é Outro.

Figura 3. Galeria de imagens responsiva quando a janela de visualização é mais larga

Conclusão

Com o NGINX Plus e o módulo Image‑Filter, podemos fornecer imagens com o tamanho ideal para as condições atuais do navegador. E podemos fazer isso sem redimensionar imagens antes da produção, realizar processamento em lote ou precisar gerenciar centenas de variantes de ativos de imagem no disco. Essa é apenas mais uma maneira pela qual o NGINX Plus ajuda você a oferecer aplicações com excelência.

Experimente você mesmo imagens responsivas com o NGINX Plus – comece hoje mesmo sua avaliação gratuita de 30 dias ou entre em contato conosco para discutir seus casos de uso.


Compartilhar
Tags: F5 NGINX, Tech

Sobre o autor

Liam Crilly
Liam CrillyDiretor Sênior de Gestão de Produtos

Mais publicações de Liam Crilly

Publicações relacionadas no blog

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade
NGINX | 10/10/2024

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade

Descubra como o F5 NGINX One aprimora o gerenciamento e a observabilidade de ambientes híbridos e multicloud ao oferecer um console web centralizado baseado em SaaS, com insights em tempo real, conformidade automatizada e gerenciamento simplificado de configurações.

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa
NGINX | 17/06/2024

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa

Descubra como transformar uma definição de esquema OpenAPI em uma configuração NGINX totalmente funcional, operando como um API Gateway com a segurança de um Web Application Firewall e um Portal do Desenvolvedor, usando uma abordagem declarativa de API.

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor
NGINX | 14/07/2021

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor

Picos de tráfego e sobrecarga de servidores são preocupações sérias para varejistas online em qualquer época, mas especialmente durante a “Cyber Season” de compras de fim de ano. Detalhamos como você pode aproveitar as soluções de entrega de aplicações de nível de produção da NGINX para se preparar para aumentos repentinos de tráfego.

Distribuição segura de chaves privadas SSL com o NGINX
NGINX | 02/04/2019

Distribuição segura de chaves privadas SSL com o NGINX

Descrevemos três maneiras progressivamente mais seguras de proteger chaves privadas SSL ao configurar o NGINX para processar tráfego HTTPS: permitir acesso de leitura apenas ao usuário root, criptografar as chaves com senhas armazenadas separadamente e distribuir as senhas a partir de um repositório central.

Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX
NGINX | 24/05/2018

Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX

O módulo JavaScript do NGINX lançado com o NGINX Plus R15 oferece suporte a subsolicitações, possibilitando uma classe totalmente nova de casos de uso para o NGINX Plus. Este exemplo completo também usa a API do NGINX Plus e os módulos de chave-valor para converter solicitações de API dos clientes em solicitações separadas para as APIs dos microsserviços que compõem um aplicativo.

Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus
NGINX | 19/04/2016

Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus

Confira dicas para evitar o esgotamento de portas efêmeras com ajustes no kernel do Linux, conexões keepalive e conexões vinculadas dinamicamente a endereços IP locais.