Friday, July 15, 2022

A look at some of Azure Container App’s new features

 

A while ago, I created a YouTube playlist about Azure Container Apps. The videos were based on the first public preview. At the time, several features were missing or still needed to be improved (as expected with a preview release):

  • An easy way to create a container app, similar to az webapp up
  • Managed Identity support (system and user assigned)
  • Authentication support with identity providers like Microsoft, Google, Twitter
  • An easy way to follow the logs of a container from your terminal (instead of using Log Analytics queries)
  • Getting a shell to your container for troubleshooting purposes

Let’s take a look at some of these features.

az containerapp up

To manage Container Apps, you can use the containerapp Azure CLI extension. Add it with the following command:

az extension add --name containerapp --upgrade

One of the commands of this extension is up. It lets you create a container app from local source files or from GitHub. With your sources in the current folder, the simplest form of this command is:

az containerapp up --name YOURAPPNAME --source .

The command above creates the following resources:

  • a resource group: mine was called geert_baeke_rg_3837
  • a Log Analytics workspace
  • a Container Apps environment: its name is YOURAPPNAME-env
  • an Azure Container Registry: used to build the container image from a Dockerfile in your source folder
  • the container app: its name is YOURAPPNAME

The great thing here is that you do not need Docker on your local machine for this to work. Building and pushing the container image is done by an ACR task. You only need a Dockerfile in your source folder.

When you change your source code, simply run the same command to deploy your changes. A new image build and push will be started by ACR and a revision of your container app will be published.

⚠️TIP: by default, the container app does not enable ingress from the Internet. To do so, include an EXPOSE command in your Dockerfile.

If you want to try az containerapp up, you can use my super-api sample from GitHub: https://github.com/gbaeke/super-api

Use the following commands to clone the source code and create the container app:

1
2
3
git clone https://github.com/gbaeke/super-api.git
cd super-api
az containerapp up --name super-api --source . --ingress external --target-port 8080

Above, we added the –ingress and –target-port parameters to enable ingress. You will get a URL like https://super-api.livelyplant-fa0ceet5.eastus.azurecontainerapps.io to access the app. In your browser, you will just get: Hello from Super API. If you want a different message, you can run this command:

1
az containerapp up --name super-api --source . --ingress external --target-port 8080 --env-vars WELCOME=YOURMESSAGE

Running the above command will result in a new revision. Use az containerapp revision list -n super-api -g RESOURCEGROUP -o table to see the revisions of your container app.

There is much more you can do with az containerapp up:

  • Deploy directly from a container image in a registry (with the option to supply registry authentication if the registry is private)
  • Deploy to an existing container app environment
  • Deploy to an existing resource group
  • Use a GitHub repo instead of local sources which uses a workflow to deploy changes as you push them

Managed Identity

You can now easily enable managed identity on a container app. Both System assigned and User assigned are supported. Below, system assigned managed identity was enabled on super-api:

System assigned identity on super-api

Next, I granted the managed identity Reader role on my subscription:

Enabling managed identity is easy enough. In your code, however, you need to obtain a token to do the things you want to do. At a low level, you can use an HTTP call to fetch the token to access a resource like Azure Key Vault. Let’s try that and introduce a new command to get a shell to a container app:

az containerapp exec  -n super-api -g geert_baeke_rg_3837 --command sh

The above command gets a shell to the super-api container. If you want to try this, first modify the Dockerfile and remove the USER command. Otherwise, you are not root and will not be able to install curl. You will also need to use an alpine base image in the second stage instead of scratch (the scratch image does not offer a shell).

In the shell, run the following commands:

1
2
3
apk add curl
curl -H "X-IDENTITY-HEADER: $IDENTITY_HEADER" \
  "$IDENTITY_ENDPOINT?resource=https://vault.azure.net&api-version=2019-08-01"

The response to the above curl command will include an access token for the Azure Key Vault resource.

A container app with managed identity has several environment variables:

  • IDENTITY_ENDPOINT: http://localhost:42356/msi/token (the endpoint to request the token from)
  • IDENTITY_HEADER: used to protect against server-side request forgery (SSRF) attacks

Instead of using these values to create raw HTTP requests, you can use SDK’s instead. The documentation provides information for .NET, JavaScript, Python, Java, and PowerShell. To try something different, I used the Azure SDK for Go. Here’s a code snippet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func (s *Server) authHandler(w http.ResponseWriter, r *http.Request) {
    // parse subscription id from request
    subscriptionId := r.URL.Query().Get("subscriptionId")
    if subscriptionId == "" {
        s.logger.Infow("Failed to get subscriptionId from request")
        w.WriteHeader(http.StatusBadRequest)
        return
    }
 
    client := resources.NewGroupsClient(subscriptionId)
    authorizer, err := auth.NewAuthorizerFromEnvironment()
    if err != nil {
        s.logger.Error("Error: ", zap.Error(err))
        return
    }
    client.Authorizer = authorizer

Although the NewAuthorizerFromEnvironment() call above supports managed identity, it seems it does not support the endpoint used in Container Apps and Azure Web App. The code above works fine on a virtual machine and even pod identity (v1) on AKS.

We can use another feature of az containerapp to check the logs:

az containerapp logs show -n super-api -g geert_baeke_rg_3837 --follow

"TimeStamp":"2022-05-05T10:49:59.83885","Log":"Connected to Logstream. Revision: super-api--0yp202c, Replica: super-api--0yp202c-64746cc57b-pf8xh, Container: super-api"}
{"TimeStamp":"2022-05-04T22:02:10.4278442+00:00","Log":"to super api"}
{"TimeStamp":"2022-05-04T22:02:10.427863+00:00","Log":""}
{"TimeStamp":"2022-05-04T22:02:10.4279478+00:00","Log":"read config error Config File "config" Not Found in "[/config]""}
{"TimeStamp":"2022-05-04T22:02:10.4280241+00:00","Log":"logger"}"}
{"TimeStamp":"2022-05-04T22:02:10.4282641+00:00","Log":"client initializing for: 127.0.0.1:50001"}
{"TimeStamp":"2022-05-04T22:02:10.4282792+00:00","Log":"values","welcome":"Hello from Super API","port":8080,"log":false,"timeout":15}"}
...

