<p>I'm trying to expose kubernetes dashboard publicly via an ingress on a single master bare-metal cluster. The issue is that the LoadBalancer (nginx ingress controller) service I'm using is not opening the 80/443 ports which I would expect it to open/use. Instead it takes some random ports from the 30-32k range. I know I can set this range with <code>--service-node-port-range</code> but I'm quite certain I didn't have to do this a year ago on another server. Am I missing something here?</p>

<p>Currently this is my stack/setup (clean install of Ubuntu 16.04):</p>

<ul>
<li>Nginx Ingress Controller (installed via helm)</li>
<li>MetalLB</li>
<li>Kubernetes Dashboard </li>
<li>Kubernetes Dashboard Ingress to deploy it publicly on <code>&lt;domain&gt;</code></li>
<li>Cert-Manager (installed via helm)</li>
</ul>

<p>k8s-dashboard-ingress.yaml</p>

<pre><code>apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    # add an annotation indicating the issuer to use.
    cert-manager.io/cluster-issuer: letsencrypt-staging
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/secure-backends: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
  name: kubernetes-dashboard-ingress
  namespace: kubernetes-dashboard
spec:
  rules:
  - host: &lt;domain&gt;
    http:
      paths:
      - backend:
          serviceName: kubernetes-dashboard
          servicePort: 443
        path: /
  tls:
  - hosts:
    - &lt;domain&gt;
    secretName: kubernetes-dashboard-staging-cert
</code></pre>

<p>This is what my <code>kubectl get svc -A</code> looks like:</p>

<pre><code>NAMESPACE              NAME                            TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)                      AGE
cert-manager           cert-manager                    ClusterIP      10.101.142.87    &lt;none&gt;          9402/TCP                     23h
cert-manager           cert-manager-webhook            ClusterIP      10.104.104.232   &lt;none&gt;          443/TCP                      23h
default                kubernetes                      ClusterIP      10.96.0.1        &lt;none&gt;          443/TCP                      6d6h
ingress-nginx          nginx-ingress-controller        LoadBalancer   10.100.64.210    10.65.106.240   80:31122/TCP,443:32697/TCP   16m
ingress-nginx          nginx-ingress-default-backend   ClusterIP      10.111.73.136    &lt;none&gt;          80/TCP                       16m
kube-system            kube-dns                        ClusterIP      10.96.0.10       &lt;none&gt;          53/UDP,53/TCP,9153/TCP       6d6h
kubernetes-dashboard   cm-acme-http-solver-kw8zn       NodePort       10.107.15.18     &lt;none&gt;          8089:30074/TCP               140m
kubernetes-dashboard   dashboard-metrics-scraper       ClusterIP      10.96.228.215    &lt;none&gt;          8000/TCP                     5d18h
kubernetes-dashboard   kubernetes-dashboard            ClusterIP      10.99.250.49     &lt;none&gt;          443/TCP                      4d6h
</code></pre>

<hr>

<p>Here are some more <strong>examples</strong> of what's happening:</p>

<ol>
<li><p><code>curl -D- http://&lt;public_ip&gt;:31122 -H 'Host: &lt;domain&gt;'</code></p>

<ul>
<li>returns 308, as the protocol is http not https. This is expected</li>
</ul></li>
<li><p><code>curl -D- http://&lt;public_ip&gt; -H 'Host: &lt;domain&gt;'</code></p>

<ul>
<li><code>curl: (7) Failed to connect to &lt;public_ip&gt; port 80: Connection refused</code></li>
<li>port 80 is closed</li>
</ul></li>
<li><p><code>curl -D- --insecure https://10.65.106.240 -H "Host: &lt;domain&gt;"</code> </p>

<ul>
<li>reaching the dashboard through an internal IP obviously works and I get the correct k8s-dashboard html. </li>
<li><code>--insecure</code> is due to the let's encrypt not working yet as the acme challenge on port 80 is unreachable.</li>
</ul></li>
</ol>

<p>So to recap, how do I get <code>2.</code> working? E.g. reaching the service through 80/443?</p>

<p>EDIT: Nginx Ingress Controller .yaml</p>

<pre><code>apiVersion: v1
kind: Service
metadata:
  creationTimestamp: "2020-02-12T20:20:45Z"
  labels:
    app: nginx-ingress
    chart: nginx-ingress-1.30.1
    component: controller
    heritage: Helm
    release: nginx-ingress
  name: nginx-ingress-controller
  namespace: ingress-nginx
  resourceVersion: "1785264"
  selfLink: /api/v1/namespaces/ingress-nginx/services/nginx-ingress-controller
  uid: b3ce0ff2-ad3e-46f7-bb02-4dc45c1e3a62
spec:
  clusterIP: 10.100.64.210
  externalTrafficPolicy: Cluster
  ports:
  - name: http
    nodePort: 31122
    port: 80
    protocol: TCP
    targetPort: http
  - name: https
    nodePort: 32697
    port: 443
    protocol: TCP
    targetPort: https
  selector:
    app: nginx-ingress
    component: controller
    release: nginx-ingress
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 10.65.106.240
</code></pre>

<hr>

<p>EDIT 2: metallb configmap yaml</p>

<pre><code>kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      -  10.65.106.240-10.65.106.250
</code></pre>

David
<p>So, to solve the 2nd question, as I suggested, you can use <code>hostNetwork: true</code> parameter to map container port to the host it is running on. Note that this is not a recommended practice, and you should always avoid to do this, unless you have a reason.</p>

<p><strong>Example:</strong></p>

<pre><code>apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  hostNetwork: true
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
      hostPort: 80           # this parameter is optional, but recommended when using host network
      name: nginx
</code></pre>

<p>When I deploy this yaml, I can check where the pod is running and curl that host's port 80.</p>

<pre><code>root@v1-16-master:~# kubectl get po -owide
NAME                     READY   STATUS    RESTARTS   AGE     IP                NODE             NOMINATED NODE   READINESS GATES
nginx                    1/1     Running   0          105s    10.132.0.50       v1-16-worker-2   &lt;none&gt;           &lt;none&gt;
</code></pre>

<p>Note: now I know the pod is running on worker node 2. I just need its IP address.</p>

<pre><code>root@v1-16-master:~# kubectl get no -owide
NAME             STATUS   ROLES    AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION    CONTAINER-RUNTIME
v1-16-master     Ready    master   52d   v1.16.4   10.132.0.48   xxxx        Ubuntu 16.04.6 LTS   4.15.0-1052-gcp   docker://19.3.5
v1-16-worker-1   Ready    &lt;none&gt;   52d   v1.16.4   10.132.0.49   xxxx        Ubuntu 16.04.6 LTS   4.15.0-1052-gcp   docker://19.3.5
v1-16-worker-2   Ready    &lt;none&gt;   52d   v1.16.4   10.132.0.50   xxxx        Ubuntu 16.04.6 LTS   4.15.0-1052-gcp   docker://19.3.5
v1-16-worker-3   Ready    &lt;none&gt;   20d   v1.16.4   10.132.0.51   xxxx        Ubuntu 16.04.6 LTS   4.15.0-1052-gcp   docker://19.3.5
root@v1-16-master:~# curl 10.132.0.50 2&gt;/dev/null | grep title
&lt;title&gt;Welcome to nginx!&lt;/title&gt;
root@v1-16-master:~# kubectl delete po nginx
pod "nginx" deleted
root@v1-16-master:~# curl 10.132.0.50
curl: (7) Failed to connect to 10.132.0.50 port 80: Connection refused
</code></pre>

<p>And of course it also works if I go to the public IP on my browser.</p>
