Problem with running an ingress: Empty reply from server

Hello,

I’m having trouble getting an ingress gateway running.

Consul and Nomad are both deployed using Ansible scripts:

setup-consul.yml:

---
# File: setup-consul.yml - Example Consul site playbook

- name: Assemble Consul cluster
  hosts: 
    - consul_instances
    - nomad_instances
  any_errors_fatal: true
  become: true
  become_user: root
  roles:
    - role: juju4.ipv6
    - role: brianshumate.consul
      consul_version: 1.9.5
      consul_client_address: 0.0.0.0
      consul_ports_grpc: 8502
    - role: firewalld_dyn

  vars:
    ansible_ssh_extra_args: "-o StrictHostKeyChecking=no"

  tasks:
    - name: Enable consul ports
      firewalld_dyn:
        immediate: yes
        permanent: yes
        state: enabled
        port:
        - "8300/tcp"
        - "8301/tcp"
        - "8301/udp"
        - "8302/tcp"
        - "8302/udp"
        - "8500/tcp"
        - "8502/tcp"
        - "8502/udp"
        - "8600/tcp"
        - "8600/udp"

setup-nomad.yml:

---
# File: setup-nomad.yml - Example nomad site playbook

- name: Setup Nomad
  hosts: nomad_instances
  become: yes
  become_user: root
  remote_user: "{{ lookup('env', 'SSH_USER') }}"
  roles:
    - role: brianshumate.nomad
      nomad_version: 1.1.0
      nomad_network_interface: ens192
      nomad_plugins:
        nomad-driver-podman:
    - role: firewalld_dyn
    - role: geerlingguy.docker

  vars:
    ansible_ssh_extra_args: "-o StrictHostKeyChecking=no"

  tasks:
    - name: Enable nomad ports
      firewalld_dyn:
        immediate: yes
        permanent: yes
        state: enabled
        port:
        - "4646/tcp"
        - "4647/tcp"
        - "4648/udp" # not needed by clients
        - "4648/tcp" # not needed by clients

    - name: Copy docker.json
      ansible.builtin.copy:
        src: config_files/docker.json
        dest: /etc/docker/daemon.json

    - name: Restart docker deamon
      ansible.builtin.service:
        name: docker
        state: restarted

    # Install CNI networking plugin (required for ingress)
    - name: Make sure plugin directory exists
      ansible.builtin.file:
        path: /opt/cni/bin
        state: directory
        mode: '0755'

    - name: Download & unpack plugin
      ansible.builtin.unarchive:
        src: https://github.com/containernetworking/plugins/releases/download/v0.9.1/cni-plugins-linux-amd64-v0.9.1.tgz
        dest: /opt/cni/bin
        remote_src: yes

hosts.ini:

[consul_instances]
deni-consul1-consul consul_iface=ens192 consul_node_role=bootstrap consul_connect_enabled=true ansible_ssh_host=10.2.232.120

[nomad_instances]
deni-nomad1-server consul_iface=ens192 consul_node_role=client consul_connect_enabled=true nomad_node_role=server ansible_ssh_host=10.2.232.121
deni-nomad1-client1 consul_iface=ens192 consul_node_role=client consul_connect_enabled=true nomad_node_role=client ansible_ssh_host=10.2.232.122
deni-nomad1-client2 consul_iface=ens192 consul_node_role=client consul_connect_enabled=true nomad_node_role=client ansible_ssh_host=10.2.232.123

ingress-test.nomad:

job "ingress-test" {

  datacenters = ["dc1"]

  # This group will have a task providing the ingress gateway automatically
  # created by Nomad. The ingress gateway is based on the Envoy proxy being
  # managed by the docker driver.
  group "ingress-group" {

    network {
      mode = "bridge"

      # This example will enable plain HTTP traffic to access the uuid-api connect
      # native example service on port 8080.
      port "inbound" {
        static = 8080
        to     = 8080
      }
    }

    service {
      name = "my-ingress-service"
      port = "8080"

      connect {
        gateway {

          # Consul gateway [envoy] proxy options.
          proxy {
            # The following options are automatically set by Nomad if not
            # explicitly configured when using bridge networking.
            #
            # envoy_gateway_no_default_bind = true
            # envoy_gateway_bind_addresses "uuid-api" {
            #   address = "0.0.0.0"
            #   port    = <associated listener.port>
            # }
            #
            # Additional options are documented at
            # https://www.nomadproject.io/docs/job-specification/gateway#proxy-parameters
          }

          # Consul Ingress Gateway Configuration Entry.
          ingress {
            # Nomad will automatically manage the Configuration Entry in Consul
            # given the parameters in the ingress block.
            #
            # Additional options are documented at
            # https://www.nomadproject.io/docs/job-specification/gateway#ingress-parameters
            listener {
              port     = 8080
              protocol = "tcp"
              service {
                name = "web"
              }
            }
          }
        }
      }
    }
  }

  group "webgroup" {
    network {
      port "http" {
        to = 80
      }
    }

    service {
      name = "web"
      task = "server"
    }

    task "server" {
      driver = "docker"

      config {
        image = "nginx"
        ports = ["http"]
      }
    }
  }
}

