Browse Source

feat: 容器增加日志切割 (#1077)

pull/1079/head
ssongliu 2 years ago committed by GitHub
parent
commit
950c6b9d08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      backend/app/dto/docker.go
  2. 71
      backend/app/service/docker.go
  3. 2
      frontend/src/api/interface/container.ts
  4. 3
      frontend/src/lang/modules/en.ts
  5. 3
      frontend/src/lang/modules/zh.ts
  6. 2
      frontend/src/views/container/container/index.vue
  7. 97
      frontend/src/views/container/setting/index.vue

3
backend/app/dto/docker.go

@ -13,6 +13,9 @@ type DaemonJsonConf struct {
LiveRestore bool `json:"liveRestore"`
IPTables bool `json:"iptables"`
CgroupDriver string `json:"cgroupDriver"`
LogMaxSize string `json:"logMaxSize"`
LogMaxFile string `json:"logMaxFile"`
}
type DockerOperation struct {

71
backend/app/service/docker.go

@ -30,12 +30,17 @@ func NewIDockerService() IDockerService {
}
type daemonJsonItem struct {
Status string `json:"status"`
Mirrors []string `json:"registry-mirrors"`
Registries []string `json:"insecure-registries"`
LiveRestore bool `json:"live-restore"`
IPTables bool `json:"iptables"`
ExecOpts []string `json:"exec-opts"`
Status string `json:"status"`
Mirrors []string `json:"registry-mirrors"`
Registries []string `json:"insecure-registries"`
LiveRestore bool `json:"live-restore"`
IPTables bool `json:"iptables"`
ExecOpts []string `json:"exec-opts"`
LogOption logOption `json:"log-opts"`
}
type logOption struct {
LogMaxSize string `json:"max-size"`
LogMaxFile string `json:"max-file"`
}
func (u *DockerService) LoadDockerStatus() string {
@ -102,6 +107,8 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
break
}
}
data.LogMaxSize = conf.LogOption.LogMaxSize
data.LogMaxFile = conf.LogOption.LogMaxFile
data.Mirrors = conf.Mirrors
data.Registries = conf.Registries
data.IPTables = conf.IPTables
@ -134,6 +141,9 @@ func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
} else {
deamonMap["registry-mirrors"] = req.Mirrors
}
changeLogOption(deamonMap, req.LogMaxFile, req.LogMaxSize)
if !req.LiveRestore {
delete(deamonMap, "live-restore")
} else {
@ -217,3 +227,52 @@ func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
}
return nil
}
func changeLogOption(deamonMap map[string]interface{}, logMaxFile, logMaxSize string) {
if opts, ok := deamonMap["log-opts"]; ok {
if len(logMaxFile) != 0 || len(logMaxSize) != 0 {
deamonMap["log-driver"] = "json-file"
}
optsMap, isMap := opts.(map[string]interface{})
if isMap {
if len(logMaxFile) != 0 {
optsMap["max-file"] = logMaxFile
} else {
delete(optsMap, "max-file")
}
if len(logMaxSize) != 0 {
optsMap["max-size"] = logMaxSize
} else {
delete(optsMap, "max-size")
}
if len(optsMap) == 0 {
delete(deamonMap, "log-opts")
}
} else {
optsMap := make(map[string]interface{})
if len(logMaxFile) != 0 {
optsMap["max-file"] = logMaxFile
}
if len(logMaxSize) != 0 {
optsMap["max-size"] = logMaxSize
}
if len(optsMap) != 0 {
deamonMap["log-opts"] = optsMap
}
}
} else {
if len(logMaxFile) != 0 || len(logMaxSize) != 0 {
deamonMap["log-driver"] = "json-file"
}
optsMap := make(map[string]interface{})
if len(logMaxFile) != 0 {
optsMap["max-file"] = logMaxFile
}
if len(logMaxSize) != 0 {
optsMap["max-size"] = logMaxSize
}
if len(optsMap) != 0 {
deamonMap["log-opts"] = optsMap
}
}
}

2
frontend/src/api/interface/container.ts

@ -256,5 +256,7 @@ export namespace Container {
liveRestore: boolean;
iptables: boolean;
cgroupDriver: string;
logMaxSize: string;
logMaxFile: string;
}
}

3
frontend/src/lang/modules/en.ts

