fix 容器重建

This commit is contained in:
bwcx_jzy 2023-06-28 10:41:19 +08:00
parent da0f307d2c
commit eb2818c3fc
No known key found for this signature in database
GPG Key ID: E187D6E9DDDE8C53
5 changed files with 111 additions and 53 deletions

View File

@ -27,6 +27,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.keepbx.jpom.plugins.IPlugin;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.dromara.jpom.common.JsonMessage;
import org.dromara.jpom.common.validator.ValidatorItem;
import org.dromara.jpom.permission.Feature;
@ -47,6 +48,7 @@ import java.util.Map;
* @author bwcx_jzy
* @since 2022/2/7
*/
@Slf4j
public abstract class BaseDockerContainerController extends BaseDockerController {
@ -212,6 +214,7 @@ public abstract class BaseDockerContainerController extends BaseDockerController
/**
* drop old container and create new container
*
* @return json
*/
@PostMapping(value = "rebuild-container", produces = MediaType.APPLICATION_JSON_VALUE)
@ -229,7 +232,11 @@ public abstract class BaseDockerContainerController extends BaseDockerController
// drop old container
if (StrUtil.isNotEmpty(containerId)) {
parameter.put("containerId", containerId);
plugin.execute("removeContainer", parameter);
try {
plugin.execute("removeContainer", parameter);
} catch (com.github.dockerjava.api.exception.NotFoundException notFoundException) {
log.warn(notFoundException.getMessage());
}
}
// create new container

View File

@ -41,6 +41,7 @@ import com.github.dockerjava.core.InvocationBuilder;
import com.github.dockerjava.core.NameParser;
import lombok.Lombok;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.dromara.jpom.util.StringUtil;
import org.springframework.util.Assert;
@ -61,6 +62,7 @@ import java.util.stream.Collectors;
* @since 2022/1/26
*/
@PluginConfig(name = "docker-cli")
@Slf4j
public class DefaultDockerPluginImpl implements IDockerConfigPlugin {
@ -571,7 +573,7 @@ public class DefaultDockerPluginImpl implements IDockerConfigPlugin {
dockerClient.removeImageCmd(imageId).withForce(false).exec();
successCount++;
} catch (Exception e) {
log.warn("删除容器异常", e);
}
}
failCount = imagesIds.length - successCount;

View File

@ -1,5 +1,12 @@
<template>
<div>
<a-alert v-if="containerData && Object.keys(containerData).length" message="操作提示" type="warning" show-icon>
<template #description>
容器重建是指使用已经创建的容器参数重新创建一个相同的容器
<div><b style="color: red">重启创建之前会自动将之前的容器删除掉</b>,如果未挂载容器数据目录请提前备份数据后再使用此功能</div>
<div><b>此功能不能保证新建的容器和之前容器参数完全一致请慎重使用</b></div>
</template>
</a-alert>
<a-form-model ref="editForm" :rules="rules" :model="temp" :label-col="{ span: 3 }" :wrapper-col="{ span: 20 }">
<a-form-model-item label="基础镜像" prop="name">
<a-row>
@ -310,12 +317,7 @@
</div>
</template>
<script>
import {
dockerImageCreateContainer,
dockerImageInspect,
dockerInspectContainer,
dockerContainerRebuildContainer
} from "@/api/docker-api";
import { dockerImageCreateContainer, dockerImageInspect, dockerInspectContainer, dockerContainerRebuildContainer } from "@/api/docker-api";
export default {
props: {
id: {
@ -351,7 +353,7 @@ export default {
{ pattern: /[a-zA-Z0-9][a-zA-Z0-9_.-]$/, message: "容器名称数字字母,且长度大于1", trigger: "blur" },
],
},
}
};
},
computed: {
reqDataId() {
@ -359,12 +361,12 @@ export default {
},
getLabels() {
if (!this.containerData.labels) {
return ""
return "";
}
let labels = ""
let labels = "";
Object.keys(this.containerData.labels).map((key) => {
labels += `${key}=${this.containerData.labels[key]}&`
})
labels += `${key}=${this.containerData.labels[key]}&`;
});
return labels.slice(0, -1);
},
},
@ -388,8 +390,8 @@ export default {
item.disabled = item.privatePort !== null;
item.port = item.privatePort;
return item;
})
return _ports.length > 0 ? _ports : null
});
return _ports.length > 0 ? _ports : null;
},
getPortsFromExposedPorts(exposedPorts) {
const _ports = exposedPorts.map((item) => {
@ -397,44 +399,80 @@ export default {
item.ip = "0.0.0.0";
item.scheme = item.scheme || "tcp";
return item;
})
return _ports.length > 0 ? _ports : null
});
return _ports.length > 0 ? _ports : null;
},
getVolumesFromMounts(mounts) {
const _mounts = mounts.map((item) => {
item.disabled = item.destination !== null;
item.host = item.source;
item.container = item.destination;
item.container = item.destination?.path;
return item;
})
return _mounts.length > 0 ? _mounts : null
});
return _mounts.length > 0 ? _mounts : null;
},
getRestartPolicy(restartPolicy) {
if (!restartPolicy) {
return "";
}
const name = restartPolicy.name;
if (restartPolicy.maximumRetryCount) {
return name + ":" + restartPolicy.maximumRetryCount;
}
return name;
},
// inspect container
inspectContainer() {
// image
this.temp = {};
dockerImageInspect(this.urlPrefix, {
id: this.reqDataId,
imageId: this.imageId,
}).then((res) => {
this.temp.image = (res.data.repoTags || []).join(",")
})
this.temp = { ...this.temp, image: (res.data.repoTags || []).join(",") };
});
dockerInspectContainer(this.urlPrefix, {
id: this.reqDataId,
containerId: this.containerId,
}).then(res => {
}).then((res) => {
this.buildVisible = true;
const storageOpt = res.data.hostConfig?.storageOpt || { "": "" };
this.temp = {
name: res.data.name,
name: res.data?.name,
labels: this.getLabels,
volumes: this.getVolumesFromMounts(this.containerData.mounts) || [{}],
volumes: this.getVolumesFromMounts(res.data?.mounts) || [{}],
exposedPorts: this.getPortsFromPorts(this.containerData.ports) || this.getPortsFromExposedPorts(res.data.config.exposedPorts) || [{}],
autorun: true,
imageId: this.imageId,
env: [{}],
storageOpt: [{}],
commands: [{}],
...this.temp
env: (res.data?.config?.env || [""]).map((item) => {
const i = item.indexOf("=");
if (i == -1) {
return {};
}
return {
key: item.substring(0, i),
value: item.substring(i + 1, item.length),
};
}) || [{}],
storageOpt: Object.keys(storageOpt).map((item) => {
return {
key: item,
value: storageOpt[item],
};
}),
commands: (res.data?.config?.cmd || [""]).map((item) => {
return {
value: item || "",
};
}) || [{}],
hostname: res.data?.config?.hostName,
restartPolicy: this.getRestartPolicy(res.data?.hostConfig?.restartPolicy),
networkMode: res.data?.hostConfig?.networkMode,
runtime: res.data?.hostConfig?.runtime,
privileged: res.data.hostConfig?.privileged || false,
...this.temp,
};
this.$refs["editForm"]?.resetFields();
});
@ -447,6 +485,7 @@ export default {
}).then((res) => {
this.buildVisible = true;
this.temp = {
name: (res.data?.repoTags[0] || "").split(":")[0] || "",
volumes: [{}],
exposedPorts: (res.data?.config?.exposedPorts || [{}]).map((item) => {
item.disabled = item.port !== null;
@ -454,7 +493,7 @@ export default {
item.scheme = item.scheme || "tcp";
return item;
}),
image: (res.data.repoTags || []).join(","),
image: (res.data?.repoTags || []).join(","),
autorun: true,
imageId: this.imageId,
env: [{}],
@ -527,7 +566,7 @@ export default {
});
//
this.$emit('confirmBtnClick');
this.$emit("confirmBtnClick");
}
});
} else {
@ -536,14 +575,14 @@ export default {
this.$notification.success({
message: res.msg,
});
//
this.$emit('confirmBtnClick');
this.$emit("confirmBtnClick");
}
});
}
});
},
}
}
},
};
</script>

