I am working to get an existing application consisting of a bunch of stateless, scalable microservices (and of course also a few stateful ones serving as backends) running on Docker Swarm and Kubernetes. Changing the code of the application is mostly out of the question, so I have to make some existing mechanisms that we have for, e.g., service discovery, work in the Swarm and K8s contexts.
One thing that helped me a lot with getting things up and running with Docker Swarm was the template feature of Swarm's "service create" command (https://docs.docker.com/engine/reference/commandline/service_create/#create-services-using-templates), where I can do something like
-e my_env_var=foo{{.Task.Slot}}
Inside each container that is part of my Swarm service, this will set the env var my_env_var to a value of the form fooX, where "X" is the container's "slot number". To grasp what a slot number is, consider a service with N instances (i.e., scale=N). Each container occupies one slot, and the slots are numbered from 1 to N.
That way, I can get a ID inside my container that is unique among all currently alive containers of my service, but at the same time, it is NOT totally random. If I scale a service from, e.g., 1 to 5, the five containers in my service will get the slots 1, 2, 3, 4, and 5. If I scale it down to, e.g., 3, two containers will be stopped (e.g., 2 and 4, leaving me with 1, 3, and 5). But if I scale it up again to 5, the slot numbers will (in general) again be 1 to 5 (and even if they were, e.g., 2-6, that is still better than being totally random).
This has proven to be very useful for Swarm-enabling my application and I am desperately looking for something similar in K8s (in particular in the context of K8s deployments, which I am using for our stateless microservices as they seem to be the most suitable K8s concept). I found the possibility to pass the pod name into the container with
env:
- name: metadata_name
valueFrom:
fieldRef:
fieldPath: metadata.name
Alas, the name of a container is a) rather lengthy b) random (i.e., scaling down and up will NOT reuse names), e.g. the pods of a deployment named foo-deployment will be named something like
foo-deployment-64db944b84-bwrxx
foo-deployment-64db944b84-5jf7c
etc. So as far as I understand, the last five characters are guaranteed by K8s to be unique among all active pods of a deployment, but they are NOT reused (rare collisions nonwithstanding) when scaling up and down.
Is there any mechanism that corresponds to Swarm's "slot" concept?
Regards PalatinateJ
11 Months late, but here is the solution:
To get stable container (pod) names within K8S you must use a StatefulSet
. StatefulSets
are designed for applications that must maintain state, however, if you aren't using K8S volumes for state (keeping them ephemeral) you can use StatefulSets
without an issue. There is a simple process of converting your Deployment
to a StatefulSet
:
apiVersion:
to apps/v1
kind:
to StatefulSet
spec:
add the selector:
tag. This tag will contain everything you use to select the appropriate service. In addition you must ensure that any items you have under spec:template:metadata:labels
match those under spec:selector:matchLabels
(This will make more sense in the provided example)strategy
in a Deployment
, change this to updateStrategy
. For future reference, if you do not update this, you will end up with both a StatefulSet
and a ReplicaSet
being deployed as K8S is trying to fill the strategy
and your StatefulSet
requirements.After applying these changes, you will have your StatefulSet
deployed. How do we get task slots from this? The hostname.
Since K8S is maintaining stable pod names, you will have names such as:
pod/mypod-0 1/1 Running 0 10m
pod/mypod-1 1/1 Running 0 9m
when running kubetctl
. After that, it is a simple matter of parsing the number out of your pod name.
Below is the YAML for a StatefulSet
:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: myStatefulSetName
labels:
app: SomeLabel1
label2: SomeLabel2
spec:
replicas: 100
selector:
matchLabels:
app: SomeLabel1
label2: SomeLabel2
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
app: SomeLabel1
label2: SomeLabel2
spec:
containers:
- name: myPodName
image: myPod:latest
imagePullPolicy: Always
ports:
- name: myPodPort
containerPort: 8080
The differences become apparent on a equivalent Deployment
:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: myDeploymentName
labels:
app: SomeLabel1
label2: SomeLabel2
spec:
replicas: 100
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: SomeLabel1
label2: SomeLabel2
spec:
containers:
- name: myPodName
image: myPod:latest
imagePullPolicy: Always
ports:
- name: myPodPort
containerPort: 8080