I'm really struggling to figure out how to setup an Azure Function that uses a Docker container image and is connected to a VNet. I cannot find any examples of this setup anywhere.
The main issue I'm running into is that after my container is up and running, it does not seem to be responding to the HTTP pings the underlying framework uses to determine if the function is up and running. I believe the primary issue is that when you setup a Linux service that uses Docker and connect it to a VNet, the ports used are not standard (from what I understand). I have updated the ENTRYPOINT
line in my dockerfile to handle this accordingly, however the port that is used to test for pings is not the port that is exposed via the docker run command. Here are the log entries that pertain to this startup error:
INFO - Starting container for site
INFO - docker run -d -p 8635:8635 --name evo-item-exporter-stage_0_42c1415b_middleware -e WEBSITE_CORS_ALLOWED_ORIGINS=https://functions.azure.com,https://functions-staging.azure.com,https://functions-next.azure.com -e WEBSITE_CORS_SUPPORT_CREDENTIALS=False -e WEBSITES_ENABLE_APP_SERVICE_STORAGE=false -e WEBSITE_SITE_NAME=evo-item-exporter-stage -e WEBSITE_AUTH_ENABLED=False -e PORT=8635 -e WEBSITE_ROLE_INSTANCE_ID=0 -e WEBSITE_HOSTNAME=evo-item-exporter-stage.azurewebsites.net -e WEBSITE_INSTANCE_ID=47d698ac06f21187d3dc07a6ddd707f955f4ca9b939be455493969c8c2fb4bb8 appsvc/middleware:1907112318 /Host.ListenUrl=http://0.0.0.0:8635 /Host.DestinationHostUrl=http://10.5.6.4:3236 /Host.UseFileLogging=true
INFO - Logging is not enabled for this container.
Please use https://aka.ms/linux-diagnostics to enable logging to see container logs here.
INFO - Initiating warmup request to container evo-item-exporter-stage_0_42c1415b_msiProxy for site evo-item-exporter-stage
INFO - Container evo-item-exporter-stage_0_42c1415b_msiProxy for site evo-item-exporter-stage initialized successfully and is ready to serve requests.
INFO - Initiating warmup request to container evo-item-exporter-stage_0_42c1415b for site evo-item-exporter-stage
ERROR - Container evo-item-exporter-stage_0_42c1415b for site evo-item-exporter-stage has exited, failing site start
INFO - Initiating warmup request to container evo-item-exporter-stage_0_42c1415b_middleware for site evo-item-exporter-stage
INFO - Container evo-item-exporter-stage_0_42c1415b_middleware for site evo-item-exporter-stage initialized successfully and is ready to serve requests.
ERROR - Container evo-item-exporter-stage_0_42c1415b didn't respond to HTTP pings on port: 3236, failing site start. See container logs for debugging.
INFO - Stoping site evo-item-exporter-stage because it failed during startup.
As you can see in this example, port 8635 is being mapped (to port 8635) and is being specified as an environment variable, which is coming from the underlying vnet setup. However, the HTTP pings are being sent to port 3236. I see that this is part of the /Host.DestinationHostUrl parameter towards the end of the docker run command, but I don't see how I can get access to this parameter since it's not being passed as an environment variable like the PORT is.
Here is my Dockerfile:
FROM mcr.microsoft.com/azure-functions/dotnet:2.0 AS base
WORKDIR /app
EXPOSE 80
ENV PORT=80
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ["nuget.config", ""]
COPY ["ItemExporter/app.ItemExporter/app.ItemExporter.csproj", "ItemExporter/app.ItemExporter/"]
COPY ["ItemExporter/evo.Domain/evo.Domain.csproj", "ItemExporter/evo.Domain/"]
COPY ["ItemExporter/evo.DependencyInjection/evo.DependencyInjection.csproj", "ItemExporter/evo.DependencyInjection/"]
COPY ["ItemExporter/evo.Infrastructure/evo.Infrastructure.csproj", "ItemExporter/evo.Infrastructure/"]
RUN dotnet restore "ItemExporter/app.ItemExporter/app.ItemExporter.csproj"
COPY . .
WORKDIR "/src/ItemExporter/app.ItemExporter"
RUN dotnet build "app.ItemExporter.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "app.ItemExporter.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENV AzureWebJobsScriptRoot=/app
#See the Azure function docker file to get the correct entrypoint syntax
#in case this changes in the future
#https://github.com/Azure/azure-functions-host/blob/dev/Dockerfile
#The --urls=... part is needed to run inside an Azure App Service w/ vnet integration
ENTRYPOINT dotnet /azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost.dll --urls="http://0.0.0.0:$PORT"
FYI The default Dockerfile created by the Azure Function tool chain does not work. It errors out with an error like this:
(Failed to bind to address http://[::]:5169: address already in use.) ---> System.IO.IOException: Failed to bind to address http://[::]:5169: address already in use. ---> Microsoft.AspNetCore.Connections.AddressInUseException: Address already in use ---> System.Net.Sockets.SocketException: Address already in use
To get around this error, I had to do what I said above by using the PORT environment variable and --urls parameter.
I assume what I need to do is add this other port to the --urls=...
parameter I'm passing to the ENTRYPOINT, but cannot figure out how to do this.
Does anyone have any idea how to setup an Azure function that uses both Docker and VNet?