Category Archives: Avançado

SentinelPT WMS Time Machine

Versão abreviada…

Podem aceder aqui a um motor de mosaicos de imagens Sentinel-2 RGB e IRG para Portugal, com serviço WMS, com suporte temporal… O serviço WMS está funcional, mas para usar em QGIS é preciso algumas definições (ler abaixo, muuuito abaixo).

http://sentinelpt.viasig.com/

Alguns avisos: isto é um projecto pessoal, de carolice, tem muitos defeitos, eu sei, que podem ou não vir a ser resolvidos… Estou muito interessado em ouvir sugestões, e para isso nada melhor que o twitter ou os comentários aqui no blog.

Se a carga for demasiada no servidor, os pedidos são “desacelerados”, por forma a manter o servidor equilibrado. Por favor, não usem scripts de download… Pretendo incluir a função de download em breve. Entretanto, se precisarem de alguma imagem é só dizer, eu farei os possíveis para responder atempadamente.

E pronto, agora quem tem curiosidade e paciência pode continuar a ler…

Introdução

Acho as imagens Sentinel um prodígio, a sério. Temos imagens de 10m de resolução, 7 em 7 dias, para grande parte do planeta, gratuitas! É espantoso…

Tenho usado imagens que pesquiso e descarrego a partir dos sites de distribuição da ESA. Mas é um processo moroso obter as imagens – cada uma é 1GB num zip com 13 ficheiros jpeg2000, e mais uma dezena de outros ficheiros de metadados. Ainda mais moroso se quiser juntar algumas imagens. Mas é tudo gratuito.

Também consulto visualizadores web com imagens processadas, com equilibrio de cores, e sem nuvens, e com várias combinações de bandas. É só pesquisar… também é gratuito.

Agora, se subirmos um pouco o grau de exigência e quisermos sobrepor a nossa informação às imagens, podemos usar um serviço WMS. Mas aqui já é pago, e começa nos 20€/mês por utilizador particular ou académico. Se for empresa o preço já sobe. E é completamente justificado. Mas também é um pouco contraditório em relação ao que se pretende do programa Copernicus, que seria disseminar o mais possível os resultados do programa na sociedade civil… 20€/mês parece-me um pouco contraditório.

Na mais recente incursão pelos sites de descarga das imagens, comecei a fazer alguns scripts, muito simples. Mas a coisa foi-se alongando, e acabei por automatizar grande parte do que seria um servidor wms, com dados processados periodicamente. Foi assim que nasceu este projecto – WMS Time Machine!

Rick and Morty a melhor série de todos os tempos!
Melhor invenção de todos os tempos

Isto não é bem uma invenção… Os nossos vizinhos aqui ao lado do Institut Cartogràfic i Geològic de Catalunya têm um servidor WMS-T de imagens Sentinel *do caraças!*…

http://www.icgc.cat/en/Public-Administration-and-Enterprises/Services/Online-services-Geoservices/WMS-Ortoimatges/WMS-Sentinel-2-orthoimages

É pena ser só para a Catalunha…

Componentes e Processo geral

