sexta-feira, 6 de dezembro de 2019

Definindo uma arquitetura focada em disponibilidade de dados

Uma arquitetura distribuída, altamente disponível e com foco na disponibilidade dos dados.

Vou utilizar a GCP (Google Cloud Platform) para essa proposta de arquitetura, mas poderia ser outro provider (desde que, obviamente, os componentes preservem as características dos que elenquei nessa proposta.

É fácil afirmar que, nenhuma arquitetura é 100% disponível, mesmo quando falamos em multi-cloud. Afinal, fatores externos podem gerar indisponibilidade e não temos como controlá-los, mas temos como minimizar as chances para termos problemas e gerar uma maior garantia de prevenção a falhas.

O modelo que vou apresentar, não é um modelo de missão crítica, nem deve ser considerado. Afinal, nem mesmo os providers como GCP, Azure ou AWS, garantem isso. 

Certo! vamos às armas oO'

Vou listar algumas ferramentas que vou utilizar nessa proposta, mas poderia ser outra, desde que tenha a proposta semelhante.

- Aplicações (cloud run, cloud function, kubernetes apps).
- Pub/Sub messaging - com disponibilidade GLOBAL - super importante.
- Bancos de dados (postgresql, mongodb, ...).
- Banco de cache (redis, memcached, ...).

O ponto mais importante aqui, é o pub/sub, por ser uma alternativa com disponibilidade GLOBAL. Ou seja, é aqui que vou garantir que receberei o dado gerado pelo usuário final independente da indisponibilidade da aplicação.

- A estratégia aqui, pode ser implementada de várias formas, vou apresentar apenas um modelo para exemplificar.

Tornando as aplicações mais disponíveis:

As aplicações, estejam onde estiverem, vamos buscar torná-las disponíveis entre diferentes zonas e regiões, usando essa solução que já expliquei como implementar e deploy em 2 regiões diferentes (sempre multi-zona), das aplicações Cloud run e Cloud function (por cobrarem por uso, não é um problema executar em regiões diferentes ou estarem duplicadas, você só vai pagar pelo processamento efetuado).

Tornando o banco de dados disponível:

Os bancos de dados, mongodb, postgresql, sqlserver o qualquer outro, podem estar onde você quiser, até mesmo em uma VM gerando snapshots para evitar dores de cabeça. O que vai tornar o dado disponível, é o banco de cache (redis ou memcached - destaco esses pela facilidade de subir instancias desses dentro do kubernetes).

A "mágica"

O segredo aqui, é a forma como as aplicações lidam com os dados. 
Basicamente nossas aplicações, independente de onde executam, vão gravar os dados em tópicos do pub/sub (pela garantia de disponibilidade) e vão ler os dados dos bancos de cache que estarão dentro do Kubernetes. 
Quando o usuário fizer uma operação para gravar um dado, a aplicação vai mandar para o pud/sub e utilizar um recurso assíncrono, para processamento dessa fila (pode ser hangfire, por exemplo) para enviar o dado da fila, para o banco de dados da aplicação (postgresql, sql server, mongodb ou outro) e esse mesmo poderia atualizar o banco de cache, mas isso pode ser feito no momento da leitura.
Vamos imaginar que agora o usuário faz uma requisição aos serviços e esses teriam de buscar o dados no banco de dados. No lugar de buscar no banco de dados, vai buscar no banco de cache e disparar uma thread para atualizar o banco de cache.
Dessa forma, estamos assegurando que a leitura está sempre na mesma região, independente se o banco de dados está distribuído em diferentes regiões ou está em uma maquina rodando apenas em uma zona.

Dica: Se quiser garantir mais disponibilidade entre regiões com seu banco de dados, opte por usar Cloud SQL da GCP, com banco MySql (ele possui recurso para replicas em diferentes regiões).

Algumas questões:

- Se o banco de dados estiver indisponível: Não tem problema, o dado estará disponível no pub/sub até que o banco esteja disponível, para receber o dado.
- Diferentes regiões, terão bancos de cache com dados diferentes. Mas isso não é um grande problema se você implementou o MCI como expliquei aqui, pois o Load Balancer vai cuidar de direcionar o usuário para o cluster mais próximo a ele e só vai ocorrer de cair em outro região, se o Load Balancer encontrar motivos para te mandar para lá. E entre não oferecer nada para o usuário e oferecer um dado que não está totalmente atualizado, prefiro dado não atualizado.

Fechamos \o/
Esse foi um exemplo BEM SIMPLES e BEM RESUMIDO. Obviamente, de acordo com a necessidade do seu negócio, isso pode ou não ser mais elaborado. É sempre valido avaliar os benefícios de uma arquitetura distribuída em qualquer problema, pode ser simples e barato resolver problemas que parecem impossíveis de serem resolvidos, apenas usando as ferramentas adequadas.

Load Balancer entre diferentes clusters Kubernetes

Como criar um Load Balancer Global, entre diferentes clusters Kubernetes na Google Cloud


Quando me foi apresentado a ideia de "multi cluster ingress" (ou MCI), deixei em standby pelo fato de aplicação trabalhar com o recurso auto devops do gitlab. E para viabilizar o MCI, eu teria de escrever os deployments e services e sair da praticidade do auto devops (perdendo recursos como incremental rollout que já está disponível no auto devops) e segui avaliando as alterações que gerariam impacto na aplicação por meio de outro recurso chamado NEG. Por fim, descobri em algumas pesquisas, recursos que o Gitlab auto devops provê, que poderiam viabilizar o MCI e retomei o foco para ele (sabiamente). 

Mas, qual o objetivo?

A proposta do MCI é basicamente unir 2 clusters através de um Load Balancer. E sabendo que cada cluster pode ter pools de maquinas em diferentes zonas, os números de disponibilidade se tornam interessantíssimos e começamos a falar em algo em torno de 99.95%.

Qual botão eu clico?

Calma lá, lembre-se que: Com grandes poderes, vêm grandes responsabilidades Com grandes problemas, vêm grandes noites sem dormir. 
- Se você acompanha meus posts, sabe que gosto muito do gitlab e nesse caso, então a coisa sai um pouco da simplicidade e se faz necessário partir para automatizações e customizações para atingir o objetivo de forma elegante. 

Blz :/ Então o que eu faço?

A aplicação que fará a comunicação com o Load Balancer, deve obrigatoriamente atender alguns critérios. 
Primeiro algumas configurações precisam ser iguais em todos os clusters (external e internal ports, são especificas para NEG, mas vamos deixar diferente para saber diferenciar facilmente uma aplicação da outra dentro da listagem de services do kubernetes) e para equalizar essa configurações, vamos utilizar algo que o gitlab nos fornece. O HELM_UPGRADE_EXTRA_ARGS

Crie um arquivo values.yaml na aplicação com o conteúdo:
service:
externalPort: 8080
internalPort: 8080
type: NodePort
- Aqui apenas coloquei as portas do container (interna e externa), para 8080 no lugar de 5000 e o tipo de ClusterIP (default), mudei para NodePort. 

No arquivo de configuração do pipeline (.gitlab-ci.yaml) inclua as variáveis abaixo:
HELM_UPGRADE_EXTRA_ARGS: "--values values.yaml"
HELM_RELEASE_NAME: mci-app
KUBE_NAMESPACE: default
NODE_PORT: 30999
- Essas variáveis, respectivamente: Atribui o arquivo values.yaml para o runner do gitlab processar no pipeline, coloca um nome padrão para a aplicação (precisa ser igual em diferentes clusters) e coloco a aplicação no namespace default do kubernetes (preciosismo apenas).

Outro requisito, é que a porta do service NodePort, seja a mesma nos clusters. E ai já é um problema a resolver, pois o gitlab não tem isso para passar pelo HELM. Então, sugiro criar um step a mais no seu pipeline, que basicamente pega o service e modifica o valor do nodePort dentro do arquivo .gitlab-ci.yaml:
node_port_change:
<<: *node_port_change
allow_failure: true
stage: setup mci
image: google/cloud-sdk:latest
script:
- gcloud auth activate-service-account --key-file ${SEU_ARQUIVO_GCLOUD}
- gcloud config set project ${SEU_PROJETO_GCP}
- gcloud container clusters get-credentials \ ${NOME_DO_SEU_CLUSTER} --zone ${ZONA} --project ${SEU_PROJETO_GCP}
- kubectl get services --namespace ${KUBE_NAMESPACE} \ -l app=${HELM_RELEASE_NAME} -o yaml | sed -E \ "s/nodePort:\ [0-9]+$/nodePort:\ ${NODE_PORT}/g" >> service.yaml
- kubectl apply -f service.yaml
only:
refs:
- master

mci prod1: &node_port_change
environment:
name: prod1
variables:
NOME_DO_SEU_CLUSTER: prod1
ZONA: ${SUA_ZONA_PROD1}

mci prod2: &node_port_change
environment:
name: prod2
variables:
NOME_DO_SEU_CLUSTER: prod2
ZONA: ${SUA_ZONA_PROD2}
- Quando essa etapa executar, basicamente vai pegar o service de cada cluster no kubernetes, baixar, fazer um replace da porta, colocar a mesma porta para ambos (perceba a variável NODE_PORT: 30999) e problema resolvido.

Show :) Terminamos?

