user1299379
user1299379

Reputation: 380

Azure function HTTP Request 404 when published to Azure through Docker and Visual Studio

I'm attempting to learn some more about Azure Functions 2.0 and Docker containers to publish to my Azure instance. I followed to tutorial below with the only difference being that I published with docker to a container registry in azure using visual studio 2019.

https://learn.microsoft.com/en-us/azure/azure-functions/functions-create-your-first-function-visual-studio

This all worked correctly and I was able to start my container and visit the site. However, in the example you can visit /api/function1 and get a response. This works on my localhost but on the live site it returns a 404. It seems that /api/function1 is not reachable after being published.

The app itself returns this when visiting the IP itself so I know it is working. Do I need to do something else in Azure to expose my APIs?

Azure screenshot working default page

My container log only shows this.

Hosting environment: Production
Content root path: C:\
Now listening on: http://[::]:80
Application started. Press Ctrl+C to shut down.

I grabbed my dockerfile from here

https://github.com/Azure/azure-functions-docker/blob/master/host/2.0/nanoserver-1809/Dockerfile

# escape=`

# Installer image
FROM mcr.microsoft.com/windows/servercore:1809 AS installer-env

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

# Retrieve .NET Core SDK
ENV DOTNET_SDK_VERSION 2.2.402

RUN Invoke-WebRequest -OutFile dotnet.zip https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$Env:DOTNET_SDK_VERSION/dotnet-sdk-$Env:DOTNET_SDK_VERSION-win-x64.zip; `
    $dotnet_sha512 = '0fa3bf476b560c8fc70749df37a41580f5b97334b7a1f19d66e32096d055043f4d7ad2828f994306e0a24c62a3030358bcc4579d2d8d439d90f36fecfb2666f6'; `
    if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512) { `
        Write-Host 'CHECKSUM VERIFICATION FAILED!'; `
        exit 1; `
    }; `
    `
    Expand-Archive dotnet.zip -DestinationPath dotnet; `
    Remove-Item -Force dotnet.zip

ENV ASPNETCORE_URLS=http://+:80 `
    DOTNET_RUNNING_IN_CONTAINER=true `
    DOTNET_USE_POLLING_FILE_WATCHER=true `
    NUGET_XMLDOC_MODE=skip `
    PublishWithAspNetCoreTargetManifest=false `
    HOST_COMMIT=69f124faed40d20d9d8e5b8d51f305d249b21512 `
    BUILD_NUMBER=12858

RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; `
    Invoke-WebRequest -OutFile host.zip https://github.com/Azure/azure-functions-host/archive/$Env:HOST_COMMIT.zip; `
    Expand-Archive host.zip .; `
    cd azure-functions-host-$Env:HOST_COMMIT; `
    /dotnet/dotnet publish /p:BuildNumber=$Env:BUILD_NUMBER /p:CommitHash=$Env:HOST_COMMIT src\WebJobs.Script.WebHost\WebJobs.Script.WebHost.csproj --output C:\runtime


# Runtime image
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2.7-nanoserver-1809

COPY --from=installer-env ["C:\\runtime", "C:\\runtime"]

ENV AzureWebJobsScriptRoot=C:\approot `
    WEBSITE_HOSTNAME=localhost:80

CMD ["dotnet", "C:\\runtime\\Microsoft.Azure.WebJobs.Script.WebHost.dll"]

Here's my function1 code for my azure function

public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            string productid = req.Query["productid"];

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            productid = productid ?? data?.product;

            Product newProduct = new Product()
            {
                ProductNumber = 0,
                ProductName = "Unknown",
                ProductCost = 0
            };
            if (Convert.ToInt32(productid) ==1)
            {
                newProduct = new Product()
                {
                    ProductCost = 100,
                    ProductName = "Lime Tree",
                    ProductNumber = 1
                };
            }
            else if(Convert.ToInt32(productid) == 2) 
            {
                newProduct = new Product()
                {
                    ProductCost = 500,
                    ProductName = "Lemon Tree",
                    ProductNumber = 2
                };
            }
            return productid != null
                ? (ActionResult)new JsonResult(newProduct)
                : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
        }

Here's a photo of my container running with my image.

Container image

I'm new to this so any advice would be helpful for sure!

Thanks!

Upvotes: 1

Views: 3684

Answers (2)

user1299379
user1299379

Reputation: 380

@silent's answer was correct - Linux containers are the way to go for Azure Functions. My environment wasn't set up correctly for Linux containers but once I got a correct environment this worked out of the box.

Here's my latest DockerFile for another project that uses Linux Containers

See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/azure-functions/dotnet:2.0 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS build
WORKDIR /src
COPY ["FunctionTestAppLinux/FunctionTestAppLinux.csproj", "FunctionTestAppLinux/"]
RUN dotnet restore "FunctionTestAppLinux/FunctionTestAppLinux.csproj"
COPY . .
WORKDIR "/src/FunctionTestAppLinux"
RUN dotnet build "FunctionTestAppLinux.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "FunctionTestAppLinux.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENV AzureWebJobsScriptRoot=/app

Upvotes: 0

silent
silent

Reputation: 16108

First, I don't know if you really need to (or want to) run Functions on Windows containers. If you want to run in a container, I would probably opt for Linux. For that, this is an example Dockerfile. It does build on top of the Microsoft-provided base image. So you don't have to build that from scratch.

I'm sure there is also a base image for Windows that is already build. If you need it, just look around in the similiar repo I guess.

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env
WORKDIR /app

COPY . ./
RUN dotnet publish myfunction -c Release -o myfunction /out

FROM mcr.microsoft.com/azure-functions/dotnet:3.0 AS base
WORKDIR /app
EXPOSE 80

COPY --from=build-env /app/ao-backendfunctions/out .

ENV AzureWebJobsScriptRoot=/app
ENV AzureFunctionsJobHost__Logging__Console__IsEnabled=true

The important part is RUN dotnet publish myfunction -c Release -o myfunction /out. Replace myfunction with the (folder) name of your actual Function.

Upvotes: 1

Related Questions