I know that there are many discussion about this, but none of the proposed solutions worked for me, so I will have to know at least if I was doing something wrong or I was hitting a limitation.
Step 1.
I created the default .NET Core 2.0 WEB API project from Visual Studio, nothing special here, output type set to Console Application, running OK from Visual Stuido 2017 Community.
Step 2. I installed latest Docker Toolbox since I am running Windows 10 Home Edition, that installed also the Virtual Box.
Step 3. I added the following docker file next to the sln:
FROM microsoft/aspnetcore-build:2.0
WORKDIR /app
EXPOSE 80
COPY . .
RUN dotnet restore
RUN dotnet build
WORKDIR /app/DockerSample
ENTRYPOINT dotnet run
Next
Step 4. I successfully build the image with a command like 'docker build -t sample1 .'
Step 5. The container successfully started to run, it was started by the following command 'docker run -d -p 8080:80 sample1'
Step 6. Pull info about the container using command docker logs c6
The following info was shown:
Interesting here is the address where the service is listening, this seems to be the same with the address I was getting when running the service directly from Visual Studio.
Is this the service address from the virtual machine that is running inside Virtual box ? Why the port is not 8080 or 80 as I mentioned inside of the "run" command ?
The container looks ok, something like:
Step 7.
Now starts the fun trying to hit the service from Windows 10 machine, was impossible using calls like http://localhost:8080/values/api I also tried calls like http://192.168.99.100:8080/values/api where 192.168.99.100 is the address of the default docker machine.
I also tried with something like 'http://172.17.0.2:8080/values/api' where the IP address was got after a call like 'docker inspect a2', changing the port to 80 did not help :).
Trying to change the port number to 80 or 58954 , the one shown in red as listening, did not help. Also Windows Firewall or any other firewalls were stopped.
I tried to port forward from VirtualBox having something like
Trying to change the 80 and 8080 ports between them for host and guest also did not work.
Basically none of the suggested solutions I found did not gave me the chance to hit the service from my Windows machine.
Mainly I was following this tutorial https://www.stevejgordon.co.uk/docker-for-dotnet-developers-part-2 which explains quite well what should be done only that at some point is using the Docker Desktop for Windows so the Docker Toolbox was left behind.
Do you know what should I do so that I can hit the service from the docker container ?
In docker compose (visual studio add docker integration "docker-compose.yml") set this:
version: '3.4'
services:
webapi.someapi:
image: ${DOCKER_REGISTRY-}somenamesomeapi
build:
context: .
dockerfile: ../webapi/Dockerfile
environment:
- ASPNETCORE_URLS=https://+:443;http://+:80
- ASPNETCORE_HTTPS_PORT=443
ports:
- "80:80"
- "443:443"
in lunch settings specify your app to start on ports 80 and 443 https
Docker for visual studio code: https://marketplace.visualstudio.com/items?itemName=PeterJausovec.vscode-docker
Follow this steps to orchestrate your containers:
https://marketplace.visualstudio.com/items?itemName=PeterJausovec.vscode-docker
For your issue, it is caused by that you run the container under Development environment which did not use the port 80 for the application.
For FROM microsoft/aspnetcore-build:2.0, it seems you could not change the ASPNETCORE_ENVIRONMENT to Production.
For a solution, you could change your docker file like below which change the base image with microsoft/aspnetcore:2.0.
FROM microsoft/aspnetcore:2.0 AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/aspnetcore-build:2.0 AS build
WORKDIR /src
COPY ["TestAPI/TestAPI.csproj", "TestAPI/"]
RUN dotnet restore "TestAPI/TestAPI.csproj"
COPY . .
WORKDIR "/src/TestAPI"
RUN dotnet build "TestAPI.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "TestAPI.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "TestAPI.dll"]
Related
I am encountering an interesting difference in startup behaviour when running a simple net6.0 web api built with docker-compose in comparison to being built with docker. The application itself runs in a kubernetes cluster.
Environment
Minikube v1.26.1
Docker Desktop v4.12
Docker Compose v2.10.2
Building with docker-compose
docker-compose.yml
version: "3.8"
services:
web.api:
build:
context: ./../
dockerfile: ./web.API/Dockerfile
The context is set to the parent directory due to some files needed there.
Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build
WORKDIR /src
ENV ASPNETCORE_URLS=http://+:80
COPY Directory.Build.props ./Directory.Build.props
COPY .editorconfig ./.editorconfig
COPY ["webapi/web.API", "web.API/"]
RUN dotnet build "web.API/web.API.csproj" -c Release --self-contained true --runtime alpine-x64
RUN dotnet publish "webapi/web.API.csproj" -c Release -o /app/publish \
--no-restore \
--runtime alpine-x64 \
--self-contained true \
/p:PublishSingleFile=true
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0-alpine
WORKDIR /app
EXPOSE 80
EXPOSE 443
COPY --from=build /app/publish .
ENTRYPOINT ["./web.API"]
This results in the app starting up within the kubernetes cluster with the following logs:
Now listening on: http://[::]:80
Building with docker build
Using the same Dockerfile mentioned earlier with the same build context you can see in the docker-compose.yml, a deployment to k8s results in the following log:
Now listening on: http://localhost:5000
Running the image locally
Running the exact same image from the k8s cluster locally however results in
Now listening on: http://[::]:80
Already tried
As suggested in many posts, I tried setting the environment variable ASPNETCORE_URLS via Dockerfile or k8s deployment.yml- neither of which had an impact on the startup url.
I can't seem to figure out why there is a difference between those 2 ways of building an image.
Update
The only thing that seems to work is to add
builder.WebHost.ConfigureKestrel(option =>{
option.ListenAnyIP(80);
});
to the Program.cs.
Still not sure about the reason behind the behaviour though.
A few things:
I assume that the container already running and working on port 80 (docker run) is stopped before attempting to run docker-compose?
Environment variables can be used in docker-compose.yml file
Ports most likely need to be exposed correctly, which from the Dockerfile and docker-compose.yml seems like it is not?
Environment Variables
First off, before the environmental ENV ASPNETCORE_URLS=http://+:80 is going to be of any use, your docker-compose instance does not define which ports to use, your docker-compose (if trimmed) does not show any ports.
Perhaps because the ports aren't exposed, this means the environmental tries to connect to 80, which fails (already in use/not exposed) and then fails, and somehow connects on 5000.
Alternatively, more likely: it does not really not _see your ENV ASPNETCORE_URLS
You can try environment variables directly in your docker-compose file:
my-service:
image: ${IMAGE_NAME}
environment:
MY_SECRET_KEY: ${MY_SECRET_KEY}
Publishing ports
In docker-compose file you need this, to publish ports:
ports:
- "80"
- "443"
... or
ports:
- "80:80" // "host-port:container-port"
- "443:1234"
Additional information
The keyword EXPOSE\expose in Dockerfile/docker-compose.yml is just informative (comments in a sense), functionally it does not process anything. A port need to be exposed (published) to be used.
So, those EXPOSE 443 & 80 is not telling Docker to use it, perhaps you are running your container for example like this:
This exposes port 80 for it to be available.
docker run -p 127.0.0.1:80:80/tcp image command
In short, use ports keyword in docker-compose.yml.
EDIT:
I read your comment above:
But the app is not accessible in k8s when listening to localhost:5000 even with correct service and container configuration
This indicates what I am trying to say regarding the ports being published or not. Your port 5000 is also not exposed because nothing in your configuration shows that is the case.
I've created a default ASP.NET Core 5 Web API through Visual Studio 2019 and have it running inside a Docker container (no https to make this example simple). I am using Windows, but the image is running as a Linux Docker Container. When I run the application from Visual Studio, it is started with Docker Compose. When the application runs I'm able to access it from my browser on the Docker host machine, but I'm not able to access it from another device on the same network.
Below are the files I'm using. You can see that I am exposing port 61234 from docker-compose.yml. Assuming my Host IP address is 192.168.1.12, if I were to navigate to http://192.168.1.12:61234/weatherforecast from a web browser on the host machine, I get a REST response from the controller. If I access that same endpoint from a different laptop on the same Wi-Fi network, the endpoint is unreachable.
I have validated that the laptop can reach the REST endpoint if the application is not running within a Docker container. For this reason, I don't believe the Windows Firewall is getting in the way.
I've looked at everything I can think of. So my question is: Is there anything else I should look at? I assume I'm missing something simple, but just can't seem to find what that might be.
The only change I made to the C# code generated by Visual Studio is making a UseUrls() call when creating the web host builder: webBuilder.UseUrls("http://*:80").UseStartup<Startup>();
Here is my docker-compose.yml file. Note the exposed TCP port and the bridge network:
version: '3.4'
services:
webapplication1:
image: ${DOCKER_REGISTRY-}webapplication1
build:
context: .
dockerfile: WebApplication1/Dockerfile
environment:
- ASPNETCORE_ENVIRONMENT=Development
ports:
- "61234:80"
networks:
- practice-webapi-network
networks:
practice-webapi-network:
driver: bridge
And here is the auto-generated Dockerfile that Visual Studio created:
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
COPY . .
WORKDIR "/src/WebApplication1"
RUN dotnet build "WebApplication1.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "WebApplication1.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApplication1.dll"]
One thing I want to point out. I believe I would be able to work around this by changing the network driver to Host instead of Bridge, but I need to use a Bridge network for my application. This is just a proof of concept.
Edit 1: Added missing port to example endpoint
I'm struggling to move my web api to the docker containers. However, it doesnt want to run.
Running docker-compose build and then docker-compose up console shows me that app is start listening on: http://localhost:5000 and https://localhost:5001, but before those line i'm receiving: " Unable to bind to http://localhost:5000 on the IPv6 loopback interface: 'Cannot assign requested address'."
Dockerfile:
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["ShoppingCart.API/ShoppingCart.API.csproj", "ShoppingCart.API/"]
#COPY ["ShoppingCart.Domain.Entities/ShoppingCart.Domain.Entities.csproj", "ShoppingCart.Domain.Entities/"]
#COPY ["ShoppingCart.Domain.Interfaces/ShoppingCart.Domain.Interfaces.csproj", "ShoppingCart.Domain.Interfaces/"]
#COPY ["ShoppingCart.Infrastructure.Data/ShoppingCart.Infrastructure.Data.csproj", "ShoppingCart.Infrastructure.Data/"]
#COPY ["ShoppingCart.Infrastructure.Business/ShoppingCart.Infrastructure.Business.csproj", "ShoppingCart.Infrastructure.Businesss/"]
#COPY ["ShoppingCart.Services.Interfaces/ShoppingCart.Services.Interfaces.csproj", "ShoppingCart.Services.Interfaces/"]
RUN dotnet restore "ShoppingCart.API/ShoppingCart.API.csproj"
COPY . .
WORKDIR "/src/ShoppingCart.API"
RUN dotnet build "ShoppingCart.API.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "ShoppingCart.API.csproj" -c Release -o /app/publish
WORKDIR /app
COPY --from=build /app/build .
ENTRYPOINT ["dotnet", "ShoppingCart.API.dll"]
Docker-compose:
version: '3.9'
networks:
localdev:
name: localdev
services:
main-api:
build: .
restart: always
ports:
- "5000:80"
- "5001:443"
networks:
- localdev
So, on http://localhost:5000 and https://localhost:5001 i dont see anything...
I even tried to add some arguments to Dockerfile:
ENTRYPOINT ["dotnet", "ShoppingCart.API.dll", "--urls", "https://0.0.0.0:5000"]
And after this, i dont receive the error of not assignning requested address. And also, cant reach any content on those ports...
Problem
You are binding port 5000 on your localhost to 80 on your docker container (and same with 5001:443). Is that really what you want to do? Since this is an aspnet-core project you are most likely running your webapp on port 5000 or 5001 (in the container), not 80 or 443. It also seems that you may already be running something on port 5000 on your localhost so docker is failing to bind it (I assume you're running your app probably).
Solution
You most likely need to:
Bind to ports 5000/5001 in your container, not 80/443.
Shut down what's running on port 5000 in your localhost environment, or pick another port to bind that isn't in use.
For example, lets pick a random higher port that isn't being used:
version: '3.9'
services:
main-api:
build: .
ports:
- "6000:5000"
- "6001:5001"
Bring this up with docker-compose up and then see what you get from http://localhost:6000 or https://localhost:6001.
Notes
It looks like you're attempting to create a multi-stage docker build where your app is built inside the sdk image and then run inside the aspnet image. But you are not introducing the aspnet image, so your container's runtime environment is still sdk which includes a lot of stuff we don't need in the runtime. You should add this line after your publish to change to the aspnet image. Example.
FROM mcr.microsoft.com/dotnet/aspnet:3.1
I created a simple asp.net core app in Visual Studio 2019 and added docker support.
Dockerfile, .dockerignore, and docker-compose file are all created.
In a command prompt I navigate to the folder docker-compose.yml file is present and then run the command
docker-compose up
I see that the containers are created and port mappings happen so that I can browse the web app in the browser.
So when I run the following inspect command on the container
docker inspect --format="{{ .NetworkSettings.Ports}}" ContainerId
I get something like this
map[80/tcp:[{0.0.0.0 32782}]]
So now I can browse the app with http://localhost:32782/index.html
Next if I tear down the containers with
docker-compose down
the containers are stopped and deleted. Created image remains.
Now when I do a docker run against that image to start a container
docker run -it --rm ae39
a new container is created but I am not able to browse the app because there is no port mapping from container to host. I have to explicitly specify this when I use the run command. Only then I am able to browse the app running inside of the container form the host.
But when I use docker compose I dont have to specify the port mapping. Something magical happens and the ports mappings are created for me. Note that the docker-compose.yml file is plane vanilla and does not contain any port mappings. So also the Dockerfile. They are included below for reference.
My question is does docker compose automatically create port mappings? If so how? Of is that is to do something with Visual Studio 2019
version: '3.4'
services:
generator31:
image: ${DOCKER_REGISTRY-}generator31
build:
context: .
dockerfile: generator31/Dockerfile
And the dockerfile is here.
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["generator31/generator31.csproj", "generator31/"]
RUN dotnet restore "generator31/generator31.csproj"
COPY . .
WORKDIR "/src/generator31"
RUN dotnet build "generator31.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "generator31.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "generator31.dll"]
I think I found the answer.
The key is the config command
docker-compose config
After I run that command, I see the following.
services:
generator31:
build:
context: D:\Trials\Docker\aspnetcore311\generator31
dockerfile: generator31/Dockerfile
environment:
ASPNETCORE_ENVIRONMENT: Development
image: generator31
ports:
- target: 80
version: '3.4'
I see ports, and also environment. In the docker compose file I pasted in the question, they are not present. So where did they come from?
I open the folder in file explorer, and I see docker-compose.override.yml file sitting quietly next to docker-compose.yml file. Open that file and I get the answer.
ports is defined there. Docker compose command picks up the configuration from multiple files, docker-compose.override.yml is one such file. And now this so ans should help as Fabian-Desnoes suggsted.
By having the EXPOSE tag in your dockerfile, it tells the platform that you are using that you need that port to be mapped. When using docker-compose, it will see this and automatically map this to a random open port. The EXPOSE keyword doesn't however expose any ports. It is just used to say to the platform you are using "Could you expose this port for me" and relies on the platform to do it for you.
If you want the ports to be mapped to a specific port every time on your host machine then you can add 'ports' to the service in you docker-compose.yml (host_machine_port:container_port)
I am working on setting up two Docker containers using Docker for Windows. A simple node based web app, and a dotnet core API application. I am starting both these containers using "docker-compose up". The node app starts up perfectly and I can hit the exposed URL, however the dotnet app isn't seeming to work.
The output of the docker-compose up command is below:
application.client_1 | INFO: Accepting connections at http://localhost:8080
application.api_1 | warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
application.api_1 | No XML encryptor configured. Key {cc83a8ac-e1de-4eb3-95ab-8c69a5961bf9} may be persisted to storage in unencrypted form.
application.api_1 | Hosting environment: Development
application.api_1 | Content root path: /app/application.Api
application.api_1 | Now listening on: http://[::]:80
application.api_1 | Application started. Press Ctrl+C to shut down.
The Docker file looks like the following:
FROM microsoft/dotnet AS build
WORKDIR /app
ENV PORT=8081
COPY application.Api/application.Api.csproj application.Api/
COPY application.Business/application.Business.csproj application.Business/
COPY application.DataAccess/application.DataAccess.csproj application.DataAccess/
COPY application.DataModel/application.DataModel.csproj application.DataModel/
WORKDIR /app/application.Api
RUN dotnet restore
WORKDIR /app/
COPY application.Api/. ./application.Api/
COPY application.Business/. ./application.Business/
COPY application.DataAccess/. ./application.DataAccess/
COPY application.DataModel/. ./application.DataModel/
WORKDIR /app/application.Api
RUN dotnet publish -c Release -o out
FROM microsoft/dotnet AS runtime
WORKDIR /app/application.Api
COPY --from=build /app/application.Api/out .
ENTRYPOINT ["dotnet", "application.Api.dll" ]
EXPOSE $PORT
I am unable to get an IP and thus hit the API url. Any thoughts would be much appreciated as I am pretty new to Docker.
UPDATE 1: Compose YML
version: '3.4'
services:
tonquin.api:
image: application.api
ports:
- 8081:5000
build:
context: .
dockerfile: Dockerfile
tonquin.client:
image: application.client
ports:
- 8080:8080
build:
context: .
dockerfile: ../application.Client/Dockerfile
As they've mentioned it seems your container is running on port 80. So for whatever reason that's the port being exposed.
Maybe the EXPOSE $PORT is not returning 8081 as you expect?
When you run the container, unless you specify where to map it, it will only be available at the container's IP at the exposed port (80 in your case). Find out this container Ip easily by running docker inspect <container_id>
Test your image by doing something like docker run -p 8080:80 yourimage. You'll see that in addition to the port 80 that the image exposes, it is being mapped to your local port 8080 so that http://localhost:8080 should be reachable.
See this in case it helps you
See this answer.
The base dotnet image overrides the default kestrel port. Why, I don't know. Adding the environment declaration to my docker file fixed the problem for me.
It's trying to use the IPv6 protocol on the network interface. Disable IPv6 and restart docker. It also looks like you might have both apps trying to use port 80. You can only serve one item on a given port with a given interface/IP. Try setting the API to use a different port number, like 8080.