Pff, terminamos a aplicação, agora vem o MCI. Seguindo os passos abaixo, você terá o MCI configurado (usando bash).

1) Crie uma pasta /mci
- mkdir mci

2) Entre na pasta
- cd mci

3) Exporte os arquivos dos clusters
- KUBECONFIG=clusters.yaml gcloud container clusters  get-credentials prod1 --zone=${ZONA-PROD1}
- KUBECONFIG=clusters.yaml gcloud container clusters  get-credentials prod2 --zone=${ZONA-PROD2}
- Isso vai gerar um arquivo com nome clusters.yaml.

4) Crie um IP global
- gcloud compute addresses create --global mci-ip

5) Se você quiser colocar certificado HTTPS no seu Load Balance, esse é o momento
- gcloud compute ssl-certificates create ssl-cert --certificate ${CERT_FILE} --private-key ${PRIVATE_KEY}

6) Faça download do kubemci

7) Atribua permissão adequada ao kubemci
- chmod +x ./kubemci

8) Crie o ingress-mci.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: mci-app-auto-deploy
annotations:
kubernetes.io/ingress.class: gce-multi-cluster
spec:
backend:
serviceName: mci-app-auto-deploy
servicePort: 8080
- Perceba que usamos a porta dos containers, nome do app gerado pelo auto devops, certificado que criamos anteriormente e nome do ip que também criamos acima.