Os passos do processo e os componentes de software usado são:

  1. Pesquisa e descargas de imagens – Sentinelsat
  2. Processamento das imagens – GDAL/OGR
  3. Manutenção da BD de imagens – GDAL/OGR
  4. Serviço WMS Time – MapServer
  5. Visualizar o serviço em QGIS
  6. Visualizador Web – OpenLayers (Feito à pressa! No futuro, talvez um de jeito 😉

Pesquisa e descarga das imagens – Sentinelsat

A pesquisa das imagens e a filtragem das que interessam é feita usando a biblioteca python SentinelSat, que também inclui uma CLI. São apenas usadas imagens Sentinel-2, nível 2A (significa que têm já correção atmosférica).

O esquema de pesquisa é muito simples, e estou a procurar formas de o melhorar. É indicada uma data, e são pesquisadas 16 quadrículas ou tiles ou grânulos (não tenho paciência para ler o dicionário do Sentinel) que correspondem a Portugal, que tenham sido obtidas até 5 dias atrás, com nuvens até 70%. Também são recusadas imagens com menos de 75% do seu tamanho normal para evitar imagens com grandes áreas sem dados. Alguns mosaicos podem ter sido construídos manualmente com nuvens até 90%…

Um exemplo de pesquisa com Sentinelsat:

 sentinelsat  --start 20190818 --end 20190819 --sentinel 2 --instrument MSI  --producttype S2MSI2A --cloud 40 --query "filename=/.+29S(N|P)C.+/"  --user bla --password blabla 

Este comando procura ficheiros com nomes que incluam “29SNC” ou “29SPC” entre os dias 18 e 19-08-2019, com cobertura de nuvens até 40%. A facilidade de usar expressões regulares é muito flexível. Os docs do Sentinelsat e da interface de pesquisa da ESA são muito bons.

Só são descarregadas 4 bandas das 13 disponíveis: B02 (Red), B03 (Green), B04 (Blue), e B08 (Infrared). Isto significa que para uma data são descarregados 64 ficheiros .jp2 (16 tiles x 4 bandas), cerca de 7GB no total. As 4 bandas são depois combinadas em combinações RGB e IRG.

O processo de pesquisa deveria ser melhorado… Gostava de evitar mosaicos com partes significativas sem dados (neste momento há tiles com 25% de área sem dados). Estou a pensar numa pesquisa sobre um maior período, ordenar as imagens por qualidade, e detectar que datas apresentam melhores coberturas. Ou seja, um processo quase inverso do actual… Outra opção é usar o footprint de cada imagem e analisar geometricamente qual a combinação com menores vazios, ou mesmo só fazer o mosaico se não houver vazios.

Processamento das imagens – GDAL

Depois de descarregados, os ficheiros são processados com GDAL e OGR. As 4 bandas de cada quadrícula ou grânulo são convertidas para 8bit, sendo criadas uma imagem virtual RGB e outra IRG para cada Tile. Depois é aplicado um stretch “virtual” para melhorar o contraste. São criados tileindexes para reunir todas as imagens RGB e IRG do país para uma data. Em seguida tento explicar melhor…

A compressão faz com que o tamanho das bandas passe de ~100MB (jp2000, 16bit) para ~6MB (tiff/jpeg, 8bit). E assim os 64 ficheiros tif só ocupam 600MB para todo Portugal Continental, numa data.

É importante referir que para passar de 16bit para 8bit usei a forma mais simples do parâmetro -scale, que efectivamente passa os valores min-max dos 16bits da imagem, para valores entre 0-255. Isto efectivamente já provoca uma alteração à cor e contraste da imagem. E, obviamente, alguma perda de informação. Estas imagens são apenas para visualização e não se recomendam para análise.

Foi necessário melhorar ainda mais o contraste para que as imagens não fiquem demasiado escuras. Como o MapServer não suporta grande coisa na simbologia de rasters, tive de resolver ao nível dos dados.

Encontrei muita informação sobre a opção scale, mas poucas soluções para ajuste do contraste. Algumas óptimas soluções permitem criar novas imagens melhoradas, mas obrigam a mais espaço em disco e mais tempo de processamento…

A solução foi criar um .vrt que faz um ajuste ao contraste dinamicamente através da opção -scale. É usado o método de ajuste pelo desvio padrão, aplicando-o a cada banda. Ou seja, em cada banda é obtida a média e o desvio padrão, e o ajuste é feito calculando novos mínimos e máximos (fazendo o mesmo que uma das opções de Contrast Enhancement do QGIS):

-scale 0 255 media-2.8*stdev media+2.8*stddev

Funciona muito bem, desde que não existam nuvens na imagem:

Com nuvens, e respectivas sombras, tudo piora, como é de esperar… será necessário melhorar o processo para evitar estas áreas brancas e negras:

Falta de contraste na presença de nuvens

Criar base de dados das datas e imagens – OGR

Numa data temos assim 16 imagens RGB, e 16 imagens IRG. Todas são virtuais (.vrt), apens combinam 3 das 4 bandas, e não ocupam espaço. Agora queremos criar uma listagem que indique que datas já recolhemos, e quais as imagens que constituem cada data.

A base de de dados é apenas um shapefile com as quadrículas de todas as imagens vrt… o processo é simples e consiste em criar tileindexes… uma forma anciã de ver mosaicos de imagens e que ainda funciona em Mapserver.

Começamos por criar um tileindex das imagens numa data. Simples comando de gdaltindex. Um exemplo de índice rgb do mosaico para o dia 30/06/2019:

Na verdade, os índices são geograficamente todos iguais, porque uso sempre as mesmas 16 tiles. Mas o ideal seria pesquisar pela área de Portugal, e ver que tiles têm melhor cobertura na data escolhida.

A única coisa que varia entre datas são os ficheiros de imagem que são descarregados.

Bom, já temos um tileindex para cada dia descarregado, que é um shapefile com um atributo a indicar o caminho para cada imagem.

Como fazer uma base de dados de todos os mosaicos que já foram descarregados e existem no servidor? A resposta é simples: copiamos estes registos para um shapefile global usando ogr2ogr com a opção -update. E sempre que se constrói um mosaico para uma data nova, vamos inserir estes registos no shapefile global.

Aqui é usada a função SQL do OGR, que é absolutamente fantástica… permite executar SQL ao carregar dados para um shapefile, ou qualquer outro formato.

Assim, ao copiar as quads de um mosaico para o índice global aproveitamos para actualizar alguns campos extra:

  • data do mosaico (campo time)
  • data da imagem (campo dataimg)
  • nome do ficheiro (campo location)
  • nome do ficheiro com contraste melhorado (campo localviz)
Exemplo de identify no Índice de imagens RGB

Significa que sabemos as datas todas que recolhemos, e as imagens que as compõem. Tudo com shapefile!! (o shapefile é eterno!)

E é compatível com MapServer…

Só um exemplo do comando ogr para apagar imagens que já existam de uma data:

ogrinfo -dialect SQLITE tileindex_global_irg.shp -sql "DELETE FROM tileindex_global_irg where location like '%_20190830%_irg_%.vrt'"

Publicar as imagens num serviço WMS-Time

Usei o MapServer como servidor WMS com suporte do parâmetro Time.

O MapServer é fácil de configurar e manipular apenas com ficheiros de texto. A sua arquitectura é tão simples que apenas é necessário um nginx para o colocar na net. A exigência de memória é também muito reduzida. E claro, é um óptimo amigo do GDAL/OGR. Tudo o que era preciso…

Assim, foi criado um mapfile único com 4 layers:

  1. Índice das imagens, com label mostrando a data de cada imagem
  2. Índice das imagens, com label mostrando o nome do ficheiro (para vermos a tile respectiva se quisermos obter o original no site da ESA)
  3. Mosaico RGB
  4. Mosaico IRG

Todos apontam para o tileindex RGB ou para o tileindex IRG. Os índices são vectoriais, e os mosaicos são rasters. Simples.

O parâmetro TIME permite filtrar os dados para só mostrar aqueles que cumprem essa query. Ou seja, passamos uma data e o servidor devolve uma imagem onde todos os layers com TIME definido são filtrados por essa data.

No nosso caso, o campo usado para o filtro de data é o campo time, que indica a data de construção do mosaico. Por exemplo, este request mostra só imagens do mosaico RGB com data de 2019-06-30:

http://sentinelpt.viasig.com/wms/sentinelpt/?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&FORMAT=image%2Fjpeg&TRANSPARENT=true&LAYERS=SentinelPT_RGB%2CIndice&TIME=2019-06-30&CRS=EPSG%3A3857&STYLES=&WIDTH=866&HEIGHT=538&BBOX=-868964.8014314906%2C4637845.355088675%2C-860690.4931196203%2C4642985.745240854

Há uma limitação ainda por resolver no serviço WMS: as datas disponíveis podem ser anunciadas pelo serviço. Mas neste momento estão fixadas:

Período anunciado no WMS-Time é fictício…

Como ver no QGIS

Pois é… o QGIS não tem grande suporte para usar WMS-Time… mas funciona com alguns truques – basta indicar a data que queremos no url do serviço, e ligar as opções para ignorar os url’s devolvidos no capabilities doc:

Usar o serviço WMS-Time no QGIS

Visualizador web

Bom, este visualizador é muito básico. Foi feito com base neste viewer baseado em OpenLayers: https://www.earder.com/tutorials/timeseries-with-geoserver-and-openlayers/.

Permite selecionar a data, tema RGB ou IRG, com ou sem labels. E ver o link WMS correspondente à data selecionada:

http://sentinelpt.viasig.com/

Melhorias??

Tantas, tantas…

  • Criar transparência onde não há dados (zonas negras)
  • Criar serviço de download (WCS)?
  • Melhorar o ajuste de contraste automático actual, de acordo com cada imagem ou fazendo um match dos histogramas (neste momento, é feito um stretch de desvio padrão em todas as bandas)
  • Excluir as nuvens do ajuste de contraste
  • Criar serviço de tiles (WMTS)?
  • Usar um visualizador web como deve ser (TerriaJS?)
  • Obter dados para 2017
  • Download dos footprints das imagens e usá-los na pesquisa e processamento
  • Selecionar entre tiles disponíveis com menor área sem dados
  • Reduzir a compressão dos ficheiros, aumentando a qualidade

Acho que nem eu próprio li isto tudo… mas fica para cábula futura.

Legalidades

Este é um projecto pessoal, sem qualquer garantia. Portanto, use por sua própria conta e risco. Contém dados de imagem de satélite Copernicus Sentinel-2 para vários anos, processados para efeitos de visualização e arquivo. Os dados originais são disponibilizados gratuitamente pela União Europeia para todos os fins. Mais informação pode ser consultada aqui: https://scihub.copernicus.eu.

Os dados deste projecto são disponibilizados sob a licença Atribuição 4.0 Internacional (CC BY 4.0)(link). Em resumo, esta licença permite qualquer uso dos dados, mesmo comercialmente, desde que seja indicada a sua fonte e não sejam impostas restrições adicionais aos dados.

PostgreSQL – mover tablespaces

É totalmente desaconselhado criar tablespaces na directoria DATA do pgsql. Podem ver-se vários avisos e explicações na net:
https://hunleyd.github.io/posts/Where-Not-To-Put-Your-Tablespaces/

Este post resulta de eu ter cometido este erro há muito tempo atrás (v8.4) e só agora estar a pagar por ele…

Como funcionam os tablespaces no postgres?

Tablespaces em pgsql são pastas onde são colocados os ficheiros de dados, e a teoria é que permitem espalhar os dados em diferentes discos para equilibrar os acessos e o desempenho. Também dão flexibilidade para gerir falta de espaço em disco, p.e. colocando um tablespace noutro disco que tem espaço, e que receberá determinados dados (todos os novos dados, só algumas tabelas, etc.).
Esta divisão também pode ser feita quando temos só um disco, ficando a bd preparada para um cenário futuro com mais discos.

O postgres tem 2 tablespaces iniciais: pg_default e pg_global.
O pg_global é destinado aos objectos de sistema, e o pg_default é obviamente onde são gravados os nossos dados.
A grande questão é: no disco, onde está o pg_default? Está na pasta DATA\base!

Localização em disco do tablespace pg_default

Existe uma directoria que baralha isto tudo: DATA\pg_tablespace. E que está vazia. É aqui que os utilizadores costumam baralhar o sistema (como eu), e pensando que esta pasta é onde está o tablespace default, vão raciocinar que podem criar os seus tablespaces ao “lado”, por exemplo DATA\meu_tablespace.

pg_tblspc – não mexer, não imitar. A pasta DATA é off-limits!

Lição – a directoria DATA não é para o user mexer! É de sistema, é gerida pelo postgres, está off-limits!
Pois mas eu não sabia… e vai daí, criei uma série de tablespaces aqui mesmo:

CREATE TABLESPACE meu_tablspc LOCATION 'C:\Program Files\PostgreSQL\9.3\data\meusdadosgeo';

Na versão actual, o postgres vai avisar que isto não se deve fazer. Eu já fiz isto há muito tempo (comecei na 8.4), e não me lembro de aviso nenhum.
Anos mais tarde, ao fazer upgrades com pg_upgrade comecei a ter os problemas respectivos…

Como funcionam os tablespaces e a directoria pg_tblspc?

Para criar um tablespace novo, temos de criar previamente a directoria onde queremos que o tablespace se localize.
Depois executamos o sql de criação do tablespace, e o postgres vai criar um link (ou junction no Windows) na pasta DATA\pg_tblspc.
Quer isto dizer que não são criadas sub-dirs – são criados apontadores para a pasta de cada tablespace.
Em Windows, podemos verificar que são junctions usando a linha de comando e fazendo um simples dir:

dir "D:\Program Files\PostgreSQL\9.3\data\pg_tblspc"
Directory of D:\Program Files\PostgreSQL\9.3\data\pg_tblspc
22/01/2019 13:23 .
22/01/2019 13:23 ..
22/01/2019 13:23 12493007 [D:\Program Files\PostgreSQL\tables
paces_digc\sisma_tblspc]
22/01/2019 13:23 18357 [D:\Program Files\PostgreSQL\tablespac
es_digc\always_tblspc]

Podemos ver que a sub-dir 18357 é na realidade um junction que aponta para uma dir noutro local.

Ao criarmos os tablespaces dentro da pasta DATA estão a ver a confusão, certo? Estamos efectivamente a alterar a forma como o postgres gere os tablespaces, criando links/junctions que apontam para a pasta DATA. Quando quiserem usar o pg_upgrade, que vai tentar perceber a estrutura dos tablespaces, e não vai conseguir… resultando em erro de upgrade.

Se está nesta situação terá de a corrigir. Não é difícil mas como temos de alterar a estrutura e localização dos ficheiros de dados da bd é arriscado, e basta um erro para arruinar a bd. Portanto, antes de mais nada faça um snapshot da vm do postgres, ou se não é vm, faça um backup integral da bd. (better paranoid than dead)

Como corrigir?

Os tablespaces, dizem os docs, devem ser criados numa pasta cujo dono é a conta do sistema operativo que controla o serviço do postgres. No meu caso é “Network Service”.
Assim, optei por criar uma pasta para todos os meus tablespaces na dir de instalação do postgres. Isto porque em Windows, o postgres instala as versões como sub-dirs desta:

C:\Program Files\PostgreSQL\9.3
C:\Program Files\PostgreSQL\9.5

Assim, a minha pasta será criada desta forma:
C:\Program Files\PostgreSQL\meus_tablespaces

Fica à prova de apagar acidentalmente… ou pelo menos parece fazer parte da instalação o que fará pensar 2x…

C:\Program Files\PostgreSQL\9.3
C:\Program Files\PostgreSQL\9.5
C:\Program Files\PostgreSQL\tablespaces_adicionais

Depois, paramos a bd, e vamos apagar os junctions e recriá-los apontando para subpastas correctas.

Receita

A seguir apresento, em jeito de resumo, uma sequência de passos para corrigir a situação. Os exemplos consideram que existem 2 tablespaces para corrigir. (Será, naturalmente, melhor exportar os comandos para ficheiros de texto e criar pequenos scripts para ajudar…)

1) parar o serviço do postgres

