I am working on an application that is hosted on Kubernetes on google cloud, it uses Ingress to manage the routing to each service, then inside each service there's some extra routing using golang.
My problem is: when I try sending an API request to /api/auth/healthz
I am expecting to reach the statusHandler()
function, instead I always hit the requestHandler()
function, even though I have an http.HandleFunc in there: http.HandleFunc("/healthz", statusHandler)
Here's my code in go
func main() {
flag.Parse()
// initialize the "database"
err := store.Initialise()
if err != nil {
glog.Error(err)
glog.Error("Store failed to initialise")
}
defer store.Uninitialise()
// Initialize the default TTL for JWT access tokens to 1 day
tokenTTL = 24 * time.Hour
glog.Infof("JWT access tokens time-to-live set to: %s", tokenTTL.String())
http.HandleFunc("/healthz", statusHandler)
http.HandleFunc("/", requestHandler)
glog.Infof("Authentication service started on port 8080...\n")
http.ListenAndServe(":8080", nil)
}
func statusHandler(w http.ResponseWriter, r *http.Request) {
glog.Infof("Reached the status handler")
statusObj := serviceStatus{
GitSHA: os.Getenv("GIT_SHA"),
BuildTimestamp: os.Getenv("BUILD_TIMESTAMP"),
DeployEnv: os.Getenv("DEPLOY_ENV"),
}
statusJSON, _ := json.Marshal(statusObj)
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, "%s", statusJSON)
}
func requestHandler(w http.ResponseWriter, r *http.Request) {
glog.Infof("Reached the request handler")
...
}
And here's my Ingress code:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
# https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/annotations.md
annotations:
kubernetes.io/ingress.class: nginx
certmanager.k8s.io/cluster-issuer: letsencrypt-staging
nginx.ingress.kubernetes.io/enable-cors: "false"
nginx.ingress.kubernetes.io/cors-enable-origin: "*"
nginx.ingress.kubernetes.io/auth-type: "basic"
nginx.ingress.kubernetes.io/auth-secret: "ci-ingress-auth"
nginx.ingress.kubernetes.io/proxy-body-size: "4g"
name: ci-api-ingress
namespace: ci
spec:
rules:
- host: ci.omitted.io
http:
paths:
- backend:
serviceName: auth-service << This is the service I am working on
servicePort: 8080
path: /api/auth
- backend:
serviceName: data-import-service
servicePort: 8080
path: /api/data-import
- backend:
serviceName: log-service
servicePort: 8080
path: /api/log
- backend:
serviceName: project-service
servicePort: 8080
path: /api/project
- backend:
serviceName: orchestration-service
servicePort: 8080
path: /api/orchestration
- backend:
serviceName: public
servicePort: 80
path: /
Based on Kubernetes' documentation that should work as what they have in there is something similar to my implementation:
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
duration := time.Now().Sub(started)
if duration.Seconds() > 10 {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
} else {
w.WriteHeader(200)
w.Write([]byte("ok"))
}
})
based on your ingress rules the path for requests should be: /api/auth/healthz
not /api/auth-service/healthz
The ingress path /api/auth/
is kept in the request uri passed to the application server.
Adding rewrite-target
to the ingress annotations will ensure the path is passed to your underlying server as you'd expect e.g. as /<path>
rather than /api/auth/<path>
. Inspecting the application server logs should show this is the case.
ingress.kubernetes.io/rewrite-target: /