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-30';
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-30';
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-30';
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-30';
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-30';
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-30';
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-30';
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-30';
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-30';
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-30';
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-30';
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-30';
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-30';
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-30';
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-30';
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 theconnections
API.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 });