@ -575,6 +575,9 @@ const message = {
'The acceleration URL is preferred to perform operations. If this parameter is set to empty, mirror acceleration is disabled.',
mirrorsHelper2: 'For details, see the official documents, ',
registries: 'Insecure registries',
cutLog: 'Log option',
maxSize: 'Max-Size',
maxFile: 'Max-File',
liveHelper:
'Allows the running container state to be preserved in case of unexpected shutdown or crash of the Docker daemon',
liveWithSwarmHelper: 'live-restore daemon configuration is incompatible with swarm mode.',

3
frontend/src/lang/modules/zh.ts

@ -589,6 +589,9 @@ const message = {
mirrorsHelper: '优先使用加速 URL 执行操作设置为空则取消镜像加速',
mirrorsHelper2: '具体操作配置请参照官方文档',
registries: '私有仓库',
cutLog: '日志切割',
maxSize: '文件大小',
maxFile: '保留份数',
liveHelper: '允许在 Docker 守护进程发生意外停机或崩溃时保留正在运行的容器状态',
liveWithSwarmHelper: 'live-restore 守护进程配置与 Swarm 模式不兼容',
iptablesDisable: '关闭 iptables',

2
frontend/src/views/container/container/index.vue

@ -78,7 +78,7 @@
</el-table-column>
<el-table-column :label="$t('container.source')" show-overflow-tooltip min-width="125" fix>
<template #default="{ row }">
cpu: {{ row.cpuPercent.toFixed(2) }}% {{ $t('monitor.memory') }}:
CPU: {{ row.cpuPercent.toFixed(2) }}% {{ $t('monitor.memory') }}:
{{ row.memoryPercent.toFixed(2) }}%
</template>
</el-table-column>

97
frontend/src/views/container/setting/index.vue

@ -44,7 +44,7 @@
<el-row style="margin-top: 20px" v-if="confShowType === 'base'">
<el-col :span="1"><br /></el-col>
<el-col :span="10">
<el-form :model="form" label-position="left" ref="formRef" label-width="150px">
<el-form :model="form" label-position="left" :rules="rules" ref="formRef" label-width="150px">
<el-form-item :label="$t('container.mirrors')" prop="mirrors">
<el-input
type="textarea"
@ -73,6 +73,32 @@
v-model="form.registries"
/>
</el-form-item>
<el-form-item :label="$t('container.cutLog')" prop="hasLogOption">
<el-switch v-model="form.logOptionShow"></el-switch>
</el-form-item>
<div v-if="form.logOptionShow">
<el-form-item prop="logMaxSize">
<el-input v-model.number="form.logMaxSize">
<template #prepend>{{ $t('container.maxSize') }}</template>
<template #append>
<el-select v-model="form.sizeUnit" style="width: 70px">
<el-option label="b" value="b"></el-option>
<el-option label="k" value="k"></el-option>
<el-option label="m" value="m"></el-option>
<el-option label="g" value="g"></el-option>
<el-option label="t" value="t"></el-option>
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item prop="logMaxFile">
<el-input v-model.number="form.logMaxFile">
<template #prepend>{{ $t('container.maxFile') }}</template>
</el-input>
</el-form-item>
</div>
<el-form-item label="iptables" prop="iptables">
<el-switch v-model="form.iptables" @change="onChangeIptables"></el-switch>
</el-form-item>
@ -163,6 +189,7 @@ import {
updateDaemonJsonByfile,
} from '@/api/modules/container';
import { MsgSuccess } from '@/utils/message';
import { checkNumberRange } from '@/global/form-rules';
const loading = ref(false);
const showDaemonJsonAlert = ref(false);
@ -178,6 +205,14 @@ const form = reactive({
liveRestore: false,
iptables: true,
cgroupDriver: '',
logOptionShow: false,
logMaxSize: 10,
sizeUnit: 'm',
logMaxFile: 3,
});
const rules = reactive({
logMaxSize: [checkNumberRange(1, 1024000)],
logMaxFile: [checkNumberRange(1, 100)],
});
const formRef = ref<FormInstance>();
@ -276,7 +311,14 @@ const onSubmitSave = async () => {
liveRestore: form.liveRestore,
iptables: form.iptables,
cgroupDriver: form.cgroupDriver,
logMaxSize: form.logMaxSize + form.sizeUnit,
logMaxFile: form.logMaxFile + '',
};
if (!form.logOptionShow) {
param.logMaxFile = '';
param.logMaxSize = '';
}
loading.value = true;
await updateDaemonJson(param)
.then(() => {
@ -308,15 +350,50 @@ const changeMode = async () => {
};
const search = async () => {
const res = await loadDaemonJson();
form.isSwarm = res.data.isSwarm;
form.status = res.data.status;
form.version = res.data.version;
form.cgroupDriver = res.data.cgroupDriver;
form.liveRestore = res.data.liveRestore;
form.iptables = res.data.iptables;
form.mirrors = res.data.registryMirrors ? res.data.registryMirrors.join('\n') : '';
form.registries = res.data.insecureRegistries ? res.data.insecureRegistries.join('\n') : '';
loading.value = true;
await loadDaemonJson()
.then((res) => {
loading.value = false;
form.isSwarm = res.data.isSwarm;
form.status = res.data.status;
form.version = res.data.version;
form.cgroupDriver = res.data.cgroupDriver;
form.liveRestore = res.data.liveRestore;
form.iptables = res.data.iptables;
form.mirrors = res.data.registryMirrors ? res.data.registryMirrors.join('\n') : '';
form.registries = res.data.insecureRegistries ? res.data.insecureRegistries.join('\n') : '';
if (res.data.logMaxFile || res.data.logMaxSize) {
form.logOptionShow = true;
}
form.logMaxFile = Number(res.data.logMaxFile);
form.logMaxSize = loadSize(res.data.logMaxSize);
})
.catch(() => {
loading.value = false;
});
};
const loadSize = (value: string) => {
if (value.indexOf('b') !== -1 || value.indexOf('B') !== -1) {
form.sizeUnit = 'b';
return Number(value.replaceAll('b', '').replaceAll('B', ''));
}
if (value.indexOf('k') !== -1 || value.indexOf('K') !== -1) {
form.sizeUnit = 'k';
return Number(value.replaceAll('k', '').replaceAll('K', ''));
}
if (value.indexOf('m') !== -1 || value.indexOf('M') !== -1) {
form.sizeUnit = 'm';
return Number(value.replaceAll('m', '').replaceAll('M', ''));
}
if (value.indexOf('g') !== -1 || value.indexOf('G') !== -1) {
form.sizeUnit = 'g';
return Number(value.replaceAll('g', '').replaceAll('G', ''));
}
if (value.indexOf('t') !== -1 || value.indexOf('T') !== -1) {
form.sizeUnit = 't';
return Number(value.replaceAll('t', '').replaceAll('T', ''));
}
};
onMounted(() => {

Loading…
Cancel
Save