2) vamos anotar as pastas dos nossos tablespaces, e os números dos junctions/links, vendo o conteúdo da pasta DATA\pg_tblspc:

 dir "D:\Program Files\PostgreSQL\9.3\data\pg_tblspc"
12493007 [D:\Program Files\PostgreSQL\9.3\data\sisma_tblspc]
18357 [D:\Program Files\PostgreSQL\9.3\data\always_tblspc]

3) vamos mover as dirs dos nossos tablespaces para a nova localização saudável

move "D:\Program Files\PostgreSQL\9.3\data\sisma_tblspc"   "D:\Program Files\PostgreSQL\tablespaces_adicionais"
move "D:\Program Files\PostgreSQL\9.3\data\always_tblspc" "D:\Program Files\PostgreSQL\tablespaces_adicionais"

4) vamos apagar os junctions todos na pasta DATA\pg_tablspc

rd D:\Program Files\PostgreSQL\9.3\data\pg_tblspc\12493007
rd D:\Program Files\PostgreSQL\9.3\data\pg_tblspc\18357

5) vamos recriar os junctions apontando-os para as dir correctas

mklink /J "D:\Program Files\PostgreSQL\9.3\data\pg_tblspc\12493007" "D:\Program Files\PostgreSQL\tablespaces_adicionais\sisma_tblspc"
mklink /J "D:\Program Files\PostgreSQL\9.3\data\pg_tblspc\18357" "D:\Program Files\PostgreSQL\tablespaces_adicionaisc\always_tblspc"

