Pod¶
A pod is essentially a collection of containers. It is the most fundamental computation unit that can be provisioned.
Create a Pod¶
To create a new pod in the cluster:
import * as kplus from 'cdk8s-plus-33';
import * as k from 'cdk8s';
const app = new k.App();
const chart = new k.Chart(app, 'Chart');
const pod = new kplus.Pod(chart, 'Pod');
Adding Containers¶
Every Pod must have at least one container before you synthesize the application.
You can add containers either during, or post instantiation:
const pod = new kplus.Pod(chart, 'Pod', {
containers: [{ image: 'image' }],
});
pod.addContainer({ image: 'another-image' });
Adding Volumes¶
Volumes can be added to pod definition either during, or post instantiation:
import * as kplus from 'cdk8s-plus-33';
const data1 = kplus.Volume.fromEmptyDir('data1');
const data2 = kplus.Volume.fromEmptyDir('data2');
const pod = new kplus.Pod(chart, 'Pod', {
volumes: [data1],
});
pod.addVolume(data2);
Note that adding a volume to a pod doesn’t actually make the volume available to its containers. For that, you also need to mount the volume onto a container.
import * as kplus from 'cdk8s-plus-33';
const data = kplus.Volume.fromEmptyDir('data');
const pod = new kplus.Pod(chart, 'Pod');
const container = pod.addContainer({ image: 'image' });
// mount the volume onto the container. this is actually enough, and you
// don't need to explicitly add the volume to the pod -- cdk8s+ will do that for you.
container.mount('/data', data);
Applying a restart policy¶
A restart policy can only be specified at instantiation time:
import * as kplus from 'cdk8s-plus-33';
const app = new k.App();
const chart = new k.Chart(app, 'Chart');
const pod = new kplus.Pod(chart, 'Pod', {
restartPolicy: kplus.RestartPolicy.NEVER,
});
Assigning a ServiceAccount¶
A service account can only be specified at instantiation time:
import * as kplus from 'cdk8s-plus-33';
const app = new k.App();
const chart = new k.Chart(app, 'Chart');
const pod = new kplus.Pod(chart, 'Pod', {
serviceAccount: kplus.ServiceAccount.fromServiceAccountName('aws'),
});
Select pods¶
Pods can also be selected by various mechanisms. These selections are often used in other cdk8s+ API’s, such as pod selection during scheduling.
Select pods with labels¶
Selects all pods that have the app=store label.
import * as kplus from 'cdk8s-plus-33';
const pods = kplus.Pods.select(this, 'Store', { labels: { app: 'store' }});
Select pods with expressions¶
Selects all pods that have the app label, regardless of the value.
import * as kplus from 'cdk8s-plus-33';
const pods = kplus.Pods.select(this, 'App', {
expressions: [kplus.LabelExpression.exists('app')]
});
Select pods with labels in a particular namespace¶
Pod selection can also be scoped to specific namespaces.
This is done using the namespaces property, which can accept any namespace selector.
For example, select all pods that have the app=store label in the backoffice namespace:
import * as kplus from 'cdk8s-plus-33';
const pods = kplus.Pods.select(this, 'Pods', {
labels: { app: 'store' },
namespaces: kplus.Namespaces.select(this, 'Backoffice', { names: ['backoffice'] }),
});
Scheduling¶
Kubernetes offers a few properties for controlling how pods are scheduled onto nodes.
CDK8s+ collapses all these different features and exposes them under one unified API we refer to as Scheduling. This API is available on a Pod via the scheduling property.
The same API is also available on all workload resources (i.e
Deployment,StatefulSet,Job,DaemonSet).
Scheduling is comprised of two different types:
Node Selection¶
Node selection is the process of directly selecting which nodes should pods be scheduled on, by selecting node attributes.
Node Assignment¶
You can statically assign a pod to a specific node, by using the node’s name.
import * as k from 'cdk8s';
import * as kplus from 'cdk8s-plus-33';
const app = new k.App();
const chart = new k.Chart(app, 'Chart');
const redis = new kplus.Pod(chart, 'Redis', {
containers: [{ image: 'redis' }]
});
redis.scheduling.assign(kplus.Node.named('node1'));
This example will cause the Redis pod to be scheduled on a node with name node1.
Node Attraction¶
Pods can attract themselves to nodes. As opposed to an assignment, an attraction can be made to a set of nodes, specified by node labels. An attraction can be either required, or preferred.
import * as k from 'cdk8s';
import * as kplus from 'cdk8s-plus-33';
const app = new k.App();
const chart = new k.Chart(app, 'Chart');
const redis = new kplus.Pod(chart, 'Redis', {
containers: [{ image: 'redis' }]
});
const highMemoryNodes = kplus.Node.labeled(kplus.NodeLabelQuery.is('memory', 'high'));
redis.scheduling.attract(highMemoryNodes);
This example will require the Redis pod be scheduled on a
node that has the memory=high label. To request a preference, specify the weight property:
redis.scheduling.attract(highMemoryNodes, { weight: 50 });
Node Toleration¶
While attractions is a property of Pods that attracts them to a set of nodes, taints are the opposite – they allow a node to repel a set of pods.
Tolerations are applied to pods, and allow (but do not require) the pods to schedule onto nodes with matching taints.
Taints and tolerations work together to ensure that pods are not scheduled onto inappropriate nodes. One or more taints are applied to a node; this marks that the node should not accept any pods that do not tolerate the taints.
A toleration can be made to a set of nodes, specified by node taints.
import * as k from 'cdk8s';
import * as kplus from 'cdk8s-plus-33';
const app = new k.App();
const chart = new k.Chart(app, 'Chart');
const redis = new kplus.Pod(chart, 'Redis', {
containers: [{ image: 'redis' }]
});
const node = kplus.Node.tainted(kplus.NodeTaintQuery.is('key1', 'value1', {
effect: kplus.TainEffect.NO_SCHEDULE
}));
redis.scheduling.tolerate(node);
This example says the the Redis pod is able to tolerate
nodes tainted with key1=value1:NoSchedule.
Pod Selection¶
Pod selection is the process of selecting which nodes should pods be scheduled on, by looking at which other pods are already scheduled on those nodes.
The API’s presented here interact either with specific pods,
i.e instances of Pod or Workload (e.g Deployment, StatefulSet, Job, …), or with a group of pods, i.e ones that are identified by a set of selectors.
Pod Co-location¶
Pod co-location is a way to tell the scheduler to place a pod in a topology that already hosts other pods that meet some criteria.
A topology is expressed via the topology property, and
represents a failure domain that Kubernetes is aware of. It can be one of:
kplus.Topology.HOSTNAME: A single node. This is the default value.kplus.Topology.ZONE: Multiple nodes in a single availability zone.kplus.Topology.REGION: Multiple nodes in a single region.kplus.Topology.custom: Any other configurable value.
Similarly to node attractions, co-location can also be either required, or preferred.
import * as k from 'cdk8s';
import * as kplus from 'cdk8s-plus-33';
const app = new k.App();
const chart = new k.Chart(app, 'Chart');
const redis = new kplus.Pod(chart, 'Redis', {
containers: [{ image: 'redis' }]
});
const web = new kplus.Pod(chart, 'Web', {
containers: [{ image: 'web' }]
});
web.scheduling.colocate(redis);
This example will require the Web pod to be scheduled on the same node as
the Redis pod. To request a preference, specify the weight property:
web.scheduling.colocate(redis, { weight: 50 });
To use a different topology, specify the topology property:
web.scheduling.colocate(redis, { weight: 50, topology: kplus.Topology.ZONE });
This scenario configures co-location between two pods that are defined and managed in the same cdk8s application. You can also co-locate with an externally managed pod, by specifying a pod selector:
const redis = kplus.Pods.select(this, 'Cache', {
labels: { app: 'cache' },
});
web.scheduling.colocate(redis);
This will co-locate the Web pod with pods that have the app=cache label, regardless of
whether they are defined in the cdk8s app or not.
Under the hood: Co-location with managed pods will automatically extract its labels and form the appropriate pod selector.
Pod Separation¶
Pod separation (e.g anti co-location) is a way to tell the scheduler not to place a pod in a topology that already hosts other pods that meet some criteria. Similarly to co-location, separation can also be either required, or preferred.
import * as k from 'cdk8s';
import * as kplus from 'cdk8s-plus-33';
const app = new k.App();
const chart = new k.Chart(app, 'Chart');
const redis = new kplus.Pod(chart, 'Redis', {
containers: [{ image: 'redis' }]
});
const web = new kplus.Pod(chart, 'Web', {
containers: [{ image: 'web' }]
});
web.scheduling.separate(redis);
This example will require the Web pod to not be scheduled on the same
node (because the default value of the topology is HOSTNAME) as the Redis pod.
To request a preference, specify the weight property:
web.scheduling.separate(redis, { weight: 50 });
To use a different topology, specify the topology property:
web.scheduling.separate(redis, { weight: 50, topology: kplus.Topology.ZONE });
This scenario configures separation between two pods that are defined and managed in the same cdk8s application. You can also separate with an externally managed pod, by specifying a pod selector:
const redis = kplus.Pods.select(this, 'Cache', {
labels: { app: 'cache' },
});
web.scheduling.separate(redis);
This will separate the Web pod from pods that have the app=cache label, regardless of
whether they are defined in the cdk8s app or not.
Under the hood: Co-location with managed pods will automatically extract its labels and form the appropriate pod selector.
Connections¶
Pod connections offer a simplified API to automatically create network policies on
both ends of a connection. Accessing this API is done via the connections property
of a specific Pod, which serves as one end of the connection.
The other end is a network policy peer.
Allow To¶
To allow connections from a Pod to a peer:
import * as k from 'cdk8s';
import * as kplus from 'cdk8s-plus-33';
const app = new k.App();
const chart = new k.Chart(app, 'Chart');
const redis = new kplus.Pod(chart, 'Redis', {
containers: [{ image: 'redis', portNumber: 6379 }]
});
const web = new kplus.Pod(chart, 'Web', {
containers: [{ image: 'web' }]
});
web.connections.allowTo(redis);
This will allow the web pod to connect to the redis port on port 6379,
and will allow the redis pod to accept connections from the web pod on port 6379.
Note that the port is not specified in the allowTo invocation, it is automatically
extracted from the redis pod definition.
You can also pass ports explicitly, overriding this extraction:
web.connections.allowTo(redis, { ports: [kplus.NetworkPolicyPort.tcp(4444)] });
Allow From¶
To allow connections from a peer to a Pod:
import * as k from 'cdk8s';
import * as kplus from 'cdk8s-plus-33';
const app = new k.App();
const chart = new k.Chart(app, 'Chart');
const redis = new kplus.Pod(chart, 'Redis', {
containers: [{ image: 'redis', portNumber: 6379 }]
});
const web = new kplus.Pod(chart, 'Web', {
containers: [{ image: 'web' }]
});
redis.connections.allowFrom(web);
This will allow the redis pod to accept connection from the web pod on port 6379,
and will allow the web pod to connect to the redis pod on port 6379.
Note that the port is not specified in the allowFrom invocation, it is automatically
extracted from the redis pod definition.
Isolation¶
By default, the allowXXX methods will create both an egress policy on the initiating end,
as well as an ingress policy on the accepting end of the connection.
This means that, if no other policies apply, both sides of the connection will be isolated,
each in the corresponding direction. In the above example, if the redis pod
needs to be accessed from any pod other than web, an explicit policy needs to be applied,
because the default non-isolated behavior is now disabled.
To control the isolation this API incurs, you can use the isolation option. It accepts two
possible values:
PodConnectionsIsolation.POD: Only isolate the pod that offers theconnectionsAPI.PodConnectionsIsolation.PEER: Only isolate the peer the pod needs to communicate with.
// this will only create an egress policy on the 'web' pod.
web.connections.allowTo(redis, { isolation: PodConnectionsIsolation.POD });
// this will only create an ingress policy on the 'redis' pod.
web.connections.allowTo(redis, { isolation: PodConnectionsIsolation.PEER });