Postgres – Criar estatísticas

O Postgres é “tão evoluído” e blá blá blá, mas não consegue ter uma rotina de criação automática de estatísticas. Parece que voltamos para antes dos anos 2000.

Bom, não vou explicar a importância da criação de estatísticas, isso você já deveria saber, se não sabe, pergunta para sua IA favorita.

A código abaixo vai ler a tabela pg_stat_statements e, a partir dela, eu tento fazer um monte de regex para separar das querys a parte do SARG para criar as estatísticas.

É preciso adicionar a extensão pg_stat_statements:

create extension pg_stat_statements;

Dependendo do seu workload logo em seguida você já vai ter acesso a alguma coisa, mas para ter dados melhores, é melhor deixar o tempo passar para acumular mais informações.

Depois de algum tempo, quando você rodar o script abaixo, deve ter um resultado mais interessante para criar as estatísticas:

WITH normalized AS (SELECT queryid
                         , query
                         -- Remove DO $ ... $ e normaliza espaços
                         , lower(regexp_replace(
            regexp_replace(query, '^do \$\$|\\$\$;$', '', 'gi'),
            '\s+', ' ', 'g'
                                 )) AS norm_query
                         , total_exec_time
                         , calls
                    FROM pg_stat_statements
                    WHERE query ILIKE 'select%'
                      AND calls > 10
                      AND query ~* ' where ')
   , tables AS (SELECT n.queryid
                     , n.query
                     , n.norm_query
                     , n.total_exec_time
                     , n.calls
                     -- Captura tabela e alias
                     , (regexp_match(n.norm_query,
                                     '(?:from|join)\s+([a-z0-9_\.]+)(?:\s+as\s+|\s+)?([a-z0-9_]+)?'))[1] AS table_obj_full
                     , (regexp_match(n.norm_query,
                                     '(?:from|join)\s+([a-z0-9_\.]+)(?:\s+as\s+|\s+)?([a-z0-9_]+)?'))[2] AS table_alias
                     , COALESCE(
            (regexp_match(n.norm_query, '(?:from|join)\s+([a-z0-9_\.]+)(?:\s+as\s+|\s+)?([a-z0-9_]+)'))[2],
            split_part((regexp_match(n.norm_query, '(?:from|join)\s+([a-z0-9_\.]+)'))[1], '.', 2)
                       )                                                                                 AS main_identifier_raw
                     , split_part(
            lower(
                    regexp_replace(
                            (regexp_match(n.norm_query, '(?:from|join)\s+([a-z0-9_\.]+)'))[1],
                            '[^a-z0-9_\.]',
                            '',
                            'g'
                    )
            ),
            '.',
            CASE WHEN (regexp_match(n.norm_query, '(?:from|join)\s+([a-z0-9_\.]+)'))[1] LIKE '%.%' THEN 2 ELSE 1 END
                       )                                                                                 AS table_name
                FROM normalized n
                WHERE (regexp_match(n.norm_query, '(?:from|join)\s+([a-z0-9_\.]+)'))[1] IS NOT NULL)
   , where_clauses AS (SELECT t.queryid
                            , t.table_name
                            , lower(t.main_identifier_raw) AS main_identifier
                            , (regexp_match(
            t.norm_query,
            'where\s+(.*?)(?:\sgroup by|\sorder by|\slimit|;|$)'
                               ))[1]                       AS where_block
                            , t.total_exec_time
                            , t.calls
                       FROM tables t)
   , where_columns AS (SELECT wc.table_name
                            , wc.main_identifier
                            , CASE
                                  WHEN rm.m[1] LIKE '%.%' THEN lower(split_part(rm.m[1], '.', 1))
                                  ELSE NULL END                                 AS column_prefix
                            , rm.m[1]                                           AS full_column_name
                            , regexp_replace(rm.m[1], '^[a-z0-9_]+\.', '', 'i') AS column_name
                            , wc.total_exec_time
                       FROM where_clauses wc
                                CROSS JOIN LATERAL regexp_matches(
                               wc.where_block,
                               '([a-z_][a-z0-9_\.]*)\s*(=|>|<|>=|<=|<>|in\b|like\b)',
                               'gi'
                                                   ) AS rm(m))
   , distinct_columns AS (SELECT table_name
                               , column_name
                               , SUM(total_exec_time) AS total_cost
                          FROM where_columns
                          WHERE column_name IS NOT NULL
                            AND (
                              column_prefix IS NULL
                                  OR column_prefix = main_identifier
                                  OR column_prefix = table_name
                              )
                          GROUP BY table_name, column_name)
   , validated_columns AS (SELECT dc.table_name
                                , dc.column_name
                                , dc.total_cost
                           FROM distinct_columns dc
                                    JOIN information_schema.columns isc
                                         ON lower(isc.table_name) = lower(dc.table_name)
                                             AND lower(isc.column_name) = lower(dc.column_name)
                           WHERE dc.table_name NOT LIKE 'pg_%')
   , family AS (SELECT vc.table_name
                     , (SELECT array_agg(c)
                        FROM (SELECT column_name AS c
                              FROM validated_columns sub
                              WHERE sub.table_name = vc.table_name
                              ORDER BY total_cost DESC
                              LIMIT 8) sub2)       AS columns
                     , COUNT(DISTINCT column_name) AS occurrences
                     , SUM(total_cost)             AS total_table_cost
                FROM validated_columns vc
                GROUP BY vc.table_name
                HAVING COUNT(DISTINCT column_name) > 1)
   , correlation_estimate AS (SELECT f.*
                                   , (array_length(f.columns, 1) ^ 1.3) * ln(total_table_cost + 5) AS score
                              FROM family f)
   , existing_ext_stats AS (SELECT cls.relname                                         AS table_name
                                 , st.stxname                                          AS stat_name
                                 , array_agg(att.attname ORDER BY att.attname)::text[] AS stat_columns
                            FROM pg_statistic_ext st
                                     JOIN pg_class cls ON cls.oid = st.stxrelid
                                     JOIN pg_attribute att
                                          ON att.attrelid = cls.oid
                                              AND att.attnum = ANY (st.stxkeys)
                            GROUP BY cls.relname, st.stxname)
   , dedup AS (SELECT ce.*
                    , 's_' || regexp_replace(ce.table_name, '[^a-z0-9_]', '', 'g') ||
                      '_' || substr(md5(array_to_string(ce.columns, ',')), 1, 8) AS stat_name
                    , EXISTS (SELECT 1
                              FROM existing_ext_stats es
                              WHERE es.table_name = ce.table_name
                                AND es.stat_columns = ce.columns::text[])        AS already_exists
               FROM correlation_estimate ce)

