Let’s make one thing clear - cloud computing is cheap if your organization is small and your workloads fit well into their cookie-cutter implementations. Does your company only run a few Node.js apps serving 100, 100,000, or even 100,000,000 requests a month? Great! Cloud is an excellent choice without question.
However, at all organizations I’ve ever worked at, a shift to cloud would require application and infrastructure reimplementations. For organizations with hundreds or even thousands of on-prem systems, I fear that migrating to cloud will only cost money and not provide much benefit to your customers.
Cloud computing can be a false narrative that locks you into a specific vendor. You gain simplicity by forfeiting agency on how you create and run your workloads. Before you waltz into a particular cloud agreement, you need to calculate the worth of your agency: the ability to take specific and immediate action on your behalf instead of bowing to the whims of a vendor.
I’d like to propose a solution that gets you both agency and simplified operations at scale: containers in Kubernetes.
Traditional VM architecture leaves much to be desired, you’ve likely got hundreds of unique dependency trees spread across your organization, leading to low server density and high operational cost:
Alternatively, containers make it easy to run any process by removing dependencies on their host and instead relying on the existence of a generic container runtime:
Containers are like virtual machines. Developers package up a single application and all dependencies like runtimes and drivers into a container image. Unlike VMs, containers don’t run an OS, making them smaller and faster; we only run a single process. Because our images have all dependencies, we can run containers on any machine with a container runtime, simplifying our infrastructure and operational cost by making all hosts identical. Additionally, devs can run containers locally the same as in production, an impossibility in many cloud offerings.
Is the cost savings of scaling to 0 granted by Function-As-A-Service greater than the salary-hours you lose while developers wait on deployments to fix a cloud-specific nuance?
Docker isn’t the only container platform, but it’s by far the most common, mostly because it optimizes builds and deployments so well. If you don’t know about buildkit already, you should definitely read up on it. Copy-On-Write enables containers to spin up in a few seconds. Optimized builds and deployments save a lot of time (and developer salaries). Docker’s open source community enables developers to easily choose the correct SDKs and tooling to perform the task they were assigned instead of forcing them into a small subset of supported cloud offerings.
While containers provide process isolation on a single host, this is rarely enough to give us a robust application stack. How do we perform disaster recovery if our host dies? What about networking, persistence, configuration, and access control?
Luckily, a system exists to manage all your containers and container hosts, providing easy ways to deploy and upgrade at scale with high availability: Kubernetes.
Kubernetes is like a cloud, it provides CPU, RAM, Disk, and Network as a service to containerized applications. Kubernetes is a set of binaries you can install on any compatible hardware, it isn’t a cloud-specific technology. You choose the compute you want (on prem, in any cloud), and installing the appropriate binaries on that machine will make it available for Kubernetes to manage and schedule containers to.
Everything in Kubernetes is managed as Desired State Configuration as a first class citizen, including the actual STATE of that desired state (something severely lacking in Terraform). You post documents to an API describing the desired state you want and Kubernetes “makes it so” - finding an appropriate host to run your containers (processes) initially while also keeping it available with full disaster recovery between hosts over time. As another great benefit: this API provides an avenue to enforce granular RBAC for the desired state (also missing in Terraform).
To make containers and their dependencies manageable, Kubernetes has a short list of desired state primitives (objects) you can create for highly-available workloads (Jobs, Deployments, StatefulSets), networking (Services and Ingress rules), configuration (ConfigMaps and Secrets), and persistence (PersistentVolumes and PersistentVolumeClaims). Learning these primitives makes it easy to deploy any containerized application stack.
Kubernetes has a fantastic open source community that’s expanding all the time. Helm is particularly great - making it easy to consolidate the objects I mentioned above into templates. Since Kubernetes is driven by desired state, it’s easy to share infrastructure online. Helm offers stacks like databases, caches, logging and monitoring suites, CI/CD and testing infrastructure, and so much more… installed with a few commands. You’ll also find FAAS offerings, certificate management, and service meshing as desired state.
Kubernetes enables agile tooling and infrastructure with a few commands. You get to build your own competitive cloud offering.
Before you get swept away by a cloud marketing campaign, check out containers on Kubernetes. While cloud is a hot buzzword, for many organizations agency beats out simplicity for competitive and cost advantages.