Site icon Blog ARC Optimizer

Docker et Angular 2: Variables d’environnement, images docker

Docker et Angular 2: Variables d’environnement, images docker


Apprenez à éviter de reconstruire des images Docker pour chaque environnement, à faciliter les variables dynamiques, à fournir une API environnement personnalisée et à partager des images avec des coéquipiers à l’aide de Docker Hub.

Dans notre article précédent Comment accrocher les applications angulairesnous avons appris docker, images docker et conteneurs. Nous avons également créé une image personnalisée de notre application Kendo Store. Mais à la fin, nous avons trouvé des problèmes avec notre solution.

L’application angulaire conteneurisée utilise toujours le https://fakestoreapi.com API définie dans le fichier Environment.ts. Cela fonctionne. Mais que se passe-t-il si vous souhaitez utiliser une autre API dans l’environnement de mise en scène ou de développement?

Lorsque nous utilisons le fichier Environment.ts en Angular, il définit les valeurs d’environnement lorsque vous créez l’application, nous devons donc créer à nouveau l’application pour chaque environnement.

Si vous ouvrez le fichier Environment.ts, cela ressemble à ceci:

export const environment = {
  api: 'https://fakestoreapi.com'
};

À l’heure actuelle, l’URL de l’API est définie pour https://fakestoreapi.com. Pour rendre les choses plus flexibles, nous devrions être en mesure de changer facilement des API, même pendant que l’application est en cours d’exécution. Sinon, c’est un problème si nous voulons utiliser la même image d’application pour différentes configurations comme la mise en scène, les tests ou la production.

Une meilleure façon de gérer cela, au lieu d’utiliser environment.tsconsiste à utiliser des variables d’environnement et à les injecter directement dans le conteneur. De cette façon, nous n’avons pas à reconstruire l’image pour chaque environnement.

Aujourd’hui, nous allons nous familiariser avec l’utilisation des variables d’environnement dynamique dans Docker, ajuster notre dockerfile, faire quelques ajustements à notre code angulaire et enfin partager notre application conteneurisée avec d’autres coéquipiers à l’aide de Docker Hub!

Cela semble amusant, faisons-le!

Injecter des variables d’environnement

Avant de commencer, rappelez-vous que cet article est la partie 2, afin que vous puissiez Lire la partie 1 ou continuez simplement en clonant le référentiel et en pointant vers le using-docker bifurquer. Enfin, ouvrez le projet avec votre éditeur préféré pour commencer à apporter nos modifications.

git clone --branch using-docker https://github.com/danywalls/dockerize-angular-app.git

Nous allons essayer une nouvelle approche. Au lieu de définir des variables codées en dur à l’intérieur du environment.ts Fichier, nous passerons le point de terminaison de l’API lors de l’exécution du conteneur.

La première étape consiste à créer un nouveau fichier appelé env.template.jsonce modèle sera utilisé pour injecter des valeurs à partir des variables d’environnement au moment de l’exécution.

{
  "API_URL": "${API_URL}"
}

Ensuite, nous allons créer un script shell nommé entrypoint.sh. Ce script lira le modèle et remplacera les espaces réservés par des valeurs réelles des variables d’environnement en utilisant envsubst.

envsubst est une partie de commande Linux du gettext Package qui remplace les variables d’environnement dans les fichiers texte. C’est comme une trouvaille et remplacer automatiquement $VARIABLE avec sa valeur actuelle.

Exemple:

export API_URL="https://fakestoreapi.com"


API_URL=${API_URL}

envsubst < config.template > config.json

En mots simples, il aide à remplacer le texte par une valeur de variable d’environnement.

Créer un fichier nommé entrypoint.shqui prend la responsabilité de copier le fichier Env.json en actifs avec les variables d’environnement spécifiques à chacun. Se combinant avec le envsubstil remplace les variables pour rendre simple à copier le contenu suivant:

Le fichier d’entrée.sh doit être dans l’espace de travail racine.

#!/bin/sh
echo "Injecting environment variables into env.json..."