SELECT table_name
     , columns
     , occurrences
     , round(score::numeric, 2)                                AS score
     , stat_name
     , 'CREATE STATISTICS IF NOT EXISTS ' ||
       quote_ident(stat_name) ||
       ' ON ' ||
       array_to_string(
               ARRAY(SELECT quote_ident(c) FROM unnest(columns) c), ', '
       ) ||
       ' FROM ' ||
       quote_ident(table_name) ||
       ';'                                                     AS suggested_cmd
     , 'Filtros mais custosos sobre (' ||
       array_to_string(columns, ', ') ||
       ') em ' || table_name ||
       '. Ocorrências: ' || occurrences ||
       '. Custo total estimado: ' || total_table_cost || 'ms.' AS justification
FROM dedup
WHERE NOT already_exists
  AND table_name NOT LIKE 'pg_%'
  AND table_name NOT IN ('table_constraints', 'columns', 'tables')
  AND array_length(columns, 1) > 1
ORDER BY score DESC, total_table_cost DESC, occurrences DESC;

GCP – CloudSQL – Painel de Governança e updates

Eu precisava de um lugar fácil para ver todos os bancos de dados na nuvem do Google com informações de versões e atualizações disponíveis e as janelas dessas atualizações.

Usando como base o python do post [GCP – Executar script em várias instâncias pg], agora é possível listar todas as instâncias em todos os projetos com a versão do banco, qual a atualização disponível e para quando está agendada, bem como a janela de manutenção para a instância.