9) Execute o ./kubemci
./kubemci create my-mci \
    --ingress=ingress-mci.yaml \
    --gcp-project=${SEU_PROJETO_GCP} \
    --kubeconfig=clusters.yaml

Atenção: Caso não queira o HTTPS, retire do ingress-mci.yaml a annotation "...allow-http: false" e não execute a etapa 5.

É isso :) Espero que não sofra o tanto que sofri pra chegar nesse resultado rsrs, pois não tem nada por ai explicando como fazer isso com auto devops do gitlab. Mas agora tem xD divirta-se e indique a solução para seu amiguinho que está com o mesmo problema!

Abraços!

Referências:

segunda-feira, 18 de novembro de 2019

Gitlab Auto DevOps com dotnet core

Como configurar o pipeline do gitlab auto devops usando dotnet core?

Já falei antes sobre o recurso Auto Devops do Gitlab. Mas não mencionei algumas restrições e como a magica funciona.

No artigo anterior, abordei o pipeline do gitlab trabalhando com um cluster kubernetes. E como disse, é possível, fazendo uso apenas de um Dockerfile, ter sua aplicação fazendo o Build, Test e Deploy em um cluster Kubernetes.
Obviamente, não é uma magica e sua aplicação deve seguir alguns critérios para que essa facilidade se torne disponível. Como por exemplo: Dockerfile deve estar na raiz da sua aplicação, recurso auto devops deve estar ativo nas configurações do projeto e você precisa utilizar uma das linguagens suportadas pelo padrão. Isso já basta para que sua aplicação "magicamente", tenha o pipeline construído sem nenhum tipo de esforço adicional.

Certo, mas e quando minha linguagem não é suportada?

