|
| 1 | +--- |
| 2 | +title: Building a Dockerless Kubelet |
| 3 | +authors: |
| 4 | + - "@mattjmcnaughton" |
| 5 | +owning-sig: sig-node |
| 6 | +participating-sigs: |
| 7 | + - sig-testing |
| 8 | + - sig-release |
| 9 | + - sig-cluster-lifecycle |
| 10 | +reviewers: |
| 11 | + - "@dims" |
| 12 | + - "@BenTheElder" |
| 13 | + - TBD |
| 14 | +approvers: |
| 15 | + - "@derekwaynecarr" |
| 16 | + - "@dchen1107" |
| 17 | +editor: TBD |
| 18 | +creation-date: 2020-02-05 |
| 19 | +last-updated: 2020-02-09 |
| 20 | +status: provisional |
| 21 | +see-also: |
| 22 | + - "/keps/sig-cloud-provider/20190729-building-without-in-tree-providers.md" |
| 23 | +--- |
| 24 | + |
| 25 | +# Building a Dockerless Kubelet |
| 26 | + |
| 27 | +## Table of Contents |
| 28 | + |
| 29 | +<!-- toc --> |
| 30 | +- [Release Signoff Checklist](#release-signoff-checklist) |
| 31 | +- [Summary](#summary) |
| 32 | +- [Motivation](#motivation) |
| 33 | + - [History](#history) |
| 34 | +- [Returning to Motivation](#returning-to-motivation) |
| 35 | + - [Goals](#goals) |
| 36 | + - [Non-Goals](#non-goals) |
| 37 | +- [Proposal](#proposal) |
| 38 | + - [User Stories](#user-stories) |
| 39 | + - [Story 1](#story-1) |
| 40 | + - [Story 2](#story-2) |
| 41 | + - [Implementation Details/Notes/Constraints](#implementation-detailsnotesconstraints) |
| 42 | + - [Risks and Mitigations](#risks-and-mitigations) |
| 43 | +- [Design Details](#design-details) |
| 44 | + - [Test Plan](#test-plan) |
| 45 | + - [Graduation Criteria](#graduation-criteria) |
| 46 | + - [Upgrade / Downgrade Strategy](#upgrade--downgrade-strategy) |
| 47 | + - [Version Skew Strategy](#version-skew-strategy) |
| 48 | +- [Implementation History](#implementation-history) |
| 49 | +- [Drawbacks](#drawbacks) |
| 50 | +- [Alternatives](#alternatives) |
| 51 | +<!-- /toc --> |
| 52 | + |
| 53 | +## Release Signoff Checklist |
| 54 | + |
| 55 | +- [ ] kubernetes/enhancements issue in release milestone, which links to KEP (this should be a link to the KEP location in kubernetes/enhancements, not the initial KEP PR) |
| 56 | +- [ ] KEP approvers have set the KEP status to `implementable` |
| 57 | +- [ ] Design details are appropriately documented |
| 58 | +- [ ] Test plan is in place, giving consideration to SIG Architecture and SIG Testing input |
| 59 | +- [ ] Graduation criteria is in place |
| 60 | +- [ ] "Implementation History" section is up-to-date for milestone |
| 61 | +- [ ] User-facing documentation has been created in [kubernetes/website], for publication to [kubernetes.io] |
| 62 | +- [ ] Supporting documentation e.g., additional design documents, links to mailing list discussions/SIG meetings, relevant PRs/issues, release notes |
| 63 | + |
| 64 | +## Summary |
| 65 | + |
| 66 | +This proposal outlines a plan to enable building a dockerless Kubelet. We |
| 67 | +define a dockerless Kubelet as a Kubelet with no "Docker-specific" code and |
| 68 | +no dependency on the `docker/docker` Golang package. We define "Docker-specific" |
| 69 | +code as code which only serves a purpose when Docker is the container runtime. |
| 70 | +"Docker-specific" code is never executed when the Kubelet uses a remote |
| 71 | +container runtime (i.e. containerd or CRI-O). |
| 72 | + |
| 73 | +Supporting building a dockerless Kubelet is a precursor to moving all |
| 74 | +"Docker-specific" Kubelet code out-of-tree, in the name of treating Docker like |
| 75 | +any other container runtime. |
| 76 | + |
| 77 | +At a high level, this undertaking is similar to the efforts of |
| 78 | +sig-cloud-provider to support [Building Kubernetes Without In-Tree Cloud Providers](/keps/sig-cloud-provider/20190729-building-without-in-tree-providers.md). |
| 79 | +A big thanks to them for their leadership; much of this KEP is based off their great work :) |
| 80 | + |
| 81 | +## Motivation |
| 82 | + |
| 83 | +For this KEP to be worthwhile, we must believe the following two statements. |
| 84 | + |
| 85 | +First, we must see a benefit to a dockerless Kubelet. |
| 86 | +Second, we must believe supporting the ability to compile a dockerless Kubelet is |
| 87 | +a useful first step towards a truly dockerless Kubelet. |
| 88 | + |
| 89 | +A quick review of recent Kubernetes history provides context when considering |
| 90 | +whether we agree with the statements in question. |
| 91 | + |
| 92 | +### History |
| 93 | + |
| 94 | +With 1.5, Kubernetes introduced the [Container Runtime Interface](https://kubernetes.io/blog/2016/12/container-runtime-interface-cri-in-kubernetes/) |
| 95 | +(CRI). The CRI defines a standard interface for the Kubelet to communicate with container |
| 96 | +runtimes. At the time of the CRI's release, Kubernetes supported only the Docker |
| 97 | +and rkt container runtimes. Kubernetes introduced the CRI to avoid needing |
| 98 | +provider specific code for each new container runtime. |
| 99 | + |
| 100 | +Since the CRI's release, the Kubelet only interacts with container runtimes via |
| 101 | +the CRI. For increasingly popular CRI implementations like containerd or CRI-O, |
| 102 | +the singular focus on the CRI poses no obstacles, as these container runtimes |
| 103 | +supported the CRI from the start. However, the Kubelet still needed a solution for |
| 104 | +Docker and rkt, in-tree runtimes which did not support the CRI. In-tree support |
| 105 | +for Rkt was deprecated, leaving only Docker as an issue. |
| 106 | + |
| 107 | +Ultimately, the Kubelet introduced the `dockershim` to address Docker's lack of |
| 108 | +CRI support. When a cluster operator chooses to use Docker as the container |
| 109 | +runtime, the Kubelet starts running the `dockershim` in a separate go |
| 110 | +routine within the main `kubelet` process; it is not currently possible to run |
| 111 | +the `dockershim` as a standalone binary/process. The `dockershim` supports the |
| 112 | +CRI, so the Kubelet communicates with `dockershim` as if it was any other remote |
| 113 | +container runtime implementing the CRI. The `dockershim` makes the appropriate |
| 114 | +calls to the Docker daemon via a heavy dependence on the `docker/docker` client library. |
| 115 | + |
| 116 | +The `docker/docker` client library is a particularly painful dependency because |
| 117 | +its pulls in code from many different open source libraries. For those managing |
| 118 | +k8s dependencies, it can be extremely difficult to keep up with the changes to |
| 119 | +all these dependencies. Additionally, all the open source libraries required by |
| 120 | +`docker/docker` bloat the Kubelet binary. |
| 121 | + |
| 122 | +## Returning to Motivation |
| 123 | + |
| 124 | +We can anticipate the following benefits from the Kubelet having no in-tree |
| 125 | +"Docker-specific" code and no dependency on the `docker/docker` Golang package. |
| 126 | + |
| 127 | +First, a dockerless Kubelet would truly treat all container runtimes the same. |
| 128 | +Second, the Kubelet's scope of responsibility would decrease: it would no longer |
| 129 | +be responsible for making Docker conform to the CRI. Finally, the painful |
| 130 | +`docker/docker` dependency would be gone. |
| 131 | + |
| 132 | +While the aforementioned benefits are desirable, they do not outweigh the cons |
| 133 | +of completely dropping support for Docker as a container runtime, as |
| 134 | +Docker remains popular. In order for Kubernetes |
| 135 | +to both support Docker as a container runtime, and for the Kubelet do have no |
| 136 | +in-tree "Docker-specific" code and no dependency on `docker/docker`, one of two |
| 137 | +following paths must be followed: either Docker must begin implementing the CRI natively |
| 138 | +or the `dockershim` must be moved out-of-tree into a standalone component. |
| 139 | + |
| 140 | +Clearly, both of these paths forward require significant work. Either effort would |
| 141 | +require finding an owner, making non-trivial code changes, and updating patterns |
| 142 | +of cluster management/operation. |
| 143 | + |
| 144 | +Faced with a hefty chunk of work, we naturally try to break it up into smaller |
| 145 | +components. This desire leads us to our second question: is supporting |
| 146 | +compiling a dockerless Kubelet an appropriate first step? |
| 147 | + |
| 148 | +We argue yes. First, the work to support compiling a dockerless Kubelet will |
| 149 | +be useful regardless of which path forward we chose. First, to compile a dockerless |
| 150 | +Kubelet we must consolidate all "Docker-specific" Kubelet code into specific |
| 151 | +locations, which are easier to move out-of-tree or delete entirely when the time comes. Furthermore, |
| 152 | +after this initial consolidation, we can create tooling to impose limitations on |
| 153 | +where "Docker-specific" code can/cannot live. Second, allowing developers to |
| 154 | +compile a dockerless Kubelet assists in testing either proposed solution. |
| 155 | +Finally, allowing compiling a dockerless Kubelet allows projects/cluster |
| 156 | +operators which already do not depend on Docker to obtain the dockerless |
| 157 | +Kubelet's benefits (i.e. smaller binaries) without waiting for the |
| 158 | +completion of the longer term projects to make Docker support the CRI or |
| 159 | +move `dockershim` out-of-tree. |
| 160 | + |
| 161 | +### Goals |
| 162 | + |
| 163 | +Our goals follow from our motivation: |
| 164 | + |
| 165 | +1. Support building Kubelet, from the `master` branch, without any "Docker-specific" code and without any |
| 166 | + dependency on `docker/docker`. As mentioned |
| 167 | + previously, we imagine the resulting binaries to be used to test the |
| 168 | + different paths for deleting/moving out-of-tree all "Docker-specific" code. |
| 169 | +2. Draw clear delineations, with CI support, for what code in Kubelet can and |
| 170 | + cannot be "Docker-specific" and depend on `docker/docker`. |
| 171 | + |
| 172 | +### Non-Goals |
| 173 | + |
| 174 | +Our non-goals also follow from our motivation: |
| 175 | + |
| 176 | +1. Either making Docker support the CRI or moving `dockershim` out-of-tree. |
| 177 | +2. Removing uses of the `docker/docker` Golang library outside of the Kubelet. |
| 178 | +3. Changing the official Kubernetes release builds. |
| 179 | + |
| 180 | +## Proposal |
| 181 | + |
| 182 | +We will undertake the following steps to obtain our goals. First, we will ensure |
| 183 | +that all "Docker-specific" code in the Kubelet lives in `dockershim`. Then, we |
| 184 | +will add a [build constraint](https://golang.org/pkg/go/build/#hdr-Build_Constraints) |
| 185 | +to the Kubelet for a pseudo "build tag" specifying not to include |
| 186 | +any in-tree "Docker-specific" code. If builds do not specify this build tag, the Go |
| 187 | +compiler will compile the Kubelet as normal. If we do, the Go compiler will |
| 188 | +compile the Kubelet without the "Docker-specific" code, and as a result, without |
| 189 | +the dependency on `docker/docker`. In other words, it will simulate the aforementioned |
| 190 | +code/dependency's removal. |
| 191 | + |
| 192 | +A prototype is available in [kubernetes/kubernetes#87746](https://github.com/kubernetes/kubernetes/pull/87746). |
| 193 | + |
| 194 | +To ensure that this dockerless Kubelet continues to function we will add CI building in this mode, |
| 195 | +and CI running end to end tests against it (to be elaborated on in the test plan). Additionally, to |
| 196 | +ensure the Kubelet doesn't introduce new dependencies on the `docker/docker` |
| 197 | +Golang library, we will add automated tooling enforcing that only the |
| 198 | +`dockershim` can depend on `docker/docker`. |
| 199 | + |
| 200 | +One quick additional note - currently `cadvisor` also depends on the |
| 201 | +`docker/docker` client library. Since `kubelet` depends on `cadvisor`, the |
| 202 | +`kubelet` can not truly be rid of the `docker/docker` client library until `cadvisor` no |
| 203 | +longer depends on `docker/docker`. Work to remove the `docker/docker` dependency |
| 204 | +from `cadvisor` is being dealt with in separate workstreams. |
| 205 | + |
| 206 | +This proposal follows the previous patterns for similar work, namely the efforts |
| 207 | +of sig-cloud-provider to support [Building Without In-Tree Cloud Providers](/keps/sig-cloud-provider/20190729-building-without-in-tree-providers.md). |
| 208 | + |
| 209 | +### User Stories |
| 210 | + |
| 211 | +#### Story 1 |
| 212 | + |
| 213 | +As a developer working to make Docker function like any other remote container |
| 214 | +runtime, I am attempting to validate that my proposed solution |
| 215 | +functions correctly. Using this dockerless build ensures the Kubelet contains no |
| 216 | +"Docker-specific" code, and any success/failures can be attributed to my |
| 217 | +implementation. |
| 218 | + |
| 219 | +#### Story 2 |
| 220 | + |
| 221 | +As a Kubernetes developer/user, for example a maintainer of [kind](https://github.com/kubernetes-sigs/kind), |
| 222 | +I want to use Kubelet binaries which only contain the code I need. Since kind does not use |
| 223 | +Docker as its container runtime, compiling a dockerless Kubelet gives me a |
| 224 | +smaller binary. |
| 225 | + |
| 226 | +### Implementation Details/Notes/Constraints |
| 227 | + |
| 228 | +A couple high level notes (to be extended over time): |
| 229 | + |
| 230 | +- We implement this functionality using a synthetic `dockerless` tag in go build |
| 231 | + constraints on relevant sources. If `GOFLAGS=-tags=dockerless` during build, |
| 232 | + then "Docker-specific" code will be excluded from the Kubelet build. With no |
| 233 | + "Docker-specific" code, we should also be excluding the dependency on |
| 234 | + `docker/docker`. |
| 235 | + |
| 236 | +### Risks and Mitigations |
| 237 | + |
| 238 | +This feature is only developer facing, which removes a large class of risks. |
| 239 | + |
| 240 | +The largest remaining risk is that the build tags fall out of date, or are |
| 241 | +burdensome to continue updating, which leads to the dockerless Kubelet build |
| 242 | +breaking and/or being costly to maintain. This risk grows the longer the |
| 243 | +dockerless Kubelet exists (i.e. the longer it takes to move `dockershim` out of |
| 244 | +tree/have Docker support the CRI). Fortunately, these risks can be mitigated via |
| 245 | +the CI tooling discussed earlier. |
| 246 | + |
| 247 | +## Design Details |
| 248 | + |
| 249 | +### Test Plan |
| 250 | + |
| 251 | +**Note:** *Section not required until targeted at a release.* |
| 252 | + |
| 253 | +TBD |
| 254 | + |
| 255 | +### Graduation Criteria |
| 256 | + |
| 257 | +**Note:** *Section not required until targeted at a release.* |
| 258 | + |
| 259 | +TBD |
| 260 | + |
| 261 | +### Upgrade / Downgrade Strategy |
| 262 | + |
| 263 | +N/A |
| 264 | + |
| 265 | +### Version Skew Strategy |
| 266 | + |
| 267 | +N/A |
| 268 | + |
| 269 | +## Implementation History |
| 270 | + |
| 271 | +- original prototype [kubernetes/kubernetes#87746](https://github.com/kubernetes/kubernetes/pull/87746) |
| 272 | +- original KEP PR [TBD] |
| 273 | + |
| 274 | +## Drawbacks |
| 275 | + |
| 276 | +One drawback is the opportunity cost of pursuing this workstream as opposed to |
| 277 | +other possible workstreams. |
| 278 | + |
| 279 | +Another drawback is the slight additional cost of the CI tooling we propose |
| 280 | +adding. |
| 281 | + |
| 282 | +## Alternatives |
| 283 | + |
| 284 | +One alternative would be to do nothing. |
| 285 | + |
| 286 | +Another alternative could be waiting to address "Docker-specific" code in the |
| 287 | +Kubelet until we have more momentum around one of the longer-term solutions |
| 288 | +discussed above. If we waited, we could delete "Docker-specific" code entirely, |
| 289 | +instead of just compiling without it. |
| 290 | + |
| 291 | +Finally, we could attempt to have a long-running branch in which all |
| 292 | +"Docker-specific" code has been deleted, instead of attempting to support |
| 293 | +compiling a dockerless Kubelet from master. |
0 commit comments