Também é possível baixar a informação para um arquivo CSV e ordenar colunas.

Não é possível aplicar as atualizações através dessa interface justamente para evitar um efeito “ooopppssss”.

O Script com o código pode ser encontrado em:
https://github.com/bigleka/gcp/blob/main/dashboard_updates.py

Atualização 20251231

Criei uma versão powershell, pra focar na linha de comando e que acaba sendo mais prática já que não precisa de tanta dependência pra rodar.

https://github.com/bigleka/gcp/blob/main/Get-CloudSQLUpdates.ps1

é mais rápido, simples, bom,,, é linha de comando, não tem nada melhor.

Postgres – vacuum e analyze verbose

Depois de passar algum tempo analisando a saída do vacuum e do analyze, para entre 800 tabelas descobrir quais estão com algum possível problema, montei esse script que, novamente, abre uma tela para fazer essa análise e indicar possíveis necessidades de atenção.

https://github.com/bigleka/pg-scripts/blob/main/vacuum_analyzer.py

Ele consegue analisar:

  • Vacuum Analyze ou
  • Verbose Analyze ou
  • ou os dois ao mesmo tempo.

É bem simples, execute o vacuum verbose e o analyze verbose no seu aplicativo favorito e depois copie e cole o resultado, clique em “Analisar” e pronto, temos alguma coisa para analisar.

GCP – Clonar instâncias entre projetos

Imagine o seguinte cenário:

Você organizou a estrutura da sua conta GCP para trabalhar com projetos distintos;

Fez as TAGs para poder ter os valores corretos por projeto e saber exatamente quanto cada um custa;

Agora precisa copiar uns bancos que estão em um servidor CloudSQL de um projeto para outro projeto, fácil certo? Até descobrir que a GCP não faz de forma fácil.

Mas tem outras formas, da pra gerar um backup do banco, mandar para o bucket, copiar o arquivo de um bucket para outro e depois restaurar, ou usar um servidor intermediário, faz a mesma coisa mas manda o arquivo para esse servidor e restaurado no outro, claro que dá, além de tomar tempo e mesmo que você consiga fazer em paralelo, quantos bancos consegue fazer? o servidor aguenta? o GCP tem limite de IO e operações de Leitura/Gravação no CloudSQL, além de inevitavelmente ser lento.

Eles tem a opção de restaurar um backup da instância em cima de outra instância no mesmo projeto, então deveria ter a opção de fazer a mesma coisa em projetos distintos, no final das contas eles tem, mas tem que ser feito por meio de requisição de API.

No link abaixo montei um python que vai carregar suas credenciais, listar os projetos que você tem direito de acesso, lista as instâncias, backups das instâncias e ai te ajuda a restaurar esse backup em uma outra instância em qualquer outro CloudSQL de qualquer outro projeto.

https://github.com/bigleka/gcp/blob/main/gcp_restore_cloud_database_in_another_project.py

O app é bem simples:

  • Clique em “Carregar Credenciais GCP”;
  • localize o arquivo json gerado pelo comando.
  • gcloud auth application-default login
  • ele vai carregar a lista dos projetos que você tem acesso nas duas listas de “Origem” e “Destino”;
  • Clicando no projeto ele vai listar as instâncias na combobox abaixo da lista dos projetos.
  • Clicando na instância de banco ele vai listar os backups disponíveis, selecione o backup que você quer restaurar na outra instância.
  • Agora faça a mesma coisa no “Destino”, selecione o projeto e a instância.
  • ELE VAI SOBRESCREVER a instância de destino, apagando e restaurando o backup por cima da instância do destino.
  • e clique em “Restaurar”.

Como o processo é assíncrono, caso você queira acompanhar o status da restauração, vá para o console da GCP e acompanhe de lá.

GCP – Copiar flags entre instâncias

Para quem já trabalhou com o GCP CloudSQL sabe a dor de cabeça que é não ter uma forma fácil de gerenciar as flags das instâncias ou uma forma fácil de copiar as flags de uma instância para outra.

