Azure Container Apps for Micro Frontend


This will be our app's initial setup
In the pre-Azure changes, 8081 was the port for remote-app and 8080 for host-app as seen from here

GITHUB

Deploying this on Azure as a Container App.

Note:

  • Docker is like a ready-to-eat meal—it’s already cooked and packed, so you can take it anywhere.
  • Azure Container Registry (ACR) is like a fridge where you store your meal until you need it.
  • Azure Container Apps (ACA) is like a microwave or chef that heats it up and serves it when you're ready to eat.

So, Docker preps the meal, ACR stores it, and ACA serves it hot when needed!
As soon as you prep a meal, first store it first in the ACR fridge. And then serve it on need basis from ACA

Docker

If you think about the application lifecycle—setting up the environment, adding the package.json file, installing dependencies, bringing in your code, building it, and finally running the application—you simply write these steps in an organized way inside a Dockerfile. This ensures everything happens in the correct order automatically.

In our example we have 2 apps that run

Both these apps need their own Docker file

./host-app/Dockerfile

FROM node:16-alpine

#STEP 1: Set the working directory
WORKDIR /app

#STEP 2: Copy package.json into the working directory
COPY package*.json ./

#STEP 3: Install dependencies
RUN npm install

#STEP 4: Copy the rest of the applciation
COPY . .

#Step 5: Build
RUN npm run build

#STEP 6: Expose the port app runs on
EXPOSE 8080

#STEP 6: WEBPACK SERVE
CMD ["npx", "webpack", "serve", "--host", "0.0.0.0"]

And the same contents go inside remote-app/Dockerfile (but port will be 8081)

Navigate to ./remote-app on command prompt

1. docker build -f  <Docker-file-name> -t <image-name>:<tag-version> .
2. docker run -p <host machine's port> (your computer>:<container's port (inside the Docker container)> <image-name>:<tag-version>
  1. docker build -f Dockerfile -t remote-app:v1 .
    • here the v1 is the tag version. You can keep incrementing for every buil
    • The . at the end tells Docker to use the current directory as the build context.
    • The context includes all files and directories inside this location, which might be required for the build process.
    2. docker run -p 8081:8081 remote-app:v1 

Repeat the same for host-app, but for port 8080

Azure

1. Creating a Resource group

2. Creating Container Registry


My Resource group: my-microFE-resourceGroup
My Container Registry: mymicrofeacr

These are going to be my remote and host URLs. You will not know them before hand. But placing it here because we need to update the "public path" in both the webpacks. There is no "localhost" anymore.

3. Update Webpack

This is how the final webpack looks with the Azure URLs
also update the "devServer" as below

remote-app/webpack.config.js
const path = require("path");
const { ModuleFederationPlugin } = require("webpack").container;
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: "development",
  entry: "./remoteModuleIndex.js",
  output: {
    publicPath: "https://mymicrofecontainerapp.salmonground-5ff0b0fe.westus2.azurecontainerapps.io/",
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  plugins: [
    new ModuleFederationPlugin({
      name: "remoteApp",
      filename: "remoteModuleIndex.js",
      exposes: {
        "./Module": "./remoteModuleIndex.js",
      },
    }),
    new HtmlWebpackPlugin({
        template: './index.html',  // Use our own template
        filename: 'index.html'         // Output file
    })
  ],
  devServer: {
    port: 8081,
    host: "0.0.0.0", // Allow external access from the container
    allowedHosts: "all", // Allow all host headers
    static: path.join(__dirname, "dist"),
  },
};


host-app/webpack.config.js
const path = require("path");
const { ModuleFederationPlugin } = require("webpack").container;
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: "development",
  entry: "./index.js",
  output: {
    publicPath: "https://mymicrofecontainerhostapp.salmonground-5ff0b0fe.westus2.azurecontainerapps.io/",
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  plugins: [
    new ModuleFederationPlugin({
      name: "hostApp",
      remotes: {
        remoteApp: "remoteApp@https://mymicrofecontainerapp.salmonground-5ff0b0fe.westus2.azurecontainerapps.io/remoteModuleIndex.js",
      },
    }),
     new HtmlWebpackPlugin({
            template: './index.html',  // Use our own template
            filename: 'index.html'         // Output file
        })
  ],
  devServer: {
    port: 8080,
    host: "0.0.0.0", // Allow external access from the container
    allowedHosts: "all", // Allow all host headers
    static: path.join(__dirname, "dist"),
  },
};

4. Create Container app


Make sure "ingress" is enabled and update the port number and Accept traffic from anywhere

5. Create your own environment if needed



Summary of the resources we configured:

  • My resource group: my-microFE-resourceGroup
  • My Container Registry: mymicrofeacr
  • My Container app name: mymicrofecontainerapp
  • My Container app name for host: mymicrofecontainerhostapp
  • My Container App Environment: mymicrofeenvironment

