Search code examples
dockerkubernetesportdockerfileminikube

Kubernetes with Docker unable to change from default port of 80


I am running a dotnet core app using Kubernetes with Docker.

The setup is as follows:

APP

In the dotnet core app, I have Kestrel server listening on port 8080 by setting the following in Program.cs:

public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
    .UseStartup<Startup>()
    .UseKestrel(options =>
    {
        options.Listen(IPAddress.Loopback, 8080);
    })
    .Build();

I have tested the app build locally and the endpoint works as expected on localhost:8080/api/test.

DOCKER IMAGE

In the Dockerfile I have the following:

EXPOSE 8080

I understand this to mean that the container gets built with an exposed 8080 port.

Question 1: ...does this necessarily mean that the container's 8080 is mapped to the app's 8080? If not, how do I map this?

KUBERNETES (MINIKUBE)

Within Kubernetes (running locally on Minikube), I then use a Replication Controller to create 3 pods that each contain 1 docker container with the app. My RC file looks like this:

{
    "apiVersion": "v1",
    "kind": "ReplicationController",
    "spec": {
        "replicas": 3,
        "selector": {
            "app": "myApp"
        },
        "template": {
            "metadata": {
                "labels": {
                    "app": "myApp"
                }
            },
            "spec": {
                "containers": [
                    {
                        "name": "my-app",
                        "image": "myname/myapp:1.0",
                        "ports": [
                            {
                                "containerPort": 8080
                            }
                        ]
                    }
                ]
            }
        }
    }
}

Notice "ports": [{ "containerPort": 8080 }]. The way I understand it, this means that the container port I want to expose is 8080.

I then have a Kubernetes Service that exposes my 3 pods' 8080 ports through the endpoint [minikubeIPAddress]:30001:

{
    "apiVersion": "v1",
    "kind": "Service",
    "spec": {
        "type": "NodePort",
        "ports": [
            {
                "port": 8080,
                "nodePort": 30001,
                "protocol": "TCP"
            }
        ],
        "selector": {
            "app": "myApp"
        }
    }
}

When I try to hit the endpoint [minikubeIPAddress]:30001/api/test I am getting a 'site can't be reached' error.

I had it working this morning when I was using the default HTTP port 80. The only changes that have been made are to the port numbers.

Question 2: ...have I missed something here? Is there a connection along the line here that is still mapped to the default port of 80?

Any help would be much appreciated.


Solution

  • After much trial and error I found the solution.

    In line with what @johnharris85 and @Yuankun said about the IP Address needing to be set to 'any' rather than on the localhost, I found this article: http://blog.scottlogic.com/2016/09/05/hosting-netcore-on-linux-with-docker.html

    The dotnet core app defaults to using the localhost network, and while running locally on a test machine, this works fine. However, running a dotnet app inside a Docker container means that the localhost network is restricted to within the container.

    To solve this, I initially changed

    options.Listen(IPAddress.Loopback, 8080);
    

    to

    options.Listen(IPAddress.Any, 8080);
    

    However, I tested this locally and could not get the app to respond. I took this to mean that this was not a valid solution. This may have been a valid solution if I had tested it with a containerized app.

    I then came across the aforementioned article and decided to try the following:

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseUrls("http://*:8080")
        .Build();
    

    This solved my problem and now my app is accessible through the Kubernetes endpoint.

    Thanks to everyone who gave advice.