Parece que eles gostam muito de dificultar uma administração que deveria ser simples.

Bom, no link abaixo eu montei um outro python que carrega a lista de projetos que você tem acesso, lista as instâncias e as flags, ai você pode escolher quais vão ser aplicadas na outra instância e até editá-las antes de aplicar.

Trabalho em progresso:

Ainda estou tentando listar quais as flags que são obrigatórias de ter reboot, então, por enquanto, assuma que todas vão causar algum tipo de reboot no destino (mesmo que não causem).

https://github.com/bigleka/gcp/blob/main/gcp_copy_cloudsql_flags.py

Eu sou muito fã de linha de comando, mas como as pessoas gostam de interface gráfica, esse python usa o tkinter para montar uma tela zoada para você ser mais feliz.

Basicamente, deixei todas as dependências que precisam ser instaladas na parte comentada do código.

GCP – Executa script em várias instâncias PG

AAAhhh o saudoso SSMS tem uma feature inigualável que é o “Registered Servers“, atende a necessidade de forma incrível.

As outras ferramentas de administração de banco que tem por ai como DBeaver, DataGrip, PGAdmin, etc., tentam ser generalistas e acabam deixando de ter esse tipo de feature.

Como esses, longos e longos dias, estou administrando e migrando vários ambientes Postgres na GCP, montei um script em python que usa o streamlit para fazer mais ou menos o que o “Registered Servers” faz, claro que não com toda a glória do SSMS, mas ainda estou trabalhando nisso.

o código encontra-se no meu repositório do GIT.

https://github.com/bigleka/gcp/blob/main/prototipo_streamlit_cloudsql.py

Ele funciona da seguinte forma:

Usando o arquivo .json, criado através do gcloud, com os dados da sua credencial para usar a API da GCP e listar os projetos e instâncias de bancos de cada projeto que você tem direito de acesso.

Ai tem um botão para carregar as bases de dados de cada instância, não fiz tudo de uma vez porque sobrecarrega o streamlit.

Ai usando uma credencia de banco, não a credencial do IAM (pq depende de um monte de configuração que pode ou não estar ajustada na instância) conseguimos executar em paralelo o mesmo script em várias instâncias nos bancos selecionados.

Problemas conhecidos:

  • Ainda estou trabalhando em ajustar a forma de operação para sessão para conseguir executar scripts que precisam de operação de sessão.
  • Estou vendo o problema dele fechar o grupo do projeto quando seleciono qualquer coisa mesmo tendo selecionado anteriormente alguma coisa, isso não é bem um problema, só é chato.
  • Ajuste já adicionar o status das execuções assim que elas acabarem na barra da esquerda.

Em testes:

  • autenticação web
  • salvar resultados em csv
  • rodar em docker

Ordem da chave primária

A ideia desses 2 scripts é fazer uma simples análise e sugerir uma possível melhoria na ordem da chave primária de uma tabela específica.

Estes scripts não são para serem seguidos a ferro e fogo, existem outros fatores para serem considerados na ordem de uma PK, mas para quem não tem nada, e quer ter pelo menos uma ideia para onde ir montei eles como procedure para o SQL e como função para o Postgresql.

Versão SQL Server:

ALTER PROCEDURE usp_SugerirNovaOrdemChavePrimaria
    @SchemaName NVARCHAR(128),
    @TableName NVARCHAR(128)
