commit
0ff3580f05
10 changed files with 154 additions and 13 deletions
|
@ -108,6 +108,10 @@ hooks_dir_path = "{{ .HooksDirPath }}"
|
|||
# pids_limit is the number of processes allowed in a container
|
||||
pids_limit = {{ .PidsLimit }}
|
||||
|
||||
# log_size_max is the max limit for the container log size in bytes.
|
||||
# Negative values indicate that no limit is imposed.
|
||||
log_size_max = {{ .LogSizeMax }}
|
||||
|
||||
# The "crio.image" table contains settings pertaining to the
|
||||
# management of OCI images.
|
||||
[crio.image]
|
||||
|
|
|
@ -125,6 +125,9 @@ func mergeConfig(config *server.Config, ctx *cli.Context) error {
|
|||
if ctx.GlobalIsSet("pids-limit") {
|
||||
config.PidsLimit = ctx.GlobalInt64("pids-limit")
|
||||
}
|
||||
if ctx.GlobalIsSet("log-size-max") {
|
||||
config.LogSizeMax = ctx.GlobalInt64("log-size-max")
|
||||
}
|
||||
if ctx.GlobalIsSet("cni-config-dir") {
|
||||
config.NetworkDir = ctx.GlobalString("cni-config-dir")
|
||||
}
|
||||
|
@ -289,6 +292,11 @@ func main() {
|
|||
Value: libkpod.DefaultPidsLimit,
|
||||
Usage: "maximum number of processes allowed in a container",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "log-size-max",
|
||||
Value: libkpod.DefaultLogSizeMax,
|
||||
Usage: "maximum log size in bytes for a container",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "cni-config-dir",
|
||||
Usage: "CNI configuration files directory",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -20,6 +21,7 @@
|
|||
#include <termios.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-unix.h>
|
||||
|
@ -107,6 +109,7 @@ static bool opt_exec = false;
|
|||
static char *opt_log_path = NULL;
|
||||
static char *opt_exit_dir = NULL;
|
||||
static int opt_timeout = 0;
|
||||
static int64_t opt_log_size_max = -1;
|
||||
static GOptionEntry opt_entries[] =
|
||||
{
|
||||
{ "terminal", 't', 0, G_OPTION_ARG_NONE, &opt_terminal, "Terminal", NULL },
|
||||
|
@ -122,6 +125,7 @@ static GOptionEntry opt_entries[] =
|
|||
{ "exit-dir", 0, 0, G_OPTION_ARG_STRING, &opt_exit_dir, "Path to the directory where exit files are written", NULL },
|
||||
{ "log-path", 'l', 0, G_OPTION_ARG_STRING, &opt_log_path, "Log file path", NULL },
|
||||
{ "timeout", 'T', 0, G_OPTION_ARG_INT, &opt_timeout, "Timeout in seconds", NULL },
|
||||
{ "log-size-max", 0, 0, G_OPTION_ARG_INT64, &opt_log_size_max, "Maximum size of log file", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
@ -130,6 +134,8 @@ static GOptionEntry opt_entries[] =
|
|||
|
||||
#define CGROUP_ROOT "/sys/fs/cgroup"
|
||||
|
||||
static int log_fd = -1;
|
||||
|
||||
static ssize_t write_all(int fd, const void *buf, size_t count)
|
||||
{
|
||||
size_t remaining = count;
|
||||
|
@ -281,11 +287,13 @@ const char *stdpipe_name(stdpipe_t pipe)
|
|||
* line in buf, and will partially write the final line of the log if buf is
|
||||
* not terminated by a newline.
|
||||
*/
|
||||
int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen)
|
||||
static int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen)
|
||||
{
|
||||
char tsbuf[TSBUFLEN];
|
||||
static stdpipe_t trailing_line = NO_PIPE;
|
||||
writev_buffer_t bufv = {0};
|
||||
static int64_t bytes_written = 0;
|
||||
int64_t bytes_to_be_written = 0;
|
||||
|
||||
/*
|
||||
* Use the same timestamp for every line of the log in this buffer.
|
||||
|
@ -299,6 +307,8 @@ int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen)
|
|||
while (buflen > 0) {
|
||||
const char *line_end = NULL;
|
||||
ptrdiff_t line_len = 0;
|
||||
bool insert_newline = FALSE;
|
||||
bool insert_timestamp = FALSE;
|
||||
|
||||
/* Find the end of the line, or alternatively the end of the buffer. */
|
||||
line_end = memchr(buf, '\n', buflen);
|
||||
|
@ -306,12 +316,15 @@ int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen)
|
|||
line_end = &buf[buflen-1];
|
||||
line_len = line_end - buf + 1;
|
||||
|
||||
/*
|
||||
* Write the (timestamp, stream) tuple if there isn't any trailing
|
||||
* output from the previous line (or if there is trailing output but
|
||||
* the current buffer being printed is from a different pipe).
|
||||
*/
|
||||
bytes_to_be_written = line_len;
|
||||
if (trailing_line != pipe) {
|
||||
/*
|
||||
* Write the (timestamp, stream) tuple if there isn't any trailing
|
||||
* output from the previous line (or if there is trailing output but
|
||||
* the current buffer being printed is from a different pipe).
|
||||
*/
|
||||
insert_timestamp = TRUE;
|
||||
bytes_to_be_written += (TSBUFLEN - 1);
|
||||
/*
|
||||
* If there was a trailing line from a different pipe, prepend a
|
||||
* newline to split it properly. This technically breaks the flow
|
||||
|
@ -319,9 +332,49 @@ int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen)
|
|||
* wasn't one output) but without modifying the file in a
|
||||
* non-append-only way there's not much we can do.
|
||||
*/
|
||||
if ((trailing_line != NO_PIPE &&
|
||||
writev_buffer_append_segment(fd, &bufv, "\n", -1) < 0) ||
|
||||
writev_buffer_append_segment(fd, &bufv, tsbuf, -1) < 0) {
|
||||
if (trailing_line != NO_PIPE) {
|
||||
insert_newline = TRUE;
|
||||
bytes_to_be_written += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We re-open the log file if writing out the bytes will exceed the max
|
||||
* log size. We also reset the state so that the new file is started with
|
||||
* a timestamp.
|
||||
*/
|
||||
if ((opt_log_size_max > 0) && (bytes_written + bytes_to_be_written) > opt_log_size_max) {
|
||||
ninfo("Creating new log file");
|
||||
insert_newline = FALSE;
|
||||
insert_timestamp = TRUE;
|
||||
bytes_written = 0;
|
||||
|
||||
/* Close the existing fd */
|
||||
close(fd);
|
||||
|
||||
/* Unlink the file */
|
||||
if (unlink(opt_log_path) < 0) {
|
||||
pexit("Failed to unlink log file");
|
||||
}
|
||||
|
||||
/* Open the log path file again */
|
||||
log_fd = open(opt_log_path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0600);
|
||||
if (log_fd < 0)
|
||||
pexit("Failed to open log file");
|
||||
fd = log_fd;
|
||||
}
|
||||
|
||||
/* Output a newline */
|
||||
if (insert_newline) {
|
||||
if (writev_buffer_append_segment(fd, &bufv, "\n", -1) < 0) {
|
||||
nwarn("failed to write newline to log");
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Output a timestamp */
|
||||
if (insert_timestamp) {
|
||||
if (writev_buffer_append_segment(fd, &bufv, tsbuf, -1) < 0) {
|
||||
nwarn("failed to write (timestamp, stream) to log");
|
||||
goto next;
|
||||
}
|
||||
|
@ -333,6 +386,8 @@ int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen)
|
|||
goto next;
|
||||
}
|
||||
|
||||
bytes_written += bytes_to_be_written;
|
||||
|
||||
/* If we did not output a full line, then we are a trailing_line. */
|
||||
trailing_line = (*line_end == '\n') ? NO_PIPE : pipe;
|
||||
|
||||
|
@ -346,6 +401,8 @@ next:
|
|||
nwarn("failed to flush buffer to log");
|
||||
}
|
||||
|
||||
ninfo("Total bytes written: %"PRId64"", bytes_written);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -481,7 +538,6 @@ static int conn_sock = -1;
|
|||
static int conn_sock_readable;
|
||||
static int conn_sock_writable;
|
||||
|
||||
static int log_fd = -1;
|
||||
static int oom_event_fd = -1;
|
||||
static int attach_socket_fd = -1;
|
||||
static int console_socket_fd = -1;
|
||||
|
|
|
@ -105,6 +105,9 @@ set the CPU profile file path
|
|||
**--log-format**=""
|
||||
Set the format used by logs ('text' (default), or 'json') (default: "text")
|
||||
|
||||
**--log-size-max**=""
|
||||
Maximum log size in bytes for a container (default: -1 (no limit))
|
||||
|
||||
**--pause-command**=""
|
||||
Path to the pause executable in the pause image (default: "/pause")
|
||||
|
||||
|
|
|
@ -54,6 +54,11 @@ The `crio` table supports the following options:
|
|||
**conmon_env**=[]
|
||||
Environment variable list for conmon process (default: ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",])
|
||||
|
||||
**log_size_max**=""
|
||||
Maximum sized allowed for the container log file (default: -1)
|
||||
Negative numbers indicate that no size limit is imposed.
|
||||
The file is truncated and re-opened so the limit is never exceeded.
|
||||
|
||||
**pids_limit**=""
|
||||
Maximum number of processes allowed in a container (default: 1024)
|
||||
|
||||
|
|
|
@ -51,6 +51,10 @@ const (
|
|||
// DefaultPidsLimit is the default value for maximum number of processes
|
||||
// allowed inside a container
|
||||
DefaultPidsLimit = 1024
|
||||
|
||||
// DefaultLogSizeMax is the default value for the maximum log size
|
||||
// allowed for a container. Negative values mean that no limit is imposed.
|
||||
DefaultLogSizeMax = -1
|
||||
)
|
||||
|
||||
// This structure is necessary to fake the TOML tables when parsing,
|
||||
|
@ -145,6 +149,12 @@ type RuntimeConfig struct {
|
|||
// by the cgroup process number controller.
|
||||
PidsLimit int64 `toml:"pids_limit"`
|
||||
|
||||
// LogSizeMax is the maximum number of bytes after which the log file
|
||||
// will be truncated. It can be expressed as a human-friendly string
|
||||
// that is parsed to bytes.
|
||||
// Negative values indicate that the log file won't be truncated.
|
||||
LogSizeMax int64 `toml:"log_size_max"`
|
||||
|
||||
// ContainerExitsDir is the directory in which container exit files are
|
||||
// written to by conmon.
|
||||
ContainerExitsDir string `toml:"container_exits_dir"`
|
||||
|
@ -274,6 +284,7 @@ func DefaultConfig() *Config {
|
|||
PidsLimit: DefaultPidsLimit,
|
||||
ContainerExitsDir: containerExitsDir,
|
||||
HooksDirPath: DefaultHooksDirPath,
|
||||
LogSizeMax: DefaultLogSizeMax,
|
||||
},
|
||||
ImageConfig: ImageConfig{
|
||||
DefaultTransport: defaultTransport,
|
||||
|
|
|
@ -121,7 +121,7 @@ func New(config *Config) (*ContainerServer, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
runtime, err := oci.New(config.Runtime, config.RuntimeUntrustedWorkload, config.DefaultWorkloadTrust, config.Conmon, config.ConmonEnv, config.CgroupManager, config.ContainerExitsDir)
|
||||
runtime, err := oci.New(config.Runtime, config.RuntimeUntrustedWorkload, config.DefaultWorkloadTrust, config.Conmon, config.ConmonEnv, config.CgroupManager, config.ContainerExitsDir, config.LogSizeMax)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ const (
|
|||
)
|
||||
|
||||
// New creates a new Runtime with options provided
|
||||
func New(runtimeTrustedPath string, runtimeUntrustedPath string, trustLevel string, conmonPath string, conmonEnv []string, cgroupManager string, containerExitsDir string) (*Runtime, error) {
|
||||
func New(runtimeTrustedPath string, runtimeUntrustedPath string, trustLevel string, conmonPath string, conmonEnv []string, cgroupManager string, containerExitsDir string, logSizeMax int64) (*Runtime, error) {
|
||||
r := &Runtime{
|
||||
name: filepath.Base(runtimeTrustedPath),
|
||||
trustedPath: runtimeTrustedPath,
|
||||
|
@ -48,6 +48,7 @@ func New(runtimeTrustedPath string, runtimeUntrustedPath string, trustLevel stri
|
|||
conmonEnv: conmonEnv,
|
||||
cgroupManager: cgroupManager,
|
||||
containerExitsDir: containerExitsDir,
|
||||
logSizeMax: logSizeMax,
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
@ -62,6 +63,7 @@ type Runtime struct {
|
|||
conmonEnv []string
|
||||
cgroupManager string
|
||||
containerExitsDir string
|
||||
logSizeMax int64
|
||||
}
|
||||
|
||||
// syncInfo is used to return data from monitor process to daemon
|
||||
|
@ -156,6 +158,9 @@ func (r *Runtime) CreateContainer(c *Container, cgroupParent string) error {
|
|||
args = append(args, "-p", filepath.Join(c.bundlePath, "pidfile"))
|
||||
args = append(args, "-l", c.logPath)
|
||||
args = append(args, "--exit-dir", r.containerExitsDir)
|
||||
if r.logSizeMax >= 0 {
|
||||
args = append(args, "--log-size-max", fmt.Sprintf("%v", r.logSizeMax))
|
||||
}
|
||||
if c.terminal {
|
||||
args = append(args, "-t")
|
||||
} else if c.stdin {
|
||||
|
|
|
@ -255,6 +255,53 @@ function teardown() {
|
|||
stop_crio
|
||||
}
|
||||
|
||||
@test "ctr log max" {
|
||||
LOG_SIZE_MAX_LIMIT=10000 start_crio
|
||||
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
pod_id="$output"
|
||||
run crioctl pod list
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# Create a new container.
|
||||
newconfig=$(mktemp --tmpdir crio-config.XXXXXX.json)
|
||||
cp "$TESTDATA"/container_config_logging.json "$newconfig"
|
||||
sed -i 's|"%shellcommand%"|"for i in $(seq 250); do echo $i; done"|' "$newconfig"
|
||||
run crioctl ctr create --config "$newconfig" --pod "$pod_id"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
ctr_id="$output"
|
||||
run crioctl ctr start --id "$ctr_id"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
sleep 6
|
||||
run crioctl ctr status --id "$ctr_id"
|
||||
[ "$status" -eq 0 ]
|
||||
run crioctl ctr remove --id "$ctr_id"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# Check that the output is what we expect.
|
||||
logpath="$DEFAULT_LOG_PATH/$pod_id/$ctr_id.log"
|
||||
[ -f "$logpath" ]
|
||||
echo "$logpath :: $(cat "$logpath")"
|
||||
len=$(wc -l "$logpath" | awk '{print $1}')
|
||||
[ $len -lt 250 ]
|
||||
|
||||
run crioctl pod stop --id "$pod_id"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run crioctl pod remove --id "$pod_id"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
cleanup_ctrs
|
||||
cleanup_pods
|
||||
stop_crio
|
||||
}
|
||||
|
||||
# regression test for #127
|
||||
@test "ctrs status for a pod" {
|
||||
start_crio
|
||||
|
|
|
@ -56,6 +56,8 @@ CGROUP_MANAGER=${CGROUP_MANAGER:-cgroupfs}
|
|||
IMAGE_VOLUMES=${IMAGE_VOLUMES:-mkdir}
|
||||
# Container pids limit
|
||||
PIDS_LIMIT=${PIDS_LIMIT:-1024}
|
||||
# Log size max limit
|
||||
LOG_SIZE_MAX_LIMIT=${LOG_SIZE_MAX_LIMIT:--1}
|
||||
|
||||
TESTDIR=$(mktemp -d)
|
||||
|
||||
|
@ -231,7 +233,7 @@ function start_crio() {
|
|||
"$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=mrunalp/image-volume-test --import-from=dir:"$ARTIFACTS_PATH"/image-volume-test-image --add-name=docker.io/library/mrunalp/image-volume-test --signature-policy="$INTEGRATION_ROOT"/policy.json
|
||||
"$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=busybox:latest --import-from=dir:"$ARTIFACTS_PATH"/busybox-image --add-name=docker.io/library/busybox:latest --signature-policy="$INTEGRATION_ROOT"/policy.json
|
||||
"$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=runcom/stderr-test:latest --import-from=dir:"$ARTIFACTS_PATH"/stderr-test --add-name=docker.io/runcom/stderr-test:latest --signature-policy="$INTEGRATION_ROOT"/policy.json
|
||||
"$CRIO_BINARY" ${HOOKS_OPTS} --conmon "$CONMON_BINARY" --listen "$CRIO_SOCKET" --cgroup-manager "$CGROUP_MANAGER" --registry "docker.io" --runtime "$RUNTIME_BINARY" --root "$TESTDIR/crio" --runroot "$TESTDIR/crio-run" $STORAGE_OPTS --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$CRIO_CNI_CONFIG" --signature-policy "$INTEGRATION_ROOT"/policy.json --image-volumes "$IMAGE_VOLUMES" --pids-limit "$PIDS_LIMIT" --config /dev/null config >$CRIO_CONFIG
|
||||
"$CRIO_BINARY" ${HOOKS_OPTS} --conmon "$CONMON_BINARY" --listen "$CRIO_SOCKET" --cgroup-manager "$CGROUP_MANAGER" --registry "docker.io" --runtime "$RUNTIME_BINARY" --root "$TESTDIR/crio" --runroot "$TESTDIR/crio-run" $STORAGE_OPTS --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$CRIO_CNI_CONFIG" --signature-policy "$INTEGRATION_ROOT"/policy.json --image-volumes "$IMAGE_VOLUMES" --pids-limit "$PIDS_LIMIT" --log-size-max "$LOG_SIZE_MAX_LIMIT" --config /dev/null config >$CRIO_CONFIG
|
||||
|
||||
# Prepare the CNI configuration files, we're running with non host networking by default
|
||||
if [[ -n "$4" ]]; then
|
||||
|
|
Loading…
Reference in a new issue