From 48a0e899e88b3128ab7d7abffc54c8d629c60a45 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 2 Nov 2023 18:54:26 +0000 Subject: [PATCH 001/101] chore: hugo initialization --- .../branching-and-workflow.md | 0 .../design-principles.md | 0 .../go-style-guide.md | 0 .../implementing-a-feature.md | 0 .../logging-guidelines.md | 0 .../pull-request.md | 0 .../quickstart.md | 0 .../release-process.md | 0 {docs/developer => docs-developer}/testing.md | 0 docs/.gitignore | 3 + docs/config/_default/config.toml | 68 ++++++++ docs/config/development/config.toml | 4 + docs/config/production/config.toml | 4 + docs/config/staging/config.toml | 4 + docs/{ => content}/README.md | 0 docs/content/_index.md | 6 + docs/{ => content}/architecture.md | 0 docs/{ => content}/building-the-images.md | 0 docs/{ => content}/cli-help.md | 0 .../control-plane-configuration.md | 0 .../gateway-api-compatibility.md | 0 docs/{ => content}/guides/README.md | 0 docs/content/guides/_index.md | 8 + docs/{ => content}/guides/advanced-routing.md | 36 ++-- .../guides/integrating-cert-manager.md | 0 .../guides/routing-traffic-to-your-app.md | 0 .../guides/upgrade-apps-without-downtime.md | 0 docs/{ => content}/installation.md | 0 docs/{ => content}/monitoring.md | 0 docs/{ => content}/proposals/README.md | 0 .../proposals/control-plane-config.md | 0 .../proposals/data-plane-config.md | 0 docs/{ => content}/proposals/template.md | 0 docs/{ => content}/resource-validation.md | 0 docs/{ => content}/running-on-kind.md | 0 docs/{ => content}/troubleshooting.md | 0 docs/go.mod | 5 + docs/go.sum | 4 + docs/makefile | 92 +++++++++++ docs/md-linkcheck-config.json | 14 ++ docs/mdlint_conf.json | 15 ++ docs/netlify.toml | 34 ++++ docs/readme.md | 154 ++++++++++++++++++ .../img}/advanced-routing.png | Bin .../img}/cert-manager-gateway-workflow.png | Bin .../img}/code-review-pyramid.jpeg | Bin .../{images => static/img}/ngf-high-level.png | Bin docs/{images => static/img}/ngf-pod.png | Bin .../img}/route-all-traffic-app.png | Bin .../img}/route-all-traffic-config.png | Bin .../img}/route-all-traffic-flow.png | Bin docs/{images => static/img}/src/README.md | 0 .../img}/src/advanced-routing.mermaid | 0 .../img}/src/route-all-traffic-app.mermaid | 0 .../img}/src/route-all-traffic-config.mermaid | 0 .../img}/src/route-all-traffic-flow.mermaid | 0 56 files changed, 432 insertions(+), 19 deletions(-) rename {docs/developer => docs-developer}/branching-and-workflow.md (100%) rename {docs/developer => docs-developer}/design-principles.md (100%) rename {docs/developer => docs-developer}/go-style-guide.md (100%) rename {docs/developer => docs-developer}/implementing-a-feature.md (100%) rename {docs/developer => docs-developer}/logging-guidelines.md (100%) rename {docs/developer => docs-developer}/pull-request.md (100%) rename {docs/developer => docs-developer}/quickstart.md (100%) rename {docs/developer => docs-developer}/release-process.md (100%) rename {docs/developer => docs-developer}/testing.md (100%) create mode 100644 docs/.gitignore create mode 100644 docs/config/_default/config.toml create mode 100644 docs/config/development/config.toml create mode 100644 docs/config/production/config.toml create mode 100644 docs/config/staging/config.toml rename docs/{ => content}/README.md (100%) create mode 100644 docs/content/_index.md rename docs/{ => content}/architecture.md (100%) rename docs/{ => content}/building-the-images.md (100%) rename docs/{ => content}/cli-help.md (100%) rename docs/{ => content}/control-plane-configuration.md (100%) rename docs/{ => content}/gateway-api-compatibility.md (100%) rename docs/{ => content}/guides/README.md (100%) create mode 100644 docs/content/guides/_index.md rename docs/{ => content}/guides/advanced-routing.md (87%) rename docs/{ => content}/guides/integrating-cert-manager.md (100%) rename docs/{ => content}/guides/routing-traffic-to-your-app.md (100%) rename docs/{ => content}/guides/upgrade-apps-without-downtime.md (100%) rename docs/{ => content}/installation.md (100%) rename docs/{ => content}/monitoring.md (100%) rename docs/{ => content}/proposals/README.md (100%) rename docs/{ => content}/proposals/control-plane-config.md (100%) rename docs/{ => content}/proposals/data-plane-config.md (100%) rename docs/{ => content}/proposals/template.md (100%) rename docs/{ => content}/resource-validation.md (100%) rename docs/{ => content}/running-on-kind.md (100%) rename docs/{ => content}/troubleshooting.md (100%) create mode 100644 docs/go.mod create mode 100644 docs/go.sum create mode 100644 docs/makefile create mode 100644 docs/md-linkcheck-config.json create mode 100644 docs/mdlint_conf.json create mode 100644 docs/netlify.toml create mode 100644 docs/readme.md rename docs/{images => static/img}/advanced-routing.png (100%) rename docs/{images => static/img}/cert-manager-gateway-workflow.png (100%) rename docs/{images => static/img}/code-review-pyramid.jpeg (100%) rename docs/{images => static/img}/ngf-high-level.png (100%) rename docs/{images => static/img}/ngf-pod.png (100%) rename docs/{images => static/img}/route-all-traffic-app.png (100%) rename docs/{images => static/img}/route-all-traffic-config.png (100%) rename docs/{images => static/img}/route-all-traffic-flow.png (100%) rename docs/{images => static/img}/src/README.md (100%) rename docs/{images => static/img}/src/advanced-routing.mermaid (100%) rename docs/{images => static/img}/src/route-all-traffic-app.mermaid (100%) rename docs/{images => static/img}/src/route-all-traffic-config.mermaid (100%) rename docs/{images => static/img}/src/route-all-traffic-flow.mermaid (100%) diff --git a/docs/developer/branching-and-workflow.md b/docs-developer/branching-and-workflow.md similarity index 100% rename from docs/developer/branching-and-workflow.md rename to docs-developer/branching-and-workflow.md diff --git a/docs/developer/design-principles.md b/docs-developer/design-principles.md similarity index 100% rename from docs/developer/design-principles.md rename to docs-developer/design-principles.md diff --git a/docs/developer/go-style-guide.md b/docs-developer/go-style-guide.md similarity index 100% rename from docs/developer/go-style-guide.md rename to docs-developer/go-style-guide.md diff --git a/docs/developer/implementing-a-feature.md b/docs-developer/implementing-a-feature.md similarity index 100% rename from docs/developer/implementing-a-feature.md rename to docs-developer/implementing-a-feature.md diff --git a/docs/developer/logging-guidelines.md b/docs-developer/logging-guidelines.md similarity index 100% rename from docs/developer/logging-guidelines.md rename to docs-developer/logging-guidelines.md diff --git a/docs/developer/pull-request.md b/docs-developer/pull-request.md similarity index 100% rename from docs/developer/pull-request.md rename to docs-developer/pull-request.md diff --git a/docs/developer/quickstart.md b/docs-developer/quickstart.md similarity index 100% rename from docs/developer/quickstart.md rename to docs-developer/quickstart.md diff --git a/docs/developer/release-process.md b/docs-developer/release-process.md similarity index 100% rename from docs/developer/release-process.md rename to docs-developer/release-process.md diff --git a/docs/developer/testing.md b/docs-developer/testing.md similarity index 100% rename from docs/developer/testing.md rename to docs-developer/testing.md diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000000..10ea4b1d12 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,3 @@ +public +.hugo_build.lock +resources \ No newline at end of file diff --git a/docs/config/_default/config.toml b/docs/config/_default/config.toml new file mode 100644 index 0000000000..e269c1d8d4 --- /dev/null +++ b/docs/config/_default/config.toml @@ -0,0 +1,68 @@ +title = "NGINX Gateway Fabric" +enableGitInfo = false +baseURL = "/" +publishDir = "public/nginx-gateway-fabric" +staticDir = ["static"] +languageCode = "en-us" +description = "NGINX Gateway Fabric." +refLinksErrorLevel = "ERROR" +enableRobotsTXT = "true" +#canonifyURLs = true +pluralizeListTitles = false +pygmentsCodeFences = true +pygmentsUseClasses = true + +[caches] + [caches.modules] + maxAge = -1 + +[module] +[[module.imports]] + path="github.com/nginxinc/nginx-hugo-theme" + +[markup] + [markup.highlight] + codeFences = true + guessSyntax = true + hl_Lines = "" + lineNoStart = 1 + lineNos = false + lineNumbersInTable = true + noClasses = true + style = "monokai" + tabWidth = 4 + [markup.goldmark] + [markup.goldmark.extensions] + definitionList = true + footnote = true + linkify = true + strikethrough = true + table = true + taskList = true + typographer = true + [markup.goldmark.parser] + attribute = true + autoHeadingID = true + autoHeadingIDType = "gitlab" + [markup.goldmark.renderer] + hardWraps = false + unsafe = true + xhtml = false + +[params] + useSectionPageLists = "false" + buildtype = "webdocs" + RSSLink = "/index.xml" + author = "NGINX Inc." # add your company name + github = "nginxinc" # add your github profile name + twitter = "@nginx" # add your twitter profile + #email = "" + noindex_kinds = [ + "taxonomy", + "taxonomyTerm" + ] + logo = "NGINX-product-icon.svg" + +sectionPagesMenu = "docs" + +ignoreFiles = [ "\\.sh$", "\\.DS_Store$", "\\.git.*$", "\\.txt$", "\\/config\\/.*", "README\\.*"] diff --git a/docs/config/development/config.toml b/docs/config/development/config.toml new file mode 100644 index 0000000000..6d96e11dfa --- /dev/null +++ b/docs/config/development/config.toml @@ -0,0 +1,4 @@ +baseURL = "https://docs-dev.nginx.com/nginx-gateway-fabric" +title = "DEV -- NGINX Gateway Fabric" +publishDir = "public/nginx-gateway-fabric" +canonifyURLs = false diff --git a/docs/config/production/config.toml b/docs/config/production/config.toml new file mode 100644 index 0000000000..0e21ba5f21 --- /dev/null +++ b/docs/config/production/config.toml @@ -0,0 +1,4 @@ +baseURL = "https://docs.nginx.com/nginx-gateway-fabric" +title = "NGINX Gateway Fabric" +publishDir = "public/nginx-gateway-fabric" +canonifyURLs = false diff --git a/docs/config/staging/config.toml b/docs/config/staging/config.toml new file mode 100644 index 0000000000..48897dd914 --- /dev/null +++ b/docs/config/staging/config.toml @@ -0,0 +1,4 @@ +baseURL = "https://docs-staging.nginx.com/nginx-gateway-fabric" +title = "STAGING -- NGINX Gateway Fabric" +publishDir = "public/nginx-gateway-fabric" +canonifyURLs = false \ No newline at end of file diff --git a/docs/README.md b/docs/content/README.md similarity index 100% rename from docs/README.md rename to docs/content/README.md diff --git a/docs/content/_index.md b/docs/content/_index.md new file mode 100644 index 0000000000..18f5c85a00 --- /dev/null +++ b/docs/content/_index.md @@ -0,0 +1,6 @@ +--- +title: "Welcome to the NGINX Gateway Fabric documentation" +description: +linkTitle: "NGINX Gateway Fabric" +menu: docs +--- \ No newline at end of file diff --git a/docs/architecture.md b/docs/content/architecture.md similarity index 100% rename from docs/architecture.md rename to docs/content/architecture.md diff --git a/docs/building-the-images.md b/docs/content/building-the-images.md similarity index 100% rename from docs/building-the-images.md rename to docs/content/building-the-images.md diff --git a/docs/cli-help.md b/docs/content/cli-help.md similarity index 100% rename from docs/cli-help.md rename to docs/content/cli-help.md diff --git a/docs/control-plane-configuration.md b/docs/content/control-plane-configuration.md similarity index 100% rename from docs/control-plane-configuration.md rename to docs/content/control-plane-configuration.md diff --git a/docs/gateway-api-compatibility.md b/docs/content/gateway-api-compatibility.md similarity index 100% rename from docs/gateway-api-compatibility.md rename to docs/content/gateway-api-compatibility.md diff --git a/docs/guides/README.md b/docs/content/guides/README.md similarity index 100% rename from docs/guides/README.md rename to docs/content/guides/README.md diff --git a/docs/content/guides/_index.md b/docs/content/guides/_index.md new file mode 100644 index 0000000000..8aa7536c2b --- /dev/null +++ b/docs/content/guides/_index.md @@ -0,0 +1,8 @@ +--- +title: "Guides" +description: +linkTitle: "Guides" +menu: + docs: + parent: NGINX Gateway Fabric +--- \ No newline at end of file diff --git a/docs/guides/advanced-routing.md b/docs/content/guides/advanced-routing.md similarity index 87% rename from docs/guides/advanced-routing.md rename to docs/content/guides/advanced-routing.md index 6ddba9249b..64fb05799e 100644 --- a/docs/guides/advanced-routing.md +++ b/docs/content/guides/advanced-routing.md @@ -1,12 +1,18 @@ -# Routing to Applications Using HTTP Matching Conditions +--- +title: "Routing to Applications Using HTTP Matching Conditions" +description: "Learn how to deploy multiple applications and HTTPRoutes with request conditions such as paths, methods, headers, and query parameters" +weight: 100 +toc: true +docs: "DOCS-000" +--- In this guide we will configure advanced routing rules for multiple applications. These rules will showcase request matching by path, headers, query parameters, and method. For an introduction to exposing your application, it is -recommended to go through the [basic guide](/docs/guides/routing-traffic-to-your-app.md) first. +recommended to go through the [basic guide]({{< relref "/guides/routing-traffic-to-your-app.md" >}}) first. The following image shows the traffic flow that we will be creating with these rules. -![Traffic Flow Diagram](/docs/images/advanced-routing.png) +![Traffic Flow Diagram](/img/advanced-routing.png) The goal is to create a set of rules that will result in client requests being sent to specific backends based on the request attributes. In this diagram, we have two versions of the `coffee` service. Traffic for v1 needs to be @@ -16,8 +22,8 @@ and `coffee` applications share the same Gateway. ## Prerequisites -- [Install](/docs/installation.md) NGINX Gateway Fabric. -- [Expose NGINX Gateway Fabric](/docs/installation.md#expose-nginx-gateway-fabric) and save the public IP +- [Install]({{< relref "/installation.md" >}}) NGINX Gateway Fabric. +- [Expose NGINX Gateway Fabric]({{< relref "/installation.md#expose-nginx-gateway-fabric" >}}) and save the public IP address and port of NGINX Gateway Fabric into shell variables: ```text @@ -25,9 +31,7 @@ and `coffee` applications share the same Gateway. GW_PORT= ``` -> **Note** -> In a production environment, you should have a DNS record for the external IP address that is exposed, -> and it should refer to the hostname that the gateway will forward for. +{{< note >}}In a production environment, you should have a DNS record for the external IP address that is exposed, and it should refer to the hostname that the gateway will forward for.{{< /note >}} ## Coffee Applications @@ -42,7 +46,7 @@ kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric ### Deploy the Gateway API Resources for the Coffee Applications The [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the -[cluster operator][roles-and-personas]. To deploy the Gateway: +[cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the Gateway: ```yaml kubectl apply -f - < **Note** -> If you have a DNS record allocated for `cafe.example.com`, you can send the request directly to that -> hostname, without needing to resolve. +{{< note >}}If you have a DNS record allocated for `cafe.example.com`, you can send the request directly to that hostname, without needing to resolve.{{< /note >}} ```shell curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee @@ -216,9 +216,7 @@ The properties of this HTTPRoute include: Using the external IP address and port for NGINX Gateway Fabric, we can send traffic to our tea applications. -> **Note** -> If you have a DNS record allocated for `cafe.example.com`, you can send the request directly to that -> hostname, without needing to resolve. +{{< note >}}If you have a DNS record allocated for `cafe.example.com`, you can send the request directly to that hostname, without needing to resolve.{{< /note >}} ```shell curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/tea @@ -352,7 +350,7 @@ If you have any issues while sending traffic, try the following to debug your co ## Further Reading -To learn more about the Gateway API and the resources we created in this guide, check out the following resources: +To learn more about the Gateway API and the resources we created in this guide, check out the following Kubernetes documentation resources: - [Gateway API Overview](https://gateway-api.sigs.k8s.io/concepts/api-overview/) - [Deploying a simple Gateway](https://gateway-api.sigs.k8s.io/guides/simple-gateway/) diff --git a/docs/guides/integrating-cert-manager.md b/docs/content/guides/integrating-cert-manager.md similarity index 100% rename from docs/guides/integrating-cert-manager.md rename to docs/content/guides/integrating-cert-manager.md diff --git a/docs/guides/routing-traffic-to-your-app.md b/docs/content/guides/routing-traffic-to-your-app.md similarity index 100% rename from docs/guides/routing-traffic-to-your-app.md rename to docs/content/guides/routing-traffic-to-your-app.md diff --git a/docs/guides/upgrade-apps-without-downtime.md b/docs/content/guides/upgrade-apps-without-downtime.md similarity index 100% rename from docs/guides/upgrade-apps-without-downtime.md rename to docs/content/guides/upgrade-apps-without-downtime.md diff --git a/docs/installation.md b/docs/content/installation.md similarity index 100% rename from docs/installation.md rename to docs/content/installation.md diff --git a/docs/monitoring.md b/docs/content/monitoring.md similarity index 100% rename from docs/monitoring.md rename to docs/content/monitoring.md diff --git a/docs/proposals/README.md b/docs/content/proposals/README.md similarity index 100% rename from docs/proposals/README.md rename to docs/content/proposals/README.md diff --git a/docs/proposals/control-plane-config.md b/docs/content/proposals/control-plane-config.md similarity index 100% rename from docs/proposals/control-plane-config.md rename to docs/content/proposals/control-plane-config.md diff --git a/docs/proposals/data-plane-config.md b/docs/content/proposals/data-plane-config.md similarity index 100% rename from docs/proposals/data-plane-config.md rename to docs/content/proposals/data-plane-config.md diff --git a/docs/proposals/template.md b/docs/content/proposals/template.md similarity index 100% rename from docs/proposals/template.md rename to docs/content/proposals/template.md diff --git a/docs/resource-validation.md b/docs/content/resource-validation.md similarity index 100% rename from docs/resource-validation.md rename to docs/content/resource-validation.md diff --git a/docs/running-on-kind.md b/docs/content/running-on-kind.md similarity index 100% rename from docs/running-on-kind.md rename to docs/content/running-on-kind.md diff --git a/docs/troubleshooting.md b/docs/content/troubleshooting.md similarity index 100% rename from docs/troubleshooting.md rename to docs/content/troubleshooting.md diff --git a/docs/go.mod b/docs/go.mod new file mode 100644 index 0000000000..3a8e1f7cdc --- /dev/null +++ b/docs/go.mod @@ -0,0 +1,5 @@ +module github.com/nginxinc/nalb-shared/docs + +go 1.18 + +require github.com/nginxinc/nginx-hugo-theme v0.39.0 // indirect diff --git a/docs/go.sum b/docs/go.sum new file mode 100644 index 0000000000..3aa016b19f --- /dev/null +++ b/docs/go.sum @@ -0,0 +1,4 @@ +github.com/nginxinc/nginx-hugo-theme v0.35.0 h1:7XB2GMy6qeJgKEJy9wOS3SYKYpfvLW3/H+UHRPLM4FU= +github.com/nginxinc/nginx-hugo-theme v0.35.0/go.mod h1:DPNgSS5QYxkjH/BfH4uPDiTfODqWJ50NKZdorguom8M= +github.com/nginxinc/nginx-hugo-theme v0.39.0 h1:P1hOPpityVUOM5OyIpQZa1UJyuUunGSmz0oZh/GYSJM= +github.com/nginxinc/nginx-hugo-theme v0.39.0/go.mod h1:DPNgSS5QYxkjH/BfH4uPDiTfODqWJ50NKZdorguom8M= diff --git a/docs/makefile b/docs/makefile new file mode 100644 index 0000000000..99234752ae --- /dev/null +++ b/docs/makefile @@ -0,0 +1,92 @@ +HUGO?=hugo +# the officially recommended unofficial docker image +HUGO_IMG?=klakegg/hugo:0.115.3 +# NGINX Hugo theme module location +THEME_MODULE=github.com/nginxinc/nginx-hugo-theme +## Pulls the current theme version from the Netlify settings +THEME_VERSION=$(NGINX_THEME_VERSION) + + +# if there's no local hugo, fallback to docker +ifeq (, $(shell ${HUGO} version 2> /dev/null)) +ifeq (, $(shell docker version 2> /dev/null)) + $(error Docker and Hugo are not installed. Hugo (<0.91) or Docker are required to build the local preview.) +else + HUGO=docker run --rm -it -v ${CURDIR}:/src -p 1313:1313 ${HUGO_IMG} +endif +endif + +MARKDOWNLINT?=markdownlint +MARKDOWNLINT_IMG?=ghcr.io/igorshubovych/markdownlint-cli:latest + +# if there's no local markdownlint, fallback to docker +ifeq (, $(shell ${MARKDOWNLINT} version 2> /dev/null)) +ifeq (, $(shell docker version 2> /dev/null)) +ifneq (, $(shell $(NETLIFY) "true")) + $(error Docker and markdownlint are not installed. markdownlint or Docker are required to lint.) +else + MARKDOWNLINT=docker run --rm -i -v ${CURDIR}:/src --workdir /src ${MARKDOWNLINT_IMG} +endif +endif +endif + +MARKDOWNLINKCHECK?=markdown-link-check +MARKDOWNLINKCHECK_IMG?=ghcr.io/tcort/markdown-link-check:stable +# if there's no local markdown-link-check, fallback to docker +ifeq (, $(shell ${MARKDOWNLINKCHECK} --version 2> /dev/null)) +ifeq (, $(shell docker version 2> /dev/null)) +ifneq (, $(shell $(NETLIFY) "true")) + $(error Docker and markdown-link-check are not installed. markdown-link-check or Docker are required to check links.) +else + MARKDOWNLINKCHECK=docker run --rm -it -v ${CURDIR}:/docs --workdir /docs ${MARKDOWNLINKCHECK_IMG} +endif +endif +endif + + +.PHONY: docs clean hugo-mod docs-local docs-drafts netlify lint-grammar lint-markdown link-check all all-staging all-dev + +## For use in Netlify CI only +all: hugo-mod build-production + +all-staging: hugo-mod build-staging + +all-dev: hugo-mod build-dev +## end for use in Netlify CI + +docs: + ${HUGO} server --disableFastRender + +clean: + rm -rf ./public + +docs-local: clean + ${HUGO} + +docs-drafts: + ${HUGO} server -D --disableFastRender + +lint-grammar: + docker run --rm -it -v ${CURDIR}/content:/root/content --entrypoint "vale" --workdir /root/content artifactory.f5net.com/cylon-indigo-docker-dev/indigo/tools/docs/vale-lint:0.7.0 ./ + +lint-markdown: + ${MARKDOWNLINT} -c mdlint_conf.json -- content + +link-check: + ${MARKDOWNLINKCHECK} $(shell find content -name '*.md') + +## commands for use in Netlify CI +hugo-mod: + hugo mod get $(THEME_MODULE)@v$(THEME_VERSION) + +build-production: hugo-mod + hugo --gc -e production + +build-staging: hugo-mod + hugo --gc -e staging + +build-dev: hugo-mod + hugo --gc -e development + +deploy-preview: hugo-mod + hugo --gc -d public/nginx-gateway-fabric/ -b ${NETLIFY_DEPLOY_URL}/nginx-gateway-fabric/ \ No newline at end of file diff --git a/docs/md-linkcheck-config.json b/docs/md-linkcheck-config.json new file mode 100644 index 0000000000..42cf466dff --- /dev/null +++ b/docs/md-linkcheck-config.json @@ -0,0 +1,14 @@ +{ + "replacementPatterns": [ + { + "pattern": "^\/", + "replacement": "/" + } + ], + "ignorePatterns": [ + { + "pattern": "^.+localhost.+$|\/.+yaml" + } + ] + } + \ No newline at end of file diff --git a/docs/mdlint_conf.json b/docs/mdlint_conf.json new file mode 100644 index 0000000000..b467fde3f4 --- /dev/null +++ b/docs/mdlint_conf.json @@ -0,0 +1,15 @@ +{ + "MD009": false, + "MD012": false, + "MD010": false, + "MD013": false, + "MD004": {"style": "dash"}, + "MD022": false, + "MD033": false, + "MD041": false, + "MD003": false, + "MD002": false, + "MD024": {"siblings_only": true}, + "MD046": false + } + \ No newline at end of file diff --git a/docs/netlify.toml b/docs/netlify.toml new file mode 100644 index 0000000000..50268abba0 --- /dev/null +++ b/docs/netlify.toml @@ -0,0 +1,34 @@ +[build] + base = "docs/" + publish = "public" + +[context.production] + command = "make all" + +[context.docs-development] + command = "make all-dev" + +[context.docs-staging] + command = "make all-staging" + +[context.branch-deploy] + command = "make deploy-preview" + +[context.deploy-preview] + command = "make deploy-preview" + +[[headers]] + for = "/*" + [headers.values] + Access-Control-Allow-Origin = "https://docs.nginx.com" + +[[redirects]] + from = "/" + to = "/nginx-gateway-fabric/" + status = 301 + force = true + +[[redirects]] + from = "/nginx-gateway-fabric/*" + to = "/nginx-gateway-fabric/404.html" + status = 404 \ No newline at end of file diff --git a/docs/readme.md b/docs/readme.md new file mode 100644 index 0000000000..69421ad879 --- /dev/null +++ b/docs/readme.md @@ -0,0 +1,154 @@ +# NGINX Gateway Fabric Docs + +This directory contains the user documentation for NGINX Gateway Fabric and the requirements for linting, building, and publishing the docs. + +We use [Hugo](https://gohugo.io/) to build the docs for NGINX, with the [nginx-hugo-theme](https://github.com/nginxinc/nginx-hugo-theme). + +Docs should be written in Markdown. + +In this directory, you will find the following files: + +- a [Netlify](https://netlify.com) configuration file; +- configuration files for [markdownlint](https://github.com/DavidAnson/markdownlint/) and [markdown-link-check](https://github.com/tcort/markdown-link-check) +- a `./config` directory that contains the [Hugo](https://gohugo.io) configuration. + +## Git workflow + +To contribute to the documentation, create your work branch from the `main` branch. Once you have added your changes, open a PR to merge your changes back to `main`. + +Netlify will create a preview of the documentation and add a link to the GitHub PR page. + +Once the preview and the changes have been reviewed, merge the request to the `main` branch. + +> NOTE: If you want to test changes in an environment that emulates the production site before updating `main`, you can push your changes to `docs-development` or `docs-staging`. These branches deploy to the nginx docs [dev](https://docs-dev.nginx.com) and [staging](https://docs-staging.nginx.com) sites, respectively. Ping the NGINX DocOps team in Slack if you need the password to these sites. + +## Setup + +### Golang + +Follow the instructions here to install Go: https://golang.org/doc/install + +> To support the use of Hugo mods, you need to install Go v1.15 or newer. + +### Hugo + +Follow the instructions here to install Hugo: [Hugo Installation](https://gohugo.io/installation/) + +> **NOTE:** We are currently running [Hugo v0.115.3](https://github.com/gohugoio/hugo/releases/tag/v0.115.3) in production. + +### Markdownlint + +We use markdownlint to check that Markdown files are correctly formatted. You can use `npm` to install markdownlint-cli: + +``` +npm install -g markdownlint-cli +``` + +## How to write docs with Hugo + +### Add a new doc + +- To create a new doc that contains all of the pre-configured Hugo front-matter and the docs task template: + + `hugo new /.` + + e.g., + + hugo new install.md + + > The default template -- task -- should be used in most docs. +- To create other types of docs, you can add the `--kind` flag: + `hugo new tutorials/deploy.md --kind tutorial` + + +The available kinds are: + +- Task: Enable the customer to achieve a specific goal, based on use case scenarios. +- Concept: Help a customer learn about a specific feature or feature set. +- Reference: Describes an API, command line tool, config options, etc.; should be generated automatically from source code. +- Troubleshooting: Helps a customer solve a specific problem. +- Tutorial: Walk a customer through an example use case scenario; results in a functional PoC environment. + +### Format internal links + +Format links as [Hugo relrefs](https://gohugo.io/content-management/cross-references/). + +> Note: Using file extensions when linking to internal docs with `relref` is optional. + +- You can use relative paths or just the filename. +- Paths without a leading `/` are first resolved relative to the current page, then to the remainder of the site. +- Anchors are supported. + +For example: + +```md +To install NGINX Controller, refer to the [installation instructions]({{< ref "install" >}}). +``` + +### Add images + +You can use the `img` [shortcode](#shortcodes) to insert images into your documentation. + +1. Add the image to the static/img directory, or to the same directory as the doc you want to use it in. + DO NOT include a forward slash at the beginning of the file path. This will break the image when it's rendered. + See the docs for the [Hugo relURL Function](https://gohugo.io/functions/relurl/#input-begins-with-a-slash) to learn more. + +1. Add the img shortcode: + + {{< img src="" >}} + +> Note: The shortcode accepts all of the same parameters as the [Hugo figure shortcode](https://gohugo.io/content-management/shortcodes/#figure). + +### Use Hugo shortcodes +You can use Hugo [shortcodes](https://gohugo.io/content-management/shortcodes) to do things like format callouts, add images, and reuse content across different docs. + +For example, to use the note callout: + +```md +{{< note >}}Provide the text of the note here. {{< /note >}} +``` + +The callout shortcodes also support multi-line blocks: + +```md +{{< caution >}} +You should probably never do this specific thing in a production environment. If you do, and things break, don't say we didn't warn you. +{{< /caution >}} +``` + +Supported callouts: +- caution +- important +- note +- see-also +- tip +- warning + +A few more useful shortcodes: + +- collapse: makes a section collapsible +- table: adds scrollbars to wide tables when viewed in small browser windows or mobile browsers +- fa: inserts a Font Awesome icon +- include: include the content of a file in another file (requires the included file to be in the /includes directory) +- link: makes it possible to link to a static file and prepend the path with the Hugo baseUrl +- openapi: loads an OpenAPI spec and renders as HTML using ReDoc +- raw-html: makes it possible to include a block of raw HTML +- readfile: includes the content of another file in the current file; useful for adding code examples + +## How to build docs locally + +To view the docs in a browser, run the Hugo server. This will reload the docs automatically so you can view updates as you work. + +> Note: The docs use build environments to control the baseURL that will be used for things like internal references and resource (CSS and JS) loading. +> You can view the config for each environment in the [config](./config) directory of this repo. +When running the Hugo server, you can specify the environment and baseURL if desired, but it's not necessary. + +For example: + +``` +hugo server +``` + +``` +hugo server -e development -b "http://127.0.0.1/nginx-gateway-fabric/" +``` diff --git a/docs/images/advanced-routing.png b/docs/static/img/advanced-routing.png similarity index 100% rename from docs/images/advanced-routing.png rename to docs/static/img/advanced-routing.png diff --git a/docs/images/cert-manager-gateway-workflow.png b/docs/static/img/cert-manager-gateway-workflow.png similarity index 100% rename from docs/images/cert-manager-gateway-workflow.png rename to docs/static/img/cert-manager-gateway-workflow.png diff --git a/docs/images/code-review-pyramid.jpeg b/docs/static/img/code-review-pyramid.jpeg similarity index 100% rename from docs/images/code-review-pyramid.jpeg rename to docs/static/img/code-review-pyramid.jpeg diff --git a/docs/images/ngf-high-level.png b/docs/static/img/ngf-high-level.png similarity index 100% rename from docs/images/ngf-high-level.png rename to docs/static/img/ngf-high-level.png diff --git a/docs/images/ngf-pod.png b/docs/static/img/ngf-pod.png similarity index 100% rename from docs/images/ngf-pod.png rename to docs/static/img/ngf-pod.png diff --git a/docs/images/route-all-traffic-app.png b/docs/static/img/route-all-traffic-app.png similarity index 100% rename from docs/images/route-all-traffic-app.png rename to docs/static/img/route-all-traffic-app.png diff --git a/docs/images/route-all-traffic-config.png b/docs/static/img/route-all-traffic-config.png similarity index 100% rename from docs/images/route-all-traffic-config.png rename to docs/static/img/route-all-traffic-config.png diff --git a/docs/images/route-all-traffic-flow.png b/docs/static/img/route-all-traffic-flow.png similarity index 100% rename from docs/images/route-all-traffic-flow.png rename to docs/static/img/route-all-traffic-flow.png diff --git a/docs/images/src/README.md b/docs/static/img/src/README.md similarity index 100% rename from docs/images/src/README.md rename to docs/static/img/src/README.md diff --git a/docs/images/src/advanced-routing.mermaid b/docs/static/img/src/advanced-routing.mermaid similarity index 100% rename from docs/images/src/advanced-routing.mermaid rename to docs/static/img/src/advanced-routing.mermaid diff --git a/docs/images/src/route-all-traffic-app.mermaid b/docs/static/img/src/route-all-traffic-app.mermaid similarity index 100% rename from docs/images/src/route-all-traffic-app.mermaid rename to docs/static/img/src/route-all-traffic-app.mermaid diff --git a/docs/images/src/route-all-traffic-config.mermaid b/docs/static/img/src/route-all-traffic-config.mermaid similarity index 100% rename from docs/images/src/route-all-traffic-config.mermaid rename to docs/static/img/src/route-all-traffic-config.mermaid diff --git a/docs/images/src/route-all-traffic-flow.mermaid b/docs/static/img/src/route-all-traffic-flow.mermaid similarity index 100% rename from docs/images/src/route-all-traffic-flow.mermaid rename to docs/static/img/src/route-all-traffic-flow.mermaid From cfce8703d3f738ae7358f3b0692c94adbd2bd5d6 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Mon, 6 Nov 2023 13:01:38 +0000 Subject: [PATCH 002/101] chore: fix conflicts --- docs/content/proposals/data-plane-config.md | 127 +++++++++++++++++++- 1 file changed, 121 insertions(+), 6 deletions(-) diff --git a/docs/content/proposals/data-plane-config.md b/docs/content/proposals/data-plane-config.md index 872383590c..3b75feddbc 100644 --- a/docs/content/proposals/data-plane-config.md +++ b/docs/content/proposals/data-plane-config.md @@ -1,21 +1,136 @@ -# Enhancement Proposal-929: Data Plane Dynamic Configuration +# Enhancement Proposal-929: Data Plane Configuration - Issue: https://github.com/nginxinc/nginx-kubernetes-gateway/issues/929 -- Status: Provisional +- Status: Implementable ## Summary -This proposal is intended to contain the design for how to dynamically configure the data plane for the -NGINX Gateway Fabric (NGF) project. Similar to control plane configuration, we should be able to leverage +This proposal is intended to contain the design for how to configure global settings for the data plane +of the NGINX Gateway Fabric (NGF) product. Similar to control plane configuration, we should be able to leverage a custom resource definition to define data plane configuration, considering fields such as telemetry and upstream zone size. ## Goals -Define a CRD to dynamically configure various settings for the NGF data plane. The initial configurable options -will be for telemetry (tracing) and upstream zone size. +Define a CRD to configure various global settings for the NGF data plane. The initial configurable +options will be for telemetry (tracing) and upstream zone size. ## Non-Goals 1. This proposal is not defining every setting that needs to be present in the configuration. 2. This proposal is not for any configuration related to control plane. + +## Introduction + +The NGF data plane will evolve to have various user-configurable options. These could include, but are not +limited to, tracing, logging, or metrics. For the best user experience, these options should be able to be +changed at runtime, to avoid having to restart NGF. The first set of options that we will allow users to +configure are tracing and upstream zone size. The easiest and most intuitive way to implement a Kubernetes-native +API is through a CRD. + +The purpose of this CRD is to contain "global" configuration options for the data plane, and not focused on policy +per route or backend. + +NGF will reload NGINX when configuration changes are made. + +In this doc, the term "user" will refer to the cluster operator (the person who installs and manages NGF). The +cluster operator owns this CRD resource. + +## API, Customer Driven Interfaces, and User Experience + +The API would be provided in a CRD. An authorized user would interact with this CRD using `kubectl` to `get` +or `edit` the configuration. + +Proposed configuration CRD example: + +```yaml +apiVersion: gateway.nginx.org/v1alpha1 +kind: NginxProxy +metadata: + name: nginx-proxy-config + namespace: nginx-gateway +spec: + http: + upstreamZoneSize: 512k # default + telemetry: + tracing: + enabled: true # default false + endpoint: my-otel-collector.svc:4317 # required + interval: 5s # default + batchSize: 512 # default + batchCount: 4 # default +status: + conditions: + ... +``` + +- The CRD would be Namespace-scoped. +- CRD is initialized and created when NGF is deployed, in the `nginx-gateway` Namespace. +- CRD would be referenced in the [ParametersReference][ref] of the NGF GatewayClass. +- Conditions include `Accepted` if the CRD config is valid, and `Programmed` to determine if an nginx +reload was successful. + +[ref]:https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParametersReference + +## Use Cases + +The high level use case is to configure options in the NGF data plane that are not currently configurable. The +CRD also allows for these to change without the need to restart the NGF Pod. + +### Tracing + +Users may want to observe how traffic is flowing through their applications. Tracing is a great way to achieve +this. By taking advantage of the OpenTelemetry standards, a user can deploy any OTLP-compliant tracing collector +to receive and visualize tracing data. Allowing a user to configure a tracing backend for NGF will forward +nginx tracing data to that backend for visualization. + +For future considerations, a user may want to disable tracing for certain routes (or only enable it for certain +routes), in order to reduce the amount of data being collected. We would likely be able to implement a [per-route +Policy](https://gateway-api.sigs.k8s.io/geps/gep-713/#direct-policy-attachment) +that would include this switch. The proposed "global" CRD in this document would remain unchanged, though +could include an additional field to enable or disable tracing globally. + +### Upstream Zone Size + +As the number of servers within an upstream increases (in other words, Pod replicas for a Service), the +shared memory zone size needs to increase to accomodate this. A user can fine-tune this number to fit their +environment. + +## Testing + +Unit tests can be leveraged for verifying that NGF properly watches and acts on CRD changes. These tests would +be similar in behavior as the current unit tests that verify the control plane CRD resource processing. + +We would need system level tests to ensure that tracing works as expected. + +## Security Considerations + +We need to ensure that any configurable fields that are exposed to a user are: + +- Properly validated. This means that the fields should be the correct type (integer, string, etc.), have appropriate +length, and use regex patterns or enums to prevent any unwanted input. This will initially be done through +OpenAPI schema validation. If necessary as the CRD evolves, CEL or control plane validation could be used. +- Have a valid use case. The more fields we expose, the more attack vectors we create. We should only be exposing +fields that are genuinely useful for a user to change dynamically. + +RBAC via the Kubernetes API server will ensure that only authorized users can update the CRD containing NGF data +plane configuration. + +## Alternatives + +- ConfigMap +A ConfigMap is another type of resource that a user can provide configuration options within, however it lacks the +benefits of a CRD, specifically built-in schema validation, versioning, and conversion webhooks. + +- Custom API server +The NGF control plane could implement its own custom API server. However the overhead of implementing this, which +would include auth, validation, endpoints, and so on, would not be worth it due to the fact that the Kubernetes +API server already does all of these things for us. + +- Policies CRD for granular control +Being that these are global settings, a user may have a need for more granular control, in other words, changing +the settings at a per-route or per-backend basis. A new Policy CRD could accomplish this in future work. + +## References + +- [Kubernetes Custom Resources](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) \ No newline at end of file From 3f5ff525137772c0ee478b1b05cf9f4b60af5f3b Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Mon, 6 Nov 2023 18:06:22 +0000 Subject: [PATCH 003/101] feat: add hugo meta, rewrite routing-traffic doc --- .../proposals => docs-proposals}/README.md | 0 .../control-plane-config.md | 0 .../data-plane-config.md | 0 .../proposals => docs-proposals}/template.md | 0 docs/content/README.md | 27 ---- docs/content/architecture.md | 11 +- docs/content/building-the-images.md | 8 + docs/content/cli-help.md | 10 +- docs/content/control-plane-configuration.md | 10 +- docs/content/gateway-api-compatibility.md | 10 +- docs/content/guides/_index.md | 2 +- docs/content/guides/advanced-routing.md | 50 +++--- .../guides/integrating-cert-manager.md | 142 +++++++----------- .../guides/routing-traffic-to-your-app.md | 8 + .../guides/upgrade-apps-without-downtime.md | 8 + docs/content/installation.md | 27 +--- docs/content/monitoring.md | 8 + docs/content/resource-validation.md | 10 +- docs/content/running-on-kind.md | 10 +- docs/content/troubleshooting.md | 10 +- 20 files changed, 160 insertions(+), 191 deletions(-) rename {docs/content/proposals => docs-proposals}/README.md (100%) rename {docs/content/proposals => docs-proposals}/control-plane-config.md (100%) rename {docs/content/proposals => docs-proposals}/data-plane-config.md (100%) rename {docs/content/proposals => docs-proposals}/template.md (100%) delete mode 100644 docs/content/README.md diff --git a/docs/content/proposals/README.md b/docs-proposals/README.md similarity index 100% rename from docs/content/proposals/README.md rename to docs-proposals/README.md diff --git a/docs/content/proposals/control-plane-config.md b/docs-proposals/control-plane-config.md similarity index 100% rename from docs/content/proposals/control-plane-config.md rename to docs-proposals/control-plane-config.md diff --git a/docs/content/proposals/data-plane-config.md b/docs-proposals/data-plane-config.md similarity index 100% rename from docs/content/proposals/data-plane-config.md rename to docs-proposals/data-plane-config.md diff --git a/docs/content/proposals/template.md b/docs-proposals/template.md similarity index 100% rename from docs/content/proposals/template.md rename to docs-proposals/template.md diff --git a/docs/content/README.md b/docs/content/README.md deleted file mode 100644 index d93ff1c5a6..0000000000 --- a/docs/content/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# NGINX Gateway Fabric Documentation - -This directory contains all of the documentation relating to NGINX Gateway Fabric. - -## Contents - -- [Architecture](architecture.md): An overview of the architecture and design principles of NGINX Gateway Fabric. -- [Gateway API Compatibility](gateway-api-compatibility.md): Describes which Gateway API resources NGINX Gateway -Fabric supports and the extent of that support. -- [Installation](installation.md): Walkthrough on how to install NGINX Gateway Fabric on a generic Kubernetes cluster. -- [Resource Validation](resource-validation.md): Describes how NGINX Gateway Fabric validates Gateway API -resources. -- [Control Plane Configuration](control-plane-configuration.md): Describes how to dynamically update the NGINX -Gateway Fabric control plane configuration. -- [Building the Images](building-the-images.md): Steps on how to build the NGINX Gateway Fabric container images -yourself. -- [Running on Kind](running-on-kind.md): Walkthrough on how to run NGINX Gateway Fabric on a `kind` cluster. -- [CLI Help](cli-help.md): Describes the commands available in the `gateway` binary of `nginx-gateway-fabric` -container. -- [Monitoring](monitoring.md): Information on monitoring NGINX Gateway Fabric using Prometheus metrics. -- [Troubleshooting](troubleshooting.md): Troubleshooting guide for common or known issues. - -### Directories - -- [Guides](guides): Guides about configuring NGINX Gateway Fabric for various use cases. -- [Developer](developer/): Docs for developers of the project. Contains guides relating to processes and workflows. -- [Proposals](proposals/): Enhancement proposals for new features. diff --git a/docs/content/architecture.md b/docs/content/architecture.md index 86f1f2177e..ae21518018 100644 --- a/docs/content/architecture.md +++ b/docs/content/architecture.md @@ -1,7 +1,14 @@ +--- +title: "Architecture" +description: "Learn about the architecture and design principles of NGINX Gateway Fabric." +weight: 100 +toc: true +docs: "DOCS-000" +--- + # Architecture -This document provides an overview of the architecture and design principles of the NGINX Gateway Fabric. The target -audience includes the following groups: +The target audience of this architecture document includes the following groups: - *Cluster Operators* who would like to know how the software works and also better understand how it can fail. - *Developers* who would like to [contribute][contribute] to the project. diff --git a/docs/content/building-the-images.md b/docs/content/building-the-images.md index c43f471cb9..fb412c3c6f 100644 --- a/docs/content/building-the-images.md +++ b/docs/content/building-the-images.md @@ -1,3 +1,11 @@ +--- +title: "Building the Images" +description: "Learn how to build the NGINX Gateway Fabric and NGINX images." +weight: 300 +toc: true +docs: "DOCS-000" +--- + # Building the Images ## Prerequisites diff --git a/docs/content/cli-help.md b/docs/content/cli-help.md index e3b795ae23..5700568a23 100644 --- a/docs/content/cli-help.md +++ b/docs/content/cli-help.md @@ -1,6 +1,10 @@ -# Command-line Help - -This document describes the commands available in the `gateway` binary of the `nginx-gateway` container. +--- +title: "Command line help" +description: "Learn about the commands available in the gateway binary of the nginx-gateway container." +weight: 400 +toc: true +docs: "DOCS-000" +--- ## Static Mode diff --git a/docs/content/control-plane-configuration.md b/docs/content/control-plane-configuration.md index 2d2a13d033..6124785586 100644 --- a/docs/content/control-plane-configuration.md +++ b/docs/content/control-plane-configuration.md @@ -1,6 +1,10 @@ -# Control Plane Configuration - -This document describes how to dynamically update the NGINX Gateway Fabric control plane configuration. +--- +title: "Control Plane Configuration" +description: "Learn how to dynamically update the NGINX Gateway Fabric control plane configuration." +weight: 500 +toc: true +docs: "DOCS-000" +--- ## Overview diff --git a/docs/content/gateway-api-compatibility.md b/docs/content/gateway-api-compatibility.md index 32eb1a7703..18c9727af6 100644 --- a/docs/content/gateway-api-compatibility.md +++ b/docs/content/gateway-api-compatibility.md @@ -1,6 +1,10 @@ -# Gateway API Compatibility - -This document describes which Gateway API resources NGINX Gateway Fabric supports and the extent of that support. +--- +title: "Gateway API Compatibility" +description: "Learn which Gateway API resources NGINX Gateway Fabric supports and the extent of that support." +weight: 700 +toc: true +docs: "DOCS-000" +--- ## Summary diff --git a/docs/content/guides/_index.md b/docs/content/guides/_index.md index 8aa7536c2b..c2317a61c6 100644 --- a/docs/content/guides/_index.md +++ b/docs/content/guides/_index.md @@ -1,5 +1,5 @@ --- -title: "Guides" +title: "How-To Guides" description: linkTitle: "Guides" menu: diff --git a/docs/content/guides/advanced-routing.md b/docs/content/guides/advanced-routing.md index 64fb05799e..9be61d5e38 100644 --- a/docs/content/guides/advanced-routing.md +++ b/docs/content/guides/advanced-routing.md @@ -6,19 +6,13 @@ toc: true docs: "DOCS-000" --- -In this guide we will configure advanced routing rules for multiple applications. These rules will showcase request -matching by path, headers, query parameters, and method. For an introduction to exposing your application, it is -recommended to go through the [basic guide]({{< relref "/guides/routing-traffic-to-your-app.md" >}}) first. +In this guide we will configure advanced routing rules for multiple applications. These rules will showcase request matching by path, headers, query parameters, and method. For an introduction to exposing your application, we recommend that you go through the [basic guide]({{< relref "/guides/routing-traffic-to-your-app.md" >}}) first. The following image shows the traffic flow that we will be creating with these rules. ![Traffic Flow Diagram](/img/advanced-routing.png) -The goal is to create a set of rules that will result in client requests being sent to specific backends based on -the request attributes. In this diagram, we have two versions of the `coffee` service. Traffic for v1 needs to be -directed to the old application, while traffic for v2 needs to be directed towards the new application. We also -have two `tea` services, one that handles GET operations and one that handles POST operations. Both the `tea` -and `coffee` applications share the same Gateway. +The goal is to create a set of rules that will result in client requests being sent to specific backends based on the request attributes. In this diagram, we have two versions of the `coffee` service. Traffic for v1 needs to be directed to the old application, while traffic for v2 needs to be directed towards the new application. We also have two `tea` services, one that handles GET operations and one that handles POST operations. Both the `tea` and `coffee` applications share the same Gateway. ## Prerequisites @@ -45,8 +39,7 @@ kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric ### Deploy the Gateway API Resources for the Coffee Applications -The [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the -[cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the Gateway: +The [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the Gateway: ```yaml kubectl apply -f - <}}If you have a DNS record allocated for `cafe.example.com`, you can send the request directly to that hostname, without needing to resolve.{{< /note >}} @@ -138,8 +128,7 @@ Server address: 10.244.0.9:8080 Server name: coffee-v2-68bd55f798-s9z5q ``` -If we want our request to be routed to `coffee-v2`, then we need to meet the defined conditions. We can include -a header: +If we want our request to be routed to `coffee-v2`, then we need to meet the defined conditions. We can include a header: ```shell curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee -H "version:v2" @@ -160,8 +149,7 @@ Server name: coffee-v2-68bd55f798-s9z5q ## Tea Applications -Let's deploy a different set of applications now called `tea` and `tea-post`. These applications will -have their own set of rules, but will still attach to the same Gateway listener as the `coffee` apps. +Let's deploy a different set of applications now called `tea` and `tea-post`. These applications will have their own set of rules, but will still attach to the same Gateway listener as the `coffee` apps. ### Deploy the Tea Applications @@ -213,8 +201,7 @@ The properties of this HTTPRoute include: ### Send Traffic to Tea -Using the external IP address and port for NGINX Gateway Fabric, we can send traffic to our tea -applications. +Using the external IP address and port for NGINX Gateway Fabric, we can send traffic to our tea applications. {{< note >}}If you have a DNS record allocated for `cafe.example.com`, you can send the request directly to that hostname, without needing to resolve.{{< /note >}} @@ -240,16 +227,14 @@ Server address: 10.244.0.7:8080 Server name: tea-post-b59b8596b-g586r ``` -This request should receive a response from the `tea-post` Pod. Any other type of method, such as PATCH, will -result in a `404 Not Found` response. +This request should receive a response from the `tea-post` Pod. Any other type of method, such as PATCH, will result in a `404 Not Found` response. ## Troubleshooting If you have any issues while sending traffic, try the following to debug your configuration and setup: -- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric - Service. Instructions for finding those values are [here](/docs/installation.md#expose-nginx-gateway-fabric). +- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are [here](/docs/installation.md#expose-nginx-gateway-fabric). - Check the status of the Gateway: @@ -307,8 +292,7 @@ If you have any issues while sending traffic, try the following to debug your co Name: http ``` - Check that the conditions match and that the attached routes for the `http` listener equals 2. If it is less than - 2, there may be an issue with the routes. + Check that the conditions match and that the attached routes for the `http` listener equals 2. If it is less than 2, there may be an issue with the routes. - Check the status of the HTTPRoutes: diff --git a/docs/content/guides/integrating-cert-manager.md b/docs/content/guides/integrating-cert-manager.md index 543da76068..9556b6c298 100644 --- a/docs/content/guides/integrating-cert-manager.md +++ b/docs/content/guides/integrating-cert-manager.md @@ -1,13 +1,14 @@ -# Securing Traffic using Let's Encrypt and Cert-Manager +--- +title: "Securing Traffic using Let's Encrypt and Cert-Manager" +description: "Learn how to issue and mange certificates using Let's Encrypt and cert-manager." +weight: 200 +toc: true +docs: "DOCS-000" +--- -Securing client server communication is a crucial part of modern application architectures. One of the most important -steps in this process is implementing HTTPS (HTTP over TLS/SSL) for all communications. This encrypts the data -transmitted between the client and server, preventing eavesdropping and tampering. To do this, you need an SSL/TLS -certificate from a trusted Certificate Authority (CA). However, issuing and managing certificates can be a complicated -manual process. Luckily, there are many services and tools available to simplify and automate certificate issuance and -management. +Securing client server communication is a crucial part of modern application architectures. One of the most important steps in this process is implementing HTTPS (HTTP over TLS/SSL) for all communications. This encrypts the data transmitted between the client and server, preventing eavesdropping and tampering. To do this, you need an SSL/TLS certificate from a trusted Certificate Authority (CA). However, issuing and managing certificates can be a complicated manual process. Luckily, there are many services and tools available to simplify and automate certificate issuance and management. -This guide will demonstrate how to: +Follow the steps in this guide to: - Configure HTTPS for your application using a [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/). - Use [Let’s Encrypt](https://letsencrypt.org) as the Certificate Authority (CA) issuing the TLS certificate. @@ -15,46 +16,30 @@ This guide will demonstrate how to: ## Prerequisities -1. Administrator access to a Kubernetes cluster. -2. [Helm](https://helm.sh) and [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) must be installed locally. -3. Deploy NGINX Gateway Fabric (NGF) following the [deployment instructions](/docs/installation.md). -4. A DNS resolvable domain name is required. It must resolve to the public endpoint of the NGF deployment, and this - public endpoint must be an external IP address or alias accessible over the internet. The process here will depend - on your DNS provider. This DNS name will need to be resolvable from the Let’s Encrypt servers, which may require - that you wait for the record to propagate before it will work. +- Administrator access to a Kubernetes cluster. +- [Helm](https://helm.sh) and [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) must be installed locally. +- [NGINX Gateway Fabric deployed]({{< relref "/installation.md" >}}) in the Kubernetes cluster. +- A DNS-resolvable domain name is required. It must resolve to the public endpoint of the NGINX Gateway Fabric deployment, and this public endpoint must be an external IP address or alias accessible over the internet. The process here will depend on your DNS provider. This DNS name will need to be resolvable from the Let’s Encrypt servers, which may require that you wait for the record to propagate before it will work. ## Overview -![cert-manager ACME Challenge and certificate management with Gateway API](/docs/images/cert-manager-gateway-workflow.png) +![cert-manager ACME Challenge and certificate management with Gateway API](/img/cert-manager-gateway-workflow.png) -The diagram above shows a simplified representation of the cert-manager ACME Challenge and certificate issuance process -using Gateway API. Please note that not all of the Kubernetes objects created in this process are represented in -this diagram. +The diagram above shows a simplified representation of the cert-manager ACME Challenge and certificate issuance process using Gateway API. Please note that not all of the kubernetes objects created in this process are represented in this diagram. At a high level, the process looks like this: -1. We deploy cert-manager and create a ClusterIssuer which specifies Let’s Encrypt as our CA and Gateway as our ACME - HTTP01 Challenge solver. -2. We create a Gateway resource for our domain (cafe.example.com) and configure cert-manager integration using an - annotation. -3. This kicks off the certificate issuance process – cert-manager contacts Let’s Encrypt to obtain a certificate, and - Let’s Encrypt starts the ACME challenge. As part of this challenge, a temporary HTTPRoute resource is created by - cert-manager which directs the traffic through NGF to verify we control the domain name in the certificate request. -4. Once the domain has been verified, the certificate is issued. Cert-manager stores the keypair in a Kubernetes secret - that is referenced by the Gateway resource. As a result, NGINX is configured to terminate HTTPS traffic from clients - using this signed keypair. -5. We deploy our application and our HTTPRoute which defines our routing rules. The routing rules defined configure - NGINX to direct requests to https://cafe.example.com/coffee to our coffee-app application, and to use the https - Listener defined in our Gateway resource. -6. When the client connects to https://cafe.example.com/coffee, the request is routed to the coffee-app application - and the communication is secured using the signed keypair contained in the cafe-secret Secret. -7. The certificate will be automatically renewed when it is close to expiry, the Secret will be updated using the new - Certificate, and NGF will dynamically update the keypair on the filesystem used by NGINX for HTTPS termination once - the Secret is updated. - -## Details - -### Step 1 – Deploy cert-manager +1. We deploy cert-manager and create a ClusterIssuer which specifies Let’s Encrypt as our CA and Gateway as our ACME HTTP01 Challenge solver. +1. We create a Gateway resource for our domain (cafe.example.com) and configure cert-manager integration using an annotation. +1. This starts the certificate issuance process – cert-manager contacts Let’s Encrypt to obtain a certificate, and Let’s Encrypt starts the ACME challenge. As part of this challenge, cert-manager creates a temporary HTTPRoute resource which directs the traffic through NGINX Gateway Fabric to verify we control the domain name in the certificate request. +1. Once the domain has been verified, the certificate is issued. Cert-manager stores the keypair in a Kubernetes secret that is referenced by the Gateway resource. As a result, NGINX is configured to terminate HTTPS traffic from clients using this signed keypair. +1. We deploy our application and our HTTPRoute which defines our routing rules. The routing rules defined configure NGINX to direct requests to https://cafe.example.com/coffee to our coffee-app application, and to use the HTTPS Listener defined in our Gateway resource. +1. When the client connects to https://cafe.example.com/coffee, the request is routed to the coffee-app application and the communication is secured using the signed keypair contained in the cafe-secret Secret. +1. The certificate will be automatically renewed when it is close to expiry, the Secret will be updated using the new Certificate, and NGINX Gateway Fabric will dynamically update the keypair on the filesystem used by NGINX for HTTPS termination once the Secret is updated. + +## Securing Traffic + +### Deploy cert-manager The first step is to deploy cert-manager onto the cluster. @@ -77,18 +62,11 @@ The first step is to deploy cert-manager onto the cluster. --set "extraArgs={--feature-gates=ExperimentalGatewayAPISupport=true}" ``` -### Step 2 – Create a ClusterIssuer +### Create a ClusterIssuer -Next we need to create a [ClusterIssuer](https://cert-manager.io/docs/concepts/issuer/), a Kubernetes resource that -represents the certificate authority (CA) that will generate the signed certificates by honouring certificate signing -requests. +Next we need to create a [ClusterIssuer](https://cert-manager.io/docs/concepts/issuer/), a Kubernetes resource that represents the certificate authority (CA) that will generate the signed certificates by honouring certificate signing requests. -We are using the ACME Issuer type, and Let's Encrypt as the CA server. In order for Let's Encypt to verify that we own -the domain a certificate is being requested for, we must complete "challenges". This is to ensure clients are -unable to request certificates for domains they do not own. We will configure the Issuer to use a HTTP01 challenge, and -our Gateway resource that we will create in the next step as the solver. To read more about HTTP01 challenges, see -[here](https://cert-manager.io/docs/configuration/acme/http01/). Use the following YAML definition to create the -resource, but please note the `email` field must be updated to your own email address. +We are using the ACME Issuer type, and Let's Encrypt as the CA server. In order for Let's Encypt to verify that we own the domain a certificate is being requested for, we must complete "challenges". This is to ensure clients are unable to request certificates for domains they do not own. We will configure the Issuer to use a HTTP01 challenge, and our Gateway resource that we will create in the next step as the solver. To read more about HTTP01 challenges, see the [cert-manager documentation](https://cert-manager.io/docs/configuration/acme/http01/). Use the following YAML definition to create the resource, but please note the `email` field must be updated to your own email address. ```yaml apiVersion: cert-manager.io/v1 @@ -105,7 +83,7 @@ spec: privateKeySecretRef: # Secret resource that will be used to store the account's private key. name: issuer-account-key - # Add a single challenge solver, HTTP01 using NGF + # Add a single challenge solver, HTTP01 using NGINX Gateway Fabric solvers: - http01: gatewayHTTPRoute: @@ -115,10 +93,9 @@ spec: kind: Gateway ``` -### Step 3 – Deploy our Gateway with the cert-manager annotation +### Deploy our Gateway with the cert-manager annotation -Next we need to deploy our Gateway. Use can use the below YAML manifest, updating the `spec.listeners[1].hostname` -field to the required value for your environment. +Next we need to deploy our Gateway. You can use the YAML manifest bewlo, updating the `spec.listeners[1].hostname` field to the required value for your environment. ```yaml apiVersion: gateway.networking.k8s.io/v1beta1 @@ -147,22 +124,13 @@ spec: It's worth noting a couple of key details in this manifest: -- The cert-manager annotation is present in the metadata – this enables the cert-manager integration, and tells - cert-manager which ClusterIssuer configuration it should use for the certificates. +- The cert-manager annotation is present in the metadata – this enables the cert-manager integration, and tells cert-manager which ClusterIssuer configuration it should use for the certificates. - There are two Listeners configured, an HTTP Listener on port 80, and an HTTPS Listener on port 443. - - The http Listener on port 80 is required for the HTTP01 ACME challenge to work. This is because as part of the - HTTP01 Challenge, a temporary HTTPRoute will be created by cert-manager to solve the ACME challenge, and this - HTTPRoute requires a Listener on port 80. See the [HTTP01 Gateway API solver documentation](https://cert-manager.io/docs/configuration/acme/http01/#configuring-the-http-01-gateway-api-solver) - for more information. - - The https Listener on port 443 is the Listener we will use in our HTTPRoute in the next step. Cert-manager will - create a Certificate for this Listener block. -- The hostname needs to set to the required value. A new certificate will be issued from the `letsencrypt-prod` - ClusterIssuer for the domain, e.g. "cafe.example.com", once the ACME challenge is successful. - -Once the certificate has been issued, cert-manager will create a Certificate resource on the cluster and the -`cafe-secret` Secret containing the signed keypair in the same Namespace as the Gateway. We can verify the Secret has -been created successfully using `kubectl`. Note it will take a little bit of time for the Challenge to complete and the -Secret to be created: + - The http Listener on port 80 is required for the HTTP01 ACME challenge to work. This is because as part of the HTTP01 Challenge, a temporary HTTPRoute will be created by cert-manager to solve the ACME challenge, and this HTTPRoute requires a Listener on port 80. See the [HTTP01 Gateway API solver documentation](https://cert-manager.io/docs/configuration/acme/http01/#configuring-the-http-01-gateway-api-solver) for more information. + - The https Listener on port 443 is the Listener we will use in our HTTPRoute in the next step. Cert-manager will create a Certificate for this Listener block. +- The hostname needs to set to the required value. A new certificate will be issued from the `letsencrypt-prod` ClusterIssuer for the domain, e.g. "cafe.example.com", once the ACME challenge is successful. + +Once the certificate has been issued, cert-manager will create a Certificate resource on the cluster and the `cafe-secret` Secret containing the signed keypair in the same Namespace as the Gateway. We can verify the Secret has been created successfully using `kubectl`. Note it will take a little bit of time for the Challenge to complete and the Secret to be created: ```shell kubectl get secret cafe-secret @@ -173,9 +141,8 @@ NAME TYPE DATA AGE cafe-secret kubernetes.io/tls 2 20s ``` -### Step 4 – Deploy our application and HTTPRoute -Now we can create our coffee Deployment and Service, and configure the routing rules. You can use the following manifest -to create the Deployment and Service: +### Deploy our application and HTTPRoute +Now we can create our coffee Deployment and Service, and configure the routing rules. You can use the following manifest to create the Deployment and Service: ```yaml apiVersion: apps/v1 @@ -212,8 +179,7 @@ spec: app: coffee ``` -Deploy our HTTPRoute to configure our routing rules for the coffee application. Note the `parentRefs` section in the -spec refers to the Listener configured in the previous step. +Deploy our HTTPRoute to configure our routing rules for the coffee application. Note the `parentRefs` section in the spec refers to the Listener configured in the previous step. ```yaml apiVersion: gateway.networking.k8s.io/v1beta1 @@ -238,14 +204,14 @@ spec: ## Testing -To test everything has worked correctly, we can use curl to the navigate to our endpoint, e.g. -https://cafe.example.com/coffee. To verify using curl, we can use the `-v` option to increase verbosity and inspect the -presented certificate. The output will look something like this: +To test everything has worked correctly, we can use curl to the navigate to our endpoint, for example, https://cafe.example.com/coffee. To verify using curl, we can use the `-v` option to increase verbosity and inspect the presented certificate. ```shell curl https://cafe.example.com/coffee -v ``` +The output will look similar to this: + ```text * Trying 54.195.47.105:443... * Connected to cafe.example.com (54.195.47.105) port 443 (#0) @@ -293,14 +259,10 @@ Request ID: e64c54a2ac253375ac085d48980f000a ## Troubleshooting -- For troubeshooting anything related to the cert-manager installation or Issuer setup, see - [the cert-manager troubleshooting guide](https://cert-manager.io/docs/troubleshooting/). -- For troubleshooting the HTTP01 ACME Challenge, please see the cert-manager - [ACME troubleshooting guide](https://cert-manager.io/docs/troubleshooting/acme/). +- To troubleshoot any issues related to the cert-manager installation or Issuer setup, see [the cert-manager troubleshooting guide](https://cert-manager.io/docs/troubleshooting/). +- To troubleshoot the HTTP01 ACME Challenge, please see the cert-manager [ACME troubleshooting guide](https://cert-manager.io/docs/troubleshooting/acme/). - Note that for the HTTP01 Challenge to work using the Gateway resource, HTTPS redirect must not be configured. - - The temporary HTTPRoute created by cert-manager routes the traffic between cert-manager and the Let's Encrypt server - through NGF. If the Challenge is not successful, it may be useful to inspect the NGINX logs to see the ACME - Challenge requests. You should see something like the following: + - The temporary HTTPRoute created by cert-manager routes the traffic between cert-manager and the Let's Encrypt server through NGINX Gateway Fabric. If the Challenge is not successful, it may be useful to inspect the NGINX logs to see the ACME Challenge requests. You should see something like the following: ```shell kubectl logs -n nginx-gateway -c nginx @@ -318,8 +280,8 @@ Request ID: e64c54a2ac253375ac085d48980f000a ## Links -- Gateway docs: https://gateway-api.sigs.k8s.io -- Cert-manager Gateway usage: https://cert-manager.io/docs/usage/gateway/ -- Cert-manager ACME: https://cert-manager.io/docs/configuration/acme/ -- Let’s Encrypt: https://letsencrypt.org -- NGINX HTTPS docs: https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/ +- [Gateway docs](https://gateway-api.sigs.k8s.io) +- [Cert-manager Gateway usage](https://cert-manager.io/docs/usage/gateway/) +- [Cert-manager ACME](https://cert-manager.io/docs/configuration/acme/) +- [Let’s Encrypt](https://letsencrypt.org) +- [NGINX HTTPS docs](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/) diff --git a/docs/content/guides/routing-traffic-to-your-app.md b/docs/content/guides/routing-traffic-to-your-app.md index 200e8dd826..8b1cecda9d 100644 --- a/docs/content/guides/routing-traffic-to-your-app.md +++ b/docs/content/guides/routing-traffic-to-your-app.md @@ -1,3 +1,11 @@ +--- +title: "Routing Traffic to Your Application" +description: "Learn how to route external traffic to your Kubernetes applications using NGINX Gateway Fabric." +weight: 300 +toc: true +docs: "DOCS-000" +--- + # Routing Traffic to Your Application In this guide, you will learn how to route external traffic to your Kubernetes applications using the Gateway API and diff --git a/docs/content/guides/upgrade-apps-without-downtime.md b/docs/content/guides/upgrade-apps-without-downtime.md index bbc5646fec..dc1fb99c57 100644 --- a/docs/content/guides/upgrade-apps-without-downtime.md +++ b/docs/content/guides/upgrade-apps-without-downtime.md @@ -1,3 +1,11 @@ +--- +title: "Upgrade Applications without Downtime" +description: "Learn how to use NGINX Gateway Fabric to upgrade applications without downtime." +weight: 300 +toc: true +docs: "DOCS-000" +--- + # Using NGINX Gateway Fabric to Upgrade Applications without Downtime This guide explains how to use NGINX Gateway Fabric to upgrade applications without downtime. diff --git a/docs/content/installation.md b/docs/content/installation.md index b42b6f3079..9517c05cc3 100644 --- a/docs/content/installation.md +++ b/docs/content/installation.md @@ -1,23 +1,10 @@ -# Installation - -This guide walks you through how to install NGINX Gateway Fabric on a generic Kubernetes cluster. - -- [Installation](#installation) - - [Prerequisites](#prerequisites) - - [Deploy NGINX Gateway Fabric using Helm](#deploy-nginx-gateway-fabric-using-helm) - - [Deploy NGINX Gateway Fabric from Manifests](#deploy-nginx-gateway-fabric-from-manifests) - - [Expose NGINX Gateway Fabric](#expose-nginx-gateway-fabric) - - [Create a NodePort Service](#create-a-nodeport-service) - - [Create a LoadBalancer Service](#create-a-loadbalancer-service) - - [Upgrading NGINX Gateway Fabric](#upgrading-nginx-gateway-fabric) - - [Upgrade NGINX Gateway Fabric from Manifests](#upgrade-nginx-gateway-fabric-from-manifests) - - [Upgrade NGINX Gateway Fabric using Helm](#upgrade-nginx-gateway-fabric-using-helm) - - [Configure Delayed Termination for Zero Downtime Upgrades](#configure-delayed-termination-for-zero-downtime-upgrades) - - [Configure Delayed Termination Using Manifests](#configure-delayed-termination-using-manifests) - - [Configure Delayed Termination Using Helm](#configure-delayed-termination-using-helm) - - [Uninstalling NGINX Gateway Fabric](#uninstalling-nginx-gateway-fabric) - - [Uninstall NGINX Gateway Fabric from Manifests](#uninstall-nginx-gateway-fabric-from-manifests) - - [Uninstall NGINX Gateway Fabric using Helm](#uninstall-nginx-gateway-fabric-using-helm) +--- +title: "Installation" +description: "Learn how to install NGINX Gateway Fabric on a generic Kubernetes cluster." +weight: 200 +toc: true +docs: "DOCS-000" +--- ## Prerequisites diff --git a/docs/content/monitoring.md b/docs/content/monitoring.md index e2dfe3c264..5aaa78f127 100644 --- a/docs/content/monitoring.md +++ b/docs/content/monitoring.md @@ -1,3 +1,11 @@ +--- +title: "Monitoring" +description: "Learn how to monitor NGINX Gateway Fabric." +weight: 600 +toc: true +docs: "DOCS-000" +--- + # Monitoring The NGINX Gateway Fabric exposes a number of metrics in the [Prometheus](https://prometheus.io/) format. Those diff --git a/docs/content/resource-validation.md b/docs/content/resource-validation.md index 26461e12c2..2c33a6fb47 100644 --- a/docs/content/resource-validation.md +++ b/docs/content/resource-validation.md @@ -1,6 +1,10 @@ -# Gateway API Resource Validation - -This document describes how NGINX Gateway Fabric (NGF) validates Gateway API resources. +--- +title: "Gateway API Resource Validation" +description: "Learn how NGINX Gateway Fabric validates Gateway API resources." +weight: 800 +toc: true +docs: "DOCS-000" +--- ## Overview diff --git a/docs/content/running-on-kind.md b/docs/content/running-on-kind.md index db181b3d15..b20dcd7a55 100644 --- a/docs/content/running-on-kind.md +++ b/docs/content/running-on-kind.md @@ -1,6 +1,10 @@ -# Running on `kind` - -This guide walks you through how to run NGINX Gateway Fabric on a [kind](https://kind.sigs.k8s.io/) cluster. +--- +title: "Running on kind" +description: "Learn how to run NGINX Gateway Fabric on a kind cluster." +weight: 900 +toc: true +docs: "DOCS-000" +--- ## Prerequisites diff --git a/docs/content/troubleshooting.md b/docs/content/troubleshooting.md index 4c97cd4ec3..19370572f4 100644 --- a/docs/content/troubleshooting.md +++ b/docs/content/troubleshooting.md @@ -1,6 +1,10 @@ -# Troubleshooting - -This document contains common or known issues and how to troubleshoot them. +--- +title: "Troubleshooting" +description: "Learn how to troubleshoot common NGINX Gateway Fabric issues." +weight: 1100 +toc: true +docs: "DOCS-000" +--- ## failed to reload NGINX: failed to send the HUP signal to NGINX main: operation not permitted From 19b159d24f62fd2abbd9a5cdf5f783e9e7fc8a9e Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Mon, 6 Nov 2023 18:21:26 +0000 Subject: [PATCH 004/101] fix: update readme for forking process --- docs/readme.md | 57 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/docs/readme.md b/docs/readme.md index 69421ad879..81a8853b47 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -12,15 +12,62 @@ In this directory, you will find the following files: - configuration files for [markdownlint](https://github.com/DavidAnson/markdownlint/) and [markdown-link-check](https://github.com/tcort/markdown-link-check) - a `./config` directory that contains the [Hugo](https://gohugo.io) configuration. -## Git workflow +## Git Guidelines -To contribute to the documentation, create your work branch from the `main` branch. Once you have added your changes, open a PR to merge your changes back to `main`. +- Keep a clean, concise and meaningful git commit history on your branch (within reason), rebasing locally and squashing before submitting a PR. +- Use the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format when writing a commit message, so that changelogs can be automatically generated +- Follow the guidelines of writing a good commit message as described here and summarised in the next few points: + - In the subject line, use the present tense ("Add feature" not "Added feature"). + - In the subject line, use the imperative mood ("Move cursor to..." not "Moves cursor to..."). + - Limit the subject line to 72 characters or less. + - Reference issues and pull requests liberally after the subject line. + - Add more detailed description in the body of the git message (`git commit -a` to give you more space and time in your text editor to write a good message instead of `git commit -am`). -Netlify will create a preview of the documentation and add a link to the GitHub PR page. +### Forking and Pull Requests -Once the preview and the changes have been reviewed, merge the request to the `main` branch. +This repo uses a [forking workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow). Take the steps below to fork the repo, check out a feature branch, and open a pull request with your changes. -> NOTE: If you want to test changes in an environment that emulates the production site before updating `main`, you can push your changes to `docs-development` or `docs-staging`. These branches deploy to the nginx docs [dev](https://docs-dev.nginx.com) and [staging](https://docs-staging.nginx.com) sites, respectively. Ping the NGINX DocOps team in Slack if you need the password to these sites. +1. In the GitHub UI, select the **Fork** button. + + - On the **Create a new fork** page, select the **Owner** (the account where the fork of the repo will be placed). + - Select the **Create fork** button. + +2. If you plan to work on docs in your local development environment, clone your fork. + For example, to clone the repo using SSH, you would run the following command: + + ```shell + git clone git@github.com:/nginx-gateway-fabric.git + ``` + +3. Check out a new feature branch in your fork. This is where you will work on your docs. + + To do this via the command line, you would run the following command: + + ```shell + git checkout -b + ``` + + **CAUTION**: Do not work on the main branch in your fork. This can cause issues when the NGINX Docs team needs to check out your feature branch for editing work. + +4. Make atomic, [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) on your feature branch. + +5. When ready, open a pull request into the **main** branch or a release branch in the **nginxinc/nginx-gateway-fabric** repo. + + - Fill in [our pull request template](https://github.com/nginxinc/nginx-gateway-fabric/blob/main/.github/PULL_REQUEST_TEMPLATE.md) when opening your PR. + - Tag the appropriate reviewers for your subject area. + Technical reviewers should be able to verify that the information provided is accurate. + Documentation reviewers ensure that the content conforms to the NGINX Style Guide, is grammatically correct, and adheres to the NGINX content templates. + +## Release Management and Publishing + +**`Main`** is the default branch in this repo. Main should always be releaseable. +**Do not merge any content into main that is not approved for release.** + +If you are working on content that isn't for a specific release (i.e., it can be published upon completion), open your pull request into the `main` branch. + +### Prepare Content for Future Releases + +If you are working on content for a future release, create a release branch from `main` that uses the naming format *ngf-release-x.y.x* (for example, `ngf-release-4.0.0`). Work on your docs in feature branches off of the release branch. Open pull requests into the release branch when you are ready to merge your work. ## Setup From 59b2668763b23029a39632776de9fbdefaf74e95 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 8 Nov 2023 16:55:07 +0000 Subject: [PATCH 005/101] feat: rename the hugo docs folder to hugo --- docs/README.md | 8 ++++++++ .../developer}/branching-and-workflow.md | 0 .../developer}/design-principles.md | 0 .../developer}/go-style-guide.md | 0 .../developer}/implementing-a-feature.md | 0 .../developer}/logging-guidelines.md | 0 {docs-developer => docs/developer}/pull-request.md | 0 {docs-developer => docs/developer}/quickstart.md | 0 .../developer}/release-process.md | 0 {docs-developer => docs/developer}/testing.md | 0 {docs-proposals => docs/proposals}/README.md | 0 .../proposals}/control-plane-config.md | 0 .../proposals}/data-plane-config.md | 0 {docs-proposals => docs/proposals}/template.md | 0 {docs => hugo}/.gitignore | 0 {docs => hugo}/config/_default/config.toml | 0 {docs => hugo}/config/development/config.toml | 0 {docs => hugo}/config/production/config.toml | 0 {docs => hugo}/config/staging/config.toml | 0 {docs => hugo}/content/_index.md | 0 {docs => hugo}/content/architecture.md | 0 {docs => hugo}/content/building-the-images.md | 0 {docs => hugo}/content/cli-help.md | 0 .../content/control-plane-configuration.md | 0 {docs => hugo}/content/gateway-api-compatibility.md | 0 {docs => hugo}/content/guides/README.md | 0 {docs => hugo}/content/guides/_index.md | 0 {docs => hugo}/content/guides/advanced-routing.md | 0 .../content/guides/integrating-cert-manager.md | 0 .../content/guides/routing-traffic-to-your-app.md | 0 .../content/guides/upgrade-apps-without-downtime.md | 0 {docs => hugo}/content/installation.md | 0 {docs => hugo}/content/monitoring.md | 0 {docs => hugo}/content/resource-validation.md | 0 {docs => hugo}/content/running-on-kind.md | 0 {docs => hugo}/content/troubleshooting.md | 0 {docs => hugo}/go.mod | 2 +- {docs => hugo}/go.sum | 0 {docs => hugo}/makefile | 2 +- {docs => hugo}/md-linkcheck-config.json | 0 {docs => hugo}/mdlint_conf.json | 0 {docs => hugo}/netlify.toml | 2 +- {docs => hugo}/readme.md | 0 {docs => hugo}/static/img/advanced-routing.png | Bin .../static/img/cert-manager-gateway-workflow.png | Bin {docs => hugo}/static/img/code-review-pyramid.jpeg | Bin {docs => hugo}/static/img/ngf-high-level.png | Bin {docs => hugo}/static/img/ngf-pod.png | Bin {docs => hugo}/static/img/route-all-traffic-app.png | Bin .../static/img/route-all-traffic-config.png | Bin .../static/img/route-all-traffic-flow.png | Bin {docs => hugo}/static/img/src/README.md | 0 .../static/img/src/advanced-routing.mermaid | 0 .../static/img/src/route-all-traffic-app.mermaid | 0 .../static/img/src/route-all-traffic-config.mermaid | 0 .../static/img/src/route-all-traffic-flow.mermaid | 0 56 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 docs/README.md rename {docs-developer => docs/developer}/branching-and-workflow.md (100%) rename {docs-developer => docs/developer}/design-principles.md (100%) rename {docs-developer => docs/developer}/go-style-guide.md (100%) rename {docs-developer => docs/developer}/implementing-a-feature.md (100%) rename {docs-developer => docs/developer}/logging-guidelines.md (100%) rename {docs-developer => docs/developer}/pull-request.md (100%) rename {docs-developer => docs/developer}/quickstart.md (100%) rename {docs-developer => docs/developer}/release-process.md (100%) rename {docs-developer => docs/developer}/testing.md (100%) rename {docs-proposals => docs/proposals}/README.md (100%) rename {docs-proposals => docs/proposals}/control-plane-config.md (100%) rename {docs-proposals => docs/proposals}/data-plane-config.md (100%) rename {docs-proposals => docs/proposals}/template.md (100%) rename {docs => hugo}/.gitignore (100%) rename {docs => hugo}/config/_default/config.toml (100%) rename {docs => hugo}/config/development/config.toml (100%) rename {docs => hugo}/config/production/config.toml (100%) rename {docs => hugo}/config/staging/config.toml (100%) rename {docs => hugo}/content/_index.md (100%) rename {docs => hugo}/content/architecture.md (100%) rename {docs => hugo}/content/building-the-images.md (100%) rename {docs => hugo}/content/cli-help.md (100%) rename {docs => hugo}/content/control-plane-configuration.md (100%) rename {docs => hugo}/content/gateway-api-compatibility.md (100%) rename {docs => hugo}/content/guides/README.md (100%) rename {docs => hugo}/content/guides/_index.md (100%) rename {docs => hugo}/content/guides/advanced-routing.md (100%) rename {docs => hugo}/content/guides/integrating-cert-manager.md (100%) rename {docs => hugo}/content/guides/routing-traffic-to-your-app.md (100%) rename {docs => hugo}/content/guides/upgrade-apps-without-downtime.md (100%) rename {docs => hugo}/content/installation.md (100%) rename {docs => hugo}/content/monitoring.md (100%) rename {docs => hugo}/content/resource-validation.md (100%) rename {docs => hugo}/content/running-on-kind.md (100%) rename {docs => hugo}/content/troubleshooting.md (100%) rename {docs => hugo}/go.mod (58%) rename {docs => hugo}/go.sum (100%) rename {docs => hugo}/makefile (96%) rename {docs => hugo}/md-linkcheck-config.json (100%) rename {docs => hugo}/mdlint_conf.json (100%) rename {docs => hugo}/netlify.toml (97%) rename {docs => hugo}/readme.md (100%) rename {docs => hugo}/static/img/advanced-routing.png (100%) rename {docs => hugo}/static/img/cert-manager-gateway-workflow.png (100%) rename {docs => hugo}/static/img/code-review-pyramid.jpeg (100%) rename {docs => hugo}/static/img/ngf-high-level.png (100%) rename {docs => hugo}/static/img/ngf-pod.png (100%) rename {docs => hugo}/static/img/route-all-traffic-app.png (100%) rename {docs => hugo}/static/img/route-all-traffic-config.png (100%) rename {docs => hugo}/static/img/route-all-traffic-flow.png (100%) rename {docs => hugo}/static/img/src/README.md (100%) rename {docs => hugo}/static/img/src/advanced-routing.mermaid (100%) rename {docs => hugo}/static/img/src/route-all-traffic-app.mermaid (100%) rename {docs => hugo}/static/img/src/route-all-traffic-config.mermaid (100%) rename {docs => hugo}/static/img/src/route-all-traffic-flow.mermaid (100%) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..a49748d7b6 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,8 @@ +# NGINX Gateway Fabric Documentation + +This directory contains the developer documentation and the Enhancement proposals relating to NGINX Gateway Fabric. + +## Contents + +- [Developer](developer/): Docs for developers of the project. Contains guides relating to processes and workflows. +- [Proposals](proposals/): Enhancement proposals for new features. \ No newline at end of file diff --git a/docs-developer/branching-and-workflow.md b/docs/developer/branching-and-workflow.md similarity index 100% rename from docs-developer/branching-and-workflow.md rename to docs/developer/branching-and-workflow.md diff --git a/docs-developer/design-principles.md b/docs/developer/design-principles.md similarity index 100% rename from docs-developer/design-principles.md rename to docs/developer/design-principles.md diff --git a/docs-developer/go-style-guide.md b/docs/developer/go-style-guide.md similarity index 100% rename from docs-developer/go-style-guide.md rename to docs/developer/go-style-guide.md diff --git a/docs-developer/implementing-a-feature.md b/docs/developer/implementing-a-feature.md similarity index 100% rename from docs-developer/implementing-a-feature.md rename to docs/developer/implementing-a-feature.md diff --git a/docs-developer/logging-guidelines.md b/docs/developer/logging-guidelines.md similarity index 100% rename from docs-developer/logging-guidelines.md rename to docs/developer/logging-guidelines.md diff --git a/docs-developer/pull-request.md b/docs/developer/pull-request.md similarity index 100% rename from docs-developer/pull-request.md rename to docs/developer/pull-request.md diff --git a/docs-developer/quickstart.md b/docs/developer/quickstart.md similarity index 100% rename from docs-developer/quickstart.md rename to docs/developer/quickstart.md diff --git a/docs-developer/release-process.md b/docs/developer/release-process.md similarity index 100% rename from docs-developer/release-process.md rename to docs/developer/release-process.md diff --git a/docs-developer/testing.md b/docs/developer/testing.md similarity index 100% rename from docs-developer/testing.md rename to docs/developer/testing.md diff --git a/docs-proposals/README.md b/docs/proposals/README.md similarity index 100% rename from docs-proposals/README.md rename to docs/proposals/README.md diff --git a/docs-proposals/control-plane-config.md b/docs/proposals/control-plane-config.md similarity index 100% rename from docs-proposals/control-plane-config.md rename to docs/proposals/control-plane-config.md diff --git a/docs-proposals/data-plane-config.md b/docs/proposals/data-plane-config.md similarity index 100% rename from docs-proposals/data-plane-config.md rename to docs/proposals/data-plane-config.md diff --git a/docs-proposals/template.md b/docs/proposals/template.md similarity index 100% rename from docs-proposals/template.md rename to docs/proposals/template.md diff --git a/docs/.gitignore b/hugo/.gitignore similarity index 100% rename from docs/.gitignore rename to hugo/.gitignore diff --git a/docs/config/_default/config.toml b/hugo/config/_default/config.toml similarity index 100% rename from docs/config/_default/config.toml rename to hugo/config/_default/config.toml diff --git a/docs/config/development/config.toml b/hugo/config/development/config.toml similarity index 100% rename from docs/config/development/config.toml rename to hugo/config/development/config.toml diff --git a/docs/config/production/config.toml b/hugo/config/production/config.toml similarity index 100% rename from docs/config/production/config.toml rename to hugo/config/production/config.toml diff --git a/docs/config/staging/config.toml b/hugo/config/staging/config.toml similarity index 100% rename from docs/config/staging/config.toml rename to hugo/config/staging/config.toml diff --git a/docs/content/_index.md b/hugo/content/_index.md similarity index 100% rename from docs/content/_index.md rename to hugo/content/_index.md diff --git a/docs/content/architecture.md b/hugo/content/architecture.md similarity index 100% rename from docs/content/architecture.md rename to hugo/content/architecture.md diff --git a/docs/content/building-the-images.md b/hugo/content/building-the-images.md similarity index 100% rename from docs/content/building-the-images.md rename to hugo/content/building-the-images.md diff --git a/docs/content/cli-help.md b/hugo/content/cli-help.md similarity index 100% rename from docs/content/cli-help.md rename to hugo/content/cli-help.md diff --git a/docs/content/control-plane-configuration.md b/hugo/content/control-plane-configuration.md similarity index 100% rename from docs/content/control-plane-configuration.md rename to hugo/content/control-plane-configuration.md diff --git a/docs/content/gateway-api-compatibility.md b/hugo/content/gateway-api-compatibility.md similarity index 100% rename from docs/content/gateway-api-compatibility.md rename to hugo/content/gateway-api-compatibility.md diff --git a/docs/content/guides/README.md b/hugo/content/guides/README.md similarity index 100% rename from docs/content/guides/README.md rename to hugo/content/guides/README.md diff --git a/docs/content/guides/_index.md b/hugo/content/guides/_index.md similarity index 100% rename from docs/content/guides/_index.md rename to hugo/content/guides/_index.md diff --git a/docs/content/guides/advanced-routing.md b/hugo/content/guides/advanced-routing.md similarity index 100% rename from docs/content/guides/advanced-routing.md rename to hugo/content/guides/advanced-routing.md diff --git a/docs/content/guides/integrating-cert-manager.md b/hugo/content/guides/integrating-cert-manager.md similarity index 100% rename from docs/content/guides/integrating-cert-manager.md rename to hugo/content/guides/integrating-cert-manager.md diff --git a/docs/content/guides/routing-traffic-to-your-app.md b/hugo/content/guides/routing-traffic-to-your-app.md similarity index 100% rename from docs/content/guides/routing-traffic-to-your-app.md rename to hugo/content/guides/routing-traffic-to-your-app.md diff --git a/docs/content/guides/upgrade-apps-without-downtime.md b/hugo/content/guides/upgrade-apps-without-downtime.md similarity index 100% rename from docs/content/guides/upgrade-apps-without-downtime.md rename to hugo/content/guides/upgrade-apps-without-downtime.md diff --git a/docs/content/installation.md b/hugo/content/installation.md similarity index 100% rename from docs/content/installation.md rename to hugo/content/installation.md diff --git a/docs/content/monitoring.md b/hugo/content/monitoring.md similarity index 100% rename from docs/content/monitoring.md rename to hugo/content/monitoring.md diff --git a/docs/content/resource-validation.md b/hugo/content/resource-validation.md similarity index 100% rename from docs/content/resource-validation.md rename to hugo/content/resource-validation.md diff --git a/docs/content/running-on-kind.md b/hugo/content/running-on-kind.md similarity index 100% rename from docs/content/running-on-kind.md rename to hugo/content/running-on-kind.md diff --git a/docs/content/troubleshooting.md b/hugo/content/troubleshooting.md similarity index 100% rename from docs/content/troubleshooting.md rename to hugo/content/troubleshooting.md diff --git a/docs/go.mod b/hugo/go.mod similarity index 58% rename from docs/go.mod rename to hugo/go.mod index 3a8e1f7cdc..6f00174f46 100644 --- a/docs/go.mod +++ b/hugo/go.mod @@ -1,4 +1,4 @@ -module github.com/nginxinc/nalb-shared/docs +module github.com/nginxinc/nginx-gateway-fabric/hugo go 1.18 diff --git a/docs/go.sum b/hugo/go.sum similarity index 100% rename from docs/go.sum rename to hugo/go.sum diff --git a/docs/makefile b/hugo/makefile similarity index 96% rename from docs/makefile rename to hugo/makefile index 99234752ae..e664ca3d14 100644 --- a/docs/makefile +++ b/hugo/makefile @@ -38,7 +38,7 @@ ifeq (, $(shell docker version 2> /dev/null)) ifneq (, $(shell $(NETLIFY) "true")) $(error Docker and markdown-link-check are not installed. markdown-link-check or Docker are required to check links.) else - MARKDOWNLINKCHECK=docker run --rm -it -v ${CURDIR}:/docs --workdir /docs ${MARKDOWNLINKCHECK_IMG} + MARKDOWNLINKCHECK=docker run --rm -it -v ${CURDIR}:/hugo --workdir /hugo ${MARKDOWNLINKCHECK_IMG} endif endif endif diff --git a/docs/md-linkcheck-config.json b/hugo/md-linkcheck-config.json similarity index 100% rename from docs/md-linkcheck-config.json rename to hugo/md-linkcheck-config.json diff --git a/docs/mdlint_conf.json b/hugo/mdlint_conf.json similarity index 100% rename from docs/mdlint_conf.json rename to hugo/mdlint_conf.json diff --git a/docs/netlify.toml b/hugo/netlify.toml similarity index 97% rename from docs/netlify.toml rename to hugo/netlify.toml index 50268abba0..df9ffd6bf5 100644 --- a/docs/netlify.toml +++ b/hugo/netlify.toml @@ -1,5 +1,5 @@ [build] - base = "docs/" + base = "hugo/" publish = "public" [context.production] diff --git a/docs/readme.md b/hugo/readme.md similarity index 100% rename from docs/readme.md rename to hugo/readme.md diff --git a/docs/static/img/advanced-routing.png b/hugo/static/img/advanced-routing.png similarity index 100% rename from docs/static/img/advanced-routing.png rename to hugo/static/img/advanced-routing.png diff --git a/docs/static/img/cert-manager-gateway-workflow.png b/hugo/static/img/cert-manager-gateway-workflow.png similarity index 100% rename from docs/static/img/cert-manager-gateway-workflow.png rename to hugo/static/img/cert-manager-gateway-workflow.png diff --git a/docs/static/img/code-review-pyramid.jpeg b/hugo/static/img/code-review-pyramid.jpeg similarity index 100% rename from docs/static/img/code-review-pyramid.jpeg rename to hugo/static/img/code-review-pyramid.jpeg diff --git a/docs/static/img/ngf-high-level.png b/hugo/static/img/ngf-high-level.png similarity index 100% rename from docs/static/img/ngf-high-level.png rename to hugo/static/img/ngf-high-level.png diff --git a/docs/static/img/ngf-pod.png b/hugo/static/img/ngf-pod.png similarity index 100% rename from docs/static/img/ngf-pod.png rename to hugo/static/img/ngf-pod.png diff --git a/docs/static/img/route-all-traffic-app.png b/hugo/static/img/route-all-traffic-app.png similarity index 100% rename from docs/static/img/route-all-traffic-app.png rename to hugo/static/img/route-all-traffic-app.png diff --git a/docs/static/img/route-all-traffic-config.png b/hugo/static/img/route-all-traffic-config.png similarity index 100% rename from docs/static/img/route-all-traffic-config.png rename to hugo/static/img/route-all-traffic-config.png diff --git a/docs/static/img/route-all-traffic-flow.png b/hugo/static/img/route-all-traffic-flow.png similarity index 100% rename from docs/static/img/route-all-traffic-flow.png rename to hugo/static/img/route-all-traffic-flow.png diff --git a/docs/static/img/src/README.md b/hugo/static/img/src/README.md similarity index 100% rename from docs/static/img/src/README.md rename to hugo/static/img/src/README.md diff --git a/docs/static/img/src/advanced-routing.mermaid b/hugo/static/img/src/advanced-routing.mermaid similarity index 100% rename from docs/static/img/src/advanced-routing.mermaid rename to hugo/static/img/src/advanced-routing.mermaid diff --git a/docs/static/img/src/route-all-traffic-app.mermaid b/hugo/static/img/src/route-all-traffic-app.mermaid similarity index 100% rename from docs/static/img/src/route-all-traffic-app.mermaid rename to hugo/static/img/src/route-all-traffic-app.mermaid diff --git a/docs/static/img/src/route-all-traffic-config.mermaid b/hugo/static/img/src/route-all-traffic-config.mermaid similarity index 100% rename from docs/static/img/src/route-all-traffic-config.mermaid rename to hugo/static/img/src/route-all-traffic-config.mermaid diff --git a/docs/static/img/src/route-all-traffic-flow.mermaid b/hugo/static/img/src/route-all-traffic-flow.mermaid similarity index 100% rename from docs/static/img/src/route-all-traffic-flow.mermaid rename to hugo/static/img/src/route-all-traffic-flow.mermaid From fe9eca34c206b2cb40e7f5946dda52f75b823e5f Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 9 Nov 2023 14:12:07 +0000 Subject: [PATCH 006/101] feat: update doc, move dev docs picture --- .../images}/code-review-pyramid.jpeg | Bin hugo/content/architecture.md | 4 +- .../guides/routing-traffic-to-your-app.md | 118 ++++++------------ 3 files changed, 38 insertions(+), 84 deletions(-) rename {hugo/static/img => docs/images}/code-review-pyramid.jpeg (100%) diff --git a/hugo/static/img/code-review-pyramid.jpeg b/docs/images/code-review-pyramid.jpeg similarity index 100% rename from hugo/static/img/code-review-pyramid.jpeg rename to docs/images/code-review-pyramid.jpeg diff --git a/hugo/content/architecture.md b/hugo/content/architecture.md index ae21518018..7bf424e175 100644 --- a/hugo/content/architecture.md +++ b/hugo/content/architecture.md @@ -38,7 +38,7 @@ To start, let's take a high-level look at the NGINX Gateway Fabric (NGF). The ac example scenario where NGF exposes two web applications hosted within a Kubernetes cluster to external clients on the internet: -![NGF High Level](/docs/images/ngf-high-level.png) +![NGF High Level](/img/ngf-high-level.png) The figure shows: @@ -97,7 +97,7 @@ The diagram below provides a visual representation of the interactions between p `nginx-gateway` containers, as well as external processes/entities. It showcases the connections and relationships between these components. -![NGF pod](/docs/images/ngf-pod.png) +![NGF pod](/img/ngf-pod.png) The following list provides a description of each connection, along with its corresponding type indicated in parentheses. To enhance readability, the suffix "process" has been omitted from the process descriptions below. diff --git a/hugo/content/guides/routing-traffic-to-your-app.md b/hugo/content/guides/routing-traffic-to-your-app.md index 8b1cecda9d..a2a636d491 100644 --- a/hugo/content/guides/routing-traffic-to-your-app.md +++ b/hugo/content/guides/routing-traffic-to-your-app.md @@ -6,17 +6,12 @@ toc: true docs: "DOCS-000" --- -# Routing Traffic to Your Application - -In this guide, you will learn how to route external traffic to your Kubernetes applications using the Gateway API and -NGINX Gateway Fabric. Whether you're managing a web application or a REST backend API, you can use NGINX Gateway -Fabric to expose your application outside the cluster. +In this guide you will learn how to route external traffic to your Kubernetes applications using the Gateway API and NGINX Gateway Fabric. Whether you're managing a web application or a REST backend API, you can use NGINX Gateway Fabric to expose your application outside the cluster. ## Prerequisites -- [Install](/docs/installation.md) NGINX Gateway Fabric. -- [Expose NGINX Gateway Fabric](/docs/installation.md#expose-nginx-gateway-fabric) and save the public IP - address and port of NGINX Gateway Fabric into shell variables: +- [Install]({{< relref "/installation.md" >}}) NGINX Gateway Fabric. +- [Expose NGINX Gateway Fabric]({{< relref "/installation.md#expose-nginx-gateway-fabric" >}}) and save the public IP address and port of NGINX Gateway Fabric into shell variables: ```text GW_IP=XXX.YYY.ZZZ.III @@ -27,16 +22,13 @@ Fabric to expose your application outside the cluster. The application we are going to use in this guide is a simple coffee application comprised of one Service and two Pods: -![coffee app](/docs/images/route-all-traffic-app.png) +![coffee app](/img/route-all-traffic-app.png) + +Using this architecture, the coffee application is not accessible outside the cluster. We want to expose this application on the hostname `cafe.example.com` so that clients outside the cluster can access it. -With this architecture, the coffee application is not accessible outside the cluster. We want to expose this application -on the hostname `cafe.example.com` so that clients outside the cluster can access it. +To do this, we will install NGINX Gateway Fabric and create two Gateway API resources: a [Gateway](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway) and an [HTTPRoute](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute). -To do this, we will install NGINX Gateway Fabric and create two Gateway API resources: -a [Gateway](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway) and -an [HTTPRoute](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute). -With these resources, we will configure a simple routing rule to match all HTTP traffic with the -hostname `cafe.example.com` and route it to the coffee Service. +With these resources, we will configure a simple routing rule to match all HTTP traffic with the hostname `cafe.example.com` and route it to the coffee Service. ## Setup @@ -79,8 +71,7 @@ spec: EOF ``` -This will create the coffee Service and a Deployment with two Pods. Run the following command to verify the resources -were created: +This will create the coffee Service and a Deployment with two Pods. Run the following command to verify the resources were created: ```shell kubectl get pods,svc @@ -100,31 +91,23 @@ service/coffee ClusterIP 10.96.75.77 80/TCP 77s ## Application Architecture with NGINX Gateway Fabric -To route traffic to the coffee application, we will create a Gateway and HTTPRoute. The following diagram shows the -configuration we'll be creating in the next step: +To route traffic to the coffee application, we will create a Gateway and HTTPRoute. The following diagram shows the configuration we'll be creating in the next step: + +![Configuration](/img/route-all-traffic-config.png) -![Configuration](/docs/images/route-all-traffic-config.png) +We need a Gateway to create an entry point for HTTP traffic coming into the cluster. The `cafe` Gateway we are going to create will open an entry point to the cluster on port 80 for HTTP traffic. -We need a Gateway to create an entry point for HTTP traffic coming into the cluster. The `cafe` Gateway we are going to -create will open an entry point to the cluster on port 80 for HTTP traffic. +To route HTTP traffic from the Gateway to the coffee Service, we need to create an HTTPRoute named `coffee` and attach to the Gateway. This HTTPRoute will have a single routing rule that routes all traffic to the hostname `cafe.example.com` from the Gateway to the coffee Service. -To route HTTP traffic from the Gateway to the coffee Service, we need to create an HTTPRoute named `coffee` and attach -to the Gateway. This HTTPRoute will have a single routing rule that routes all traffic to the -hostname `cafe.example.com` from the Gateway to the coffee Service. +Once NGINX Gateway Fabric processes the `cafe` Gateway and `coffee` HTTPRoute, it will configure its dataplane, NGINX, to route all HTTP requests to `cafe.example.com` to the Pods that the `coffee` Service targets: -Once NGINX Gateway Fabric processes the `cafe` Gateway and `coffee` HTTPRoute, it will configure its dataplane, NGINX, -to route all HTTP requests to `cafe.example.com` to the Pods that the `coffee` Service targets: +![Traffic Flow](/img/route-all-traffic-flow.png) -![Traffic Flow](/docs/images/route-all-traffic-flow.png) +The coffee Service is omitted from the diagram above because the NGINX Gateway Fabric routes directly to the Pods that the coffee Service targets. -The coffee Service is omitted from the diagram above because the NGINX Gateway Fabric routes directly to the Pods -that the coffee Service targets. +{{< note >}}In the diagrams above, all resources that are the responsibility of the cluster operator are shown in green. The orange resources are the responsibility of the application developers. -> **Note** -> In the diagrams above, all resources that are the responsibility of the cluster operator are shown in green. -> The orange resources are the responsibility of the application developers. -> See the [roles and personas](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1) -> Gateway API document for more information on these roles. +See the [roles and personas](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1) Gateway API document for more information on these roles.{{< /note >}} ## Create the Gateway API Resources @@ -145,22 +128,14 @@ spec: EOF ``` -This Gateway is associated with the NGINX Gateway Fabric through the `gatewayClassName` field. The default -installation of NGINX Gateway Fabric creates a GatewayClass with the name `nginx`. NGINX Gateway Fabric will -only configure Gateways with a `gatewayClassName` of `nginx` unless you change the name via the `--gatewayclass` -[command-line flag](/docs/cli-help.md#static-mode). +This Gateway is associated with the NGINX Gateway Fabric through the `gatewayClassName` field. The default installation of NGINX Gateway Fabric creates a GatewayClass with the name `nginx`. NGINX Gateway Fabric will only configure Gateways with a `gatewayClassName` of `nginx` unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). -We specify -a [listener](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on -the Gateway to open an entry point on the cluster. In this case, since the coffee application accepts HTTP requests, we -create an HTTP listener, named `http`, that listens on port 80. +We specify a [listener](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on the Gateway to open an entry point on the cluster. In this case, since the coffee application accepts HTTP requests, we create an HTTP listener, named `http`, that listens on port 80. -By default, Gateways only allow routes (such as HTTPRoutes) to attach if they are in the same namespace as the Gateway. -If you want to change this behavior, you can set -the [`allowedRoutes`](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.AllowedRoutes) -field. +By default, Gateways only allow routes (such as HTTPRoutes) to attach if they are in the same namespace as the Gateway. If you want to change this behavior, you can set +the [`allowedRoutes`](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.AllowedRoutes) field. -Now, let's create the HTTPRoute by copying and pasting the following into your terminal: +Next you will create the HTTPRoute by copying and pasting the following into your terminal: ```yaml kubectl apply -f - < **Note** -> Your clients should be able to resolve the domain name `cafe.example.com` to the public IP of the -> NGINX Gateway Fabric. In this guide we will simulate that using curl's `--resolve` option. +{{< note >}}Your clients should be able to resolve the domain name `cafe.example.com` to the public IP of the NGINX Gateway Fabric. In this guide we will simulate that using curl's `--resolve` option. {{< /note >}} First, let's send a request to the path `/`: @@ -222,8 +185,7 @@ Server address: 10.12.0.18:8080 Server name: coffee-7dd75bc79b-cqvb7 ``` -Since the `cafe` HTTPRoute routes all traffic on any path to the coffee application, the following requests should also -be handled by the coffee Pods: +Since the `cafe` HTTPRoute routes all traffic on any path to the coffee application, the following requests should also be handled by the coffee Pods: ```shell curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/some-path @@ -243,9 +205,7 @@ Server address: 10.12.0.19:8080 Server name: coffee-7dd75bc79b-dett3 ``` -Requests to hostnames other than `cafe.example.com` should _not_ be routed to the coffee application, since the `cafe` -HTTPRoute only matches requests with the `cafe.example.com` hostname. To verify this, send a request to the hostname -`pub.example.com`: +Requests to hostnames other than `cafe.example.com` should _not_ be routed to the coffee application, since the `cafe` HTTPRoute only matches requests with the `cafe.example.com` hostname. To verify this, send a request to the hostname `pub.example.com`: ```shell curl --resolve pub.example.com:$GW_PORT:$GW_IP http://pub.example.com:$GW_PORT/ @@ -267,8 +227,7 @@ You should receive a 404 Not Found error: If you have any issues while testing the configuration, try the following to debug your configuration and setup: -- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric - Service. Instructions for finding those values are [here](/docs/installation.md#expose-nginx-gateway-fabric). +- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are in the [installation documentation]({{< relref "/installation.md#expose-nginx-gateway-fabric" >}}). - Check the status of the Gateway: @@ -326,8 +285,7 @@ If you have any issues while testing the configuration, try the following to deb Name: http ``` - Check that the conditions match and that the attached routes for the `http` listener equals 1. If it is 0, there may - be an issue with the HTTPRoute. + Check that the conditions match and that the attached routes for the `http` listener equals 1. If it is 0, there may be an issue with the HTTPRoute. - Check the status of the HTTPRoute: @@ -369,8 +327,7 @@ If you have any issues while testing the configuration, try the following to deb kubectl exec -it -n nginx-gateway -c nginx -- nginx -T ``` - The config should contain a server block with the server name `cafe.example.com` that listens on port 80. This - server block should have a single location `/` that proxy passes to the coffee upstream: + The config should contain a server block with the server name `cafe.example.com` that listens on port 80. This server block should have a single location `/` that proxy passes to the coffee upstream: ```nginx configuration server { @@ -386,8 +343,7 @@ If you have any issues while testing the configuration, try the following to deb } ``` - There should also be an upstream block with a name that matches the upstream in the `proxy_pass` directive. This - upstream block should contain the Pod IPs of the coffee Pods: + There should also be an upstream block with a name that matches the upstream in the `proxy_pass` directive. This upstream block should contain the Pod IPs of the coffee Pods: ```nginx configuration upstream default_coffee_80 { @@ -398,9 +354,7 @@ If you have any issues while testing the configuration, try the following to deb } ``` - > **Note** - > The entire configuration is not shown because it is subject to change. - > Ellipses indicate that there's configuration not shown. +{{< note >}}The entire configuration is not shown because it is subject to change. Ellipses indicate that there's configuration not shown.{{< /note >}} If your issue persists, [contact us](https://github.com/nginxinc/nginx-gateway-fabric#contacts). From 0208b09ddbec8998516ac5222e12d427ea165b51 Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Thu, 9 Nov 2023 15:28:53 +0000 Subject: [PATCH 007/101] Update docs/README.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index a49748d7b6..9c6d957572 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,4 +5,4 @@ This directory contains the developer documentation and the Enhancement proposal ## Contents - [Developer](developer/): Docs for developers of the project. Contains guides relating to processes and workflows. -- [Proposals](proposals/): Enhancement proposals for new features. \ No newline at end of file +- [Proposals](proposals/): Enhancement proposals for new features. From 4c1f5bc63fc363a7cfafea62fcf5713ce6ae2b81 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 9 Nov 2023 15:33:30 +0000 Subject: [PATCH 008/101] fix: update go version. nits --- hugo/{makefile => Makefile} | 0 hugo/go.mod | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename hugo/{makefile => Makefile} (100%) diff --git a/hugo/makefile b/hugo/Makefile similarity index 100% rename from hugo/makefile rename to hugo/Makefile diff --git a/hugo/go.mod b/hugo/go.mod index 6f00174f46..dac2d808b5 100644 --- a/hugo/go.mod +++ b/hugo/go.mod @@ -1,5 +1,5 @@ module github.com/nginxinc/nginx-gateway-fabric/hugo -go 1.18 +go 1.21 require github.com/nginxinc/nginx-hugo-theme v0.39.0 // indirect From b8f155f7374b44a6f15f5ce88920f5c7590be11c Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 9 Nov 2023 15:34:20 +0000 Subject: [PATCH 009/101] fix: update filename --- hugo/{readme.md => README.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename hugo/{readme.md => README.md} (100%) diff --git a/hugo/readme.md b/hugo/README.md similarity index 100% rename from hugo/readme.md rename to hugo/README.md From 5e234ef321a5c4803fc743b9064fbb922223110f Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 9 Nov 2023 16:19:49 +0000 Subject: [PATCH 010/101] fix: update README --- hugo/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hugo/README.md b/hugo/README.md index 81a8853b47..38c71c4d0a 100644 --- a/hugo/README.md +++ b/hugo/README.md @@ -51,7 +51,7 @@ This repo uses a [forking workflow](https://www.atlassian.com/git/tutorials/comp 4. Make atomic, [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) on your feature branch. -5. When ready, open a pull request into the **main** branch or a release branch in the **nginxinc/nginx-gateway-fabric** repo. +5. When ready, open a pull request into the **main** branch in the **nginxinc/nginx-gateway-fabric** repo. - Fill in [our pull request template](https://github.com/nginxinc/nginx-gateway-fabric/blob/main/.github/PULL_REQUEST_TEMPLATE.md) when opening your PR. - Tag the appropriate reviewers for your subject area. @@ -60,14 +60,14 @@ This repo uses a [forking workflow](https://www.atlassian.com/git/tutorials/comp ## Release Management and Publishing -**`Main`** is the default branch in this repo. Main should always be releaseable. -**Do not merge any content into main that is not approved for release.** +**`Main`** is the default branch in this repo. All the latest content updates are merged into this branch. -If you are working on content that isn't for a specific release (i.e., it can be published upon completion), open your pull request into the `main` branch. +The documentation is published from the latest public release branch, (for example, `ngf-release-4.0.0`). Work on your docs in feature branches off of the main branch. Open pull requests into the `main` when you are ready to merge your work. -### Prepare Content for Future Releases +If you are working on content for immediate publication in the docs site, cherrypick your changes to the current public release branch. + +If you are working on content for a future release, cherrypick your changes to the appropriate upcoming release branch. -If you are working on content for a future release, create a release branch from `main` that uses the naming format *ngf-release-x.y.x* (for example, `ngf-release-4.0.0`). Work on your docs in feature branches off of the release branch. Open pull requests into the release branch when you are ready to merge your work. ## Setup From 2cb56594d8d7dbd8cca9df909bb0b0b8d496ea0f Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 9 Nov 2023 16:26:55 +0000 Subject: [PATCH 011/101] fix: README update --- hugo/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hugo/README.md b/hugo/README.md index 38c71c4d0a..e0527fc00a 100644 --- a/hugo/README.md +++ b/hugo/README.md @@ -62,11 +62,11 @@ This repo uses a [forking workflow](https://www.atlassian.com/git/tutorials/comp **`Main`** is the default branch in this repo. All the latest content updates are merged into this branch. -The documentation is published from the latest public release branch, (for example, `ngf-release-4.0.0`). Work on your docs in feature branches off of the main branch. Open pull requests into the `main` when you are ready to merge your work. +The documentation is published from the latest public release branch, (for example, `release-4.0`). Work on your docs in feature branches off of the main branch. Open pull requests into the `main` when you are ready to merge your work. If you are working on content for immediate publication in the docs site, cherrypick your changes to the current public release branch. -If you are working on content for a future release, cherrypick your changes to the appropriate upcoming release branch. +If you are working on content for a future release, make sure that you **do not** cherrypick them to the current public release branch, as this will publish them automatically. ## Setup From 62b9459d66cc2c4cbffddb153672f4b2bbed466b Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Thu, 9 Nov 2023 10:49:33 -0800 Subject: [PATCH 012/101] organzied the NGF docs into diataxis IA --- hugo/content/concepts/_index.md | 9 +++++++++ hugo/content/{ => concepts}/architecture.md | 2 +- .../{ => concepts}/gateway-api-compatibility.md | 0 hugo/content/{ => concepts}/resource-validation.md | 0 hugo/content/guides/README.md | 14 -------------- hugo/content/{guides => how-to}/_index.md | 0 hugo/content/how-to/configuration/_index.md | 9 +++++++++ .../configuration}/control-plane-configuration.md | 2 +- hugo/content/how-to/installation/_index.md | 9 +++++++++ .../installation}/building-the-images.md | 4 ++-- .../{ => how-to/installation}/installation.md | 4 ++-- .../{ => how-to/installation}/running-on-kind.md | 4 ++-- hugo/content/how-to/maintenance/_index.md | 9 +++++++++ .../maintenance}/upgrade-apps-without-downtime.md | 2 +- hugo/content/how-to/monitoring/_index.md | 9 +++++++++ hugo/content/{ => how-to/monitoring}/monitoring.md | 2 +- .../{ => how-to/monitoring}/troubleshooting.md | 2 +- hugo/content/how-to/traffic-management/_index.md | 9 +++++++++ .../traffic-management}/advanced-routing.md | 10 +++++----- .../integrating-cert-manager.md | 4 ++-- .../routing-traffic-to-your-app.md | 8 ++++---- hugo/content/reference/_index.md | 8 ++++++++ hugo/content/{ => reference}/cli-help.md | 2 +- 23 files changed, 85 insertions(+), 37 deletions(-) create mode 100644 hugo/content/concepts/_index.md rename hugo/content/{ => concepts}/architecture.md (98%) rename hugo/content/{ => concepts}/gateway-api-compatibility.md (100%) rename hugo/content/{ => concepts}/resource-validation.md (100%) delete mode 100644 hugo/content/guides/README.md rename hugo/content/{guides => how-to}/_index.md (100%) create mode 100644 hugo/content/how-to/configuration/_index.md rename hugo/content/{ => how-to/configuration}/control-plane-configuration.md (99%) create mode 100644 hugo/content/how-to/installation/_index.md rename hugo/content/{ => how-to/installation}/building-the-images.md (96%) rename hugo/content/{ => how-to/installation}/installation.md (99%) rename hugo/content/{ => how-to/installation}/running-on-kind.md (87%) create mode 100644 hugo/content/how-to/maintenance/_index.md rename hugo/content/{guides => how-to/maintenance}/upgrade-apps-without-downtime.md (99%) create mode 100644 hugo/content/how-to/monitoring/_index.md rename hugo/content/{ => how-to/monitoring}/monitoring.md (99%) rename hugo/content/{ => how-to/monitoring}/troubleshooting.md (98%) create mode 100644 hugo/content/how-to/traffic-management/_index.md rename hugo/content/{guides => how-to/traffic-management}/advanced-routing.md (96%) rename hugo/content/{guides => how-to/traffic-management}/integrating-cert-manager.md (99%) rename hugo/content/{guides => how-to/traffic-management}/routing-traffic-to-your-app.md (97%) create mode 100644 hugo/content/reference/_index.md rename hugo/content/{ => reference}/cli-help.md (99%) diff --git a/hugo/content/concepts/_index.md b/hugo/content/concepts/_index.md new file mode 100644 index 0000000000..7123cf5096 --- /dev/null +++ b/hugo/content/concepts/_index.md @@ -0,0 +1,9 @@ +--- +title: "Concepts" +description: +weight: 100 +linkTitle: "Concepts" +menu: + docs: + parent: NGINX Gateway Fabric +--- \ No newline at end of file diff --git a/hugo/content/architecture.md b/hugo/content/concepts/architecture.md similarity index 98% rename from hugo/content/architecture.md rename to hugo/content/concepts/architecture.md index 7bf424e175..dd316ba85b 100644 --- a/hugo/content/architecture.md +++ b/hugo/content/concepts/architecture.md @@ -47,7 +47,7 @@ The figure shows: cluster through the Kubernetes API by creating Kubernetes objects. - *Clients A* and *Clients B* connect to *Applications A* and *B*, respectively. This applications have been deployed by the corresponding users. -- The *NGF Pod*, [deployed by *Cluster Operator*](/docs/installation.md) in the Namespace *nginx-gateway*. For +- The *NGF Pod*, [deployed by *Cluster Operator*](/docs/how-to/installation/installation.md) in the Namespace *nginx-gateway*. For scalability and availability, you can have multiple replicas. This Pod consists of two containers: `NGINX` and `NGF`. The *NGF* container interacts with the Kubernetes API to retrieve the most up-to-date Gateway API resources created within the cluster. It then dynamically configures the *NGINX* container based on these resources, ensuring proper diff --git a/hugo/content/gateway-api-compatibility.md b/hugo/content/concepts/gateway-api-compatibility.md similarity index 100% rename from hugo/content/gateway-api-compatibility.md rename to hugo/content/concepts/gateway-api-compatibility.md diff --git a/hugo/content/resource-validation.md b/hugo/content/concepts/resource-validation.md similarity index 100% rename from hugo/content/resource-validation.md rename to hugo/content/concepts/resource-validation.md diff --git a/hugo/content/guides/README.md b/hugo/content/guides/README.md deleted file mode 100644 index 6869f98838..0000000000 --- a/hugo/content/guides/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Guides - -This directory contains guides for configuring NGINX Gateway Fabric for various use cases. - -## Contents - -- [Routing Traffic to Your Application](routing-traffic-to-your-app.md): How to use NGINX Gateway Fabric to route - all Ingress traffic to your Kubernetes application. -- [Routing to Applications Using HTTP Matching Conditions](advanced-routing.md): Guide on how to deploy multiple - applications and HTTPRoutes with request conditions such as paths, methods, headers, and query parameters. -- [Securing Traffic using Let's Encrypt and Cert-Manager](integrating-cert-manager.md): Shows how to secure - traffic from clients to NGINX Gateway Fabric with TLS using Let's Encrypt and Cert-Manager. -- [Using NGINX Gateway Fabric to Upgrade Applications without Downtime](upgrade-apps-without-downtime.md): - Explains how to use NGINX Gateway Fabric to upgrade applications without downtime. diff --git a/hugo/content/guides/_index.md b/hugo/content/how-to/_index.md similarity index 100% rename from hugo/content/guides/_index.md rename to hugo/content/how-to/_index.md diff --git a/hugo/content/how-to/configuration/_index.md b/hugo/content/how-to/configuration/_index.md new file mode 100644 index 0000000000..a6cf76daeb --- /dev/null +++ b/hugo/content/how-to/configuration/_index.md @@ -0,0 +1,9 @@ +--- +title: "Configuration" +description: +weight: 200 +linkTitle: "Configuration" +menu: + docs: + parent: How-To Guides +--- \ No newline at end of file diff --git a/hugo/content/control-plane-configuration.md b/hugo/content/how-to/configuration/control-plane-configuration.md similarity index 99% rename from hugo/content/control-plane-configuration.md rename to hugo/content/how-to/configuration/control-plane-configuration.md index 6124785586..d300b76ba1 100644 --- a/hugo/content/control-plane-configuration.md +++ b/hugo/content/how-to/configuration/control-plane-configuration.md @@ -1,7 +1,7 @@ --- title: "Control Plane Configuration" description: "Learn how to dynamically update the NGINX Gateway Fabric control plane configuration." -weight: 500 +weight: 100 toc: true docs: "DOCS-000" --- diff --git a/hugo/content/how-to/installation/_index.md b/hugo/content/how-to/installation/_index.md new file mode 100644 index 0000000000..4813f80328 --- /dev/null +++ b/hugo/content/how-to/installation/_index.md @@ -0,0 +1,9 @@ +--- +title: "Initial Setup" +description: +weight: 100 +linkTitle: "Initial Setup" +menu: + docs: + parent: How-To Guides +--- \ No newline at end of file diff --git a/hugo/content/building-the-images.md b/hugo/content/how-to/installation/building-the-images.md similarity index 96% rename from hugo/content/building-the-images.md rename to hugo/content/how-to/installation/building-the-images.md index fb412c3c6f..78370c3dca 100644 --- a/hugo/content/building-the-images.md +++ b/hugo/content/how-to/installation/building-the-images.md @@ -1,7 +1,7 @@ --- -title: "Building the Images" +title: "Building NGINX Gateway Fabric and NGINX Images" description: "Learn how to build the NGINX Gateway Fabric and NGINX images." -weight: 300 +weight: 200 toc: true docs: "DOCS-000" --- diff --git a/hugo/content/installation.md b/hugo/content/how-to/installation/installation.md similarity index 99% rename from hugo/content/installation.md rename to hugo/content/how-to/installation/installation.md index 9517c05cc3..e94132bb07 100644 --- a/hugo/content/installation.md +++ b/hugo/content/how-to/installation/installation.md @@ -1,7 +1,7 @@ --- -title: "Installation" +title: "Installing NGINX Gateway Fabric" description: "Learn how to install NGINX Gateway Fabric on a generic Kubernetes cluster." -weight: 200 +weight: 100 toc: true docs: "DOCS-000" --- diff --git a/hugo/content/running-on-kind.md b/hugo/content/how-to/installation/running-on-kind.md similarity index 87% rename from hugo/content/running-on-kind.md rename to hugo/content/how-to/installation/running-on-kind.md index b20dcd7a55..01e6cf7346 100644 --- a/hugo/content/running-on-kind.md +++ b/hugo/content/how-to/installation/running-on-kind.md @@ -1,7 +1,7 @@ --- title: "Running on kind" description: "Learn how to run NGINX Gateway Fabric on a kind cluster." -weight: 900 +weight: 300 toc: true docs: "DOCS-000" --- @@ -23,7 +23,7 @@ make create-kind-cluster ## Deploy NGINX Gateway Fabric -Follow the [installation](./installation.md) instructions to deploy NGINX Gateway Fabric on your Kind cluster. +Follow the [installation](./how-to/installation/installation.md) instructions to deploy NGINX Gateway Fabric on your Kind cluster. ## Access NGINX Gateway Fabric diff --git a/hugo/content/how-to/maintenance/_index.md b/hugo/content/how-to/maintenance/_index.md new file mode 100644 index 0000000000..39996db6dc --- /dev/null +++ b/hugo/content/how-to/maintenance/_index.md @@ -0,0 +1,9 @@ +--- +title: "Maintenance and Upgrades" +description: +weight: 400 +linkTitle: "Maintenance and Upgrades" +menu: + docs: + parent: How-To Guides +--- \ No newline at end of file diff --git a/hugo/content/guides/upgrade-apps-without-downtime.md b/hugo/content/how-to/maintenance/upgrade-apps-without-downtime.md similarity index 99% rename from hugo/content/guides/upgrade-apps-without-downtime.md rename to hugo/content/how-to/maintenance/upgrade-apps-without-downtime.md index dc1fb99c57..a30c69cad6 100644 --- a/hugo/content/guides/upgrade-apps-without-downtime.md +++ b/hugo/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -1,7 +1,7 @@ --- title: "Upgrade Applications without Downtime" description: "Learn how to use NGINX Gateway Fabric to upgrade applications without downtime." -weight: 300 +weight: 100 toc: true docs: "DOCS-000" --- diff --git a/hugo/content/how-to/monitoring/_index.md b/hugo/content/how-to/monitoring/_index.md new file mode 100644 index 0000000000..860266d953 --- /dev/null +++ b/hugo/content/how-to/monitoring/_index.md @@ -0,0 +1,9 @@ +--- +title: "Monitoring and Troubleshooting" +description: +weight: 500 +linkTitle: "Monitoring and Troubleshooting" +menu: + docs: + parent: How-To Guides +--- \ No newline at end of file diff --git a/hugo/content/monitoring.md b/hugo/content/how-to/monitoring/monitoring.md similarity index 99% rename from hugo/content/monitoring.md rename to hugo/content/how-to/monitoring/monitoring.md index 5aaa78f127..a8796a4bbc 100644 --- a/hugo/content/monitoring.md +++ b/hugo/content/how-to/monitoring/monitoring.md @@ -1,7 +1,7 @@ --- title: "Monitoring" description: "Learn how to monitor NGINX Gateway Fabric." -weight: 600 +weight: 100 toc: true docs: "DOCS-000" --- diff --git a/hugo/content/troubleshooting.md b/hugo/content/how-to/monitoring/troubleshooting.md similarity index 98% rename from hugo/content/troubleshooting.md rename to hugo/content/how-to/monitoring/troubleshooting.md index 19370572f4..4b79ed622c 100644 --- a/hugo/content/troubleshooting.md +++ b/hugo/content/how-to/monitoring/troubleshooting.md @@ -1,7 +1,7 @@ --- title: "Troubleshooting" description: "Learn how to troubleshoot common NGINX Gateway Fabric issues." -weight: 1100 +weight: 200 toc: true docs: "DOCS-000" --- diff --git a/hugo/content/how-to/traffic-management/_index.md b/hugo/content/how-to/traffic-management/_index.md new file mode 100644 index 0000000000..3071333a05 --- /dev/null +++ b/hugo/content/how-to/traffic-management/_index.md @@ -0,0 +1,9 @@ +--- +title: "Traffic Management" +description: +weight: 300 +linkTitle: "Traffic Management" +menu: + docs: + parent: How-To Guides +--- \ No newline at end of file diff --git a/hugo/content/guides/advanced-routing.md b/hugo/content/how-to/traffic-management/advanced-routing.md similarity index 96% rename from hugo/content/guides/advanced-routing.md rename to hugo/content/how-to/traffic-management/advanced-routing.md index 9be61d5e38..9d0cf1d664 100644 --- a/hugo/content/guides/advanced-routing.md +++ b/hugo/content/how-to/traffic-management/advanced-routing.md @@ -1,12 +1,12 @@ --- title: "Routing to Applications Using HTTP Matching Conditions" description: "Learn how to deploy multiple applications and HTTPRoutes with request conditions such as paths, methods, headers, and query parameters" -weight: 100 +weight: 200 toc: true docs: "DOCS-000" --- -In this guide we will configure advanced routing rules for multiple applications. These rules will showcase request matching by path, headers, query parameters, and method. For an introduction to exposing your application, we recommend that you go through the [basic guide]({{< relref "/guides/routing-traffic-to-your-app.md" >}}) first. +In this guide we will configure advanced routing rules for multiple applications. These rules will showcase request matching by path, headers, query parameters, and method. For an introduction to exposing your application, we recommend that you go through the [basic guide]({{< relref "/how-to/traffic-management/routing-traffic-to-your-app.md" >}}) first. The following image shows the traffic flow that we will be creating with these rules. @@ -16,8 +16,8 @@ The goal is to create a set of rules that will result in client requests being s ## Prerequisites -- [Install]({{< relref "/installation.md" >}}) NGINX Gateway Fabric. -- [Expose NGINX Gateway Fabric]({{< relref "/installation.md#expose-nginx-gateway-fabric" >}}) and save the public IP +- [Install]({{< relref "/how-to/installation/installation.md" >}}) NGINX Gateway Fabric. +- [Expose NGINX Gateway Fabric]({{< relref "/how-to/installation/installation.md#expose-nginx-gateway-fabric" >}}) and save the public IP address and port of NGINX Gateway Fabric into shell variables: ```text @@ -234,7 +234,7 @@ This request should receive a response from the `tea-post` Pod. Any other type o If you have any issues while sending traffic, try the following to debug your configuration and setup: -- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are [here](/docs/installation.md#expose-nginx-gateway-fabric). +- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are [here](/docs/how-to/installation/installation.md#expose-nginx-gateway-fabric). - Check the status of the Gateway: diff --git a/hugo/content/guides/integrating-cert-manager.md b/hugo/content/how-to/traffic-management/integrating-cert-manager.md similarity index 99% rename from hugo/content/guides/integrating-cert-manager.md rename to hugo/content/how-to/traffic-management/integrating-cert-manager.md index 9556b6c298..cfa0ec759f 100644 --- a/hugo/content/guides/integrating-cert-manager.md +++ b/hugo/content/how-to/traffic-management/integrating-cert-manager.md @@ -1,7 +1,7 @@ --- title: "Securing Traffic using Let's Encrypt and Cert-Manager" description: "Learn how to issue and mange certificates using Let's Encrypt and cert-manager." -weight: 200 +weight: 300 toc: true docs: "DOCS-000" --- @@ -18,7 +18,7 @@ Follow the steps in this guide to: - Administrator access to a Kubernetes cluster. - [Helm](https://helm.sh) and [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) must be installed locally. -- [NGINX Gateway Fabric deployed]({{< relref "/installation.md" >}}) in the Kubernetes cluster. +- [NGINX Gateway Fabric deployed]({{< relref "/how-to/installation/installation.md" >}}) in the Kubernetes cluster. - A DNS-resolvable domain name is required. It must resolve to the public endpoint of the NGINX Gateway Fabric deployment, and this public endpoint must be an external IP address or alias accessible over the internet. The process here will depend on your DNS provider. This DNS name will need to be resolvable from the Let’s Encrypt servers, which may require that you wait for the record to propagate before it will work. ## Overview diff --git a/hugo/content/guides/routing-traffic-to-your-app.md b/hugo/content/how-to/traffic-management/routing-traffic-to-your-app.md similarity index 97% rename from hugo/content/guides/routing-traffic-to-your-app.md rename to hugo/content/how-to/traffic-management/routing-traffic-to-your-app.md index a2a636d491..c30b72da4b 100644 --- a/hugo/content/guides/routing-traffic-to-your-app.md +++ b/hugo/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -1,7 +1,7 @@ --- title: "Routing Traffic to Your Application" description: "Learn how to route external traffic to your Kubernetes applications using NGINX Gateway Fabric." -weight: 300 +weight: 100 toc: true docs: "DOCS-000" --- @@ -10,8 +10,8 @@ In this guide you will learn how to route external traffic to your Kubernetes ap ## Prerequisites -- [Install]({{< relref "/installation.md" >}}) NGINX Gateway Fabric. -- [Expose NGINX Gateway Fabric]({{< relref "/installation.md#expose-nginx-gateway-fabric" >}}) and save the public IP address and port of NGINX Gateway Fabric into shell variables: +- [Install]({{< relref "/how-to/installation/installation.md" >}}) NGINX Gateway Fabric. +- [Expose NGINX Gateway Fabric]({{< relref "/how-to/installation/installation.md#expose-nginx-gateway-fabric" >}}) and save the public IP address and port of NGINX Gateway Fabric into shell variables: ```text GW_IP=XXX.YYY.ZZZ.III @@ -227,7 +227,7 @@ You should receive a 404 Not Found error: If you have any issues while testing the configuration, try the following to debug your configuration and setup: -- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are in the [installation documentation]({{< relref "/installation.md#expose-nginx-gateway-fabric" >}}). +- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are in the [installation documentation]({{< relref "/how-to/installation/installation.md#expose-nginx-gateway-fabric" >}}). - Check the status of the Gateway: diff --git a/hugo/content/reference/_index.md b/hugo/content/reference/_index.md new file mode 100644 index 0000000000..47843d27cd --- /dev/null +++ b/hugo/content/reference/_index.md @@ -0,0 +1,8 @@ +--- +title: "Reference" +description: +linkTitle: "Reference" +menu: + docs: + parent: NGINX Gateway Fabric +--- \ No newline at end of file diff --git a/hugo/content/cli-help.md b/hugo/content/reference/cli-help.md similarity index 99% rename from hugo/content/cli-help.md rename to hugo/content/reference/cli-help.md index 5700568a23..8d4cffafc5 100644 --- a/hugo/content/cli-help.md +++ b/hugo/content/reference/cli-help.md @@ -1,5 +1,5 @@ --- -title: "Command line help" +title: "Command-line Reference Guide" description: "Learn about the commands available in the gateway binary of the nginx-gateway container." weight: 400 toc: true From 75536083b627f4a3fce01ba1eaf66aca0aebdd46 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Thu, 9 Nov 2023 14:15:34 -0800 Subject: [PATCH 013/101] added weights to doc folders and the changelog file --- hugo/content/_index.md | 1 + hugo/content/changelog.md | 8 ++++++++ hugo/content/how-to/_index.md | 1 + hugo/content/reference/_index.md | 1 + 4 files changed, 11 insertions(+) create mode 100644 hugo/content/changelog.md diff --git a/hugo/content/_index.md b/hugo/content/_index.md index 18f5c85a00..2d28bc787d 100644 --- a/hugo/content/_index.md +++ b/hugo/content/_index.md @@ -1,6 +1,7 @@ --- title: "Welcome to the NGINX Gateway Fabric documentation" description: +weight: 300 linkTitle: "NGINX Gateway Fabric" menu: docs --- \ No newline at end of file diff --git a/hugo/content/changelog.md b/hugo/content/changelog.md new file mode 100644 index 0000000000..cba9060be9 --- /dev/null +++ b/hugo/content/changelog.md @@ -0,0 +1,8 @@ +--- +title: "Changelog" +description: "" +weight: 10000 +toc: true +draft: true +docs: "DOCS-000" +--- \ No newline at end of file diff --git a/hugo/content/how-to/_index.md b/hugo/content/how-to/_index.md index c2317a61c6..67c28e98c4 100644 --- a/hugo/content/how-to/_index.md +++ b/hugo/content/how-to/_index.md @@ -1,6 +1,7 @@ --- title: "How-To Guides" description: +weight: 200 linkTitle: "Guides" menu: docs: diff --git a/hugo/content/reference/_index.md b/hugo/content/reference/_index.md index 47843d27cd..3acd6879e6 100644 --- a/hugo/content/reference/_index.md +++ b/hugo/content/reference/_index.md @@ -1,6 +1,7 @@ --- title: "Reference" description: +weight: 400 linkTitle: "Reference" menu: docs: From 4efca2133f743efe3b457e534d5bd585fce1bdb1 Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Tue, 14 Nov 2023 12:37:39 +0000 Subject: [PATCH 014/101] Update hugo/README.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- hugo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugo/README.md b/hugo/README.md index e0527fc00a..a140bc3ccf 100644 --- a/hugo/README.md +++ b/hugo/README.md @@ -60,7 +60,7 @@ This repo uses a [forking workflow](https://www.atlassian.com/git/tutorials/comp ## Release Management and Publishing -**`Main`** is the default branch in this repo. All the latest content updates are merged into this branch. +**`main`** is the default branch in this repo. All the latest content updates are merged into this branch. The documentation is published from the latest public release branch, (for example, `release-4.0`). Work on your docs in feature branches off of the main branch. Open pull requests into the `main` when you are ready to merge your work. From 82b24432d910a54c1546af198e554d3b94d58cae Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Tue, 14 Nov 2023 12:37:58 +0000 Subject: [PATCH 015/101] Update hugo/README.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- hugo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugo/README.md b/hugo/README.md index a140bc3ccf..c79e22d8d4 100644 --- a/hugo/README.md +++ b/hugo/README.md @@ -129,7 +129,7 @@ Format links as [Hugo relrefs](https://gohugo.io/content-management/cross-refere For example: ```md -To install NGINX Controller, refer to the [installation instructions]({{< ref "install" >}}). +To install NGINX Gateway Fabric, refer to the [installation instructions]({{< ref "install" >}}). ``` ### Add images From c53793460885f0857bdc7a50772aab0133cbdeb3 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Tue, 14 Nov 2023 12:41:34 +0000 Subject: [PATCH 016/101] fix: change docs folder to `site` --- {hugo => site}/.gitignore | 0 {hugo => site}/Makefile | 0 {hugo => site}/README.md | 0 {hugo => site}/config/_default/config.toml | 0 {hugo => site}/config/development/config.toml | 0 {hugo => site}/config/production/config.toml | 0 {hugo => site}/config/staging/config.toml | 0 {hugo => site}/content/_index.md | 0 {hugo => site}/content/changelog.md | 0 {hugo => site}/content/concepts/_index.md | 0 {hugo => site}/content/concepts/architecture.md | 0 .../content/concepts/gateway-api-compatibility.md | 0 .../content/concepts/resource-validation.md | 0 {hugo => site}/content/how-to/_index.md | 0 .../content/how-to/configuration/_index.md | 0 .../configuration/control-plane-configuration.md | 0 .../content/how-to/installation/_index.md | 0 .../how-to/installation/building-the-images.md | 0 .../content/how-to/installation/installation.md | 0 .../content/how-to/installation/running-on-kind.md | 0 {hugo => site}/content/how-to/maintenance/_index.md | 0 .../maintenance/upgrade-apps-without-downtime.md | 0 {hugo => site}/content/how-to/monitoring/_index.md | 0 .../content/how-to/monitoring/monitoring.md | 0 .../content/how-to/monitoring/troubleshooting.md | 0 .../content/how-to/traffic-management/_index.md | 0 .../how-to/traffic-management/advanced-routing.md | 0 .../traffic-management/integrating-cert-manager.md | 0 .../routing-traffic-to-your-app.md | 0 {hugo => site}/content/reference/_index.md | 0 {hugo => site}/content/reference/cli-help.md | 0 {hugo => site}/go.mod | 2 +- {hugo => site}/go.sum | 0 {hugo => site}/md-linkcheck-config.json | 0 {hugo => site}/mdlint_conf.json | 0 {hugo => site}/netlify.toml | 2 +- {hugo => site}/static/img/advanced-routing.png | Bin .../static/img/cert-manager-gateway-workflow.png | Bin {hugo => site}/static/img/ngf-high-level.png | Bin {hugo => site}/static/img/ngf-pod.png | Bin {hugo => site}/static/img/route-all-traffic-app.png | Bin .../static/img/route-all-traffic-config.png | Bin .../static/img/route-all-traffic-flow.png | Bin {hugo => site}/static/img/src/README.md | 0 .../static/img/src/advanced-routing.mermaid | 0 .../static/img/src/route-all-traffic-app.mermaid | 0 .../static/img/src/route-all-traffic-config.mermaid | 0 .../static/img/src/route-all-traffic-flow.mermaid | 0 48 files changed, 2 insertions(+), 2 deletions(-) rename {hugo => site}/.gitignore (100%) rename {hugo => site}/Makefile (100%) rename {hugo => site}/README.md (100%) rename {hugo => site}/config/_default/config.toml (100%) rename {hugo => site}/config/development/config.toml (100%) rename {hugo => site}/config/production/config.toml (100%) rename {hugo => site}/config/staging/config.toml (100%) rename {hugo => site}/content/_index.md (100%) rename {hugo => site}/content/changelog.md (100%) rename {hugo => site}/content/concepts/_index.md (100%) rename {hugo => site}/content/concepts/architecture.md (100%) rename {hugo => site}/content/concepts/gateway-api-compatibility.md (100%) rename {hugo => site}/content/concepts/resource-validation.md (100%) rename {hugo => site}/content/how-to/_index.md (100%) rename {hugo => site}/content/how-to/configuration/_index.md (100%) rename {hugo => site}/content/how-to/configuration/control-plane-configuration.md (100%) rename {hugo => site}/content/how-to/installation/_index.md (100%) rename {hugo => site}/content/how-to/installation/building-the-images.md (100%) rename {hugo => site}/content/how-to/installation/installation.md (100%) rename {hugo => site}/content/how-to/installation/running-on-kind.md (100%) rename {hugo => site}/content/how-to/maintenance/_index.md (100%) rename {hugo => site}/content/how-to/maintenance/upgrade-apps-without-downtime.md (100%) rename {hugo => site}/content/how-to/monitoring/_index.md (100%) rename {hugo => site}/content/how-to/monitoring/monitoring.md (100%) rename {hugo => site}/content/how-to/monitoring/troubleshooting.md (100%) rename {hugo => site}/content/how-to/traffic-management/_index.md (100%) rename {hugo => site}/content/how-to/traffic-management/advanced-routing.md (100%) rename {hugo => site}/content/how-to/traffic-management/integrating-cert-manager.md (100%) rename {hugo => site}/content/how-to/traffic-management/routing-traffic-to-your-app.md (100%) rename {hugo => site}/content/reference/_index.md (100%) rename {hugo => site}/content/reference/cli-help.md (100%) rename {hugo => site}/go.mod (58%) rename {hugo => site}/go.sum (100%) rename {hugo => site}/md-linkcheck-config.json (100%) rename {hugo => site}/mdlint_conf.json (100%) rename {hugo => site}/netlify.toml (97%) rename {hugo => site}/static/img/advanced-routing.png (100%) rename {hugo => site}/static/img/cert-manager-gateway-workflow.png (100%) rename {hugo => site}/static/img/ngf-high-level.png (100%) rename {hugo => site}/static/img/ngf-pod.png (100%) rename {hugo => site}/static/img/route-all-traffic-app.png (100%) rename {hugo => site}/static/img/route-all-traffic-config.png (100%) rename {hugo => site}/static/img/route-all-traffic-flow.png (100%) rename {hugo => site}/static/img/src/README.md (100%) rename {hugo => site}/static/img/src/advanced-routing.mermaid (100%) rename {hugo => site}/static/img/src/route-all-traffic-app.mermaid (100%) rename {hugo => site}/static/img/src/route-all-traffic-config.mermaid (100%) rename {hugo => site}/static/img/src/route-all-traffic-flow.mermaid (100%) diff --git a/hugo/.gitignore b/site/.gitignore similarity index 100% rename from hugo/.gitignore rename to site/.gitignore diff --git a/hugo/Makefile b/site/Makefile similarity index 100% rename from hugo/Makefile rename to site/Makefile diff --git a/hugo/README.md b/site/README.md similarity index 100% rename from hugo/README.md rename to site/README.md diff --git a/hugo/config/_default/config.toml b/site/config/_default/config.toml similarity index 100% rename from hugo/config/_default/config.toml rename to site/config/_default/config.toml diff --git a/hugo/config/development/config.toml b/site/config/development/config.toml similarity index 100% rename from hugo/config/development/config.toml rename to site/config/development/config.toml diff --git a/hugo/config/production/config.toml b/site/config/production/config.toml similarity index 100% rename from hugo/config/production/config.toml rename to site/config/production/config.toml diff --git a/hugo/config/staging/config.toml b/site/config/staging/config.toml similarity index 100% rename from hugo/config/staging/config.toml rename to site/config/staging/config.toml diff --git a/hugo/content/_index.md b/site/content/_index.md similarity index 100% rename from hugo/content/_index.md rename to site/content/_index.md diff --git a/hugo/content/changelog.md b/site/content/changelog.md similarity index 100% rename from hugo/content/changelog.md rename to site/content/changelog.md diff --git a/hugo/content/concepts/_index.md b/site/content/concepts/_index.md similarity index 100% rename from hugo/content/concepts/_index.md rename to site/content/concepts/_index.md diff --git a/hugo/content/concepts/architecture.md b/site/content/concepts/architecture.md similarity index 100% rename from hugo/content/concepts/architecture.md rename to site/content/concepts/architecture.md diff --git a/hugo/content/concepts/gateway-api-compatibility.md b/site/content/concepts/gateway-api-compatibility.md similarity index 100% rename from hugo/content/concepts/gateway-api-compatibility.md rename to site/content/concepts/gateway-api-compatibility.md diff --git a/hugo/content/concepts/resource-validation.md b/site/content/concepts/resource-validation.md similarity index 100% rename from hugo/content/concepts/resource-validation.md rename to site/content/concepts/resource-validation.md diff --git a/hugo/content/how-to/_index.md b/site/content/how-to/_index.md similarity index 100% rename from hugo/content/how-to/_index.md rename to site/content/how-to/_index.md diff --git a/hugo/content/how-to/configuration/_index.md b/site/content/how-to/configuration/_index.md similarity index 100% rename from hugo/content/how-to/configuration/_index.md rename to site/content/how-to/configuration/_index.md diff --git a/hugo/content/how-to/configuration/control-plane-configuration.md b/site/content/how-to/configuration/control-plane-configuration.md similarity index 100% rename from hugo/content/how-to/configuration/control-plane-configuration.md rename to site/content/how-to/configuration/control-plane-configuration.md diff --git a/hugo/content/how-to/installation/_index.md b/site/content/how-to/installation/_index.md similarity index 100% rename from hugo/content/how-to/installation/_index.md rename to site/content/how-to/installation/_index.md diff --git a/hugo/content/how-to/installation/building-the-images.md b/site/content/how-to/installation/building-the-images.md similarity index 100% rename from hugo/content/how-to/installation/building-the-images.md rename to site/content/how-to/installation/building-the-images.md diff --git a/hugo/content/how-to/installation/installation.md b/site/content/how-to/installation/installation.md similarity index 100% rename from hugo/content/how-to/installation/installation.md rename to site/content/how-to/installation/installation.md diff --git a/hugo/content/how-to/installation/running-on-kind.md b/site/content/how-to/installation/running-on-kind.md similarity index 100% rename from hugo/content/how-to/installation/running-on-kind.md rename to site/content/how-to/installation/running-on-kind.md diff --git a/hugo/content/how-to/maintenance/_index.md b/site/content/how-to/maintenance/_index.md similarity index 100% rename from hugo/content/how-to/maintenance/_index.md rename to site/content/how-to/maintenance/_index.md diff --git a/hugo/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md similarity index 100% rename from hugo/content/how-to/maintenance/upgrade-apps-without-downtime.md rename to site/content/how-to/maintenance/upgrade-apps-without-downtime.md diff --git a/hugo/content/how-to/monitoring/_index.md b/site/content/how-to/monitoring/_index.md similarity index 100% rename from hugo/content/how-to/monitoring/_index.md rename to site/content/how-to/monitoring/_index.md diff --git a/hugo/content/how-to/monitoring/monitoring.md b/site/content/how-to/monitoring/monitoring.md similarity index 100% rename from hugo/content/how-to/monitoring/monitoring.md rename to site/content/how-to/monitoring/monitoring.md diff --git a/hugo/content/how-to/monitoring/troubleshooting.md b/site/content/how-to/monitoring/troubleshooting.md similarity index 100% rename from hugo/content/how-to/monitoring/troubleshooting.md rename to site/content/how-to/monitoring/troubleshooting.md diff --git a/hugo/content/how-to/traffic-management/_index.md b/site/content/how-to/traffic-management/_index.md similarity index 100% rename from hugo/content/how-to/traffic-management/_index.md rename to site/content/how-to/traffic-management/_index.md diff --git a/hugo/content/how-to/traffic-management/advanced-routing.md b/site/content/how-to/traffic-management/advanced-routing.md similarity index 100% rename from hugo/content/how-to/traffic-management/advanced-routing.md rename to site/content/how-to/traffic-management/advanced-routing.md diff --git a/hugo/content/how-to/traffic-management/integrating-cert-manager.md b/site/content/how-to/traffic-management/integrating-cert-manager.md similarity index 100% rename from hugo/content/how-to/traffic-management/integrating-cert-manager.md rename to site/content/how-to/traffic-management/integrating-cert-manager.md diff --git a/hugo/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md similarity index 100% rename from hugo/content/how-to/traffic-management/routing-traffic-to-your-app.md rename to site/content/how-to/traffic-management/routing-traffic-to-your-app.md diff --git a/hugo/content/reference/_index.md b/site/content/reference/_index.md similarity index 100% rename from hugo/content/reference/_index.md rename to site/content/reference/_index.md diff --git a/hugo/content/reference/cli-help.md b/site/content/reference/cli-help.md similarity index 100% rename from hugo/content/reference/cli-help.md rename to site/content/reference/cli-help.md diff --git a/hugo/go.mod b/site/go.mod similarity index 58% rename from hugo/go.mod rename to site/go.mod index dac2d808b5..93b32af858 100644 --- a/hugo/go.mod +++ b/site/go.mod @@ -1,4 +1,4 @@ -module github.com/nginxinc/nginx-gateway-fabric/hugo +module github.com/nginxinc/nginx-gateway-fabric/site go 1.21 diff --git a/hugo/go.sum b/site/go.sum similarity index 100% rename from hugo/go.sum rename to site/go.sum diff --git a/hugo/md-linkcheck-config.json b/site/md-linkcheck-config.json similarity index 100% rename from hugo/md-linkcheck-config.json rename to site/md-linkcheck-config.json diff --git a/hugo/mdlint_conf.json b/site/mdlint_conf.json similarity index 100% rename from hugo/mdlint_conf.json rename to site/mdlint_conf.json diff --git a/hugo/netlify.toml b/site/netlify.toml similarity index 97% rename from hugo/netlify.toml rename to site/netlify.toml index df9ffd6bf5..98444017e8 100644 --- a/hugo/netlify.toml +++ b/site/netlify.toml @@ -1,5 +1,5 @@ [build] - base = "hugo/" + base = "site/" publish = "public" [context.production] diff --git a/hugo/static/img/advanced-routing.png b/site/static/img/advanced-routing.png similarity index 100% rename from hugo/static/img/advanced-routing.png rename to site/static/img/advanced-routing.png diff --git a/hugo/static/img/cert-manager-gateway-workflow.png b/site/static/img/cert-manager-gateway-workflow.png similarity index 100% rename from hugo/static/img/cert-manager-gateway-workflow.png rename to site/static/img/cert-manager-gateway-workflow.png diff --git a/hugo/static/img/ngf-high-level.png b/site/static/img/ngf-high-level.png similarity index 100% rename from hugo/static/img/ngf-high-level.png rename to site/static/img/ngf-high-level.png diff --git a/hugo/static/img/ngf-pod.png b/site/static/img/ngf-pod.png similarity index 100% rename from hugo/static/img/ngf-pod.png rename to site/static/img/ngf-pod.png diff --git a/hugo/static/img/route-all-traffic-app.png b/site/static/img/route-all-traffic-app.png similarity index 100% rename from hugo/static/img/route-all-traffic-app.png rename to site/static/img/route-all-traffic-app.png diff --git a/hugo/static/img/route-all-traffic-config.png b/site/static/img/route-all-traffic-config.png similarity index 100% rename from hugo/static/img/route-all-traffic-config.png rename to site/static/img/route-all-traffic-config.png diff --git a/hugo/static/img/route-all-traffic-flow.png b/site/static/img/route-all-traffic-flow.png similarity index 100% rename from hugo/static/img/route-all-traffic-flow.png rename to site/static/img/route-all-traffic-flow.png diff --git a/hugo/static/img/src/README.md b/site/static/img/src/README.md similarity index 100% rename from hugo/static/img/src/README.md rename to site/static/img/src/README.md diff --git a/hugo/static/img/src/advanced-routing.mermaid b/site/static/img/src/advanced-routing.mermaid similarity index 100% rename from hugo/static/img/src/advanced-routing.mermaid rename to site/static/img/src/advanced-routing.mermaid diff --git a/hugo/static/img/src/route-all-traffic-app.mermaid b/site/static/img/src/route-all-traffic-app.mermaid similarity index 100% rename from hugo/static/img/src/route-all-traffic-app.mermaid rename to site/static/img/src/route-all-traffic-app.mermaid diff --git a/hugo/static/img/src/route-all-traffic-config.mermaid b/site/static/img/src/route-all-traffic-config.mermaid similarity index 100% rename from hugo/static/img/src/route-all-traffic-config.mermaid rename to site/static/img/src/route-all-traffic-config.mermaid diff --git a/hugo/static/img/src/route-all-traffic-flow.mermaid b/site/static/img/src/route-all-traffic-flow.mermaid similarity index 100% rename from hugo/static/img/src/route-all-traffic-flow.mermaid rename to site/static/img/src/route-all-traffic-flow.mermaid From 8251857d71a042b39e84e99a8732977f8c9866e8 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Tue, 14 Nov 2023 13:33:09 +0000 Subject: [PATCH 017/101] fix: updates to docs readme --- .../developer/documentation.md | 62 ++++--------------- docs/developer/implementing-a-feature.md | 9 +-- 2 files changed, 17 insertions(+), 54 deletions(-) rename site/README.md => docs/developer/documentation.md (61%) diff --git a/site/README.md b/docs/developer/documentation.md similarity index 61% rename from site/README.md rename to docs/developer/documentation.md index c79e22d8d4..9a54b5a976 100644 --- a/site/README.md +++ b/docs/developer/documentation.md @@ -1,12 +1,12 @@ # NGINX Gateway Fabric Docs -This directory contains the user documentation for NGINX Gateway Fabric and the requirements for linting, building, and publishing the docs. +The `/site` directory contains the user documentation for NGINX Gateway Fabric and the requirements for linting, building, and publishing the docs. We use [Hugo](https://gohugo.io/) to build the docs for NGINX, with the [nginx-hugo-theme](https://github.com/nginxinc/nginx-hugo-theme). Docs should be written in Markdown. -In this directory, you will find the following files: +In the `/site` directory, you will find the following files: - a [Netlify](https://netlify.com) configuration file; - configuration files for [markdownlint](https://github.com/DavidAnson/markdownlint/) and [markdown-link-check](https://github.com/tcort/markdown-link-check) @@ -14,59 +14,21 @@ In this directory, you will find the following files: ## Git Guidelines -- Keep a clean, concise and meaningful git commit history on your branch (within reason), rebasing locally and squashing before submitting a PR. -- Use the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format when writing a commit message, so that changelogs can be automatically generated -- Follow the guidelines of writing a good commit message as described here and summarised in the next few points: - - In the subject line, use the present tense ("Add feature" not "Added feature"). - - In the subject line, use the imperative mood ("Move cursor to..." not "Moves cursor to..."). - - Limit the subject line to 72 characters or less. - - Reference issues and pull requests liberally after the subject line. - - Add more detailed description in the body of the git message (`git commit -a` to give you more space and time in your text editor to write a good message instead of `git commit -am`). +See the [Pull Request Guide](pull-request.md) for specfic instructions on how to submit a pull request. -### Forking and Pull Requests +### Branching and Workflow -This repo uses a [forking workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow). Take the steps below to fork the repo, check out a feature branch, and open a pull request with your changes. +This repo uses a [forking workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow). See our [Branching and Workflow](branching-and-workflow.md) documentation for more information. -1. In the GitHub UI, select the **Fork** button. - - - On the **Create a new fork** page, select the **Owner** (the account where the fork of the repo will be placed). - - Select the **Create fork** button. - -2. If you plan to work on docs in your local development environment, clone your fork. - For example, to clone the repo using SSH, you would run the following command: - - ```shell - git clone git@github.com:/nginx-gateway-fabric.git - ``` - -3. Check out a new feature branch in your fork. This is where you will work on your docs. - - To do this via the command line, you would run the following command: - - ```shell - git checkout -b - ``` - - **CAUTION**: Do not work on the main branch in your fork. This can cause issues when the NGINX Docs team needs to check out your feature branch for editing work. - -4. Make atomic, [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) on your feature branch. - -5. When ready, open a pull request into the **main** branch in the **nginxinc/nginx-gateway-fabric** repo. - - - Fill in [our pull request template](https://github.com/nginxinc/nginx-gateway-fabric/blob/main/.github/PULL_REQUEST_TEMPLATE.md) when opening your PR. - - Tag the appropriate reviewers for your subject area. - Technical reviewers should be able to verify that the information provided is accurate. - Documentation reviewers ensure that the content conforms to the NGINX Style Guide, is grammatically correct, and adheres to the NGINX content templates. - -## Release Management and Publishing +### Publishing Documentation Updates **`main`** is the default branch in this repo. All the latest content updates are merged into this branch. -The documentation is published from the latest public release branch, (for example, `release-4.0`). Work on your docs in feature branches off of the main branch. Open pull requests into the `main` when you are ready to merge your work. +The documentation is published from the latest public release branch, (for example, `release-4.0`). Work on your docs in a feature branch in your fork of the repo. Open pull requests into the `main` branch when you are ready to merge your work. If you are working on content for immediate publication in the docs site, cherrypick your changes to the current public release branch. -If you are working on content for a future release, make sure that you **do not** cherrypick them to the current public release branch, as this will publish them automatically. +If you are working on content for a future release, make sure that you **do not** cherrypick them to the current public release branch, as this will publish them automatically. See the [Release Process documentation](release-process.md) for more information. ## Setup @@ -122,27 +84,27 @@ Format links as [Hugo relrefs](https://gohugo.io/content-management/cross-refere > Note: Using file extensions when linking to internal docs with `relref` is optional. -- You can use relative paths or just the filename. +- You can use relative paths or just the filename. We recommend using the filename - Paths without a leading `/` are first resolved relative to the current page, then to the remainder of the site. - Anchors are supported. For example: ```md -To install NGINX Gateway Fabric, refer to the [installation instructions]({{< ref "install" >}}). +To install NGINX Gateway Fabric, refer to the [installation instructions]({{< relref "/installation/install.md#section-1" >}}). ``` ### Add images You can use the `img` [shortcode](#shortcodes) to insert images into your documentation. -1. Add the image to the static/img directory, or to the same directory as the doc you want to use it in. +1. Add the image to the static/img directory. DO NOT include a forward slash at the beginning of the file path. This will break the image when it's rendered. See the docs for the [Hugo relURL Function](https://gohugo.io/functions/relurl/#input-begins-with-a-slash) to learn more. 1. Add the img shortcode: - {{< img src="" >}} + {{< img src="img/" >}} > Note: The shortcode accepts all of the same parameters as the [Hugo figure shortcode](https://gohugo.io/content-management/shortcodes/#figure). diff --git a/docs/developer/implementing-a-feature.md b/docs/developer/implementing-a-feature.md index 262a93ef7c..c56d5dd7ee 100644 --- a/docs/developer/implementing-a-feature.md +++ b/docs/developer/implementing-a-feature.md @@ -32,18 +32,19 @@ practices to ensure a successful feature development process. the [testing](/docs/developer/testing.md#unit-test-guidelines) documentation. 9. **Manually verify your changes**: Refer to the [manual testing](/docs/developer/testing.md#manual-testing) section of the testing documentation for instructions on how to manually test your changes. -10. **Update any relevant documentation**: Here are some guidelines for updating documentation: +10. **Update any relevant documentation**: See the [documentation](/docs/developer/documentation.md) guide for in-depth information about the workflow to update the docs and how we publish them. + Here are some basic guidelines for updating documentation: - **Gateway API Feature**: If you are implementing a Gateway API feature, make sure to update - the [Gateway API Compatibility](/docs/gateway-api-compatibility.md) documentation. + the [Gateway API Compatibility](/site/content/concepts/gateway-api-compatibility.md) documentation. - **New Use Case:** If your feature introduces a new use case, add an example of how to use it in the [examples](/examples) directory. This example will help users understand how to leverage the new feature. > For security, a Docker image used in an example must be either managed by F5/NGINX or be an [official image](https://docs.docker.com/docker-hub/official_images/). - **Installation Changes**: If your feature involves changes to the installation process of NGF, update - the [installation](/docs/installation.md) documentation. + the [installation](/site/content/how-to/installation/installation.md) documentation. - **Helm Changes**: If your feature introduces or changes any values of the NGF Helm Chart, update the [Helm README](/deploy/helm-chart/README.md). - **Command-line Changes**: If your feature introduces or changes a command-line flag or subcommand, update - the [cli help](/docs/cli-help.md) documentation. + the [cli help](/site/content/reference/cli-help.md) documentation. - **Other Documentation Updates**: For any other changes that affect the behavior, usage, or configuration of NGF, review the existing documentation and update it as necessary. Ensure that the documentation remains accurate and up to date with the latest changes. From 9d8c1bde352e7312fe4cde0ce87b9b57a485df86 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Tue, 14 Nov 2023 17:00:23 +0000 Subject: [PATCH 018/101] chore: add releases and tech specs dummy pages --- site/content/concepts/technical-specifications.md | 13 +++++++++++++ site/content/releases.md | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 site/content/concepts/technical-specifications.md create mode 100644 site/content/releases.md diff --git a/site/content/concepts/technical-specifications.md b/site/content/concepts/technical-specifications.md new file mode 100644 index 0000000000..b688b050f2 --- /dev/null +++ b/site/content/concepts/technical-specifications.md @@ -0,0 +1,13 @@ +--- +title: "Technical Specifications" +draft: false +description: "NGINX Gateway Fabric technical specifications." +weight: 800 +toc: true +tags: [ "docs" ] +docs: "DOCS-000" +--- + +See the NGINX Gateway Fabric technical specifications page: + +https://github.com/nginxinc/nginx-gateway-fabric#technical-specifications diff --git a/site/content/releases.md b/site/content/releases.md new file mode 100644 index 0000000000..8b3a84c781 --- /dev/null +++ b/site/content/releases.md @@ -0,0 +1,13 @@ +--- +title: "Releases" +draft: false +description: "NGINX Gateway Fabric releases." +weight: 1200 +toc: true +tags: [ "docs" ] +docs: "DOCS-000" +--- + +See the NGINX Gateway Fabric changelog page: + +https://github.com/nginxinc/nginx-gateway-fabric/blob/main/CHANGELOG.md From 6208ac9c77d9411860bd055f8b53bf5b87bde8c4 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Wed, 15 Nov 2023 12:31:22 -0800 Subject: [PATCH 019/101] Edits to Monitoring topic --- site/content/how-to/monitoring/monitoring.md | 121 ++++++++++--------- site/layouts/shortcodes/call-out.html | 3 + site/layouts/shortcodes/custom-styles.html | 43 +++++++ 3 files changed, 109 insertions(+), 58 deletions(-) create mode 100644 site/layouts/shortcodes/call-out.html create mode 100644 site/layouts/shortcodes/custom-styles.html diff --git a/site/content/how-to/monitoring/monitoring.md b/site/content/how-to/monitoring/monitoring.md index a8796a4bbc..aa6e7e38f3 100644 --- a/site/content/how-to/monitoring/monitoring.md +++ b/site/content/how-to/monitoring/monitoring.md @@ -1,49 +1,48 @@ --- -title: "Monitoring" -description: "Learn how to monitor NGINX Gateway Fabric." +title: "Monitoring NGINX Gateway Fabric" +description: "Learn how to monitor your NGINX Gateway Fabric effectively. This guide provides easy steps for configuring monitoring settings and understanding key performance metrics." weight: 100 toc: true docs: "DOCS-000" --- -# Monitoring +{{}} -The NGINX Gateway Fabric exposes a number of metrics in the [Prometheus](https://prometheus.io/) format. Those -include NGINX and the controller-runtime metrics. These are delivered using a metrics server orchestrated by the -controller-runtime package. Metrics are enabled by default, and are served via http on port `9113`. +## Overview -> **Note** -> By default metrics are served via http. Please note that if serving metrics via https is enabled, this -> endpoint will be secured with a self-signed certificate. Since the metrics server is using a self-signed certificate, -> the Prometheus Pod scrape configuration will also require the `insecure_skip_verify` flag set. See -> [the Prometheus documentation](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#tls_config). -## Changing the default Metrics configuration +NGINX Gateway Fabric metrics are displayed in [Prometheus](https://prometheus.io/) format, simplifying monitoring. You can track NGINX and controller-runtime metrics through a metrics server orchestrated by the controller-runtime package. These metrics are enabled by default and can be accessed on HTTP port `9113`. + + +{{}} +Metrics are served over HTTP by default. Enabling HTTPS will secure the metrics endpoint with a self-signed certificate. When using HTTPS, adjust the Prometheus Pod scrape settings by adding the `insecure_skip_verify` flag to handle the self-signed certificate. For further details, refer to the [Prometheus documentation](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#tls_config). +{{}} + +## How to Change the Default Metrics Configuration + +Configuring NGINX Gateway Fabric for monitoring is straightforward. You can change metric settings using Helm or Kubernetes manifests, depending on your setup. ### Using Helm -If you're using *Helm* to install the NGINX Gateway Fabric, set the `metrics.*` parameters to the required values -for your environment. See the [Helm README](/deploy/helm-chart/README.md). +If you're setting up NGINX Gateway Fabric with Helm, you can adjust the `metrics.*` parameters to fit your needs. For detailed options and instructions, see the [Helm README](/deploy/helm-chart/README.md). -### Using Manifests +### Using Kubernetes Manifests -If you're using *Kubernetes manifests* to install NGINX Gateway Fabric, you can modify the -[manifest](/deploy/manifests/nginx-gateway.yaml) to change the default metrics configuration: +For setups using Kubernetes manifests, change the metrics configuration by editing the [NGINX Gateway manifest](/deploy/manifests/nginx-gateway.yaml). -#### Disabling metrics +#### Disabling Metrics -1. Set the `-metrics-disable` [command-line argument](/docs/cli-help.md) to `true` and remove the other `-metrics-*` - command line arguments. +If you need to disable metrics: -2. Remove the metrics port entry from the list of the ports of the NGINX Gateway Fabric container in the template - of the NGINX Gateway Fabric Pod: +1. Set the `-metrics-disable` [command-line argument]({{< relref "reference/cli-help.md">}}) to `true` in the NGINX Gateway Fabric Pod's configuration. Remove any other `-metrics-*` arguments. +2. In the Pod template for NGINX Gateway Fabric, delete the metrics port entry from the container ports list: ```yaml - name: metrics containerPort: 9113 ``` -3. Remove the following annotations from the template of the NGINX Gateway Fabric Pod: +3. Also, remove the following annotations from the NGINX Gateway Fabric Pod template: ```yaml annotations: @@ -51,19 +50,19 @@ If you're using *Kubernetes manifests* to install NGINX Gateway Fabric, you can prometheus.io/port: "9113" ``` -#### Changing the default port +#### Changing the Default Port -1. Set the `-metrics-port` [command-line argument](/docs/cli-help.md) to the required value. +To change the default port for metrics: -2. Change the metrics port entry in the list of the ports of the NGINX Gateway Fabric container in the template - of the NGINX Gateway Fabric Pod: +1. Update the `-metrics-port` [command-line argument]({{< relref "reference/cli-help.md">}}) in the NGINX Gateway Fabric Pod's configuration to your chosen port number. +2. In the Pod template, change the metrics port entry to reflect the new port: ```yaml - name: metrics containerPort: ``` -3. Change the following annotation in the template of the NGINX Gateway Fabric Pod: +3. Modify the `prometheus.io/port` annotation in the Pod template to match the new port: ```yaml annotations: @@ -72,11 +71,13 @@ If you're using *Kubernetes manifests* to install NGINX Gateway Fabric, you can <...> ``` -#### Enable serving metrics via https +#### Enabling HTTPS for Metrics + +For enhanced security with HTTPS: -1. Set the `-metrics-secure-serving` [command-line argument](/docs/cli-help.md) to `true`. +1. Turn on secure serving by setting the `-metrics-secure-serving` [command-line argument]({{< relref "reference/cli-help.md">}}) to `true` in the NGINX Gateway Fabric Pod's configuration. -2. Add the following annotation in the template of the NGINX Gateway Fabric Pod: +2. Add an HTTPS scheme annotation to the Pod template: ```yaml annotations: @@ -85,30 +86,34 @@ If you're using *Kubernetes manifests* to install NGINX Gateway Fabric, you can <...> ``` -## Available Metrics - -NGINX Gateway Fabric exports the following metrics: - -- NGINX metrics: - - Exported by NGINX. Refer to the [NGINX Prometheus Exporter developer docs](https://github.com/nginxinc/nginx-prometheus-exporter#metrics-for-nginx-oss) - - These metrics have the namespace `nginx_gateway_fabric`, and include the label `class` which is set to the - Gateway class of NGF. For example, `nginx_gateway_fabric_connections_accepted{class="nginx"}`. - -- NGINX Gateway Fabric metrics: - - nginx_reloads_total. Number of successful NGINX reloads. - - nginx_reload_errors_total. Number of unsuccessful NGINX reloads. - - nginx_stale_config. 1 means NGF failed to configure NGINX with the latest version of the configuration, which means - NGINX is running with a stale version. - - nginx_last_reload_milliseconds. Duration in milliseconds of NGINX reloads (histogram). - - event_batch_processing_milliseconds: Duration in milliseconds of event batch processing (histogram), which is the - time it takes NGF to process batches of Kubernetes events (changes to cluster resources). Note that NGF processes - events in batches, and while processing the current batch, it accumulates events for the next batch. - - These metrics have the namespace `nginx_gateway_fabric`, and include the label `class` which is set to the - Gateway class of NGF. For example, `nginx_gateway_fabric_nginx_reloads_total{class="nginx"}`. - -- [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) metrics. These include: - - Total number of reconciliation errors per controller - - Length of reconcile queue per controller - - Reconciliation latency - - Usual resource metrics such as CPU, memory usage, file descriptor usage - - Go runtime metrics such as number of Go routines, GC duration, and Go version information +Thank you for pointing that out. Let's include that important detail in the NGINX Gateway Fabric metrics subsection. Here's the revised section: + +## Available Metrics in NGINX Gateway Fabric + +NGINX Gateway Fabric provides a variety of metrics to assist in monitoring and analyzing performance. These metrics are categorized as follows: + +### NGINX Metrics + +NGINX metrics, essential for monitoring specific NGINX operations, include details like the total number of accepted client connections. For a complete list of available NGINX metrics, refer to the [NGINX Prometheus Exporter developer docs](https://github.com/nginxinc/nginx-prometheus-exporter#metrics-for-nginx-oss). + +These metrics use the `nginx_gateway_fabric` namespace and include the `class` label, indicating the NGINX Gateway class. For example, `nginx_gateway_fabric_connections_accepted{class="nginx"}`. + +### NGINX Gateway Fabric Metrics + +Metrics specific to the NGINX Gateway Fabric include: + +- `nginx_reloads_total`: Counts successful NGINX reloads. +- `nginx_reload_errors_total`: Counts NGINX reload failures. +- `nginx_stale_config`: Indicates if NGINX Gateway Fabric couldn't update NGINX with the latest configuration, resulting in a stale version. +- `nginx_last_reload_milliseconds`: Time in milliseconds for NGINX reloads. +- `event_batch_processing_milliseconds`: Time in milliseconds to process batches of Kubernetes events. + +All these metrics are under the `nginx_gateway_fabric` namespace and include a `class` label set to the Gateway class of NGINX Gateway Fabric. For example, `nginx_gateway_fabric_nginx_reloads_total{class="nginx"}`. + +### Controller-runtime Metrics + +Provided by the [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) library, these metrics cover a range of aspects: + +- General resource usage like CPU and memory. +- Go runtime metrics such as the number of Go routines, garbage collection duration, and Go version. +- Controller-specific metrics, including reconciliation errors per controller, length of the reconcile queue, and reconciliation latency. diff --git a/site/layouts/shortcodes/call-out.html b/site/layouts/shortcodes/call-out.html new file mode 100644 index 0000000000..d8e591d832 --- /dev/null +++ b/site/layouts/shortcodes/call-out.html @@ -0,0 +1,3 @@ +
+
{{ .Get 1 }}
{{ .Inner | markdownify }}
+
diff --git a/site/layouts/shortcodes/custom-styles.html b/site/layouts/shortcodes/custom-styles.html new file mode 100644 index 0000000000..8ec2a0bbf4 --- /dev/null +++ b/site/layouts/shortcodes/custom-styles.html @@ -0,0 +1,43 @@ + From b74eeed3392e22f2b51a0d9939b0eabadeef49ce Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Wed, 15 Nov 2023 12:52:39 -0800 Subject: [PATCH 020/101] chore: edits --- site/content/how-to/monitoring/monitoring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/monitoring/monitoring.md b/site/content/how-to/monitoring/monitoring.md index aa6e7e38f3..454bd8cb53 100644 --- a/site/content/how-to/monitoring/monitoring.md +++ b/site/content/how-to/monitoring/monitoring.md @@ -75,7 +75,7 @@ To change the default port for metrics: For enhanced security with HTTPS: -1. Turn on secure serving by setting the `-metrics-secure-serving` [command-line argument]({{< relref "reference/cli-help.md">}}) to `true` in the NGINX Gateway Fabric Pod's configuration. +1. Enable HTTPS security by setting the `-metrics-secure-serving` [command-line argument]({{< relref "reference/cli-help.md">}}) to `true` in the NGINX Gateway Fabric Pod's configuration. 2. Add an HTTPS scheme annotation to the Pod template: From 6801a6d08eefb3cf1ed33561f9d41ef95c2b2883 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Wed, 15 Nov 2023 12:56:19 -0800 Subject: [PATCH 021/101] chore: edits --- site/content/how-to/monitoring/monitoring.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/site/content/how-to/monitoring/monitoring.md b/site/content/how-to/monitoring/monitoring.md index 454bd8cb53..a74a367901 100644 --- a/site/content/how-to/monitoring/monitoring.md +++ b/site/content/how-to/monitoring/monitoring.md @@ -18,7 +18,7 @@ NGINX Gateway Fabric metrics are displayed in [Prometheus](https://prometheus.io Metrics are served over HTTP by default. Enabling HTTPS will secure the metrics endpoint with a self-signed certificate. When using HTTPS, adjust the Prometheus Pod scrape settings by adding the `insecure_skip_verify` flag to handle the self-signed certificate. For further details, refer to the [Prometheus documentation](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#tls_config). {{}} -## How to Change the Default Metrics Configuration +## How to change the default metrics configuration Configuring NGINX Gateway Fabric for monitoring is straightforward. You can change metric settings using Helm or Kubernetes manifests, depending on your setup. @@ -26,11 +26,11 @@ Configuring NGINX Gateway Fabric for monitoring is straightforward. You can chan If you're setting up NGINX Gateway Fabric with Helm, you can adjust the `metrics.*` parameters to fit your needs. For detailed options and instructions, see the [Helm README](/deploy/helm-chart/README.md). -### Using Kubernetes Manifests +### Using Kubernetes manifests For setups using Kubernetes manifests, change the metrics configuration by editing the [NGINX Gateway manifest](/deploy/manifests/nginx-gateway.yaml). -#### Disabling Metrics +#### Disabling metrics If you need to disable metrics: @@ -50,7 +50,7 @@ If you need to disable metrics: prometheus.io/port: "9113" ``` -#### Changing the Default Port +#### Changing the default port To change the default port for metrics: @@ -71,7 +71,7 @@ To change the default port for metrics: <...> ``` -#### Enabling HTTPS for Metrics +#### Enabling HTTPS for metrics For enhanced security with HTTPS: @@ -88,17 +88,17 @@ For enhanced security with HTTPS: Thank you for pointing that out. Let's include that important detail in the NGINX Gateway Fabric metrics subsection. Here's the revised section: -## Available Metrics in NGINX Gateway Fabric +## Available metrics in NGINX Gateway Fabric NGINX Gateway Fabric provides a variety of metrics to assist in monitoring and analyzing performance. These metrics are categorized as follows: -### NGINX Metrics +### NGINX metrics NGINX metrics, essential for monitoring specific NGINX operations, include details like the total number of accepted client connections. For a complete list of available NGINX metrics, refer to the [NGINX Prometheus Exporter developer docs](https://github.com/nginxinc/nginx-prometheus-exporter#metrics-for-nginx-oss). These metrics use the `nginx_gateway_fabric` namespace and include the `class` label, indicating the NGINX Gateway class. For example, `nginx_gateway_fabric_connections_accepted{class="nginx"}`. -### NGINX Gateway Fabric Metrics +### NGINX Gateway Fabric metrics Metrics specific to the NGINX Gateway Fabric include: @@ -110,7 +110,7 @@ Metrics specific to the NGINX Gateway Fabric include: All these metrics are under the `nginx_gateway_fabric` namespace and include a `class` label set to the Gateway class of NGINX Gateway Fabric. For example, `nginx_gateway_fabric_nginx_reloads_total{class="nginx"}`. -### Controller-runtime Metrics +### Controller-runtime metrics Provided by the [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) library, these metrics cover a range of aspects: From 8160a682310b35eeac52aba8d0ed68d9ca5f76cc Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Wed, 15 Nov 2023 13:18:21 -0800 Subject: [PATCH 022/101] chore: edits --- site/content/how-to/monitoring/monitoring.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/site/content/how-to/monitoring/monitoring.md b/site/content/how-to/monitoring/monitoring.md index a74a367901..de75eef202 100644 --- a/site/content/how-to/monitoring/monitoring.md +++ b/site/content/how-to/monitoring/monitoring.md @@ -86,8 +86,6 @@ For enhanced security with HTTPS: <...> ``` -Thank you for pointing that out. Let's include that important detail in the NGINX Gateway Fabric metrics subsection. Here's the revised section: - ## Available metrics in NGINX Gateway Fabric NGINX Gateway Fabric provides a variety of metrics to assist in monitoring and analyzing performance. These metrics are categorized as follows: From 4f6b26c32ea890ff0708cec223bbff01fc0eb085 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Thu, 16 Nov 2023 09:11:53 -0800 Subject: [PATCH 023/101] edits to NGF installation guide --- .../how-to/installation/installation.md | 107 +++++++++++++++++- 1 file changed, 104 insertions(+), 3 deletions(-) diff --git a/site/content/how-to/installation/installation.md b/site/content/how-to/installation/installation.md index e94132bb07..f559287f11 100644 --- a/site/content/how-to/installation/installation.md +++ b/site/content/how-to/installation/installation.md @@ -6,14 +6,115 @@ toc: true docs: "DOCS-000" --- +{{}} + ## Prerequisites -- [kubectl](https://kubernetes.io/docs/tasks/tools/) +- Install [kubectl](https://kubernetes.io/docs/tasks/tools/) ## Deploy NGINX Gateway Fabric using Helm -To deploy NGINX Gateway Fabric using Helm, please follow the instructions on [this](/deploy/helm-chart/README.md) -page. +Follow the instructions [to deploy NGINX Gateway Fabric using Helm](/deploy/helm-chart/README.md). + + +## Deploy NGINX Gateway Fabric from Manifests + +> **Note:** NGINX Gateway Fabric installs into the _nginx-gateway_ namespace by default. To run NGINX Gateway Fabric in a different namespace, modify the installation manifests. + +1. Install the Gateway API resources (Custom Resource Definitions [CRDs] and validating webhook) from the standard channel: + + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` + +2. Deploy the NGINX Gateway Fabric CRDs: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml + ``` + +3. Deploy NGINX Gateway Fabric: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml + ``` + +4. Verify NGINX Gateway Fabric is running in the `nginx-gateway` namespace: + + ```shell + kubectl get pods -n nginx-gateway + ``` + + Expected output (note that _5d4f4c7db7-xk2kq_ is a randomly generated string and will vary): + + ```text + NAME READY STATUS RESTARTS AGE + nginx-gateway-5d4f4c7db7-xk2kq 2/2 Running 0 112s + ``` + + +## Expose NGINX Gateway Fabric + +Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the **--service** argument of the controller. + +{{}} +The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any Gateway [Listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports. +{{}} + +NGINX Gateway Fabric utilizes this service to update the **Addresses** field in the **Gateway Status** resource. A LoadBalancer service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. + +### Create a NodePort service + +To create a `NodePort` service: + +```shell +kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml +``` + +A `NodePort` service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. + +### Create a LoadBalancer Service + +To create a `LoadBalancer` service, use the appropriate manifest for your cloud provider: + +- For GCP or Azure: + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml + ``` + + Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the following command: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + Use the public IP of the load balancer to access NGINX Gateway Fabric. + +- For AWS: + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml + ``` + + In AWS, the NLB DNS name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + Generally, use the NLB DNS name, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: + + ```shell + nslookup + ``` + +--- + +This revision ensures that 'service,' 'hostname,' and 'namespace' are treated as common nouns and are in lowercase, as per your instruction. Let me know if this meets your needs or if we should proceed to the next section! +--- + +Feel free to copy this text. Let me know if you need any further modifications or if we should proceed to the next section! + +%%%% ## Deploy NGINX Gateway Fabric from Manifests From 69f9814de0917382e15771c7a5f1a93a574c7f7c Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 16 Nov 2023 19:12:19 +0000 Subject: [PATCH 024/101] fix: update caps --- .../routing-traffic-to-your-app.md | 87 ++++++++++--------- 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index c30b72da4b..9174cd9c1f 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -5,8 +5,11 @@ weight: 100 toc: true docs: "DOCS-000" --- +{{}} -In this guide you will learn how to route external traffic to your Kubernetes applications using the Gateway API and NGINX Gateway Fabric. Whether you're managing a web application or a REST backend API, you can use NGINX Gateway Fabric to expose your application outside the cluster. +## Overview + +You can route traffic to your Kubernetes applications using the Gateway API and NGINX Gateway Fabric. Whether you're managing a web application or a REST backend API, you can use NGINX Gateway Fabric to expose your application outside the cluster. ## Prerequisites @@ -18,21 +21,21 @@ In this guide you will learn how to route external traffic to your Kubernetes ap GW_PORT= ``` -## The Application +## Example application -The application we are going to use in this guide is a simple coffee application comprised of one Service and two Pods: +The application we are going to use in this guide is a simple **coffee** application comprised of one service and two pods: ![coffee app](/img/route-all-traffic-app.png) -Using this architecture, the coffee application is not accessible outside the cluster. We want to expose this application on the hostname `cafe.example.com` so that clients outside the cluster can access it. +Using this architecture, the **coffee** application is not accessible outside the cluster. We want to expose this application on the hostname "cafe.example.com" so that clients outside the cluster can access it. -To do this, we will install NGINX Gateway Fabric and create two Gateway API resources: a [Gateway](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway) and an [HTTPRoute](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute). +Install NGINX Gateway Fabric and create two gateway API resources: a [gateway](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway) and an [HTTPRoute](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute). -With these resources, we will configure a simple routing rule to match all HTTP traffic with the hostname `cafe.example.com` and route it to the coffee Service. +Using these resources we will configure a simple routing rule to match all HTTP traffic with the hostname "cafe.example.com" and route it to the **coffee** service. -## Setup +## Set up -Create the coffee application in Kubernetes by copying and pasting the following into your terminal: +Create the **coffee** application in Kubernetes by copying and pasting the following block into your terminal: ```yaml kubectl apply -f - < 80/TCP 77s ``` -## Application Architecture with NGINX Gateway Fabric +## Application architecture with NGINX Gateway Fabric -To route traffic to the coffee application, we will create a Gateway and HTTPRoute. The following diagram shows the configuration we'll be creating in the next step: +To route traffic to the **coffee** application, we will create a gateway and HTTPRoute. The following diagram shows the configuration we'll be creating in the next step: ![Configuration](/img/route-all-traffic-config.png) -We need a Gateway to create an entry point for HTTP traffic coming into the cluster. The `cafe` Gateway we are going to create will open an entry point to the cluster on port 80 for HTTP traffic. +We need a gateway to create an entry point for HTTP traffic coming into the cluster. The **cafe** gateway we are going to create will open an entry point to the cluster on port 80 for HTTP traffic. -To route HTTP traffic from the Gateway to the coffee Service, we need to create an HTTPRoute named `coffee` and attach to the Gateway. This HTTPRoute will have a single routing rule that routes all traffic to the hostname `cafe.example.com` from the Gateway to the coffee Service. +To route HTTP traffic from the gateway to the **coffee** service, we need to create an HTTPRoute named **coffee** and attach to the gateway. This HTTPRoute will have a single routing rule that routes all traffic to the hostname "cafe.example.com" from the gateway to the **coffee** service. -Once NGINX Gateway Fabric processes the `cafe` Gateway and `coffee` HTTPRoute, it will configure its dataplane, NGINX, to route all HTTP requests to `cafe.example.com` to the Pods that the `coffee` Service targets: +Once NGINX Gateway Fabric processes the **cafe** gateway and **coffee** HTTPRoute, it will configure its dataplane, NGINX, to route all HTTP requests to "cafe.example.com" to the pods that the **coffee** service targets: ![Traffic Flow](/img/route-all-traffic-flow.png) -The coffee Service is omitted from the diagram above because the NGINX Gateway Fabric routes directly to the Pods that the coffee Service targets. +The **coffee** service is omitted from the diagram above because the NGINX Gateway Fabric routes directly to the pods that the **coffee** service targets. {{< note >}}In the diagrams above, all resources that are the responsibility of the cluster operator are shown in green. The orange resources are the responsibility of the application developers. -See the [roles and personas](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1) Gateway API document for more information on these roles.{{< /note >}} +See the [roles and personas](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1) gateway API document for more information on these roles.{{< /note >}} -## Create the Gateway API Resources +## Create the gateway API resources -To create the `cafe` Gateway, copy and paste the following into your terminal: +To create the **cafe** gateway, copy and paste the following into your terminal: ```yaml kubectl apply -f - <}}Your clients should be able to resolve the domain name `cafe.example.com` to the public IP of the NGINX Gateway Fabric. In this guide we will simulate that using curl's `--resolve` option. {{< /note >}} +{{< note >}}Your clients should be able to resolve the domain name "cafe.example.com" to the public IP of the NGINX Gateway Fabric. In this guide we will simulate that using curl's `--resolve` option. {{< /note >}} -First, let's send a request to the path `/`: +First, let's send a request to the path "/": ```shell curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/ ``` -We should get a response from one of the coffee Pods: +We should get a response from one of the **coffee** pods: ```text Server address: 10.12.0.18:8080 Server name: coffee-7dd75bc79b-cqvb7 ``` -Since the `cafe` HTTPRoute routes all traffic on any path to the coffee application, the following requests should also be handled by the coffee Pods: +Since the **cafe** HTTPRoute routes all traffic on any path to the **coffee** application, the following requests should also be handled by the **coffee** pods: ```shell curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/some-path @@ -205,7 +208,7 @@ Server address: 10.12.0.19:8080 Server name: coffee-7dd75bc79b-dett3 ``` -Requests to hostnames other than `cafe.example.com` should _not_ be routed to the coffee application, since the `cafe` HTTPRoute only matches requests with the `cafe.example.com` hostname. To verify this, send a request to the hostname `pub.example.com`: +Requests to hostnames other than "cafe.example.com" should _not_ be routed to the coffee application, since the **cafe** HTTPRoute only matches requests with the "cafe.example.com"need hostname. To verify this, send a request to the hostname "pub.example.com": ```shell curl --resolve pub.example.com:$GW_PORT:$GW_IP http://pub.example.com:$GW_PORT/ @@ -227,15 +230,15 @@ You should receive a 404 Not Found error: If you have any issues while testing the configuration, try the following to debug your configuration and setup: -- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are in the [installation documentation]({{< relref "/how-to/installation/installation.md#expose-nginx-gateway-fabric" >}}). +- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric service. Instructions for finding those values are in the [installation documentation]({{< relref "/how-to/installation/installation.md#expose-nginx-gateway-fabric" >}}). -- Check the status of the Gateway: +- Check the status of the gateway: ```shell kubectl describe gateway cafe ``` - The Gateway status should look similar to this: + The gateway status should look similar to this: ```text Status: @@ -285,7 +288,7 @@ If you have any issues while testing the configuration, try the following to deb Name: http ``` - Check that the conditions match and that the attached routes for the `http` listener equals 1. If it is 0, there may be an issue with the HTTPRoute. + Check that the conditions match and that the attached routes for the **http** listener equals 1. If it is 0, there may be an issue with the HTTPRoute. - Check the status of the HTTPRoute: @@ -324,10 +327,10 @@ If you have any issues while testing the configuration, try the following to deb - Check the generated nginx config: ```shell - kubectl exec -it -n nginx-gateway -c nginx -- nginx -T + kubectl exec -it -n nginx-gateway -c nginx -- nginx -T ``` - The config should contain a server block with the server name `cafe.example.com` that listens on port 80. This server block should have a single location `/` that proxy passes to the coffee upstream: + The config should contain a server block with the server name "cafe.example.com" that listens on port 80. This server block should have a single location `/` that proxy passes to the coffee upstream: ```nginx configuration server { @@ -343,12 +346,12 @@ If you have any issues while testing the configuration, try the following to deb } ``` - There should also be an upstream block with a name that matches the upstream in the `proxy_pass` directive. This upstream block should contain the Pod IPs of the coffee Pods: + There should also be an upstream block with a name that matches the upstream in the **proxy_pass** directive. This upstream block should contain the pod IPs of the **coffee** pods: ```nginx configuration upstream default_coffee_80 { ... - server 10.12.0.18:8080; # these should be the Pod IPs of the coffee Pods + server 10.12.0.18:8080; # these should be the pod IPs of the coffee pods server 10.12.0.19:8080; ... } @@ -360,7 +363,7 @@ If your issue persists, [contact us](https://github.com/nginxinc/nginx-gateway-f ## Further Reading -To learn more about the Gateway API and the resources we created in this guide, check out the following resources: +To learn more about the gateway API and the resources we created in this guide, check out the following resources: - [Gateway API Overview](https://gateway-api.sigs.k8s.io/concepts/api-overview/) - [Deploying a simple Gateway](https://gateway-api.sigs.k8s.io/guides/simple-gateway/) From 5911ef358594d8b50c47973ce852c3e5b9e268b2 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 16 Nov 2023 19:33:25 +0000 Subject: [PATCH 025/101] chore: rewrite upgrade apps without downtime --- .../upgrade-apps-without-downtime.md | 136 ++++++------------ 1 file changed, 40 insertions(+), 96 deletions(-) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index a30c69cad6..cdccfd5fdd 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -6,78 +6,46 @@ toc: true docs: "DOCS-000" --- -# Using NGINX Gateway Fabric to Upgrade Applications without Downtime +## Overview -This guide explains how to use NGINX Gateway Fabric to upgrade applications without downtime. +NGINX Gateway Fabric allows to upgrade applications without downtime. -Multiple upgrade methods are mentioned, assuming existing familiarity: this guide focuses primarily on how to use NGINX -Gateway Fabric to accomplish them. +{{< note >}}See the [Architecture document]({{< relref "/concepts/architecture.md" >}}) to learn more about NGINX Gateway Fabric architecture.{{< /note >}} -> See the [Architecture document](../architecture.md) to learn more about NGINX Gateway Fabric architecture. +## NGINX Gateway Fabric functionality -## NGINX Gateway Fabric Functionality +To understand the upgrade methods, you need to be familiar with the NGINX features that help prevent application downtime: Graceful configuration reloads and upstream server updates. -To understand the upgrade methods, you should be aware of the NGINX features that help prevent application downtime: -graceful configuration reloads and upstream server updates. +### Graceful configuration reloads -### Graceful Configuration Reloads +If a relevant gateway API or built-in Kubernetes resource is changed, NGINX Gateway Fabric will update NGINX by regenerating the NGINX configuration. NGINX Gateway Fabric then sends a reload signal to the master NGINX process to apply the new configuration. -If a relevant Gateway API or built-in Kubernetes resource is changed, NGINX Gateway Fabric will update NGINX by -regenerating the NGINX configuration. NGINX Gateway Fabric then sends a reload signal to the master NGINX process to -apply the new configuration. +We call such an operation a "reload", during which client requests are not dropped - which defines it as a graceful reload. -We call such an operation a reload, during which client requests are not dropped - which defines it as a graceful reload. +This process is further explained in the [NGINX configuration documentation](https://nginx.org/en/docs/control.html?#reconfiguration). -This process is further explained in [NGINX's documentation](https://nginx.org/en/docs/control.html?#reconfiguration). +### Upstream server updates -### Upstream Server Updates +Endpoints frequently change during application upgrades: Kubernetes creates pods for the new version of an application and removes the old ones, creating and removing the respective endpoints as well. -Endpoints frequently change during application upgrades: Kubernetes creates Pods for the new version of an application -and removes the old ones, creating and removing the respective Endpoints as well. +NGINX Gateway Fabric detects changes to endpoints by watching their corresponding [EndpointSlices](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/). -NGINX Gateway Fabric detects changes to Endpoints by watching their corresponding [EndpointSlices][endpoint-slices]. +In NGINX configuration, a service is represented as an [upstream](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream), and an endpoint as an [upstream server](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#server). -[endpoint-slices]:https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/ +Adding and removing endpoints are two of the most common cases: -In NGINX configuration, a Service is represented as an [upstream][upstream], and an Endpoint as an -[upstream server][upstream-server]. +- If an endpoint is added, NGINX Gateway Fabric adds an upstream server to NGINX that corresponds to the endpoint, then reloads NGINX. Next, NGINX will start proxying traffic to that endpoint. +- If an endpoint is removed, NGINX Gateway Fabric removes the corresponding upstream server from NGINX. After a reload, NGINX will stop proxying traffic to that server. However, it will finish proxying any pending requests to that server before switching to another endpoint. -[upstream]:https://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream +As long as you have more than one endpoint ready, the clients will not experience any downtime during upgrades. -[upstream-server]:https://nginx.org/en/docs/http/ngx_http_upstream_module.html#server +{{< note >}}It is good practice to configure a [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) in the deployment so that a pod can advertise when it is ready to receive traffic. Note that NGINX Gateway Fabric will not add any endpoint to NGINX that is not ready.{{< /note >}} -Two common cases are adding and removing Endpoints: +## Prerequisites -- If an Endpoint is added, NGINX Gateway Fabric adds an upstream server to NGINX that corresponds to the Endpoint, - then reload NGINX. After that, NGINX will start proxying traffic to that Endpoint. -- If an Endpoint is removed, NGINX Gateway Fabric removes the corresponding upstream server from NGINX. After - a reload, NGINX will stop proxying traffic to it. However, it will finish proxying any pending requests to that - server before switching to another Endpoint. - -As long as you have more than one ready Endpoint, the clients should not experience any downtime during upgrades. - -> It is good practice to configure a [Readiness probe][readiness-probe] in the Deployment so that a Pod can advertise -> when it is ready to receive traffic. Note that NGINX Gateway Fabric will not add any Endpoint to NGINX that is not -> ready. - -[readiness-probe]:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ - -## Before You Begin - -For the upgrade methods covered in the next sections, we make the following assumptions: - -- You deploy your application as a [Deployment][deployment]. -- The Pods of the Deployment belong to a [Service][service] so that Kubernetes creates an [Endpoint][endpoints] for - each Pod. -- You expose the application to the clients via an [HTTPRoute][httproute] resource that references that Service. - -[deployment]:https://kubernetes.io/docs/concepts/workloads/controllers/deployment/ - -[service]:https://kubernetes.io/docs/concepts/services-networking/service/ - -[httproute]:https://gateway-api.sigs.k8s.io/api-types/httproute/ - -[endpoints]:https://kubernetes.io/docs/reference/kubernetes-api/service-resources/endpoints-v1/ +- You have deployed your application as a [deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) +- The pods of the deployment belong to a [service](https://kubernetes.io/docs/concepts/services-networking/service/) so that Kubernetes creates an [endpoint](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/endpoints-v1/) for each pod. +- You have exposed the application to the clients via an [HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) resource that references that service. For example, an application can be exposed using a routing rule like below: @@ -91,58 +59,36 @@ For example, an application can be exposed using a routing rule like below: port: 80 ``` -> See the [Cafe example](../../examples/cafe-example) for a basic example. +{{< note >}}See the [Cafe example](https://github.com/nginxinc/nginx-gateway-fabric/blob/main/examples/cafe-example) for a basic example.{{< /note >}} The upgrade methods in the next sections cover: -- Rolling Deployment Upgrades -- Blue-green Deployments -- Canary Releases - -## Rolling Deployment Upgrade +- Rolling deployment upgrades +- Blue-green deployments +- Canary releases -To start a [rolling Deployment upgrade][rolling-upgrade], you update the Deployment to use the new version tag of -the application. As a result, Kubernetes terminates the Pods with the old version and create new ones. By default, -Kubernetes also ensures that some number of Pods always stay available during the upgrade. +## Rolling deployment upgrade -[rolling-upgrade]:https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#rolling-update-deployment +To start a [rolling deployment upgrade](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#rolling-update-deployment), you update the deployment to use the new version tag of the application. As a result, Kubernetes terminates the pods with the old version and create new ones. By default, Kubernetes also ensures that some number of pods always stay available during the upgrade. -Such an upgrade will add new upstream servers to NGINX and remove the old ones. As long as the number -of Pods (ready Endpoints) during an upgrade does not reach zero, NGINX will be able to proxy traffic, and thus prevent -any downtime. +This upgrade will add new upstream servers to NGINX and remove the old ones. As long as the number of pods (ready endpoints) during an upgrade does not reach zero, NGINX will be able to proxy traffic, and therefore prevent any downtime. -This method does not require you to update the HTTPRoute. +This method does not require you to update the **HTTPRoute**. -## Blue-Green Deployments +## Blue-green deployments -With this method, you deploy a new version of the application (blue version) as a separate Deployment, -while the old version (green) keeps running and handling client traffic. Next, you switch the traffic from the -green version to the blue. If the blue works as expected, you terminate the green. Otherwise, you switch the traffic -back to the green. +With this method, you deploy a new version of the application (blue version) as a separate deployment, while the old version (green) keeps running and handling client traffic. Next, you switch the traffic from the green version to the blue. If the blue works as expected, you terminate the green. Otherwise, you switch the traffic back to the green. There are two ways to switch the traffic: -- Update the Service selector to select the Pods of the blue version instead of the green. As a result, NGINX Gateway - Fabric removes the green upstream servers from NGINX and add the blue ones. With this approach, it is not - necessary to update the HTTPRoute. -- Create a separate Service for the blue version and update the backend reference in the HTTPRoute to reference this - Service, which leads to the same result as with the previous option. - -## Canary Releases +- Update the service selector to select the pods of the blue version instead of the green. As a result, NGINX Gateway Fabric removes the green upstream servers from NGINX and add the blue ones. With this approach, it is not necessary to update the **HTTPRoute**. +- Create a separate service for the blue version and update the backend reference in the **HTTPRoute** to reference this service, which leads to the same result as with the previous option. -To support canary releases, you can implement an approach with two Deployments behind the same Service (see -[Canary deployment][canary] in the Kubernetes documentation). However, this approach lacks precision for defining the -traffic split between the old and the canary version. You can greatly influence it by controlling the number of Pods -(for example, four Pods of the old version and one Pod of the canary). However, note that NGINX Gateway Fabric uses -[`random two least_conn`][random-method] load balancing method, which doesn't guarantee an exact split based on the -number of Pods (80/20 in the given example). +## Canary releases -[canary]:https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#canary-deployment -[random-method]:https://nginx.org/en/docs/http/ngx_http_upstream_module.html#random +To support canary releases, you can implement an approach with two deployments behind the same service (see [Canary deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#canary-deployment) in the Kubernetes documentation). However, this approach lacks precision for defining the traffic split between the old and the canary version. You can greatly influence it by controlling the number of pods (for example, four pods of the old version and one pod of the canary). However, note that NGINX Gateway Fabric uses [`random two least_conn`](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#random) load balancing method, which doesn't guarantee an exact split based on the number of pods (80/20 in the given example). -A more flexible and precise way to implement canary releases is to configure a traffic split in an HTTPRoute. In this -case, you create a separate Deployment for the new version with a separate Service. For example, for the rule below, -NGINX will proxy 95% of the traffic to the old version Endpoints and only 5% to the new ones. +A more flexible and precise way to implement canary releases is to configure a traffic split in an **HTTPRoute**. In this case, you create a separate deployment for the new version with a separate service. For example, for the rule below, NGINX will proxy 95% of the traffic to the old version endpoints and only 5% to the new ones. ```yaml - matches: @@ -158,11 +104,9 @@ NGINX will proxy 95% of the traffic to the old version Endpoints and only 5% to weight: 5 ``` -> There is no stickiness for the requests from the same client. NGINX will independently split each request among -> the backend references. +{{< note >}}There is no stickiness for the requests from the same client. NGINX will independently split each request among the backend references.{{< /note >}} -By updating the rule you can further increase the share of traffic the new version gets and finally completely switch -to the new version: +By updating the rule you can further increase the share of traffic the new version gets and finally completely switch to the new version: ```yaml - matches: @@ -178,4 +122,4 @@ to the new version: weight: 1 ``` -See the [Traffic splitting example](/examples/traffic-splitting) from our repository. +See the [Traffic splitting example](https://github.com/nginxinc/nginx-gateway-fabric/blob/main/examples/traffic-splitting) from our repository. From fe8a3746d57c88cb2022667c383fe17be70eb498 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 16 Nov 2023 19:38:27 +0000 Subject: [PATCH 026/101] fix: update documentation readme run from /site --- docs/developer/documentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer/documentation.md b/docs/developer/documentation.md index 9a54b5a976..a112418821 100644 --- a/docs/developer/documentation.md +++ b/docs/developer/documentation.md @@ -1,6 +1,6 @@ # NGINX Gateway Fabric Docs -The `/site` directory contains the user documentation for NGINX Gateway Fabric and the requirements for linting, building, and publishing the docs. +The `/site` directory contains the user documentation for NGINX Gateway Fabric and the requirements for linting, building, and publishing the docs. Run all the `hugo` commands below from this directory. We use [Hugo](https://gohugo.io/) to build the docs for NGINX, with the [nginx-hugo-theme](https://github.com/nginxinc/nginx-hugo-theme). From ff388c8cde216b3e1a04bba4d04882eb3efe8bf1 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 16 Nov 2023 19:54:57 +0000 Subject: [PATCH 027/101] chore: disable lint line --- .markdownlint-cli2.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index 0503c1fec0..416fbcedb1 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -5,10 +5,7 @@ config: style: dash no-hard-tabs: false no-multiple-blanks: false - line-length: - line_length: 120 - code_blocks: false - tables: false + line-length: false blanks-around-headers: false no-duplicate-heading: siblings_only: true From fb4f5846fcf2f2370debc2566b9f0f6dcac17d71 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 16 Nov 2023 20:03:28 +0000 Subject: [PATCH 028/101] chore: add custom-styles shortcode --- .../content/how-to/maintenance/upgrade-apps-without-downtime.md | 2 ++ .../how-to/traffic-management/routing-traffic-to-your-app.md | 1 + 2 files changed, 3 insertions(+) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index cdccfd5fdd..76a77cee96 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -6,6 +6,8 @@ toc: true docs: "DOCS-000" --- +{{}} + ## Overview NGINX Gateway Fabric allows to upgrade applications without downtime. diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index 9174cd9c1f..977ab537b4 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -5,6 +5,7 @@ weight: 100 toc: true docs: "DOCS-000" --- + {{}} ## Overview From c0b0c5c8ea115aeb97b6b811c82443b1d4377547 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Thu, 16 Nov 2023 17:34:01 -0800 Subject: [PATCH 029/101] reconfigured IA, added Installation dir --- site/content/concepts/_index.md | 4 +- site/content/how-to/installation/_index.md | 9 - .../traffic-management/advanced-routing.md | 6 +- .../integrating-cert-manager.md | 2 +- .../routing-traffic-to-your-app.md | 6 +- site/content/includes/index.md | 3 + .../includes/installation/expose-ngf.md | 13 + site/content/installation/_index.md | 9 + .../installation/building-the-images.md | 2 +- .../expose-nginx-gateway-fabric.md | 85 +++ .../installation/installing-ngf/_index.md | 9 + .../installing-ngf/helm.md} | 150 ++++- .../installation/installing-ngf/manifests.md | 515 ++++++++++++++++++ .../installation/running-on-kind.md | 0 site/content/reference/cli-help.md | 2 +- .../technical-specifications.md | 2 +- 16 files changed, 777 insertions(+), 40 deletions(-) delete mode 100644 site/content/how-to/installation/_index.md create mode 100644 site/content/includes/index.md create mode 100644 site/content/includes/installation/expose-ngf.md create mode 100644 site/content/installation/_index.md rename site/content/{how-to => }/installation/building-the-images.md (99%) create mode 100644 site/content/installation/expose-nginx-gateway-fabric.md create mode 100644 site/content/installation/installing-ngf/_index.md rename site/content/{how-to/installation/installation.md => installation/installing-ngf/helm.md} (65%) create mode 100644 site/content/installation/installing-ngf/manifests.md rename site/content/{how-to => }/installation/running-on-kind.md (100%) rename site/content/{concepts => reference}/technical-specifications.md (96%) diff --git a/site/content/concepts/_index.md b/site/content/concepts/_index.md index 7123cf5096..ea61fb588b 100644 --- a/site/content/concepts/_index.md +++ b/site/content/concepts/_index.md @@ -1,8 +1,8 @@ --- -title: "Concepts" +title: "Overview" description: weight: 100 -linkTitle: "Concepts" +linkTitle: "Overview" menu: docs: parent: NGINX Gateway Fabric diff --git a/site/content/how-to/installation/_index.md b/site/content/how-to/installation/_index.md deleted file mode 100644 index 4813f80328..0000000000 --- a/site/content/how-to/installation/_index.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: "Initial Setup" -description: -weight: 100 -linkTitle: "Initial Setup" -menu: - docs: - parent: How-To Guides ---- \ No newline at end of file diff --git a/site/content/how-to/traffic-management/advanced-routing.md b/site/content/how-to/traffic-management/advanced-routing.md index 9d0cf1d664..cb23fcd624 100644 --- a/site/content/how-to/traffic-management/advanced-routing.md +++ b/site/content/how-to/traffic-management/advanced-routing.md @@ -16,8 +16,8 @@ The goal is to create a set of rules that will result in client requests being s ## Prerequisites -- [Install]({{< relref "/how-to/installation/installation.md" >}}) NGINX Gateway Fabric. -- [Expose NGINX Gateway Fabric]({{< relref "/how-to/installation/installation.md#expose-nginx-gateway-fabric" >}}) and save the public IP +- [Install]({{< relref "/installation/" >}}) NGINX Gateway Fabric. +- [Expose NGINX Gateway Fabric]({{< relref "/installation/#expose-nginx-gateway-fabric" >}}) and save the public IP address and port of NGINX Gateway Fabric into shell variables: ```text @@ -234,7 +234,7 @@ This request should receive a response from the `tea-post` Pod. Any other type o If you have any issues while sending traffic, try the following to debug your configuration and setup: -- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are [here](/docs/how-to/installation/installation.md#expose-nginx-gateway-fabric). +- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are [here](/docs/installation/#expose-nginx-gateway-fabric). - Check the status of the Gateway: diff --git a/site/content/how-to/traffic-management/integrating-cert-manager.md b/site/content/how-to/traffic-management/integrating-cert-manager.md index cfa0ec759f..362abd1c32 100644 --- a/site/content/how-to/traffic-management/integrating-cert-manager.md +++ b/site/content/how-to/traffic-management/integrating-cert-manager.md @@ -18,7 +18,7 @@ Follow the steps in this guide to: - Administrator access to a Kubernetes cluster. - [Helm](https://helm.sh) and [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) must be installed locally. -- [NGINX Gateway Fabric deployed]({{< relref "/how-to/installation/installation.md" >}}) in the Kubernetes cluster. +- [NGINX Gateway Fabric deployed]({{< relref "/installation/" >}}) in the Kubernetes cluster. - A DNS-resolvable domain name is required. It must resolve to the public endpoint of the NGINX Gateway Fabric deployment, and this public endpoint must be an external IP address or alias accessible over the internet. The process here will depend on your DNS provider. This DNS name will need to be resolvable from the Let’s Encrypt servers, which may require that you wait for the record to propagate before it will work. ## Overview diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index c30b72da4b..261951d84b 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -10,8 +10,8 @@ In this guide you will learn how to route external traffic to your Kubernetes ap ## Prerequisites -- [Install]({{< relref "/how-to/installation/installation.md" >}}) NGINX Gateway Fabric. -- [Expose NGINX Gateway Fabric]({{< relref "/how-to/installation/installation.md#expose-nginx-gateway-fabric" >}}) and save the public IP address and port of NGINX Gateway Fabric into shell variables: +- [Install]({{< relref "installation/" >}}) NGINX Gateway Fabric. +- [Expose NGINX Gateway Fabric]({{< relref "installation/" >}}) and save the public IP address and port of NGINX Gateway Fabric into shell variables: ```text GW_IP=XXX.YYY.ZZZ.III @@ -227,7 +227,7 @@ You should receive a 404 Not Found error: If you have any issues while testing the configuration, try the following to debug your configuration and setup: -- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are in the [installation documentation]({{< relref "/how-to/installation/installation.md#expose-nginx-gateway-fabric" >}}). +- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are in the [installation documentation]({{< relref "installation/#expose-nginx-gateway-fabric" >}}). - Check the status of the Gateway: diff --git a/site/content/includes/index.md b/site/content/includes/index.md new file mode 100644 index 0000000000..ca03031f1e --- /dev/null +++ b/site/content/includes/index.md @@ -0,0 +1,3 @@ +--- +headless: true +--- diff --git a/site/content/includes/installation/expose-ngf.md b/site/content/includes/installation/expose-ngf.md new file mode 100644 index 0000000000..f9862d23de --- /dev/null +++ b/site/content/includes/installation/expose-ngf.md @@ -0,0 +1,13 @@ +--- +docs: +--- + +Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. + +{{}} +The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any Gateway [Listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports. +{{}} + +NGINX Gateway Fabric uses this service to update the **Addresses** field in the **Gateway Status** resource. A **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. + +This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). diff --git a/site/content/installation/_index.md b/site/content/installation/_index.md new file mode 100644 index 0000000000..7e9ffd8c24 --- /dev/null +++ b/site/content/installation/_index.md @@ -0,0 +1,9 @@ +--- +title: "Installation" +description: +weight: 200 +linkTitle: "Installation" +menu: + docs: + parent: NGINX Gateway Fabric +--- \ No newline at end of file diff --git a/site/content/how-to/installation/building-the-images.md b/site/content/installation/building-the-images.md similarity index 99% rename from site/content/how-to/installation/building-the-images.md rename to site/content/installation/building-the-images.md index 78370c3dca..ae9c0d2d5d 100644 --- a/site/content/how-to/installation/building-the-images.md +++ b/site/content/installation/building-the-images.md @@ -1,7 +1,7 @@ --- title: "Building NGINX Gateway Fabric and NGINX Images" description: "Learn how to build the NGINX Gateway Fabric and NGINX images." -weight: 200 +weight: 300 toc: true docs: "DOCS-000" --- diff --git a/site/content/installation/expose-nginx-gateway-fabric.md b/site/content/installation/expose-nginx-gateway-fabric.md new file mode 100644 index 0000000000..eaa67321bc --- /dev/null +++ b/site/content/installation/expose-nginx-gateway-fabric.md @@ -0,0 +1,85 @@ +--- +title: "Expose NGINX Gateway Fabric" +date: 2023-11-16T16:37:42-08:00 +# Change draft status to false to publish doc. +draft: false +# Description +# Add a short description (150 chars) for the doc. Include keywords for SEO. +# The description text appears in search results and at the top of the doc. +description: "" +# Assign weights in increments of 100 +weight: 300 +toc: true +tags: [ "docs" ] +# Create a new entry in the Jira DOCS Catalog and add the ticket ID (DOCS-) below +docs: "DOCS-000" +# Taxonomies +# These are pre-populated with all available terms for your convenience. +# Remove all terms that do not apply. +categories: ["installation", "platform management", "load balancing", "api management", "service mesh", "security", "analytics"] +doctypes: ["task"] +journeys: ["researching", "getting started", "using", "renewing", "self service"] +personas: ["devops", "netops", "secops", "support"] +versions: [] +authors: [] + +--- + +### Expose NGINX Gateway Fabric + + +Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. + +{{}} +The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any Gateway [Listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports. +{{}} + +NGINX Gateway Fabric uses this service to update the **Addresses** field in the **Gateway Status** resource. A **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. + +This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). + +#### Create a NodePort service + +To create a **NodePort** service: + +```shell +kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml +``` + +A **NodePort** service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. + +#### Create a LoadBalancer Service + +To create a **LoadBalancer** service, use the appropriate manifest for your cloud provider: + +- For GCP (Google Cloud Platform) or Azure: + + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml + ``` + + Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the following command: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + Use the public IP of the load balancer to access NGINX Gateway Fabric. + +- For AWS (Amazon Web Services): + + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml + ``` + + In AWS, the NLB (Network Load Balancer) DNS (directory name system) name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + Generally, use the NLB DNS name, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: + + ```shell + nslookup + ``` diff --git a/site/content/installation/installing-ngf/_index.md b/site/content/installation/installing-ngf/_index.md new file mode 100644 index 0000000000..b51a79d985 --- /dev/null +++ b/site/content/installation/installing-ngf/_index.md @@ -0,0 +1,9 @@ +--- +title: "Installing NGINX Gateway Fabric" +description: +weight: 200 +linkTitle: "Installing NGINX Gateway Fabric" +menu: + docs: + parent: Installation +--- \ No newline at end of file diff --git a/site/content/how-to/installation/installation.md b/site/content/installation/installing-ngf/helm.md similarity index 65% rename from site/content/how-to/installation/installation.md rename to site/content/installation/installing-ngf/helm.md index f559287f11..3c3a000d98 100644 --- a/site/content/how-to/installation/installation.md +++ b/site/content/installation/installing-ngf/helm.md @@ -1,5 +1,5 @@ --- -title: "Installing NGINX Gateway Fabric" +title: "Installation with Helm NGINX" description: "Learn how to install NGINX Gateway Fabric on a generic Kubernetes cluster." weight: 100 toc: true @@ -12,14 +12,25 @@ docs: "DOCS-000" - Install [kubectl](https://kubernetes.io/docs/tasks/tools/) -## Deploy NGINX Gateway Fabric using Helm +## Deploying NGINX Gateway Fabric + +{{}} + +{{%tab name="Helm"%}} + +### Deploying NGINX Gateway Fabric using Helm + Follow the instructions [to deploy NGINX Gateway Fabric using Helm](/deploy/helm-chart/README.md). -## Deploy NGINX Gateway Fabric from Manifests +{{%/tab%}} + +{{%tab name="Manifest"%}} -> **Note:** NGINX Gateway Fabric installs into the _nginx-gateway_ namespace by default. To run NGINX Gateway Fabric in a different namespace, modify the installation manifests. +### Deploy NGINX Gateway Fabric from Manifests + +{{}}NGINX Gateway Fabric installs into the **nginx-gateway** namespace by default. To run NGINX Gateway Fabric in a different namespace, modify the installation manifests.{{}} 1. Install the Gateway API resources (Custom Resource Definitions [CRDs] and validating webhook) from the standard channel: @@ -45,7 +56,7 @@ Follow the instructions [to deploy NGINX Gateway Fabric using Helm](/deploy/helm kubectl get pods -n nginx-gateway ``` - Expected output (note that _5d4f4c7db7-xk2kq_ is a randomly generated string and will vary): + Expected output (note that `5d4f4c7db7-xk2kq` is a randomly generated string and will vary): ```text NAME READY STATUS RESTARTS AGE @@ -53,31 +64,72 @@ Follow the instructions [to deploy NGINX Gateway Fabric using Helm](/deploy/helm ``` + +### Upgrade NGINX Gateway Fabric from Manifests + +Upgrading NGINX Gateway Fabric from manifests involves several steps to ensure all components are updated to their latest versions. + +1. **Upgrade Gateway Resources:** + - Check that the **Gateway API** resources are compatible with your version of NGINX Gateway Fabric ([Technical Specifications](/README.md#technical-specifications)). + - Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) for any important upgrade-specific information. + - To upgrade the gateway resources, run: + + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` + +2. **Upgrade NGINX Gateway Fabric CRDs:** + - To upgrade the Custom Resource Definitions (CRDs), run: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml + ``` + +3. **Upgrade NGINX Gateway Fabric Deployment:** + - To upgrade the deployment, run: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml + ``` + +{{%/tab%}} + +{{}} + + + + + + + ## Expose NGINX Gateway Fabric -Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the **--service** argument of the controller. +Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. {{}} The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any Gateway [Listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports. {{}} -NGINX Gateway Fabric utilizes this service to update the **Addresses** field in the **Gateway Status** resource. A LoadBalancer service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. +NGINX Gateway Fabric uses this service to update the **Addresses** field in the **Gateway Status** resource. A **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. + +This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). ### Create a NodePort service -To create a `NodePort` service: +To create a **NodePort** service: ```shell kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml ``` -A `NodePort` service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. +A **NodePort** service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. ### Create a LoadBalancer Service -To create a `LoadBalancer` service, use the appropriate manifest for your cloud provider: +To create a **LoadBalancer** service, use the appropriate manifest for your cloud provider: + +- For GCP (Google Cloud Platform) or Azure: -- For GCP or Azure: ```shell kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml ``` @@ -90,12 +142,13 @@ To create a `LoadBalancer` service, use the appropriate manifest for your cloud Use the public IP of the load balancer to access NGINX Gateway Fabric. -- For AWS: +- For AWS (Amazon Web Services): + ```shell kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml ``` - In AWS, the NLB DNS name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: + In AWS, the NLB (Network Load Balancer) DNS (directory name system) name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: ```shell kubectl get svc nginx-gateway -n nginx-gateway @@ -107,12 +160,71 @@ To create a `LoadBalancer` service, use the appropriate manifest for your cloud nslookup ``` + --- -This revision ensures that 'service,' 'hostname,' and 'namespace' are treated as common nouns and are in lowercase, as per your instruction. Let me know if this meets your needs or if we should proceed to the next section! +## Upgrading NGINX Gateway Fabric + +This section provides guidelines for upgrading your NGINX Gateway Fabric deployment to ensure you are using the latest features and improvements. + +{{}}For zero-downtime upgrades, follow the instructions to [configure a delayed pod termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades) for the NGINX Gateway Fabric pod.{{}} + + + +### Upgrade NGINX Gateway Fabric Using Helm + +For Helm-managed deployments, follow the [Helm upgrade instructions](/deploy/helm-chart/README.md#upgrading-the-chart). + +### Configure Delayed Pod Termination {#configure-delayed-pod-termination-for-zero-downtime-upgrades} + +Configuring delayed pod termination is crucial for ensuring zero downtime during upgrades, particularly in environments handling persistent or long-lived connections. The configuration settings required are the same for both Helm- and Manifest-based deployments, although the specific file to update will depend on your deployment type. + +{{}}NGINX won't shut down until all websocket or long-lived connections are closed. Keeping these connections open during an upgrade can lead to Kubernetes forcibly shutting down NGINX, potentially causing downtime for clients.{{}} + +#### For Helm-Based Deployments + +To configure delayed pod termination, follow these steps: + +1. Depending on your deployment type, update the `values.yaml` file for Helm-based deployments or the `nginx-gateway.yaml` file for Manifest-based deployments. + +1. Add `lifecycle: preStop` hooks to both `nginx` and `nginx-gateway` container definitions. These hooks delay the shutdown process to allow time for connections to close gracefully. + + ```yaml + <...> + name: nginx-gateway + <...> + lifecycle: + preStop: + exec: + command: + - /usr/bin/gateway + - sleep + - --duration=40s # This flag is optional, the default is 30s + <...> + name: nginx + <...> + lifecycle: + preStop: + exec: + command: + - /bin/sleep + - "40" + <...> + ``` + +1. Ensure `terminationGracePeriodSeconds` is equal to or greater than the `sleep` duration in the preStop hook (default is `30`). This is to ensure Kubernetes does not terminate the pod before the `preStop` Hook is complete. To do so, update your `values.yaml` or `nginx-gateway.yaml` file to include the following (update the value to what is required in your environment): + + ```yaml + terminationGracePeriodSeconds: 50 + ``` + +#### Using Helm to Configure Delayed Pod Termination + +For Helm-based deployments, follow the [Helm-specific instructions for extending the termination period](/deploy/helm-chart/README.md#configure-delayed-termination-for-zero-downtime-upgrades). + --- -Feel free to copy this text. Let me know if you need any further modifications or if we should proceed to the next section! +Feel free to copy this revised section. Let me know if you need any further modifications or if we should proceed to the next section! %%%% @@ -153,7 +265,7 @@ Feel free to copy this text. Let me know if you need any further modifications o ## Expose NGINX Gateway Fabric -You can gain access to NGINX Gateway Fabric by creating a `NodePort` Service or a `LoadBalancer` Service. +You can gain access to NGINX Gateway Fabric by creating a **NodePort** Service or a **LoadBalancer** Service. This Service must live in the same Namespace as the controller. The name of this Service is provided in the `--service` argument to the controller. @@ -171,18 +283,18 @@ Service sets the status field to the IP address and/or Hostname. If no Service e ### Create a NodePort Service -Create a Service with type `NodePort`: +Create a Service with type **NodePort**: ```shell kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml ``` -A `NodePort` Service will randomly allocate one port on every Node of the cluster. To access NGINX Gateway Fabric, +A **NodePort** Service will randomly allocate one port on every Node of the cluster. To access NGINX Gateway Fabric, use an IP address of any Node in the cluster along with the allocated port. ### Create a LoadBalancer Service -Create a Service with type `LoadBalancer` using the appropriate manifest for your cloud provider. +Create a Service with type **LoadBalancer** using the appropriate manifest for your cloud provider. - For GCP or Azure: diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md new file mode 100644 index 0000000000..6eb1c1421f --- /dev/null +++ b/site/content/installation/installing-ngf/manifests.md @@ -0,0 +1,515 @@ +--- +title: "Installation with Manifests" +description: "Learn how to install NGINX Gateway Fabric on a generic Kubernetes cluster." +weight: 100 +toc: true +docs: "DOCS-000" +--- + +{{}} + +## Prerequisites + +- Install [kubectl](https://kubernetes.io/docs/tasks/tools/) + +## Deploying NGINX Gateway Fabric + +{{}} + +{{%tab name="Helm"%}} + +### Deploying NGINX Gateway Fabric using Helm + + +Follow the instructions [to deploy NGINX Gateway Fabric using Helm](/deploy/helm-chart/README.md). + + +{{%/tab%}} + +{{%tab name="Manifest"%}} + +### Deploy NGINX Gateway Fabric from Manifests + +{{}}NGINX Gateway Fabric installs into the **nginx-gateway** namespace by default. To run NGINX Gateway Fabric in a different namespace, modify the installation manifests.{{}} + +1. Install the Gateway API resources (Custom Resource Definitions [CRDs] and validating webhook) from the standard channel: + + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` + +2. Deploy the NGINX Gateway Fabric CRDs: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml + ``` + +3. Deploy NGINX Gateway Fabric: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml + ``` + +4. Verify NGINX Gateway Fabric is running in the `nginx-gateway` namespace: + + ```shell + kubectl get pods -n nginx-gateway + ``` + + Expected output (note that `5d4f4c7db7-xk2kq` is a randomly generated string and will vary): + + ```text + NAME READY STATUS RESTARTS AGE + nginx-gateway-5d4f4c7db7-xk2kq 2/2 Running 0 112s + ``` + +### Expose NGINX Gateway Fabric + + +Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. + +{{}} +The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any Gateway [Listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports. +{{}} + +NGINX Gateway Fabric uses this service to update the **Addresses** field in the **Gateway Status** resource. A **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. + +This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). + +#### Create a NodePort service + +To create a **NodePort** service: + +```shell +kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml +``` + +A **NodePort** service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. + +#### Create a LoadBalancer Service + +To create a **LoadBalancer** service, use the appropriate manifest for your cloud provider: + +- For GCP (Google Cloud Platform) or Azure: + + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml + ``` + + Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the following command: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + Use the public IP of the load balancer to access NGINX Gateway Fabric. + +- For AWS (Amazon Web Services): + + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml + ``` + + In AWS, the NLB (Network Load Balancer) DNS (directory name system) name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + Generally, use the NLB DNS name, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: + + ```shell + nslookup + ``` + +### Upgrade NGINX Gateway Fabric from Manifests + +Upgrading NGINX Gateway Fabric from manifests involves several steps to ensure all components are updated to their latest versions. + +1. **Upgrade Gateway Resources:** + - Check that the **Gateway API** resources are compatible with your version of NGINX Gateway Fabric ([Technical Specifications](/README.md#technical-specifications)). + - Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) for any important upgrade-specific information. + - To upgrade the gateway resources, run: + + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` + +2. **Upgrade NGINX Gateway Fabric CRDs:** + - To upgrade the Custom Resource Definitions (CRDs), run: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml + ``` + +3. **Upgrade NGINX Gateway Fabric Deployment:** + - To upgrade the deployment, run: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml + ``` + +{{%/tab%}} + +{{}} + + + + + + + +## Expose NGINX Gateway Fabric + +Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. + +{{}} +The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any Gateway [Listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports. +{{}} + +NGINX Gateway Fabric uses this service to update the **Addresses** field in the **Gateway Status** resource. A **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. + +This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). + +### Create a NodePort service + +To create a **NodePort** service: + +```shell +kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml +``` + +A **NodePort** service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. + +### Create a LoadBalancer Service + +To create a **LoadBalancer** service, use the appropriate manifest for your cloud provider: + +- For GCP (Google Cloud Platform) or Azure: + + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml + ``` + + Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the following command: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + Use the public IP of the load balancer to access NGINX Gateway Fabric. + +- For AWS (Amazon Web Services): + + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml + ``` + + In AWS, the NLB (Network Load Balancer) DNS (directory name system) name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + Generally, use the NLB DNS name, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: + + ```shell + nslookup + ``` + + +--- + +## Upgrading NGINX Gateway Fabric + +This section provides guidelines for upgrading your NGINX Gateway Fabric deployment to ensure you are using the latest features and improvements. + +{{}}For zero-downtime upgrades, follow the instructions to [configure a delayed pod termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades) for the NGINX Gateway Fabric pod.{{}} + + + +### Upgrade NGINX Gateway Fabric Using Helm + +For Helm-managed deployments, follow the [Helm upgrade instructions](/deploy/helm-chart/README.md#upgrading-the-chart). + +### Configure Delayed Pod Termination {#configure-delayed-pod-termination-for-zero-downtime-upgrades} + +Configuring delayed pod termination is crucial for ensuring zero downtime during upgrades, particularly in environments handling persistent or long-lived connections. The configuration settings required are the same for both Helm- and Manifest-based deployments, although the specific file to update will depend on your deployment type. + +{{}}NGINX won't shut down until all websocket or long-lived connections are closed. Keeping these connections open during an upgrade can lead to Kubernetes forcibly shutting down NGINX, potentially causing downtime for clients.{{}} + +#### For Helm-Based Deployments + +To configure delayed pod termination, follow these steps: + +1. Depending on your deployment type, update the `values.yaml` file for Helm-based deployments or the `nginx-gateway.yaml` file for Manifest-based deployments. + +1. Add `lifecycle: preStop` hooks to both `nginx` and `nginx-gateway` container definitions. These hooks delay the shutdown process to allow time for connections to close gracefully. + + ```yaml + <...> + name: nginx-gateway + <...> + lifecycle: + preStop: + exec: + command: + - /usr/bin/gateway + - sleep + - --duration=40s # This flag is optional, the default is 30s + <...> + name: nginx + <...> + lifecycle: + preStop: + exec: + command: + - /bin/sleep + - "40" + <...> + ``` + +1. Ensure `terminationGracePeriodSeconds` is equal to or greater than the `sleep` duration in the preStop hook (default is `30`). This is to ensure Kubernetes does not terminate the pod before the `preStop` Hook is complete. To do so, update your `values.yaml` or `nginx-gateway.yaml` file to include the following (update the value to what is required in your environment): + + ```yaml + terminationGracePeriodSeconds: 50 + ``` + +#### Using Helm to Configure Delayed Pod Termination + +For Helm-based deployments, follow the [Helm-specific instructions for extending the termination period](/deploy/helm-chart/README.md#configure-delayed-termination-for-zero-downtime-upgrades). + +--- + +Feel free to copy this revised section. Let me know if you need any further modifications or if we should proceed to the next section! + +%%%% + +## Deploy NGINX Gateway Fabric from Manifests + +> Note: By default, NGINX Gateway Fabric (NGF) will be installed into the nginx-gateway Namespace. +> It is possible to run NGF in a different Namespace, although you'll need to make modifications to the installation +> manifests. + +1. Install the Gateway API resources from the standard channel (the CRDs and the validating webhook): + + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` + +1. Deploy the NGINX Gateway Fabric CRDs: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml + ``` + +1. Deploy the NGINX Gateway Fabric: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml + ``` + +1. Confirm the NGINX Gateway Fabric is running in `nginx-gateway` namespace: + + ```shell + kubectl get pods -n nginx-gateway + ``` + + ```text + NAME READY STATUS RESTARTS AGE + nginx-gateway-5d4f4c7db7-xk2kq 2/2 Running 0 112s + ``` + +## Expose NGINX Gateway Fabric + +You can gain access to NGINX Gateway Fabric by creating a **NodePort** Service or a **LoadBalancer** Service. +This Service must live in the same Namespace as the controller. The name of this Service is provided in +the `--service` argument to the controller. + +> **Important** +> The Service manifests expose NGINX Gateway Fabric on ports 80 and 443, which exposes any +> Gateway [Listener](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) +> configured for those ports. If you'd like to use different ports in your listeners, +> update the manifests accordingly. +> +> Additionally, NGINX Gateway Fabric will not listen on any ports until you configure a +[Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener. + +NGINX Gateway Fabric will use this Service to set the Addresses field in the Gateway Status resource. A LoadBalancer +Service sets the status field to the IP address and/or Hostname. If no Service exists, the Pod IP address is used. + +### Create a NodePort Service + +Create a Service with type **NodePort**: + +```shell +kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml +``` + +A **NodePort** Service will randomly allocate one port on every Node of the cluster. To access NGINX Gateway Fabric, +use an IP address of any Node in the cluster along with the allocated port. + +### Create a LoadBalancer Service + +Create a Service with type **LoadBalancer** using the appropriate manifest for your cloud provider. + +- For GCP or Azure: + + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml + ``` + + Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the + following command: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + Use the public IP of the load balancer to access NGINX Gateway Fabric. + +- For AWS: + + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml + ``` + + In AWS, the NLB DNS name will be reported by Kubernetes in lieu of a public IP in the `EXTERNAL-IP` column. To get the + DNS name run: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + In general, you should rely on the NLB DNS name, however for testing purposes you can resolve the DNS name to get the + IP address of the load balancer: + + ```shell + nslookup + ``` + +## Upgrading NGINX Gateway Fabric + +> **Note** +> See [below](#configure-delayed-termination-for-zero-downtime-upgrades) for instructions on how to configure delayed +> termination if required for zero downtime upgrades in your environment. + +### Upgrade NGINX Gateway Fabric from Manifests + +1. Upgrade the Gateway Resources + + Before you upgrade, ensure the Gateway API resources are the correct version as supported by the NGINX Gateway + Fabric - [see the Technical Specifications](/README.md#technical-specifications). + The [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) of the new version of the + Gateway API might include important upgrade-specific notes and instructions. We advise to check the release notes of + all versions between the one you're using and the new one. + + To upgrade the Gateway resources from [the Gateway API repo](https://github.com/kubernetes-sigs/gateway-api), run: + + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` + +1. Upgrade the NGINX Gateway Fabric CRDs + + Run the following command to upgrade the NGINX Gateway Fabric CRDs: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml + ``` + +1. Upgrade NGINX Gateway Fabric Deployment + + Run the following command to upgrade NGINX Gateway Fabric: + + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml + ``` + +### Upgrade NGINX Gateway Fabric using Helm + +To upgrade NGINX Gateway Fabric when the deployment method is Helm, please follow the instructions +[here](/deploy/helm-chart/README.md#upgrading-the-chart). + +### Configure Delayed Termination for Zero Downtime Upgrades + +To achieve zero downtime upgrades (meaning clients will not see any interruption in traffic while a rolling upgrade is +being performed on NGF), you may need to configure delayed termination on the NGF Pod, depending on your environment. + +> **Note** +> When proxying Websocket or any long-lived connections, NGINX will not terminate until that connection is closed +> by either the client or the backend. This means that unless all those connections are closed by clients/backends +> before or during an upgrade, NGINX will not terminate, which means Kubernetes will kill NGINX. As a result, the +> clients will see the connections abruptly closed and thus experience downtime. + +#### Configure Delayed Termination Using Manifests + +Edit the `nginx-gateway.yaml` to include the following: + +1. Add `lifecycle` prestop hooks to both the nginx and the nginx-gateway container definitions: + + ```yaml + <...> + name: nginx-gateway + <...> + lifecycle: + preStop: + exec: + command: + - /usr/bin/gateway + - sleep + - --duration=40s # This flag is optional, the default is 30s + <...> + name: nginx + <...> + lifecycle: + preStop: + exec: + command: + - /bin/sleep + - "40" + <...> + ``` + +2. Ensure the `terminationGracePeriodSeconds` matches or exceeds the `sleep` value from the `preStopHook` (the default + is 30). This is to ensure Kubernetes does not terminate the Pod before the `preStopHook` is complete. + +> **Note** +> More information on container lifecycle hooks can be found +> [here](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) and a detailed +> description of Pod termination behavior can be found in +> [Termination of Pods](https://kubernetes.io/docs/concepts/workloads/Pods/Pod-lifecycle/#Pod-termination). + +#### Configure Delayed Termination Using Helm + +To configure delayed termination on the NGF Pod when the deployment method is Helm, please follow the instructions +[here](/deploy/helm-chart/README.md#configure-delayed-termination-for-zero-downtime-upgrades). + +## Uninstalling NGINX Gateway Fabric + +### Uninstall NGINX Gateway Fabric from Manifests + +1. Uninstall the NGINX Gateway Fabric: + + ```shell + kubectl delete -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml + ``` + + ```shell + kubectl delete -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml + ``` + +1. Uninstall the Gateway API resources from the standard channel (the CRDs and the validating webhook): + + >**Warning: This command will delete all the corresponding custom resources in your cluster across all namespaces! + Please ensure there are no custom resources that you want to keep and there are no other Gateway API implementations + running in the cluster!** + + ```shell + kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` + +### Uninstall NGINX Gateway Fabric using Helm + +To uninstall NGINX Gateway Fabric when the deployment method is Helm, please follow the instructions +[here](/deploy/helm-chart/README.md#uninstalling-the-chart). diff --git a/site/content/how-to/installation/running-on-kind.md b/site/content/installation/running-on-kind.md similarity index 100% rename from site/content/how-to/installation/running-on-kind.md rename to site/content/installation/running-on-kind.md diff --git a/site/content/reference/cli-help.md b/site/content/reference/cli-help.md index 8d4cffafc5..74b5a9d734 100644 --- a/site/content/reference/cli-help.md +++ b/site/content/reference/cli-help.md @@ -1,7 +1,7 @@ --- title: "Command-line Reference Guide" description: "Learn about the commands available in the gateway binary of the nginx-gateway container." -weight: 400 +weight: 100 toc: true docs: "DOCS-000" --- diff --git a/site/content/concepts/technical-specifications.md b/site/content/reference/technical-specifications.md similarity index 96% rename from site/content/concepts/technical-specifications.md rename to site/content/reference/technical-specifications.md index b688b050f2..bca7430608 100644 --- a/site/content/concepts/technical-specifications.md +++ b/site/content/reference/technical-specifications.md @@ -2,7 +2,7 @@ title: "Technical Specifications" draft: false description: "NGINX Gateway Fabric technical specifications." -weight: 800 +weight: 200 toc: true tags: [ "docs" ] docs: "DOCS-000" From 2bd0d5e4e4b36f9d7a4a264fc77628a4e084b39e Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Fri, 17 Nov 2023 11:16:57 -0800 Subject: [PATCH 030/101] edits to Manifests install guide --- .../create-loadbalancer-service.md | 37 ++ .../installation/create-nodeport-service.md | 11 + .../installation/installing-ngf/helm.md | 4 +- .../installation/installing-ngf/manifests.md | 429 ++---------------- 4 files changed, 90 insertions(+), 391 deletions(-) create mode 100644 site/content/includes/installation/create-loadbalancer-service.md create mode 100644 site/content/includes/installation/create-nodeport-service.md diff --git a/site/content/includes/installation/create-loadbalancer-service.md b/site/content/includes/installation/create-loadbalancer-service.md new file mode 100644 index 0000000000..74cab5eba8 --- /dev/null +++ b/site/content/includes/installation/create-loadbalancer-service.md @@ -0,0 +1,37 @@ +--- +docs: +--- + +To create a **LoadBalancer** service, use the appropriate manifest for your cloud provider: + +- For GCP (Google Cloud Platform) or Azure: + + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml + ``` + + Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the following command: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + Use the public IP of the load balancer to access NGINX Gateway Fabric. + +- For AWS (Amazon Web Services): + + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml + ``` + + In AWS, the NLB (Network Load Balancer) DNS (directory name system) name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: + + ```shell + kubectl get svc nginx-gateway -n nginx-gateway + ``` + + Generally, use the NLB DNS name, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: + + ```shell + nslookup + ``` diff --git a/site/content/includes/installation/create-nodeport-service.md b/site/content/includes/installation/create-nodeport-service.md new file mode 100644 index 0000000000..8805969134 --- /dev/null +++ b/site/content/includes/installation/create-nodeport-service.md @@ -0,0 +1,11 @@ +--- +docs: +--- + +To create a **NodePort** service: + +```shell +kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml +``` + +A **NodePort** service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index 3c3a000d98..1169c7c600 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -1,7 +1,7 @@ --- -title: "Installation with Helm NGINX" +title: "Installation with Helm" description: "Learn how to install NGINX Gateway Fabric on a generic Kubernetes cluster." -weight: 100 +weight: 200 toc: true docs: "DOCS-000" --- diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index 6eb1c1421f..e3f1d48c71 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -1,6 +1,6 @@ --- title: "Installation with Manifests" -description: "Learn how to install NGINX Gateway Fabric on a generic Kubernetes cluster." +description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric using manifest deployments in a Kubernetes cluster. This guide offers clear, step-by-step instructions to get you started." weight: 100 toc: true docs: "DOCS-000" @@ -10,29 +10,18 @@ docs: "DOCS-000" ## Prerequisites -- Install [kubectl](https://kubernetes.io/docs/tasks/tools/) +In order to complete the steps in this guide, you must first: -## Deploying NGINX Gateway Fabric +- Install [kubectl](https://kubernetes.io/docs/tasks/tools/), a command-line interface for managing Kubernetes clusters. -{{}} -{{%tab name="Helm"%}} - -### Deploying NGINX Gateway Fabric using Helm - - -Follow the instructions [to deploy NGINX Gateway Fabric using Helm](/deploy/helm-chart/README.md). - - -{{%/tab%}} - -{{%tab name="Manifest"%}} +## Deploy NGINX Gateway Fabric from Manifests -### Deploy NGINX Gateway Fabric from Manifests +Deploying NGINX Gateway Fabric using Kubernetes manifests is a straightforward process that involves setting up necessary resources and deploying NGINX Gateway Fabric components within your cluster. This method allows for a detailed and controlled deployment, suitable for environments where customization and precise configuration are required. {{}}NGINX Gateway Fabric installs into the **nginx-gateway** namespace by default. To run NGINX Gateway Fabric in a different namespace, modify the installation manifests.{{}} -1. Install the Gateway API resources (Custom Resource Definitions [CRDs] and validating webhook) from the standard channel: +1. Install the Gateway API resources from the standard channel (the CRDs and validating webhook): ```shell kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml @@ -50,7 +39,7 @@ Follow the instructions [to deploy NGINX Gateway Fabric using Helm](/deploy/helm kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml ``` -4. Verify NGINX Gateway Fabric is running in the `nginx-gateway` namespace: +4. Confirm the NGINX Gateway Fabric is running in the `nginx-gateway` namespace: ```shell kubectl get pods -n nginx-gateway @@ -63,66 +52,11 @@ Follow the instructions [to deploy NGINX Gateway Fabric using Helm](/deploy/helm nginx-gateway-5d4f4c7db7-xk2kq 2/2 Running 0 112s ``` -### Expose NGINX Gateway Fabric - - -Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. - -{{}} -The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any Gateway [Listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports. -{{}} - -NGINX Gateway Fabric uses this service to update the **Addresses** field in the **Gateway Status** resource. A **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. - -This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). - -#### Create a NodePort service - -To create a **NodePort** service: - -```shell -kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml -``` - -A **NodePort** service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. - -#### Create a LoadBalancer Service - -To create a **LoadBalancer** service, use the appropriate manifest for your cloud provider: - -- For GCP (Google Cloud Platform) or Azure: - - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml - ``` - - Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the following command: - - ```shell - kubectl get svc nginx-gateway -n nginx-gateway - ``` +## Upgrade NGINX Gateway Fabric from Manifests - Use the public IP of the load balancer to access NGINX Gateway Fabric. - -- For AWS (Amazon Web Services): - - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml - ``` - - In AWS, the NLB (Network Load Balancer) DNS (directory name system) name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: - - ```shell - kubectl get svc nginx-gateway -n nginx-gateway - ``` - - Generally, use the NLB DNS name, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: - - ```shell - nslookup - ``` +This section provides guidelines for upgrading your NGINX Gateway Fabric deployment to ensure you are using the latest features and improvements. -### Upgrade NGINX Gateway Fabric from Manifests +{{}}For zero-downtime upgrades, follow the instructions to [configure a delayed pod termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades) for the NGINX Gateway Fabric pod.{{}} Upgrading NGINX Gateway Fabric from manifests involves several steps to ensure all components are updated to their latest versions. @@ -149,102 +83,19 @@ Upgrading NGINX Gateway Fabric from manifests involves several steps to ensure a kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml ``` -{{%/tab%}} - -{{}} - - - - - - - -## Expose NGINX Gateway Fabric - -Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. - -{{}} -The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any Gateway [Listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports. -{{}} - -NGINX Gateway Fabric uses this service to update the **Addresses** field in the **Gateway Status** resource. A **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. -This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). -### Create a NodePort service +### Configure Delayed Pod Termination for Zero-Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} -To create a **NodePort** service: - -```shell -kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml -``` - -A **NodePort** service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. - -### Create a LoadBalancer Service - -To create a **LoadBalancer** service, use the appropriate manifest for your cloud provider: - -- For GCP (Google Cloud Platform) or Azure: - - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml - ``` - - Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the following command: - - ```shell - kubectl get svc nginx-gateway -n nginx-gateway - ``` - - Use the public IP of the load balancer to access NGINX Gateway Fabric. - -- For AWS (Amazon Web Services): - - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml - ``` - - In AWS, the NLB (Network Load Balancer) DNS (directory name system) name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: - - ```shell - kubectl get svc nginx-gateway -n nginx-gateway - ``` - - Generally, use the NLB DNS name, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: - - ```shell - nslookup - ``` - - ---- - -## Upgrading NGINX Gateway Fabric - -This section provides guidelines for upgrading your NGINX Gateway Fabric deployment to ensure you are using the latest features and improvements. - -{{}}For zero-downtime upgrades, follow the instructions to [configure a delayed pod termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades) for the NGINX Gateway Fabric pod.{{}} - - - -### Upgrade NGINX Gateway Fabric Using Helm - -For Helm-managed deployments, follow the [Helm upgrade instructions](/deploy/helm-chart/README.md#upgrading-the-chart). - -### Configure Delayed Pod Termination {#configure-delayed-pod-termination-for-zero-downtime-upgrades} - -Configuring delayed pod termination is crucial for ensuring zero downtime during upgrades, particularly in environments handling persistent or long-lived connections. The configuration settings required are the same for both Helm- and Manifest-based deployments, although the specific file to update will depend on your deployment type. +In order to achieve zero-downtime upgrades and maintain continuous service availability, it's important to configure delayed pod termination. This setup is especially critical in environments that manage persistent or long-lived connections. {{}}NGINX won't shut down until all websocket or long-lived connections are closed. Keeping these connections open during an upgrade can lead to Kubernetes forcibly shutting down NGINX, potentially causing downtime for clients.{{}} -#### For Helm-Based Deployments - -To configure delayed pod termination, follow these steps: +1. Open the `nginx-gateway.yaml` for editing. -1. Depending on your deployment type, update the `values.yaml` file for Helm-based deployments or the `nginx-gateway.yaml` file for Manifest-based deployments. +1. **Add delayed shutdown hooks**: -1. Add `lifecycle: preStop` hooks to both `nginx` and `nginx-gateway` container definitions. These hooks delay the shutdown process to allow time for connections to close gracefully. + In the `nginx-gateway.yaml` file, add `lifecycle: preStop` hooks to both the `nginx` and `nginx-gateway` container definitions. These hooks instruct the containers to delay their shutdown process, allowing time for connections to close gracefully. ```yaml <...> @@ -269,247 +120,47 @@ To configure delayed pod termination, follow these steps: <...> ``` -1. Ensure `terminationGracePeriodSeconds` is equal to or greater than the `sleep` duration in the preStop hook (default is `30`). This is to ensure Kubernetes does not terminate the pod before the `preStop` Hook is complete. To do so, update your `values.yaml` or `nginx-gateway.yaml` file to include the following (update the value to what is required in your environment): +1. **Set the termination grace period**: + + Set `terminationGracePeriodSeconds` to a value that is equal to or greater than the `sleep` duration specified in the `preStop` hook (default is `30`). This setting prevents Kubernetes from terminating the pod before before the `preStop` hook has completed running. ```yaml terminationGracePeriodSeconds: 50 ``` -#### Using Helm to Configure Delayed Pod Termination +1. Save the changes. -For Helm-based deployments, follow the [Helm-specific instructions for extending the termination period](/deploy/helm-chart/README.md#configure-delayed-termination-for-zero-downtime-upgrades). ---- - -Feel free to copy this revised section. Let me know if you need any further modifications or if we should proceed to the next section! -%%%% +{{}} +For additional information on configuring and understanding the behavior of containers and pods during their lifecycle, refer to the following Kubernetes documentation: +- [Container Lifecycle Hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) +- [Pod Lifecycle](https://kubernetes.io/docs/concepts/workloads/Pods/Pod-lifecycle/#Pod-termination) +{{}} -## Deploy NGINX Gateway Fabric from Manifests -> Note: By default, NGINX Gateway Fabric (NGF) will be installed into the nginx-gateway Namespace. -> It is possible to run NGF in a different Namespace, although you'll need to make modifications to the installation -> manifests. +## Uninstall NGINX Gateway Fabric from Manifests -1. Install the Gateway API resources from the standard channel (the CRDs and the validating webhook): +Uninstalling NGINX Gateway Fabric from your Kubernetes cluster involves removing the deployed NGINX Gateway Fabric components and the Gateway API resources. This procedure should be followed carefully to ensure that all relevant components are cleanly removed from your system. - ```shell - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml - ``` +1. **Uninstall NGINX Gateway Fabric:** -1. Deploy the NGINX Gateway Fabric CRDs: + - Run the following commands to remove the NGINX Gateway Fabric and its Custom Resource Definitions (CRDs): - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml - ``` - -1. Deploy the NGINX Gateway Fabric: - - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml - ``` - -1. Confirm the NGINX Gateway Fabric is running in `nginx-gateway` namespace: - - ```shell - kubectl get pods -n nginx-gateway - ``` - - ```text - NAME READY STATUS RESTARTS AGE - nginx-gateway-5d4f4c7db7-xk2kq 2/2 Running 0 112s - ``` - -## Expose NGINX Gateway Fabric - -You can gain access to NGINX Gateway Fabric by creating a **NodePort** Service or a **LoadBalancer** Service. -This Service must live in the same Namespace as the controller. The name of this Service is provided in -the `--service` argument to the controller. - -> **Important** -> The Service manifests expose NGINX Gateway Fabric on ports 80 and 443, which exposes any -> Gateway [Listener](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) -> configured for those ports. If you'd like to use different ports in your listeners, -> update the manifests accordingly. -> -> Additionally, NGINX Gateway Fabric will not listen on any ports until you configure a -[Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener. - -NGINX Gateway Fabric will use this Service to set the Addresses field in the Gateway Status resource. A LoadBalancer -Service sets the status field to the IP address and/or Hostname. If no Service exists, the Pod IP address is used. - -### Create a NodePort Service - -Create a Service with type **NodePort**: - -```shell -kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml -``` - -A **NodePort** Service will randomly allocate one port on every Node of the cluster. To access NGINX Gateway Fabric, -use an IP address of any Node in the cluster along with the allocated port. - -### Create a LoadBalancer Service - -Create a Service with type **LoadBalancer** using the appropriate manifest for your cloud provider. - -- For GCP or Azure: - - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml - ``` - - Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the - following command: - - ```shell - kubectl get svc nginx-gateway -n nginx-gateway - ``` - - Use the public IP of the load balancer to access NGINX Gateway Fabric. - -- For AWS: - - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml - ``` - - In AWS, the NLB DNS name will be reported by Kubernetes in lieu of a public IP in the `EXTERNAL-IP` column. To get the - DNS name run: - - ```shell - kubectl get svc nginx-gateway -n nginx-gateway - ``` - - In general, you should rely on the NLB DNS name, however for testing purposes you can resolve the DNS name to get the - IP address of the load balancer: - - ```shell - nslookup - ``` - -## Upgrading NGINX Gateway Fabric - -> **Note** -> See [below](#configure-delayed-termination-for-zero-downtime-upgrades) for instructions on how to configure delayed -> termination if required for zero downtime upgrades in your environment. - -### Upgrade NGINX Gateway Fabric from Manifests - -1. Upgrade the Gateway Resources - - Before you upgrade, ensure the Gateway API resources are the correct version as supported by the NGINX Gateway - Fabric - [see the Technical Specifications](/README.md#technical-specifications). - The [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) of the new version of the - Gateway API might include important upgrade-specific notes and instructions. We advise to check the release notes of - all versions between the one you're using and the new one. - - To upgrade the Gateway resources from [the Gateway API repo](https://github.com/kubernetes-sigs/gateway-api), run: - - ```shell - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml - ``` - -1. Upgrade the NGINX Gateway Fabric CRDs - - Run the following command to upgrade the NGINX Gateway Fabric CRDs: - - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml - ``` - -1. Upgrade NGINX Gateway Fabric Deployment - - Run the following command to upgrade NGINX Gateway Fabric: - - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml - ``` - -### Upgrade NGINX Gateway Fabric using Helm - -To upgrade NGINX Gateway Fabric when the deployment method is Helm, please follow the instructions -[here](/deploy/helm-chart/README.md#upgrading-the-chart). - -### Configure Delayed Termination for Zero Downtime Upgrades - -To achieve zero downtime upgrades (meaning clients will not see any interruption in traffic while a rolling upgrade is -being performed on NGF), you may need to configure delayed termination on the NGF Pod, depending on your environment. - -> **Note** -> When proxying Websocket or any long-lived connections, NGINX will not terminate until that connection is closed -> by either the client or the backend. This means that unless all those connections are closed by clients/backends -> before or during an upgrade, NGINX will not terminate, which means Kubernetes will kill NGINX. As a result, the -> clients will see the connections abruptly closed and thus experience downtime. - -#### Configure Delayed Termination Using Manifests - -Edit the `nginx-gateway.yaml` to include the following: - -1. Add `lifecycle` prestop hooks to both the nginx and the nginx-gateway container definitions: - - ```yaml - <...> - name: nginx-gateway - <...> - lifecycle: - preStop: - exec: - command: - - /usr/bin/gateway - - sleep - - --duration=40s # This flag is optional, the default is 30s - <...> - name: nginx - <...> - lifecycle: - preStop: - exec: - command: - - /bin/sleep - - "40" - <...> - ``` - -2. Ensure the `terminationGracePeriodSeconds` matches or exceeds the `sleep` value from the `preStopHook` (the default - is 30). This is to ensure Kubernetes does not terminate the Pod before the `preStopHook` is complete. - -> **Note** -> More information on container lifecycle hooks can be found -> [here](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) and a detailed -> description of Pod termination behavior can be found in -> [Termination of Pods](https://kubernetes.io/docs/concepts/workloads/Pods/Pod-lifecycle/#Pod-termination). - -#### Configure Delayed Termination Using Helm - -To configure delayed termination on the NGF Pod when the deployment method is Helm, please follow the instructions -[here](/deploy/helm-chart/README.md#configure-delayed-termination-for-zero-downtime-upgrades). - -## Uninstalling NGINX Gateway Fabric - -### Uninstall NGINX Gateway Fabric from Manifests - -1. Uninstall the NGINX Gateway Fabric: - - ```shell - kubectl delete -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml - ``` - - ```shell - kubectl delete -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml - ``` + ```shell + kubectl delete -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml + ``` -1. Uninstall the Gateway API resources from the standard channel (the CRDs and the validating webhook): + ```shell + kubectl delete -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml + ``` - >**Warning: This command will delete all the corresponding custom resources in your cluster across all namespaces! - Please ensure there are no custom resources that you want to keep and there are no other Gateway API implementations - running in the cluster!** +2. **Remove the Gateway API resources:** - ```shell - kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml - ``` + - To uninstall the Gateway API resources, including the CRDs and the validating webhook, use the command below. Ensure no custom resources you wish to keep or other Gateway API implementations are running in the cluster before proceeding: -### Uninstall NGINX Gateway Fabric using Helm + {{}}This command will remove all corresponding custom resources in your cluster across all namespaces!{{}} -To uninstall NGINX Gateway Fabric when the deployment method is Helm, please follow the instructions -[here](/deploy/helm-chart/README.md#uninstalling-the-chart). + ```shell + kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` From d30d51ed20e01eb7cb405cb4e6b4a6619ccd6c59 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Fri, 17 Nov 2023 16:05:57 -0800 Subject: [PATCH 031/101] chore: edits to Heml install --- .../create-loadbalancer-service.md | 37 -- .../installation/create-nodeport-service.md | 11 - .../includes/installation/expose-ngf.md | 13 - .../installation/helm/pulling-the-chart.md | 12 + .../helm/uninstall-gateway-api-resources.md | 11 + .../installation/installing-ngf/helm.md | 464 ++++-------------- .../installation/installing-ngf/manifests.md | 32 +- 7 files changed, 140 insertions(+), 440 deletions(-) delete mode 100644 site/content/includes/installation/create-loadbalancer-service.md delete mode 100644 site/content/includes/installation/create-nodeport-service.md delete mode 100644 site/content/includes/installation/expose-ngf.md create mode 100644 site/content/includes/installation/helm/pulling-the-chart.md create mode 100644 site/content/includes/installation/helm/uninstall-gateway-api-resources.md diff --git a/site/content/includes/installation/create-loadbalancer-service.md b/site/content/includes/installation/create-loadbalancer-service.md deleted file mode 100644 index 74cab5eba8..0000000000 --- a/site/content/includes/installation/create-loadbalancer-service.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -docs: ---- - -To create a **LoadBalancer** service, use the appropriate manifest for your cloud provider: - -- For GCP (Google Cloud Platform) or Azure: - - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml - ``` - - Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the following command: - - ```shell - kubectl get svc nginx-gateway -n nginx-gateway - ``` - - Use the public IP of the load balancer to access NGINX Gateway Fabric. - -- For AWS (Amazon Web Services): - - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml - ``` - - In AWS, the NLB (Network Load Balancer) DNS (directory name system) name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: - - ```shell - kubectl get svc nginx-gateway -n nginx-gateway - ``` - - Generally, use the NLB DNS name, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: - - ```shell - nslookup - ``` diff --git a/site/content/includes/installation/create-nodeport-service.md b/site/content/includes/installation/create-nodeport-service.md deleted file mode 100644 index 8805969134..0000000000 --- a/site/content/includes/installation/create-nodeport-service.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -docs: ---- - -To create a **NodePort** service: - -```shell -kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml -``` - -A **NodePort** service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. diff --git a/site/content/includes/installation/expose-ngf.md b/site/content/includes/installation/expose-ngf.md deleted file mode 100644 index f9862d23de..0000000000 --- a/site/content/includes/installation/expose-ngf.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -docs: ---- - -Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. - -{{}} -The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any Gateway [Listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports. -{{}} - -NGINX Gateway Fabric uses this service to update the **Addresses** field in the **Gateway Status** resource. A **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. - -This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). diff --git a/site/content/includes/installation/helm/pulling-the-chart.md b/site/content/includes/installation/helm/pulling-the-chart.md new file mode 100644 index 0000000000..85b3e1aebc --- /dev/null +++ b/site/content/includes/installation/helm/pulling-the-chart.md @@ -0,0 +1,12 @@ +--- +docs: +--- + +Pull the latest stable release: + + ```shell + helm pull oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric --untar + cd nginx-gateway-fabric + ``` + + For the latest **main** branch version, add `--version 0.0.0-edge`. \ No newline at end of file diff --git a/site/content/includes/installation/helm/uninstall-gateway-api-resources.md b/site/content/includes/installation/helm/uninstall-gateway-api-resources.md new file mode 100644 index 0000000000..73dd30c92d --- /dev/null +++ b/site/content/includes/installation/helm/uninstall-gateway-api-resources.md @@ -0,0 +1,11 @@ +--- +docs: +--- + +To remove the Gateway resources using the Gateway API repository, use the following command: + + {{}}This action will delete all corresponding custom resources in your cluster across all namespaces. Ensure no custom resources you want to keep or other Gateway API implementations are running in your cluster before proceeding!{{}} + + ```shell + kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index 1169c7c600..ba10479981 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -1,6 +1,6 @@ --- title: "Installation with Helm" -description: "Learn how to install NGINX Gateway Fabric on a generic Kubernetes cluster." +description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric using Helm in a Kubernetes cluster. This guide offers clear, step-by-step instructions to get you started." weight: 200 toc: true docs: "DOCS-000" @@ -10,449 +10,189 @@ docs: "DOCS-000" ## Prerequisites -- Install [kubectl](https://kubernetes.io/docs/tasks/tools/) +Before starting this guide, ensure you've installed: -## Deploying NGINX Gateway Fabric +- [kubectl](https://kubernetes.io/docs/tasks/tools/), a command-line tool for managing Kubernetes clusters. +- [Helm 3.0 or later](https://helm.sh/docs/intro/install/), for deploying and managing applications on Kubernetes. -{{}} -{{%tab name="Helm"%}} +## Deploy NGINX Gateway Fabric with Helm -### Deploying NGINX Gateway Fabric using Helm +### Install from the OCI Registry - -Follow the instructions [to deploy NGINX Gateway Fabric using Helm](/deploy/helm-chart/README.md). - - -{{%/tab%}} - -{{%tab name="Manifest"%}} - -### Deploy NGINX Gateway Fabric from Manifests - -{{}}NGINX Gateway Fabric installs into the **nginx-gateway** namespace by default. To run NGINX Gateway Fabric in a different namespace, modify the installation manifests.{{}} - -1. Install the Gateway API resources (Custom Resource Definitions [CRDs] and validating webhook) from the standard channel: +- To install the latest stable release of NGINX Gateway Fabric, use the following command, replacing `` with your chosen release name: ```shell - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + helm install oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric --create-namespace --wait -n nginx-gateway ``` -2. Deploy the NGINX Gateway Fabric CRDs: + For the latest **main** branch version, add `--version 0.0.0-edge`. - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml - ``` +### Install from Sources{#install-from-sources} -3. Deploy NGINX Gateway Fabric: +Installing NGINX Gateway Fabric from sources offers more control and flexibility, which is especially useful in environments where customization is key. Follow these steps to install the latest stable release or the cutting-edge version directly from the sources. - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml - ``` +1. {{}} -4. Verify NGINX Gateway Fabric is running in the `nginx-gateway` namespace: +2. To install the chart into the **nginx-gateway** namespace, run the following command, replacing `` with your chosen release name: ```shell - kubectl get pods -n nginx-gateway - ``` - - Expected output (note that `5d4f4c7db7-xk2kq` is a randomly generated string and will vary): - - ```text - NAME READY STATUS RESTARTS AGE - nginx-gateway-5d4f4c7db7-xk2kq 2/2 Running 0 112s + helm install . --create-namespace --wait -n nginx-gateway ``` + Omit the `--create-namespace` flag if the namespace already exists. -### Upgrade NGINX Gateway Fabric from Manifests - -Upgrading NGINX Gateway Fabric from manifests involves several steps to ensure all components are updated to their latest versions. - -1. **Upgrade Gateway Resources:** - - Check that the **Gateway API** resources are compatible with your version of NGINX Gateway Fabric ([Technical Specifications](/README.md#technical-specifications)). - - Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) for any important upgrade-specific information. - - To upgrade the gateway resources, run: - ```shell - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml - ``` +## Upgrade NGINX Gateway Fabric Using Helm -2. **Upgrade NGINX Gateway Fabric CRDs:** - - To upgrade the Custom Resource Definitions (CRDs), run: +Upgrading your NGINX Gateway Fabric deployment is crucial to take advantage of the latest features, security updates, and performance improvements. This section guides you through the upgrade process, ensuring a smooth transition to the latest version of NGINX Gateway Fabric. Pay attention to the compatibility of Gateway API resources and follow each step carefully for a successful upgrade. - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml - ``` - -3. **Upgrade NGINX Gateway Fabric Deployment:** - - To upgrade the deployment, run: - - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml - ``` - -{{%/tab%}} - -{{}} +{{}}For guidance on zero-downtime upgrades (ensuring service continuity without interruptions during upgrades), see [Configure Delayed Pod Termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades).{{}} +### Upgrade Gateway Resources +Keeping your Gateway API resources up-to-date is a key part of maintaining NGINX Gateway Fabric. This subsection focuses on verifying and upgrading these resources to ensure they are compatible with the latest version of your NGINX Gateway Fabric. +To upgrade your Gateway API resources, take the following steps: +- Verify the Gateway API resources are compatible with your NGINX Gateway Fabric version. Refer to the [Technical Specifications]({{< relref "reference/technical-specifications.md" >}}) for details. +- Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) for any important upgrade-specific information. +- To upgrade the Gateway API resources, run: + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` -## Expose NGINX Gateway Fabric +### Upgrade NGINX Gateway Fabric CRDs -Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. +Helm's upgrade process does not automatically upgrade the NGINX Gateway Fabric CRDs (Custom Resource Definitions). This subsection provides the necessary steps to manually upgrade your CRDs. -{{}} -The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any Gateway [Listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports. -{{}} -NGINX Gateway Fabric uses this service to update the **Addresses** field in the **Gateway Status** resource. A **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. +1. {{}} -This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). +2. To upgrade the Custom Resource Definitions (CRDs), run: -### Create a NodePort service + ```shell + kubectl apply -f crds/ + ``` -To create a **NodePort** service: + {{}}Ignore the following warning, as it is expected.{{}} -```shell -kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml -``` + ``` + Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply. + ``` -A **NodePort** service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. +### Upgrade NGINX Gateway Fabric Release -### Create a LoadBalancer Service +Upgrading your NGINX Gateway Fabric release is a crucial step to keep your deployment current. This subsection covers two methods for upgrading: -To create a **LoadBalancer** service, use the appropriate manifest for your cloud provider: +- Upgrade from the OCI Registry +- Upgrade from Sources -- For GCP (Google Cloud Platform) or Azure: +Choose the method that best fits your setup and follow the corresponding steps to ensure a successful upgrade. - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml - ``` +#### Upgrade from the OCI Registry - Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the following command: +- To upgrade to the latest stable release, run the following command, replacing `` with your chosen release name: ```shell - kubectl get svc nginx-gateway -n nginx-gateway + helm upgrade oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric -n nginx-gateway ``` - Use the public IP of the load balancer to access NGINX Gateway Fabric. +#### Upgrade from Sources -- For AWS (Amazon Web Services): - - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml - ``` +1. {{}} - In AWS, the NLB (Network Load Balancer) DNS (directory name system) name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: +2. To upgrade your release, run the following command, replacing `` with your chosen release name: ```shell - kubectl get svc nginx-gateway -n nginx-gateway + helm upgrade my-release . -n nginx-gateway ``` - Generally, use the NLB DNS name, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: - - ```shell - nslookup - ``` +## Configure Delayed Pod Termination for Zero-Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} - ---- - -## Upgrading NGINX Gateway Fabric - -This section provides guidelines for upgrading your NGINX Gateway Fabric deployment to ensure you are using the latest features and improvements. - -{{}}For zero-downtime upgrades, follow the instructions to [configure a delayed pod termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades) for the NGINX Gateway Fabric pod.{{}} - - - -### Upgrade NGINX Gateway Fabric Using Helm - -For Helm-managed deployments, follow the [Helm upgrade instructions](/deploy/helm-chart/README.md#upgrading-the-chart). - -### Configure Delayed Pod Termination {#configure-delayed-pod-termination-for-zero-downtime-upgrades} - -Configuring delayed pod termination is crucial for ensuring zero downtime during upgrades, particularly in environments handling persistent or long-lived connections. The configuration settings required are the same for both Helm- and Manifest-based deployments, although the specific file to update will depend on your deployment type. +In order to achieve zero-downtime upgrades and maintain continuous service availability, it's important to configure delayed pod termination. This setup is especially critical in environments that manage persistent or long-lived connections. {{}}NGINX won't shut down until all websocket or long-lived connections are closed. Keeping these connections open during an upgrade can lead to Kubernetes forcibly shutting down NGINX, potentially causing downtime for clients.{{}} -#### For Helm-Based Deployments - -To configure delayed pod termination, follow these steps: +1. Open the `values.yaml` for editing. -1. Depending on your deployment type, update the `values.yaml` file for Helm-based deployments or the `nginx-gateway.yaml` file for Manifest-based deployments. +1. **Add delayed shutdown hooks**: -1. Add `lifecycle: preStop` hooks to both `nginx` and `nginx-gateway` container definitions. These hooks delay the shutdown process to allow time for connections to close gracefully. + In the `nginx-gateway.yaml` file, add `lifecycle: preStop` hooks to both the `nginx` and `nginxGateway` container definitions. These hooks instruct the containers to delay their shutdown process, allowing time for connections to close gracefully. ```yaml - <...> - name: nginx-gateway - <...> - lifecycle: - preStop: - exec: - command: - - /usr/bin/gateway - - sleep - - --duration=40s # This flag is optional, the default is 30s - <...> - name: nginx - <...> - lifecycle: - preStop: - exec: - command: - - /bin/sleep - - "40" - <...> - ``` - -1. Ensure `terminationGracePeriodSeconds` is equal to or greater than the `sleep` duration in the preStop hook (default is `30`). This is to ensure Kubernetes does not terminate the pod before the `preStop` Hook is complete. To do so, update your `values.yaml` or `nginx-gateway.yaml` file to include the following (update the value to what is required in your environment): + nginxGateway: + <...> + lifecycle: + preStop: + exec: + command: + - /usr/bin/gateway + - sleep + - --duration=40s # This flag is optional, the default is 30s + + nginx: + <...> + lifecycle: + preStop: + exec: + command: + - /bin/sleep + - "40" + ``` + +1. **Set the termination grace period**: + + Set `terminationGracePeriodSeconds` to a value that is equal to or greater than the `sleep` duration specified in the `preStop` hook (default is `30`). This setting prevents Kubernetes from terminating the pod before before the `preStop` hook has completed running. ```yaml terminationGracePeriodSeconds: 50 ``` -#### Using Helm to Configure Delayed Pod Termination +1. Save the changes. -For Helm-based deployments, follow the [Helm-specific instructions for extending the termination period](/deploy/helm-chart/README.md#configure-delayed-termination-for-zero-downtime-upgrades). +{{}} +For additional information on configuring and understanding the behavior of containers and pods during their lifecycle, refer to the following Kubernetes documentation: +- [Container Lifecycle Hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) +- [Pod Lifecycle](https://kubernetes.io/docs/concepts/workloads/Pods/Pod-lifecycle/#Pod-termination) +{{}} ---- -Feel free to copy this revised section. Let me know if you need any further modifications or if we should proceed to the next section! +## Uninstall NGINX Gateway Fabric Using Helm -%%%% +Uninstalling NGINX Gateway Fabric is a straightforward process but requires careful attention to remove all related components. Follow these steps to uninstall the release and clean up resources: -## Deploy NGINX Gateway Fabric from Manifests +1. **Uninstall NGINX Gateway Fabric:** -> Note: By default, NGINX Gateway Fabric (NGF) will be installed into the nginx-gateway Namespace. -> It is possible to run NGF in a different Namespace, although you'll need to make modifications to the installation -> manifests. + - Run the following command uninstall the NGINX Gateway Fabric release, replacing `my-release` with your chosen release name: -1. Install the Gateway API resources from the standard channel (the CRDs and the validating webhook): + ```shell + helm uninstall -n nginx-gateway + ``` - ```shell - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml - ``` +2. **Remove namespace and CRDs:** -1. Deploy the NGINX Gateway Fabric CRDs: + - Remove the **nginx-gateway** namespace and CRDs to completely clean up all associated resources: - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml - ``` + ```shell + kubectl delete ns nginx-gateway + kubectl delete crd nginxgateways.gateway.nginx.org + ``` -1. Deploy the NGINX Gateway Fabric: + These commands will remove all Kubernetes components associated with the release and delete the release itself. - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml - ``` +3. **Remove the Gateway API resources:** -1. Confirm the NGINX Gateway Fabric is running in `nginx-gateway` namespace: + - {{}} - ```shell - kubectl get pods -n nginx-gateway - ``` - ```text - NAME READY STATUS RESTARTS AGE - nginx-gateway-5d4f4c7db7-xk2kq 2/2 Running 0 112s - ``` ## Expose NGINX Gateway Fabric -You can gain access to NGINX Gateway Fabric by creating a **NodePort** Service or a **LoadBalancer** Service. -This Service must live in the same Namespace as the controller. The name of this Service is provided in -the `--service` argument to the controller. - -> **Important** -> The Service manifests expose NGINX Gateway Fabric on ports 80 and 443, which exposes any -> Gateway [Listener](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) -> configured for those ports. If you'd like to use different ports in your listeners, -> update the manifests accordingly. -> -> Additionally, NGINX Gateway Fabric will not listen on any ports until you configure a -[Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener. - -NGINX Gateway Fabric will use this Service to set the Addresses field in the Gateway Status resource. A LoadBalancer -Service sets the status field to the IP address and/or Hostname. If no Service exists, the Pod IP address is used. - -### Create a NodePort Service - -Create a Service with type **NodePort**: - -```shell -kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml -``` - -A **NodePort** Service will randomly allocate one port on every Node of the cluster. To access NGINX Gateway Fabric, -use an IP address of any Node in the cluster along with the allocated port. - -### Create a LoadBalancer Service - -Create a Service with type **LoadBalancer** using the appropriate manifest for your cloud provider. - -- For GCP or Azure: - - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml - ``` - - Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the - following command: - - ```shell - kubectl get svc nginx-gateway -n nginx-gateway - ``` - - Use the public IP of the load balancer to access NGINX Gateway Fabric. - -- For AWS: - - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml - ``` - - In AWS, the NLB DNS name will be reported by Kubernetes in lieu of a public IP in the `EXTERNAL-IP` column. To get the - DNS name run: - - ```shell - kubectl get svc nginx-gateway -n nginx-gateway - ``` - - In general, you should rely on the NLB DNS name, however for testing purposes you can resolve the DNS name to get the - IP address of the load balancer: - - ```shell - nslookup - ``` - -## Upgrading NGINX Gateway Fabric - -> **Note** -> See [below](#configure-delayed-termination-for-zero-downtime-upgrades) for instructions on how to configure delayed -> termination if required for zero downtime upgrades in your environment. - -### Upgrade NGINX Gateway Fabric from Manifests - -1. Upgrade the Gateway Resources - - Before you upgrade, ensure the Gateway API resources are the correct version as supported by the NGINX Gateway - Fabric - [see the Technical Specifications](/README.md#technical-specifications). - The [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) of the new version of the - Gateway API might include important upgrade-specific notes and instructions. We advise to check the release notes of - all versions between the one you're using and the new one. - - To upgrade the Gateway resources from [the Gateway API repo](https://github.com/kubernetes-sigs/gateway-api), run: - - ```shell - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml - ``` - -1. Upgrade the NGINX Gateway Fabric CRDs - - Run the following command to upgrade the NGINX Gateway Fabric CRDs: - - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml - ``` - -1. Upgrade NGINX Gateway Fabric Deployment - - Run the following command to upgrade NGINX Gateway Fabric: - - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml - ``` - -### Upgrade NGINX Gateway Fabric using Helm - -To upgrade NGINX Gateway Fabric when the deployment method is Helm, please follow the instructions -[here](/deploy/helm-chart/README.md#upgrading-the-chart). - -### Configure Delayed Termination for Zero Downtime Upgrades - -To achieve zero downtime upgrades (meaning clients will not see any interruption in traffic while a rolling upgrade is -being performed on NGF), you may need to configure delayed termination on the NGF Pod, depending on your environment. - -> **Note** -> When proxying Websocket or any long-lived connections, NGINX will not terminate until that connection is closed -> by either the client or the backend. This means that unless all those connections are closed by clients/backends -> before or during an upgrade, NGINX will not terminate, which means Kubernetes will kill NGINX. As a result, the -> clients will see the connections abruptly closed and thus experience downtime. - -#### Configure Delayed Termination Using Manifests - -Edit the `nginx-gateway.yaml` to include the following: - -1. Add `lifecycle` prestop hooks to both the nginx and the nginx-gateway container definitions: - - ```yaml - <...> - name: nginx-gateway - <...> - lifecycle: - preStop: - exec: - command: - - /usr/bin/gateway - - sleep - - --duration=40s # This flag is optional, the default is 30s - <...> - name: nginx - <...> - lifecycle: - preStop: - exec: - command: - - /bin/sleep - - "40" - <...> - ``` - -2. Ensure the `terminationGracePeriodSeconds` matches or exceeds the `sleep` value from the `preStopHook` (the default - is 30). This is to ensure Kubernetes does not terminate the Pod before the `preStopHook` is complete. - -> **Note** -> More information on container lifecycle hooks can be found -> [here](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) and a detailed -> description of Pod termination behavior can be found in -> [Termination of Pods](https://kubernetes.io/docs/concepts/workloads/Pods/Pod-lifecycle/#Pod-termination). - -#### Configure Delayed Termination Using Helm - -To configure delayed termination on the NGF Pod when the deployment method is Helm, please follow the instructions -[here](/deploy/helm-chart/README.md#configure-delayed-termination-for-zero-downtime-upgrades). - -## Uninstalling NGINX Gateway Fabric - -### Uninstall NGINX Gateway Fabric from Manifests - -1. Uninstall the NGINX Gateway Fabric: - - ```shell - kubectl delete -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml - ``` - - ```shell - kubectl delete -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml - ``` - -1. Uninstall the Gateway API resources from the standard channel (the CRDs and the validating webhook): - - >**Warning: This command will delete all the corresponding custom resources in your cluster across all namespaces! - Please ensure there are no custom resources that you want to keep and there are no other Gateway API implementations - running in the cluster!** - - ```shell - kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml - ``` +Once NGINX Gateway Fabric is installed, the next step is to make it accessible. Refer to the following instructions for guidance on configuring access and creating the necessary services: -### Uninstall NGINX Gateway Fabric using Helm +- [Expose the NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}). -To uninstall NGINX Gateway Fabric when the deployment method is Helm, please follow the instructions -[here](/deploy/helm-chart/README.md#uninstalling-the-chart). diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index e3f1d48c71..ba363b46ef 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -1,6 +1,6 @@ --- title: "Installation with Manifests" -description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric using manifest deployments in a Kubernetes cluster. This guide offers clear, step-by-step instructions to get you started." +description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric using Manifest deployments in a Kubernetes cluster. This guide offers clear, step-by-step instructions to get you started." weight: 100 toc: true docs: "DOCS-000" @@ -56,18 +56,19 @@ Deploying NGINX Gateway Fabric using Kubernetes manifests is a straightforward p This section provides guidelines for upgrading your NGINX Gateway Fabric deployment to ensure you are using the latest features and improvements. -{{}}For zero-downtime upgrades, follow the instructions to [configure a delayed pod termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades) for the NGINX Gateway Fabric pod.{{}} +{{}}For guidance on zero-downtime upgrades (ensuring service continuity without interruptions during upgrades), see [Configure Delayed Pod Termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades).{{}} Upgrading NGINX Gateway Fabric from manifests involves several steps to ensure all components are updated to their latest versions. 1. **Upgrade Gateway Resources:** - - Check that the **Gateway API** resources are compatible with your version of NGINX Gateway Fabric ([Technical Specifications](/README.md#technical-specifications)). + + - Verify the Gateway API resources are compatible with your NGINX Gateway Fabric version. Refer to the [Technical Specifications]({{< relref "reference/technical-specifications.md" >}}) for details. - Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) for any important upgrade-specific information. - - To upgrade the gateway resources, run: + - To upgrade the Gateway API resources, run: - ```shell - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml - ``` + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` 2. **Upgrade NGINX Gateway Fabric CRDs:** - To upgrade the Custom Resource Definitions (CRDs), run: @@ -83,9 +84,7 @@ Upgrading NGINX Gateway Fabric from manifests involves several steps to ensure a kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml ``` - - -### Configure Delayed Pod Termination for Zero-Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} +## Configure Delayed Pod Termination for Zero-Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} In order to achieve zero-downtime upgrades and maintain continuous service availability, it's important to configure delayed pod termination. This setup is especially critical in environments that manage persistent or long-lived connections. @@ -130,8 +129,6 @@ In order to achieve zero-downtime upgrades and maintain continuous service avail 1. Save the changes. - - {{}} For additional information on configuring and understanding the behavior of containers and pods during their lifecycle, refer to the following Kubernetes documentation: - [Container Lifecycle Hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) @@ -157,10 +154,11 @@ Uninstalling NGINX Gateway Fabric from your Kubernetes cluster involves removing 2. **Remove the Gateway API resources:** - - To uninstall the Gateway API resources, including the CRDs and the validating webhook, use the command below. Ensure no custom resources you wish to keep or other Gateway API implementations are running in the cluster before proceeding: + - {{}} - {{}}This command will remove all corresponding custom resources in your cluster across all namespaces!{{}} +## Expose NGINX Gateway Fabric + +Once NGINX Gateway Fabric is installed, the next step is to make it accessible. Refer to the following instructions for guidance on configuring access and creating the necessary services: + +- [Expose the NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}). - ```shell - kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml - ``` From 2c10d237869daa65aa5d39daee960ac56fd239be Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Fri, 17 Nov 2023 16:07:53 -0800 Subject: [PATCH 032/101] chore: edits --- site/content/installation/installing-ngf/helm.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index ba10479981..12c983d26a 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -108,7 +108,7 @@ Choose the method that best fits your setup and follow the corresponding steps t 2. To upgrade your release, run the following command, replacing `` with your chosen release name: ```shell - helm upgrade my-release . -n nginx-gateway + helm upgrade . -n nginx-gateway ``` ## Configure Delayed Pod Termination for Zero-Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} @@ -121,7 +121,7 @@ In order to achieve zero-downtime upgrades and maintain continuous service avail 1. **Add delayed shutdown hooks**: - In the `nginx-gateway.yaml` file, add `lifecycle: preStop` hooks to both the `nginx` and `nginxGateway` container definitions. These hooks instruct the containers to delay their shutdown process, allowing time for connections to close gracefully. + In the `values.yaml` file, add `lifecycle: preStop` hooks to both the `nginx` and `nginxGateway` container definitions. These hooks instruct the containers to delay their shutdown process, allowing time for connections to close gracefully. ```yaml nginxGateway: @@ -167,7 +167,7 @@ Uninstalling NGINX Gateway Fabric is a straightforward process but requires care 1. **Uninstall NGINX Gateway Fabric:** - - Run the following command uninstall the NGINX Gateway Fabric release, replacing `my-release` with your chosen release name: + - Run the following command uninstall the NGINX Gateway Fabric release, replacing `` with your chosen release name: ```shell helm uninstall -n nginx-gateway From 4ec498081908abcf0a1f58c4a2b79c805291c0e8 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Fri, 17 Nov 2023 16:24:23 -0800 Subject: [PATCH 033/101] chore: fixed broken link --- .../how-to/traffic-management/routing-traffic-to-your-app.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index 7925e3e7d3..39d4fba37b 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -15,7 +15,7 @@ You can route traffic to your Kubernetes applications using the Gateway API and ## Prerequisites - [Install]({{< relref "installation/" >}}) NGINX Gateway Fabric. -- [Expose NGINX Gateway Fabric]({{< relref "installation/" >}}) and save the public IP address and port of NGINX Gateway Fabric into shell variables: +- [Expose NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}) and save the public IP address and port of NGINX Gateway Fabric into shell variables: ```text GW_IP=XXX.YYY.ZZZ.III From 51548457e3dbab1e7baf3a08c111d6e07033f41c Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Fri, 17 Nov 2023 17:11:14 -0800 Subject: [PATCH 034/101] fixed links --- site/content/how-to/traffic-management/advanced-routing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/how-to/traffic-management/advanced-routing.md b/site/content/how-to/traffic-management/advanced-routing.md index cb23fcd624..232cb1ca9c 100644 --- a/site/content/how-to/traffic-management/advanced-routing.md +++ b/site/content/how-to/traffic-management/advanced-routing.md @@ -17,7 +17,7 @@ The goal is to create a set of rules that will result in client requests being s ## Prerequisites - [Install]({{< relref "/installation/" >}}) NGINX Gateway Fabric. -- [Expose NGINX Gateway Fabric]({{< relref "/installation/#expose-nginx-gateway-fabric" >}}) and save the public IP +- [Expose NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}) and save the public IP address and port of NGINX Gateway Fabric into shell variables: ```text @@ -234,7 +234,7 @@ This request should receive a response from the `tea-post` Pod. Any other type o If you have any issues while sending traffic, try the following to debug your configuration and setup: -- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Instructions for finding those values are [here](/docs/installation/#expose-nginx-gateway-fabric). +- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Refer to the topic [Expose NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}) for instructions on finding those values. - Check the status of the Gateway: From 04e33c8ee9aa7ed4c480b57685acc5e0f8206d87 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Fri, 17 Nov 2023 23:16:49 -0800 Subject: [PATCH 035/101] chore: edits --- .../installation/installing-ngf/helm.md | 2 +- .../installation/installing-ngf/manifests.md | 63 +++++++++---------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index 12c983d26a..0994522bcf 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -1,6 +1,6 @@ --- title: "Installation with Helm" -description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric using Helm in a Kubernetes cluster. This guide offers clear, step-by-step instructions to get you started." +description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric in a Kubernetes cluster with using Helm." weight: 200 toc: true docs: "DOCS-000" diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index ba363b46ef..9a5cf2dc4e 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -1,6 +1,6 @@ --- title: "Installation with Manifests" -description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric using Manifest deployments in a Kubernetes cluster. This guide offers clear, step-by-step instructions to get you started." +description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric using Kubernetes manifests." weight: 100 toc: true docs: "DOCS-000" @@ -10,55 +10,52 @@ docs: "DOCS-000" ## Prerequisites -In order to complete the steps in this guide, you must first: +To complete this guide, you'll need to: - Install [kubectl](https://kubernetes.io/docs/tasks/tools/), a command-line interface for managing Kubernetes clusters. ## Deploy NGINX Gateway Fabric from Manifests -Deploying NGINX Gateway Fabric using Kubernetes manifests is a straightforward process that involves setting up necessary resources and deploying NGINX Gateway Fabric components within your cluster. This method allows for a detailed and controlled deployment, suitable for environments where customization and precise configuration are required. +Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. With manifests, you can configure your deployment exactly how you want. Manifests also make it easy to replicate deployments across environments or clusters, ensuring consistency. -{{}}NGINX Gateway Fabric installs into the **nginx-gateway** namespace by default. To run NGINX Gateway Fabric in a different namespace, modify the installation manifests.{{}} +{{}}By default, NGINX Gateway Fabric is installed in the **nginx-gateway** namespace. You can deploy in another namespace by modifying the installation manifests.{{}} -1. Install the Gateway API resources from the standard channel (the CRDs and validating webhook): - - ```shell - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml - ``` - -2. Deploy the NGINX Gateway Fabric CRDs: - - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml - ``` - -3. Deploy NGINX Gateway Fabric: - - ```shell - kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml - ``` +1. **Install the Gateway API Resources:** + - Start by installing the Gateway API resources, including the CRDs and the validating webhook: + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + ``` -4. Confirm the NGINX Gateway Fabric is running in the `nginx-gateway` namespace: +2. **Deploy the NGINX Gateway Fabric CRDs:** + - Next, deploy the NGINX Gateway Fabric CRDs: + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml + ``` - ```shell - kubectl get pods -n nginx-gateway - ``` +3. **Deploy NGINX Gateway Fabric:** + - Then, deploy NGINX Gateway Fabric: + ```shell + kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml + ``` - Expected output (note that `5d4f4c7db7-xk2kq` is a randomly generated string and will vary): +4. **Verify the Deployment:** + - To confirm that NGINX Gateway Fabric is running, check the pods in the `nginx-gateway` namespace: + ```shell + kubectl get pods -n nginx-gateway + ``` + The output should look similar to this (note that the pod name will include a unique string): + ```text + NAME READY STATUS RESTARTS AGE + nginx-gateway-5d4f4c7db7-xk2kq 2/2 Running 0 112s + ``` - ```text - NAME READY STATUS RESTARTS AGE - nginx-gateway-5d4f4c7db7-xk2kq 2/2 Running 0 112s - ``` ## Upgrade NGINX Gateway Fabric from Manifests -This section provides guidelines for upgrading your NGINX Gateway Fabric deployment to ensure you are using the latest features and improvements. - {{}}For guidance on zero-downtime upgrades (ensuring service continuity without interruptions during upgrades), see [Configure Delayed Pod Termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades).{{}} -Upgrading NGINX Gateway Fabric from manifests involves several steps to ensure all components are updated to their latest versions. +Follow these steps to upgrade NGINX Gateway Fabric and get the latest features and improvements: 1. **Upgrade Gateway Resources:** From dc68f93a34227ec620961aa3e144c10961f477df Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Sat, 18 Nov 2023 09:15:54 -0800 Subject: [PATCH 036/101] chore: edits --- .../installation/installing-ngf/manifests.md | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index 9a5cf2dc4e..7c6a50cd83 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -19,7 +19,7 @@ To complete this guide, you'll need to: Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. With manifests, you can configure your deployment exactly how you want. Manifests also make it easy to replicate deployments across environments or clusters, ensuring consistency. -{{}}By default, NGINX Gateway Fabric is installed in the **nginx-gateway** namespace. You can deploy in another namespace by modifying the installation manifests.{{}} +{{}}By default, NGINX Gateway Fabric is installed in the **nginx-gateway** namespace. You can deploy in another namespace by modifying the manifest files.{{}} 1. **Install the Gateway API Resources:** - Start by installing the Gateway API resources, including the CRDs and the validating webhook: @@ -53,13 +53,13 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. ## Upgrade NGINX Gateway Fabric from Manifests -{{}}For guidance on zero-downtime upgrades (ensuring service continuity without interruptions during upgrades), see [Configure Delayed Pod Termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades).{{}} +{{}}For guidance on zero downtime upgrades, see the [Configure Delayed Pod Termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades) section below.{{}} -Follow these steps to upgrade NGINX Gateway Fabric and get the latest features and improvements: +To upgrade NGINX Gateway Fabric and get the latest features and improvements, take the following steps: 1. **Upgrade Gateway Resources:** - - Verify the Gateway API resources are compatible with your NGINX Gateway Fabric version. Refer to the [Technical Specifications]({{< relref "reference/technical-specifications.md" >}}) for details. + - Verify that your NGINX Gateway Fabric version is compatible with the Gateway API resources. Refer to the [Technical Specifications]({{< relref "reference/technical-specifications.md" >}}) for details. - Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) for any important upgrade-specific information. - To upgrade the Gateway API resources, run: @@ -83,15 +83,17 @@ Follow these steps to upgrade NGINX Gateway Fabric and get the latest features a ## Configure Delayed Pod Termination for Zero-Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} -In order to achieve zero-downtime upgrades and maintain continuous service availability, it's important to configure delayed pod termination. This setup is especially critical in environments that manage persistent or long-lived connections. +To avoid client service interruptions during an upgrade, you might need to configure delayed termination for your NGINX Gateway Fabric pod. This ensures a smooth upgrade without any downtime, also known as a zero downtime upgrade. -{{}}NGINX won't shut down until all websocket or long-lived connections are closed. Keeping these connections open during an upgrade can lead to Kubernetes forcibly shutting down NGINX, potentially causing downtime for clients.{{}} +{{}}Keep in mind that NGINX won't shut down while WebSocket or other long-lived connections are open. NGINX will only stop when these connections are closed by the client or the backend. If these connections stay open during an upgrade, Kubernetes might need to shut down NGINX forcefully. This sudden shutdown could interrupt service for clients.{{}}} + +To configure delayed pod termination, take the following steps: 1. Open the `nginx-gateway.yaml` for editing. -1. **Add delayed shutdown hooks**: +2. **Add delayed shutdown hooks**: - In the `nginx-gateway.yaml` file, add `lifecycle: preStop` hooks to both the `nginx` and `nginx-gateway` container definitions. These hooks instruct the containers to delay their shutdown process, allowing time for connections to close gracefully. + In the `nginx-gateway.yaml` file, add `lifecycle: preStop` hooks to both the `nginx` and `nginx-gateway` container definitions. These hooks instruct the containers to delay their shutdown process, allowing time for connections to close gracefully. Update the `sleep` value to what works for your environment. ```yaml <...> @@ -116,7 +118,7 @@ In order to achieve zero-downtime upgrades and maintain continuous service avail <...> ``` -1. **Set the termination grace period**: +3. **Set the termination grace period**: Set `terminationGracePeriodSeconds` to a value that is equal to or greater than the `sleep` duration specified in the `preStop` hook (default is `30`). This setting prevents Kubernetes from terminating the pod before before the `preStop` hook has completed running. @@ -124,7 +126,7 @@ In order to achieve zero-downtime upgrades and maintain continuous service avail terminationGracePeriodSeconds: 50 ``` -1. Save the changes. +4. Save the changes. {{}} For additional information on configuring and understanding the behavior of containers and pods during their lifecycle, refer to the following Kubernetes documentation: From 0b19d0169ce9580669e3190b4a9ccc24176bb5ea Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Sat, 18 Nov 2023 09:48:17 -0800 Subject: [PATCH 037/101] chore: edits --- .../content/installation/installing-ngf/manifests.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index 7c6a50cd83..702e73e4ab 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -53,7 +53,7 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. ## Upgrade NGINX Gateway Fabric from Manifests -{{}}For guidance on zero downtime upgrades, see the [Configure Delayed Pod Termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades) section below.{{}} +{{}}For guidance on zero downtime upgrades, see the [Delay Pod Termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades) section below.{{}} To upgrade NGINX Gateway Fabric and get the latest features and improvements, take the following steps: @@ -81,13 +81,15 @@ To upgrade NGINX Gateway Fabric and get the latest features and improvements, ta kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml ``` -## Configure Delayed Pod Termination for Zero-Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} +## Delay Pod Termination for Zero Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} -To avoid client service interruptions during an upgrade, you might need to configure delayed termination for your NGINX Gateway Fabric pod. This ensures a smooth upgrade without any downtime, also known as a zero downtime upgrade. +To avoid client service interruptions when upgrading NGINX Gateway Fabric, you can configure [`PreStop` hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) to delay terminating the NGINX Gateway Fabric pod, allowing the pod to complete certain actions before shutting down. This ensures a smooth upgrade without any downtime, also known as a zero downtime upgrade. -{{}}Keep in mind that NGINX won't shut down while WebSocket or other long-lived connections are open. NGINX will only stop when these connections are closed by the client or the backend. If these connections stay open during an upgrade, Kubernetes might need to shut down NGINX forcefully. This sudden shutdown could interrupt service for clients.{{}}} +For an in-depth explanation of how Kubernetes handles pod termination, see the [Termination of Pods](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination) topic on their official website. -To configure delayed pod termination, take the following steps: +{{}}Keep in mind that NGINX won't shut down while WebSocket or other long-lived connections are open. NGINX will only stop when these connections are closed by the client or the backend. If these connections stay open during an upgrade, Kubernetes might need to shut down NGINX forcefully. This sudden shutdown could interrupt service for clients.{{}} + +Follow these steps to configure delayed pod termination: 1. Open the `nginx-gateway.yaml` for editing. From 2957ecc55d602d5b10d3dde72a9398fb09e23492 Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Sat, 18 Nov 2023 11:05:54 -0800 Subject: [PATCH 038/101] chore: edits --- .../installation/helm/pulling-the-chart.md | 4 ++-- .../helm/uninstall-gateway-api-resources.md | 4 ++-- site/content/installation/installing-ngf/helm.md | 11 ++++------- .../installation/installing-ngf/manifests.md | 16 +++++++--------- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/site/content/includes/installation/helm/pulling-the-chart.md b/site/content/includes/installation/helm/pulling-the-chart.md index 85b3e1aebc..a2495ef06b 100644 --- a/site/content/includes/installation/helm/pulling-the-chart.md +++ b/site/content/includes/installation/helm/pulling-the-chart.md @@ -2,11 +2,11 @@ docs: --- -Pull the latest stable release: +Pull the latest stable release of the NGINX Gateway Fabric chart: ```shell helm pull oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric --untar cd nginx-gateway-fabric ``` - For the latest **main** branch version, add `--version 0.0.0-edge`. \ No newline at end of file + If you want the latest version from the **main** branch, add `--version 0.0.0-edge` to your pull command. diff --git a/site/content/includes/installation/helm/uninstall-gateway-api-resources.md b/site/content/includes/installation/helm/uninstall-gateway-api-resources.md index 73dd30c92d..25cf75752d 100644 --- a/site/content/includes/installation/helm/uninstall-gateway-api-resources.md +++ b/site/content/includes/installation/helm/uninstall-gateway-api-resources.md @@ -2,9 +2,9 @@ docs: --- -To remove the Gateway resources using the Gateway API repository, use the following command: +To uninstall the Gateway API resources, including the CRDs and the validating webhook, run: - {{}}This action will delete all corresponding custom resources in your cluster across all namespaces. Ensure no custom resources you want to keep or other Gateway API implementations are running in your cluster before proceeding!{{}} + {{}}This will remove all corresponding custom resources in your entire cluster, across all namespaces. Double-check to make sure you don't have any custom resources you need to keep, and confirm that there are no other Gateway API implementations active in your cluster.{{}} ```shell kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index 0994522bcf..cf3ecd1fce 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -10,7 +10,7 @@ docs: "DOCS-000" ## Prerequisites -Before starting this guide, ensure you've installed: +To complete this guide, you'll need to install: - [kubectl](https://kubernetes.io/docs/tasks/tools/), a command-line tool for managing Kubernetes clusters. - [Helm 3.0 or later](https://helm.sh/docs/intro/install/), for deploying and managing applications on Kubernetes. @@ -20,27 +20,24 @@ Before starting this guide, ensure you've installed: ### Install from the OCI Registry -- To install the latest stable release of NGINX Gateway Fabric, use the following command, replacing `` with your chosen release name: +- To install the latest stable release of the NGINX Gateway Fabric in the **nginx-gateway** namespace, run the following command. Change `` to the name you want for your release. If the namespace already exists, you can omit the optional `--create-namespace` flag. ```shell helm install oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric --create-namespace --wait -n nginx-gateway ``` - For the latest **main** branch version, add `--version 0.0.0-edge`. + If you want the latest version from the **main** branch, add `--version 0.0.0-edge` to your install command. ### Install from Sources{#install-from-sources} -Installing NGINX Gateway Fabric from sources offers more control and flexibility, which is especially useful in environments where customization is key. Follow these steps to install the latest stable release or the cutting-edge version directly from the sources. - 1. {{}} -2. To install the chart into the **nginx-gateway** namespace, run the following command, replacing `` with your chosen release name: +2. To install the chart into the **nginx-gateway** namespace, run the following command. Change `` to the name you want for your release. If the namespace already exists, you can omit the optional `--create-namespace` flag. ```shell helm install . --create-namespace --wait -n nginx-gateway ``` - Omit the `--create-namespace` flag if the namespace already exists. diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index 702e73e4ab..dc9359ec3b 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -10,9 +10,9 @@ docs: "DOCS-000" ## Prerequisites -To complete this guide, you'll need to: +To complete this guide, you'll need to install: -- Install [kubectl](https://kubernetes.io/docs/tasks/tools/), a command-line interface for managing Kubernetes clusters. +- [kubectl](https://kubernetes.io/docs/tasks/tools/), a command-line interface for managing Kubernetes clusters. ## Deploy NGINX Gateway Fabric from Manifests @@ -57,9 +57,9 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. To upgrade NGINX Gateway Fabric and get the latest features and improvements, take the following steps: -1. **Upgrade Gateway Resources:** +1. **Upgrade Gateway API Resources:** - - Verify that your NGINX Gateway Fabric version is compatible with the Gateway API resources. Refer to the [Technical Specifications]({{< relref "reference/technical-specifications.md" >}}) for details. + - Verify that your NGINX Gateway Fabric version is compatible with the Gateway API resources. Refer to the [Technical Specifications]({{< relref "reference/technical-specifications.md" >}}) for details. - Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) for any important upgrade-specific information. - To upgrade the Gateway API resources, run: @@ -139,11 +139,11 @@ For additional information on configuring and understanding the behavior of cont ## Uninstall NGINX Gateway Fabric from Manifests -Uninstalling NGINX Gateway Fabric from your Kubernetes cluster involves removing the deployed NGINX Gateway Fabric components and the Gateway API resources. This procedure should be followed carefully to ensure that all relevant components are cleanly removed from your system. +To uninstall NGINX Gateway Fabric from your Kubernetes cluster, you must delete both the Gateway API resources and the NGINX Gateway Fabric components. Follow the steps carefully to ensure that everything related to NGINX Gateway Fabric is completely removed. 1. **Uninstall NGINX Gateway Fabric:** - - Run the following commands to remove the NGINX Gateway Fabric and its Custom Resource Definitions (CRDs): + - Run the following commands to remove NGINX Gateway Fabric its custom resource definitions (CRDs) from your system: ```shell kubectl delete -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml @@ -159,7 +159,5 @@ Uninstalling NGINX Gateway Fabric from your Kubernetes cluster involves removing ## Expose NGINX Gateway Fabric -Once NGINX Gateway Fabric is installed, the next step is to make it accessible. Refer to the following instructions for guidance on configuring access and creating the necessary services: - -- [Expose the NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}). +After installing NGINX Gateway Fabric, the next step is to ensure it's accessible. For directions on configuring access and creating the required services, follow the directions in [Expose the NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}). From 54ba522c488785324d72e2aae8a3c8a450dcab4f Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Sun, 19 Nov 2023 20:50:30 -0800 Subject: [PATCH 039/101] chore: edits --- .../delay-pod-termination-overview.md | 9 ++ .../termination-grace-period.md | 9 ++ .../installation/next-step-expose-fabric.md | 5 + .../installation/installing-ngf/helm.md | 118 ++++++++---------- .../installation/installing-ngf/manifests.md | 90 +++++++------ 5 files changed, 118 insertions(+), 113 deletions(-) create mode 100644 site/content/includes/installation/delay-pod-termination/delay-pod-termination-overview.md create mode 100644 site/content/includes/installation/delay-pod-termination/termination-grace-period.md create mode 100644 site/content/includes/installation/next-step-expose-fabric.md diff --git a/site/content/includes/installation/delay-pod-termination/delay-pod-termination-overview.md b/site/content/includes/installation/delay-pod-termination/delay-pod-termination-overview.md new file mode 100644 index 0000000000..297d6dba32 --- /dev/null +++ b/site/content/includes/installation/delay-pod-termination/delay-pod-termination-overview.md @@ -0,0 +1,9 @@ +--- +docs: +--- + +To avoid client service interruptions when upgrading NGINX Gateway Fabric, you can configure [`PreStop` hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) to delay terminating the NGINX Gateway Fabric pod, allowing the pod to complete certain actions before shutting down. This ensures a smooth upgrade without any downtime, also known as a zero downtime upgrade. + +For an in-depth explanation of how Kubernetes handles pod termination, see the [Termination of Pods](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination) topic on their official website. + +{{}}Keep in mind that NGINX won't shut down while WebSocket or other long-lived connections are open. NGINX will only stop when these connections are closed by the client or the backend. If these connections stay open during an upgrade, Kubernetes might need to shut down NGINX forcefully. This sudden shutdown could interrupt service for clients.{{}} diff --git a/site/content/includes/installation/delay-pod-termination/termination-grace-period.md b/site/content/includes/installation/delay-pod-termination/termination-grace-period.md new file mode 100644 index 0000000000..0d45f66910 --- /dev/null +++ b/site/content/includes/installation/delay-pod-termination/termination-grace-period.md @@ -0,0 +1,9 @@ +--- +docs: +--- + +Set `terminationGracePeriodSeconds` to a value that is equal to or greater than the `sleep` duration specified in the `preStop` hook (default is `30`). This setting prevents Kubernetes from terminating the pod before before the `preStop` hook has completed running. + + ```yaml + terminationGracePeriodSeconds: 50 + ``` diff --git a/site/content/includes/installation/next-step-expose-fabric.md b/site/content/includes/installation/next-step-expose-fabric.md new file mode 100644 index 0000000000..eecadbb83a --- /dev/null +++ b/site/content/includes/installation/next-step-expose-fabric.md @@ -0,0 +1,5 @@ +--- +docs: +--- + +After installing NGINX Gateway Fabric, the next step is to make it accessible. Detailed instructions can be found in [Expose the NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}). diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index cf3ecd1fce..937d3a6dc5 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -16,42 +16,38 @@ To complete this guide, you'll need to install: - [Helm 3.0 or later](https://helm.sh/docs/intro/install/), for deploying and managing applications on Kubernetes. -## Deploy NGINX Gateway Fabric with Helm +## Deploy NGINX Gateway Fabric ### Install from the OCI Registry -- To install the latest stable release of the NGINX Gateway Fabric in the **nginx-gateway** namespace, run the following command. Change `` to the name you want for your release. If the namespace already exists, you can omit the optional `--create-namespace` flag. +- To install the latest stable release of NGINX Gateway Fabric in the **nginx-gateway** namespace, run the following command: ```shell helm install oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric --create-namespace --wait -n nginx-gateway ``` - If you want the latest version from the **main** branch, add `--version 0.0.0-edge` to your install command. + Change `` to the name you want for your release. If the namespace already exists, you can omit the optional `--create-namespace` flag. If you want the latest version from the **main** branch, add `--version 0.0.0-edge` to your install command. ### Install from Sources{#install-from-sources} 1. {{}} -2. To install the chart into the **nginx-gateway** namespace, run the following command. Change `` to the name you want for your release. If the namespace already exists, you can omit the optional `--create-namespace` flag. +2. To install the chart into the **nginx-gateway** namespace, run the following command. ```shell helm install . --create-namespace --wait -n nginx-gateway ``` + Change `` to the name you want for your release. If the namespace already exists, you can omit the optional `--create-namespace` flag. +## Upgrade NGINX Gateway Fabric +{{}}For guidance on zero downtime upgrades, see the [Delay Pod Termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades) section below.{{}} -## Upgrade NGINX Gateway Fabric Using Helm - -Upgrading your NGINX Gateway Fabric deployment is crucial to take advantage of the latest features, security updates, and performance improvements. This section guides you through the upgrade process, ensuring a smooth transition to the latest version of NGINX Gateway Fabric. Pay attention to the compatibility of Gateway API resources and follow each step carefully for a successful upgrade. - -{{}}For guidance on zero-downtime upgrades (ensuring service continuity without interruptions during upgrades), see [Configure Delayed Pod Termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades).{{}} - +To upgrade NGINX Gateway Fabric and get the latest features and improvements, take the following steps: ### Upgrade Gateway Resources -Keeping your Gateway API resources up-to-date is a key part of maintaining NGINX Gateway Fabric. This subsection focuses on verifying and upgrading these resources to ensure they are compatible with the latest version of your NGINX Gateway Fabric. - To upgrade your Gateway API resources, take the following steps: - Verify the Gateway API resources are compatible with your NGINX Gateway Fabric version. Refer to the [Technical Specifications]({{< relref "reference/technical-specifications.md" >}}) for details. @@ -64,12 +60,13 @@ To upgrade your Gateway API resources, take the following steps: ### Upgrade NGINX Gateway Fabric CRDs -Helm's upgrade process does not automatically upgrade the NGINX Gateway Fabric CRDs (Custom Resource Definitions). This subsection provides the necessary steps to manually upgrade your CRDs. +Helm's upgrade process does not automatically upgrade the NGINX Gateway Fabric CRDs (Custom Resource Definitions). +To upgrade the CRDs, take the following steps: 1. {{}} -2. To upgrade the Custom Resource Definitions (CRDs), run: +2. Upgrade the CRDs: ```shell kubectl apply -f crds/ @@ -77,77 +74,70 @@ Helm's upgrade process does not automatically upgrade the NGINX Gateway Fabric C {{}}Ignore the following warning, as it is expected.{{}} - ``` + ``` text Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply. ``` ### Upgrade NGINX Gateway Fabric Release -Upgrading your NGINX Gateway Fabric release is a crucial step to keep your deployment current. This subsection covers two methods for upgrading: - -- Upgrade from the OCI Registry -- Upgrade from Sources - -Choose the method that best fits your setup and follow the corresponding steps to ensure a successful upgrade. - #### Upgrade from the OCI Registry -- To upgrade to the latest stable release, run the following command, replacing `` with your chosen release name: +- To upgrade to the latest stable release of NGINX Gateway Fabric, run: ```shell helm upgrade oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric -n nginx-gateway ``` + Replace `` with your chosen release name. + #### Upgrade from Sources 1. {{}} -2. To upgrade your release, run the following command, replacing `` with your chosen release name: +1. To upgrade, run: the following command: ```shell helm upgrade . -n nginx-gateway ``` -## Configure Delayed Pod Termination for Zero-Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} + Replace `` with your chosen release name. -In order to achieve zero-downtime upgrades and maintain continuous service availability, it's important to configure delayed pod termination. This setup is especially critical in environments that manage persistent or long-lived connections. +## Delay Pod Termination for Zero Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} -{{}}NGINX won't shut down until all websocket or long-lived connections are closed. Keeping these connections open during an upgrade can lead to Kubernetes forcibly shutting down NGINX, potentially causing downtime for clients.{{}} +{{< include "installation/delay-pod-termination/delay-pod-termination-overview.md" >}} + +Follow these steps to configure delayed pod termination: 1. Open the `values.yaml` for editing. 1. **Add delayed shutdown hooks**: - In the `values.yaml` file, add `lifecycle: preStop` hooks to both the `nginx` and `nginxGateway` container definitions. These hooks instruct the containers to delay their shutdown process, allowing time for connections to close gracefully. - - ```yaml - nginxGateway: - <...> - lifecycle: - preStop: - exec: - command: - - /usr/bin/gateway - - sleep - - --duration=40s # This flag is optional, the default is 30s - - nginx: - <...> - lifecycle: - preStop: - exec: - command: - - /bin/sleep - - "40" - ``` + - In the `values.yaml` file, add `lifecycle: preStop` hooks to both the `nginx` and `nginx-gateway` container definitions. These hooks instruct the containers to delay their shutdown process, allowing time for connections to close gracefully. Update the `sleep` value to what works for your environment. + + ```yaml + nginxGateway: + <...> + lifecycle: + preStop: + exec: + command: + - /usr/bin/gateway + - sleep + - --duration=40s # This flag is optional, the default is 30s + + nginx: + <...> + lifecycle: + preStop: + exec: + command: + - /bin/sleep + - "40" + ``` 1. **Set the termination grace period**: - Set `terminationGracePeriodSeconds` to a value that is equal to or greater than the `sleep` duration specified in the `preStop` hook (default is `30`). This setting prevents Kubernetes from terminating the pod before before the `preStop` hook has completed running. - - ```yaml - terminationGracePeriodSeconds: 50 - ``` + - {{}} 1. Save the changes. @@ -158,38 +148,36 @@ For additional information on configuring and understanding the behavior of cont {{}} -## Uninstall NGINX Gateway Fabric Using Helm +## Uninstall NGINX Gateway Fabric -Uninstalling NGINX Gateway Fabric is a straightforward process but requires careful attention to remove all related components. Follow these steps to uninstall the release and clean up resources: +Follow these steps to uninstall NGINX Gateway Fabric and Gateway API from your Kubernetes cluster: 1. **Uninstall NGINX Gateway Fabric:** - - Run the following command uninstall the NGINX Gateway Fabric release, replacing `` with your chosen release name: + - To uninstall NGINX Gateway Fabric, run: ```shell helm uninstall -n nginx-gateway ``` + Replace `` with your chosen release name. + 2. **Remove namespace and CRDs:** - - Remove the **nginx-gateway** namespace and CRDs to completely clean up all associated resources: + - To remove the **nginx-gateway** namespace and its custom resource definitions (CRDs), run: ```shell kubectl delete ns nginx-gateway kubectl delete crd nginxgateways.gateway.nginx.org ``` - These commands will remove all Kubernetes components associated with the release and delete the release itself. - 3. **Remove the Gateway API resources:** - {{}} +## Next Steps +### Expose NGINX Gateway Fabric -## Expose NGINX Gateway Fabric - -Once NGINX Gateway Fabric is installed, the next step is to make it accessible. Refer to the following instructions for guidance on configuring access and creating the necessary services: - -- [Expose the NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}). +{{}} diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index dc9359ec3b..f6b528e585 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -15,7 +15,7 @@ To complete this guide, you'll need to install: - [kubectl](https://kubernetes.io/docs/tasks/tools/), a command-line interface for managing Kubernetes clusters. -## Deploy NGINX Gateway Fabric from Manifests +## Deploy NGINX Gateway Fabric Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. With manifests, you can configure your deployment exactly how you want. Manifests also make it easy to replicate deployments across environments or clusters, ensuring consistency. @@ -51,7 +51,7 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. ``` -## Upgrade NGINX Gateway Fabric from Manifests +## Upgrade NGINX Gateway Fabric {{}}For guidance on zero downtime upgrades, see the [Delay Pod Termination](#configure-delayed-pod-termination-for-zero-downtime-upgrades) section below.{{}} @@ -83,52 +83,44 @@ To upgrade NGINX Gateway Fabric and get the latest features and improvements, ta ## Delay Pod Termination for Zero Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} -To avoid client service interruptions when upgrading NGINX Gateway Fabric, you can configure [`PreStop` hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) to delay terminating the NGINX Gateway Fabric pod, allowing the pod to complete certain actions before shutting down. This ensures a smooth upgrade without any downtime, also known as a zero downtime upgrade. - -For an in-depth explanation of how Kubernetes handles pod termination, see the [Termination of Pods](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination) topic on their official website. - -{{}}Keep in mind that NGINX won't shut down while WebSocket or other long-lived connections are open. NGINX will only stop when these connections are closed by the client or the backend. If these connections stay open during an upgrade, Kubernetes might need to shut down NGINX forcefully. This sudden shutdown could interrupt service for clients.{{}} +{{< include "installation/delay-pod-termination/delay-pod-termination-overview.md" >}} Follow these steps to configure delayed pod termination: 1. Open the `nginx-gateway.yaml` for editing. -2. **Add delayed shutdown hooks**: - - In the `nginx-gateway.yaml` file, add `lifecycle: preStop` hooks to both the `nginx` and `nginx-gateway` container definitions. These hooks instruct the containers to delay their shutdown process, allowing time for connections to close gracefully. Update the `sleep` value to what works for your environment. - - ```yaml - <...> - name: nginx-gateway - <...> - lifecycle: - preStop: - exec: - command: - - /usr/bin/gateway - - sleep - - --duration=40s # This flag is optional, the default is 30s - <...> - name: nginx - <...> - lifecycle: - preStop: - exec: - command: - - /bin/sleep - - "40" - <...> - ``` - -3. **Set the termination grace period**: - - Set `terminationGracePeriodSeconds` to a value that is equal to or greater than the `sleep` duration specified in the `preStop` hook (default is `30`). This setting prevents Kubernetes from terminating the pod before before the `preStop` hook has completed running. - - ```yaml - terminationGracePeriodSeconds: 50 - ``` - -4. Save the changes. +1. **Add delayed shutdown hooks**: + + - In the `nginx-gateway.yaml` file, add `lifecycle: preStop` hooks to both the `nginx` and `nginx-gateway` container definitions. These hooks instruct the containers to delay their shutdown process, allowing time for connections to close gracefully. Update the `sleep` value to what works for your environment. + + ```yaml + <...> + name: nginx-gateway + <...> + lifecycle: + preStop: + exec: + command: + - /usr/bin/gateway + - sleep + - --duration=40s # This flag is optional, the default is 30s + <...> + name: nginx + <...> + lifecycle: + preStop: + exec: + command: + - /bin/sleep + - "40" + <...> + ``` + +1. **Set the termination grace period**: + + - {{}} + +1. Save the changes. {{}} For additional information on configuring and understanding the behavior of containers and pods during their lifecycle, refer to the following Kubernetes documentation: @@ -137,13 +129,13 @@ For additional information on configuring and understanding the behavior of cont {{}} -## Uninstall NGINX Gateway Fabric from Manifests +## Uninstall NGINX Gateway Fabric -To uninstall NGINX Gateway Fabric from your Kubernetes cluster, you must delete both the Gateway API resources and the NGINX Gateway Fabric components. Follow the steps carefully to ensure that everything related to NGINX Gateway Fabric is completely removed. +Follow these steps to uninstall NGINX Gateway Fabric and Gateway API from your Kubernetes cluster: 1. **Uninstall NGINX Gateway Fabric:** - - Run the following commands to remove NGINX Gateway Fabric its custom resource definitions (CRDs) from your system: + - To remove NGINX Gateway Fabric and its custom resource definitions (CRDs), run: ```shell kubectl delete -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml @@ -157,7 +149,9 @@ To uninstall NGINX Gateway Fabric from your Kubernetes cluster, you must delete - {{}} -## Expose NGINX Gateway Fabric +## Next Steps + +### Expose NGINX Gateway Fabric -After installing NGINX Gateway Fabric, the next step is to ensure it's accessible. For directions on configuring access and creating the required services, follow the directions in [Expose the NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}). +{{}} From f55fb98c63070f0543808fabe607fed4d42dc401 Mon Sep 17 00:00:00 2001 From: Travis Martin <33876974+travisamartin@users.noreply.github.com> Date: Mon, 20 Nov 2023 05:59:34 -0800 Subject: [PATCH 040/101] Update site/content/installation/installing-ngf/manifests.md Co-authored-by: Alan Dooley --- site/content/installation/installing-ngf/manifests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index f6b528e585..d61f25e12a 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -1,7 +1,7 @@ --- title: "Installation with Manifests" description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric using Kubernetes manifests." -weight: 100 +weight: 200 toc: true docs: "DOCS-000" --- From 5cb86267a77aef71ae631cbf3e0b093f346014bc Mon Sep 17 00:00:00 2001 From: Travis Martin <33876974+travisamartin@users.noreply.github.com> Date: Mon, 20 Nov 2023 05:59:42 -0800 Subject: [PATCH 041/101] Update site/content/installation/installing-ngf/helm.md Co-authored-by: Alan Dooley --- site/content/installation/installing-ngf/helm.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index 937d3a6dc5..f630b3ba74 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -1,7 +1,7 @@ --- title: "Installation with Helm" description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric in a Kubernetes cluster with using Helm." -weight: 200 +weight: 100 toc: true docs: "DOCS-000" --- From a34ed7779b847e4b38522a046dda85765843c18f Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Mon, 20 Nov 2023 17:25:10 +0000 Subject: [PATCH 042/101] Update Gateway Architecture document to fit standard NGINX Formatting This commit updates the architecture document to be more specific in its titling, to add the F5 prefix to the description, and to change the formatting used in the document to match our markdown conventions. There are some other, smaller changes included to rephrase or change the text in order to match the newer phrasing and architecture of the equivalent NGINX Ingress Controller document for consistency. --- site/content/concepts/architecture.md | 167 ------------------ site/content/{concepts => overview}/_index.md | 4 +- .../gateway-api-compatibility.md | 0 site/content/overview/gateway-architecture.md | 129 ++++++++++++++ .../resource-validation.md | 0 5 files changed, 131 insertions(+), 169 deletions(-) delete mode 100644 site/content/concepts/architecture.md rename site/content/{concepts => overview}/_index.md (66%) rename site/content/{concepts => overview}/gateway-api-compatibility.md (100%) create mode 100644 site/content/overview/gateway-architecture.md rename site/content/{concepts => overview}/resource-validation.md (100%) diff --git a/site/content/concepts/architecture.md b/site/content/concepts/architecture.md deleted file mode 100644 index dd316ba85b..0000000000 --- a/site/content/concepts/architecture.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -title: "Architecture" -description: "Learn about the architecture and design principles of NGINX Gateway Fabric." -weight: 100 -toc: true -docs: "DOCS-000" ---- - -# Architecture - -The target audience of this architecture document includes the following groups: - -- *Cluster Operators* who would like to know how the software works and also better understand how it can fail. -- *Developers* who would like to [contribute][contribute] to the project. - -We assume that the reader is familiar with core Kubernetes concepts, such as Pods, Deployments, Services, and Endpoints. -Additionally, we recommend reading [this blog post][blog] for an overview of the NGINX architecture. - -[contribute]: https://github.com/nginxinc/nginx-gateway-fabric/blob/main/CONTRIBUTING.md - -[blog]: https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/ - -## What is NGINX Gateway Fabric? - -The NGINX Gateway Fabric is a component in a Kubernetes cluster that configures an HTTP load balancer according to -Gateway API resources created by Cluster Operators and Application Developers. - -> If you’d like to read more about the Gateway API, refer to [Gateway API documentation][sig-gateway]. - -This document focuses specifically on the NGINX Gateway Fabric, also known as NGF, which uses NGINX as its data -plane. - -[sig-gateway]: https://gateway-api.sigs.k8s.io/ - -## NGINX Gateway Fabric at a High Level - -To start, let's take a high-level look at the NGINX Gateway Fabric (NGF). The accompanying diagram illustrates an -example scenario where NGF exposes two web applications hosted within a Kubernetes cluster to external clients on the -internet: - -![NGF High Level](/img/ngf-high-level.png) - -The figure shows: - -- A *Kubernetes cluster*. -- Users *Cluster Operator*, *Application Developer A* and *Application Developer B*. These users interact with the -cluster through the Kubernetes API by creating Kubernetes objects. -- *Clients A* and *Clients B* connect to *Applications A* and *B*, respectively. This applications have been deployed by -the corresponding users. -- The *NGF Pod*, [deployed by *Cluster Operator*](/docs/how-to/installation/installation.md) in the Namespace *nginx-gateway*. For -scalability and availability, you can have multiple replicas. This Pod consists of two containers: `NGINX` and `NGF`. -The *NGF* container interacts with the Kubernetes API to retrieve the most up-to-date Gateway API resources created -within the cluster. It then dynamically configures the *NGINX* container based on these resources, ensuring proper -alignment between the cluster state and the NGINX configuration. -- *Gateway AB*, created by *Cluster Operator*, requests a point where traffic can be translated to Services within the -cluster. This Gateway includes a listener with a hostname `*.example.com`. Application Developers have the ability to -attach their application's routes to this Gateway if their application's hostname matches `*.example.com`. -- *Application A* with two Pods deployed in the *applications* Namespace by *Application Developer A*. To expose the -application to its clients (*Clients A*) via the host `a.example.com`, *Application Developer A* creates *HTTPRoute A* -and attaches it to `Gateway AB`. -- *Application B* with one Pod deployed in the *applications* Namespace by *Application Developer B*. To expose the -application to its clients (*Clients B*) via the host `b.example.com`, *Application Developer B* creates *HTTPRoute B* -and attaches it to `Gateway AB`. -- *Public Endpoint*, which fronts the *NGF* Pod. This is typically a TCP load balancer (cloud, software, or hardware) -or a combination of such load balancer with a NodePort Service. *Clients A* and *B* connect to their applications via -the *Public Endpoint*. - -The connections related to client traffic are depicted by the yellow and purple arrows, while the black arrows represent -access to the Kubernetes API. The resources within the cluster are color-coded based on the user responsible for their -creation. For example, the Cluster Operator is denoted by the color green, indicating that they have created and manage -all the green resources. - -> Note: For simplicity, many necessary Kubernetes resources like Deployment and Services aren't shown, -> which the Cluster Operator and the Application Developers also need to create. - -Next, let's explore the NGF Pod. - -## The NGINX Gateway Fabric Pod - -The NGINX Gateway Fabric consists of two containers: - -1. `nginx`: the data plane. Consists of an NGINX master process and NGINX worker processes. The master process controls -the worker processes. The worker processes handle the client traffic and load balance the traffic to the backend -applications. -2. `nginx-gateway`: the control plane. Watches Kubernetes objects and configures NGINX. - -These containers are deployed in a single Pod as a Kubernetes Deployment. - -The `nginx-gateway`, or the control plane, is a [Kubernetes controller][controller], written with -the [controller-runtime][runtime] library. It watches Kubernetes objects (Services, Endpoints, Secrets, and Gateway API -CRDs), translates them to NGINX configuration, and configures NGINX. This configuration happens in two stages. First, -NGINX configuration files are written to the NGINX configuration volume shared by the `nginx-gateway` and `nginx` -containers. Next, the control plane reloads the NGINX process. This is possible because the two -containers [share a process namespace][share], which allows the NGF process to send signals to the NGINX master process. - -The diagram below provides a visual representation of the interactions between processes within the `nginx` and -`nginx-gateway` containers, as well as external processes/entities. It showcases the connections and relationships between -these components. - -![NGF pod](/img/ngf-pod.png) - -The following list provides a description of each connection, along with its corresponding type indicated in -parentheses. To enhance readability, the suffix "process" has been omitted from the process descriptions below. - -1. (HTTPS) - - Read: *NGF* reads the *Kubernetes API* to get the latest versions of the resources in the cluster. - - Write: *NGF* writes to the *Kubernetes API* to update the handled resources' statuses and emit events. If there's - more than one replica of *NGF* and [leader election](/deploy/helm-chart/README.md#configuration) is enabled, only - the *NGF* Pod that is leading will write statuses to the *Kubernetes API*. -2. (HTTP, HTTPS) *Prometheus* fetches the `controller-runtime` and NGINX metrics via an HTTP endpoint that *NGF* exposes. - The default is :9113/metrics. Note: Prometheus is not required by NGF, the endpoint can be turned off. -3. (File I/O) - - Write: *NGF* generates NGINX *configuration* based on the cluster resources and writes them as `.conf` files to the - mounted `nginx-conf` volume, located at `/etc/nginx/conf.d`. It also writes *TLS certificates* and *keys* - from [TLS Secrets][secrets] referenced in the accepted Gateway resource to the `nginx-secrets` volume at the - path `/etc/nginx/secrets`. - - Read: *NGF* reads the PID file `nginx.pid` from the `nginx-run` volume, located at `/var/run/nginx`. *NGF* - extracts the PID of the nginx process from this file in order to send reload signals to *NGINX master*. -4. (File I/O) *NGF* writes logs to its *stdout* and *stderr*, which are collected by the container runtime. -5. (HTTP) *NGF* fetches the NGINX metrics via the unix:/var/run/nginx/nginx-status.sock UNIX socket and converts it to - *Prometheus* format used in #2. -6. (Signal) To reload NGINX, *NGF* sends the [reload signal][reload] to the **NGINX master**. -7. (File I/O) - - Write: The *NGINX master* writes its PID to the `nginx.pid` file stored in the `nginx-run` volume. - - Read: The *NGINX master* reads *configuration files* and the *TLS cert and keys* referenced in the configuration when - it starts or during a reload. These files, certificates, and keys are stored in the `nginx-conf` and `nginx-secrets` - volumes that are mounted to both the `nginx-gateway` and `nginx` containers. -8. (File I/O) - - Write: The *NGINX master* writes to the auxiliary Unix sockets folder, which is located in the `/var/lib/nginx` - directory. - - Read: The *NGINX master* reads the `nginx.conf` file from the `/etc/nginx` directory. This [file][conf-file] contains - the global and http configuration settings for NGINX. In addition, *NGINX master* - reads the NJS modules referenced in the configuration when it starts or during a reload. NJS modules are stored in - the `/usr/lib/nginx/modules` directory. -9. (File I/O) The *NGINX master* sends logs to its *stdout* and *stderr*, which are collected by the container runtime. -10. (File I/O) An *NGINX worker* writes logs to its *stdout* and *stderr*, which are collected by the container runtime. -11. (Signal) The *NGINX master* controls the [lifecycle of *NGINX workers*][lifecycle] it creates workers with the new - configuration and shutdowns workers with the old configuration. -12. (HTTP) To consider a configuration reload a success, *NGF* ensures that at least one NGINX worker has the new - configuration. To do that, *NGF* checks a particular endpoint via the unix:/var/run/nginx/nginx-config-version.sock - UNIX socket. -13. (HTTP,HTTPS) A *client* sends traffic to and receives traffic from any of the *NGINX workers* on ports 80 and 443. -14. (HTTP,HTTPS) An *NGINX worker* sends traffic to and receives traffic from the *backends*. - -[controller]: https://kubernetes.io/docs/concepts/architecture/controller/ - -[runtime]: https://github.com/kubernetes-sigs/controller-runtime - -[secrets]: https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets - -[reload]: https://nginx.org/en/docs/control.html - -[lifecycle]: https://nginx.org/en/docs/control.html#reconfiguration - -[conf-file]: https://github.com/nginxinc/nginx-gateway-fabric/blob/main/internal/mode/static/nginx/conf/nginx.conf - -[share]: https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/ - -## Pod Readiness - -The `nginx-gateway` container includes a readiness endpoint available via the `/readyz` path. This endpoint -is periodically checked by a [readiness probe][readiness] on startup, and returns a 200 OK response when the Pod is -ready to accept traffic for the data plane. The Pod will become Ready after the control plane successfully starts. -If there are relevant Gateway API resources in the cluster, the control plane will also generate the first NGINX -configuration and successfully reload NGINX before the Pod is considered Ready. - -[readiness]: (https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) diff --git a/site/content/concepts/_index.md b/site/content/overview/_index.md similarity index 66% rename from site/content/concepts/_index.md rename to site/content/overview/_index.md index 7123cf5096..ea61fb588b 100644 --- a/site/content/concepts/_index.md +++ b/site/content/overview/_index.md @@ -1,8 +1,8 @@ --- -title: "Concepts" +title: "Overview" description: weight: 100 -linkTitle: "Concepts" +linkTitle: "Overview" menu: docs: parent: NGINX Gateway Fabric diff --git a/site/content/concepts/gateway-api-compatibility.md b/site/content/overview/gateway-api-compatibility.md similarity index 100% rename from site/content/concepts/gateway-api-compatibility.md rename to site/content/overview/gateway-api-compatibility.md diff --git a/site/content/overview/gateway-architecture.md b/site/content/overview/gateway-architecture.md new file mode 100644 index 0000000000..dc635a671e --- /dev/null +++ b/site/content/overview/gateway-architecture.md @@ -0,0 +1,129 @@ +--- +title: "Gateway Architecture" +description: "Learn about the architecture and design principles of F5 NGINX Gateway Fabric." +weight: 100 +toc: true +docs: "DOCS-000" +--- + +The intended audience for this information is primarily the two following groups: + +- _Cluster Operators_ who would like to know how the software works and understand how it can fail. +- _Developers_ who would like to [contribute](https://github.com/nginxinc/nginx-gateway-fabric/blob/main/CONTRIBUTING.md) to the project. + +We assume that the reader is familiar with core Kubernetes concepts, such as Pods, Deployments, Services, and Endpoints. For an understanding of how NGINX itself works, you can read the ["Inside NGINX: How We Designed for Performance & Scale"](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/) blog post. + +## What is NGINX Gateway Fabric? + +NGINX Gateway Fabric (NGF) is a Kubernetes cluster component that uses Gateway API Resources to configure an HTTP load balancer with NGINX as its data plane. + +{{< note >}} To learn more about the Gateway API, you can read the [official Kubernetes documentation](https://gateway-api.sigs.k8s.io/). {{< /note >}} + +## NGINX Gateway Fabric at a High Level + +This figure depicts an example of NGINX Gateway Fabric exposing two web applications within a Kubernetes cluster to clients on the internet: + +{{}} + +{{< note >}} For simplicity, many necessary Kubernetes resources like Deployment and Services aren't shown, which the Cluster Operator and the Application Developers also need to create. {{< /note >}} + +The figure shows: + +- A _Kubernetes cluster_. +- Users _Cluster Operator_, _Application Developer A_ and _Application Developer B_. These users interact with the +cluster through the Kubernetes API by creating Kubernetes objects. +- _Clients A_ and _Clients B_ connect to _Applications A_ and _B_, respectively, which they have deployed. +- The _NGF Pod_, [deployed by _Cluster Operator_]({{< relref "how-to/installation/installation.md">}}) in the Namespace _nginx-gateway_. For +scalability and availability, you can have multiple replicas. This Pod consists of two containers: `NGINX` and `NGF`. +The _NGF_ container interacts with the Kubernetes API to retrieve the most up-to-date Gateway API resources created +within the cluster. It then dynamically configures the _NGINX_ container based on these resources, ensuring proper +alignment between the cluster state and the NGINX configuration. +- _Gateway AB_, created by _Cluster Operator_, requests a point where traffic can be translated to Services within the +cluster. This Gateway includes a listener with a hostname `*.example.com`. Application Developers have the ability to +attach their application's routes to this Gateway if their application's hostname matches `*.example.com`. +- _Application A_ with two Pods deployed in the _applications_ Namespace by _Application Developer A_. To expose the +application to its clients (_Clients A_) via the host `a.example.com`, _Application Developer A_ creates _HTTPRoute A_ +and attaches it to `Gateway AB`. +- _Application B_ with one Pod deployed in the _applications_ Namespace by _Application Developer B_. To expose the +application to its clients (_Clients B_) via the host `b.example.com`, _Application Developer B_ creates _HTTPRoute B_ +and attaches it to `Gateway AB`. +- _Public Endpoint_, which fronts the _NGF_ Pod. This is typically a TCP load balancer (cloud, software, or hardware) +or a combination of such load balancer with a NodePort Service. _Clients A_ and _B_ connect to their applications via +the _Public Endpoint_. + +The yellow and purple arrows represent connections related to the client traffic, and the black arrows represent access to the Kubernetes API. The resources within the cluster are color-coded based on the user responsible for their creation. + +For example, the Cluster Operator is denoted by the color green, indicating they create and manage all the green resources. + +## The NGINX Gateway Fabric Pod + +The NGINX Gateway Fabric consists of two containers: + +1. `nginx`: the data plane. Consists of an NGINX master process and NGINX worker processes. The master process controls the worker processes. The worker processes handle the client traffic and load balance traffic to the backend applications. +1. `nginx-gateway`: the control plane. Watches Kubernetes objects and configures NGINX. + +These containers are deployed in a single Pod as a Kubernetes Deployment. + +The `nginx-gateway`, or the control plane, is a [Kubernetes controller](https://kubernetes.io/docs/concepts/architecture/controller/), written with the [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) library. It watches Kubernetes objects (Services, Endpoints, Secrets, and Gateway API CRDs), translates them to NGINX configuration, and configures NGINX. + +This configuration happens in two stages: + +1. NGINX configuration files are written to the NGINX configuration volume shared by the `nginx-gateway` and `nginx` +containers. +2. The control plane reloads the NGINX process. + +This is possible because the two +containers [share a process namespace](https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/), allowing the NGF process to send signals to the NGINX master process. + +The following diagram represents the connections, relationships and interactions between process with the `nginx` and `nginx-gateway` containers, as well as external processes/entities. + +{{}} + +The following list describes the connections, preceeded by their types in parentheses. For brevity, the suffix "process" has been omitted from the process descriptions. + +1. (HTTPS) + - Read: _NGF_ reads the _Kubernetes API_ to get the latest versions of the resources in the cluster. + - Write: _NGF_ writes to the _Kubernetes API_ to update the handled resources' statuses and emit events. If there's + more than one replica of _NGF_ and [leader election](https://github.com/nginxinc/nginx-gateway-fabric/tree/main/deploy/helm-chart#configuration) is enabled, only + the _NGF_ Pod that is leading will write statuses to the _Kubernetes API_. +1. (HTTP, HTTPS) _Prometheus_ fetches the `controller-runtime` and NGINX metrics via an HTTP endpoint that _NGF_ exposes. + The default is :9113/metrics. Note: Prometheus is not required by NGF, the endpoint can be turned off. +1. (File I/O) + - Write: _NGF_ generates NGINX _configuration_ based on the cluster resources and writes them as `.conf` files to the + mounted `nginx-conf` volume, located at `/etc/nginx/conf.d`. It also writes _TLS certificates_ and _keys_ + from [TLS Secrets](https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets) referenced in the accepted Gateway resource to the `nginx-secrets` volume at the + path `/etc/nginx/secrets`. + - Read: _NGF_ reads the PID file `nginx.pid` from the `nginx-run` volume, located at `/var/run/nginx`. _NGF_ + extracts the PID of the nginx process from this file in order to send reload signals to _NGINX master_. +1. (File I/O) _NGF_ writes logs to its _stdout_ and _stderr_, which are collected by the container runtime. +1. (HTTP) _NGF_ fetches the NGINX metrics via the unix:/var/run/nginx/nginx-status.sock UNIX socket and converts it to + _Prometheus_ format used in #2. +1. (Signal) To reload NGINX, _NGF_ sends the [reload signal](https://nginx.org/en/docs/control.html) to the **NGINX master**. +1. (File I/O) + - Write: The _NGINX master_ writes its PID to the `nginx.pid` file stored in the `nginx-run` volume. + - Read: The _NGINX master_ reads _configuration files_ and the _TLS cert and keys_ referenced in the configuration when + it starts or during a reload. These files, certificates, and keys are stored in the `nginx-conf` and `nginx-secrets` + volumes that are mounted to both the `nginx-gateway` and `nginx` containers. +8. (File I/O) + - Write: The _NGINX master_ writes to the auxiliary Unix sockets folder, which is located in the `/var/lib/nginx` + directory. + - Read: The _NGINX master_ reads the `nginx.conf` file from the `/etc/nginx` directory. This [file](https://github.com/nginxinc/nginx-gateway-fabric/blob/main/internal/mode/static/nginx/conf/nginx.conf) contains + the global and http configuration settings for NGINX. In addition, _NGINX master_ + reads the NJS modules referenced in the configuration when it starts or during a reload. NJS modules are stored in + the `/usr/lib/nginx/modules` directory. +9. (File I/O) The _NGINX master_ sends logs to its _stdout_ and _stderr_, which are collected by the container runtime. +10. (File I/O) An _NGINX worker_ writes logs to its _stdout_ and _stderr_, which are collected by the container runtime. +11. (Signal) The _NGINX master_ controls the [lifecycle of _NGINX workers_](https://nginx.org/en/docs/control.html#reconfiguration) it creates workers with the new + configuration and shutdowns workers with the old configuration. +12. (HTTP) To consider a configuration reload a success, _NGF_ ensures that at least one NGINX worker has the new + configuration. To do that, _NGF_ checks a particular endpoint via the unix:/var/run/nginx/nginx-config-version.sock + UNIX socket. +13. (HTTP, HTTPS) A _client_ sends traffic to and receives traffic from any of the _NGINX workers_ on ports 80 and 443. +14. (HTTP, HTTPS) An _NGINX worker_ sends traffic to and receives traffic from the _backends_. + +## Pod Readiness + +The `nginx-gateway` container includes a readiness endpoint available via the `/readyz` path. The endpoint +is periodically checked by a [readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) on startup, returning a `200 OK` response when the Pod can accept traffic for the data plane. After the control plane successfully starts, the Pod becomes ready. + +If there are relevant Gateway API resources in the cluster, the control plane will generate the first NGINX configuration and successfully reload NGINX before the Pod is considered ready. \ No newline at end of file diff --git a/site/content/concepts/resource-validation.md b/site/content/overview/resource-validation.md similarity index 100% rename from site/content/concepts/resource-validation.md rename to site/content/overview/resource-validation.md From 01a446629be512b8e0de363185c60bec51a2fffe Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 20 Nov 2023 09:29:23 -0800 Subject: [PATCH 043/101] chore: fixed typo --- site/content/installation/installing-ngf/helm.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index f630b3ba74..f13dbee61c 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -1,6 +1,6 @@ --- title: "Installation with Helm" -description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric in a Kubernetes cluster with using Helm." +description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric in a Kubernetes cluster with Helm." weight: 100 toc: true docs: "DOCS-000" From adcd3d32f20a866dd303b0a9507361db4c8d0e1b Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Mon, 20 Nov 2023 18:25:55 +0000 Subject: [PATCH 044/101] fix: typos, style fixes --- .../traffic-management/advanced-routing.md | 16 +++---- .../integrating-cert-manager.md | 45 +++++++++---------- .../routing-traffic-to-your-app.md | 12 ++--- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/site/content/how-to/traffic-management/advanced-routing.md b/site/content/how-to/traffic-management/advanced-routing.md index 232cb1ca9c..57b95cc6a9 100644 --- a/site/content/how-to/traffic-management/advanced-routing.md +++ b/site/content/how-to/traffic-management/advanced-routing.md @@ -6,11 +6,11 @@ toc: true docs: "DOCS-000" --- -In this guide we will configure advanced routing rules for multiple applications. These rules will showcase request matching by path, headers, query parameters, and method. For an introduction to exposing your application, we recommend that you go through the [basic guide]({{< relref "/how-to/traffic-management/routing-traffic-to-your-app.md" >}}) first. +In this guide we will configure advanced routing rules for multiple applications. These rules will showcase request matching by path, headers, query parameters, and method. For an introduction to exposing your application, we recommend that you follow the [basic guide]({{< relref "/how-to/traffic-management/routing-traffic-to-your-app.md" >}}) first. The following image shows the traffic flow that we will be creating with these rules. -![Traffic Flow Diagram](/img/advanced-routing.png) +{{Traffic Flow Diagram}} The goal is to create a set of rules that will result in client requests being sent to specific backends based on the request attributes. In this diagram, we have two versions of the `coffee` service. Traffic for v1 needs to be directed to the old application, while traffic for v2 needs to be directed towards the new application. We also have two `tea` services, one that handles GET operations and one that handles POST operations. Both the `tea` and `coffee` applications share the same Gateway. @@ -39,7 +39,7 @@ kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric ### Deploy the Gateway API Resources for the Coffee Applications -The [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the Gateway: +The [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the gateway: ```yaml kubectl apply -f - <}} -![cert-manager ACME Challenge and certificate management with Gateway API](/img/cert-manager-gateway-workflow.png) - -The diagram above shows a simplified representation of the cert-manager ACME Challenge and certificate issuance process using Gateway API. Please note that not all of the kubernetes objects created in this process are represented in this diagram. +The diagram above shows a simplified representation of the cert-manager ACME challenge and certificate issuance process using Gateway API. Please note that not all of the kubernetes objects created in this process are represented in this diagram. At a high level, the process looks like this: -1. We deploy cert-manager and create a ClusterIssuer which specifies Let’s Encrypt as our CA and Gateway as our ACME HTTP01 Challenge solver. -1. We create a Gateway resource for our domain (cafe.example.com) and configure cert-manager integration using an annotation. +1. We deploy cert-manager and create a ClusterIssuer which specifies Let’s Encrypt as our CA and gateway as our ACME HTTP01 challenge solver. +1. We create a gateway resource for our domain (cafe.example.com) and configure cert-manager integration using an annotation. 1. This starts the certificate issuance process – cert-manager contacts Let’s Encrypt to obtain a certificate, and Let’s Encrypt starts the ACME challenge. As part of this challenge, cert-manager creates a temporary HTTPRoute resource which directs the traffic through NGINX Gateway Fabric to verify we control the domain name in the certificate request. -1. Once the domain has been verified, the certificate is issued. Cert-manager stores the keypair in a Kubernetes secret that is referenced by the Gateway resource. As a result, NGINX is configured to terminate HTTPS traffic from clients using this signed keypair. -1. We deploy our application and our HTTPRoute which defines our routing rules. The routing rules defined configure NGINX to direct requests to https://cafe.example.com/coffee to our coffee-app application, and to use the HTTPS Listener defined in our Gateway resource. -1. When the client connects to https://cafe.example.com/coffee, the request is routed to the coffee-app application and the communication is secured using the signed keypair contained in the cafe-secret Secret. -1. The certificate will be automatically renewed when it is close to expiry, the Secret will be updated using the new Certificate, and NGINX Gateway Fabric will dynamically update the keypair on the filesystem used by NGINX for HTTPS termination once the Secret is updated. +1. Once the domain has been verified, the certificate is issued. Cert-manager stores the keypair in a Kubernetes secret that is referenced by the gateway resource. As a result, NGINX is configured to terminate HTTPS traffic from clients using this signed keypair. +1. We deploy our application and our HTTPRoute which defines our routing rules. The routing rules defined configure NGINX to direct requests to https://cafe.example.com/coffee to our coffee-app application, and to use the HTTPS listener defined in our gateway resource. +1. When the client connects to https://cafe.example.com/coffee, the request is routed to the coffee-app application and the communication is secured using the signed keypair contained in the cafe-secret secret. +1. The certificate will be automatically renewed when it is close to expiry, the secret will be updated using the new certificate, and NGINX Gateway Fabric will dynamically update the keypair on the filesystem used by NGINX for HTTPS termination once the secret is updated. ## Securing Traffic @@ -66,7 +65,7 @@ The first step is to deploy cert-manager onto the cluster. Next we need to create a [ClusterIssuer](https://cert-manager.io/docs/concepts/issuer/), a Kubernetes resource that represents the certificate authority (CA) that will generate the signed certificates by honouring certificate signing requests. -We are using the ACME Issuer type, and Let's Encrypt as the CA server. In order for Let's Encypt to verify that we own the domain a certificate is being requested for, we must complete "challenges". This is to ensure clients are unable to request certificates for domains they do not own. We will configure the Issuer to use a HTTP01 challenge, and our Gateway resource that we will create in the next step as the solver. To read more about HTTP01 challenges, see the [cert-manager documentation](https://cert-manager.io/docs/configuration/acme/http01/). Use the following YAML definition to create the resource, but please note the `email` field must be updated to your own email address. +We are using the ACME Issuer type, and Let's Encrypt as the CA server. In order for Let's Encypt to verify that we own the domain a certificate is being requested for, we must complete "challenges". This is to ensure clients are unable to request certificates for domains they do not own. We will configure the issuer to use a HTTP01 challenge, and our gateway resource that we will create in the next step as the solver. To read more about HTTP01 challenges, see the [cert-manager documentation](https://cert-manager.io/docs/configuration/acme/http01/). Use the following YAML definition to create the resource, but please note the `email` field must be updated to your own email address. ```yaml apiVersion: cert-manager.io/v1 @@ -95,7 +94,7 @@ spec: ### Deploy our Gateway with the cert-manager annotation -Next we need to deploy our Gateway. You can use the YAML manifest bewlo, updating the `spec.listeners[1].hostname` field to the required value for your environment. +Next we need to deploy our gateway. You can use the YAML manifest below, updating the `spec.listeners[1].hostname` field to the required value for your environment. ```yaml apiVersion: gateway.networking.k8s.io/v1beta1 @@ -125,12 +124,12 @@ spec: It's worth noting a couple of key details in this manifest: - The cert-manager annotation is present in the metadata – this enables the cert-manager integration, and tells cert-manager which ClusterIssuer configuration it should use for the certificates. -- There are two Listeners configured, an HTTP Listener on port 80, and an HTTPS Listener on port 443. - - The http Listener on port 80 is required for the HTTP01 ACME challenge to work. This is because as part of the HTTP01 Challenge, a temporary HTTPRoute will be created by cert-manager to solve the ACME challenge, and this HTTPRoute requires a Listener on port 80. See the [HTTP01 Gateway API solver documentation](https://cert-manager.io/docs/configuration/acme/http01/#configuring-the-http-01-gateway-api-solver) for more information. - - The https Listener on port 443 is the Listener we will use in our HTTPRoute in the next step. Cert-manager will create a Certificate for this Listener block. +- There are two listeners configured, an HTTP listener on port 80, and an HTTPS listener on port 443. + - The HTTP listener on port 80 is required for the HTTP01 ACME challenge to work. This is because as part of the HTTP01 challenge, a temporary HTTPRoute will be created by cert-manager to solve the ACME challenge, and this HTTPRoute requires a listener on port 80. See the [HTTP01 Gateway API solver documentation](https://cert-manager.io/docs/configuration/acme/http01/#configuring-the-http-01-gateway-api-solver) for more information. + - The HTTPS listener on port 443 is the listener we will use in our HTTPRoute in the next step. Cert-manager will create a certificate for this listener block. - The hostname needs to set to the required value. A new certificate will be issued from the `letsencrypt-prod` ClusterIssuer for the domain, e.g. "cafe.example.com", once the ACME challenge is successful. -Once the certificate has been issued, cert-manager will create a Certificate resource on the cluster and the `cafe-secret` Secret containing the signed keypair in the same Namespace as the Gateway. We can verify the Secret has been created successfully using `kubectl`. Note it will take a little bit of time for the Challenge to complete and the Secret to be created: +Once the certificate has been issued, cert-manager will create a certificate resource on the cluster and the `cafe-secret` Secret containing the signed keypair in the same Namespace as the gateway. We can verify the secret has been created successfully using `kubectl`. Note it will take a little bit of time for the challenge to complete and the secret to be created: ```shell kubectl get secret cafe-secret @@ -142,7 +141,7 @@ cafe-secret kubernetes.io/tls 2 20s ``` ### Deploy our application and HTTPRoute -Now we can create our coffee Deployment and Service, and configure the routing rules. You can use the following manifest to create the Deployment and Service: +Now we can create our coffee deployment and service, and configure the routing rules. You can use the following manifest to create the deployment and service: ```yaml apiVersion: apps/v1 @@ -179,7 +178,7 @@ spec: app: coffee ``` -Deploy our HTTPRoute to configure our routing rules for the coffee application. Note the `parentRefs` section in the spec refers to the Listener configured in the previous step. +Deploy our HTTPRoute to configure our routing rules for the coffee application. Note the `parentRefs` section in the spec refers to the listener configured in the previous step. ```yaml apiVersion: gateway.networking.k8s.io/v1beta1 @@ -259,10 +258,10 @@ Request ID: e64c54a2ac253375ac085d48980f000a ## Troubleshooting -- To troubleshoot any issues related to the cert-manager installation or Issuer setup, see [the cert-manager troubleshooting guide](https://cert-manager.io/docs/troubleshooting/). -- To troubleshoot the HTTP01 ACME Challenge, please see the cert-manager [ACME troubleshooting guide](https://cert-manager.io/docs/troubleshooting/acme/). - - Note that for the HTTP01 Challenge to work using the Gateway resource, HTTPS redirect must not be configured. - - The temporary HTTPRoute created by cert-manager routes the traffic between cert-manager and the Let's Encrypt server through NGINX Gateway Fabric. If the Challenge is not successful, it may be useful to inspect the NGINX logs to see the ACME Challenge requests. You should see something like the following: +- To troubleshoot any issues related to the cert-manager installation or issuer setup, see [the cert-manager troubleshooting guide](https://cert-manager.io/docs/troubleshooting/). +- To troubleshoot the HTTP01 ACME challenge, please see the cert-manager [ACME troubleshooting guide](https://cert-manager.io/docs/troubleshooting/acme/). + - Note that for the HTTP01 challenge to work using the gateway resource, HTTPS redirect must not be configured. + - The temporary HTTPRoute created by cert-manager routes the traffic between cert-manager and the Let's Encrypt server through NGINX Gateway Fabric. If the challenge is not successful, it may be useful to inspect the NGINX logs to see the ACME challenge requests. You should see something like the following: ```shell kubectl logs -n nginx-gateway -c nginx @@ -281,7 +280,7 @@ Request ID: e64c54a2ac253375ac085d48980f000a ## Links - [Gateway docs](https://gateway-api.sigs.k8s.io) -- [Cert-manager Gateway usage](https://cert-manager.io/docs/usage/gateway/) +- [Cert-manager gateway usage](https://cert-manager.io/docs/usage/gateway/) - [Cert-manager ACME](https://cert-manager.io/docs/configuration/acme/) - [Let’s Encrypt](https://letsencrypt.org) - [NGINX HTTPS docs](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/) diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index 39d4fba37b..7d10dd9af2 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -30,7 +30,7 @@ The application we are going to use in this guide is a simple **coffee** applica Using this architecture, the **coffee** application is not accessible outside the cluster. We want to expose this application on the hostname "cafe.example.com" so that clients outside the cluster can access it. -Install NGINX Gateway Fabric and create two gateway API resources: a [gateway](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway) and an [HTTPRoute](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute). +Install NGINX Gateway Fabric and create two Gateway API resources: a [gateway](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway) and an [HTTPRoute](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute). Using these resources we will configure a simple routing rule to match all HTTP traffic with the hostname "cafe.example.com" and route it to the **coffee** service. @@ -95,7 +95,7 @@ service/coffee ClusterIP 10.96.75.77 80/TCP 77s ## Application architecture with NGINX Gateway Fabric -To route traffic to the **coffee** application, we will create a gateway and HTTPRoute. The following diagram shows the configuration we'll be creating in the next step: +To route traffic to the **coffee** application, we will create a gateway and HTTPRoute. The following diagram shows the configuration we are creating in the next step: ![Configuration](/img/route-all-traffic-config.png) @@ -103,7 +103,7 @@ We need a gateway to create an entry point for HTTP traffic coming into the clus To route HTTP traffic from the gateway to the **coffee** service, we need to create an HTTPRoute named **coffee** and attach to the gateway. This HTTPRoute will have a single routing rule that routes all traffic to the hostname "cafe.example.com" from the gateway to the **coffee** service. -Once NGINX Gateway Fabric processes the **cafe** gateway and **coffee** HTTPRoute, it will configure its dataplane, NGINX, to route all HTTP requests to "cafe.example.com" to the pods that the **coffee** service targets: +Once NGINX Gateway Fabric processes the **cafe** gateway and **coffee** HTTPRoute, it will configure its data plane, NGINX, to route all HTTP requests to "cafe.example.com" to the pods that the **coffee** service targets: ![Traffic Flow](/img/route-all-traffic-flow.png) @@ -111,9 +111,9 @@ The **coffee** service is omitted from the diagram above because the NGINX Gatew {{< note >}}In the diagrams above, all resources that are the responsibility of the cluster operator are shown in green. The orange resources are the responsibility of the application developers. -See the [roles and personas](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1) gateway API document for more information on these roles.{{< /note >}} +See the [roles and personas](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1) Gateway API document for more information on these roles.{{< /note >}} -## Create the gateway API resources +## Create the Gateway API resources To create the **cafe** gateway, copy and paste the following into your terminal: @@ -364,7 +364,7 @@ If your issue persists, [contact us](https://github.com/nginxinc/nginx-gateway-f ## Further Reading -To learn more about the gateway API and the resources we created in this guide, check out the following resources: +To learn more about the Gateway API and the resources we created in this guide, check out the following resources: - [Gateway API Overview](https://gateway-api.sigs.k8s.io/concepts/api-overview/) - [Deploying a simple Gateway](https://gateway-api.sigs.k8s.io/guides/simple-gateway/) From c8069f943c37f3d4ad7b8e5c754a72eba35df17c Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 20 Nov 2023 12:14:37 -0800 Subject: [PATCH 045/101] changed title case to sentence --- site/content/installation/installing-ngf/helm.md | 16 ++++++++-------- .../installation/installing-ngf/manifests.md | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index f13dbee61c..c035b6ab23 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -18,7 +18,7 @@ To complete this guide, you'll need to install: ## Deploy NGINX Gateway Fabric -### Install from the OCI Registry +### Install from the OCI registry - To install the latest stable release of NGINX Gateway Fabric in the **nginx-gateway** namespace, run the following command: @@ -28,7 +28,7 @@ To complete this guide, you'll need to install: Change `` to the name you want for your release. If the namespace already exists, you can omit the optional `--create-namespace` flag. If you want the latest version from the **main** branch, add `--version 0.0.0-edge` to your install command. -### Install from Sources{#install-from-sources} +### Install from sources {#install-from-sources} 1. {{}} @@ -46,7 +46,7 @@ To complete this guide, you'll need to install: To upgrade NGINX Gateway Fabric and get the latest features and improvements, take the following steps: -### Upgrade Gateway Resources +### Upgrade Gateway resources To upgrade your Gateway API resources, take the following steps: @@ -78,9 +78,9 @@ To upgrade the CRDs, take the following steps: Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply. ``` -### Upgrade NGINX Gateway Fabric Release +### Upgrade NGINX Gateway Fabric release -#### Upgrade from the OCI Registry +#### Upgrade from the OCI registry - To upgrade to the latest stable release of NGINX Gateway Fabric, run: @@ -90,7 +90,7 @@ To upgrade the CRDs, take the following steps: Replace `` with your chosen release name. -#### Upgrade from Sources +#### Upgrade from sources 1. {{}} @@ -102,7 +102,7 @@ To upgrade the CRDs, take the following steps: Replace `` with your chosen release name. -## Delay Pod Termination for Zero Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} +## Delay pod termination for zero downtime upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} {{< include "installation/delay-pod-termination/delay-pod-termination-overview.md" >}} @@ -175,7 +175,7 @@ Follow these steps to uninstall NGINX Gateway Fabric and Gateway API from your K - {{}} -## Next Steps +## Next steps ### Expose NGINX Gateway Fabric diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index d61f25e12a..6732d95508 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -21,7 +21,7 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. {{}}By default, NGINX Gateway Fabric is installed in the **nginx-gateway** namespace. You can deploy in another namespace by modifying the manifest files.{{}} -1. **Install the Gateway API Resources:** +1. **Install the Gateway API resources:** - Start by installing the Gateway API resources, including the CRDs and the validating webhook: ```shell kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml @@ -57,7 +57,7 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. To upgrade NGINX Gateway Fabric and get the latest features and improvements, take the following steps: -1. **Upgrade Gateway API Resources:** +1. **Upgrade Gateway API resources:** - Verify that your NGINX Gateway Fabric version is compatible with the Gateway API resources. Refer to the [Technical Specifications]({{< relref "reference/technical-specifications.md" >}}) for details. - Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) for any important upgrade-specific information. @@ -74,14 +74,14 @@ To upgrade NGINX Gateway Fabric and get the latest features and improvements, ta kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml ``` -3. **Upgrade NGINX Gateway Fabric Deployment:** +3. **Upgrade NGINX Gateway Fabric deployment:** - To upgrade the deployment, run: ```shell kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml ``` -## Delay Pod Termination for Zero Downtime Upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} +## Delay pod termination for zero downtime upgrades {#configure-delayed-pod-termination-for-zero-downtime-upgrades} {{< include "installation/delay-pod-termination/delay-pod-termination-overview.md" >}} @@ -149,7 +149,7 @@ Follow these steps to uninstall NGINX Gateway Fabric and Gateway API from your K - {{}} -## Next Steps +## Next steps ### Expose NGINX Gateway Fabric From 7e4da059392f0f856f942ae6467eaa5336c6238e Mon Sep 17 00:00:00 2001 From: Travis Martin Date: Mon, 20 Nov 2023 12:19:51 -0800 Subject: [PATCH 046/101] changed Manifests title to sentence case --- site/content/installation/installing-ngf/manifests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index 6732d95508..e02c853470 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -1,5 +1,5 @@ --- -title: "Installation with Manifests" +title: "Installation with Kubernetes manifests" description: "Learn how to install, upgrade, and uninstall NGINX Gateway Fabric using Kubernetes manifests." weight: 200 toc: true From c67a46f2be7f19071d304b72d3e9c943cb4367a1 Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:12:47 +0000 Subject: [PATCH 047/101] Update site/content/how-to/maintenance/upgrade-apps-without-downtime.md Co-authored-by: Travis Martin <33876974+travisamartin@users.noreply.github.com> --- .../content/how-to/maintenance/upgrade-apps-without-downtime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index 76a77cee96..f2da36b388 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -10,7 +10,7 @@ docs: "DOCS-000" ## Overview -NGINX Gateway Fabric allows to upgrade applications without downtime. +NGINX Gateway Fabric allows upgrading applications without downtime. {{< note >}}See the [Architecture document]({{< relref "/concepts/architecture.md" >}}) to learn more about NGINX Gateway Fabric architecture.{{< /note >}} From 3ee2769d7bc539a403a52e924b4ff4df0b3eaa70 Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:13:02 +0000 Subject: [PATCH 048/101] Update site/content/how-to/maintenance/upgrade-apps-without-downtime.md Co-authored-by: Travis Martin <33876974+travisamartin@users.noreply.github.com> --- .../content/how-to/maintenance/upgrade-apps-without-downtime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index f2da36b388..1846a82493 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -1,5 +1,5 @@ --- -title: "Upgrade Applications without Downtime" +title: "Upgrade applications without downtime" description: "Learn how to use NGINX Gateway Fabric to upgrade applications without downtime." weight: 100 toc: true From d0fa1540335137abf3cd41da13a3e1f32eee985f Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:13:11 +0000 Subject: [PATCH 049/101] Update site/content/how-to/maintenance/upgrade-apps-without-downtime.md Co-authored-by: Travis Martin <33876974+travisamartin@users.noreply.github.com> --- .../content/how-to/maintenance/upgrade-apps-without-downtime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index 1846a82493..18bc78c1a1 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -32,7 +32,7 @@ Endpoints frequently change during application upgrades: Kubernetes creates pods NGINX Gateway Fabric detects changes to endpoints by watching their corresponding [EndpointSlices](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/). -In NGINX configuration, a service is represented as an [upstream](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream), and an endpoint as an [upstream server](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#server). +In an NGINX configuration, a service is represented as an [upstream](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream), and an endpoint as an [upstream server](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#server). Adding and removing endpoints are two of the most common cases: From ffa047be51eea50ed7a6ba79a02abdd07a49f127 Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:13:44 +0000 Subject: [PATCH 050/101] Update site/content/how-to/maintenance/upgrade-apps-without-downtime.md Co-authored-by: Travis Martin <33876974+travisamartin@users.noreply.github.com> --- .../content/how-to/maintenance/upgrade-apps-without-downtime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index 18bc78c1a1..50c56370a5 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -39,7 +39,7 @@ Adding and removing endpoints are two of the most common cases: - If an endpoint is added, NGINX Gateway Fabric adds an upstream server to NGINX that corresponds to the endpoint, then reloads NGINX. Next, NGINX will start proxying traffic to that endpoint. - If an endpoint is removed, NGINX Gateway Fabric removes the corresponding upstream server from NGINX. After a reload, NGINX will stop proxying traffic to that server. However, it will finish proxying any pending requests to that server before switching to another endpoint. -As long as you have more than one endpoint ready, the clients will not experience any downtime during upgrades. +As long as you have more than one endpoint ready, clients won't experience downtime during upgrades. {{< note >}}It is good practice to configure a [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) in the deployment so that a pod can advertise when it is ready to receive traffic. Note that NGINX Gateway Fabric will not add any endpoint to NGINX that is not ready.{{< /note >}} From a93eb38d021f5ac150159aa96822adcf67c6d9b4 Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:17:18 +0000 Subject: [PATCH 051/101] Update site/content/how-to/maintenance/upgrade-apps-without-downtime.md Co-authored-by: Travis Martin <33876974+travisamartin@users.noreply.github.com> --- .../content/how-to/maintenance/upgrade-apps-without-downtime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index 50c56370a5..07d51162ca 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -41,7 +41,7 @@ Adding and removing endpoints are two of the most common cases: As long as you have more than one endpoint ready, clients won't experience downtime during upgrades. -{{< note >}}It is good practice to configure a [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) in the deployment so that a pod can advertise when it is ready to receive traffic. Note that NGINX Gateway Fabric will not add any endpoint to NGINX that is not ready.{{< /note >}} +{{< note >}}It is good practice to configure a [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) in the deployment so that a pod can report when it is ready to receive traffic. Note that NGINX Gateway Fabric will not add any endpoint to NGINX that is not ready.{{< /note >}} ## Prerequisites From 327fb326f4e8dfb01bb23969f2d5b2aa73d5adde Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:17:35 +0000 Subject: [PATCH 052/101] Update site/content/how-to/maintenance/upgrade-apps-without-downtime.md Co-authored-by: Travis Martin <33876974+travisamartin@users.noreply.github.com> --- .../content/how-to/maintenance/upgrade-apps-without-downtime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index 07d51162ca..aa3181fb17 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -83,7 +83,7 @@ With this method, you deploy a new version of the application (blue version) as There are two ways to switch the traffic: -- Update the service selector to select the pods of the blue version instead of the green. As a result, NGINX Gateway Fabric removes the green upstream servers from NGINX and add the blue ones. With this approach, it is not necessary to update the **HTTPRoute**. +- Update the service selector to select the pods of the blue version instead of the green. As a result, NGINX Gateway Fabric removes the green upstream servers from NGINX and adds the blue ones. With this approach, it is not necessary to update the **HTTPRoute**. - Create a separate service for the blue version and update the backend reference in the **HTTPRoute** to reference this service, which leads to the same result as with the previous option. ## Canary releases From 1b2adb849bf5172c18f0452d4cc2bdcaa654ee5c Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:31:39 +0000 Subject: [PATCH 053/101] Apply suggestions from code review Co-authored-by: Travis Martin <33876974+travisamartin@users.noreply.github.com> --- .../traffic-management/routing-traffic-to-your-app.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index 7d10dd9af2..914e5b46bd 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -81,7 +81,7 @@ This will create the **coffee** service and a deployment with two pods. Run the kubectl get pods,svc ``` -Your output should include 2 **coffee** pods and the **coffee** service: +Your output should include two **coffee** pods and the **coffee** service: ```text NAME READY STATUS RESTARTS AGE @@ -101,9 +101,9 @@ To route traffic to the **coffee** application, we will create a gateway and HTT We need a gateway to create an entry point for HTTP traffic coming into the cluster. The **cafe** gateway we are going to create will open an entry point to the cluster on port 80 for HTTP traffic. -To route HTTP traffic from the gateway to the **coffee** service, we need to create an HTTPRoute named **coffee** and attach to the gateway. This HTTPRoute will have a single routing rule that routes all traffic to the hostname "cafe.example.com" from the gateway to the **coffee** service. +To route HTTP traffic from the gateway to the **coffee** service, we need to create an HTTPRoute named **coffee** and attach it to the gateway. This HTTPRoute will have a single routing rule that routes all traffic to the hostname "cafe.example.com" from the gateway to the **coffee** service. -Once NGINX Gateway Fabric processes the **cafe** gateway and **coffee** HTTPRoute, it will configure its data plane, NGINX, to route all HTTP requests to "cafe.example.com" to the pods that the **coffee** service targets: +Once NGINX Gateway Fabric processes the **cafe** gateway and **coffee** HTTPRoute, it will configure its data plane (NGINX) to route all HTTP requests sent to "cafe.example.com" to the pods that the **coffee** service targets: ![Traffic Flow](/img/route-all-traffic-flow.png) From bb41615a159a84b0fd8244d92e915d00b80981ba Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:34:28 +0000 Subject: [PATCH 054/101] Update site/content/how-to/traffic-management/routing-traffic-to-your-app.md --- .../how-to/traffic-management/routing-traffic-to-your-app.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index 914e5b46bd..79cec4e43b 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -90,7 +90,7 @@ pod/coffee-7dd75bc79b-dett3 1/1 Running 0 77s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/coffee ClusterIP 10.96.75.77 80/TCP 77s +service/coffee ClusterIP 198.51.100.1 80/TCP 77s ``` ## Application architecture with NGINX Gateway Fabric From 4044cf0315ddf68e2bf946c0258cddde344c324e Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Mon, 20 Nov 2023 22:35:23 +0000 Subject: [PATCH 055/101] fix: add canary release def --- .../content/how-to/maintenance/upgrade-apps-without-downtime.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index aa3181fb17..13b74e1b2f 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -88,6 +88,8 @@ There are two ways to switch the traffic: ## Canary releases +Canary releases involve gradually introducing a new version of your application to a subset of nodes in a controlled manner, splitting the traffic between the old are new (canary) release. This allows for monitoring and testing the new release's performance and reliability before full deployment, helping to identify and address issues without impacting the entire user base. + To support canary releases, you can implement an approach with two deployments behind the same service (see [Canary deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#canary-deployment) in the Kubernetes documentation). However, this approach lacks precision for defining the traffic split between the old and the canary version. You can greatly influence it by controlling the number of pods (for example, four pods of the old version and one pod of the canary). However, note that NGINX Gateway Fabric uses [`random two least_conn`](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#random) load balancing method, which doesn't guarantee an exact split based on the number of pods (80/20 in the given example). A more flexible and precise way to implement canary releases is to configure a traffic split in an **HTTPRoute**. In this case, you create a separate deployment for the new version with a separate service. For example, for the rule below, NGINX will proxy 95% of the traffic to the old version endpoints and only 5% to the new ones. From 38626102978dfc15a88ee1eafc9302699a609869 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Mon, 20 Nov 2023 22:39:45 +0000 Subject: [PATCH 056/101] fix: change images shortcode --- .../traffic-management/routing-traffic-to-your-app.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index 79cec4e43b..1a3414db27 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -26,7 +26,7 @@ You can route traffic to your Kubernetes applications using the Gateway API and The application we are going to use in this guide is a simple **coffee** application comprised of one service and two pods: -![coffee app](/img/route-all-traffic-app.png) +{{coffee app}} Using this architecture, the **coffee** application is not accessible outside the cluster. We want to expose this application on the hostname "cafe.example.com" so that clients outside the cluster can access it. @@ -97,7 +97,7 @@ service/coffee ClusterIP 198.51.100.1 80/TCP 77s To route traffic to the **coffee** application, we will create a gateway and HTTPRoute. The following diagram shows the configuration we are creating in the next step: -![Configuration](/img/route-all-traffic-config.png) +{{Configuration}} We need a gateway to create an entry point for HTTP traffic coming into the cluster. The **cafe** gateway we are going to create will open an entry point to the cluster on port 80 for HTTP traffic. @@ -105,7 +105,7 @@ To route HTTP traffic from the gateway to the **coffee** service, we need to cre Once NGINX Gateway Fabric processes the **cafe** gateway and **coffee** HTTPRoute, it will configure its data plane (NGINX) to route all HTTP requests sent to "cafe.example.com" to the pods that the **coffee** service targets: -![Traffic Flow](/img/route-all-traffic-flow.png) +{{Traffic Flow}} The **coffee** service is omitted from the diagram above because the NGINX Gateway Fabric routes directly to the pods that the **coffee** service targets. From faad11cd00c41353de0e8c6d5111a9d1b6a72320 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 21 Nov 2023 10:48:25 +0000 Subject: [PATCH 057/101] Apply suggestions from code review Co-authored-by: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> --- site/content/overview/gateway-architecture.md | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/site/content/overview/gateway-architecture.md b/site/content/overview/gateway-architecture.md index dc635a671e..0fd9854e60 100644 --- a/site/content/overview/gateway-architecture.md +++ b/site/content/overview/gateway-architecture.md @@ -11,21 +11,21 @@ The intended audience for this information is primarily the two following groups - _Cluster Operators_ who would like to know how the software works and understand how it can fail. - _Developers_ who would like to [contribute](https://github.com/nginxinc/nginx-gateway-fabric/blob/main/CONTRIBUTING.md) to the project. -We assume that the reader is familiar with core Kubernetes concepts, such as Pods, Deployments, Services, and Endpoints. For an understanding of how NGINX itself works, you can read the ["Inside NGINX: How We Designed for Performance & Scale"](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/) blog post. +The reader needs to be familiar with core Kubernetes concepts, such as Pods, Deployments, Services, and Endpoints. For an understanding of how NGINX itself works, you can read the ["Inside NGINX: How We Designed for Performance & Scale"](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/) blog post. ## What is NGINX Gateway Fabric? -NGINX Gateway Fabric (NGF) is a Kubernetes cluster component that uses Gateway API Resources to configure an HTTP load balancer with NGINX as its data plane. +NGINX Gateway Fabric is a Kubernetes cluster component that uses Gateway API Resources to configure an HTTP load balancer with NGINX as its data plane. {{< note >}} To learn more about the Gateway API, you can read the [official Kubernetes documentation](https://gateway-api.sigs.k8s.io/). {{< /note >}} -## NGINX Gateway Fabric at a High Level +## NGINX Gateway Fabric at a high level This figure depicts an example of NGINX Gateway Fabric exposing two web applications within a Kubernetes cluster to clients on the internet: {{}} -{{< note >}} For simplicity, many necessary Kubernetes resources like Deployment and Services aren't shown, which the Cluster Operator and the Application Developers also need to create. {{< /note >}} +{{< note >}} For simplicity, the figure does not show many of the necessary Kubernetes resources like deployment and services that the the Cluster Operator and the Application Developers also need to create {{< /note >}} The figure shows: @@ -55,9 +55,9 @@ The yellow and purple arrows represent connections related to the client traffic For example, the Cluster Operator is denoted by the color green, indicating they create and manage all the green resources. -## The NGINX Gateway Fabric Pod +## The NGINX Gateway Fabric pod -The NGINX Gateway Fabric consists of two containers: +NGINX Gateway Fabric consists of two containers: 1. `nginx`: the data plane. Consists of an NGINX master process and NGINX worker processes. The master process controls the worker processes. The worker processes handle the client traffic and load balance traffic to the backend applications. 1. `nginx-gateway`: the control plane. Watches Kubernetes objects and configures NGINX. @@ -70,10 +70,9 @@ This configuration happens in two stages: 1. NGINX configuration files are written to the NGINX configuration volume shared by the `nginx-gateway` and `nginx` containers. -2. The control plane reloads the NGINX process. +1. The control plane reloads the NGINX process. -This is possible because the two -containers [share a process namespace](https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/), allowing the NGF process to send signals to the NGINX master process. +This is possible because the two containers [share a process namespace](https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/), allowing the NGF process to send signals to the NGINX main process. The following diagram represents the connections, relationships and interactions between process with the `nginx` and `nginx-gateway` containers, as well as external processes/entities. @@ -121,9 +120,8 @@ The following list describes the connections, preceeded by their types in parent 13. (HTTP, HTTPS) A _client_ sends traffic to and receives traffic from any of the _NGINX workers_ on ports 80 and 443. 14. (HTTP, HTTPS) An _NGINX worker_ sends traffic to and receives traffic from the _backends_. -## Pod Readiness +## Pod readiness -The `nginx-gateway` container includes a readiness endpoint available via the `/readyz` path. The endpoint -is periodically checked by a [readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) on startup, returning a `200 OK` response when the Pod can accept traffic for the data plane. After the control plane successfully starts, the Pod becomes ready. +The `nginx-gateway` container includes a readiness endpoint available via the `/readyz` path. A [readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) periodically checks the endpoint on startup, returning a `200 OK` response when the Pod can accept traffic for the data plane. After the control plane successfully starts, the Pod becomes ready. If there are relevant Gateway API resources in the cluster, the control plane will generate the first NGINX configuration and successfully reload NGINX before the Pod is considered ready. \ No newline at end of file From c570955c8a9deb2b484ac86e3e2102e1a8a87567 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 21 Nov 2023 11:24:25 +0000 Subject: [PATCH 058/101] Fix broken links to reflect new file paths --- .../content/how-to/maintenance/upgrade-apps-without-downtime.md | 2 +- site/content/overview/gateway-architecture.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index 76a77cee96..09e0c1176a 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -12,7 +12,7 @@ docs: "DOCS-000" NGINX Gateway Fabric allows to upgrade applications without downtime. -{{< note >}}See the [Architecture document]({{< relref "/concepts/architecture.md" >}}) to learn more about NGINX Gateway Fabric architecture.{{< /note >}} +{{< note >}} See the [Architecture document]({{< relref "/overview/gateway-architecture.md" >}}) to learn more about NGINX Gateway Fabric architecture.{{< /note >}} ## NGINX Gateway Fabric functionality diff --git a/site/content/overview/gateway-architecture.md b/site/content/overview/gateway-architecture.md index 0fd9854e60..be192b2b69 100644 --- a/site/content/overview/gateway-architecture.md +++ b/site/content/overview/gateway-architecture.md @@ -33,7 +33,7 @@ The figure shows: - Users _Cluster Operator_, _Application Developer A_ and _Application Developer B_. These users interact with the cluster through the Kubernetes API by creating Kubernetes objects. - _Clients A_ and _Clients B_ connect to _Applications A_ and _B_, respectively, which they have deployed. -- The _NGF Pod_, [deployed by _Cluster Operator_]({{< relref "how-to/installation/installation.md">}}) in the Namespace _nginx-gateway_. For +- The _NGF Pod_, [deployed by _Cluster Operator_]({{< relref "installation">}}) in the Namespace _nginx-gateway_. For scalability and availability, you can have multiple replicas. This Pod consists of two containers: `NGINX` and `NGF`. The _NGF_ container interacts with the Kubernetes API to retrieve the most up-to-date Gateway API resources created within the cluster. It then dynamically configures the _NGINX_ container based on these resources, ensuring proper From 31b7a325ff64a1003c56f1341570f8cbd0bb0d33 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 21 Nov 2023 14:38:24 +0000 Subject: [PATCH 059/101] Fix noun capitalization inconsistencies & line formatting. --- site/content/overview/gateway-architecture.md | 82 ++++++------------- 1 file changed, 26 insertions(+), 56 deletions(-) diff --git a/site/content/overview/gateway-architecture.md b/site/content/overview/gateway-architecture.md index be192b2b69..6e4aea5d35 100644 --- a/site/content/overview/gateway-architecture.md +++ b/site/content/overview/gateway-architecture.md @@ -11,7 +11,7 @@ The intended audience for this information is primarily the two following groups - _Cluster Operators_ who would like to know how the software works and understand how it can fail. - _Developers_ who would like to [contribute](https://github.com/nginxinc/nginx-gateway-fabric/blob/main/CONTRIBUTING.md) to the project. -The reader needs to be familiar with core Kubernetes concepts, such as Pods, Deployments, Services, and Endpoints. For an understanding of how NGINX itself works, you can read the ["Inside NGINX: How We Designed for Performance & Scale"](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/) blog post. +The reader needs to be familiar with core Kubernetes concepts, such as pods, deployments, services, and endpoints. For an understanding of how NGINX itself works, you can read the ["Inside NGINX: How We Designed for Performance & Scale"](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/) blog post. ## What is NGINX Gateway Fabric? @@ -25,31 +25,18 @@ This figure depicts an example of NGINX Gateway Fabric exposing two web applicat {{}} -{{< note >}} For simplicity, the figure does not show many of the necessary Kubernetes resources like deployment and services that the the Cluster Operator and the Application Developers also need to create {{< /note >}} +{{< note >}} The figure does not show many of the necessary Kubernetes resources the Cluster Operators and Application Developers need to create, like deployment and services. {{< /note >}} The figure shows: - A _Kubernetes cluster_. -- Users _Cluster Operator_, _Application Developer A_ and _Application Developer B_. These users interact with the -cluster through the Kubernetes API by creating Kubernetes objects. +- Users _Cluster Operator_, _Application Developer A_ and _Application Developer B_. These users interact with the cluster through the Kubernetes API by creating Kubernetes objects. - _Clients A_ and _Clients B_ connect to _Applications A_ and _B_, respectively, which they have deployed. -- The _NGF Pod_, [deployed by _Cluster Operator_]({{< relref "installation">}}) in the Namespace _nginx-gateway_. For -scalability and availability, you can have multiple replicas. This Pod consists of two containers: `NGINX` and `NGF`. -The _NGF_ container interacts with the Kubernetes API to retrieve the most up-to-date Gateway API resources created -within the cluster. It then dynamically configures the _NGINX_ container based on these resources, ensuring proper -alignment between the cluster state and the NGINX configuration. -- _Gateway AB_, created by _Cluster Operator_, requests a point where traffic can be translated to Services within the -cluster. This Gateway includes a listener with a hostname `*.example.com`. Application Developers have the ability to -attach their application's routes to this Gateway if their application's hostname matches `*.example.com`. -- _Application A_ with two Pods deployed in the _applications_ Namespace by _Application Developer A_. To expose the -application to its clients (_Clients A_) via the host `a.example.com`, _Application Developer A_ creates _HTTPRoute A_ -and attaches it to `Gateway AB`. -- _Application B_ with one Pod deployed in the _applications_ Namespace by _Application Developer B_. To expose the -application to its clients (_Clients B_) via the host `b.example.com`, _Application Developer B_ creates _HTTPRoute B_ -and attaches it to `Gateway AB`. -- _Public Endpoint_, which fronts the _NGF_ Pod. This is typically a TCP load balancer (cloud, software, or hardware) -or a combination of such load balancer with a NodePort Service. _Clients A_ and _B_ connect to their applications via -the _Public Endpoint_. +- The _NGF Pod_, [deployed by _Cluster Operator_]({{< relref "installation">}}) in the namespace _nginx-gateway_. For scalability and availability, you can have multiple replicas. This pod consists of two containers: `NGINX` and `NGF`. The _NGF_ container interacts with the Kubernetes API to retrieve the most up-to-date Gateway API resources created within the cluster. It then dynamically configures the _NGINX_ container based on these resources, ensuring proper alignment between the cluster state and the NGINX configuration. +- _Gateway AB_, created by _Cluster Operator_, requests a point where traffic can be translated to Services within the cluster. This Gateway includes a listener with a hostname `*.example.com`. Application Developers have the ability to attach their application's routes to this Gateway if their application's hostname matches `*.example.com`. +- _Application A_ with two pods deployed in the _applications_ namespace by _Application Developer A_. To expose the application to its clients (_Clients A_) via the host `a.example.com`, _Application Developer A_ creates _HTTPRoute A_ and attaches it to `Gateway AB`. +- _Application B_ with one pod deployed in the _applications_ namespace by _Application Developer B_. To expose the application to its clients (_Clients B_) via the host `b.example.com`, _Application Developer B_ creates _HTTPRoute B_ and attaches it to `Gateway AB`. +- _Public Endpoint_, which fronts the _NGF_ pod. This is typically a TCP load balancer (cloud, software, or hardware) or a combination of such load balancer with a NodePort Service. _Clients A_ and _B_ connect to their applications via the _Public Endpoint_. The yellow and purple arrows represent connections related to the client traffic, and the black arrows represent access to the Kubernetes API. The resources within the cluster are color-coded based on the user responsible for their creation. @@ -62,14 +49,13 @@ NGINX Gateway Fabric consists of two containers: 1. `nginx`: the data plane. Consists of an NGINX master process and NGINX worker processes. The master process controls the worker processes. The worker processes handle the client traffic and load balance traffic to the backend applications. 1. `nginx-gateway`: the control plane. Watches Kubernetes objects and configures NGINX. -These containers are deployed in a single Pod as a Kubernetes Deployment. +These containers are deployed in a single pod as a Kubernetes Deployment. The `nginx-gateway`, or the control plane, is a [Kubernetes controller](https://kubernetes.io/docs/concepts/architecture/controller/), written with the [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) library. It watches Kubernetes objects (Services, Endpoints, Secrets, and Gateway API CRDs), translates them to NGINX configuration, and configures NGINX. This configuration happens in two stages: -1. NGINX configuration files are written to the NGINX configuration volume shared by the `nginx-gateway` and `nginx` -containers. +1. NGINX configuration files are written to the NGINX configuration volume shared by the `nginx-gateway` and `nginx` containers. 1. The control plane reloads the NGINX process. This is possible because the two containers [share a process namespace](https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/), allowing the NGF process to send signals to the NGINX main process. @@ -82,46 +68,30 @@ The following list describes the connections, preceeded by their types in parent 1. (HTTPS) - Read: _NGF_ reads the _Kubernetes API_ to get the latest versions of the resources in the cluster. - - Write: _NGF_ writes to the _Kubernetes API_ to update the handled resources' statuses and emit events. If there's - more than one replica of _NGF_ and [leader election](https://github.com/nginxinc/nginx-gateway-fabric/tree/main/deploy/helm-chart#configuration) is enabled, only - the _NGF_ Pod that is leading will write statuses to the _Kubernetes API_. -1. (HTTP, HTTPS) _Prometheus_ fetches the `controller-runtime` and NGINX metrics via an HTTP endpoint that _NGF_ exposes. - The default is :9113/metrics. Note: Prometheus is not required by NGF, the endpoint can be turned off. + - Write: _NGF_ writes to the _Kubernetes API_ to update the handled resources' statuses and emit events. If there's more than one replica of _NGF_ and [leader election](https://github.com/nginxinc/nginx-gateway-fabric/tree/main/deploy/helm-chart#configuration) is enabled, only the _NGF_ pod that is leading will write statuses to the _Kubernetes API_. +1. (HTTP, HTTPS) _Prometheus_ fetches the `controller-runtime` and NGINX metrics via an HTTP endpoint that _NGF_ exposes (`:9113/metrics` by default). Prometheus is **not** required by NGF, and its endpoint can be turned off. 1. (File I/O) - - Write: _NGF_ generates NGINX _configuration_ based on the cluster resources and writes them as `.conf` files to the - mounted `nginx-conf` volume, located at `/etc/nginx/conf.d`. It also writes _TLS certificates_ and _keys_ - from [TLS Secrets](https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets) referenced in the accepted Gateway resource to the `nginx-secrets` volume at the - path `/etc/nginx/secrets`. - - Read: _NGF_ reads the PID file `nginx.pid` from the `nginx-run` volume, located at `/var/run/nginx`. _NGF_ - extracts the PID of the nginx process from this file in order to send reload signals to _NGINX master_. + - Write: _NGF_ generates NGINX _configuration_ based on the cluster resources and writes them as `.conf` files to the mounted `nginx-conf` volume, located at `/etc/nginx/conf.d`. It also writes _TLS certificates_ and _keys_ from [TLS Secrets](https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets) referenced in the accepted Gateway resource to the `nginx-secrets` volume at the path `/etc/nginx/secrets`. + - Read: _NGF_ reads the PID file `nginx.pid` from the `nginx-run` volume, located at `/var/run/nginx`. _NGF_ extracts the PID of the nginx process from this file in order to send reload signals to _NGINX master_. 1. (File I/O) _NGF_ writes logs to its _stdout_ and _stderr_, which are collected by the container runtime. -1. (HTTP) _NGF_ fetches the NGINX metrics via the unix:/var/run/nginx/nginx-status.sock UNIX socket and converts it to - _Prometheus_ format used in #2. +1. (HTTP) _NGF_ fetches the NGINX metrics via the unix:/var/run/nginx/nginx-status.sock UNIX socket and converts it to _Prometheus_ format used in #2. 1. (Signal) To reload NGINX, _NGF_ sends the [reload signal](https://nginx.org/en/docs/control.html) to the **NGINX master**. 1. (File I/O) - Write: The _NGINX master_ writes its PID to the `nginx.pid` file stored in the `nginx-run` volume. - - Read: The _NGINX master_ reads _configuration files_ and the _TLS cert and keys_ referenced in the configuration when - it starts or during a reload. These files, certificates, and keys are stored in the `nginx-conf` and `nginx-secrets` - volumes that are mounted to both the `nginx-gateway` and `nginx` containers. -8. (File I/O) + - Read: The _NGINX master_ reads _configuration files_ and the _TLS cert and keys_ referenced in the configuration when it starts or during a reload. These files, certificates, and keys are stored in the `nginx-conf` and `nginx-secrets` volumes that are mounted to both the `nginx-gateway` and `nginx` containers. +1. (File I/O) - Write: The _NGINX master_ writes to the auxiliary Unix sockets folder, which is located in the `/var/lib/nginx` directory. - - Read: The _NGINX master_ reads the `nginx.conf` file from the `/etc/nginx` directory. This [file](https://github.com/nginxinc/nginx-gateway-fabric/blob/main/internal/mode/static/nginx/conf/nginx.conf) contains - the global and http configuration settings for NGINX. In addition, _NGINX master_ - reads the NJS modules referenced in the configuration when it starts or during a reload. NJS modules are stored in - the `/usr/lib/nginx/modules` directory. -9. (File I/O) The _NGINX master_ sends logs to its _stdout_ and _stderr_, which are collected by the container runtime. -10. (File I/O) An _NGINX worker_ writes logs to its _stdout_ and _stderr_, which are collected by the container runtime. -11. (Signal) The _NGINX master_ controls the [lifecycle of _NGINX workers_](https://nginx.org/en/docs/control.html#reconfiguration) it creates workers with the new - configuration and shutdowns workers with the old configuration. -12. (HTTP) To consider a configuration reload a success, _NGF_ ensures that at least one NGINX worker has the new - configuration. To do that, _NGF_ checks a particular endpoint via the unix:/var/run/nginx/nginx-config-version.sock - UNIX socket. -13. (HTTP, HTTPS) A _client_ sends traffic to and receives traffic from any of the _NGINX workers_ on ports 80 and 443. -14. (HTTP, HTTPS) An _NGINX worker_ sends traffic to and receives traffic from the _backends_. + - Read: The _NGINX master_ reads the `nginx.conf` file from the `/etc/nginx` directory. This [file](https://github.com/nginxinc/nginx-gateway-fabric/blob/main/internal/mode/static/nginx/conf/nginx.conf) contains the global and http configuration settings for NGINX. In addition, _NGINX master_ reads the NJS modules referenced in the configuration when it starts or during a reload. NJS modules are stored in the `/usr/lib/nginx/modules` directory. +1. (File I/O) The _NGINX master_ sends logs to its _stdout_ and _stderr_, which are collected by the container runtime. +1. (File I/O) An _NGINX worker_ writes logs to its _stdout_ and _stderr_, which are collected by the container runtime. +1. (Signal) The _NGINX master_ controls the [lifecycle of _NGINX workers_](https://nginx.org/en/docs/control.html#reconfiguration) it creates workers with the new configuration and shutdowns workers with the old configuration. +1. (HTTP) To consider a configuration reload a success, _NGF_ ensures that at least one NGINX worker has the new configuration. To do that, _NGF_ checks a particular endpoint via the unix:/var/run/nginx/nginx-config-version.sock UNIX socket. +1. (HTTP, HTTPS) A _client_ sends traffic to and receives traffic from any of the _NGINX workers_ on ports 80 and 443. +1. (HTTP, HTTPS) An _NGINX worker_ sends traffic to and receives traffic from the _backends_. ## Pod readiness -The `nginx-gateway` container includes a readiness endpoint available via the `/readyz` path. A [readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) periodically checks the endpoint on startup, returning a `200 OK` response when the Pod can accept traffic for the data plane. After the control plane successfully starts, the Pod becomes ready. +The `nginx-gateway` container includes a readiness endpoint available through the path `/readyz`. A [readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) periodically checks the endpoint on startup, returning a `200 OK` response when the pod can accept traffic for the data plane. Once the control plane successfully starts, the pod becomes ready. -If there are relevant Gateway API resources in the cluster, the control plane will generate the first NGINX configuration and successfully reload NGINX before the Pod is considered ready. \ No newline at end of file +If there are relevant Gateway API resources in the cluster, the control plane will generate the first NGINX configuration and successfully reload NGINX before the pod is considered ready. \ No newline at end of file From 236554e5e69bb80446d698cd39403ef89db1eca7 Mon Sep 17 00:00:00 2001 From: Alan Dooley Date: Tue, 21 Nov 2023 16:49:25 +0000 Subject: [PATCH 060/101] Update CLI reference guide to match standard formatting, rephrase text --- site/content/reference/cli-help.md | 58 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/site/content/reference/cli-help.md b/site/content/reference/cli-help.md index 74b5a9d734..7e30a179e4 100644 --- a/site/content/reference/cli-help.md +++ b/site/content/reference/cli-help.md @@ -1,6 +1,6 @@ --- title: "Command-line Reference Guide" -description: "Learn about the commands available in the gateway binary of the nginx-gateway container." +description: "Learn about the commands available for the executable file of the F5 NGINX Gateway Fabric container." weight: 100 toc: true docs: "DOCS-000" @@ -8,46 +8,46 @@ docs: "DOCS-000" ## Static Mode -This command configures NGINX in the scope of a single Gateway resource. +This command configures NGINX for a single NGINX Gateway Fabric resource. -Usage: +_Usage_: -```text +```shell gateway static-mode [flags] ``` -Flags: - -| Name | Type | Description | -|------------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `gateway-ctlr-name` | `string` | The name of the Gateway controller. The controller name must be of the form: `DOMAIN/PATH`. The controller's domain is `gateway.nginx.org`. | -| `gatewayclass` | `string` | The name of the GatewayClass resource. Every NGINX Gateway Fabric must have a unique corresponding GatewayClass resource. | -| `gateway` | `string` | The namespaced name of the Gateway resource to use. Must be of the form: `NAMESPACE/NAME`. If not specified, the control plane will process all Gateways for the configured GatewayClass. However, among them, it will choose the oldest resource by creation timestamp. If the timestamps are equal, it will choose the resource that appears first in alphabetical order by {namespace}/{name}. | -| `config` | `string` | The name of the NginxGateway resource to be used for this controller's dynamic configuration. Lives in the same Namespace as the controller. | -| `service` | `string` | The name of the Service that fronts this NGINX Gateway Fabric Pod. Lives in the same Namespace as the controller. | -| `metrics-disable` | `bool` | Disable exposing metrics in the Prometheus format. (default false) | -| `metrics-listen-port` | `int` | Sets the port where the Prometheus metrics are exposed. Format: `[1024 - 65535]` (default `9113`) | -| `metrics-secure-serving` | `bool` | Configures if the metrics endpoint should be secured using https. Please note that this endpoint will be secured with a self-signed certificate. (default false) | -| `update-gatewayclass-status` | `bool` | Update the status of the GatewayClass resource. (default true) | -| `health-disable` | `bool` | Disable running the health probe server. (default false) | -| `health-port` | `int` | Set the port where the health probe server is exposed. Format: `[1024 - 65535]` (default `8081`) | -| `leader-election-disable` | `bool` | Disable leader election. Leader election is used to avoid multiple replicas of the NGINX Gateway Fabric reporting the status of the Gateway API resources. If disabled, all replicas of NGINX Gateway Fabric will update the statuses of the Gateway API resources. (default false) | -| `leader-election-lock-name` | `string` | The name of the leader election lock. A Lease object with this name will be created in the same Namespace as the controller. (default "nginx-gateway-leader-election-lock") | +### Flags + +{{< bootstrap-table "table table-bordered table-striped table-responsive" >}} +| Name | Type | Description | +|------------------------------|----------|-------------| +| _gateway-ctlr-name_ | _string_ | The name of the Gateway controller. The controller name must be in the form: `DOMAIN/PATH`. The controller's domain is `gateway.nginx.org`. | +| _gatewayclass_ | _string_ | The name of the GatewayClass resource. Every NGINX Gateway Fabric must have a unique corresponding GatewayClass resource. | +| _gateway_ | _string_ | The namespaced name of the Gateway resource to use. Must be of the form: `NAMESPACE/NAME`. If not specified, the control plane will process all Gateways for the configured GatewayClass. Among them, it will choose the oldest resource by creation timestamp. If the timestamps are equal, it will choose the resource that appears first in alphabetical order by {namespace}/{name}. | +| _config_ | _string_ | The name of the NginxGateway resource to be used for this controller's dynamic configuration. Lives in the same namespace as the controller. | +| _service_ | _string_ | The name of the service that fronts this NGINX Gateway Fabric pod. Lives in the same namespace as the controller. | +| _metrics-disable_ | _bool_ | Disable exposing metrics in the Prometheus format (Default: `false`). | +| _metrics-listen-port_ | _int_ | Sets the port where the Prometheus metrics are exposed. An integer between 1024 - 65535 (Default: `9113`) | +| _metrics-secure-serving_ | _bool_ | Configures if the metrics endpoint should be secured using https. Note that this endpoint will be secured with a self-signed certificate (Default `false`). | +| _update-gatewayclass-status_ | _bool_ | Update the status of the GatewayClass resource (Default: `true`). | +| _health-disable_ | _bool_ | Disable running the health probe server (Default: `false`). | +| _health-port_ | _int_ | Set the port where the health probe server is exposed. An integer between 1024 - 65535 (Default: `8081`). | +| _leader-election-disable_ | _bool_ | Disable leader election, which is used to avoid multiple replicas of the NGINX Gateway Fabric reporting the status of the Gateway API resources. If disabled, all replicas of NGINX Gateway Fabric will update the statuses of the Gateway API resources (Default: `false`). | +| _leader-election-lock-name_ | _string_ | The name of the leader election lock. A lease object with this name will be created in the same namespace as the controller (Default: `"nginx-gateway-leader-election-lock"`). | +{{% /bootstrap-table %}} ## Sleep -This command sleeps for specified duration and exits. +This command sleeps for specified duration, then exits. -Usage: +_Usage_: -```text -Usage: +```shell gateway sleep [flags] ``` +{{< bootstrap-table "table table-bordered table-striped table-responsive" >}} | Name | Type | Description | |----------|-----------------|-------------------------------------------------------------------------------------------------------| -| duration | `time.Duration` | Set the duration of sleep. Must be parsable by [`time.ParseDuration`][parseDuration]. (default `30s`) | - - -[parseDuration]:https://pkg.go.dev/time#ParseDuration +| duration | `time.Duration` | Set the duration of sleep. Must be parsable by [`time.ParseDuration`](https://pkg.go.dev/time#ParseDuration). (default `30s`) | +{{% /bootstrap-table %}} \ No newline at end of file From d7c3ddddc784e916e183a907f37811b77fe2078c Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Tue, 21 Nov 2023 17:25:42 +0000 Subject: [PATCH 061/101] fix: update colorbind image , bump theme --- .../routing-traffic-to-your-app.md | 2 +- site/go.mod | 2 +- site/go.sum | 2 ++ site/static/img/route-all-traffic-config.png | Bin 50370 -> 40397 bytes site/static/img/route-all-traffic-flow.png | Bin 58438 -> 47364 bytes 5 files changed, 4 insertions(+), 2 deletions(-) diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index 1a3414db27..94308d3798 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -109,7 +109,7 @@ Once NGINX Gateway Fabric processes the **cafe** gateway and **coffee** HTTPRout The **coffee** service is omitted from the diagram above because the NGINX Gateway Fabric routes directly to the pods that the **coffee** service targets. -{{< note >}}In the diagrams above, all resources that are the responsibility of the cluster operator are shown in green. The orange resources are the responsibility of the application developers. +{{< note >}}In the diagrams above, all resources that are the responsibility of the cluster operator are shown in blue. The orange resources are the responsibility of the application developers. See the [roles and personas](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1) Gateway API document for more information on these roles.{{< /note >}} diff --git a/site/go.mod b/site/go.mod index 93b32af858..36179c78da 100644 --- a/site/go.mod +++ b/site/go.mod @@ -2,4 +2,4 @@ module github.com/nginxinc/nginx-gateway-fabric/site go 1.21 -require github.com/nginxinc/nginx-hugo-theme v0.39.0 // indirect +require github.com/nginxinc/nginx-hugo-theme v0.40.0 // indirect diff --git a/site/go.sum b/site/go.sum index 3aa016b19f..ef95ed80dc 100644 --- a/site/go.sum +++ b/site/go.sum @@ -2,3 +2,5 @@ github.com/nginxinc/nginx-hugo-theme v0.35.0 h1:7XB2GMy6qeJgKEJy9wOS3SYKYpfvLW3/ github.com/nginxinc/nginx-hugo-theme v0.35.0/go.mod h1:DPNgSS5QYxkjH/BfH4uPDiTfODqWJ50NKZdorguom8M= github.com/nginxinc/nginx-hugo-theme v0.39.0 h1:P1hOPpityVUOM5OyIpQZa1UJyuUunGSmz0oZh/GYSJM= github.com/nginxinc/nginx-hugo-theme v0.39.0/go.mod h1:DPNgSS5QYxkjH/BfH4uPDiTfODqWJ50NKZdorguom8M= +github.com/nginxinc/nginx-hugo-theme v0.40.0 h1:YP0I0+bRKcJ5WEb1s/OWcnlcvNvIcKscagJkCzsa+Vs= +github.com/nginxinc/nginx-hugo-theme v0.40.0/go.mod h1:DPNgSS5QYxkjH/BfH4uPDiTfODqWJ50NKZdorguom8M= diff --git a/site/static/img/route-all-traffic-config.png b/site/static/img/route-all-traffic-config.png index 38c4e08a6e161433eb63682531e7896cc4fbc8c3..01ae2210304c71631536c948869431d4673caef2 100644 GIT binary patch literal 40397 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�CA6`O}&U=MF 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 aF;f*bo#cF_?+c%Z^H4+@l_B)x#s2{v$!9wN literal 50370 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 Ii`RYr2iNuJ1poj5 diff --git a/site/static/img/route-all-traffic-flow.png b/site/static/img/route-all-traffic-flow.png index 9fc2089c73adcb4bef46b4c3e7adb50e7d1aa691..04a09c94cc80b73b3180b763d1c112ddd4fecd81 100644 GIT binary patch literal 47364 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

w0sO$9`5zb@#%S_8!0b)m{~xTDm_&@JzqV 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 literal 58438 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

!vh3 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 Date: Wed, 22 Nov 2023 18:05:57 +0000 Subject: [PATCH 062/101] chore: update hugo config --- site/config/_default/config.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/site/config/_default/config.toml b/site/config/_default/config.toml index e269c1d8d4..2e4df7b527 100644 --- a/site/config/_default/config.toml +++ b/site/config/_default/config.toml @@ -1,7 +1,6 @@ title = "NGINX Gateway Fabric" enableGitInfo = false baseURL = "/" -publishDir = "public/nginx-gateway-fabric" staticDir = ["static"] languageCode = "en-us" description = "NGINX Gateway Fabric." From a7802300cca9bf3bb5db6d30a8e9ff3f2eb600d4 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 22 Nov 2023 18:08:18 +0000 Subject: [PATCH 063/101] chore: update hugo config --- site/config/development/config.toml | 2 +- site/config/production/config.toml | 2 +- site/config/staging/config.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/site/config/development/config.toml b/site/config/development/config.toml index 6d96e11dfa..7fe728696b 100644 --- a/site/config/development/config.toml +++ b/site/config/development/config.toml @@ -1,4 +1,4 @@ -baseURL = "https://docs-dev.nginx.com/nginx-gateway-fabric" +baseURL = "/" title = "DEV -- NGINX Gateway Fabric" publishDir = "public/nginx-gateway-fabric" canonifyURLs = false diff --git a/site/config/production/config.toml b/site/config/production/config.toml index 0e21ba5f21..3a60a7bb6d 100644 --- a/site/config/production/config.toml +++ b/site/config/production/config.toml @@ -1,4 +1,4 @@ -baseURL = "https://docs.nginx.com/nginx-gateway-fabric" +baseURL = "/" title = "NGINX Gateway Fabric" publishDir = "public/nginx-gateway-fabric" canonifyURLs = false diff --git a/site/config/staging/config.toml b/site/config/staging/config.toml index 48897dd914..5295674548 100644 --- a/site/config/staging/config.toml +++ b/site/config/staging/config.toml @@ -1,4 +1,4 @@ -baseURL = "https://docs-staging.nginx.com/nginx-gateway-fabric" +baseURL = "/" title = "STAGING -- NGINX Gateway Fabric" publishDir = "public/nginx-gateway-fabric" canonifyURLs = false \ No newline at end of file From 2f88c2d572a03a469f7276e22c215ea9150eba4d Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 22 Nov 2023 18:10:28 +0000 Subject: [PATCH 064/101] chore: update hugo config files --- site/config/development/config.toml | 3 +-- site/config/production/config.toml | 3 +-- site/config/staging/config.toml | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/site/config/development/config.toml b/site/config/development/config.toml index 7fe728696b..937119dffb 100644 --- a/site/config/development/config.toml +++ b/site/config/development/config.toml @@ -1,4 +1,3 @@ -baseURL = "/" +baseURL = "https://docs-dev.nginx.com/nginx-gateway-fabric" title = "DEV -- NGINX Gateway Fabric" -publishDir = "public/nginx-gateway-fabric" canonifyURLs = false diff --git a/site/config/production/config.toml b/site/config/production/config.toml index 3a60a7bb6d..22263ca072 100644 --- a/site/config/production/config.toml +++ b/site/config/production/config.toml @@ -1,4 +1,3 @@ -baseURL = "/" +baseURL = "https://docs.nginx.com/nginx-gateway-fabric" title = "NGINX Gateway Fabric" -publishDir = "public/nginx-gateway-fabric" canonifyURLs = false diff --git a/site/config/staging/config.toml b/site/config/staging/config.toml index 5295674548..b9e5085762 100644 --- a/site/config/staging/config.toml +++ b/site/config/staging/config.toml @@ -1,4 +1,3 @@ -baseURL = "/" +baseURL = "https://docs-staging.nginx.com/nginx-gateway-fabric" title = "STAGING -- NGINX Gateway Fabric" -publishDir = "public/nginx-gateway-fabric" canonifyURLs = false \ No newline at end of file From 4d788500a4a6cf5472ed262a1b6f09458219400d Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 22 Nov 2023 18:19:37 +0000 Subject: [PATCH 065/101] feat: update makefile --- site/Makefile | 54 +++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/site/Makefile b/site/Makefile index e664ca3d14..4cb8916575 100644 --- a/site/Makefile +++ b/site/Makefile @@ -1,18 +1,18 @@ HUGO?=hugo # the officially recommended unofficial docker image -HUGO_IMG?=klakegg/hugo:0.115.3 -# NGINX Hugo theme module location -THEME_MODULE=github.com/nginxinc/nginx-hugo-theme -## Pulls the current theme version from the Netlify settings -THEME_VERSION=$(NGINX_THEME_VERSION) +HUGO_IMG?=hugomods/hugo:0.115.3 +THEME_MODULE = github.com/nginxinc/nginx-hugo-theme +## Pulls the current theme version from the Netlify settings +THEME_VERSION = $(NGINX_THEME_VERSION) +NETLIFY_DEPLOY_URL = ${DEPLOY_PRIME_URL} # if there's no local hugo, fallback to docker ifeq (, $(shell ${HUGO} version 2> /dev/null)) ifeq (, $(shell docker version 2> /dev/null)) $(error Docker and Hugo are not installed. Hugo (<0.91) or Docker are required to build the local preview.) else - HUGO=docker run --rm -it -v ${CURDIR}:/src -p 1313:1313 ${HUGO_IMG} + HUGO=docker run --rm -it -v ${CURDIR}:/src -p 1313:1313 ${HUGO_IMG} hugo --bind 0.0.0.0 -p 1313 endif endif @@ -24,11 +24,11 @@ ifeq (, $(shell ${MARKDOWNLINT} version 2> /dev/null)) ifeq (, $(shell docker version 2> /dev/null)) ifneq (, $(shell $(NETLIFY) "true")) $(error Docker and markdownlint are not installed. markdownlint or Docker are required to lint.) +endif else MARKDOWNLINT=docker run --rm -i -v ${CURDIR}:/src --workdir /src ${MARKDOWNLINT_IMG} endif endif -endif MARKDOWNLINKCHECK?=markdown-link-check MARKDOWNLINKCHECK_IMG?=ghcr.io/tcort/markdown-link-check:stable @@ -37,56 +37,60 @@ ifeq (, $(shell ${MARKDOWNLINKCHECK} --version 2> /dev/null)) ifeq (, $(shell docker version 2> /dev/null)) ifneq (, $(shell $(NETLIFY) "true")) $(error Docker and markdown-link-check are not installed. markdown-link-check or Docker are required to check links.) -else - MARKDOWNLINKCHECK=docker run --rm -it -v ${CURDIR}:/hugo --workdir /hugo ${MARKDOWNLINKCHECK_IMG} endif +else + MARKDOWNLINKCHECK=docker run --rm -it -v ${CURDIR}:/site --workdir /site ${MARKDOWNLINKCHECK_IMG} endif endif -.PHONY: docs clean hugo-mod docs-local docs-drafts netlify lint-grammar lint-markdown link-check all all-staging all-dev +.PHONY: all all-staging all-dev all-local clean hugo-mod build-production build-staging build-dev docs-drafts docs deploy-preview -## For use in Netlify CI only all: hugo-mod build-production all-staging: hugo-mod build-staging all-dev: hugo-mod build-dev -## end for use in Netlify CI -docs: - ${HUGO} server --disableFastRender +all-local: clean hugo-mod build-production +# Removes the public directory generated by the `hugo` command clean: - rm -rf ./public + if [[ -d ${PWD}/public ]] ; then rm -rf ${PWD}/public && echo "Removed public directory" ; else echo "Did not find a public directory to remove" ; fi -docs-local: clean - ${HUGO} docs-drafts: ${HUGO} server -D --disableFastRender -lint-grammar: - docker run --rm -it -v ${CURDIR}/content:/root/content --entrypoint "vale" --workdir /root/content artifactory.f5net.com/cylon-indigo-docker-dev/indigo/tools/docs/vale-lint:0.7.0 ./ +docs-local: clean + ${HUGO} + +docs: + ${HUGO} server --disableFastRender lint-markdown: - ${MARKDOWNLINT} -c mdlint_conf.json -- content + ${MARKDOWNLINT} -c .markdownlint.yaml -- content link-check: ${MARKDOWNLINKCHECK} $(shell find content -name '*.md') + ## commands for use in Netlify CI hugo-mod: hugo mod get $(THEME_MODULE)@v$(THEME_VERSION) -build-production: hugo-mod +build-production: hugo --gc -e production - -build-staging: hugo-mod + cp _redirects public/_redirects + +build-staging: hugo --gc -e staging + cp _redirects_staging public/_redirects -build-dev: hugo-mod +build-dev: hugo --gc -e development + cp _redirects_dev public/_redirects deploy-preview: hugo-mod - hugo --gc -d public/nginx-gateway-fabric/ -b ${NETLIFY_DEPLOY_URL}/nginx-gateway-fabric/ \ No newline at end of file + hugo --gc -b ${NETLIFY_DEPLOY_URL} + cp _redirects_dev public/_redirects From 3619273af2ede2e3a37c11a264f24dd30dcaab91 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 22 Nov 2023 18:21:24 +0000 Subject: [PATCH 066/101] chore: update netlify.toml --- site/netlify.toml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/site/netlify.toml b/site/netlify.toml index 98444017e8..da8c5bc369 100644 --- a/site/netlify.toml +++ b/site/netlify.toml @@ -20,15 +20,4 @@ [[headers]] for = "/*" [headers.values] - Access-Control-Allow-Origin = "https://docs.nginx.com" - -[[redirects]] - from = "/" - to = "/nginx-gateway-fabric/" - status = 301 - force = true - -[[redirects]] - from = "/nginx-gateway-fabric/*" - to = "/nginx-gateway-fabric/404.html" - status = 404 \ No newline at end of file + Access-Control-Allow-Origin = "https://docs.nginx.com" \ No newline at end of file From e726c253068f85fbb8e562555de707e10780e847 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 22 Nov 2023 18:22:55 +0000 Subject: [PATCH 067/101] fix: update makefile --- site/Makefile | 4 ---- site/netlify.toml | 13 ++++++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/site/Makefile b/site/Makefile index 4cb8916575..3b93debabd 100644 --- a/site/Makefile +++ b/site/Makefile @@ -81,16 +81,12 @@ hugo-mod: build-production: hugo --gc -e production - cp _redirects public/_redirects build-staging: hugo --gc -e staging - cp _redirects_staging public/_redirects build-dev: hugo --gc -e development - cp _redirects_dev public/_redirects deploy-preview: hugo-mod hugo --gc -b ${NETLIFY_DEPLOY_URL} - cp _redirects_dev public/_redirects diff --git a/site/netlify.toml b/site/netlify.toml index da8c5bc369..98444017e8 100644 --- a/site/netlify.toml +++ b/site/netlify.toml @@ -20,4 +20,15 @@ [[headers]] for = "/*" [headers.values] - Access-Control-Allow-Origin = "https://docs.nginx.com" \ No newline at end of file + Access-Control-Allow-Origin = "https://docs.nginx.com" + +[[redirects]] + from = "/" + to = "/nginx-gateway-fabric/" + status = 301 + force = true + +[[redirects]] + from = "/nginx-gateway-fabric/*" + to = "/nginx-gateway-fabric/404.html" + status = 404 \ No newline at end of file From f2369d1ed69171c7ed8ebf7968c8614e2a3502a8 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 22 Nov 2023 18:31:56 +0000 Subject: [PATCH 068/101] fix: update hugo config --- site/config/_default/config.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/site/config/_default/config.toml b/site/config/_default/config.toml index 2e4df7b527..e269c1d8d4 100644 --- a/site/config/_default/config.toml +++ b/site/config/_default/config.toml @@ -1,6 +1,7 @@ title = "NGINX Gateway Fabric" enableGitInfo = false baseURL = "/" +publishDir = "public/nginx-gateway-fabric" staticDir = ["static"] languageCode = "en-us" description = "NGINX Gateway Fabric." From 22e0869465a23aa562f1af34bab6cb82770266d7 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 22 Nov 2023 18:35:37 +0000 Subject: [PATCH 069/101] fix: update config file --- site/config/production/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/config/production/config.toml b/site/config/production/config.toml index 22263ca072..f818400d21 100644 --- a/site/config/production/config.toml +++ b/site/config/production/config.toml @@ -1,3 +1,3 @@ -baseURL = "https://docs.nginx.com/nginx-gateway-fabric" +baseURL = "/nginx-gateway-fabric" title = "NGINX Gateway Fabric" canonifyURLs = false From cbfb753ba4541d5cd5413695a1422d8919911aac Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 22 Nov 2023 18:41:16 +0000 Subject: [PATCH 070/101] fix: fix image test --- site/content/how-to/traffic-management/advanced-routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/advanced-routing.md b/site/content/how-to/traffic-management/advanced-routing.md index 57b95cc6a9..66d55dde26 100644 --- a/site/content/how-to/traffic-management/advanced-routing.md +++ b/site/content/how-to/traffic-management/advanced-routing.md @@ -10,7 +10,7 @@ In this guide we will configure advanced routing rules for multiple applications The following image shows the traffic flow that we will be creating with these rules. -{{Traffic Flow Diagram}} +{{Traffic Flow Diagram}} The goal is to create a set of rules that will result in client requests being sent to specific backends based on the request attributes. In this diagram, we have two versions of the `coffee` service. Traffic for v1 needs to be directed to the old application, while traffic for v2 needs to be directed towards the new application. We also have two `tea` services, one that handles GET operations and one that handles POST operations. Both the `tea` and `coffee` applications share the same Gateway. From d836d3acfef0b92eee38cd91d6485b73a8ad88ef Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 22 Nov 2023 18:43:38 +0000 Subject: [PATCH 071/101] fix: fix images --- .../how-to/traffic-management/integrating-cert-manager.md | 2 +- .../traffic-management/routing-traffic-to-your-app.md | 6 +++--- site/content/overview/gateway-architecture.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/content/how-to/traffic-management/integrating-cert-manager.md b/site/content/how-to/traffic-management/integrating-cert-manager.md index 597d93fe11..dfbc9f4839 100644 --- a/site/content/how-to/traffic-management/integrating-cert-manager.md +++ b/site/content/how-to/traffic-management/integrating-cert-manager.md @@ -22,7 +22,7 @@ Follow the steps in this guide to: - A DNS-resolvable domain name is required. It must resolve to the public endpoint of the NGINX Gateway Fabric deployment, and this public endpoint must be an external IP address or alias accessible over the internet. The process here will depend on your DNS provider. This DNS name will need to be resolvable from the Let’s Encrypt servers, which may require that you wait for the record to propagate before it will work. ## Overview -{{cert-manager ACME challenge and certificate management with Gateway API}} +{{cert-manager ACME challenge and certificate management with Gateway API}} The diagram above shows a simplified representation of the cert-manager ACME challenge and certificate issuance process using Gateway API. Please note that not all of the kubernetes objects created in this process are represented in this diagram. diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index 94308d3798..0d830fbef0 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -26,7 +26,7 @@ You can route traffic to your Kubernetes applications using the Gateway API and The application we are going to use in this guide is a simple **coffee** application comprised of one service and two pods: -{{coffee app}} +{{coffee app}} Using this architecture, the **coffee** application is not accessible outside the cluster. We want to expose this application on the hostname "cafe.example.com" so that clients outside the cluster can access it. @@ -97,7 +97,7 @@ service/coffee ClusterIP 198.51.100.1 80/TCP 77s To route traffic to the **coffee** application, we will create a gateway and HTTPRoute. The following diagram shows the configuration we are creating in the next step: -{{Configuration}} +{{Configuration}} We need a gateway to create an entry point for HTTP traffic coming into the cluster. The **cafe** gateway we are going to create will open an entry point to the cluster on port 80 for HTTP traffic. @@ -105,7 +105,7 @@ To route HTTP traffic from the gateway to the **coffee** service, we need to cre Once NGINX Gateway Fabric processes the **cafe** gateway and **coffee** HTTPRoute, it will configure its data plane (NGINX) to route all HTTP requests sent to "cafe.example.com" to the pods that the **coffee** service targets: -{{Traffic Flow}} +{{Traffic Flow}} The **coffee** service is omitted from the diagram above because the NGINX Gateway Fabric routes directly to the pods that the **coffee** service targets. diff --git a/site/content/overview/gateway-architecture.md b/site/content/overview/gateway-architecture.md index 6e4aea5d35..4252fd56b3 100644 --- a/site/content/overview/gateway-architecture.md +++ b/site/content/overview/gateway-architecture.md @@ -23,7 +23,7 @@ NGINX Gateway Fabric is a Kubernetes cluster component that uses Gateway API Res This figure depicts an example of NGINX Gateway Fabric exposing two web applications within a Kubernetes cluster to clients on the internet: -{{}} +{{}} {{< note >}} The figure does not show many of the necessary Kubernetes resources the Cluster Operators and Application Developers need to create, like deployment and services. {{< /note >}} @@ -62,7 +62,7 @@ This is possible because the two containers [share a process namespace](https:// The following diagram represents the connections, relationships and interactions between process with the `nginx` and `nginx-gateway` containers, as well as external processes/entities. -{{}} +{{}} The following list describes the connections, preceeded by their types in parentheses. For brevity, the suffix "process" has been omitted from the process descriptions. From 9b186b78d55340e7cfda9077cdfbe5fc6b99ee33 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 23 Nov 2023 14:55:41 +0000 Subject: [PATCH 072/101] feat: rewrites --- .../how-to/monitoring/troubleshooting.md | 5 +- .../expose-nginx-gateway-fabric.md | 50 ++++++------------- 2 files changed, 17 insertions(+), 38 deletions(-) diff --git a/site/content/how-to/monitoring/troubleshooting.md b/site/content/how-to/monitoring/troubleshooting.md index 4b79ed622c..49cd2350cb 100644 --- a/site/content/how-to/monitoring/troubleshooting.md +++ b/site/content/how-to/monitoring/troubleshooting.md @@ -8,8 +8,5 @@ docs: "DOCS-000" ## failed to reload NGINX: failed to send the HUP signal to NGINX main: operation not permitted -Depending on your environment's configuration, the control plane may not have the proper permissions to reload -NGINX. If NGINX configuration is not applied and you see the above error in the `nginx-gateway` logs, you will need -to set `allowPrivilegeEscalation` to `true`. If using Helm, you can set the -`nginxGateway.securityContext.allowPrivilegeEscalation` value. +Depending on your environment's configuration, the control plane may not have the proper permissions to reload NGINX. If NGINX configuration is not applied and you see the above error in the `nginx-gateway` logs, you will need to set `allowPrivilegeEscalation` to `true`. If using Helm, you can set the `nginxGateway.securityContext.allowPrivilegeEscalation` value. If using the manifests directly, you can update this field under the `nginx-gateway` container's `securityContext`. diff --git a/site/content/installation/expose-nginx-gateway-fabric.md b/site/content/installation/expose-nginx-gateway-fabric.md index eaa67321bc..bb9d8ec060 100644 --- a/site/content/installation/expose-nginx-gateway-fabric.md +++ b/site/content/installation/expose-nginx-gateway-fabric.md @@ -1,46 +1,24 @@ --- title: "Expose NGINX Gateway Fabric" -date: 2023-11-16T16:37:42-08:00 -# Change draft status to false to publish doc. -draft: false -# Description -# Add a short description (150 chars) for the doc. Include keywords for SEO. -# The description text appears in search results and at the top of the doc. description: "" -# Assign weights in increments of 100 weight: 300 toc: true -tags: [ "docs" ] -# Create a new entry in the Jira DOCS Catalog and add the ticket ID (DOCS-) below docs: "DOCS-000" -# Taxonomies -# These are pre-populated with all available terms for your convenience. -# Remove all terms that do not apply. -categories: ["installation", "platform management", "load balancing", "api management", "service mesh", "security", "analytics"] -doctypes: ["task"] -journeys: ["researching", "getting started", "using", "renewing", "self service"] -personas: ["devops", "netops", "secops", "support"] -versions: [] -authors: [] - --- -### Expose NGINX Gateway Fabric - +## Overview Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. -{{}} -The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any Gateway [Listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports. -{{}} +{{}}The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any gateway [listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports.{{}} -NGINX Gateway Fabric uses this service to update the **Addresses** field in the **Gateway Status** resource. A **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the Pod IP address is used. +NGINX Gateway Fabric uses the created service to update the **Addresses** field in the **Gateway Status** resource. The **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the pod IP address is used. This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). -#### Create a NodePort service +## Create a NodePort service -To create a **NodePort** service: +To create a **NodePort** service run the following command: ```shell kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/nodeport.yaml @@ -48,37 +26,41 @@ kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric A **NodePort** service allocates a port on every cluster node. Access NGINX Gateway Fabric using any node's IP address and the allocated port. -#### Create a LoadBalancer Service +## Create a LoadBalancer Service To create a **LoadBalancer** service, use the appropriate manifest for your cloud provider: -- For GCP (Google Cloud Platform) or Azure: +### GCP (Google Cloud Platform) and Azure + +1. Run the following command: ```shell kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer.yaml ``` - Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the following command: +2. Lookup the public IP of the load balancer, which is reported in the `EXTERNAL-IP` column in the output of the following command: ```shell kubectl get svc nginx-gateway -n nginx-gateway ``` - Use the public IP of the load balancer to access NGINX Gateway Fabric. +3. Use the public IP of the load balancer to access NGINX Gateway Fabric. + +### AWS (Amazon Web Services): -- For AWS (Amazon Web Services): +1. Run the following command: ```shell kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.0.0/deploy/manifests/service/loadbalancer-aws-nlb.yaml ``` - In AWS, the NLB (Network Load Balancer) DNS (directory name system) name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: +2. In AWS, the NLB (Network Load Balancer) DNS (directory name system) name will be reported by Kubernetes instead of a public IP in the `EXTERNAL-IP` column. To get the DNS name, run: ```shell kubectl get svc nginx-gateway -n nginx-gateway ``` - Generally, use the NLB DNS name, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: +3. We recommend using the NLB DNS whenever possible, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: ```shell nslookup From 9077dd588ea609b95f6eea834185884c61fac251 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Thu, 23 Nov 2023 15:06:54 +0000 Subject: [PATCH 073/101] chore: rewrite updates --- site/content/installation/expose-nginx-gateway-fabric.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/site/content/installation/expose-nginx-gateway-fabric.md b/site/content/installation/expose-nginx-gateway-fabric.md index bb9d8ec060..5696162865 100644 --- a/site/content/installation/expose-nginx-gateway-fabric.md +++ b/site/content/installation/expose-nginx-gateway-fabric.md @@ -6,6 +6,8 @@ toc: true docs: "DOCS-000" --- +{{}} + ## Overview Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. @@ -59,9 +61,9 @@ To create a **LoadBalancer** service, use the appropriate manifest for your clou ```shell kubectl get svc nginx-gateway -n nginx-gateway ``` - -3. We recommend using the NLB DNS whenever possible, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: + {{< note >}} We recommend using the NLB DNS whenever possible, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: ```shell nslookup ``` + {{< /note >}} From 7ea40c025cb3340194f06f9136411be5bdac29e6 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Mon, 27 Nov 2023 18:36:39 +0000 Subject: [PATCH 074/101] fix: update expose doc wording --- site/content/installation/expose-nginx-gateway-fabric.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/installation/expose-nginx-gateway-fabric.md b/site/content/installation/expose-nginx-gateway-fabric.md index 5696162865..b96a3c020d 100644 --- a/site/content/installation/expose-nginx-gateway-fabric.md +++ b/site/content/installation/expose-nginx-gateway-fabric.md @@ -14,7 +14,7 @@ Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or {{}}The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any gateway [listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports.{{}} -NGINX Gateway Fabric uses the created service to update the **Addresses** field in the **Gateway Status** resource. The **LoadBalancer** service sets this field to the IP address and/or hostname. Without a service, the pod IP address is used. +NGINX Gateway Fabric uses the created service to update the **Addresses** field in the **Gateway Status** resource. Using a **LoadBalancer** service sets this field to the IP address and/or hostname of that Service. Without a service, the Pod IP address is used. This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). From 11f009185a05e16181e2297404dfa268d1691283 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Tue, 28 Nov 2023 14:31:03 +0000 Subject: [PATCH 075/101] fix: rewrite docs, fix order --- site/content/how-to/_index.md | 2 +- .../how-to/monitoring/troubleshooting.md | 19 +++++++++++++++---- .../installation/building-the-images.md | 7 +++++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/site/content/how-to/_index.md b/site/content/how-to/_index.md index 67c28e98c4..f02304bb4e 100644 --- a/site/content/how-to/_index.md +++ b/site/content/how-to/_index.md @@ -1,7 +1,7 @@ --- title: "How-To Guides" description: -weight: 200 +weight: 300 linkTitle: "Guides" menu: docs: diff --git a/site/content/how-to/monitoring/troubleshooting.md b/site/content/how-to/monitoring/troubleshooting.md index 49cd2350cb..c27c9e23ea 100644 --- a/site/content/how-to/monitoring/troubleshooting.md +++ b/site/content/how-to/monitoring/troubleshooting.md @@ -1,12 +1,23 @@ --- title: "Troubleshooting" -description: "Learn how to troubleshoot common NGINX Gateway Fabric issues." + weight: 200 toc: true docs: "DOCS-000" --- -## failed to reload NGINX: failed to send the HUP signal to NGINX main: operation not permitted +{{< custom-styles >}} + +This topic describes possible issues users might encounter when using Instance Manager. When possible, suggested workarounds are provided. + +## NGINX fails to reload + +#### Description + +Depending on your environment's configuration, the control plane may not have the proper permissions to reload NGINX. The NGINX configuration will not be applied and you will see the following error in the _nginx-gateway_ logs: `failed to reload NGINX: failed to send the HUP signal to NGINX main: operation not permitted` + +#### Resolution +To resolve this issue you will need to set `allowPrivilegeEscalation` to `true`. -Depending on your environment's configuration, the control plane may not have the proper permissions to reload NGINX. If NGINX configuration is not applied and you see the above error in the `nginx-gateway` logs, you will need to set `allowPrivilegeEscalation` to `true`. If using Helm, you can set the `nginxGateway.securityContext.allowPrivilegeEscalation` value. -If using the manifests directly, you can update this field under the `nginx-gateway` container's `securityContext`. +- If using Helm, you can set the `nginxGateway.securityContext.allowPrivilegeEscalation` value. +- If using the manifests directly, you can update this field under the `nginx-gateway` container's `securityContext`. diff --git a/site/content/installation/building-the-images.md b/site/content/installation/building-the-images.md index ae9c0d2d5d..ba73cf275b 100644 --- a/site/content/installation/building-the-images.md +++ b/site/content/installation/building-the-images.md @@ -1,12 +1,15 @@ --- title: "Building NGINX Gateway Fabric and NGINX Images" -description: "Learn how to build the NGINX Gateway Fabric and NGINX images." weight: 300 toc: true docs: "DOCS-000" --- + +{{}} -# Building the Images +## Overview + +While most users will install NGINX Gateway Fabric [with Helm]({{< relref "/installation/installing-ngf/helm.md" >}}) or [Kubernetes manifests]({{< relref "/installation/installing-ngf/manifests.md" >}}), manually building the images can be helpful for testing and development purposes. Follow the steps in this document to build the NGINX Gateway Fabric and NGINX images. ## Prerequisites From 56fa7ea6c7383332149dc44ab07be11e897bfbf9 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Tue, 28 Nov 2023 14:37:17 +0000 Subject: [PATCH 076/101] fix: update building the images --- site/content/installation/building-the-images.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/installation/building-the-images.md b/site/content/installation/building-the-images.md index ba73cf275b..96ca927e39 100644 --- a/site/content/installation/building-the-images.md +++ b/site/content/installation/building-the-images.md @@ -9,7 +9,7 @@ docs: "DOCS-000" ## Overview -While most users will install NGINX Gateway Fabric [with Helm]({{< relref "/installation/installing-ngf/helm.md" >}}) or [Kubernetes manifests]({{< relref "/installation/installing-ngf/manifests.md" >}}), manually building the images can be helpful for testing and development purposes. Follow the steps in this document to build the NGINX Gateway Fabric and NGINX images. +While most users will install NGINX Gateway Fabric [with Helm]({{< relref "/installation/installing-ngf/helm.md" >}}) or [Kubernetes manifests]({{< relref "/installation/installing-ngf/manifests.md" >}}), manually building the [NGINX Gateway Fabric and NGINX images]({{< relref "/overview/gateway-architecture.md#the-nginx-gateway-fabric-pod" >}}) can be helpful for testing and development purposes. Follow the steps in this document to build the NGINX Gateway Fabric and NGINX images. ## Prerequisites From f82a54390fa1b9b3e1ea21c763e009c6a3e55315 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Tue, 28 Nov 2023 16:45:29 +0000 Subject: [PATCH 077/101] fix: update new doc changes --- .../routing-traffic-to-your-app.md | 12 +++---- .../helm/uninstall-gateway-api-resources.md | 9 +++++- .../expose-nginx-gateway-fabric.md | 2 +- .../installation/installing-ngf/helm.md | 4 +-- .../installation/installing-ngf/manifests.md | 32 +++++++++++++++---- 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index 0d830fbef0..983dfd7a81 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -30,7 +30,7 @@ The application we are going to use in this guide is a simple **coffee** applica Using this architecture, the **coffee** application is not accessible outside the cluster. We want to expose this application on the hostname "cafe.example.com" so that clients outside the cluster can access it. -Install NGINX Gateway Fabric and create two Gateway API resources: a [gateway](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway) and an [HTTPRoute](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute). +Install NGINX Gateway Fabric and create two Gateway API resources: a [gateway](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.Gateway) and an [HTTPRoute](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.HTTPRoute). Using these resources we will configure a simple routing rule to match all HTTP traffic with the hostname "cafe.example.com" and route it to the **coffee** service. @@ -134,10 +134,10 @@ EOF This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a GatewayClass with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). -We specify a [listener](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on the gateway to open an entry point on the cluster. In this case, since the coffee application accepts HTTP requests, we create an HTTP listener, named **http**, that listens on port 80. +We specify a [listener](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.Listener) on the gateway to open an entry point on the cluster. In this case, since the coffee application accepts HTTP requests, we create an HTTP listener, named **http**, that listens on port 80. By default, gateways only allow routes (such as HTTPRoutes) to attach if they are in the same namespace as the gateway. If you want to change this behavior, you can set -the [**allowedRoutes**](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.AllowedRoutes) field. +the [**allowedRoutes**](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.AllowedRoutes) field. Next you will create the HTTPRoute by copying and pasting the following into your terminal: @@ -163,11 +163,11 @@ spec: EOF ``` -To attach the **coffee** HTTPRoute to the **cafe** gateway, we specify the gateway name in the [**parentRefs**](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.CommonRouteSpec) field. The attachment will succeed if the hostnames and protocol in the HTTPRoute are allowed by at least one of the gateway's listeners. +To attach the **coffee** HTTPRoute to the **cafe** gateway, we specify the gateway name in the [**parentRefs**](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.CommonRouteSpec) field. The attachment will succeed if the hostnames and protocol in the HTTPRoute are allowed by at least one of the gateway's listeners. -The [**hostnames**](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteSpec) field allows you to list the hostnames that the HTTPRoute matches. In this case, incoming requests handled by the **http** listener with the HTTP host header "cafe.example.com" will match this HTTPRoute and will be routed according to the rules in the spec. +The [**hostnames**](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.HTTPRouteSpec) field allows you to list the hostnames that the HTTPRoute matches. In this case, incoming requests handled by the **http** listener with the HTTP host header "cafe.example.com" will match this HTTPRoute and will be routed according to the rules in the spec. -The [**rules**](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteRule) field defines routing rules for the HTTPRoute. A rule is selected if the request satisfies one of the rule's **matches**. To forward traffic for all paths to the coffee service we specify a match with the PathPrefix "/" and target the coffee service using the **backendRef** field. +The [**rules**](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule) field defines routing rules for the HTTPRoute. A rule is selected if the request satisfies one of the rule's **matches**. To forward traffic for all paths to the coffee service we specify a match with the PathPrefix "/" and target the coffee service using the **backendRef** field. ## Test the configuration diff --git a/site/content/includes/installation/helm/uninstall-gateway-api-resources.md b/site/content/includes/installation/helm/uninstall-gateway-api-resources.md index 25cf75752d..07a2e46407 100644 --- a/site/content/includes/installation/helm/uninstall-gateway-api-resources.md +++ b/site/content/includes/installation/helm/uninstall-gateway-api-resources.md @@ -7,5 +7,12 @@ To uninstall the Gateway API resources, including the CRDs and the validating we {{}}This will remove all corresponding custom resources in your entire cluster, across all namespaces. Double-check to make sure you don't have any custom resources you need to keep, and confirm that there are no other Gateway API implementations active in your cluster.{{}} ```shell - kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml ``` + + +If you are running on Kubernetes 1.23 or 1.24, you also need to delete the validating webhook. To do so, run: + + ```shell + kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/webhook-install.yaml + ``` \ No newline at end of file diff --git a/site/content/installation/expose-nginx-gateway-fabric.md b/site/content/installation/expose-nginx-gateway-fabric.md index b96a3c020d..4d2201e471 100644 --- a/site/content/installation/expose-nginx-gateway-fabric.md +++ b/site/content/installation/expose-nginx-gateway-fabric.md @@ -12,7 +12,7 @@ docs: "DOCS-000" Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or a **LoadBalancer** service in the same namespace as the controller. The service name is specified in the `--service` argument of the controller. -{{}}The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any gateway [listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports.{{}} +{{}}The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any gateway [listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports.{{}} NGINX Gateway Fabric uses the created service to update the **Addresses** field in the **Gateway Status** resource. Using a **LoadBalancer** service sets this field to the IP address and/or hostname of that Service. Without a service, the Pod IP address is used. diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index c035b6ab23..ed24625046 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -51,11 +51,11 @@ To upgrade NGINX Gateway Fabric and get the latest features and improvements, ta To upgrade your Gateway API resources, take the following steps: - Verify the Gateway API resources are compatible with your NGINX Gateway Fabric version. Refer to the [Technical Specifications]({{< relref "reference/technical-specifications.md" >}}) for details. -- Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) for any important upgrade-specific information. +- Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.0.0) for any important upgrade-specific information. - To upgrade the Gateway API resources, run: ```shell - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml ``` ### Upgrade NGINX Gateway Fabric CRDs diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index e02c853470..361af237b4 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -22,10 +22,17 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. {{}}By default, NGINX Gateway Fabric is installed in the **nginx-gateway** namespace. You can deploy in another namespace by modifying the manifest files.{{}} 1. **Install the Gateway API resources:** - - Start by installing the Gateway API resources, including the CRDs and the validating webhook: - ```shell - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml - ``` + - Install the Gateway API CRDs from [the Gateway API repo](https://github.com/kubernetes-sigs/gateway-api): + + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml + ``` + - If you are running on Kubernetes 1.23 or 1.24, you also need to install the validating webhook. To do so, run: + + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/webhook-install.yaml + ``` +{{< important >}}The validating webhook is not needed if you are running Kubernetes 1.25+. Validation is done using CEL on the CRDs. See the [resource validation doc]({{< relref "/overview/resource-validation.md" >}}) for more information. 2. **Deploy the NGINX Gateway Fabric CRDs:** - Next, deploy the NGINX Gateway Fabric CRDs: @@ -60,13 +67,26 @@ To upgrade NGINX Gateway Fabric and get the latest features and improvements, ta 1. **Upgrade Gateway API resources:** - Verify that your NGINX Gateway Fabric version is compatible with the Gateway API resources. Refer to the [Technical Specifications]({{< relref "reference/technical-specifications.md" >}}) for details. - - Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.8.1) for any important upgrade-specific information. + - Review the [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.0.0) for any important upgrade-specific information. - To upgrade the Gateway API resources, run: ```shell - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.1/standard-install.yaml + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml ``` + - If you are running on Kubernetes 1.23 or 1.24, you also need to update the validating webhook: + + ```shell + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/webhook-install.yaml + ``` + + - If you are running on Kubernetes 1.25 or newer and have the validating webhook installed, you should remove the + webhook: + + ```shell + kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/webhook-install.yaml + ``` + 2. **Upgrade NGINX Gateway Fabric CRDs:** - To upgrade the Custom Resource Definitions (CRDs), run: From a6f2ad4e425e67d32bad54f5cbf0a01db8a04eb8 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Tue, 28 Nov 2023 17:09:26 +0000 Subject: [PATCH 078/101] fix: update README --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cf92fdfc55..d6fb91525c 100644 --- a/README.md +++ b/README.md @@ -10,19 +10,21 @@ and `UDPRoute` -- to configure an HTTP or TCP/UDP load balancer, reverse-proxy, on Kubernetes. NGINX Gateway Fabric supports a subset of the Gateway API. For a list of supported Gateway API resources and features, see -the [Gateway API Compatibility](docs/gateway-api-compatibility.md) doc. +the [Gateway API Compatibility](https://docs.nginx.com/nginx-gateway-fabric/gateway-api-compatibility.md) doc. -Learn about our [design principles](/docs/developer/design-principles.md) and [architecture](/docs/architecture.md). +Learn about our [design principles](/docs/developer/design-principles.md) and [architecture](https://docs.nginx.com/nginx-gateway-fabric/overview/gateway-architecture.md). ## Getting Started -1. [Quick Start on a kind cluster](docs/running-on-kind.md). -2. [Install](docs/installation.md) NGINX Gateway Fabric. -3. [Build](docs/building-the-images.md) an NGINX Gateway Fabric container image from source or use a pre-built image +1. [Quick Start on a kind cluster](https://docs.nginx.com/nginx-gateway-fabric/installation/running-on-kind.md). +2. [Install](https://docs.nginx.com/nginx-gateway-fabric/installation/) NGINX Gateway Fabric. +3. [Build](https://docs.nginx.com/nginx-gateway-fabric/installation/building-the-images.md) an NGINX Gateway Fabric container image from source or use a pre-built image available on [GitHub Container Registry](https://github.com/nginxinc/nginx-gateway-fabric/pkgs/container/nginx-gateway-fabric). 4. Deploy various [examples](examples). -5. Read our [guides](/docs/guides). +5. Read our [How-to guides](https://docs.nginx.com/nginx-gateway-fabric/how-to/). + +You can find the comprehensive user documentation in the [NGINX Documentation](https://docs.nginx.com/nginx-gateway-fabric/) website. ## NGINX Gateway Fabric Releases @@ -99,7 +101,7 @@ docker buildx imagetools inspect ghcr.io/nginxinc/nginx-gateway-fabric:edge --fo ## Troubleshooting -For troubleshooting help, see the [Troubleshooting](/docs/troubleshooting.md) document. +For troubleshooting help, see the [Troubleshooting](https://docs.nginx.com/nginx-gateway-fabric/how-to/monitoring/troubleshooting.md) document. ## Contacts From 38ac0e31b95396e00f1a940edeb79883d4d916d7 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Tue, 28 Nov 2023 17:12:47 +0000 Subject: [PATCH 079/101] fix: update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d6fb91525c..d0f7a0fbab 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Learn about our [design principles](/docs/developer/design-principles.md) and [a 4. Deploy various [examples](examples). 5. Read our [How-to guides](https://docs.nginx.com/nginx-gateway-fabric/how-to/). -You can find the comprehensive user documentation in the [NGINX Documentation](https://docs.nginx.com/nginx-gateway-fabric/) website. +You can find the comprehensive NGINX Gateway Fabric user documentation on the [NGINX Documentation](https://docs.nginx.com/nginx-gateway-fabric/) website. ## NGINX Gateway Fabric Releases From c9b99a3ea71f16443d1d80507c9fe126659de085 Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:14:27 +0000 Subject: [PATCH 080/101] Update docs/README.md --- docs/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 9c6d957572..516d7c9108 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,8 @@ # NGINX Gateway Fabric Documentation -This directory contains the developer documentation and the Enhancement proposals relating to NGINX Gateway Fabric. +This directory contains the developer documentation and the enhancement proposals relating to NGINX Gateway Fabric. + +_Please note: You can find the user documentation for NGINX Gateway Fabric in the [NGINX Documentation](https://docs.nginx.com/nginx-gateway-fabric/) website._ ## Contents From a495f0c388a0353d7cd67b73741242e9c6cea946 Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Tue, 28 Nov 2023 18:59:58 +0000 Subject: [PATCH 081/101] Update site/content/how-to/monitoring/troubleshooting.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/monitoring/troubleshooting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/monitoring/troubleshooting.md b/site/content/how-to/monitoring/troubleshooting.md index c27c9e23ea..c0355a9d6b 100644 --- a/site/content/how-to/monitoring/troubleshooting.md +++ b/site/content/how-to/monitoring/troubleshooting.md @@ -8,7 +8,7 @@ docs: "DOCS-000" {{< custom-styles >}} -This topic describes possible issues users might encounter when using Instance Manager. When possible, suggested workarounds are provided. +This topic describes possible issues users might encounter when using NGINX Gateway Fabric. When possible, suggested workarounds are provided. ## NGINX fails to reload From 6229e75e311e2c0cbb5913f5a637950920028dbc Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:00:38 +0000 Subject: [PATCH 082/101] Update site/content/how-to/traffic-management/advanced-routing.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/advanced-routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/advanced-routing.md b/site/content/how-to/traffic-management/advanced-routing.md index 1e7f9eab71..6eea195278 100644 --- a/site/content/how-to/traffic-management/advanced-routing.md +++ b/site/content/how-to/traffic-management/advanced-routing.md @@ -234,7 +234,7 @@ This request should receive a response from the `tea-post` Pod. Any other type o If you have any issues while sending traffic, try the following to debug your configuration and setup: -- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric Service. Refer to the topic [Expose NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}) for instructions on finding those values. +- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Gateway Fabric service. Refer to the topic [Expose NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}) for instructions on finding those values. - Check the status of the Gateway: From ab2ebb3126e2498e69b2cdaff673a53887ab5a9f Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:01:55 +0000 Subject: [PATCH 083/101] Update site/content/installation/expose-nginx-gateway-fabric.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/installation/expose-nginx-gateway-fabric.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/installation/expose-nginx-gateway-fabric.md b/site/content/installation/expose-nginx-gateway-fabric.md index 4d2201e471..1bf175325f 100644 --- a/site/content/installation/expose-nginx-gateway-fabric.md +++ b/site/content/installation/expose-nginx-gateway-fabric.md @@ -14,7 +14,7 @@ Gain access to NGINX Gateway Fabric by creating either a **NodePort** service or {{}}The service manifests configure NGINX Gateway Fabric on ports `80` and `443`, affecting any gateway [listeners](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.Listener) on these ports. To use different ports, update the manifests. NGINX Gateway Fabric requires a configured [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/#gateway) resource with a valid listener to listen on any ports.{{}} -NGINX Gateway Fabric uses the created service to update the **Addresses** field in the **Gateway Status** resource. Using a **LoadBalancer** service sets this field to the IP address and/or hostname of that Service. Without a service, the Pod IP address is used. +NGINX Gateway Fabric uses the created service to update the **Addresses** field in the **Gateway Status** resource. Using a **LoadBalancer** service sets this field to the IP address and/or hostname of that service. Without a service, the pod IP address is used. This gateway is associated with the NGINX Gateway Fabric through the **gatewayClassName** field. The default installation of NGINX Gateway Fabric creates a **GatewayClass** with the name **nginx**. NGINX Gateway Fabric will only configure gateways with a **gatewayClassName** of **nginx** unless you change the name via the `--gatewayclass` [command-line flag](/docs/cli-help.md#static-mode). From c8d9c7288326e1fe7df75bc660a1e54389141cdb Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:02:06 +0000 Subject: [PATCH 084/101] Update site/content/how-to/traffic-management/advanced-routing.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/advanced-routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/advanced-routing.md b/site/content/how-to/traffic-management/advanced-routing.md index 6eea195278..b47ff16076 100644 --- a/site/content/how-to/traffic-management/advanced-routing.md +++ b/site/content/how-to/traffic-management/advanced-routing.md @@ -227,7 +227,7 @@ Server address: 10.244.0.7:8080 Server name: tea-post-b59b8596b-g586r ``` -This request should receive a response from the `tea-post` Pod. Any other type of method, such as PATCH, will result in a `404 Not Found` response. +This request should receive a response from the `tea-post` pod. Any other type of method, such as PATCH, will result in a `404 Not Found` response. ## Troubleshooting From 882cf7db89d6c485f97d16b322a676080c42036f Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:02:25 +0000 Subject: [PATCH 085/101] Update site/content/overview/gateway-architecture.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/overview/gateway-architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/overview/gateway-architecture.md b/site/content/overview/gateway-architecture.md index 4252fd56b3..a51a984d7e 100644 --- a/site/content/overview/gateway-architecture.md +++ b/site/content/overview/gateway-architecture.md @@ -71,7 +71,7 @@ The following list describes the connections, preceeded by their types in parent - Write: _NGF_ writes to the _Kubernetes API_ to update the handled resources' statuses and emit events. If there's more than one replica of _NGF_ and [leader election](https://github.com/nginxinc/nginx-gateway-fabric/tree/main/deploy/helm-chart#configuration) is enabled, only the _NGF_ pod that is leading will write statuses to the _Kubernetes API_. 1. (HTTP, HTTPS) _Prometheus_ fetches the `controller-runtime` and NGINX metrics via an HTTP endpoint that _NGF_ exposes (`:9113/metrics` by default). Prometheus is **not** required by NGF, and its endpoint can be turned off. 1. (File I/O) - - Write: _NGF_ generates NGINX _configuration_ based on the cluster resources and writes them as `.conf` files to the mounted `nginx-conf` volume, located at `/etc/nginx/conf.d`. It also writes _TLS certificates_ and _keys_ from [TLS Secrets](https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets) referenced in the accepted Gateway resource to the `nginx-secrets` volume at the path `/etc/nginx/secrets`. + - Write: _NGF_ generates NGINX _configuration_ based on the cluster resources and writes them as `.conf` files to the mounted `nginx-conf` volume, located at `/etc/nginx/conf.d`. It also writes _TLS certificates_ and _keys_ from [TLS secrets](https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets) referenced in the accepted Gateway resource to the `nginx-secrets` volume at the path `/etc/nginx/secrets`. - Read: _NGF_ reads the PID file `nginx.pid` from the `nginx-run` volume, located at `/var/run/nginx`. _NGF_ extracts the PID of the nginx process from this file in order to send reload signals to _NGINX master_. 1. (File I/O) _NGF_ writes logs to its _stdout_ and _stderr_, which are collected by the container runtime. 1. (HTTP) _NGF_ fetches the NGINX metrics via the unix:/var/run/nginx/nginx-status.sock UNIX socket and converts it to _Prometheus_ format used in #2. From 66f39a42785c9285dc229b3640bdafef4adcb8d3 Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:02:34 +0000 Subject: [PATCH 086/101] Update site/content/overview/gateway-architecture.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/overview/gateway-architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/overview/gateway-architecture.md b/site/content/overview/gateway-architecture.md index a51a984d7e..ae9e2a23c5 100644 --- a/site/content/overview/gateway-architecture.md +++ b/site/content/overview/gateway-architecture.md @@ -51,7 +51,7 @@ NGINX Gateway Fabric consists of two containers: These containers are deployed in a single pod as a Kubernetes Deployment. -The `nginx-gateway`, or the control plane, is a [Kubernetes controller](https://kubernetes.io/docs/concepts/architecture/controller/), written with the [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) library. It watches Kubernetes objects (Services, Endpoints, Secrets, and Gateway API CRDs), translates them to NGINX configuration, and configures NGINX. +The `nginx-gateway`, or the control plane, is a [Kubernetes controller](https://kubernetes.io/docs/concepts/architecture/controller/), written with the [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) library. It watches Kubernetes objects (services, endpoints, secrets, and Gateway API CRDs), translates them to NGINX configuration, and configures NGINX. This configuration happens in two stages: From c2b60de0ac6e9c693b055b0e01cb2408dea40d05 Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:02:45 +0000 Subject: [PATCH 087/101] Update site/content/overview/gateway-architecture.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/overview/gateway-architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/overview/gateway-architecture.md b/site/content/overview/gateway-architecture.md index ae9e2a23c5..9d475ff32c 100644 --- a/site/content/overview/gateway-architecture.md +++ b/site/content/overview/gateway-architecture.md @@ -36,7 +36,7 @@ The figure shows: - _Gateway AB_, created by _Cluster Operator_, requests a point where traffic can be translated to Services within the cluster. This Gateway includes a listener with a hostname `*.example.com`. Application Developers have the ability to attach their application's routes to this Gateway if their application's hostname matches `*.example.com`. - _Application A_ with two pods deployed in the _applications_ namespace by _Application Developer A_. To expose the application to its clients (_Clients A_) via the host `a.example.com`, _Application Developer A_ creates _HTTPRoute A_ and attaches it to `Gateway AB`. - _Application B_ with one pod deployed in the _applications_ namespace by _Application Developer B_. To expose the application to its clients (_Clients B_) via the host `b.example.com`, _Application Developer B_ creates _HTTPRoute B_ and attaches it to `Gateway AB`. -- _Public Endpoint_, which fronts the _NGF_ pod. This is typically a TCP load balancer (cloud, software, or hardware) or a combination of such load balancer with a NodePort Service. _Clients A_ and _B_ connect to their applications via the _Public Endpoint_. +- _Public Endpoint_, which fronts the _NGF_ pod. This is typically a TCP load balancer (cloud, software, or hardware) or a combination of such load balancer with a NodePort service. _Clients A_ and _B_ connect to their applications via the _Public Endpoint_. The yellow and purple arrows represent connections related to the client traffic, and the black arrows represent access to the Kubernetes API. The resources within the cluster are color-coded based on the user responsible for their creation. From 063014e39365076925105433a02ff3f9a9fa05ca Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:05:32 +0000 Subject: [PATCH 088/101] Update site/content/reference/cli-help.md --- site/content/reference/cli-help.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/reference/cli-help.md b/site/content/reference/cli-help.md index 7e30a179e4..7c512d67c9 100644 --- a/site/content/reference/cli-help.md +++ b/site/content/reference/cli-help.md @@ -1,6 +1,6 @@ --- title: "Command-line Reference Guide" -description: "Learn about the commands available for the executable file of the F5 NGINX Gateway Fabric container." +description: "Learn about the commands available for the executable file of the NGINX Gateway Fabric container." weight: 100 toc: true docs: "DOCS-000" From a9c1993cc9ab0a3bffd92aa3c7f17d3716ad5b0d Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Tue, 28 Nov 2023 22:01:15 +0000 Subject: [PATCH 089/101] fix: fix broken shortcode --- site/content/installation/installing-ngf/manifests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index 361af237b4..efb798f537 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -32,7 +32,7 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. ```shell kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/webhook-install.yaml ``` -{{< important >}}The validating webhook is not needed if you are running Kubernetes 1.25+. Validation is done using CEL on the CRDs. See the [resource validation doc]({{< relref "/overview/resource-validation.md" >}}) for more information. +{{< important >}}The validating webhook is not needed if you are running Kubernetes 1.25+. Validation is done using CEL on the CRDs. See the [resource validation doc]({{< relref "/overview/resource-validation.md" >}}) for more information. {{< /important >}} 2. **Deploy the NGINX Gateway Fabric CRDs:** - Next, deploy the NGINX Gateway Fabric CRDs: From e2fe3115745eb3a49a078294c09f25c1867ec230 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 29 Nov 2023 13:06:01 +0000 Subject: [PATCH 090/101] fix: fix linting issues --- docs/developer/documentation.md | 25 ++++++++++--------- .../control-plane-configuration.md | 4 +++ .../upgrade-apps-without-downtime.md | 2 +- .../how-to/monitoring/troubleshooting.md | 6 ++--- .../routing-traffic-to-your-app.md | 2 +- .../delay-pod-termination-overview.md | 2 +- .../helm/uninstall-gateway-api-resources.md | 3 ++- .../expose-nginx-gateway-fabric.md | 6 +++-- .../installation/installing-ngf/helm.md | 6 +++-- .../installation/installing-ngf/manifests.md | 9 +++++++ .../overview/gateway-api-compatibility.md | 2 ++ site/mdlint_conf.json | 3 ++- 12 files changed, 46 insertions(+), 24 deletions(-) diff --git a/docs/developer/documentation.md b/docs/developer/documentation.md index a112418821..b68c718a30 100644 --- a/docs/developer/documentation.md +++ b/docs/developer/documentation.md @@ -22,7 +22,7 @@ This repo uses a [forking workflow](https://www.atlassian.com/git/tutorials/comp ### Publishing Documentation Updates -**`main`** is the default branch in this repo. All the latest content updates are merged into this branch. +**`main`** is the default branch in this repo. All the latest content updates are merged into this branch. The documentation is published from the latest public release branch, (for example, `release-4.0`). Work on your docs in a feature branch in your fork of the repo. Open pull requests into the `main` branch when you are ready to merge your work. @@ -49,7 +49,7 @@ Follow the instructions here to install Hugo: [Hugo Installation](https://gohugo We use markdownlint to check that Markdown files are correctly formatted. You can use `npm` to install markdownlint-cli: -``` +```shell npm install -g markdownlint-cli ``` @@ -74,13 +74,13 @@ The available kinds are: - Task: Enable the customer to achieve a specific goal, based on use case scenarios. - Concept: Help a customer learn about a specific feature or feature set. -- Reference: Describes an API, command line tool, config options, etc.; should be generated automatically from source code. +- Reference: Describes an API, command line tool, config options, etc.; should be generated automatically from source code. - Troubleshooting: Helps a customer solve a specific problem. - Tutorial: Walk a customer through an example use case scenario; results in a functional PoC environment. ### Format internal links -Format links as [Hugo relrefs](https://gohugo.io/content-management/cross-references/). +Format links as [Hugo relrefs](https://gohugo.io/content-management/cross-references/). > Note: Using file extensions when linking to internal docs with `relref` is optional. @@ -96,7 +96,7 @@ To install NGINX Gateway Fabric, refer to the [installation instructions]({{< re ### Add images -You can use the `img` [shortcode](#shortcodes) to insert images into your documentation. +You can use the `img` [shortcode](#Use-Hugo-shortcodes) to insert images into your documentation. 1. Add the image to the static/img directory. DO NOT include a forward slash at the beginning of the file path. This will break the image when it's rendered. @@ -105,17 +105,17 @@ You can use the `img` [shortcode](#shortcodes) to insert images into your docume 1. Add the img shortcode: {{< img src="img/" >}} - -> Note: The shortcode accepts all of the same parameters as the [Hugo figure shortcode](https://gohugo.io/content-management/shortcodes/#figure). + +> Note: The shortcode accepts all of the same parameters as the [Hugo figure shortcode](https://gohugo.io/content-management/shortcodes/#figure). ### Use Hugo shortcodes -You can use Hugo [shortcodes](https://gohugo.io/content-management/shortcodes) to do things like format callouts, add images, and reuse content across different docs. +You can use Hugo [shortcodes](https://gohugo.io/content-management/shortcodes) to do things like format callouts, add images, and reuse content across different docs. For example, to use the note callout: ```md {{< note >}}Provide the text of the note here. {{< /note >}} -``` +``` The callout shortcodes also support multi-line blocks: @@ -126,6 +126,7 @@ You should probably never do this specific thing in a production environment. If ``` Supported callouts: + - caution - important - note @@ -148,16 +149,16 @@ A few more useful shortcodes: To view the docs in a browser, run the Hugo server. This will reload the docs automatically so you can view updates as you work. -> Note: The docs use build environments to control the baseURL that will be used for things like internal references and resource (CSS and JS) loading. +> Note: The docs use build environments to control the baseURL that will be used for things like internal references and resource (CSS and JS) loading. > You can view the config for each environment in the [config](./config) directory of this repo. When running the Hugo server, you can specify the environment and baseURL if desired, but it's not necessary. For example: -``` +```shell hugo server ``` -``` +```shell hugo server -e development -b "http://127.0.0.1/nginx-gateway-fabric/" ``` diff --git a/site/content/how-to/configuration/control-plane-configuration.md b/site/content/how-to/configuration/control-plane-configuration.md index d300b76ba1..89f4016bad 100644 --- a/site/content/how-to/configuration/control-plane-configuration.md +++ b/site/content/how-to/configuration/control-plane-configuration.md @@ -24,15 +24,19 @@ to reflect whether it is valid or not. ### Spec +{{< bootstrap-table "table table-striped table-bordered" >}} | name | description | type | required | |---------|-----------------------------------------------------------------|--------------------------|----------| | logging | Logging defines logging related settings for the control plane. | [logging](#speclogging) | no | +{{< /bootstrap-table >}} ### Spec.Logging +{{< bootstrap-table "table table-striped table-bordered" >}} | name | description | type | required | |-------|------------------------------------------------------------------------|--------|----------| | level | Level defines the logging level. Supported values: info, debug, error. | string | no | +{{< /bootstrap-table >}} ## Viewing and Updating the Configuration diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index 7585aa83e4..271e16bdf3 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -10,7 +10,7 @@ docs: "DOCS-000" ## Overview -NGINX Gateway Fabric allows upgrading applications without downtime. +NGINX Gateway Fabric allows upgrading applications without downtime. {{< note >}} See the [Architecture document]({{< relref "/overview/gateway-architecture.md" >}}) to learn more about NGINX Gateway Fabric architecture.{{< /note >}} diff --git a/site/content/how-to/monitoring/troubleshooting.md b/site/content/how-to/monitoring/troubleshooting.md index c0355a9d6b..5c68a3288b 100644 --- a/site/content/how-to/monitoring/troubleshooting.md +++ b/site/content/how-to/monitoring/troubleshooting.md @@ -10,14 +10,14 @@ docs: "DOCS-000" This topic describes possible issues users might encounter when using NGINX Gateway Fabric. When possible, suggested workarounds are provided. -## NGINX fails to reload +### NGINX fails to reload #### Description - + Depending on your environment's configuration, the control plane may not have the proper permissions to reload NGINX. The NGINX configuration will not be applied and you will see the following error in the _nginx-gateway_ logs: `failed to reload NGINX: failed to send the HUP signal to NGINX main: operation not permitted` #### Resolution -To resolve this issue you will need to set `allowPrivilegeEscalation` to `true`. +To resolve this issue you will need to set `allowPrivilegeEscalation` to `true`. - If using Helm, you can set the `nginxGateway.securityContext.allowPrivilegeEscalation` value. - If using the manifests directly, you can update this field under the `nginx-gateway` container's `securityContext`. diff --git a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md index 983dfd7a81..21c0c3f941 100644 --- a/site/content/how-to/traffic-management/routing-traffic-to-your-app.md +++ b/site/content/how-to/traffic-management/routing-traffic-to-your-app.md @@ -109,7 +109,7 @@ Once NGINX Gateway Fabric processes the **cafe** gateway and **coffee** HTTPRout The **coffee** service is omitted from the diagram above because the NGINX Gateway Fabric routes directly to the pods that the **coffee** service targets. -{{< note >}}In the diagrams above, all resources that are the responsibility of the cluster operator are shown in blue. The orange resources are the responsibility of the application developers. +{{< note >}}In the diagrams above, all resources that are the responsibility of the cluster operator are shown in blue. The orange resources are the responsibility of the application developers. See the [roles and personas](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1) Gateway API document for more information on these roles.{{< /note >}} diff --git a/site/content/includes/installation/delay-pod-termination/delay-pod-termination-overview.md b/site/content/includes/installation/delay-pod-termination/delay-pod-termination-overview.md index 297d6dba32..4da3c375af 100644 --- a/site/content/includes/installation/delay-pod-termination/delay-pod-termination-overview.md +++ b/site/content/includes/installation/delay-pod-termination/delay-pod-termination-overview.md @@ -2,7 +2,7 @@ docs: --- -To avoid client service interruptions when upgrading NGINX Gateway Fabric, you can configure [`PreStop` hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) to delay terminating the NGINX Gateway Fabric pod, allowing the pod to complete certain actions before shutting down. This ensures a smooth upgrade without any downtime, also known as a zero downtime upgrade. +To avoid client service interruptions when upgrading NGINX Gateway Fabric, you can configure [`PreStop` hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) to delay terminating the NGINX Gateway Fabric pod, allowing the pod to complete certain actions before shutting down. This ensures a smooth upgrade without any downtime, also known as a zero downtime upgrade. For an in-depth explanation of how Kubernetes handles pod termination, see the [Termination of Pods](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination) topic on their official website. diff --git a/site/content/includes/installation/helm/uninstall-gateway-api-resources.md b/site/content/includes/installation/helm/uninstall-gateway-api-resources.md index 07a2e46407..76342ec8d8 100644 --- a/site/content/includes/installation/helm/uninstall-gateway-api-resources.md +++ b/site/content/includes/installation/helm/uninstall-gateway-api-resources.md @@ -15,4 +15,5 @@ If you are running on Kubernetes 1.23 or 1.24, you also need to delete the valid ```shell kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/webhook-install.yaml - ``` \ No newline at end of file + ``` + \ No newline at end of file diff --git a/site/content/installation/expose-nginx-gateway-fabric.md b/site/content/installation/expose-nginx-gateway-fabric.md index 1bf175325f..7ef53f65ea 100644 --- a/site/content/installation/expose-nginx-gateway-fabric.md +++ b/site/content/installation/expose-nginx-gateway-fabric.md @@ -32,7 +32,7 @@ A **NodePort** service allocates a port on every cluster node. Access NGINX Gate To create a **LoadBalancer** service, use the appropriate manifest for your cloud provider: -### GCP (Google Cloud Platform) and Azure +### GCP (Google Cloud Platform) and Azure 1. Run the following command: @@ -48,7 +48,7 @@ To create a **LoadBalancer** service, use the appropriate manifest for your clou 3. Use the public IP of the load balancer to access NGINX Gateway Fabric. -### AWS (Amazon Web Services): +### AWS (Amazon Web Services) 1. Run the following command: @@ -61,9 +61,11 @@ To create a **LoadBalancer** service, use the appropriate manifest for your clou ```shell kubectl get svc nginx-gateway -n nginx-gateway ``` + {{< note >}} We recommend using the NLB DNS whenever possible, but for testing purposes, you can resolve the DNS name to get the IP address of the load balancer: ```shell nslookup ``` + {{< /note >}} diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index ed24625046..dcd5e1f69c 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -141,11 +141,13 @@ Follow these steps to configure delayed pod termination: 1. Save the changes. -{{}} +{{}} For additional information on configuring and understanding the behavior of containers and pods during their lifecycle, refer to the following Kubernetes documentation: + - [Container Lifecycle Hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) - [Pod Lifecycle](https://kubernetes.io/docs/concepts/workloads/Pods/Pod-lifecycle/#Pod-termination) -{{}} + +{{}} ## Uninstall NGINX Gateway Fabric diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index efb798f537..3c32ede67f 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -22,36 +22,45 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. {{}}By default, NGINX Gateway Fabric is installed in the **nginx-gateway** namespace. You can deploy in another namespace by modifying the manifest files.{{}} 1. **Install the Gateway API resources:** + - Install the Gateway API CRDs from [the Gateway API repo](https://github.com/kubernetes-sigs/gateway-api): ```shell kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml ``` + - If you are running on Kubernetes 1.23 or 1.24, you also need to install the validating webhook. To do so, run: ```shell kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/webhook-install.yaml ``` + {{< important >}}The validating webhook is not needed if you are running Kubernetes 1.25+. Validation is done using CEL on the CRDs. See the [resource validation doc]({{< relref "/overview/resource-validation.md" >}}) for more information. {{< /important >}} 2. **Deploy the NGINX Gateway Fabric CRDs:** + - Next, deploy the NGINX Gateway Fabric CRDs: ```shell kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml ``` 3. **Deploy NGINX Gateway Fabric:** + - Then, deploy NGINX Gateway Fabric: + ```shell kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml ``` 4. **Verify the Deployment:** - To confirm that NGINX Gateway Fabric is running, check the pods in the `nginx-gateway` namespace: + ```shell kubectl get pods -n nginx-gateway ``` + The output should look similar to this (note that the pod name will include a unique string): + ```text NAME READY STATUS RESTARTS AGE nginx-gateway-5d4f4c7db7-xk2kq 2/2 Running 0 112s diff --git a/site/content/overview/gateway-api-compatibility.md b/site/content/overview/gateway-api-compatibility.md index 42c1a259a0..c748ab37fb 100644 --- a/site/content/overview/gateway-api-compatibility.md +++ b/site/content/overview/gateway-api-compatibility.md @@ -8,6 +8,7 @@ docs: "DOCS-000" ## Summary +{{< bootstrap-table "table table-striped table-bordered" >}} | Resource | Core Support Level | Extended Support Level | Implementation-Specific Support Level | API Version | |-------------------------------------|--------------------|------------------------|---------------------------------------|-------------| | [GatewayClass](#gatewayclass) | Supported | Not supported | Not Supported | v1 | @@ -18,6 +19,7 @@ docs: "DOCS-000" | [TLSRoute](#tlsroute) | Not supported | Not supported | Not Supported | N/A | | [TCPRoute](#tcproute) | Not supported | Not supported | Not Supported | N/A | | [UDPRoute](#udproute) | Not supported | Not supported | Not Supported | N/A | +{{< /bootstrap-table >}} ## Terminology diff --git a/site/mdlint_conf.json b/site/mdlint_conf.json index b467fde3f4..81943eafa3 100644 --- a/site/mdlint_conf.json +++ b/site/mdlint_conf.json @@ -10,6 +10,7 @@ "MD003": false, "MD002": false, "MD024": {"siblings_only": true}, - "MD046": false + "MD046": false, + "MD001": false } \ No newline at end of file From e558e8e5e797e7e0677325efba880e5aef00005e Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 29 Nov 2023 13:23:16 +0000 Subject: [PATCH 091/101] fix: fix lint issues --- docs/developer/documentation.md | 2 +- .../helm/uninstall-gateway-api-resources.md | 1 - .../expose-nginx-gateway-fabric.md | 2 +- .../installation/installing-ngf/manifests.md | 26 ++++++++++--------- site/content/overview/gateway-architecture.md | 10 +++---- site/content/reference/cli-help.md | 2 +- .../reference/technical-specifications.md | 2 +- site/content/releases.md | 2 +- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/docs/developer/documentation.md b/docs/developer/documentation.md index b68c718a30..f86721ea78 100644 --- a/docs/developer/documentation.md +++ b/docs/developer/documentation.md @@ -96,7 +96,7 @@ To install NGINX Gateway Fabric, refer to the [installation instructions]({{< re ### Add images -You can use the `img` [shortcode](#Use-Hugo-shortcodes) to insert images into your documentation. +You can use the `img` [shortcode](#use-hugo-shortcodes to insert images into your documentation. 1. Add the image to the static/img directory. DO NOT include a forward slash at the beginning of the file path. This will break the image when it's rendered. diff --git a/site/content/includes/installation/helm/uninstall-gateway-api-resources.md b/site/content/includes/installation/helm/uninstall-gateway-api-resources.md index 76342ec8d8..0753799dde 100644 --- a/site/content/includes/installation/helm/uninstall-gateway-api-resources.md +++ b/site/content/includes/installation/helm/uninstall-gateway-api-resources.md @@ -16,4 +16,3 @@ If you are running on Kubernetes 1.23 or 1.24, you also need to delete the valid ```shell kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/webhook-install.yaml ``` - \ No newline at end of file diff --git a/site/content/installation/expose-nginx-gateway-fabric.md b/site/content/installation/expose-nginx-gateway-fabric.md index 7ef53f65ea..7e92c1de24 100644 --- a/site/content/installation/expose-nginx-gateway-fabric.md +++ b/site/content/installation/expose-nginx-gateway-fabric.md @@ -67,5 +67,5 @@ To create a **LoadBalancer** service, use the appropriate manifest for your clou ```shell nslookup ``` - + {{< /note >}} diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index 3c32ede67f..6dadeda6bf 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -35,16 +35,16 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/webhook-install.yaml ``` -{{< important >}}The validating webhook is not needed if you are running Kubernetes 1.25+. Validation is done using CEL on the CRDs. See the [resource validation doc]({{< relref "/overview/resource-validation.md" >}}) for more information. {{< /important >}} + {{< important >}}The validating webhook is not needed if you are running Kubernetes 1.25+. Validation is done using CEL on the CRDs. See the [resource validation doc]({{< relref "/overview/resource-validation.md" >}}) for more information. {{< /important >}} -2. **Deploy the NGINX Gateway Fabric CRDs:** +1. **Deploy the NGINX Gateway Fabric CRDs:** - Next, deploy the NGINX Gateway Fabric CRDs: ```shell kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml ``` -3. **Deploy NGINX Gateway Fabric:** +1. **Deploy NGINX Gateway Fabric:** - Then, deploy NGINX Gateway Fabric: @@ -52,15 +52,15 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/nginx-gateway.yaml ``` -4. **Verify the Deployment:** +1. **Verify the Deployment:** - To confirm that NGINX Gateway Fabric is running, check the pods in the `nginx-gateway` namespace: ```shell kubectl get pods -n nginx-gateway ``` - + The output should look similar to this (note that the pod name will include a unique string): - + ```text NAME READY STATUS RESTARTS AGE nginx-gateway-5d4f4c7db7-xk2kq 2/2 Running 0 112s @@ -90,20 +90,20 @@ To upgrade NGINX Gateway Fabric and get the latest features and improvements, ta ``` - If you are running on Kubernetes 1.25 or newer and have the validating webhook installed, you should remove the - webhook: + webhook: ```shell kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/webhook-install.yaml ``` -2. **Upgrade NGINX Gateway Fabric CRDs:** +1. **Upgrade NGINX Gateway Fabric CRDs:** - To upgrade the Custom Resource Definitions (CRDs), run: ```shell kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml ``` -3. **Upgrade NGINX Gateway Fabric deployment:** +1. **Upgrade NGINX Gateway Fabric deployment:** - To upgrade the deployment, run: ```shell @@ -151,11 +151,13 @@ Follow these steps to configure delayed pod termination: 1. Save the changes. -{{}} +{{}} For additional information on configuring and understanding the behavior of containers and pods during their lifecycle, refer to the following Kubernetes documentation: + - [Container Lifecycle Hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks) - [Pod Lifecycle](https://kubernetes.io/docs/concepts/workloads/Pods/Pod-lifecycle/#Pod-termination) -{{}} + +{{}} ## Uninstall NGINX Gateway Fabric @@ -174,7 +176,7 @@ Follow these steps to uninstall NGINX Gateway Fabric and Gateway API from your K kubectl delete -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml ``` -2. **Remove the Gateway API resources:** +1. **Remove the Gateway API resources:** - {{}} diff --git a/site/content/overview/gateway-architecture.md b/site/content/overview/gateway-architecture.md index 9d475ff32c..73535eb6da 100644 --- a/site/content/overview/gateway-architecture.md +++ b/site/content/overview/gateway-architecture.md @@ -25,7 +25,7 @@ This figure depicts an example of NGINX Gateway Fabric exposing two web applicat {{}} -{{< note >}} The figure does not show many of the necessary Kubernetes resources the Cluster Operators and Application Developers need to create, like deployment and services. {{< /note >}} +{{< note >}} The figure does not show many of the necessary Kubernetes resources the Cluster Operators and Application Developers need to create, like deployment and services. {{< /note >}} The figure shows: @@ -51,12 +51,12 @@ NGINX Gateway Fabric consists of two containers: These containers are deployed in a single pod as a Kubernetes Deployment. -The `nginx-gateway`, or the control plane, is a [Kubernetes controller](https://kubernetes.io/docs/concepts/architecture/controller/), written with the [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) library. It watches Kubernetes objects (services, endpoints, secrets, and Gateway API CRDs), translates them to NGINX configuration, and configures NGINX. +The `nginx-gateway`, or the control plane, is a [Kubernetes controller](https://kubernetes.io/docs/concepts/architecture/controller/), written with the [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) library. It watches Kubernetes objects (services, endpoints, secrets, and Gateway API CRDs), translates them to NGINX configuration, and configures NGINX. This configuration happens in two stages: -1. NGINX configuration files are written to the NGINX configuration volume shared by the `nginx-gateway` and `nginx` containers. -1. The control plane reloads the NGINX process. +1. NGINX configuration files are written to the NGINX configuration volume shared by the `nginx-gateway` and `nginx` containers. +1. The control plane reloads the NGINX process. This is possible because the two containers [share a process namespace](https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/), allowing the NGF process to send signals to the NGINX main process. @@ -94,4 +94,4 @@ The following list describes the connections, preceeded by their types in parent The `nginx-gateway` container includes a readiness endpoint available through the path `/readyz`. A [readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) periodically checks the endpoint on startup, returning a `200 OK` response when the pod can accept traffic for the data plane. Once the control plane successfully starts, the pod becomes ready. -If there are relevant Gateway API resources in the cluster, the control plane will generate the first NGINX configuration and successfully reload NGINX before the pod is considered ready. \ No newline at end of file +If there are relevant Gateway API resources in the cluster, the control plane will generate the first NGINX configuration and successfully reload NGINX before the pod is considered ready. diff --git a/site/content/reference/cli-help.md b/site/content/reference/cli-help.md index 7c512d67c9..5bc5ae1c92 100644 --- a/site/content/reference/cli-help.md +++ b/site/content/reference/cli-help.md @@ -50,4 +50,4 @@ _Usage_: | Name | Type | Description | |----------|-----------------|-------------------------------------------------------------------------------------------------------| | duration | `time.Duration` | Set the duration of sleep. Must be parsable by [`time.ParseDuration`](https://pkg.go.dev/time#ParseDuration). (default `30s`) | -{{% /bootstrap-table %}} \ No newline at end of file +{{% /bootstrap-table %}} diff --git a/site/content/reference/technical-specifications.md b/site/content/reference/technical-specifications.md index bca7430608..7a31f83dc5 100644 --- a/site/content/reference/technical-specifications.md +++ b/site/content/reference/technical-specifications.md @@ -8,6 +8,6 @@ tags: [ "docs" ] docs: "DOCS-000" --- -See the NGINX Gateway Fabric technical specifications page: +See the NGINX Gateway Fabric technical specifications page: https://github.com/nginxinc/nginx-gateway-fabric#technical-specifications diff --git a/site/content/releases.md b/site/content/releases.md index 8b3a84c781..a66ca685d5 100644 --- a/site/content/releases.md +++ b/site/content/releases.md @@ -8,6 +8,6 @@ tags: [ "docs" ] docs: "DOCS-000" --- -See the NGINX Gateway Fabric changelog page: +See the NGINX Gateway Fabric changelog page: https://github.com/nginxinc/nginx-gateway-fabric/blob/main/CHANGELOG.md From e00756ab1c219c52b22e9e8b29439c5309e38109 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 29 Nov 2023 13:24:59 +0000 Subject: [PATCH 092/101] fix: fix lint issues --- site/content/installation/installing-ngf/manifests.md | 1 + 1 file changed, 1 insertion(+) diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index 6dadeda6bf..36ceb6bd97 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -40,6 +40,7 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. 1. **Deploy the NGINX Gateway Fabric CRDs:** - Next, deploy the NGINX Gateway Fabric CRDs: + ```shell kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml ``` From 858d405549e94682b0a8e38e9e8b2b7a49cfef05 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 29 Nov 2023 13:25:58 +0000 Subject: [PATCH 093/101] fix: fix lint issues --- site/content/installation/installing-ngf/manifests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index 36ceb6bd97..35d8ab6187 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -40,7 +40,7 @@ Deploying NGINX Gateway Fabric with Kubernetes manifests takes only a few steps. 1. **Deploy the NGINX Gateway Fabric CRDs:** - Next, deploy the NGINX Gateway Fabric CRDs: - + ```shell kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.0.0/crds.yaml ``` From e8fc9dda659c2ccef3a4b9c8e4fe4abcdac1e4bc Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 29 Nov 2023 13:30:54 +0000 Subject: [PATCH 094/101] chore: update upgrade apps doc --- .../how-to/maintenance/upgrade-apps-without-downtime.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index 271e16bdf3..126f3ba1e0 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -10,13 +10,9 @@ docs: "DOCS-000" ## Overview -NGINX Gateway Fabric allows upgrading applications without downtime. - {{< note >}} See the [Architecture document]({{< relref "/overview/gateway-architecture.md" >}}) to learn more about NGINX Gateway Fabric architecture.{{< /note >}} -## NGINX Gateway Fabric functionality - -To understand the upgrade methods, you need to be familiar with the NGINX features that help prevent application downtime: Graceful configuration reloads and upstream server updates. +NGINX Gateway Fabric allows upgrading applications without downtime. To understand the upgrade methods, you need to be familiar with the NGINX features that help prevent application downtime: Graceful configuration reloads and upstream server updates. ### Graceful configuration reloads From 392e62745ffe303e8632373ff2b2420996b12fb8 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 29 Nov 2023 13:35:00 +0000 Subject: [PATCH 095/101] fix: reword sentence upgrade apps without downtime --- .../content/how-to/maintenance/upgrade-apps-without-downtime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index 126f3ba1e0..47e12b0978 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -104,7 +104,7 @@ A more flexible and precise way to implement canary releases is to configure a t weight: 5 ``` -{{< note >}}There is no stickiness for the requests from the same client. NGINX will independently split each request among the backend references.{{< /note >}} +{{< note >}}Every requests coming from the same client won't necessarily be sent to the same backend. NGINX will independently split each request among the backend references.{{< /note >}} By updating the rule you can further increase the share of traffic the new version gets and finally completely switch to the new version: From ebadc7490c8c7c5b2193e929d5a14ecf012773a2 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 29 Nov 2023 15:03:17 +0000 Subject: [PATCH 096/101] fix: various fixes --- site/content/_index.md | 4 ++-- site/content/changelog.md | 6 +++--- site/content/how-to/_index.md | 6 +++--- site/content/reference/_index.md | 6 +++--- site/content/releases.md | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/site/content/_index.md b/site/content/_index.md index 2d28bc787d..999aa3155e 100644 --- a/site/content/_index.md +++ b/site/content/_index.md @@ -1,7 +1,7 @@ --- title: "Welcome to the NGINX Gateway Fabric documentation" -description: +description: weight: 300 linkTitle: "NGINX Gateway Fabric" menu: docs ---- \ No newline at end of file +--- diff --git a/site/content/changelog.md b/site/content/changelog.md index cba9060be9..cd22389b78 100644 --- a/site/content/changelog.md +++ b/site/content/changelog.md @@ -1,8 +1,8 @@ --- title: "Changelog" -description: "" +description: "No description" weight: 10000 toc: true draft: true -docs: "DOCS-000" ---- \ No newline at end of file +docs: "DOCS-1358" +--- diff --git a/site/content/how-to/_index.md b/site/content/how-to/_index.md index f02304bb4e..969045d4d8 100644 --- a/site/content/how-to/_index.md +++ b/site/content/how-to/_index.md @@ -1,9 +1,9 @@ --- title: "How-To Guides" -description: +description: weight: 300 linkTitle: "Guides" -menu: +menu: docs: parent: NGINX Gateway Fabric ---- \ No newline at end of file +--- diff --git a/site/content/reference/_index.md b/site/content/reference/_index.md index 3acd6879e6..e2262acf20 100644 --- a/site/content/reference/_index.md +++ b/site/content/reference/_index.md @@ -1,9 +1,9 @@ --- title: "Reference" -description: +description: weight: 400 linkTitle: "Reference" -menu: +menu: docs: parent: NGINX Gateway Fabric ---- \ No newline at end of file +--- diff --git a/site/content/releases.md b/site/content/releases.md index a66ca685d5..92f630639c 100644 --- a/site/content/releases.md +++ b/site/content/releases.md @@ -5,7 +5,7 @@ description: "NGINX Gateway Fabric releases." weight: 1200 toc: true tags: [ "docs" ] -docs: "DOCS-000" +docs: "DOCS-1359" --- See the NGINX Gateway Fabric changelog page: From 3c2bbfc9f30f33317952eab9b4a76889b91b47f1 Mon Sep 17 00:00:00 2001 From: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:08:14 +0000 Subject: [PATCH 097/101] Update site/content/how-to/maintenance/upgrade-apps-without-downtime.md --- .../content/how-to/maintenance/upgrade-apps-without-downtime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md index 47e12b0978..c39fb0e071 100644 --- a/site/content/how-to/maintenance/upgrade-apps-without-downtime.md +++ b/site/content/how-to/maintenance/upgrade-apps-without-downtime.md @@ -104,7 +104,7 @@ A more flexible and precise way to implement canary releases is to configure a t weight: 5 ``` -{{< note >}}Every requests coming from the same client won't necessarily be sent to the same backend. NGINX will independently split each request among the backend references.{{< /note >}} +{{< note >}}Every request coming from the same client won't necessarily be sent to the same backend. NGINX will independently split each request among the backend references.{{< /note >}} By updating the rule you can further increase the share of traffic the new version gets and finally completely switch to the new version: From 55eab7ed66d0045f2a04b2b0f193e8f1fbf9308b Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 29 Nov 2023 15:13:27 +0000 Subject: [PATCH 098/101] fix: fix lint issues --- site/content/overview/_index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/overview/_index.md b/site/content/overview/_index.md index ea61fb588b..fb6a7d7d89 100644 --- a/site/content/overview/_index.md +++ b/site/content/overview/_index.md @@ -1,9 +1,9 @@ --- title: "Overview" -description: +description: weight: 100 linkTitle: "Overview" -menu: +menu: docs: parent: NGINX Gateway Fabric ---- \ No newline at end of file +--- From 05573e10fd6860829c10bcd33bf2694353cdea32 Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 29 Nov 2023 15:21:48 +0000 Subject: [PATCH 099/101] fix: fix lint issues --- site/Makefile | 2 +- site/content/how-to/configuration/_index.md | 6 ++-- site/content/how-to/maintenance/_index.md | 6 ++-- site/content/how-to/monitoring/_index.md | 6 ++-- .../how-to/traffic-management/_index.md | 6 ++-- site/content/installation/_index.md | 6 ++-- .../installation/building-the-images.md | 2 +- .../installation/installing-ngf/_index.md | 6 ++-- site/content/overview/gateway-architecture.md | 2 +- site/md-linkcheck-config.json | 25 +++++++------- site/mdlint_conf.json | 33 ++++++++++--------- site/netlify.toml | 4 +-- 12 files changed, 53 insertions(+), 51 deletions(-) diff --git a/site/Makefile b/site/Makefile index 3b93debabd..b5231e351c 100644 --- a/site/Makefile +++ b/site/Makefile @@ -81,7 +81,7 @@ hugo-mod: build-production: hugo --gc -e production - + build-staging: hugo --gc -e staging diff --git a/site/content/how-to/configuration/_index.md b/site/content/how-to/configuration/_index.md index a6cf76daeb..cd5e67c0e8 100644 --- a/site/content/how-to/configuration/_index.md +++ b/site/content/how-to/configuration/_index.md @@ -1,9 +1,9 @@ --- title: "Configuration" -description: +description: weight: 200 linkTitle: "Configuration" -menu: +menu: docs: parent: How-To Guides ---- \ No newline at end of file +--- diff --git a/site/content/how-to/maintenance/_index.md b/site/content/how-to/maintenance/_index.md index 39996db6dc..5c33e95bbc 100644 --- a/site/content/how-to/maintenance/_index.md +++ b/site/content/how-to/maintenance/_index.md @@ -1,9 +1,9 @@ --- title: "Maintenance and Upgrades" -description: +description: weight: 400 linkTitle: "Maintenance and Upgrades" -menu: +menu: docs: parent: How-To Guides ---- \ No newline at end of file +--- diff --git a/site/content/how-to/monitoring/_index.md b/site/content/how-to/monitoring/_index.md index 860266d953..0213ee98e3 100644 --- a/site/content/how-to/monitoring/_index.md +++ b/site/content/how-to/monitoring/_index.md @@ -1,9 +1,9 @@ --- title: "Monitoring and Troubleshooting" -description: +description: weight: 500 linkTitle: "Monitoring and Troubleshooting" -menu: +menu: docs: parent: How-To Guides ---- \ No newline at end of file +--- diff --git a/site/content/how-to/traffic-management/_index.md b/site/content/how-to/traffic-management/_index.md index 3071333a05..8bf8679eac 100644 --- a/site/content/how-to/traffic-management/_index.md +++ b/site/content/how-to/traffic-management/_index.md @@ -1,9 +1,9 @@ --- title: "Traffic Management" -description: +description: weight: 300 linkTitle: "Traffic Management" -menu: +menu: docs: parent: How-To Guides ---- \ No newline at end of file +--- diff --git a/site/content/installation/_index.md b/site/content/installation/_index.md index 7e9ffd8c24..3144876a5f 100644 --- a/site/content/installation/_index.md +++ b/site/content/installation/_index.md @@ -1,9 +1,9 @@ --- title: "Installation" -description: +description: weight: 200 linkTitle: "Installation" -menu: +menu: docs: parent: NGINX Gateway Fabric ---- \ No newline at end of file +--- diff --git a/site/content/installation/building-the-images.md b/site/content/installation/building-the-images.md index 96ca927e39..5c86147229 100644 --- a/site/content/installation/building-the-images.md +++ b/site/content/installation/building-the-images.md @@ -4,7 +4,7 @@ weight: 300 toc: true docs: "DOCS-000" --- - + {{}} ## Overview diff --git a/site/content/installation/installing-ngf/_index.md b/site/content/installation/installing-ngf/_index.md index b51a79d985..e37f668478 100644 --- a/site/content/installation/installing-ngf/_index.md +++ b/site/content/installation/installing-ngf/_index.md @@ -1,9 +1,9 @@ --- title: "Installing NGINX Gateway Fabric" -description: +description: weight: 200 linkTitle: "Installing NGINX Gateway Fabric" -menu: +menu: docs: parent: Installation ---- \ No newline at end of file +--- diff --git a/site/content/overview/gateway-architecture.md b/site/content/overview/gateway-architecture.md index 73535eb6da..ecfdf3036a 100644 --- a/site/content/overview/gateway-architecture.md +++ b/site/content/overview/gateway-architecture.md @@ -38,7 +38,7 @@ The figure shows: - _Application B_ with one pod deployed in the _applications_ namespace by _Application Developer B_. To expose the application to its clients (_Clients B_) via the host `b.example.com`, _Application Developer B_ creates _HTTPRoute B_ and attaches it to `Gateway AB`. - _Public Endpoint_, which fronts the _NGF_ pod. This is typically a TCP load balancer (cloud, software, or hardware) or a combination of such load balancer with a NodePort service. _Clients A_ and _B_ connect to their applications via the _Public Endpoint_. -The yellow and purple arrows represent connections related to the client traffic, and the black arrows represent access to the Kubernetes API. The resources within the cluster are color-coded based on the user responsible for their creation. +The yellow and purple arrows represent connections related to the client traffic, and the black arrows represent access to the Kubernetes API. The resources within the cluster are color-coded based on the user responsible for their creation. For example, the Cluster Operator is denoted by the color green, indicating they create and manage all the green resources. diff --git a/site/md-linkcheck-config.json b/site/md-linkcheck-config.json index 42cf466dff..aff3727179 100644 --- a/site/md-linkcheck-config.json +++ b/site/md-linkcheck-config.json @@ -1,14 +1,13 @@ { - "replacementPatterns": [ - { - "pattern": "^\/", - "replacement": "/" - } - ], - "ignorePatterns": [ - { - "pattern": "^.+localhost.+$|\/.+yaml" - } - ] - } - \ No newline at end of file + "replacementPatterns": [ + { + "pattern": "^/", + "replacement": "/" + } + ], + "ignorePatterns": [ + { + "pattern": "^.+localhost.+$|/.+yaml" + } + ] +} diff --git a/site/mdlint_conf.json b/site/mdlint_conf.json index 81943eafa3..c35d58405b 100644 --- a/site/mdlint_conf.json +++ b/site/mdlint_conf.json @@ -1,16 +1,19 @@ { - "MD009": false, - "MD012": false, - "MD010": false, - "MD013": false, - "MD004": {"style": "dash"}, - "MD022": false, - "MD033": false, - "MD041": false, - "MD003": false, - "MD002": false, - "MD024": {"siblings_only": true}, - "MD046": false, - "MD001": false - } - \ No newline at end of file + "MD009": false, + "MD012": false, + "MD010": false, + "MD013": false, + "MD004": { + "style": "dash" + }, + "MD022": false, + "MD033": false, + "MD041": false, + "MD003": false, + "MD002": false, + "MD024": { + "siblings_only": true + }, + "MD046": false, + "MD001": false +} diff --git a/site/netlify.toml b/site/netlify.toml index 98444017e8..4809083559 100644 --- a/site/netlify.toml +++ b/site/netlify.toml @@ -3,7 +3,7 @@ publish = "public" [context.production] - command = "make all" + command = "make all" [context.docs-development] command = "make all-dev" @@ -31,4 +31,4 @@ [[redirects]] from = "/nginx-gateway-fabric/*" to = "/nginx-gateway-fabric/404.html" - status = 404 \ No newline at end of file + status = 404 From 7e710eccabeabca9258aadbd5da3bec55176e25e Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 29 Nov 2023 15:27:15 +0000 Subject: [PATCH 100/101] fix: fix lint issues --- docs/developer/documentation.md | 5 +++-- site/config/staging/config.toml | 2 +- site/content/installation/installing-ngf/helm.md | 1 - site/content/installation/installing-ngf/manifests.md | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/developer/documentation.md b/docs/developer/documentation.md index f86721ea78..f927fa59bb 100644 --- a/docs/developer/documentation.md +++ b/docs/developer/documentation.md @@ -50,7 +50,7 @@ Follow the instructions here to install Hugo: [Hugo Installation](https://gohugo We use markdownlint to check that Markdown files are correctly formatted. You can use `npm` to install markdownlint-cli: ```shell -npm install -g markdownlint-cli +npm install -g markdownlint-cli ``` ## How to write docs with Hugo @@ -83,7 +83,7 @@ The available kinds are: Format links as [Hugo relrefs](https://gohugo.io/content-management/cross-references/). > Note: Using file extensions when linking to internal docs with `relref` is optional. - + - You can use relative paths or just the filename. We recommend using the filename - Paths without a leading `/` are first resolved relative to the current page, then to the remainder of the site. - Anchors are supported. @@ -109,6 +109,7 @@ You can use the `img` [shortcode](#use-hugo-shortcodes to insert images into you > Note: The shortcode accepts all of the same parameters as the [Hugo figure shortcode](https://gohugo.io/content-management/shortcodes/#figure). ### Use Hugo shortcodes + You can use Hugo [shortcodes](https://gohugo.io/content-management/shortcodes) to do things like format callouts, add images, and reuse content across different docs. For example, to use the note callout: diff --git a/site/config/staging/config.toml b/site/config/staging/config.toml index b9e5085762..251ca5fdfa 100644 --- a/site/config/staging/config.toml +++ b/site/config/staging/config.toml @@ -1,3 +1,3 @@ baseURL = "https://docs-staging.nginx.com/nginx-gateway-fabric" title = "STAGING -- NGINX Gateway Fabric" -canonifyURLs = false \ No newline at end of file +canonifyURLs = false diff --git a/site/content/installation/installing-ngf/helm.md b/site/content/installation/installing-ngf/helm.md index dcd5e1f69c..9ed7337edc 100644 --- a/site/content/installation/installing-ngf/helm.md +++ b/site/content/installation/installing-ngf/helm.md @@ -182,4 +182,3 @@ Follow these steps to uninstall NGINX Gateway Fabric and Gateway API from your K ### Expose NGINX Gateway Fabric {{}} - diff --git a/site/content/installation/installing-ngf/manifests.md b/site/content/installation/installing-ngf/manifests.md index 35d8ab6187..fdba9604bc 100644 --- a/site/content/installation/installing-ngf/manifests.md +++ b/site/content/installation/installing-ngf/manifests.md @@ -186,4 +186,3 @@ Follow these steps to uninstall NGINX Gateway Fabric and Gateway API from your K ### Expose NGINX Gateway Fabric {{}} - From 62b5f064b265f250c7506b03b429e59dadeb328f Mon Sep 17 00:00:00 2001 From: Jon Cahill-Torre Date: Wed, 29 Nov 2023 15:33:47 +0000 Subject: [PATCH 101/101] fix: add empty line gitignore --- site/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/.gitignore b/site/.gitignore index 10ea4b1d12..919b131ed5 100644 --- a/site/.gitignore +++ b/site/.gitignore @@ -1,3 +1,3 @@ public .hugo_build.lock -resources \ No newline at end of file +resources