Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRI: proposal for managing container stdout/stderr streams #34376

Merged
merged 1 commit into from Nov 22, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
269 changes: 269 additions & 0 deletions docs/proposals/kubelet-cri-logging.md
@@ -0,0 +1,269 @@
<!-- BEGIN MUNGE: UNVERSIONED_WARNING -->

<!-- BEGIN STRIP_FOR_RELEASE -->

<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/kubernetes/img/warning.png" alt="WARNING"
width="25" height="25">

<h2>PLEASE NOTE: This document applies to the HEAD of the source tree</h2>

If you are using a released version of Kubernetes, you should
refer to the docs that go with that version.

Documentation for other releases can be found at
[releases.k8s.io](http://releases.k8s.io).
</strong>
--

<!-- END STRIP_FOR_RELEASE -->

<!-- END MUNGE: UNVERSIONED_WARNING -->

# CRI: Log management for container stdout/stderr streams


## Goals and non-goals

Container Runtime Interface (CRI) is an ongoing project to allow container
runtimes to integrate with kubernetes via a newly-defined API. The goal of this
proposal is to define how container's *stdout/stderr* log streams should be
handled in CRI.

The explicit non-goal is to define how (non-stdout/stderr) application logs
should be handled. Collecting and managing arbitrary application logs is a
long-standing issue [1] in kubernetes and is worth a proposal of its own. Even
though this proposal does not touch upon these logs, the direction of
this proposal is aligned with one of the most-discussed solutions, logging
volumes [1], for general logging management.

*In this proposal, “logs” refer to the stdout/stderr streams of the
containers, unless specified otherwise.*

Previous CRI logging issues:
- Tracking issue: https://github.com/kubernetes/kubernetes/issues/30709
- Proposal (by @tmrtfs): https://github.com/kubernetes/kubernetes/pull/33111

The scope of this proposal is narrower than the #33111 proposal, and hopefully
this will encourage a more focused discussion.


## Background

Below is a brief overview of logging in kubernetes with docker, which is the
only container runtime with fully functional integration today.

**Log lifecycle and management**

Docker supports various logging drivers (e.g., syslog, journal, and json-file),
and allows users to configure the driver by passing flags to the docker daemon
at startup. Kubernetes defaults to the "json-file" logging driver, in which
docker writes the stdout/stderr streams to a file in the json format as shown
below.

```
{“log”: “The actual log line”, “stream”: “stderr”, “time”: “2016-10-05T00:00:30.082640485Z”}
```

Docker deletes the log files when the container is removed, and a cron-job (or
systemd timer-based job) on the node is responsible to rotate the logs (using
`logrotate`). To preserve the logs for introspection and debuggability, kubelet
keeps the terminated container until the pod object has been deleted from the
apiserver.

**Container log retrieval**

The kubernetes CLI tool, kubectl, allows users to access the container logs
using [`kubectl logs`]
(http://kubernetes.io/docs/user-guide/kubectl/kubectl_logs/) command.
`kubectl logs` supports flags such as `--since` that requires understanding of
the format and the metadata (i.e., timestamps) of the logs. In the current
implementation, kubelet calls `docker logs` with parameters to return the log
content. As of now, docker only supports `log` operations for the “journal” and
“json-file” drivers [2]. In other words, *the support of `kubectl logs` is not
universal in all kuernetes deployments*.

**Cluster logging support**

In a production cluster, logs are usually collected, aggregated, and shipped to
a remote store where advanced analysis/search/archiving functions are
supported. In kubernetes, the default cluster-addons includes a per-node log
collection daemon, `fluentd`. To facilitate the log collection, kubelet creates
symbolic links to all the docker containers logs under `/var/log/containers`
with pod and container metadata embedded in the filename.

```
/var/log/containers/<pod_name>_<pod_namespace>_<container_name>-<container_id>.log`
```

The fluentd daemon watches the `/var/log/containers/` directory and extract the
metadata associated with the log from the path. Note that this integration
requires kubelet to know where the container runtime stores the logs, and will
not be directly applicable to CRI.


## Requirements

1. **Provide ways for CRI-compliant runtimes to support all existing logging
features, i.e., `kubectl logs`.**

2. **Allow kubelet to manage the lifecycle of the logs to pave the way for
better disk management in the future.** This implies that the lifecycle
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also implies the kubelet must be able to rotate logs, and thus must be able to work with the given format.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another component can be set up to rotate the log if needed. I think the proposal discusses more about the logging formats later. Let me know if you find that insufficient.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is an orthogonal concern... So long as it's not strict control.

of containers and their logs need to be decoupled.

3. **Allow log collectors to easily integrate with Kubernetes across
different container runtimes while preserving efficient storage and
retrieval.**

Requirement (1) provides opportunities for runtimes to continue support
`kubectl logs --since` and related features. Note that even though such
features are only supported today for a limited set of log drivers, this is an
important usability tool for a fresh, basic kubernetes cluster, and should not
be overlooked. Requirement (2) stems from the fact that disk is managed by
kubelet as a node-level resource (not per-pod) today, hence it is difficult to
delegate to the runtime by enforcing per-pod disk quota policy. In addition,
container disk quota is not well supported yet, and such limitation may not
even be well-perceived by users. Requirement (1) is crucial to the kubernetes'
extensibility and usability across all deployments.

## Proposed solution

This proposal intends to satisfy the requirements by

1. Enforce where the container logs should be stored on the host
filesystem. Both kubelet and the log collector can interact with
the log files directly.

2. Ask the runtime to decorate the logs in a format that kubelet understands.

**Log directories and structures**

Kubelet will be configured with a root directory (e.g., `/var/log/pods` or
`/var/lib/kubelet/logs/) to store all container logs. Below is an example of a
path to the log of a container in a pod.

```
/var/log/pods/<podUID>/<containerName>_<instance#>.log
```

In CRI, this is implemented by setting the pod-level log directory when
creating the pod sandbox, and passing the relative container log path
when creating a container.

```
PodSandboxConfig.LogDirectory: /var/log/pods/<podUID>/
ContainerConfig.LogPath: <containerName>_<instance#>.log
```

Because kubelet determines where the logs are stores and can access them
directly, this meets requirement (1). As for requirement (2), the log collector
can easily extract basic pod metadata (e.g., pod UID, container name) from
the paths, and watch the directly for any changes. In the future, we can
extend this by maintaining a metada file in the pod directory.

**Log format**

The runtime should decorate each log entry with a RFC 3339Nano timestamp
prefix, the stream type (i.e., "stdout" or "stderr"), and ends with a newline.

```
2016-10-06T00:17:09.669794202Z stdout The content of the log entry 1
2016-10-06T00:17:10.113242941Z stderr The content of the log entry 2
```

With the knowledge, kubelet can parses the logs and serve them for `kubectl
logs` requests. This meets requirement (3). Note that the format is defined
deliberately simple to provide only information necessary to serve the requests.
We do not intend for kubelet to host various logging plugins. It is also worth
mentioning again that the scope of this proposal is restricted to stdout/stderr
streams of the container, and we impose no restriction to the logging format of
arbitrary container logs.

**Who should rotate the logs?**

We assume that a separate task (e.g., cron job) will be configured on the node
to rotate the logs periodically, similar to today’s implementation.

We do not rule out the possibility of letting kubelet or a per-node daemon
(`DaemonSet`) to take up the responsibility, or even declare rotation policy
in the kubernetes API as part of the `PodSpec`, but it is beyond the scope of
the this proposal.

**What about non-supported log formats?**

If a runtime chooses to store logs in non-supported formats, it essentially
opts out of `kubectl logs` features, which is backed by kubelet today. It is
assumed that the user can rely on the advanced, cluster logging infrastructure
to examine the logs.

It is also possible that in the future, `kubectl logs` can contact the cluster
logging infrastructure directly to serve logs [1a]. Note that this does not
eliminate the need to store the logs on the node locally for reliability.


**How can existing runtimes (docker/rkt) comply to the logging requirements?**

In the short term, the ongoing docker-CRI integration [3] will support the
proposed solution only partially by (1) creating symbolic links for kubelet
to access, but not manage the logs, and (2) add support for json format in
kubelet. A more sophisticated solution that either involves using a custom
plugin or launching a separate process to copy and decorate the log will be
considered as a mid-term solution.

For rkt, implementation will rely on providing external file-descriptors for
stdout/stderr to applications via systemd [4]. Those streams are currently
managed by a journald sidecar, which collects stream outputs and store them
in the journal file of the pod. This will replaced by a custom sidecar which
can produce logs in the format expected by this specification and can handle
clients attaching as well.

## Alternatives

There are ad-hoc solutions/discussions that addresses one or two of the
requirements, but no comprehensive solution for CRI specifically has been
proposed so far (with the excpetion of @tmrtfs's proposal
[#33111](https://github.com/kubernetes/kubernetes/pull/33111), which has a much
wider scope). It has come up in discussions that kubelet can delegate all the
logging management to the runtime to allow maximum flexibility. However, it is
difficult for this approach to meet either requirement (1) or (2), without
defining complex logging API.

There are also possibilities to implement the current proposal by imposing the
log file paths, while leveraging the runtime to access and/or manage logs. This
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that this proposal makes a really strong argument against this (which I would consider the "default" path because it leverages the existing runtime support).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few other things to consider:

  1. Log collection should not require going through a daemon - A process like fluentd should be able to collect logs without having to rely on another daemon being up. This implies that logs must be a set of files at a known location in a format known to log aggregators.
  2. Rotation policies might vary by containers -- I don't think there are any runtimes now that support log rotation at any reasonable granularity, each runtime would have to independently develop that feature
  3. Logs persisting and being rotated after the container is removed - If a runtime is specifically only a container runtime, decoupling the lifetime of container+logs while having the runtime manage it will result in either the runtime having to have a firstclass concept of logs separate from containers (I don't think any do), or in the shim having to manage the logs itself... if the shim has to do it, then it might as well be kubelet.

I originally was in favor of having the runtimes manage everything, but if we add all of the above requirements, I think it's a good argument against it. We can question how much we actually need most of those requirements still though, since several of them are quite forward-looking.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to @euank's comment, which explains more clearly than the statement in the proposal.

TBH, I am not completely against leveraging runtime for various log operations. However,

  1. We are not sure what we want yet, since disk management is still in its early stages.
  2. Container runtimes such as docker also does not provide good support of logging yet. Even if we were to improve that (or hack docker internals in the shim), it'd take a longer time.

Instead of committing resources to do this right now, I think the current proposal gives us room to figure things out without having to develop and commit to a premature api.

Copy link
Contributor Author

@yujuhong yujuhong Oct 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can question how much we actually need most of those requirements still though, since several of them are quite forward-looking.

I think I did this on purpose. I wanted to see if we can try finding a solution that addresses all requirements first. If, instead, sig-node thinks that some requirements can be dropped, it's perfectly reasonable to question the requirements themselves, and convince people who are in favor of the requirements.

requires the runtime to expose knobs in CRI to retrieve, remove, and examine
the disk usage of logs. The upside of this approach is that kubelet needs not
mandate the logging format, assuming runtime already includes plugins for
various logging formats. Unfortunately, this is not true for existing runtimes
such as docker, which supports log retrieval only for a very limited number of
log drivers [2]. On the other hand, the downside is that we would be enforcing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really a critical problem? If supporting more log drivers in docker is important, we can always support that.

Copy link
Contributor Author

@yujuhong yujuhong Oct 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just to point out that runtime support for log drivers are far from perfect today, and it's not good enough to simply say "runtime already supports log drivers, why can we reuse that?"

more requirements on the runtime through log storage location on the host, and
a potentially premature logging API that may change as the disk management
evolves.

## References

[1] Log management issues:
- a. https://github.com/kubernetes/kubernetes/issues/17183
- b. https://github.com/kubernetes/kubernetes/issues/24677
- c. https://github.com/kubernetes/kubernetes/pull/13010

[2] Docker logging drivers:
- https://docs.docker.com/engine/admin/logging/overview/

[3] Docker CRI integration:
- https://github.com/kubernetes/kubernetes/issues/31459

[4] rkt support: https://github.com/systemd/systemd/pull/4179



<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/proposals/kubelet-cri-logging.md?pixel)]()
<!-- END MUNGE: GENERATED_ANALYTICS -->