View File

@ -383,6 +383,11 @@
<a-icon type="more" />
</a>
<a-menu slot="overlay">
<a-menu-item>
<a-tooltip title="修改容器配置,重新运行">
<a-button size="small" type="link" @click="rebuild(record)"><a-icon type="redo" />重建</a-button>
</a-tooltip>
</a-menu-item>
<a-menu-item>
<a-tooltip title="编辑容器的一些基础参数">
<a-button size="small" type="link" icon="edit" :disabled="record.state !== 'running'" @click="editContainer(record)">编辑</a-button>
@ -455,22 +460,24 @@
title="重建容器"
:maskClosable="false"
>
<BuildContainer
:id="this.id"
<BuildContainer
:id="this.id"
:imageId="this.temp.imageId"
:machineDockerId="this.machineDockerId"
:machineDockerId="this.machineDockerId"
:urlPrefix="this.urlPrefix"
:containerId="this.temp.id"
:containerData="this.temp"
@cancelBtnClick="
() => {
this.buildVisible = false;
}"
}
"
@confirmBtnClick="
() => {
this.buildVisible = false;
this.loadData();
}"
}
"
/>
</a-drawer>
</div>
@ -489,7 +496,7 @@ export default {
LogView,
Terminal,
editContainer,
BuildContainer
BuildContainer,
},
props: {
id: {
@ -571,7 +578,7 @@ export default {
start: {
msg: "您确定要启动当前容器吗?",
api: dockerContainerStart,
}
},
},
editVisible: false,
@ -674,7 +681,7 @@ export default {
rebuild(record) {
this.temp = Object.assign({}, record);
this.buildVisible = true;
}
},
},
};
</script>

