Not able to run express-gateway on heroku - failed to bind to $PORT - express-gateway

I'm trying to have the basic example express-gateway running on Heroku
http:
port: ${HTTP_PORT:-8080}
hostname: ${HOST:-localhost}
admin:
port: 9876
host: localhost
apiEndpoints:
api:
host: 'localhost'
paths: '/ip'
serviceEndpoints:
httpbin:
url: 'https://httpbin.org'
...
Looking the logs I got the following error:
2021-02-15T18:53:09.947569+00:00 heroku[web.1]: Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch
Any ideas?

I managed to fix that with the following config:
http:
port: ${PORT:-8080}
https:
port: ${PORT:-8081}
admin:
port: 9876
apiEndpoints:
api:
paths: '/ip'
So the default port variable is called PORT and by removing all references to localhost 🙌

Related

502 Server Error when creating http load balancer in google/GKE

My application consists of play web application deployed using GKE. The application was running fine (using Deployment and Loadbalancer service) and then I decided to use Ingress. I made the following changes which has made the application unreachable. I get 502 error when I try to connect with the application using ingress IP.
The application is of kind Deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 2
selector:
matchLabels:
app: webapp
It has a service associated with it
apiVersion: v1
kind: Service
metadata:
name: webapp-service
spec:
selector:
app: webapp
ports:
- protocol: TCP
port: 9000 #this service is reachable at this port
targetPort: 9000 #this service will forward the request to correspoding nodes of the service at this port
#type: LoadBalancer
type: NodePort
Then I applied the following file to create ingress
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: webapp-https-loadbalancer-ingress
annotations:
kubernetes.io/ingress.class: "gce"
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: webapp-service
servicePort: 9000
I can see that there is an IP address (which is also reachable from outside) when I run
kubectl describe ingress webapp-https-loadbalancer-ingress
Name: webapp-https-loadbalancer-ingress
Namespace: default
Address: 3x.yyy.zzz.pq
Default backend: default-http-backend:80 (10.88.0.5:8080)
Rules:
Host Path Backends
---- ---- --------
*
... Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 10m loadbalancer-controller default/webapp-https-loadbalancer-ingress
Normal CREATE 9m10s loadbalancer-controller ip: 3x.yyy.zzz.pq
But I am not able to reach the application using https://3x.yyy.zzz.pq. I haven't yet associated the domain with the IP. I tried to connect using curl and got error- 502 bad gateway error
curl -v 3x.xxx.xxx.xxx
* Expire in 0 ms for 6 (transfer 0x55d4c5258f90)
* Trying 3x.xxx.xxx.xxx...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x55d4c5258f90)
* Connected to 3x.xxx.xxx.xxx (3x.xxx.xxx.xxx) port 80 (#0)
> GET / HTTP/1.1
> Host: 3x.xxx.xxx.xxx
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 502 Bad Gateway
< Content-Type: text/html; charset=UTF-8
< Referrer-Policy: no-referrer
< Content-Length: 332
< Date: Tue, 22 Dec 2020 22:27:23 GMT
<
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>502 Server Error</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Server Error</h1>
<h2>The server encountered a temporary error and could not complete your request.<p>Please try again in 30 seconds.</h2>
<h2></h2>
</body></html>
* Connection #0 to host 3x.xxx.xxx.xxx left intact
The issue was that the load balancer's IP was not in the allowed-hosts list in the server. As temporary fix, I used wild card in server configuration to allow traffic from all hosts. I am still figuring out how to restrict it to load balancer's internal IPs

Nginx Ingress rules not find a match

I'm struggling on an Ingress configuration in yaml because the pattern matching seems not to work.
I would like the frontend-lb ClusterIP Service for the frontend deployment to respond to any of these:
https://example.com
https://example.com/home
https://example.com/login
... any other without /api/
And the backend-lb ClusterIP Service for the backend deployment to respond to any of these:
https://example.com/api/...
The yaml for the ingress rules is the following:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls:
- secretName: example-com-tls
hosts:
- example.com
rules:
- host: example.com
http:
paths:
- path: /api
backend:
serviceName: gateway-lb
servicePort: 80
- path: /
backend:
serviceName: frontend-lb
servicePort: 80
The result is that every backend url is recognized as a frontend url and gives back 404 Not Found.
I've tried many other regexp and also I've tried to exclude /api in the frontend path (!?api).* but with no success.
UPDATE:
What I see with the logs it's like in the backend the url path gets blanked because if I call:
https://example.com/api/javalin-api-gateway/login
I get this error:
Not found. Request is below context-path (context-path:
'/javalin-api-gateway')
While when I call the frontend with a specific url path:
https://example.com/home
The /home controller is effectively called (it doesn't get blanked).
If I call the backend service directly (if the service is a LoadBalancer) with the same url:
http://192.168.64.17:31186/javalin-api-gateway/login
I get the right response, signal that the backend part is working properly.
How is possible that only the backend service doesn't receive the complete path?

Hosting django on aks behind nginx-ingress

I am trying to host a django website on Azure kubernetes service behide nginx-ingress, and I would like my django web show under a path.
e.g. when access the default admin site, I would like to access it at http://example.com/django/admin instead of http://example.com/admin
I tried the configure below, when I access http://example.com/django/admin it will forward me to http://example.com/admin and show me 404 error from default ingress backend, as I set django debug to ture I assume this mean ingress did not send my request to my django service
# path example
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- backend:
serviceName: django-service
servicePort: 80
path: /django(/|$)(.*)
so I try to curl -I -k http://example.com/django/admin, and it show something like below
HTTP/1.1 301 Moved Permanently
Server: openresty/1.15.8.2
Date: Wed, 06 Nov 2019 04:14:14 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Location: /admin/
The same thing happen to any valid page in the site, if I curl -I -k http://example.com/django/any_valid_page it show below:
HTTP/1.1 301 Moved Permanently
Server: openresty/1.15.8.2
Date: Wed, 06 Nov 2019 04:14:14 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Location: /any_valid_page/
I wonder it is caused by I am doing the test with the default django development web server? (i.e. python manage.py runserver)
If I try to host it at root like below, everything is fine...
# root example
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- backend:
serviceName: django-service
servicePort: 80
path: /
Trying adding this
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
ingress.kubernetes.io/rewrite-target: /django
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- backend:
serviceName: django-service
servicePort: 80
path: /django
Starting in Version 0.22.0, ingress definitions using the annotation
nginx.ingress.kubernetes.io/rewrite-target are not backwards
compatible with previous versions. In Version 0.22.0 and beyond, any
substrings within the request URI that need to be passed to the
rewritten path must explicitly be defined in a capture group. So make
sure you have right version.
When using SSL offloading outside of cluster it may be useful to enforce a redirect to HTTPS even when there is no TLS certificate available. This can be achieved by using the nginx.ingress.kubernetes.io/force-ssl-redirect: "true" annotation in the particular resource.
I think your Ingress configuration file should look like this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: example.com
http:
paths:
- path: /django(/|$)(.*)
backend:
serviceName: django-service
servicePort: 80
If you get 404 error, there is possible solution:
Please change https to http in the curl command?:
curl --resolve your-host:80:xx.xxx.xx.xxx http://my-host:80
To get the IP from kubectl get ing command, it is necessary to
enable the reporting Ingress status feature. Take a look on: reporting-ingress-status.
There is the default server in the Ingress controller. It returns
the Not Found page with the 404 status code for all requests for
domains for which there are no Ingress rules defined. Those requests
are not shown in the access log.
Since you're getting a 404, this means that the host header of your
requests doesn't match with the host field in the Ingress resource.
To set the host header in curl, please see previous curl
commands. Optionally, you can also do:
curl http://<ip> -H "host: example.com"
Please take a look on ngnix-ingress, server-side-https-enforcement-nginx.
This is a problem from Django's side. Whenever the admin is not logged in, the /django/admin results in a redirect to /admin/. In this case, if you just replace /django/admin/ with /django/admin/ in the browser URL field it will work and open django admin login.
So basically Django's built-in redirect conflicts with the Ingress's rewrite module.

Ingress Controller on Minikube not routing correctly flask POST request

I have a simple Flask app. It worked fine when I connected to it via port-forwarding to send the HTTP Post request directly to the Service.
from flask import Flask, request
import redis
from rq import Queue
from worker import job_worker
UPLOAD_FOLDER = './uploads/'
app = Flask(__name__)
r = redis.Redis()
q = Queue(connection = r)
#app.route('/', methods=['POST'])
def upload():
scale = int(request.form['scale'])
q.enqueue(job_worker, scale)
return ""
if __name__ == "__main__":
app.run()
I also have a simple index.html file in an nginx container which is served at port 80. It does an ajax POST request to "/upload". Which if you look at the ingress controller, should convert that to a port 5000 request and strip away the "upload"
The flask app gets served at port 5000
Here is the ingress controller:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: emoji-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /upload
backend:
serviceName: emoji-backend
servicePort: 5000
- path: /
backend:
serviceName: emoji-frontend
servicePort: 80
And for completeness, the emoji-backend service:
apiVersion: v1
kind: Service
metadata:
name: emoji-backend
labels:
app: emoji-backend
tier: backend
spec:
type: LoadBalancer
ports:
- port: 5000
selector:
app: emoji-backend
tier: backend
I get a 502 bad gateway without really any indication except the ingress log does say this:
2019/09/29 21:41:04 [error] 2021#2021: *78651 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.64.1, server: _,
request: "POST /upload HTTP/2.0", upstream: "http://172.17.0.4:5000/", host: "192.168.64.5", referrer: "https://192.168.64.5/"
"http://172.17.0.4:5000/" is the correct endpoint and port for the emoji-backend service.
Adding the following line fixed it:
app.run(debug=True,host='0.0.0.0',port=5000)
However, it took me a while to figure that out because at first when I tried it my docker image was not updating when I re-deployed.

Django, Nginx, uwsgi, Kubernetes: unable to connect nginx to uwsgi stream

I have a django container and an Ngix container. they work fine with docker-compose, and now im trying to use the images with kubernetes. Everything works fine, except the fact that the nginx container cannot connect to the uwsgi upstream. No response is being returned.
Here are my configuration:
# Nginx congifuration
upstream django {
server admin-api-app:8001 max_fails=20 fail_timeout=10s; # for a web port socket (we'll use this first),
}
server {
# the port your site will be served on
listen 80;
server_name server localhost my-website-domain.de;
charset utf-8;
location / {
uwsgi_pass django;
include /etc/nginx/uwsgi_params;
}
}
# Uwsgi file
module = site_module.wsgi
master = true
processes = 5
socket = :8001
enable-threads = true
vacuum=True
# Kubernetes
# Backend Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: container-backend
labels:
app: backend
spec:
replicas: 1
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: container-backend
image: my-djangoimage:latest
command: ["./docker/entrypoint.sh"]
ports:
- containerPort: 8001
name: uwsgi
- name: nginx
image: my-nginx-image:latest
imagePullPolicy: Always
ports:
- containerPort: 80
name: http
---
# Backend Service
kind: Service
apiVersion: v1
metadata:
name: admin-api-app
spec:
selector:
app: backend
ports:
- port: 80
targetPort: 80
type: LoadBalancer
You probably need to change host in your django upstream because, as far as I understand, you want to connect to your django app located in the same pod where is nginx so try to change:
server admin-api-app:8001 max_fails=20 fail_timeout=10s;
to
server localhost:8001 max_fails=20 fail_timeout=10s;
Edit:
To make it work you need to change socket to http-socket but it can be painful/pointless as described here: Should I have separate containers for Flask, uWSGI, and nginx?