6) iniciamos o serviço do postgres

E testamos, com diversas queries, e verificamos se no log surgem erros. Mas em principio se a bd iniciou não haverá problemas.
Se ocorrer um erro ao iniciar o serviço e não conseguir perceber a razão, terá de repor a situação, usando o snapshot da vm, ou restaurando toda a bd a partir do backup que fez.

A partir daqui, podemos ver que a estrutura dos tablespaces em disco ficou:

dir "D:\Program Files\PostgreSQL\tablespaces_adicionais"
Volume in drive D is Data
Volume Serial Number is 9CB2-EBBD
Directory of D:\Program Files\PostgreSQL\tablespaces_adicionais
22/01/2019 13:18 .
22/01/2019 13:18 ..
25/01/2019 18:58 always_tblspc
25/01/2019 18:58 sisma_tblspc

Interessante também, e relevante para um processo de upgrade, é ver o conteúdo destas pastas:

dir "D:\Program Files\PostgreSQL\tablespaces_adicionais\sisma_tblspc"
Volume in drive D is Data
Volume Serial Number is 9CB2-EBBD
Directory of D:\Program Files\PostgreSQL\tablespaces_adicionais\sisma_tblspc
25/01/2019 18:58 .
25/01/2019 18:58 ..
24/01/2019 18:59 PG_9.3_201306121

Ou seja, o postgresql cria uma sub-pasta marcando a versão do postgres que coloca aqui os dados. Num upgrade serão criadas aqui sub-pastas correspondentes à nova versão.

E isso fica para um próximo post… Até breve.

PostgreSQL – autovacuum found orphan temp table

Quando o postgresql termina abruptamente de forma anormal, geralmente recupera sem problemas.
Mas recentemente sucedeu-me que no log apareciam milhares de mensagens deste tipo:

2019-01-24 13:31:39 GMT LOG:  autovacuum: found orphan temp table "pg_temp_56"."sde_logfile_data" in database "postgis"

Aparentemente estas mensagens são escritas pelo menos todos os segundos, o que degrada o desempenho e aumenta o tamanho dos logs.

Para resolver, basta apagar os schemas tablespaces problemáticos:

DROP SCHEMA pg_temp_56;

Estes schemas tablespaces “especiais” podem ser listados assim:

select relname,nspname from pg_class join pg_namespace on (relnamespace=
pg_namespace.oid) where pg_is_other_temp_schema(relnamespace);

E confirmando com as mensagens de log, podemos apagar apenas aqueles de que o autovacuum se queixa.

Ao procurar na web, esta queixa não é muito frequente, mas acontece [1]. E nas listas de mail do postgresql [2], vemos até um debate acesso entre os programadores do postgres sobre se continuam a debitar este n.º exagerado de mensagens ou se devem limitar ou eliminar mesmo as mensagens. A opção actual é jogar pelo seguro – manter, para que os responsáveis pela base de dados notem que algo se passa e se preocupem o suficiente para corrigir a situação.

Comigo funcionou…

Evitar conexões…

É conveniente apagar estes schemas tablespaces tendo a certeza de que não há conexões à bd… Para isso, basta editar o ficheiro pg_hba.conf de forma a permitir apenas conexões do localhost.

Devemos comentar as linhas que dão acesso à bd de outros endereços, e deixar ou incluir apenas as linhas que dão acesso ao localhost. Um exemplo seria:

#IPv4 local connections:
host all all 127.0.0.1/32 md5
#IPv6 local connections:
host all all ::1/128 md5
#INTRANET - comentado temporariamente
#host all all 192.168.0.0/16 md5
#host all all 10.10.0.0/16 md5

Reiniciamos o serviço do postgresql o que provocará o fecho de todas as conexões, e o assumir da nova configuração, impedindo conexões indesejadas. No meu caso, antes disto, parei o servidor web e o servidor de mapas. Só para ser simpático e não deixar aplicações em estados de erro…

Depois de apagar os schemas tablespaces, devemos reiniciar o serviço postgresql, e verificar se no log aparecem mais avisos deste tipo, porque o autovacuum é também iniciado. Se sim, apagamos os schemas tablespaces em erro.