AS
BEGIN
	SET ARITHABORT OFF 
	SET ANSI_WARNINGS OFF
    DECLARE @ColumnName NVARCHAR(128);
    DECLARE @Sql NVARCHAR(MAX) = '';
    DECLARE @OrderSuggestions NVARCHAR(MAX) = '';
    DECLARE @DynamicSQL NVARCHAR(MAX);
    DECLARE @Densidade FLOAT;
    DECLARE @OrderTable TABLE (ColumnName NVARCHAR(128), Densidade FLOAT);
    DECLARE @CurrentOrder NVARCHAR(MAX) = '';

    -- Cursor para iterar sobre as colunas da chave primária
    DECLARE ColumnCursor CURSOR FOR
        SELECT COL_NAME(ic.object_id, ic.column_id) 
        FROM sys.indexes AS i 
        INNER JOIN sys.index_columns AS ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id 
        WHERE i.is_primary_key = 1 
        AND OBJECT_NAME(ic.object_id) = @TableName;

    OPEN ColumnCursor;
    FETCH NEXT FROM ColumnCursor INTO @ColumnName;

    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- Constrói e executa a consulta SQL dinâmica para calcular a densidade para cada coluna
        SET @DynamicSQL = 'SELECT @DensidadeOUT = (COUNT(DISTINCT ' + QUOTENAME(@ColumnName) + ') * 1.0 / COUNT(*)) FROM ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@TableName);
        EXEC sp_executesql @DynamicSQL, N'@DensidadeOUT FLOAT OUTPUT', @Densidade OUTPUT;

        -- Adiciona os resultados em uma tabela temporária
        INSERT INTO @OrderTable (ColumnName, Densidade) VALUES (@ColumnName, @Densidade);

        -- Constrói a ordem atual
        SET @CurrentOrder += @ColumnName + ', ';

        FETCH NEXT FROM ColumnCursor INTO @ColumnName;
    END

    CLOSE ColumnCursor;
    DEALLOCATE ColumnCursor;

    -- Remove a última vírgula e espaço da ordem atual
    IF LEN(@CurrentOrder) > 0
    BEGIN
        SET @CurrentOrder = LEFT(@CurrentOrder, LEN(@CurrentOrder) - 1);
    END

    -- Constrói a sugestão de ordem com base na densidade
    SELECT @OrderSuggestions += ColumnName + ', '
    FROM @OrderTable
    ORDER BY Densidade ASC, ColumnName;

    -- Remove a última vírgula e espaço
    IF LEN(@OrderSuggestions) > 0
    BEGIN
        SET @OrderSuggestions = LEFT(@OrderSuggestions, LEN(@OrderSuggestions) - 1);
    END

    -- Compara a ordem atual com a sugerida
    IF @CurrentOrder = @OrderSuggestions
    BEGIN
        SELECT @TableName as [Object], 'A ordem atual já é a melhor.' AS SuggestedOrder;
    END
    ELSE
    BEGIN
        -- Retorna a sugestão de ordem
        SELECT @TableName as [Object], @OrderSuggestions AS SuggestedOrder;
    END
END

Versão para Postgresql:

CREATE OR REPLACE FUNCTION mc1_sugerir_nova_ordem_chave_primaria(schema_name text, nome_tabela text)
RETURNS text AS $$
DECLARE
    coluna_atual record;
    ordem_sugerida text := '';
    ordem_atual text := '';
    query text;
BEGIN
    -- Prepara a query para obter a ordem atual das colunas da chave primária
    FOR coluna_atual IN
        SELECT kcu.column_name
        FROM information_schema.table_constraints AS tc
        JOIN information_schema.key_column_usage AS kcu
            ON tc.constraint_name = kcu.constraint_name
            AND tc.table_schema = kcu.table_schema
        WHERE tc.constraint_type = 'PRIMARY KEY'
            AND tc.table_name = nome_tabela
            AND tc.table_schema = schema_name
        ORDER BY kcu.ordinal_position
    LOOP
        ordem_atual := ordem_atual || coluna_atual.column_name || ', ';
    END LOOP;

    -- Remove a última vírgula e espaço da ordem atual
    IF LENGTH(ordem_atual) > 0 THEN
        ordem_atual := substr(ordem_atual, 1, LENGTH(ordem_atual) - 2);
    END IF;

    -- Prepara a query para calcular a densidade das colunas da chave primária
    query := format($f$
        SELECT 
            kcu.column_name,
            (COUNT(DISTINCT %I) * 1.0 / COUNT(*)) AS densidade
        FROM 
            information_schema.table_constraints AS tc
        JOIN 
            information_schema.key_column_usage AS kcu
            ON tc.constraint_name = kcu.constraint_name
            AND tc.table_schema = kcu.table_schema
        JOIN 
            %I.%I AS t
            ON true
        WHERE 
            tc.constraint_type = 'PRIMARY KEY'
            AND tc.table_name = %L
            AND tc.table_schema = %L
        GROUP BY 
            kcu.column_name
        ORDER BY 
            densidade DESC, kcu.column_name
    $f$, 'column_name', schema_name, nome_tabela, nome_tabela, schema_name);

    -- Executa a consulta e processa os resultados
    FOR coluna_atual IN EXECUTE query
    LOOP
        ordem_sugerida := ordem_sugerida || coluna_atual.column_name || ', ';
    END LOOP;

    -- Remove a última vírgula e espaço
    IF LENGTH(ordem_sugerida) > 0 THEN
        ordem_sugerida := substr(ordem_sugerida, 1, LENGTH(ordem_sugerida) - 2);
    END IF;

    -- Compara a ordem atual com a sugerida
    IF ordem_atual = ordem_sugerida THEN
        RETURN 'A ordem atual já é a melhor.';
    ELSE
        RETURN ordem_sugerida;
    END IF;
