All the tutorials I've found on the Internet demonstrate how to deploy a web site/app in an nginx container and set up an Ingress to proxy related requests to that container.
That seems kind of redundant to me. Why not to place the content on a volume, mount it to ingress-nginx-controller and serve static assets from there. It seems to be better from performance perspective: no additional proxy and no additional container (with nginx) that consumes compute resources.
An Ingress resource is an abstract concept that was designed to route HTTP/S requests to kubernetes services. It defines a limited subset of that functionality to remain generic and be implemented by many types of ingress controller.
ingress-nginx is only one implementation of an ingress controller, that happens to also be good at serving static content. HAProxy, Traefik or the AWS lb are at the other end of the scale.
An nginx based ingress controller could add custom annotations to support something custom like what you propose, but I imagine you would have a hard time arguing that into the project. Internally, they already add a second nginx instance for the default backend.
If one proxy hop is a critical differentiator in performance then you would probably get more benefit hosting the static content on an edge CDN/device. Another alternative is to not rely on an Ingress and publish the service directly to the outside world.
You need a container to run the application. Ingress service is just used to access that application outside the Kubernetes cluster. You cannot just keep the content in a volume and mount it to the ingress controller. One Ingress controller is used to route requests for multiple applications.
Related
I am digging into nginx, Kubernetes and SSL as I am trying to deploy my web app onto Kubernetes via Google Kubernetes Engine (GKE).
A little about my project:
My frontend is a React web application
My backend is a Node Express API service
They are on separate repos (ex: frontend repo and backend repo)
I am planning on using nginx to serve my frontend and proxy requests to my backend.
My question is...
Would it be possible to have both my backend and frontend within the same Kubernetes config yaml file, and if possible, is this best practice to do so?
How I see it is...
In my nginx.conf file, I will have a server section that has a proxy_pass to something like localhost:8000, which is the port of the backend. I would assume this would work if both are within the same container and local network.
I would then probably have a load balancer that points to nginx.
Any help or advice is appreciated! I hope this makes sense (still very new to this)!
Let's start from the beginning. There is too much to say on this topic to put everything in the comments, so let's move it to an answer. If something below isn't entirely clear, don't hesitate to ask.
First of all you need to start from your application architecture and try to determine how it is supposed to work, what part should be able to communicate with another one.
Microservices approach in design of applications is entire broad topic and would deserve rather separate article than attempt to explain it in a single answer. But in a nutshell, all it is about is decoupling the application into separate parts where each of them have a distinct functionality and can be developed, deployed and updated separately. Those elements can be closely related, they can even totally depend on one another but at the same time they are separate entities.
Kubernetes by its very nature encourages you to follow the above approach. If you read more about Pods, you will see that they are perfectly made for this purpose, for being a kind of wrapper for single microservice. This is a simplification to some extent, but I believe it reflects the essence of the matter. In real production environment you can have a set of Pods managed by higher abstraction such as Deployment, but note that even if you have 3 or more replicas of a single Pod it still represents the same single microservice, only scaled horizontaly e.g. to be able to handle bigger load or more requests.
Separate sets of Pods representing different microservices can communicate with each other and be exposed to external world thanks to Services.
As you can read in kubernetes docs, an Ingress is:
An API object that manages external access to the services in a
cluster, typically HTTP. Ingress may provide load balancing, SSL
termination and name-based virtual hosting.
Ingress exposes HTTP and HTTPS routes from outside the cluster to
services within the cluster. Traffic routing is controlled by rules
defined on the Ingress resource.
internet
|
[ Ingress ]
--|-----|--
[ Services ]
You should ask yourself if you really want to expose to the external world both your frontend and backend Pods. Typically you don't want to do it. Backend by its very definition should act as a... well... as a backend of your app :) It is not supposed to be reachable directly by external users but only via frontend part of the application so it shouldn't be exposed via Ingress. Only after the frontend part of your app receives a request from external user, it makes its own request to the backend, retrives some data processed by the backend app and then passes it to the end user. User doesn't make direct requests to your backend app.
You can expose via Ingress different parts of your application using different paths (like in the example given in another answer), which can be backed by different microservices running in different sets of Pods but still they should be frontend parts of your app, NOT backend.
Backend Pods is something that you usually expose only within your kubernetes cluster, to make it available to other components of your app. For that purpose simple Service of type ClusterIP (which is by the way the default type, automatically chosen when the type: field is not specified) is the way to go.
I would encourage you to read more about different Service types and what they are used for.
You may also want to take a look at the following articles. I believe they will make the whole concept even clearer:
Connecting Applications with Services
Connect a Front End to a Back End Using a Service
As to merging different kubernetes objects definitions to a single yaml file like in this example, where you can see Service and Deployment defined in a single yaml file and separated with --- is just a convention and I wouldn't pay too much attention to it. If it makes your work more convenient, you can use it.
As to your additional question in the comment:
I'm curious if having two load balancers would also work as well or
if Ingress is suited better for this scenario?
I hope after reading the whole answer it's already much clearer. Note that typically an Ingress also uses loadbalancer under the hood. If you only want to expose externally your frontend app without using different paths which are backed by separate microservices, you may not even need an Ingress. LoadBalancer Service would be totally enough for what you want to accomplish. Remember that you also use it to expose your app to the external world. If you expose something only within your cluster, that shouldn't be reachable from outside, use simple ClusterIP Service instead.
LoadBalancer: Exposes the Service externally using a cloud provider’s
load balancer. NodePort and ClusterIP Services, to which the external
load balancer routes, are automatically created.
I hope this answered your question.
I would say use nginx ingress controller as an implementation of ingress. And you can have ingress rules for both frontend and backend in the same ingress.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /frontend
backend:
serviceName: frontend
servicePort: 80
path: /backend
backend:
serviceName: backend
servicePort: 80
I am a kubernetes newbie, and no matter how much I read about it I cannot get my head around this issue.
I have a simple deployment , which is creating a pod with a not so complex app.
I know what an ingress and an ingress controller is doing ,but as far as I understand it is not required for me to expose my pod-app externally. Only a LoadBalancer service should be enough.
I do not have need of more than one rule for traffic routing.
Am I wrong about that?
Traditionally, you would create a LoadBalancer service for each service you want to expose externally. This can get rather expensive. Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint.
Also load balancer provisioning takes time and works only in supported cloud providers such as AWS, GCP etc.
One more thing to consider is the need of L4(TCP/UDP) layer routing because kubernetes Ingress API is primarily L7 layer but some of the ingress controllers such as traefik, nginx supports L4 layer(TCP/UDP) along with L7 layer(HTTP) routing.
So answer to your question is it depends based on your environment and use cases.
Ingress and IngressControllers are used for traffic routing at layer 7 i.e., if your backends talk L7 protocols like HTTP, GRPC etc. You can route requests based on request path to different backend services using Ingress.
If your app doesn't operate at Layer 7, you might not need an Ingress.
Another question you could ask yourself if your migrating your app from non-kubernetes environment to kuberneters is - are you using a reverse proxy like nginx already? If so, you might want to use Ingress. I say might because it is not necessary. You can achieve the same affect of using an Ingress by running the nginx container as a pod, writing the nginx.conf yourself and making it available externally(using a LoadBalancer service for example). Instead by using IngressController you need not maintain the nginx pod or write nginx.conf. You can instead express the same configuration as Ingress resource which is much simpler.
Ingress is needed if you want to expose your service to external. Especially for layer 7 in OSI model (HTTP transport). Ingress also provide the mechanism to enable TLS support in your load balancer. Traffic routing is controlled by rules defined on the Ingress resource. An Ingress can be configured to give Services externally-reachable URLs, load balance traffic, terminate SSL / TLS, and offer name based virtual hosting. An Ingress controller is responsible for fulfilling the Ingress, usually with a load balancer, though it may also configure your edge router or additional frontends to help handle the traffic.
By default Ingress controller depends on which kind of cloud provider you're using, or if you're using on premise you'll need to set it up based on what you need. In one cluster, you can create multiple Ingress controller as well. There's many kinds of Ingress controller, you can take a look on this article.
I think this article about Load Balancer and Ingress may also help.
jwilder/nginx-proxy has 1.3K STARS and 10M+ PULLS on Docker Hub. And Watch 262, Star 7701, Fork 1546 on GitHub.
https://github.com/jwilder/nginx-proxy
kubernetes/ingress-nginx has 13 stars on kubeapps.com (one of the most starred charts) and Watch 137, Star 1596, Fork 918 on GitHub.
https://github.com/kubernetes/ingress-nginx
What's the difference between the two?
When would you use one over the other?
That is 2 different applications, but both are based on Nginx and have the similar function.
Nginx-proxy by jwilder is a proxy server for Docker containers which includes docker-gen to generate a configuration for Nginx automatically. You can use it for SSL termination, load balancing etc. But it will be hard to manage nginx-proxy in Kubernetes.
Ingress-nginx by Kubernetes is Ingress Controller which provides Ingress functional for your Kubernetes cluster. It also can do SSL termination and some other things, but it was created especially for use in Kubernetes, and it's abstractions. That means you can create the Ingress object which includes Services as backends and use selectors etc.
So, if you are using Kubernetes, Ingress-nginx is the best choice. If you are using just Docker containers without an orchestrator, use Nginx-proxy.
In Kubernetes, the user decides what set of features to make public and in what manner configuration should be implemented. Help may come from enterprise
vendors, like Ingress controller provided by GKE, and from community/private-held sides covering the uncommon approach to similar aspects of delivery services.
In this particular case, we have two nginx driven solutions.
An Ingress controller is full-featured and mostly recognized as default traffic controller to use with GKE.
Ingress can be configured to give services externally-reachable URLs, load balance traffic, terminate SSL, and offer name-based virtual hosting.
Users request ingress by POSTing the Ingress resource to the API server. An Ingress controller is responsible for fulfilling the Ingress, usually with a loadbalancer, though it may also configure your edge router or additional frontends to help handle the traffic in an HA manner. Nowadays Ingress is strictly cloud-oriented regarding configuration, it uses ConfigMap style and kubedns to register services.
If you know how old-fashioned virtual hosts work and you are not interested in every new cloud-oriented aspects of web services delivery, jwilder/nginx-proxy may be interesting for you. In this solution, nginx can act as a proxy to control internal hosting and world Web traffic with IPv6 ready endpoints. jwilder/nginx is not especially dedicated to clouds but also works fine there. If you are interested in having free Let's Encrypt certificates, there is an out-of-a-box support for it. Some users find it interesting that Basic Authentication is available, and the SSL is more flexible to configure for advanced purposed.
I want to use Nginx to load balance a kubernetes deployment.
The deployment is part of a service. It contains pod which can be scaled. I want NGINX to be part of the service without being scaled.
I know that I can use NGINX as an external load balancer by configuring it with external dns resolver. With that it can get the IP of the pods scaled and apply its own load balanced rules.
Is it possible to make NGINX part of the service? Then how to do the DNS resolution to the pods? In that case, which pods the service name is refered to?
I would like to avoid the declaration of two services to keep a single definition of the setup which represent a microservice.
More generally, how can I declare in a same service:
a unit which is scaled
a backend, not scaled
a database, not scaled
Thanks all
You can't have NGINX as part of the service. Service doesn't contain any pods, deployment does. It sounds like you want an ingress service, that would be a load balancer any and all services on the cluster
EDIT:
An ingress controller in essence is a deployment of NGINX exposed publicly as a service acting as a load balancer/fan out. The deployment scans your cluster for ingress resources and reconfigures NGINX to forward requests to appropriate services.
Typically people deploy a single controller that acts as the load balancer for all of your microservices. You can fan out based on DNS, URI, other headers and so on. You can also do TLS termination, add basic auth to specific services, it's even possible to splice in NGINX config snippets directly into the ingress resources.
We make use of Ingress to create HTTPS load balancers that forward directly to our (typically nodejs) services. However, recently we have wanted more control of traffic in front of nodejs which the Google load balancer doesn't provide.
Standardised, custom error pages
Standard rewrite rules (e.g redirect http to https)
Decouple pod readinessProbes from load balancer health checks (so we can still serve custom error pages when there are no healthy pods).
We use nginx in other parts of our stack so this seems like a good choice, and I have seen several examples of nginx being used to front services in Kubernetes, typically in one of two configurations.
An nginx container in every pod forwarding traffic directly to the application on localhost.
A separate nginx Deployment & Service, scaled independently and forwarding traffic to the appropriate Kubernetes Service.
What are the pros/cons of each method and how should I determine which one is most appropriate for our use case?
Following on from Vincent H, I'd suggest pipelining the Google HTTPS Load Balancer to an nginx ingress controller.
As you've mentioned, this can scale independently; have it's own health checks; and you can standardise your error pages.
We've achieved this by having a single kubernetes.io/ingress.class: "gce" ingress object, which has a default backend of our nginx ingress controller. All our other ingress objects are annotated with kubernetes.io/ingress.class: "nginx".
We're using the controller documented here: https://github.com/kubernetes/ingress/tree/master/controllers/nginx. With a custom /etc/nginx/template/nginx.tmpl allowing complete control over the ingress.
For complete transparency, we haven't (yet) setup custom error pages in the nginx controller, however the documentation appears straight forward.
One of the requirements listed is that you want to decouple the pod readinessProbes so you can serve custom error pages. If you are adding a nginx container to every pod this isn't possible. Then the pod will be restarted when one of the containers in the pod fails to adhere to the liveness/readiness probes. Personally I also prefer to decouple as much as you can so you can scale independent pods, assign custom machine types if needed, and it saves you some resources by starting only the amount of nginx instances you really need (mostly memory).