mirror of
https://github.com/kemko/nomad.git
synced 2026-01-03 08:55:43 +03:00
* Move commands from docs to its own root-level directory * temporarily use modified dev-portal branch with nomad ia changes * explicitly clone nomad ia exp branch * retrigger build, fixed dev-portal broken build * architecture, concepts and get started individual pages * fix get started section destinations * reference section * update repo comment in website-build.sh to show branch * docs nav file update capitalization * update capitalization to force deploy * remove nomad-vs-kubernetes dir; move content to what is nomad pg * job section * Nomad operations category, deploy section * operations category, govern section * operations - manage * operations/scale; concepts scheduling fix * networking * monitor * secure section * remote auth-methods folder and move up pages to sso; linkcheck * Fix install2deploy redirects * fix architecture redirects * Job section: Add missing section index pages * Add section index pages so breadcrumbs build correctly * concepts/index fix front matter indentation * move task driver plugin config to new deploy section * Finish adding full URL to tutorials links in nav * change SSO to Authentication in nav and file system * Docs NomadIA: Move tutorials into NomadIA branch (#26132) * Move governance and policy from tutorials to docs * Move tutorials content to job-declare section * run jobs section * stateful workloads * advanced job scheduling * deploy section * manage section * monitor section * secure/acl and secure/authorization * fix example that contains an unseal key in real format * remove images from sso-vault * secure/traffic * secure/workload-identities * vault-acl change unseal key and root token in command output sample * remove lines from sample output * fix front matter * move nomad pack tutorials to tools * search/replace /nomad/tutorials links * update acl overview with content from deleted architecture/acl * fix spelling mistake * linkcheck - fix broken links * fix link to Nomad variables tutorial * fix link to Prometheus tutorial * move who uses Nomad to use cases page; move spec/config shortcuts add dividers * Move Consul out of Integrations; move namespaces to govern * move integrations/vault to secure/vault; delete integrations * move ref arch to docs; rename Deploy Nomad back to Install Nomad * address feedback * linkcheck fixes * Fixed raw_exec redirect * add info from /nomad/tutorials/manage-jobs/jobs * update page content with newer tutorial * link updates for architecture sub-folders * Add redirects for removed section index pages. Fix links. * fix broken links from linkcheck * Revert to use dev-portal main branch instead of nomadIA branch * build workaround: add intro-nav-data.json with single entry * fix content-check error * add intro directory to get around Vercel build error * workound for emtpry directory * remove mdx from /intro/ to fix content-check and git snafu * Add intro index.mdx so Vercel build should work --------- Co-authored-by: Tu Nguyen <im2nguyen@gmail.com>
393 lines
17 KiB
Plaintext
393 lines
17 KiB
Plaintext
---
|
|
layout: docs
|
|
page_title: Configure a web UI reverse proxy
|
|
description: |-
|
|
Run and configure NGINX as a reverse proxy for the Nomad web UI to
|
|
create a secure way for users to access detailed cluster information.
|
|
---
|
|
|
|
# Configure a web UI reverse proxy
|
|
|
|
NGINX can be used to reverse proxy web services and balance load across multiple instances of the same service. A reverse proxy has the added benefits of enabling multiple web services to share a single, memorable domain and authentication to view internal systems.
|
|
|
|
To ensure every feature in the Nomad UI remains fully functional, you must properly configure your reverse proxy to meet Nomad's specific networking requirements.
|
|
|
|
This guide will explore common configuration changes necessary when reverse proxying Nomad's Web UI. Issues common to default proxy configurations will be discussed and demonstrated. As you learn about each issue, you will deploy NGINX configuration changes that will address it.
|
|
|
|
## Prerequisites
|
|
|
|
This guide assumes basic familiarity with Nomad and NGINX.
|
|
|
|
Here is what you will need for this guide:
|
|
|
|
- Nomad 0.11.0 installed locally
|
|
- Docker
|
|
|
|
## Start Nomad
|
|
|
|
Because of best practices around least access to nodes, it is typical for Nomad
|
|
UI users to not have direct access to the Nomad client nodes. You can simulate
|
|
that for the purposes of this guide by advertising an incorrect `http` address.
|
|
|
|
Create a file named `nomad.hcl` with the following configuration snippet.
|
|
|
|
```hcl
|
|
# Advertise a bogus HTTP address to force the UI
|
|
# to fallback to streaming logs through the proxy.
|
|
advertise {
|
|
http = "internal-ip:4646"
|
|
}
|
|
```
|
|
|
|
Start Nomad as a dev agent with this custom configuration file.
|
|
|
|
```shell-session
|
|
$ sudo nomad agent -dev -config=nomad.hcl
|
|
```
|
|
|
|
Next, create a service job file that will frequently write logs to `stdout`. This sample job file below can be used if you don't have your own.
|
|
|
|
```hcl
|
|
# fs-example.nomad.hcl
|
|
|
|
job "fs-example" {
|
|
datacenters = ["dc1"]
|
|
|
|
task "fs-example" {
|
|
driver = "docker"
|
|
|
|
config {
|
|
image = "dingoeatingfuzz/fs-example:0.3.0"
|
|
}
|
|
|
|
resources {
|
|
cpu = 500
|
|
memory = 512
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Run this service job using the Nomad CLI or UI.
|
|
|
|
```shell-session
|
|
$ nomad run fs-example.nomad.hcl
|
|
```
|
|
|
|
At this point, you have a Nomad cluster running locally with one job in it. You can visit the Web UI at `http://localhost:4646`.
|
|
|
|
## Configure NGINX to reverse proxy the web UI
|
|
|
|
As mentioned earlier, the overarching goal is to configure a proxy from Nomad UI users to the Nomad UI running on the Nomad cluster. To do that, you will configure a NGINX instance as your reverse proxy.
|
|
|
|
Create a basic NGINX configuration file to reverse proxy the Web UI. It is important to name the NGINX configuration file `nginx.conf` otherwise the file will not bind correctly.
|
|
|
|
```nginx
|
|
# nginx.conf
|
|
events {}
|
|
|
|
http {
|
|
server {
|
|
location / {
|
|
proxy_pass http://host.docker.internal:4646;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
<Note>
|
|
|
|
If you are not using Docker for Mac or Docker for Windows, the `host.docker.internal` DNS record may not be available.
|
|
|
|
</Note>
|
|
|
|
This basic NGINX configuration does two things. The first is forward all traffic into NGINX to the proxy address at `http://host.docker.internal:4646`. Since NGINX will be running in Docker and Nomad is running locally, this address is equivalent to `http://localhost:4646` which is where the Nomad API and Web UI are served. The second thing this configuration does is attach the `X-Forwarded-For` header which allows HTTP requests to be traced back to their origin.
|
|
|
|
Next in a new terminal session, start NGINX in Docker using this configuration file.
|
|
|
|
```shell-session
|
|
$ docker run --publish=8080:80 \
|
|
--mount type=bind,source=$PWD/nginx.conf,target=/etc/nginx/nginx.conf \
|
|
nginx:latest
|
|
```
|
|
|
|
NGINX will be started as soon as Docker has finished pulling layers. At that point you can visit `http://localhost:8080` to visit the Nomad Web UI through the NGINX reverse proxy.
|
|
|
|
## Extend connection timeout
|
|
|
|
The Nomad Web UI uses long-lived connections for its live-update feature. If the proxy closes the connection early because of a connection timeout, it could prevent the Web UI from continuing to live-reload data.
|
|
|
|
The Nomad Web UI will live-reload all data to make sure views are always fresh as Nomad's server state changes. This is achieved using the [blocking queries][blocking-queries] to the Nomad API. Blocking queries are an implementation of long-polling which works by keeping HTTP connections open until server-side state has changed. This is advantageous over traditional polling which results in more requests that often return no new information. It is also faster since a connection will close as soon as new information is available rather than having to wait for the next iteration of a polling loop. A consequence of this design is that HTTP requests aren't always expected to be short-lived. NGINX has a [default proxy timeout of 60 seconds](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout) while Nomad's blocking query system will leave connections open for five minutes by default.
|
|
|
|
To observe the proxy time out a connection, visit the Nomad jobs list through the proxy at `http://localhost:8080/ui/jobs` with your Browser's developer tools open.
|
|
|
|
<Tabs>
|
|
<Tab heading="Chrome">
|
|
|
|
|
|
With the Nomad UI page open, press the `F12` key to open the Developer tools. If it is not already selected, go to the Developer tools pane and select the **Network** tab. Leaving the tools pane open. Refresh the UI page.
|
|
|
|
The blocking query connection for jobs will remain in "(pending)" status.
|
|
|
|
[![Chrome developer tools window showing pending connection.][img-chrome-pending]][img-chrome-pending]
|
|
|
|
In approximately 60 seconds it will transition to a "504 Gateway Time-out" status.
|
|
|
|
[![Chrome developer tools window showing connection timeout.][img-chrome-timeout]][img-chrome-timeout]
|
|
|
|
</Tab>
|
|
<Tab heading="Firefox">
|
|
|
|
|
|
With the Nomad UI page open, go to the **Tools** menu, **Web Developer** flyout, and **Network** option.
|
|
|
|
The blocking query connection for jobs will not show a status while it is still active.
|
|
|
|
[![Firefox developer tools window showing pending connection.][img-firefox-pending]][img-firefox-pending]
|
|
|
|
In approximately 60 seconds it will transition to a "504 Gateway Time-out" status.
|
|
|
|
[![Firefox developer tools window showing connection timeout.][img-firefox-pending]][img-firefox-pending]
|
|
|
|
</Tab>
|
|
<Tab heading="Safari">
|
|
|
|
|
|
With the Nomad UI page open, go to the **Tools** menu, **Web Developer** flyout, and **Network** option.
|
|
|
|
The blocking query connection for jobs will have a spinning icon next to the Name while it is still active.
|
|
|
|
[![Safari developer tools window showing pending connection.][img-safari-pending]][img-safari-pending]
|
|
|
|
In approximately 60 seconds it will transition to a red error state. Clicking on the red error will show that you received a "504 Gateway Time-out" error.
|
|
|
|
[![Safari developer tools window showing connection timeout.][img-safari-pending]][img-safari-pending]
|
|
|
|
</Tab>
|
|
<Tab heading="Edge">
|
|
|
|
|
|
With the Nomad UI page open, press the `F12` key to open the Developer tools. If it is not already selected, go to the Developer tools pane and select the **Network** tab. Leaving the tools pane open. Refresh the UI page.
|
|
|
|
The blocking query connection for jobs will remain in "(pending)" status.
|
|
|
|
[![Edge developer tools window showing pending connection.][img-chrome-pending]][img-chrome-pending]
|
|
|
|
In approximately 60 seconds it will transition to a "504 Gateway Time-out" status.
|
|
|
|
[![Edge developer tools window showing connection timeout.][img-chrome-timeout]][img-chrome-timeout]
|
|
|
|
</Tab>
|
|
<Tab heading="Other">
|
|
|
|
|
|
Open your browser's developer tools. Locate the network information and wait approximately 60 seconds. The request to `/v1/jobs` will timeout with the following error message.
|
|
|
|
```plaintext
|
|
Failed to load resource: the server responded with a status of
|
|
504 (Gateway Time-out)
|
|
```
|
|
|
|
</Tab>
|
|
</Tabs>
|
|
<hr />
|
|
|
|
|
|
To prevent these timeouts, update the NGINX configuration's `location` block to extend the
|
|
`proxy_read_timeout` setting. The Nomad API documentation's [Blocking
|
|
Queries][blocking-queries] section explains that Nomad adds the result of (`wait` / 16) to the declared wait
|
|
time. You should set the `proxy_read_timeout` to slightly exceed Nomad's calculated wait time.
|
|
|
|
This guide uses the default blocking query `wait` of 300 seconds. Nomad adds
|
|
18.75 seconds to that wait time, so the `proxy_read_timeout` should be greater than 318.75 seconds.
|
|
|
|
Set the`proxy_read_timeout` to `319s`.
|
|
|
|
```nginx
|
|
# ...
|
|
proxy_pass http://host.docker.internal:4646;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
|
|
# Nomad blocking queries will remain open for a default of 5 minutes.
|
|
# Increase the proxy timeout to accommodate this timeout with an
|
|
# additional grace period.
|
|
proxy_read_timeout 319s;
|
|
# ...
|
|
```
|
|
|
|
Restart the NGINX docker container to load these configuration changes.
|
|
|
|
## Disable proxy buffering
|
|
|
|
When possible the Web UI will use a streaming HTTP request to stream logs on the task logs page. NGINX by default will buffer proxy responses in an attempt to free up connections to the backend server being proxied as soon as possible.
|
|
|
|
Proxy buffering causes logs events to not stream because they will be temporarily captured within NGINX's proxy buffer until either the connection is closed or the proxy buffer size is hit and the data is finally flushed to the client.
|
|
|
|
Older browsers may not support this technology, in which case logs are streamed using a simple polling mechanism.
|
|
|
|
To observe this issue, visit the task logs page of your sample job by first visiting the sample job at `http://localhost:8080/jobs/fs-example` then clicking into the most recent allocation, then clicking into the `fs-example` task, then clicking the logs tab.
|
|
|
|
Logs will not load and eventually the following error will appear in the UI.
|
|
|
|
[![Error in the UI. Cannot fetch logs. The logs for this task are inaccessible][img-cannot-fetch-logs]][img-cannot-fetch-logs]
|
|
|
|
There will also be this additional error in the browser developer tools console.
|
|
|
|
```plaintext
|
|
GET http://internal-ip:4646/v1/client/fs/logs/131f60f7-ef46-9fc0-d80d-29e673f01bd6?follow=true&offset=50000&origin=end&task=ansi&type=stdout net::ERR_NAME_NOT_RESOLVED
|
|
```
|
|
|
|
This `ERR_NAME_NOT_RESOLVED` error can be safely ignored. To prevent streaming logs through Nomad server nodes when unnecessary, the Web UI optimistically attempts to connect directly to the client node the task is running on. Since the Nomad configuration file used in this guide specifically advertises an address that can't be reached, the UI automatically falls back to requesting logs through the proxy.
|
|
|
|
To allow log streaming through NGINX, the NGINX configuration needs to be updated to disable proxy buffering. Add the following to the `location` block of the existing NGINX configuration file.
|
|
|
|
```nginx
|
|
# ...
|
|
proxy_read_timeout 319s;
|
|
|
|
# Nomad log streaming uses streaming HTTP requests. In order to
|
|
# synchronously stream logs from Nomad to NGINX to the browser
|
|
# proxy buffering needs to be turned off.
|
|
proxy_buffering off;
|
|
# ...
|
|
```
|
|
|
|
Restart the NGINX docker container to load these configuration changes.
|
|
|
|
## Enable WebSocket connections
|
|
|
|
As of Nomad 0.11.0, the Web UI has supported [interactive exec sessions with any running task in the cluster](https://www.hashicorp.com/blog/hashicorp-nomad-remote-exec-web-ui). This is achieved using the exec API which is implemented using WebSockets.
|
|
|
|
WebSockets are necessary for the exec API because they allow bidirectional data transfer. This is used to receive changes to the remote output as well as send commands and signals from the browser-based terminal.
|
|
|
|
The way a WebSocket connection is established is through a handshake request. The handshake is an HTTP request with special `Connection` and `Upgrade` headers.
|
|
|
|
WebSockets also do not support CORS headers. The server-side of a WebSocket connection needs to verify trusted origins on its own. Nomad does this verification by checking if the `Origin` header of the handshake request is equal to the address of the Nomad API.
|
|
|
|
By default NGINX will not fulfill the handshake or the origin verification. This results in exec sessions immediately terminating. You can experience this in the Web UI by going to `http://localhost:8080/jobs/fs-example`, clicking the Exec button, choosing the task, and attempting to run the command `/bin/sh`.
|
|
|
|
[![Error in the UI when running /bin/sh. The connection has closed.][img-cannot-remote-exec]][img-cannot-remote-exec]
|
|
|
|
The fulfill the handshake NGINX will need to forward the `Connection` and `Upgrade` headers. To meet the origin verification required by the Nomad API, NGINX will have to override the existing `Origin` header to match the host address. Add the following to the `location` block of the existing NGINX configuration file.
|
|
|
|
```nginx
|
|
# ...
|
|
proxy_buffering off;
|
|
|
|
# The Upgrade and Connection headers are used to establish
|
|
# a WebSockets connection.
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
|
|
# The default Origin header will be the proxy address, which
|
|
# will be rejected by Nomad. It must be rewritten to be the
|
|
# host address instead.
|
|
proxy_set_header Origin "${scheme}://${proxy_host}";
|
|
# ...
|
|
```
|
|
|
|
Restart the NGINX docker container to load these configuration changes.
|
|
|
|
WebSocket connections are also stateful. If you are planning on using NGINX to balance load across all Nomad server nodes, it is important to ensure that WebSocket connections get routed to a consistent host.
|
|
|
|
This can be done by specifying an upstream in NGINX and using it as the proxy pass. Add the following after the server block in the existing NGINX configuration file.
|
|
|
|
```nginx
|
|
# ...
|
|
# Since WebSockets are stateful connections but Nomad has multiple
|
|
# server nodes, an upstream with ip_hash declared is required to ensure
|
|
# that connections are always proxied to the same server node when possible.
|
|
upstream nomad-ws {
|
|
ip_hash;
|
|
server host.docker.internal:4646;
|
|
}
|
|
# ...
|
|
```
|
|
|
|
Traffic must also pass through the upstream. To do this, change the `proxy_pass` in the NGINX configuration file.
|
|
|
|
```nginx
|
|
# ...
|
|
location / {
|
|
proxy_pass http://nomad-ws
|
|
# ...
|
|
```
|
|
|
|
Since a dev environment only has one node, this change has no observable effect.
|
|
|
|
## Review the complete NGINX configuration
|
|
|
|
At this point all Web UI features are now working through the NGINX proxy. Here is the completed NGINX configuration file.
|
|
|
|
```nginx
|
|
events {}
|
|
|
|
http {
|
|
server {
|
|
location / {
|
|
proxy_pass http://nomad-ws;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
|
|
# Nomad blocking queries will remain open for a default of 5 minutes.
|
|
# Increase the proxy timeout to accommodate this timeout with an
|
|
# additional grace period.
|
|
proxy_read_timeout 319s;
|
|
|
|
# Nomad log streaming uses streaming HTTP requests. In order to
|
|
# synchronously stream logs from Nomad to NGINX to the browser
|
|
# proxy buffering needs to be turned off.
|
|
proxy_buffering off;
|
|
|
|
# The Upgrade and Connection headers are used to establish
|
|
# a WebSockets connection.
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
|
|
# The default Origin header will be the proxy address, which
|
|
# will be rejected by Nomad. It must be rewritten to be the
|
|
# host address instead.
|
|
proxy_set_header Origin "${scheme}://${proxy_host}";
|
|
}
|
|
}
|
|
|
|
# Since WebSockets are stateful connections but Nomad has multiple
|
|
# server nodes, an upstream with ip_hash declared is required to ensure
|
|
# that connections are always proxied to the same server node when possible.
|
|
upstream nomad-ws {
|
|
ip_hash;
|
|
server host.docker.internal:4646;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Next steps
|
|
|
|
In this guide, you set up a reverse NGINX proxy configured for the Nomad UI.
|
|
You also explored common configuration properties necessary to allow the Nomad
|
|
UI to work properly through a proxy—connection timeouts, proxy buffering,
|
|
WebSocket connections, and Origin header rewriting.
|
|
|
|
You can use these building blocks to configure your preferred proxy server
|
|
software to work with the Nomad UI. For further information about the NGINX
|
|
specific configuration highlighted in this guide, consult:
|
|
|
|
- [connection timeout][nginx-proxy-read-timeout]
|
|
- [proxy buffering][nginx-proxy-buffering]
|
|
- [WebSocket proxying][nginx-websocket-proxying]
|
|
- [session persistence][nginx-session-persistence]
|
|
|
|
[blocking-queries]: /nomad/api-docs#blocking-queries
|
|
[nginx-proxy-buffering]: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_request_buffering
|
|
[nginx-proxy-read-timeout]: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout
|
|
[nginx-session-persistence]: https://nginx.org/en/docs/http/load_balancing.html#nginx_load_balancing_with_ip_hash
|
|
[nginx-websocket-proxying]: https://nginx.org/en/docs/http/websocket.html
|
|
[img-cannot-fetch-logs]: /img/clusters/cannot-fetch-logs.png
|
|
[img-cannot-remote-exec]: /img/clusters/cannot-remote-exec.png
|
|
[img-chrome-pending]: /img/clusters/chrome-pending.png
|
|
[img-chrome-timeout]: /img/clusters/chrome-timeout.png
|
|
[img-firefox-pending]: /img/clusters/firefox-pending.png
|
|
[img-firefox-timeout]: /img/clusters/firefox-timeout.png
|
|
[img-safari-pending]: /img/clusters/safari-pending.png
|
|
[img-safari-timeout]: /img/clusters/safari-timeout.png
|