END;
$$ LANGUAGE plpgsql;

AWS – VPN Server

Existem vários motivos para você querer rotear sua saída de internet por outro local ao invés do seu ISP de costume, privacidade? segurança? acesso a outros conteúdos de mídia?

O motivo em si é irrelevante, hoje contamos com diversos serviços de VPN pagos e gratuitos muito bons por aí, mas da pra confiar neles? uma VPN é basicamente um contrato de mão dupla, você confia em uma ponta enquanto a outra ponta confia em você, é estabelecido um canal de criptografia entre essas duas pontas e os dados podem ou não ser roteados através desse canal, em uma visão simplista uma análise de tráfego ou scan de rede pode partir de uma das pontas para “analisar” o que encontra-se do outro lado, por isso, mesmo uma VPN meia-boca costuma ter firewall restringindo quem e da onde pode vir o tráfego, mas em dispositivos de celular isso é quase impossível.

Claro que existem serviços honestos de VPN por aí, pagos ou gratuitos, mas por que ariscar se você pode montar um para você,,, e de graça,,,

Pra começar é simples, crie uma conta na AWS.

Depois vamos usar o serviço de Marketplace:

Procure por OpenVPN Access Server

Escolha a opção de Continue to Subscribe

Aceite os termos

Muitos termos

é só esperar para ele ativar o serviço na sua conta

Basicamente terminada a subscrição agora vamos iniciar uma máquina EC2 com o OpenVPN, escolha o local onde você vai hospedar a máquina EC2, basicamente qualquer lugar do mundo dentro da nuvem da AWS serve.

eu disse que era grátis? foi mesmo,,, você tem que alterar o tipo de máquina que vai ser usada para hospedar o OpenVPN para o modelo t2.micro

nessa parte onde você troca a máquina.

Aqui vem um detalhe, esse ambiente é grátis por 1 ano, se você já usou esse serviço de EC2 da AWS alguma vez e não aparecer a que vai ser grátis então chegou até aqui a toa, eles vão te cobrar, mas cabaçada sua de não prestar atenção nos termos de uso dos serviços deles…

VPC e Subnet a sua escolha, o que importa são as regras de firewall e o IP valido que vai ser criado para a máquina.

Como você está criado a máquina através de um template, ele já vem com um conjunto de regras pré definidas.

Essa parte é importante, salve a chave para que você tenha acesso a essa máquina através de SSH.

Basicamente após clicar em “Launch” a AWS vai criar a máquina, na região, com regras de firewall e o OpenVPN para você.

Mas não acabou por aqui, afinal, você vai querer acessar essa VPN.

Para acessar essa VPN antes você vai precisar configurar ela, nada tão complicado,,, apenas preste atenção nos detalhes, eles são a grande diferença

Após a máquina iniciar, você terá um endereço de IP válido e uma entrada de DNS. Se você não esqueceu de liberar o seu IP lá na regra de firewall você deve conseguir acessar esse servidor.

