Angular4+K8s部署:如何用服务器环境变量替代多镜像构建?
Great question! The key issue here is that Angular's default setup bakes environment variables into the compiled bundle at build time, which forces you to create separate images for each environment. To fix this, we can switch to runtime configuration where we inject environment variables when the container starts, allowing a single image to work across both production and sandbox environments.
Here's a step-by-step solution tailored for your Angular 4 project:
1. Update Your Environment Config to Use Placeholders
First, modify your environment.prod.ts (we'll use this as the base for all builds since it includes production optimizations) to replace hardcoded values with placeholders. We'll keep production as true by default, but make it configurable if needed:
// src/environments/environment.prod.ts export const environment = { production: true, // Or use '{{PRODUCTION}}' if you need to toggle this at runtime server_url: '{{SERVER_URL}}' };
2. Create a Startup Script to Replace Placeholders
We'll write a lightweight shell script that runs when the container starts, replacing the placeholders in the compiled bundle with actual environment variables from the server/Kubernetes.
Create a start.sh file in your project root:
#!/bin/sh # Replace the SERVER_URL placeholder with the environment variable value sed -i "s|{{SERVER_URL}}|${SERVER_URL:-https://default-api.example.com/app/}|g" /usr/share/nginx/html/main*.js # Optional: If you made production configurable, handle the boolean conversion if [ -n "${PRODUCTION}" ]; then sed -i "s|{{PRODUCTION}}|${PRODUCTION}|g" /usr/share/nginx/html/main*.js fi # Start the nginx server nginx -g 'daemon off;'
Make sure to make the script executable:
chmod +x start.sh
3. Adjust Your Dockerfile
Update your Dockerfile to copy the startup script and use it as the entry point instead of directly starting nginx. Assuming you're using nginx as your base image:
FROM nginx:alpine # Copy your production-built Angular app to the nginx web directory COPY dist/your-app-name /usr/share/nginx/html # Copy the startup script COPY start.sh /start.sh # Set the startup command to run our script CMD ["/start.sh"]
4. Build a Single Production Image
Now build your image once using the production build command:
ng build --prod docker build -t your-app-image:latest .
5. Configure Kubernetes Deployments for Each Environment
In your Kubernetes Deployment manifests, set the environment variables specific to each environment.
Production Deployment Example:
apiVersion: apps/v1 kind: Deployment metadata: name: your-app-prod spec: replicas: 3 selector: matchLabels: app: your-app template: metadata: labels: app: your-app spec: containers: - name: your-app image: your-app-image:latest env: - name: SERVER_URL value: "https://api.example.com/app/" # Optional: Omit PRODUCTION to keep the default true value ports: - containerPort: 80
Sandbox Deployment Example:
apiVersion: apps/v1 kind: Deployment metadata: name: your-app-sandbox spec: replicas: 2 selector: matchLabels: app: your-app template: metadata: labels: app: your-app spec: containers: - name: your-app image: your-app-image:latest env: - name: SERVER_URL value: "https://sandbox-api.example.com/app/" - name: PRODUCTION value: "false" # Only if you need to toggle this ports: - containerPort: 80
Key Notes:
- Why this works: The
sedcommand modifies the compiled JavaScript bundle at runtime, replacing our placeholders with the environment variables passed into the container. Since we're using the production build (--prod), we get all the Angular optimizations (tree shaking, minification) regardless of the environment. - Handling Bundle Hashes: The
main*.jswildcard matches the hashed bundle filename Angular generates, so you don't have to update the script every time you rebuild. - Fallback Values: The
${SERVER_URL:-default-value}syntax ensures that if the environment variable isn't set, a default value is used (useful for local testing).
内容的提问来源于stack exchange,提问作者shulamit chatamov

