├── Chapter 02: Creating and Running Containers ├── .dockerignore ├── Dockerfile └── index.md ├── Chapter 13: ConfigMaps and Secrets ├── my-config.txt ├── kuard-secret.yaml ├── kuard-secret-ips.yaml ├── kuard-config.yaml ├── kuard.crt ├── kuard.key └── index.md ├── Chapter 19: Securing Applications in Kubernetes ├── service-account.yaml ├── networkpolicy-default-deny.yaml ├── amicontained-pod.yaml ├── kuard-pod.yaml ├── kuard-pod-runtimeclass.yaml ├── networkpolicy-kuard-allow-test-source.yaml ├── baseline-ns.yaml ├── kuard-pod-securitycontext.yaml ├── amicontained-pod-securitycontext.yaml └── index.md ├── Chapter 08: HTTP Load Balancing with Ingress ├── simple-ingress.yaml ├── tls-secret.yaml ├── tls-ingress.yaml ├── host-ingress.yaml ├── path-ingress.yaml └── index.md ├── .gitignore ├── Chapter 14: Role-Based Access Control for Kubernetes ├── pod-and-service-role.yaml ├── rolebinding.yaml └── index.md ├── Chapter 20: Policy and Governance for Kubernetes Clusters ├── config-sync.yaml ├── allowedrepos-constraint.yaml ├── allowedrepos-constraint-dryrun.yaml ├── imagepullpolicyalways-mutation.yaml └── index.md ├── Chapter 05: Pods ├── kuard-pod-resreq.yaml ├── kuard-pod-reslim.yaml ├── kuard-pod-vol.yaml ├── kuard-pod-with-liveness.yaml └── index.md ├── Chapter 10: Deployments ├── kuard-deployment.yaml └── index.md ├── Chapter 12: Jobs ├── job-oneshot.yaml ├── job-parallel.yaml └── index.md ├── Chapter 09: ReplicaSets ├── kuard-rs.yaml └── index.md ├── Chapter 11: DamonSets ├── nginx-fast-storage.yaml ├── fluentd.yaml └── index.md ├── README.md ├── Chapter 01: Introduction └── index.md ├── Chapter 22: Organizing your application └── index.md ├── Chapter 06: Labels and Annotations └── index.md ├── Chapter 03: Deploying a Kubernetes Cluster └── index.md ├── Chapter 15: Service Meshes └── index.md ├── Chapter 07: Service Discovery └── index.md ├── Chapter 21: Multicluster Appllication Deployments └── index.md ├── Chapter 04: Common kubectl commands └── index.md └── LICENSE /Chapter 02: Creating and Running Containers/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /Chapter 13: ConfigMaps and Secrets/my-config.txt: -------------------------------------------------------------------------------- 1 | # This is a sample config file that I might use to configure an application 2 | parameter1 = value1 3 | parameter2 = value2 -------------------------------------------------------------------------------- /Chapter 19: Securing Applications in Kubernetes/service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: default 5 | automountServiceAccountToken: false -------------------------------------------------------------------------------- /Chapter 08: HTTP Load Balancing with Ingress/simple-ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: simple-ingress 5 | spec: 6 | defaultBackend: 7 | service: 8 | name: alpaca 9 | port: 10 | number: 8080 -------------------------------------------------------------------------------- /Chapter 19: Securing Applications in Kubernetes/networkpolicy-default-deny.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: default-deny-ingress 5 | namespace: kuard-networkpolicy 6 | spec: 7 | podSelector: {} 8 | policyTypes: 9 | - Ingress -------------------------------------------------------------------------------- /Chapter 08: HTTP Load Balancing with Ingress/tls-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | creationTimestamp: null 5 | name: tls-secret-name 6 | type: kubernetes.io/tls 7 | data: 8 | tls.crt: 9 | tls.key: -------------------------------------------------------------------------------- /Chapter 19: Securing Applications in Kubernetes/amicontained-pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: amicontained 5 | spec: 6 | containers: 7 | - image: r.j3ss.co/amicontained:v0.4.9 8 | name: amicontained 9 | command: [ "/bin/sh", "-c", "--" ] 10 | args: [ "amicontained"] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /Chapter 14: Role-Based Access Control for Kubernetes/pod-and-service-role.yaml: -------------------------------------------------------------------------------- 1 | kind: Role 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | namespace: default 5 | name: pod-and-services 6 | rules: 7 | - apiGroups: [""] 8 | resources: ["pods", "services"] 9 | verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] -------------------------------------------------------------------------------- /Chapter 19: Securing Applications in Kubernetes/kuard-pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: kuard 5 | labels: 6 | app: kuard 7 | spec: 8 | containers: 9 | - image: gcr.io/kuar-demo/kuard-amd64:blue 10 | name: kuard 11 | ports: 12 | - containerPort: 8080 13 | name: http 14 | protocol: TCP -------------------------------------------------------------------------------- /Chapter 20: Policy and Governance for Kubernetes Clusters/config-sync.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: config.gatekeeper.sh/v1alpha1 2 | kind: Config 3 | metadata: 4 | name: config 5 | namespace: "gatekeeper-system" 6 | spec: 7 | sync: 8 | syncOnly: 9 | - group: "" 10 | version: "v1" 11 | kind: "Namespace" 12 | - group: "" 13 | version: "v1" 14 | kind: "Pod" -------------------------------------------------------------------------------- /Chapter 05: Pods/kuard-pod-resreq.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: kuard 5 | spec: 6 | containers: 7 | - image: gcr.io/kuar-demo/kuard-amd64:blue 8 | name: kuard 9 | resources: 10 | requests: 11 | cpu: "500m" 12 | memory: "128Mi" 13 | ports: 14 | - containerPort: 8080 15 | name: http 16 | protocol: TCP -------------------------------------------------------------------------------- /Chapter 19: Securing Applications in Kubernetes/kuard-pod-runtimeclass.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: kuard 5 | labels: 6 | app: kuard 7 | spec: 8 | runtimeClassName: firecracker 9 | containers: 10 | - image: gcr.io/kuar-demo/kuard-amd64:blue 11 | name: kuard 12 | ports: 13 | - containerPort: 8080 14 | name: http 15 | protocol: TCP -------------------------------------------------------------------------------- /Chapter 19: Securing Applications in Kubernetes/networkpolicy-kuard-allow-test-source.yaml: -------------------------------------------------------------------------------- 1 | kind: NetworkPolicy 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: access-kuard 5 | namespace: kuard-networkpolicy 6 | spec: 7 | podSelector: 8 | matchLabels: 9 | app: kuard 10 | ingress: 11 | - from: 12 | - podSelector: 13 | matchLabels: 14 | run: test-source -------------------------------------------------------------------------------- /Chapter 08: HTTP Load Balancing with Ingress/tls-ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: tls-ingress 5 | spec: 6 | tls: 7 | - hosts: 8 | - alpaca.example.com 9 | secretName: tls-secret-name 10 | rules: 11 | - host: alpaca.example.com 12 | http: 13 | paths: 14 | - backend: 15 | serviceName: alpaca 16 | servicePort: 8080 -------------------------------------------------------------------------------- /Chapter 20: Policy and Governance for Kubernetes Clusters/allowedrepos-constraint.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: constraints.gatekeeper.sh/v1beta1 2 | kind: K8sAllowedRepos 3 | metadata: 4 | name: repo-is-kuar-demo 5 | spec: 6 | enforcementAction: deny 7 | match: 8 | kinds: 9 | - apiGroups: [""] 10 | kinds: ["Pod"] 11 | namespaces: 12 | - "default" 13 | parameters: 14 | repos: 15 | - "gcr.io/kuar-demo/" -------------------------------------------------------------------------------- /Chapter 10: Deployments/kuard-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: kuard 5 | labels: 6 | run: kuard 7 | spec: 8 | selector: 9 | matchLabels: 10 | run: kuard 11 | replicas: 1 12 | template: 13 | metadata: 14 | labels: 15 | run: kuard 16 | spec: 17 | containers: 18 | - name: kuard 19 | image: gcr.io/kuar-demo/kuard-amd64:blue -------------------------------------------------------------------------------- /Chapter 20: Policy and Governance for Kubernetes Clusters/allowedrepos-constraint-dryrun.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: constraints.gatekeeper.sh/v1beta1 2 | kind: K8sAllowedRepos 3 | metadata: 4 | name: repo-is-kuar-demo 5 | spec: 6 | enforcementAction: dryrun 7 | match: 8 | kinds: 9 | - apiGroups: [""] 10 | kinds: ["Pod"] 11 | namespaces: 12 | - "default" 13 | parameters: 14 | repos: 15 | - "gcr.io/kuar-demo/" -------------------------------------------------------------------------------- /Chapter 13: ConfigMaps and Secrets/kuard-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: kuard-tls 5 | spec: 6 | containers: 7 | - name: kuard-tls 8 | image: gcr.io/kuar-demo/kuard-amd64:blue 9 | imagePullPolicy: Always 10 | volumeMounts: 11 | - name: tls-certs 12 | mountPath: "/tls" 13 | readOnly: true 14 | volumes: 15 | - name: tls-certs 16 | secret: 17 | secretName: kuard-tls -------------------------------------------------------------------------------- /Chapter 19: Securing Applications in Kubernetes/baseline-ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: baseline-ns 5 | labels: 6 | pod-security.kubernetes.io/enforce: baseline 7 | pod-security.kubernetes.io/enforce-version: v1.22 8 | pod-security.kubernetes.io/audit: restricted 9 | pod-security.kubernetes.io/audit-version: v1.22 10 | pod-security.kubernetes.io/warn: restricted 11 | pod-security.kubernetes.io/warn-version: v1.22 -------------------------------------------------------------------------------- /Chapter 05: Pods/kuard-pod-reslim.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: kuard 5 | spec: 6 | containers: 7 | - image: gcr.io/kuar-demo/kuard-amd64:blue 8 | name: kuard 9 | resources: 10 | requests: 11 | cpu: "500m" 12 | memory: "128Mi" 13 | limits: 14 | cpu: "1000m" 15 | memory: "256Mi" 16 | ports: 17 | - containerPort: 8080 18 | name: http 19 | protocol: TCP -------------------------------------------------------------------------------- /Chapter 05: Pods/kuard-pod-vol.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: kuard 5 | spec: 6 | volumes: 7 | - name: "kuard-data" 8 | hostPath: 9 | path: "/var/lib/kuard" 10 | containers: 11 | - image: gcr.io/kuar-demo/kuard-amd64:blue 12 | name: kuard 13 | volumeMounts: 14 | - mountPath: "/data" 15 | name: "kuard-data" 16 | ports: 17 | - containerPort: 8080 18 | name: http 19 | protocol: TCP -------------------------------------------------------------------------------- /Chapter 12: Jobs/job-oneshot.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: oneshot 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: kuard 10 | image: gcr.io/kuar-demo/kuard-amd64:blue 11 | imagePullPolicy: Always 12 | command: 13 | - "/kuard" 14 | args: 15 | - "--keygen-enable" 16 | - "--keygen-exit-on-complete" 17 | - "--keygen-num-to-gen=10" 18 | restartPolicy: OnFailure -------------------------------------------------------------------------------- /Chapter 14: Role-Based Access Control for Kubernetes/rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | namespace: default 5 | name: pods-and-services 6 | subjects: 7 | - apiGroup: rbac.authorization.k8s.io 8 | kind: User 9 | name: alice 10 | - apiGroup: rbac.authorization.k8s.io 11 | kind: Group 12 | name: mydevs 13 | roleRef: 14 | apiGroup: rbac.authorization.k8s.io 15 | kind: Role 16 | name: pod-and-services -------------------------------------------------------------------------------- /Chapter 09: ReplicaSets/kuard-rs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: ReplicaSet 3 | metadata: 4 | labels: 5 | app: kuard 6 | version: "2" 7 | name: kuard 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: kuard 13 | version: "2" 14 | template: 15 | metadata: 16 | labels: 17 | app: kuard 18 | version: "2" 19 | spec: 20 | containers: 21 | - name: kuard 22 | image: "gcr.io/kuar-demo/kuard-amd64:green" -------------------------------------------------------------------------------- /Chapter 11: DamonSets/nginx-fast-storage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: "DaemonSet" 3 | metadata: 4 | labels: 5 | app: nginx 6 | ssd: "true" 7 | name: nginx-fast-storage 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: nginx 12 | ssd: "true" 13 | template: 14 | metadata: 15 | labels: 16 | app: nginx 17 | ssd: "true" 18 | spec: 19 | nodeSelector: 20 | ssd: "true" 21 | containers: 22 | - name: nginx 23 | image: nginx:1.10.0 -------------------------------------------------------------------------------- /Chapter 13: ConfigMaps and Secrets/kuard-secret-ips.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: kuard-tls 5 | spec: 6 | containers: 7 | - name: kuard-tls 8 | image: gcr.io/kuar-demo/kuard-amd64:blue 9 | imagePullPolicy: Always 10 | volumeMounts: 11 | - name: tls-certs 12 | mountPath: "/tls" 13 | readOnly: true 14 | imagePullSecrets: 15 | - name: my-image-pull-secret 16 | volumes: 17 | - name: tls-certs 18 | secret: 19 | secretName: kuard-tls -------------------------------------------------------------------------------- /Chapter 08: HTTP Load Balancing with Ingress/host-ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: host-ingress 5 | spec: 6 | defaultBackend: 7 | service: 8 | name: be-default 9 | port: 10 | number: 8080 11 | rules: 12 | - host: alpaca.example.com 13 | http: 14 | paths: 15 | - pathType: Prefix 16 | path: / 17 | backend: 18 | service: 19 | name: alpaca 20 | port: 21 | number: 8080 -------------------------------------------------------------------------------- /Chapter 05: Pods/kuard-pod-with-liveness.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: kuard-liveness 5 | spec: 6 | containers: 7 | - image: gcr.io/kuar-demo/kuard-amd64:blue 8 | name: kuard 9 | livenessProbe: 10 | httpGet: 11 | path: /healthy 12 | port: 8080 13 | initialDelaySeconds: 5 14 | timeoutSeconds: 1 15 | periodSeconds: 10 16 | failureThreshold: 3 17 | ports: 18 | - containerPort: 8080 19 | name: http 20 | protocol: TCP -------------------------------------------------------------------------------- /Chapter 20: Policy and Governance for Kubernetes Clusters/imagepullpolicyalways-mutation.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: mutations.gatekeeper.sh/v1alpha1 2 | kind: Assign 3 | metadata: 4 | name: demo-image-pull-policy 5 | spec: 6 | applyTo: 7 | - groups: [""] 8 | kinds: ["Pod"] 9 | versions: ["v1"] 10 | match: 11 | scope: Namespaced 12 | kinds: 13 | - apiGroups: ["*"] 14 | kinds: ["Pod"] 15 | excludedNamespaces: ["system"] 16 | location: "spec.containers[name:*].imagePullPolicy" 17 | parameters: 18 | assign: 19 | value: Always -------------------------------------------------------------------------------- /Chapter 08: HTTP Load Balancing with Ingress/path-ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: path-ingress 5 | spec: 6 | rules: 7 | - host: bandicoot.example.com 8 | http: 9 | paths: 10 | - pathType: Prefix 11 | path: "/" 12 | backend: 13 | service: 14 | name: bandicoot 15 | port: 16 | number: 8080 17 | - pathType: Prefix 18 | path: "/a/" 19 | backend: 20 | service: 21 | name: alpaca 22 | port: 23 | number: 8080 -------------------------------------------------------------------------------- /Chapter 19: Securing Applications in Kubernetes/kuard-pod-securitycontext.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: kuard 5 | spec: 6 | securityContext: 7 | runAsNonRoot: true 8 | runAsUser: 1000 9 | runAsGroup: 3000 10 | fsGroup: 2000 11 | containers: 12 | - image: gcr.io/kuar-demo/kuard-amd64:blue 13 | name: kuard 14 | securityContext: 15 | allowPrivilegeEscalation: false 16 | readOnlyRootFilesystem: true 17 | privileged: false 18 | ports: 19 | - containerPort: 8080 20 | name: http 21 | protocol: TCP -------------------------------------------------------------------------------- /Chapter 12: Jobs/job-parallel.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: parallel 5 | labels: 6 | chapter: jobs 7 | spec: 8 | parallelism: 5 9 | completions: 10 10 | template: 11 | metadata: 12 | labels: 13 | chapter: jobs 14 | spec: 15 | containers: 16 | - name: kuard 17 | image: gcr.io/kuar-demo/kuard-amd64:blue 18 | imagePullPolicy: Always 19 | command: 20 | - "/kuard" 21 | args: 22 | - "--keygen-enable" 23 | - "--keygen-exit-on-complete" 24 | - "--keygen-num-to-gen=10" 25 | restartPolicy: OnFailure -------------------------------------------------------------------------------- /Chapter 19: Securing Applications in Kubernetes/amicontained-pod-securitycontext.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: amicontained 5 | annotations: 6 | container.apparmor.security.beta.kubernetes.io/amicontained: "runtime/default" 7 | spec: 8 | securityContext: 9 | runAsNonRoot: true 10 | runAsUser: 1000 11 | runAsGroup: 3000 12 | fsGroup: 2000 13 | seccompProfile: 14 | type: RuntimeDefault 15 | containers: 16 | - image: r.j3ss.co/amicontained:v0.4.9 17 | name: amicontained 18 | command: [ "/bin/sh", "-c", "--" ] 19 | args: [ "amicontained" ] 20 | securityContext: 21 | capabilities: 22 | add: ["SYS_TIME"] 23 | drop: ["NET_BIND_SERVICE"] 24 | allowPrivilegeEscalation: false 25 | readOnlyRootFilesystem: true 26 | privileged: false -------------------------------------------------------------------------------- /Chapter 02: Creating and Running Containers/Dockerfile: -------------------------------------------------------------------------------- 1 | # STAGE 1: Build 2 | FROM golang:1.12-alpine AS build 3 | 4 | # Install Node and NPM. 5 | RUN apk update && apk upgrade && apk add --no-cache git nodejs bash npm 6 | 7 | # Get dependencies for Go part of build 8 | RUN go get -u github.com/jteeuwen/go-bindata/... 9 | 10 | WORKDIR /go/src/github.com/kubernetes-up-and-running/kuard 11 | 12 | # Copy all sources in 13 | COPY . . 14 | 15 | # This is a set of variables that the build script expects 16 | ENV VERBOSE=0 17 | ENV PKG=github.com/kubernetes-up-and-running/kuard 18 | ENV ARCH=amd64 19 | ENV VERSION=test 20 | 21 | # When running on Windows 10, you need to clean up the ^Ms in the script 22 | RUN dos2unix build/build.sh 23 | 24 | # Do the build. Script is part of incoming sources. 25 | RUN build/build.sh 26 | 27 | # STAGE 2: Runtime 28 | FROM alpine 29 | 30 | USER nobody:nobody 31 | COPY --from=build /go/bin/kuard /kuard 32 | 33 | CMD [ "/kuard" ] -------------------------------------------------------------------------------- /Chapter 11: DamonSets/fluentd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: DaemonSet 3 | metadata: 4 | name: fluentd 5 | labels: 6 | app: fluentd 7 | spec: 8 | selector: 9 | matchLabels: 10 | app: fluentd 11 | template: 12 | metadata: 13 | labels: 14 | app: fluentd 15 | spec: 16 | containers: 17 | - name: fluentd 18 | image: fluent/fluentd:v0.14.10 19 | resources: 20 | limits: 21 | memory: 200Mi 22 | requests: 23 | cpu: 100m 24 | memory: 200Mi 25 | volumeMounts: 26 | - name: varlog 27 | mountPath: /var/log 28 | - name: varlibdockercontainers 29 | mountPath: /var/lib/docker/containers 30 | readOnly: true 31 | terminationGracePeriodSeconds: 30 32 | volumes: 33 | - name: varlog 34 | hostPath: 35 | path: /var/log 36 | - name: varlibdockercontainers 37 | hostPath: 38 | path: /var/lib/docker/containers -------------------------------------------------------------------------------- /Chapter 13: ConfigMaps and Secrets/kuard-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: kuard-config 5 | spec: 6 | containers: 7 | - name: test-container 8 | image: gcr.io/kuar-demo/kuard-amd64:blue 9 | imagePullPolicy: Always 10 | command: 11 | - "/kuard" 12 | - "$(EXTRA_PARAM)" 13 | env: 14 | # An example of an environment variable used inside the container 15 | - name: ANOTHER_PARAM 16 | valueFrom: 17 | configMapKeyRef: 18 | name: my-config 19 | key: another-param 20 | # An example of an environment variable passed to the command to start 21 | # the container (above). 22 | - name: EXTRA_PARAM 23 | valueFrom: 24 | configMapKeyRef: 25 | name: my-config 26 | key: extra-param 27 | volumeMounts: 28 | # Mounting the ConfigMap as a set of files 29 | - name: config-volume 30 | mountPath: /config 31 | volumes: 32 | - name: config-volume 33 | configMap: 34 | name: my-config 35 | restartPolicy: Never -------------------------------------------------------------------------------- /Chapter 13: ConfigMaps and Secrets/kuard.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC2zCCAcOgAwIBAgIJAI2AxVznPQNNMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNV 3 | BAMMEWt1YXJkLmV4YW1wbGUuY29tMB4XDTE3MDMxOTIwMTQyMVoXDTE4MDMxOTIw 4 | MTQyMVowHDEaMBgGA1UEAwwRa3VhcmQuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3 5 | DQEBAQUAA4IBDwAwggEKAoIBAQDO6HjeVc/OzCqCKTb9ESB4fOuHywM5R93q3ssl 6 | 5uFy6sooOvRfpQ1ADLZjNwfNaEkVcNEssHWCH+Dhbvsta90zTXYdPFWvVaXc32uy 7 | dQDR+FIRxl5c1oHrhD6yYJJop9Nyw0hwrfOyj7+NUW0fgSaVtbdrLlhQP0VuoQUG 8 | RlHpl7imqP3PlgQLmo8xnNQ1+R072l0rB/BqUGdG6MA6RXf9NixaECNVgSxuu+BB 9 | wx1upL/6c0rJaqjLpcEqoyp5FGo8ttOvWqwDukMATJD/7Ei5MzS6RdQjFC19rh88 10 | 1zpQ5hBPxcy3Lkj/Xaf6ehX5nrPcuRx4vadGagLnrxTgjk7XAgMBAAGjIDAeMBwG 11 | A1UdEQQVMBOCEWt1YXJkLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCb 12 | vOa6pCLJw9K2/929Wsn6CEuZrZfhXUrNGT87cVibQSj2a48sMdblIjxbvkAHtmbg 13 | mMxMOMgea1hCwZCaJw3ECEmCB4LHlBTnFDWbdqnRvs+/UiLhpaq4x5j2spf4VysY 14 | 1XqgkdHI+JQ1II07poqB/LkmpBPy3p/vCnHl5qgZ5ShS3GCZF45lHrMrwmq8ujJc 15 | nCa0CCZpjzYRp8pT5W/88OL+toeb9rN3ckKmBtws5q7J3Dbfkmv1sCPwSusBfATZ 16 | z4sKe4mopBMwE5yJzcjoPeiY6dhMZ2FsmOE9b4XaMkDPVIzeZS3S/0nUD36vCWsP 17 | jkFJS9Y6uqIRF7Tp7+q3 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /Chapter 13: ConfigMaps and Secrets/kuard.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAzuh43lXPzswqgik2/REgeHzrh8sDOUfd6t7LJebhcurKKDr0 3 | X6UNQAy2YzcHzWhJFXDRLLB1gh/g4W77LWvdM012HTxVr1Wl3N9rsnUA0fhSEcZe 4 | XNaB64Q+smCSaKfTcsNIcK3zso+/jVFtH4EmlbW3ay5YUD9FbqEFBkZR6Ze4pqj9 5 | z5YEC5qPMZzUNfkdO9pdKwfwalBnRujAOkV3/TYsWhAjVYEsbrvgQcMdbqS/+nNK 6 | yWqoy6XBKqMqeRRqPLbTr1qsA7pDAEyQ/+xIuTM0ukXUIxQtfa4fPNc6UOYQT8XM 7 | ty5I/12n+noV+Z6z3LkceL2nRmoC568U4I5O1wIDAQABAoIBAHb8SUXSo0aRMoDX 8 | or+1ca7YZ7oXjSsLoRrINJr/QvcK/mZUQOQfzpbeuKQlqV7+ccjar++7KlhCbNg3 9 | MkrUlMhD64C0ibHi1xdFhHGDx4z3+HowUWOiF+SQkF2UG54DpR2CH85supAdClMC 10 | WHadlsrRTUFdzXuYZx1Pi8wn9EMX7Gumq8nf+cyTCna5QoGu00RxFVmJjO2PN5YI 11 | FC4iQRF9D8VZvAY799Z/k0noUZNhUqDaFH4Uw9d6+aryz/YBaiHIa5D64SP8Sh3j 12 | xXNwVfDV6Gy/4bnyJ+n/EYoZvg3fis7C74t6c94B/dRsvjo2/cP3roRdMApf7qMv 13 | FLJYVwECgYEA5n/pYTVSmsD4tSFvZeHG7o11zO3TiXWrzNwdlF7VtiIDuJBR1fWl 14 | BweUABzcdTFqC5HyNZWWdVH8GGsqVhuupBIScPYxopvf7EXwAzha7RfC0x0h4OW3 15 | lVPa9H3/wdBR6oRqBxO9q0fXoidUlM48Ul0oiXB2yR7qnlk3wKC9FwcCgYEA5cxp 16 | s9tWkZTiJMUbfXhS3KiaZzGjNrT7S9vhHCov6X/qxKGFV/icvk0vTuF+aF0Nq21c 17 | 6OncxhzNWnTwr5ILZ3ulp0rACSRFL5Z04DhXGFk/hTBm9nzcHq7po90IbkntlpAy 18 | UkiRfyyWoAAteYO1CJOUXIA8g1qGH8tKAnTfxbECgYEAkLinE32jS771aOSBSPql 19 | KIpw+CYqtxfGsm1RtSKGFEDvDSav+yKsZul/J0L3uC06Y+FSrgop2aeMHNjMUBw5 20 | XpJqObqaF3q+8V5H//2WOV692EdmSgpxZbSsuO2Tc2EUzaYt3Bus7qnA3fLLzFJg 21 | 1WXgWcrfCg/7R0jFFJDXqGkCgYA3taaqg7Io+P8aCu7vLAxqkjVcbxwyVw3VBik8 22 | ur2CzhAMO1woR404VV3ysZgDlQuLU6gCjy+Gl9Tw6Qiwh6dcHw250NVdYf2j28XZ 23 | 1c7HiFtt3p4Xg42Zoq3hm+Q/WIzQw4Rvjkg3nIenomNj4hoxShXdlvkUnw2FB7OZ 24 | hNv7AQKBgQDNZG0c/I4ew+iVEm3I5cheEV6/l9w+rPsfaojU0t/F7FfEvlHjyhXC 25 | /pIdK+nZtR7AhMbUnKYAAqYauU4TT7RdEagKi4MshLpMHQPR0F15jx7tkGADl4E6 26 | GJcYXa0OUmgaPelwbH3oR2yEiHSz6guHt9BlYi2bFmAezQIw8xdK0A== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes-Up-and-Running 2 | Examples and sneak peek of Kubernetes Up and Running Book, 3rd Edition 3 | 4 | ## Index 5 | 6 | 1. [Chapter 1: Introduction](./Chapter%2001:%20Introduction/index.md) 7 | 2. [Chapter 2: Creating and Running Containers](./Chapter%2002:%20Creating%20and%20Running%20Containers/index.md) 8 | 3. [Chapter 3: Deploying a Kubernetes Cluster](./Chapter%2003:%20Deploying%20a%20Kubernetes%20Cluster/index.md) 9 | 4. [Chapter 4: Common kubectl commands](./Chapter%2004:%20Common%20kubectl%20commands/index.md) 10 | 5. [Chapter 5: Pods](./Chapter%2005:%20Pods/index.md) 11 | 6. [Chapter 6: Labels and Annotations](./Chapter%2006:%20Labels%20and%20Annotations/index.md) 12 | 7. [Chapter 7: Service Discovery](./Chapter%2007:%20Service%20Discovery/index.md) 13 | 8. [Chapter 8: HTTP Load Balancing with Ingress](./Chapter%2008:%20HTTP%20Load%20Balancing%20with%20Ingress/index.md) 14 | 9. [Chapter 9: ReplicaSets](./Chapter%2009:%20ReplicaSets/index.md) 15 | 10. [Chapter 10: Deployments](./Chapter%2010%3A%20Deployments/index.md) 16 | 11. [Chapter 11: DaemonSets](./Chapter%2011%3A%20DamonSets/index.md) 17 | 12. [Chapter 12: Jobs](./Chapter%2012%3A%20Jobs/index.md) 18 | 13. [Chapter 13: ConfigMaps and Secrets](./Chapter%2013%3A%20ConfigMaps%20and%20Secrets/index.md) 19 | 14. [Chapter 14: Role-Based Access Control for Kubernetes](./Chapter%2014%3A%20Role-Based%20Access%20Control%20for%20Kubernetes/index.md) 20 | 15. [Chapter 15: Service Meshes](./Chapter%2015%3A%20Service%20Meshes/index.md) 21 | 16. [Chapter 19: Securing Applications in Kubernetes](./Chapter%2019%3A%20Securing%20Applications%20in%20Kubernetes/index.md) 22 | 17. [Chapter 20: Policy and Governance for Kubernetes Clusters](./Chapter%2020%3A%20Policy%20and%20Governance%20for%20Kubernetes%20Clusters/index.md) 23 | 18. [Chapter 21: Multicluster Application Deployments](./Chapter%2021%3A%20Multicluster%20Appllication%20Deployments/index.md) 24 | 19. [Chapter 22: Organizing Your Application](./Chapter%2022%3A%20Organizing%20your%20application/index.md) 25 | 26 | ## Refs 27 | [Demo Application of this book](https://github.com/kubernetes-up-and-running/kuard) 28 | 29 | ## Acknowledgments 30 | ``` 31 | Kubernetes: Up and Running,3rd edition, 32 | Brendan Burns, Joe Beda, Kelsey Hightower, and Lachlan Evenson 33 | (O’Reilly). Copyright 2019 Brendan Burns, Joe Beda, Kelsey Hightower, and Lachlan 34 | Evenson, 978-1-098-11020-8 35 | ``` 36 | -------------------------------------------------------------------------------- /Chapter 01: Introduction/index.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | People mostly use kubernetes for one of the following benefits: 4 | - Development velocity 5 | - Scaling (of both software and teams) 6 | - Abstracting your infrastructure 7 | - Efficiency 8 | - Cloud native ecosystem 9 | 10 | ## Velocity 11 | 12 | The core concepts that enable this are: 13 | - **Immutability**: Immutable container images are at the core of everything that you will build in Kubernetes 14 | - **Declarative configuration** : While imperative commands define actions, declarative configurations define state. 15 | - **Online self-healing systems**: Kubernetes continuously takes actions to ensure that the current state matches the desired state. 16 | - Shared reusable libraries and tools 17 | 18 | ## Scaling Your Service and Your Teams 19 | 20 | Kubernetes achieves scalability by favoring decoupled architectures. 21 | 22 | - **Decoupling**: In a decoupled architecture, each component is separated from other components by defined APIs and service load balancers. 23 | - **Easy Scaling for Applications and Clusters**: Because your containers are immutable, and the number of replicas is merely a number in a declarative config,scaling your service upward is simply a matter of changing a number in a configura‐tion file, asserting this new declarative state to Kubernetes, and letting it take care of the rest. Kubernetes can simplify forecasting future compute costs. 24 | - **Scaling Development Teams with Microservices**: Kubernetes provides numerous abstractions and APIs that make it easier to build these decoupled microservice architectures: Pods, Kubernetes services, Namespaces, Ingress. Decoupling the application container image and machine means that different microservices can colocate on the same machine without interfering with one another, reducing the overhead and cost of microservice architectures. 25 | - **Separation of Concerns for Consistency and Scaling**: The decoupling and separation of concerns produced by the Kubernetes stack lead to significantly greater consistency for the lower levels of your infrastructure. 26 | 27 | ## Abstracting Your Infrastructure 28 | 29 | The move to application-oriented container APIs like Kubernetes has two concrete benefits. First, it separates developers from specific machines. 30 | Kubernetes has a number of plug-ins that can abstract you from a particular cloud. 31 | 32 | ## Efficiency 33 | 34 | - Kubernetes provides tools that automate the distribution of applications across a cluster of machines, ensuring higher levels of utilization than are possible with traditional tooling. 35 | - A further increase in efficiency comes from the fact that a developer’s test environ‐ment can be quickly and cheaply created as a set of containers running in a personal view of a shared Kubernetes cluster (using a feature called namespaces). 36 | 37 | ## Cloud Native Ecosystem 38 | 39 | Following the lead of Kubernetes (and Docker and Linux before it), most of these projects are also open source. This means that a developer beginning to build does not have to start from scratch. In the years since it was released, tools for nearly every task, from machine learning to continuous development and serverless programming models have been built for Kubernetes. Indeed, in many cases the challenge isn’t finding a potential solution, but rather deciding which of the many solutions is best suited to the task. The wealth of tools in the cloud native ecosystem has itself become a strong reason for many people to adopt Kubernetes. -------------------------------------------------------------------------------- /Chapter 22: Organizing your application/index.md: -------------------------------------------------------------------------------- 1 | # Organizing Your Application 2 | 3 | Obviously, reliability and agility are the general goals of developing a cloud native application in Kubernetes. The following sections describe three principles that can guide you in designing a structure that best suits these goals. The principles are: 4 | 5 | - Treat filesystems as the source of truth 6 | - Conduct code review to ensure the quality of changes 7 | - Use feature flags to stage rollouts and rollbacks 8 | 9 | ### Filesystems as the Source of Truth 10 | 11 | Rather than viewing the state of the cluster—the data in etcd —as the source of truth, it is optimal to view the filesystem of YAML objects as the source of truth for your application. The API objects deployed into your Kubernetes cluster(s) are then a reflection of the truth stored in the filesystem. It is absolutely a first principle that all applications deployed to Kubernetes should first be described in files stored in a filesystem. The 12 | actual API objects are then just a projection of this filesystem into a particular cluster. 13 | 14 | ### The Role of Code Review 15 | 16 | Most service outages are self-inflicted via unexpected consequences, typos, or other simple mistakes. Ensuring that at least two people look at any configuration change significantly decreases the probability of such errors. 17 | 18 | ### Feature Gates 19 | 20 | The idea is that when some new feature is developed, that development takes place entirely behind a feature flag or gate. This gate looks something like: 21 | 22 | ``` 23 | if (featureFlags.myFlag) { 24 | // Feature implementation goes here 25 | } 26 | ``` 27 | 28 | Working behind a feature flag also means that enabling a feature simply involves making a configuration change to activate the flag. This makes it very clear what 29 | changed in the production environment, and very simple to roll back the feature activation if it causes problems. 30 | 31 | Using feature flags thus both simplifies debugging and ensures that disabling a feature doesn’t require a binary rollback to an older version of the code that would remove all of the bug fixes and other improvements made by the newer version. 32 | 33 | ## Managing Your Application in Source Control 34 | 35 | Obviously, filesystems contain hierarchical directories, and a source-control system adds concepts like tags and branches, so this section describes 36 | how to put these together to represent and manage your application. 37 | 38 | ### Filesystem Layout 39 | 40 | Thus, for an application with a frontend that uses two services, the filesystem might look like this: 41 | 42 | ``` 43 | frontend/ 44 | service-1/ 45 | service-2/ 46 | ``` 47 | 48 | Within each of these directories, the configurations for each application are stored. These are the YAML files that directly represent the current state of the cluster. It’s generally useful to include both the service name and the object type within the same file. Thus, extending our previous example, the filesystem might look like: 49 | 50 | ``` 51 | frontend/ 52 | frontend-deployment.yaml 53 | frontend-service.yaml 54 | frontend-ingress.yaml 55 | service-1/ 56 | service-1-deployment.yaml 57 | service-1-service.yaml 58 | service-1-configmap.yaml 59 | ... 60 | ``` 61 | 62 | ## Managing Periodic Versions 63 | 64 | It’s handy to be able to simultaneously store and maintain multiple revisions of your configuration. There are two different approaches that you can use 65 | with the file and version control systems we’ve outlined here: 66 | - Use tags, branches, and source-control features 67 | - Clone the configuration within the filesystem and use directories for different revisions. 68 | 69 | ### Versioning with branches and tags 70 | 71 | When you use branches and tags to manage configuration revisions, the directory structure does not change from the example in the previous section. When you are 72 | ready for a release, you place a source-control tag (such as git tag v1.0 ) in the configuration source-control system. The tag represents the configuration used for that version, and the HEAD of source control continues to iterate forward. -------------------------------------------------------------------------------- /Chapter 12: Jobs/index.md: -------------------------------------------------------------------------------- 1 | # Jobs 2 | 3 | A Job creates Pods that run until successful termination(for instance, exit with 0). Jobs are useful for things you only want to do once, such as database migrations or batch jobs. 4 | 5 | ## The Job Object 6 | 7 | The Job object is responsible for creating and managing Pods defined in a template in the job specification. These Pods generally run until successful completion. The Job 8 | object coordinates running a number of Pods in parallel. 9 | 10 | If the Pod fails before a successful termination, the job controller will create a new Pod based on the Pod template in the job specification. Given that Pods have to be scheduled, there is a chance that your job will not execute if the scheduler does not find the required resources. Also, due to the nature of distributed systems, there is a small chance that duplicate Pods will be created for a specific task during certain failure scenarios. 11 | 12 | ## Job Patterns 13 | 14 | Jobs are designed to manage batch-like workloads where work items are processed by one or more Pods. By default, each job runs a single Pod once until successful ter‐ 15 | mination. This job pattern is defined by two primary attributes of a job: the number of job completions and the number of Pods to run in parallel. 16 | 17 | ### One Shot 18 | 19 | One-shot jobs provide a way to run a single Pod once until successful termination. This is done using a Pod template defined in the job configuration. Once a job is up and running,the Pod backing the job must be monitored for successful termination. A job can fail for any number of reasons. 20 | 21 | There are multiple ways to create a one-shot job in Kubernetes. The easiest is to use the kubectl command-line tool: 22 | 23 | ``` 24 | $ kubectl run -i oneshot \ 25 | --image=gcr.io/kuar-demo/kuard-amd64:blue \ 26 | --restart=OnFailure \ 27 | --command /kuard \ 28 | -- --keygen-enable \ 29 | --keygen-exit-on-complete \ 30 | --keygen-num-to-gen 10 31 | ``` 32 | There are some things to note here: 33 | 34 | - The `-i` option to kubectl indicates that this is an interactive command. kubectl will wait until the job is running and then show the log output from the first (and in this case only) Pod in the job. 35 | - All of the options after `--` are command-line arguments to the container image. These instruct our test server (kuard) to generate ten 4,096-bit SSH keys and then exit. 36 | 37 | Note that this job won’t show up in kubectl get jobs 38 | unless you pass the `-a` flag. Without this flag, kubectl hides completed jobs. Delete the job before continuing: 39 | 40 | ``` 41 | $ kubectl delete pods oneshot 42 | ``` 43 | The other option for creating a one-shot job is using a configuration file, as shown in the [example](./job-oneshot.yaml) 44 | 45 | You can view the results of the job by looking at the logs of the Pod that was created: 46 | ``` 47 | $ kubectl logs oneshot-4kfdt 48 | ``` 49 | Because jobs have a finite beginning and ending, users often create many of them. This makes picking unique labels more difficult and more critical. For this reason, the Job object will automatically pick a unique label and use it to identify the Pods it creates. In advanced 50 | scenarios (such as swapping out a running job without killing the Pods it is managing), users can choose to turn off this automatic behavior and manually specify labels and selectors. 51 | 52 | We suggest you use `restartPolicy: OnFailure`, so failed Pods are rerun in place. 53 | 54 | ### Parallelism 55 | 56 | Our goal is to generate 100 keys by having 10 runs of 57 | kuard, with each run generating 10 keys. But we don’t want to swamp our cluster, so we’ll limit ourselves to only five Pods at a time. 58 | 59 | This translates to setting completions to 10 and parallelism to 5. The config is 60 | shown in [Example](./job-parallel.yaml). 61 | 62 | ### Work Queues 63 | 64 | A common use case for jobs is to process work from a work queue. 65 | 66 | #### Starting a work queue 67 | 68 | We start by launching a centralized work queue service. kuard has a simple memory-based work queue system built in. We will start an instance of kuard to act as a coordinator for all the work. 69 | 70 | ### CronJobs 71 | 72 | Sometimes you want to schedule a job to be run at a certain interval. To achieve this, you can declare a CronJob in Kubernetes, which is responsible for creating a new Job object at a particular interval. -------------------------------------------------------------------------------- /Chapter 11: DamonSets/index.md: -------------------------------------------------------------------------------- 1 | # DaemonSets 2 | 3 | A DaemonSet ensures that a copy of a Pod is running across a set of nodes in a Kubernetes cluster. DaemonSets are used to deploy system daemons such as log collectors and monitoring agents, which typically must run on every node. 4 | 5 | 6 | DaemonSets share similar functionality with ReplicaSets; both create Pods that are expected to be long-running services and ensure that the desired state and the observed state of the cluster match. ReplicaSets should be used when your application is completely decoupled from the node and you can run multiple copies on a given node without special consideration. DaemonSets should be used when a single copy of your application must run on all or a subset of the nodes in the cluster. 7 | 8 | You can use labels to run DaemonSet Pods on specific nodes; for example, you may want to run specialized intrusion-detection software on nodes that are exposed to the edge network. You can also use DaemonSets to install software on nodes in a cloud-based cluster. 9 | 10 | ## DaemonSet Scheduler 11 | 12 | By default, a DaemonSet will create a copy of a Pod on every node unless a node selector is used, which will limit eligible nodes to those with a matching set of labels. 13 | 14 | Like ReplicaSets, DaemonSets are managed by a reconciliation control loop that measures the desired state (a Pod is present on all nodes) with the observed state (is the Pod present on a particular node?). Given this information, the DaemonSet controller creates a Pod on each node that doesn’t currently have a matching Pod. 15 | 16 | If a new node is added to the cluster, then the DaemonSet controller notices that it is missing a Pod and adds the Pod to the new node. 17 | 18 | ## Creating DaemonSets 19 | 20 | DaemonSets are created by submitting a DaemonSet configuration to the Kubernetes API server. The DaemonSet in the following Example will create a fluentd logging agent on every node in the target cluster. 21 | 22 | [fluentd-daemonSets](./fluentd.yaml) 23 | 24 | DaemonSets require a unique name across all DaemonSets in a given Kubernetes namespace. Each DaemonSet must include a Pod template spec, which will be used to create Pods as needed. This is where the similarities between ReplicaSets and DaemonSets end. Unlike ReplicaSets, DaemonSets will create Pods on every node in the cluster by default unless a node selector is used. 25 | 26 | Once the fluentd DaemonSet has been successfully submitted to the Kubernetes API, you can query its current state using the kubectl describe command: 27 | ``` 28 | $ kubectl describe daemonset fluentd 29 | ``` 30 | 31 | This output indicates a fluentd Pod was successfully deployed to all three nodes in our cluster. We can verify this using the kubectl get pods command with the -o flag 32 | to print the nodes where each fluentd Pod was assigned: 33 | 34 | ``` 35 | $ kubectl get pods -l app=fluentd -o wide 36 | ``` 37 | 38 | ## Limiting DaemonSets to Specific Nodes 39 | 40 | There are some cases where you want to deploy a Pod 41 | to only a subset of nodes. For example, maybe you have a workload that requires a GPU or access to fast storage only available on a subset of nodes in your cluster. In 42 | cases like these, node labels can be used to tag specific nodes that meet workload requirements. 43 | 44 | ### Adding Labels to Nodes 45 | 46 | The first step in limiting DaemonSets to specific nodes is to add the desired set of labels to a subset of nodes. This can be achieved using the kubectl label command. 47 | 48 | The following command adds the ssd=true label to a single node: 49 | ``` 50 | $ kubectl label nodes k0-default-pool-35609c18-z7tb ssd=true 51 | ``` 52 | 53 | Using a label selector, we can filter nodes based on labels. 54 | ``` 55 | $ kubectl get nodes --selector ssd=true 56 | ``` 57 | 58 | ### Node Selectors 59 | 60 | Node selectors can be used to limit what nodes a Pod can run on in a given Kubernetes cluster. Node selectors are defined as part of the Pod spec when creating a DaemonSet. The DaemonSet configuration in the following Example limits NGINX to running only on nodes with the ssd=true label set. 61 | 62 | Example: [nginx-fast-storage-damonset](./nginx-fast-storage.yaml) 63 | 64 | Adding the ssd=true label to additional nodes will cause the nginx-fast-storage Pod to be deployed on those nodes. The inverse is also true: if a required label is 65 | removed from a node, the Pod will be removed by the DaemonSet controller. 66 | 67 | ## Updating a DaamonSet 68 | 69 | DaemonSets can be rolled out using the same RollingUpdate strategy that Deployments use. You can configure the update strategy using the `spec.update Strategy.type` field, which should have the value RollingUpdate. 70 | 71 | There are two parameters that control the rolling update of a DaemonSet: 72 | `spec.minReadySeconds` Determines how long a Pod must be “ready” before the rolling update proceeds to upgrade subsequent Pods 73 | 74 | `spec.updateStrategy.rollingUpdate.maxUnavailable` 75 | Indicates how many Pods may be simultaneously updated by the rolling update 76 | 77 | Once a rolling update has started, you can use the kubectl rollout commands to see the current status of a DaemonSet rollout. For example, `kubectl rollout status daemonSets my-daemon-set` will show the current rollout status of a DaemonSet named my-daemon-set. 78 | 79 | ## Deleting a DaemonSet 80 | 81 | Deleting a DaemonSet using the kubectl delete command is pretty straightfoward. Just be sure to supply the correct name of the DaemonSet you would like to delete: 82 | 83 | ``` 84 | $ kubectl delete -f fluentd.yaml 85 | ``` -------------------------------------------------------------------------------- /Chapter 06: Labels and Annotations/index.md: -------------------------------------------------------------------------------- 1 | # Labels and Annotations 2 | 3 | Labels are key/value pairs that can be attached to Kubernetes objects such as Pods and ReplicaSets. Labels provide the foundation for grouping objects. 4 | 5 | Annotations, on the other hand, provide a storage mechanism that resembles labels: key/value pairs designed to hold nonidentifying information that tools and libraries can leverage. Unlike labels, annotations are not meant for querying, filtering, or otherwise differentiating Pods from each other. 6 | 7 | ## Labels 8 | 9 | Labels provide identifying metadata for objects. These are fundamental qualities of the object that will be used for grouping, viewing, and operating. 10 | 11 | Labels have simple syntax. They are key/value pairs, where both the key and value are represented by strings. Label keys can be broken down into two parts: an optional prefix and a name, separated by a slash. The key name is required and have a maximum length of 63 characters. Names must also start and end with an alphanumeric character and permit the use of dashes ( - ), underscores ( _ ), and dots ( . ) between characters. Label values are strings with a maximum length of 63 characters. 12 | 13 | ### Applying Labels 14 | 15 | Here we create a few deployments (a way to create an array of Pods) with some interesting labels. We’ll take two apps (called alpaca and bandicoot ) and have two environments and two versions for each. 16 | 17 | ```bash 18 | $ kubectl run alpaca-prod \ 19 | --image=gcr.io/kuar-demo/kuard-amd64:blue \ 20 | --replicas=2 \ 21 | --labels="ver=1,app=alpaca,env=prod" 22 | 23 | 24 | $ kubectl run alpaca-test \ 25 | --image=gcr.io/kuar-demo/kuard-amd64:green \ 26 | --replicas=1 \ 27 | --labels="ver=2,app=alpaca,env=test" 28 | 29 | $ kubectl run bandicoot-prod \ 30 | --image=gcr.io/kuar-demo/kuard-amd64:green \ 31 | --replicas=2 \ 32 | --labels="ver=2,app=bandicoot,env=prod" 33 | 34 | $ kubectl run bandicoot-staging \ 35 | --image=gcr.io/kuar-demo/kuard-amd64:green \ 36 | --replicas=1 \ 37 | --labels="ver=2,app=bandicoot,env=staging" 38 | ``` 39 | 40 | To see the Pods with Labels: 41 | ```bash 42 | $ kubectl get deployments --show-labels 43 | ``` 44 | 45 | 46 | ### Modifying Labels 47 | 48 | You can also apply or update labels on objects after you create them: 49 | 50 | ```bash 51 | $ kubectl label deployments alpaca-test "canary=true" 52 | ``` 53 | 54 | You can remove a label by applying a dash-suffix: 55 | ```bash 56 | $ kubectl label deployments alpaca-test "canary-" 57 | ``` 58 | 59 | ### Label Selectors 60 | 61 | Label selectors are used to filter Kubernetes objects based on a set of labels. Selectors use a simple syntax for Boolean expressions. They are used both by end users (via tools like kubectl ) and by different types of objects (such as how a ReplicaSet relates to its Pods). 62 | 63 | If we want to list only Pods that have the ver label set to 2 , we could use the `--selector flag`: 64 | ``` 65 | $ kubectl get pods --selector="ver=2" 66 | ``` 67 | 68 | If we specify two selectors separated by a comma, only the objects that satisfy both will be returned. This is a logical AND operation: 69 | ``` 70 | $ kubectl get pods --selector="app=bandicoot,ver=2" 71 | ``` 72 | 73 | We can also ask if a label is one of a set of values. Here we ask for all Pods where the app label is set to alpaca or bandicoot: 74 | ``` 75 | $ kubectl get pods --selector="app in (alpaca,bandicoot)" 76 | ``` 77 | 78 | Finally, we can ask if a label is set at all. Here we are asking for all of the deployments with the canary label set to anything: 79 | 80 | ``` 81 | $ kubectl get deployments --selector="canary" 82 | ``` 83 | 84 | There are also `negative` versions of each of these. 85 | 86 | ### Label Selectors in API Objects 87 | 88 | A Kubernetes object uses a label selector to refer to a set of other Kubernetes objects. Instead of a simple string as described in the previous section, we use a parsed structure. A selector of app=alpaca,ver in (1, 2) would be converted to this: 89 | 90 | ```yaml 91 | selector: 92 | matchLabels: 93 | app: alpaca 94 | matchExpressions: 95 | - {key: ver, operator: In, values: [1, 2]} 96 | ``` 97 | 98 | ### Labels in the Kubernetes Architecture\ 99 | 100 | Kubernetes is a purposefully decoupled system. There is no hierarchy and all components operate independently. However, in many cases, objects need to relate to one another, and these relationships are defined by labels and label selectors. 101 | 102 | For example, ReplicaSets, which create and maintain multiple replicas of a Pod, find the Pods that they are managing via a selector. Likewise, a service load balancer finds the Pods to which it should bring traffic via a selector query. 103 | 104 | ## Annotations 105 | 106 | Annotations provide a place to store additional metadata for Kubernetes objects where the sole purpose of the metadata is assisting tools and libraries. They are a way for other programs driving Kubernetes via an API to store some opaque data with an object. 107 | 108 | While labels are used to identify and group objects, annotations are used to provide extra information about where an object came from, how to use it, or policy around that object. There is overlap, and it is a matter of taste as to when to use an annotation or a label. When in doubt, add information to an object as an annotation and promote it to a label if you find yourself wanting to use it in a selector. 109 | 110 | Annotations are defined in the common metadata section in every Kubernetes object: 111 | ```yaml 112 | metadata: 113 | annotations: 114 | example.com/icon-url: "https://example.com/icon.png" 115 | ``` -------------------------------------------------------------------------------- /Chapter 03: Deploying a Kubernetes Cluster/index.md: -------------------------------------------------------------------------------- 1 | # Deploying a Kubernetes Cluster 2 | 3 | At this point, there are cloud-based Kubernetes services in most public clouds that make it easy to create a cluster with a few command-line instructions. 4 | 5 | ## Installing Kubernetes on a Public Cloud Provider 6 | 7 | This chapter covers installing Kubernetes on the three major cloud providers: the Google Cloud Platform, Microsoft Azure, and Amazon Web Services. 8 | 9 | ### Installing Kubernetes with Google Kubernetes Engine 10 | 11 | Once you have gcloud installed, set a default zone: 12 | 13 | ``` 14 | $ gcloud config set compute/zone us-west1-a 15 | ``` 16 | 17 | Then you can create a cluster: 18 | 19 | ``` 20 | $ gcloud container clusters create kuar-cluster --num-nodes=3` 21 | ``` 22 | 23 | This will take a few minutes. When the cluster is ready, you can get credentials for thecluster using: 24 | 25 | ``` 26 | $ gcloud container clusters get-credentials kuar-cluster` 27 | ``` 28 | 29 | ### Installing Kubernetes with Azure Kubernetes Service 30 | 31 | When you have the shell up and working, you can run: 32 | ``` 33 | $ az group create --name=kuar --location=westus 34 | ``` 35 | 36 | Once the resource group is created, you can create a cluster using: 37 | ``` 38 | $ az aks create --resource-group=kuar --name=kuar-cluster 39 | ``` 40 | 41 | This will take a few minutes. Once the cluster is created, you can get credentials for the cluster with: 42 | ``` 43 | $ az aks get-credentials --resource-group=kuar --name=kuar-cluster 44 | ``` 45 | 46 | If you don’t already have the kubectl tool installed, you can install it using: 47 | ``` 48 | $ az aks install-cli 49 | ``` 50 | 51 | ### Installing Kubernetes on Amazon Web Services 52 | 53 | Amazon offers a managed Kubernetes service called Elastic Kubernetes Service(EKS). The easiest way to create an EKS cluster is via the open source eksctl 54 | command-line tool. 55 | 56 | Once you have eksctl installed and in your path, you can run the following command to create a cluster: 57 | 58 | ``` 59 | $ eksctl create cluster 60 | ``` 61 | 62 | For more details on installation options (such as node size and more), view the help using this command: 63 | ``` 64 | $ eksctl create cluster --help 65 | ``` 66 | 67 | ## Installing Kubernetes Locally Using minikube 68 | 69 | You can create a local cluster using: 70 | 71 | ``` 72 | $ minikube start 73 | ``` 74 | 75 | This will create a local VM, provision Kubernetes, and create a local kubectl configuration that points to that cluster. As mentioned previously, this cluster only has a single node, so while it is useful, it has some differences with most production deployments of Kubernetes. 76 | 77 | When you are done with your cluster, you can stop the VM with: 78 | ``` 79 | $ minikube stop 80 | ``` 81 | 82 | ## Running Kubernetes in Docker 83 | 84 | The [kind project](https://kind.sigs.k8s.io) provides a great experience for launching and managing test clusters in Docker. (kind stands for Kubernetes IN Docker.) 85 | 86 | Once you get it installed, creating a cluster is as easy as: 87 | ``` 88 | $ kind create cluster --wait 5m 89 | $ export KUBECONFIG="$(kind get kubeconfig-path)" 90 | $ kubectl cluster-info 91 | $ kind delete cluster 92 | ``` 93 | 94 | ## The Kubernetes Client 95 | 96 | The official Kubernetes client is `kubectl` : a command-line tool for interacting with the Kubernetes API. 97 | 98 | ### Checking Cluster Status 99 | 100 | You can check the version of the cluster that you are running: 101 | ``` 102 | $ kubectl version 103 | ``` 104 | 105 | To verify that your cluster is generally healthy: 106 | 107 | ``` 108 | $ kubectl get componentstatuses 109 | ``` 110 | 111 | ### Listing Kubernetes Nodes 112 | 113 | You can list all of the nodes in your cluster: 114 | 115 | ``` 116 | $ kubectl get nodes 117 | ``` 118 | 119 | In Kubernetes, nodes are separated into control-plane nodes that contain containers like the API server, scheduler, etc., which manage the cluster, and worker nodes where your containers will run. Kubernetes won’t generally schedule work onto control-plane nodes to ensure that user workloads don’t harm the overall operation of the cluster. 120 | 121 | You can use the kubectl describe command to get more information about a specific node, such as kind-control-plane : 122 | ``` 123 | $ kubectl describe nodes kind-control-plane 124 | ``` 125 | 126 | ## Cluster Components 127 | 128 | One of the interesting aspects of Kubernetes is that many of the components that make up the Kubernetes cluster are actually deployed using Kubernetes itself. 129 | 130 | ### Kubernetes Proxy 131 | 132 | The Kubernetes proxy is responsible for routing network traffic to load-balanced services in the Kubernetes cluster. Kubernetes has an API object named DaemonSet, which is used in many clusters to accomplish this. 133 | 134 | ``` 135 | $ kubectl get daemonSets --namespace=kube-system kube-proxy 136 | ``` 137 | 138 | ### Kubernetes DNS 139 | 140 | Kubernetes also runs a DNS server, which provides naming and discovery for the services that are defined in the cluster. This DNS server also runs as a replicated 141 | service on the cluster. Depending on the size of your cluster, you may see one or more DNS servers running in your cluster. The DNS service is run as a Kubernetes 142 | deployment, which manages these replicas. 143 | 144 | ``` 145 | $ kubectl get deployments --namespace=kube-system coredns 146 | ``` 147 | 148 | There is also a Kubernetes service that performs load balancing for the DNS server: 149 | 150 | ``` 151 | $ kubectl get services --namespace=kube-system kube-dns 152 | ``` 153 | 154 | ### Kubernetes UI 155 | 156 | There is a community supported GUI for Kubernetes - [documentation](https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/) -------------------------------------------------------------------------------- /Chapter 15: Service Meshes/index.md: -------------------------------------------------------------------------------- 1 | # Service Meshes 2 | 3 | There are three general capabilities provided by most service mesh implementations: 4 | - network encryption and authorization, 5 | - traffic shaping 6 | - observability 7 | 8 | ### Encryption and Authentication with Mutual TLS 9 | 10 | Encryption of network traffic between Pods is a key component to security in a microservice architecture. Encryptions provided by Mutual Transport Layer Security, 11 | or mTLS, is one of the most popular use cases for a service mesh. By contrast, installing a service mesh on your Kubernetes cluster automatically provides 12 | encryption to network traffic between every Pod in the cluster. The service mesh adds a sidecar container to every Pod, which transparently intercepts all network 13 | communication. In addition to securing the communication, mTLS adds identity to the encryption using client certificates so your application securely knows the identity of every network client. 14 | 15 | ### Traffic Shaping 16 | 17 | Experiments are an incredibly useful way to add reliability, agility, and insight to your application, but they are often difficult to implement in practice and thus not used as often as they might otherwise be. 18 | 19 | Service meshes change this by building experimentation into the mesh itself. Instead of writing code to implement your experiment, or deploying an entirely new copy of your application on new infrastructure, you declaratively define the parameters of the experiment (10% of traffic to version Y, 90% of traffic to version X), and the service mesh implements it for you. Although, as a developer, you are involved in defining the experiment, the implementation is transparent and automatic, meaning that many more experiments will be run with a corresponding increase in reliability, agility, and insight. 20 | 21 | ### Introspection 22 | 23 | Debugging is even more difficult when applications are spread across multiple microservices. It is hard to stitch together a single request when it spans multiple Pods. 24 | 25 | Automatic introspection is another important capability provided by a service mesh. Because it is involved in all communication between Pods, the service mesh knows where requests were routed, and it can keep track of the information required to put a complete request trace back together. Instead of seeing a flurry of requests to a bunch of different microservices, the developer can see a single aggregate request that defines the user experience of their complete application. 26 | 27 | ## Do You Really Need a Service Mesh? 28 | 29 | A service mesh is a distributed system that adds complexity to your application design. The service mesh is deeply integrated into the core communication of your microservices. When a service mesh fails, your entire application stops working. Before you adopt a service mesh, you must be confident that you can fix problems when they occur. 30 | 31 | Ultimately, weighing the costs versus benefits of a service mesh is something each application or platform team needs to do at a cluster level. To maximize the benefits of a service mesh, it’s helpful for all microservices in the cluster to adopt it at the same time. 32 | 33 | ### Introspecting a Service Mesh Implementation 34 | 35 | Most service mesh implementations add a sidecar container to every Pod in the mesh. Because the sidecar sits in the same network stack as the application Pod, it can use tools like iptables or, more recently, eBPF to introspect and intercept network traffic coming from your application container and process it into the service mesh. Most service mesh implementations depend on a mutating admission controller to automatically add the service mesh sidecar to all Pods that are created in a particular cluster. Any REST API request to create a Pod is first routed to this admission controller. The service mesh admission controller modifies the Pod definition by adding the sidecar. Because this admission controller is installed by the cluster administrator, it transparently and consistently implements a service mesh for an entire cluster. 36 | 37 | But the service mesh isn’t just about modifying the Pod network. You also need to be able to control how the service mesh behaves; for example, by defining routing rules for experiments or access restrictions for services in the mesh. Service mesh implementations take advantages of custom resource definitions (CRDs) to add specialized resources to your Kubernetes cluster that are not part of the default installation. In most cases, the specific custom resources are tightly tied to the service mesh itself. An ongoing effort in the CNCF is defining a standard vendor-neutral Service Mesh Interface (SMI) that many different 38 | service meshes can implement. 39 | 40 | ### Service Mesh Landscape 41 | 42 | The most daunting aspect of the service mesh landscape may be figuring out which mesh to choose. Though 43 | concrete statistics are hard to come by, the most popular service mesh is likely the Istio project. In addition to Istio, there are many other open source meshes, including Linkerd, Consul Connect, Open Service Mesh, and others. There are also proprietary meshes like AWS App Mesh. 44 | 45 | How is a developer or cluster administrator to choose? The truth is that the best service mesh for you is likely the one that your cloud provider supplies for you. Adding operating a service mesh to the already complicated duties of your cluster operators is generally unnecessary. It is much better to let a cloud provider manage it for you. 46 | 47 | If that isn’t an option for you, do your research. Don’t be drawn in by flashy demos and promises of functionality. A service mesh lives deep within your infrastructure, and any failures can significantly impact the availability of your application. Additionally, because service mesh APIs tend to be implementation specific, it is difficult to change your choice of service mesh once you have spent time developing applications around it. In the end, you may find that the right mesh for you is no mesh at all. 48 | -------------------------------------------------------------------------------- /Chapter 07: Service Discovery/index.md: -------------------------------------------------------------------------------- 1 | # Service Discovery 2 | 3 | Kubernetes is a very dynamic system. The API-driven nature of the system encourages others to create higher and higher levels of automation. While the dynamic nature of Kubernetes makes it easy to run a lot of things, it creates problems when it comes to finding those things. 4 | 5 | ### What Is Service Discovery? 6 | 7 | Service-discovery tools help solve the problem of finding which processes are listening at which addresses for which services. A good service-discovery system will enable users to resolve this information quickly and reliably. A good system is also low-latency; clients are updated soon after the information associated with a service changes. Finally, a good service-discovery system can store a richer definition of what that service is. For example, perhaps there are multiple ports associated with the service. 8 | 9 | ## The Service Object 10 | 11 | Real service discovery in Kubernetes starts with a Service object. A Service object is a way to create a named label selector. We can use `kubectl expose` to create a service. To get services in the cluster: 12 | 13 | ```bash 14 | $ kubectl create deployment alpaca-prod \ 15 | --image=gcr.io/kuar-demo/kuard-amd64:blue \ 16 | --port=8080 17 | $ kubectl scale deployment alpaca-prod --replicas 3 18 | $ kubectl expose deployment alpaca-prod 19 | $ kubectl get services -o wide 20 | ``` 21 | 22 | The kubernetes service is automatically created for you so that you can find and talk to the Kubernetes API from within the app. The `kubectl expose` command will conveniently pull both the label selector and the relevant ports (8080, in this case) from the deployment definition. 23 | 24 | Furthermore, that service is assigned a new type of virtual IP called a cluster IP. This is a special IP address the system will load balance across all of the Pods that are identified by the selector. 25 | 26 | ### Service DNS 27 | 28 | Because the cluster IP is virtual, it is stable, and it is appropriate to give it a DNS address. 29 | 30 | Kubernetes provides a DNS service exposed to Pods running in the cluster. This Kubernetes DNS service was installed as a system component when the cluster was first created. The DNS service is, itself, managed by Kubernetes and is a great example 31 | of Kubernetes building on Kubernetes. The Kubernetes DNS service provides DNS names for cluster IPs. 32 | 33 | The full DNS name here is alpaca-prod.default.svc.cluster.local. 34 | 35 | ### Readiness Checks 36 | 37 | One nice thing the Service object does is track which of your Pods are ready via a readiness check. 38 | 39 | This readiness check is a way for an overloaded or sick server to signal to the system that it doesn’t want to receive traffic anymore. This is a great way to implement graceful shutdown. 40 | 41 | ### Looking Beyond the Cluster 42 | 43 | Oftentimes, the IPs for Pods are only reachable from within the cluster. At some point, we have to allow new traffic in! 44 | 45 | The most portable way to do this is to use a feature called NodePorts, which enhance a service even further. In addition to a cluster IP, the system picks a port (or the user can specify one), and every node in the cluster then forwards traffic to that port to the service. 46 | 47 | With this feature, if you can reach any node in the cluster, you can contact a service. You can use the NodePort without knowing where any of the Pods for that service are running. This can be integrated with hardware or software load balancers to expose 48 | the service further. 49 | 50 | ### Load Balancer Integration 51 | 52 | Creating a service of type LoadBalancer exposes that service to the public internet. 53 | 54 | Edit the alpaca-prod service again ( kubectl edit service alpaca-prod ) and change `spec.type` to LoadBalancer. If you do a `kubectl get services` right away, you’ll see that the EXTERNAL-IP column for alpaca-prod now says . 55 | 56 | You’ll often want to expose your application within only your private network. To achieve this, use an internal load balancer 57 | 58 | ## Advanced Details 59 | 60 | Kubernetes is built to be an extensible system. As such, there are layers that allow for more advanced integrations. 61 | 62 | ### Endpoints 63 | 64 | Some applications (and the system itself) want to be able to use services without using a cluster IP. This is done with another type of object called an Endpoints object. 65 | 66 | For every Service object, Kubernetes creates a buddy Endpoints object that contains the IP addresses for that service: 67 | 68 | ``` 69 | $ kubectl describe endpoints alpaca-prod 70 | ``` 71 | 72 | ### Manual Service Discovery 73 | 74 | Kubernetes services are built on top of `label selectors over Pods`. That means that you can use the Kubernetes API to do rudimentary service discovery without using a Service object at all! 75 | 76 | You can always use labels to identify the set of Pods you are interested in, get all of the Pods for those labels, and dig out the IP address. But keeping the correct set of labels to use in sync can be tricky. This is why the Service object was created. 77 | 78 | 79 | ### kube-proxy and Cluster IPs 80 | 81 | The kube-proxy watches for new services in the cluster via the API server. It then programs a set of iptables rules in the kernel of that host to rewrite 82 | the destinations of packets so they are directed at one of the endpoints for that service. If the set of endpoints for a service changes (due to Pods coming and going or due to a failed readiness check), the set of iptables rules is rewritten. 83 | 84 | The cluster IP itself is usually assigned by the API server as the service is created. However, when creating the service, the user can specify a specific cluster IP. Once set, the cluster IP cannot be modified without deleting and re-creating the Service 85 | object. 86 | 87 | ### Connecting External Resources to Services Inside a Cluster 88 | 89 | You can use a `NodePort` service to expose the service on the IP addresses of the nodes in the cluster. You can then either program a physical load 90 | balancer to serve traffic to those nodes, or use DNS-based load-balancing to spread traffic between the nodes. There are also a variety of open source projects (for example, HashiCorp’s Consul) that can be used to manage connectivity between in-cluster and out-of-cluster resources. -------------------------------------------------------------------------------- /Chapter 21: Multicluster Appllication Deployments/index.md: -------------------------------------------------------------------------------- 1 | # Multicluster Application Deployments 2 | 3 | In many cases, a single Kubernetes cluster is tied to a single location and thus is a single failure domain. 4 | 5 | In some cases, especially in cloud environments, the Kubernetes cluster is designed to be regional. Regional clusters span across multiple independent zones and are thus resilient to the problems in the underlying infrastructure. In addition to resiliency requirements, another strong driver of multicluster deployments is some business or application need for regional affinity. 6 | 7 | ## Before You Even Begin 8 | 9 | It is critical that you have the right foundations in place in a single cluster deployment before you consider moving to multiple clusters. 10 | 11 | Don’t use a GUI or CLI tool to create your cluster. It may seem cumbersome at first to push all changes through source control and CI/CD, but the stable foundation pays significant dividends. 12 | 13 | The same is true of the foundational components that you deploy into your clusters. These components include monitoring, logging, and security scanners, which need to be present before any application is deployed. These tools also need to be managed using infrastructure as code tools like Helm and deployed using automation. 14 | 15 | Though Kubernetes supports simple certificate-based authentication, we stronglysuggest using integrations with a global identity provider, such as Azure Active 16 | Directory or any other OpenID Connect–compatible identity provider. Ensuring thateveryone uses the same identity when accessing all of the clusters is a critical part of maintaining security best practices and avoiding dangerous behaviors like sharing certificates. 17 | 18 | Just like setting up the right unit testing and build infrastructure is critical to your application development, setting up the right foundation for managing multiple Kubernetes clusters sets the stage for stable application deployments across a broad fleet of infrastructure. In the coming sections, we’ll talk about how to build your application to operate successfully in a multicluster environment. 19 | 20 | ### Starting at the Top with a Load-Balancing Approach 21 | 22 | Access to your application starts with a domain name. This means that the start of your multicluster load-balancing strategy starts with a DNS lookup. This DNS lookup is the first choice in your load-balancing strategy. In many traditional load-balancing approaches, this DNS lookup was used for routing traffic to specific locations. This is generally referred to as “GeoDNS.” In GeoDNS, the IP address returned by the DNS lookup is tied to the physical location of the client. The IP address is generally the regional cluster that is closest to the client. 23 | 24 | The other alternative to using DNS to select your cluster is a load-balancing technique known as anycast. With anycast networking, a single static IP address is advertised from multiple locations around the internet using core routing protocols. While traditionally we think of an IP address mapping to a single machine, with anycast networking the IP address is actually a virtual IP address that is routed to a different location depending on your network location. Your traffic is routed to the “closest” location based on the distance in terms of network performance rather than geographic distance. 25 | 26 | One final consideration as you design your load balancing is whether the load balancing happens at the TCP or HTTP level. If you are writing an HTTP-based application (as most applications these days are), then using a global HTTP-aware load balancer enables you to be aware of more details of the client communication. For example,you can make load-balancing decisions based on cookies that have been set in the browser. 27 | 28 | ## Building Applications for Multiple Clusters 29 | 30 | Ideally, your application doesn’t require state, or all of the state is read-only. In such circumstances, there is little that you need to do to support multiple cluster deployments. Your application can be deployed individually to each of your clusters, a load balancer added to the top, and your multicluster deployment is complete. 31 | 32 | The challenges of replicated data and customer experience is succinctly captured by this question: “Can I read my own write?” It may seem obvious that the answer should be “Yes,” but achieving this is harder than it seems. 33 | 34 | Consistency governs how you think about replicating data. We assume that we want our data to be consistent; that is, that we will be able to read the same data regardless of where we read it from. But the complicating factor is time: how quickly must our data be consistent? 35 | 36 | Your choice of concurrency model also has significant implications for your application’s design and is difficult to change. Consequently, choosing your 37 | consistency model is an important first step before designing your application for multiple environments. 38 | 39 | ### Replicated Silos: The Simplest Cross-Regional Model 40 | 41 | The simplest way to replicate your application across multiple clusters and multiple regions is simply to copy your application into every region. Each instance of your application is an exact clone and looks exactly alike no matter which cluster it is running in. 42 | 43 | When you design your application this way, each region is its own silo. All of the data that it needs is present within the region, and once a request enters that region, it is served entirely by the containers running in that one cluster. This has significant benefits in terms of reduced complexity, but as is always the case, this comes at the cost of efficiency. 44 | 45 | Especially when taking an existing application from single cluster to multicluster, a replicated silos design is the easiest approach to use, but it is worth understanding that it comes with costs that may be sustainable initially but eventually will require your application to be refactored. 46 | 47 | ### Sharding: Regional Data 48 | 49 | Sharding your data across regions means that not all data is present in all of the clusters where your application is present and this (obviously) impacts the design of your application. The routing layer is responsible for determining whether the request needs to go to a local or cross-regional data shard. 50 | 51 | ### Better Flexibility: Microservice Routing 52 | 53 | A better approach is to treat each microservice within your application as a public-facing service in terms of its application design. It may never be expected to actually be public facing, but it should have its own global load balancer. -------------------------------------------------------------------------------- /Chapter 04: Common kubectl commands/index.md: -------------------------------------------------------------------------------- 1 | # Common kubectl commands 2 | 3 | The kubectl command-line utility is a powerful tool. 4 | 5 | ## Namespaces 6 | 7 | By default, the kubectl command-line tool interacts with the `default` namespace. If you want to use a different namespace, you can pass kubectl the `--namespace` flag. 8 | 9 | ## Contexts 10 | 11 | If you want to change the default namespace more permanently, you can use a context. This gets recorded in a kubectl configuration file, usually located at 12 | `$HOME/.kube/config`. This configuration file also stores how to both find and authen‐ticate to your cluster. For example, you can create a context with a different default 13 | namespace for your kubectl commands using: 14 | ``` 15 | $ kubectl config set-context my-context --namespace=mystuff 16 | ``` 17 | 18 | To use this newly created context, you can run: 19 | ``` 20 | $ kubectl config use-context my-context 21 | ``` 22 | 23 | ## Viewing Kubernetes API Objects 24 | 25 | Everything contained in Kubernetes is represented by a RESTful resource. Each Kubernetes 26 | object exists at a unique HTTP path; for example, https://your-k8s.com/api/v1/name‐spaces/default/pods/my-pod leads to the representation of a Pod in the default name‐ 27 | space named my-pod . The kubectl command makes HTTP requests to these URLs to access the Kubernetes objects that reside at these paths. 28 | 29 | By default, kubectl uses a human-readable printer for viewing the responses from the API server. One way to get slightly more information is to add the -o wide flag, which gives more details, on a longer line. 30 | 31 | Another common task is extracting specific fields from the object. kubectl uses the 32 | JSONPath query language to select fields in the returned object. As an example, this command will extract and print the IP address of the specified Pod: 33 | ``` 34 | $ kubectl get pods my-pod -o jsonpath --template={.status.podIP} 35 | ``` 36 | 37 | If you are interested in more detailed information about a particular object, use the describe command: 38 | ``` 39 | $ kubectl describe 40 | ``` 41 | 42 | If you would like to see a list of supported fields for each supported type of Kubernetes object, you can use the explain command: 43 | ``` 44 | $ kubectl explain pods 45 | ``` 46 | 47 | To continuously monitor the state of a particular resource, use the `--watch` flag with kubectl command. 48 | 49 | 50 | ## Creating, Updating, and Destroying Kubernetes Objects 51 | 52 | Objects in the Kubernetes API are represented as JSON or YAML files. These files are either returned by the server in response to a query or posted to the server as part of an API request. You can use these YAML or JSON files to create, update, or delete objects on the Kubernetes server. 53 | 54 | Let’s assume that you have a simple object stored in obj.yaml. You can use kubectl to create this object in Kubernetes by running: 55 | ``` 56 | $ kubectl apply -f obj.yaml 57 | ``` 58 | 59 | Similarly, after you make changes to the object, you can use the apply command again to update the object: 60 | ``` 61 | $ kubectl apply -f obj.yaml 62 | ``` 63 | 64 | The apply tool will only modify objects that are different from the current objects in the cluster. If the objects you are creating already exist in the cluster, it will simply exit successfully without making any changes. 65 | 66 | You can use the `--dry-run` flag to print the objects to the terminal without actually sending them to the server. 67 | 68 | If you feel like making interactive edits instead of editing a local file, you can instead use the edit command, which will download 69 | the latest object state and then launch an editor that contains the definition: 70 | ``` 71 | $ kubectl edit 72 | ``` 73 | After you save the file, it will be automatically uploaded back to the Kubernetes cluster. 74 | 75 | The apply command also records the history of previous configurations in an annotation within the object. You can manipulate these records with the `edit-last- 76 | applied` , `set-last-applied` , and `view-last-applied` commands. For example: 77 | ``` 78 | $ kubectl apply -f myobj.yaml view-last-applied 79 | ``` 80 | 81 | When you want to delete an object, you can simply run: 82 | ``` 83 | $ kubectl delete -f obj.yaml 84 | ``` 85 | 86 | Likewise, you can delete an object using the resource type and name: 87 | ``` 88 | $ kubectl delete 89 | ``` 90 | 91 | ## Labeling and Annotating Objects 92 | 93 | Labels and annotations are tags for your objects. You can update the labels and annotations on any Kubernetes object using the label and annotate commands. For example, to add the color=red label to a Pod named bar , you can run: 94 | ``` 95 | $ kubectl label pods bar color=red 96 | ``` 97 | 98 | The syntax for annotations is identical. 99 | 100 | By default, label and annotate will not let you overwrite an existing label. To do this, you need to add the `--overwrite` flag. 101 | If you want to remove a label, you can use the `-` syntax: 102 | ``` 103 | $ kubectl label pods bar color- 104 | ``` 105 | 106 | ## Debugging Commands 107 | 108 | You can use the following to see the logs for a running container: 109 | ``` 110 | $ kubectl logs 111 | ``` 112 | 113 | If you have multiple containers in your Pod, you can choose the container to view 114 | using the `-c` flag. By default, kubectl logs lists the current logs and exits. If you instead want to 115 | continuously stream the logs back to the terminal without exiting, you can add the `-f` 116 | (follow) command-line flag. 117 | 118 | You can also use the exec command to execute a command in a running container: 119 | ``` 120 | $ kubectl exec -it -- bash 121 | ``` 122 | 123 | You can also copy files to and from a container using the cp command: 124 | ``` 125 | $ kubectl cp : 126 | ``` 127 | 128 | To forward traffic from the local machine on port 8080 to the remote container on port 80: 129 | ``` 130 | $ kubectl port-forward 8080:80 131 | ``` 132 | 133 | To see a list of the latest 10 events on all objects in a given namespace: 134 | ``` 135 | $ kubectl get events 136 | ``` 137 | You can also stream events as they happen by adding `--watch` to the kubectl get 138 | events command. You may also wish to include `-A` to see events in all namespaces. 139 | 140 | You can use the top command to see the list of resources in use by either nodes or Pods. This 141 | command: 142 | ``` 143 | $ kubectl top nodes 144 | ``` 145 | These top commands only work if a metrics server is running in your cluster. 146 | -------------------------------------------------------------------------------- /Chapter 08: HTTP Load Balancing with Ingress/index.md: -------------------------------------------------------------------------------- 1 | # HTTP Load Balancing with Ingress 2 | 3 | Kubernetes calls its HTTP-based load-balancing system Ingress. One of the more complex aspects of the pattern is that the user has to manage the load balancer configuration file. In a dynamic environment and as the set of virtual 4 | hosts expands, this can be very complex. 5 | 6 | The Kubernetes Ingress system works to simplify this by (a) standardizing that configuration, (b) moving it to a standard 7 | Kubernetes object, and (c) merging multiple Ingress objects into a single config for the load balancer. 8 | 9 | The Ingress controller is a software system made up of two parts. The first is the `Ingress proxy`, which is exposed outside the cluster using a service of type: LoadBalancer . This proxy sends requests to “upstream” servers. The other 10 | component is the `Ingress reconciler`, or operator. The Ingress operator is responsible for reading and monitoring Ingress objects in the Kubernetes API and reconfiguring 11 | the Ingress proxy to route traffic as specified in the Ingress resource. 12 | 13 | ## Ingress Spec Versus Ingress Controllers 14 | 15 | While conceptually simple, at an implementation level, Ingress is very different from pretty much every other regular resource object in Kubernetes. Specifically, it is split into a common resource specification and a controller implementation. There is no “standard” Ingress controller that is built into Kubernetes, so the user must install one 16 | of many optional implementations. 17 | 18 | ### Installing Contour 19 | 20 | This is a controller built to configure the open source (and CNCF project) load balancer called Envoy. Envoy is built to be dynamically configured via an API. The Contour Ingress controller takes care of translating the Ingress objects into something that Envoy can understand. 21 | 22 | You can install Contour with a simple one-line invocation: 23 | ``` 24 | $ kubectl apply -f https://projectcontour.io/quickstart/contour.yaml 25 | ``` 26 | Note that this requires execution by a user who has cluster-admin permissions. 27 | 28 | After you install it, you can fetch the external address of Contour via: 29 | ``` 30 | $ kubectl get -n projectcontour service envoy -o wide 31 | ``` 32 | 33 | ### Configuring DNS 34 | 35 | To make Ingress work well, you need to configure DNS entries to the external address for your load balancer. You can map multiple hostnames to a single external endpoint and the Ingress controller will direct incoming requests to the appropriate upstream service based on that hostname. 36 | 37 | The ExternalDNS project is a cluster add-on that you can use to manage DNS records for you. ExternalDNS monitors your Kubernetes cluster and synchronizes IP addresses for Kubernetes Service resources with an external DNS provider. 38 | 39 | 40 | ## Using Ingress 41 | 42 | The simplest way to use Ingress is to have it just blindly pass everything that it sees through to an upstream service. 43 | 44 | Apply the [ingress](./simple-ingress.yaml) 45 | 46 | Verify that it was set up correctly using kubectl get and kubectl describe : 47 | ``` 48 | $ kubectl get ingress 49 | $ kubectl describe ingress simple-ingress 50 | ``` 51 | 52 | This sets things up so that any HTTP request that hits the Ingress controller is forwarded on to the alpaca service. 53 | 54 | ### Using Hostnames 55 | 56 | Things start to get interesting when we direct traffic based on properties of the request. The most common example of this is to have the Ingress system look at the 57 | HTTP host header and direct traffic based on that header. 58 | 59 | example: [host ingress](./host-ingress.yaml) 60 | 61 | ### Using Paths 62 | 63 | The next interesting scenario is to direct traffic based on not just the hostname, but also the path in the HTTP request. We can do this easily by specifying a path in 64 | the paths entry. 65 | 66 | example : [path ingress](./path-ingress.yaml) 67 | 68 | When there are multiple paths on the same host listed in the Ingress system, the longest prefix matches. So, in this example, traffic starting with /a/ is forwarded to 69 | the alpaca service, while all other traffic (starting with / ) is directed to the bandicoot service. 70 | 71 | ## Advanced Ingress Topics and Gotchas 72 | 73 | Ingress supports some other fancy features. The level of support for these features differs based on the Ingress controller implementation, and two controllers may implement a feature in slightly different ways. 74 | 75 | ### Running Multiple Ingress Controllers 76 | 77 | There are multiple Ingress controller implementations, and you may want to run multiple Ingress controllers on a single cluster. 78 | 79 | ### Running Multiple Ingress Controllers 80 | 81 | You may want to run multiple Ingress controllers on a single cluster. To solve this case, the `IngressClass` 82 | resource exists so that an Ingress resource can request a particular implementation. 83 | 84 | If you specify multiple Ingress objects, the Ingress controllers should read them all and try to merge them into a coherent configuration. However, if you specify 85 | duplicate and conflicting configurations, the behavior is undefined. 86 | 87 | ### Ingress and Namespaces 88 | 89 | First, due to an abundance of security caution, an Ingress object can refer to only an upstream service 90 | in the same namespace. This means that you can’t use an Ingress object to point a subpath to a service in another namespace. 91 | 92 | ### Path Rewriting 93 | 94 | Some Ingress controller implementations support optionally, doing path rewriting. This can be used to modify the path in the HTTP request as it gets proxied. 95 | 96 | There are multiple implementations that not only implement path rewriting, but also support regular expressions when specifying the path. For example, the NGINX controller allows regular expressions to capture parts of the path and then use that captured content when doing rewriting. 97 | 98 | ### Serving TLS 99 | 100 | When serving websites, it is becoming increasingly necessary to do so securely using TLS and HTTPS. Ingress supports this (as do most Ingress controllers). 101 | 102 | First, users need to specify a Secret with their TLS certificate and keys. Once you have the certificate uploaded, you can reference it in an Ingress object. This 103 | specifies a list of certificates along with the hostnames that those certificates should be used for. Again, if multiple Ingress objects specify certificates for 104 | the same hostname, the behavior is undefined. 105 | 106 | ### Alternate Ingress Implementations 107 | 108 | There are many different implementations of Ingress controllers, each building on the base Ingress object with unique features. It is a vibrant ecosystem. 109 | 110 | First, each cloud provider has an Ingress implementation that exposes the specific cloud-based L7 load balancer for that cloud. 111 | 112 | The most popular generic Ingress controller is probably the open source NGINX Ingress controller. 113 | 114 | [Emissary](https://github.com/emissary-ingress/emissary) and [Gloo](https://github.com/solo-io/gloo) are two other Envoy-based Ingress controllers that are focused on being API gateways. 115 | 116 | [Traefik](https://doc.traefik.io/traefik/) is a reverse proxy implemented in Go that also can function as an Ingress controller. It has a set of features and dashboards that are very developer-friendly. 117 | 118 | ## The Future of Ingress 119 | 120 | As you have seen, the Ingress object provides a very useful abstraction for configuring L7 load balancers—but it hasn’t scaled to all the features that users want and 121 | various implementations are looking to offer. 122 | 123 | The future of HTTP load balancing for Kubernetes looks to be the Gateway API, which is in the midst of development by the Kubernetes special interest group (SIG) 124 | dedicated to networking. The Gateway API project is intended to develop a more modern API for routing in Kubernetes. Though it is more focused on HTTP balanc‐ 125 | ing, Gateway also includes resources for controlling Layer 4 (TCP) balancing. -------------------------------------------------------------------------------- /Chapter 02: Creating and Running Containers/index.md: -------------------------------------------------------------------------------- 1 | # Creating and Running Containers 2 | 3 | Container images bundle a program and its dependencies into a single artifact under a root filesystem. The most popular container image format is the Docker image 4 | format, which has been standardized by the Open Container Initiative to the OCI image format. Kubernetes supports both Docker- and OCI-compatible images via 5 | Docker and other runtimes. 6 | 7 | ## Container images 8 | 9 | A container image is a binary package that encapsulates all of the files necessary to run a program inside of an OS container. The most popular and widespread container image format is the Docker image format, which was developed by the Docker open source project for packaging, distributing, and running containers using the docker command. Docker image format continues to be the de facto standard and is made up of a series of filesystem layers. Each layer adds, removes, or modifies files from the preceding layer in the filesystem. During runtime, there are a variety of different concrete implementations of such filesystems, including aufs , overlay , and overlay2. 10 | 11 | Container images are typically combined with a container configuration file, which provides instructions on how to set up the container environment and execute an 12 | application entry point. The container configuration often includes information on how to set up networking, namespace isolation, resource constraints (cgroups), 13 | and what syscall restrictions should be placed on a running container instance. The container root filesystem and configuration file are typically bundled using the Docker image format. 14 | 15 | Containers fall into two main categories: 16 | - System containers 17 | - Application containers 18 | 19 | 20 | ## Building Application Images with Docker 21 | 22 | In general, container orchestration systems like Kubernetes are focused on building and deploying distributed systems made up of application containers. 23 | 24 | ### Dockerfiles 25 | 26 | A Dockerfile can be used to automate the creation of a Docker container image. 27 | 28 | #### Optimizing Image Sizes 29 | The first thing to remember is that files that are removed by subsequent layers in the system are actually still present in the images; they’re just inaccessible. 30 | Consider the following situation: 31 | 32 | - layer A: contains a large file named 'BigFile' 33 | - layer B: removes 'BigFile' 34 | - layer C: builds on B by adding a static binary 35 | 36 | You might think that BigFile is no longer present in this image. After all, when you run the image, it is no longer accessible. But in fact it is still present in layer A, which means that whenever you push or pull the image, BigFile is still transmitted through the network, even if you can no longer access it. 37 | 38 | Another pitfall revolves around image caching and building. Remember that each layer is an independent delta from the layer below it. Every time you change a layer, it changes every layer that comes after it. Changing the preceding layers means that they need to be rebuilt, repushed, and repulled to deploy your image to development. 39 | 40 | #### Image Security 41 | 42 | When building images that will ultimately run in a production Kubernetes cluster, be sure to follow best practices for packaging and distributing applications. For example, don’t build containers with passwords baked in—and this includes not just in the final layer, but any layers in the image. Secrets and images should never be mixed. Depending on the language, you can achieve very small images with a very tight set of dependencies. This smaller set ensures that your image isn’t 43 | exposed to vulnerabilities in libraries it would never use. 44 | 45 | #### Multistage Image Builds 46 | 47 | With multistage builds, rather than producing a single image, a Docker file can actually produce multiple images. Each image is considered a stage. Artifacts can be copied from preceding stages to the current stage. Building a container image using multistage builds can reduce your final container image size by hundreds 48 | of megabytes and thus dramatically speed up your deployment times, since generally, deployment latency is gated on network performance. 49 | 50 | #### Storing Images in a Remote Registry 51 | 52 | Public registries are great for sharing images with the world because they allow for easy, unauthenticated use of the container images. You can easily distribute your software as a container image and have confidence that users everywhere will have the exact same experience. 53 | 54 | In contrast, a private registry is best for storing applications that are private to your service and that you don’t want the world to use. Additionally, private registries often provide better availability and security guarantees because they are specific to you and your images rather than serving the world. 55 | 56 | #### The Container Runtime Interface 57 | 58 | Kubernetes provides an API for describing an application deployment, but relies on a container runtime to set up an application container using the container-specific APIs native to the target OS. On a Linux system that means configuring cgroups and namespaces. The interface to this container runtime is defined by the Container Runtime Interface (CRI) standard. 59 | 60 | #### Running Containers with Docker 61 | 62 | In Kubernetes, containers are usually launched by a daemon on each node called 63 | the kubelet; however, it’s easier to get started with containers using the Docker 64 | command-line tool. 65 | 66 | ```bash 67 | $ docker run -d --name kuard \ 68 | --publish 8080:8080 \ 69 | gcr.io/kuar-demo/kuard-amd64:blue 70 | ``` 71 | 72 | This command starts the kuard container and maps ports 8080 on your local 73 | machine to 8080 in the container. 74 | 75 | ### Limiting Resource Usage 76 | 77 | Docker enables applications to use fewer resources by exposing the underlying 78 | cgroup technology provided by the Linux kernel. These capabilities are likewise used 79 | by Kubernetes to limit the resources each Pod uses. 80 | 81 | #### Limiting memory resources 82 | 83 | One of the key benefits to running applications within a container is the ability to restrict resource utilization. This allows multiple applications to coexist on the same hardware and ensures fair usage. 84 | 85 | To limit kuard to 200 MB of memory and 1 GB of swap space, use the `--memory` and 86 | `--memory-swap` flags with the docker run command. 87 | 88 | ```bash 89 | Stop and remove the current kuard container: 90 | $ docker stop kuard 91 | $ docker rm kuard 92 | 93 | Then start another kuard container using the appropriate flags to limit memory 94 | usage: 95 | $ docker run -d --name kuard \ 96 | --publish 8080:8080 \ 97 | --memory 200m \ 98 | --memory-swap 1G \ 99 | gcr.io/kuar-demo/kuard-amd64:blue 100 | ``` 101 | 102 | #### Limiting CPU resources 103 | 104 | Restrict CPU utilization using the `--cpu-shares` flag with the docker run command: 105 | 106 | ```bash 107 | $ docker run -d --name kuard \ 108 | --publish 8080:8080 \ 109 | --memory 200m \ 110 | --memory-swap 1G \ 111 | --cpu-shares 1024 \ 112 | gcr.io/kuar-demo/kuard-amd64:blue 113 | ``` 114 | 115 | ### Cleanup 116 | 117 | Once you are done building an image, you can delete it with `docker rmi` command: 118 | ``` 119 | docker rmu 120 | or 121 | docker rmi 122 | ``` 123 | 124 | Images can either be deleted via their tag name (e.g., gcr.io/kuar-demo/kuard-amd64:blue) or via their image ID. As with all ID values in the docker tool, the 125 | image ID can be shortened as long as it remains unique. Generally only three or four 126 | characters of the ID are necessary. 127 | 128 | To see the images currently on your machine, you can use the `docker images` command. 129 | You can then delete tags you are no longer using. 130 | 131 | Docker provides a tool called `docker system prune` for doing general cleanup. This 132 | will remove all stopped containers, all networks not used by at least one container, all untagged images, and all unused image layers cached as part of the build process. Use it carefully! -------------------------------------------------------------------------------- /Chapter 20: Policy and Governance for Kubernetes Clusters/index.md: -------------------------------------------------------------------------------- 1 | # Policy and Governance for Kubernetes Clusters 2 | 3 | Policy is a set of constraints and conditions for how Kubernetes resources can be configured. Governance provides the ability to verify and enforce organizational policies for allresources deployed to a Kubernetes cluster, such as ensuring all resources use current best practices, comply with security policy, or adhere to company conventions. 4 | 5 | ## Why Policy and Governance Matter 6 | 7 | NetworkPolicy allows you to specify what network services and endpoints a Pod can connect to. 8 | 9 | Here are some common examples of policies that cluster administrators often 10 | configure: 11 | - All containers must only come from a specific container registry 12 | - All Pods must be labeled with the department name and contact information 13 | - All Pods must have both CPU and memory resource limits set 14 | - All Ingress hostnames must be unique across a cluster 15 | - A certain service must not be made available on the internet 16 | - Containers must not listen on privileged ports 17 | 18 | ## Admission Flow 19 | 20 | Admission controllers operate inline as an API request flows through the Kubernetes API server and are used to either mutate or validate the API request resource 21 | before it’s saved to storage. Mutating admission controllers allow the resource to be modified; validating admission controllers do not. There are many different types of admission controllers; this chapter focuses on admission webhooks, which are dynamically configurable. They allow a cluster administrator to configure an endpoint to which the API server can send requests for evaluation by creating either a MutatingWebhookConfiguration or a ValidatingWebhookConfiguration resource. 22 | 23 | ## Policy and Governance with Gatekeeper 24 | 25 | Here, we will focus on an open source ecosystem project called [Gatekeeper](https://open-policy-agent.github.io/gatekeeper/website/docs/). Gatekeeper is a Kubernetes-native policy controller that evaluates resources based on defined policy and determines whether to allow a Kubernetes resource to be created or modified. These evaluations happen server-side as the API request flows through the Kubernetes API server, which means each cluster has a single point of processing. Processing the policy evaluations server-side means that you can install Gatekeeper on existing Kubernetes clusters without changing developer tooling, workflows, or continuous delivery pipelines. 26 | 27 | Gatekeeper uses custom resource definitions (CRDs) to define a new set of Kubernetes resources specific to configuring it, which allows cluster administrators to use familiar tools like kubectl to operate Gatekeeper. In addition, it provides real-time, meaningful feedback to the user on why a resource was denied and how to remediate the problem. These Gatekeeper-specific custom resources can be stored in source control and managed using GitOps workflows. Gatekeeper also performs resource mutation (resource modification based on defined conditions) and auditing. 28 | 29 | ### What Is Open Policy Agent? 30 | 31 | At the core of Gatekeeper is [Open Policy Agent](https://www.openpolicyagent.org/), a cloud native open source policy engine that is extensible and allows policy to be portable across different applications. Open Policy Agent (OPA) is responsible for performing all policy evaluations and returning either an admit or deny. This gives Gatekeeper access to an ecosystem of policy tooling, such as, [Conftest](https://github.com/open-policy-agent/conftest), which enables you to write policy tests and implement them in continuous integration pipelines before deployment. 32 | 33 | Open Policy Agent exclusively uses a native query language called Rego for all policies. 34 | 35 | ### Installing Gatekeeper 36 | 37 | Before you start configuring policies, you’ll need to install Gatekeeper. Gatekeeper components run as Pods in the gatekeeper-system namespace and configure a 38 | webhook admission controller. 39 | 40 | ``` 41 | $ helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts 42 | $ helm install gatekeeper/gatekeeper --name-template=gatekeeper \ 43 | --namespace gatekeeper-system --create-namespace 44 | ``` 45 | 46 | ### Configuring Policies 47 | 48 | First, you’ll need to configure the policy we need to create a custom resource called a constraint template. This is usually done by a cluster administrator. Now you can create a constraint resource to put the policy into effect (again, playing the role of the cluster administrator). 49 | 50 | ### Understanding Constraint Templates 51 | 52 | This constraint template has an apiVersion and kind that are part of the custom resources used only by Gatekeeper. Under the spec section, you’ll see the name 53 | K8sAllowedRepos : remember that name, because you’ll use it as the constraint kind when creating constraints. 54 | 55 | ### Creating Constraints 56 | 57 | Let’s take a closer look at the constraint in the [Example](./allowedrepos-constraint.yaml), which allows only container images that originate from gcr.io/kuar-demo/. You may notice that the constraint is of the kind “K8sAllowedRepos,” which was defined as part of the constraint template. It also defines an enforcementAction of “deny,” meaning that noncompliant resources will be denied. The match portion defines the scope of this constraint, all Pods in the default namespace. Finally, the parameters section is required to satisfy the constraint template (an array of strings). 58 | 59 | ### Audit 60 | 61 | Gatekeeper’s audit capabilities allow cluster administrators to get a list of current, noncompliant resources on a cluster. To audit the list of noncompliant resources for a given constraint, run a kubectl get constraint on that constraint and specify that you want the output in YAML format as follows: 62 | 63 | ``` 64 | $ kubectl get constraint repo-is-kuar-demo -o yaml 65 | ``` 66 | 67 | Under the status section, you can see the auditTimestamp , which is the last time the audit was run. totalViolations lists the number of resources that violate this constraint. The violations section lists the violations. We can see that the nginx-noncompliant Pod is in violation and the message with the details why. Using a constraint enforcementAction of “dryrun” along withaudit is a powerful way to confirm that your policy is having the 68 | desired impact. It also creates a workflow to bring resources into compliance. 69 | 70 | ### Mutation 71 | 72 | By default, Gatekeeper is only deployed as a validating admission webhook, but it can be configured to operate as a mutating admission webhook. To enable mutation, please refer to the [doc](https://open-policy-agent.github.io/gatekeeper/website/docs/mutation/). We will assume that 73 | Gatekeeper is configured correctly to support mutation. Example 20-6 defines a mutation assignment that matches all Pods except those in the “system” namespace, 74 | and assigns a value of “Always” to imagePullPolicy. 75 | 76 | Create the mutation assignment: 77 | ``` 78 | $ kubectl apply -f imagepullpolicyalways-mutation.yaml 79 | assign.mutations.gatekeeper.sh/demo-image-pull-policy created 80 | ``` 81 | 82 | Now create a Pod. This Pod doesn’t have imagePullPolicy explicitly set, so by default this field is set to “IfNotPresent.” However, we expect Gatekeeper to mutate this fieldto “Always”: 83 | 84 | ``` 85 | $ kubectl apply -f compliant-pod.yaml 86 | pod/kuard created 87 | ``` 88 | 89 | Validate that the imagePullPolicy has been successfully mutated to “Always” by running the following: 90 | ``` 91 | $ kubectl get pods kuard -o=jsonpath="{.spec.containers[0].imagePullPolicy}" 92 | ``` 93 | 94 | Mutating admission happens before validating admission, so createconstraints that validate the mutations you expect to apply to the specific resource. 95 | 96 | ### Data Replication 97 | 98 | Gatekeeper can be configured to cache specific resources into Open Policy Agent to allow comparisons across resources. The resource in [Example](./config-sync.yaml) configures Gatekeeper to cache Namespace and Pod resources. 99 | 100 | ### Policy Library 101 | 102 | The Gatekeeper project has a great [policy library](https://github.com/open-policy-agent/gatekeeper-library). It contains a general library with the most common policies as well as pod-security-policy library that models the capabilities of the PodSecurityPolicy API as Gatekeeper policy. -------------------------------------------------------------------------------- /Chapter 09: ReplicaSets/index.md: -------------------------------------------------------------------------------- 1 | # ReplicaSets 2 | 3 | More often than not, you want multiple replicas of a container running at a particular time for a variety of reasons: 4 | - Redundancy 5 | - Scale 6 | - Sharding 7 | 8 | A ReplicaSet acts as a cluster-wide Pod manager, ensuring that the right types and numbers of Pods are running at all times. 9 | 10 | The easiest way to think of a ReplicaSet is that it combines a cookie cutter and a desired number of cookies into a single API object. 11 | 12 | The easiest way to think of a ReplicaSet is that it combines a cookie cutter and a desired number of cookies into a single API object. 13 | 14 | ## Reconciliation Loops 15 | 16 | The central concept behind a reconciliation loop is the notion of desired state versus observed or current state. Desired state is the state you want. With a ReplicaSet, it is the desired number of replicas and the definition of the Pod to replicate. 17 | 18 | ## Relating Pods and ReplicaSets 19 | 20 | Decoupling is a key theme in Kubernetes. In particular, it’s important that all of the core concepts of Kubernetes are modular with respect to each other and that they 21 | are swappable and replaceable with other components. In this spirit, the relationship between ReplicaSets and Pods is loosely coupled. Though ReplicaSets create and 22 | manage Pods, they do not own the Pods they create. ReplicaSets use label queries to identify the set of Pods they should be managing. In a similar decoupling, ReplicaSets that create multiple Pods and the services 23 | that load balance to those Pods are also totally separate, decoupled API objects. 24 | 25 | ### Adopting Existing Containers 26 | 27 | Because ReplicaSets are decoupled from the Pods they manage, you can simply create a ReplicaSet that will “adopt” the existing Pod and scale out additional copies of those containers. In this way, you can seamlessly move from a single imperative Pod to a replicated set of Pods managed by a ReplicaSet. 28 | 29 | ### Quarantining Containers 30 | 31 | You can modify the set of labels on the sick Pod. Doing so will disassociate it from the ReplicaSet (and service) so that you can debug the Pod. The ReplicaSet controller will notice that a Pod is missing and create a new copy, but because the Pod is still running, it is available to developers for interactive debugging, which is significantly more valuable than debugging from logs. 32 | 33 | ## Designing with ReplicaSets 34 | 35 | ReplicaSets are designed to represent a single, scalable microservice inside your architecture. heir key characteristic is that every Pod the ReplicaSet controller 36 | creates is entirely homogeneous. Typically, these Pods are then fronted by a Kubernetes service load balancer, which spreads traffic across the Pods that make up the service. 37 | 38 | ## ReplicaSet Spec 39 | 40 | Like all objects in Kubernetes, ReplicaSets are defined using a specification. A spec section that describes the number of Pods (replicas) that should be running cluster-wide at any given time, and a Pod template that describes the Pod to be created when the defined number of replicas is not met. 41 | 42 | Example: [Simple ReplicaSet Spec](./kuard-rs.yaml) 43 | 44 | ### Pod Templates 45 | 46 | When the number of Pods in the current state is less than 47 | the number of Pods in the desired state, the ReplicaSet controller will create new Pods using a template contained in the ReplicaSet specification. The Kubernetes ReplicaSet controller creates and submits a Pod manifest based on the Pod template directly to the API server. 48 | 49 | ### Labels 50 | 51 | ReplicaSets monitor cluster state using a set of Pod labels to filter Pod listings and track Pods running within a cluster. When initially created, a ReplicaSet 52 | fetches a Pod listing from the Kubernetes API and filters the results by labels. 53 | 54 | ### Creating a ReplicaSet 55 | 56 | ReplicaSets are created by submitting a ReplicaSet object to the Kubernetes API. 57 | 58 | Use the kubectl apply command to submit the kuard ReplicaSet to the Kubernetes API: 59 | 60 | ``` 61 | $ kubectl apply -f kuard-rs.yaml 62 | replicaset "kuard" created 63 | ``` 64 | 65 | ### Inspecting a ReplicaSet 66 | 67 | If you are interested in further details about a ReplicaSet, you can use the describe command to provide much more information about its state. 68 | 69 | ``` 70 | $ kubectl describe rs kuard 71 | 72 | Name: kuard 73 | Namespace: default 74 | Selector: app=kuard,version=2 75 | Labels: app=kuard 76 | version=2 77 | Annotations: 78 | Replicas: 1 current / 1 desired 79 | Pods Status: 1 Running / 0 Waiting / 0 Succeeded / 0 Failed 80 | Pod Template: 81 | Labels: app=kuard 82 | version=2 83 | Containers: 84 | kuard: 85 | Image: gcr.io/kuar-demo/kuard-amd64:green 86 | Port: 87 | Host Port: 88 | Environment: 89 | Mounts: 90 | Volumes: 91 | Events: 92 | Type Reason Age From Message 93 | ---- ------ ---- ---- ------- 94 | Normal SuccessfulCreate 20s replicaset-controller Created pod: kuard-sfjlv 95 | 96 | ``` 97 | 98 | ### Finding a ReplicaSet from a Pod 99 | 100 | Sometimes you may wonder if a Pod is being managed by a ReplicaSet, and if it is, which one. To enable this kind of discovery, the ReplicaSet controller adds an `ownerReferences` section to every Pod that it creates. 101 | If you run the following, look for the ownerReferences section: 102 | 103 | ``` 104 | $ kubectl get pods -o=jsonpath='{.metadata.ownerReferences[0].name}' 105 | ``` 106 | 107 | If applicable, this will list the name of the ReplicaSet that is managing this Pod. 108 | 109 | ### Finding a Set of Pods for a ReplicaSet 110 | 111 | First, get the set of labels using the kubectl describe command. In the previous example, the label selector was app=kuard,version=2. 112 | To find the Pods that match this selector, use the 113 | `--selector` flag or the shorthand `-l`: 114 | 115 | ``` 116 | $ kubectl get pods -l app=kuard,version=2 117 | ``` 118 | 119 | This is exactly the same query that the ReplicaSetexecutes to determine the current number of Pods. 120 | 121 | 122 | ## Scaling ReplicaSets 123 | 124 | You can scale ReplicaSets up or down by updating the `spec.replicas` key on the ReplicaSet object stored in Kubernetes. When you scale up a ReplicaSet, it submits 125 | new Pods to the Kubernetes API using the Pod template defined on the ReplicaSet. 126 | 127 | ### Imperative Scaling with kubectl scale 128 | 129 | The easiest way to achieve this is using the scale command in kubectl. For example, to scale up to four replicas, you could run: 130 | 131 | ``` 132 | $ kubectl scale replicasets kuard --replicas=4 133 | ``` 134 | 135 | ## Declaratively Scaling with kubectl apply 136 | 137 | In a declarative world, you make changes by editing the configuration file in version control and then applying those changes to your cluster. To scale the kuard Replica‐ 138 | Set, edit the kuard-rs.yaml configuration file and set the replicas count to 3: 139 | 140 | ``` 141 | ... 142 | spec: 143 | replicas: 3 144 | ... 145 | ``` 146 | 147 | ### Autoscaling a ReplicaSet 148 | 149 | While there will be times when you want to have explicit control over the number of replicas in a ReplicaSet, often you simply want to have “enough” replicas. The definition varies depending on the needs of the containersin the ReplicaSet. For example, with a web server like NGINX, you might want to scale due to CPU usage. For an in-memory cache, you might want to scale with memory consumption. In 150 | some cases, you might want to scale in response to custom application metrics. Kubernetes can handle all of these scenarios via Horizontal Pod Autoscaling (HPA). 151 | 152 | ``` 153 | Autoscaling requires the presence of the metrics-server in your cluster. The metrics-server keeps track of metrics and provides an API for consuming metrics that HPA uses when making scaling decisions. 154 | ``` 155 | 156 | Scaling based on CPU usage is the most common use case for Pod autoscaling. You can also scale based on memory usage. CPU-based autoscaling is most useful for request-based systems that consume CPU proportionally to the number of requests they are receiving, while using a relatively static amount of memory. 157 | 158 | To scale a ReplicaSet, you can run a command like the following: 159 | ``` 160 | $ kubectl autoscale rs kuard --min=2 --max=5 --cpu-percent=80 161 | ``` 162 | 163 | To view, modify, or delete this resource, you can use 164 | the standard kubectl commands: 165 | 166 | ``` 167 | $ kubectl get hpa 168 | ``` 169 | 170 | It’s a bad idea to combine autoscaling with imperative or 171 | declarative management of the number of replicas. If both you and an autoscaler are attempting to modify the number of replicas, it’s highly likely that you will clash, resulting in unexpected behavior. 172 | 173 | ## Deleting ReplicaSets 174 | 175 | When a ReplicaSet is no longer required, it can be deleted using the kubectl delete command. By default, this also deletes the Pods that are managed by the ReplicaSet: 176 | 177 | ``` 178 | $ kubectl delete rs kuard 179 | ``` 180 | 181 | If you don’t want to delete the Pods that the ReplicaSet is managing, you can set the `--cascade` flag to false to ensure only the ReplicaSet object is deleted and not the 182 | Pods: 183 | 184 | ``` 185 | $ kubectl delete rs kuard --cascade=false 186 | ``` -------------------------------------------------------------------------------- /Chapter 13: ConfigMaps and Secrets/index.md: -------------------------------------------------------------------------------- 1 | # ConfigMaps and Secrets 2 | 3 | ConfigMaps are used to provide configuration information for workloads. This can be either fine-grained information like a string or a composite value in the form of a file. Secrets are similar to ConfigMaps but focus on making sensitive information available to the workload. 4 | They can be used for things like credentials or TLS certificates. 5 | 6 | ## ConfigMaps 7 | 8 | One way to think of a ConfigMap is as a Kubernetes object that defines a small filesystem. Another way is as a set of variables that can be used when defining the environment or command line for your containers. 9 | 10 | ### Creating ConfigMaps 11 | 12 | First, suppose we have a file on disk (called my-config.txt) that we want to make available to the Pod. 13 | 14 | Next, let’s create a ConfigMap with that file. We’ll also add a couple of simple key/value pairs here. These are referred to as literal values on the command line: 15 | 16 | ``` 17 | $ kubectl create configmap my-config \ 18 | --from-file=my-config.txt \ 19 | --from-literal=extra-param=extra-value \ 20 | --from-literal=another-param=another-value 21 | ``` 22 | 23 | The equivalent YAML for the ConfigMap object we just created is as follows: 24 | ``` 25 | $ kubectl get configmaps my-config -o yaml 26 | 27 | apiVersion: v1 28 | data: 29 | another-param: another-value 30 | extra-param: extra-value 31 | my-config.txt: |- 32 | # This is a sample config file that I might use to configure an application 33 | parameter1 = value1 34 | parameter2 = value2 35 | kind: ConfigMap 36 | metadata: 37 | creationTimestamp: "2023-01-27T14:43:16Z" 38 | name: my-config 39 | namespace: default 40 | resourceVersion: "507356" 41 | uid: 975e9820-5414-402c-9800-2f0186d0f8d8 42 | ``` 43 | 44 | As you can see, the ConfigMap is just some key/value pairs stored in an object. 45 | 46 | ### Using a ConfigMap 47 | 48 | There are three main ways to use a ConfigMap: 49 | 50 | - **Filesystem**: You can mount a ConfigMap into a Pod. A file is created for each entry based on the key name. The contents of that file are set to the value. 51 | - **Environment variable**: A ConfigMap can be used to dynamically set the value of an environment variable. 52 | - **Command-line argument**: Kubernetes supports dynamically creating the command line for a container based on ConfigMap values. 53 | 54 | Let’s create a manifest for kuard that pulls all of these together, as shown in 55 | [Example](./kuard-config.yaml). 56 | 57 | For the filesystem method, we create a new volume inside the Pod and give it the name `config-volume`. We then define this volume to be a ConfigMap volume and point at the ConfigMap to mount. We have to specify where this gets mounted into the kuard container with a volumeMount. In this case, we are mounting it at `/config`. Environment variables are specified with a special valueFrom member. This references the ConfigMap and the data key to use within that ConfigMap. Command-line arguments build on environment variables. Kubernetes will perform the correct substitution with a special $() syntax. 58 | 59 | ## Secrets 60 | 61 | While ConfigMaps are great for most configuration data, there is certain data that is extra sensitive. This includes passwords, security tokens, or other types of private keys. Collectively, we call this type of data “Secrets.” 62 | 63 | Secrets enable container images to be created without bundling sensitive data. This allows containers to remain portable across environments. Secrets are exposed to 64 | Pods via explicit declaration in Pod manifests and the Kubernetes API. 65 | 66 | ### Creating Secrets 67 | 68 | Secrets are created using the Kubernetes API or the kubectl command-line tool. Secrets hold one or more data elements as a collection of key/value pairs. 69 | 70 | With the kuard.crt and kuard.key files stored locally, we are ready to create a Secret. Create a Secret named kuard-tls using the `create secret` command: 71 | 72 | ``` 73 | $ kubectl create secret generic kuard-tls \ 74 | --from-file=kuard.crt \ 75 | --from-file=kuard.key 76 | ``` 77 | 78 | The kuard-tls Secret has been created with two data elements. Run the following command to get details: 79 | 80 | ``` 81 | $ kubectl describe secrets kuard-tls 82 | ``` 83 | 84 | ### Consuming Secrets 85 | 86 | Secrets can be consumed using the Kubernetes REST API by applications that know how to call that API directly. However, our goal is to keep applications portable. Not 87 | only should they run well in Kubernetes, but they should run, unmodified, on other platforms. 88 | 89 | Instead of accessing Secrets through the API server, we can use a Secrets volume. Secret data can be exposed to Pods using the Secrets volume type. 90 | 91 | Each data element of a Secret is stored in a separate file under the target mount point specified in the volume mount. The kuard-tls Secret contains two data elements: 92 | `kuard.crt` and `kuard.key`. Mounting the kuard-tls Secrets volume to /tls results in the following files: 93 | /tls/kuard.crt 94 | /tls/kuard.key 95 | 96 | The Pod manifest in [Example](./kuard-secret.yaml) demonstrates how to declare a Secrets volume, which exposes the kuard-tls Secret to the kuard container under /tls. 97 | 98 | ### Private Container Registries 99 | 100 | Image pull Secrets leverage the Secrets API to automate the distribution of private registry credentials. Image pull Secrets are stored just like regular Secrets but are 101 | consumed through the `spec.imagePullSecrets` Pod specification field. 102 | 103 | Use kubectl create secret docker-registry to create this special kind of Secret: 104 | 105 | ``` 106 | $ kubectl create secret docker-registry my-image-pull-secret \ 107 | --docker-username= \ 108 | --docker-password= \ 109 | --docker-email= 110 | ``` 111 | 112 | Enable access to the private repository by referencing the image pull secret in the Pod manifest file, as shown in [Example](./kuard-secret-ips.yaml). 113 | 114 | ## Managing ConfigMaps and Secrets 115 | 116 | ConfigMaps and Secrets are managed through the Kubernetes API. The usual create, delete, get, and describe commands work for manipulating these objects. 117 | 118 | ### Listing 119 | 120 | You can use the `kubectl get secrets` command to list all Secrets in the current namespace. Similarly, you can list all of the ConfigMaps in a namespace: 121 | 122 | ``` 123 | $ kubectl get secrets 124 | $ kubectl get configmaps 125 | ``` 126 | 127 | kubectl describe can be used to get more details on a single object: 128 | 129 | ``` 130 | $ kubectl describe configmap my-config 131 | ``` 132 | 133 | Finally, you can see the raw data (including values in Secrets!) by using a command similar to the following: `kubectl get configmap my-config -o yaml` or `kubectl get secret kuard-tls -o yaml` 134 | 135 | ### Creating 136 | 137 | The easiest way to create a Secret or a ConfigMap is via `kubectl create secret generic` or `kubectl create configmap`. There are a variety of ways to specify the 138 | data items that go into the Secret or ConfigMap. These can be combined in a single command: 139 | 140 | - `--from-file=` 141 | Load from the file with the Secret data key that’s the same as the filename. 142 | - `--from-file==` 143 | Load from the file with the Secret data key explicitly specified. 144 | - `--from-file=` 145 | Load all the files in the specified directory where the filename is an acceptable 146 | key name. 147 | - `--from-literal==` 148 | Use the specified key/value pair directly. 149 | 150 | ### Updating 151 | 152 | You can update a ConfigMap or Secret and have it reflected in running applications. 153 | 154 | #### Update from file 155 | 156 | If you have a manifest for your ConfigMap or Secret, you can just edit it directly and replace it with a new version using `kubectl replace -f `. You can 157 | also use `kubectl apply -f ` if you previously created the resource with 158 | kubectl apply. 159 | 160 | #### Re-create and update 161 | 162 | If you store the inputs into your ConfigMaps or Secrets as separate files on disk, you can use kubectl to re-create the manifest and then use it to update the object, which will look something like this: 163 | 164 | ``` 165 | $ kubectl create secret generic kuard-tls \ 166 | --from-file=kuard.crt --from-file=kuard.key \ 167 | --dry-run -o yaml | kubectl replace -f - 168 | ``` 169 | 170 | This command line first creates a new Secret with the same name as our existing Secret. We then pipe that to kubectl 171 | replace and use -f - to tell it to read from stdin. In this way, we can update a Secret from files on disk without having to manually base64-encode data. 172 | 173 | #### Edit current version 174 | 175 | The final way to update a ConfigMap is to use `kubectl edit` to bring up a version of the ConfigMap in your editor so you can tweak it: 176 | 177 | ``` 178 | $ kubectl edit configmap my-config 179 | ``` 180 | 181 | #### Live updates 182 | 183 | Once a ConfigMap or Secret is updated using the API, it’ll be automatically pushed to all volumes that use that ConfigMap or Secret. It may take a few seconds, but 184 | the file listing and contents of the files, as seen by kuard, will be updated with these new values. Using this live update feature, you can update the configuration of 185 | applications without restarting them. 186 | 187 | Currently there is no built-in way to signal an application when a new version of a ConfigMap is deployed. It is up to the application (or some helper script) to look for the config files to change and reload them. -------------------------------------------------------------------------------- /Chapter 14: Role-Based Access Control for Kubernetes/index.md: -------------------------------------------------------------------------------- 1 | # Role-Based Access Control for Kubernetes 2 | 3 | Role-based access control provides a mechanism for restricting both access to and actions on Kubernetes APIs to ensure that only authorized users have access. RBAC 4 | is a critical component to both harden access to the Kubernetes cluster where you are deploying your application and (possibly more importantly) prevent unexpected accidents where one person in the wrong namespace mistakenly takes down production when they think they are destroying their test cluster. 5 | 6 | Every request to Kubernetes is first authenticated. Authentication provides the identity of the caller issuing the request. It could be as simple as saying that the request is unauthenticated, or it could integrate deeply with a pluggable authentication provider (e.g., Azure Active Directory) to establish an identity within that third-party system. 7 | 8 | Once users have been authenticated, the authorization phase determines whether they are authorized to perform the request. Authorization is a combination of the 9 | identity of the user, the resource (effectively the HTTP path), and the verb or action the user is attempting to perform. If the particular user is authorized to perform that action on that resource, then the request is allowed to proceed. Otherwise, an HTTP 10 | 403 error is returned. 11 | 12 | ## Role-Based Access Control 13 | 14 | To properly manage access in Kubernetes, it’s critical to understand how identity, roles, and role bindings interact to control who can do what with which resources. 15 | 16 | ### Identity in Kubernetes 17 | 18 | Every request to Kubernetes is associated with some identity. Even a request with no identity is associated with the `system:unauthenticated` group. Kubernetes makes a distinction between user identities and service account identities. Service accounts are created and managed by Kubernetes itself and are generally associated with components running inside the cluster. User accounts are all other accounts associated with actual users of the cluster, and often include automation like continuous delivery services that run outside the cluster. 19 | 20 | Kubernetes uses a generic interface for authentication providers. Each of the providers supplies a username and, optionally, the set of groups to which the user belongs. 21 | 22 | Kubernetes supports a number of authentication providers, including: 23 | - HTTP Basic Authentication (largely deprecated) 24 | - x509 client certificates 25 | - Static token files on the host 26 | - Cloud authentication providers, such as Azure Active Directory and AWS Identity and Access Management (IAM) 27 | - Authentication webhooks 28 | 29 | ### Roles and Role Bindings in Kubernetes 30 | 31 | Identity is just the beginning of authorization in Kubernetes. Once Kubernetes knows the identity of the request, it needs to determine if the request is authorized for that user. To achieve this, it uses roles and role bindings. 32 | 33 | In Kubernetes, two pairs of related resources represent roles and role bindings. One pair is scoped to a namespace (Role and RoleBinding), while the other pair is scoped to the cluster (ClusterRole and ClusterRoleBinding). 34 | 35 | Role resources are namespaced and represent capabilities within that single namespace. You cannot use namespaced roles for nonnamespaced resources (e.g., CustomResourceDefinitions), and binding a Role‐ 36 | Binding to a role only provides authorization within the Kubernetes namespace that contains both the Role and the RoleBinding. 37 | 38 | Sometimes you need to create a role that applies to the entire cluster, or you want to limit access to cluster-level resources. To achieve this, you use the ClusterRole and ClusterRoleBinding resources. They are largely identical to their namespaced peers, but are cluster-scoped. 39 | 40 | #### Verbs for Kubernetes roles 41 | 42 | Roles are defined in terms of both a resource (e.g., Pods) and a verb that describes 43 | an action that can be performed on that resource. 44 | 45 | #### Using built-in roles 46 | 47 | Kubernetes has a large number of built-in cluster roles for well-known system identities (e.g., a 48 | scheduler) that require a known set of capabilities. You can view these by running: 49 | 50 | ``` 51 | $ kubectl get clusterroles 52 | ``` 53 | 54 | Most clusters already have numerous ClusterRole bindings set up, and you can view 55 | these bindings with `kubectl get clusterrolebindings`. 56 | 57 | #### Auto-reconciliation of built-in roles 58 | 59 | When the Kubernetes API server starts up, it automatically installs a number of default ClusterRoles that are defined in the code of the API server itself. This means that if you modify any built-in cluster role, those modifications are transient. 60 | Whenever the API server is restarted (e.g., for an upgrade), your changes will be overwritten. To prevent this from happening, before you make any other modifications, you need to add the rbac.authorization.kubernetes.io/autoupdate annotation with a 61 | value of false to the built-in ClusterRole resource. If this annotation is set to false, the API server will not overwrite the modified ClusterRole resource. 62 | 63 | By default, the Kubernetes API server installs a cluster role that allows system:unauthenticated users access to the API server’s API discovery endpoint. For any cluster exposed to a hostile environment (e.g., the public internet) this is a bad idea, and there has 64 | been at least one serious security vulnerability via this exposure. If you are running a Kubernetes service on the public internet or an other hostile environment, you should ensure that the `--anonymous-auth=false` flag is set on your API server. 65 | 66 | ## Techniques for Managing RBAC 67 | 68 | Fortunately, there are several tools and techniques that make managing RBAC easier. 69 | 70 | ### Testing Authorization with can-i 71 | 72 | The first useful tool is the `auth can-i` command for kubectl. This tool is used for testing whether a specific user can perform a specific action. You can use `can-i` to validate configuration settings as you configure your cluster, or you can ask users to use the tool to validate their access when filing errors or bug reports. 73 | 74 | In its simplest usage, the can-i command takes a verb and a resource. For example, this command will indicate if the current kubectl user is authorized to create Pods: 75 | 76 | ``` 77 | $ kubectl auth can-i create pods 78 | ``` 79 | 80 | You can also test subresources like logs or port-forwarding with the --subresource 81 | command-line flag: 82 | 83 | ``` 84 | $ kubectl auth can-i get pods --subresource=logs 85 | ``` 86 | 87 | ### Managing RBAC in Source Control 88 | 89 | The kubectl command-line tool provides a reconcile command that operates somewhat like kubectl apply and will reconcile a set of roles and role bindings 90 | with the current state of the cluster. You can run: 91 | ``` 92 | $ kubectl auth reconcile -f some-rbac-config.yaml 93 | ``` 94 | 95 | ## Advanced Topics 96 | 97 | But when managing a large number of users or roles, there are additional advanced capabilities you can use to manage RBAC at scale. 98 | 99 | ### Aggregating ClusterRoles 100 | 101 | Kubernetes RBAC supports the usage of an aggregation rule to combine multiple roles in a new role. This new role combines all of the capabilities of all of the aggregate roles, and any changes to any of the constituent subroles will automatically be propogated back into the aggregate role. 102 | 103 | In this particular case, the `aggregationRule` field in the ClusterRole resource contains a `clusterRoleSelector` field, which in turn is a label selector. All ClusterRole resources that match this selector are dynamically aggregated into the rules array in the aggregate ClusterRole resource. 104 | 105 | A best practice for managing ClusterRole resources is to create a number of fine-grained cluster roles and then aggregate them to form higher-level or broader cluster roles. This is how the built-in cluster roles are defined. For example, you can see that 106 | the built-in edit role looks like this: 107 | 108 | ``` 109 | apiVersion: rbac.authorization.k8s.io/v1 110 | kind: ClusterRole 111 | metadata: 112 | name: edit 113 | ... 114 | aggregationRule: 115 | clusterRoleSelectors: 116 | - matchLabels: 117 | rbac.authorization.k8s.io/aggregate-to-edit: "true" 118 | ... 119 | ``` 120 | 121 | This means that the edit role is defined to be the aggregate of all ClusterRole objects that have a label of `rbac.authorization.k8s.io/aggregate-to-edit` set to true. 122 | 123 | ### Using Groups for Bindings 124 | 125 | When you bind a group to a Role or ClusterRole, anyone who is a member of that group gains access to the resources and verbs defined by that role. Thus, to enable any individual to gain access to the group’s role, that individual needs to be added to the group. 126 | 127 | Many group systems enable “just in time” (JIT) access, such that people are only temporarily added to a group in response to an event (say, a page in the middle of the 128 | night) rather than having standing access. This means that you can both audit who had access at any particular time and ensure that, in general, even a compromised 129 | identity can’t have access to your production infrastructure. 130 | 131 | Finally, in many cases, these same groups are used to manage access to other resources, from facilities to documents and machine logins. Thus, using the same 132 | groups for access control to Kubernetes dramatically simplifies management. 133 | 134 | To bind a group to a ClusterRole, use a Group kind for the subject in the binding: 135 | ``` 136 | ... 137 | subjects: 138 | - apiGroup: rbac.authorization.k8s.io 139 | kind: Group 140 | name: my-great-groups-name 141 | ... 142 | ``` -------------------------------------------------------------------------------- /Chapter 05: Pods/index.md: -------------------------------------------------------------------------------- 1 | # Pods 2 | 3 | Kubernetes groups multiple containers into a single atomic unit called a Pod. (The name goes with the whale theme of Docker containers, since a pod is also a group of whales.) 4 | 5 | ## Pods in Kubernetes 6 | 7 | A Pod is a collection of application containers and volumes running in the same execution environment. Pods, not containers, are the smallest deployable artifact in a Kubernetes cluster. This means all of the containers in a Pod always land on the same machine. 8 | 9 | Each container within a Pod runs in its own cgroup, but they share a number of Linux namespaces. 10 | 11 | Applications running in the same Pod share the same IP address and port space(network namespace), have the same hostname (UTS namespace), and can commu‐nicate using native interprocess communication channels over System V IPC or POSIX message queues (IPC namespace). 12 | 13 | ### Thinking with Pods 14 | 15 | In general, the right question to ask yourself when designing Pods is “Will these containers work correctly, if they land on different machines?” If the answer is no, a Pod is the correct grouping for the containers. If the answer is yes, using multiple Pods is probably the correct solution. 16 | 17 | ### The Pod Manifest 18 | 19 | Kubernetes strongly believes in declarative configuration, which means that you write down the desired state of the world in a configuration file and then submit that configuration to a service that takes actions to ensure the desired state becomes the actual state. The Kubernetes API server accepts and processes Pod manifests before storing them in persistent storage (etcd). 20 | 21 | Scheduling multiple replicas of the same application onto the same machine is worse for reliability, since the machine is a single failure domain. Kubernetes scheduler tries to ensure that Pods from the same application are distributed onto different machines for reliability in the presence of such failures. Once scheduled to a node, Pods don’t move and must be explicitly destroyed and rescheduled. 22 | 23 | ### Creating a pod 24 | 25 | The simplest way to create a Pod is via the imperative kubectl run command. For example, to run our same kuard server, use: 26 | 27 | ``` 28 | $ kubectl run kuard --image=gcr.io/kuar-demo/kuard-amd64:blue 29 | ``` 30 | 31 | ### Creating a Pod Manifest 32 | 33 | Pod manifests include a couple of key fields and attributes: namely, a metadata section for describing the Pod and its labels, a spec section for describing volumes, and a list of containers that will run in the Pod. You can achieve similar result as previous section like below: 34 | 35 | ```yaml 36 | apiVersion: v1 37 | kind: Pod 38 | metadata: 39 | name: kuard 40 | spec: 41 | containers: 42 | - image: gcr.io/kuar-demo/kuard-amd64:blue 43 | name: kuard 44 | ports: 45 | - containerPort: 8080 46 | name: http 47 | protocol: TCP 48 | ``` 49 | 50 | Use the kubectl apply command to launch a single instance of kuard: 51 | 52 | ``` 53 | $ kubectl apply -f kuard-pod.yaml 54 | ``` 55 | 56 | The Pod manifest will be submitted to the Kubernetes API server. The Kubernetes system will then schedule that Pod to run on a healthy node in the cluster, where the kubelet daemon will monitor it. 57 | 58 | ### Listing Pods 59 | 60 | Using the kubectl command-line tool, we can list all Pods running in the cluster. 61 | 62 | ``` 63 | kubectl get pods 64 | ``` 65 | 66 | If you ran this command immediately after the Pod was created, you might see: 67 | 68 | ``` 69 | NAME READY STATUS RESTARTS AGE 70 | kuard 0/1 pending 0 1s 71 | ``` 72 | 73 | The Pending state indicates that the Pod has been submitted but hasn’t been scheduled yet. If a more significant error occurs, such as an attempt to create a Pod with a container image that doesn’t exist, it will also be listed in the status field. 74 | 75 | Adding `-o wide` to any kubectl command will print out 76 | slightly more information (while still keeping the information to a single line). Adding `-o json` or `-o yaml` will print out the complete objects in JSON or YAML, respectively. 77 | 78 | ### Pod Details 79 | 80 | To find out more information about a Pod (or any Kubernetes object), you can use the kubectl describe command. For example, to describe the Pod we previously created, you can run: 81 | 82 | ``` 83 | $ kubectl describe pods kuard 84 | ``` 85 | 86 | ### Deleting a Pod 87 | 88 | When it is time to delete a Pod, you can delete it either by name: 89 | ``` 90 | $ kubectl delete pods/kuard 91 | ``` 92 | or you can use the same file that you used to create it: 93 | ``` 94 | $ kubectl delete -f kuard-pod.yaml 95 | ``` 96 | 97 | All Pods have a termination grace period. By default, this is 30 seconds. When a Pod is transitioned 98 | to Terminating , it no longer receives new requests. In a serving scenario, the grace period is important for reliability because it allows the Pod to finish any active requests that it may be in the middle of processing before it is terminated. 99 | 100 | 101 | ## Accessing Your Pod 102 | 103 | Now that your Pod is running, you’re going to want to access it for a variety of reasons. 104 | 105 | ### Getting More Information with Logs 106 | 107 | The kubectl logs command downloads the current logs from the running instance: 108 | 109 | ``` 110 | $ kubectl logs kuard 111 | ``` 112 | 113 | Adding the `-f` flag will cause the logs to stream continuously. 114 | 115 | ### Running Commands in Your Container with `exec` 116 | 117 | To do this, you can use: 118 | ``` 119 | $ kubectl exec kuard -- date 120 | ``` 121 | You can also get an interactive session by adding the 122 | `-it` flag: 123 | ``` 124 | $ kubectl exec -it kuard -- ash 125 | ``` 126 | 127 | ## Health Checks 128 | 129 | Kubernetes introduced health checks for application liveness. 130 | 131 | **Liveness Probe**: Liveness probes are defined per container, which means each container inside a Pod is health checked separately. Containers that fail liveness checks are restarted. 132 | 133 | **Readiness Probe**: Readiness describes when a container is ready to serve user requests. Containers that fail readiness checks are removed from service load balancers. Readiness probes are configured similarly to liveness probes. 134 | 135 | **Startup Probe**: When a Pod is started, the startup probe is run before any other probing of the Pod is started. The startup probe proceeds until it either times out (in which case the Pod is restarted) or it succeeds, at which time the 136 | liveness probe takes over. 137 | 138 | **Advanced Probe Configuration**: Probes in Kubernetes have a number of advanced options, including how long to wait 139 | after Pod startup to start probing, how many failures should be considered a true failure, and how many successes are necessary to reset the failure count. 140 | 141 | **Other Types of Health Checks**: In addition to HTTP checks, Kubernetes also supports tcpSocket health checks that 142 | open a TCP socket; if the connection succeeds, the probe succeeds. This style of probe is useful for non-HTTP applications, such as databases or other non–HTTP- 143 | based APIs. 144 | 145 | ## Resource Management 146 | 147 | Kubernetes allows users to specify two different resource metrics. `Resource requests` specify the minimum amount of a resource required to run the application. 148 | `Resource limits specify the maximum amount of a resource that an application can consume. 149 | 150 | ### Resource Requests: Minimum Required Resources 151 | 152 | When a Pod requests the resources required to run its containers, Kubernetes guar‐ 153 | antees that these resources are available to the Pod. 154 | 155 | [example of resource requests](./kuard-pod-resreq.yaml) 156 | 157 | Resources are requested per container, not per Pod. The total resources requested by the Pod is the sum of all resources requested by all containers in the Pod because the different containers often have very different CPU requirements. 158 | 159 | CPU requests are implemented using the cpu-shares functionality in the Linux kernel. 160 | 161 | If a container is over its memory request, the 162 | OS can’t just remove memory from the process, because it’s been allocated. Consequently, when the system runs out of memory, the kubelet terminates containers whose memory usage is greater than their requested memory. These containers are automatically 163 | restarted, but with less available memory on the machine for the container to consume. 164 | 165 | ### Capping Resource Usage with Limits 166 | 167 | In addition to setting the resources required by a Pod, which establishes the mini‐ 168 | mum resources available to it, you can also set a maximum on a its resource usage via 169 | resource limits. 170 | 171 | [example of resource limits](./kuard-pod-reslim.yaml) 172 | 173 | When you establish limits on a container, the kernel is configured to ensure that 174 | consumption cannot exceed these limits. A container with a CPU limit of 0.5 cores will only ever get 0.5 cores, even if the CPU is otherwise idle. A container with a memory limit of 256 MB will not be allowed additional memory; for example, malloc will fail if its memory usage exceeds 256 MB. 175 | 176 | ## Persisting Data with Volumes 177 | 178 | In some use cases, having access to persistent disk storage is an important part of a healthy application. Kubernetes models such persistent storage. 179 | 180 | ### Using Volumes with Pods 181 | 182 | To add a volume to a Pod manifest, there are two new stanzas to add to our configuration. The first is a new `spec.volumes` section. This array defines all of the volumes that may be accessed by containers in the Pod manifest. It’s important to note that not all containers are required to mount all volumes defined in the Pod. The second addition is the `volumeMounts` array in the container definition. This array defines 183 | the volumes that are mounted into a particular container and the path where each volume should be mounted. Note that two different containers in a Pod can mount the same volume at different mount paths. 184 | 185 | [example: pod manifest with volume](./kuard-pod-vol.yaml) 186 | 187 | ### Different Ways of Using Volumes with Pods 188 | 189 | - Communication/synchronization 190 | - Cache 191 | - Persistent data 192 | - Mounting the host filesystem -------------------------------------------------------------------------------- /Chapter 19: Securing Applications in Kubernetes/index.md: -------------------------------------------------------------------------------- 1 | # Securing Applications in Kubernetes 2 | 3 | It’s important to understand the following two concepts when securing Pods in Kubernetes: defense in depth and principle of least privilege. Defense in depth is a 4 | concept where you use multiple layers of security controls across your computing systems that include Kubernetes. The principle of least privilege means giving your workloads access only to resources that are required for them to operate. Both these concepts are not destinations, but constantly applied to the ever-changing computing system landscape. 5 | 6 | ## Understanding SecurityContext 7 | 8 | At the core of securing Pods is SecurityContext, which is an aggregation of all security-focused fields that may be applied at both the Pod and container specification level. Here are some example security controls covered by SecurityContext: 9 | - User permissions and access control (e.g., setting User ID and Group ID) 10 | - Read-only root filesystem 11 | - Allow privilege escalation 12 | - Seccomp, AppArmor, and SELinux profile and label assignments 13 | - Run as privileged or unprivileged 14 | 15 | You can see in this [example](./kuard-pod-securitycontext.yaml) that there is a SecurityContext at both the Pod and the container level. Many of the security controls can be applied at both of these levels. In the case that they are applied in both, the container level configuration takes 16 | precedence. 17 | 18 | - runAsNonRoot: The Pod or container must run as a nonroot user. The container will fail to start if it is running as a root user. 19 | - runAsUser/runAsGroup: This setting overrides the user and group that the container process is run as. Container images may have this configured as part of the Dockerfile. 20 | - fsgroup: Configures Kubernetes to change the group of all files in a volume when they are mounted into a Pod. 21 | - allowPrivilegeEscalation: Configures whether a process in a container can gain more privileges than its parent. 22 | - privileged: Runs the container as privileged, which elevates the container to the same permissions as the host. 23 | - readOnlyRootFilesystem: Mounts the container root filesystem to read-only. This is a common attack vector and is best practice to enable. 24 | 25 | We will introduce the operating system level security controls and then how to configure them via SecurityContext. It’s important to note that many of these controls are host operating system dependent. Here are a list of the core set of operating system controls that are covered by SecurityContext: 26 | 27 | - Capabilities: Allow either the addition or removal of groups of privilege that may be required for a workload to operate. 28 | - AppArmor: Controls which files processes can access. AppArmor profiles can be applied to containers via the addition of an annotation of `container.apparmor.security.beta.kubernetes.io/: ` to the Pod specification. 29 | - Seccomp: Seccomp (secure computing) profiles allow the creation of syscall filters. These filters allow specific syscalls to be allowed or blocked, which limits the surfacearea of the Linux kernel that is exposed to the processes in the Pods. 30 | - SELinux: Defines access controls for files and processes. SELinux operators use labels that are grouped together to create a security context (not to be mistaken with a Kubernetes SecurityContext), which is used to limit access to a process. By default, Kubernetes allocates a random SELinux context for each container; how‐ever, you may choose to set one via SecurityContext. 31 | 32 | ### SecurityContext Challenges 33 | 34 | The creation and management of AppArmor, seccomp, and SELinux profiles and contexts is not easy and is error prone. The cost of an error is breaking the 35 | ability for an application to perform its function. There are several tools out there that create a way to generate a seccomp profile from a running Pod, which can then be applied using SecurityContext. One such project is the [Security Profiles Operator](https://github.com/kubernetes-sigs/security-profiles-operator), which makes it easy to generate and manage Seccomp profiles. 36 | 37 | ## Pod Security 38 | 39 | One of the main differences between Pod Security and its predecessor is that Pod Security only performs validation and not mutation. 40 | 41 | ### What Is Pod Security? 42 | 43 | Pod Security allows you to declare different security profiles for Pods. These security profiles are known as Pod Security Standards and are applied at the namespace level. Pod Security Standards are a collection of security-sensitive fields in a Pod specification (including, but not limited to, SecurityContext) and their associated values. There are three different standards that range from restricted to permissive. The idea is that you can apply a general security posture to all Pods in a given namespace. The three Pod Security Standards are as follows: 44 | 45 | - Baseline: Most common privilege escalation while enabling easier onboarding. 46 | - Restricted: Highly restricted, covering security best practices. May cause workloads to break. 47 | - Privileged: Open and unrestricted. 48 | 49 | Each Pod Security Standard defines a list of fields in the Pod specification and their allowed values. Here are some fields that are covered by these standards: 50 | - spec.securityContext 51 | - spec.containers[*].securityContext 52 | - spec.containers[*].ports 53 | - spec.volumes[*].hostPath 54 | 55 | Each standard is applied to a namespace using a given mode. There are three modes a policy may be applied to. They are as follows: 56 | - Enforce: Any Pods that violate the policy will be denied 57 | - Warn: Any Pods that violate the policy will be allowed, and a warning message will be displayed to the user 58 | - Audit: Any Pods that violate the policy will generate an audit message in the audit log 59 | 60 | ### Applying Pod Security Standards 61 | 62 | Pod Security Standards are applied to a namespace using labels as follows: 63 | - Required: pod-security.kubernetes.io/: 64 | - Optional: pod-security.kubernetes.io/-version: (defaults to latest) 65 | 66 | The namespace in [Example](./baseline-ns.yaml) illustrates how you may use multiple modes to enforce at one standard (baseline in this example) and audit and warn at another(restricted). 67 | 68 | Pod Security is a great way to manage the security posture of your workloads byapplying policy at the namespace level and allowing Pods to be created only if 69 | they don’t violate the policy. It’s flexible and offers different prebuilt policies from permissive to restricted along with tooling to easily roll out policy changes without the risk of breaking workloads. 70 | 71 | ### Service Account Management 72 | 73 | Service accounts are Kubernetes resources that provide an identity to workloads that run inside Pods. RBAC can be applied to service accounts to control what resources, via the Kubernetes API, the identity has access to. By default, Kubernetes creates a default service account in each namespace, which is automatically set as the serviceaccount for all Pods. This service account contains a token that is automounted in each Pod and is used to access the Kubernetes API. To disable this behavior, you must add `automountServiceAccountToken: false` to the service account configuration. 74 | 75 | This [Example](./service-account.yaml) demonstrates how this can be done for the default service account. 76 | 77 | Service accounts are often overlooked when considering Pod security; however, they allow direct access to the Kubernetes API and, without adequate RBAC, could allow an attacker access to Kubernetes. 78 | 79 | ### RuntimeClass 80 | 81 | Kubernetes interacts with the container runtime on the node’s operating system via the Container Runtime Interface (CRI). The creation and standardization of 82 | this interface has allowed for an ecosystem of container runtimes to exist. These container runtimes may offer different levels of isolation, which include stronger security guarantees based on how they are implemented. Projects like Kata Containers, Firecracker, and gVisor are based on different isolation mechanisms from nested virtualization to more sophisticated syscall filtering. 83 | 84 | You can use a RuntimeClass by specifying runtimeClassName in the Pod specification. [Here](./kuard-pod-runtimeclass.yaml) is an example Pod that specifies a RuntimeClass. 85 | 86 | RuntimeClass allows users to select different container runtimes that may have different security isolation. Using RuntimeClass can help complement the overall security of your workloads, especially if workloads are processing sensitive information or running untrusted code. 87 | 88 | ### Network Policy 89 | 90 | Kubernetes also has a Network Policy API that allows you to create both ingress and egress network policies for your workload. Network policies are configured using labels that allow you to select specific Pods and define how they can communicate with other Pods and endpoints. Network Policy resources 91 | are implemented by network plug-ins, such as Calico, Cilium, and Weave Net. 92 | 93 | The Network Policy resource is namespaced and is structured with the podSelector, policyTypes, ingress, and egress sections with the only required field being pod 94 | Selector. If the podSelector field is empty, the policy matches all Pods in a namespace. This field may also contain a matchLabels section, which functions in the same way as a Service resource, allowing you to add a set of labels to match a specific set of Pods. 95 | 96 | If a Pod is matched by any Network Policy resource, then any ingress or egress communication must be explicitly defined, otherwise it will be blocked. If a 97 | Pod matches multiple Network Policy resources, then the policies are additive. If a Pod isn’t matched by any Network Policy, then traffic is allowed. This decision was intentionally made to ease onboarding of new workloads. If you do, however, want all traffic to be blocked by default, you can create a default deny rule per namespace. [Example]() 19-9 shows a default deny rule that can be applied per namespace. 98 | 99 | ### Security Benchmark Tools 100 | 101 | There are several open source tools that allow you to run a suite of security benchmarks against your Kubernetes cluster to determine if your configuration meets a predefined set of security baselines. Once such tool is called [kube-bench](https://github.com/aquasecurity/kube-bench). kube-bench can be used to run the CIS Benchmarks for Kubernetes. Tools like kube-bench running the CIS Benchmarks aren’t specifically focused on Pod security; however, they can certainly expose any cluster misconfigurations and help identify remediations. 102 | 103 | ### Image Security 104 | 105 | Another important part of Pod security is keeping the code and application within the Pod secure. Securing an application’s code is a complex topic beyond the scope of this chapter; however, the basics for container image security include making sure that your container image registry is doing static scanning for known code vulnerabilities. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Chapter 10: Deployments/index.md: -------------------------------------------------------------------------------- 1 | # Deployments 2 | 3 | The Deployment object exists to manage the release of new versions. Deployments represent deployed applications in a way that transcends any particular version. Additionally, Deployments enable you to easily move from one version of your code to the next. 4 | 5 | ### Your First Deployment 6 | 7 | Like all objects in Kubernetes, a Deployment can be represented as a declarative YAML object that provides the details about what you want to run. In the following 8 | case, the Deployment is requesting a single instance of the kuard application: 9 | 10 | [kuard-deployments](./kuard-deployment.yaml) 11 | 12 | Just as we learned that ReplicaSets manage Pods, Deployments manage ReplicaSets. As with all relationships in Kubernetes, this relationship is defined by labels and a label selector. You can see the label selector by looking at the Deployment object: 13 | 14 | ``` 15 | $ kubectl get deployments kuard \ 16 | -o jsonpath --template {.spec.selector.matchLabels} 17 | ``` 18 | 19 | You can use this in a label selector query across ReplicaSets to find that specific ReplicaSet the Deployment is managing: 20 | 21 | ``` 22 | $ kubectl get replicasets --selector=run=kuard 23 | ``` 24 | 25 | We can resize the Deployment using the imperative scale command: 26 | ``` 27 | $ kubectl scale deployments kuard --replicas=2 28 | ``` 29 | 30 | Now if we list that ReplicaSet again, we should see that ReplicaSet has been scaled also: 31 | 32 | ``` 33 | $ kubectl get replicasets --selector=run=kuard 34 | ``` 35 | Scaling the Deployment has also scaled the ReplicaSet it controls. 36 | 37 | Now let’s try the opposite, scaling the ReplicaSet: 38 | ``` 39 | $ kubectl scale replicasets kuard-1128242161 --replicas=1 40 | ``` 41 | 42 | Now, get that ReplicaSet again: 43 | ``` 44 | $ kubectl get replicasets --selector=run=kuard 45 | ``` 46 | 47 | That’s odd. Despite scaling the ReplicaSet to one replica, it still has two replicas as its desired state. What’s going on? 48 | 49 | Remember, Kubernetes is an online, self-healing system. The top-level Deployment object is managing this ReplicaSet. When you adjust the number of replicas to one, it no longer matches the desired state of the Deployment,which has replicas set to 2. The Deployment controller notices this and takes action to ensure the observed state 50 | matches the desired state, in this case readjusting the number of replicas back to two. If you ever want to manage that ReplicaSet directly, you need to delete the Deploy‐ 51 | ment. (Remember to set --cascade to false, or else it will delete the ReplicaSet and Pods as well!) 52 | 53 | ### Managing Deployments 54 | 55 | As with all Kubernetes objects, you can get detailed information about your Deployment via the `kubectl describe` command. This command provides an overview 56 | of the Deployment configuration, which includes interesting fields like the Selector, Replicas, and Events: 57 | 58 | ``` 59 | $ kubectl describe deployments kuard 60 | ``` 61 | 62 | In the output of describe, there is a great deal of important information. Two of the most important pieces of information in the output are `OldReplicaSets` and `New ReplicaSet`. These fields point to the ReplicaSet objects this Deployment is currently managing. If a Deployment is in the middle of a rollout, both fields will be set to a 63 | value. If a rollout is complete, OldReplicaSets will be set to . 64 | 65 | In addition to the describe command, there is also the `kubectl rollout` command for Deployments. If you have a current Deployment in progress, you can use `kubectl rollout status` to obtain the current status of that 66 | rollout. 67 | 68 | ## Updating Deployments 69 | 70 | The two most common operations on a Deployment are scaling and application updates. 71 | 72 | ### Scaling a Deployment 73 | 74 | The best practice is to manage your Deployments declaratively via the YAML files, then use those files to update your Deployment. To scale up a Deployment, you would edit your YAML file to increase the number of replicas: 75 | 76 | ``` 77 | ... 78 | spec: 79 | replicas: 3 80 | ... 81 | ``` 82 | 83 | ### Updating a Container Image 84 | 85 | The other common use case for updating a Deployment is to roll out a new version of the software running in one or more containers. To do this, you should likewise edit 86 | the Deployment YAML file, though in this case you are updating the container image, rather than the number of replicas: 87 | 88 | ``` 89 | ... 90 | containers: 91 | - image: gcr.io/kuar-demo/kuard-amd64:green 92 | imagePullPolicy: Always 93 | ... 94 | ``` 95 | 96 | Annotate the template for the Deployment to record some information about the update: 97 | 98 | ... 99 | spec: 100 | ... 101 | template: 102 | metadata: 103 | annotations: 104 | kubernetes.io/change-cause: "Update togreenkuard" 105 | ... 106 | 107 | Also, do not update the change- 108 | cause annotation when doing simple scaling operations. A modification of change-cause is a significant change to the template and will trigger a new rollout. 109 | 110 | After you update the Deployment, it will trigger a rollout, which you can then monitor via the kubectl rollout command: 111 | ``` 112 | $ kubectl rollout status deployments kuard 113 | ``` 114 | 115 | If you are in the middle of a rollout and you want to temporarily pause it (e.g., if yo start seeing weird behavior in your system that you want to investigate), you can use the `pause` command: 116 | 117 | ``` 118 | $ kubectl rollout pause deployments kuard 119 | ``` 120 | 121 | If, after investigation, you believe the rollout can safely proceed, you can use the `resume` command to start up where you left off: 122 | ``` 123 | $ kubectl rollout resume deployments kuard 124 | ``` 125 | 126 | ### Rollout History 127 | 128 | Kubernetes Deployments maintain a history of rollouts, which can be useful both for understanding the previous state of the Deployment and for rolling back to a specific 129 | version. 130 | You can see the Deployment history by running: 131 | ``` 132 | $ kubectl rollout history deployment kuard 133 | 134 | The revision history is given in oldest to newest order. A unique revision number is incremented for each new rollout. 135 | 136 | If you are interested in more details about a particular revision, you can add the `--revision` flag to view details about that specific revision: 137 | 138 | ``` 139 | $ kubectl rollout history deployment kuard --revision=2 140 | `` 141 | 142 | Let’s say there is an issue with the latest release and you want to roll back while you investigate. You can simply undo the last rollout: 143 | ``` 144 | $ kubectl rollout undo deployments kuard 145 | ``` 146 | 147 | Let’s look at the Deployment history again: 148 | 149 | ``` 150 | $ kubectl rollout history deployment kuard 151 | deployment.apps/kuard 152 | REVISION CHANGE-CAUSE 153 | 1 154 | 3 Update to blue kuard 155 | 4 Update to green kuard 156 | ``` 157 | Revision 2 is missing! It turns out that when you roll back to a previous revision, the Deployment simply reuses the template and renumbers it so that it is the latest 158 | revision. What was revision 2 before is now revision 4. 159 | 160 | Additionally, you can roll back to a specific revision in the history using the `--to-revision` flag: 161 | 162 | ``` 163 | $ kubectl rollout undo deployments kuard --to-revision=3 164 | ``` 165 | 166 | Again, the undo took revision 3, applied it, and renumbered it as revision 5. 167 | 168 | By default, the last 10 revisions of a Deployment are kept attached to the Deployment object itself. It is recommended that if you have Deployments that you expect to keep around for a long time, you set a maximum history size for the Deployment revision history. To accomplish this, use the `revisionHistoryLimit` property in the Deployment specification: 169 | 170 | ``` 171 | ... 172 | spec: 173 | # We do daily rollouts, limit the revision history to two weeks of 174 | # releases as we don't expect to roll back beyond that. 175 | revisionHistoryLimit: 14 176 | ... 177 | ``` 178 | 179 | ## Deployment Strategies 180 | 181 | Kubernetes deployment supports two different rollout strategies, `Recreate` and `RollingUpdate` 182 | 183 | ### Recreate Strategy 184 | 185 | The Recreate strategy is the simpler of the two. It simply updates the ReplicaSet it manages to use the new image and terminates all of the Pods associated with the Deployment. The ReplicaSet notices that it no longer has any replicas and re-creates all Pods using the new image. Once the Pods are re-created, they are running the new version. 186 | 187 | While this strategy is fast and simple, it will result in workload downtime. Because of this, the Recreate strategy should be used only for test Deployments where a service downtime is acceptable. 188 | 189 | ### RollingUpdate Strategy 190 | 191 | The RollingUpdate strategy is the generally preferable strategy for any user-facing service. While it is slower than Recreate, it is also significantly more sophisticated 192 | and robust. Using RollingUpdate, you can roll out a new version of your service while it is still receiving user traffic, without any downtime. 193 | 194 | As you might infer from the name, the RollingUpdate strategy works by updating a few Pods at a time, moving incrementally until all of the Pods are running the new 195 | version of your software. 196 | 197 | #### Managing multiple versions of your service 198 | 199 | Importantly, this means that for a while, both the new and the old version of your service will be receiving requests and serving traffic. This has important implications 200 | for how you build your software. Namely, it is critically important that each version of your software, and each of its clients, is capable of talking interchangeably with 201 | both a slightly older and a slightly newer version of your software. 202 | 203 | Your service needs to be backward compatible to ensure zero downtime and to function correctly. This sort of backward compatibility is critical to decoupling your service from systems that depend on your service. If you don’t formalize your APIs and decouple yourself, you are forced to carefully manage your rollouts with all of the other systems that call into your service. This kind of tight coupling makes it extremely hard to produce the necessary agility to be able to push out new software every week, let alone every hour or every day. 204 | 205 | #### Configuring a rolling update 206 | 207 | RollingUpdate is a fairly generic strategy; it can be used to update a variety of applications in a variety of settings. Consequently, the rolling update itself is quite 208 | configurable; you can tune its behavior to suit your particular needs. There are two parameters you can use to tune the rolling update behavior: `maxUnavailable` and 209 | `maxSurge`. 210 | 211 | The `maxUnavailable` parameter sets the maximum number of Pods that can be unavailable during a rolling update. It can either be set to an absolute number (e.g., 3, 212 | meaning a maximum of three Pods can be unavailable) or to a percentage (e.g., 20%, meaning a maximum of 20% of the desired number of replicas can be unavailable). Generally speaking, using a percentage is a good approach for most services, since the value is correctly applied regardless of the desired number of replicas in the Deploy‐ 213 | ment. However, there are times when you may want to use an absolute number (e.g., limiting the maximum unavailable Pods to one). 214 | 215 | However, there are situations where you don’t want to fall below 100% capacity,but you are willing to temporarily use additional resources to perform a rollout. In these situations, you can set the maxUnavailable parameter to 0 and instead control the rollout using the maxSurge parameter. Like maxUnavailable, maxSurge can be specified either as a specific number or a percentage. 216 | 217 | ### Slowing Rollouts to Ensure Service Health 218 | 219 | Staged rollouts are meant to ensure that the rollout results in a healthy, stable service running the new software version. To do this, the Deployment controller always waits until a Pod reports that it is ready before moving on to update the next Pod. 220 | 221 | The Deployment controller examines the Pod’s status as determined by its readiness checks. f you want to use Deployments to reliably roll out your software, you have to specify readiness health checks for the containers in your Pod. Without these checks, the Deployment controller is running without knowing the Pod’s status. 222 | 223 | Sometimes, however, simply noticing that a Pod has become ready doesn’t give you sufficient confidence that the Pod is actually behaving correctly. Some error conditions don’t occur immediately. For example, you could have a serious memory leak that takes a few minutes to show up, or you could have a bug that is only triggered by 1% of all requests. In most real-world scenarios, you want to wait a period of time to have high confidence that the new version is operating correctly before you move on to updating the next Pod. 224 | 225 | ``` 226 | ... 227 | spec: 228 | minReadySeconds: 60 229 | ... 230 | ``` 231 | 232 | Setting minReadySeconds to 60 indicates that the Deployment must wait for 60 seconds after seeing a Pod become healthy before moving on to updating the next 233 | Pod. 234 | 235 | In addition to waiting for a Pod to become healthy, you also want to set a timeout that limits how long the system will wait. Suppose, for example, the new version of your 236 | service has a bug and immediately deadlocks. It will never become ready, and in the absence of a timeout, the Deployment controller will stall your rollout forever. 237 | 238 | In order to set the timeout period, you will use the Deployment parameter progres `DeadlineSeconds`: 239 | 240 | ``` 241 | ... 242 | spec: 243 | progressDeadlineSeconds: 600 244 | ... 245 | ``` 246 | This example sets the progress deadline to 10 minutes. If any particular stage in the rollout fails to progress in 10 minutes, then the Deployment is marked as failed, and 247 | all attempts to move the Deployment forward are halted. It is important to note that this timeout is given in terms of Deployment progress, not the overall length of a Deployment. In this context, progress is defined as any time the Deployment creates or deletes a Pod. When that happens, the timeout clock is reset to zero. 248 | 249 | ## Deleting a Deployment 250 | 251 | If you ever want to delete a Deployment, you can do it with the imperative command: 252 | 253 | ``` 254 | $ kubectl delete deployments kuard 255 | ``` 256 | By default, deleting a Deployment deletes the entire service. The means it will delete not just the Deployment, but also any ReplicaSets it manages, as well as any Pods the ReplicaSets manage. As with ReplicaSets, if this is not the desire behavior, you can use the `--cascade=false` flag to delete only the Deployment object. 257 | 258 | --------------------------------------------------------------------------------