envsubst < /usr/share/nginx/html/assets/env.template.json > /usr/share/nginx/html/assets/env.json

exec "$@"

Alors qu’est-ce que fait $@ signifier? Le $@ est utilisé pour exécuter la commande utilisée à l’origine pour s’exécuter dans le conteneur.

La combinaison du env.template.json avec entrypoint.shLe script fonctionnera ensemble dans votre conteneur Docker. Au moment de l’exécution, le conteneur remplacera les espaces réservés par des valeurs d’environnement réelles, vous n’avez donc pas besoin de reconstruire l’image à chaque fois.

Maintenant que nous avons notre env.template.json et entrypoint.sh Script, nous devons dire à Docker comment les utiliser. Nous allons apporter ces modifications à l’intérieur du dockerfile.

Le dockerfile

Ouvrez le dockerfile, où nous devons copier notre modèle et notre script dans l’image. Après avoir copie l’état de construction, nous copie le env.template.json fichier et point d’entrée et apporter une autorisation exécutable dans le conteneur.

Pour simplifier, ajoutons les lignes suivantes.

COPY env.template.json /usr/share/nginx/html/assets/env.template.json
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

Que font ces lignes? Premièrement, nous copierons env.template.json dans le dossier Angular Assets à l’intérieur du conteneur. Et entryPoint.sh est copié dans la racine du conteneur.

Par défaut, les nouveaux fichiers n’ont pas d’autorisations d’exécution dans Linux, nous devons donc donner au script des autorisations exécutables en utilisant chmod +x /entrypoint.sh.

chmod +x entrypoint.sh

En savoir plus sur Autorisation Linux.

Le point d’entrée de ligne [« /entrypoint.sh »] fait que le script s’exécute en premier à chaque fois que le conteneur démarre.

La version finale:

FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist/kendo-store/browser /usr/share/nginx/html


COPY env.template.json /usr/share/nginx/html/assets/env.template.json
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

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

Maintenant, notre conteneur est maintenant flexible – nous pouvons utiliser la même image Docker en développement, en stadification ou en production simplement en passant différentes variables d’environnement à l’exécution.

Mais comment Angular le prendra-t-il? Nous devons apporter quelques modifications.

Chargez l’env.json avec un service angulaire

Créons maintenant le service angulaire qui charge la configuration de l’environnement d’exécution, en utilisant la CLI angulaire exécuter la commande ng g s services/enviroment-config.

import { Injectable } from '@angular/core';

@Injectable({
 providedIn: 'root'
})
export class EnviromentConfigService {

 constructor() { }
}

Que fera le EnviromentConfigService?

La responsabilité de EnviromentConfigService est d’obtenir et de fournir la clé Env.json et les valeurs de notre application. N’oubliez pas que Env.json est créé lors de l’exécution lorsque Docker commence. Ensuite, notre service prend ce fichier avec les clés d’environnement et le stocke pour donner accès à d’autres services, en lisant le bon environnement.

Ouvrir le environment-config.service.ts déposer. Pour obtenir l’environnement et le magasin, nous devons créer des actions clés. Faites une demande HTTP au fichier Env.json et stockez-le quelque part avec deux champs privés, http et env.

Le http est une instance de httpclient en utilisant le inject fonction et env est un Record type de données de <string, string> valeurs.

private http = inject(HttpClient)
private env : Record<string, string> = {
 API_URL: ''
};

Parfait, ensuite nous devons créer deux méthodes – une pour faire la demande et charger l’env.json dans le champ Env, et l’autre pour fournir les touches d’environnement chargées à l’application angulaire, créant une fonction load.

Le load La fonction utilise le http avec get Méthode pour demander le assets/env.json. Après charge, en utilisant le tap Opérateur RXJS, définissez la valeur dans le envchamp.

En savoir plus sur http et tap opérateur.

  load() {
    return this.http.get<Record<string, string>>('/assets/env.json').pipe(
      tap(data => {
        this.env = data;
        })
    )
  }

