-
Notifications
You must be signed in to change notification settings - Fork 878
iottymux: Store logs for kubelet in the appropriate location #3798
Conversation
cc86f34 to
972fa62
Compare
iaguis
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments.
rkt/run.go
Outdated
| cmdRun.Flags().StringVar(&flagHostname, "hostname", "", `pod's hostname. If empty, it will be "rkt-$PODUUID"`) | ||
| cmdRun.Flags().Var((*appsVolume)(&rktApps), "volume", "volumes to make available in the pod") | ||
| cmdRun.Flags().StringVar(&flagIPCMode, "ipc", "", `whether to stay in the host IPC namespace. Syntax: --ipc=[auto|private|parent]`) | ||
| cmdRun.Flags().StringVar(&flagKubeletLogDir, "kubelet-log-dir", "", "path to kubelet log dir") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this flag in rkt run? It should be for rkt app sandbox, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a function addAppFlags which is used for almost every app command, and for sure it's used in rkt app add:
stage1/iottymux/iottymux.go
Outdated
| // TODO(lucab): check what should be the target path with Euan | ||
| logTarget := filepath.Join(pathPrefix, appName, "logfile") | ||
| logDir := os.Getenv("KUBELET_LOG_DIR") | ||
| kubernetesIndex := os.Getenv("KUBERNETES_INDEX") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is a per-app identifier, how can it work with only one variable?
972fa62 to
99890d4
Compare
rkt/app_sandbox.go
Outdated
| flagAppPorts appPortList | ||
| flagAnnotations kvMap | ||
| flagLabels kvMap | ||
| flagKubeletLogDir string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: here and everywhere else please s/kubelet/kubernetes/
rkt/app_sandbox.go
Outdated
| cmdAppSandbox.Flags().Var(&flagAnnotations, "user-annotation", "optional, set the pod's annotations in the form of key=value") | ||
| cmdAppSandbox.Flags().Var(&flagLabels, "user-label", "optional, set the pod's label in the form of key=value") | ||
|
|
||
| cmdAppSandbox.Flags().StringVar(&flagKubeletLogDir, "kubelet-log-dir", "", "Optional, path to kubelet log dir. Should be used only by rktlet.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a default matching the kubernetes one here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really, because when this string is non-empty, we bind mount it to the stage1 container. We don't want to do it when we don't use rktlet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack
stage1/init/common/units.go
Outdated
| } | ||
| file.WriteString(fmt.Sprintf("KUBELET_LOG_DIR=%s\n", p.KubeletLogDir)) | ||
|
|
||
| kubeletLogPath, ok := ra.Annotations.Get("coreos.com/rkt/experiment/kubelet-log-path") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name is a bit confusing, as it isn't a proper path, just a suffix/name (according to my understanding of your explanation yesterday). What about kubernetes-log-filename or something similar?
stage1/iottymux/iottymux.go
Outdated
| logFile, err = os.OpenFile(logTarget, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) | ||
| logDir := os.Getenv("KUBELET_LOG_DIR") | ||
| logPath := os.Getenv("KUBELET_LOG_PATH") | ||
| logFullPath := filepath.Join(logDir, logPath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This just works because there is an implicit invariant that "kubelet logpath on host == logpath in stage1". This should be either documented somewhere, or the logDir part should be dropped from here and scoped instead somewhere under /rkt or similar (probably better).
/cc @alban
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer the latter, to choose a fixed path inside stage1 so that the kubelet will not be able to overshadow some other file/directory by mistake.
Then, --bind= in stage1/init/init.go must be updated accordingly using 2 paths.
99890d4 to
faa2b64
Compare
|
Way to test that change: |
1e30ae2 to
08ba149
Compare
|
This works: Now if I do: I get this on the sandbox: Maybe because the log file has the same name? If I remove the app and add it again with a different log file: I see this now: Doing the same but with different log files seems to work 👍 |
stage1/init/common/units.go
Outdated
| opts = append(opts, unit.NewUnitOption("Unit", "Requires", fmt.Sprintf("iomux@%s.service", ra.Name))) | ||
| opts = append(opts, unit.NewUnitOption("Unit", "Before", fmt.Sprintf("iomux@%s.service", ra.Name))) | ||
| logMode, ok := p.Manifest.Annotations.Get("coreos.com/rkt/experiment/logmode") | ||
| logMode, ok := p.Manifest.UserAnnotations["coreos.com/rkt/experiment/logmode"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this was correct before as an annotation, not a user-annotation. Same for the one you added below. See https://github.com/appc/spec/blob/master/spec/pods.md.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the concern with this is how to pass a pod annotation in rktlet if we use the current rkt CLI. Since we're not using a pod manifest, we'd need to add an option to pass annotations to rkt run and rkt app sandbox
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exaclty, using non-user annotations is just impossible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I'm following, I'm just commenting on stage1 logic here.
It shouldn't matter how you get this information from the user in stage0 (CLI flag, env flag, hardcoded value), you can still pass it as an annotation to stage1.
Unless you are trying to tell me that you want to pass this to stage0 CLI as --user-annotation=, in which case I'm saying that you shouldn't (because of spec, and because it will appear back in k8s among user-facing annotations).
stage1/init/init.go
Outdated
| }) | ||
| flag.Var(dnsConfMode, "dns-conf-mode", "DNS config file modes") | ||
| flag.StringVar(&rp.IPCMode, "ipc", "", "IPC mode --ipc=[auto|private|parent]") | ||
| flag.StringVar(&rp.KubernetesLogDir, "kubernetes-log-dir", "", "Mount the kubernetes log directory to write CRI logs") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit heavy and needs to be put in the stage1 interface protocol. Can't we instead just generate a volume+mount pair in stage0 at app_add time? If not, then an annotation on the pod is probably fine for the time being.
stage1/iottymux/iottymux.go
Outdated
| logTarget := filepath.Join(pathPrefix, appName, "logfile") | ||
| logFile, err = os.OpenFile(logTarget, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) | ||
| logPath := os.Getenv("KUBERNETES_LOG_PATH") | ||
| logFullPath := filepath.Join("/rkt/kubernetes", logPath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this is coming from the outside, I suggest to Clean it before use and make sure the result is still within the prefix dir after.
|
@iaguis This is expected, you cannot log to the same file from two apps. And kubelet generates the another filename for each container, so that's totally fine. Or you mean that we should check whether the file isn't used already before doing anything? That could be cool in terms of UX. |
If it's not too difficult to implement I rather see errors than panics :) |
|
Yeah, can do that. I will just check whether the file exists in the dir we mount from host. |
|
#3814 adds the |
08ba149 to
bb434bc
Compare
d440d6d to
26a3e25
Compare
26a3e25 to
75531ff
Compare
|
Since we changed the implementation a little bit and now everything relies on annotations (instead of user annotations or new CLI arguments), the current way of testing that branch is: |
iaguis
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some small changes. Otherwise looks good.
stage1/iottymux/iottymux.go
Outdated
|
|
||
| match, err := filepath.Match("/rkt/kubernetes/*", logFullPath) | ||
| if err != nil { | ||
| return fmt.Errorf("Couldn't analyze the full log path %s: %s", logFullPath, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Errors should be lower case
stage1/iottymux/iottymux.go
Outdated
| if err != nil { | ||
| return fmt.Errorf("Couldn't analyze the full log path %s: %s", logFullPath, err) | ||
| } else if !match { | ||
| return fmt.Errorf("The log path isn't inside /rkt/kubernetes, which most probably means that user tried to escape that directory by using '..'") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Errors should be lower case
stage1/iottymux/iottymux.go
Outdated
| if err != nil { | ||
| return fmt.Errorf("Couldn't analyze the full log path %s: %s", logFullPath, err) | ||
| } else if !match { | ||
| return fmt.Errorf("The log path isn't inside /rkt/kubernetes, which most probably means that user tried to escape that directory by using '..'") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd just say:
log path is not inside /rkt/kubernetes, refusing path traversal
tests/rkt_app_sandbox_test.go
Outdated
| const ( | ||
| // Number of retries to read CRI log file. | ||
| criLogsReadRetries = 5 | ||
| // Delay between each retry attemtp in reading CRI log file. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attempt
| } | ||
| switch logMode { | ||
| case "k8s-plain": | ||
| kubernetesLogPath, ok := ra.Annotations.Get("coreos.com/rkt/experiment/kubernetes-log-path") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this kubernetes-log-filename
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log path is the term that CRI and kubelet use and I just wanted to be consistent. But I don't care if you seriously care about it. ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. In the CRI they use LogDirectory and LogPath, which I find confusing. Since these annotations are k8s-specific I guess staying consistent with their names is ok. Disregard the comment then.
| uw.err = fmt.Errorf("kubernetes-log-path annotation needs to be specified when k8s-plain logging mode is used") | ||
| return nil | ||
| } | ||
| file.WriteString(fmt.Sprintf("KUBERNETES_LOG_PATH=%s\n", kubernetesLogPath)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too, KUBERNETES_LOG_FILENAME
| // TODO(lucab): check what should be the target path with Euan | ||
| logTarget := filepath.Join(pathPrefix, appName, "logfile") | ||
| logFile, err = os.OpenFile(logTarget, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) | ||
| logPath := os.Getenv("KUBERNETES_LOG_PATH") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then this would be logFilename
stage1/iottymux/iottymux.go
Outdated
| logTarget := filepath.Join(pathPrefix, appName, "logfile") | ||
| logFile, err = os.OpenFile(logTarget, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) | ||
| logPath := os.Getenv("KUBERNETES_LOG_PATH") | ||
| logFullPath := filepath.Clean(filepath.Join("/rkt/kubernetes", logPath)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this logPath
stage1/init/init.go
Outdated
|
|
||
| kubernetesLogDir, ok := p.Manifest.Annotations.Get("coreos.com/rkt/experiment/kubernetes-log-dir") | ||
| if ok { | ||
| args = append(args, fmt.Sprintf("--bind=%s:/rkt/kubernetes", kubernetesLogDir)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/rkt/kubernetes seems too broad, maybe we can make it /rkt/kubernetes/log or such
75531ff to
690d6f5
Compare
| return fmt.Errorf("The log path isn't inside /rkt/kubernetes, which most probably means that user tried to escape that directory by using '..'") | ||
| } | ||
|
|
||
| logFile, err = os.OpenFile(logFullPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that according to the CRI KUBERNETES_LOG_PATH can be a real path and not just a file name so this would fail if it turns out to be more than a file name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can just add a comment mentioning this because I don't think it's an issue for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... I don't think so, logPath is always a file name. I didn't see any code in kubelet or fragment of spec which proves otherwise. The name is just confusing. But yes, I can add a comment.
iaguis
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, actually can you re-work the commit message, it doesn't seem to reflect the contents of the commit anymore.
eb4b52f to
4aaedfe
Compare
|
@iaguis done |
4aaedfe to
c302c34
Compare
|
Thanks! |
|
Tests are failing: Probably because in the test |
|
@iaguis checking whether the log file contains the actual output is important, we cannot just accept empty logs. I assume that rkt is just working slightly slower in semaphoreci and we need to add some recheks and timeouts. |
|
Yes, I mean doing that in the test so we give some more time for the logs
to appear
…On Oct 6, 2017 15:08, "Michal Rostecki" ***@***.***> wrote:
@iaguis <https://github.com/iaguis> checking whether the log file
contains the actual output is important, we cannot just accept empty logs.
I assume that rkt is just working slightly slower in semaphoreci and we
need to add some recheks and timeouts.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#3798 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAe92gT8Cjpw_sY_xzyxwFOJOAXLIzJBks5spiZWgaJpZM4PbESX>
.
|
37d1b4d to
64e165e
Compare
This is the change made for rktlet. Kubelet expect the CRI-formatted logs to be in specific directory and it provides that information with CRI. CRI-compatible logs are generated by iottymux. That specific directory which kubelet is looking at, exists in the host filesystem. In order to make it available for iottymux, we need to mount it into the stage1 container. That's why this commit introduces the new annotation called coreos.com/rkt/experiment/kubernetes-log-dir. This annotation is read by stage1 init and the directory by it is bind mounted to the stage1 nspawn container (the mountpoint inside container is /rkt/kubernetes/log). Kubelet provides the name of a logfile of a container via CRI. Since the container concept in Kubernetes refers to an app in rkt, we use the annotation named coreos.com/rkt/experiment/kubernetes-log-path to pass it from rktlet. Then stage1 init redirects that value in KUBERNETES_LOG_PATH environment variable for iottymux. Then finally, iottymux saves the logs to /rkt/kubernetes/log/$KUBERNETES_LOG_PATH file, i.e. /rkt/kubernetes/log/0a8f619b-df34-4c57-86e5-84a551452db1/etcd_0.log. Fixes rkt#3795
64e165e to
b710725
Compare
rktlet was using journal2cri binary for converting journald logs to CRI format. Container with that binary was injected into each pod. Unfortunately, it couldn't disginguish stdout and stderr output. It also wasn't very stable and reliable. Currently rkt is able to store logs in CRI format in provided directory[1]. Therefore we can remove journal2cri completely and just point rkt to the directory which CRI provides. Fixes kubernetes-retired#107 [1] rkt/rkt#3798
rktlet was using journal2cri binary for converting journald logs to CRI format. Container with that binary was injected into each pod. Unfortunately, it couldn't disginguish stdout and stderr output. It also wasn't very stable and reliable. Currently rkt is able to store logs in CRI format in provided directory[1]. Therefore we can remove journal2cri completely and just point rkt to the directory which CRI provides. Fixes kubernetes-retired#107 [1] rkt/rkt#3798
rktlet was using journal2cri binary for converting journald logs to CRI format. Container with that binary was injected into each pod. Unfortunately, it couldn't disginguish stdout and stderr output. It also wasn't very stable and reliable. Currently rkt is able to store logs in CRI format in provided directory[1]. Therefore we can remove journal2cri completely and just point rkt to the directory which CRI provides. Fixes kubernetes-retired#107 [1] rkt/rkt#3798
rktlet was using journal2cri binary for converting journald logs to CRI format. Container with that binary was injected into each pod. Unfortunately, it couldn't disginguish stdout and stderr output. It also wasn't very stable and reliable. Currently rkt is able to store logs in CRI format in provided directory[1]. Therefore we can remove journal2cri completely and just point rkt to the directory which CRI provides. Fixes kubernetes-retired#107 [1] rkt/rkt#3798
rktlet was using journal2cri binary for converting journald logs to CRI format. Container with that binary was injected into each pod. Unfortunately, it couldn't disginguish stdout and stderr output. It also wasn't very stable and reliable. Currently rkt is able to store logs in CRI format in provided directory[1]. Therefore we can remove journal2cri completely and just point rkt to the directory which CRI provides. Fixes kubernetes-retired#107 [1] rkt/rkt#3798
rktlet was using journal2cri binary for converting journald logs to CRI format. Container with that binary was injected into each pod. Unfortunately, it couldn't disginguish stdout and stderr output. It also wasn't very stable and reliable. Currently rkt is able to store logs in CRI format in provided directory[1]. Therefore we can remove journal2cri completely and just point rkt to the directory which CRI provides. Fixes kubernetes-retired#107 [1] rkt/rkt#3798
rktlet was using journal2cri binary for converting journald logs to CRI format. Container with that binary was injected into each pod. Unfortunately, it couldn't disginguish stdout and stderr output. It also wasn't very stable and reliable. Currently rkt is able to store logs in CRI format in provided directory[1]. Therefore we can remove journal2cri completely and just point rkt to the directory which CRI provides. Fixes kubernetes-retired#107 [1] rkt/rkt#3798
rktlet was using journal2cri binary for converting journald logs to CRI format. Container with that binary was injected into each pod. Unfortunately, it couldn't disginguish stdout and stderr output. It also wasn't very stable and reliable. Currently rkt is able to store logs in CRI format in provided directory[1]. Therefore we can remove journal2cri completely and just point rkt to the directory which CRI provides. Fixes kubernetes-retired#107 [1] rkt/rkt#3798
rktlet was using journal2cri binary for converting journald logs to CRI format. Container with that binary was injected into each pod. Unfortunately, it couldn't disginguish stdout and stderr output. It also wasn't very stable and reliable. Currently rkt is able to store logs in CRI format in provided directory[1]. Therefore we can remove journal2cri completely and just point rkt to the directory which CRI provides. Fixes kubernetes-retired#107 [1] rkt/rkt#3798
rktlet was using journal2cri binary for converting journald logs to CRI format. Container with that binary was injected into each pod. Unfortunately, it couldn't disginguish stdout and stderr output. It also wasn't very stable and reliable. Currently rkt is able to store logs in CRI format in provided directory[1]. Therefore we can remove journal2cri completely and just point rkt to the directory which CRI provides. Fixes kubernetes-retired#107 [1] rkt/rkt#3798
rktlet was using journal2cri binary for converting journald logs to CRI format. Container with that binary was injected into each pod. Unfortunately, it couldn't disginguish stdout and stderr output. It also wasn't very stable and reliable. Currently rkt is able to store logs in CRI format in provided directory[1]. Therefore we can remove journal2cri completely and just point rkt to the directory which CRI provides. Fixes kubernetes-retired#107 [1] rkt/rkt#3798
This is the change made for rktlet. Kubelet expect the CRI-formatted
logs to be in specific directory and it provides that information with
CRI.
CRI-compatible logs are generated by iottymux. That specific directory
which kubelet is looking at, exists in the host filesystem. In order to
make it available for iottymux, we need to mount it into the stage1
container. That's why this commit introduces the new annotation called
coreos.com/rkt/experiment/kubernetes-log-dir. This annotation is read
by stage1 init and the directory by it is bind mounted to the stage1
nspawn container (the mountpoint inside container is /rkt/kubernetes/log).
Kubelet provides the name of a logfile of a container via CRI. Since the
container concept in Kubernetes refers to an app in rkt, we use the
annotation named coreos.com/rkt/experiment/kubernetes-log-path to pass it
from rktlet. Then stage1 init redirects that value in KUBERNETES_LOG_PATH
environment variable for iottymux.
Then finally, iottymux saves the logs to /rkt/kubernetes/log/$KUBERNETES_LOG_PATH
file, i.e. /rkt/kubernetes/log/0a8f619b-df34-4c57-86e5-84a551452db1/etcd_0.log.
Fixes #3795