Onde estou? – Definição de variável de ambiente para informar a aplicação de qual é o ambiente

Esse é um texto sobre uma definição que é bem simples de arquitetura, mas que muitos “arquitetos” não fazem. O conceito é informar a aplicação, com o uso de uma definição externa, em qual ambiente essa aplicação se encontra. Com isso, a aplicação é capaz de tomar ações com base no ambiente em que ela está em execução.

Leia mais: Onde estou? – Definição de variável de ambiente para informar a aplicação de qual é o ambiente

Nesse cenário, o código é o menos importante. O conceito é o que faz a diferença. Mas caso queira fazer o acompanhamento, aqui está o código: https://github.com/escovabit-tec-br/nodejs-env-environment

Bem, vamos ao conceito.

Nossa aplicação precisa se relacionar com o meio (ambiente) em que ela está em execução, e o meio deve influenciar como nossa aplicação se comporta. Partindo disso, podemos definir um padrão:

Deve existir uma variável de ambiente com o nome environment que pode ter os valores:

  • local
  • development
  • staging
  • production

Sabendo disso. Nossa aplicação deve ser capaz de se adaptar ao “ambiente” que se encontra, abaixo um exemplo bem simples:

const appConfig = {
  development: {
    apiUrl: "https://api.publicapis.org/entries",
  },
  production: {
    apiUrl: "https://catfact.ninja/fact",
  },
};
const environment = process.env.ENVIRONMENT || "development";
const resultData = appConfig[environment];

module.exports = resultData;

Podemos ver que o conteúdo no module.export muda de acordo com o ambiente em que e aplicação se encontra. Assim, para cada ambiente, a apiUrl será diferente.

Este é um modelo bem simples de fazer o uso deste tipo de arquitetura. Já falei aqui de um modelo mais organizado que é usando o spring-cloud.

http://3.139.95.241/2021/05/03/spring-cloud-config-server-o-que-e-e-como-criar-o-seu/

Do lado do “ambiente”, é necessário de alguma forma ter esse valor de ENVIRONMENT disponível para a aplicação. Como exemplo, vou simular a aplicação rodando em um Kubernetes.

Primeiro, criamos uma secret com o dado:

apiVersion: v1
kind: Secret
metadata:
  name: default-cfg-environment
  namespace: l2-app-backend
  labels:
    app.kubernetes.io/managed-by: Helm
  annotations:
    meta.helm.sh/release-name: base
    meta.helm.sh/release-namespace: l4-cfg-helm
type: Opaque
data:
  ENVIRONMENT: ZGV2ZWxvcG1lbnQ= #development


Abaixo em destaque a configuração no deployment:

apiVersion: apps/v1
kind: Deployment
spec:
    spec:
      containers:
        - name: ms-generic
          envFrom:
           - secretRef:
                name: default-cfg-environment

Aqui o arquivo de deployment completo com o uso da secret:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: l2-app-backend
  labels:
    app.kubernetes.io/instance: my-app
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: my-app
  annotations:
    deployment.kubernetes.io/revision: '24'
    meta.helm.sh/release-name: my-app
    meta.helm.sh/release-namespace: l2-app-backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/instance: my-app
      app.kubernetes.io/name: my-app
  template:
    metadata:
      creationTimestamp: null
      labels:
        app.kubernetes.io/instance: my-app
        app.kubernetes.io/managed-by: Helm
        app.kubernetes.io/name: my-app
    spec:
      containers:
        - name: ms-generic
          image: ### IMAGE NAME ###
          ports:
            - name: app
              containerPort: 3000
              protocol: TCP
          envFrom:
            - secretRef:
                name: my-app-helm
            - secretRef:
                name: default-cfg-environment
          resources: {}
          livenessProbe:
            httpGet:
              path: /metrics
              port: app
              scheme: HTTP
            initialDelaySeconds: 80
            timeoutSeconds: 10
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /metrics
              port: app
              scheme: HTTP
            initialDelaySeconds: 80
            timeoutSeconds: 10
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 3
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: IfNotPresent
          securityContext: {}
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      dnsPolicy: ClusterFirst
      serviceAccountName: my-app
      serviceAccount: my-app
      securityContext: {}
      schedulerName: default-scheduler
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600

Com “tudo” isso feito, agora temos uma aplicação “portável” entre ambientes. Podendo assim levar a mesma aplicação sem precisar recompilar (“re-buildar”) ela.

Obrigado pela atenção, e até a próxima.