0

I need some help getting this to work. I've got it to the point where my container is built, but Cloud Run says the service isn't listening on the specified port.

Updated: 6/28/2023

  • Changed project to not use top-level statements, in order to have a Main method for entrypoint.
  • Added cloudbuild.yaml to repo root (posted below)
  • Updated Dockerfile to correct multiple issues (updated below)
    • Biggest problems were the copy commands and then specifying port to run on
  • Latest status:
    • Builds successfully in Cloud Build, with default (for ASP.NET 6.0 WebAPI) output type of "Console Application"
    • Cloud Run still failing to start, but the logs reveal better details (posted below)

Details:

Source code for my sample app, a simple ASP.NET 6.0 WebAPI with default WeatherForecast controller, can be found here: https://github.com/andre-engelbrecht/sample_net_api

cloudbuild.yaml:

steps:
  - name: 'gcr.io/cloud-builders/docker'
    args:
      - 'build'
      - '-t'
      - 'gcr.io/$PROJECT_ID/sample-api'
      - '-f'
      - 'Sample_API/Sample_API.API/Dockerfile'  # Update the path to your Dockerfile
      - '.'  # Set the build context to the root directory
images:
  - 'gcr.io/$PROJECT_ID/sample-api'

Dockerfile: (which builds successfully on CloudBuild)

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["Sample_API/Sample_API.API/Sample_API.API.csproj", "Sample_API.API/"]
COPY ["Sample_API/Sample_API.API/", "Sample_API.API/"]
RUN dotnet restore "Sample_API.API/Sample_API.API.csproj"
WORKDIR "/src/Sample_API.API"
RUN ls -a
RUN dotnet build "Sample_API.API.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "Sample_API.API.csproj" -c Release -o /app/publish /p:UseAppHost=true

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS final
#Set the environment variable to listen on the specified port
ENV ASPNETCORE_URLS=http://*:$PORT  
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Sample_API.API.dll"]

Cloud Run complains of the following:

The user-provided container failed to start and listen on the port defined provided by the PORT=8080 environment variable

cloud run failed

Cloud Run Logs:

- A fatal error was encountered. The library 'libhostpolicy.so' required to execute the application was not found in '/app/'.
- Failed to run as a self-contained app.
- The application was run as a self-contained app because '/app/Sample_API.API.runtimeconfig.json' was not found.
- If this should be a framework-dependent app, add the '/app/Sample_API.API.runtimeconfig.json' file and specify the appropriate framework.

Question:

What can I do to get this to work? I'm new to Docker and GCP, so I really don't know what I'm doing wrong? And though there are many guides out there, I could not find one for my particular use case.

According to this post, the problem is that my service is not listening on the defined port (8080), but how/where do I specify what port it should listen on when in Cloud Run? Locally I've set it up so that it starts on port 8080, but that didn't fix GCP.

I followed the following guides to get this far:

Any help will be appreciated!

  • (1) Update the `EXPOSE` instruction in your Dockerfile to expose port 8080 instead of port 80. (2) Modify the `CreateHostBuilder` method in your `Program.cs` file to make the application listen on port 8080. (3) Ensure your Cloud Build configuration file includes the `--port=8080` flag in the `gcloud run deploy` command – Chanpols Jun 27 '23 at 21:31
  • With the help of ChatGPT I now feel much more confident about my Cloud Build setup and process. I believe the Cloud Run logs provide the best clues as to what's wrong here, but I don't know what to make of it yet. I've tried various options so far, but with no success. – André Engelbrecht Jun 28 '23 at 17:06

1 Answers1

1

I finally got it all working!


Core things I needed to add / change:

1. Create (or alter) project without top-level statements

Initially I had (unknowingly) created my project with top-level statements, this meant that there was no "Main" method in Program.cs to use as an entry point.

2. Include cloudbuild.yaml to define build and deploy steps

There's likely more then one way to do this, but I ended up adding a couldbuild.yaml file to the root of my repo to define build and deploy steps for Cloud Build.

This is what my cloudbuild.yaml file looks like:
steps:
  - name: 'gcr.io/cloud-builders/docker'
    args:
      - 'build'
      - '-t'
      - 'gcr.io/$PROJECT_ID/sample-api'
      - '-f'
      - 'Sample_API/Sample_API.API/Dockerfile'  # Update the path to your Dockerfile
      - '.'  # Set the build context to the root directory

  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'gcr.io/$PROJECT_ID/sample-api']

  - name: 'gcr.io/cloud-builders/gcloud'
    args:
      - 'run'
      - 'deploy'
      - 'sample-net-api'  # Update with your service name
      - '--image=gcr.io/$PROJECT_ID/sample-api'
      - '--region=us-central1'
      - '--platform=managed'
      # Additional deployment options go here

images:
  - 'gcr.io/$PROJECT_ID/sample-api'

3. Fix my Dockerfile

My Dockerfile has some errors, most notably the commands to copy the project- and other files to the /src directory during build.

I also added a 2nd project to my solution, a class-library to define core code and keep my controller lean. Which mandated further updates to make the COPY more generic.

This is what my Dockerfile looks like
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
# Copy all files to /src directory
COPY ["Sample_API/", "Sample_API/"] 
# "Navigate" to main project directory
WORKDIR "/src/Sample_API/Sample_API.API"
# Restore and build
RUN dotnet restore "Sample_API.API.csproj"
RUN dotnet build "Sample_API.API.csproj" -c Release -o /app/build

FROM build AS publish
# Publish
RUN dotnet publish "Sample_API.API.csproj" -c Release -o /app/publish /p:UseAppHost=true

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS final
# Set the environment variable to listen on the specified port
# Only used in load Docker test deployments - not used in Cloud Run
# ENV ASPNETCORE_URLS=http://*:$PORT  
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Sample_API.API.dll"]

4. Start service on port defined by Cloud Run

Lastly I had to ensure my service started on the port that was specified in Cloud Run setup, to avoid the infamous The user-provided container failed to start and listen on the port defined provided by the PORT=$PORT environment variable error.

I did this by adding the following to Program.cs:

if (Environment.GetEnvironmentVariable("PORT") != null)
{
    builder.WebHost.UseUrls($"http://0.0.0.0:{Environment.GetEnvironmentVariable("PORT")}");
}

A big shoutout to ChatGPT, which when you ask the right questions, can indeed be very useful.