Como tudo é baseado em Dockerfile, podemos utilizar a imagem correta para fazer o trabalho por nós. No caso do dotnet core, teremos de utilizar duas imagens, uma para build e outra de runtime.
Já testei diversas imagens, a que mais utilizei, foram as versões 2.1 e 2.2.

Para montar o Dockerfile, vamos imaginar a seguinte estrutura de projeto Pokemon:

./src/api (sua aplicação aqui)
./src/api/nuget.config
./src/api/pokemon.csproj
./src/test (seus testes aqui)
./src/test/nuget.config

E o resultado do Dockerfile, é o seguinte:

FROM microsoft/dotnet:2.2-sdk-alpine AS build

WORKDIR /app

COPY ./src/ ./

RUN dotnet restore --configfile Api/nuget.config Api
RUN dotnet restore --configfile Test/nuget.config Test

RUN dotnet test test
RUN dotnet publish api -c production -o out
FROM microsoft/dotnet:2.2-aspnetcore-runtime-alpine
COPY --from=build /app/api/out ./app WORKDIR /app EXPOSE 5000
ENTRYPOINT [ "dotnet", "Pokemon.dll" ]

Curiosidade: Se fosse um projeto node, por exemplo. O simples fato de ter um projeto de teste, já seria o bastante para que (com o padrão herokuish) ocorresse a detecção do padrão do projeto e as coisas acontecessem sem ter de rodar os testes dentro do Dockerfile (claro que você pode criar um step de teste no gitlab, mas teria que customizar o auto DevOps).

Bom, creio que seja isso :) 
Caso tenha dúvidas no seu projeto, basta postar no stackoverflow e mandar o link aqui nos comentários que tentarei te ajudar.

Ah, da uma lida nesse material:
https://docs.gitlab.com/ee/topics/autodevops/#custom-helm-chart.
Você certamente vai precisar em algum momento :P

Abraços, boa sorte \o/

segunda-feira, 21 de outubro de 2019

Definindo a arquitetura no início do projeto

Onde começa e onde termina a arquitetura de um Software?

Sempre defendi que arquitetura é arquitetura, independente da cenários que estamos abordando (não significa que vou sair por ai construindo prédios rsrs). Mas segue uma comparação que podemos fazer:

De acordo com o site significado. Significado de arquitetura é:
"Arquitetura é a arte e técnica de projetar uma edificação ou um ambiente de uma construção. É o processo artístico e técnico que envolve a elaboração de espaços organizados e criativos para abrigar diferentes tipos de atividades humanas.
A arquitetura é a disposição das partes ou dos elementos que compõem os edifícios ou os espaços urbanos em geral."

Em software podemos traduzir para:

"Arquitetura é a arte e técnica de projetar um software ou infraestrutura de um software. É o processo artístico e técnico que envolve a elaboração de componentes organizados e coesos para abrigar diferentes tipos de requisito funcionais e não funcionais de um projeto de software.
A arquitetura é a disposição das partes ou dos componentes que compõem o software ou os recursos de infraestrutura em geral."

Dito isso... gostaria de compartilhar algumas alterações que fiquei sabendo que ocorreram na Obra do meu apartamento (certamente você já passou por isso ou sabe de alguém que passou).

Assim que entrei em meu apartamento (onde estou morando a pouco mais de 4 anos), percebi algumas coisas diferentes do que foram apresentados na planta e outras que mudaram no meio do caminho. Duas dessas mudanças, lembro ter questionado:

1. Na planta, existia uma entrada no corredor para prateleiras (na mesma parede de um dos quartos). Mas ao longo da obra, perceberam que se tornaria um espaço de poucas opções e reduziria o quarto por um espaço que poucos utilizariam.

2. No banheiro, o teto do box não deveria ter saída de energia para lampada, mas tem. Pois a medida que a obra foi evoluindo, o arquiteto percebeu que o box poderia ficar escuro por conta de uma leve separação entre o box e o teto do banheiro. O que faria, dependendo da lampada que fosse usada, que o box não recebesse luz o bastante e então ele incluiu uma saída a mais para receber outra lampada, dentro do box.

Percebam que o que foi definido lá no inicio da obra, sofreu alterações (não apenas essa, mas deveríamos ter um espaço para visitantes estacionarem, mas se tivessem levado isso pra frente, hoje não teríamos vagas exclusivas, sem rodizio e totalmente separadas)