Antes de sair tentando acessar pelo Browser e configurar as coisas, você precisa logar no servidor e aceitar aqueles contratos, que como TODOS fazemos lemos até o fim um a um,,,, um a um,,,

Para acessar o servidor não precisa de nenhuma ferramenta especial (caso você esteja usando pelo menos o Windows 10), nos Linux e Mac’s da vida o ssh vem por padrão, no Windows 10 pra cima também, se você está usando alguma coisa mais antiga, procura algum cliente de SSH.

Para acessar esse novo servidor você vai abrir o prompt/terminal/posh o raio que quiser e basicamente digitar:

ssh -i "aquele_arquivo_de_chave" ec2user@IP_ou_FQDN
se não der certo tenta como root mesmo
ssh -i "aquele_arquivo_de_chave" root@IP_ou_FQDN

Se tudo deu certo, deve aparecer a licença, leia com cuidado, tudinho, tintim por tintim,,, e se achar que está tudo bem aceita a licença.

Na primeira vez que você faz login no Servidor de Acesso, um assistente de configuração é executado para permitir que você configure os parâmetros de inicialização antes de poder acessar a interface web administrativa. Neste assistente, você especifica alguns detalhes da rede e define um usuário administrador.

Se você optar por usar o usuário openvpn padrão como usuário administrador, certifique-se de definir uma senha para ele antes de acessar a interface web de administração. Para definir uma senha, use o seguinte comando shell:

sudo senha openvpn

Para acessar a interface web, abra o endereço IP público que você atribuiu e faça login como o usuário administrador que você configurou. A URL da interface da web tem o seguinte formato: https://xxx.xxx.xxx.xxx/admin.

O login abre a página Visão geral do status, conforme mostrado na imagem a seguir. É aqui que você obtém a visão geral do status do dispositivo VPN. Você também pode usar este portal para ajustar a VPN, alterar as configurações de rede e gerenciar permissões e autenticação do usuário.

Por padrão, a VPN é configurada para funcionar no modo NAT (tradução de endereço de rede) da Camada 3. Nesse modo, os clientes VPN são atribuídos a uma sub-rede privada cujos IPs são atribuídos dinamicamente a partir do pool padrão 172.27.224.0/20 (CIDR), conforme mostrado na imagem a seguir.

Você pode alterar esse pool de IPs, mas esteja ciente de que o novo deve ser diferente das outras sub-redes utilizadas na sua rede. Você também pode configurar outra sub-rede privada usada para atribuir endereços IP estáticos a usuários específicos designados na página Permissões do usuário.

Para roteamento de rede, a opção padrão é Sim, usando NAT, conforme mostrado na imagem a seguir.

Para testar a VPN

Normalmente você precisa baixar um cliente VPN para os testes, a principal vantagem do OpenVPN é ele ter clientes para todos os SO’s e Mobiles do mercado.

baixe o cliente correto, ajuste a configuração de IP que você está usando como IP público e terá uma VPN saindo pelo lugar do mundo onde você fez deploy da maquina.

GCP Cloud SQL – Restauração de instância

Imagine o seguinte cenário:

No universo de bancos de dados em nuvem, a necessidade de restaurar backups pode surgir devido a várias razões: um erro humano, falha em algum sistema ou até mesmo para criar um ambiente de teste. Independentemente do motivo, ter uma ferramenta que simplifique esse processo é essencial.

Apesar do Google Cloud Platform (GCP) oferecer uma solução robusta e escalável com o Cloud SQL, a restauração de backups pode ser um processo que exige múltiplos passos e certa familiaridade com a plataforma. Foi pensando nisso que desenvolvi o GCP Restore Tool.

Por Que Usar o GCP Restore Tool?

A ferramenta foi projetada com o objetivo de tornar o processo de restauração mais amigável e menos propenso a erros. Com uma interface gráfica intuitiva, você pode selecionar projetos, instâncias e o backup desejado para restaurar com apenas alguns cliques.