View File

@ -3,6 +3,7 @@
<a-table size="middle" :data-source="list" :columns="columns" :pagination="false" bordered rowKey="id" :row-selection="rowSelection">
<template slot="title">
<a-space>
<!-- <a-input v-model="listQuery['name']" @pressEnter="loadData" placeholder="名称" class="search-input-item" /> -->
<div>
显示所有
<a-switch checked-children="" un-checked-children="" v-model="listQuery['showAll']" />
@ -71,20 +72,22 @@
title="构建容器"
:maskClosable="false"
>
<BuildContainer
:id="this.id"
<BuildContainer
:id="this.id"
:imageId="this.temp.id"
:machineDockerId="this.machineDockerId"
:machineDockerId="this.machineDockerId"
:urlPrefix="this.urlPrefix"
@cancelBtnClick="
() => {
this.buildVisible = false;
}"
}
"
@confirmBtnClick="
() => {
this.buildVisible = false;
this.loadData();
}"
}
"
/>
</a-drawer>
<!-- 日志 -->
@ -95,7 +98,7 @@
</template>
<script>
import { parseTime, renderSize } from "@/utils/const";
import {dockerImageCreateContainer, dockerImagePullImage, dockerImageRemove, dockerImagesList, dockerImageBatchRemove} from "@/api/docker-api";
import { dockerImageCreateContainer, dockerImagePullImage, dockerImageRemove, dockerImagesList, dockerImageBatchRemove } from "@/api/docker-api";
import PullImageLog from "@/pages/docker/pull-image-log";
import BuildContainer from "./buildContainer.vue";
@ -162,7 +165,7 @@ export default {
},
},
buildVisible: false,
tableSelections: []
tableSelections: [],
};
},
computed: {
@ -321,7 +324,7 @@ export default {
});
},
batchDelete() {
let ids = this.tableSelections
let ids = this.tableSelections;
this.$confirm({
title: "系统提示",
content: "真的要批量删除选择的镜像吗?已经被容器使用的镜像无法删除!",
@ -331,7 +334,7 @@ export default {
//
const params = {
id: this.reqDataId,
imagesIds: ids.join(','),
imagesIds: ids.join(","),
};
dockerImageBatchRemove(this.urlPrefix, params).then((res) => {
if (res.code === 200) {
@ -343,7 +346,7 @@ export default {
});
},
});
}
},
},
};
</script>