E o kiko?

Agora vou citar dois casos reais, onde alterei a abordagem que estava em uso, também por alguns motivos e também com o carro andando.

1. Uma aplicação web (front), utilizada apenas no horário comercial. Estávamos rodando no kubernetes, junto com outras aplicações (bacana, utilizando recursos já reservados, faz sentido).  Mas começamos a utilizar mais e mais recursos do kubernetes, então, eu poderia tomar duas ações: Fazer upgrade do cluster kubernetes para atender a necessidade, ou mover a aplicação (que só era usada poucas vezes no horário comercial), para serverless (pagando pelo uso apenas - ainda não saímos da faixa gratuita), garantindo liberação de recursos no kubernetes, para outras aplicações que necessitam executar 24/7.

2. Um service executa algumas tarefas de replicação de dados dentro do kubernetes, usando uma biblioteca para agendamento de baseado em cron. Porém, como o responsável por agendar as tasks está dentro da aplicação, somos obrigados a manter apenas uma instancia de POD (unidade de container), nessa apliação. Perdendo um dos grandes benefícios do kubernetes (escalabilidade vertical dos pods). E também somos obrigados a realizar novos deploys ou executar os endpoints dentro da rede manualmente para forçar ma atualização quando necessário. Para contornar isso, optamos por usar o cloud scheduler para chamar esse service. Permitindo auto scale, alteração direto pelo painel do cloud scheduler no intervalo das execuções ou até mesmo, solicitar uma imediata execução manual, sem ter que fazer nada na aplicação.

Humm, parecem abordagens semelhantes.

Se esse titulo não foi sua conclusão, desculpe!
Em ambas as situações, projetamos algo no inicio e isso foi alterado a medida que a aplicação ou a infraestrutura cresce. Nos forçando a tomar outras decisões e rever o que antes era visto como "adequado".

Em resumo, o que pretendo com isso, é mostrar que arquitetura é algo tão mutável, como o próprio projeto. Arrisco dizer que mais mutável que o próprio projeto, visto que geralmente os projetos recebem novas features e pouco se altera nas já implementadas, enquanto a arquitetura pode mudar drasticamente se um crescimento exponencial for identificado, por exemplo.

Enfim. Arquitetura nunca tem fim!

Se eu tivesse de escolher uma palavra para definir arquitetura, eu certamente escolheria Resiliência. 
Projetar um software, por mais simples ou inadequado que sejam as tecnologias elencadas, deixa de ser um problema se o software está desacoplado e distribuído o suficiente para tornar a refatoração não impactante para o projeto.

Então, respondendo a pergunta no titulo desse artigo: Arquitetura inicia junto com o projeto e nunca tem fim.

segunda-feira, 22 de julho de 2019

Usando secret tls para configurar https no kubernetes

Como configurar certificado https para  uma aplicação em um cluster Kubernetes?

Se você usa docker, mas nunca usou o Kubernetes (k8s). Então, você precisa conhecer :)
Dentre diversas vantagens em utilizar k8s, destaco a facilidade em ter uma aplicação sempre disponível, facilidade em recuperação nos casos de falhas e até mesmo, a simplicidade em ter uma aplicação escalando horizontal e verticalmente para atender a demanda no exato momento em que existe a necessidade.

É muito comum utilizar um cluster k8s para criar arquiteturas baseadas em micro serviços, onde, geralmente, temos um gateway exposto, fornecendo acesso a serviços e APIs que executam dentro da rede do cluster. Mas também é possível ter aplicações web ou scheduled jobs rodando nesse cluster. E em alguns momentos, como no caso de um gateway ou de uma aplicação web, queremos oferecer acesso externo a aplicação que está rodando dentro do cluster. E é ai que tratamos o assunto desse post!

Algo importante: Estou partindo da premissa de que você já tem o Ingress configurado e possui um endereço configurado no DNS com o IP do Ingress.


Mas porque diabos você faz uma introdução gigante ao invés de iniciar o assunto?

Puff!, sei lá =). Porque trabalhei com redatores por muito tempo?


Agora é sério...