A maior vantagem? Você não precisa ser um expert em GCP ou lidar com comandos complexos. A ferramenta cuida de tudo para você.

Como Utilizar a Ferramenta

  • Instale a CLI da GCP
  • Abra o terminal e digite:
  • gcloud auth application-default login
  • Você será redirecionado para a tela de autenticação do console da GCP, faça a autenticação.
  • Olhe no terminal e ele terá retornado com algumas informações sobre a localização do arquivo
  • “application_default_credentials.json”
  • Esse arquivo é o que você deve carregar pelo botão “Load GCP Credentials”
  • Na seleção à esquerda, selecione o projeto e a instância de origem nos menus dropdown correspondentes.
  • Clique no botão “Load Backups” e a ferramenta irá listar todos os backups disponíveis para a instância escolhida.
  • Na seção à direita, selecione o projeto e a instância onde deseja restaurar o backup.
  • Após confirmar sua seleção, clique no botão “Restore”. A ferramenta fará todo o trabalho pesado para você, garantindo que o backup selecionado seja restaurado na instância de destino escolhida.

Este processo só funciona sobrescrevendo uma instância pré-existente, logo, você precisa criar uma instância no projeto de destino ou entender que esta atividade vai sobrescrever a instância de destino, tenha certeza de ter selecionado a instância certa.

Se tudo der certo, você vai receber uma mensagem que o processo foi enviado para a GCP, todo o processo ocorre em background pela GCP, então acompanhe pelo portal para saber o status da atividade.

No final, a instância restaurada vai ter o nome, IP, etc da instância que ela sobrescreveu, mas todos os usuários, senhas, bancos, ajustes pontuais, etc. da instância original.

Você pode baixar e acessar o código fonte no GitHub.

ATENÇÃO !!!

A ferramenta está em testes, então faça testes antes de executar a operação em produção.

Execute por sua conta e risco.

BSOD

Existem cenários que você precisa entender o que está acontecendo no ambiente, na sua aplicação, no SO, reiniciar a máquina e perder o que está acontecendo não é uma opção.

Se a aplicação é consistente na geração de Logs, ou o Windows consegue manter os logs de um problema enquanto a maquina está “travada” já ajuda consideravelmente, mas existem cenários que infelizmente tudo para de uma tal forma que infelizmente só um reboot salva, mas vc vai perder a rastreabilidade do problema.

Para situações como essa, alguns servidores possuem bem próximo ao botão de liga/desliga um outro botão de interrupção do SO onde força o despejo de memória através de uma Tela Azul da Morte para o arquivo de dump para uma análise posterior do ocorrido (verificar o manual do seu fabricante).

Para outros cenários, principalmente quando você não tem acesso ao servidor físico, existe a opção de adicionar duas chaves de registro que vão gerar esse BSOD no seu Windows (Cliente ou Servidor).

Windows Registry Editor Version 5.00

; For PS/2 keyboards
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\i8042prt\Parameters]
"CrashOnCtrlScroll"=dword:00000001

; For USB keyoards
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\kbdhid\Parameters]
"CrashOnCtrlScroll"=dword:00000001

Adicionadas estas chaves, reinicie o seu Windows.

Pressionando CTRL + 2x Scroll Lock o Windows vai iniciar o processo da Tela Azul.

Atenção !!!

Existem outras configurações necessárias para que isso funcione direito e você tenha o que analisar !

  1. Espaço em disco (Normalmente ninguém se preocupa onde está o Page File nem mesmo a configuração do Dump)
  2. Configuração do Dump (Se você nunca mexeu ele é Full RAM e vai gerar o arquivo na unidade C)
  3. Mais espaço em Disco (A geração do arquivo de Dump vem com a memória RAM e o conteúdo do PF, no final do processo o Windows gera um PF novo)
  4. Tempo (é totalmente variável já que trata-se de toda a memória da maquina sendo armazenada em um arquivo, quanto mais RAM usada maior é o arquivo)

Se você conseguir gerar um dump bem sucedido, vai conseguiu analisar com o WinDBG.

Boa Sorte.