Move CI to the Cloud
Before cloud-native architecture became the dominant approach to designing, deploying, and releasing software, the continuous delivery story was much simpler. Typically a sysadmin would create a build server and install a version control system and continuous integration tool, such as Jenkins, TeamCity, or GoCD. In addition to continually building and integrating code, these tools could be augmented via plugins to perform rudimentary continuous deployment operations, such as FTPing binaries to VMs or uploading an artifact to a remote application server via a bespoke SDK/API.
This approach worked well when dealing with a small number of applications and a relatively static deployment environment. The initial configuration of a delivery pipeline was typically challenging and involved much trial and error. When a successful configuration was discovered, this was used as a template and then copy-pasted as more build jobs were added. Debugging a build failure often required specialist support.
The rise in popularity of containers and Kubernetes has meant that roles and responsibilities in relation to continuous delivery have changed. Operators may still set up the initial continuous integration and deployment tooling, but developers now want to self-service as they are releasing and operating what they build. This means that the scope of the infrastructure a developer needs to understand and manage has expanded from pure development tools (e.g., IDE, libraries and APIs) to deployment infrastructure (e.g., container registries and deployment templates) to runtime infrastructure (e.g., API gateways and observability systems).
All code within an application (and its subsystems) must be continuously integrated at the code level. Typically in a cloud native system this requires building a language-specific package, artifact, or binary, and then building this into a container image.