Quando tudo estiver ok, revertemos o pg_hba.conf para permitir novamente conexões, e reiniciamos o serviço postgresql. Testamos uma aplicação qualquer para vermos se tudo está bem. Vamos para casa ter com a família…

[1] – https://www.postgresql.org/message-id/flat/51C9975D.1040508%40uib.cat
[2] – https://www.postgresql.org/message-id/flat/48F4599D.7010601%40enterprisedb.com

LXD o virtualizador ubuntu

Este post é sobre o LXD, uma forma de criar “máquinas virtuais” que residem em directorias na nossa máquina, sem mais intermediários (hypervisors). Para quem usa docker podem ver mais info sobre o projecto e como se comparam os 2: https://www.ubuntu.com/containers/lxd.

Fica já a nota que o docker e o lxd são concorrentes, mas talvez sejam mais complementares. O docker isola componentes, o lxd isola sistemas operativos. Terão por isso vantagens e desvantagens muitas vezes simétricas. E ambos são similares na tecnologia de base que usam (lxc e chroot + cgroups). Basicamente, usam capacidades do kernel linux para isolar componentes (é por isso que só podemos “virtualizar” componentes linux e não windows ou mac). Na verdade, e para ser tecnicamente menos incorrecto, não se criam máquinas virtuais, mas sim containers. Mas para simplicidade, no resto do texto quando falo de containers ou VM’s é tudo o mesmo… containers.

Aquilo que me entusiasma no LXD é a simplicidade de uso, com muito poucos  comandos, e a facilidade de gerir sistemas que integram vários componentes, como geralmente acontece numa plataforma SIG (qgis, postgresql, geoserver, etc.). Como o LXD virtualiza um SO inteiro, é natural e fácil usá-lo para conter sistemas aplicacionais inteiros.

As máquinas LXD podem ser criadas em directorias no nosso servidor Ubuntu, e podem conter diversos sistemas operativos. E é muito fácil clonar, fazer snapshots, parar e iniciar estes contentores de SO. Já cópias de segurança obrigam a alguma ginástica, mas estão a trabalhar num método simplificado.

Também é fácil limitar os recursos usados por cada VM, quer na memória máxima, quer no processador (cores, % de tempo, etc.), e vários outros recursos. Podem ver info completa aqui: https://stgraber.org/2016/03/26/lxd-2-0-resource-control-412/.

A existência do LXD vem baixar a complexidade da virtualização baseada em containers, deixando de usar hypervisors como o KVM, OpenVZ, VMWare, VirtualBox, e HyperV.

Podemos instalar o Ubuntu directamente na máquina física, e criar VM’s usando comandos LXD. Os seus defensores indicam ganhos de desempenho, ou mais correctamente menos perda de desempenho, e por isso potencial maior densidade de VM’s. Não posso confirmar nem desmentir porque não testei nada disto. Mas parece-me algo lógico.

Só mais um detalhe – o LXD suporta nested virtualization, ou seja, dentro de uma VM Ubuntu podemos usá-lo para criar VM’s, que estão dentro da nossa VM inicial… confuso? Cuidado, que o OpenVZ não suporta lxd… portanto ao comprarem uma VPS assegurem-se que é baseada em KVM.

Nested Virtualization… whaaat?

Instalar o LXD

Instalar o LXD é muito simples – já vem incluído no Ubuntu.

É preciso no entanto iniciá-lo uma primeira vez e definir a sua configuração, que principalmente define o tipo storage usado pelas máquinas a criar (baseadas em directorias ou pools zfs), e a configuração da rede que dirá se as VM’s são acessíveis do exterior, e se poderão comunicar entre si.

O tutorial que usei como base foi este da Digital Ocean: https://www.digitalocean.com/community/tutorials/how-to-set-up-and-use-lxd-on-ubuntu-16-04.

A diferença é que escolhi usar directorias para mais simplicidade, em vez de ZFS. Embora ZFS seja a opção recomendada pela Canonical, fabricante do Ubuntu, envolve uma complexidade que seria contraproducente para mim – o objectivo é ter simplicidade máxima e o menor número de pontos de falha. Mais info aqui: https://insights.ubuntu.com/2016/02/16/zfs-is-the-fs-for-containers-in-ubuntu-16-04/.

Então, a sessão de configuração do lxd foi assim (o meu user é dncpax e as respostas estão a negrito):

$ sudo usermod --append --groups lxd dncpax
$ sudo lxd init
Do you want to configure a new storage pool (yes/no) [default=yes]? y
Name of the storage backend to use (dir or zfs) [default=dir]: dir
Would you like LXD to be available over the network (yes/no) [default=no]? no
Do you want to configure the LXD bridge (yes/no) [default=yes]? yes

Como se vê escolhi sempre os defaults.

Neste ponto, o lxd lança um wizard de configuração da rede em modo texto (podemos usar num terminal). Se respondermos sempre com defaults, vamos ter no final uma rede interna gerida pelo LXD, do tipo 10.0.0.X.

Desta forma, as VM’s poderão contactar a máquina “mãe” e outras VM’s, e também a Internet. Mas não receberão tráfego exterior, ou seja, ninguém conseguirá aceder às nossas VM’s.

(Para permitir o acesso do “exterior” às nossas VM’s LXD vamos ter de usar algum tipo de router/firewall (IPTables) ou reverse proxy (NGinx ou HAProxy). Eu escolhi usar o HAProxy, que veremos noutro artigo.)

Durante o wizard, vai ser configurada uma rede IPv4, e opcionalmente uma rede IPv6. Eu usei sempre os defaults, mas não configurei a rede IPv6:

wizard do lxd para configurar a rede
wizard do lxd para configurar a rede IPv6

A minha rede foi configurada como 10.70.99.X, e assim todas as VM’s terão um IP deste tipo.

Utilizar LXD

A partir daqui temos os comandos do lxd ao dispor. Parte são baseados em lxc e outros em lxd. (lxc é o componente de base do lxd.)

Por exemplo, ver as VM’s criadas (não há ainda) – lxc list:

$ lxc list
+------+--------+------+------+------+-----------+
| NOME | ESTADO | IPV4 | IPV6 | TIPO | SNAPSHOTS |
+------+--------+------+------+------+-----------