Si vous souhaitez vérifier que les variables d’environnement requises sont réellement présentes dans le env.jsonvous pouvez également ajouter une étape de validation simple dans le load() méthode. Cela aide à attraper les erreurs de configurations tôt pendant l’exécution:

import {inject, Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {tap} from "rxjs";

@Injectable({ providedIn: 'root' })
export class EnvironmentConfigService {
  private http = inject(HttpClient)
  private env : Record<string, string> = {
    API_URL: ''
  };

  load() {
    return this.http.get<Record<string, string>>('/assets/env.json').pipe(
      tap(data => {
        this.env = data;
        if (!this.env?.["API_URL"]) {
          console.warn('Up’s  the API_URL is not defined in env.json');
        }
      })
    )
  }

}

Enfin, créez la fonction Get pour obtenir la clé demandée par l’application, stockée dans le env champ. Le code est simple.

  get(key: string): string {
    return this.env?.[key];
  }

Parfait, nous avons notre EnvironmentConfigService prêt. Le code final ressemble:

import {inject, Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {tap} from "rxjs";

@Injectable({ providedIn: 'root' })
export class EnvironmentConfigService {
 private http = inject(HttpClient)
 private env : Record<string, string> = {
   API_URL: ''
 };

 load() {
   return this.http.get<Record<string, string>>('/assets/env.json').pipe(
     tap(data => {
       this.env = data;
       if (!this.env?.["API_URL"]) {
         console.warn('⚠️ Ups: API_URL is not defined in env.json');
       }
     })
   )
 }

 get(key: string): string {
   return this.env?.[key];
 }
}

Nous devons apporter des modifications au products.service.ts fichier pour utiliser le EnviromentConfigServiceen l’injectant et en appelant le get Fonction pour obtenir la valeur de l’API_URL.

private environmentConfigService = inject(EnvironmentConfigService)
private apiUrl = this.environmentConfigService.get('API_URL');

N’oubliez pas de mettre à jour les méthodes du ProductsService fichier pour utiliser le apiUrl variable.


public products$ = this.http.get<Product[]>(this.apiUrl);
public productById = (id: string) => this.http.get<Product>(`${this.apiUrl}/${id}`);

Le code final pour products.service.tson dirait:

import { HttpClient } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';
import { EnvironmentConfigService } from './enviroment-config.service';

export type Product = {
 id: string;
 title: string;
description: string;
 image: string;
 price: string;
}

@Injectable({
 providedIn: 'root'
})
export class ProductsService {

 private http = inject(HttpClient)
 private environmentConfigService = inject(EnvironmentConfigService)
 private apiUrl = this.environmentConfigService.get('API_URL');
 public products$ = this.http.get<Product[]>(this.apiUrl);
 public productById = (id: string) => this.http.get<Product>(`${this.apiUrl}/${id}`);
}

OK, mais maintenez-le sur une seconde. Le ProductsService utilise la valeur de EnvironmentConfigServicemais la valeur de l’environnement doit être chargée avant de commencer à utiliser ProductsService. Nous devons charger env.json et fournir les clés avant que l’utilisateur ne commence à naviguer dans l’application. Comment pouvons-nous atteindre cet objectif?

Ne t’inquiète pas – l’angulaire nous donne provideAppInitializer. Pour la nostalgie, c’est similaire à App_initializer Token, qui est maintenant obsolète, mais provideAppInitialize est plus facile et moins passe-partout. Il nous permet également d’utiliser inject Pour le rendre facile à utiliser EnvironmentConfigService. Passons à autre chose.

Utilisation de ProvideAppinitializer

Tout d’abord, ouvrez le app.config.ts. Dans le appConfig mettre à jour les fournisseurs utilisent le provideAppInitializer Parce qu’il nous permet d’utiliser inject Pour charger le EnviromentConfigService et appelez le load Méthode, car c’est une promesse, nous renvoyons la charge pour nous assurer que le provideAppInitializerAttendez de compléter la demande.

 provideAppInitializer(() => {
      const runtimeConfig: EnvironmentConfigService = inject(EnvironmentConfigService);
       return runtimeConfig.load()
    }),

Le code final ressemble:

import {ApplicationConfig, inject, provideAppInitializer} from '@angular/core';
import {provideRouter, withComponentInputBinding} from '@angular/router';

import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';
import {EnvironmentConfigService} from "./services/enviroment-config.service";


export const appConfig: ApplicationConfig = {
 providers: [
   provideRouter(routes, withComponentInputBinding()),
   provideHttpClient(),
   provideAppInitializer(() => {
     const runtimeConfig: EnvironmentConfigService = inject(EnvironmentConfigService);
      return runtimeConfig.load()
   }),

 ]
};

Ok, on dirait que tout est prêt. Il est temps de tester notre solution en exécutant Docker.

Exécutez avec des variables personnalisées

Parce que nous avons apporté des modifications au dockerfile, exécuter le conteneur avec les dernières modifications créera une nouvelle image avec une nouvelle balise comme testing-kendo-store-env. Dans votre terminal, exécutez la commande suivante.

docker build -t testing-kendo-store-env .

[+] Building 169.3s (18/18) FINISHED                              docker:default
 => [internal] load build definition from Dockerfile                        0.0s
 => => transferring dockerfile: 424B                                        0.0s
 => [internal] load metadata for docker.io/library/node:20-alpine          22.3s
 => [internal] load metadata for docker.io/library/nginx:alpine            22.3s
 => [auth] library/node:pull token for registry-1.docker.io                 0.0s
 => [auth] library/nginx:pull token for registry-1.docker.io                0.0s
 => [internal] load .dockerignore                                           0.0s
 => => transferring context: 2B                                             0.0s
 => [builder 1/5] FROM docker.io/library/node:20-alpine@sha256:df02558528d  0.0s
 => => resolve docker.io/library/node:20-alpine@sha256:df02558528d3d3d0d62  0.0s
 => [internal] load build context                                           6.1s
 => => transferring context: 282.99MB                                       6.0s
 => CACHED [stage-1 1/5] FROM docker.io/library/nginx:alpine@sha256:d67ea0  0.0s
 => => resolve docker.io/library/nginx:alpine@sha256:d67ea0d64d518b1bb04ac  0.0s
 => CACHED [builder 2/5] WORKDIR /app                                       0.0s
 => [builder 3/5] COPY . .                                                  7.5s
 => [builder 4/5] RUN npm ci                                              125.3s
 => [builder 5/5] RUN npm run build                                         6.6s 
 => [stage-1 2/5] COPY --from=builder /app/dist/kendo-store/browser /usr/s  0.6s 
 => [stage-1 3/5] COPY env.template.json /usr/share/nginx/html/assets/env.  0.0s 
 => [stage-1 4/5] COPY entrypoint.sh /entrypoint.sh                         0.0s 
 => [stage-1 5/5] RUN chmod +x /entrypoint.sh                               0.3s 
 => exporting to image                                                      0.3s 
 => => exporting layers                                                     0.1s
 => => exporting manifest sha256:695a76fd1bf1148280b37ae4207a44fc2b4bb7e55  0.0s
 => => exporting config sha256:f2693563b59b77ec9e7e7a02ee6a8dcfcc731457bdf  0.0s
 => => exporting attestation manifest sha256:86777963f91d2fe05dbbf6fe60570  0.0s
 => => exporting manifest list sha256:0825193cfdc3d320711eeb9bffc4e1a26433  0.0s
 => => naming to docker.io/library/testing-kendo-store-env:latest           0.0s
 => => unpacking to docker.io/library/testing-kendo-store-env:latest        0.1s

Parfait, nous avons une nouvelle image testing-kendo-store-env:latest.

docker images

Lorsque vous exécutez votre conteneur Docker, vous pouvez injecter dynamiquement le API_URL en le définissant avec -e:

docker run -p 8080:80 -e API_URL=https://staging.fakestoreapi.com testing-kendo-store-env

Ouais! Il pointe vers un autre point final comme il est censé le faire, mais peut-être au lieu de taper l’URL à chaque fois, nous devrions créer .env Fichiers pour chaque environnement – comme .env.development, .env.staging et .env.production.

API_URL=http://localhost:3000
API_URL=https://staging.fakestoreapi.com
API_URL=https://fakestoreapi.com

Enfin, exécutez le conteneur pointant vers votre fichier en utilisant --env-filecomme:

docker run --env-file .env.staging -p 8080:80 testing-kendo-store-env

Cela vous permet de conserver une seule image Docker et de changer proprement des environnements sans reconstruire. Le conteneur sera désormais chargé de la configuration de l’environnement dynamiquement à partir du env.json généré pendant le démarrage du conteneur. Il fait une seule image pour tous les environnements, découplant la construction à partir de la configuration de l’environnement afin qu’elle soit réutilisable et facile à entretenir.

Parfait, nous avons eu une image avec un environnement dynamique. Mais, hmm, comment puis-je partager cette image avec d’autres coéquipiers? Eh bien, il est temps de passer à Docker Hub!

Partage avec Docker Hub!

Maintenant que nous avons une image Docker dynamique et réutilisable, il est temps de le partager avec le monde – ou du moins avec des coéquipiers comme Alyssa et moi – en utilisant Docker Hub.

Qu’est-ce que Docker Hub?

Docker Hub est un endroit où vous pouvez stocker, gérer et partager des images Docker. Vous pouvez le considérer comme GitHub, mais pour les conteneurs Docker. Il est facile de distribuer des applications conteneurisées avec votre équipe ou le public, alors créons notre compte et partageons notre application de magasin angulaire conteneurisé!

Tout d’abord, créez un compte gratuit à hub.docker.com. Une fois que vous avez terminé votre inscription, vous devez vous connecter à partir du terminal avec docker login commande:

docker login

Vous serez invité à entrer votre nom d’utilisateur et votre mot de passe Docker Hub.

Ensuite, créez votre image Docker et donnez-lui un nom et une balise afin qu’il puisse être poussé vers votre compte Docker Hub. Si votre nom d’utilisateur Docker Hub est danywalls4la commande peut ressembler à ceci:

docker build -t danywalls4/kendo-store:latest .

:latest est une balise. Vous pouvez également utiliser des balises comme v1, prod ou stagingselon le contexte.

Ensuite, poussez et publiez votre image sur Docker Hub. Utiliser le docker push commande:

docker push danywalls4/kendo-store:latest

Cela peut prendre quelques secondes en fonction de la taille de l’image et de votre vitesse Internet.

Une fois terminé, votre image est publique et accessible. Par exemple, vous pouvez trouver le mien à:
👉 https://hub.docker.com/r/danywalls4/kendo-store/tags.

Maintenant, à l’aide de Docker Hub, vos coéquipiers peuvent retirer l’image et exécuter votre application angulaire – la personnaliser avec des variables d’environnement pour leurs environnements spécifiques!

Résumer

Nous avons appris les scénarios du monde réel concernant la façon d’éviter de reconstruire des images pour chaque environnement, de faciliter les variables dynamiques, de fournir une API environnement personnalisée et de partager nos images avec d’autres coéquipiers et le monde à l’aide de Docker Hub.

Notre application angulaire est passée de valeurs codées en dur dans Environment.ts à un fichier Env.json dynamique et des variables d’environnement injectées à l’exécution à l’aide d’un petit script de shell pour charger la configuration avant le début de l’application. Cela nous a aidés à utiliser la même image Docker pour différents environnements comme le développement, la mise en scène ou la production et enfin partager notre image Docker avec d’autres à l’aide de Docker Hub.

Vous êtes maintenant prêt à déployer vos applications angulaires avec Docker et un environnement flexible dans le monde réel!

Heureux docker !!!




Source link
Quitter la version mobile