I’ve encountered a rather rookie issue with k8s. I’m particularly new to k8s, and setup staging and production services / deployments for a Django – celery – redis application within a cluster. However. In my excitement that I actually managed to get something working, I didn’t check to think if it was 100% correct.
Essentially, I’ve noticed that the pre-production Django application doesn’t care which celery deployment it references when dispatching a periodic task. It might go to staging, it might try the pre-production deployment. THIS IS BAD.
So, I’ve been looking at labels and selectors, as well as namespaces.
However, I should probably slow down – my first question, how would I use something native to k8s to run different environments of deployments, such that they are all isolated from each other. So the pre-production Django application can only talk to the pre-production celery-worker or pre-production celery-beat deployments…
*My answer I feel is to use labels and selectors? But … is it best to use namespaces?
Any pro-guidance around this subject would be amazing.
2
Answers
You could go with a namespace per environment and then make sure that when calling another service you always use the dns shorthand of a a single service name.
This will, however, not limit a specific service to call another in another namespace/environment. If you are the sole developer, or trust all other this might be fine.
An other alternative it so run different clusters but run them identically. When your number of deployments grows you’ll likely end up with this anyway.
This way you can have one namespace per team, or per domain, and it will look identical in all environments.
Aside from creating a new cluster per environment, you can separate deployments by namespace or just different stacks in one namespace. The last case (the one you use now) is the easiest to shoot in the leg since you have to change a lot of thing to make it isolated. At very least you need a different set of resource names (both in manifests and configuration) and labels to match.
Out of the three methods I think namespace separation is the easiest; it works on DNS-based service discovery. Suppose you have a copy of redis and your application in two different namespaces (
dev
andprod
for example). Both instances of the app are configured to use redis atredis:6379
. When they call DNS to resolve the hostname,CoreDNS
(the internal DNS service) would respond with different answers depending on which namespace the request came from. And so your app indev
namespace will get an IP-address of redis indev
namespace, and the app fromprod
namespace will contact redis fromprod
namespace. This method does not apply any restriction, if you wish you can specifically make it so that both apps use the same copy of redis. For that, instead ofredis:6379
you have to use a full service DNS name, like this:redis.<namespace>.svc.cluster.local:6379
Regardless of whatever method you choose to go with, I strongly recommend you to get familiar with Kustomize, Helm, or both. These tools are to help you avoid duplicating resource manifests and thus spend less time spawning and managing instances. I will give you a minimal example for
Kustomize
because it is built inkubectl
. Consider the following directory structure:bases
is where you keep a non-specific skeleton of your application. This isn’t meant to be deployed, like a class it has to be instantiated.instances
is where you describe various instances of your application. Instances are meant to be deployed.bases/my-app/kustomization.yml:
instances/prod/kustomization.yml:
instances/test/kustomization.yml:
Now if you go into
instances
directory and usekubectl apply -k prod
you will deploydeployment.yml
toprod
namespace. Similarlykubectl apply -k test
will deploy it to thetest
namespace.And this is how you can create several identical copies of your application stack in different namespaces. It should be fairly isolated unless some shared resources from other namespaces involved. In other words, if you deploy each component (such as the database) per namespace and those components are not configured to access components from other namespaces – it will work as expected.
I encourage you to read more on Kustomize and Helm, since namespace overriding is just a basic thing these can do. You can manage labels, configuration, names, stack components and more.