Nota: se tiverem um erro de permissão negada, é porque o vosso novo grupo lxd não está ainda “activo”. Podem fechar a sessão e abrir uma nova com o mesmo login e continuar o trabalho, ou executarem “newgrp lxd”.

Ver a configuração do lxd – lxc info:

$ lxc info
config: {}
api_extensions:
- id_map
- id_map_base
- resource_limits
api_status: stable
api_version: "1.0"
auth: trusted
auth_methods: []
public: false
environment:
  addresses: []
  architectures:
  - x86_64
  - i686
  certificate: |
    -----BEGIN CERTIFICATE-----
    MIIFWzCCA0OgAwIVK3ESSSDQAIgte5p8cNcFQHJo0byAjiQD2YkT/sXr+3mf5U
    bla bla bla bla
    hTuelEv1+SWzBsN2nDbF7ZUDhw4cioBS59fkWidNrg==
    -----END CERTIFICATE-----
  certificate_fingerprint: 85ab3c61 bla 6d2bae
  driver: lxc
  driver_version: 2.0.8
  kernel: Linux
  kernel_architecture: x86_64
  kernel_version: 4.4.0-109-generic
  server: lxd
  server_pid: 3276
  server_version: 2.0.11
  storage: dir
  storage_version: ""

Então o que temos por agora? Um deamon lxd que coordenará a existência e o isolamento das VM’s a criar. Podemos ver o seu estado actual com systemctl (notem a memória ocupada):