Veremos abaixo os passos necessários para configurar uma secret TLS no k8s, montar o yaml da secret, executar no k8s e vincular a secret gerada no service do k8s para possibilitar acesso HTTPS em sua aplicação. 

O secret.yaml

Não necessariamente precisa ser "secret.yaml", só usei esse nome para simplificar. O importante é o conteúdo do arquivo.
apiVersion: v1
kind: Secret
metadata:
name: $nome_do_certificado
namespace: $namespace_do_seu_app_no_k8s
type: kubernetes.io/tls
data:
tls.crt: $hash_base64_extraido_do_seu_certificado_https tls.key: $hash_key_base64_extraido_do_seu_certificado_https
*Tudo que está com $, você preenche com seus dados 😙


O criando no Kubernetes

Bom, para criar a secret, basta conectar no seu cluster k8s (via cli - eu uso bash) e usar o comando abaixo:
- kubectl create -f secret.yaml

Após a mensagem de sucesso, a secret nome_do_certificado estará disponível e bastará apenas vincular a secret gerada a sua aplicação (aquela do namespace_do_seu_app_no_k8s), no campo tls.hosts.secretName.

Eu uso Google Cloud. Se esse for também seu caso, siga o caminho: kubernetes engine > services > acesse pod Entrada (Ingress) > YAML > tls.hosts.secretName = nome_do_certificado (ou via VI usando bash DeUXx mE livRe do VIMMM - É real, não gosto do vim, uso sei lá porque).

Feito isso, basta acessar sua aplicação pelo endereço vinculado ao IP do Ingress (espero que você já tenha configurado o DNS para o IP do Ingress).

Fica ai a referência que usei quando configurei: https://kubernetes.io/docs/concepts/configuration/secret/

Bom, é isso ai. Tem algumas coisas particulares aqui, como Google Cloud e Ingress. Mas creio que vá te ajudar caso esteja com o mesmo problema.


Ah, outra coisa...

Caso esteja se perguntando sobre vincular usar o HTTPS em diferentes aplicações para no mesmo cluster k8s, fique tranquilo. É um cluster e quem faz a magica de direcionar o endereço para a aplicação correta é o load balance, no meu caso o Ingress quebra essa :P.


Caso tenha alguma dificuldade, pergunta ae!

Abraços

quinta-feira, 27 de junho de 2019

Convertendo o appsettings em um objeto com c#

Como converter o appsettings.json em um objeto fortemente tipado no netcore e C#?

Antes de mais nada, vale pontuar que existem várias soluções para esse problema, mas, fui buscar esses dias, a forma mais adequada de trabalhar com os dados de um appsettings. Claro que dependendo do problema, isso não é necessário. Por exemplo, ler apenas uma chave, um texto ou um número. Mas esse caso que vou apresentar, é uma solução para caso você tenha um objeto grande, com uma estrutura de árvore que precise ser manipulado usando o System.Collection.Generic por exemplo.

Vou apresentar um problema para depois apresentar a solução.

Imagine a seguinte estrutura:
{
"Clients": [
{
"name": "pic",
"id": "2378sdkjsk"
},
{
"name": "poc",
"id": "f9d8sdkjsk"
},
{
"name": "pluc",
"id": "n323dj8"
}
]
}


Certo! imaginado... e agora?

Agora você deseja consultar o "client" com "name" = "poc". Blz?

To fazendo sentido? (assisti uma palestra onde o cara repetiu essa pergunta a palestra toda. Em algum número entre o 15 e 20 eu parei de contar).

Para "mapear" esses valores, precisamos primeiro de um objeto. Então criamos um:

namespace PicPocPluc.Models.Configuration
{
public class Clients
{
public string Name { get; set; }
public string Id { get; set; }
}
}

No nosso Startup.cs, geralmente temos as propriedades (se não tem, crie :P):


public IConfiguration Configuration { get; private set; }
public IHostingEnvironment Environment { get; private set; }

E também temos dois métodos:


public Startup(IHostingEnvironment env, IConfiguration configuration);
e
public void ConfigureServices(IServiceCollection services);

Nesses métodos, faremos 2 coisas. A primeira é carregar o appsettings de acordo com nosso ambiente e a segunda é carregar os valores do appsettings na classe "Clients" e oferecer essa classe para nossas controllers via injeção de dependência.

