Skip to content

Commit 4554b24

Browse files
committed
Build and use custom nginx container (nginx#934)
Replace the publicly available nginx image with a custom nginx image. Problem: Using the publicly available nginx image requires users to create ConfigMaps for the nginx.conf file and the njs module and mount them to the NKG Pod as volumes. This pattern is not extensible and adds extra steps for developers and users. Additionally, an init container is required in order to set up the nginx config environment. Solution: Build and use a custom nginx container. The nginx.conf and njs module are now baked into the nginx image. This eliminates the need for ConfigMaps. The config directories /etc/nginx/conf.d and /etc/nginx/secrets are created as volumes and mounted to the Pod with a group ID 1001. This allows the control plane to write to the directories and nginx to read from them. Both the nginx and nginx-gateway processes run under group ID 1001 but have different user IDs (101 and 102). The nginx container runs as user 101 instead of root and runs with the minimum set of capabilities.
1 parent 5b037d1 commit 4554b24

25 files changed

+348
-784
lines changed

.github/workflows/ci.yml

+58-23
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,6 @@ jobs:
5555
- name: Check if generated go files are up to date
5656
run: make generate && git diff --exit-code
5757

58-
- name: Check if njs-modules yaml is up to date
59-
run: make generate-njs-yaml && git diff --exit-code
60-
6158
- name: Check if generated manifests are up to date
6259
run: make generate-manifests && git diff --exit-code
6360

@@ -173,8 +170,8 @@ jobs:
173170
- name: Docker Buildx
174171
uses: docker/setup-buildx-action@4c0219f9ac95b02789c1075625400b2acbff50b1 # v2.9.1
175172

176-
- name: Docker meta
177-
id: meta
173+
- name: NKG Docker meta
174+
id: nkg-meta
178175
uses: docker/metadata-action@818d4b7b91585d195f67373fd9cb0332e31a7175 # v4.6.0
179176
with:
180177
images: |
@@ -185,37 +182,67 @@ jobs:
185182
type=ref,event=pr
186183
type=ref,event=branch,suffix=-rc,enable=${{ startsWith(github.ref, 'refs/heads/release') }}
187184
188-
- name: Build Docker Image
185+
- name: NGINX Docker meta
186+
id: nginx-meta
187+
uses: docker/metadata-action@818d4b7b91585d195f67373fd9cb0332e31a7175 # v4.6.0
188+
with:
189+
images: |
190+
name=ghcr.io/nginxinc/nginx-kubernetes-gateway/nginx
191+
tags: |
192+
type=semver,pattern={{version}}
193+
type=edge
194+
type=ref,event=pr
195+
type=ref,event=branch,suffix=-rc,enable=${{ startsWith(github.ref, 'refs/heads/release') }}
196+
197+
- name: Build NKG Docker Image
189198
uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 # v4.1.1
190199
with:
191200
file: build/Dockerfile
192-
tags: ${{ steps.meta.outputs.tags }}
201+
tags: ${{ steps.nkg-meta.outputs.tags }}
193202
context: "."
194203
target: goreleaser
195204
load: true
196-
cache-from: type=gha
197-
cache-to: type=gha,mode=max
205+
cache-from: type=gha,scope=nkg
206+
cache-to: type=gha,scope=nkg,mode=max
207+
pull: true
208+
209+
- name: Build NGINX Docker Image
210+
uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 # v4.1.1
211+
with:
212+
file: build/Dockerfile.nginx
213+
tags: ${{ steps.nginx-meta.outputs.tags }}
214+
context: "."
215+
load: true
216+
cache-from: type=gha,scope=nginx
217+
cache-to: type=gha,scope=nginx,mode=max
198218
pull: true
219+
build-args: |
220+
NJS_DIR=internal/mode/static/nginx/modules/src
221+
NGINX_CONF_DIR=internal/mode/static/nginx/conf
199222
200223
- name: Deploy Kubernetes
201224
id: k8s
202225
run: |
203226
kube_config=${{ github.workspace }}/deploy/helm-chart/kube-${{ github.run_id }}-helm
204227
make create-kind-cluster KIND_KUBE_CONFIG=${kube_config}
205228
echo "KUBECONFIG=${kube_config}" >> "$GITHUB_ENV"
206-
kind load docker-image ${{ steps.meta.outputs.tags }}
229+
kind load docker-image ${{ steps.nkg-meta.outputs.tags }} ${{ steps.nginx-meta.outputs.tags }}
207230
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.7.1/standard-install.yaml
208231
kubectl wait --for=condition=complete job/gateway-api-admission-patch job/gateway-api-admission -n gateway-system
209232
210233
- name: Install Chart
211234
run: >
212235
helm install
213-
helm-$(echo ${{ steps.meta.outputs.tags }} | cut -d ":" -f 2)
236+
helm-$(echo ${{ steps.nkg-meta.outputs.tags }} | cut -d ":" -f 2)
214237
.
215238
--wait
216239
--create-namespace
217-
--set controller.image.repository=$(echo ${{ steps.meta.outputs.tags }} | cut -d ":" -f 1)
218-
--set controller.image.tag=$(echo ${{ steps.meta.outputs.tags }} | cut -d ":" -f 2)
240+
--set nginxGateway.image.repository=$(echo ${{ steps.nkg-meta.outputs.tags }} | cut -d ":" -f 1)
241+
--set nginxGateway.image.tag=$(echo ${{ steps.nkg-meta.outputs.tags }} | cut -d ":" -f 2)
242+
--set nginxGateway.image.pullPolicy=Never
243+
--set nginx.image.repository=$(echo ${{ steps.nginx-meta.outputs.tags }} | cut -d ":" -f 1)
244+
--set nginx.image.tag=$(echo ${{ steps.nginx-meta.outputs.tags }} | cut -d ":" -f 2)
245+
--set nginx.image.pullPolicy=Never
219246
--set service.type=NodePort
220247
-n nginx-gateway
221248
working-directory: ${{ github.workspace }}/deploy/helm-chart
@@ -224,6 +251,10 @@ jobs:
224251
name: Build Image
225252
runs-on: ubuntu-22.04
226253
needs: [vars, binary]
254+
strategy:
255+
fail-fast: false
256+
matrix:
257+
container: [nkg, nginx]
227258
permissions:
228259
contents: read # for docker/build-push-action to read repo content
229260
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
@@ -259,7 +290,7 @@ jobs:
259290
uses: docker/metadata-action@818d4b7b91585d195f67373fd9cb0332e31a7175 # v4.6.0
260291
with:
261292
images: |
262-
name=ghcr.io/nginxinc/nginx-kubernetes-gateway
293+
name=ghcr.io/nginxinc/nginx-kubernetes-gateway${{ matrix.container == 'nginx' && '/nginx' || '' }}
263294
tags: |
264295
type=semver,pattern={{version}}
265296
type=edge
@@ -269,42 +300,46 @@ jobs:
269300
- name: Build Docker Image
270301
uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 # v4.1.1
271302
with:
272-
file: build/Dockerfile
303+
file: ${{ matrix.container == 'nginx' && 'build/Dockerfile.nginx' || 'build/Dockerfile' }}
273304
context: "."
274-
target: goreleaser
305+
target: ${{ matrix.container == 'nkg' && 'goreleaser' || '' }}
275306
tags: ${{ steps.meta.outputs.tags }}
276307
labels: ${{ steps.meta.outputs.labels }}
277308
load: ${{ github.event_name == 'pull_request' }}
278309
push: ${{ github.event_name != 'pull_request' }}
279310
platforms: ${{ github.event_name != 'pull_request' && env.platforms || '' }}
280-
cache-from: type=gha
281-
cache-to: type=gha,mode=max
311+
cache-from: type=gha,scope=${{ matrix.container }}
312+
cache-to: type=gha,scope=${{ matrix.container }},mode=max
282313
pull: true
283314
no-cache: ${{ github.event_name != 'pull_request' }}
284315
sbom: ${{ github.event_name != 'pull_request' }}
285316
provenance: false
317+
build-args: |
318+
NJS_DIR=internal/mode/static/nginx/modules/src
319+
NGINX_CONF_DIR=internal/mode/static/nginx/conf
320+
286321
287322
- name: Run Trivy vulnerability scanner
288323
uses: aquasecurity/trivy-action@41f05d9ecffa2ed3f1580af306000f734b733e54 # 0.11.2
289324
continue-on-error: true
290325
with:
291-
image-ref: ghcr.io/nginxinc/nginx-kubernetes-gateway:${{ steps.meta.outputs.version }}
326+
image-ref: ghcr.io/nginxinc/nginx-kubernetes-gateway${{ matrix.container == 'nginx' && '/nginx' || '' }}:${{ steps.meta.outputs.version }}
292327
format: "sarif"
293-
output: "trivy-results-nginx-kubernetes-gateway.sarif"
328+
output: trivy-results-nginx-kubernetes-gateway${{ matrix.container == 'nginx' && '-nginx' || '' }}.sarif
294329
ignore-unfixed: "true"
295330

296331
- name: Upload Trivy scan results to GitHub Security tab
297332
uses: github/codeql-action/upload-sarif@5b6282e01c62d02e720b81eb8a51204f527c3624 # v2.21.3
298333
continue-on-error: true
299334
with:
300-
sarif_file: "trivy-results-nginx-kubernetes-gateway.sarif"
335+
sarif_file: trivy-results-nginx-kubernetes-gateway${{ matrix.container == 'nginx' && '-nginx' || '' }}.sarif
301336

302337
- name: Upload Scan Results
303338
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
304339
continue-on-error: true
305340
with:
306-
name: "trivy-results-nginx-kubernetes-gateway.sarif"
307-
path: "trivy-results-nginx-kubernetes-gateway.sarif"
341+
name: trivy-results-nginx-kubernetes-gateway${{ matrix.container == 'nginx' && '-nginx' || '' }}.sarif
342+
path: trivy-results-nginx-kubernetes-gateway${{ matrix.container == 'nginx' && '-nginx' || '' }}.sarif
308343
if: always()
309344

310345
publish-helm:

.github/workflows/conformance.yml

+36-10
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ jobs:
4242
- name: Docker Buildx
4343
uses: docker/setup-buildx-action@4c0219f9ac95b02789c1075625400b2acbff50b1 # v2.9.1
4444

45-
- name: Docker meta
46-
id: meta
45+
- name: NKG Docker meta
46+
id: nkg-meta
4747
uses: docker/metadata-action@818d4b7b91585d195f67373fd9cb0332e31a7175 # v4.6.0
4848
with:
4949
images: |
@@ -54,10 +54,22 @@ jobs:
5454
type=ref,event=pr
5555
type=ref,event=branch,suffix=-rc,enable=${{ startsWith(github.ref, 'refs/heads/release') }}
5656
57+
- name: NGINX Docker meta
58+
id: nginx-meta
59+
uses: docker/metadata-action@818d4b7b91585d195f67373fd9cb0332e31a7175 # v4.6.0
60+
with:
61+
images: |
62+
name=ghcr.io/nginxinc/nginx-kubernetes-gateway/nginx
63+
tags: |
64+
type=semver,pattern={{version}}
65+
type=edge
66+
type=ref,event=pr
67+
type=ref,event=branch,suffix=-rc,enable=${{ startsWith(github.ref, 'refs/heads/release') }}
68+
5769
- name: Prepare NKG files
5870
run: |
59-
nkg_prefix=$(echo ${{ steps.meta.outputs.tags }} | cut -d ":" -f 1)
60-
nkg_tag=$(echo ${{ steps.meta.outputs.tags }} | cut -d ":" -f 2)
71+
nkg_prefix=$(echo ${{ steps.nkg-meta.outputs.tags }} | cut -d ":" -f 1)
72+
nkg_tag=$(echo ${{ steps.nkg-meta.outputs.tags }} | cut -d ":" -f 2)
6173
make update-nkg-manifest NKG_PREFIX=${nkg_prefix} NKG_TAG=${nkg_tag}
6274
working-directory: ./conformance
6375

@@ -67,17 +79,31 @@ jobs:
6779
version: latest
6880
args: build --snapshot --clean
6981

70-
- name: Build Docker Image
82+
- name: Build NKG Docker Image
7183
uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 # v4.1.1
7284
with:
7385
file: build/Dockerfile
74-
tags: ${{ steps.meta.outputs.tags }}
86+
tags: ${{ steps.nkg-meta.outputs.tags }}
7587
context: "."
7688
target: goreleaser
7789
load: true
78-
cache-from: type=gha
79-
cache-to: type=gha,mode=max
90+
cache-from: type=gha,scope=nkg
91+
cache-to: type=gha,scope=nkg,mode=max
92+
pull: true
93+
94+
- name: Build NGINX Docker Image
95+
uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 # v4.1.1
96+
with:
97+
file: build/Dockerfile.nginx
98+
tags: ${{ steps.nginx-meta.outputs.tags }}
99+
context: "."
100+
load: true
101+
cache-from: type=gha,scope=nginx
102+
cache-to: type=gha,scope=nginx,mode=max
80103
pull: true
104+
build-args: |
105+
NJS_DIR=internal/mode/static/nginx/modules/src
106+
NGINX_CONF_DIR=internal/mode/static/nginx/conf
81107
82108
- name: Update Go Modules
83109
if: ${{ github.event_name == 'schedule' }}
@@ -104,8 +130,8 @@ jobs:
104130

105131
- name: Setup conformance tests
106132
run: |
107-
nkg_prefix=$(echo ${{ steps.meta.outputs.tags }} | cut -d ":" -f 1)
108-
nkg_tag=$(echo ${{ steps.meta.outputs.tags }} | cut -d ":" -f 2)
133+
nkg_prefix=$(echo ${{ steps.nkg-meta.outputs.tags }} | cut -d ":" -f 1)
134+
nkg_tag=$(echo ${{ steps.nkg-meta.outputs.tags }} | cut -d ":" -f 2)
109135
if [ ${{ github.event_name }} == "schedule" ]; then
110136
export GW_API_VERSION=main
111137
fi

Makefile

+20-12
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ VERSION = edge
33
GIT_COMMIT = $(shell git rev-parse HEAD || echo "unknown")
44
DATE = $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
55
MANIFEST_DIR = $(shell pwd)/deploy/manifests
6-
NJS_DIR = $(shell pwd)/internal/mode/static/nginx/modules/src
76
CHART_DIR = $(shell pwd)/deploy/helm-chart
7+
NGINX_CONF_DIR = internal/mode/static/nginx/conf
8+
NJS_DIR = internal/mode/static/nginx/modules/src
89

910
# go build flags - should not be overridden by the user
1011
GO_LINKER_FlAGS_VARS = -X main.version=${VERSION} -X main.commit=${GIT_COMMIT} -X main.date=${DATE}
1112
GO_LINKER_FLAGS_OPTIMIZATIONS = -s -w
1213
GO_LINKER_FLAGS = $(GO_LINKER_FLAGS_OPTIMIZATIONS) $(GO_LINKER_FlAGS_VARS)
1314

1415
# variables that can be overridden by the user
15-
PREFIX ?= nginx-kubernetes-gateway## The name of the image. For example, nginx-kubernetes-gateway
16+
PREFIX ?= nginx-kubernetes-gateway## The name of the NKG image. For example, nginx-kubernetes-gateway
17+
NGINX_PREFIX ?= $(PREFIX)/nginx## The name of the nginx image. For example: nginx-kubernetes-gateway/nginx
1618
TAG ?= $(VERSION:v%=%)## The tag of the image. For example, 0.3.0
1719
TARGET ?= local## The target of the build. Possible values: local and container
1820
KIND_KUBE_CONFIG=$${HOME}/.kube/kind/config## The location of the kind kubeconfig
@@ -21,19 +23,29 @@ ARCH ?= amd64## The architecture of the image and/or binary. For example: amd64
2123
override HELM_TEMPLATE_COMMON_ARGS += --set creator=template --set nameOverride=nginx-gateway## The common options for the Helm template command.
2224
override HELM_TEMPLATE_EXTRA_ARGS_FOR_ALL_MANIFESTS_FILE += --set service.create=false## The options to be passed to the full Helm templating command only.
2325
override DOCKER_BUILD_OPTIONS += --build-arg VERSION=$(VERSION) --build-arg GIT_COMMIT=$(GIT_COMMIT) --build-arg DATE=$(DATE)## The options for the docker build command. For example, --pull
24-
26+
override NGINX_DOCKER_BUILD_OPTIONS += --build-arg NJS_DIR=$(NJS_DIR) --build-arg NGINX_CONF_DIR=$(NGINX_CONF_DIR)
2527
.DEFAULT_GOAL := help
2628

2729
.PHONY: help
2830
help: Makefile ## Display this help
2931
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "; printf "Usage:\n\n make \033[36m<target>\033[0m [VARIABLE=value...]\n\nTargets:\n\n"}; {printf " \033[36m%-30s\033[0m %s\n", $$1, $$2}'
3032
@grep -E '^(override )?[a-zA-Z_-]+ \??\+?= .*?## .*$$' $< | sort | awk 'BEGIN {FS = " \\??\\+?= .*?## "; printf "\nVariables:\n\n"}; {gsub(/override /, "", $$1); printf " \033[36m%-30s\033[0m %s\n", $$1, $$2}'
3133

32-
.PHONY: container
33-
container: build ## Build the container
34-
@docker -v || (code=$$?; printf "\033[0;31mError\033[0m: there was a problem with Docker\n"; exit $$code)
34+
.PHONY: build-images
35+
build-images: build-nkg-image build-nginx-image ## Build the NKG and nginx docker images
36+
37+
.PHONY: build-nkg-image
38+
build-nkg-image: check-for-docker build ## Build the NKG docker image
3539
docker build --platform linux/$(ARCH) $(strip $(DOCKER_BUILD_OPTIONS)) --target $(strip $(TARGET)) -f build/Dockerfile -t $(strip $(PREFIX)):$(strip $(TAG)) .
3640

41+
.PHONY: build-nginx-image
42+
build-nginx-image: check-for-docker ## Build the custom nginx image
43+
docker build --platform linux/$(ARCH) $(strip $(NGINX_DOCKER_BUILD_OPTIONS)) -f build/Dockerfile.nginx -t $(strip $(NGINX_PREFIX)):$(strip $(TAG)) .
44+
45+
.PHONY: check-for-docker
46+
check-for-docker: ## Check if Docker is installed
47+
@docker -v || (code=$$?; printf "\033[0;31mError\033[0m: there was a problem with Docker\n"; exit $$code)
48+
3749
.PHONY: build
3850
build: ## Build the binary
3951
ifeq (${TARGET},local)
@@ -103,10 +115,6 @@ njs-unit-test: ## Run unit tests for the njs httpmatches module
103115
node:18 \
104116
/bin/bash -c "npm install && npm test && npm run clean"
105117

106-
.PHONY: generate-njs-yaml
107-
generate-njs-yaml: ## Generate the njs-modules ConfigMap
108-
kubectl create configmap njs-modules --from-file=$(NJS_DIR)/httpmatches.js --dry-run=client --output=yaml > $(strip $(MANIFEST_DIR))/njs-modules.yaml
109-
110118
.PHONY: lint-helm
111119
lint-helm: ## Run the helm chart linter
112120
helm lint $(CHART_DIR)
@@ -116,8 +124,8 @@ debug-build: GO_LINKER_FLAGS=$(GO_LINKER_FlAGS_VARS)
116124
debug-build: ADDITIONAL_GO_BUILD_FLAGS=-gcflags "all=-N -l"
117125
debug-build: build ## Build binary with debug info, symbols, and no optimizations
118126

119-
.PHONY: debug-container
120-
debug-container: debug-build container ## Build container with debug binary
127+
.PHONY: build-nkg-debug-image
128+
build-nkg-debug-image: debug-build build-nkg-image ## Build NKG image with debug binary
121129

122130
.PHONY: generate-manifests
123131
generate-manifests: ## Generate manifests using Helm.

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Learn about our [design principles](/docs/developer/design-principles.md) and [a
2121

2222
1. [Quick Start on a kind cluster](docs/running-on-kind.md).
2323
2. [Install](docs/installation.md) NGINX Kubernetes Gateway.
24-
3. [Build](docs/building-the-image.md) an NGINX Kubernetes Gateway container image from source or use a pre-built image
24+
3. [Build](docs/building-the-images.md) an NGINX Kubernetes Gateway container image from source or use a pre-built image
2525
available
2626
on [GitHub Container Registry](https://github.com/nginxinc/nginx-kubernetes-gateway/pkgs/container/nginx-kubernetes-gateway).
2727
4. Deploy various [examples](examples).
@@ -59,7 +59,7 @@ The following table lists the software versions NGINX Kubernetes Gateway support
5959

6060
| NGINX Kubernetes Gateway | Gateway API | Kubernetes | NGINX OSS |
6161
|--------------------------|-------------|------------|-----------|
62-
| Edge | 0.7.1 | 1.21+ | 1.25.x * |
62+
| Edge | 0.7.1 | 1.21+ | 1.25.1 |
6363
| 0.5.0 | 0.7.1 | 1.21+ | 1.25.x * |
6464
| 0.4.0 | 0.7.1 | 1.21+ | 1.25.x * |
6565
| 0.3.0 | 0.6.2 | 1.21+ | 1.23.x * |

build/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ COPY dist/gateway_linux_$TARGETARCH*/gateway /usr/bin/
3131
RUN setcap 'cap_kill=+ep' /usr/bin/gateway
3232

3333
FROM scratch as common
34-
USER 1001:1001
34+
USER 102:1001
3535
ENTRYPOINT [ "/usr/bin/gateway" ]
3636

3737
FROM common as container

build/Dockerfile.nginx

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# syntax=docker/dockerfile:1.4
2+
FROM nginx:1.25.1-alpine
3+
4+
ARG NJS_DIR
5+
ARG NGINX_CONF_DIR
6+
7+
RUN apk update && apk add --no-cache libcap \
8+
&& mkdir -p /var/lib/nginx /usr/lib/nginx/modules \
9+
&& setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx \
10+
&& setcap -v 'cap_net_bind_service=+ep' /usr/sbin/nginx \
11+
&& apk del libcap
12+
13+
COPY ${NJS_DIR}/httpmatches.js /usr/lib/nginx/modules/njs/httpmatches.js
14+
COPY ${NGINX_CONF_DIR}/nginx.conf /etc/nginx/nginx.conf
15+
16+
RUN chown -R 101:1001 /etc/nginx /var/cache/nginx /var/lib/nginx
17+
18+
USER 101:1001

0 commit comments

Comments
 (0)