When I try to execute the code that’s supposed to get the token, I get the following error:

{"TimeStamp":"2022-05-05T10:51:58.9469835+00:00","Log":"{error 26 0  MSI not available}","stacktrace":"..."}

As always, it is easy to enable managed identity but tricky to do from code (sometimes ðŸ˜‰). With the new feature that lets you easily grab the logs, it is simpler to check the errors you get back at runtime. Using Log Analytics queries was just not intuitive.

Conclusion

The az container up command makes it extremely simple to deploy a container app from your local machine or GitHub. It greatly enhances the inner loop experience before you start deploying your app to other environments.

The tooling now makes it easy to exec into containers and troubleshoot. Checking runtime errors from logs is now much easier as well.

Managed Identity is something we all were looking forward to. As always, it is easy to implement but do check if the SDKs you use support it. When all else fails, you can always use HTTP! ðŸ˜‰

Sunday, June 12, 2022

How to cache API response - Angular.

 


C# 9.0 - initialisation of mutable objects

 C# 9.0 - initialisation of mutable objects


Initialisation of mutable objects simplified in c# 9 using




dot net 3.0 -Asyn streams

 


Nullish Coaleasching - Angular 12

 Angular 12 : Nullish Coaleascing


Allows developers to replace complex epressions : {{ age !=  null && age != undefined ? age: calculateage()}}




Angular 11 vs 12 major differnces.


Friday, May 27, 2022

C++ simple example for Factory code to complex example.

 Simple factory method code:

#include <iostream>
#include <string>

using namespace std;

/*
 * Define an interface for creating an object, but let 
 * subclasses decide which class to instantiate. 
 */
 
class Shape {
    public:
       virtual void Draw() = 0;
       static Shape* ShapeFactory(string type);
};

class Circle : public Shape {
    public:
       void Draw() { cout << "I am circle" << endl; }
       friend class Shape;
};

class Square : public Shape {
    public:
       void Draw() { cout << "I am square" << endl; }
       friend class Shape;
};

class Rectangle : public Shape {
    public:
       void Draw() { cout << "I am rectangle " << endl; }
       friend class Shape;
};

Shape* Shape::ShapeFactory(string type) {
    if ( type == "circle" ) return new Circle();
    if ( type == "square" ) return new Square();
    if ( type == "rectangle" ) return new Rectangle();
    return NULL;
}

int main(){
   Shape* obj1 = Shape::ShapeFactory("circle");
   Shape* obj2 = Shape::ShapeFactory("square");
   Shape* obj3 = Shape::ShapeFactory("rectangle");
   obj1->Draw();
   obj2->Draw();
   obj3->Draw();
}
Abstract Factory:










using AbstractFactoryPattern;
 
AbstractFactory factoryI = new ConcreteFactoryI();
Client clientI = new Client(factoryI);
clientI.Run();
 
AbstractFactory factoryII = new ConcreteFactoryII();
Client clientII = new Client(factoryII);
clientII.Run();
public class VehicleBuilderProvider : IVehicleBuilderProvider
	{
		private readonly Dictionary<VehicleType , IVehicleBuilder> _vehicleSet;

		public VehicleBuilderProvider(CarBuilder car , BykeBuilder byke)
		{
			_vehicleSet = new Dictionary<VehicleType, IVehicleBuilderProvider>()
			{
				{ VehicleType.Car, car},
				{ VehicleType.Byke, byke}
			};
		}

		public IIVehicleBuilderProvider Get(VehicleType vehicleType)
		{
			return _vehicleSet[VehicleType];
		}
	}
Dot net.: 
Controller and factory :
public class StreamController : Controller
{
private readonly StreamFactory streamFactory;
public StreamController(StreamFactory streamFactory)
{
this.streamFactory = streamFactory;
}
[HttpGet("movies/{userSelection}")]
public IEnumerable<string> GetMovies(string userSelection)
{
return streamFactory.GetStreamService(userSelection).ShowMovies();
}
}
public class StreamFactory
{
private readonly IServiceProvider serviceProvider;
public StreamFactory(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public IStreamService GetStreamService(string userSelection)
{
if(userSelection =="Netflix")
return (IStreamService)serviceProvider.GetService(typeof(NetflixStreamService));
return (IStreamService)serviceProvider.GetService(typeof(AmazonStreamService));
}

How Netflix Scales its API with GraphQL Federation (Part 1)

  Netflix is known for its loosely coupled and highly scalable microservice architecture. Independent services allow for evolving at differe...