New content dir for 2.11 docs (#1178)

* New content dir for 2.11 docs

`/content/2.11` is basically a copy of `/content/2.10`, where all new
content pertaining to 2.11 should reside. I tried to remove all direct
references to `2.10` and made them relative dir references instead, so
this process gets easier as new major versions are released.
This commit is contained in:
Alejandro Pedraza 2021-09-30 11:01:19 -05:00 committed by GitHub
parent 24701b0712
commit e9ae7770ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
121 changed files with 15138 additions and 95 deletions

View File

@ -102,7 +102,7 @@ identifier = "start"
name = "GET STARTED"
post = "</span>"
pre = "<span class='button-spacer'>"
url = "/2.10/getting-started/"
url = "/getting-started/"
weight = 6
[outputFormats.REDIRECTS]
baseName = "_redirects"

View File

@ -10,7 +10,7 @@ weight = 34
+++
{{< note >}}
This document is specific to Linkerd 1.x. If you're on Kubernetes, you may wish
to consider [Linkerd 2.x](/2.10/getting-started/) instead.
to consider [Linkerd 2.x](/getting-started/) instead.
{{< /note >}}
If you have a Kubernetes cluster or even just run

View File

@ -10,64 +10,71 @@ aliases = [
Linkerd is capable of proxying all TCP traffic, including TLS connections,
WebSockets, and HTTP tunneling.
In most cases, Linkerd can do this without configuration. To do this, Linkerd
performs *protocol detection* to determine whether traffic is HTTP or HTTP/2
(including gRPC). If Linkerd detects that a connection is HTTP or HTTP/2,
Linkerd will automatically provide HTTP-level metrics and routing.
In most cases, Linkerd can do this without configuration. To accomplish this,
Linkerd performs *protocol detection* to determine whether traffic is HTTP or
HTTP/2 (including gRPC). If Linkerd detects that a connection is HTTP or
HTTP/2, Linkerd automatically provides HTTP-level metrics and routing.
If Linkerd *cannot* determine that a connection is using HTTP or HTTP/2,
Linkerd will proxy the connection as a plain TCP connection, applying
[mTLS](../automatic-mtls/) and providing byte-level metrics as usual.
{{< note >}}
Client-initiated HTTPS will be treated as TCP, not as HTTP, as Linkerd will not
be able to observe the HTTP transactions on the connection.
{{< /note >}}
(Note that HTTPS calls to or from meshed pods are treated as TCP, not as HTTP.
Because the client initiates the TLS connection, Linkerd is not be able to
decrypt the connection to observe the HTTP transactions.)
## Configuring protocol detection
In some cases, Linkerd's protocol detection cannot function because it is not
provided with enough client data. This can result in a 10-second delay in
creating the connection as the protocol detection code waits for more data.
This situation is often encountered when using "server-speaks-first" protocols,
or protocols where the server sends data before the client does, and can be
avoided by supplying Linkerd with some additional configuration.
{{< note >}}
Regardless of the underlying protocol, client-initiated TLS connections do not
require any additional configuration, as TLS itself is a client-speaks-first
protocol.
If you are experiencing 10-second delays when establishing connections, you are
likely running into a protocol detection timeout. This section will help you
understand how to fix this.
{{< /note >}}
In some cases, Linkerd's protocol detection will time out because it doesn't
see any bytes from the client. This situation is commonly encountered when
using "server-speaks-first" protocols where the server sends data before the
client does, such as SMTP, or protocols that proactively establish connections
without sending data, such as Memcache. In this case, the connection will
proceed as a TCP connection after a 10-second protocol detection delay.
To avoid this delay, you will need to provide some configuration for Linkerd.
There are two basic mechanisms for configuring protocol detection: _opaque
ports_ and _skip ports_. Marking a port as _opaque_ instructs Linkerd to proxy
the connection as a TCP stream and not to attempt protocol detection. Marking a
port as _skip_ bypasses the proxy entirely. Opaque ports are generally
preferred (as Linkerd can provide mTLS, TCP-level metrics, etc), but crucially,
opaque ports can only be used for services inside the cluster.
ports_ and _skip ports_. Marking a port as _opaque_ instructs Linkerd to skip
protocol detection and immediately proxy the connection as a TCP stream;
marking a port as a _skip port_ bypasses the proxy entirely. Opaque ports are
generally preferred (as Linkerd can provide mTLS, TCP-level metrics, etc), but
can only be used for services inside the cluster.
By default, Linkerd automatically marks some ports as opaque, including the
default ports for SMTP, MySQL, PostgresQL, and Memcache. Services that speak
those protocols, use the default ports, and are inside the cluster do not need
further configuration.
By default, Linkerd automatically marks the ports for some server-speaks-first
protocol as opaque. Services that speak those protocols over the default ports
to destinations inside the cluster do not need further configuration.
Linkerd's default list of opaque ports in the 2.10 release is 25 (SMTP), 443
(client-initiated TLS), 587 (SMTP), 3306 (MySQL), 5432 (Postgres), and 11211
(Memcache). Note that this may change in future releases.
The following table summarizes some common server-speaks-first protocols and
the configuration necessary to handle them. The "on-cluster config" column
refers to the configuration when the destination is *on* the same cluster; the
"off-cluster config" to when the destination is external to the cluster.
The following table contains common protocols that may require configuration.
| Protocol | Default port(s) | On-cluster config | Off-cluster config |
|-----------------|-----------------|-------------------|--------------------|
| SMTP | 25, 587 | none\* | skip ports |
| MySQL | 3306 | none\* | skip ports |
| MySQL with Galera replication | 3306, 4444, 4567, 4568 | none\* | skip ports |
| PostgreSQL | 5432 | none\* | skip ports |
| Redis | 6379 | none\* | skip ports |
| ElasticSearch | 9300 | none\* | skip ports |
| Memcache | 11211 | none\* | skip ports |
| Protocol | Default port(s) | Notes |
|-----------------|-----------------|-------|
| SMTP | 25, 587 | |
| MySQL | 3306 | |
| MySQL with Galera | 3306, 4444, 4567, 4568 | Ports 4444, 4567, and 4568 are not in Linkerd's default set of opaque ports |
| PostgreSQL | 5432 | |
| Redis | 6379 | |
| ElasticSearch | 9300 | Not in Linkerd's default set of opaque ports |
| Memcache | 11211 | |
_\* No configuration is required if the standard port is used. If a
non-standard port is used, you must mark the port as opaque._
If you are using one of those protocols, follow this decision tree to determine
which configuration you need to apply.
* Is the protocol wrapped in TLS?
* Yes: no configuration required.
* No: is the destination on the cluster?
* Yes: is the port in Linkerd's default list of opaque ports?
* Yes: no configuration required.
* No: mark port(s) as opaque.
* No: mark port(s) as skip.
## Marking a port as opaque
@ -84,8 +91,7 @@ workloads in that namespace.
{{< note >}}
Since this annotation informs the behavior of meshed _clients_, it can be
applied to services that use server-speaks-first protocols even if the service
itself is not meshed.
applied to unmeshed services as well as meshed services.
{{< /note >}}
Setting the opaque-ports annotation can be done by using the `--opaque-ports`
@ -104,7 +110,7 @@ Multiple ports can be provided as a comma-delimited string. The values you
provide will replace, not augment, the default list of opaque ports.
{{< /note >}}
## Skipping the proxy
## Marking a port as skip
Sometimes it is necessary to bypass the proxy altogether. For example, when
connecting to a server-speaks-first destination that is outside of the cluster,

View File

@ -0,0 +1,6 @@
---
title: "Overview"
---
<!-- markdownlint-disable -->
<meta http-equiv="Refresh" content="0; url=overview/">
<!-- markdownlint-enable -->

View File

@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0; url=../tasks/troubleshooting/">
<script type="text/javascript">
window.onload = function() {
var hash = window.location.hash;
window.location.href = window.location.origin + "/2.11/tasks/troubleshooting/" + hash;
}
</script>
<title>Linkerd Check Redirection</title>
</head>
<body>
If you are not redirected automatically, follow this
<a href='../tasks/troubleshooting/'>link</a>.
</body>
</html>

View File

@ -0,0 +1,14 @@
+++
title = "Features"
weight = 3
[sitemap]
priority = 1.0
+++
Linkerd offers many features, outlined below. For our walkthroughs and guides,
please see the [Linkerd task docs]({{% ref "../tasks" %}}). For a reference,
see the [Linkerd reference docs]({{% ref "../reference" %}}).
## Linkerd's features
{{% sectiontoc "features" %}}

View File

@ -0,0 +1,127 @@
+++
title = "Automatic mTLS"
description = "Linkerd automatically enables mutual Transport Layer Security (TLS) for all communication between meshed applications."
weight = 4
aliases = [
"../automatic-tls"
]
+++
By default, Linkerd automatically enables mutually-authenticated Transport
Layer Security (mTLS) for all TCP traffic between meshed pods. This means that
Linkerd adds authenticated, encrypted communication to your application with
no extra work on your part. (And because the Linkerd control plane also runs
on the data plane, this means that communication between Linkerd's control
plane components are also automatically secured via mTLS.)
See [Caveats and future work](#caveats-and-future-work) below for some details.
## What is mTLS?
A full definition of mTLS is outside the scope of this doc. For an overview of
what mTLS is and how it works in Kuberentes clusters, we suggest reading
through [A Kubernetes engineer's guide to
mTLS](https://buoyant.io/mtls-guide/).
## Which traffic can Linkerd automatically mTLS?
Linkerd transparently applies mTLS to all TCP communication between meshed
pods. However, there are still ways in which you may still have non-mTLS
traffic in your system, including:
* Traffic to or from non-meshed pods (e.g. Kubernetes healthchecks)
* Traffic on ports that were marked as [skip ports](../protocol-detection/),
which bypass the proxy entirely.
You can [verify which traffic is mTLS'd](../../tasks/validating-your-traffic/)
in a variety of ways. External systems such as [Buoyant
Cloud](https://buoyant.io/cloud) can also automatically generate reports of TLS
traffic patterns on your cluster.
## Operational concerns
Linkerd's mTLS requires some preparation for production use, especially for
long-lived clusters or clusters that expect to have cross-cluster traffic.
The trust anchor generated by the default `linkerd install` CLI command expires
after 365 days. After that, it must be [manually
rotated](../../tasks/manually-rotating-control-plane-tls-credentials/)—a
non-trivial task. Alternatively, you can [provide the trust anchor
yourself](../../tasks/generate-certificates/) and control the expiration date,
e.g. setting it to 10 years rather than one year.
Kubernetes clusters that make use of Linkerd's [multi-cluster
communication](../multicluster/) must share a trust anchor. Thus, the default
`linkerd install` setup will not work for this situation and you must provide
an explicit trust anchor.
Similarly, the default cluster issuer certificate and key expire after a year.
These must be [rotated before they
expire](../../tasks/manually-rotating-control-plane-tls-credentials/).
Alternatively, you can [set up automatic rotation with
`cert-manager`](../../tasks/automatically-rotating-control-plane-tls-credentials/).
External systems such as [Buoyant Cloud](https://buoyant.io/cloud) can be used
to monitor cluster credentials and to send reminders if they are close to
expiration.
## How does Linkerd's mTLS implementation work?
The [Linkerd control plane](../../reference/architecture/) contains a certificate
authority (CA) called `identity`. This CA issues TLS certificates to each
Linkerd data plane proxy. Each certificate is bound to the [Kubernetes
ServiceAccount](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)
identity of the containing pod. These TLS certificates expire after 24 hours
and are automatically rotated. The proxies use these certificates to encrypt
and authenticate TCP traffic to other proxies.
On the control plane side, Linkerd maintains a set of credentials in the
cluster: a trust anchor, and an issuer certificate and private key. These
credentials can be generated by Linkerd during install time, or optionally
provided by an external source, e.g. [Vault](https://vaultproject.io) or
[cert-manager](https://github.com/jetstack/cert-manager). The issuer
certificate and private key are stored in a [Kubernetes
Secret](https://kubernetes.io/docs/concepts/configuration/secret/); this Secret
is placed in the `linkerd` namespace and can only be read by the service
account used by the [Linkerd control plane](../../reference/architecture/)'s
`identity` component.
On the data plane side, each proxy is passed the trust anchor in an environment
variable. At startup, the proxy generates a private key, stored in a [tmpfs
emptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir) which
stays in memory and never leaves the pod. The proxy connects to the control
plane's `identity` component, validating the connection to `identity` with the
trust anchor, and issues a [certificate signing request
(CSR)](https://en.wikipedia.org/wiki/Certificate_signing_request). The CSR
contains an initial certificate with identity set to the pod's [Kubernetes
ServiceAccount](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/),
and the actual service account token, so that `identity` can validate that the
CSR is valid. After validation, the signed trust bundle is returned to the
proxy, which can use it as both a client and server certificate. These
certificates are scoped to 24 hours and dynamically refreshed using the same
mechanism.
Finally, when a proxy receives an outbound connection from the application
container within its pod, it looks up that desitnation with the Linkerd control
plane. If it's in the Kubernetes cluster, the control plane provides the proxy
with the destination's endpoint addresses, along with metadata including an
identity name. When the proxy connects to the destination, it initiates a TLS
handshake and verifies that that the destination proxy's certificate is signed
by the trust anchor and contains the expected identity.
## Caveats and future work
There are a few known gaps in Linkerd's ability to automatically encrypt and
authenticate all communication in the cluster. These gaps will be fixed in
future releases:
* Linkerd does not currently *enforce* mTLS. Any unencrypted requests inside
the mesh will be opportunistically upgraded to mTLS. Any requests originating
from inside or outside the mesh will not be automatically mTLS'd by Linkerd.
This will be addressed in a future Linkerd release, likely as an opt-in
behavior as it may break some existing applications.
* Ideally, the ServiceAccount token that Linkerd uses would not be shared with
other potential uses of that token. In future Kubernetes releases, Kubernetes
will support audience/time-bound ServiceAccount tokens, and Linkerd will use
those instead.

View File

@ -0,0 +1,106 @@
+++
title = "CNI Plugin"
description = "Linkerd can be configured to run a CNI plugin that rewrites each pod's iptables rules automatically."
+++
Linkerd installs can be configured to run a
[CNI plugin](https://github.com/containernetworking/cni) that rewrites each
pod's iptables rules automatically. Rewriting iptables is required for routing
network traffic through the pod's `linkerd-proxy` container. When the CNI plugin
is enabled, individual pods no longer need to include an init container that
requires the `NET_ADMIN` capability to perform rewriting. This can be useful in
clusters where that capability is restricted by cluster administrators.
## Installation
Usage of the Linkerd CNI plugin requires that the `linkerd-cni` DaemonSet be
successfully installed on your cluster _first_, before installing the Linkerd
control plane.
### Using the CLI
To install the `linkerd-cni` DaemonSet, run:
```bash
linkerd install-cni | kubectl apply -f -
```
Once the DaemonSet is up and running, all subsequent installs that include a
`linkerd-proxy` container (including the Linkerd control plane), no longer need
to include the `linkerd-init` container. Omission of the init container is
controlled by the `--linkerd-cni-enabled` flag at control plane install time.
Install the Linkerd control plane, with:
```bash
linkerd install --linkerd-cni-enabled | kubectl apply -f -
```
This will set a `cniEnabled` flag in the `linkerd-config` ConfigMap. All
subsequent proxy injections will read this field and omit init containers.
### Using Helm
First ensure that your Helm local cache is updated:
```bash
helm repo update
helm search linkerd2-cni
NAME CHART VERSION APP VERSION DESCRIPTION
linkerd-edge/linkerd2-cni 20.1.1 edge-20.1.1 A helm chart containing the resources needed by the Linke...
linkerd-stable/linkerd2-cni 2.7.0 stable-2.7.0 A helm chart containing the resources needed by the Linke...
```
Run the following commands to install the CNI DaemonSet:
```bash
# install the CNI plugin first
helm install linkerd2-cni linkerd2/linkerd2-cni
# ensure the plugin is installed and ready
linkerd check --pre --linkerd-cni-enabled
```
{{< note >}}
For Helm versions < v3, `--name` flag has to specifically be passed.
In Helm v3, It has been deprecated, and is the first argument as
specified above.
{{< /note >}}
At that point you are ready to install Linkerd with CNI enabled.
You can follow [Installing Linkerd with Helm](../../tasks/install-helm/) to do so.
## Additional configuration
The `linkerd install-cni` command includes additional flags that you can use to
customize the installation. See `linkerd install-cni --help` for more
information. Note that many of the flags are similar to the flags that can be
used to configure the proxy when running `linkerd inject`. If you change a
default when running `linkerd install-cni`, you will want to ensure that you
make a corresponding change when running `linkerd inject`.
The most important flags are:
1. `--dest-cni-net-dir`: This is the directory on the node where the CNI
Configuration resides. It defaults to: `/etc/cni/net.d`.
2. `--dest-cni-bin-dir`: This is the directory on the node where the CNI Plugin
binaries reside. It defaults to: `/opt/cni/bin`.
3. `--cni-log-level`: Setting this to `debug` will allow more verbose logging.
In order to view the CNI Plugin logs, you must be able to see the `kubelet`
logs. One way to do this is to log onto the node and use
`journalctl -t kubelet`. The string `linkerd-cni:` can be used as a search to
find the plugin log output.
## Upgrading the CNI plugin
Since the CNI plugin is basically stateless, there is no need for a separate
`upgrade` command. If you are using the CLI to upgrade the CNI plugin you can
just do:
```bash
linkerd install-cni | kubectl apply --prune -l linkerd.io/cni-resource=true -f -
```
Keep in mind that if you are upgrading the plugin from an experimental version,
you need to uninstall and install it again.

View File

@ -0,0 +1,127 @@
+++
title = "On-cluster metrics stack"
description = "Linkerd provides a full on-cluster metrics stack, including CLI tools and dashboards."
+++
Linkerd provides a full on-cluster metrics stack, including CLI tools, a web
dashboard, and pre-configured Grafana dashboards.
To access this functionality, you install the viz extension:
```bash
linkerd viz install | kubectl apply -f -
```
This extension installs the following components into your `linkerd-viz`
namespace:
* A [Prometheus](https://prometheus.io/) instance
* A [Grafana](https://grafana.com/) instance
* metrics-api, tap, tap-injector, and web components
These components work together to provide an on-cluster metrics stack.
{{< note >}}
To limit excessive resource usage on the cluster, the metrics stored by this
extension are _transient_. Only the past 6 hours are stored, and metrics do not
persist in the event of pod restart or node outages.
{{< /note >}}
## Operating notes
This metrics stack may require significant cluster resources. Prometheus, in
particular, will consume resources as a function of traffic volume within the
cluster.
Additionally, by default, metrics data is stored in a transient manner that is
not resilient to pod restarts or to node outages. See [Bringing your own
Prometheus](../../tasks/external-prometheus/) for one way to address this.
## Linkerd dashboard
The Linkerd dashboard provides a high level view of what is happening with your
services in real time. It can be used to view the "golden" metrics (success
rate, requests/second and latency), visualize service dependencies and
understand the health of specific service routes. One way to pull it up is by
running `linkerd viz dashboard` from the command line.
{{< fig src="/images/architecture/stat.png" title="Top Line Metrics">}}
## Grafana
As a component of the control plane, Grafana provides actionable dashboards for
your services out of the box. It is possible to see high level metrics and dig
down into the details, even for pods.
The dashboards that are provided out of the box include:
{{< gallery >}}
{{< gallery-item src="/images/screenshots/grafana-top.png"
title="Top Line Metrics" >}}
{{< gallery-item src="/images/screenshots/grafana-deployment.png"
title="Deployment Detail" >}}
{{< gallery-item src="/images/screenshots/grafana-pod.png"
title="Pod Detail" >}}
{{< gallery-item src="/images/screenshots/grafana-health.png"
title="Linkerd Health" >}}
{{< /gallery >}}
linkerd -n emojivoto check --proxy
## Examples
In these examples, we assume you've installed the emojivoto example
application. Please refer to the [Getting Started
Guide](../../getting-started/) for how to do this.
You can use your dashboard extension and see all the services in the demo app.
Since the demo app comes with a load generator, we can see live traffic metrics
by running:
```bash
linkerd -n emojivoto viz stat deploy
```
This will show the "golden" metrics for each deployment:
* Success rates
* Request rates
* Latency distribution percentiles
To dig in a little further, it is possible to use `top` to get a real-time
view of which paths are being called:
```bash
linkerd -n emojivoto viz top deploy
```
To go even deeper, we can use `tap` shows the stream of requests across a
single pod, deployment, or even everything in the emojivoto namespace:
```bash
linkerd -n emojivoto viz tap deploy/web
```
All of this functionality is also available in the dashboard, if you would like
to use your browser instead:
{{< gallery >}}
{{< gallery-item src="/images/getting-started/stat.png"
title="Top Line Metrics">}}
{{< gallery-item src="/images/getting-started/inbound-outbound.png"
title="Deployment Detail">}}
{{< gallery-item src="/images/getting-started/top.png"
title="Top" >}}
{{< gallery-item src="/images/getting-started/tap.png"
title="Tap" >}}
{{< /gallery >}}

View File

@ -0,0 +1,59 @@
+++
title = "Distributed Tracing"
description = "You can enable distributed tracing support in Linkerd."
+++
Tracing can be an invaluable tool in debugging distributed systems performance,
especially for identifying bottlenecks and understanding the latency cost of
each component in your system. Linkerd can be configured to emit trace spans
from the proxies, allowing you to see exactly what time requests and responses
spend inside.
Unlike most of the features of Linkerd, distributed tracing requires both code
changes and configuration. (You can read up on [Distributed tracing in the
service mesh: four myths](/2019/08/09/service-mesh-distributed-tracing-myths/)
for why this is.)
Furthermore, Linkerd provides many of the features that are often associated
with distributed tracing, *without* requiring configuration or application
changes, including:
* Live service topology and dependency graphs
* Aggregated service health, latencies, and request volumes
* Aggregated path / route health, latencies, and request volumes
For example, Linkerd can display a live topology of all incoming and outgoing
dependencies for a service, without requiring distributed tracing or any other
such application modification:
{{< fig src="/images/books/webapp-detail.png"
title="The Linkerd dashboard showing an automatically generated topology graph"
>}}
Likewise, Linkerd can provide golden metrics per service and per *route*, again
without requiring distributed tracing or any other such application
modification:
{{< fig src="/images/books/webapp-routes.png"
title="Linkerd dashboard showing an automatically generated route metrics"
>}}
## Using distributed tracing
That said, distributed tracing certainly has its uses, and Linkerd makes this
as easy as it can. Linkerd's role in distributed tracing is actually quite
simple: when a Linkerd data plane proxy sees a tracing header in a proxied HTTP
request, Linkerd will emit a trace span for that request. This span will
include information about the exact amount of time spent in the Linkerd proxy.
When paired with software to collect, store, and analyze this information, this
can provide significant insight into the behavior of the mesh.
To use this feature, you'll also need to introduce several additional
components in your system., including an ingress layer that kicks off the trace
on particular requests, a client library for your application (or a mechanism
to propagate trace headers), a trace collector to collect span data and turn
them into traces, and a trace backend to store the trace data and allow the
user to view/query it.
For details, please see our [guide to adding distributed tracing to your
application with Linkerd](../../tasks/distributed-tracing/).

View File

@ -0,0 +1,12 @@
+++
title = "Fault Injection"
description = "Linkerd provides mechanisms to programmatically inject failures into services."
+++
Fault injection is a form of chaos engineering where the error rate of a service
is artificially increased to see what impact there is on the system as a whole.
Traditionally, this would require modifying the service's code to add a fault
injection library that would be doing the actual work. Linkerd can do this
without any service code changes, only requiring a little configuration.
To inject faults into your own services, follow the [tutorial](../../tasks/fault-injection/).

View File

@ -0,0 +1,162 @@
+++
title = "High Availability"
description = "The Linkerd control plane can run in high availability (HA) mode."
aliases = [
"../ha/"
]
+++
For production workloads, Linkerd's control plane can run in high availability
(HA) mode. This mode:
* Runs three replicas of critical control plane components.
* Sets production-ready CPU and memory resource requests on control plane
components.
* Sets production-ready CPU and memory resource requests on data plane proxies
* *Requires* that the [proxy auto-injector](../proxy-injection/) be
functional for any pods to be scheduled.
* Sets [anti-affinity
policies](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity)
on critical control plane components to ensure, if possible, that they are
scheduled on separate nodes and in separate zones by default.
## Enabling HA
You can enable HA mode at control plane installation time with the `--ha` flag:
```bash
linkerd install --ha | kubectl apply -f -
```
Also note the Viz extension also supports an `--ha` flag with similar
characteristics:
```bash
linkerd viz install --ha | kubectl apply -f -
```
You can override certain aspects of the HA behavior at installation time by
passing other flags to the `install` command. For example, you can override the
number of replicas for critical components with the `--controller-replicas`
flag:
```bash
linkerd install --ha --controller-replicas=2 | kubectl apply -f -
```
See the full [`install` CLI documentation](../../reference/cli/install/) for
reference.
The `linkerd upgrade` command can be used to enable HA mode on an existing
control plane:
```bash
linkerd upgrade --ha | kubectl apply -f -
```
## Proxy injector failure policy
The HA proxy injector is deployed with a stricter failure policy to enforce
[automatic proxy injection](../proxy-injection/). This setup ensures
that no annotated workloads are accidentally scheduled to run on your cluster,
without the Linkerd proxy. (This can happen when the proxy injector is down.)
If proxy injection process failed due to unrecognized or timeout errors during
the admission phase, the workload admission will be rejected by the Kubernetes
API server, and the deployment will fail.
Hence, it is very important that there is always at least one healthy replica
of the proxy injector running on your cluster.
If you cannot guarantee the number of healthy proxy injector on your cluster,
you can loosen the webhook failure policy by setting its value to `Ignore`, as
seen in the
[Linkerd Helm chart](https://github.com/linkerd/linkerd2/blob/803511d77b33bd9250b4a7fecd36752fcbd715ac/charts/linkerd2/templates/proxy-injector-rbac.yaml#L98).
{{< note >}}
See the Kubernetes
[documentation](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
for more information on the admission webhook failure policy.
{{< /note >}}
## Exclude the kube-system namespace
Per recommendation from the Kubernetes
[documentation](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#avoiding-operating-on-the-kube-system-namespace),
the proxy injector should be disabled for the `kube-system` namespace.
This can be done by labeling the `kube-system` namespace with the following
label:
```bash
kubectl label namespace kube-system config.linkerd.io/admission-webhooks=disabled
```
The Kubernetes API server will not call the proxy injector during the admission
phase of workloads in namespace with this label.
If your Kubernetes cluster have built-in reconcilers that would revert any changes
made to the `kube-system` namespace, you should loosen the proxy injector
failure policy following these [instructions](#proxy-injector-failure-policy).
## Pod anti-affinity rules
All critical control plane components are deployed with pod anti-affinity rules
to ensure redundancy.
Linkerd uses a `requiredDuringSchedulingIgnoredDuringExecution` pod
anti-affinity rule to ensure that the Kubernetes scheduler does not colocate
replicas of critical component on the same node. A
`preferredDuringSchedulingIgnoredDuringExecution` pod anti-affinity rule is also
added to try to schedule replicas in different zones, where possible.
In order to satisfy these anti-affinity rules, HA mode assumes that there
are always at least three nodes in the Kubernetes cluster. If this assumption is
violated (e.g. the cluster is scaled down to two or fewer nodes), then the
system may be left in a non-functional state.
Note that these anti-affinity rules don't apply to add-on components like
Prometheus and Grafana.
## Scaling Prometheus
The Linkerd Viz extension provides a pre-configured Prometheus pod, but for
production workloads we recommend setting up your own Prometheus instance. To
scrape the data plane metrics, follow the instructions
[here](../../tasks/external-prometheus/). This will provide you
with more control over resource requirement, backup strategy and data retention.
When planning for memory capacity to store Linkerd timeseries data, the usual
guidance is 5MB per meshed pod.
If your Prometheus is experiencing regular `OOMKilled` events due to the amount
of data coming from the data plane, the two key parameters that can be adjusted
are:
* `storage.tsdb.retention.time` defines how long to retain samples in storage.
A higher value implies that more memory is required to keep the data around
for a longer period of time. Lowering this value will reduce the number of
`OOMKilled` events as data is retained for a shorter period of time
* `storage.tsdb.retention.size` defines the maximum number of bytes that can be
stored for blocks. A lower value will also help to reduce the number of
`OOMKilled` events
For more information and other supported storage options, see the Prometheus
documentation
[here](https://prometheus.io/docs/prometheus/latest/storage/#operational-aspects).
## Working with Cluster AutoScaler
The Linkerd proxy stores its mTLS private key in a
[tmpfs emptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir)
volume to ensure that this information never leaves the pod. This causes the
default setup of Cluster AutoScaler to not be able to scale down nodes with
injected workload replicas.
The workaround is to annotate the injected workload with the
`cluster-autoscaler.kubernetes.io/safe-to-evict: "true"` annotation. If you
have full control over the Cluster AutoScaler configuration, you can start the
Cluster AutoScaler with the `--skip-nodes-with-local-storage=false` option.
For more information on this, see the Cluster AutoScaler documentation
[here](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#what-types-of-pods-can-prevent-ca-from-removing-a-node).

View File

@ -0,0 +1,21 @@
+++
title = "HTTP, HTTP/2, and gRPC Proxying"
description = "Linkerd will automatically enable advanced features (including metrics, load balancing, retries, and more) for HTTP, HTTP/2, and gRPC connections."
weight = 1
+++
Linkerd can proxy all TCP connections, and will automatically enable advanced
features (including metrics, load balancing, retries, and more) for HTTP,
HTTP/2, and gRPC connections. (See
[TCP Proxying and Protocol Detection](../protocol-detection/) for details of how
this detection happens).
## Notes
* gRPC applications that use [grpc-go][grpc-go] must use version 1.3 or later due
to a [bug](https://github.com/grpc/grpc-go/issues/1120) in earlier versions.
* gRPC applications that use [@grpc/grpc-js][grpc-js] must use version 1.1.0 or later
due to a [bug](https://github.com/grpc/grpc-node/issues/1475) in earlier versions.
[grpc-go]: https://github.com/grpc/grpc-go
[grpc-js]: https://github.com/grpc/grpc-node/tree/master/packages/grpc-js

View File

@ -0,0 +1,14 @@
+++
title = "Ingress"
description = "Linkerd can work alongside your ingress controller of choice."
weight = 7
aliases = [
"../ingress/"
]
+++
For reasons of simplicity, Linkerd does not provide its own ingress controller.
Instead, Linkerd is designed to work alongside your ingress controller of choice.
See the [Using Ingress with Linkerd Guide](../../tasks/using-ingress/) for examples
of how to get it all working together.

View File

@ -0,0 +1,37 @@
+++
title = "Load Balancing"
description = "Linkerd automatically load balances requests across all destination endpoints on HTTP, HTTP/2, and gRPC connections."
weight = 9
+++
For HTTP, HTTP/2, and gRPC connections, Linkerd automatically load balances
requests across all destination endpoints without any configuration required.
(For TCP connections, Linkerd will balance connections.)
Linkerd uses an algorithm called EWMA, or *exponentially weighted moving average*,
to automatically send requests to the fastest endpoints. This load balancing can
improve end-to-end latencies.
## Service discovery
For destinations that are not in Kubernetes, Linkerd will balance across
endpoints provided by DNS.
For destinations that are in Kubernetes, Linkerd will look up the IP address in
the Kubernetes API. If the IP address corresponds to a Service, Linkerd will
load balance across the endpoints of that Service and apply any policy from that
Service's [Service Profile](../service-profiles/). On the other hand,
if the IP address corresponds to a Pod, Linkerd will not perform any load
balancing or apply any [Service Profiles](../service-profiles/).
{{< note >}}
If working with headless services, endpoints of the service cannot be retrieved.
Therefore, Linkerd will not perform load balancing and instead route only to the
target IP address.
{{< /note >}}
## Load balancing gRPC
Linkerd's load balancing is particularly useful for gRPC (or HTTP/2) services
in Kubernetes, for which [Kubernetes's default load balancing is not
effective](https://kubernetes.io/blog/2018/11/07/grpc-load-balancing-on-kubernetes-without-tears/).

View File

@ -0,0 +1,104 @@
+++
title = "Multi-cluster communication"
description = "Linkerd can transparently and securely connect services that are running in different clusters."
aliases = [ "multicluster_support" ]
+++
Linkerd can connect Kubernetes services across cluster boundaries in a way that
is secure, fully transparent to the application, and independent of network
topology. This multi-cluster capability is designed to provide:
1. **A unified trust domain.** The identity of source and destination workloads
are validated at every step, both in and across cluster boundaries.
2. **Separate failure domains.** Failure of a cluster allows the remaining
clusters to function.
3. **Support for heterogeneous networks.** Since clusters can span clouds,
VPCs, on-premises data centers, and combinations thereof, Linkerd does not
introduce any L3/L4 requirements other than gateway connectivity.
4. **A unified model alongside in-cluster communication.** The same
observability, reliability, and security features that Linkerd provides for
in-cluster communication extend to cross-cluster communication.
Just as with in-cluster connections, Linkerds cross-cluster connections are
transparent to the application code. Regardless of whether that communication
happens within a cluster, across clusters within a datacenter or VPC, or across
the public Internet, Linkerd will establish a connection between clusters
thats encrypted and authenticated on both sides with mTLS.
## How it works
Linkerd's multi-cluster support works by "mirroring" service information
between clusters. Because remote services are represented as Kubernetes
services, the full observability, security and routing features of Linkerd
apply uniformly to both in-cluster and cluster-calls, and the application does
not need to distinguish between those situations.
{{< fig
alt="Overview"
title="Overview"
center="true"
src="/images/multicluster/feature-overview.svg" >}}
Linkerd's multi-cluster functionality is implemented by two components:
a *service mirror* and a *gateway*. The *service mirror* component watches
a target cluster for updates to services and mirrors those service updates
locally on a source cluster. This provides visibility into the service names of
the target cluster so that applications can address them directly. The
*multi-cluster gateway* component provides target clusters a way to receive
requests from source clusters. (This allows Linkerd to support [hierarchical
networks](/2020/02/17/architecting-for-multicluster-kubernetes/#requirement-i-support-hierarchical-networks).)
Once these components are installed, Kubernetes `Service` resources that match
a label selector can be exported to other clusters.
## Headless services
By default, Linkerd will mirror all exported services from a target cluster as
`clusterIP` services in a source cluster (they will be assigned a virtual IP).
This also extends to [headless
services](https://kubernetes.io/docs/concepts/services-networking/service/#headless-services);
an exported headless service will be mirrored as a `clusterIP` service in the
source cluster. In general, headless services are used when a workloads needs a
stable network identifier or to facilitate service discovery without being tied
to Kubernetes' native implementation, this allows clients to either implement
their own load balancing or to address a pod directly through its DNS name. In
certain situations, it is desireable to preserve some of this functionality,
especially when working with Kubernetes objects that require it, such as
[StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/).
Linkerd's multi-cluster extension can be configured with support for headless
services when linking two clusters together. When the feature is turned on, the
*service mirror* component will export headless services without assigning them
an IP. This allows clients to talk to specific pods (or hosts) across clusters.
To support direct communication, underneath the hood, the service mirror
component will create an *endpoint mirror* for each host that backs a headless
service. To exemplify, if in a target cluster there is a StatefulSet deployed
with two replicas, and the StatefulSet is backed by a headless service, when
the service will be exported, the source cluster will create a headless mirror
along with two "endpoint mirrors" representing the hosts in the StatefulSet.
This approach allows Linkerd to preserve DNS record creation and support direct
communication to pods across clusters. Clients may also implement their own
load balancing based on the DNS records created by the headless service.
Hostnames are also preserved across clusters, meaning that the only difference
in the DNS name (or FQDN) is the headless service's mirror name. In order to be
exported as a headless service, the hosts backing the service need to be named
(e.g a StatefulSet is supported since all pods have a hostname, but a
Deployment would not be supported, since they do not allow for arbitrary
hostnames in the pod spec).
Ready to get started? See the [getting started with multi-cluster
guide](../../tasks/multicluster/) for a walkthrough.
## Further reading
* [Multi-cluster installation instructions](../../tasks/installing-multicluster/).
* [Multi-cluster communication with StatefulSets](../../tasks/multicluster-using-statefulsets/).
* [Architecting for multi-cluster
Kubernetes](/2020/02/17/architecting-for-multicluster-kubernetes/), a blog
post explaining some of the design rationale behind Linkerd's multi-cluster
implementation.
* [Multi-cluster Kubernetes with service
mirroring](/2020/02/25/multicluster-kubernetes-with-service-mirroring/), a
deep dive of some of the architectural decisions behind Linkerd's
multi-cluster implementation.

View File

@ -0,0 +1,131 @@
+++
title = "TCP Proxying and Protocol Detection"
description = "Linkerd is capable of proxying all TCP traffic, including TLS'd connections, WebSockets, and HTTP tunneling."
weight = 2
aliases = [
"/2.11/supported-protocols/"
]
+++
Linkerd is capable of proxying all TCP traffic, including TLS connections,
WebSockets, and HTTP tunneling.
In most cases, Linkerd can do this without configuration. To accomplish this,
Linkerd performs *protocol detection* to determine whether traffic is HTTP or
HTTP/2 (including gRPC). If Linkerd detects that a connection is HTTP or
HTTP/2, Linkerd automatically provides HTTP-level metrics and routing.
If Linkerd *cannot* determine that a connection is using HTTP or HTTP/2,
Linkerd will proxy the connection as a plain TCP connection, applying
[mTLS](../automatic-mtls/) and providing byte-level metrics as usual.
(Note that HTTPS calls to or from meshed pods are treated as TCP, not as HTTP.
Because the client initiates the TLS connection, Linkerd is not be able to
decrypt the connection to observe the HTTP transactions.)
## Configuring protocol detection
{{< note >}}
If you are experiencing 10-second delays when establishing connections, you are
likely running into a protocol detection timeout. This section will help you
understand how to fix this.
{{< /note >}}
In some cases, Linkerd's protocol detection will time out because it doesn't
see any bytes from the client. This situation is commonly encountered when
using "server-speaks-first" protocols where the server sends data before the
client does, such as SMTP, or protocols that proactively establish connections
without sending data, such as Memcache. In this case, the connection will
proceed as a TCP connection after a 10-second protocol detection delay.
To avoid this delay, you will need to provide some configuration for Linkerd.
There are two basic mechanisms for configuring protocol detection: _opaque
ports_ and _skip ports_. Marking a port as _opaque_ instructs Linkerd to skip
protocol detection and immediately proxy the connection as a TCP stream;
marking a port as a _skip port_ bypasses the proxy entirely. Opaque ports are
generally preferred (as Linkerd can provide mTLS, TCP-level metrics, etc), but
can only be used for services inside the cluster.
By default, Linkerd automatically marks the ports for some server-speaks-first
protocol as opaque. Services that speak those protocols over the default ports
to destinations inside the cluster do not need further configuration.
Linkerd's default list of opaque ports in the 2.11 release is 25 (SMTP), 587
(SMTP), 3306 (MySQL), 4444 (Galera), 5432 (Postgres), 6379 (Redis), 9300
(ElasticSearch), and 11211 (Memcache). Note that this may change in future
releases.
The following table contains common protocols that may require configuration.
| Protocol | Default port(s) | Notes |
|-----------------|-----------------|-------|
| SMTP | 25, 587 | |
| MySQL | 3306 | |
| MySQL with Galera | 3306, 4444, 4567, 4568 | Ports 4567 and 4568 are not in Linkerd's default set of opaque ports |
| PostgreSQL | 5432 | |
| Redis | 6379 | |
| ElasticSearch | 9300 | |
| Memcache | 11211 | |
If you are using one of those protocols, follow this decision tree to determine
which configuration you need to apply.
* Is the protocol wrapped in TLS?
* Yes: no configuration required.
* No: is the destination on the cluster?
* Yes: is the port in Linkerd's default list of opaque ports?
* Yes: no configuration required.
* No: mark port(s) as opaque.
* No: mark port(s) as skip.
## Marking a port as opaque
You can use the `config.linkerd.io/opaque-ports` annotation to mark a port as
opaque. This instructions Linkerd to skip protocol detection for that port.
This annotation can be set on a workload, service, or namespace. Setting it on
a workload tells meshed clients of that workload to skip protocol detection for
connections established to the workload, and tells Linkerd to skip protocol
detection when reverse-proxying incoming connections. Setting it on a service
tells meshed clients to skip protocol detection when proxying connections to
the service. Set it on a namespace applies this behavior to all services and
workloads in that namespace.
{{< note >}}
Since this annotation informs the behavior of meshed _clients_, it can be
applied to unmeshed services as well as meshed services.
{{< /note >}}
Setting the opaque-ports annotation can be done by using the `--opaque-ports`
flag when running `linkerd inject`. For example, for a MySQL database running
on the cluster using a non-standard port 4406, you can use the commands:
```bash
linkerd inject mysql-deployment.yml --opaque-ports=4406 \
| kubectl apply -f -
linkerd inject mysql-service.yml --opaque-ports=4406 \
| kubectl apply -f -
```
{{< note >}}
Multiple ports can be provided as a comma-delimited string. The values you
provide will replace, not augment, the default list of opaque ports.
{{< /note >}}
## Marking a port as skip
Sometimes it is necessary to bypass the proxy altogether. For example, when
connecting to a server-speaks-first destination that is outside of the cluster,
there is no Service resource on which to set the
`config.linkerd.io/opaque-ports` annotation.
In this case, you can use the `--skip-outbound-ports` flag when running
`linkerd inject` to configure resources to bypass the proxy entirely when
sending to those ports. (Similarly, the `--skip-inbound-ports` flag will
configure the resource to bypass the proxy for incoming connections to those
ports.)
Skipping the proxy can be useful for these situations, as well as for
diagnosing issues, but otherwise should rarely be necessary.
As with opaque ports, multiple skipports can be provided as a comma-delimited
string.

View File

@ -0,0 +1,64 @@
+++
title = "Automatic Proxy Injection"
description = "Linkerd will automatically inject the data plane proxy into your pods based annotations."
aliases = [
"../proxy-injection/"
]
+++
Linkerd automatically adds the data plane proxy to pods when the
`linkerd.io/inject: enabled` annotation is present on a namespace or any
workloads, such as deployments or pods. This is known as "proxy injection".
See [Adding Your Service](../../tasks/adding-your-service/) for a walkthrough of
how to use this feature in practice.
{{< note >}}
Proxy injection is also where proxy *configuration* happens. While it's rarely
necessary, you can configure proxy settings by setting additional Kubernetes
annotations at the resource level prior to injection. See the [full list of
proxy configuration options](../../reference/proxy-configuration/).
{{< /note >}}
## Details
Proxy injection is implemented as a [Kubernetes admission
webhook](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#admission-webhooks).
This means that the proxies are added to pods within the Kubernetes cluster
itself, regardless of whether the pods are created by `kubectl`, a CI/CD
system, or any other system.
For each pod, two containers are injected:
1. `linkerd-init`, a Kubernetes [Init
Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)
that configures `iptables` to automatically forward all incoming and
outgoing TCP traffic through the proxy. (Note that this container is not
present if the [Linkerd CNI Plugin](../cni/) has been enabled.)
1. `linkerd-proxy`, the Linkerd data plane proxy itself.
Note that simply adding the annotation to a resource with pre-existing pods
will not automatically inject those pods. You will need to update the pods
(e.g. with `kubectl rollout restart` etc.) for them to be injected. This is
because Kubernetes does not call the webhook until it needs to update the
underlying resources.
## Overriding injection
Automatic injection can be disabled for a pod or deployment for which it would
otherwise be enabled, by adding the `linkerd.io/inject: disabled` annotation.
## Manual injection
The [`linkerd inject`](../../reference/cli/inject/) CLI command is a text
transform that, by default, simply adds the inject annotation to a given
Kubernetes manifest.
Alternatively, this command can also perform the full injection purely on the
client side with the `--manual` flag. This was the default behavior prior to
Linkerd 2.4; however, having injection to the cluster side makes it easier to
ensure that the data plane is always present and configured correctly,
regardless of how pods are deployed.
See the [`linkerd inject` reference](../../reference/cli/inject/) for more
information.

View File

@ -0,0 +1,77 @@
+++
title = "Retries and Timeouts"
description = "Linkerd can perform service-specific retries and timeouts."
weight = 3
+++
Automatic retries are one the most powerful and useful mechanisms a service mesh
has for gracefully handling partial or transient application failures. If
implemented incorrectly retries can amplify small errors into system wide
outages. For that reason, we made sure they were implemented in a way that would
increase the reliability of the system while limiting the risk.
Timeouts work hand in hand with retries. Once requests are retried a certain
number of times, it becomes important to limit the total amount of time a client
waits before giving up entirely. Imagine a number of retries forcing a client
to wait for 10 seconds.
A [service profile](../service-profiles/) may define certain routes as
retryable or specify timeouts for routes. This will cause the Linkerd proxy to
perform the appropriate retries or timeouts when calling that service. Retries
and timeouts are always performed on the *outbound* (client) side.
{{< note >}}
If working with headless services, service profiles cannot be retrieved. Linkerd
reads service discovery information based off the target IP address, and if that
happens to be a pod IP address then it cannot tell which service the pod belongs
to.
{{< /note >}}
These can be setup by following the guides:
- [Configuring Retries](../../tasks/configuring-retries/)
- [Configuring Timeouts](../../tasks/configuring-timeouts/)
## How Retries Can Go Wrong
Traditionally, when performing retries, you must specify a maximum number of
retry attempts before giving up. Unfortunately, there are two major problems
with configuring retries this way.
### Choosing a maximum number of retry attempts is a guessing game
You need to pick a number thats high enough to make a difference; allowing
more than one retry attempt is usually prudent and, if your service is less
reliable, youll probably want to allow several retry attempts. On the other
hand, allowing too many retry attempts can generate a lot of extra requests and
extra load on the system. Performing a lot of retries can also seriously
increase the latency of requests that need to be retried. In practice, you
usually pick a maximum retry attempts number out of a hat (3?) and then tweak
it through trial and error until the system behaves roughly how you want it to.
### Systems configured this way are vulnerable to retry storms
A [retry storm](https://twitter.github.io/finagle/guide/Glossary.html)
begins when one service starts (for any reason) to experience a larger than
normal failure rate. This causes its clients to retry those failed requests.
The extra load from the retries causes the service to slow down further and
fail more requests, triggering more retries. If each client is configured to
retry up to 3 times, this can quadruple the number of requests being sent! To
make matters even worse, if any of the clients clients are configured with
retries, the number of retries compounds multiplicatively and can turn a small
number of errors into a self-inflicted denial of service attack.
## Retry Budgets to the Rescue
To avoid the problems of retry storms and arbitrary numbers of retry attempts,
retries are configured using retry budgets. Rather than specifying a fixed
maximum number of retry attempts per request, Linkerd keeps track of the ratio
between regular requests and retries and keeps this number below a configurable
limit. For example, you may specify that you want retries to add at most 20%
more requests. Linkerd will then retry as much as it can while maintaining that
ratio.
Configuring retries is always a trade-off between improving success rate and
not adding too much extra load to the system. Retry budgets make that trade-off
explicit by letting you specify exactly how much extra load your system is
willing to accept from retries.

View File

@ -0,0 +1,105 @@
+++
title = "Authorization Policy"
description = "Linkerd can restrict which types of traffic are allowed to ."
+++
Linkerd's server authorization policy allows you to control which types of
traffic are allowed to meshed pods. For example, you can restrict communication
to a particular service to only come from certain other services; or you can
enforce that mTLS must be used on a certain port; and so on.
## Adding traffic policy on your services
{{< note >}}
Linkerd can only enforce policy on meshed pods, i.e. pods where the Linkerd
proxy has been injected. If policy is a strict requirement, you should pair the
usage of these features with [HA mode](../ha/), which enforces that the proxy
*must* be present when pods start up.
{{< /note >}}
By default Linkerd allows all traffic to transit the mesh, and uses a variety
of mechanisms, including [retries](../retries-and-timeouts/) and [load
balancing](../load-balancing/), to ensure that requests are delivered
successfully.
Sometimes, however, we want to restrict which types of traffic are allowed.
Linkerd's policy features allow you to *deny* traffic under certain conditions.
It is configured with two basic mechanisms:
1. A set of basic _default policies_, which can be set at the cluster,
namespace, workload, and pod level through Kubernetes annotations.
2. `Server` and `ServerAuthorization` CRDs that specify fine-grained policy
for specific ports.
These mechanisms work in conjunction. For example, a default cluster-wide
policy of `deny` would prohibit any traffic to any meshed pod; traffic must
then be explicitly allowed through the use of `Server` and
`ServerAuthorization` CRDs.
### Policy annotations
The `config.linkerd.io/default-inbound-policy` annotation can be set at a
namespace, workload, and pod level, and will determine the default traffic
policy at that point in the hierarchy. Valid default policies include:
- `all-unauthenticated`: inbound proxies allow all connections
- `all-authenticated`: inbound proxies allow only mTLS connections from other
meshed pods.
- `cluster-unauthenticated`: inbound proxies allow all connections from client
IPs in the cluster's `clusterNetworks` (must be configured at install-time).
- `cluster-authenticated`: inbound proxies allow only mTLS connections from other
meshed pods from IPs in the cluster's `clusterNetworks`.
- `deny` inbound proxies deny all connections that are not explicitly
authorized.
See the [Policy reference](../../reference/authorization-policy/) for more default
policies.
Every cluster has a default policy (by default, `all-unauthenticated`), set at
install / upgrade time. Annotations that are present at the workload or
namespace level *at pod creation time* can override that value to determine the
default policy for that pod. Note that the default policy is fixed at proxy
initialization time, and thus, after a pod is created, changing the annotation
will not change the default policy for that pod.
### Policy CRDs
The `Server` and `ServerAuthorization` CRDs further configure Linkerd's policy
beyond the default policies. In contrast to annotations, these CRDs can be
changed dynamically and policy behavior will be updated on the fly.
A `Server` selects a port and a set of pods that is subject to policy. This set
of pods can correspond to a single workload, or to multiple workloads (e.g.
port 4191 for every pod in a namespace). Once created, a `Server` resource
denies all traffic to that port, and traffic to that port can only be enabled
by creating `ServerAuthorization` resources.
A `ServerAuthorization` defines a set of allowed traffic to a `Server`. A
`ServerAuthorization` can allow traffic based on any number of things,
including IP address; use of mTLS; specific mTLS identities (including
wildcards, to allow for namespace selection); specific Service Accounts; and
more.
See the [Policy reference](../../reference/authorization-policy/) for more on
the `Server` and `ServerAuthorization` resources.
{{< note >}}
Currently, `Servers` can only reference ports that are defined as container
ports in the pod's manifest.
{{< /note >}}
### Policy rejections
Any traffic that is known to be HTTP (including HTTP/2 and gRPC) that is denied
by policy will result in the proxy returning an HTTP 403. Any non-HTTP traffic
will be denied at the TCP level, i.e. by refusing the connection.
Note that dynamically changing the policy may result in abrupt termination of
existing TCP connections.
### Examples
See
[emojivoto-policy.yml](https://github.com/linkerd/website/blob/main/run.linkerd.io/public/emojivoto-policy.yml)
for an example set of policy definitions for the [Emojivoto sample
application](/getting-started/).

View File

@ -0,0 +1,33 @@
+++
title = "Service Profiles"
description = "Linkerd's service profiles enable per-route metrics as well as retries and timeouts."
aliases = [
"../service-profiles/"
]
+++
A service profile is a custom Kubernetes resource ([CRD][crd]) that can provide
Linkerd additional information about a service. In particular, it allows you to
define a list of routes for the service. Each route uses a regular expression
to define which paths should match that route. Defining a service profile
enables Linkerd to report per-route metrics and also allows you to enable
per-route features such as retries and timeouts.
{{< note >}}
If working with headless services, service profiles cannot be retrieved. Linkerd
reads service discovery information based off the target IP address, and if that
happens to be a pod IP address then it cannot tell which service the pod belongs
to.
{{< /note >}}
To get started with service profiles you can:
- Look into [setting up service profiles](../../tasks/setting-up-service-profiles/)
for your own services.
- Understand what is required to see
[per-route metrics](../../tasks/getting-per-route-metrics/).
- [Configure retries](../../tasks/configuring-retries/) on your own services.
- [Configure timeouts](../../tasks/configuring-timeouts/) on your own services.
- Glance at the [reference](../../reference/service-profiles/) documentation.
[crd]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/

View File

@ -0,0 +1,79 @@
+++
title = "Telemetry and Monitoring"
description = "Linkerd automatically collects metrics from all services that send traffic through it."
weight = 8
aliases = [
"../observability/"
]
+++
One of Linkerd's most powerful features is its extensive set of tooling around
*observability*&mdash;the measuring and reporting of observed behavior in
meshed applications. While Linkerd doesn't have insight directly into the
*internals* of service code, it has tremendous insight into the external
behavior of service code.
To gain access to Linkerd's observability features you only need to install the
Viz extension:
```bash
linkerd viz install | kubectl apply -f -
```
Linkerd's telemetry and monitoring features function automatically, without
requiring any work on the part of the developer. These features include:
* Recording of top-line ("golden") metrics (request volume, success rate, and
latency distributions) for HTTP, HTTP/2, and gRPC traffic.
* Recording of TCP-level metrics (bytes in/out, etc) for other TCP traffic.
* Reporting metrics per service, per caller/callee pair, or per route/path
(with [Service Profiles](../service-profiles/)).
* Generating topology graphs that display the runtime relationship between
services.
* Live, on-demand request sampling.
This data can be consumed in several ways:
* Through the [Linkerd CLI](../../reference/cli/), e.g. with `linkerd viz stat` and
`linkerd viz routes`.
* Through the [Linkerd dashboard](../dashboard/), and
[pre-built Grafana dashboards](../dashboard/#grafana).
* Directly from Linkerd's built-in Prometheus instance
## Golden metrics
### Success Rate
This is the percentage of successful requests during a time window (1 minute by
default).
In the output of the command `linkerd viz routes -o wide`, this metric is split
into EFFECTIVE_SUCCESS and ACTUAL_SUCCESS. For routes configured with retries,
the former calculates the percentage of success after retries (as perceived by
the client-side), and the latter before retries (which can expose potential
problems with the service).
### Traffic (Requests Per Second)
This gives an overview of how much demand is placed on the service/route. As
with success rates, `linkerd viz routes --o wide` splits this metric into
EFFECTIVE_RPS and ACTUAL_RPS, corresponding to rates after and before retries
respectively.
### Latencies
Times taken to service requests per service/route are split into 50th, 95th and
99th percentiles. Lower percentiles give you an overview of the average
performance of the system, while tail percentiles help catch outlier behavior.
## Lifespan of Linkerd metrics
Linkerd is not designed as a long-term historical metrics store. While
Linkerd's Viz extension does include a Prometheus instance, this instance
expires metrics at a short, fixed interval (currently 6 hours).
Rather, Linkerd is designed to *supplement* your existing metrics store. If
Linkerd's metrics are valuable, you should export them into your existing
historical metrics store.
See [Exporting Metrics](../../tasks/exporting-metrics/) for more.

View File

@ -0,0 +1,36 @@
+++
title = "Traffic Split (canaries, blue/green deploys)"
description = "Linkerd can dynamically send a portion of traffic to different services."
+++
Linkerd's traffic split functionality allows you to dynamically shift arbitrary
portions of traffic destined for a Kubernetes service to a different destination
service. This feature can be used to implement sophisticated rollout strategies
such as [canary deployments](https://martinfowler.com/bliki/CanaryRelease.html)
and
[blue/green deployments](https://martinfowler.com/bliki/BlueGreenDeployment.html),
for example, by slowly easing traffic off of an older version of a service and
onto a newer version.
{{< note >}}
If working with headless services, traffic splits cannot be retrieved. Linkerd
reads service discovery information based off the target IP address, and if that
happens to be a pod IP address then it cannot tell which service the pod belongs
to.
{{< /note >}}
Linkerd exposes this functionality via the
[Service Mesh Interface](https://smi-spec.io/) (SMI)
[TrafficSplit API](https://github.com/servicemeshinterface/smi-spec/tree/master/apis/traffic-split).
To use this feature, you create a Kubernetes resource as described in the
TrafficSplit spec, and Linkerd takes care of the rest.
By combining traffic splitting with Linkerd's metrics, it is possible to
accomplish even more powerful deployment techniques that automatically take into
account the success rate and latency of old and new versions. See the
[Flagger](https://flagger.app/) project for one example of this.
Check out some examples of what you can do with traffic splitting:
- [Canary Releases](../../tasks/canary-release/)
- [Fault Injection](../../tasks/fault-injection/)

View File

@ -0,0 +1,258 @@
+++
title = "Getting Started"
aliases = [
"/getting-started/istio/",
"/choose-your-platform/",
"/../katacoda/",
"/doc/getting-started",
"/getting-started"
]
weight = 2
[sitemap]
priority = 1.0
+++
Welcome to Linkerd! 🎈
In this guide, we'll walk you through how to install Linkerd into your
Kubernetes cluster. Then we'll deploy a sample application to show off what
Linkerd can do.
Installing Linkerd is easy. First, you will install the CLI (command-line
interface) onto your local machine. Using this CLI, you'll then install the
*control plane* onto your Kubernetes cluster. Finally, you'll "mesh" one or
more of your own services by adding Linkerd's *data plane* to them.
## Step 0: Setup
Before we can do anything, we need to ensure you have access to modern
Kubernetes cluster and a functioning `kubectl` command on your local machine.
(If you don't already have a Kubernetes cluster, one easy option is to run one
on your local machine. There are many ways to do this, including
[kind](https://kind.sigs.k8s.io/), [k3d](https://k3d.io/), [Docker for
Desktop](https://www.docker.com/products/docker-desktop), [and
more](https://kubernetes.io/docs/setup/).)
You can validate your setup by running:
```bash
kubectl version --short
```
You should see output with both a `Client Version` and `Server Version`
component.
Now that we have our cluster, we'll install the Linkerd CLI and use it validate
that your cluster is capable of hosting the Linkerd control plane.
(Note: if you're using a GKE "private cluster", there are some [extra steps
required](../reference/cluster-configuration/#private-clusters) before you can
proceed to the next step.)
## Step 1: Install the CLI
If this is your first time running Linkerd, you will need to download the
`linkerd` command-line interface (CLI) onto your local machine. The CLI will
allow you to interact with your Linkerd deployment.
To install the CLI manually, run:
```bash
curl -sL run.linkerd.io/install | sh
```
Be sure to follow the instructions to add it to your path.
Alternatively, if you use [Homebrew](https://brew.sh), you can install the CLI
with `brew install linkerd`. You can also download the CLI directly via the
[Linkerd releases page](https://github.com/linkerd/linkerd2/releases/).
Once installed, verify the CLI is running correctly with:
```bash
linkerd version
```
You should see the CLI version, and also `Server version: unavailable`. This is
because you haven't installed the control plane on your cluster. Don't
worry&mdash;we'll fix that soon enough.
## Step 2: Validate your Kubernetes cluster
Kubernetes clusters can be configured in many different ways. Before we can
install the Linkerd control plane, we need to check and validate that
everything is configured correctly. To check that your cluster is ready to
install Linkerd, run:
```bash
linkerd check --pre
```
If there are any checks that do not pass, make sure to follow the provided links
and fix those issues before proceeding.
## Step 3: Install the control plane onto your cluster
Now that you have the CLI running locally and a cluster that is ready to go,
it's time to install the control plane.
The first step is to install the control plane core. To do this, run:
```bash
linkerd install | kubectl apply -f -
```
The `linkerd install` command generates a Kubernetes manifest with all the core
control plane resources. (Feel free to inspect the output.) Piping this
manifest into `kubectl apply` then instructs Kubernetes to add those resources
to your cluster.
{{< note >}}
Some control plane resources require cluster-wide permissions. If you are
installing on a cluster where these permissions are restricted, you may prefer
the alternative [multi-stage install](../tasks/install/#multi-stage-install)
process, which will split these "sensitive" components into a separate,
self-contained step which can be handed off to another party.
{{< /note >}}
Now let's wait for the control plane to finish installing. Depending on the
speed of your cluster's Internet connection, this may take a minute or two.
Wait for the control plane to be ready (and verify your installation) by
running:
```bash
linkerd check
```
Next, we'll install some *extensions*. Extensions add non-critical but often
useful functionality to Linkerd. For this guide, we will need:
1. The **viz** extension, which will install an on-cluster metric stack; or
2. The **buoyant-cloud** extension, which will connect to a hosted metrics stack.
For this guide, you can install either or both. To install the viz extension,
run:
```bash
linkerd viz install | kubectl apply -f - # install the on-cluster metrics stack
```
To install the buoyant-cloud extension, run:
```bash
curl -sL buoyant.cloud/install | sh # get the installer
linkerd buoyant install | kubectl apply -f - # connect to the hosted metrics stack
```
Once you've installed your extensions, let's validate everything one last time:
```bash
linkerd check
```
Assuming everything is green, we're ready for the next step!
## Step 4: Explore Linkerd!
With the control plane and extensions installed and running, we're now ready
to explore Linkerd! If you installed the viz extension, run:
```bash
linkerd viz dashboard &
```
You should see a screen like this:
{{< fig src="/images/getting-started/viz-empty-dashboard.png"
title="The Linkerd dashboard in action" >}}
If you installed the buoyant-cloud extension, run:
```bash
linkerd buoyant dashboard &
```
You should see a screen lke this:
{{< fig src="/images/getting-started/bcloud-empty-dashboard.png"
title="The Linkerd dashboard in action" >}}
Click around, explore, and have fun! One thing you'll see is that, even if you
don't have any applications running on this cluster, you still have traffic!
This is because Linkerd's control plane components all have the proxy injected
(i.e. the control plane runs on the data plane), so traffic between control
plane compnments is also part of the mesh.
## Step 5: Install the demo app
To get a feel for how Linkerd would work for one of your services, you can
install a demo application. The *emojivoto* application is a standalone
Kubernetes application that uses a mix of gRPC and HTTP calls to allow the
users to vote on their favorite emojis.
Install *emojivoto* into the `emojivoto` namespace by running:
```bash
curl -sL run.linkerd.io/emojivoto.yml | kubectl apply -f -
```
Before we mesh it, let's take a look at the app. If you're using [Docker
Desktop](https://www.docker.com/products/docker-desktop) at this point you can
visit [http://localhost](http://localhost) directly. If you're not using
Docker Desktop, we'll need to forward the `web-svc` service. To forward
`web-svc` locally to port 8080, you can run:
```bash
kubectl -n emojivoto port-forward svc/web-svc 8080:80
```
Now visit [http://localhost:8080](http://localhost:8080). Voila! The emojivoto
app in all its glory.
Clicking around, you might notice that some parts of *emojivoto* are broken!
For example, if you click on a doughnut emoji, you'll get a 404 page. Don't
worry, these errors are intentional. (And we can use Linkerd to identify the
problem. Check out the [debugging guide](../debugging-an-app/) if you're
interested in how to figure out exactly what is wrong.)
Next, let's add Linkerd to *emojivoto* by running:
```bash
kubectl get -n emojivoto deploy -o yaml \
| linkerd inject - \
| kubectl apply -f -
```
This command retrieves all of the deployments running in the `emojivoto`
namespace, runs the manifest through `linkerd inject`, and then reapplies it to
the cluster. The `linkerd inject` command adds annotations to the pod spec
instructing Linkerd to "inject" the proxy as a container to the pod spec.
As with `install`, `inject` is a pure text operation, meaning that you can
inspect the input and output before you use it. Once piped into `kubectl
apply`, Kubernetes will execute a rolling deploy and update each pod with the
data plane's proxies, all without any downtime.
Congratulations! You've now added Linkerd to existing services! Just as with
the control plane, it is possible to verify that everything worked the way it
should with the data plane. To do this check, run:
```bash
linkerd -n emojivoto check --proxy
```
## That's it! 👏
Congratulations, you're now a Linkerd user! Here are some suggested next steps:
* Use Linkerd to [debug the errors in *emojivoto*](../debugging-an-app/)
* [Add your own service](../adding-your-service/) to Linkerd without downtime
* Set up [automatic control plane mTLS credential
rotation](../tasks/automatically-rotating-control-plane-tls-credentials/) or
set a reminder to [do it
manually](../tasks/manually-rotating-control-plane-tls-credentials/) before
they expire
* Learn more about [Linkerd's architecture](../reference/architecture/)
* Hop into the #linkerd2 channel on [the Linkerd
Slack](https://slack.linkerd.io)
Welcome to the Linkerd community!

View File

@ -0,0 +1,69 @@
+++
title = "Overview"
aliases = [
"/docs",
"/documentation",
"/2.11/",
"../docs/",
"/doc/network-performance/",
"/in-depth/network-performance/",
"/in-depth/debugging-guide/",
"/in-depth/concepts/"
]
weight = 1
+++
Linkerd is a _service mesh_ for Kubernetes. It makes running services easier
and safer by giving you runtime debugging, observability, reliability, and
security&mdash;all without requiring any changes to your code.
For a brief introduction to the service mesh model, we recommend reading [The
Service Mesh: What Every Software Engineer Needs to Know about the World's Most
Over-Hyped Technology](https://servicemesh.io/).
Linkerd is fully open source, licensed under [Apache
v2](https://github.com/linkerd/linkerd2/blob/main/LICENSE), and is a [Cloud
Native Computing Foundation](https://cncf.io) graduated project. Linkerd is
developed in the open in the [Linkerd GitHub organization](https://github.com/linkerd).
Linkerd has three basic components: a UI, a *data plane*, and a *control
plane*. You run Linkerd by:
1. [Installing the CLI on your local system](../getting-started/#step-1-install-the-cli);
1. [Installing the control plane into your cluster](../getting-started/#step-3-install-linkerd-onto-the-cluster);
1. [Adding your services to Linkerd's data plane](../tasks/adding-your-service/).
Once a service is running with Linkerd, you can use [Linkerd's
UI](../getting-started/#step-4-explore-linkerd) to inspect and
manipulate it.
You can [get started](../getting-started/) in minutes!
## How it works
Linkerd works by installing a set of ultralight, transparent proxies next to
each service instance. These proxies automatically handle all traffic to and
from the service. Because they're transparent, these proxies act as highly
instrumented out-of-process network stacks, sending telemetry to, and receiving
control signals from, the control plane. This design allows Linkerd to measure
and manipulate traffic to and from your service without introducing excessive
latency.
In order to be as small, lightweight, and safe as possible, Linkerd's proxies
are written in [Rust](https://www.rust-lang.org/) and specialized for Linkerd.
You can learn more about the proxies in the [Linkerd proxy
repo](https://github.com/linkerd/linkerd2-proxy).
## Versions and channels
Linkerd is currently published in several tracks:
* [Linkerd 2.x stable releases](/edge/)
* [Linkerd 2.x edge releases.](/edge/)
* [Linkerd 1.x.](/1/overview/)
## Next steps
[Get started with Linkerd](../getting-started/) in minutes, or check out the
[architecture](../reference/architecture/) for more details on Linkerd's
components and how they all fit together.

View File

@ -0,0 +1,6 @@
+++
title = "Reference"
weight = 5
+++
{{% sectiontoc "reference" %}}

View File

@ -0,0 +1,155 @@
+++
title = "Architecture"
description = "Deep dive into the architecture of Linkerd."
aliases = [
"../architecture/"
]
+++
At a high level, Linkerd consists of a *control plane* and a *data plane*.
The *control plane* is a set of services that run in a dedicated
namespace. These services accomplish various things---aggregating telemetry
data, providing a user-facing API, providing control data to the data plane
proxies, etc. Together, they drive the behavior of the data plane.
The *data plane* consists of transparent proxies that are run next
to each service instance. These proxies automatically handle all traffic to and
from the service. Because they're transparent, these proxies act as highly
instrumented out-of-process network stacks, sending telemetry to, and receiving
control signals from, the control plane.
{{< fig src="/images/architecture/control-plane.png"
title="Linkerd's architecture" >}}
## CLI
The Linkerd CLI is typically run outside of the cluster (e.g. on your local
machine) and is used to interact with the Linkerd control planes.
## Control Plane
The Linkerd control plane is a set of services that run in a dedicated
Kubernetes namespace (`linkerd` by default). The control plane has several
components, enumerated below.
### Controller
The controller component provides an API for the CLI to interface with.
### Destination
The destination component is used by data plane proxies to look up where to
send requests. The destination deployment is also used to fetch service profile
information used for per-route metrics, retries and timeouts.
### Identity
The identity component acts as a [TLS Certificate
Authority](https://en.wikipedia.org/wiki/Certificate_authority) that accepts
[CSRs](https://en.wikipedia.org/wiki/Certificate_signing_request) from proxies
and returns signed certificates. These certificates are issued at proxy
initialization time and are used for proxy-to-proxy connections to implement
[mTLS](../../features/automatic-mtls/).
### Proxy Injector
The proxy injector is a Kubernetes [admission
controller][admission-controller] which receives a webhook request every time a
pod is created. This injector inspects resources for a Linkerd-specific
annotation (`linkerd.io/inject: enabled`). When that annotation exists, the
injector mutates the pod's specification and adds the `proxy-init` and
`linkerd-proxy` containers to the pod.
### Service Profile Validator (sp-validator)
The validator is a Kubernetes [admission controller][admission-controller],
which validates new [service profiles](../service-profiles/) before they are
saved.
[admission-controller]: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/
## Data Plane
The Linkerd data plane comprises ultralight _micro-proxies_, written in Rust,
which are deployed as sidecar containers alongside each instance of your
service code.
These proxies transparently intercept communication to and from each pod by
utilizing iptables rules that are automatically configured by
[linkerd-init](#linkerd-init-container). These proxies are not designed to be
configured by hand. Rather, their behavior is driven by the control plane.
You can read more about these micro-proxies here:
* [Why Linkerd doesn't use Envoy](/2020/12/03/why-linkerd-doesnt-use-envoy/)
* [Under the hood of Linkerd's state-of-the-art Rust proxy,
Linkerd2-proxy](/2020/07/23/under-the-hood-of-linkerds-state-of-the-art-rust-proxy-linkerd2-proxy/)
### Proxy
An ultralight transparent _micro-proxy_ written in
[Rust](https://www.rust-lang.org/), the proxy is installed into each pod of a
meshed workload, and handles all incoming and outgoing TCP traffic to/from that
pod. This model (called a "sidecar container" or "sidecar proxy")
allows it to add functionality without requiring code changes.
The proxy's features include:
* Transparent, zero-config proxying for HTTP, HTTP/2, and arbitrary TCP
protocols.
* Automatic Prometheus metrics export for HTTP and TCP traffic.
* Transparent, zero-config WebSocket proxying.
* Automatic, latency-aware, layer-7 load balancing.
* Automatic layer-4 load balancing for non-HTTP traffic.
* Automatic TLS.
* An on-demand diagnostic tap API.
* And lots more.
The proxy supports service discovery via DNS and the
[destination gRPC API](https://github.com/linkerd/linkerd2-proxy-api).
### Linkerd Init Container
The `linkerd-init` container is added to each meshed pod as a Kubernetes [init
container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)
that runs before any other containers are started. It [uses
iptables](https://github.com/linkerd/linkerd2-proxy-init) to route all TCP
traffic to and from the pod through the proxy.
There are two main rules that `iptables` uses:
* Any traffic being sent to the pod's external IP address (10.0.0.1 for
example) is forwarded to a specific port on the proxy (4143). By setting
`SO_ORIGINAL_DST` on the socket, the proxy is able to forward the traffic to
the original destination port that your application is listening on.
* Any traffic originating from within the pod and being sent to an external IP
address (not 127.0.0.1) is forwarded to a specific port on the proxy (4140).
Because `SO_ORIGINAL_DST` was set on the socket, the proxy is able to forward
the traffic to the original recipient (unless there is a reason to send it
elsewhere). This does not result in a traffic loop because the `iptables`
rules explicitly skip the proxy's UID.
Additionally, `iptables` has rules in place for special scenarios, such as when
traffic is sent over the loopback interface:
* When traffic is sent over the loopback interface by the application, it will
be sent directly to the process, instead of being forwarded to the proxy. This
allows an application to talk to itself, or to another container in the pod,
without being intercepted by the proxy, as long as the destination is a port
bound on localhost (such as 127.0.0.1:80, localhost:8080), or the pod's own
IP.
* When traffic is sent by the application to its own cluster IP, it will be
forwarded to the proxy. If the proxy chooses its own pod as an endpoint, then
traffic will be sent over the loopback interface directly to the application.
Consequently, traffic will not be opportunistically upgraded to mTLS or
HTTP/2.
A list of all `iptables` rules used by Linkerd can be found [here](../iptables/)
{{< note >}}
By default, most ports are forwarded through the proxy. This is not always
desirable and it is possible to have specific ports skip the proxy entirely for
both incoming and outgoing traffic. See the [protocol
detection](../../features/protocol-detection/) documentation.
{{< /note >}}

View File

@ -0,0 +1,256 @@
+++
title = "Authorization Policy"
description = "Details on the specification and what is possible with policy resources."
+++
[Server](#server) and [ServerAuthorization](#serverauthorization) are the two types
of policy resources in Linkerd, used to control inbound access to your meshed
applications.
During the linkerd install, the `policyController.defaultAllowPolicy` field is used
to specify the default policy when no [Server](#server) selects a pod.
This field can be one of the following:
- `all-unauthenticated`: allow all requests. This is the default.
- `all-authenticated`: allow requests from meshed clients in the same or from
a different cluster (with multi-cluster).
- `cluster-authenticated`: allow requests from meshed clients in the same cluster.
- `cluster-unauthenticated`: allow requests from both meshed and non-meshed clients
in the same cluster.
- `deny`: all requests are denied. (Policy resources should then be created to
allow specific communications between services).
This default can be overridden by setting the annotation `config.linkerd.io/default-
inbound-policy` on either a pod spec or its namespace.
Once a [Server](#server) is configured for a pod & port, its default behavior
is to _deny_ traffic and [ServerAuthorization](#serverauthorization) resources
must be created to allow traffic on a `Server`.
## Server
A `Server` selects a port on a set of pods in the same namespace as the server.
It typically selects a single port on a pod, though it may select multiple
ports when referring to the port by name (e.g. `admin-http`). While the
`Server` resource is similar to a Kubernetes `Service`, it has the added
restriction that multiple `Server` instances must not overlap: they must not
select the same pod/port pairs. Linkerd ships with an admission controller that
tries to prevent overlapping servers from being created.
When a Server selects a port, traffic is denied by default and [`ServerAuthorizations`](#serverauthorization)
must be used to authorize traffic on ports selected by the Server.
### Spec
A `Server` spec may contain the following top level fields:
{{< table >}}
| field| value |
|------|-------|
| `podSelector`| A [podSelector](#podselector) selects pods in the same namespace. |
| `port`| A port name or number. Only ports in a pod spec's `ports` are considered. |
| `proxyProtocol`| Configures protocol discovery for inbound connections. Supersedes the `config.linkerd.io/opaque-ports` annotation. Must be one of `unknown`,`HTTP/1`,`HTTP/2`,`gRPC`,`opaque`,`TLS`. Defaults to `unknown` if not set. |
{{< /table >}}
### podSelector
This is the [same labelSelector field in Kubernetes](https://kubernetes.io/docs/reference/kubernetes-api/common-definitions/label-selector/#LabelSelector).
All the pods that are part of this selector will be part of the `Server` group.
A podSelector object must contain _exactly one_ of the following fields:
{{< table >}}
| field | value |
|-------|-------|
| `matchExpressions` | matchExpressions is a list of label selector requirements. The requirements are ANDed. |
| `matchLabels` | matchLabels is a map of {key,value} pairs. |
{{< /table >}}
See [the Kubernetes LabelSelector reference](https://kubernetes.io/docs/reference/kubernetes-api/common-definitions/label-selector/#LabelSelector)
for more details.
### Server Examples
A [Server](#server) that selects over pods with a specific label, with `gRPC` as
the `proxyProtocol`.
```yaml
apiVersion: policy.linkerd.io/v1beta1
kind: Server
metadata:
namespace: emojivoto
name: emoji-grpc
spec:
podSelector:
matchLabels:
app: emoji-svc
port: grpc
proxyProtocol: gRPC
```
A [Server](#server) that selects over pods with `matchExpressions`, with `HTTP/2`
as the `proxyProtocol`, on port `8080`.
```yaml
apiVersion: policy.linkerd.io/v1beta1
kind: Server
metadata:
namespace: emojivoto
name: backend-services
spec:
podSelector:
matchExpressions:
- {key: app, operator: In, values: [voting-svc, emoji-svc]}
- {key: environment, operator: NotIn, values: [dev]}
port: 8080
proxyProtocol: "HTTP/2"
```
## ServerAuthorization
A [ServerAuthorization](#serverauthorization) provides a way to authorize
traffic to one or more [`Server`](#server)s.
### Spec
A ServerAuthorization spec must contain the following top level fields:
{{< table >}}
| field| value |
|------|-------|
| `client`| A [client](#client) describes clients authorized to access a server. |
| `server`| A [server](#server) identifies `Servers` in the same namespace for which this authorization applies. |
{{< /table >}}
### Server
A `Server` object must contain _exactly one_ of the following fields:
{{< table >}}
| field| value |
|------|-------|
| `name`| References a `Server` instance by name. |
| `selector`| A [selector](#selector) selects servers on which this authorization applies in the same namespace. |
{{< /table >}}
### selector
This is the [same labelSelector field in Kubernetes](https://kubernetes.io/docs/reference/kubernetes-api/common-definitions/label-selector/#LabelSelector).
All the servers that are part of this selector will have this authorization applied.
A selector object must contain _exactly one_ of the following fields:
{{< table >}}
| field | value |
|-------|-------|
| `matchExpressions` | matchExpressions is a list of label selector requirements. The requirements are ANDed. |
| `matchLabels` | matchLabels is a map of {key,value} pairs. |
{{< /table >}}
See [the Kubernetes LabelSelector reference](https://kubernetes.io/docs/reference/kubernetes-api/common-definitions/label-selector/#LabelSelector)
for more details.
### client
A `client` object must contain _exactly one_ of the following fields:
{{< table >}}
| field| value |
|------|-------|
| `meshTLS`| A [meshTLS](#meshtls) is used to authorize meshed clients to access a server. |
| `unauthenticated`| A boolean value that authorizes unauthenticated clients to access a server. |
{{< /table >}}
Optionally, it can also contain the `networks` field:
{{< table >}}
| field| value |
|------|-------|
| `networks`| Limits the client IP addresses to which this authorization applies. If unset, the server chooses a default (typically, all IPs or the cluster's pod network). |
{{< /table >}}
### meshTLS
A `meshTLS` object must contain _exactly one_ of the following fields:
{{< table >}}
| field| value |
|------|-------|
| `unauthenticatedTLS`| A boolean to indicate that no client identity is required for communication.This is mostly important for the identity controller, which must terminate TLS connections from clients that do not yet have a certificate. |
| `identities`| A list of proxy identity strings (as provided via MTLS) that are authorized. The `*` prefix can be used to match all identities in a domain. An identity string of `*` indicates that all authentication clients are authorized. |
| `serviceAccounts`| A list of authorized client [serviceAccount](#serviceAccount)s (as provided via MTLS). |
{{< /table >}}
### serviceAccount
A serviceAccount field contains the following top level fields:
{{< table >}}
| field| value |
|------|-------|
| `name`| The ServiceAccount's name. |
| `namespace`| The ServiceAccount's namespace. If unset, the authorization's namespace is used. |
{{< /table >}}
### ServerAuthorization Examples
A [ServerAuthorization](#serverauthorization) that allows meshed clients with
`*.emojivoto.serviceaccount.identity.linkerd.cluster.local` proxy identity i.e. all
service accounts in the `emojivoto` namespace.
```yaml
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
namespace: emojivoto
name: emoji-grpc
spec:
# Allow all authenticated clients to access the (read-only) emoji service.
server:
selector:
matchLabels:
app: emoji-svc
client:
meshTLS:
identities:
- "*.emojivoto.serviceaccount.identity.linkerd.cluster.local"
```
A [ServerAuthorization](#serverauthorization) that allows any unauthenticated
clients.
```yaml
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
namespace: emojivoto
name: web-public
spec:
server:
name: web-http
# Allow all clients to access the web HTTP port without regard for
# authentication. If unauthenticated connections are permitted, there is no
# need to describe authenticated clients.
client:
unauthenticated: true
networks:
- cidr: 0.0.0.0/0
- cidr: ::/0
```
A [ServerAuthorization](#serverauthorization) that allows meshed clients with a
specific service account.
```yaml
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
namespace: emojivoto
name: prom-prometheus
spec:
server:
name: prom
client:
meshTLS:
serviceAccounts:
- namespace: linkerd-viz
name: prometheus
```

View File

@ -0,0 +1,21 @@
+++
title = "CLI"
description = "Reference documentation for all the CLI commands."
aliases = [
"../cli/"
]
+++
The Linkerd CLI is the primary way to interact with Linkerd. It can install the
control plane to your cluster, add the proxy to your service and provide
detailed metrics for how your service is performing.
As reference, check out the commands below:
{{< cli-2-10 >}}
## Global flags
The following flags are available for *all* linkerd CLI commands:
{{< global-flags >}}

View File

@ -0,0 +1,72 @@
+++
title = "check"
aliases = [
"../check-reference/"
]
+++
{{< cli-2-10/description "check" >}}
Take a look at the [troubleshooting](../../../tasks/troubleshooting/) documentation
for a full list of all the possible checks, what they do and how to fix them.
{{< cli-2-10/examples "check" >}}
## Example output
```bash
$ linkerd check
kubernetes-api
--------------
√ can initialize the client
√ can query the Kubernetes API
kubernetes-version
------------------
√ is running the minimum Kubernetes API version
linkerd-existence
-----------------
√ control plane namespace exists
√ controller pod is running
√ can initialize the client
√ can query the control plane API
linkerd-api
-----------
√ control plane pods are ready
√ control plane self-check
√ [kubernetes] control plane can talk to Kubernetes
√ [prometheus] control plane can talk to Prometheus
linkerd-service-profile
-----------------------
√ no invalid service profiles
linkerd-version
---------------
√ can determine the latest version
√ cli is up-to-date
control-plane-version
---------------------
√ control plane is up-to-date
√ control plane and cli versions match
Status check results are √
```
{{< cli-2-10/flags "check" >}}
## Subcommands
Check supports subcommands as part of the
[Multi-stage install](../../../tasks/install/#multi-stage-install) feature.
### config
{{< cli-2-10/description "check config" >}}
{{< cli-2-10/examples "check config" >}}
{{< cli-2-10/flags "check config" >}}

View File

@ -0,0 +1,9 @@
+++
title = "completion"
+++
{{< cli-2-10/description "completion" >}}
{{< cli-2-10/examples "completion" >}}
{{< cli-2-10/flags "completion" >}}

View File

@ -0,0 +1,48 @@
+++
title = "diagnostics"
aliases = [
"endpoints",
"install-sp",
"metrics"
]
+++
{{< cli-2-10/description "diagnostics" >}}
{{< cli-2-10/examples "diagnostics" >}}
{{< cli-2-10/flags "diagnostics" >}}
## Subcommands
### controller-metrics
{{< cli-2-10/description "diagnostics controller-metrics" >}}
{{< cli-2-10/examples "diagnostics controller-metrics" >}}
{{< cli-2-10/flags "diagnostics controller-metrics" >}}
### endpoints
{{< cli-2-10/description "diagnostics endpoints" >}}
{{< cli-2-10/examples "diagnostics endpoints" >}}
{{< cli-2-10/flags "diagnostics endpoints" >}}
### install-sp
{{< cli-2-10/description "diagnostics install-sp" >}}
{{< cli-2-10/examples "diagnostics install-sp" >}}
{{< cli-2-10/flags "diagnostics install-sp" >}}
### proxy-metrics
{{< cli-2-10/description "diagnostics proxy-metrics" >}}
{{< cli-2-10/examples "diagnostics proxy-metrics" >}}
{{< cli-2-10/flags "diagnostics proxy-metrics" >}}

View File

@ -0,0 +1,9 @@
+++
title = "identity"
+++
{{< cli-2-10/description "identity" >}}
{{< cli-2-10/examples "identity" >}}
{{< cli-2-10/flags "identity" >}}

View File

@ -0,0 +1,27 @@
+++
title = "inject"
aliases = [
"../inject-reference/"
]
+++
The `inject` command is a text transform that modifies Kubernetes manifests
passed to it either as a file or as a stream (`-`) to adds a
`linkerd.io/inject: enabled` annotation to eligible resources in the manifest.
When the resulting annotated manifest is applied to the Kubernetes cluster,
Linkerd's [proxy autoinjector](../../../features/proxy-injection/) automatically
adds the Linkerd data plane proxies to the corresponding pods.
Note that there is no *a priori* reason to use this command. In production,
these annotations may be instead set by a CI/CD system, or any other
deploy-time mechanism.
## Manual injection
Alternatively, this command can also perform the full injection purely on the
client side, by enabling with the `--manual` flag. (Prior to Linkerd 2.4, this
was the default behavior.)
{{< cli-2-10/examples "inject" >}}
{{< cli-2-10/flags "inject" >}}

View File

@ -0,0 +1,9 @@
+++
title = "install-cni"
+++
{{< cli-2-10/description "install-cni" >}}
{{< cli-2-10/examples "install-cni" >}}
{{< cli-2-10/flags "install-cni" >}}

View File

@ -0,0 +1,33 @@
+++
title = "install"
+++
{{< cli-2-10/description "install" >}}
For further details on how to install Linkerd onto your own cluster, check out
the [install documentation](../../../tasks/install/).
{{< cli-2-10/examples "install" >}}
{{< cli-2-10/flags "install" >}}
## Subcommands
Install supports subcommands as part of the
[Multi-stage install](../../../tasks/install/#multi-stage-install) feature.
### config
{{< cli-2-10/description "install config" >}}
{{< cli-2-10/examples "install config" >}}
{{< cli-2-10/flags "install config" >}}
### control-plane
{{< cli-2-10/description "install control-plane" >}}
{{< cli-2-10/examples "install control-plane" >}}
{{< cli-2-10/flags "install control-plane" >}}

View File

@ -0,0 +1,51 @@
+++
title = "jaeger"
+++
{{< cli-2-10/description "jaeger" >}}
{{< cli-2-10/examples "jaeger" >}}
{{< cli-2-10/flags "jaeger" >}}
## Subcommands
### check
{{< cli-2-10/description "jaeger check" >}}
{{< cli-2-10/examples "jaeger check" >}}
{{< cli-2-10/flags "jaeger check" >}}
### dashboard
{{< cli-2-10/description "jaeger dashboard" >}}
{{< cli-2-10/examples "jaeger dashboard" >}}
{{< cli-2-10/flags "jaeger dashboard" >}}
### install
{{< cli-2-10/description "jaeger install" >}}
{{< cli-2-10/examples "jaeger install" >}}
{{< cli-2-10/flags "jaeger install" >}}
### list
{{< cli-2-10/description "jaeger list" >}}
{{< cli-2-10/examples "jaeger list" >}}
{{< cli-2-10/flags "jaeger list" >}}
### uninstall
{{< cli-2-10/description "jaeger uninstall" >}}
{{< cli-2-10/examples "jaeger uninstall" >}}
{{< cli-2-10/flags "jaeger uninstall" >}}

View File

@ -0,0 +1,67 @@
+++
title = "multicluster"
+++
{{< cli-2-10/description "multicluster" >}}
{{< cli-2-10/examples "multicluster" >}}
{{< cli-2-10/flags "multicluster" >}}
## Subcommands
### allow
{{< cli-2-10/description "multicluster allow" >}}
{{< cli-2-10/examples "multicluster allow" >}}
{{< cli-2-10/flags "multicluster allow" >}}
### check
{{< cli-2-10/description "multicluster check" >}}
{{< cli-2-10/examples "multicluster check" >}}
{{< cli-2-10/flags "multicluster check" >}}
### gateways
{{< cli-2-10/description "multicluster gateways" >}}
{{< cli-2-10/examples "multicluster gateways" >}}
{{< cli-2-10/flags "multicluster gateways" >}}
### install
{{< cli-2-10/description "multicluster install" >}}
{{< cli-2-10/examples "multicluster install" >}}
{{< cli-2-10/flags "multicluster install" >}}
### link
{{< cli-2-10/description "multicluster link" >}}
{{< cli-2-10/examples "multicluster link" >}}
{{< cli-2-10/flags "multicluster link" >}}
### uninstall
{{< cli-2-10/description "multicluster uninstall" >}}
{{< cli-2-10/examples "multicluster uninstall" >}}
{{< cli-2-10/flags "multicluster uninstall" >}}
### unlink
{{< cli-2-10/description "multicluster unlink" >}}
{{< cli-2-10/examples "multicluster unlink" >}}
{{< cli-2-10/flags "multicluster unlink" >}}

View File

@ -0,0 +1,13 @@
+++
title = "profile"
+++
{{< cli-2-10/description "profile" >}}
Check out the [service profile](../../../features/service-profiles/)
documentation for more details on what this command does and what you can do
with service profiles.
{{< cli-2-10/examples "profile" >}}
{{< cli-2-10/flags "profile" >}}

View File

@ -0,0 +1,9 @@
+++
title = "repair"
+++
{{< cli-2-10/description "repair" >}}
{{< cli-2-10/examples "repair" >}}
{{< cli-2-10/flags "repair" >}}

View File

@ -0,0 +1,9 @@
+++
title = "uninject"
+++
{{< cli-2-10/description "uninject" >}}
{{< cli-2-10/examples "uninject" >}}
{{< cli-2-10/flags "uninject" >}}

View File

@ -0,0 +1,9 @@
+++
title = "uninstall"
+++
{{< cli-2-10/description "uninstall" >}}
{{< cli-2-10/examples "uninstall" >}}
{{< cli-2-10/flags "uninstall" >}}

View File

@ -0,0 +1,30 @@
+++
title = "upgrade"
+++
{{< cli-2-10/description "upgrade" >}}
{{< cli-2-10/examples "upgrade" >}}
{{< cli-2-10/flags "upgrade" >}}
## Subcommands
Upgrade supports subcommands as part of the
[Multi-stage install](../../../tasks/install/#multi-stage-install) feature.
### config
{{< cli-2-10/description "upgrade config" >}}
{{< cli-2-10/examples "upgrade config" >}}
{{< cli-2-10/flags "upgrade config" >}}
### control-plane
{{< cli-2-10/description "upgrade control-plane" >}}
{{< cli-2-10/examples "upgrade control-plane" >}}
{{< cli-2-10/flags "upgrade control-plane" >}}

View File

@ -0,0 +1,9 @@
+++
title = "version"
+++
{{< cli-2-10/description "version" >}}
{{< cli-2-10/examples "version" >}}
{{< cli-2-10/flags "version" >}}

View File

@ -0,0 +1,167 @@
+++
title = "viz"
aliases = [
"dashboard",
"edges",
"routes",
"stat",
"tap",
"top"
]
+++
{{< cli-2-10/description "viz" >}}
{{< cli-2-10/examples "viz" >}}
{{< cli-2-10/flags "viz" >}}
## Subcommands
### check
{{< cli-2-10/description "viz check" >}}
{{< cli-2-10/examples "viz check" >}}
{{< cli-2-10/flags "viz check" >}}
### dashboard
{{< cli-2-10/description "viz dashboard" >}}
Check out the [architecture](../../architecture/#dashboard) docs for a
more thorough explanation of what this command does.
{{< cli-2-10/examples "viz dashboard" >}}
{{< cli-2-10/flags "viz dashboard" >}}
(*) You'll need to tweak the dashboard's `enforced-host` parameter with this
value, as explained in [the DNS-rebinding protection
docs](../../../tasks/exposing-dashboard/#tweaking-host-requirement)
### edges
{{< cli-2-10/description "viz edges" >}}
{{< cli-2-10/examples "viz edges" >}}
{{< cli-2-10/flags "viz edges" >}}
### install
{{< cli-2-10/description "viz install" >}}
{{< cli-2-10/examples "viz install" >}}
{{< cli-2-10/flags "viz install" >}}
### list
{{< cli-2-10/description "viz list" >}}
{{< cli-2-10/examples "viz list" >}}
{{< cli-2-10/flags "viz list" >}}
### profile
{{< cli-2-10/description "viz profile" >}}
{{< cli-2-10/examples "viz profile" >}}
{{< cli-2-10/flags "viz profile" >}}
### routes
The `routes` command displays per-route service metrics. In order for
this information to be available, a service profile must be defined for the
service that is receiving the requests. For more information about how to
create a service profile, see [service profiles](../../../features/service-profiles/).
and the [profile](../../cli/profile/) command reference.
## Inbound Metrics
By default, `routes` displays *inbound* metrics for a target. In other
words, it shows information about requests which are sent to the target and
responses which are returned by the target. For example, the command:
```bash
linkerd viz routes deploy/webapp
```
Displays the request volume, success rate, and latency of requests to the
`webapp` deployment. These metrics are from the `webapp` deployment's
perspective, which means that, for example, these latencies do not include the
network latency between a client and the `webapp` deployment.
## Outbound Metrics
If you specify the `--to` flag then `linkerd viz routes` displays *outbound* metrics
from the target resource to the resource in the `--to` flag. In contrast to
the inbound metrics, these metrics are from the perspective of the sender. This
means that these latencies do include the network latency between the client
and the server. For example, the command:
```bash
linkerd viz routes deploy/traffic --to deploy/webapp
```
Displays the request volume, success rate, and latency of requests from
`traffic` to `webapp` from the perspective of the `traffic` deployment.
## Effective and Actual Metrics
If you are looking at *outbound* metrics (by specifying the `--to` flag) you
can also supply the `-o wide` flag to differentiate between *effective* and
*actual* metrics.
Effective requests are requests which are sent by some client to the Linkerd
proxy. Actual requests are requests which the Linkerd proxy sends to some
server. If the Linkerd proxy is performing retries, one effective request can
translate into more than one actual request. If the Linkerd proxy is not
performing retries, effective requests and actual requests will always be equal.
When enabling retries, you should expect to see the actual request rate
increase and the effective success rate increase. See the
[retries and timeouts section](../../../features/retries-and-timeouts/) for more
information.
Because retries are only performed on the *outbound* (client) side, the
`-o wide` flag can only be used when the `--to` flag is specified.
{{< cli-2-10/examples "viz routes" >}}
{{< cli-2-10/flags "viz routes" >}}
### stat
{{< cli-2-10/description "viz stat" >}}
{{< cli-2-10/examples "viz stat" >}}
{{< cli-2-10/flags "viz stat" >}}
### tap
{{< cli-2-10/description "viz tap" >}}
{{< cli-2-10/examples "viz tap" >}}
{{< cli-2-10/flags "viz tap" >}}
### top
{{< cli-2-10/description "viz top" >}}
{{< cli-2-10/examples "viz top" >}}
{{< cli-2-10/flags "viz top" >}}
### uninstall
{{< cli-2-10/description "viz uninstall" >}}
{{< cli-2-10/examples "viz uninstall" >}}
{{< cli-2-10/flags "viz uninstall" >}}

View File

@ -0,0 +1,77 @@
+++
title = "Cluster Configuration"
description = "Configuration settings unique to providers and install methods."
+++
## GKE
### Private Clusters
If you are using a **private GKE cluster**, you are required to create a
firewall rule that allows the GKE operated api-server to communicate with the
Linkerd control plane. This makes it possible for features such as automatic
proxy injection to receive requests directly from the api-server.
In this example, we will use [gcloud](https://cloud.google.com/sdk/install) to
simplify the creation of the said firewall rule.
Setup:
```bash
CLUSTER_NAME=your-cluster-name
gcloud config set compute/zone your-zone-or-region
```
Get the cluster `MASTER_IPV4_CIDR`:
```bash
MASTER_IPV4_CIDR=$(gcloud container clusters describe $CLUSTER_NAME \
| grep "masterIpv4CidrBlock: " \
| awk '{print $2}')
```
Get the cluster `NETWORK`:
```bash
NETWORK=$(gcloud container clusters describe $CLUSTER_NAME \
| grep "^network: " \
| awk '{print $2}')
```
Get the cluster auto-generated `NETWORK_TARGET_TAG`:
```bash
NETWORK_TARGET_TAG=$(gcloud compute firewall-rules list \
--filter network=$NETWORK --format json \
| jq ".[] | select(.name | contains(\"$CLUSTER_NAME\"))" \
| jq -r '.targetTags[0]' | head -1)
```
The format of the network tag should be something like `gke-cluster-name-xxxx-node`.
Verify the values:
```bash
echo $MASTER_IPV4_CIDR $NETWORK $NETWORK_TARGET_TAG
# example output
10.0.0.0/28 foo-network gke-foo-cluster-c1ecba83-node
```
Create the firewall rules for `proxy-injector` and `tap`:
```bash
gcloud compute firewall-rules create gke-to-linkerd-control-plane \
--network "$NETWORK" \
--allow "tcp:8443,tcp:8089" \
--source-ranges "$MASTER_IPV4_CIDR" \
--target-tags "$NETWORK_TARGET_TAG" \
--priority 1000 \
--description "Allow traffic on ports 8443, 8089 for linkerd control-plane components"
```
Finally, verify that the firewall is created:
```bash
gcloud compute firewall-rules describe gke-to-linkerd-control-plane
```

View File

@ -0,0 +1,14 @@
+++
title = "Extensions List"
description = "List of Linkerd extensions that can be added to the installation for additional functionality"
+++
Linkerd provides a mix of built-in and third-party
[extensions](../../tasks/extensions/) to add additional functionality to the
base installation. The following is the list of known extensions:
{{< extensions-2-10 >}}
If you have an extension for Linkerd and it is not on the list, [please edit
this
page!](https://github.com/linkerd/website/edit/main/linkerd.io/data/extension-list.yaml)

View File

@ -0,0 +1,198 @@
+++
title = "IPTables Reference"
description = "A table with all of the chains and associated rules"
+++
In order to route TCP traffic in a pod to and from the proxy, an [`init
container`](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)
is used to set up `iptables` rules at the start of an injected pod's
lifecycle.
At first, `linkerd-init` will create two chains in the `nat` table:
`PROXY_INIT_REDIRECT`, and `PROXY_INIT_OUTPUT`. These chains are used to route
inbound and outbound packets through the proxy. Each chain has a set of rules
attached to it, these rules are traversed by a packet in order.
## Inbound connections
When a packet arrives in a pod, it will typically be processed by the
`PREROUTING` chain, a default chain attached to the `nat` table. The sidecar
container will create a new chain to process inbound packets, called
`PROXY_INIT_REDIRECT`. The sidecar container creates a rule
(`install-proxy-init-prerouting`) to send packets from the `PREROUTING` chain
to our redirect chain. This is the first rule traversed by an inbound packet.
The redirect chain will be configured with two more rules:
1. `ignore-port`: will ignore processing packets whose destination ports are
included in the `skip-inbound-ports` install option.
2. `proxy-init-redirect-all`: will redirect all incoming TCP packets through
the proxy, on port `4143`.
Based on these two rules, there are two possible paths that an inbound packet
can take, both of which are outlined below.
{{<fig src="/images/iptables/iptables-fig2-1.png"
title="Inbound iptables chain traversal">}}
The packet will arrive on the `PREROUTING` chain and will be immediately routed
to the redirect chain. If its destination port matches any of the inbound ports
to skip, then it will be forwarded directly to the application process,
_bypassing the proxy_. The list of destination ports to check against can be
[configured when installing Linkerd](/2.11/reference/cli/install/#). If the
packet does not match any of the ports in the list, it will be redirected
through the proxy. Redirection is done by changing the incoming packet's
destination header, the target port will be replaced with `4143`, which is the
proxy's inbound port. The proxy will process the packet and produce a new one
that will be forwarded to the service; it will be able to get the original
target (IP:PORT) of the inbound packet by using a special socket option
[`SO_ORIGINAL_DST`](https://linux.die.net/man/3/getsockopt). The new packet
will be routed through the `OUTPUT` chain, from there it will be sent to the
application. The `OUTPUT` chain rules are covered in more detail below.
## Outbound connections
When a packet leaves a pod, it will first traverse the `OUTPUT` chain, the
first default chain an outgoing packet traverses in the `nat` table. To
redirect outgoing packets through the outbound side of the proxy, the sidecar
container will again create a new chain. The first outgoing rule is similar to
the inbound counterpart: any packet that traverses the `OUTPUT` chain should be
forwarded to our `PROXY_INIT_OUTPUT` chain to be processed.
The output redirect chain is slightly harder to understand but follows the same
logical flow as the inbound redirect chain, in total there are 4 rules
configured:
1. `ignore-proxy-uid`: any packets owned by the proxy (whose user id is
`2102`), will skip processing and return to the previous (`OUTPUT`) chain.
From there, it will be sent on the outbound network interface (either to
the application, in the case of an inbound packet, or outside of the pod,
for an outbound packet).
2. `ignore-loopback`: if the packet is sent over the loopback interface
(`lo`), it will skip processing and return to the previous chain. From
here, the packet will be sent to the destination, much like the first rule
in the chain.
3. `ignore-port`: will ignore processing packets whose destination ports are
included in the `skip-outbound-ports` install option.
4. `redirect-all-outgoing`: the last rule in the chain, it will redirect all
outgoing TCP packets to port `4140`, the proxy's outbound port. If a
packet has made it this far, it is guaranteed its destination is not local
(i.e `lo`) and it has not been produced by the proxy. This means the
packet has been produced by the service, so it should be forwarded to its
destination by the proxy.
{{< fig src="/images/iptables/iptables-fig2-2.png"
title="Outbound iptables chain traversal" >}}
A packet produced by the service will first hit the `OUTPUT` chain; from here,
it will be sent to our own output chain for processing. The first rule it
encounters in `PROXY_INIT_OUTPUT` will be `ignore-proxy-uid`. Since the packet
was generated by the service, this rule will be skipped. If the packet's
destination is not a port bound on localhost (e.g `127.0.0.1:80`), then it will
skip the second rule as well. The third rule, `ignore-port` will be matched if
the packet's destination port is in the outbound ports to skip list, in this
case, it will be sent out on the network interface, bypassing the proxy. If the
rule is not matched, then the packet will reach the final rule in the chain
`redirect-all-outgoing`-- as the name implies, it will be sent to the proxy to
be processed, on its outbound port `4140`. Much like in the inbound case, the
routing happens at the `nat` level, the packet's header will be re-written to
target the outbound port. The proxy will process the packet and then forward it
to its destination. The new packet will take the same path through the `OUTPUT`
chain, however, it will stop at the first rule, since it was produced by the
proxy.
The substantiated explanation applies to a packet whose destination is another
service, outside of the pod. In practice, an application can also send traffic
locally. As such, there are two other possible scenarios that we will explore:
_when a service talks to itself_ (by sending traffic over localhost or by using
its own endpoint address), and when _a service talks to itself through a
`clusterIP` target_. Both scenarios are somehow related, but the path a packet
takes differs.
**A service may send requests to itself**. It can also target another container
in the pod. This scenario would typically apply when:
* The destination is the pod (or endpoint) IP address.
* The destination is a port bound on localhost (regardless of which container
it belongs to).
{{< fig src="/images/iptables/iptables-fig2-3.png"
title="Outbound iptables chain traversal" >}}
When the application targets itself through its pod's IP (or loopback address),
the packets will traverse the two output chains. The first rule will be
skipped, since the owner is the application, and not the proxy. Once the second
rule is matched, the packets will return to the first output chain, from here,
they'll be sent directly to the service.
{{< note >}}
Usually, packets traverse another chain on the outbound side called
`POSTROUTING`. This chain is traversed after the `OUTPUT` chain, but to keep
the explanation simple, it has not been mentioned. Likewise, outbound packets that
are sent over the loopback interface become inbound packets, since they need to
be processed again. The kernel takes shortcuts in this case and bypasses the
`PREROUTING` chain that inbound packets from the outside world traverse when
they first arrive. For this reason, we do not need any special rules on the
inbound side to account for outbound packets that are sent locally.
{{< /note >}}
**A service may send requests to itself using its clusterIP**. In such cases,
it is not guaranteed that the destination will be local. The packet follows an
unusual path, as depicted in the diagram below.
{{< fig src="/images/iptables/iptables-fig2-4.png"
title="Outbound iptables chain traversal" >}}
When the packet first traverses the output chains, it will follow the same path
an outbound packet would normally take. In such a scenario, the packet's
destination will be an address that is not considered to be local by the
kernel-- it is, after all, a virtual IP. The proxy will process the packet, at
a connection level, connections to a `clusterIP` will be load balanced between
endpoints. Chances are that the endpoint selected will be the pod itself,
packets will therefore never leave the pod; the destination will be resolved to
the podIP. The packets produced by the proxy will traverse the output chain and
stop at the first rule, then they will be forwarded to the service. This
constitutes an edge case because at this point, the packet has been processed
by the proxy, unlike the scenario previously discussed where it skips it
altogether. For this reason, at a connection level, the proxy will _not_ mTLS
or opportunistically upgrade the connection to HTTP/2 when the endpoint is
local to the pod. In practice, this is treated as if the destination was
loopback, with the exception that the packet is forwarded through the proxy,
instead of being forwarded from the service directly to itself.
## Rules table
For reference, you can find the actual commands used to create the rules below.
Alternatively, if you want to inspect the iptables rules created for a pod, you
can retrieve them through the following command:
```bash
$ kubectl -n <namesppace> logs <pod-name> linkerd-init
# where <pod-name> is the name of the pod
# you want to see the iptables rules for
```
<!-- markdownlint-disable MD013 -->
### Inbound
{{< table >}}
| # | name | iptables rule | description|
|---|------|---------------|------------|
| 1 | redirect-common-chain | `iptables -t nat -N PROXY_INIT_REDIRECT`| creates a new `iptables` chain to add inbound redirect rules to; the chain is attached to the `nat` table |
| 2 | ignore-port | `iptables -t nat -A PROXY_INIT_REDIRECT -p tcp --match multiport --dports <ports> -j RETURN` | configures `iptables` to ignore the redirect chain for packets whose dst ports are included in the `--skip-inbound-ports` config option |
| 3 | proxy-init-redirect-all | `iptables -t nat -A PROXY_INIT_REDIRECT -p tcp -j REDIRECT --to-port 4143` | configures `iptables` to redirect all incoming TCP packets to port `4143`, the proxy's inbound port |
| 4 | install-proxy-init-prerouting | `iptables -t nat -A PREROUTING -j PROXY_INIT_REDIRECT` | the last inbound rule configures the `PREROUTING` chain (first chain a packet traverses inbound) to send packets to the redirect chain for processing |
{{< /table >}}
### Outbound
{{< table >}}
| # | name | iptables rule | description |
|---|------|---------------|-------------|
| 1 | redirect-common-chain | `iptables -t nat -N PROXY_INIT_OUTPUT`| creates a new `iptables` chain to add outbound redirect rules to, also attached to the `nat` table |
| 2 | ignore-proxy-uid | `iptables -t nat -A PROXY_INIT_OUTPUT -m owner --uid-owner 2102 -j RETURN` | when a packet is owned by the proxy (`--uid-owner 2102`), skip processing and return to the previous (`OUTPUT`) chain |
| 3 | ignore-loopback | `iptables -t nat -A PROXY_INIT_OUTPUT -o lo -j RETURN` | when a packet is sent over the loopback interface (`lo`), skip processing and return to the previous chain |
| 4 | ignore-port | `iptables -t nat -A PROXY_INIT_OUTPUT -p tcp --match multiport --dports <ports> -j RETURN` | configures `iptables` to ignore the redirect output chain for packets whose dst ports are included in the `--skip-outbound-ports` config option |
| 5 | redirect-all-outgoing | `iptables -t nat -A PROXY_INIT_OUTPUT -p tcp -j REDIRECT --to-port 4140`| configures `iptables` to redirect all outgoing TCP packets to port `4140`, the proxy's outbound port |
| 6 | install-proxy-init-output | `iptables -t nat -A OUTPUT -j PROXY_INIT_OUTPUT` | the last outbound rule configures the `OUTPUT` chain (second before last chain a packet traverses outbound) to send packets to the redirect output chain for processing |
{{< /table >}}
<!-- markdownlint-enable MD013 -->

View File

@ -0,0 +1,55 @@
+++
title = "Proxy Configuration"
description = "Linkerd provides a set of annotations that can be used to override the data plane proxy's configuration."
+++
Linkerd provides a set of annotations that can be used to **override** the data
plane proxy's configuration. This is useful for **overriding** the default
configurations of [auto-injected proxies](../../features/proxy-injection/).
The following is the list of supported annotations:
{{< cli-2-10/annotations "inject" >}}
For example, to update an auto-injected proxy's CPU and memory resources, we
insert the appropriate annotations into the `spec.template.metadata.annotations`
of the owner's pod spec, using `kubectl edit` like this:
```yaml
spec:
template:
metadata:
annotations:
config.linkerd.io/proxy-cpu-limit: "1"
config.linkerd.io/proxy-cpu-request: "0.2"
config.linkerd.io/proxy-memory-limit: 2Gi
config.linkerd.io/proxy-memory-request: 128Mi
```
See [here](../../tasks/configuring-proxy-concurrency/) for details on tuning the
proxy's resource usage.
For proxies injected using the `linkerd inject` command, configuration can be
overridden using the [command-line flags](../cli/inject/).
## Ingress Mode
Proxy ingress mode is a mode of operation designed to help Linkerd integrate
with certain ingress controllers. Ingress mode is necessary if the ingress
itself cannot be otherwise configured to use the Service port/ip as the
destination.
When an individual Linkerd proxy is set to `ingress` mode, it will route
requests based on their `:authority`, `Host`, or `l5d-dst-override` headers
instead of their original destination. This will inform Linkerd to override the
endpoint selection of the ingress container and to perform its own endpoint
selection, enabling features such as per-route metrics and traffic splitting.
The proxy can be made to run in `ingress` mode by used the `linkerd.io/inject:
ingress` annotation rather than the default `linkerd.io/inject: enabled`
annotation. This can also be done with the `--ingress` flag in the `inject` CLI
command:
```bash
kubectl get deployment <ingress-controller> -n <ingress-namespace> -o yaml | linkerd inject --ingress - | kubectl apply -f -
```

View File

@ -0,0 +1,39 @@
+++
title = "Proxy Log Level"
description = "Syntax of the proxy log level."
+++
The Linkerd proxy's log level can be configured via the:
* `LINKERD_PROXY_LOG` environment variable
* `--proxy-log-level` CLI flag of the `install`, `inject` and `upgrade` commands
* `config.linkerd.io/proxy-log-level` annotation
(see [Proxy Configuration](../proxy-configuration/))
which sets `LINKERD_PROXY_LOG` environment-variable on the injected sidecar
* an [endpoint on the admin port](../../tasks/modifying-proxy-log-level/)
of a running proxy.
The log level is a comma-separated list of log directives, which is
based on the logging syntax of the [`env_logger` crate](https://docs.rs/env_logger/0.6.1/env_logger/#enabling-logging).
A log directive consists of either:
* A level (e.g. `info`), which sets the global log level, or
* A module path (e.g. `foo` or `foo::bar::baz`), or
* A module path followed by an equals sign and a level (e.g. `foo=warn`
or `foo::bar::baz=debug`), which sets the log level for that module
A level is one of:
* `trace`
* `debug`
* `info`
* `warn`
* `error`
A module path represents the path to a Rust module. It consists of one or more
module names, separated by `::`.
A module name starts with a letter, and consists of alphanumeric characters and `_`.
The proxy's default log level is set to `warn,linkerd2_proxy=info`.

View File

@ -0,0 +1,206 @@
+++
title = "Proxy Metrics"
description = "The Linkerd proxy natively exports Prometheus metrics for all incoming and outgoing traffic."
aliases = [
"/proxy-metrics/",
"../proxy-metrics/",
"../observability/proxy-metrics/"
]
+++
The Linkerd proxy exposes metrics that describe the traffic flowing through the
proxy. The following metrics are available at `/metrics` on the proxy's metrics
port (default: `:4191`) in the [Prometheus format][prom-format].
## Protocol-Level Metrics
* `request_total`: A counter of the number of requests the proxy has received.
This is incremented when the request stream begins.
* `response_total`: A counter of the number of responses the proxy has received.
This is incremented when the response stream ends.
* `response_latency_ms`: A histogram of response latencies. This measurement
reflects the [time-to-first-byte][ttfb] (TTFB) by recording the elapsed time
between the proxy processing a request's headers and the first data frame of the
response. If a response does not include any data, the end-of-stream event is
used. The TTFB measurement is used so that Linkerd accurately reflects
application behavior when a server provides response headers immediately but is
slow to begin serving the response body.
* `route_request_total`, `route_response_latency_ms`, and `route_response_total`:
These metrics are analogous to `request_total`, `response_latency_ms`, and
`response_total` except that they are collected at the route level. This
means that they do not have `authority`, `tls`, `grpc_status_code` or any
outbound labels but instead they have:
* `dst`: The authority of this request.
* `rt_route`: The name of the route for this request.
* `control_request_total`, `control_response_latency_ms`, and `control_response_total`:
These metrics are analogous to `request_total`, `response_latency_ms`, and
`response_total` but for requests that the proxy makes to the Linkerd control
plane. Instead of `authority`, `direction`, or any outbound labels, instead
they have:
* `addr`: The address used to connect to the control plane.
Note that latency measurements are not exported to Prometheus until the stream
_completes_. This is necessary so that latencies can be labeled with the appropriate
[response classification](#response-labels).
### Labels
Each of these metrics has the following labels:
* `authority`: The value of the `:authority` (HTTP/2) or `Host` (HTTP/1.1)
header of the request.
* `direction`: `inbound` if the request originated from outside of the pod,
`outbound` if the request originated from inside of the pod.
* `tls`: `true` if the request's connection was secured with TLS.
#### Response Labels
The following labels are only applicable on `response_*` metrics.
* `classification`: `success` if the response was successful, or `failure` if
a server error occurred. This classification is based on
the gRPC status code if one is present, and on the HTTP
status code otherwise. Only applicable to response metrics.
* `grpc_status_code`: The value of the `grpc-status` trailer. Only applicable
for gRPC responses.
* `status_code`: The HTTP status code of the response.
#### Outbound labels
The following labels are only applicable if `direction=outbound`.
* `dst_deployment`: The deployment to which this request is being sent.
* `dst_k8s_job`: The job to which this request is being sent.
* `dst_replicaset`: The replica set to which this request is being sent.
* `dst_daemonset`: The daemon set to which this request is being sent.
* `dst_statefulset`: The stateful set to which this request is being sent.
* `dst_replicationcontroller`: The replication controller to which this request
is being sent.
* `dst_namespace`: The namespace to which this request is being sent.
* `dst_service`: The service to which this request is being sent.
* `dst_pod_template_hash`: The [pod-template-hash][pod-template-hash] of the pod
to which this request is being sent. This label
selector roughly approximates a pod's `ReplicaSet` or
`ReplicationController`.
#### Prometheus Collector labels
The following labels are added by the Prometheus collector.
* `instance`: ip:port of the pod.
* `job`: The Prometheus job responsible for the collection, typically
`linkerd-proxy`.
##### Kubernetes labels added at collection time
Kubernetes namespace, pod name, and all labels are mapped to corresponding
Prometheus labels.
* `namespace`: Kubernetes namespace that the pod belongs to.
* `pod`: Kubernetes pod name.
* `pod_template_hash`: Corresponds to the [pod-template-hash][pod-template-hash]
Kubernetes label. This value changes during redeploys and
rolling restarts. This label selector roughly
approximates a pod's `ReplicaSet` or
`ReplicationController`.
##### Linkerd labels added at collection time
Kubernetes labels prefixed with `linkerd.io/` are added to your application at
`linkerd inject` time. More specifically, Kubernetes labels prefixed with
`linkerd.io/proxy-*` will correspond to these Prometheus labels:
* `daemonset`: The daemon set that the pod belongs to (if applicable).
* `deployment`: The deployment that the pod belongs to (if applicable).
* `k8s_job`: The job that the pod belongs to (if applicable).
* `replicaset`: The replica set that the pod belongs to (if applicable).
* `replicationcontroller`: The replication controller that the pod belongs to
(if applicable).
* `statefulset`: The stateful set that the pod belongs to (if applicable).
### Example
Here's a concrete example, given the following pod snippet:
```yaml
name: vote-bot-5b7f5657f6-xbjjw
namespace: emojivoto
labels:
app: vote-bot
linkerd.io/control-plane-ns: linkerd
linkerd.io/proxy-deployment: vote-bot
pod-template-hash: "3957278789"
test: vote-bot-test
```
The resulting Prometheus labels will look like this:
```bash
request_total{
pod="vote-bot-5b7f5657f6-xbjjw",
namespace="emojivoto",
app="vote-bot",
control_plane_ns="linkerd",
deployment="vote-bot",
pod_template_hash="3957278789",
test="vote-bot-test",
instance="10.1.3.93:4191",
job="linkerd-proxy"
}
```
## Transport-Level Metrics
The following metrics are collected at the level of the underlying transport
layer.
* `tcp_open_total`: A counter of the total number of opened transport
connections.
* `tcp_close_total`: A counter of the total number of transport connections
which have closed.
* `tcp_open_connections`: A gauge of the number of transport connections
currently open.
* `tcp_write_bytes_total`: A counter of the total number of sent bytes. This is
updated when the connection closes.
* `tcp_read_bytes_total`: A counter of the total number of received bytes. This
is updated when the connection closes.
* `tcp_connection_duration_ms`: A histogram of the duration of the lifetime of a
connection, in milliseconds. This is updated when the connection closes.
### Labels
Each of these metrics has the following labels:
* `direction`: `inbound` if the connection was established either from outside the
pod to the proxy, or from the proxy to the application,
`outbound` if the connection was established either from the
application to the proxy, or from the proxy to outside the pod.
* `peer`: `src` if the connection was accepted by the proxy from the source,
`dst` if the connection was opened by the proxy to the destination.
Note that the labels described above under the heading "Prometheus Collector labels"
are also added to transport-level metrics, when applicable.
#### Connection Close Labels
The following labels are added only to metrics which are updated when a
connection closes (`tcp_close_total` and `tcp_connection_duration_ms`):
* `classification`: `success` if the connection terminated cleanly, `failure` if
the connection closed due to a connection failure.
[prom-format]: https://prometheus.io/docs/instrumenting/exposition_formats/#format-version-0.0.4
[pod-template-hash]: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#pod-template-hash-label
[ttfb]: https://en.wikipedia.org/wiki/Time_to_first_byte
## Identity Metrics
* `identity_cert_expiration_timestamp_seconds`: A gauge of the time when the
proxy's current mTLS identity certificate will expire (in seconds since the UNIX
epoch).
* `identity_cert_refresh_count`: A counter of the total number of times the
proxy's mTLS identity certificate has been refreshed by the Identity service.

View File

@ -0,0 +1,135 @@
+++
title = "Service Profiles"
description = "Details on the specification and what is possible with service profiles."
+++
[Service profiles](../../features/service-profiles/) provide Linkerd additional
information about a service. This is a reference for everything that can be done
with service profiles.
## Spec
A service profile spec must contain the following top level fields:
{{< table >}}
| field| value |
|------|-------|
| `routes`| a list of [route](#route) objects |
| `retryBudget`| a [retry budget](#retry-budget) object that defines the maximum retry rate to this service |
{{< /table >}}
## Route
A route object must contain the following fields:
{{< table >}}
| field | value |
|-------|-------|
| `name` | the name of this route as it will appear in the route label |
| `condition` | a [request match](#request-match) object that defines if a request matches this route |
| `responseClasses` | (optional) a list of [response class](#response-class) objects |
| `isRetryable` | indicates that requests to this route are always safe to retry and will cause the proxy to retry failed requests on this route whenever possible |
| `timeout` | the maximum amount of time to wait for a response (including retries) to complete after the request is sent |
{{< /table >}}
## Request Match
A request match object must contain _exactly one_ of the following fields:
{{< table >}}
| field | value |
|-------|-------|
| `pathRegex` | a regular expression to match the request path against |
| `method` | one of GET, POST, PUT, DELETE, OPTION, HEAD, TRACE |
| `all` | a list of [request match](#request-match) objects which must _all_ match |
| `any` | a list of [request match](#request-match) objects, at least one of which must match |
| `not` | a [request match](#request-match) object which must _not_ match |
{{< /table >}}
### Request Match Usage Examples
The simplest condition is a path regular expression:
```yaml
pathRegex: '/authors/\d+'
```
This is a condition that checks the request method:
```yaml
method: POST
```
If more than one condition field is set, all of them must be satisfied. This is
equivalent to using the 'all' condition:
```yaml
all:
- pathRegex: '/authors/\d+'
- method: POST
```
Conditions can be combined using 'all', 'any', and 'not':
```yaml
any:
- all:
- method: POST
- pathRegex: '/authors/\d+'
- all:
- not:
method: DELETE
- pathRegex: /info.txt
```
## Response Class
A response class object must contain the following fields:
{{< table >}}
| field | value |
|-------|-------|
| `condition` | a [response match](#response-match) object that defines if a response matches this response class |
| `isFailure` | a boolean that defines if these responses should be classified as failed |
{{< /table >}}
## Response Match
A response match object must contain _exactly one_ of the following fields:
{{< table >}}
| field | value |
|-------|-------|
| `status` | a [status range](#status-range) object to match the response status code against |
| `all` | a list of [response match](#response-match) objects which must _all_ match |
| `any` | a list of [response match](#response-match) objects, at least one of which must match |
| `not` | a [response match](#response-match) object which must _not_ match |
{{< /table >}}
Response Match conditions can be combined in a similar way as shown above for
[Request Match Usage Examples](#request-match-usage-examples)
## Status Range
A status range object must contain _at least one_ of the following fields.
Specifying only one of min or max matches just that one status code.
{{< table >}}
| field | value |
|-------|-------|
| `min` | the status code must be greater than or equal to this value |
| `max` | the status code must be less than or equal to this value |
{{< /table >}}
## Retry Budget
A retry budget specifies the maximum total number of retries that should be sent
to this service as a ratio of the original request volume.
{{< table >}}
| field | value |
|-------|-------|
| `retryRatio` | the maximum ratio of retries requests to original requests |
| `minRetriesPerSecond` | allowance of retries per second in addition to those allowed by the retryRatio |
| `ttl` | indicates for how long requests should be considered for the purposes of calculating the retryRatio |
{{< /table >}}

View File

@ -0,0 +1,15 @@
+++
title = "Tasks"
weight = 4
aliases = [
"./next-steps/"
]
+++
As a complement to the [Linkerd feature docs]({{% ref "../features" %}}) and
the [Linkerd reference docs]({{% ref "../reference" %}}), we've provided guides
and examples of common tasks that you may need to perform when using Linkerd.
## Common Linkerd tasks
{{% sectiontoc "tasks" %}}

View File

@ -0,0 +1,109 @@
+++
title = "Adding Your Services to Linkerd"
description = "In order for your services to take advantage of Linkerd, they also need to be *meshed* by injecting Linkerd's data plane proxy into their pods."
aliases = [
"../adding-your-service/",
"../automating-injection/"
]
+++
Adding Linkerd's control plane to your cluster doesn't change anything about
your application. In order for your services to take advantage of Linkerd, they
need to be *meshed*, by injecting Linkerd's data plane proxy into their pods.
For most applications, meshing a service is as simple as adding a Kubernetes
annotation. However, services that make network calls immediately on startup
may need to [handle startup race
conditions](#a-note-on-startup-race-conditions), and services that use MySQL,
SMTP, Memcache, and similar protocols may need to [handle server-speaks-first
protocols](#a-note-on-server-speaks-first-protocols).
Read on for more!
## Meshing a service with annotations
Meshing a Kubernetes resource is typically done by annotating the resource, or
its namespace, with the `linkerd.io/inject: enabled` Kubernetes annotation.
This annotation triggers automatic proxy injection when the resources are
created or updated. (See the [proxy injection
page](../../features/proxy-injection/) for more on how this works.)
For convenience, Linkerd provides a [`linkerd
inject`](../../reference/cli/inject/) text transform command will add this
annotation to a given Kubernetes manifest. Of course, these annotations can be
set by any other mechanism.
{{< note >}}
Simply adding the annotation will not automatically mesh existing pods. After
setting the annotation, you will need to recreate or update any resources (e.g.
with `kubectl rollout restart`) to trigger proxy injection. (Often, a
[rolling
update](https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/)
can be performed to inject the proxy into a live service without interruption.)
{{< /note >}}
## Example
To add Linkerd's data plane proxies to a service defined in a Kubernetes
manifest, you can use `linkerd inject` to add the annotations before applying
the manifest to Kubernetes:
```bash
cat deployment.yml | linkerd inject - | kubectl apply -f -
```
This example transforms the `deployment.yml` file to add injection annotations
in the correct places, then applies it to the cluster.
## Verifying the data plane pods have been injected
To verify that your services have been added to the mesh, you can query
Kubernetes for the list of containers in the pods and ensure that the proxy is
listed:
```bash
kubectl -n MYNAMESPACE get po -o jsonpath='{.items[0].spec.containers[*].name}'
```
If everything was successful, you'll see `linkerd-proxy` in the output, e.g.:
```bash
MYCONTAINER linkerd-proxy
```
## A note on startup race conditions
While the proxy starts very quickly, Kubernetes doesn't provide any guarantees
about container startup ordering, so the application container may start before
the proxy is ready. This means that any connections made immediately at app
startup time may fail until the proxy is active.
In many cases, this can be ignored: the application will ideally retry the
connection, or Kubernetes will restart the container after it fails, and
eventually the proxy will be ready. Alternatively, you can use
[linkerd-await](https://github.com/linkerd/linkerd-await) to delay the
application container until the proxy is ready, or set a
[`skip-outbound-ports`
annotation](../../features/protocol-detection/#skipping-the-proxy)
to bypass the proxy for these connections.
## A note on server-speaks-first protocols
Linkerd's [protocol
detection](../../features/protocol-detection/) works by
looking at the first few bytes of client data to determine the protocol of the
connection. Some protocols such as MySQL, SMTP, and other server-speaks-first
protocols don't send these bytes. In some cases, this may require additional
configuration to avoid a 10-second delay in establishing the first connection.
See [Configuring protocol
detection](../../features/protocol-detection/#configuring-protocol-detection)
for details.
## More reading
For more information on how the inject command works and all of the parameters
that can be set, see the [`linkerd inject` reference
page](../../reference/cli/inject/).
For details on how autoinjection works, see the the [proxy injection
page](../../features/proxy-injection/).

View File

@ -0,0 +1,234 @@
+++
title = "Automatically Rotating Control Plane TLS Credentials"
description = "Use cert-manager to automatically rotate control plane TLS credentials."
aliases = [ "use_external_certs" ]
+++
Linkerd's [automatic mTLS](../../features/automatic-mtls/) feature uses a set of
TLS credentials to generate TLS certificates for proxies: a trust anchor, and
an issuer certificate and private key. While Linkerd automatically rotates the
TLS certificates for data plane proxies every 24 hours, it does not rotate the
TLS credentials used to issue these certificate. In this doc, we'll describe
how to automatically rotate the issuer certificate and private key, by using
an external solution.
(Note that Linkerd's trust anchor [must still be manually
rotated](../manually-rotating-control-plane-tls-credentials/) on
long-lived clusters.)
## Cert manager
[Cert-manager](https://github.com/jetstack/cert-manager) is a popular project
for making TLS credentials from external sources available to Kubernetes
clusters.
As a first step, [install cert-manager on your
cluster](https://docs.cert-manager.io/en/latest/getting-started/install/kubernetes.html).
{{< note >}}
If you are installing cert-manager `>= 1.0`,
you will need to have kubernetes `>= 1.16`.
Legacy custom resource definitions in cert-manager for kubernetes `<= 1.15`
do not have a keyAlgorithm option,
so the certificates will be generated using RSA and be incompatible with linkerd.
See [v0.16 to v1.0 upgrade notes](https://cert-manager.io/docs/installation/upgrading/upgrading-0.16-1.0/)
for more details on version requirements.
{{< /note >}}
### Cert manager as an on-cluster CA
In this case, rather than pulling credentials from an external
source, we'll configure it to act as an on-cluster
[CA](https://en.wikipedia.org/wiki/Certificate_authority) and have it re-issue
Linkerd's issuer certificate and private key on a periodic basis.
First, create the namespace that cert-manager will use to store its
Linkerd-related resources. For simplicity, we suggest the default Linkerd
control plane namespace:
```bash
kubectl create namespace linkerd
```
#### Save the signing key pair as a Secret
Next, using the [`step`](https://smallstep.com/cli/) tool, create a signing key
pair and store it in a Kubernetes Secret in the namespace created above:
```bash
step certificate create root.linkerd.cluster.local ca.crt ca.key \
--profile root-ca --no-password --insecure &&
kubectl create secret tls \
linkerd-trust-anchor \
--cert=ca.crt \
--key=ca.key \
--namespace=linkerd
```
For a longer-lived trust anchor certificate, pass the `--not-after` argument
to the step command with the desired value (e.g. `--not-after=87600h`).
#### Create an Issuer referencing the secret
With the Secret in place, we can create a cert-manager "Issuer" resource that
references it:
```bash
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: linkerd-trust-anchor
namespace: linkerd
spec:
ca:
secretName: linkerd-trust-anchor
EOF
```
#### Issuing certificates and writing them to a secret
Finally, we can create a cert-manager "Certificate" resource which uses this
Issuer to generate the desired certificate:
```bash
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: linkerd-identity-issuer
namespace: linkerd
spec:
secretName: linkerd-identity-issuer
duration: 48h
renewBefore: 25h
issuerRef:
name: linkerd-trust-anchor
kind: Issuer
commonName: identity.linkerd.cluster.local
dnsNames:
- identity.linkerd.cluster.local
isCA: true
privateKey:
algorithm: ECDSA
usages:
- cert sign
- crl sign
- server auth
- client auth
EOF
```
(In the YAML manifest above, the `duration` key instructs cert-manager to
consider certificates as valid for `48` hours and the `renewBefore` key indicates
that cert-manager will attempt to issue a new certificate `25` hours before
expiration of the current one. These values can be customized to your liking.)
At this point, cert-manager can now use this Certificate resource to obtain TLS
credentials, which will be stored in a secret named `linkerd-identity-issuer`.
To validate your newly-issued certificate, you can run:
```bash
kubectl get secret linkerd-identity-issuer -o yaml -n linkerd
```
Now we just need to inform Linkerd to consume these credentials.
{{< note >}}
Due to a [bug](https://github.com/jetstack/cert-manager/issues/2942) in
cert-manager, if you are using cert-manager version `0.15` with experimental
controllers, the certificate it issues are not compatible with with Linkerd
versions `<= stable-2.8.1`.
Your `linkerd-identity` pods will likely crash with the following log output:
```log
"Failed to initialize identity service: failed to read CA from disk:
unsupported block type: 'PRIVATE KEY'"
```
Some possible ways to resolve this issue are:
- Upgrade Linkerd to the edge versions `>= edge-20.6.4` which contains
a [fix](https://github.com/linkerd/linkerd2/pull/4597/).
- Upgrade cert-manager to versions `>= 0.16`.
[(how to upgrade)](https://cert-manager.io/docs/installation/upgrading/upgrading-0.15-0.16/)
- Turn off cert-manager experimental controllers.
[(docs)](https://cert-manager.io/docs/release-notes/release-notes-0.15/#using-the-experimental-controllers)
{{< /note >}}
### Alternative CA providers
Instead of using Cert Manager as CA, you can configure it to rely on a number
of other solutions such as [Vault](https://www.vaultproject.io). More detail on
how to setup the existing Cert Manager to use different type of issuers
can be found [here](https://cert-manager.io/docs/configuration/vault/).
## Third party cert management solutions
It is important to note that the mechanism that Linkerd provides is also
usable outside of cert-manager. Linkerd will read the `linkerd-identity-issuer`
Secret, and if it's of type `kubernetes.io/tls`, will use the contents as its
TLS credentials. This means that any solution that is able to rotate TLS
certificates by writing them to this secret can be used to provide dynamic
TLS certificate management.
You could generate that secret with a command such as:
```bash
kubectl create secret tls linkerd-identity-issuer --cert=issuer.crt --key=issuer.key --namespace=linkerd
```
Where `issuer.crt` and `issuer.key` would be the cert and private key of an
intermediary cert rooted at the trust root (`ca.crt`) referred above (check this
[guide](../generate-certificates/) to see how to generate them).
Note that the root cert (`ca.crt`) needs to be included in that Secret as well.
You can just edit the generated Secret and include the `ca.crt` field with the
contents of the file base64-encoded.
After setting up the `linkerd-identity-issuer` Secret, continue with the
following instructions to install and configure Linkerd to use it.
## Using these credentials with CLI installation
For CLI installation, the Linkerd control plane should be installed with the
`--identity-external-issuer` flag, which instructs Linkerd to read certificates
from the `linkerd-identity-issuer` secret. Whenever certificate and key stored
in the secret are updated, the `identity` service will automatically detect
this change and reload the new credentials.
Voila! We have set up automatic rotation of Linkerd's control plane TLS
credentials. And if you want to monitor the update process, you can check the
`IssuerUpdated` events emitted by the service:
```bash
kubectl get events --field-selector reason=IssuerUpdated -n linkerd
```
## Installing with Helm
For Helm installation, rather than running `linkerd install`, set the
`identityTrustAnchorsPEM` to the value of `ca.crt` in the
`linkerd-identity-issuer` Secret:
```bash
helm install linkerd2 \
--set-file identityTrustAnchorsPEM=ca.crt \
--set identity.issuer.scheme=kubernetes.io/tls \
--set installNamespace=false \
linkerd/linkerd2 \
-n linkerd
```
{{< note >}}
For Helm versions < v3, `--name` flag has to specifically be passed.
In Helm v3, It has been deprecated, and is the first argument as
specified above.
{{< /note >}}
See [Automatically Rotating Webhook TLS
Credentials](../automatically-rotating-webhook-tls-credentials/) for how
to do something similar for webhook TLS credentials.

View File

@ -0,0 +1,323 @@
+++
title = "Automatically Rotating Webhook TLS Credentials"
description = "Use cert-manager to automatically rotate webhook TLS credentials."
+++
The Linkerd control plane contains several components, called webhooks, which
are called directly by Kubernetes itself. The traffic from Kubernetes to the
Linkerd webhooks is secured with TLS and therefore each of the webhooks requires
a secret containing TLS credentials. These certificates are different from the
ones that the Linkerd proxies use to secure pod-to-pod communication and use a
completely separate trust chain. For more information on rotating the TLS
credentials used by the Linkerd proxies, see
[Automatically Rotating Control Plane TLS Credentials](../use_external_certs/).
By default, when Linkerd is installed
with the Linkerd CLI or with the Linkerd Helm chart, TLS credentials are
automatically generated for all of the webhooks. If these certificates expire
or need to be regenerated for any reason, performing a
[Linkerd upgrade](../upgrade/) (using the Linkerd CLI or using Helm) will
regenerate them.
This workflow is suitable for most users. However, if you need these webhook
certificates to be rotated automatically on a regular basis, it is possible to
use cert-manager to automatically manage them.
## Install Cert manager
As a first step, [install cert-manager on your
cluster](https://docs.cert-manager.io/en/latest/getting-started/install/kubernetes.html)
and create the namespaces that cert-manager will use to store its
webhook-related resources. For simplicity, we suggest using the defaule
namespace linkerd uses:
```bash
# control plane core
kubectl create namespace linkerd
# viz (ignore if not using the viz extension)
kubectl create namespace linkerd-viz
# viz (ignore if not using the jaeger extension)
kubectl create namespace linkerd-jaeger
```
## Save the signing key pair as a Secret
Next, we will use the [`step`](https://smallstep.com/cli/) tool, to create a
signing key pair which will be used to sign each of the webhook certificates:
```bash
step certificate create webhook.linkerd.cluster.local ca.crt ca.key \
--profile root-ca --no-password --insecure --san webhook.linkerd.cluster.local
kubectl create secret tls webhook-issuer-tls --cert=ca.crt --key=ca.key --namespace=linkerd
# ignore if not using the viz extension
kubectl create secret tls webhook-issuer-tls --cert=ca.crt --key=ca.key --namespace=linkerd-viz
# ignore if not using the jaeger extension
kubectl create secret tls webhook-issuer-tls --cert=ca.crt --key=ca.key --namespace=linkerd-jaeger
```
## Create Issuers referencing the secrets
With the Secrets in place, we can create cert-manager "Issuer" resources that
reference them:
```bash
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: webhook-issuer
namespace: linkerd
spec:
ca:
secretName: webhook-issuer-tls
---
# ignore if not using the viz extension
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: webhook-issuer
namespace: linkerd-viz
spec:
ca:
secretName: webhook-issuer-tls
---
# ignore if not using the jaeger extension
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: webhook-issuer
namespace: linkerd-jaeger
spec:
ca:
secretName: webhook-issuer-tls
EOF
```
## Issuing certificates and writing them to secrets
Finally, we can create cert-manager "Certificate" resources which use the
Issuers to generate the desired certificates:
```bash
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: linkerd-proxy-injector
namespace: linkerd
spec:
secretName: linkerd-proxy-injector-k8s-tls
duration: 24h
renewBefore: 1h
issuerRef:
name: webhook-issuer
kind: Issuer
commonName: linkerd-proxy-injector.linkerd.svc
dnsNames:
- linkerd-proxy-injector.linkerd.svc
isCA: false
privateKey:
algorithm: ECDSA
usages:
- server auth
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: linkerd-sp-validator
namespace: linkerd
spec:
secretName: linkerd-sp-validator-k8s-tls
duration: 24h
renewBefore: 1h
issuerRef:
name: webhook-issuer
kind: Issuer
commonName: linkerd-sp-validator.linkerd.svc
dnsNames:
- linkerd-sp-validator.linkerd.svc
isCA: false
privateKey:
algorithm: ECDSA
usages:
- server auth
---
# ignore if not using the viz extension
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: tap
namespace: linkerd-viz
spec:
secretName: tap-k8s-tls
duration: 24h
renewBefore: 1h
issuerRef:
name: webhook-issuer
kind: Issuer
commonName: tap.linkerd-viz.svc
dnsNames:
- tap.linkerd-viz.svc
isCA: false
privateKey:
algorithm: ECDSA
usages:
- server auth
---
# ignore if not using the viz extension
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: linkerd-tap-injector
namespace: linkerd-viz
spec:
secretName: tap-injector-k8s-tls
duration: 24h
renewBefore: 1h
issuerRef:
name: webhook-issuer
kind: Issuer
commonName: tap-injector.linkerd-viz.svc
dnsNames:
- tap-injector.linkerd-viz.svc
isCA: false
privateKey:
algorithm: ECDSA
usages:
- server auth
---
# ignore if not using the jaeger extension
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: jaeger-injector
namespace: linkerd-jaeger
spec:
secretName: jaeger-injector-k8s-tls
duration: 24h
renewBefore: 1h
issuerRef:
name: webhook-issuer
kind: Issuer
commonName: jaeger-injector.linkerd.svc
dnsNames:
- jaeger-injector.linkerd.svc
isCA: false
privateKey:
algorithm: ECDSA
usages:
- server auth
EOF
```
At this point, cert-manager can now use these Certificate resources to obtain
TLS credentials, which are stored in the `linkerd-proxy-injector-k8s-tls`,
`linkerd-sp-validator-k8s-tls`, `tap-k8s-tls`, `tap-injector-k8s-tls` and
`jaeger-injector-k8s-tls` secrets respectively.
Now we just need to inform Linkerd to consume these credentials.
## Using these credentials with CLI installation
To configure Linkerd to use the credentials from cert-manager rather than
generating its own, we generate a supplemental config file:
```bash
CA=$(awk '{ print " " $0 }' ca.crt)
cat > config.yml <<EOF
proxyInjector:
externalSecret: true
caBundle: |
$CA
profileValidator:
externalSecret: true
caBundle: |
$CA
EOF
# ignore if not using the viz extension
cat > config-viz.yml <<EOF
tap:
externalSecret: true
caBundle: |
$CA
tapInjector:
externalSecret: true
caBundle: |
$CA
EOF
# ignore if not using the jaeger extension
cat > config-jaeger.yml <<EOF
webhook:
externalSecret: true
caBundle: |
$CA
EOF
```
Now we can install Linkerd using these config files:
```bash
linkerd install --values=config.yml | kubectl apply -f -
# ignore if not using the viz extension
linkerd viz install --values=config-viz.yml | kubectl apply -f -
# ignore if not using the jaeger extension
linkerd jaeger install --values=config-jaeger.yml | kubectl apply -f -
```
## Installing with Helm
For Helm installation, we can configure the Helm values directly:
```bash
helm install linkerd2 \
--set installNamespace=false \
--set proxyInjector.externalSecret=true \
--set-file proxyInjector.caBundle=ca.crt \
--set profileValidator.externalSecret=true \
--set-file profileValidator.caBundle=ca.crt \
linkerd/linkerd2 \
-n linkerd
# ignore if not using the viz extension
helm install linkerd-viz \
--set installNamespace=false \
--set tap.externalSecret=true \
--set-file tap.caBundle=ca.crt \
--set tapInjector.externalSecret=true \
--set-file tapInjector.caBundle=ca.crt \
linkerd/linkerd-viz \
-n linkerd-viz
# ignore if not using the jaeger extension
helm install linkerd-jaeger \
--set installNamespace=false \
--set webhook.externalSecret=true \
--set-file webhook.caBundle=ca.crt \
linkerd/linkerd-jaeger \
-n linkerd-jaeger
```
{{< note >}}
When installing Linkerd with Helm, you must also provide the issuer trust root
and issuer credentials as described in [Installing Linkerd with Helm](../install-helm/).
{{< /note >}}
{{< note >}}
For Helm versions < v3, `--name` flag has to specifically be passed.
In Helm v3, It has been deprecated, and is the first argument as
specified above.
{{< /note >}}
See [Automatically Rotating Control Plane TLS
Credentials](../automatically-rotating-control-plane-tls-credentials/)
for details on how to do something similar for control plane credentials.

View File

@ -0,0 +1,472 @@
+++
title = "Debugging HTTP applications with per-route metrics"
description = "Follow a long-form example of debugging a failing HTTP application using per-route metrics."
+++
This demo is of a Ruby application that helps you manage your bookshelf. It
consists of multiple microservices and uses JSON over HTTP to communicate with
the other services. There are three services:
- [webapp](https://github.com/BuoyantIO/booksapp/blob/master/webapp.rb): the
frontend
- [authors](https://github.com/BuoyantIO/booksapp/blob/master/authors.rb): an
API to manage the authors in the system
- [books](https://github.com/BuoyantIO/booksapp/blob/master/books.rb): an API
to manage the books in the system
For demo purposes, the app comes with a simple traffic generator. The overall
topology looks like this:
{{< fig src="/images/books/topology.png" title="Topology" >}}
## Prerequisites
To use this guide, you'll need to have Linkerd and its Viz extension installed
on your cluster. Follow the [Installing Linkerd Guide](../install/) if
you haven't already done this.
## Install the app
To get started, let's install the books app onto your cluster. In your local
terminal, run:
```bash
kubectl create ns booksapp && \
curl -sL https://run.linkerd.io/booksapp.yml \
| kubectl -n booksapp apply -f -
```
This command creates a namespace for the demo, downloads its Kubernetes
resource manifest and uses `kubectl` to apply it to your cluster. The app
comprises the Kubernetes deployments and services that run in the `booksapp`
namespace.
Downloading a bunch of containers for the first time takes a little while.
Kubernetes can tell you when all the services are running and ready for
traffic. Wait for that to happen by running:
```bash
kubectl -n booksapp rollout status deploy webapp
```
You can also take a quick look at all the components that were added to your
cluster by running:
```bash
kubectl -n booksapp get all
```
Once the rollout has completed successfully, you can access the app itself by
port-forwarding `webapp` locally:
```bash
kubectl -n booksapp port-forward svc/webapp 7000 &
```
Open [http://localhost:7000/](http://localhost:7000/) in your browser to see the
frontend.
{{< fig src="/images/books/frontend.png" title="Frontend" >}}
Unfortunately, there is an error in the app: if you click *Add Book*, it will
fail 50% of the time. This is a classic case of non-obvious, intermittent
failure---the type that drives service owners mad because it is so difficult to
debug. Kubernetes itself cannot detect or surface this error. From Kubernetes's
perspective, it looks like everything's fine, but you know the application is
returning errors.
{{< fig src="/images/books/failure.png" title="Failure" >}}
## Add Linkerd to the service
Now we need to add the Linkerd data plane proxies to the service. The easiest
option is to do something like this:
```bash
kubectl get -n booksapp deploy -o yaml \
| linkerd inject - \
| kubectl apply -f -
```
This command retrieves the manifest of all deployments in the `booksapp`
namespace, runs them through `linkerd inject`, and then re-applies with
`kubectl apply`. The `linkerd inject` command annotates each resource to
specify that they should have the Linkerd data plane proxies added, and
Kubernetes does this when the manifest is reapplied to the cluster. Best of
all, since Kubernetes does a rolling deploy, the application stays running the
entire time. (See [Automatic Proxy Injection](../../features/proxy-injection/) for
more details on how this works.)
## Debugging
Let's use Linkerd to discover the root cause of this app's failures. To check
out the Linkerd dashboard, run:
```bash
linkerd viz dashboard &
```
{{< fig src="/images/books/dashboard.png" title="Dashboard" >}}
Select `booksapp` from the namespace dropdown and click on the
[Deployments](http://localhost:50750/namespaces/booksapp/deployments) workload.
You should see all the deployments in the `booksapp` namespace show up. There
will be success rate, requests per second, and latency percentiles.
Thats cool, but youll notice that the success rate for `webapp` is not 100%.
This is because the traffic generator is submitting new books. You can do the
same thing yourself and push that success rate even lower. Click on `webapp` in
the Linkerd dashboard for a live debugging session.
You should now be looking at the detail view for the `webapp` service. Youll
see that `webapp` is taking traffic from `traffic` (the load generator), and it
has two outgoing dependencies: `authors` and `book`. One is the service for
pulling in author information and the other is the service for pulling in book
information.
{{< fig src="/images/books/webapp-detail.png" title="Detail" >}}
A failure in a dependent service may be exactly whats causing the errors that
`webapp` is returning (and the errors you as a user can see when you click). We
can see that the `books` service is also failing. Lets scroll a little further
down the page, well see a live list of all traffic endpoints that `webapp` is
receiving. This is interesting:
{{< fig src="/images/books/top.png" title="Top" >}}
Aha! We can see that inbound traffic coming from the `webapp` service going to
the `books` service is failing a significant percentage of the time. That could
explain why `webapp` was throwing intermittent failures. Lets click on the tap
(🔬) icon and then on the Start button to look at the actual request and
response stream.
{{< fig src="/images/books/tap.png" title="Tap" >}}
Indeed, many of these requests are returning 500s.
It was surprisingly easy to diagnose an intermittent issue that affected only a
single route. You now have everything you need to open a detailed bug report
explaining exactly what the root cause is. If the `books` service was your own,
you know exactly where to look in the code.
## Service Profiles
To understand the root cause, we used live traffic. For some issues this is
great, but what happens if the issue is intermittent and happens in the middle of
the night? [Service profiles](../../features/service-profiles/) provide Linkerd
with some additional information about your services. These define the routes
that you're serving and, among other things, allow for the collection of metrics
on a per route basis. With Prometheus storing these metrics, you'll be able to
sleep soundly and look up intermittent issues in the morning.
One of the easiest ways to get service profiles setup is by using existing
[OpenAPI (Swagger)](https://swagger.io/docs/specification/about/) specs. This
demo has published specs for each of its services. You can create a service
profile for `webapp` by running:
```bash
curl -sL https://run.linkerd.io/booksapp/webapp.swagger \
| linkerd -n booksapp profile --open-api - webapp \
| kubectl -n booksapp apply -f -
```
This command will do three things:
1. Fetch the swagger specification for `webapp`.
1. Take the spec and convert it into a service profile by using the `profile`
command.
1. Apply this configuration to the cluster.
Alongside `install` and `inject`, `profile` is also a pure text operation. Check
out the profile that is generated:
```yaml
apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
creationTimestamp: null
name: webapp.booksapp.svc.cluster.local
namespace: booksapp
spec:
routes:
- condition:
method: GET
pathRegex: /
name: GET /
- condition:
method: POST
pathRegex: /authors
name: POST /authors
- condition:
method: GET
pathRegex: /authors/[^/]*
name: GET /authors/{id}
- condition:
method: POST
pathRegex: /authors/[^/]*/delete
name: POST /authors/{id}/delete
- condition:
method: POST
pathRegex: /authors/[^/]*/edit
name: POST /authors/{id}/edit
- condition:
method: POST
pathRegex: /books
name: POST /books
- condition:
method: GET
pathRegex: /books/[^/]*
name: GET /books/{id}
- condition:
method: POST
pathRegex: /books/[^/]*/delete
name: POST /books/{id}/delete
- condition:
method: POST
pathRegex: /books/[^/]*/edit
name: POST /books/{id}/edit
```
The `name` refers to the FQDN of your Kubernetes service,
`webapp.booksapp.svc.cluster.local` in this instance. Linkerd uses the `Host`
header of requests to associate service profiles with requests. When the proxy
sees a `Host` header of `webapp.booksapp.svc.cluster.local`, it will use that to
look up the service profile's configuration.
Routes are simple conditions that contain the method (`GET` for example) and a
regex to match the path. This allows you to group REST style resources together
instead of seeing a huge list. The names for routes can be whatever you'd like.
For this demo, the method is appended to the route regex.
To get profiles for `authors` and `books`, you can run:
```bash
curl -sL https://run.linkerd.io/booksapp/authors.swagger \
| linkerd -n booksapp profile --open-api - authors \
| kubectl -n booksapp apply -f -
curl -sL https://run.linkerd.io/booksapp/books.swagger \
| linkerd -n booksapp profile --open-api - books \
| kubectl -n booksapp apply -f -
```
Verifying that this all works is easy when you use `linkerd viz tap`. Each live
request will show up with what `:authority` or `Host` header is being seen as
well as the `:path` and `rt_route` being used. Run:
```bash
linkerd viz tap -n booksapp deploy/webapp -o wide | grep req
```
This will watch all the live requests flowing through `webapp` and look
something like:
```bash
req id=0:1 proxy=in src=10.1.3.76:57152 dst=10.1.3.74:7000 tls=true :method=POST :authority=webapp.default:7000 :path=/books/2878/edit src_res=deploy/traffic src_ns=booksapp dst_res=deploy/webapp dst_ns=booksapp rt_route=POST /books/{id}/edit
```
As you can see:
- `:authority` is the correct host
- `:path` correctly matches
- `rt_route` contains the name of the route
These metrics are part of the [`linkerd viz routes`](../../reference/cli/viz/#routes)
command instead of [`linkerd viz stat`](../../reference/cli/viz/#stat). To see the
metrics that have accumulated so far, run:
```bash
linkerd viz -n booksapp routes svc/webapp
```
This will output a table of all the routes observed and their golden metrics.
The `[DEFAULT]` route is a catch all for anything that does not match the
service profile.
Profiles can be used to observe *outgoing* requests as well as *incoming*
requests. To do that, run:
```bash
linkerd viz -n booksapp routes deploy/webapp --to svc/books
```
This will show all requests and routes that originate in the `webapp` deployment
and are destined to the `books` service. Similarly to using `tap` and `top`
views in the [debugging](#debugging) section, the root cause of errors in this
demo is immediately apparent:
```bash
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
DELETE /books/{id}.json books 100.00% 0.5rps 18ms 29ms 30ms
GET /books.json books 100.00% 1.1rps 7ms 12ms 18ms
GET /books/{id}.json books 100.00% 2.5rps 6ms 10ms 10ms
POST /books.json books 52.24% 2.2rps 23ms 34ms 39ms
PUT /books/{id}.json books 41.98% 1.4rps 73ms 97ms 99ms
[DEFAULT] books - - - - -
```
## Retries
As it can take a while to update code and roll out a new version, let's
tell Linkerd that it can retry requests to the failing endpoint. This will
increase request latencies, as requests will be retried multiple times, but not
require rolling out a new version.
In this application, the success rate of requests from the `books` deployment to
the `authors` service is poor. To see these metrics, run:
```bash
linkerd viz -n booksapp routes deploy/books --to svc/authors
```
The output should look like:
```bash
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
DELETE /authors/{id}.json authors - - - - -
GET /authors.json authors - - - - -
GET /authors/{id}.json authors - - - - -
HEAD /authors/{id}.json authors 50.85% 3.9rps 5ms 10ms 17ms
POST /authors.json authors - - - - -
[DEFAULT] authors - - - - -
```
One thing thats clear is that all requests from books to authors are to the
`HEAD /authors/{id}.json` route and those requests are failing about 50% of the
time.
To correct this, lets edit the authors service profile and make those
requests retryable by running:
```bash
kubectl -n booksapp edit sp/authors.booksapp.svc.cluster.local
```
You'll want to add `isRetryable` to a specific route. It should look like:
```yaml
spec:
routes:
- condition:
method: HEAD
pathRegex: /authors/[^/]*\.json
name: HEAD /authors/{id}.json
isRetryable: true ### ADD THIS LINE ###
```
After editing the service profile, Linkerd will begin to retry requests to
this route automatically. We see a nearly immediate improvement in success rate
by running:
```bash
linkerd viz -n booksapp routes deploy/books --to svc/authors -o wide
```
This should look like:
```bash
ROUTE SERVICE EFFECTIVE_SUCCESS EFFECTIVE_RPS ACTUAL_SUCCESS ACTUAL_RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
DELETE /authors/{id}.json authors - - - - - 0ms
GET /authors.json authors - - - - - 0ms
GET /authors/{id}.json authors - - - - - 0ms
HEAD /authors/{id}.json authors 100.00% 2.8rps 58.45% 4.7rps 7ms 25ms 37ms
POST /authors.json authors - - - - - 0ms
[DEFAULT] authors - - - - - 0ms
```
You'll notice that the `-o wide` flag has added some columns to the `routes`
view. These show the difference between `EFFECTIVE_SUCCESS` and
`ACTUAL_SUCCESS`. The difference between these two show how well retries are
working. `EFFECTIVE_RPS` and `ACTUAL_RPS` show how many requests are being sent
to the destination service and and how many are being received by the client's
Linkerd proxy.
With retries automatically happening now, success rate looks great but the p95
and p99 latencies have increased. This is to be expected because doing retries
takes time.
## Timeouts
Linkerd can limit how long to wait before failing outgoing requests to another
service. These timeouts work by adding another key to a service profile's routes
configuration.
To get started, let's take a look at the current latency for requests from
`webapp` to the `books` service:
```bash
linkerd viz -n booksapp routes deploy/webapp --to svc/books
```
This should look something like:
```bash
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
DELETE /books/{id}.json books 100.00% 0.7rps 10ms 27ms 29ms
GET /books.json books 100.00% 1.3rps 9ms 34ms 39ms
GET /books/{id}.json books 100.00% 2.0rps 9ms 52ms 91ms
POST /books.json books 100.00% 1.3rps 45ms 140ms 188ms
PUT /books/{id}.json books 100.00% 0.7rps 80ms 170ms 194ms
[DEFAULT] books - - - - -
```
Requests to the `books` service's `PUT /books/{id}.json` route include retries
for when that service calls the `authors` service as part of serving those
requests, as described in the previous section. This improves success rate, at
the cost of additional latency. For the purposes of this demo, let's set a 25ms
timeout for calls to that route. Your latency numbers will vary depending on the
characteristics of your cluster. To edit the `books` service profile, run:
```bash
kubectl -n booksapp edit sp/books.booksapp.svc.cluster.local
```
Update the `PUT /books/{id}.json` route to have a timeout:
```yaml
spec:
routes:
- condition:
method: PUT
pathRegex: /books/[^/]*\.json
name: PUT /books/{id}.json
timeout: 25ms ### ADD THIS LINE ###
```
Linkerd will now return errors to the `webapp` REST client when the timeout is
reached. This timeout includes retried requests and is the maximum amount of
time a REST client would wait for a response.
Run `routes` to see what has changed:
```bash
linkerd viz -n booksapp routes deploy/webapp --to svc/books -o wide
```
With timeouts happening now, the metrics will change:
```bash
ROUTE SERVICE EFFECTIVE_SUCCESS EFFECTIVE_RPS ACTUAL_SUCCESS ACTUAL_RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
DELETE /books/{id}.json books 100.00% 0.7rps 100.00% 0.7rps 8ms 46ms 49ms
GET /books.json books 100.00% 1.3rps 100.00% 1.3rps 9ms 33ms 39ms
GET /books/{id}.json books 100.00% 2.2rps 100.00% 2.2rps 8ms 19ms 28ms
POST /books.json books 100.00% 1.3rps 100.00% 1.3rps 27ms 81ms 96ms
PUT /books/{id}.json books 86.96% 0.8rps 100.00% 0.7rps 75ms 98ms 100ms
[DEFAULT] books - - - - -
```
The latency numbers include time spent in the `webapp` application itself, so
it's expected that they exceed the 25ms timeout that we set for requests from
`webapp` to `books`. We can see that the timeouts are working by observing that
the effective success rate for our route has dropped below 100%.
## Clean Up
To remove the books app and the booksapp namespace from your cluster, run:
```bash
curl -sL https://run.linkerd.io/booksapp.yml \
| kubectl -n booksapp delete -f - \
&& kubectl delete ns booksapp
```

View File

@ -0,0 +1,298 @@
+++
title = "Automated Canary Releases"
description = "Reduce deployment risk by combining Linkerd and Flagger to automate canary releases based on service metrics."
+++
Linkerd's [traffic split](../../features/traffic-split/) feature allows you to
dynamically shift traffic between services. This can be used to implement
lower-risk deployment strategies like blue-green deploys and canaries.
But simply shifting traffic from one version of a service to the next is just
the beginning. We can combine traffic splitting with [Linkerd's automatic
*golden metrics* telemetry](../../features/telemetry/) and drive traffic decisions
based on the observed metrics. For example, we can gradually shift traffic from
an old deployment to a new one while continually monitoring its success rate. If
at any point the success rate drops, we can shift traffic back to the original
deployment and back out of the release. Ideally, our users remain happy
throughout, not noticing a thing!
In this tutorial, we'll walk you through how to combine Linkerd with
[Flagger](https://flagger.app/), a progressive delivery tool that ties
Linkerd's metrics and traffic splitting together in a control loop,
allowing for fully-automated, metrics-aware canary deployments.
## Prerequisites
- To use this guide, you'll need to have Linkerd installed on your cluster,
along with its Viz extension.
Follow the [Installing Linkerd Guide](../install/) if you haven't
already done this.
- The installation of Flagger depends on `kubectl` 1.14 or newer.
## Install Flagger
While Linkerd will be managing the actual traffic routing, Flagger automates
the process of creating new Kubernetes resources, watching metrics and
incrementally sending users over to the new version. To add Flagger to your
cluster and have it configured to work with Linkerd, run:
```bash
kubectl apply -k github.com/fluxcd/flagger/kustomize/linkerd
```
This command adds:
- The canary
[CRD](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/)
that enables configuring how a rollout should occur.
- RBAC which grants Flagger permissions to modify all the resources that it
needs to, such as deployments and services.
- A controller configured to interact with the Linkerd control plane.
To watch until everything is up and running, you can use `kubectl`:
```bash
kubectl -n linkerd rollout status deploy/flagger
```
## Set up the demo
This demo consists of three components: a load generator, a deployment and a
frontend. The deployment creates a pod that returns some information such as
name. You can use the responses to watch the incremental rollout as Flagger
orchestrates it. A load generator simply makes it easier to execute the rollout
as there needs to be some kind of active traffic to complete the operation.
Together, these components have a topology that looks like:
{{< fig src="/images/canary/simple-topology.svg"
title="Topology" >}}
To add these components to your cluster and include them in the Linkerd
[data plane](../../reference/architecture/#data-plane), run:
```bash
kubectl create ns test && \
kubectl apply -f https://run.linkerd.io/flagger.yml
```
Verify that everything has started up successfully by running:
```bash
kubectl -n test rollout status deploy podinfo
```
Check it out by forwarding the frontend service locally and opening
[http://localhost:8080](http://localhost:8080) locally by running:
```bash
kubectl -n test port-forward svc/frontend 8080
```
{{< note >}}
Traffic shifting occurs on the *client* side of the connection and not the
server side. Any requests coming from outside the mesh will not be shifted and
will always be directed to the primary backend. A service of type `LoadBalancer`
will exhibit this behavior as the source is not part of the mesh. To shift
external traffic, add your ingress controller to the mesh.
{{< /note>}}
## Configure the release
Before changing anything, you need to configure how a release should be rolled
out on the cluster. The configuration is contained in a
[Canary](https://docs.flagger.app/tutorials/linkerd-progressive-delivery)
definition. To apply to your cluster, run:
```bash
cat <<EOF | kubectl apply -f -
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
service:
port: 9898
analysis:
interval: 10s
threshold: 5
stepWeight: 10
maxWeight: 100
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m
EOF
```
The Flagger controller is watching these definitions and will create some new
resources on your cluster. To watch as this happens, run:
```bash
kubectl -n test get ev --watch
```
A new deployment named `podinfo-primary` will be created with the same number of
replicas that `podinfo` has. Once the new pods are ready, the original
deployment is scaled down to zero. This provides a deployment that is managed by
Flagger as an implementation detail and maintains your original configuration
files and workflows. Once you see the following line, everything is setup:
```bash
0s Normal Synced canary/podinfo Initialization done! podinfo.test
```
In addition to a managed deployment, there are also services created to
orchestrate routing traffic between the new and old versions of your
application. These can be viewed with `kubectl -n test get svc` and should look
like:
```bash
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend ClusterIP 10.7.251.33 <none> 8080/TCP 96m
podinfo ClusterIP 10.7.252.86 <none> 9898/TCP 96m
podinfo-canary ClusterIP 10.7.245.17 <none> 9898/TCP 23m
podinfo-primary ClusterIP 10.7.249.63 <none> 9898/TCP 23m
```
At this point, the topology looks a little like:
{{< fig src="/images/canary/initialized.svg"
title="Initialized" >}}
{{< note >}}
This guide barely touches all the functionality provided by Flagger. Make sure
to read the [documentation](https://docs.flagger.app/) if you're interested in
combining canary releases with HPA, working off custom metrics or doing other
types of releases such as A/B testing.
{{< /note >}}
## Start the rollout
As a system, Kubernetes resources have two major sections: the spec and status.
When a controller sees a spec, it tries as hard as it can to make the status of
the current system match the spec. With a deployment, if any of the pod spec
configuration is changed, a controller will kick off a rollout. By default, the
deployment controller will orchestrate a [rolling
update](https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/).
In this example, Flagger will notice that a deployment's spec changed and start
orchestrating the canary rollout. To kick this process off, you can update the
image to a new version by running:
```bash
kubectl -n test set image deployment/podinfo \
podinfod=quay.io/stefanprodan/podinfo:1.7.1
```
Any kind of modification to the pod's spec such as updating an environment
variable or annotation would result in the same behavior as updating the image.
On update, the canary deployment (`podinfo`) will be scaled up. Once ready,
Flagger will begin to update the [TrafficSplit CRD](../../features/traffic-split/)
incrementally. With a configured stepWeight of 10, each increment will increase
the weight of `podinfo` by 10. For each period, the success rate will be
observed and as long as it is over the threshold of 99%, Flagger will continue
the rollout. To watch this entire process, run:
```bash
kubectl -n test get ev --watch
```
While an update is occurring, the resources and traffic will look like this at a
high level:
{{< fig src="/images/canary/ongoing.svg"
title="Ongoing" >}}
After the update is complete, this picture will go back to looking just like the
figure from the previous section.
{{< note >}}
You can toggle the image tag between `1.7.1` and `1.7.0` to start the rollout
again.
{{< /note >}}
### Resource
The canary resource updates with the current status and progress. You can watch
by running:
```bash
watch kubectl -n test get canary
```
Behind the scenes, Flagger is splitting traffic between the primary and canary
backends by updating the traffic split resource. To watch how this configuration
changes over the rollout, run:
```bash
kubectl -n test get trafficsplit podinfo -o yaml
```
Each increment will increase the weight of `podinfo-canary` and decrease the
weight of `podinfo-primary`. Once the rollout is successful, the weight of
`podinfo-primary` will be set back to 100 and the underlying canary deployment
(`podinfo`) will be scaled down.
### Metrics
As traffic shifts from the primary deployment to the canary one, Linkerd
provides visibility into what is happening to the destination of requests. The
metrics show the backends receiving traffic in real time and measure the success
rate, latencies and throughput. From the CLI, you can watch this by running:
```bash
watch linkerd viz -n test stat deploy --from deploy/load
```
For something a little more visual, you can use the dashboard. Start it by
running `linkerd viz dashboard` and then look at the detail page for the
[podinfo traffic
split](http://localhost:50750/namespaces/test/trafficsplits/podinfo).
{{< fig src="/images/canary/traffic-split.png"
title="Dashboard" >}}
### Browser
Visit again [http://localhost:8080](http://localhost:8080). Refreshing the page
will show toggling between the new version and a different header color.
Alternatively, running `curl http://localhost:8080` will return a JSON response
that looks something like:
```bash
{
"hostname": "podinfo-primary-74459c7db8-lbtxf",
"version": "1.7.0",
"revision": "4fc593f42c7cd2e7319c83f6bfd3743c05523883",
"color": "blue",
"message": "greetings from podinfo v1.7.0",
"goos": "linux",
"goarch": "amd64",
"runtime": "go1.11.2",
"num_goroutine": "6",
"num_cpu": "8"
}
```
This response will slowly change as the rollout continues.
## Cleanup
To cleanup, remove the Flagger controller from your cluster and delete the
`test` namespace by running:
```bash
kubectl delete -k github.com/fluxcd/flagger/kustomize/linkerd && \
kubectl delete ns test
```

View File

@ -0,0 +1,131 @@
+++
title = "Configuring Proxy Concurrency"
description = "Limit the Linkerd proxy's CPU usage."
+++
The Linkerd data plane's proxies are multithreaded, and are capable of running a
variable number of worker threads so that their resource usage matches the
application workload.
In a vacuum, of course, proxies will exhibit the best throughput and lowest
latency when allowed to use as many CPU cores as possible. However, in practice,
there are other considerations to take into account.
A real world deployment is _not_ a load test where clients and servers perform
no other work beyond saturating the proxy with requests. Instead, the service
mesh model has proxy instances deployed as sidecars to application containers.
Each proxy only handles traffic to and from the pod it is injected into. This
means that throughput and latency are limited by the application workload. If an
application container instance can only handle so many requests per second, it
may not actually matter that the proxy could handle more. In fact, giving the
proxy more CPU cores than it requires to keep up with the application may _harm_
overall performance, as the application may have to compete with the proxy for
finite system resources.
Therefore, it is more important for individual proxies to handle their traffic
efficiently than to configure all proxies to handle the maximum possible load.
The primary method of tuning proxy resource usage is limiting the number of
worker threads used by the proxy to forward traffic. There are multiple methods
for doing this.
## Using the `proxy-cpu-limit` Annotation
The simplest way to configure the proxy's thread pool is using the
`config.linkerd.io/proxy-cpu-limit` annotation. This annotation configures the
proxy injector to set an environment variable that controls the number of CPU
cores the proxy will use.
When installing Linkerd using the [`linkerd install` CLI
command](../install/), the `--proxy-cpu-limit` argument sets this
annotation globally for all proxies injected by the Linkerd installation. For
example,
```bash
linkerd install --proxy-cpu-limit 2 | kubectl apply -f -
```
For more fine-grained configuration, the annotation may be added to any
[injectable Kubernetes resource](../../proxy-injection/), such as a namespace, pod,
or deployment.
For example, the following will configure any proxies in the `my-deployment`
deployment to use two CPU cores:
```yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: my-deployment
# ...
spec:
template:
metadata:
annotations:
config.linkerd.io/proxy-cpu-limit: '1'
# ...
```
{{< note >}} Unlike Kubernetes CPU limits and requests, which can be expressed
in milliCPUs, the `proxy-cpu-limit` annotation should be expressed in whole
numbers of CPU cores. Fractional values will be rounded up to the nearest whole
number. {{< /note >}}
## Using Kubernetes CPU Limits and Requests
Kubernetes provides
[CPU limits and CPU requests](https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/#specify-a-cpu-request-and-a-cpu-limit)
to configure the resources assigned to any pod or container. These may also be
used to configure the Linkerd proxy's CPU usage. However, depending on how the
kubelet is configured, using Kubernetes resource limits rather than the
`proxy-cpu-limit` annotation may not be ideal.
The kubelet uses one of two mechanisms for enforcing pod CPU limits. This is
determined by the
[`--cpu-manager-policy` kubelet option](https://kubernetes.io/docs/tasks/administer-cluster/cpu-management-policies/#configuration).
With the default CPU manager policy,
[`none`](https://kubernetes.io/docs/tasks/administer-cluster/cpu-management-policies/#none-policy),
the kubelet uses
[CFS quotas](https://en.wikipedia.org/wiki/Completely_Fair_Scheduler) to enforce
CPU limits. This means that the Linux kernel is configured to limit the amount
of time threads belonging to a given process are scheduled. Alternatively, the
CPU manager policy may be set to
[`static`](https://kubernetes.io/docs/tasks/administer-cluster/cpu-management-policies/#static-policy).
In this case, the kubelet will use Linux `cgroup`s to enforce CPU limits for
containers which meet certain criteria.
When the environment variable configured by the `proxy-cpu-limit` annotation is
unset, the proxy will run a number of worker threads equal to the number of CPU
cores available. This means that with the default `none` CPU manager policy, the
proxy may spawn a large number of worker threads, but the Linux kernel will
limit how often they are scheduled. This is less efficient than simply reducing
the number of worker threads, as `proxy-cpu-limit` does: more time is spent on
context switches, and each worker thread will run less frequently, potentially
impacting latency.
On the other hand, using
[cgroup cpusets](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt)
will limit the number of CPU cores available to the process. In essence, it will
appear to the proxy that the system has fewer CPU cores than it actually does.
This will result in similar behavior to the `proxy-cpu-limit` annotation.
However, it's worth noting that in order for this mechanism to be used, certain
criteria must be met:
- The kubelet must be configured with the `static` CPU manager policy
- The pod must be in the
[Guaranteed QoS class](https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/#create-a-pod-that-gets-assigned-a-qos-class-of-guaranteed).
This means that all containers in the pod must have both a limit and a request
for memory and CPU, and the limit for each must have the same value as the
request.
- The CPU limit and CPU request must be an integer greater than or equal to 1.
If you're not sure whether these criteria will all be met, it's best to use the
`proxy-cpu-limit` annotation in addition to any Kubernetes CPU limits and
requests.
## Using Helm
When using [Helm](../install-helm/), users must take care to set the
`proxy.cores` Helm variable in addition to `proxy.cpu.limit`, if
the criteria for cgroup-based CPU limits
[described above](#using-kubernetes-cpu-limits-and-requests) are not met.

View File

@ -0,0 +1,81 @@
+++
title = "Configuring Retries"
description = "Configure Linkerd to automatically retry failing requests."
+++
In order for Linkerd to do automatic retries of failures, there are two
questions that need to be answered:
- Which requests should be retried?
- How many times should the requests be retried?
Both of these questions can be answered by specifying a bit of extra information
in the [service profile](../../features/service-profiles/) for the service you're
sending requests to.
The reason why these pieces of configuration are required is because retries can
potentially be dangerous. Automatically retrying a request that changes state
(e.g. a request that submits a financial transaction) could potentially impact
your user's experience negatively. In addition, retries increase the load on
your system. A set of services that have requests being constantly retried
could potentially get taken down by the retries instead of being allowed time
to recover.
Check out the [retries section](../books/#retries) of the books demo
for a tutorial of how to configure retries.
## Retries
For routes that are idempotent and don't have bodies, you can edit the service
profile and add `isRetryable` to the retryable route:
```yaml
spec:
routes:
- name: GET /api/annotations
condition:
method: GET
pathRegex: /api/annotations
isRetryable: true ### ADD THIS LINE ###
```
## Retry Budgets
A retry budget is a mechanism that limits the number of retries that can be
performed against a service as a percentage of original requests. This
prevents retries from overwhelming your system. By default, retries may add at
most an additional 20% to the request load (plus an additional 10 "free"
retries per second). These settings can be adjusted by setting a `retryBudget`
on your service profile.
```yaml
spec:
retryBudget:
retryRatio: 0.2
minRetriesPerSecond: 10
ttl: 10s
```
## Monitoring Retries
Retries can be monitored by using the `linkerd viz routes` command with the `--to`
flag and the `-o wide` flag. Since retries are performed on the client-side,
we need to use the `--to` flag to see metrics for requests that one resource
is sending to another (from the server's point of view, retries are just
regular requests). When both of these flags are specified, the `linkerd routes`
command will differentiate between "effective" and "actual" traffic.
```bash
ROUTE SERVICE EFFECTIVE_SUCCESS EFFECTIVE_RPS ACTUAL_SUCCESS ACTUAL_RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
HEAD /authors/{id}.json authors 100.00% 2.8rps 58.45% 4.7rps 7ms 25ms 37ms
[DEFAULT] authors 0.00% 0.0rps 0.00% 0.0rps 0ms 0ms 0ms
```
Actual requests represent all requests that the client actually sends, including
original requests and retries. Effective requests only count the original
requests. Since an original request may trigger one or more retries, the actual
request volume is usually higher than the effective request volume when retries
are enabled. Since an original request may fail the first time, but a retry of
that request might succeed, the effective success rate is usually ([but not
always](../configuring-timeouts/#monitoring-timeouts)) higher than the
actual success rate.

View File

@ -0,0 +1,40 @@
+++
title = "Configuring Timeouts"
description = "Configure Linkerd to automatically fail requests that take too long."
+++
To limit how long Linkerd will wait before failing an outgoing request to
another service, you can configure timeouts. These work by adding a little bit
of extra information to the [service profile](../../features/service-profiles/) for
the service you're sending requests to.
Each route may define a timeout which specifies the maximum amount of time to
wait for a response (including retries) to complete after the request is sent.
If this timeout is reached, Linkerd will cancel the request, and return a 504
response. If unspecified, the default timeout is 10 seconds.
```yaml
spec:
routes:
- condition:
method: HEAD
pathRegex: /authors/[^/]*\.json
name: HEAD /authors/{id}.json
timeout: 300ms
```
Check out the [timeouts section](../books/#timeouts) of the books demo for
a tutorial of how to configure timeouts.
## Monitoring Timeouts
Requests which reach the timeout will be canceled, return a 504 Gateway Timeout
response, and count as a failure for the purposes of [effective success
rate](../configuring-retries/#monitoring-retries). Since the request was
canceled before any actual response was received, a timeout will not count
towards the actual request volume at all. This means that effective request
rate can be higher than actual request rate when timeouts are configured.
Furthermore, if a response is received just as the timeout is exceeded, it is
possible for the request to be counted as an actual success but an effective
failure. This can result in effective success rate being lower than actual
success rate.

View File

@ -0,0 +1,145 @@
+++
title = "Customizing Linkerd's Configuration with Kustomize"
description = "Use Kustomize to modify Linkerd's configuration in a programmatic way."
+++
Instead of forking the Linkerd install and upgrade process,
[Kustomize](https://kustomize.io/) can be used to patch the output of `linkerd
install` in a consistent way. This allows customization of the install to add
functionality specific to installations.
To get started, save the output of `install` to a YAML file. This will be the
base resource that Kustomize uses to patch and generate what is added to your
cluster.
```bash
linkerd install > linkerd.yaml
```
{{< note >}}
When upgrading, make sure you populate this file with the content from `linkerd
upgrade`. Using the latest `kustomize` releases, it would be possible to
automate this with an [exec
plugin](https://github.com/kubernetes-sigs/kustomize/tree/master/docs/plugins#exec-plugins).
{{< /note >}}
Next, create a `kustomization.yaml` file. This file will contain the
instructions for Kustomize listing the base resources and the transformations to
do on those resources. Right now, this looks pretty empty:
```yaml
resources:
- linkerd.yaml
```
Now, let's look at how to do some example customizations.
{{< note >}}
Kustomize allows as many patches, transforms and generators as you'd like. These
examples show modifications one at a time but it is possible to do as many as
required in a single `kustomization.yaml` file.
{{< /note >}}
## Add PriorityClass
There are a couple components in the control plane that can benefit from being
associated with a critical `PriorityClass`. While this configuration isn't
currently supported as a flag to `linkerd install`, it is not hard to add by
using Kustomize.
First, create a file named `priority-class.yaml` that will create define a
`PriorityClass` resource.
```yaml
apiVersion: scheduling.k8s.io/v1
description: Used for critical linkerd pods that must run in the cluster, but
can be moved to another node if necessary.
kind: PriorityClass
metadata:
name: linkerd-critical
value: 1000000000
```
{{< note >}}
`1000000000` is the max. allowed user-defined priority, adjust
accordingly.
{{< /note >}}
Next, create a file named `patch-priority-class.yaml` that will contain the
overlay. This overlay will explain what needs to be modified.
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: linkerd-identity
spec:
template:
spec:
priorityClassName: linkerd-critical
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: linkerd-controller
spec:
template:
spec:
priorityClassName: linkerd-critical
```
Then, add this as a strategic merge option to `kustomization.yaml`:
```yaml
resources:
- priority-class.yaml
- linkerd.yaml
patchesStrategicMerge:
- patch-priority-class.yaml
```
Applying this to your cluster requires taking the output of `kustomize build`
and piping it to `kubectl apply`. For example you can run:
```bash
kubectl kustomize build . | kubectl apply -f -
```
## Modify Grafana Configuration
Interested in enabling authentication for Grafana? It is possible to
modify the `ConfigMap` as a one off to do this. Unfortunately, the changes will
end up being reverted every time `linkerd upgrade` happens. Instead, create a
file named `grafana.yaml` and add your modifications:
```yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: grafana-config
data:
grafana.ini: |-
instance_name = grafana
[server]
root_url = %(protocol)s://%(domain)s:/grafana/
[analytics]
check_for_updates = false
```
Then, add this as a strategic merge option to `kustomization.yaml`:
```yaml
resources:
- linkerd.yaml
patchesStrategicMerge:
- grafana.yaml
```
Finally, apply this to your cluster by generating YAML with `kustomize build`
and piping the output to `kubectl apply`.
```bash
kubectl kustomize build . | kubectl apply -f -
```

View File

@ -0,0 +1,75 @@
+++
title = "Debugging 502s"
description = "Determine why Linkerd is returning 502 responses."
+++
When the Linkerd proxy encounters connection errors while processing a
request, it will typically return an HTTP 502 (Bad Gateway) response. It can be
very difficult to figure out why these errors are happening because of the lack
of information available.
## Why do these errors only occur when Linkerd is injected?
Linkerd turns connection errors into HTTP 502 responses. This can make issues
which were previously undetected suddenly visible. This is a good thing.
Linkerd also changes the way that connections to your application are managed:
it re-uses persistent connections and establishes an additional layer of
connection tracking. Managing connections in this way can sometimes expose
underlying application or infrastructure issues such as misconfigured connection
timeouts which can manifest as connection errors.
## Why can't Linkerd give a more informative error message?
From the Linkerd proxy's perspective, it just sees its connections to the
application refused or closed without explanation. This makes it nearly
impossible for Linkerd to report any error message in the 502 response. However,
if these errors coincide with the introduction of Linkerd, it does suggest that
the problem is related to connection re-use or connection tracking. Here are
some common reasons why the application may be refusing or terminating
connections.
## Common Causes of Connection Errors
### Connection Idle Timeouts
Some servers are configured with a connection idle timeout (for example, [this
timeout in the Go HTTP
server](https://golang.org/src/net/http/server.go#L2535]). This means that the
server will close any connections which do not receive any traffic in the
specified time period. If any requests are already in transit when the
connection shutdown is initiated, those requests will fail. This scenario is
likely to occur if you have traffic with a regular period (such as liveness
checks, for example) and an idle timeout equal to that period.
To remedy this, ensure that your server's idle timeouts are sufficiently long so
that they do not close connections which are actively in use.
### Half-closed Connection Timeouts
During the shutdown of a TCP connection, each side of the connection must be
closed independently. When one side is closed but the other is not, the
connection is said to be "half-closed". It is valid for the connection to be in
this state, however, the operating system's connection tracker can lose track of
connections which remain half-closed for long periods of time. This can lead to
responses not being delivered and to port conflicts when establishing new
connections which manifest as 502 responses.
You can use a [script to detect half-closed
connections](https://gist.github.com/adleong/0203b0864af2c29ddb821dd48f339f49)
on your Kubernetes cluster. If you detect a large number of half-closed
connections, you have a couple of ways to remedy the situation.
One solution would be to update your application to not leave connections
half-closed for long periods of time or to stop using software that does this.
Unfortunately, this is not always an option.
Another option is to increase the connection tracker's timeout for half-closed
connections. The default value of this timeout is platform dependent but is
typically 1 minute or 1 hour. You can view the current value by looking at the
file `/proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close_wait` in any
injected container. To increase this value, you can use the
`--close-wait-timeout` flag with `linkerd inject`. Note, however, that setting
this flag will also set the `privileged` field of the proxy init container to
true. Setting this timeout to 1 hour is usually sufficient and matches the
[value used by
kube-proxy](https://github.com/kubernetes/kubernetes/issues/32551).

View File

@ -0,0 +1,64 @@
+++
title = "Debugging gRPC applications with request tracing"
description = "Follow a long-form example of debugging a failing gRPC application using live request tracing."
aliases = [
"/debugging-an-app/",
"../debugging-an-app/"
]
+++
The demo application emojivoto has some issues. Let's use that and Linkerd to
diagnose an application that fails in ways which are a little more subtle than
the entire service crashing. This guide assumes that you've followed the steps
in the [Getting Started](../../getting-started/) guide and have Linkerd and the
demo application running in a Kubernetes cluster. If you've not done that yet,
go get started and come back when you're done!
If you glance at the Linkerd dashboard (by running the `linkerd viz dashboard`
command), you should see all the resources in the `emojivoto` namespace,
including the deployments. Each deployment running Linkerd shows success rate,
requests per second and latency percentiles.
{{< fig src="/images/debugging/stat.png" title="Top Level Metrics" >}}
That's pretty neat, but the first thing you might notice is that the success
rate is well below 100%! Click on `web` and let's dig in.
{{< fig src="/images/debugging/octopus.png" title="Deployment Detail" >}}
You should now be looking at the Deployment page for the web deployment. The first
thing you'll see here is that the web deployment is taking traffic from `vote-bot`
(a deployment included with emojivoto to continually generate a low level of
live traffic). The web deployment also has two outgoing dependencies, `emoji`
and `voting`.
While the emoji deployment is handling every request from web successfully, it
looks like the voting deployment is failing some requests! A failure in a dependent
deployment may be exactly what is causing the errors that web is returning.
Let's scroll a little further down the page, we'll see a live list of all
traffic that is incoming to *and* outgoing from `web`. This is interesting:
{{< fig src="/images/debugging/web-top.png" title="Top" >}}
There are two calls that are not at 100%: the first is vote-bot's call to the
`/api/vote` endpoint. The second is the `VoteDoughnut` call from the web
deployment to its dependent deployment, `voting`. Very interesting! Since
`/api/vote` is an incoming call, and `VoteDoughnut` is an outgoing call, this is
a good clue that this endpoint is what's causing the problem!
Finally, to dig a little deeper, we can click on the `tap` icon in the far right
column. This will take us to the live list of requests that match only this
endpoint. You'll see `Unknown` under the `GRPC status` column. This is because
the requests are failing with a
[gRPC status code 2](https://godoc.org/google.golang.org/grpc/codes#Code),
which is a common error response as you can see from
[the code][code]. Linkerd is aware of gRPC's response classification without any
other configuration!
{{< fig src="/images/debugging/web-tap.png" title="Tap" >}}
At this point, we have everything required to get the endpoint fixed and restore
the overall health of our applications.
[code]: https://github.com/BuoyantIO/emojivoto/blob/67faa83af33db647927946a672fc63ab7ce869aa/emojivoto-voting-svc/api/api.go#L21

View File

@ -0,0 +1,273 @@
+++
title = "Distributed tracing with Linkerd"
description = "Use Linkerd to help instrument your application with distributed tracing."
+++
Using distributed tracing in practice can be complex, for a high level
explanation of what you get and how it is done, we've assembled a [list of
myths](https://linkerd.io/2019/08/09/service-mesh-distributed-tracing-myths/).
This guide will walk you through configuring and enabling tracing for
[emojivoto](../../getting-started/#step-5-install-the-demo-app). Jump to the end
for some recommendations on the best way to make use of distributed tracing with
Linkerd.
To use distributed tracing, you'll need to:
- Install the Linkerd-Jaeger extension.
- Modify your application to emit spans.
In the case of emojivoto, once all these steps are complete there will be a
topology that looks like:
{{< fig src="/images/tracing/tracing-topology.svg"
title="Topology" >}}
## Prerequisites
- To use this guide, you'll need to have Linkerd installed on your cluster.
Follow the [Installing Linkerd Guide](../install/) if you haven't
already done this.
## Install the Linkerd-Jaeger extension
The first step of getting distributed tracing setup is installing the
Linkerd-Jaeger extension onto your cluster. This extension consists of a
collector, a Jaeger backend, and a Jaeger-injector. The collector consumes spans
emitted from the mesh and your applications and sends them to the Jaeger backend
which stores them and serves a dashboard to view them. The Jaeger-injector is
responsible for configuring the Linkerd proxies to emit spans.
To install the Linkerd-Jaeger extension, run the command:
```bash
linkerd jaeger install | kubectl apply -f -
```
You can verify that the Linkerd-Jaeger extension was installed correctly by
running:
```bash
linkerd jaeger check
```
## Install Emojivoto
Add emojivoto to your cluster and inject it with the Linkerd proxy:
```bash
linkerd inject https://run.linkerd.io/emojivoto.yml | kubectl apply -f -
```
Before moving onto the next step, make sure everything is up and running with
`kubectl`:
```bash
kubectl -n emojivoto rollout status deploy/web
```
## Modify the application
Unlike most features of a service mesh, distributed tracing requires modifying
the source of your application. Tracing needs some way to tie incoming requests
to your application together with outgoing requests to dependent services. To do
this, some headers are added to each request that contain a unique ID for the
trace. Linkerd uses the [b3
propagation](https://github.com/openzipkin/b3-propagation) format to tie these
things together.
We've already modified emojivoto to instrument its requests with this
information, this
[commit](https://github.com/BuoyantIO/emojivoto/commit/47a026c2e4085f4e536c2735f3ff3788b0870072)
shows how this was done. For most programming languages, it simply requires the
addition of a client library to take care of this. Emojivoto uses the OpenCensus
client, but others can be used.
To enable tracing in emojivoto, run:
```bash
kubectl -n emojivoto set env --all deploy OC_AGENT_HOST=collector.linkerd-jaeger:55678
```
This command will add an environment variable that enables the applications to
propagate context and emit spans.
## Explore Jaeger
With `vote-bot` starting traces for every request, spans should now be showing
up in Jaeger. To get to the UI, run:
```bash
linkerd jaeger dashboard
```
{{< fig src="/images/tracing/jaeger-empty.png"
title="Jaeger" >}}
You can search for any service in the dropdown and click Find Traces. `vote-bot`
is a great way to get started.
{{< fig src="/images/tracing/jaeger-search.png"
title="Search" >}}
Clicking on a specific trace will provide all the details, you'll be able to see
the spans for every proxy!
{{< fig src="/images/tracing/example-trace.png"
title="Search" >}}
There sure are a lot of `linkerd-proxy` spans in that output. Internally, the
proxy has a server and client side. When a request goes through the proxy, it is
received by the server and then issued by the client. For a single request that
goes between two meshed pods, there will be a total of 4 spans. Two will be on
the source side as the request traverses that proxy and two will be on the
destination side as the request is received by the remote proxy.
## Integration with the Dashboard
After having set up the Linkerd-Jaeger extension, as the proxy adds application
meta-data as trace attributes, users can directly jump into related resources
traces directly from the linkerd-web dashboard by clicking the Jaeger icon in
the Metrics Table, as shown below:
{{< fig src="/images/tracing/linkerd-jaeger-ui.png"
title="Linkerd-Jaeger" >}}
To obtain that functionality you need to install (or upgrade) the Linkerd-Viz
extension specifying the service exposing the Jaeger UI. By default, this would
be something like this:
```bash
linkerd viz install --set jaegerUrl=jaeger.linkerd-jaeger:16686
```
## Cleanup
To cleanup, uninstall the Linkerd-Jaeger extension along with emojivoto by running:
```bash
linkerd jaeger uninstall | kubectl delete -f -
kubectl delete ns emojivoto
```
## Bring your own Jaeger
If you have an existing Jaeger installation, you can configure the OpenCensus
collector to send traces to it instead of the Jaeger instance built into the
Linkerd-Jaeger extension.
```bash
linkerd jaeger install --set collector.jaegerAddr='http://my-jaeger-collector.my-jaeger-ns:14268/api/traces' | kubectl apply -f -
```
It is also possible to manually edit the OpenCensus configuration to have it
export to any backend which it supports. See the
[OpenCensus documentation](https://opencensus.io/service/exporters/) for a full
list.
## Troubleshooting
### I don't see any spans for the proxies
The Linkerd proxy uses the [b3
propagation](https://github.com/openzipkin/b3-propagation) format. Some client
libraries, such as Jaeger, use different formats by default. You'll want to
configure your client library to use the b3 format to have the proxies
participate in traces.
## Recommendations
### Ingress
The ingress is an especially important component for distributed tracing because
it creates the root span of each trace and is responsible for deciding if that
trace should be sampled or not. Having the ingress make all sampling decisions
ensures that either an entire trace is sampled or none of it is, and avoids
creating "partial traces".
Distributed tracing systems all rely on services to propagate metadata about the
current trace from requests that they receive to requests that they send. This
metadata, called the trace context, is usually encoded in one or more request
headers. There are many different trace context header formats and while we hope
that the ecosystem will eventually converge on open standards like [W3C
tracecontext](https://www.w3.org/TR/trace-context/), we only use the [b3
format](https://github.com/openzipkin/b3-propagation) today. Being one of the
earliest widely used formats, it has the widest support, especially among
ingresses like Nginx.
This reference architecture includes a simple Nginx config that samples 50% of
traces and emits trace data to the collector (using the Zipkin protocol). Any
ingress controller can be used here in place of Nginx as long as it:
- Supports probabilistic sampling
- Encodes trace context in the b3 format
- Emits spans in a protocol supported by the OpenCensus collector
If using helm to install ingress-nginx, you can configure tracing by using:
```yaml
controller:
config:
enable-opentracing: "true"
zipkin-collector-host: linkerd-collector.linkerd
```
### Client Library
While it is possible for services to manually propagate trace propagation
headers, it's usually much easier to use a library which does three things:
- Propagates the trace context from incoming request headers to outgoing request
headers
- Modifies the trace context (i.e. starts a new span)
- Transmits this data to a trace collector
We recommend using OpenCensus in your service and configuring it with:
- [b3 propagation](https://github.com/openzipkin/b3-propagation) (this is the
default)
- [the OpenCensus agent
exporter](https://opencensus.io/exporters/supported-exporters/go/ocagent/)
The OpenCensus agent exporter will export trace data to the OpenCensus collector
over a gRPC API. The details of how to configure OpenCensus will vary language
by language, but there are [guides for many popular
languages](https://opencensus.io/quickstart/). You can also see an end-to-end
example of this in Go with our example application,
[Emojivoto](https://github.com/adleong/emojivoto).
You may notice that the OpenCensus project is in maintenance mode and will
become part of [OpenTelemetry](https://opentelemetry.io/). Unfortunately,
OpenTelemetry is not yet production ready and so OpenCensus remains our
recommendation for the moment.
It is possible to use many other tracing client libraries as well. Just make
sure the b3 propagation format is being used and the client library can export
its spans in a format the collector has been configured to receive.
## Collector: OpenCensus
The OpenCensus collector receives trace data from the OpenCensus agent exporter
and potentially does translation and filtering before sending that data to
Jaeger. Having the OpenCensus exporter send to the OpenCensus collector gives us
a lot of flexibility: we can switch to any backend that OpenCensus supports
without needing to interrupt the application.
## Backend: Jaeger
Jaeger is one of the most widely used tracing backends and for good reason: it
is easy to use and does a great job of visualizing traces. However, [any backend
supported by OpenCensus](https://opencensus.io/service/exporters/) can be used
instead.
## Linkerd
If your application is injected with Linkerd, the Linkerd proxy will participate
in the traces and will also emit trace data to the OpenCensus collector. This
enriches the trace data and allows you to see exactly how much time requests are
spending in the proxy and on the wire.
While Linkerd can only actively participate in traces that use the b3
propagation format, Linkerd will always forward unknown request headers
transparently, which means it will never interfere with traces that use other
propagation formats.

View File

@ -0,0 +1,165 @@
+++
title = "Exporting Metrics"
description = "Integrate Linkerd's Prometheus with your existing metrics infrastructure."
aliases = [
"../prometheus/",
"../observability/prometheus/",
"../observability/exporting-metrics/"
]
+++
By design, Linkerd only keeps metrics data for a short, fixed window of time
(currently, 6 hours). This means that if Linkerd's metrics data is valuable to
you, you will probably want to export it into a full-fledged metrics store.
Internally, Linkerd stores its metrics in a Prometheus instance that runs as
part of the Viz extension. The following tutorial requires the viz extension
to be installed with prometheus enabled. There are several basic approaches
to exporting metrics data from Linkerd:
- [Federating data to your own Prometheus cluster](#federation)
- [Using a Prometheus integration](#integration)
- [Extracting data via Prometheus's APIs](#api)
- [Gather data from the proxies directly](#proxy)
## Using the Prometheus federation API {#federation}
If you are using Prometheus as your own metrics store, we recommend taking
advantage of Prometheus's *federation* API, which is designed exactly for the
use case of copying data from one Prometheus to another.
Simply add the following item to your `scrape_configs` in your Prometheus config
file (replace `{{.Namespace}}` with the namespace where the Linkerd Viz
extension is running):
```yaml
- job_name: 'linkerd'
kubernetes_sd_configs:
- role: pod
namespaces:
names: ['{{.Namespace}}']
relabel_configs:
- source_labels:
- __meta_kubernetes_pod_container_name
action: keep
regex: ^prometheus$
honor_labels: true
metrics_path: '/federate'
params:
'match[]':
- '{job="linkerd-proxy"}'
- '{job="linkerd-controller"}'
```
Alternatively, if you prefer to use Prometheus' ServiceMonitors to configure
your Prometheus, you can use this ServiceMonitor YAML (replace `{{.Namespace}}`
with the namespace where Linkerd Viz extension is running):
```yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
k8s-app: linkerd-prometheus
release: monitoring
name: linkerd-federate
namespace: {{.Namespace}}
spec:
endpoints:
- interval: 30s
scrapeTimeout: 30s
params:
match[]:
- '{job="linkerd-proxy"}'
- '{job="linkerd-controller"}'
path: /federate
port: admin-http
honorLabels: true
relabelings:
- action: keep
regex: '^prometheus$'
sourceLabels:
- '__meta_kubernetes_pod_container_name'
jobLabel: app
namespaceSelector:
matchNames:
- {{.Namespace}}
selector:
matchLabels:
component: prometheus
```
That's it! Your Prometheus cluster is now configured to federate Linkerd's
metrics from Linkerd's internal Prometheus instance.
Once the metrics are in your Prometheus, Linkerd's proxy metrics will have the
label `job="linkerd-proxy"` and Linkerd's control plane metrics will have the
label `job="linkerd-controller"`. For more information on specific metric and
label definitions, have a look at [Proxy Metrics](../../reference/proxy-metrics/).
For more information on Prometheus' `/federate` endpoint, have a look at the
[Prometheus federation docs](https://prometheus.io/docs/prometheus/latest/federation/).
## Using a Prometheus integration {#integration}
If you are not using Prometheus as your own long-term data store, you may be
able to leverage one of Prometheus's [many
integrations](https://prometheus.io/docs/operating/integrations/) to
automatically extract data from Linkerd's Prometheus instance into the data
store of your choice. Please refer to the Prometheus documentation for details.
## Extracting data via Prometheus's APIs {#api}
If neither Prometheus federation nor Prometheus integrations are options for
you, it is possible to call Prometheus's APIs to extract data from Linkerd.
For example, you can call the federation API directly via a command like:
```bash
curl -G \
--data-urlencode 'match[]={job="linkerd-proxy"}' \
--data-urlencode 'match[]={job="linkerd-controller"}' \
http://prometheus.linkerd-viz.svc.cluster.local:9090/federate
```
{{< note >}}
If your data store is outside the Kubernetes cluster, it is likely that
you'll want to set up
[ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)
at a domain name of your choice with authentication.
{{< /note >}}
Similar to the `/federate` API, Prometheus provides a JSON query API to
retrieve all metrics:
```bash
curl http://prometheus.linkerd-viz.svc.cluster.local:9090/api/v1/query?query=request_total
```
## Gathering data from the Linkerd proxies directly {#proxy}
Finally, if you want to avoid Linkerd's Prometheus entirely, you can query the
Linkerd proxies directly on their `/metrics` endpoint.
For example, to view `/metrics` from a single Linkerd proxy, running in the
`linkerd` namespace:
```bash
kubectl -n linkerd port-forward \
$(kubectl -n linkerd get pods \
-l linkerd.io/control-plane-ns=linkerd \
-o jsonpath='{.items[0].metadata.name}') \
4191:4191
```
and then:
```bash
curl localhost:4191/metrics
```
Alternatively, `linkerd diagnostics proxy-metrics` can be used to retrieve
proxy metrics for a given workload.

View File

@ -0,0 +1,248 @@
+++
title = "Exposing the Dashboard"
description = "Make it easy for others to access Linkerd and Grafana dashboards without the CLI."
aliases = [
"../dns-rebinding/",
]
+++
Instead of using `linkerd viz dashboard` every time you'd like to see what's
going on, you can expose the dashboard via an ingress. This will also expose
Grafana.
{{< pagetoc >}}
## Nginx
### Nginx with basic auth
A sample ingress definition is:
```yaml
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: web-ingress-auth
namespace: linkerd-viz
data:
auth: YWRtaW46JGFwcjEkbjdDdTZnSGwkRTQ3b2dmN0NPOE5SWWpFakJPa1dNLgoK
---
# apiVersion: networking.k8s.io/v1beta1 # for k8s < v1.19
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: linkerd-viz
annotations:
nginx.ingress.kubernetes.io/upstream-vhost: $service_name.$namespace.svc.cluster.local:8084
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header Origin "";
proxy_hide_header l5d-remote-ip;
proxy_hide_header l5d-server-id;
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: web-ingress-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required'
spec:
ingressClassName: nginx
rules:
- host: dashboard.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 8084
```
This exposes the dashboard at `dashboard.example.com` and protects it with basic
auth using admin/admin. Take a look at the [ingress-nginx][nginx-auth]
documentation for details on how to change the username and password.
### Nginx with oauth2-proxy
A more secure alternative to basic auth is using an authentication proxy, such
as [oauth2-proxy](https://oauth2-proxy.github.io/oauth2-proxy/).
For reference on how to deploy and configure oauth2-proxy in kubernetes, see
this [blog post by Don
Bowman](https://blog.donbowman.ca/2019/02/14/using-single-sign-on-oauth2-across-many-sites-in-kubernetes/).
tl;dr: If you deploy oauth2-proxy via the [helm
chart](https://github.com/helm/charts/tree/master/stable/oauth2-proxy), the
following values are required:
```yaml
config:
existingSecret: oauth2-proxy
configFile: |-
email_domains = [ "example.com" ]
upstreams = [ "file:///dev/null" ]
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
path: /oauth2
ingress:
hosts:
- linkerd.example.com
```
Where the `oauth2-proxy` secret would contain the required [oauth2
config](https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/oauth_provider)
such as, `client-id` `client-secret` and `cookie-secret`.
Once setup, a sample ingress would be:
```yaml
# apiVersion: networking.k8s.io/v1beta1 # for k8s < v1.19
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
namespace: linkerd-viz
annotations:
nginx.ingress.kubernetes.io/upstream-vhost: $service_name.$namespace.svc.cluster.local:8084
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header Origin "";
proxy_hide_header l5d-remote-ip;
proxy_hide_header l5d-server-id;
nginx.ingress.kubernetes.io/auth-signin: https://$host/oauth2/start?rd=$escaped_request_uri
nginx.ingress.kubernetes.io/auth-url: https://$host/oauth2/auth
spec:
ingressClassName: nginx
rules:
- host: linkerd.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 8084
```
## Traefik
A sample ingress definition is:
```yaml
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: web-ingress-auth
namespace: linkerd-viz
data:
auth: YWRtaW46JGFwcjEkbjdDdTZnSGwkRTQ3b2dmN0NPOE5SWWpFakJPa1dNLgoK
---
# apiVersion: networking.k8s.io/v1beta1 # for k8s < v1.19
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: linkerd-viz
annotations:
ingress.kubernetes.io/custom-request-headers: l5d-dst-override:web.linkerd-viz.svc.cluster.local:8084
traefik.ingress.kubernetes.io/auth-type: basic
traefik.ingress.kubernetes.io/auth-secret: web-ingress-auth
spec:
ingressClassName: traefik
rules:
- host: dashboard.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 8084
```
This exposes the dashboard at `dashboard.example.com` and protects it with basic
auth using admin/admin. Take a look at the [Traefik][traefik-auth] documentation
for details on how to change the username and password.
## Ambassador
Ambassador works by defining a [mapping
](https://www.getambassador.io/docs/latest/topics/using/intro-mappings/) as an
annotation on a service.
The below annotation exposes the dashboard at `dashboard.example.com`.
```yaml
annotations:
getambassador.io/config: |-
---
apiVersion: getambassador.io/v2
kind: Mapping
name: web-mapping
host: dashboard.example.com
prefix: /
host_rewrite: web.linkerd-viz.svc.cluster.local:8084
service: web.linkerd-viz.svc.cluster.local:8084
```
## DNS Rebinding Protection
To prevent [DNS-rebinding](https://en.wikipedia.org/wiki/DNS_rebinding) attacks,
the dashboard rejects any request whose `Host` header is not `localhost`,
`127.0.0.1` or the service name `web.linkerd-viz.svc`.
Note that this protection also covers the [Grafana
dashboard](../../reference/architecture/#grafana).
The ingress-nginx config above uses the
`nginx.ingress.kubernetes.io/upstream-vhost` annotation to properly set the
upstream `Host` header. Traefik on the other hand doesn't offer that option, so
you'll have to manually set the required `Host` as explained below.
### Tweaking Host Requirement
If your HTTP client (Ingress or otherwise) doesn't allow to rewrite the `Host`
header, you can change the validation regexp that the dashboard server uses,
which is fed into the `web` deployment via the `enforced-host` container
argument.
If you're managing Linkerd with Helm, then you can set the host using the
`enforcedHostRegexp` value.
Another way of doing that is through Kustomize, as explained in [Customizing
Installation](../customize-install/), using an overlay like this one:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
template:
spec:
containers:
- name: web
args:
- -linkerd-controller-api-addr=linkerd-controller-api.linkerd.svc.cluster.local:8085
- -linkerd-metrics-api-addr=metrics-api.linkerd-viz.svc.cluster.local:8085
- -cluster-domain=cluster.local
- -grafana-addr=grafana.linkerd-viz.svc.cluster.local:3000
- -controller-namespace=linkerd
- -viz-namespace=linkerd-viz
- -log-level=info
- -enforced-host=^dashboard\.example\.com$
```
If you want to completely disable the `Host` header check, simply use a
catch-all regexp `.*` for `-enforced-host`.
[nginx-auth]:
https://github.com/kubernetes/ingress-nginx/blob/master/docs/examples/auth/basic/README.md
[traefik-auth]: https://docs.traefik.io/middlewares/basicauth/

View File

@ -0,0 +1,77 @@
+++
title = "Using extensions"
description = "Add functionality to Linkerd with optional extensions."
+++
Linkerd extensions are components which can be added to a Linkerd installation
to enable additional functionality. By default, the following extensions are
available:
* [viz](../../features/dashboard/): Metrics and visibility features
* [jaeger](../distributed-tracing/): Distributed tracing
* [multicluster](../multicluster/): Cross-cluster routing
But other extensions are also possible. Read on for more!
## Installing extensions
Before installing any extensions, make sure that you have already [installed
Linkerd](../install/) and validated your cluster with `linkerd check`.
Then, you can install the extension with the extension's `install` command. For
example, to install the `viz` extension, you can use:
```bash
linkerd viz install | kubectl apply -f -
```
For built-in extensions, such as `viz`, `jaeger`, and `multicluster`, that's
all you need to do. Of course, these extensions can also be installed by with
Helm by installing that extension's Helm chart.
## Installing third-party extensions
Third-party extensions are also possible, with one additional step: you must
download the extension's CLI and put it in your path. This will allow you to
invoke the extension CLI through the Linkerd CLI: any invocation of `linkerd
foo` will automatically invoke the `linkerd-foo` binary, if it is found on your
path.
For example, [Buoyant Cloud](https://buoyant.io/cloud) is a free, hosted
metrics dashboard for Linkerd that can be installed alongside the `viz`
extension, but doesn't require it. To install this extension, run:
```bash
## optional
curl -sL buoyant.cloud/install | sh
linkerd buoyant install | kubectl apply -f - # hosted metrics dashboard
```
Once the extension is installed, run `linkerd check` to ensure Linkerd and all
installed extensions are healthy or run `linkerd foo check` to perform health
checks for that extension only.
## Listing extensions
Every extension creates a Kubernetes namespace with the `linkerd.io/extension`
label. Thus, you can list all extensions installed on your cluster by running:
```bash
kubectl get ns -l linkerd.io/extension
```
## Upgrading extensions
Unless otherwise stated, extensions do not persist any configuration in the
cluster. To upgrade an extension, run the install again with a newer version
of the extension CLI or with a different set of configuration flags.
## Uninstalling extensions
All extensions have an `uninstall` command that should be used to gracefully
clean up all resources owned by an extension. For example, to uninstall the
foo extension, run:
```bash
linkerd foo uninstall | kubectl delete -f -
```

View File

@ -0,0 +1,160 @@
+++
title = "Bringing your own Prometheus"
description = "Use an existing Prometheus instance with Linkerd."
+++
Even though [the linkerd-viz extension](../../features/dashboard/) comes with
its own Prometheus instance, there can be cases where using an external
instance makes more sense for various reasons.
This tutorial shows how to configure an external Prometheus instance to scrape both
the control plane as well as the proxy's metrics in a format that is consumable
both by a user as well as Linkerd control plane components like web, etc.
There are two important points to tackle here.
- Configuring external Prometheus instance to get the Linkerd metrics.
- Configuring the linkerd-viz extension to use that Prometheus.
## Prometheus Scrape Configuration
The following scrape configuration has to be applied to the external
Prometheus instance.
{{< note >}}
The below scrape configuration is a [subset of the full `linkerd-prometheus`
scrape
configuration](https://github.com/linkerd/linkerd2/blob/main/viz/charts/linkerd-viz/templates/prometheus.yaml#L47-L151).
{{< /note >}}
Before applying, it is important to replace templated values (present in `{{}}`)
with direct values for the below configuration to work.
```yaml
- job_name: 'linkerd-controller'
kubernetes_sd_configs:
- role: pod
namespaces:
names:
- '{{.Values.linkerdNamespace}}'
- '{{.Values.namespace}}'
relabel_configs:
- source_labels:
- __meta_kubernetes_pod_container_port_name
action: keep
regex: admin-http
- source_labels: [__meta_kubernetes_pod_container_name]
action: replace
target_label: component
- job_name: 'linkerd-service-mirror'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels:
- __meta_kubernetes_pod_label_linkerd_io_control_plane_component
- __meta_kubernetes_pod_container_port_name
action: keep
regex: linkerd-service-mirror;admin-http$
- source_labels: [__meta_kubernetes_pod_container_name]
action: replace
target_label: component
- job_name: 'linkerd-proxy'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels:
- __meta_kubernetes_pod_container_name
- __meta_kubernetes_pod_container_port_name
- __meta_kubernetes_pod_label_linkerd_io_control_plane_ns
action: keep
regex: ^{{default .Values.proxyContainerName "linkerd-proxy" .Values.proxyContainerName}};linkerd-admin;{{.Values.linkerdNamespace}}$
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: pod
# special case k8s' "job" label, to not interfere with prometheus' "job"
# label
# __meta_kubernetes_pod_label_linkerd_io_proxy_job=foo =>
# k8s_job=foo
- source_labels: [__meta_kubernetes_pod_label_linkerd_io_proxy_job]
action: replace
target_label: k8s_job
# drop __meta_kubernetes_pod_label_linkerd_io_proxy_job
- action: labeldrop
regex: __meta_kubernetes_pod_label_linkerd_io_proxy_job
# __meta_kubernetes_pod_label_linkerd_io_proxy_deployment=foo =>
# deployment=foo
- action: labelmap
regex: __meta_kubernetes_pod_label_linkerd_io_proxy_(.+)
# drop all labels that we just made copies of in the previous labelmap
- action: labeldrop
regex: __meta_kubernetes_pod_label_linkerd_io_proxy_(.+)
# __meta_kubernetes_pod_label_linkerd_io_foo=bar =>
# foo=bar
- action: labelmap
regex: __meta_kubernetes_pod_label_linkerd_io_(.+)
# Copy all pod labels to tmp labels
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
replacement: __tmp_pod_label_$1
# Take `linkerd_io_` prefixed labels and copy them without the prefix
- action: labelmap
regex: __tmp_pod_label_linkerd_io_(.+)
replacement: __tmp_pod_label_$1
# Drop the `linkerd_io_` originals
- action: labeldrop
regex: __tmp_pod_label_linkerd_io_(.+)
# Copy tmp labels into real labels
- action: labelmap
regex: __tmp_pod_label_(.+)
```
The running configuration of the builtin prometheus can be used as a reference.
```bash
kubectl -n linkerd-viz get configmap prometheus-config -o yaml
```
## Linkerd-Viz Extension Configuration
Linkerd's viz extension components like `metrics-api`, etc depend
on the Prometheus instance to power the dashboard and CLI.
The `prometheusUrl` field gives you a single place through
which all these components can be configured to an external Prometheus URL.
This is allowed both through the CLI and Helm.
### CLI
This can be done by passing a file with the above field to the `values` flag,
which is available through `linkerd viz install` command.
```yaml
prometheusUrl: existing-prometheus.xyz:9090
```
Once applied, this configuration is not persistent across installs.
The same has to be passed again by the user during re-installs, upgrades, etc.
When using an external Prometheus and configuring the `prometheusUrl`
field, Linkerd's Prometheus will still be included in installation.
If you wish to disable it, be sure to include the
following configuration as well:
```yaml
prometheus:
enabled: false
```
### Helm
The same configuration can be applied through `values.yaml` when using Helm.
Once applied, Helm makes sure that the configuration is
persistent across upgrades.
More information on installation through Helm can be found
[here](../install-helm/)

View File

@ -0,0 +1,197 @@
+++
title = "Injecting Faults"
description = "Practice chaos engineering by injecting faults into services with Linkerd."
+++
It is easy to inject failures into applications by using the [Traffic Split
API](https://github.com/deislabs/smi-spec/blob/master/traffic-split.md) of the
[Service Mesh Interface](https://smi-spec.io/). TrafficSplit allows you to
redirect a percentage of traffic to a specific backend. This backend is
completely flexible and can return whatever responses you want - 500s, timeouts
or even crazy payloads.
The [books demo](../books/) is a great way to show off this behavior. The
overall topology looks like:
{{< fig src="/images/books/topology.png" title="Topology" >}}
In this guide, you will split some of the requests from `webapp` to `books`.
Most requests will end up at the correct `books` destination, however some of
them will be redirected to a faulty backend. This backend will return 500s for
every request and inject faults into the `webapp` service. No code changes are
required and as this method is configuration driven, it is a process that can be
added to integration tests and CI pipelines. If you are really living the chaos
engineering lifestyle, fault injection could even be used in production.
## Prerequisites
To use this guide, you'll need to have Linkerd installed on your cluster, along
with its Viz extension. Follow the [Installing Linkerd Guide](../install/)
if you haven't already done this.
## Setup the service
First, add the [books](../books/) sample application to your cluster:
```bash
kubectl create ns booksapp && \
linkerd inject https://run.linkerd.io/booksapp.yml | \
kubectl -n booksapp apply -f -
```
As this manifest is used as a demo elsewhere, it has been configured with an
error rate. To show how fault injection works, the error rate needs to be
removed so that there is a reliable baseline. To increase success rate for
booksapp to 100%, run:
```bash
kubectl -n booksapp patch deploy authors \
--type='json' \
-p='[{"op":"remove", "path":"/spec/template/spec/containers/0/env/2"}]'
```
After a little while, the stats will show 100% success rate. You can verify this
by running:
```bash
linkerd viz -n booksapp stat deploy
```
The output will end up looking at little like:
```bash
NAME MESHED SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 TCP_CONN
authors 1/1 100.00% 7.1rps 4ms 26ms 33ms 6
books 1/1 100.00% 8.6rps 6ms 73ms 95ms 6
traffic 1/1 - - - - - -
webapp 3/3 100.00% 7.9rps 20ms 76ms 95ms 9
```
## Create the faulty backend
Injecting faults into booksapp requires a service that is configured to return
errors. To do this, you can start NGINX and configure it to return 500s by
running:
```bash
cat <<EOF | linkerd inject - | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: error-injector
namespace: booksapp
data:
nginx.conf: |-
events {}
http {
server {
listen 8080;
location / {
return 500;
}
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: error-injector
namespace: booksapp
labels:
app: error-injector
spec:
selector:
matchLabels:
app: error-injector
replicas: 1
template:
metadata:
labels:
app: error-injector
spec:
containers:
- name: nginx
image: nginx:alpine
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
volumes:
- name: nginx-config
configMap:
name: error-injector
---
apiVersion: v1
kind: Service
metadata:
name: error-injector
namespace: booksapp
spec:
ports:
- name: service
port: 8080
selector:
app: error-injector
EOF
```
## Inject faults
With booksapp and NGINX running, it is now time to partially split the traffic
between an existing backend, `books`, and the newly created
`error-injector`. This is done by adding a
[TrafficSplit](https://github.com/deislabs/smi-spec/blob/master/traffic-split.md)
configuration to your cluster:
```bash
cat <<EOF | kubectl apply -f -
apiVersion: split.smi-spec.io/v1alpha1
kind: TrafficSplit
metadata:
name: error-split
namespace: booksapp
spec:
service: books
backends:
- service: books
weight: 900m
- service: error-injector
weight: 100m
EOF
```
When Linkerd sees traffic going to the `books` service, it will send 9/10
requests to the original service and 1/10 to the error injector. You can see
what this looks like by running `stat` and filtering explicitly to just the
requests from `webapp`:
```bash
linkerd viz -n booksapp routes deploy/webapp --to service/books
```
Unlike the previous `stat` command which only looks at the requests received by
servers, this `routes` command filters to all the requests being issued by
`webapp` destined for the `books` service itself. The output should show a 90%
success rate:
```bash
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
[DEFAULT] books 90.08% 2.0rps 5ms 69ms 94ms
```
{{< note >}}
In this instance, you are looking at the *service* instead of the deployment. If
you were to run this command and look at `deploy/books`, the success rate would
still be 100%. The reason for this is that `error-injector` is a completely
separate deployment and traffic is being shifted at the service level. The
requests never reach the `books` pods and are instead rerouted to the error
injector's pods.
{{< /note >}}
## Cleanup
To remove everything in this guide from your cluster, run:
```bash
kubectl delete ns booksapp
```

View File

@ -0,0 +1,86 @@
+++
title = "Generating your own mTLS root certificates"
description = "Generate your own mTLS root certificate instead of letting Linkerd do it for you."
+++
In order to support [mTLS connections between meshed
pods](../../features/automatic-mtls/), Linkerd needs a trust anchor certificate and
an issuer certificate with its corresponding key.
When installing with `linkerd install`, these certificates are automatically
generated. Alternatively, you can specify your own with the `--identity-*` flags
(see the [linkerd install reference](../../reference/cli/install/)).
On the other hand when using Helm to install Linkerd, it's not possible to
automatically generate them and you're required to provide them.
You can generate these certificates using a tool like openssl or
[step](https://smallstep.com/cli/). All certificates must use the ECDSA P-256
algorithm which is the default for `step`. To generate ECDSA P-256 certificates
with openssl, you can use the `openssl ecparam -name prime256v1` command. In
this tutorial, we'll walk you through how to to use the `step` CLI to do this.
## Generating the certificates with `step`
### Trust anchor certificate
First generate the root certificate with its private key (using `step` version
0.10.1):
```bash
step certificate create root.linkerd.cluster.local ca.crt ca.key \
--profile root-ca --no-password --insecure
```
This generates the `ca.crt` and `ca.key` files. The `ca.crt` file is what you
need to pass to the `--identity-trust-anchors-file` option when installing
Linkerd with the CLI, and the `identityTrustAnchorsPEM` value when installing
Linkerd with Helm.
Note we use `--no-password --insecure` to avoid encrypting those files with a
passphrase.
For a longer-lived trust anchor certificate, pass the `--not-after` argument
to the step command with the desired value (e.g. `--not-after=87600h`).
### Issuer certificate and key
Then generate the intermediate certificate and key pair that will be used to
sign the Linkerd proxies' CSR.
```bash
step certificate create identity.linkerd.cluster.local issuer.crt issuer.key \
--profile intermediate-ca --not-after 8760h --no-password --insecure \
--ca ca.crt --ca-key ca.key
```
This will generate the `issuer.crt` and `issuer.key` files.
## Passing the certificates to Linkerd
You can finally provide these files when installing Linkerd with the CLI:
```bash
linkerd install \
--identity-trust-anchors-file ca.crt \
--identity-issuer-certificate-file issuer.crt \
--identity-issuer-key-file issuer.key \
| kubectl apply -f -
```
Or when installing with Helm:
```bash
helm install linkerd2 \
--set-file identityTrustAnchorsPEM=ca.crt \
--set-file identity.issuer.tls.crtPEM=issuer.crt \
--set-file identity.issuer.tls.keyPEM=issuer.key \
--set identity.issuer.crtExpiry=$(date -d '+8760 hour' +"%Y-%m-%dT%H:%M:%SZ") \
linkerd/linkerd2
```
{{< note >}}
For Helm versions < v3, `--name` flag has to specifically be passed.
In Helm v3, It has been deprecated, and is the first argument as
specified above.
{{< /note >}}

View File

@ -0,0 +1,93 @@
+++
title = "Getting Per-Route Metrics"
description = "Configure per-route metrics for your application."
+++
To get per-route metrics, you must first create a
[service profile](../../features/service-profiles/). Once a service
profile has been created, Linkerd will add labels to the Prometheus metrics that
associate a specific request to a specific route.
For a tutorial that shows this functionality off, check out the
[books demo](../books/#service-profiles).
You can view per-route metrics in the CLI by running `linkerd viz routes`:
```bash
$ linkerd viz routes svc/webapp
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
GET / webapp 100.00% 0.6rps 25ms 30ms 30ms
GET /authors/{id} webapp 100.00% 0.6rps 22ms 29ms 30ms
GET /books/{id} webapp 100.00% 1.2rps 18ms 29ms 30ms
POST /authors webapp 100.00% 0.6rps 32ms 46ms 49ms
POST /authors/{id}/delete webapp 100.00% 0.6rps 45ms 87ms 98ms
POST /authors/{id}/edit webapp 0.00% 0.0rps 0ms 0ms 0ms
POST /books webapp 50.76% 2.2rps 26ms 38ms 40ms
POST /books/{id}/delete webapp 100.00% 0.6rps 24ms 29ms 30ms
POST /books/{id}/edit webapp 60.71% 0.9rps 75ms 98ms 100ms
[DEFAULT] webapp 0.00% 0.0rps 0ms 0ms 0ms
```
The `[DEFAULT]` route is a catch-all, anything that does not match the regexes
specified in your service profile will end up there.
It is also possible to look the metrics up by other resource types, such as:
```bash
$ linkerd viz routes deploy/webapp
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
[DEFAULT] kubernetes 0.00% 0.0rps 0ms 0ms 0ms
GET / webapp 100.00% 0.5rps 27ms 38ms 40ms
GET /authors/{id} webapp 100.00% 0.6rps 18ms 29ms 30ms
GET /books/{id} webapp 100.00% 1.1rps 17ms 28ms 30ms
POST /authors webapp 100.00% 0.5rps 25ms 30ms 30ms
POST /authors/{id}/delete webapp 100.00% 0.5rps 58ms 96ms 99ms
POST /authors/{id}/edit webapp 0.00% 0.0rps 0ms 0ms 0ms
POST /books webapp 45.58% 2.5rps 33ms 82ms 97ms
POST /books/{id}/delete webapp 100.00% 0.6rps 33ms 48ms 50ms
POST /books/{id}/edit webapp 55.36% 0.9rps 79ms 160ms 192ms
[DEFAULT] webapp 0.00% 0.0rps 0ms 0ms 0ms
```
Then, it is possible to filter all the way down to requests going from a
specific resource to other services:
```bash
$ linkerd viz routes deploy/webapp --to svc/books
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
DELETE /books/{id}.json books 100.00% 0.5rps 18ms 29ms 30ms
GET /books.json books 100.00% 1.1rps 7ms 12ms 18ms
GET /books/{id}.json books 100.00% 2.5rps 6ms 10ms 10ms
POST /books.json books 52.24% 2.2rps 23ms 34ms 39ms
PUT /books/{id}.json books 41.98% 1.4rps 73ms 97ms 99ms
[DEFAULT] books 0.00% 0.0rps 0ms 0ms 0ms
```
## Troubleshooting
If you're not seeing any metrics, there are two likely culprits. In both cases,
`linkerd viz tap` can be used to understand the problem. For the resource that
the service points to, run:
```bash
linkerd viz tap deploy/webapp -o wide | grep req
```
A sample output is:
```bash
req id=3:1 proxy=in src=10.4.0.14:58562 dst=10.4.1.4:7000 tls=disabled :method=POST :authority=webapp:7000 :path=/books/24783/edit src_res=deploy/traffic src_ns=default dst_res=deploy/webapp dst_ns=default rt_route=POST /books/{id}/edit
```
This will select only the requests observed and show the `:authority` and
`rt_route` that was used for each request.
- Linkerd discovers the right service profile to use via `:authority` or
`Host` headers. The name of your service profile must match these headers.
There are many reasons why these would not match, see
[ingress](../../features/ingress/) for one reason. Another would be clients that
use IPs directly such as Prometheus.
- Getting regexes to match can be tough and the ordering is important. Pay
attention to `rt_route`. If it is missing entirely, compare the `:path` to
the regex you'd like for it to match, and use a
[tester](https://regex101.com/) with the Golang flavor of regex.

View File

@ -0,0 +1,534 @@
+++
title = "Using GitOps with Linkerd with Argo CD"
description = "Use Argo CD to manage Linkerd installation and upgrade lifecycle."
+++
GitOps is an approach to automate the management and delivery of your Kubernetes
infrastructure and applications using Git as a single source of truth. It
usually utilizes some software agents to detect and reconcile any divergence
between version-controlled artifacts in Git with what's running in a cluster.
This guide will show you how to set up
[Argo CD](https://argoproj.github.io/argo-cd/) to manage the installation and
upgrade of Linkerd using a GitOps workflow.
Specifically, this guide provides instructions on how to securely generate and
manage Linkerd's mTLS private keys and certificates using
[Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets) and
[cert-manager](https://cert-manager.io). It will also show you how to integrate
the [auto proxy injection](../../features/proxy-injection/) feature into your
workflow. Finally, this guide conclude with steps to upgrade Linkerd to a newer
version following a GitOps workflow.
{{< fig alt="Linkerd GitOps workflow"
title="Linkerd GitOps workflow"
src="/images/gitops/architecture.png" >}}
The software and tools used in this guide are selected for demonstration
purposes only. Feel free to choose others that are most suited for your
requirements.
You will need to clone this
[example repository](https://github.com/linkerd/linkerd-examples) to your local
machine and replicate it in your Kubernetes cluster following the steps defined
in the next section.
## Set up the repositories
Clone the example repository to your local machine:
```sh
git clone https://github.com/linkerd/linkerd-examples.git
```
This repository will be used to demonstrate Git operations like `add`, `commit`
and `push` later in this guide.
Add a new remote endpoint to the repository to point to the in-cluster Git
server, which will be set up in the next section:
```sh
cd linkerd-examples
git remote add git-server git://localhost/linkerd-examples.git
```
{{< note >}}
To simplify the steps in this guide, we will be interacting with the in-cluster
Git server via port-forwarding. Hence, the remote endpoint that we just created
targets your localhost.
{{< /note >}}
Deploy the Git server to the `scm` namespace in your cluster:
```sh
kubectl apply -f gitops/resources/git-server.yaml
```
Later in this guide, Argo CD will be configured to watch the repositories hosted
by this Git server.
{{< note >}}
This Git server is configured to run as a
[daemon](https://git-scm.com/book/en/v2/Git-on-the-Server-Git-Daemon) over the
`git` protocol, with unauthenticated access to the Git data. This setup is not
recommended for production use.
{{< /note >}}
Confirm that the Git server is healthy:
```sh
kubectl -n scm rollout status deploy/git-server
```
Clone the example repository to your in-cluster Git server:
```sh
git_server=`kubectl -n scm get po -l app=git-server -oname | awk -F/ '{ print $2 }'`
kubectl -n scm exec "${git_server}" -- \
git clone --bare https://github.com/linkerd/linkerd-examples.git
```
Confirm that the remote repository is successfully cloned:
```sh
kubectl -n scm exec "${git_server}" -- ls -al /git/linkerd-examples.git
```
Confirm that you can push from the local repository to the remote repository
via port-forwarding:
```sh
kubectl -n scm port-forward "${git_server}" 9418 &
git push git-server master
```
## Deploy Argo CD
Install Argo CD:
```sh
kubectl create ns argocd
kubectl -n argocd apply -f \
https://raw.githubusercontent.com/argoproj/argo-cd/v1.6.1/manifests/install.yaml
```
Confirm that all the pods are ready:
```sh
for deploy in "application-controller" "dex-server" "redis" "repo-server" "server"; \
do kubectl -n argocd rollout status deploy/argocd-${deploy}; \
done
```
Use port-forward to access the Argo CD dashboard:
```sh
kubectl -n argocd port-forward svc/argocd-server 8080:443 \
> /dev/null 2>&1 &
```
The Argo CD dashboard is now accessible at
[https://localhost:8080](https://localhost:8080/), using the default `admin`
username and
[password](https://argoproj.github.io/argo-cd/getting_started/#4-login-using-the-cli).
{{< note >}}
The default admin password is the auto-generated name of the Argo CD API server
pod. You can use the `argocd account update-password` command to change it.
{{< /note >}}
Authenticate the Argo CD CLI:
```sh
argocd_server=`kubectl -n argocd get pods -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f 2`
argocd login 127.0.0.1:8080 \
--username=admin \
--password="${argocd_server}" \
--insecure
```
## Configure project access and permissions
Set up the `demo`
[project](https://argoproj.github.io/argo-cd/user-guide/projects/) to group our
[applications](https://argoproj.github.io/argo-cd/operator-manual/declarative-setup/#applications):
```sh
kubectl apply -f gitops/project.yaml
```
This project defines the list of permitted resource kinds and target clusters
that our applications can work with.
Confirm that the project is deployed correctly:
```sh
argocd proj get demo
```
On the dashboard:
{{< fig alt="New project in Argo CD dashboard"
title="New project in Argo CD dashboard"
src="/images/gitops/dashboard-project.png" >}}
### Deploy the applications
Deploy the `main` application which serves as the "parent" application that of
all the other applications:
```sh
kubectl apply -f gitops/main.yaml
```
{{< note >}}
The "app of apps" pattern is commonly used in Argo CD workflows to bootstrap
applications. See the Argo CD documentation for more
[information](https://argoproj.github.io/argo-cd/operator-manual/cluster-bootstrapping/#app-of-apps-pattern).
{{< /note >}}
Confirm that the `main` application is deployed successfully:
```sh
argocd app get main
```
Sync the `main` application:
```sh
argocd app sync main
```
{{< fig alt="Synchronize the main application"
title="Synchronize the main application"
src="/images/gitops/dashboard-applications-main-sync.png" >}}
Notice that only the `main` application is synchronized.
Next, we will synchronize the remaining applications individually.
### Deploy cert-manager
Synchronize the `cert-manager` application:
```sh
argocd app sync cert-manager
```
{{< note >}}
This guide uses cert-manager 0.15.0 due to an issue with cert-manager 0.16.0
and kubectl <1.19 and Helm 3.2, which Argo CD uses. See the upgrade notes
[here](https://cert-manager.io/docs/installation/upgrading/upgrading-0.15-0.16/#helm).
{{< /note >}}
Confirm that cert-manager is running:
```sh
for deploy in "cert-manager" "cert-manager-cainjector" "cert-manager-webhook"; \
do kubectl -n cert-manager rollout status deploy/${deploy}; \
done
```
{{< fig alt="Synchronize the cert-manager application"
title="Synchronize the cert-manager application"
center="true"
src="/images/gitops/dashboard-cert-manager-sync.png" >}}
### Deploy Sealed Secrets
Synchronize the `sealed-secrets` application:
```sh
argocd app sync sealed-secrets
```
Confirm that sealed-secrets is running:
```sh
kubectl -n kube-system rollout status deploy/sealed-secrets
```
{{< fig alt="Synchronize the sealed-secrets application"
title="Synchronize the sealed-secrets application"
center="true"
src="/images/gitops/dashboard-sealed-secrets-sync.png" >}}
### Create mTLS trust anchor
Before proceeding with deploying Linkerd, we will need to create the mTLS trust
anchor. Then we will also set up the `linkerd-bootstrap` application to manage
the trust anchor certificate.
Create a new mTLS trust anchor private key and certificate:
```sh
step certificate create root.linkerd.cluster.local sample-trust.crt sample-trust.key \
--profile root-ca \
--no-password \
--not-after 43800h \
--insecure
```
Confirm the details (encryption algorithm, expiry date, SAN etc.) of the new
trust anchor:
```sh
step certificate inspect sample-trust.crt
```
Create a `SealedSecret` resource to store the encrypted trust anchor:
```sh
kubectl -n linkerd create secret tls linkerd-trust-anchor \
--cert sample-trust.crt \
--key sample-trust.key \
--dry-run=client -oyaml | \
kubeseal --controller-name=sealed-secrets -oyaml - | \
kubectl patch -f - \
-p '{"spec": {"template": {"type":"kubernetes.io/tls", "metadata": {"labels": {"linkerd.io/control-plane-component":"identity", "linkerd.io/control-plane-ns":"linkerd"}, "annotations": {"linkerd.io/created-by":"linkerd/cli stable-2.8.1", "linkerd.io/identity-issuer-expiry":"2021-07-19T20:51:01Z"}}}}}' \
--dry-run=client \
--type=merge \
--local -oyaml > gitops/resources/linkerd/trust-anchor.yaml
```
This will overwrite the existing `SealedSecret` resource in your local
`gitops/resources/linkerd/trust-anchor.yaml` file. We will push this change to
the in-cluster Git server.
Confirm that only the `spec.encryptedData` is changed:
```sh
git diff gitops/resources/linkerd/trust-anchor.yaml
```
Commit and push the new trust anchor secret to your in-cluster Git server:
```sh
git add gitops/resources/linkerd/trust-anchor.yaml
git commit -m "update encrypted trust anchor"
git push git-server master
```
Confirm the commit is successfully pushed:
```sh
kubectl -n scm exec "${git_server}" -- git --git-dir linkerd-examples.git log -1
```
## Deploy linkerd-bootstrap
Synchronize the `linkerd-bootstrap` application:
```sh
argocd app sync linkerd-bootstrap
```
{{< note >}}
If the issuer and certificate resources appear in a degraded state, it's likely
that the SealedSecrets controller failed to decrypt the sealed
`linkerd-trust-anchor` secret. Check the SealedSecrets controller for error logs.
For debugging purposes, the sealed resource can be retrieved using the
`kubectl -n linkerd get sealedsecrets linkerd-trust-anchor -oyaml` command.
Ensure that this resource matches the
`gitops/resources/linkerd/trust-anchor.yaml` file you pushed to the in-cluster
Git server earlier.
{{< /note >}}
{{< fig alt="Synchronize the linkerd-bootstrap application"
title="Synchronize the linkerd-bootstrap application"
src="/images/gitops/dashboard-linkerd-bootstrap-sync.png" >}}
SealedSecrets should have created a secret containing the decrypted trust
anchor. Retrieve the decrypted trust anchor from the secret:
```sh
trust_anchor=`kubectl -n linkerd get secret linkerd-trust-anchor -ojsonpath="{.data['tls\.crt']}" | base64 -d -w 0 -`
```
Confirm that it matches the decrypted trust anchor certificate you created
earlier in your local `sample-trust.crt` file:
```sh
diff -b \
<(echo "${trust_anchor}" | step certificate inspect -) \
<(step certificate inspect sample-trust.crt)
```
### Deploy Linkerd
Now we are ready to install Linkerd. The decrypted trust anchor we just
retrieved will be passed to the installation process using the
`identityTrustAnchorsPEM` parameter.
Prior to installing Linkerd, note that the `global.identityTrustAnchorsPEM`
parameter is set to an "empty" certificate string:
```sh
argocd app get linkerd -ojson | \
jq -r '.spec.source.helm.parameters[] | select(.name == "identityTrustAnchorsPEM") | .value'
```
{{< fig alt="Empty default trust anchor"
title="Empty default trust anchor"
src="/images/gitops/dashboard-trust-anchor-empty.png" >}}
We will override this parameter in the `linkerd` application with the value of
`${trust_anchor}`.
Locate the `identityTrustAnchorsPEM` variable in your local
`gitops/argo-apps/linkerd.yaml` file, and set its `value` to that of
`${trust_anchor}`.
Ensure that the multi-line string is indented correctly. E.g.,
```yaml
source:
chart: linkerd2
repoURL: https://helm.linkerd.io/stable
targetRevision: 2.8.0
helm:
parameters:
- name: identityTrustAnchorsPEM
value: |
-----BEGIN CERTIFICATE-----
MIIBlTCCATygAwIBAgIRAKQr9ASqULvXDeyWpY1LJUQwCgYIKoZIzj0EAwIwKTEn
MCUGA1UEAxMeaWRlbnRpdHkubGlua2VyZC5jbHVzdGVyLmxvY2FsMB4XDTIwMDkx
ODIwMTAxMFoXDTI1MDkxNzIwMTAxMFowKTEnMCUGA1UEAxMeaWRlbnRpdHkubGlu
a2VyZC5jbHVzdGVyLmxvY2FsMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+PUp
IR74PsU+geheoyseycyquYyes5eeksIb5FDm8ptOXQ2xPcBpvesZkj6uIyS3k4qV
E0S9VtMmHNeycL7446NFMEMwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYB
Af8CAQEwHQYDVR0OBBYEFHypCh7hiSLNxsKhMylQgqD9t7NNMAoGCCqGSM49BAMC
A0cAMEQCIEWhI86bXWEd4wKTnG07hBfBuVCT0bxopaYnn3wRFx7UAiAwXyh5uaVg
MwCC5xL+PM+bm3PRqtrmI6TocWH07GbMxg==
-----END CERTIFICATE-----
```
Confirm that only one `spec.source.helm.parameters.value` field is changed:
```sh
git diff gitops/argo-apps/linkerd.yaml
```
Commit and push the changes to the Git server:
```sh
git add gitops/argo-apps/linkerd.yaml
git commit -m "set identityTrustAnchorsPEM parameter"
git push git-server master
```
Synchronize the `main` application:
```sh
argocd app sync main
```
Confirm that the new trust anchor is picked up by the `linkerd` application:
```sh
argocd app get linkerd -ojson | \
jq -r '.spec.source.helm.parameters[] | select(.name == "identityTrustAnchorsPEM") | .value'
```
{{< fig alt="Override mTLS trust anchor"
title="Override mTLS trust anchor"
src="/images/gitops/dashboard-trust-anchor-override.png" >}}
Synchronize the `linkerd` application:
```sh
argocd app sync linkerd
```
Check that Linkerd is ready:
```sh
linkerd check
```
{{< fig alt="Synchronize Linkerd"
title="Synchronize Linkerd"
src="/images/gitops/dashboard-linkerd-sync.png" >}}
### Test with emojivoto
Deploy emojivoto to test auto proxy injection:
```sh
argocd app sync emojivoto
```
Check that the applications are healthy:
```sh
for deploy in "emoji" "vote-bot" "voting" "web" ; \
do kubectl -n emojivoto rollout status deploy/${deploy}; \
done
```
{{< fig alt="Synchronize emojivoto"
title="Synchronize emojivoto"
src="/images/gitops/dashboard-emojivoto-sync.png" >}}
### Upgrade Linkerd to 2.8.1
Use your editor to change the `spec.source.targetRevision` field to `2.8.1` in
the `gitops/argo-apps/linkerd.yaml` file:
Confirm that only the `targetRevision` field is changed:
```sh
git diff gitops/argo-apps/linkerd.yaml
```
Commit and push this change to the Git server:
```sh
git add gitops/argo-apps/linkerd.yaml
git commit -m "upgrade Linkerd to 2.8.1"
git push git-server master
```
Synchronize the `main` application:
```sh
argocd app sync main
```
Synchronize the `linkerd` application:
```sh
argocd app sync linkerd
```
Confirm that the upgrade completed successfully:
```sh
linkerd check
```
Confirm the new version of the control plane:
```sh
linkerd version
```
### Clean up
All the applications can be removed by removing the `main` application:
```sh
argocd app delete main --cascade=true
```

View File

@ -0,0 +1,61 @@
+++
title = "Graceful Pod Shutdown"
description = "Gracefully handle pod shutdown signal."
+++
When Kubernetes begins to terminate a pod, it starts by sending all containers
in that pod a TERM signal. When the Linkerd proxy sidecar receives this signal,
it will immediately begin a graceful shutdown where it refuses all new requests
and allows existing requests to complete before shutting down.
This means that if the pod's main container attempts to make any new network
calls after the proxy has received the TERM signal, those network calls will
fail. This also has implications for clients of the terminating pod and for
job resources.
## Slow Updating Clients
Before Kubernetes terminates a pod, it first removes that pod from the endpoints
resource of any services that pod is a member of. This means that clients of
that service should stop sending traffic to the pod before it is terminated.
However, certain clients can be slow to receive the endpoints update and may
attempt to send requests to the terminating pod after that pod's proxy has
already received the TERM signal and begun graceful shutdown. Those requests
will fail.
To mitigate this, use the `--wait-before-exit-seconds` flag with
`linkerd inject` to delay the Linkerd proxy's handling of the TERM signal for
a given number of seconds using a `preStop` hook. This delay gives slow clients
additional time to receive the endpoints update before beginning graceful
shutdown. To achieve max benefit from the option, the main container should have
its own `preStop` hook with the sleep command inside which has a smaller period
than is set for the proxy sidecar. And none of them must be bigger than
`terminationGracePeriodSeconds` configured for the entire pod.
For example,
```yaml
# application container
lifecycle:
preStop:
exec:
command:
- /bin/bash
- -c
- sleep 20
# for entire pod
terminationGracePeriodSeconds: 160
```
## Job Resources
Pods which are part of a job resource run until all of the containers in the
pod complete. However, the Linkerd proxy container runs continuously until it
receives a TERM signal. This means that job pods which have been injected will
continue to run, even once the main container has completed.
Better support for
[sidecar containers in Kubernetes](https://github.com/kubernetes/kubernetes/issues/25908)
has been proposed and Linkerd will take advantage of this support when it
becomes available.

View File

@ -0,0 +1,159 @@
+++
title = "Installing Linkerd with Helm"
description = "Install Linkerd onto your own Kubernetes cluster using Helm."
+++
Linkerd can optionally be installed via Helm rather than with the `linkerd
install` command.
## Prerequisite: identity certificates
The identity component of Linkerd requires setting up a trust anchor
certificate, and an issuer certificate with its key. These must use the ECDSA
P-256 algorithm and need to be provided to Helm by the user (unlike when using
the `linkerd install` CLI which can generate these automatically). You can
provide your own, or follow [these instructions](../generate-certificates/)
to generate new ones.
## Adding Linkerd's Helm repository
```bash
# To add the repo for Linkerd2 stable releases:
helm repo add linkerd https://helm.linkerd.io/stable
# To add the repo for Linkerd2 edge releases:
helm repo add linkerd-edge https://helm.linkerd.io/edge
```
The following instructions use the `linkerd` repo. For installing an edge
release, just replace with `linkerd-edge`.
## Helm install procedure
```bash
# set expiry date one year from now, in Mac:
exp=$(date -v+8760H +"%Y-%m-%dT%H:%M:%SZ")
# in Linux:
exp=$(date -d '+8760 hour' +"%Y-%m-%dT%H:%M:%SZ")
helm install linkerd2 \
--set-file identityTrustAnchorsPEM=ca.crt \
--set-file identity.issuer.tls.crtPEM=issuer.crt \
--set-file identity.issuer.tls.keyPEM=issuer.key \
--set identity.issuer.crtExpiry=$exp \
linkerd/linkerd2
```
{{< note >}}
For Helm versions < v3, `--name` flag has to specifically be passed.
In Helm v3, It has been deprecated, and is the first argument as
specified above.
{{< /note >}}
The chart values will be picked from the chart's `values.yaml` file.
You can override the values in that file by providing your own `values.yaml`
file passed with a `-f` option, or overriding specific values using the family of
`--set` flags like we did above for certificates.
## Disabling The Proxy Init Container
If installing with CNI, make sure that you add the `--set
cniEnabled=true` flag to your `helm install` command.
## Setting High-Availability
The chart contains a file `values-ha.yaml` that overrides some
default values as to set things up under a high-availability scenario, analogous
to the `--ha` option in `linkerd install`. Values such as higher number of
replicas, higher memory/cpu limits and affinities are specified in that file.
You can get ahold of `values-ha.yaml` by fetching the chart files:
```bash
helm fetch --untar linkerd/linkerd2
```
Then use the `-f` flag to provide the override file, for example:
```bash
## see above on how to set $exp
helm install linkerd2 \
--set-file identityTrustAnchorsPEM=ca.crt \
--set-file identity.issuer.tls.crtPEM=issuer.crt \
--set-file identity.issuer.tls.keyPEM=issuer.key \
--set identity.issuer.crtExpiry=$exp \
-f linkerd2/values-ha.yaml \
linkerd/linkerd2
```
{{< note >}}
For Helm versions < v3, `--name` flag has to specifically be passed.
In Helm v3, It has been deprecated, and is the first argument as
specified above.
{{< /note >}}
## Customizing the Namespace
To install Linkerd to a different namespace than the default `linkerd`,
override the `Namespace` variable.
By default, the chart creates the control plane namespace with the
`config.linkerd.io/admission-webhooks: disabled` label. It is required for the
control plane to work correctly. This means that the chart won't work with
Helm v2's `--namespace` option. If you're relying on a separate tool to create
the control plane namespace, make sure that:
1. The namespace is labeled with `config.linkerd.io/admission-webhooks: disabled`
1. The `installNamespace` is set to `false`
1. The `namespace` variable is overridden with the name of your namespace
{{< note >}}
In Helm v3 the `--namespace` option must be used with an existing namespace.
{{< /note >}}
## Helm upgrade procedure
Make sure your local Helm repos are updated:
```bash
helm repo update
helm search linkerd2 -v {{% latestversion %}}
NAME CHART VERSION APP VERSION DESCRIPTION
linkerd/linkerd2 <chart-semver-version> {{% latestversion %}} Linkerd gives you observability, reliability, and securit...
```
The `helm upgrade` command has a number of flags that allow you to customize
its behaviour. The ones that special attention should be paid to are
`--reuse-values` and `--reset-values` and how they behave when charts change
from version to version and/or overrides are applied through `--set` and
`--set-file`. To summarize there are the following prominent cases that can be
observed:
- `--reuse-values` with no overrides - all values are reused
- `--reuse-values` with overrides - all except the values that are overridden
are reused
- `--reset-values` with no overrides - no values are reused and all changes
from provided release are applied during the upgrade
- `--reset-values` with overrides - no values are reused and changed from
provided release are applied together with the overrides
- no flag and no overrides - `--reuse-values` will be used by default
- no flag and overrides - `--reset-values` will be used by default
Bearing all that in mind, you have to decide whether you want to reuse the
values in the chart or move to the values specified in the newer chart.
The advised practice is to use a `values.yaml` file that stores all custom
overrides that you have for your chart. Before upgrade, check whether there
are breaking changes to the chart (i.e. renamed or moved keys, etc). You can
consult the [edge](https://hub.helm.sh/charts/linkerd2-edge/linkerd2) or the
[stable](https://hub.helm.sh/charts/linkerd2/linkerd2) chart docs, depending on
which one your are upgrading to. If there are, make the corresponding changes to
your `values.yaml` file. Then you can use:
```bash
helm upgrade linkerd2 linkerd/linkerd2 --reset-values -f values.yaml --atomic
```
The `--atomic` flag will ensure that all changes are rolled back in case the
upgrade operation fails

View File

@ -0,0 +1,155 @@
+++
title = "Installing Linkerd"
description = "Install Linkerd to your own Kubernetes cluster."
aliases = [
"../upgrading/",
"../installing/",
"../rbac/"
]
+++
Before you can use Linkerd, you'll need to install the
[core control plane](../../reference/architecture/#control-plane). This page
covers how to accomplish that, as well as common problems that you may
encounter.
Note that the control plane is typically installed by using Linkerd's CLI. See
[Getting Started](../../getting-started/) for how to install the CLI onto your local
environment.
Linkerd also comprises of some first party extensions which add additional features
i.e `viz`, `multicluster` and `jaeger`. See [Extensions](../extensions/)
to understand how to install them.
Note also that, once the control plane is installed, you'll need to "mesh" any
services you want Linkerd active for. See
[Adding Your Service](../../adding-your-service/) for how to add Linkerd's data
plane to your services.
## Requirements
Linkerd 2.x requires a functioning Kubernetes cluster on which to run. This
cluster may be hosted on a cloud provider or may be running locally via
Minikube or Docker for Desktop.
You can validate that this Kubernetes cluster is configured appropriately for
Linkerd by running
```bash
linkerd check --pre
```
### GKE
If installing Linkerd on GKE, there are some extra steps required depending on
how your cluster has been configured. If you are using any of these features,
check out the additional instructions.
- [Private clusters](../../reference/cluster-configuration/#private-clusters)
## Installing
Once you have a cluster ready, generally speaking, installing Linkerd is as
easy as running `linkerd install` to generate a Kubernetes manifest, and
applying that to your cluster, for example, via
```bash
linkerd install | kubectl apply -f -
```
See [Getting Started](../../getting-started/) for an example.
{{< note >}}
Most common configuration options are provided as flags for `install`. See the
[reference documentation](../../reference/cli/install/) for a complete list of
options. To do configuration that is not part of the `install` command, see how
you can create a [customized install](../customize-install/).
{{< /note >}}
{{< note >}}
For organizations that distinguish cluster privileges by role, jump to the
[Multi-stage install](#multi-stage-install) section.
{{< /note >}}
## Verification
After installation, you can validate that the installation was successful by
running:
```bash
linkerd check
```
## Uninstalling
See [Uninstalling Linkerd](../uninstall/).
## Multi-stage install
If your organization assigns Kubernetes cluster privileges based on role
(typically cluster owner and service owner), Linkerd provides a "multi-stage"
installation to accommodate these two roles. The two installation stages are
`config` (for the cluster owner) and `control-plane` (for the service owner).
The cluster owner has privileges necessary to create namespaces, as well as
global resources including cluster roles, bindings, and custom resource
definitions. The service owner has privileges within a namespace necessary to
create deployments, configmaps, services, and secrets.
### Stage 1: config
The `config` stage is intended to be run by the cluster owner, the role with
more privileges. It is also the cluster owner's responsibility to run the
initial pre-install check:
```bash
linkerd check --pre
```
Once the pre-install check passes, install the config stage with:
```bash
linkerd install config | kubectl apply -f -
```
In addition to creating the `linkerd` namespace, this command installs the
following resources onto your Kubernetes cluster:
- ClusterRole
- ClusterRoleBinding
- CustomResourceDefinition
- MutatingWebhookConfiguration
- PodSecurityPolicy
- Role
- RoleBinding
- Secret
- ServiceAccount
- ValidatingWebhookConfiguration
To validate the `config` stage succeeded, run:
```bash
linkerd check config
```
### Stage 2: control-plane
Following successful installation of the `config` stage, the service owner may
install the `control-plane` with:
```bash
linkerd install control-plane | kubectl apply -f -
```
This command installs the following resources onto your Kubernetes cluster, all
within the `linkerd` namespace:
- ConfigMap
- Deployment
- Secret
- Service
To validate the `control-plane` stage succeeded, run:
```bash
linkerd check
```

View File

@ -0,0 +1,363 @@
+++
title = "Installing Multi-cluster Components"
description = "Allow Linkerd to manage cross-cluster communication."
+++
Multicluster support in Linkerd requires extra installation and configuration on
top of the default [control plane installation](../install/). This guide
walks through this installation and configuration as well as common problems
that you may encounter. For a detailed walkthrough and explanation of what's
going on, check out [getting started](../multicluster/).
If you'd like to use an existing [Ambassador](https://www.getambassador.io/)
installation, check out the
[leverage](../installing-multicluster/#leverage-ambassador) instructions.
Alternatively, check out the Ambassador
[documentation](https://www.getambassador.io/docs/latest/howtos/linkerd2/#multicluster-operation)
for a more detailed explanation of the configuration and what's going on.
## Requirements
- Two clusters.
- A [control plane installation](../install/) in each cluster that shares
a common
[trust anchor](../generate-certificates/#trust-anchor-certificate).
If you have an existing installation, see the
[trust anchor bundle](../installing-multicluster/#trust-anchor-bundle)
documentation to understand what is required.
- Each of these clusters should be configured as `kubectl`
[contexts](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/).
- Elevated privileges on both clusters. We'll be creating service accounts and
granting extended privileges, so you'll need to be able to do that on your
test clusters.
- Support for services of type `LoadBalancer` in the `east` cluster. Check out
the documentation for your cluster provider or take a look at
[inlets](https://blog.alexellis.io/ingress-for-your-local-kubernetes-cluster/).
This is what the `west` cluster will use to communicate with `east` via the
gateway.
## Step 1: Install the multicluster control plane
On each cluster, run:
```bash
linkerd multicluster install | \
kubectl apply -f -
```
To verify that everything has started up successfully, run:
```bash
linkerd multicluster check
```
For a deep dive into what components are being added to your cluster and how all
the pieces fit together, check out the
[getting started documentation](../multicluster/#preparing-your-cluster).
## Step 2: Link the clusters
Each cluster must be linked. This consists of installing several resources in
the source cluster including a secret containing a kubeconfig that allows access
to the target cluster Kubernetes API, a service mirror control for mirroring
services, and a Link custom resource for holding configuration. To link cluster
`west` to cluster `east`, you would run:
```bash
linkerd --context=east multicluster link --cluster-name east |
kubectl --context=west apply -f -
```
To verify that the credentials were created successfully and the clusters are
able to reach each other, run:
```bash
linkerd --context=west multicluster check
```
You should also see the list of gateways show up by running. Note that you'll
need Linkerd's Viz extension to be installed in the source cluster to get the
list of gateways:
```bash
linkerd --context=west multicluster gateways
```
For a detailed explanation of what this step does, check out the
[linking the clusters section](../multicluster/#linking-the-clusters).
## Step 3: Export services
Services are not automatically mirrored in linked clusters. By default, only
services with the `mirror.linkerd.io/exported` label will be mirrored. For each
service you would like mirrored to linked clusters, run:
```bash
kubectl label svc foobar mirror.linkerd.io/exported=true
```
{{< note >}} You can configure a different label selector by using the
`--selector` flag on the `linkerd multicluster link` command or by editing
the Link resource created by the `linkerd multicluster link` command.
{{< /note >}}
## Leverage Ambassador
The bundled Linkerd gateway is not required. In fact, if you have an existing
Ambassador installation, it is easy to use it instead! By using your existing
Ambassador installation, you avoid needing to manage multiple ingress gateways
and pay for extra cloud load balancers. This guide assumes that Ambassador has
been installed into the `ambassador` namespace.
First, you'll want to inject the `ambassador` deployment with Linkerd:
```bash
kubectl -n ambassador get deploy ambassador -o yaml | \
linkerd inject \
--skip-inbound-ports 80,443 \
--require-identity-on-inbound-ports 4183 - | \
kubectl apply -f -
```
This will add the Linkerd proxy, skip the ports that Ambassador is handling for
public traffic and require identity on the gateway port. Check out the
[docs](../multicluster/#security) to understand why it is important to
require identity on the gateway port.
Next, you'll want to add some configuration so that Ambassador knows how to
handle requests:
```bash
cat <<EOF | kubectl --context=${ctx} apply -f -
---
apiVersion: getambassador.io/v2
kind: Module
metadata:
name: ambassador
namespace: ambassador
spec:
config:
add_linkerd_headers: true
---
apiVersion: getambassador.io/v2
kind: Host
metadata:
name: wildcard
namespace: ambassador
spec:
hostname: "*"
selector:
matchLabels:
nothing: nothing
acmeProvider:
authority: none
requestPolicy:
insecure:
action: Route
---
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
name: public-health-check
namespace: ambassador
spec:
prefix: /-/ambassador/ready
rewrite: /ambassador/v0/check_ready
service: localhost:8877
bypass_auth: true
EOF
```
The Ambassador service and deployment definitions need to be patched a little
bit. This adds metadata required by the
[service mirror controller](https://linkerd.io/2020/02/25/multicluster-kubernetes-with-service-mirroring/#step-1-service-discovery).
To get these resources patched, run:
```bash
kubectl --context=${ctx} -n ambassador patch deploy ambassador -p='
spec:
template:
metadata:
annotations:
config.linkerd.io/enable-gateway: "true"
'
kubectl --context=${ctx} -n ambassador patch svc ambassador --type='json' -p='[
{"op":"add","path":"/spec/ports/-", "value":{"name": "mc-gateway", "port": 4143}},
{"op":"replace","path":"/spec/ports/0", "value":{"name": "mc-probe", "port": 80, "targetPort": 8080}}
]'
kubectl --context=${ctx} -n ambassador patch svc ambassador -p='
metadata:
annotations:
mirror.linkerd.io/gateway-identity: ambassador.ambassador.serviceaccount.identity.linkerd.cluster.local
mirror.linkerd.io/multicluster-gateway: "true"
mirror.linkerd.io/probe-path: /-/ambassador/ready
mirror.linkerd.io/probe-period: "3"
'
```
Now you can install the Linkerd multicluster components onto your target cluster.
Since we're using Ambassador as our gateway, we need to skip installing the
Linkerd gateway by using the `--gateway=false` flag:
```bash
linkerd --context=${ctx} multicluster install --gateway=false | kubectl --context=${ctx} apply -f -
```
With everything setup and configured, you're ready to link a source cluster to
this Ambassador gateway. Run the `link` command specifying the name and
namespace of your Ambassador service:
```bash
linkerd --context=${ctx} multicluster link --cluster-name=${ctx} --gateway-name=ambassador --gateway-namespace=ambassador \
| kubectl --context=${src_ctx} apply -f -
```
From the source cluster (the one not running Ambassador), you can validate that
everything is working correctly by running:
```bash
linkerd multicluster check
```
Additionally, the `ambassador` gateway will show up when listing the active
gateways:
```bash
linkerd multicluster gateways
```
## Trust Anchor Bundle
To secure the connections between clusters, Linkerd requires that there is a
shared trust anchor. This allows the control plane to encrypt the requests that
go between clusters and verify the identity of those requests. This identity is
used to control access to clusters, so it is critical that the trust anchor is
shared.
The easiest way to do this is to have a single trust anchor certificate shared
between multiple clusters. If you have an existing Linkerd installation and have
thrown away the trust anchor key, it might not be possible to have a single
certificate for the trust anchor. Luckily, the trust anchor can be a bundle of
certificates as well!
To fetch your existing cluster's trust anchor, run:
```bash
kubectl -n linkerd get cm linkerd-config -ojsonpath="{.data.values}" | \
yq e .identityTrustAnchorsPEM - > trustAnchor.crt
```
{{< note >}} This command requires [yq](https://github.com/mikefarah/yq). If you
don't have yq, feel free to extract the certificate from the `identityTrustAnchorsPEM`
field with your tool of choice.
{{< /note >}}
Now, you'll want to create a new trust anchor and issuer for the new cluster:
```bash
step certificate create root.linkerd.cluster.local root.crt root.key \
--profile root-ca --no-password --insecure
step certificate create identity.linkerd.cluster.local issuer.crt issuer.key \
--profile intermediate-ca --not-after 8760h --no-password --insecure \
--ca root.crt --ca-key root.key
```
{{< note >}} We use the [step cli](https://smallstep.com/cli/) to generate
certificates. `openssl` works just as well! {{< /note >}}
With the old cluster's trust anchor and the new cluster's trust anchor, you can
create a bundle by running:
```bash
cat trustAnchor.crt root.crt > bundle.crt
```
You'll want to upgrade your existing cluster with the new bundle. Make sure
every pod you'd like to have talk to the new cluster is restarted so that it can
use this bundle. To upgrade the existing cluster with this new trust anchor
bundle, run:
```bash
linkerd upgrade --identity-trust-anchors-file=./bundle.crt | \
kubectl apply -f -
```
Finally, you'll be able to install Linkerd on the new cluster by using the trust
anchor bundle that you just created along with the issuer certificate and key.
```bash
linkerd install \
--identity-trust-anchors-file bundle.crt \
--identity-issuer-certificate-file issuer.crt \
--identity-issuer-key-file issuer.key | \
kubectl apply -f -
```
Make sure to verify that the cluster's have started up successfully by running
`check` on each one.
```bash
linkerd check
```
## Installing the multicluster control plane components through Helm
Linkerd's multicluster components i.e Gateway and Service Mirror can
be installed via Helm rather than the `linkerd multicluster install` command.
This not only allows advanced configuration, but also allows users to bundle the
multicluster installation as part of their existing Helm based installation
pipeline.
### Adding Linkerd's Helm repository
First, let's add the Linkerd's Helm repository by running
```bash
# To add the repo for Linkerd2 stable releases:
helm repo add linkerd https://helm.linkerd.io/stable
```
### Helm multicluster install procedure
```bash
helm install linkerd2-multicluster linkerd/linkerd2-multicluster
```
The chart values will be picked from the chart's `values.yaml` file.
You can override the values in that file by providing your own `values.yaml`
file passed with a `-f` option, or overriding specific values using the family of
`--set` flags.
Full set of configuration options can be found [here](https://github.com/linkerd/linkerd2/tree/main/charts/linkerd2-multicluster#configuration)
The installation can be verified by running
```bash
linkerd multicluster check
```
Installation of the gateway can be disabled with the `gateway` setting. By
default this value is true.
### Installing additional access credentials
When the multicluster components are installed onto a target cluster with
`linkerd multicluster install`, a service account is created which source clusters
will use to mirror services. Using a distinct service account for each source
cluster can be benefitial since it gives you the ability to revoke service mirroring
access from specific source clusters. Generating additional service accounts
and associated RBAC can be done using the `linkerd multicluster allow` command
through the CLI.
The same functionality can also be done through Helm setting the
`remoteMirrorServiceAccountName` value to a list.
```bash
helm install linkerd2-mc-source linkerd/linkerd2-multicluster --set remoteMirrorServiceAccountName={source1\,source2\,source3} --kube-context target
```
Now that the multicluster components are installed, operations like linking, etc
can be performed by using the linkerd CLI's multicluster sub-command as per the
[multicluster task](../../features/multicluster/).

View File

@ -0,0 +1,218 @@
+++
title = "Getting started with Linkerd SMI extension"
description = "Use Linkerd SMI extension to work with Service Mesh Interface(SMI) resources."
+++
[Service Mesh Interface](https://smi-spec.io/) is a standard interface for
service meshes on Kubernetes. It defines a set of resources that could be
used across service meshes that implement it.
You can read more about it in the [specification](https://github.com/servicemeshinterface/smi-spec)
Currently, Linkerd supports SMI's `TrafficSplit` specification which can be
used to perform traffic splitting across services natively. This means that
you can apply the SMI resources without any additional
components/configuration but this obviously has some downsides, as
Linkerd may not be able to add extra specific configurations specific to it,
as SMI is more like a lowest common denominator of service mesh functionality.
To get around these problems, Linkerd can instead have an adaptor that converts
SMI specifications into native Linkerd configurations that it can understand
and perform the operation. This also removes the extra native coupling with SMI
resources with the control-plane, and the adaptor can move independently and
have it's own release cycle. [Linkerd SMI](https://www.github.com/linkerd/linkerd-smi)
is an extension that does just that.
This guide will walk you through installing the SMI extension and configuring
a `TrafficSplit` specification, to perform Traffic Splitting across services.
## Prerequisites
- To use this guide, you'll need to have Linkerd installed on your cluster.
Follow the [Installing Linkerd Guide](../install/) if you haven't
already done this.
## Install the Linkerd-SMI extension
### CLI
Install the SMI extension CLI binary by running:
```bash
curl -sL https://linkerd.github.io/linkerd-smi/install | sh
```
Alternatively, you can download the CLI directly via the [releases page](https://github.com/linkerd/linkerd-smi/releases).
The first step is installing the Linkerd-SMI extension onto your cluster.
This extension consists of a SMI-Adaptor which converts SMI resources into
native Linkerd resources.
To install the Linkerd-SMI extension, run the command:
```bash
linkerd smi install | kubectl apply -f -
```
You can verify that the Linkerd-SMI extension was installed correctly by
running:
```bash
linkerd smi check
```
### Helm
To install the `linkerd-smi` Helm chart, run:
```bash
helm repo add l5d-smi https://linkerd.github.io/linkerd-smi
helm install l5d-smi/linkerd-smi --generate-name
```
## Install Sample Application
First, let's install the sample application.
```bash
# create a namespace for the sample application
kubectl create namespace trafficsplit-sample
# install the sample application
linkerd inject https://raw.githubusercontent.com/linkerd/linkerd2/main/test/integration/trafficsplit/testdata/application.yaml | kubectl -n trafficsplit-sample apply -f -
```
This installs a simple client, and two server deployments.
One of the server deployments i.e `faling-svc` always returns a 500 error,
and the other one i.e `backend-svc` always returns a 200.
```bash
kubectl get deployments -n trafficsplit-sample
NAME READY UP-TO-DATE AVAILABLE AGE
backend 1/1 1 1 2m29s
failing 1/1 1 1 2m29s
slow-cooker 1/1 1 1 2m29s
```
By default, the client will hit the `backend-svc`service. This is evident by
the `edges` sub command.
```bash
linkerd viz edges deploy -n trafficsplit-sample
SRC DST SRC_NS DST_NS SECURED
prometheus backend linkerd-viz trafficsplit-sample √
prometheus failing linkerd-viz trafficsplit-sample √
prometheus slow-cooker linkerd-viz trafficsplit-sample √
slow-cooker backend trafficsplit-sample trafficsplit-sample √
```
## Configuring a TrafficSplit
Now, Let's apply a `TrafficSplit` resource to perform Traffic Splitting on the
`backend-svc` to distribute load between it and the `failing-svc`.
```bash
cat <<EOF | kubectl apply -f -
apiVersion: split.smi-spec.io/v1alpha2
kind: TrafficSplit
metadata:
name: backend-split
namespace: trafficsplit-sample
spec:
service: backend-svc
backends:
- service: backend-svc
weight: 500
- service: failing-svc
weight: 500
EOF
```
Because the `smi-adaptor` watches for `TrafficSplit` resources, it will
automatically create a respective `ServiceProfile` resource to perform
the same. This can be verified by retrieving the `ServiceProfile` resource.
```bash
kubectl describe serviceprofile -n trafficsplit-sample
Name: backend-svc.trafficsplit-sample.svc.cluster.local
Namespace: trafficsplit-sample
Labels: <none>
Annotations: <none>
API Version: linkerd.io/v1alpha2
Kind: ServiceProfile
Metadata:
Creation Timestamp: 2021-08-02T12:42:52Z
Generation: 1
Managed Fields:
API Version: linkerd.io/v1alpha2
Fields Type: FieldsV1
fieldsV1:
f:spec:
.:
f:dstOverrides:
Manager: smi-adaptor
Operation: Update
Time: 2021-08-02T12:42:52Z
Resource Version: 3542
UID: cbcdb74f-07e0-42f0-a7a8-9bbcf5e0e54e
Spec:
Dst Overrides:
Authority: backend-svc.trafficsplit-sample.svc.cluster.local
Weight: 500
Authority: failing-svc.trafficsplit-sample.svc.cluster.local
Weight: 500
Events: <none>
```
As we can see, A relevant `ServiceProfile` with `DstOverrides` has
been created to perform the TrafficSplit.
The Traffic Splitting can be verified by running the `edges` command.
```bash
linkerd viz edges deploy -n trafficsplit-sample
SRC DST SRC_NS DST_NS SECURED
prometheus backend linkerd-viz trafficsplit-sample √
prometheus failing linkerd-viz trafficsplit-sample √
prometheus slow-cooker linkerd-viz trafficsplit-sample √
slow-cooker backend trafficsplit-sample trafficsplit-sample √
slow-cooker failing trafficsplit-sample trafficsplit-sample √
```
This can also be verified by running `stat` sub command on the `TrafficSplit`
resource.
```bash
linkerd viz stat ts/backend-split -n traffic-sample
NAME APEX LEAF WEIGHT SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
backend-split backend-svc backend-svc 500 100.00% 0.5rps 1ms 1ms 1ms
backend-split backend-svc failing-svc 500 0.00% 0.5rps 1ms 1ms 1ms
```
This can also be verified by checking the `smi-adaptor` logs.
```bash
kubectl -n linkerd-smi logs deploy/smi-adaptor smi-adaptor
time="2021-08-04T11:04:35Z" level=info msg="Using cluster domain: cluster.local"
time="2021-08-04T11:04:35Z" level=info msg="Starting SMI Controller"
time="2021-08-04T11:04:35Z" level=info msg="Waiting for informer caches to sync"
time="2021-08-04T11:04:35Z" level=info msg="starting admin server on :9995"
time="2021-08-04T11:04:35Z" level=info msg="Starting workers"
time="2021-08-04T11:04:35Z" level=info msg="Started workers"
time="2021-08-04T11:05:17Z" level=info msg="created serviceprofile/backend-svc.trafficsplit-sample.svc.cluster.local for trafficsplit/backend-split"
time="2021-08-04T11:05:17Z" level=info msg="Successfully synced 'trafficsplit-sample/backend-split'"
```
## Cleanup
Delete the `trafficsplit-sample` resource by running
```bash
kubectl delete namespace/trafficsplit-sample
```
### Conclusion
Though, Linkerd currently supports reading `TrafficSplit` resources directly
`ServiceProfiles` would always take a precedence over `TrafficSplit` resources. The
support for `TrafficSplit` resource will be removed in a further release at which
the `linkerd-smi` extension would be necessary to use `SMI` resources with Linkerd.

View File

@ -0,0 +1,335 @@
+++
title = "Manually Rotating Control Plane TLS Credentials"
description = "Update Linkerd's TLS trust anchor and issuer certificate."
aliases = [ "rotating_identity_certificates" ]
+++
Linkerd's [automatic mTLS](../../features/automatic-mtls/) feature uses a set of
TLS credentials to generate TLS certificates for proxies: a trust anchor, and
an issuer certificate and private key. The trust anchor has a limited period of
validity: 365 days if generated by `linkerd install`, or a customized value if
[generated manually](../generate-certificates/).
Thus, for clusters that are expected to outlive this lifetime, you must
manually rotate the trust anchor. In this document, we describe how to
accomplish this without downtime.
Independent of the trust anchor, the issuer certificate and key pair can also
expire (though it is possible to [use `cert-manager` to set up automatic
rotation](../automatically-rotating-control-plane-tls-credentials/). This
document also covers how to rotate the issuer certificate and key pair without
downtime.
## Prerequisites
These instructions use the [step](https://smallstep.com/cli/) and
[jq](https://stedolan.github.io/jq/) CLI tools.
## Understanding the current state of your system
Begin by running:
```bash
linkerd check --proxy
```
If your configuration is valid and your credentials are not expiring soon, you
should see output similar to:
```text
linkerd-identity
----------------
√ certificate config is valid
√ trust roots are using supported crypto algorithm
√ trust roots are within their validity period
√ trust roots are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
√ issuer cert is valid for at least 60 days
√ issuer cert is issued by the trust root
linkerd-identity-data-plane
---------------------------
√ data plane proxies certificate match CA
```
However, if you see a message warning you that your trust anchor ("trust root")
or issuer certificates are expiring soon, then you must rotate them.
Note that this document only applies if the trust root and issuer certificate
are currently valid. If your trust anchor or issuer certificate have expired,
please follow the [Replacing Expired
Certificates Guide](../replacing_expired_certificates/) instead.
For example, if your issuer certificate has expired, you will see a message
similar to:
```text
linkerd-identity
----------------
√ certificate config is valid
√ trust roots are using supported crypto algorithm
√ trust roots are within their validity period
√ trust roots are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
× issuer cert is within its validity period
issuer certificate is not valid anymore. Expired on 2019-12-19T09:02:01Z
see https://linkerd.io/checks/#l5d-identity-issuer-cert-is-time-valid for hints
```
If your trust anchor has expired, you will see a message similar to:
```text
linkerd-identity
----------------
√ certificate config is valid
√ trust roots are using supported crypto algorithm
× trust roots are within their validity period
Invalid roots:
* 79461543992952791393769540277800684467 identity.linkerd.cluster.local not valid anymore. Expired on 2019-12-19T09:11:30Z
see https://linkerd.io/checks/#l5d-identity-roots-are-time-valid for hints
```
## Rotating the trust anchor
Rotating the trust anchor without downtime is a multi-step process: you must
generate a new trust anchor, bundle it with the old one, rotate the issuer
certificate and key pair, and finally remove the old trust anchor from the
bundle. If you simply need to rotate the issuer certificate and key pair, you
can skip directly to [Rotating the identity issuer
certificate](#rotating-the-identity-issuer-certificate) and ignore the trust
anchor rotation steps.
## Generate a new trust anchor
First, generate a new trust anchor certificate and private key:
```bash
step certificate create root.linkerd.cluster.local ca-new.crt ca-new.key --profile root-ca --no-password --insecure
```
Note that we use `--no-password --insecure` to avoid encrypting these files
with a passphrase. Store the private key somewhere secure so that it can be
used in the future to [generate new issuer
certificates](../generate-certificates/).
## Bundle your original trust anchor with the new one
Next, we need to bundle the trust anchor currently used by Linkerd together with
the new anchor. The following command uses `kubectl` to fetch the Linkerd config,
`jq`/[`yq`](https://github.com/mikefarah/yq) to extract the current trust anchor,
and `step` to combine it with the newly generated trust anchor:
```bash
kubectl -n linkerd get cm linkerd-config -o=jsonpath='{.data.values}' \
| yq e .identityTrustAnchorsPEM - > original-trust.crt
step certificate bundle ca-new.crt original-trust.crt bundle.crt
rm original-trust.crt
```
## Deploying the new bundle to Linkerd
At this point you can use the `linkerd upgrade` command to instruct Linkerd to
work with the new trust bundle:
```bash
linkerd upgrade --identity-trust-anchors-file=./bundle.crt | kubectl apply -f -
```
or you can also use the `helm upgrade` command:
```bash
helm upgrade linkerd2 --set-file identityTrustAnchorsPEM=./bundle.crt
```
This will restart the proxies in the Linkerd control plane, and they will be
reconfigured with the new trust anchor.
Finally, you must restart the proxy for all injected workloads in your cluster.
For example, doing that for the `emojivoto` namespace would look like:
```bash
kubectl -n emojivoto rollout restart deploy
```
Now you can run the `check` command to ensure that everything is ok:
```bash
linkerd check --proxy
```
You might have to wait a few moments until all the pods have been restarted and
are configured with the correct trust anchor. Meanwhile you might observe warnings:
```text
linkerd-identity
----------------
√ certificate config is valid
√ trust roots are using supported crypto algorithm
√ trust roots are within their validity period
√ trust roots are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
‼ issuer cert is valid for at least 60 days
issuer certificate will expire on 2019-12-19T09:51:19Z
see https://linkerd.io/checks/#l5d-identity-issuer-cert-not-expiring-soon for hints
√ issuer cert is issued by the trust root
linkerd-identity-data-plane
---------------------------
‼ data plane proxies certificate match CA
Some pods do not have the current trust bundle and must be restarted:
* emojivoto/emoji-d8d7d9c6b-8qwfx
* emojivoto/vote-bot-588499c9f6-zpwz6
* emojivoto/voting-8599548fdc-6v64k
* emojivoto/web-67c7599f6d-xx98n
* linkerd/linkerd-sp-validator-75f9d96dc-rch4x
* linkerd/linkerd-tap-68d8bbf64-mpzgb
* linkerd/linkerd-web-849f74b7c6-qlhwc
see https://linkerd.io/checks/#l5d-identity-data-plane-proxies-certs-match-ca for hints
```
When the rollout completes, your `check` command should stop warning you that
pods need to be restarted. It may still warn you, however, that your issuer
certificate is about to expire soon:
```text
linkerd-identity
----------------
√ certificate config is valid
√ trust roots are using supported crypto algorithm
√ trust roots are within their validity period
√ trust roots are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
‼ issuer cert is valid for at least 60 days
issuer certificate will expire on 2019-12-19T09:51:19Z
see https://linkerd.io/checks/#l5d-identity-issuer-cert-not-expiring-soon for hints
√ issuer cert is issued by the trust root
linkerd-identity-data-plane
---------------------------
√ data plane proxies certificate match CA
```
## Rotating the identity issuer certificate
To rotate the issuer certificate and key pair, first generate a new pair:
```bash
step certificate create identity.linkerd.cluster.local issuer-new.crt issuer-new.key \
--profile intermediate-ca --not-after 8760h --no-password --insecure \
--ca ca-new.crt --ca-key ca-new.key
```
Provided that the trust anchor has not expired and that, if recently rotated,
all proxies have been updated to include a working trust anchor (as outlined in
the previous section) it is now safe to rotate the identity issuer certificate
by using the `upgrade` command again:
```bash
linkerd upgrade --identity-issuer-certificate-file=./issuer-new.crt --identity-issuer-key-file=./issuer-new.key | kubectl apply -f -
```
or
```bash
exp=$(cat ca-new.crt | openssl x509 -noout -dates | grep "notAfter" | sed -e 's/notAfter=\(.*\)$/"\1"/' | TZ='GMT' xargs -I{} date -d {} +"%Y-%m-%dT%H:%M:%SZ")
helm upgrade linkerd2
--set-file identity.issuer.tls.crtPEM=./issuer-new.crt
--set-file identity.issuer.tls.keyPEM=./issuer-new.key
--set identity.issuer.crtExpiry=$exp
```
At this point Linkerd's `identity` control plane service should detect the
change of the secret and automatically update its issuer certificates.
To ensure this has happened, you can check for the specific Kubernetes event:
```bash
kubectl get events --field-selector reason=IssuerUpdated -n linkerd
LAST SEEN TYPE REASON OBJECT MESSAGE
9s Normal IssuerUpdated deployment/linkerd-identity Updated identity issuer
```
Restart the proxy for all injected workloads in your cluster to ensure that
their proxies pick up certificates issued by the new issuer:
```bash
kubectl -n emojivoto rollout restart deploy
```
Run the `check` command to make sure that everything is going as expected:
```bash
linkerd check --proxy
```
You should see output without any certificate expiration warnings (unless an
expired trust anchor still needs to be removed):
```text
linkerd-identity
----------------
√ certificate config is valid
√ trust roots are using supported crypto algorithm
√ trust roots are within their validity period
√ trust roots are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
√ issuer cert is valid for at least 60 days
√ issuer cert is issued by the trust root
linkerd-identity-data-plane
---------------------------
√ data plane proxies certificate match CA
```
## Removing the old trust anchor
We can now remove the old trust anchor from the trust bundle we created earlier.
The `upgrade` command can do that for the Linkerd components:
```bash
linkerd upgrade --identity-trust-anchors-file=./ca-new.crt | kubectl apply -f -
```
or
```bash
helm upgrade linkerd2 --set-file --set-file identityTrustAnchorsPEM=./ca-new.crt
```
Note that the ./ca-new.crt file is the same trust anchor you created at the start
of this process. Additionally, you can use the `rollout restart` command to
bring the configuration of your other injected resources up to date:
```bash
kubectl -n emojivoto rollout restart deploy
linkerd check --proxy
```
Finally the output of the `check` command should not produce any warnings or
errors:
```text
linkerd-identity
----------------
√ certificate config is valid
√ trust roots are using supported crypto algorithm
√ trust roots are within their validity period
√ trust roots are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
√ issuer cert is valid for at least 60 days
√ issuer cert is issued by the trust root
linkerd-identity-data-plane
---------------------------
√ data plane proxies certificate match CA
```
Congratulations, you have rotated your trust anchor! 🎉

View File

@ -0,0 +1,38 @@
+++
title = "Modifying the Proxy Log Level"
description = "Linkerd proxy log levels can be modified dynamically to assist with debugging."
+++
Emitting logs is an expensive operation for a network proxy, and by default,
the Linkerd data plane proxies are configured to only log exceptional events.
However, sometimes it is useful to increase the verbosity of proxy logs to
assist with diagnosing proxy behavior. Happily, Linkerd allows you to modify
these logs dynamically.
The log level of a Linkerd proxy can be modified on the fly by using the proxy's
`/proxy-log-level` endpoint on the admin-port.
For example, to change the proxy log-level of a pod to
`debug`, run
(replace `${POD:?}` or set the environment-variable `POD` with the pod name):
```sh
kubectl port-forward ${POD:?} linkerd-admin
curl -v --data 'linkerd=debug' -X PUT localhost:4191/proxy-log-level
```
whereby `linkerd-admin` is the name of the admin-port (`4191` by default)
of the injected sidecar-proxy.
The resulting logs can be viewed with `kubectl logs ${POD:?}`.
If changes to the proxy log level should be retained beyond the lifetime of a
pod, add the `config.linkerd.io/proxy-log-level` annotation to the pod template
(or other options, see reference).
The syntax of the proxy log level can be found in the
[proxy log level reference](../../reference/proxy-log-level/).
Note that logging has a noticeable, negative impact on proxy throughput. If the
pod will continue to serve production traffic, you may wish to reset the log
level once you are done.

View File

@ -0,0 +1,336 @@
+++
title = "Multi-cluster communication with StatefulSets"
description = "cross-cluster communication to and from headless services."
+++
Linkerd's multi-cluster extension works by "mirroring" service information
between clusters. Exported services in a target cluster will be mirrored as
`clusterIP` replicas. By default, every exported service will be mirrored as
`clusterIP`. When running workloads that require a headless service, such as
[StatefulSets](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/),
Linkerd's multi-cluster extension can be configured with support for headless
services to preserve the service type. Exported services that are headless will
be mirrored in a source cluster as headless, preserving functionality such as
DNS record creation and the ability to address an individual pod.
This guide will walk you through installing and configuring Linkerd and the
multi-cluster extension with support for headless services and will exemplify
how a StatefulSet can be deployed in a target cluster. After deploying, we will
also look at how to communicate with an arbitrary pod from the target cluster's
StatefulSet from a client in the source cluster. For a more detailed overview
on how multi-cluster support for headless services work, check out
[multi-cluster communication](../../features/multicluster/).
## Prerequisites
- Two Kubernetes clusters. They will be referred to as `east` and `west` with
east being the "source" cluster and "west" the target cluster respectively.
These can be in any cloud or local environment, this guide will make use of
[k3d](https://github.com/rancher/k3d/releases/tag/v4.1.1) to configure two
local clusters.
- [`smallstep/CLI`](https://github.com/smallstep/cli/releases) to generate
certificates for Linkerd installation.
- [`linkerd:stable-2.11.0`](https://github.com/linkerd/linkerd2/releases) to
install Linkerd.
To help with cluster creation and installation, there is a demo repository
available. Throughout the guide, we will be using the scripts from the
repository, but you can follow along without cloning or using the scripts.
## Install Linkerd multi-cluster with headless support
To start our demo and see everything in practice, we will go through a
multi-cluster scenario where a pod in an `east` cluster will try to communicate
to an arbitrary pod from a `west` cluster.
The first step is to clone the demo
repository on your local machine.
```sh
# clone example repository
$ git clone git@github.com:mateiidavid/l2d-k3d-statefulset.git
$ cd l2d-k3d-statefulset
```
The second step consists of creating two `k3d` clusters named `east` and
`west`, where the `east` cluster is the source and the `west` cluster is the
target. When creating our clusters, we need a shared trust root. Luckily, the
repository you have just cloned includes a handful of scripts that will greatly
simplify everything.
```sh
# create k3d clusters
$ ./create.sh
# list the clusters
$ k3d cluster list
NAME SERVERS AGENTS LOADBALANCER
east 1/1 0/0 true
west 1/1 0/0 true
```
Once our clusters are created, we will install Linkerd and the multi-cluster
extension. Finally, once both are installed, we need to link the two clusters
together so their services may be mirrored. To enable support for headless
services, we will pass an additional `--set "enableHeadlessServices=true` flag
to `linkerd multicluster link`. As before, these steps are automated through
the provided scripts, but feel free to have a look!
```sh
# Install Linkerd and multicluster, output to check should be a success
$ ./install.sh
# Next, link the two clusters together
$ ./link.sh
```
Perfect! If you've made it this far with no errors, then it's a good sign. In
the next chapter, we'll deploy some services and look at how communication
works.
## Pod-to-Pod: from east, to west
With our install steps out of the way, we can now focus on our pod-to-pod
communication. First, we will deploy our pods and services:
- We will mesh the default namespaces in `east` and `west`.
- In `west`, we will deploy an nginx StatefulSet with its own headless
service, `nginx-svc`.
- In `east`, our script will deploy a `curl` pod that will then be used to
curl the nginx service.
```sh
# deploy services and mesh namespaces
$ ./deploy.sh
# verify both clusters
#
# verify east
$ kubectl --context=k3d-east get pods
NAME READY STATUS RESTARTS AGE
curl-56dc7d945d-96r6p 2/2 Running 0 7s
# verify west has headless service
$ kubectl --context=k3d-west get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 10m
nginx-svc ClusterIP None <none> 80/TCP 8s
# verify west has statefulset
#
# this may take a while to come up
$ kubectl --context=k3d-west get pods
NAME READY STATUS RESTARTS AGE
nginx-set-0 2/2 Running 0 53s
nginx-set-1 2/2 Running 0 43s
nginx-set-2 2/2 Running 0 36s
```
Before we go further, let's have a look at the endpoints object for the
`nginx-svc`:
```sh
$ kubectl --context=k3d-west get endpoints nginx-svc -o yaml
...
subsets:
- addresses:
- hostname: nginx-set-0
ip: 10.42.0.31
nodeName: k3d-west-server-0
targetRef:
kind: Pod
name: nginx-set-0
namespace: default
resourceVersion: "114743"
uid: 7049f1c1-55dc-4b7b-a598-27003409d274
- hostname: nginx-set-1
ip: 10.42.0.32
nodeName: k3d-west-server-0
targetRef:
kind: Pod
name: nginx-set-1
namespace: default
resourceVersion: "114775"
uid: 60df15fd-9db0-4830-9c8f-e682f3000800
- hostname: nginx-set-2
ip: 10.42.0.33
nodeName: k3d-west-server-0
targetRef:
kind: Pod
name: nginx-set-2
namespace: default
resourceVersion: "114808"
uid: 3873bc34-26c4-454d-bd3d-7c783de16304
```
We can see, based on the endpoints object that the service has three endpoints,
with each endpoint having an address (or IP) whose hostname corresponds to a
StatefulSet pod. If we were to do a curl to any of these endpoints directly, we
would get an answer back. We can test this out by applying the curl pod to the
`west` cluster:
```sh
$ kubectl --context=k3d-west apply -f east/curl.yml
$ kubectl --context=k3d-west get pods
NAME READY STATUS RESTARTS AGE
nginx-set-0 2/2 Running 0 5m8s
nginx-set-1 2/2 Running 0 4m58s
nginx-set-2 2/2 Running 0 4m51s
curl-56dc7d945d-s4n8j 0/2 PodInitializing 0 4s
$ kubectl --context=k3d-west exec -it curl-56dc7d945d-s4n8j -c curl -- bin/sh
/$ # prompt for curl pod
```
If we now curl one of these instances, we will get back a response.
```sh
# exec'd on the pod
/ $ curl nginx-set-0.nginx-svc.default.svc.west.cluster.local
"<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>"
```
Now, let's do the same, but this time from the `east` cluster. We will first
export the service.
```sh
$ kubectl --context=k3d-west label service nginx-svc mirror.linkerd.io/exported="true"
service/nginx-svc labeled
$ kubectl --context=k3d-east get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 20h
nginx-svc-west ClusterIP None <none> 80/TCP 29s
nginx-set-0-west ClusterIP 10.43.179.60 <none> 80/TCP 29s
nginx-set-1-west ClusterIP 10.43.218.18 <none> 80/TCP 29s
nginx-set-2-west ClusterIP 10.43.245.244 <none> 80/TCP 29s
```
If we take a look at the endpoints object, we will notice something odd, the
endpoints for `nginx-svc-west` will have the same hostnames, but each hostname
will point to one of the services we see above:
```sh
$ kubectl --context=k3d-east get endpoints nginx-svc-west -o yaml
subsets:
- addresses:
- hostname: nginx-set-0
ip: 10.43.179.60
- hostname: nginx-set-1
ip: 10.43.218.18
- hostname: nginx-set-2
ip: 10.43.245.244
```
This is what we outlined at the start of the tutorial. Each pod from the target
cluster (`west`), will be mirrored as a clusterIP service. We will see in a
second why this matters.
```sh
$ kubectl --context=k3d-east get pods
NAME READY STATUS RESTARTS AGE
curl-56dc7d945d-96r6p 2/2 Running 0 23m
# exec and curl
$ kubectl --context=k3d-east exec pod curl-56dc7d945d-96r6p -it -c curl -- bin/sh
# we want to curl the same hostname we see in the endpoints object above.
# however, the service and cluster domain will now be different, since we
# are in a different cluster.
#
/ $ curl nginx-set-0.nginx-svc-west.default.svc.east.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```
As you can see, we get the same response back! But, nginx is in a different
cluster. So, what happened behind the scenes?
1. When we mirrored the headless service, we created a clusterIP service for
each pod. Since services create DNS records, naming each endpoint with the
hostname from the target gave us these pod FQDNs
(`nginx-set-0.(...).cluster.local`).
2. Curl resolved the pod DNS name to an IP address. In our case, this IP
would be `10.43.179.60`.
3. Once the request is in-flight, the linkerd2-proxy intercepts it. It looks
at the IP address and associates it with our `clusterIP` service. The
service itself points to the gateway, so the proxy forwards the request to
the target cluster gateway. This is the usual multi-cluster scenario.
4. The gateway in the target cluster looks at the request and looks-up the
original destination address. In our case, since this is an "endpoint
mirror", it knows it has to go to `nginx-set-0.nginx-svc` in the same
cluster.
5. The request is again forwarded by the gateway to the pod, and the response
comes back.
And that's it! You can now send requests to pods across clusters. Querying any
of the 3 StatefulSet pods should have the same results.
{{< note >}}
To mirror a headless service as headless, the service's endpoints
must also have at least one named address (e.g a hostname for an IP),
otherwise, there will be no endpoints to mirror so the service will be mirrored
as `clusterIP`. A headless service may under normal conditions also be created
without exposing a port; the mulit-cluster service-mirror does not support
this, however, since the lack of ports means we cannot create a service that
passes Kubernetes validation.
{{< /note >}}
## Cleanup
To clean-up, you can remove both clusters entirely using the k3d CLI:
```sh
$ k3d cluster delete east
cluster east deleted
$ k3d cluster delete west
cluster west deleted
```

View File

@ -0,0 +1,520 @@
+++
title = "Multi-cluster communication"
description = "Allow Linkerd to manage cross-cluster communication."
+++
This guide will walk you through installing and configuring Linkerd so that two
clusters can talk to services hosted on both. There are a lot of moving parts
and concepts here, so it is valuable to read through our
[introduction](../../features/multicluster/) that explains how this works beneath
the hood. By the end of this guide, you will understand how to split traffic
between services that live on different clusters.
At a high level, you will:
1. [Install Linkerd](#install-linkerd) on two clusters with a shared trust
anchor.
1. [Prepare](#preparing-your-cluster) the clusters.
1. [Link](#linking-the-clusters) the clusters.
1. [Install](#installing-the-test-services) the demo.
1. [Export](#exporting-the-services) the demo services, to control visibility.
1. [Verify](#security) the security of your clusters.
1. [Split traffic](#traffic-splitting) from pods on the source cluster (`west`)
to the target cluster (`east`)
## Prerequisites
- Two clusters. We will refer to them as `east` and `west` in this guide. Follow
along with the
[blog post](/2020/02/25/multicluster-kubernetes-with-service-mirroring/) as
you walk through this guide! The easiest way to do this for development is
running a [kind](https://kind.sigs.k8s.io/docs/user/quick-start/) or
[k3d](https://github.com/rancher/k3d#usage) cluster locally on your laptop and
one remotely on a cloud provider, such as
[AKS](https://azure.microsoft.com/en-us/services/kubernetes-service/).
- Each of these clusters should be configured as `kubectl`
[contexts](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/).
We'd recommend you use the names `east` and `west` so that you can follow
along with this guide. It is easy to
[rename contexts](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#-em-rename-context-em-)
with `kubectl`, so don't feel like you need to keep it all named this way
forever.
- Elevated privileges on both clusters. We'll be creating service accounts and
granting extended privileges, so you'll need to be able to do that on your
test clusters.
- Linkerd's `viz` extension should be installed in order to run `stat` commands,
view the Grafana or Linkerd dashboard and run the `linkerd multicluster gateways`
command.
- Support for services of type `LoadBalancer` in the `east` cluster. Check out
the documentation for your cluster provider or take a look at
[inlets](https://blog.alexellis.io/ingress-for-your-local-kubernetes-cluster/).
This is what the `west` cluster will use to communicate with `east` via the
gateway.
## Install Linkerd
{{< fig
alt="install"
title="Two Clusters"
center="true"
src="/images/multicluster/install.svg" >}}
Linkerd requires a shared
[trust anchor](https://linkerd.io../generate-certificates/#trust-anchor-certificate)
to exist between the installations in all clusters that communicate with each
other. This is used to encrypt the traffic between clusters and authorize
requests that reach the gateway so that your cluster is not open to the public
internet. Instead of letting `linkerd` generate everything, we'll need to
generate the credentials and use them as configuration for the `install`
command.
We like to use the [step](https://smallstep.com/cli/) CLI to generate these
certificates. If you prefer `openssl` instead, feel free to use that! To
generate the trust anchor with step, you can run:
```bash
step certificate create root.linkerd.cluster.local root.crt root.key \
--profile root-ca --no-password --insecure
```
This certificate will form the common base of trust between all your clusters.
Each proxy will get a copy of this certificate and use it to validate the
certificates that it receives from peers as part of the mTLS handshake. With a
common base of trust, we now need to generate a certificate that can be used in
each cluster to issue certificates to the proxies. If you'd like to get a deeper
picture into how this all works, check out the
[deep dive](../../features/automatic-mtls/#how-does-it-work).
The trust anchor that we've generated is a self-signed certificate which can be
used to create new certificates (a certificate authority). To generate the
[issuer credentials](../generate-certificates/#issuer-certificate-and-key)
using the trust anchor, run:
```bash
step certificate create identity.linkerd.cluster.local issuer.crt issuer.key \
--profile intermediate-ca --not-after 8760h --no-password --insecure \
--ca root.crt --ca-key root.key
```
An `identity` service in your cluster will use the certificate and key that you
generated here to generate the certificates that each individual proxy uses.
While we will be using the same issuer credentials on each cluster for this
guide, it is a good idea to have separate ones for each cluster. Read through
the [certificate documentation](../generate-certificates/) for more
details.
With a valid trust anchor and issuer credentials, we can install Linkerd on your
`west` and `east` clusters now.
```bash
linkerd install \
--identity-trust-anchors-file root.crt \
--identity-issuer-certificate-file issuer.crt \
--identity-issuer-key-file issuer.key \
| tee \
>(kubectl --context=west apply -f -) \
>(kubectl --context=east apply -f -)
```
The output from `install` will get applied to each cluster and come up! You can
verify that everything has come up successfully with `check`.
```bash
for ctx in west east; do
echo "Checking cluster: ${ctx} .........\n"
linkerd --context=${ctx} check || break
echo "-------------\n"
done
```
## Preparing your cluster
{{< fig
alt="preparation"
title="Preparation"
center="true"
src="/images/multicluster/prep-overview.svg" >}}
In order to route traffic between clusters, Linkerd leverages Kubernetes
services so that your application code does not need to change and there is
nothing new to learn. This requires a gateway component that routes incoming
requests to the correct internal service. The gateway will be exposed to the
public internet via a `Service` of type `LoadBalancer`. Only requests verified
through Linkerd's mTLS (with a shared trust anchor) will be allowed through this
gateway. If you're interested, we go into more detail as to why this is
important in [architecting for multicluster Kubernetes](/2020/02/17/architecting-for-multicluster-kubernetes/#requirement-i-support-hierarchical-networks).
To install the multicluster components on both `west` and `east`, you can run:
```bash
for ctx in west east; do
echo "Installing on cluster: ${ctx} ........."
linkerd --context=${ctx} multicluster install | \
kubectl --context=${ctx} apply -f - || break
echo "-------------\n"
done
```
{{< fig
alt="install"
title="Components"
center="true"
src="/images/multicluster/components.svg" >}}
Installed into the `linkerd-multicluster` namespace, the gateway is a simple
[pause container](https://github.com/linkerd/linkerd2/blob/main/multicluster/charts/linkerd-multicluster/templates/gateway.yaml#L3)
which has been injected with the Linkerd proxy. On the inbound side, Linkerd
takes care of validating that the connection uses a TLS certificate that is part
of the trust anchor, then handles the outbound connection. At this point, the
Linkerd proxy is operating like any other in the data plane and forwards the
requests to the correct service. Make sure the gateway comes up successfully by
running:
```bash
for ctx in west east; do
echo "Checking gateway on cluster: ${ctx} ........."
kubectl --context=${ctx} -n linkerd-multicluster \
rollout status deploy/linkerd-gateway || break
echo "-------------\n"
done
```
Double check that the load balancer was able to allocate a public IP address by
running:
```bash
for ctx in west east; do
printf "Checking cluster: ${ctx} ........."
while [ "$(kubectl --context=${ctx} -n linkerd-multicluster get service \
-o 'custom-columns=:.status.loadBalancer.ingress[0].ip' \
--no-headers)" = "<none>" ]; do
printf '.'
sleep 1
done
printf "\n"
done
```
Every cluster is now running the multicluster control plane and ready to start
mirroring services. We'll want to link the clusters together now!
## Linking the clusters
{{< fig
alt="link-clusters"
title="Link"
center="true"
src="/images/multicluster/link-flow.svg" >}}
For `west` to mirror services from `east`, the `west` cluster needs to have
credentials so that it can watch for services in `east` to be exported. You'd
not want anyone to be able to introspect what's running on your cluster after
all! The credentials consist of a service account to authenticate the service
mirror as well as a `ClusterRole` and `ClusterRoleBinding` to allow watching
services. In total, the service mirror component uses these credentials to watch
services on `east` or the target cluster and add/remove them from itself
(`west`). There is a default set added as part of
`linkerd multicluster install`, but if you would like to have separate
credentials for every cluster you can run `linkerd multicluster allow`.
The next step is to link `west` to `east`. This will create a credentials
secret, a Link resource, and a service-mirror controller. The credentials secret
contains a kubeconfig which can be used to access the target (`east`) cluster's
Kubernetes API. The Link resource is custom resource that configures service
mirroring and contains things such as the gateway address, gateway identity,
and the label selector to use when determining which services to mirror. The
service-mirror controller uses the Link and the secret to find services on
the target cluster that match the given label selector and copy them into
the source (local) cluster.
To link the `west` cluster to the `east` one, run:
```bash
linkerd --context=east multicluster link --cluster-name east |
kubectl --context=west apply -f -
```
Linkerd will look at your current `east` context, extract the `cluster`
configuration which contains the server location as well as the CA bundle. It
will then fetch the `ServiceAccount` token and merge these pieces of
configuration into a kubeconfig that is a secret.
Running `check` again will make sure that the service mirror has discovered this
secret and can reach `east`.
```bash
linkerd --context=west multicluster check
```
Additionally, the `east` gateway should now show up in the list:
```bash
linkerd --context=west multicluster gateways
```
{{< note >}} `link` assumes that the two clusters will connect to each other
with the same configuration as you're using locally. If this is not the case,
you'll want to use the `--api-server-address` flag for `link`.{{< /note >}}
## Installing the test services
{{< fig
alt="test-services"
title="Topology"
center="true"
src="/images/multicluster/example-topology.svg" >}}
It is time to test this all out! The first step is to add some services that we
can mirror. To add these to both clusters, you can run:
```bash
for ctx in west east; do
echo "Adding test services on cluster: ${ctx} ........."
kubectl --context=${ctx} apply \
-k "github.com/linkerd/website/multicluster/${ctx}/"
kubectl --context=${ctx} -n test \
rollout status deploy/podinfo || break
echo "-------------\n"
done
```
You'll now have a `test` namespace running two deployments in each cluster -
frontend and podinfo. `podinfo` has been configured slightly differently in each
cluster with a different name and color so that we can tell where requests are
going.
To see what it looks like from the `west` cluster right now, you can run:
```bash
kubectl --context=west -n test port-forward svc/frontend 8080
```
{{< fig
alt="west-podinfo"
title="West Podinfo"
center="true"
src="/images/multicluster/west-podinfo.gif" >}}
With the podinfo landing page available at
[http://localhost:8080](http://localhost:8080), you can see how it looks in the
`west` cluster right now. Alternatively, running `curl http://localhost:8080`
will return a JSON response that looks something like:
```json
{
"hostname": "podinfo-5c8cf55777-zbfls",
"version": "4.0.2",
"revision": "b4138fdb4dce7b34b6fc46069f70bb295aa8963c",
"color": "#6c757d",
"logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif",
"message": "greetings from west",
"goos": "linux",
"goarch": "amd64",
"runtime": "go1.14.3",
"num_goroutine": "8",
"num_cpu": "4"
}
```
Notice that the `message` references the `west` cluster name.
## Exporting the services
To make sure sensitive services are not mirrored and cluster performance is
impacted by the creation or deletion of services, we require that services be
explicitly exported. For the purposes of this guide, we will be exporting the
`podinfo` service from the `east` cluster to the `west` cluster. To do this, we
must first export the `podinfo` service in the `east` cluster. You can do this
by adding the `mirror.linkerd.io/exported` label:
```bash
kubectl --context=east label svc -n test podinfo mirror.linkerd.io/exported=true
```
{{< note >}} You can configure a different label selector by using the
`--selector` flag on the `linkerd multicluster link` command or by editting
the Link resource created by the `linkerd multicluster link` command.
{{< /note >}}
Check out the service that was just created by the service mirror controller!
```bash
kubectl --context=west -n test get svc podinfo-east
```
From the
[architecture](https://linkerd.io/2020/02/25/multicluster-kubernetes-with-service-mirroring/#step-2-endpoint-juggling),
you'll remember that the service mirror component is doing more than just moving
services over. It is also managing the endpoints on the mirrored service. To
verify that is setup correctly, you can check the endpoints in `west` and verify
that they match the gateway's public IP address in `east`.
```bash
kubectl --context=west -n test get endpoints podinfo-east \
-o 'custom-columns=ENDPOINT_IP:.subsets[*].addresses[*].ip'
kubectl --context=east -n linkerd-multicluster get svc linkerd-gateway \
-o "custom-columns=GATEWAY_IP:.status.loadBalancer.ingress[*].ip"
```
At this point, we can hit the `podinfo` service in `east` from the `west`
cluster. This requires the client to be meshed, so let's run `curl` from within
the frontend pod:
```bash
kubectl --context=west -n test exec -c nginx -it \
$(kubectl --context=west -n test get po -l app=frontend \
--no-headers -o custom-columns=:.metadata.name) \
-- /bin/sh -c "apk add curl && curl http://podinfo-east:9898"
```
You'll see the `greeting from east` message! Requests from the `frontend` pod
running in `west` are being transparently forwarded to `east`. Assuming that
you're still port forwarding from the previous step, you can also reach this
from your browser at [http://localhost:8080/east](http://localhost:8080/east).
Refresh a couple times and you'll be able to get metrics from `linkerd viz stat`
as well.
```bash
linkerd --context=west -n test viz stat --from deploy/frontend svc
```
We also provide a grafana dashboard to get a feel for what's going on here. You
can get to it by running `linkerd --context=west viz dashboard` and going to
[http://localhost:50750/grafana/](http://localhost:50750/grafana/d/linkerd-multicluster/linkerd-multicluster?orgId=1&refresh=1m)
{{< fig
alt="grafana-dashboard"
title="Grafana"
center="true"
src="/images/multicluster/grafana-dashboard.png" >}}
## Security
By default, requests will be going across the public internet. Linkerd extends
its [automatic mTLS](../../features/automatic-mtls/) across clusters to make sure
that the communication going across the public internet is encrypted. If you'd
like to have a deep dive on how to validate this, check out the
[docs](../securing-your-service/). To quickly check, however, you can run:
```bash
linkerd --context=west -n test viz tap deploy/frontend | \
grep "$(kubectl --context=east -n linkerd-multicluster get svc linkerd-gateway \
-o "custom-columns=GATEWAY_IP:.status.loadBalancer.ingress[*].ip")"
```
`tls=true` tells you that the requests are being encrypted!
{{< note >}} As `linkerd edges` works on concrete resources and cannot see two
clusters at once, it is not currently able to show the edges between pods in
`east` and `west`. This is the reason we're using `tap` to validate mTLS here.
{{< /note >}}
In addition to making sure all your requests are encrypted, it is important to
block arbitrary requests coming into your cluster. We do this by validating that
requests are coming from clients in the mesh. To do this validation, we rely on
a shared trust anchor between clusters. To see what happens when a client is
outside the mesh, you can run:
```bash
kubectl --context=west -n test run -it --rm --image=alpine:3 test -- \
/bin/sh -c "apk add curl && curl -vv http://podinfo-east:9898"
```
## Traffic Splitting
{{< fig
alt="with-split"
title="Traffic Split"
center="true"
src="/images/multicluster/with-split.svg" >}}
It is pretty useful to have services automatically show up in clusters and be
able to explicitly address them, however that only covers one use case for
operating multiple clusters. Another scenario for multicluster is failover. In a
failover scenario, you don't have time to update the configuration. Instead, you
need to be able to leave the application alone and just change the routing. If
this sounds a lot like how we do [canary](../canary-release/) deployments,
you'd be correct!
`TrafficSplit` allows us to define weights between multiple services and split
traffic between them. In a failover scenario, you want to do this slowly as to
make sure you don't overload the other cluster or trip any SLOs because of the
added latency. To get this all working with our scenario, let's split between
the `podinfo` service in `west` and `east`. To configure this, you'll run:
```bash
cat <<EOF | kubectl --context=west apply -f -
apiVersion: split.smi-spec.io/v1alpha1
kind: TrafficSplit
metadata:
name: podinfo
namespace: test
spec:
service: podinfo
backends:
- service: podinfo
weight: 50
- service: podinfo-east
weight: 50
EOF
```
Any requests to `podinfo` will now be forwarded to the `podinfo-east` cluster
50% of the time and the local `podinfo` service the other 50%. Requests sent to
`podinfo-east` end up in the `east` cluster, so we've now effectively failed
over 50% of the traffic from `west` to `east`.
If you're still running `port-forward`, you can send your browser to
[http://localhost:8080](http://localhost:8080). Refreshing the page should show
both clusters.Alternatively, for the command line approach,
`curl localhost:8080` will give you a message that greets from `west` and
`east`.
{{< fig
alt="podinfo-split"
title="Cross Cluster Podinfo"
center="true"
src="/images/multicluster/split-podinfo.gif" >}}
You can also watch what's happening with metrics. To see the source side of
things (`west`), you can run:
```bash
linkerd --context=west -n test viz stat trafficsplit
```
It is also possible to watch this from the target (`east`) side by running:
```bash
linkerd --context=east -n test viz stat \
--from deploy/linkerd-gateway \
--from-namespace linkerd-multicluster \
deploy/podinfo
```
There's even a dashboard! Run `linkerd viz dashboard` and send your browser to
[localhost:50750](http://localhost:50750/namespaces/test/trafficsplits/podinfo).
{{< fig
alt="podinfo-split"
title="Cross Cluster Podinfo"
center="true"
src="/images/multicluster/ts-dashboard.png" >}}
## Cleanup
To cleanup the multicluster control plane, you can run:
```bash
for ctx in west east; do
linkerd --context=${ctx} multicluster uninstall | kubectl --context=${ctx} delete -f -
done
```
If you'd also like to remove your Linkerd installation, run:
```bash
for ctx in west east; do
linkerd --context=${ctx} uninstall | kubectl --context=${ctx} delete -f -
done
```

View File

@ -0,0 +1,123 @@
+++
title = "Replacing expired certificates"
description = "Follow this workflow if any of your TLS certs have expired."
+++
If any of your TLS certs are approaching expiry and you are not relying on an
external certificate management solution such as `cert-manager`, you can follow
[Rotating your identity certificates](../rotating_identity_certificates/)
to update them without incurring downtime. In case you are in a situation where
any of your certs are expired however, you are already in an invalid state and
any measures to avoid downtime are not guaranteed to give results. Therefore it
is best to proceed with replacing the certificates with valid ones.
## Replacing only the issuer certificate
It might be the case that your issuer certificate is expired. If this it true
running `linkerd check --proxy` will produce output similar to:
```bash
linkerd-identity
----------------
√ certificate config is valid
√ trust roots are using supported crypto algorithm
√ trust roots are within their validity period
√ trust roots are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
× issuer cert is within its validity period
issuer certificate is not valid anymore. Expired on 2019-12-19T09:21:08Z
see https://linkerd.io/checks/#l5d-identity-issuer-cert-is-time-valid for hints
```
In this situation, if you have installed Linkerd with a manually supplied trust
root and you have its key, you can follow
[Updating the identity issuer certificate](../manually-rotating-control-plane-tls-credentials/#rotating-the-identity-issuer-certificate)
to update your expired cert.
## Replacing the root and issuer certificates
If your root certificate is expired or you do not have its key, you need to
replace both your root and issuer certificates at the same time. If your root
has expired `linkerd check` will indicate that by outputting an error similar
to:
```bash
linkerd-identity
----------------
√ certificate config is valid
√ trust roots are using supported crypto algorithm
× trust roots are within their validity period
Invalid roots:
* 272080721524060688352608293567629376512 identity.linkerd.cluster.local not valid anymore. Expired on 2019-12-19T10:05:31Z
see https://linkerd.io/checks/#l5d-identity-roots-are-time-valid for hints
```
You can follow [Generating your own mTLS root certificates](../generate-certificates/#generating-the-certificates-with-step)
to create new root and issuer certificates. Then use the `linkerd upgrade`
command:
```bash
linkerd upgrade \
--identity-issuer-certificate-file=./issuer-new.crt \
--identity-issuer-key-file=./issuer-new.key \
--identity-trust-anchors-file=./ca-new.crt \
--force \
| kubectl apply -f -
```
Usually `upgrade` will prevent you from using an issuer certificate that
will not work with the roots your meshed pods are using. At that point we
do not need this check as we are updating both the root and issuer certs at
the same time. Therefore we use the `--force` flag to ignore this error.
If you run `linkerd check --proxy` you might see some warning, while the
upgrade process is being performed:
```bash
linkerd-identity
----------------
√ certificate config is valid
√ trust roots are using supported crypto algorithm
√ trust roots are within their validity period
√ trust roots are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
√ issuer cert is valid for at least 60 days
√ issuer cert is issued by the trust root
linkerd-identity-data-plane
---------------------------
‼ data plane proxies certificate match CA
Some pods do not have the current trust bundle and must be restarted:
* linkerd/linkerd-controller-5b69fd4fcc-7skqb
* linkerd/linkerd-destination-749df5c74-brchg
* linkerd/linkerd-grafana-6dcf86b74b-vvxjq
* linkerd/linkerd-prometheus-74cb4f4b69-kqtss
* linkerd/linkerd-proxy-injector-cbd5545bd-rblq5
* linkerd/linkerd-sp-validator-6ff949649f-gjgfl
* linkerd/linkerd-tap-7b5bb954b6-zl9w6
* linkerd/linkerd-web-84c555f78-v7t44
see https://linkerd.io/checks/#l5d-identity-data-plane-proxies-certs-match-ca for hints
```
Additionally you can use the `kubectl rollout restart` command to bring the
configuration of your other injected resources up to date, and then the `check`
command should stop producing warning or errors:
```bash
linkerd-identity
----------------
√ certificate config is valid
√ trust roots are using supported crypto algorithm
√ trust roots are within their validity period
√ trust roots are valid for at least 60 days
√ issuer cert is using supported crypto algorithm
√ issuer cert is within its validity period
√ issuer cert is valid for at least 60 days
√ issuer cert is issued by the trust root
linkerd-identity-data-plane
---------------------------
√ data plane proxies certificate match CA
```

View File

@ -0,0 +1,157 @@
+++
title = "Restricting Access To Services"
description = "Use Linkerd policy to restrict access to a service."
+++
Linkerd policy resources can be used to restrict which clients may access a
service. In this example, we'll use Emojivoto to show how to restrict access
to the Voting service so that it may only be called from the Web service.
For a more comprehensive description of the policy resources, see the
[Policy reference docs](../../reference/authorization-policy/).
## Setup
Ensure that you have Linkerd version stable-2.11.0 or later installed, and that
it is healthy:
```console
$ linkerd install | kubectl apply -f -
...
$ linkerd check -o short
...
```
Inject and install the Emojivoto application:
```console
$ linkerd inject https://run.linkerd.io/emojivoto.yml | kubectl apply -f -
...
$ linkerd check -n emojivoto --proxy -o short
...
```
In order to observe what's going on, we'll also install the Viz extension:
```console
$ linkerd viz install | kubectl apply -f -
...
$ linkerd viz check
...
```
## Creating a Server resource
We start by creating a `Server` resource for the Voting service. A `Server`
is a Linkerd custom resource which describes a specific port of a workload.
Once the `Server` resource has been created, only clients which have been
authorized may access it (we'll see how to authorize clients in a moment).
```console
cat << EOF | kubectl apply -f -
---
apiVersion: policy.linkerd.io/v1beta1
kind: Server
metadata:
namespace: emojivoto
name: voting-grpc
labels:
app: voting-svc
spec:
podSelector:
matchLabels:
app: voting-svc
port: grpc
proxyProtocol: gRPC
EOF
```
We see that this `Server` uses a `podSelector` to select the pods that it
describes: in this case the voting service pods. It also specifies the named
port (grpc) that it applies to. Finally, it specifies the protocol that is
served on this port. This ensures that the proxy treats traffic correctly and
allows it skip protocol detection.
At this point, no clients have been authorized to access this service and you
will likely see a drop in success rate as requests from the Web service to
Voting start to get rejected.
We can use the `linkerd viz authz` command to look at the authorization status
of requests coming to the voting service and see that all incoming requests
are currently unauthorized:
```console
> linkerd viz authz -n emojivoto deploy/voting
SERVER AUTHZ SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
voting-grpc [UNAUTHORIZED] - 0.9rps - - -
```
## Creating a ServerAuthorization resource
A `ServerAuthorization` grants a set of clients access to a set of `Servers`.
Here we will create a `ServerAuthorization` which grants the Web service access
to the Voting `Server` we created above. Note that meshed mTLS uses
`ServiceAccounts` as the basis for identity, thus our authorization will also
be based on `ServiceAccounts`.
```console
cat << EOF | kubectl apply -f -
---
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
namespace: emojivoto
name: voting-grpc
labels:
app.kubernetes.io/part-of: emojivoto
app.kubernetes.io/name: voting
app.kubernetes.io/version: v11
spec:
server:
name: voting-grpc
# The voting service only allows requests from the web service.
client:
meshTLS:
serviceAccounts:
- name: web
EOF
```
With this in place, we can now see that all of the requests to the Voting
service are authorized by the `voting-grpc` ServerAuthorization. Note that since
the `linkerd viz auth` command queries over a time-window, you may see some
UNAUTHORIZED requests displayed for a short amount of time.
```console
> linkerd viz authz -n emojivoto deploy/voting
SERVER AUTHZ SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99
voting-grpc voting-grpc 70.00% 1.0rps 1ms 1ms 1ms
```
We can also test that request from other pods will be rejected by creating a
`grpcurl` pod and attempting to access the Voting service from it:
```console
> kubectl run grpcurl --rm -it --image=networld/grpcurl --restart=Never --command -- ./grpcurl -plaintext voting-svc.emojivoto:8080 emojivoto.v1.VotingService/VoteDog
Error invoking method "emojivoto.v1.VotingService/VoteDog": failed to query for service descriptor "emojivoto.v1.VotingService": rpc error: code = PermissionDenied desc =
pod "grpcurl" deleted
pod default/grpcurl terminated (Error)
```
Because this client has not been authorized, this request gets rejected with a
`PermissionDenied` error.
You can create as many `ServerAuthorization` resources as you like to authorize
many different clients. You can also specify whether to authorize
unauthenticated (i.e. unmeshed) client, any authenticated client, or only
authenticated clients with a particular identity. For more details, please see
the [Policy reference docs](../../reference/authorization-policy/).
## Further Considerations
You may have noticed that there was a period of time after we created the
`Server` resource but before we created the `ServerAuthorization` where all
requests were being rejected. To avoid this situation in live systems, we
recommend you either create the policy resources before deploying your services
or to create the `ServiceAuthorizations` BEFORE creating the `Server` so that
clients will be authorized immediately.

View File

@ -0,0 +1,103 @@
+++
title = "Rotating webhooks certificates"
description = "Follow these steps to rotate your Linkerd webhooks certificates."
+++
Linkerd uses the
[Kubernetes admission webhooks](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#admission-webhooks)
and
[extension API server](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/)
to implement some of its core features like
[automatic proxy injection](../../features/proxy-injection/) and
[service profiles validation](../../features/service-profiles/).
Also, the viz extension uses a webhook to make pods tappable, as does the jaeger
extension to turn on tracing on pods.
To secure the connections between the Kubernetes API server and the
webhooks, all the webhooks are TLS-enabled. The x509 certificates used by these
webhooks are issued by the self-signed CA certificates embedded in the webhooks
configuration.
By default, these certificates have a validity period of 365 days. They are
stored in the following secrets:
- In the `linkerd` namespace: `linkerd-proxy-injector-k8s-tls` and `linkerd-sp-validator-k8s-tls`
- In the `linkerd-viz` namespace: `tap-injector-k8s-tls`
- In the `linkerd-jaeger` namespace: `jaeger-injector-k8s-tls`
The rest of this documentation provides instructions on how to renew these
certificates.
## Renewing the webhook certificates
To check the validity of all the TLS secrets
(using [`step`](https://smallstep.com/cli/)):
```bash
# assuming you have viz and jaeger installed, otherwise trim down these arrays
# accordingly
SECRETS=("linkerd-proxy-injector-k8s-tls" "linkerd-sp-validator-k8s-tls" "tap-injector-k8s-tls" "jaeger-injector-k8s-tls")
NS=("linkerd" "linkerd" "linkerd-viz" "linkerd-jaeger")
for idx in "${!SECRETS[@]}"; do \
kubectl -n "${NS[$idx]}" get secret "${SECRETS[$idx]}" -ojsonpath='{.data.tls\.crt}' | \
base64 --decode - | \
step certificate inspect - | \
grep -iA2 validity; \
done
```
Manually delete these secrets and use `upgrade`/`install` to recreate them:
```bash
for idx in "${!SECRETS[@]}"; do \
kubectl -n "${NS[$idx]}" delete secret "${SECRETS[$idx]}"; \
done
linkerd upgrade | kubectl apply -f -
linkerd viz install | kubectl apply -f -
linkerd jaeger install | kubectl apply -f -
```
The above command will recreate the secrets without restarting Linkerd.
{{< note >}}
For Helm users, use the `helm upgrade` command to recreate the deleted secrets.
If you render the helm charts externally and apply them with `kubectl apply`
(e.g. in a CI/CD pipeline), you do not need to delete the secrets manually,
as they wil be overwritten by a new cert and key generated by the helm chart.
{{< /note >}}
Confirm that the secrets are recreated with new certificates:
```bash
for idx in "${!SECRETS[@]}"; do \
kubectl -n "${NS[$idx]}" get secret "${SECRETS[$idx]}" -ojsonpath='{.data.crt\.pem}' | \
base64 --decode - | \
step certificate inspect - | \
grep -iA2 validity; \
done
```
Ensure that Linkerd remains healthy:
```bash
linkerd check
```
Restarting the pods that implement the webhooks and API services is usually not
necessary. But if the cluster is large, or has a high pod churn, it may be
advisable to restart the pods manually, to avoid cascading failures.
If you observe certificate expiry errors or mismatched CA certs, restart their
pods with:
```sh
kubectl -n linkerd rollout restart deploy \
linkerd-proxy-injector \
linkerd-sp-validator \
kubectl -n linkerd-viz rollout restart deploy tap tap-injector
kubectl -n linkerd-jaeger rollout restart deploy jaeger-injector
```

View File

@ -0,0 +1,220 @@
+++
title = "Securing Your Cluster"
description = "Best practices for securing your Linkerd installation."
aliases = [
"../tap-rbac/",
]
+++
Linkerd provides powerful introspection into your Kubernetes cluster and
services. Linkerd installations are secure by default. This page illustrates
best practices to enable this introspection in a secure way.
## Tap
Linkerd's Viz extension includes Tap support. This feature is available via the
following commands:
- [`linkerd viz tap`](../../reference/cli/viz/#tap)
- [`linkerd viz top`](../../reference/cli/viz/#top)
- [`linkerd viz profile --tap`](../../reference/cli/viz/#profile)
- [`linkerd viz dashboard`](../../reference/cli/viz/#dashboard)
Depending on your RBAC setup, you may need to perform additional steps to enable
your user(s) to perform Tap actions.
{{< note >}}
If you are on GKE, skip to the [GKE section below](#gke).
{{< /note >}}
### Check for Tap access
Use `kubectl` to determine whether your user is authorized to perform tap
actions. For more information, see the
[Kubernetes docs on authorization](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#checking-api-access).
To determine if you can watch pods in all namespaces:
```bash
kubectl auth can-i watch pods.tap.linkerd.io --all-namespaces
```
To determine if you can watch deployments in the emojivoto namespace:
```bash
kubectl auth can-i watch deployments.tap.linkerd.io -n emojivoto
```
To determine if a specific user can watch deployments in the emojivoto namespace:
```bash
kubectl auth can-i watch deployments.tap.linkerd.io -n emojivoto --as $(whoami)
```
You can also use the Linkerd CLI's `--as` flag to confirm:
```bash
$ linkerd viz tap -n linkerd deploy/linkerd-controller --as $(whoami)
Cannot connect to Linkerd Viz: namespaces is forbidden: User "XXXX" cannot list resource "namespaces" in API group "" at the cluster scope
Validate the install with: linkerd viz check
...
```
### Enabling Tap access
If the above commands indicate you need additional access, you can enable access
with as much granularity as you choose.
#### Granular Tap access
To enable tap access to all resources in all namespaces, you may bind your user
to the `linkerd-linkerd-tap-admin` ClusterRole, installed by default:
```bash
$ kubectl describe clusterroles/linkerd-linkerd-viz-tap-admin
Name: linkerd-linkerd-viz-tap-admin
Labels: component=tap
linkerd.io/extension=viz
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{},"labels":{"component=tap...
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
*.tap.linkerd.io [] [] [watch]
```
{{< note >}}
This ClusterRole name includes the Linkerd Viz namespace, so it may vary if you
installed Viz into a non-default namespace:
`linkerd-[LINKERD_VIZ_NAMESPACE]-tap-admin`
{{< /note >}}
To bind the `linkerd-linkerd-viz-tap-admin` ClusterRole to a particular user:
```bash
kubectl create clusterrolebinding \
$(whoami)-tap-admin \
--clusterrole=linkerd-linkerd-viz-tap-admin \
--user=$(whoami)
```
You can verify you now have tap access with:
```bash
$ linkerd viz tap -n linkerd deploy/linkerd-controller --as $(whoami)
req id=3:0 proxy=in src=10.244.0.1:37392 dst=10.244.0.13:9996 tls=not_provided_by_remote :method=GET :authority=10.244.0.13:9996 :path=/ping
...
```
#### Cluster admin access
To simply give your user cluster-admin access:
```bash
kubectl create clusterrolebinding \
$(whoami)-cluster-admin \
--clusterrole=cluster-admin \
--user=$(whoami)
```
{{< note >}}
Not recommended for production, only do this for testing/development.
{{< /note >}}
### GKE
Google Kubernetes Engine (GKE) provides access to your Kubernetes cluster via
Google Cloud IAM. See the
[GKE IAM Docs](https://cloud.google.com/kubernetes-engine/docs/how-to/iam) for
more information.
Because GCloud provides this additional level of access, there are cases where
`kubectl auth can-i` will report you have Tap access when your RBAC user may
not. To validate this, check whether your GCloud user has Tap access:
```bash
$ kubectl auth can-i watch pods.tap.linkerd.io --all-namespaces
yes
```
And then validate whether your RBAC user has Tap access:
```bash
$ kubectl auth can-i watch pods.tap.linkerd.io --all-namespaces --as $(gcloud config get-value account)
no - no RBAC policy matched
```
If the second command reported you do not have access, you may enable access
with:
```bash
kubectl create clusterrolebinding \
$(whoami)-tap-admin \
--clusterrole=linkerd-linkerd-viz-tap-admin \
--user=$(gcloud config get-value account)
```
To simply give your user cluster-admin access:
```bash
kubectl create clusterrolebinding \
$(whoami)-cluster-admin \
--clusterrole=cluster-admin \
--user=$(gcloud config get-value account)
```
{{< note >}}
Not recommended for production, only do this for testing/development.
{{< /note >}}
### Linkerd Dashboard tap access
By default, the [Linkerd dashboard](../../features/dashboard/) has the RBAC
privileges necessary to tap resources.
To confirm:
```bash
$ kubectl auth can-i watch pods.tap.linkerd.io --all-namespaces --as system:serviceaccount:linkerd-viz:web
yes
```
This access is enabled via a `linkerd-linkerd-viz-web-admin` ClusterRoleBinding:
```bash
$ kubectl describe clusterrolebindings/linkerd-linkerd-viz-web-admin
Name: linkerd-linkerd-viz-web-admin
Labels: component=web
linkerd.io/extensions=viz
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRoleBinding","metadata":{"annotations":{},"labels":{"component=web...
Role:
Kind: ClusterRole
Name: linkerd-linkerd-viz-tap-admin
Subjects:
Kind Name Namespace
---- ---- ---------
ServiceAccount web linkerd-viz
```
If you would like to restrict the Linkerd dashboard's tap access. You may
install Linkerd viz with the `--set dashboard.restrictPrivileges` flag:
```bash
linkerd viz install --set dashboard.restrictPrivileges
```
This will omit the `linkerd-linkerd-web-admin` ClusterRoleBinding. If you have
already installed Linkerd, you may simply delete the ClusterRoleBinding
manually:
```bash
kubectl delete clusterrolebindings/linkerd-linkerd-viz-web-admin
```
To confirm:
```bash
$ kubectl auth can-i watch pods.tap.linkerd.io --all-namespaces --as system:serviceaccount:linkerd-viz:web
no
```

View File

@ -0,0 +1,148 @@
+++
title = "Setting Up Service Profiles"
description = "Create a service profile that provides more details for Linkerd to build on."
+++
[Service profiles](../../features/service-profiles/) provide Linkerd additional
information about a service and how to handle requests for a service.
When an HTTP (not HTTPS) request is received by a Linkerd proxy,
the `destination service` of that request is identified. If a
service profile for that destination service exists, then that
service profile is used to
to provide [per-route metrics](../getting-per-route-metrics/),
[retries](../configuring-retries/) and
[timeouts](../configuring-timeouts/).
The `destination service` for a request is computed by selecting
the value of the first header to exist of, `l5d-dst-override`,
`:authority`, and `Host`. The port component, if included and
including the colon, is stripped. That value is mapped to the fully
qualified DNS name. When the `destination service` matches the
name of a service profile in the namespace of the sender or the
receiver, Linkerd will use that to provide [per-route
metrics](../getting-per-route-metrics/),
[retries](../configuring-retries/) and
[timeouts](../configuring-timeouts/).
There are times when you may need to define a service profile for
a service which resides in a namespace that you do not control. To
accomplish this, simply create a service profile as before, but
edit the namespace of the service profile to the namespace of the
pod which is calling the service. When Linkerd proxies a request
to a service, a service profile in the source namespace will take
priority over a service profile in the destination namespace.
Your `destination service` may be a [ExternalName
service](https://kubernetes.io/docs/concepts/services-networking/service/#externalname).
In that case, use the `spec.metadata.name` and the
`spec.metadata.namespace' values to name your ServiceProfile. For
example,
```yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
```
use the name `my-service.prod.svc.cluster.local` for the ServiceProfile.
Note that at present, you cannot view statistics gathered for routes
in this ServiceProfile in the web dashboard. You can get the
statistics using the CLI.
For a complete demo walkthrough, check out the
[books](../books/#service-profiles) demo.
There are a couple different ways to use `linkerd profile` to create service
profiles.
{{< pagetoc >}}
Requests which have been associated with a route will have a `rt_route`
annotation. To manually verify if the requests are being associated correctly,
run `tap` on your own deployment:
```bash
linkerd viz tap -o wide <target> | grep req
```
The output will stream the requests that `deploy/webapp` is receiving in real
time. A sample is:
```bash
req id=0:1 proxy=in src=10.1.3.76:57152 dst=10.1.3.74:7000 tls=disabled :method=POST :authority=webapp.default:7000 :path=/books/2878/edit src_res=deploy/traffic src_ns=foobar dst_res=deploy/webapp dst_ns=default rt_route=POST /books/{id}/edit
```
Conversely, if `rt_route` is not present, a request has *not* been associated
with any route. Try running:
```bash
linkerd viz tap -o wide <target> | grep req | grep -v rt_route
```
## Swagger
If you have an [OpenAPI (Swagger)](https://swagger.io/docs/specification/about/)
spec for your service, you can use the `--open-api` flag to generate a service
profile from the OpenAPI spec file.
```bash
linkerd profile --open-api webapp.swagger webapp
```
This generates a service profile from the `webapp.swagger` OpenAPI spec file
for the `webapp` service. The resulting service profile can be piped directly
to `kubectl apply` and will be installed into the service's namespace.
```bash
linkerd profile --open-api webapp.swagger webapp | kubectl apply -f -
```
## Protobuf
If you have a [protobuf](https://developers.google.com/protocol-buffers/) format
for your service, you can use the `--proto` flag to generate a service profile.
```bash
linkerd profile --proto web.proto web-svc
```
This generates a service profile from the `web.proto` format file for the
`web-svc` service. The resulting service profile can be piped directly to
`kubectl apply` and will be installed into the service's namespace.
## Auto-Creation
It is common to not have an OpenAPI spec or a protobuf format. You can also
generate service profiles from watching live traffic. This is based off tap data
and is a great way to understand what service profiles can do for you. To start
this generation process, you can use the `--tap` flag:
```bash
linkerd viz profile -n emojivoto web-svc --tap deploy/web --tap-duration 10s
```
This generates a service profile from the traffic observed to
`deploy/web` over the 10 seconds that this command is running. The resulting service
profile can be piped directly to `kubectl apply` and will be installed into the
service's namespace.
## Template
Alongside all the methods for automatically creating service profiles, you can
get a template that allows you to add routes manually. To generate the template,
run:
```bash
linkerd profile -n emojivoto web-svc --template
```
This generates a service profile template with examples that can be manually
updated. Once you've updated the service profile, use `kubectl apply` to get it
installed into the service's namespace on your cluster.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,41 @@
+++
title = "Uninstalling Multicluster"
description = "Unlink and uninstall Linkerd multicluster."
+++
The Linkerd multicluster components allow for sending traffic from one cluster
to another. For more information on how to set this up, see [installing multicluster](../installing-multicluster/).
## Unlinking
Unlinking a cluster will delete all resources associated with that link
including:
* the service mirror controller
* the Link resource
* the credentials secret
* mirror services
It is recommended that you use the `unlink` command rather than deleting any
of these resources individually to help ensure that all mirror services get
cleaned up correctly and are not left orphaned.
To unlink, run the `linkerd multicluster unlink` command and pipe the output
to `kubectl delete`:
```bash
linkerd multicluster unlink --cluster-name=target | kubectl delete -f -
```
## Uninstalling
Uninstalling the multicluster components will remove all components associated
with Linkerd's multicluster functionality including the gateway and service
account. Before you can uninstall, you must remove all existing links as
described above. Once all links have been removed, run:
```bash
linkerd multicluster uninstall | kubectl delete -f -
```
Attempting to uninstall while at least one link remains will result in an error.

View File

@ -0,0 +1,52 @@
+++
title = "Uninstalling Linkerd"
description = "Linkerd can be easily removed from a Kubernetes cluster."
+++
Removing Linkerd from a Kubernetes cluster requires a few steps: removing any
data plane proxies, removing all the extensions and then removing the core
control plane.
## Removing Linkerd data plane proxies
To remove the Linkerd data plane proxies, you should remove any [Linkerd proxy
injection annotations](../../features/proxy-injection/) and roll the deployments.
When Kubernetes recreates the pods, they will not have the Linkerd data plane
attached.
## Removing extensions
To remove any extension, call its `uninstall` subcommand and pipe it to `kubectl
delete -f -`. For the bundled extensions that means:
```bash
# To remove Linkerd Viz
linkerd viz uninstall | kubectl delete -f -
# To remove Linkerd Jaeger
linkerd jaeger uninstall | kubectl delete -f -
# To remove Linkerd Multicluster
linkerd multicluster uninstall | kubectl delete -f -
```
## Removing the control plane
{{< note >}}
Uninstallating the control plane requires cluster-wide permissions.
{{< /note >}}
To remove the [control plane](../../reference/architecture/#control-plane), run:
```bash
linkerd uninstall | kubectl delete -f -
```
The `linkerd uninstall` command outputs the manifest for all of the Kubernetes
resources necessary for the control plane, including namespaces, service
accounts, CRDs, and more; `kubectl delete` then deletes those resources.
This command can also be used to remove control planes that have been partially
installed. Note that `kubectl delete` will complain about any resources that it
was asked to delete that hadn't been created, but these errors can be safely
ignored.

View File

@ -0,0 +1,109 @@
+++
title = "Upgrading Multicluster in Linkerd 2.9"
description = "Upgrading Multicluster to Linkerd 2.9."
+++
Linkerd 2.9 changes the way that some of the multicluster components work and
are installed compared to Linkerd 2.8.x. Users installing the multicluster
components for the first time with Linkerd 2.9 can ignore these instructions
and instead refer directly to the [installing multicluster](../installing-multicluster/).
Users who installed the multicluster component in Linkerd 2.8.x and wish to
upgrade to Linkerd 2.9 should follow these instructions.
## Overview
The main differences between multicluster in 2.8 and 2.9 is that in 2.9 we
create a service mirror controller for each target cluster that a source
cluster is linked to. The service mirror controller is created as part of the
`linkerd multicluster link` command instead of the `linkerd multicluster install`
command. There is also a new CRD type called `Link` which is used to configure
the service mirror controller and allows you to be able to specify the label
selector used to determine which services to mirror.
## Ordering of Cluster Upgrades
Clusters may be upgraded in any order regardless of if each cluster is source
cluster, target cluster, or both.
## Target Clusters
A cluster which receives multicluster traffic but does not send multicluster
traffic requires no special upgrade treatment. It can safely be upgraded by
just upgrading the main Linkerd controller plane:
```bash
linkerd upgrade | kubectl apply -f -
```
## Source Clusters
A cluster which sends multicluster traffic must be upgraded carefully to ensure
that mirror services remain up during the upgrade so as to avoid downtime.
### Control Plane
Begin by upgrading the Linkerd control plane and multicluster resources to
version 2.9 by running
```bash
linkerd upgrade | kubectl apply -f -
linkerd --context=source multicluster install | kubectl --context=source apply -f -
linkerd --context=target multicluster install | kubectl --context=target apply -f -
```
### Label Exported Services
Next must apply a label to each exported service in the target cluster. This
label is how the 2.9 service mirror controller will know to mirror those
services. The label can be anything you want, but by default we will use
`mirror.linkerd.io/exported=true`. For each exported service in the target
cluster, run:
```bash
kubectl --context=target label svc/<SERVICE NAME> mirror.linkerd.io=true
```
Any services not labeled in this way will no longer be mirrored after the
upgrade is complete.
### Upgrade Link
Next we re-establish the link. This will create a 2.9 version of the service
mirror controller. Note that this is the same command that you used to establish
the link while running Linkerd 2.8 but here we are running it with version 2.9
of the Linkerd CLI:
```bash
linkerd --context=target multicluster link --cluster-name=<CLUSTER NAME> \
| kubectl --context=source apply -f -
```
If you used a label other than `mirror.linkerd.io/exported=true` when labeling
your exported services, you must specify that in the `--selector` flag:
```bash
linkerd --context=target multicluster link --cluster-name=<CLUSTER NAME> \
--selector my.cool.label=true | kubectl --context=source apply -f -
```
There should now be two service mirror deployments running: one from version
2.8 called `linkerd-service-mirror` and one from version 2.9 called
`linkerd-service-mirror-<CLUSTER NAME>`. All mirror services should remain
active and healthy.
### Cleanup
The 2.8 version of the service mirror controller can now be safely deleted:
```bash
kubectl --context=source -n linkerd-multicluster delete deploy/linkerd-service-mirror
```
Ensure that your cluster is still in a healthy state by running:
```bash
linkerd --context=source multicluster check
```
Congratulations, your upgrade is complete!

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,121 @@
+++
title = "Upgrading to Linkerd 2.10: ports and protocols"
description = "Upgrading to Linkerd 2.10 and handling skip-ports, server-speaks-first protocols, and more."
+++
Linkerd 2.10 introduced some significant changes to the way that certain types
of traffic are handled. These changes may require new or different
configuration on your part.
## What were the changes?
The majority of traffic "just works" in Linkerd. However, there are certain
types of traffic that Linkerd cannot handle without additional configuration.
This includes "server-speaks-first" protocols such as MySQL and SMTP, as well
(in some Linkerd versions) protocols such as Postgres and Memcache. Linkerd's
protocol detection logic is unable to efficiently handle these protocols.
In Linkerd 2.9 and earlier, these protocols were handled by simply bypassing
them. Users could mark specific ports as "skip ports" and ensure that traffic
to these ports would not transit Linkerd's data plane proxy. To make this easy,
Linkerd 2.9 and earlier shipped with a default set of skip ports which included
25 (SMTP), 443 (client-initiated TLS), 587 (SMTP), 3306 (MySQL), 5432
(Postgres), and 11211 (Memcache).
In the 2.10 release, Linkerd introduced three changes to the way that protocols
are detected and handled:
1. It added an _opaque ports_ feature, which disables protocol detection on
specific ports. This means Linkerd 2.10 can now handle these protocols and
provide TCP-level metrics, mTLS, etc.
2. It replaced the default list of skip ports with a default list of opaque
ports, covering the same ports. This means that the default behavior for
these protocols is to transit the proxy rather than bypass it.
3. It changed the handling of connections to continue even if protocol
detection times out. This means that attempting e.g. server-speaks-first
protocols through the proxy _without_ skip ports or opaque ports
configuration has a better behavior: instead of failing, the proxy will
forward the connection (with service discovery, TCP load balancing, and
mTLS) after a 10-second connect delay.
## What does this mean for me?
In short, it means that there are several types of traffic that, in Linkerd 2.9
and earlier, simply bypassed the proxy by default, but that in Linkerd 2.10 now
transit the proxy. This is a good thing!—you are using Linkerd for a reason,
after all—but it has some implications in certain situations.
## What do I need to change?
As part of Linkerd 2.10, you may need to update your configuration in certain
situations.
### SMTP, MySQL, Postgres, or Memcache traffic to an off-cluster destination
If you have existing SMTP, MySQL, Postgres, or Memcache traffic to an
off-cluster destination, *on the default port for that protocol*, then you will
need to update your configuration.
**Behavior in 2.9:** Traffic automatically *skips* the proxy.
**Behavior in 2.10:** Traffic automatically *transits* the proxy, and will incur
a 10-second connect delay.
**Steps to upgrade:** Use `skip-outbound-ports` to mark the port so as to
bypass the proxy. (There is currently no ability to use opaque ports in this
situation.)
### Client-initiated TLS calls at startup
If you have client-initiated TLS calls to any destination, on- or off-cluster,
you may have to update your configuration if these connections are made at
application startup and not retried.
**Behavior in 2.9:** Traffic automatically *skips* the proxy.
**Behavior in 2.10:** Traffic automatically *transits* the proxy.
**Steps to upgrade:** See "Connecting at startup" below.
### An existing skip-ports configuration
If you have an existing configuration involving `skip-inbound-ports` or
`skip-outbound-ports` annotations, everything should continue working as is.
However, you may choose to convert this configuration to opaque ports.
**Behavior in 2.9:** Traffic skips the proxy.
**Behavior in 2.10:** Traffic skips the proxy.
**Steps to upgrade:** Optionally, change this configuration to opaque ports to
take advantage of metrics, mTLS (for meshed destinations), etc. See "Connecting
at startup" below if any of these connections happen at application startup and
are not retried.
## Note: Connecting at startup
There is one additional consideration for traffic that previously skipped the
proxy but now transits the proxy. If your application makes connections at
_startup time_, those connections will now require the proxy to be active
before they succeed. Unfortunately, Kubernetes currently provides no container
ordering constraints, so the proxy may not be active before the application
container starts. Thus, if your application does not retry with sufficient
leeway to allow the proxy to start up, it may fail. (This typically manifests
as container restarts.)
To handle this situation, you have four options:
1. Ignore the container restarts. (It's "eventually consistent".)
2. Use [linkerd-await](https://github.com/olix0r/linkerd-await) to make the
application container wait for the proxy to be ready before starting.
3. Set a `skip-outbound-ports` annotation to bypass the proxy for that port.
(You will lose Linkerd's features for that connection.)
4. Add retry logic to the application to make it resilient to transient network
failures.
The last option is arguably the "rightest" approach, but not always the most
practical.
In the future, Kubernetes may provide mechanisms for specifying container
startup ordering, at which point this will no longer be an issue.
## How do I set an opaque port or skip port?
Ports can be marked as opaque ports or as skip ports via Kubernetes
annotations. These annotations can be set at the namespace, workload, or
service level. The `linkerd inject` CLI command provides flags to set these
annotations; they are also settable as defaults in the Helm config.

View File

@ -0,0 +1,52 @@
+++
title = "Using A Private Docker Repository"
description = "Using Linkerd with a Private Docker Repository."
+++
In some cases, you will want to use a private docker repository to store the
Linkerd images. This scenario requires knowing the names and locations of the
docker images used by the Linkerd control and data planes so that you can
store them in your private repository.
The easiest way to get those images is to use the
[Linkerd CLI](../../getting-started/#step-1-install-the-cli)
to pull the images to an internal host and push them to your private repository.
To get the names of the images used by the control plane, [install]
(../../getting-started/#step-1-install-the-cli)
the Linkerd CLI and run this command:
```bash
linkerd install --ignore-cluster | grep image: | sed -e 's/^ *//' | sort | uniq
```
For the current stable version, the output will be:
```bash
image: gcr.io/linkerd-io/controller:stable-2.6.0
image: gcr.io/linkerd-io/grafana:stable-2.6.0
image: gcr.io/linkerd-io/proxy-init:v1.2.0
image: gcr.io/linkerd-io/proxy:stable-2.6.0
image: gcr.io/linkerd-io/web:stable-2.6.0
image: prom/prometheus:v2.11.1
```
All of the Linkerd images are publicly available in the
[Linkerd Google Container Repository](https://console.cloud.google.com/gcr/images/linkerd-io/GLOBAL/)
Stable images are named using the convention `stable-<version>` and the edge
images use the convention `edge-<year>.<month>.<release-number>`.
Examples of each are: `stable-2.6.0` and `edge-2019.11.1`.
Once you have identified which images you want to store in your private
repository, use the `docker pull <image-name>` command to pull the images to
a machine on your network, then use the `docker push` command to push the
images to your private repository.
Now that the images are hosted by your private repository, you can update
your deployment configuration to pull from your private docker repository.
For a more advanced configuration, you can clone the [linkerd2 repository
](https://github.com/linkerd/linkerd2) to your CI/CD system and build
specific tags to push to your private repository.

View File

@ -0,0 +1,31 @@
+++
title = "Using a Custom Cluster Domain"
description = "Use Linkerd with a custom cluster domain."
+++
For Kubernetes clusters that use [custom cluster domain](https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/),
Linkerd must be installed using the `--cluster-domain` option:
```bash
linkerd install --cluster-domain=example.org \
--identity-trust-domain=example.org \
| kubectl apply -f -
# The Linkerd Viz extension also requires a similar setting:
linkerd viz install --set clusterDomain=example.org | kubectl apply -f -
# And so does the Multicluster extension:
linkerd multicluster install --set identityTrustDomain=example.org | kubectl apply -f -
```
This ensures that all Linkerd handles all service discovery, routing, service
profiles and traffic split resources using the `example.org` domain.
{{< note >}}
Note that the identity trust domain must match the cluster domain for mTLS to
work.
{{< /note >}}
{{< note >}}
Changing the cluster domain while upgrading Linkerd isn't supported.
{{< /note >}}

View File

@ -0,0 +1,59 @@
+++
title = "Control Plane Debug Endpoints"
description = "Linkerd's control plane components provide debug endpoints."
+++
All of the control plane components (with the exception of Grafana) expose
runtime profiling information through the path `/debug/pprof`, using Go's
[pprof](https://golang.org/pkg/net/http/pprof/) package.
You can consume the provided data with `go tool pprof` to generate output in
many formats (PDF, DOT, PNG, etc).
The following diagnostics are provided (a summary with links is provided at
`/debug/pprof`):
- allocs: A sampling of all past memory allocations
- block: Stack traces that led to blocking on synchronization primitives
- cmdline: The command line invocation of the current program
- goroutine: Stack traces of all current goroutines
- heap: A sampling of memory allocations of live objects. You can specify the gc
GET parameter to run GC before taking the heap sample.
- mutex: Stack traces of holders of contended mutexes
- profile: CPU profile. You can specify the duration in the seconds GET
parameter. After you get the profile file, use the go tool pprof command to
investigate the profile.
- threadcreate: Stack traces that led to the creation of new OS threads
- trace: A trace of execution of the current program. You can specify the
duration in the seconds GET parameter. After you get the trace file, use the
go tool trace command to investigate the trace.
## Example Usage
This data is served over the `admin-http` port.
To find this port, you can examine the pod's yaml, or for the identity pod for
example, issue a command like so:
```bash
kubectl -n linkerd get po \
$(kubectl -n linkerd get pod -l linkerd.io/control-plane-component=identity \
-o jsonpath='{.items[0].metadata.name}') \
-o=jsonpath='{.spec.containers[*].ports[?(@.name=="admin-http")].containerPort}'
```
Then use the `kubectl port-forward` command to access that port from outside
the cluster (in this example the port is 9990):
```bash
kubectl -n linkerd port-forward \
$(kubectl -n linkerd get pod -l linkerd.io/control-plane-component=identity \
-o jsonpath='{.items[0].metadata.name}') \
9990
```
It is now possible to use `go tool` to inspect this data. For example to
generate a graph in a PDF file describing memory allocations:
```bash
go tool pprof -seconds 5 -pdf http://localhost:9990/debug/pprof/allocs
```

View File

@ -0,0 +1,540 @@
+++
title = "Ingress traffic"
description = "Linkerd works alongside your ingress controller of choice."
+++
For reasons of simplicity and composability, Linkerd doesn't provide a built-in
ingress. Instead, Linkerd is designed to work with existing Kubernetes ingress
solutions.
Combining Linkerd and your ingress solution requires two things:
1. Configuring your ingress to support Linkerd.
2. Meshing your ingress pods so that they have the Linkerd proxy installed.
Meshing your ingress pods will allow Linkerd to provide features like L7
metrics and mTLS the moment the traffic is inside the cluster. (See
[Adding your service](../adding-your-service/) for instructions on how to mesh
your ingress.)
Note that some ingress options need to be meshed in "ingress" mode. See details
below.
Common ingress options that Linkerd has been used with include:
- [Ambassador / Emissary-ingress](#ambassador)
- [Contour](#contour)
- [GCE](#gce)
- [Gloo](#gloo)
- [Haproxy]({{< ref "#haproxy" >}})
- [Kong](#kong)
- [Nginx](#nginx)
- [Traefik](#traefik)
For a quick start guide to using a particular ingress, please visit the section
for that ingress. If your ingress is not on that list, never fear—it likely
works anyways. See [Ingress details](#ingress-details) below.
{{< note >}}
If your ingress terminates TLS, this TLS traffic (e.g. HTTPS calls from outside
the cluster) will pass through Linkerd as an opaque TCP stream and Linkerd will
only be able to provide byte-level metrics for this side of the connection. The
resulting HTTP or gRPC traffic to internal services, of course, will have the
full set of metrics and mTLS support.
{{< /note >}}
## Ambassador (aka Emissary) {id="ambassador"}
Ambassador can be meshed normally. An example manifest for configuring the
Ambassador / Emissary is as follows:
```yaml
apiVersion: v1
kind: Service
metadata:
name: web-ambassador
namespace: emojivoto
annotations:
getambassador.io/config: |
---
apiVersion: getambassador.io/v2
kind: Mapping
name: web-ambassador-mapping
service: http://web-svc.emojivoto.svc.cluster.local:80
host: example.com
prefix: /
spec:
selector:
app: web-svc
ports:
- name: http
port: 80
targetPort: http
```
For a more detailed guide, we recommend reading [Installing the Emissary
ingress with the Linkerd service
mesh](https://buoyant.io/2021/05/24/emissary-and-linkerd-the-best-of-both-worlds/).
## Nginx
Nginx can be meshed normally, but the
[`nginx.ingress.kubernetes.io/service-upstream`](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#service-upstream)
annotation should be set to `true`. No further configuration is required.
```yaml
# apiVersion: networking.k8s.io/v1beta1 # for k8s < v1.19
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: emojivoto-web-ingress
namespace: emojivoto
annotations:
nginx.ingress.kubernetes.io/service-upstream: true
spec:
ingressClassName: nginx
defaultBackend:
service:
name: web-svc
port:
number: 80
```
## Traefik
Traefik should be meshed with ingress mode enabled, i.e. with the
`linkerd.io/inject: ingress` annotation rather than the default `enabled`.
Instructions differ for 1.x and 2.x versions of Traefik.
### Traefik 1.x
The simplest way to use Traefik 1.x as an ingress for Linkerd is to configure a
Kubernetes `Ingress` resource with the
`ingress.kubernetes.io/custom-request-headers` like this:
```yaml
# apiVersion: networking.k8s.io/v1beta1 # for k8s < v1.19
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: emojivoto
annotations:
ingress.kubernetes.io/custom-request-headers: l5d-dst-override:web-svc.emojivoto.svc.cluster.local:80
spec:
ingressClassName: traefik
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
```
The important annotation here is:
```yaml
ingress.kubernetes.io/custom-request-headers: l5d-dst-override:web-svc.emojivoto.svc.cluster.local:80
```
Traefik will add a `l5d-dst-override` header to instruct Linkerd what service
the request is destined for. You'll want to include both the Kubernetes service
FQDN (`web-svc.emojivoto.svc.cluster.local`) *and* the destination
`servicePort`.
To test this, you'll want to get the external IP address for your controller. If
you installed Traefik via Helm, you can get that IP address by running:
```bash
kubectl get svc --all-namespaces \
-l app=traefik \
-o='custom-columns=EXTERNAL-IP:.status.loadBalancer.ingress[0].ip'
```
You can then use this IP with curl:
```bash
curl -H "Host: example.com" http://external-ip
```
{{< note >}}
This solution won't work if you're using Traefik's service weights as
Linkerd will always send requests to the service name in `l5d-dst-override`. A
workaround is to use `traefik.frontend.passHostHeader: "false"` instead.
{{< /note >}}
### Traefik 2.x
Traefik 2.x adds support for path based request routing with a Custom Resource
Definition (CRD) called
[`IngressRoute`](https://docs.traefik.io/providers/kubernetes-crd/).
If you choose to use `IngressRoute` instead of the default Kubernetes `Ingress`
resource, then you'll also need to use the Traefik's
[`Middleware`](https://docs.traefik.io/middlewares/headers/) Custom Resource
Definition to add the `l5d-dst-override` header.
The YAML below uses the Traefik CRDs to produce the same results for the
`emojivoto` application, as described above.
```yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: l5d-header-middleware
namespace: traefik
spec:
headers:
customRequestHeaders:
l5d-dst-override: "web-svc.emojivoto.svc.cluster.local:80"
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
annotations:
kubernetes.io/ingress.class: traefik
creationTimestamp: null
name: emojivoto-web-ingress-route
namespace: emojivoto
spec:
entryPoints: []
routes:
- kind: Rule
match: PathPrefix(`/`)
priority: 0
middlewares:
- name: l5d-header-middleware
services:
- kind: Service
name: web-svc
port: 80
```
## GCE
The GCE ingress should be meshed with ingress mode enabled, i.e. with the
`linkerd.io/inject: ingress` annotation rather than the default `enabled`.
This example shows how to use a [Google Cloud Static External IP
Address](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address)
and TLS with a [Google-managed
certificate](https://cloud.google.com/load-balancing/docs/ssl-certificates#managed-certs).
```yaml
# apiVersion: networking.k8s.io/v1beta1 # for k8s < v1.19
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: emojivoto
annotations:
ingress.kubernetes.io/custom-request-headers: "l5d-dst-override: web-svc.emojivoto.svc.cluster.local:80"
ingress.gcp.kubernetes.io/pre-shared-cert: "managed-cert-name"
kubernetes.io/ingress.global-static-ip-name: "static-ip-name"
spec:
ingressClassName: gce
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
```
To use this example definition, substitute `managed-cert-name` and
`static-ip-name` with the short names defined in your project (n.b. use the name
for the IP address, not the address itself).
The managed certificate will take about 30-60 minutes to provision, but the
status of the ingress should be healthy within a few minutes. Once the managed
certificate is provisioned, the ingress should be visible to the Internet.
## Gloo
Gloo should be meshed with ingress mode enabled, i.e. with the
`linkerd.io/inject: ingress` annotation rather than the default `enabled`.
As of Gloo v0.13.20, Gloo has native integration with Linkerd, so that the
required Linkerd headers are added automatically. Assuming you installed Gloo
to the default location, you can enable the native integration by running:
```bash
kubectl patch settings -n gloo-system default \
-p '{"spec":{"linkerd":true}}' --type=merge
```
Gloo will now automatically add the `l5d-dst-override` header to every
Kubernetes upstream.
Now simply add a route to the upstream, e.g.:
```bash
glooctl add route --path-prefix=/ --dest-name booksapp-webapp-7000
```
## Contour
Contour should be meshed with ingress mode enabled, i.e. with the
`linkerd.io/inject: ingress` annotation rather than the default `enabled`.
The following example uses the
[Contour getting started](https://projectcontour.io/getting-started/) documentation
to demonstrate how to set the required header manually.
Contour's Envoy DaemonSet doesn't auto-mount the service account token, which
is required for the Linkerd proxy to do mTLS between pods. So first we need to
install Contour uninjected, patch the DaemonSet with
`automountServiceAccountToken: true`, and then inject it. Optionally you can
create a dedicated service account to avoid using the `default` one.
```bash
# install Contour
kubectl apply -f https://projectcontour.io/quickstart/contour.yaml
# create a service account (optional)
kubectl apply -f - << EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: envoy
namespace: projectcontour
EOF
# add service account to envoy (optional)
kubectl patch daemonset envoy -n projectcontour --type json -p='[{"op": "add", "path": "/spec/template/spec/serviceAccount", "value": "envoy"}]'
# auto mount the service account token (required)
kubectl patch daemonset envoy -n projectcontour --type json -p='[{"op": "replace", "path": "/spec/template/spec/automountServiceAccountToken", "value": true}]'
# inject linkerd first into the DaemonSet
kubectl -n projectcontour get daemonset -oyaml | linkerd inject - | kubectl apply -f -
# inject linkerd into the Deployment
kubectl -n projectcontour get deployment -oyaml | linkerd inject - | kubectl apply -f -
```
Verify your Contour and Envoy installation has a running Linkerd sidecar.
Next we'll deploy a demo service:
```bash
linkerd inject https://projectcontour.io/examples/kuard.yaml | kubectl apply -f -
```
To route external traffic to your service you'll need to provide a HTTPProxy:
```yaml
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: kuard
namespace: default
spec:
routes:
- requestHeadersPolicy:
set:
- name: l5d-dst-override
value: kuard.default.svc.cluster.local:80
services:
- name: kuard
port: 80
virtualhost:
fqdn: 127.0.0.1.nip.io
```
Notice the `l5d-dst-override` header is explicitly set to the target `service`.
Finally, you can test your working service mesh:
```bash
kubectl port-forward svc/envoy -n projectcontour 3200:80
http://127.0.0.1.nip.io:3200
```
{{< note >}}
You should annotate the pod spec with `config.linkerd.io/skip-outbound-ports:
8001`. The Envoy pod will try to connect to the Contour pod at port 8001
through TLS, which is not supported under this ingress mode, so you need to
have the proxy skip that outbound port.
{{< /note >}}
{{< note >}}
If you are using Contour with [flagger](https://github.com/weaveworks/flagger)
the `l5d-dst-override` headers will be set automatically.
{{< /note >}}
### Kong
Kong should be meshed with ingress mode enabled, i.e. with the
`linkerd.io/inject: ingress` annotation rather than the default `enabled`.
This example will use the following elements:
- The [Kong chart](https://github.com/Kong/charts)
- The [emojivoto] example application(../../getting-started/)
Before installing emojivoto, install Linkerd and Kong on your cluster. When
injecting the Kong deployment, use the `--ingress` flag (or annotation).
We need to declare these objects as well:
- KongPlugin, a CRD provided by Kong
- Ingress
```yaml
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: set-l5d-header
namespace: emojivoto
plugin: request-transformer
config:
add:
headers:
- l5d-dst-override:$(headers.host).svc.cluster.local
---
# apiVersion: networking.k8s.io/v1beta1 # for k8s < v1.19
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: emojivoto
annotations:
konghq.com/plugins: set-l5d-header
spec:
ingressClassName: kong
rules:
- http:
paths:
- path: /api/vote
pathType: Prefix
backend:
service:
name: web-svc
port:
number: http
- path: /api/list
pathType: Prefix
backend:
service:
name: web-svc
port:
name: http
```
Here we are explicitly setting the `l5d-dst-override` in the `KongPlugin`.
Using [templates as
values](https://docs.konghq.com/hub/kong-inc/request-transformer/#template-as-value),
we can use the `host` header from requests and set the `l5d-dst-override` value
based off that.
Finally, install emojivoto so that it's `deploy/vote-bot` targets the
ingress and includes a `host` header value for the `web-svc.emojivoto` service.
Before applying the injected emojivoto application, make the following changes
to the `vote-bot` Deployment:
```yaml
env:
# Target the Kong ingress instead of the Emojivoto web service
- name: WEB_HOST
value: kong-proxy.kong:80
# Override the host header on requests so that it can be used to set the l5d-dst-override header
- name: HOST_OVERRIDE
value: web-svc.emojivoto
```
### Haproxy
{{< note >}}
There are two different haproxy-based ingress controllers. This example is for
the [kubernetes-ingress controller by
haproxytech](https://www.haproxy.com/documentation/kubernetes/latest/) and not
the [haproxy-ingress controller](https://haproxy-ingress.github.io/).
{{< /note >}}
Haproxy should be meshed with ingress mode enabled, i.e. with the
`linkerd.io/inject: ingress` annotation rather than the default `enabled`.
The simplest way to use Haproxy as an ingress for Linkerd is to configure a
Kubernetes `Ingress` resource with the
`haproxy.org/request-set-header` annotation like this:
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: emojivoto
annotations:
kubernetes.io/ingress.class: haproxy
haproxy.org/request-set-header: |
l5d-dst-override web-svc.emojivoto.svc.cluster.local:80
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
```
Unfortunately, there is currently no support to do this dynamically in
a global config map by using the service name, namespace and port as variable.
This also means, that you can't combine more than one service ingress rule
in an ingress manifest as each one needs their own
`haproxy.org/request-set-header` annotation with hard coded value.
## Ingress details
In this section we cover how Linkerd interacts with ingress controllers in
general.
In general, Linkerd can be used with any ingress controller. In order for
Linkerd to properly apply features such as route-based metrics and traffic
splitting, Linkerd needs the IP/port of the Kubernetes Service. However, by
default, many ingresses do their own endpoint selection and pass the IP/port of
the destination Pod, rather than the Service as a whole.
Thus, combining an ingress with Linkerd takes one of two forms:
1. Configure the ingress to pass the IP and port of the Service as the
destination, i.e. to skip its own endpoint selection. (E.g. see
[Nginx](#nginx) above.)
2. If this is not possible, then configure the ingress to pass the Service
IP/port in a header such as `l5d-dst-override`, `Host`, or `:authority`, and
configure Linkerd in *ingress* mode. In this mode, it will read from one of
those headers instead.
The most common approach in form #2 is to use the explicit `l5d-dst-override` header.
{{< note >}}
If requests experience a 2-3 second delay after injecting your ingress
controller, it is likely that this is because the service of `type:
LoadBalancer` is obscuring the client source IP. You can fix this by setting
`externalTrafficPolicy: Local` in the ingress' service definition.
{{< /note >}}
{{< note >}}
While the Kubernetes Ingress API definition allows a `backend`'s `servicePort`
to be a string value, only numeric `servicePort` values can be used with
Linkerd. If a string value is encountered, Linkerd will default to using port
80.
{{< /note >}}

View File

@ -0,0 +1,121 @@
+++
title = "Linkerd and Pod Security Policies (PSP)"
description = "Using Linkerd with a pod security policies enabled."
+++
The Linkerd control plane comes with its own minimally privileged
[Pod Security Policy](https://kubernetes.io/docs/concepts/policy/pod-security-policy/)
and the associated RBAC resources. This Pod Security Policy is enforced only if
the `PodSecurityPolicy` admission controller is enabled.
To view the definition of the control plane's Pod Security Policy, run:
```bash
kubectl describe psp -l linkerd.io/control-plane-ns=linkerd
```
Adjust the value of the above label to match your control plane's namespace.
Notice that to minimize attack surface, all Linux capabilities are dropped from
the control plane's Pod Security Policy, with the exception of the `NET_ADMIN`
and `NET_RAW` capabilities. These capabilities provide the `proxy-init` init
container with runtime privilege to rewrite the pod's `iptable`. Note that adding
these capabilities to the Pod Security Policy doesn't make the container a
[`privileged`](https://kubernetes.io/docs/concepts/workloads/pods/pod/#privileged-mode-for-pod-containers)
container. The control plane's Pod Security Policy prevents container privilege
escalation with the `allowPrivilegeEscalation: false` policy. To understand the
full implication of the `NET_ADMIN` and `NET_RAW` capabilities, refer to the
Linux capabilities [manual](https://www.man7.org/linux/man-pages/man7/capabilities.7.html).
More information on the `iptables` rules used by the `proxy-init` init
container can be found on the [Architecture](../../reference/architecture/#linkerd-init)
page.
If your environment disallows the operation of containers with escalated Linux
capabilities, Linkerd can be installed with its [CNI plugin](../../features/cni/),
which doesn't require the `NET_ADMIN` and `NET_RAW` capabilities.
Linkerd doesn't provide any default Pod Security Policy for the data plane
because the policies will vary depending on the security requirements of your
application. The security context requirement for the Linkerd proxy sidecar
container will be very similar to that defined in the control plane's Pod
Security Policy.
For example, the following Pod Security Policy and RBAC will work with the injected
`emojivoto` demo application:
```yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: linkerd-emojivoto-data-plane
spec:
allowPrivilegeEscalation: false
fsGroup:
ranges:
- max: 65535
min: 10001
rule: MustRunAs
readOnlyRootFilesystem: true
allowedCapabilities:
- NET_ADMIN
- NET_RAW
- NET_BIND_SERVICE
requiredDropCapabilities:
- ALL
runAsUser:
rule: RunAsAny
seLinux:
rule: RunAsAny
supplementalGroups:
ranges:
- max: 65535
min: 10001
rule: MustRunAs
volumes:
- configMap
- emptyDir
- projected
- secret
- downwardAPI
- persistentVolumeClaim
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: emojivoto-psp
namespace: emojivoto
rules:
- apiGroups: ['policy','extensions']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['linkerd-emojivoto-data-plane']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: emojivoto-psp
namespace: emojivoto
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: emojivoto-psp
subjects:
- kind: ServiceAccount
name: default
namespace: emojivoto
- kind: ServiceAccount
name: emoji
namespace: emojivoto
- kind: ServiceAccount
name: voting
namespace: emojivoto
- kind: ServiceAccount
name: web
namespace: emojivoto
```
Note that the Linkerd proxy only requires the `NET_ADMIN` and `NET_RAW`
capabilities when running without Linkerd CNI, and it's run with UID `2102`.

View File

@ -0,0 +1,102 @@
+++
title = "Using the Debug Sidecar"
description = "Inject the debug container to capture network packets."
+++
Debugging a service mesh can be hard. When something just isn't working, is
the problem with the proxy? With the application? With the client? With the
underlying network? Sometimes, nothing beats looking at raw network data.
In cases where you need network-level visibility into packets entering and
leaving your application, Linkerd provides a *debug sidecar* with some helpful
tooling. Similar to how [proxy sidecar
injection](../../features/proxy-injection/) works, you add a debug sidecar to
a pod by setting the `config.linkerd.io/enable-debug-sidecar: "true"` annotation
at pod creation time. For convenience, the `linkerd inject` command provides an
`--enable-debug-sidecar` option that does this annotation for you.
(Note that the set of containers in a Kubernetes pod is not mutable, so simply
adding this annotation to a pre-existing pod will not work. It must be present
at pod *creation* time.)
The debug sidecar image contains
[`tshark`](https://www.wireshark.org/docs/man-pages/tshark.html), `tcpdump`,
`lsof`, and `iproute2`. Once installed, it starts automatically logging all
incoming and outgoing traffic with `tshark`, which can then be viewed with
`kubectl logs`. Alternatively, you can use `kubectl exec` to access the
container and run commands directly.
For instance, if you've gone through the [Linkerd Getting
Started](../../getting-started/) guide and installed the
*emojivoto* application, and wish to debug traffic to the *voting* service, you
could run:
```bash
kubectl -n emojivoto get deploy/voting -o yaml \
| linkerd inject --enable-debug-sidecar - \
| kubectl apply -f -
```
to deploy the debug sidecar container to all pods in the *voting* service.
(Note that there's only one pod in this deployment, which will be recreated
to do this--see the note about pod mutability above.)
You can confirm that the debug container is running by listing
all the containers in pods with the `voting-svc` label:
```bash
kubectl get pods -n emojivoto -l app=voting-svc \
-o jsonpath='{.items[*].spec.containers[*].name}'
```
Then, you can watch live tshark output from the logs by simply running:
```bash
kubectl -n emojivoto logs deploy/voting linkerd-debug -f
```
If that's not enough, you can exec to the container and run your own commands
in the context of the network. For example, if you want to inspect the HTTP headers
of the requests, you could run something like this:
```bash
kubectl -n emojivoto exec -it \
$(kubectl -n emojivoto get pod -l app=voting-svc \
-o jsonpath='{.items[0].metadata.name}') \
-c linkerd-debug -- tshark -i any -f "tcp" -V -Y "http.request"
```
A real-world error message written by the proxy that the debug sidecar is
effective in troubleshooting is a `Connection Refused` error like this one:
```log
ERR! [<time>] proxy={server=in listen=0.0.0.0:4143 remote=some.svc:50416}
linkerd2_proxy::app::errors unexpected error: error trying to connect:
Connection refused (os error 111) (address: 127.0.0.1:8080)
```
In this case, the `tshark` command can be modified to listen for
traffic between the specific ports mentioned in the error, like this:
```bash
kubectl -n emojivoto exec -it \
$(kubectl -n emojivoto get pod -l app=voting-svc \
-o jsonpath='{.items[0].metadata.name}') \
-c linkerd-debug -- tshark -i any -f "tcp" -V \
-Y "(tcp.srcport == 4143 and tcp.dstport == 50416) or tcp.port == 8080"
```
Be aware that there is a similar error with the message `Connection reset by
peer`. This error is usually benign, if you do not see correlated errors or
messages in your application log output. In this scenario, the debug
container may not help to troubleshoot the error message.
```log
ERR! [<time>] proxy={server=in listen=0.0.0.0:4143 remote=some.svc:35314}
linkerd2_proxy::app::errors unexpected error: connection error:
Connection reset by peer (os error 104)
```
Of course, these examples only work if you have the ability to `exec` into
arbitrary containers in the Kubernetes cluster. See [`linkerd
tap`](../../reference/cli/viz/#tap) for an alternative to this approach.

View File

@ -0,0 +1,150 @@
+++
title = "Validating your mTLS traffic"
description = "You can validate whether or not your traffic is being mTLS'd by Linkerd."
aliases = ["securing-your-service"]
+++
By default, [Linkerd automatically enables mutual Transport Layer Security
(mTLS)](../../features/automatic-mtls/) for TCP traffic between meshed pods, by
establishing and authenticating secure, private TLS connections between Linkerd
proxies. Simply [add your services](../adding-your-service/) to Linkerd,
and Linkerd will take care of the rest.
Linkerd's automatic mTLS is done in a way that's completely transparent to
the application. Of course, sometimes it's helpful to be able to validate
whether mTLS is in effect!
{{< note >}}
Linkerd uses Kubernetes *ServiceAccounts* to define service identity. This
requires that the `automountServiceAccountToken` feature (on by default) has
not been disabled on the pods. See the [Kubernetes service account
documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)
for more.
{{< /note >}}
## Validating mTLS with `linkerd viz edges`
To validate that mTLS is working, you can view a summary of the TCP
connections between services that are managed by Linkerd using the [`linkerd
viz edges`](../../reference/cli/viz/#edges) command. For example:
```bash
linkerd viz -n linkerd edges deployment
```
The output will look like:
```bash
SRC DST SRC_NS DST_NS SECURED
prometheus linkerd-controller linkerd-viz linkerd √
prometheus linkerd-destination linkerd-viz linkerd √
prometheus linkerd-identity linkerd-viz linkerd √
prometheus linkerd-proxy-injector linkerd-viz linkerd √
prometheus linkerd-sp-validator linkerd-viz linkerd √
```
In this example, everything is successfully mTLS'd, and the `CLIENT` and
`SERVER` columns denote the identities used, in the form
`service-account-name.namespace`. (See [Linkerd's automatic mTLS
documentation](../../features/automatic-mtls/) for more on what these identities
mean.) If there were a problem automatically upgrading the connection with
mTLS, the `MSG` field would contain the reason why.
## Validating mTLS with `linkerd viz tap`
Instead of relying on an aggregate, it is also possible to watch the requests
and responses in real time to understand what is getting mTLS'd. We can use the
[`linkerd viz tap` command](../../reference/cli/viz/#tap) to sample real time
request data.
```bash
linkerd viz -n linkerd tap deploy
```
{{< note >}}
By default, the control plane resources are not tappable. After having
installed the Viz extension (through `linkerd viz install`), you can enable tap
on the control plane components simply by restarting them, which can be done
with no downtime with `kubectl -n linkerd rollout restart deploy`. To enable tap
on the Viz extension itself, issue `kubectl -n linkerd-viz rollout restart
deploy`.
{{< /note >}}
Looking at the control plane specifically, there will be two main types of output.
```bash
req id=0:0 proxy=in src=10.42.0.1:60318 dst=10.42.0.23:9995 tls=no_tls_from_remote :method=GET :authority=10.42.0.23:9995 :path=/ready
rsp id=0:0 proxy=in src=10.42.0.1:60318 dst=10.42.0.23:9995 tls=no_tls_from_remote :status=200 latency=267µs
end id=0:0 proxy=in src=10.42.0.1:60318 dst=10.42.0.23:9995 tls=no_tls_from_remote duration=20µs response-length=3B
```
These are calls by the [Kubernetes readiness
probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/).
As probes are initiated from the kubelet, which is not in the mesh, there is no
identity and these requests are not mTLS'd, as denoted by the
`tls=no_tls_from_remote` message.
Other requests to the control plane *are* TLS'd:
```bash
ireq id=2:1 proxy=in src=10.42.0.31:55428 dst=10.42.0.22:9995 tls=true :method=GET :authority=10.42.0.22:9995 :path=/metrics
rsp id=2:1 proxy=in src=10.42.0.31:55428 dst=10.42.0.22:9995 tls=true :status=200 latency=1597µs
end id=2:1 proxy=in src=10.42.0.31:55428 dst=10.42.0.22:9995 tls=true duration=228µs response-length=2272B
```
This connection comes from Prometheus, which in the mesh, so the request is
automatically mTLS'd, as denoted by the `tls=true` output.
## Validating mTLS with tshark
The final way to validate mTLS is to look at raw network traffic within the
cluster.
Linkerd includes a [debug sidecar](../using-the-debug-container/) that
comes with a selection of commands that make it easier to verify and debug the
service mesh itself. For example, with our [*emojivoto* demo
application](../../getting-started/), we can add the debug sidecar by running:
```bash
curl -sL https://run.linkerd.io/emojivoto.yml \
| linkerd inject --enable-debug-sidecar - \
| kubectl apply -f -
```
We can then establish a remote shell directly in the debug container of a pod in
the `voting` service with:
```bash
kubectl -n emojivoto exec -it \
$(kubectl -n emojivoto get po -o name | grep voting) \
-c linkerd-debug -- /bin/bash
```
Once we're inside the debug sidecar, the built-in `tshark` command can be used
to inspect the raw packets on the network interface. For example:
```bash
tshark -i any -d tcp.port==8080,ssl | grep -v 127.0.0.1
```
This tells `tshark` that port 8080 might be TLS'd, and to ignore localhost (as
that traffic will always be unencrypted). The output will show the primary
application traffic being automatically mTLS'd.
```bash
133 11.391540872 10.4.0.17 → 10.4.0.23 TCP 68 46766 → 4191 [ACK] Seq=557 Ack=3942 Win=1329 Len=0 TSval=3389590636 TSecr=1915605020
134 12.128190076 10.4.0.25 → 10.4.0.23 TLSv1.2 154 Application Data
140 12.129497053 10.4.0.23 → 10.4.0.25 TLSv1.2 149 Application Data
141 12.129534848 10.4.0.25 → 10.4.0.23 TCP 68 48138 → 8080 [ACK] Seq=1089 Ack=985 Win=236 Len=0 TSval=2234109459 TSecr=617799816
143 13.140288400 10.4.0.25 → 10.4.0.23 TLSv1.2 150 Application Data
148 13.141219945 10.4.0.23 → 10.4.0.25 TLSv1.2 136 Application Data
```
## Summary
In this guide, we've provided several different ways to validate whether
Linkerd has been able to automatically upgrade connections to mTLS. Note that
there are several reasons why Linkerd may not be able to do this upgrade---see
the "Caveats and future work" section of the [Linkerd automatic mTLS
documentation](../../features/automatic-mtls/)---so if you are relying on Linkerd
for security purposes, this kind of validation can be instructive.

Some files were not shown because too many files have changed in this diff Show More