After running the nomad job I can see the ingress registering in the Nomad and Consul UIs, but when curling it I get the following:

$ curl http://10.2.232.122:29558/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

$ curl -vvv http://10.2.232.122:8080/
*   Trying 10.2.232.122:8080...
* TCP_NODELAY set
* Connected to 10.2.232.122 (10.2.232.122) port 8080 (#0)
> GET / HTTP/1.1
> Host: 10.2.232.122:8080
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Empty reply from server
* Connection #0 to host 10.2.232.122 left intact
curl: (52) Empty reply from server

If anyone could help with this problem it would be greatly appreciated!

Thank you

Hi @Beardedhippo it looks like your web service for nginx is not yet configured to run a connect sidecar proxy. Since nginx itself is not connect native service, it needs to include a sidecar_service to be able to join the service mesh. The use of a sidecar will also require the webgroup group to use bridge networking mode.

Hello shoenig. Thanks for your reply. I now updated the job specification to the following:

job "ingress-demo" {

  datacenters = ["dc1"]

  group "ingress-group" {

    network {
      mode = "bridge"

      port "inbound" {
        static = 8080
        to     = 8080
      }
    }

    service {
      name = "my-ingress-service"
      port = "8080"

      connect {
        gateway {
          proxy {
          }

          ingress {
            listener {
              port     = 8080
              protocol = "tcp"
              service {
                name = "web"
              }
            }
          }
        }
      }
    }
  }

  group "webgroup" {
    network {
      mode = "bridge"
      port "http" {
        to = 80
      }
    }

    service {
      name = "web"
      task = "server"
      port = "http"

      connect {
        sidecar_service {}
      }
    }

    task "server" {
      driver = "docker"

      config {
        image = "nginx"
        ports = ["http"]
      }
    }
  }
}

Unfortunately, the same problem still persists. And when trying to curl the connect-proxy-web port, an empty response is returned aswell.

I think you’re getting close! I removed the port mapping from the nginx docker task - which isn’t necessary when using group-level networking. I also set the webgroup service.port value to “80” instead of “http”. You don’t want the external port here, but rather the literal port your service is listening to. When using the “http” port label, that was like saying, “nginx is listening on some random port nomad allocated”. The dynamic part of that port mapping is used by Connect, not your service.

Here’s a working job file, running with

consul agent -dev
sudo nomad agent -dev-connect
job "ingress-demo" {                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                  
  datacenters = ["dc1"]                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                  
  group "ingress-group" {                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                  
    network {                                                                                                                                                                                                                                     
      mode = "bridge"                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                  
      port "inbound" {                                                                                                                                                                                                                            
        static = 8080                                                                                                                                                                                                                             
        to     = 8080                                                                                                                                                                                                                             
      }                                                                                                                                                                                                                                           
    }                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                  
    service {                                                                                                                                                                                                                                     
      name = "my-ingress-service"                                                                                                                                                                                                                 
      port = "8080"                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                  
      connect {                                                                                                                                                                                                                                   
        gateway {                                                                                                                                                                                                                                 
          ingress {                                                                                                                                                                                                                               
            listener {                                                                                                                                                                                                                            
              port     = 8080                                                                                                                                                                                                                     
              protocol = "tcp"                                                                                                                                                                                                                    
              service {                                                                                                                                                                                                                           
                name = "web"                                                                                                                                                                                                                      
              }                                                                                                                                                                                                                                   
            }                                                                                                                                                                                                                                     
          }                                                                                                                                                                                                                                       
        }                                                                                                                                                                                                                                         
      }                                                                                                                                                                                                                                           
    }                                                                                                                                                                                                                                             
  }                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                  
  group "webgroup" {                                                                                                                                                                                                                              
    network {                                                                                                                                                                                                                                     
      mode = "bridge"                                                                                                                                                                                                                             
      port "http" {                                                                                                                                                                                                                               
        to = 80                                                                                                                                                                                                                                   
      }                                                                                                                                                                                                                                           
    }                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                  
    service {                                                                                                                                                                                                                                     
      name = "web"                                                                                                                                                                                                                                
      port = "80"                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                  
      connect {                                                                                                                                                                                                                                   
        sidecar_service {}                                                                                                                                                                                                                        
      }                                                                                                                                                                                                                                           
    }                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                  
    task "server" {                                                                                                                                                                                                                               
      driver = "docker"                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                  
      config {                                                                                                                                                                                                                                    
        image = "nginx"                                                                                                                                                                                                                           
      }                                                                                                                                                                                                                                           
    }                                                                                                                                                                                                                                             
  }                                                                                                                                                                                                                                               
}

Using Consul DNS to perform a query:

curl $(dig +short @127.0.0.1 -p 8600 web.ingress.dc1.consul. ANY):8080

Hello again,

I managed to get this job running on dev nodes, but now I am trying to run it on regular ones. This results in the following error which can be seen in consul ui:

I already enabled connect in the consul config.json file by adding

...
"connect": {
        "enabled": true
},
...
"ports": {
        ...
        "grpc": 8502,
        ...
},
...

Is there any other configuration, maybe in Nomad that I might have missed?