From 62c44774780af308324d0745ba9f7f0b2b0de78d Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Sun, 18 Aug 2024 08:59:57 -0500 Subject: [PATCH] projects: Add dynk8s page --- content/projects/dynk8s/cloudcontainer.jpg | Bin 0 -> 33615 bytes content/projects/dynk8s/index.md | 164 ++++++++++ content/projects/dynk8s/sequence.plantuml | 36 ++ content/projects/dynk8s/sequence.svg | 363 +++++++++++++++++++++ 4 files changed, 563 insertions(+) create mode 100644 content/projects/dynk8s/cloudcontainer.jpg create mode 100644 content/projects/dynk8s/index.md create mode 100644 content/projects/dynk8s/sequence.plantuml create mode 100644 content/projects/dynk8s/sequence.svg diff --git a/content/projects/dynk8s/cloudcontainer.jpg b/content/projects/dynk8s/cloudcontainer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..838fc6d3e3221735ef4e12636a220d1455247e2d GIT binary patch literal 33615 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 Example: If you use `kubeadm` to provision your cluster, it is up to you to +> automatically execute `kubeadm join` at boot time via some script. + +With that in mind, I set out to build a solution that uses the Cluster +Autoscaler, WireGuard, and `kubeadm` to automatically provision nodes in the +cloud to run Jenkins jobs on pods created by the Jenkins Kubernetes plugin. + +[0]: https://plugins.jenkins.io/kubernetes +[1]: https://plugins.jenkins.io/ec2 +[2]: https://plugins.jenkins.io/docker-workflow +[3]: https://github.com/kubernetes/autoscaler +[4]: https://github.com/kubernetes/autoscaler/blob/de560600991a5039fd9157b0eeeb39ec59247779/cluster-autoscaler/FAQ.md#how-does-scale-up-work + + +## Process + +
+ +[![Sequence Diagram](sequence.svg)](sequence.svg) + +
+ + +1. When Jenkins starts running a job that is configured to run in a Kubernetes + Pod, it uses the job's pod template to create the Pod resource. It also + creates a worker node and waits for the JNLP agent in the pod to attach + itself to that node. +2. Kubernetes attempts to schedule the pod Jenkins created. If there is not a + node available, the scheduling fails. +3. The Cluster Autoscaler detects that scheduling the pod failed. It checks + the requirements for the pod, matches them to an EC2 Autoscaling Group, and + determines that scheduling would succeed if it increased the capacity of the + group. +4. The Cluster Autoscaler increases the desired capacity of the EC2 Autoscaling + Group, launching a new EC2 instance. +5. Amazon EventBridge sends a notification, via Amazon Simple Notification + Service, to the provisioning service, indicating that a new EC2 instance has + started. +6. The provisioning service generates a `kubeadm` boostrap token for the new + instance and stores it as a Secret resource in Kubernetes. +7. The provisioning service looks for an available Secret resource in + Kubernetes containing WireGuard configuration and marks it as assigned to + the new EC2 instance. +8. The EC2 instance, via a script executed by *cloud-init*, fetches the + WireGuard configuration assigned to it from the provisioning service. +9. The provisioning service searches for the Secret resource in Kubernetes + containing the WireGuard configuration assigned to the EC2 instance and + returns it in the HTTP response. +10. The *cloud-init* script on the EC2 instance uses the returned WireGuard + configuration to configure a WireGuard interface and connect to the VPN. +11. The *cloud-init* script on the EC2 instance generates a + [`JoinConfiguration`][7] document with cluster discovery configuration + pointing to the provisioning service and passes it to `kubeadm join`. +12. The provisioning service looks up the Secret resource in Kubernetes + containing the bootstrap token assigned to the EC2 instance and generates a + *kubeconfig* file containing the cluster configuration information and that + token. The *kubeconfig* file is returned in the HTTP response. +13. `kubeadm join`, running on the EC2 instance communicates with the + Kubernetes API server, over the WireGuard tunnel, to perform TLS + bootstrapping and configure the Kubelet as a worker node in the cluster. +14. When the Kubelet on the new EC2 instance is ready, Kubernetes detects that + the pod created by Jenkins can now be scheduled to run on it and instructs + the Kublet to start the containers in the pod. +15. The Kublet on the new EC2 instance starts the pod's containers. The JNLP + agent, running as one of the containers in the pod, connects to the Jenkins + controller. +16. Jenkins assigns the job run to the new agent, which executes the job. + +[7]: https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta3/#kubeadm-k8s-io-v1beta3-JoinConfiguration + + +## Components + +### Jenkins Kubernetes Plugin + +The [Kubernetes plugin][0] for Jenkins is responsible for dynamically creating +Kubernetes pods from templates associated with pipeline jobs. Jobs provide a +pod template that describe the containers and configuration they require in +order to run. Jenkins creates the corresponding resources using the Kubernetes +API. + +### Autoscaler + +The [Cluster Autoscaler][3] is an optional Kubernetes component that integrates +with cloud provider APIs to create or destroy worker nodes. It does not handle +any configuration on the machines themselves (i.e. running `kubeadm join`), but +it does watch the cluster state and determine when to create or destroy new +nodes based on pod requests. + +### cloud-init + +[cloud-init][5] is a tool that comes pre-installed on most cloud machine images +(including the official Fedora AMIs) that can be used to automatically +provision machines when they are first launched. It can install packages, +create configuration files, run commands, etc. + +[5]: https://cloud-init.io/ + +### WireGuard + +[WireGuard][6] is a simple and high-performance VPN protocol. It will provide +the cloud instances with connectivity back to the private network, and +therefore access to internal resources including the Kubernetes API. + +Unfortunately, WireGuard is not particularly amenable to "dynamic" clients +(i.e. peers that come and go). This means either custom tooling will be +necessary to configure WireGuard peers on the fly OR pre-generating +configuration for a set number of peers and ensuring that no more than that +number of instances are every online simultaneously. + +[6]: https://www.wireguard.com/ + +### Provisioning Service + +This is a custom piece of software that is responsible for provisioning +secrets, etc. for the dynamic nodes. Since it will be responsible for handing +out WireGuard keys, it will have to be accessible directly over the Internet. +It will have to authenticate requests somehow to ensure that they are from +authorized clients (i.e. EC2 nodes created by the k8s Autoscaler) before +generating any keys/tokens. diff --git a/content/projects/dynk8s/sequence.plantuml b/content/projects/dynk8s/sequence.plantuml new file mode 100644 index 0000000..e0fec04 --- /dev/null +++ b/content/projects/dynk8s/sequence.plantuml @@ -0,0 +1,36 @@ +@startuml +box Internal Network +participant Jenkins +participant Pod +participant Kubernetes +participant Autoscaler +participant Provisioner +Jenkins -> Kubernetes : Create Pod +Kubernetes -> Autoscaler : Scale Up +end box +Autoscaler -> AWS : Launch Instance +create "EC2 Instance" +AWS -> "EC2 Instance" : Start +AWS --> Provisioner : Instance Started +Provisioner -> Provisioner : Generate Bootstrap Token +Provisioner -> Kubernetes : Store Bootstrap Token +Provisioner -> Kubernetes : Allocate WireGuard Config +"EC2 Instance" -> Provisioner : Request WireGuard Config +Provisioner -> Kubernetes : Request WireGuard Config +Kubernetes -> Provisioner : Return WireGuard Config +Provisioner -> "EC2 Instance" : Return WireGuard Config +"EC2 Instance" -> "EC2 Instance" : Configure WireGuard +"EC2 Instance" -> Provisioner : Request Cluster Config +Provisioner -> "EC2 Instance" : Return Cluster Config +group WireGuard Tunnel +"EC2 Instance" -> Kubernetes : Request Certificate +Kubernetes -> "EC2 Instance" : Return Certificate +"EC2 Instance" -> Kubernetes : Join Cluster +Kubernetes -> "EC2 Instance" : Acknowledge Join +Kubernetes -> "EC2 Instance" : Schedule Pod +"EC2 Instance" -> Kubernetes : Pod Started +end +Kubernetes -> Jenkins : Pod Started +create Pod +Jenkins -> Pod : Execute job +@enduml diff --git a/content/projects/dynk8s/sequence.svg b/content/projects/dynk8s/sequence.svg new file mode 100644 index 0000000..76a5423 --- /dev/null +++ b/content/projects/dynk8s/sequence.svg @@ -0,0 +1,363 @@ +Internal NetworkJenkinsJenkinsPodKubernetesKubernetesAutoscalerAutoscalerProvisionerProvisionerAWSAWSEC2 InstanceCreate PodScale UpLaunch InstanceStartEC2 InstanceInstance StartedGenerate Bootstrap TokenStore Bootstrap TokenAllocate WireGuard ConfigRequest WireGuard ConfigRequest WireGuard ConfigReturn WireGuard ConfigReturn WireGuard ConfigConfigure WireGuardRequest Cluster ConfigReturn Cluster ConfigWireGuard TunnelRequest CertificateReturn CertificateJoin ClusterAcknowledge JoinSchedule PodPod StartedPod StartedExecute jobPod \ No newline at end of file