systemctl status lxd
● lxd.service - LXD - main daemon
   Loaded: loaded (/lib/systemd/system/lxd.service; indirect; vendor preset: ena
   Active: active (running) since Qui 2018-01-18 21:35:36 WET; 14min ago
     Docs: man:lxd(1)
  Process: 3315 ExecStartPost=/usr/bin/lxd waitready --timeout=600 (code=exited,
  Process: 3277 ExecStartPost=/usr/lib/lxd/profile-config (code=exited, status=0
  Process: 3265 ExecStartPre=/usr/lib/x86_64-linux-gnu/lxc/lxc-apparmor-load (co
 Main PID: 3276 (lxd)
    Tasks: 9
   Memory: 7.5M
      CPU: 438ms
   CGroup: /system.slice/lxd.service
           └─3276 /usr/bin/lxd --group lxd --logfile=/var/log/lxd/lxd.log

Jan 18 21:35:35 ubuntumin systemd[1]: Starting LXD - main daemon...
Jan 18 21:35:36 ubuntumin lxd[3276]: lvl=warn msg="CGroup memory swap accounting
Jan 18 21:35:36 ubuntumin systemd[1]: Started LXD - main daemon.

Criar e gerir VM’s

Trabalhar com VM’s ou containers LXD é muito fácil, e basicamente limita-se a estes passos:

  1. Criar o container com o OS escolhido
  2. Aceder ao container na nossa shell ou por ssh
  3. Configurar alguns limites se quisermos (de memória p.e.)
  4. Parar ou reiniciar um container
  5. Ver os detalhes de um container, incluindo memória ocupada

A melhor página que conheço sobre gestão de containers LXD é esta: https://insights.ubuntu.com/2016/03/22/lxd-2-0-your-first-lxd-container/

Então para criar uma VM de Ubuntu dentro do nosso Ubuntu 😉 usamos o comando lxc launch. O sistema operativo que queremos usar pode ser escolhido de uma lista já extensa que pode ser consultada aqui: https://us.images.linuxcontainers.org/.

Antes de instalar uma máquina novinha vamos antes ver como temos a memória:

$ free -m
              total        used        free      shared  buff/cache   available
Mem:           2000          47        1501           3         451        1795

Como se vê, estamos só com 47MB usados e 451MB em cache.

Para instalar a nova vm chamada “wordpress1” (como é a 1ª vez, a imagem é descarregada do repositório online):

$ lxc launch ubuntu:16.04 wordpress1
A criar wordpress1
A iniciar wordpress1

E pronto. Temos um novo ubuntu fresquinho. Podemos ver na lista de containers com lxc list:

$ lxc list
+------------+---------+---------------------+------+-------------+-----------+
|    NOME    | ESTADO  |        IPV4         | IPV6 |    TIPO     | SNAPSHOTS |
+------------+---------+---------------------+------+-------------+-----------+
| wordpress1 | RUNNING | 10.70.99.165 (eth0) |      | PERSISTENTE | 0         |
+------------+---------+---------------------+------+-------------+-----------+

Podemos ver a lista dos containers, se estão iniciados, e os endereços IP internos.

Para vermos a configuração de um container, incluíndo a memória usada:

$ lxc info wordpress1
Nome: wordpress1
Remote: unix://
Arquitetura: x86_64
Criado: 2018/01/18 22:20 UTC
Estado: Running
Tipo: persistente
Perfis: default
Pid: 4426
Ips:
  eth0: inet    10.70.99.165    vethWJS19I
  eth0: inet6   fe80::216:3eff:fef3:793c        vethWJS19I
  lo:   inet    127.0.0.1
  lo:   inet6   ::1
Recursos:
  Processos: 27
  Memory usage:
    Memória (atual): 31.77MB
    Memória (máxima): 152.54MB
  Network usage:
    lo:
      Bytes recebidos: 0B
      Bytes enviados: 0B
      Pacotes recebidos: 0
      Pacotes enviados: 0
    eth0:
      Bytes recebidos: 195.18kB
      Bytes enviados: 7.45kB
      Pacotes recebidos: 138
      Pacotes enviados: 90

Até agora a memória ocupada variou entre 31 e 157 MB…

Para aceder podemos usar ssh ou um comando do lxc (exec):

$ lxc exec wordpress1 -- sudo --login --user ubuntu
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

ubuntu@wordpress1:~

Estamos agora “dentro” da nova máquina wordpress1, com o login ubuntu… podemos ver os seus processos de forma isolada, sem acesso à máquina mãe:

$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.2  37596  5616 ?        Ss   22:20   0:00 /sbin/init
root        55  0.0  0.1  41724  3284 ?        Ss   22:20   0:00 /lib/systemd/sy
root        57  0.0  0.1  35280  3228 ?        Ss   22:20   0:00 /lib/systemd/sy
root       232  0.0  0.0  16000   856 ?        Ss   22:20   0:00 /sbin/dhclient
daemon     318  0.0  0.1  26048  2164 ?        Ss   22:20   0:00 /usr/sbin/atd -
syslog     324  0.0  0.1 186904  3288 ?        Ssl  22:20   0:00 /usr/sbin/rsysl
root       326  0.0  0.0  20104  1208 ?        Ss   22:20   0:00 /lib/systemd/sy
root       327  0.0  0.2 272872  5960 ?        Ssl  22:20   0:00 /usr/lib/accoun
root       328  0.0  0.2  65524  5476 ?        Ss   22:20   0:00 /usr/sbin/sshd
message+   329  0.0  0.1  42896  3828 ?        Ss   22:20   0:00 /usr/bin/dbus-d
root       341  0.0  0.1  26072  2528 ?        Ss   22:20   0:00 /usr/sbin/cron
root       342  0.0  1.2 158844 26576 ?        Ssl  22:20   0:00 /usr/lib/snapd/
root       350  0.0  0.4 277184  8260 ?        Ssl  22:20   0:00 /usr/lib/policy
root       378  0.0  0.0  12848  1784 ?        Ss+  22:20   0:00 /sbin/agetty --
root       441  0.0  0.1  59508  4032 ?        Ss   22:25   0:00 sudo --login --
ubuntu     442  0.0  0.2  23012  5064 ?        S    22:25   0:00 -bash
ubuntu     459  0.0  0.1  37764  3276 ?        R+   22:26   0:00 ps aux

A partir daqui podemos instalar o que quisermos da forma habitual. Podemos aceder ao exterior, mas não o inverso – do exterior aceder ao nosso container. E isso será para resolver noutro post.

Por agora, deixem-me terminar com um pequeno teste de desempenho. Uso sempre o 7zip para testes rápidos de cpu. Portanto vamos instalar o 7zip no container testar o cpu com só 1 thread, e depois fazer o mesmo na máquina mãe, para vermos neste caso a quebra de rendimento.

$ sudo apt-get update
Hit:1 http://archive.ubuntu.com/ubuntu xenial InRelease
bla bla
Fetched 24.6 MB in 10s (2276 kB/s)
Reading package lists... Done
$ sudo apt-get install 7zip-full
bla bla
Setting up p7zip-full (9.20.1~dfsg.1-4.2) ...
$ 7z b -mmt1

7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18
p7zip Version 9.20 (locale=C.UTF-8,Utf16=on,HugeFiles=on,2 CPUs)

RAM size:    2000 MB,  # CPU hardware threads:   2
RAM usage:    419 MB,  # Benchmark threads:      1

Dict        Compressing          |        Decompressing
      Speed Usage    R/U Rating  |    Speed Usage    R/U Rating
       KB/s     %   MIPS   MIPS  |     KB/s     %   MIPS   MIPS

22:    3917   100   3811   3810  |    41983   100   3791   3790
23:    3735   100   3806   3806  |    39076   100   3578   3577
24:    3337   100   3589   3588  |    39359   100   3652   3652
25:    3339   100   3812   3812  |    39856   100   3748   3748
----------------------------------------------------------------
Avr:          100   3755   3754               100   3692   3692
Tot:          100   3723   3723

Portanto, dentro do container temos um total de 3723 MIPS de compressão. Vamos sair do container e fazer o mesmo exercício:

ubuntu@wordpress1:~$ exit
logout
dncpax@ubuntumin:~$
dncpax@ubuntumin:~$ 7z b -mmt1

7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18
p7zip Version 9.20 (locale=pt_PT.UTF-8,Utf16=on,HugeFiles=on,2 CPUs)

RAM size:    2000 MB,  # CPU hardware threads:   2
RAM usage:    419 MB,  # Benchmark threads:      1

Dict        Compressing          |        Decompressing
      Speed Usage    R/U Rating  |    Speed Usage    R/U Rating
       KB/s     %   MIPS   MIPS  |     KB/s     %   MIPS   MIPS

22:    3815   100   3712   3711  |    41670   100   3762   3762
23:    3696   100   3767   3766  |    41115   100   3765   3764
24:    3557   100   3825   3825  |    40444   100   3753   3753
25:    3408   100   3891   3891  |    40016   100   3764   3763
----------------------------------------------------------------
Avr:          100   3799   3798               100   3761   3761
Tot:          100   3780   3779

Temos então na máquina LXD 3780 MIPS de compressão.

Ou seja, 3723/3780 = 98% de desempenho no container! Sim, é um teste simplista, e não representa uma avaliação real, mas é óptimo indicador.

E pronto, acabo por aqui. O próximo post da série será dedicado a coisas mais sérias, como ter várias máquinas LXD a comunicar com o exterior, e ver como limitar os recursos usados por uma delas. Até breve.

Mosaicos com áreas transparentes

Neste artigo regresso a um assunto já familiar neste blog – criar mosaicos de ortofotomapas usando o GDAL – (sim eu sei, outra vez?) mas como tenho andado às voltas com as áreas sem informação, que surgem negras nos mosaicos pensei em postar o que acabei por fazer. A solução final é usar máscaras, e não bandas alfa como habitual. Vamos ver como e porquê… assume-se já alguma familiaridade com o GDAL, mas pode sempre saltar as partes teóricas aborrecidas e ver os comandos usados no final do artigo 😉 Ler artigo completo

PostgreSQL e ESRI – parte 4

O subtítulo deste artigo devia ser “O bom, o mau e o péssimo”…

Depois de ter respondido a um comentário que me perguntava sobre a nossa experiência em curso de migrar para PostgreSQL, pensei em melhorar a resposta e fazer um artigo – a maior parte da escrita já estava feita de qualquer forma 😉

Responder ao comentário levou-me a pensar mais um pouco sobre a questão… e uma parte que me parecia pouco clara é o porquê de fazermos a migração para PostgreSQL (pgsql prós amigos) e porquê insistir em usar geometrias PostGIS (geometrias pg)? Só para recordar: a ESRI permite 2 formatos de armazenamento das geometrias nas bases de dados que suporta – ou no formato ESRI (que chamou de ST_Geometry) ou no formato “nativo” da bd.
Ler artigo completo

Desenvolver aplicações SIG de forma Agile!

Estou a digerir tudo o que vi na formação de 3 dias sobre metodologias Agile e Scrum e este post servirá como bloco de notas.

Esta semana estive 3 dias em formação com o excelente formador Mitch Lacey. Este sr. tem já uma grande experiência em projectos de desenvolvimento de software, e ouvi-lo foi realmente uma experiência enriquecedora.

Não vou entrar em grandes detalhes teóricos sobre Agile/Scrum, porque não sou definitavemente a pessoa indicada para o fazer. Vou em vez disso enumerar os pontos que mais me impressionaram e que mais valor terão se os integrar na forma como a “minha” equipa funciona. Portanto, as afirmações seguintes devem ser encaradas como uma perspectiva muito pessoal…

Agile é um conjunto de práticas que visam desenvolver um projecto de forma iteractiva, com ciclos curtos entre versões intermédias antes de chegar à entrega final, com vista a reduzir os riscos associados a este tipo de projectos – sendo o maior entregar um produto que não se ajusta à visão do cliente!

Scrum é a metodologia Agile mais difundida, e obteve o seu nome do Rugby: scrum é a altura em que os jogadores se reunem para reiniciar o jogo, e ambas as equipas disputam a bola. Em Agile, Scrum é um método em que uma equipa de desenvolvimento se concentra ferozmente em terminar um conjunto de tarefas num prazo curto – tipicamente 14 ou 28 dias – e se compromete a no final do prazo produzir uma versão funcional do produto que está a desenvolver.

Qual é a grande diferença em relação ao processo tradicional (designado “Waterfall” ou “em Cascata“)? É que no processo Waterfall, os requisitos para a aplicação a desenvolver são definidos à partida, na fase de levantamento e definição dos mesmos. Em seguida, esta lista de especificações é trabalhada com o cliente final, e atinge-se um acordo, sendo fechada a lista de itens a implementar. E é aqui que este método tem a sua maior fraqueza: é pouco flexível, e não considera facilmente requisitos que se alteram com o tempo (alguém se identifica com esta experiência??). A analogia de construir uma casa é muito clarificadora: ao olhar para a planta da minha futura casa, posso ficar muito satisfeito com o que está planeado. Mas, mais tarde, ao andar pela estrutura já construída da casa, vou conseguir obter uma visão mais clara do que estava na planta, e consequentemente vou definir melhoramentos e até novas funções que quero ter na versão final da casa. Isto é um processo natural e não deve ser combatido. E é o que os métodos Agile nos oferecem: ao cliente final permite algum controlo durante a vida do projecto, e à equipa de desenvolvimento permite um grande poder de adaptação à mudança, permitindo concluir o projecto dentro do prazo e do orçamento, mesmo absorvendo alterações aos pressupostos iniciais. E para descansar os espíritos mais desconfiados, resta acrescentar que esta metodologia é reconhecida pelo PMI, e é usada pelas maiores empresas de software como Microsoft ou IBM.

Porque é que estas técnicas me interessaram? Os pontos mais importantes:

  • A equipa de desenvolvimento com que trabalho é pequena – podemos contar com 2,5 técnicos (sim, temos realmente uma metade de técnico!), e somos facilmente dispersados por várias solicitações em simultâneo
  • O meio onde nos inserimos é muito dinâmico – as alterações aos requisitos são muito frequentes, e a sua definição inicial é muito difícil de obter e estabilizar
  • Trabalhamos para o “Cliente Interno”, onde as relações informais dominam, e os processos formais inerentes ao método Waterfall dificilmente são aplicados e respeitados

Da abordagem Scrum, estes são os pontos que mais me agradam e que vejo possibilidade de implementar rapidamente:

  1. Definir o “Product Backlog”, que mais não é que uma lista de funções a implementar numa aplicação, ordenada por prioridade
  2. Definir o “Sprint Backlog”, que é a lista prioritizada de funções que vamos implementar no próximo ciclo de 14 dias (o sprint)
  3. Reuniões relâmpago diárias, onde rapidamente (15 min.) cada um dos elementos da equipa percorre estes 3 pontos – o que concretizaste ontem, o que vais fazer hoje, e tens algum ponto crítico?
  4. Estar atento a solicitações de alterações ou novas funções, mas lutar por integrá-las no Product Backlog, que poderá ser re-prioritizado todas as semanas (mas as funções só podem entrar num sprint no seu início)
  5. Consciencializar todos os elementos da equipa de que todos são responsáveis pela equipa! Todos se devem preocupar com a saúde emocional da equipa e com a concretização dos compromissos assumidos com o exterior (notem aqui a grande diferença mental entre cumprir objectivos e cumprir compromissos)
  6. Obter uma versão funcional no final de cada Sprint! E demonstrá-la. Na teoria Scrum, a demo deve ser feita ao cliente. No meu caso, parece-me mais plausível realizar uma demo interna à equipa, para validar a direcção dada ao projecto, detectar incongruências, enfim, andar pela estrutura da casa e ver se é como imaginámos quando fizemos a planta…

É realmente uma forma de trabalhar muito aliciante, e que de alguma forma torna o desenvolvimento de projectos mais humano.

Algumas ferramentas consideradas essenciais a uma boa prática Agile já utilizamos na equipa:

  • Repositório de código e Controle de versões – usamos o SVN, e o AnkhSVN para integrar com o Visual Studio. Não há checkin de código que não compile. Este é um passo fundamental na organização da equipa!
  • Documentação – embora não tenhamos ainda a prática de documentação automática do código, embora já se tenha discutido o assunto várias vezes, usamos um Wiki para documentar quer a vertente técnica quer a vertente de utilização das aplicações

E das peças que nos faltam, o que podemos integrar no nosso caso particular?

  • Teste unitários – é algo que deveremos implementar, mas que está ainda algo longíquo de ser possível… é necessário primeiro re-organizar o código em componentes suficientemente pequenos, modulares, para possibilitar esta técnica. Mas os ganhos são óbvios – rapidez de efectuar testes e na detecção de bugs
  • Test Driven Development – ver ponto anterior, é algo desejável, mas cuja execução obriga a remodelar o repositório de código existente. Talvez a longo prazo seja possível…

Para mais informação recomendo vivamente a leitura deste artigo: “Agile Project Management for GIS“. Muitos mais podem ser encontrados “googlando” a Internet (como por exemplo este na revista PM Network, pp 42). E para aqueles que ficarem convencidos, fica a referência do curso – Fullsix.