6. Let us now login to Azure Container Apps:

az acr login --name mymicrofeacr
az acr update --name mymicrofeacr --admin-enabled true

7. Build & Push Docker Images to ACR.

In your command prompt,
/remote-app/
docker build -f Dockerfile -t remote-app:v1 .

/host-app/
docker build -f Dockerfile -t host-app:v1 .

docker tag remote-app:v1 mymicrofeacr.azurecr.io/remote-app:v1
docker tag host-app:v1 mymicrofeacr.azurecr.io/host-app:v1

docker push mymicrofeacr.azurecr.io/remote-app:v1
docker push mymicrofeacr.azurecr.io/host-app:v1

8. Deploy

Now that we have pushed an image to the Container Registry, we will navigate to the "Container Apps > Containers > Edit & deploy "





9. Verify!

it will take a while to deploy. You can now go to the "overview" tab of the container. Here you get your application URL where the app is deployed

Host App


Remote App



These were the PR changes made for to upgrade our Micro Front-End App 
https://github.com/Kavshree/moduleFederation-MicroFE/pull/8

PUSHING UPDATES TO THE CODE

Make sure you are logged in to acr
az acr login --name mymicrofeacr
az acr update --name mymicrofeacr --admin-enabled true

docker build -f Dockerfile -t remote-app:v2 .
docker build -f Dockerfile -t host-app:v2 .

docker tag remote-app:v4 mymicrofeacr.azurecr.io/remote-app:v2
docker tag host-app:v4 mymicrofeacr.azurecr.io/host-app:v2

docker push mymicrofeacr.azurecr.io/remote-app:v2
docker push mymicrofeacr.azurecr.io/host-app:v2

Navigate to the container app and edit the image version to the new version pushed

USING ENVIRONMENT VARIABLES

Now since the public path is updated, in order to work on our local changes, we need to revert it back without affecting the excising configs. 

remote-app/webpack.config.js
const path = require("path");
const { ModuleFederationPlugin } = require("webpack").container;
const HtmlWebpackPlugin = require('html-webpack-plugin');

const remoteURL = process.env.REMOTE_URL || "http://localhost:8081/";
console.log("Configured REMOTE_URL env variable: ", process?.env?.REMOTE_URL);

/* To configure the below environment variables in Azure Container
REMOTE_URL
https://mymicrofecontainerapp.salmonground-5ff0b0fe.westus2.azurecontainerapps.io

HOST_URL:
https://mymicrofecontainerhostapp.salmonground-5ff0b0fe.westus2.azurecontainerapps.io
**/

module.exports = {
  mode: "development",
  entry: "./remoteModuleIndex.js",
  output: {
    publicPath: remoteURL,
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  plugins: [
    new ModuleFederationPlugin({
      name: "remoteApp",
      filename: "remoteModuleIndex.js",
      exposes: {
        "./Module": "./remoteModuleIndex.js",
      },
    }),
    new HtmlWebpackPlugin({
        template: './index.html',  // Use our own template
        filename: 'index.html'         // Output file
    })
  ],
  devServer: {
    port: 8081,
    host: "0.0.0.0", // Allow external access from the container
    allowedHosts: "all", // Allow all host headers
    static: path.join(__dirname, "dist"),
  },
};


host-app/webpack.config.js
const path = require("path");
const { ModuleFederationPlugin } = require("webpack").container;
const HtmlWebpackPlugin = require('html-webpack-plugin');

const hostURL = process.env.HOST_URL || "http://localhost:8080/";
const remoteURL = process.env.REMOTE_URL || "http://localhost:8081/";
console.log("Configured HOST_URL env variable: ", process?.env?.HOST_URL);
console.log("Configured REMOTE_URL env variable: ", process?.env?.REMOTE_URL);
/* To configure the below environment variables in Azure Container
REMOTE_URL
https://mymicrofecontainerapp.salmonground-5ff0b0fe.westus2.azurecontainerapps.io

HOST_URL:
https://mymicrofecontainerhostapp.salmonground-5ff0b0fe.westus2.azurecontainerapps.io
**/

module.exports = {
  mode: "development",
  entry: "./index.js",
  output: {
    publicPath: hostURL,
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  plugins: [
    new ModuleFederationPlugin({
      name: "hostApp",
      remotes: {
        remoteApp: `remoteApp@${remoteURL}remoteModuleIndex.js`,
      },
    }),
     new HtmlWebpackPlugin({
            template: './index.html',  // Use our own template
            filename: 'index.html'         // Output file
        })
  ],
  devServer: {
    port: 8080,
    host: "0.0.0.0", // Allow external access from the container
    allowedHosts: "all", // Allow all host headers
    static: path.join(__dirname, "dist"),
  },
};

CONFIGURE THE CONTAINER




To check console.log, navigate to container app > Log Stream on the left menu



The changes are here in the "environment-variables" branch



Comments