Kubernets - nginx: [emerg] host not found in upstream - docker

I'm attempting to expose a service from my home network via a sub-url from my domain. This service is running on a Kubernetes (K3s) cluster on a Ubuntu 20.04 server. However there's an issue with proxy_pass in that pod that is giving me the `nginx: [emerg] host not found in upstream in 'webhook.lan' error.
Here's an overview of my setup
.lan DNS entry is setup on my router. I have many other services accessible (locally) that use .lan. I'm confident this is setup correctly.
My Ubuntu server is set up to use my router (192.168.0.1) as a DNS server. I can run dig webhook.lan and it's successful. I can also run wget webhook.lan and it downloads the HTML. So, the server can definitely resolve the address. resolvectl status has 192.168.0.1 as the Current DNS Server. Everything seems okay.
The K3s cluster is using an NGINX Ingress controller. I have two hosts setup. A wildcard host for *.lan addresses, along with a host to handle requests from my sub-url sub.url.com. I'm not attempting any sort of communication within the cluster, as I don't see the point (they're both already exposed separately).
Each container has it's own NGINX configuration to handle / proxy requests.
The .lan config looks like this:
server {
server_name service-a.lan;
listen 80;
listen [::]:80;
location / {
include proxy_params;
proxy_pass http://192.168.0.27:8000;
}
}
server {
server_name service-b.lan;
listen 80;
listen [::]:80;
location / {
include proxy_params;
proxy_pass http://192.168.0.27:3000;
}
}
That's just a sample. There's many more. They all work perfect on my local network.
Here's the config for the pod that is handling requests from the web. I currently use it to serve static files, along with a few basic HTML pages. This is where the proxy_pass issue is coming up:
upstream webhook {
server webhook.lan;
}
server {
server_name genweb;
listen 80;
listen [::]:80;
#basic welcome screen
location / {
alias /srv/web/;
try_files $uri $uri/ /index.html;
}
#static images, music, etc.
location /static {
alias /app/static;
autoindex on;
}
#problem area!!
location /webhook {
#resolver 127.0.0.11;
resolver 192.168.0.1;
include proxy_params;
proxy_pass http://webhook;
}
}
Originally, I just imported proxy_params and used the proxy_pass directive. Since that's all I was doing with the other container. Then, I tried using upstream{}; tried resolver 192.168.0.1 to make it resolve to the router. Finally, tried resolver 127.0.0.11 because that's the docker localhost (apparently).
No dice. Also, I should mention that the pod/deployment doesn't even start. It got that error from kubectl logs. It tries to restart a bunch of times, and eventually gives a status of Failure (I think), and never starts. Commenting out the webhook location in the NGINX config allows it to start right up.
I'm guessing the issue comes down to NGINX in the sub-url's container not being able to resolve 192.168.0.1, but I cannot figure out why.
Here's the Dockerfile (which I basically copied from the other running app) and Service/Deployment yaml file, in case that's where the problem lies:
Dockerfile
FROM nginx:1.21.4
RUN rm /etc/nginx/conf.d/default.conf
COPY web-nginx.conf /etc/nginx/conf.d
COPY proxy_params /etc/nginx
RUN mkdir -p /srv/web/static
COPY index.html /srv/web
ADD static /srv/web/static
web-service.yml
apiVersion: v1
kind: Service
metadata:
name: gen-web-service
spec:
selector:
app: genweb
ports:
- protocol: TCP
port: 8080
targetPort: 80
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gen-web-deployment
labels:
app: genweb
spec:
replicas: 1
selector:
matchLabels:
app: genweb
template:
metadata:
labels:
app: genweb
spec:
containers:
- name: genweb
image: genweb:v1.0.16
imagePullPolicy: Never
ports:
- containerPort: 80
volumeMounts:
- name: gen-web-volume
mountPath: /app/static
volumes:
- name: gen-web-volume
hostPath:
path: /srv/web/static

Related

How do I configure nginx to serve files with a prefix?

I am trying to host static files in kubernetes with an nginx container, and expose them on a private network with istio.
I want the root of my server to exist at site.com/foo, as I have other applications existing at site.com/bar, site.com/boo, etc.
My istio virtual service configuration:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: cluster-foo
namespace: namespace
spec:
gateways:
- cluster-gateway
hosts:
- site.com
http:
- match:
- name: http
port: 80
uri:
prefix: /foo
route:
- destination:
host: app.namespace.svc.cluster.local
port:
number: 80
All of my static files exist in the directory /data on my nginx container. My nginx config:
events {}
http {
server {
root /data;
location /foo {
autoindex on;
}
}
}
Applying this virtual service, and a kube deployment that runs an nginx container with this config, I get a nginx server at site.com/foo that serves all of the static files in /data on the container. All good. The problem is that the autoindexing that nginx does, does not respect the prefix /foo. So all the file links that nginx indexes at site.com/foo, look like this:
site.com/page.html, rather than site.com/foo/page.html. Furthermore, when I put site.com/foo/page.html in my browser manually, site.com/foo/page.html is displayed correctly, So I know that nginx is serving it in the correct location, just the links that it is indexing are incorrect.
Is there any way to configure nginx autoindex with a prefix?

Traefik - proxy to backend for angular application

I have set up a proxy with Nginx which is as follows
server {
listen 80;
server_name localhost;
location /api {
proxy_pass https://api.mydomain.com/;
}
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
my Dockerfile
FROM node:12-alpine as builder
WORKDIR /workspace
COPY ./package.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/www /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
This works fine But wants to replace Nginx with Traefik for the above proxy settings. Any help would be much appreciated since I'm very new to traefik.
With Traefik 2+, you need to configure 2 routers:
- One for the API
- One for the webapp
For the API proxy, you will have a rule like:
rule = "Host(`example.com`) && Path(`/api`)"
And the webapp will juste have the host as rule
rule = "Host(`example.com`)"
For kubernetes, you can do it in an ingress like that:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: simpleingressroute
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`example.com`) && PathPrefix(`/api`)
kind: Rule
services:
- name: mywebapp-svc
port: 80
- match: Host(`example.com`)
Kind: Rule
services:
- name: myapi-svc
port: 80
If the API is not inside the kubernetes cluster, you can define the rule tu use an externalService like that:
---
apiVersion: v1
kind: Service
metadata:
name: myapi-svc
namespace: default
spec:
externalName: api.mydomain.com
type: ExternalName
if you want to step up from that manual configuration, you may use Traefik as described here. Watch how he uses docker labels to define how to route HTTP traffic.
I personally use caddy docker proxy in docker (swarm, but not required) which I find easier to understand and use

Setting the correct hostname in Kubernetes/Nginx for the client

I'm using a dockerized microservice architecture running on Kubernetes with Nginx, and am encountering an issue with hostnames. How do you correctly add the hostname to Kubernetes (or perhaps Nginx too)?
The problem: When microservice A called admin tries to talk to microservice B called session, admin logs the following error and session is not reached:
{ Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's
altnames: Host: session. is not in the cert's altnames: DNS:*.example.com, example.com
at Object.checkServerIdentity (tls.js:225:17)
at TLSSocket.onConnectSecure (_tls_wrap.js:1051:27)
at TLSSocket.emit (events.js:160:13)
at TLSSocket._finishInit (_tls_wrap.js:638:8)
reason: 'Host: session. is not in the cert\'s altnames:
DNS:*.example.com, example.com',
host: 'session',
cert:
{ subject: { OU: 'Domain Control Validated', CN:
'*.example.com' },
issuer: ...
In response to this error, I tried to update the hostname in the kubernetes config yaml file unsuccessfully (based on this). See the added hostname below.
apiVersion: apps/v1
kind: Deployment
metadata:
name: session
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: session
component: demo
template:
metadata:
labels:
app: session
component: demo
spec:
hostname: session.example.com . ----> added host name here
imagePullSecrets:
- name: docker-secret
containers:
- name: session
...
However, when I try to apply this updated config file in Kubernetes, an error emerges that I cannot use a period. If I cannot use a period, and the hostname is *.example.com (i.e. session.example.com), where/how should the hostname be updated.
The Deployment "session" is invalid: spec.template.spec.hostname:
Invalid value: "session.example.com": a DNS-1123 label must
consist of lower case alphanumeric characters or '-', and must start and
end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex
used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')
Meanwhile, the server name in the nginx config file is indeed updated with session.example.com.
upstream session {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name "session.example.com"; ---> updated for hostname
ssl_certificate /etc/ssl/nginx/certificate.pem;
ssl_certificate_key /etc/ssl/nginx/key.pem;
location / {
proxy_pass http://session/;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name "session.example.com"; ---> updated for hostname
return 301 https://$host$request_uri;
}
How do you suggest fixing this? My goal is for admin to successfully communicate with session.
You can use kubernetes own dns.
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/
So you can access your pod using pod dns;
When enabled, pods are assigned a DNS A record in the form of
“pod-ip-address.my-namespace.pod.cluster.local”
With service you can use
my-svc.my-namespace.svc.cluster.local

NGINX + Kubernetes Ingress + Google Cloud Load Balancing

I'm currently trying to configure a Kubernetes environment.
Currently I have a Docker image which serves my compiled Angular application via Nginx.
The default.conf of the Nginx is:
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}
}
Ingress configuration (mapped to Google Cloud Load Balancer since I'm using Kubernetes Cluster)
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: fullstack-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /app
backend:
serviceName: frontend-main
servicePort: 80
- path: /app/*
backend:
serviceName: frontend-main
servicePort: 80
- path: /core
backend:
serviceName: backend-core
servicePort: 80
Whenever I request my front-end using http://[loadbalancerip]/app/, all request sent to my angular docker are GET /app/inline.js when they should be GET /inline.js in order to work.
I tried directly in my cluster with curl and the file works. I only need to get rid of the "/app". How can I do that? Is that in the Nginx configuration or in the Ingress file?
I would rather fix it in the Ingress file, since I will probably have the same issue when I will be deploying my back-end which are .NET Core and Kestrel.

Why am I not seeing higher concurrent requests when increasing nodes/pods on a simple NGINX docker cluster?

In trying to achieve a highly available web server set up, I ran a load test against an extremely simple NGINX docker container serving a single static file.
At first, I tried with a single node (n1-standard-4) & single pod cluster, to benchmark how much one "unit" could do. This single node/pod setup could cope with around 20k concurrent requests before starting to timeout for some requests/drop in throughput.
I then added another node of the same machine type and scaled the pods to two replicas. Once I confirmed both pods/nodes were up, I ran the test again with 20k. Performance was good so I bumped up to 40k - expecting that I would see similar results to the 20k on the 1 node/pod setup.
However, the performance was very bad, never with requests per second jumping violently between 15k and 30k.
I tried the same test again with 4 nodes/pods and saw similar, if not slightly worse results.
My question(s) are:
Am I wrong to think that my concurrent requests should scale linearly this way with GKE/containers/kubernetes?
What am I missing in order to achieve the desired results of being able to cope with N concurrent users with M nodes/pods in the cluster?
EDIT: I also do not believe it is an issue with the load testing tool - as I am using an external paid service that claims to be able to simulate up to 100k concurrent requests.
EDIT 2: Here's some more information about the setup:
Dockerfile:
FROM nginx
ADD nginx.conf /etc/nginx/nginx.conf
ADD index.html /usr/share/nginx/html/index.html
ADD data.json /usr/share/nginx/html/data.json
nginx.conf:
user nginx;
worker_processes 4;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 4096;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log off;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
server {
listen 80 default_server;
listen [::]:80 default_server;
root /usr/share/nginx/html;
# Serve the index.html page
location / {
try_files /index.html =404;
}
}
include /etc/nginx/conf.d/*.conf;
}
index.html:
<head><title>Load Test Page</title></head><body><h3>Load Test!</h3></body>
I used the "LoadBalancer" service, which I believe setups a Google Cloud Balancer (I confirmed this and saw the nodes being added to it). Here are the files below I used with kubernetes to manage the cluster.
rc.yml:
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx-rc
labels:
name: nginx-rc
spec:
replicas: 2
selector:
name: nginx-server
template:
metadata:
labels:
name: nginx-server
name: nginx-server
spec:
containers:
- name: nginx-server
image: [[ my image ]]
ports:
- containerPort: 80
hostPort: 80
services.yml:
apiVersion: v1
kind: Service
metadata:
labels:
name: nginx-lb
name: nginx-lb
spec:
ports:
- port: 80
targetPort: 80
selector:
name: nginx-server
type: LoadBalancer
If you want to run a preconfigured load test, you can check out the instructions for the kubernetes scale-demo that shows how to serve 1 million QPS of static files using nginx, which is very similar to your test setup.
Also keep in mind that a single VM (regardless of the number of pods running on it) will have a limit to how much bandwidth it can use and how many packets-per-second it can process. To serve more load, you should create a larger VM type or add additional VMs.

Resources