Here is what you need to get this running in Azure in no time:
- Create your Web API project let's call it
MyApp.Image.Api
and let's say it depends on another projectMyApp.Core
- Add
Dockerfile
to yourMyApp.Image.Api
project
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
ARG ENVIRONMENT
ENV ASPNETCORE_URLS http://*:5000
ENV ENVIRONMENT_NAME "${ENVIRONMENT}"
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
# copy project dependencies
COPY ["src/MyApp.Core/MyApp.Core.csproj", "src/MyApp.Core/"]
COPY ["src/MyApp.Image.Api/MyApp.Image.Api.csproj", "src/MyApp.Image.Api/"]
RUN dotnet restore "src/MyApp.Image.Api/MyApp.Image.Api.csproj"
COPY . .
RUN dotnet build "src/MyApp.Image.Api/MyApp.Image.Api.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "src/MyApp.Image.Api/MyApp.Image.Api.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.Image.Api.dll"]
-
Next within the same project create directory called
Proxy
in it you will need 4 files:-
Caddyfile.development
- this will create proxy when running locally with self-signed certificate
-
{
email artur@isready.io
}
https://localhost {
reverse_proxy localhost:5000
}
-
Caddyfile.staging
{
email artur@isready.io
}
https://myapp-image-api-staging.eastus.azurecontainer.io {
reverse_proxy localhost:5000
}
-
Caddyfile.production
{
email artur@isready.io
}
https://myapp-image-api.eastus.azurecontainer.io {
reverse_proxy localhost:5000
}
-
Dockerfile
- we will set environment prior to executing this
FROM caddy:latest
ARG ENVIRONMENT
COPY "src/MyApp.Image.Api/Proxy/Caddyfile.${ENVIRONMENT}" /etc/caddy/Caddyfile
*if you don't have multiple environments you know what to skip :-)
- Now you need to add
docker-compose.yml
to the project
version: '3.4'
services:
proxy:
image: myapp-image-api-proxy:${CONTAINER_VERSION}-${ENVIRONMENT}
build:
context: ../../
args:
ENVIRONMENT: ${ENVIRONMENT}
dockerfile: src/MyApp.Image.Api/Proxy/Dockerfile
api:
image: myapp-image-api:${CONTAINER_VERSION}-${ENVIRONMENT}
depends_on:
- proxy
build:
context: ../../
args:
ENVIRONMENT: ${ENVIRONMENT}
dockerfile: src/MyApp.Image.Api/Dockerfile
You should end up with this structure:
- Now you are ready to go! I use that powershell script to build my images. If you are building to locally just run this:
# set azure environment
$env:ENVIRONMENT = 'development'
$env:CONTAINER_VERSION = 'latest'
# builds images
docker-compose build
If you want to build and push images to Azure Container Registry go for commands below but before make sure you have created registry:
az provider register --namespace Microsoft.ContainerInstance`
az acr create --resource-group EastUS--name myapp --sku Basic // *enable admin user in azure portal once Azure Container Registry is created
# logs to azure
az login
# set azure environment
$env:ENVIRONMENT = 'staging'
$env:CONTAINER_VERSION = 'latest'
# builds images
docker-compose build
# logs to Azure Container Registry
az acr login --name myapp
# tag images
docker tag myapp-image-api-proxy:latest-staging myapp.azurecr.io/myapp-image-api-proxy:latest-staging
docker tag myapp-image-api:latest-staging myapp.azurecr.io/myapp-image-api:latest-staging
# push images
docker push myapp.azurecr.io/myapp-image-api-proxy:latest-staging
docker push myapp.azurecr.io/myapp-image-api:latest-staging
# clean up
docker rmi myapp-image-api-proxy:latest-staging
docker rmi myapp-image-api:latest-staging
docker rmi myapp.azurecr.io/myapp-image-api-proxy:latest-staging
docker rmi myapp.azurecr.io/myapp-image-api:latest-staging
Cool! You are ready to fire it up!
- You will need two packages:
Microsoft.Azure.Management.ContainerInstance.Fluent
Microsoft.Azure.Management.Fluent
- Let's start from connecting to Azure using C# Fluent API
private IAzure GetAzureContext()
{
Log.Information("[Container] Getting Service Principal");
var creds = new AzureCredentialsFactory().FromServicePrincipal(
_config["AppSettings:ImageApi:ServicePrincipalClientId"],
_config["AppSettings:ImageApi:ServicePrincipalSecretId"],
_config["AppSettings:ImageApi:ServicePrincipalTenat"],
AzureEnvironment.AzureGlobalCloud);
Log.Information("[Container] Getting subscribtion");
var azure = Microsoft.Azure.Management.Fluent.Azure.Authenticate(creds).WithSubscription(_config["AppSettings:ImageApi:SubscribtionId"]);
return azure;
}
Where do you get these service principal ids?
In Azure CLI do:
az ad sp create-for-rbac --name myapp-containers --sdk-auth > my.azureauth
That file will have all service principal data needed.
- Now the main part:
private string CreateContainer()
{
Log.Information("[Container] Authenticating with Azure");
var azureContext = GetAzureContext();`
// Get azure container registry for my resource group named 'EastUS' and registry 'myapp'
var azureRegistry = azureContext.ContainerRegistries.GetByResourceGroup(
_config["AppSettings:ImageApi:ResourceGroupName"],
_config["AppSettings:ImageApi:ContainerRegistryName"]);
var acrCredentials = azureRegistry.GetCredentials();
// Get container group for my resource group named 'EastUS' and container group i.e 'myapp-image-api-staging'
var containerGroup = azureContext.ContainerGroups.GetByResourceGroup(
_config["AppSettings:ImageApi:ResourceGroupName"],
_config["AppSettings:ImageApi:ContainerGroupName"]
);
if (containerGroup is null)
{
Log.Information("[Container] Creating with fluent API");
// ContainerGroupName = 'myapp-image-api-staging'
// ResourceGroupName = 'EastUS'
// VolumeName = 'image-api-volume'
// FileShare = 'containers'
// # yes you need to have storage account with file share, we need it so that proxy (caddy2) can store Let's Encrypt certs in there
// az storage share create --name myapp-staging-containers-share --account-name myappstaging
//
// StorageAccountName = 'myappstaging'
// StorageAccountKey = 'well-that-key-here'
// ProxyContainerName = 'image-api-proxy'
// ProxyImageName = 'myapp.azurecr.io/myapp-image-api-proxy:latest-staging'
// VolumeMountPath = '/data/'
// ApiContainerName 'image-api'
// ApiImageName 'myapp.azurecr.io/myapp-image-api:latest-staging'
containerGroup = azureContext.ContainerGroups.Define(_config["AppSettings:ImageApi:ContainerGroupName"])
.WithRegion(Region.USEast)
.WithExistingResourceGroup(_config["AppSettings:ImageApi:ResourceGroupName"])
.WithLinux()
.WithPrivateImageRegistry(azureRegistry.LoginServerUrl, acrCredentials.Username, acrCredentials.AccessKeys[AccessKeyType.Primary])
.DefineVolume(_config["AppSettings:ImageApi:VolumeName"])
.WithExistingReadWriteAzureFileShare(_config["AppSettings:ImageApi:FileShare"])
.WithStorageAccountName(_config["AppSettings:ImageApi:StorageAccountName"])
.WithStorageAccountKey(_config["AppSettings:ImageApi:StorageAccountKey"])
.Attach()
.DefineContainerInstance(_config["AppSettings:ImageApi:ProxyContainerName"])
.WithImage(_config["AppSettings:ImageApi:ProxyImageName"])
.WithExternalTcpPort(443)
.WithExternalTcpPort(80)
.WithCpuCoreCount(1.0)
.WithMemorySizeInGB(0.5)
.WithVolumeMountSetting(
_config["AppSettings:ImageApi:VolumeName"],
_config["AppSettings:ImageApi:VolumeMountPath"])
.Attach()
.DefineContainerInstance(_config["AppSettings:ImageApi:ApiContainerName"])
.WithImage(_config["AppSettings:ImageApi:ApiImageName"])
.WithExternalTcpPort(5000)
.WithCpuCoreCount(1.0)
.WithMemorySizeInGB(3.5)
.Attach()
.WithDnsPrefix(_config["AppSettings:ImageApi:ContainerGroupName"])
.WithRestartPolicy(ContainerGroupRestartPolicy.Always)
.Create();
}
Log.Information("[Container] created {fqdn}", containerGroup.Fqdn);
return containerGroup.Fqdn;
}
Containers are running!
It takes around 2:50 min to have them fully available.
Hitting fqdn
given in the code above gives me my swagger index page:
Top comments (1)
Nice blog and informative content,
We are providing Best Dot NET Training in Hyderabad,
Thanks for sharing with us,
DOT NET Training in Hyderabad
DOT NET Online Training in Hyderabad