1<!-- markdownlint-disable-file code-block-style -->
2# Linkerd2 Development Guide
3
4:balloon: Welcome to the Linkerd2 development guide! :wave:
5
6This document will help you build and run Linkerd2 from source. More information
7about testing from source can be found in the [TEST.md](TEST.md) guide.
8
9## Table of contents
10
11- [Repo Layout](#repo-layout)
12 - [Control Plane (Go/React)](#control-plane-goreact)
13 - [Data Plane (Rust)](#data-plane-rust)
14- [Components](#components)
15- [Development configurations](#development-configurations)
16 - [Comprehensive](#comprehensive)
17 - [Deploying Control Plane components with Tracing](#deploying-control-plane-components-with-tracing)
18 - [Publishing Images](#publishing-images)
19 - [Go](#go)
20 - [A note about Go run](#a-note-about-go-run)
21 - [Lint](#lint)
22 - [Formatting](#formatting)
23 - [Building the CLI for development](#building-the-cli-for-development)
24 - [Running the control plane for development](#running-the-control-plane-for-development)
25 - [Running the Tap APIService for development](#debugging-the-tap-apiservice-for-development)
26 - [Generating CLI docs](#generating-cli-docs)
27 - [Web](#web)
28 - [First time setup](#first-time-setup)
29 - [Run web standalone](#run-web-standalone)
30 - [Webpack dev server](#webpack-dev-server)
31 - [JavaScript dependencies](#javascript-dependencies)
32 - [Translations](#translations)
33 - [Rust](#rust)
34 - [Docker](#docker)
35- [Dependencies](#dependencies)
36 - [Updating protobuf dependencies](#updating-protobuf-dependencies)
37 - [Updating ServiceProfile generated
38 code](#updating-serviceprofile-generated-code)
39- [Linkerd Helm Chart](#linkerd-helm-chart)
40 - [Extensions Helm charts](#extensions-helm-charts)
41 - [Making changes to the chart templates](#making-changes-to-the-chart-templates)
42 - [Generating Helm charts docs](#generating-helm-charts-docs)
43 - [Using helm-docs](#using-helm-docs)
44 - [Annotating values.yaml](#annotating-valuesyaml)
45 - [Markdown templates](#markdown-templates)
46
47## Repo layout
48
49Linkerd2 is primarily written in Rust, Go, and React. At its core is a
50high-performance data plane written in Rust. The control plane components and
51its extensions are written in Go. The dashboard UI is a React application.
52
53### Control Plane (Go/React)
54
55- [`cli`](cli): Command-line `linkerd` utility, view and drive the control
56 plane.
57- [`controller`](controller)
58 - [`destination`](controller/api/destination): Accepts requests from `proxy`
59 instances and serves service discovery information.
60 - [`proxy-injector`](controller/proxy-injector): Mutating webhook triggered by
61 pods creation, that injects the proxy container as a sidecar.
62 - [`identity`](controller/identity): Provides a CA to distribute certificates
63 to proxies for them to establish mTLS connections between them.
64- [`viz extension`](viz)
65 - [`metrics-api`](viz/metrics-api): Accepts requests from API clients such as
66 cli and web, serving metrics from the proxies in the cluster through
67 Prometheus queries.
68 - [`tap`](viz/tap/api): Provides a live pipeline of requests.
69 - [`tap-injector`](viz/tap/injector): Mutating webhook triggered by pods
70 creation, that injects metadata into the proxy container in order to enable
71 tap.
72 - [`web`](web): Provides a UI dashboard to view and drive the control plane.
73- [`multicluster extension`](multicluster)
74 - [`linkerd-gateway`]: Accepts requests from other clusters and forwards them
75 to the appropriate destination in the local cluster.
76 - [`linkerd-service-mirror-xxx`](multicluster/service-mirror): Controller
77 observing the labeling of exported services in the target cluster, each one
78 for which it will create a mirrored service in the local cluster.
79- [`jaeger extension`](jaeger)
80 - [`jaeger-injector`](jaeger/injector): Mutating webhook triggered by pods
81 creation, that expands the proxy container for it to produce tracing spans.
82
83### Data Plane (Rust)
84
85- [`linkerd2-proxy`](https://github.com/linkerd/linkerd2-proxy): Rust source
86 code for the proxy lives in the linkerd2-proxy repo.
87- [`linkerd2-proxy-api`](https://github.com/linkerd/linkerd2-proxy-api):
88 Protobuf definitions for the data plane APIs live in the linkerd2-proxy-api
89 repo.
90
91## Components
92
93
94
95<!-- markdownlint-disable no-inline-html -->
96<details>
97<summary></summary>
98linkerd2_components
99 digraph G {
100 rankdir=LR;
101
102 node [style=filled, shape=rect];
103
104 "cli" [color=lightblue];
105 "destination" [color=lightblue];
106 "identity" [color=lightblue];
107 "metrics-api" [color=lightblue];
108 "tap" [color=lightblue];
109 "web" [color=lightblue];
110
111 "proxy" [color=orange];
112
113 "cli" -> "metrics-api";
114 "cli" -> "tap";
115
116 "web" -> "metrics-api";
117 "web" -> "tap";
118 "web" -> "grafana";
119
120 "metrics-api" -> "prometheus";
121
122 "tap" -> "proxy";
123
124 "proxy" -> "destination";
125 "proxy" -> "identity";
126
127 "identity" -> "kubernetes api"
128
129 "destination" -> "kubernetes api";
130
131 "grafana" -> "prometheus";
132 "prometheus" -> "proxy";
133 }
134linkerd2_components
135</details>
136<!-- markdownlint-enable no-inline-html -->
137
138## Development configurations
139
140Depending on use case, there are several configurations with which to develop
141and run Linkerd2:
142
143- [Comprehensive](#comprehensive): Integrated configuration using k3d, most
144 closely matches release.
145- [Web](#web): Development of the Linkerd2 Dashboard.
146
147### Comprehensive
148
149This configuration builds all Linkerd2 components in Docker images, and deploys
150them onto a k3d cluster. This setup most closely parallels our recommended
151production installation, documented in [Getting
152Started](https://linkerd.io/2/getting-started/).
153
154Note that you need to have first installed docker buildx, as explained
155[here](https://github.com/docker/buildx).
156
157```bash
158# create the k3d cluster
159bin/k3d cluster create
160
161# build all docker images
162bin/docker-build
163
164# load all the images into k3d
165bin/image-load --k3d
166
167# install linkerd
168bin/linkerd install --crds | kubectl apply -f -
169bin/linkerd install | kubectl apply -f -
170
171# wait for the core components to be ready, then install linkerd-viz
172bin/linkerd viz install | kubectl apply -f -
173
174# in order to use `linkerd viz tap` against control plane components, you need
175# to restart them (so that the tap-injector enables tap on their proxies)
176kubectl -n linkerd rollout restart deploy
177
178# verify cli and server versions
179bin/linkerd version
180
181# validate installation
182bin/linkerd check --expected-version $(bin/root-tag)
183
184# view linkerd dashboard
185bin/linkerd viz dashboard
186
187# install the demo app
188curl https://run.linkerd.io/emojivoto.yml | bin/linkerd inject - | kubectl apply -f -
189
190# port-forward the demo app's frontend to see it at http://localhost:8080
191kubectl -n emojivoto port-forward svc/web-svc 8080:80
192
193# view details per deployment
194bin/linkerd viz -n emojivoto stat deployments
195
196# view a live pipeline of requests
197bin/linkerd viz -n emojivoto tap deploy voting
198```
199
200#### Deploying Control Plane components with Tracing
201
202Control Plane components have the `trace-collector` flag used to enable
203[Distributed Tracing](https://opentracing.io/docs/overview/what-is-tracing/) for
204development purposes. It can be enabled globally i.e Control plane components
205and their proxies by using the `--set controlPlaneTracing=true` installation
206flag.
207
208This will configure all the components to send the traces at
209`collector.{{.Values.controlPlaneTracingNamespace}}.svc.{{.Values.ClusterDomain}}:55678`
210
211```bash
212
213# install Linkerd with tracing
214linkerd install --set controlPlaneTracing=true | kubectl apply -f -
215
216# install the Jaeger extension
217linkerd jaeger install | kubectl apply -f -
218
219# restart the control plane components so that the jaeger-injector enables
220# tracing in their proxies
221kubectl -n linkerd rollout restart deploy
222```
223
224### Publishing images
225
226The example above builds and loads the docker images into k3d. For testing your
227built images outside your local environment, you need to publish your images so
228they become accessible in those external environments.
229
230To signal `bin/docker-build` or any of the more specific scripts
231`bin/docker-build-*` what registry to use, just set the environment variable
232`DOCKER_REGISTRY` (which defaults to the official registry `cr.l5d.io/linkerd`).
233After having pushed those images through the usual means (`docker push`) you'll
234have to pass the `--registry` flag to `linkerd install` with a value matching
235your registry. Extensions don't have that flag and instead you need to use the
236equivalent Helm value; e.g. for Viz `linkerd viz install --set
237defaultRegistry=...`.
238
239### Go
240
241#### A note about Go run
242
243Our instructions use a [`bin/go-run`](bin/go-run) script in lieu `go run`. This
244is a convenience script that leverages caching via `go build` to make your
245build/run/debug loop faster.
246
247In general, replace commands like this:
248
249```bash
250go run cli/main.go check
251```
252
253with this:
254
255```bash
256bin/go-run cli check
257```
258
259That is equivalent to running `linkerd check` using the code on your branch.
260
261#### Lint
262
263To analyze and lint the Go code using golangci-lint, run:
264
265```bash
266golangci-lint run
267```
268
269#### Formatting
270
271All Go source code is formatted with `goimports`. The version of `goimports`
272used by this project is specified in `go.mod`. To ensure you have the same
273version installed, run `go install -mod=readonly
274golang.org/x/tools/cmd/goimports`. It's recommended that you set your IDE or
275other development tools to use `goimports`. Formatting is checked during CI by
276the `bin/fmt` script.
277
278#### Building the CLI for development
279
280The script for building the CLI binaries using docker is
281`bin/docker-build-cli-bin`. This will also be called indirectly when calling
282`bin/docker-build`. By default it creates binaries for your current host's
283OS/arch.
284
285To cross-build targeting a different OS or architecture, set the environment
286variable `DOCKER_TARGET` according to any of the final stages available in
287[cli/Dockerfile](cli/Dockerfile).
288
289For local development and a faster edit-build-test cycle you can build directly
290without going through a docker container by calling `bin/build-cli-bin`.
291
292If you set the environment variable `LINKERD_LOCAL_BUILD_CLI=1` then
293`bin/docker-build` will use this last method for the step that builds the CLI.
294
295#### Running the control plane for development
296
297Linkerd2's control plane is composed of several Go microservices. You can run
298these components in a Kubernetes cluster, or even locally.
299
300To run an individual component locally, you can use the `go-run` command, and
301pass in valid Kubernetes credentials via the `-kubeconfig` flag. For instance,
302to run the destination service locally, run:
303
304```bash
305bin/go-run controller/cmd destination -kubeconfig ~/.kube/config -log-level debug
306```
307
308You can send test requests to the destination service using the
309`destination-client` in the `controller/script` directory. For instance:
310
311```bash
312bin/go-run controller/script/destination-client -path hello.default.svc.cluster.local:80
313```
314
315##### Debugging the Tap APIService for development
316
317The Tap APIService is a Kubernetes extension API server, so it can be
318challenging to run outside the cluster. The most straightforward workflow is to
319simply test changes by building and loading the container image as explained in
320the [comprehensive configuration](#comprehensive) section above (in order to
321just build this component use `bin/docker-build-tap`).
322
323#### Generating CLI docs
324
325The [documentation](https://linkerd.io/2/cli/) for the CLI tool is partially
326generated from YAML. This can be generated by running the `linkerd doc` command.
327
328### Web
329
330This is a React app fronting a Go process. It uses webpack to bundle assets, and
331postcss to transform css.
332
333These commands assume working [Go](https://golang.org) and
334[Yarn](https://yarnpkg.com) environments.
335
336#### First time setup
337
3381. Install [Yarn](https://yarnpkg.com) and use it to install JS dependencies:
339
340 ```bash
341 brew install yarn
342 bin/web setup
343 ```
344
3452. Install Linkerd on a Kubernetes cluster.
346
347#### Run web standalone
348
349```bash
350bin/web run
351```
352
353The web server will be running on `localhost:7777`.
354
355#### Webpack dev server
356
357To develop with a webpack dev server:
358
3591. Start the development server.
360
361 ```bash
362 bin/web dev
363 ```
364
365 Note: this will start up:
366
367 - `web` on :7777. This is the golang process that serves the dashboard.
368 - `webpack-dev-server` on :8080 to manage rebuilding/reloading of the
369 javascript.
370 - `metrics-api` is port-forwarded from the Kubernetes cluster via `kubectl`
371 on :8085
372
3732. Go to [http://localhost:7777](http://localhost:7777) to see everything
374 running.
375
376#### JavaScript dependencies
377
378To add a JS dependency:
379
380```bash
381cd web/app
382yarn add [dep]
383```
384
385#### Translations
386
387To add a locale:
388
389```bash
390cd web/app
391yarn lingui add-locale [locales...] # will create a messages.json file for new locale(s)
392```
393
394To extract message keys from existing components:
395
396```bash
397cd web/app
398yarn lingui extract
399...
400yarn lingui compile # done automatically in bin/web run
401```
402
403Finally, make sure the new locale is also referred in the following places:
404
405- Under the `lingui` section in `package.json`
406- In the `make-plural/plurals` import in `index.js`
407- In the `langOptions` object in `index.js`
408
409### Rust
410
411All Rust development happens in the
412[`linkerd2-proxy`](https://github.com/linkerd/linkerd2-proxy) repo.
413
414#### Docker
415
416The `bin/docker-build-proxy` script builds the proxy by pulling a pre-published
417proxy binary:
418
419```bash
420bin/docker-build-proxy
421```
422
423#### Locally built proxy
424
425If you want to deploy a locally built proxy, you can build it in the
426[`linkerd2-proxy`](https://github.com/linkerd/linkerd2-proxy) repo by running:
427
428```bash
429DOCKER_TAG=cr.l5d.io/linkerd/proxy:dev make docker
430```
431
432Then, in this repo, run:
433
434```bash
435./bin/k3d image import cr.l5d.io/linkerd/proxy:dev
436```
437
438Now, to make a pod use your image, add the following annotations to it:
439
440```yaml
441config.linkerd.io/proxy-version: dev
442```
443
444## Dependencies
445
446### Updating protobuf dependencies
447
448 If you make Protobuf changes, run:
449
450 ```bash
451bin/protoc-go.sh
452```
453
454### Updating ServiceProfile generated code
455
456The [ServiceProfile client code](./controller/gen/client) is generated by
457[`bin/update-codegen.sh`](bin/update-codegen.sh), which depends on [K8s
458code-generator](https://github.com/kubernetes/code-generator), which does not
459yet support Go Modules. To re-generate this code, check out this repo into your
460`GOPATH`:
461
462```bash
463go get -u github.com/linkerd/linkerd2
464cd $GOPATH/src/github.com/linkerd/linkerd2
465bin/update-codegen.sh
466```
467
468## Linkerd Helm chart
469
470The Linkerd control plane chart is located in the
471[`charts/linkerd2`](charts/linkerd2) folder. The [`charts/patch`](charts/patch)
472chart consists of the Linkerd proxy specification, which is used by the proxy
473injector to inject the proxy container. Both charts depend on the partials
474subchart which can be found in the [`charts/partials`](charts/partials) folder.
475
476Note that the `charts/linkerd2/values.yaml` file contains a placeholder
477`linkerdVersionValue` that you need to replace with an appropriate string (like
478`edge-20.2.2`) before proceeding.
479
480During development, please use the [`bin/helm`](bin/helm) wrapper script to
481invoke the Helm commands. For example,
482
483```bash
484bin/helm install linkerd2 charts/linkerd2
485```
486
487This ensures that you use the same Helm version as that of the Linkerd CI
488system.
489
490For general instructions on how to install the charts check out the
491[docs](https://linkerd.io/2/tasks/install-helm/). You also need to supply or
492generate your own certificates to use the chart, as explained
493[here](https://linkerd.io/2/tasks/generate-certificates/).
494
495### Extensions Helm charts
496
497Extensions provide each their own chart:
498
499- Viz: [`viz/charts/linkerd-viz`](viz/charts/linkerd-viz)
500- Multicluster:
501 [`multicluster/charts/linkerd-multicluster`](multicluster/charts/linkerd-multicluster)
502- Jaeger: [`jaeger/charts/linkerd-jaeger`](jaeger/charts/linkerd-jaeger)
503
504### Making changes to the chart templates
505
506Whenever you make changes to the files under
507[`charts/linkerd2/templates`](charts/linkerd2/templates) or its dependency
508[`charts/partials`](charts/partials), make sure to run
509[`bin/helm-build`](bin/helm-build) which will refresh the dependencies and lint
510the templates.
511
512### Generating Helm charts docs
513
514Whenever a new chart is created or updated a README should be generated from
515the chart's `values.yaml`. This can be done by utilizing the bundled
516[helm-docs](https://github.com/norwoodj/helm-docs) binary. For adding additional
517information, such as specific installation instructions a README template is
518required to be created. Check existing charts for examples.
519
520#### Using helm-docs
521
522Example usage:
523
524```sh
525bin/helm-docs
526bin/helm-docs --dry-run #Prints to cli instead
527bin/helm-docs --chart-search-root=./charts #Sets search root for charts
528bin/helm-docs --template-files=README.md.gotmpl #Sets the template file used
529```
530
531Note:
532The tool searches through the current directory and sub-directories by default.
533For additional information checkout their repo above.
534
535#### Annotating values.yaml
536
537To allow helm-docs to properly document the values in `values.yaml` a descriptive
538comment is required. This can be done in two ways.
539Either comment the value directly above with
540`# -- This is a really nice value` where the double dashes automatically
541annotates the value. Another explicit usage is to type out the value name.
542`# global.MyNiceValue -- I really like this value`
543
544#### Markdown templates
545
546In order to accommodate for extra data that might not have a proper place in the
547´values.yaml´ file the corresponding ´README.md.gotmpl´ can be modified for each
548chart. This template allows the standard markdown syntax as well as the go
549templating functions. Checkout
550[helm-docs](https://github.com/norwoodj/helm-docs) for more info.
View as plain text