CUMA? Como?

Usando o System.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"), podemos descobrir qual ambientes estamos rodando e com um belo IFzão, vamos carregar o appsettings que queremos no método Startup():


if(System.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "production"))
Configuration = new ConfigurationBuilder()
.AddJsonFile(Environment.ContentRootPath + @"/appsettings.production.json")
.Build();

Feito isso, teremos o appsettings de cada ambiente funcionando corretamente e o próximo passo é no método ConfigureServices(), carregar nosso objeto:


services.AddSingleton<IConfiguration>(Configuration);
services.AddSingleton(Configuration.GetSection("Clients").Get<List<Clients>>());

Objeto carregado, é hora de usá-lo via injeção de dependência em qualquer controller.

readonly List<Clients> _clients;
public PocController(List<Clients> clients) => _clients = clients;

E pronto ! É isso. Criamos o appsettings com dados, uma classe para mapea-lo, carregamos o appsettings de acordo com o ambiente, usamos a propriedade Configuration do Startup.cs para acessar a chave Clients do appsettings carregado e assim, via injeção de dependência (padrão no netcore), acessamos os dados já carregados no controller. 

Como mencionei no inicio, tem várias formas de fazer. Esse é o formato que usei e me pareceu mais adequado para esse problema. Se te ajudei de alguma forma, valeu escrever o post :)

Abraço!

domingo, 27 de janeiro de 2019

Publicando um aplicativo pelo gitlab

Como publicar uma branch do git, no gitlab usando o gitlab, Ingress e criando um apontamento DNS para um domino?

No artigo anterior apresentei o passo a passo que utilize para montar um pipeline completo no gitlab, criar uma instancia  de um container da imagem do meu aplicativo e colocar essa instancia em um cluster kubernetes usando google cloud. Para tornar acessivel o aplicativo, utilizamos um wildcard dns para responder para um IP através de um domínio não existente.

Link para o artigo: https://www.rcelebrone.com/2019/01/ci-cd-gitlab-google-cloud-kubernetes.html

Mas e se eu desejar publicar automaticamente em um domínio existente?

É perfeitamente simples e possível.
No artigo anterior, dentro do gitlab. Entre no projeto, em configurações, CI/CD e abra as configurações do recurso Auto DevOps. No campo domínio, no artigo anterior, utilizamos um serviço de wildcard DNS (o xip.io), para apontar para o IP do Ingress. É nesse campo domínio que temos que informar o domínio oficial por onde queremos servir nosso aplicativo.

* No gitlab, o IP do Ingress (caso tenha seguido o artigo anterior), está disponível dentro do projeto em Operações > Kubernetes > (clique no nome do cluster na lista) > O ip estará visível em aplicações > Ingress.


IP em mãos e domínio configurado no gitlab e agora?

Aqui tem uma observação. Existem diversas ferramentas de registro de domínio. No meu caso, trabalho com o uolhosts e diretamente no painel do uolhosts eu consigo fazer o apontamento que preciso. Para isso, basta criar no domínio um apontamento A Name para o endereço gerado pelo gitlab e o IP do Ingress.

Vale lembrar que por padrão (quando não definimos um arquivo gitlab-ci.yml), o gitlab utiliza uma junção do nome do seu grupo no gitlab com o nome do seu projeto. Então, por exemplo, se nossas informações são as seguintes:

  • Grupo do gitlab: universo
  • Nome do projeto: admin-jupiter
  • Ip do Ingress: 35.234.567.67
  • Domínio: nasa.com
Teremos o seguinte:

No serviço de configuração do domínio:
Crie um registro "A NAME" > com IP: 35.234.567.67 e endereço: universo-admin-juniper.nasa.com

No campo domínio do Ingress no gitlab:
Informe o domínio: nasa.com


* Para utilizar um endereço customizado, basta utilizar nas variáveis de ambiente: Dentro do projeto > Configurações > CI/CD > variáveis de ambiente. Então basta utilizar a variável: CI_PROJECT_PATH_SLUG e o nome do subdomínio. Ou seja, para o endereço xpto.exemplo.com, o valor dessa variável será xpto.