web-dev-qa-db-fra.com

Comment passer des variables d'environnement à une application Web frontale?

J'essaie de conteneuriser une application Web frontale et j'ai du mal à comprendre comment passer des variables d'environnement. L'application est une application angulaire, elle est donc 100% côté client.

Dans un service dorsal typique, le passage des variables d’environnement est facile, car tout est exécuté sur le même hôte, de sorte que les variables d’environnement peuvent être facilement sélectionnées par le service principal. Cependant, dans une application frontale, cela est différent: l'application s'exécute dans le navigateur du client.

Je souhaite configurer mon application via des variables d'environnement, car cela facilite beaucoup le déploiement. Toute la configuration peut être faite en docker-compose.yml et il n’est pas nécessaire de gérer plusieurs images, une pour chaque environnement possible. Il n'y a qu'une seule image immuable. Ceci est conforme à la philosophie d’application à 12 facteurs, comme on peut le trouver sur https://12factor.net/config .

Je construis mon image d'application comme suit:

FROM node:Alpine as builder
COPY package.json ./
RUN npm i && mkdir /app && cp -R ./node_modules ./app
WORKDIR /app
COPY . .
RUN $(npm bin)/ng build

FROM nginx:Alpine
COPY nginx/default.conf /etc/nginx/conf.d/
RUN rm -rf /usr/share/nginx/html/*
COPY --from=builder /app/dist /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]

Dans app/config.ts, j'ai:

export const config = {
    REST_API_URL: 'http://default-url-to-my-backend-rest-api'
};

Idéalement, je veux faire quelque chose comme ceci dans mon docker-compose.yml:

backend:
  image: ...
frontend:
  image: my-frontend-app
  environment:
    - REST_API_URL=http://backend:8080/api

Je pense donc que je devrais modifier ce app/config.ts pour remplacer REST_API_URL par la variable d'environnement. Comme je préfère une image Docker immuable (donc je ne veux pas faire cela remplacer pendant la construction), je suis assez perplexe de savoir comment progresser ici. Je crois que je devrais aider à modifier le app/config.ts au moment de l'exécution avant que le proxy nginx ne soit démarré. Cependant, le fait que ce fichier soit minifié et groupé avec Webpack rend cela plus difficile.

Des idées comment aborder cela?

14
dockernodejs

La façon dont j'ai résolu ceci est la suivante:

1.Définissez la valeur dans enviroment.prod.ts avec une chaîne unique et identifiable:

export const environment = {
  production: true,
  REST_API_URL: 'REST_API_URL_REPLACE',
};

2.Créez un entryPoint.sh, cet entryPoint sera exécuté chaque fois que vous effectuez une exécution du menu fixe dans le menu fixe.

#!/bin/bash
set -xe
: "${REST_API_URL_REPLACE?Need an api url}"

sed -i "s/REST_API_URL_REPLACE/$REST_API_URL_REPLACE/g" /usr/share/nginx/html/main*bundle.js

exec "$@"

Comme vous pouvez le constater, ce point d'entrée récupère l'argument 'REST_API_URL_REPLACE' et le remplace (dans ce cas) dans le fichier * bundle.js principal par la valeur de la variable var.

3.Ajouter le entrypoint.sh dans le fichier de dock avant le CMD (il a besoin d'autorisations d'exécution): 

FROM node:Alpine as builder
COPY package.json ./
RUN npm i && mkdir /app && cp -R ./node_modules ./app
WORKDIR /app
COPY . .
RUN $(npm bin)/ng build --prod

FROM nginx:Alpine
COPY nginx/default.conf /etc/nginx/conf.d/
RUN rm -rf /usr/share/nginx/html/*
COPY --from=builder /app/dist /usr/share/nginx/html

# Copy the EntryPoint
COPY ./entryPoint.sh /
RUN chmod +x entryPoint.sh

ENTRYPOINT ["/entryPoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

4. Lancez l'image avec env ou utilisez docker-compose (la barre oblique doit être échappée):

docker run -e REST_API_URL_REPLACE='http:\/\/backend:8080\/api'-p 80:80 image:tag

Il existe probablement une meilleure solution qui n’a pas besoin d’utiliser une expression régulière dans le fichier minifié, mais cela fonctionne bien.

16
Daniel Caldera

Mettez vos variables d'environnement dans le index.html !!

Croyez-moi, je sais d'où vous venez! Intégrer des variables propres à l'environnement dans la phase de création de mon application Angular va à l'encontre de tout ce que j'ai appris sur la portabilité et la séparation des problèmes.

Mais attendez! Examinez de près un index.html angulaire commun:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>mysite</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="stylesheet" href="https://assets.mysite.com/styles.3ff695c00d717f2d2a11.css">
  <script>
  env = {
    api: 'https://api.mysite.com/'
  }
  </script>
</head>
<body>
  <app-root></app-root>
  <script type="text/javascript" src="https://assets.mysite.com/runtime.ec2944dd8b20ec099bf3.js"></script>
  <script type="text/javascript" src="https://assets.mysite.com/polyfills.20ab2d163684112c2aba.js"></script>
  <script type="text/javascript" src="https://assets.mysite.com/main.b55345327c564b0c305e.js"></script>
</body>
</html>

C'est toute la configuration !!!

C'est comme le fichier docker-compose.yml que vous utilisez pour gérer vos applications Docker: 

  • actifs immuables versionnés
  • variables d'environnement
  • liaison d'application
  • méta-données d'environnement
  • même les différents paquets ont l’impression de former des couches d’une image de docker, n’est-ce pas?
    • _ {runtime est comme votre image de base que vous modifiez rarement.}
    • _ {polyfills sont les éléments dont vous avez besoin et qui ne figuraient pas dans l'image de base dont vous avez besoin.} _
    • _ {main est votre application actuelle qui change à peu près chaque version.} _

Vous pouvez faire la même chose avec votre application frontend qu'avec votre application Docker!

  1. Construire, version et publier des actifs immuables (bundles js/image Docker)
  2. Publier un manifeste de déploiement dans le stockage intermédiaire (index.html/docker-compose.yml)
  3. Test en mise en scène
  4. Publiez un manifeste de déploiement en production .. référençant les mêmes actifs que ceux que vous venez de tester! Immédiatement! Atomiquement!

Comment??

Il suffit de pointer le /src/environments/environment.prod.ts puant sur l'objet window.

export const environment = (window as any).env;
// or be a rebel and just use window.env directly in your components

et ajoutez un script à votre index.html avec la variable d'environnement WHERE THEY BELONG!:

<script>
  env = { api: 'https://api.myapp.com' }
</script>

Je suis tellement convaincu par cette approche que j'ai créé un site Web dédié à celle-ci: https://immutablewebapps.org . Je pense que vous constaterez qu'il y a beaucoup d'autres avantages!

~~~

À présent, j'ai réussi à utiliser cette méthode avec deux AWS S3 Buckets: un pour les actifs statiques versionnés et un pour le index.html (le routage est extrêmement simple: servir index.html pour chaque chemin). Je ne l'ai pas fait avec des conteneurs comme vous le proposez. Si je devais utiliser des conteneurs, je voudrais faire une séparation nette entre le bâtiment et la publication de nouveaux actifs, et la publication d'un nouveau index.html. Peut-être que je rendrais index.html à la volée à partir d'un modèle avec les variables d'environnement du conteneur.

Si vous choisissez cette approche, j'aimerais savoir comment cela se passe!

2
Gene C

Je me débattais avec le même problème, mais je devais aussi passer les valeurs de configuration de docker-compose à Angular, ce que je n’ai pas trouvé aussi simple.

Fondamentalement, j'ai adopté une approche similaire et est venu avec la solution suivante:

  1. J'ai passé les valeurs désirées de docker-compose.yml à Dockerfile à l'aide du compose ARGS . Donc, dans docker-compose.yml j'ai:

magicsword.core.web: build: args: - AUTH_SERVER_URL=http://${EXTERNAL_DNS_NAME_OR_IP}:55888/ - GAME_SERVER_URL=http://${EXTERNAL_DNS_NAME_OR_IP}:55889/ - GUI_SERVER_URL=http://${EXTERNAL_DNS_NAME_OR_IP}:55890/ # =self

  1. Celles-ci doivent maintenant être marquées dans la variable Dockerfile en tant que variables:

ARG AUTH_SERVER_URL ARG GAME_SERVER_URL ARG GUI_SERVER_URL

  1. Comme elles deviennent des variables d’environnement normales au cours du processus de construction, la dernière étape consiste à effectuer la substitution réelle dans le fichier cible, par exemple. en utilisant un one-liner magique. J'ai fait comme suit (juste un projet d'animal familier donc n'a pas besoin d'être optimal):

RUN apt-get update && apt-get install -y gettext RUN envsubst < ./src/environments/environment.ts > ./src/environments/environment.ts.tmp && mv ./src/environments/environment.ts.tmp ./src/environments/environment.ts

Le environment.ts avant la substitution, pour référence:

export const environment = { production: true, GAME_SERVER_URL: "$GAME_SERVER_URL", GUI_SERVER_URL: "$GUI_SERVER_URL", AUTH_SERVER_URL: "$AUTH_SERVER_URL" };

Voila. J'espère que ça aide quelqu'un :)

1
Pawel Gorczynski

Ma solution: au moment de l'exécution, utilisez des volumes de menu fixe pour monter un fichier de configuration js spécifique sous le nom env.js.

J'ai un fichier docker composer pour dev et prod. 

J'ai dev.env.js et prod.env.js.

Mon fichier html fait référence à env.js.

Dans le volume docker-compose.yml, montez le fichier env sous la forme env.js.

Par exemple. mon dev compose:

web:
    image: nginx
    ports:
      - 80:80
    volumes:
      - ../frontend:/usr/share/nginx/html
      - ../frontend/dev.env.js:/usr/share/nginx/html/env.js

Et ma prod compose:

web:
    image: nginx
    ports:
      - 80:80
    volumes:
      - ../frontend:/usr/share/nginx/html
      - ../frontend/prod.env.js:/usr/share/nginx/html/env.js
0
jmoz