fix: Fix the issue of extremely slow container list retrieval (#11142)

Refs #11129
This commit is contained in:
ssongliu 2025-12-01 18:03:37 +08:00 committed by GitHub
parent d4edf2f874
commit f63658db9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 79 additions and 12 deletions

View file

@ -263,6 +263,21 @@ func (b *BaseApi) ContainerListStats(c *gin.Context) {
helper.SuccessWithData(c, data)
}
// @Summary Load container stats size
// @Success 200 {object} dto.ContainerItemStats
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/item/stats/:id [get]
func (b *BaseApi) ContainerItemStats(c *gin.Context) {
containerID := c.Param("id")
data, err := containerService.ContainerItemStats(containerID)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Container
// @Summary Create container
// @Accept json

View file

@ -31,9 +31,6 @@ type ContainerInfo struct {
Network []string `json:"network"`
Ports []string `json:"ports"`
SizeRw int64 `json:"sizeRw"`
SizeRootFs int64 `json:"sizeRootFs"`
IsFromApp bool `json:"isFromApp"`
IsFromCompose bool `json:"isFromCompose"`
@ -129,6 +126,10 @@ type ContainerUpgrade struct {
ForcePull bool `json:"forcePull"`
}
type ContainerItemStats struct {
SizeRw int64 `json:"sizeRw"`
SizeRootFs int64 `json:"sizeRootFs"`
}
type ContainerListStats struct {
ContainerID string `json:"containerID"`

View file

@ -70,6 +70,7 @@ type IContainerService interface {
ContainerUpgrade(req dto.ContainerUpgrade) error
ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error)
ContainerListStats() ([]dto.ContainerListStats, error)
ContainerItemStats(containerID string) (dto.ContainerItemStats, error)
LoadResourceLimit() (*dto.ResourceLimit, error)
ContainerRename(req dto.ContainerRename) error
ContainerCommit(req dto.ContainerCommit) error
@ -101,10 +102,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
return 0, nil, err
}
defer client.Close()
options := container.ListOptions{
All: true,
Size: true,
}
options := container.ListOptions{All: true}
if len(req.Filters) != 0 {
options.Filters = filters.NewArgs()
options.Filters.Add("label", req.Filters)
@ -259,6 +257,21 @@ func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
}
return data, nil
}
func (u *ContainerService) ContainerItemStats(containerID string) (dto.ContainerItemStats, error) {
var data dto.ContainerItemStats
client, err := docker.NewDockerClient()
if err != nil {
return data, err
}
defer client.Close()
containerInfo, _, err := client.ContainerInspectWithRaw(context.Background(), containerID, true)
if err != nil {
return data, err
}
data.SizeRw = *containerInfo.SizeRw
data.SizeRootFs = *containerInfo.SizeRootFs
return data, nil
}
func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error) {
client, err := docker.NewDockerClient()
if err != nil {
@ -1760,8 +1773,6 @@ func searchWithFilter(req dto.PageContainer, containers []container.Summary) []d
ImageName: item.Image,
State: item.State,
RunTime: item.Status,
SizeRw: item.SizeRw,
SizeRootFs: item.SizeRootFs,
IsFromApp: IsFromApp,
IsFromCompose: IsFromCompose,
}

View file

@ -24,6 +24,7 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
baRouter.POST("/list/byimage", baseApi.ListContainerByImage)
baRouter.GET("/status", baseApi.LoadContainerStatus)
baRouter.GET("/list/stats", baseApi.ContainerListStats)
baRouter.GET("/item/stats/:id", baseApi.ContainerItemStats)
baRouter.GET("/search/log", baseApi.ContainerStreamLogs)
baRouter.POST("/download/log", baseApi.DownloadContainerLogs)
baRouter.GET("/limit", baseApi.LoadResourceLimit)

View file

@ -134,6 +134,10 @@ export namespace Container {
name: string;
state: string;
}
export interface ContainerItemStats {
sizeRw: number;
sizeRootFs: number;
}
export interface ContainerListStats {
containerID: string;
cpuTotalUsage: number;

View file

@ -43,6 +43,9 @@ export const cleanContainerLog = (containerName: string, operateNode?: string) =
const params = operateNode ? `?operateNode=${operateNode}` : '';
return http.post(`/containers/clean/log${params}`, { name: containerName });
};
export const containerItemStats = (containerID: string) => {
return http.get<Container.ContainerItemStats>(`/containers/item/stats/${containerID}`);
};
export const containerListStats = () => {
return http.get<Array<Container.ContainerListStats>>(`/containers/list/stats`);
};

View file

@ -823,6 +823,7 @@ const message = {
memUsage: 'Memory usage',
memTotal: 'Memory limit',
memCache: 'Memory cache',
loadSize: 'Get Container Size',
ip: 'IP address',
cpuShare: 'CPU shares',
cpuShareHelper:

View file

@ -826,6 +826,7 @@ const message = {
memUsage: 'Uso de memoria',
memTotal: 'Límite de memoria',
memCache: 'Caché de memoria',
loadSize: 'Obtener Tamaño del Contenedor',
ip: 'Dirección IP',
cpuShare: 'Proporción de CPU',
cpuShareHelper:

View file

@ -803,6 +803,7 @@ const message = {
memUsage: 'メモリの使用',
memTotal: 'メモリ制限',
memCache: 'メモリキャッシュ',
loadSize: 'コンテナサイズを取得',
ip: 'IPアドレス',
cpuShare: 'CPU共有',
cpuShareHelper:

View file

@ -796,6 +796,7 @@ const message = {
memUsage: '메모리 사용',
memTotal: '메모리 한도',
memCache: '메모리 캐시',
loadSize: '컨테이너 크기 가져오기',
ip: 'IP 주소',
cpuShare: 'CPU 공유',
cpuShareHelper:

View file

@ -816,6 +816,7 @@ const message = {
memUsage: 'Penggunaan memori',
memTotal: 'Had memori',
memCache: 'Cache memori',
loadSize: 'Dapatkan Saiz Bekas',
ip: 'Alamat IP',
cpuShare: 'Bahagian CPU',
cpuShareHelper:

View file

@ -814,6 +814,7 @@ const message = {
memUsage: 'Uso de memória',
memTotal: 'Limite de memória',
memCache: 'Cache de memória',
loadSize: 'Obter Tamanho do Contêiner',
ip: 'Endereço IP',
cpuShare: 'Atribuição de CPU',
cpuShareHelper:

View file

@ -812,6 +812,7 @@ const message = {
memUsage: 'Использование памяти',
memTotal: 'Лимит памяти',
memCache: 'Кэш памяти',
loadSize: 'Получить Размер Контейнера',
ip: 'IP-адрес',
cpuShare: 'Доли CPU',
cpuShareHelper:

View file

@ -832,6 +832,7 @@ const message = {
memUsage: 'Bellek kullanımı',
memTotal: 'Bellek sınırı',
memCache: 'Bellek önbelleği',
loadSize: 'Konteyner Boyutunu Al',
ip: 'IP adresi',
cpuShare: 'CPU paylaşımları',
cpuShareHelper:

View file

@ -788,6 +788,7 @@ const message = {
memUsage: '記憶體使用',
memTotal: '記憶體限額',
memCache: '快取使用',
loadSize: '取得容器大小',
ip: 'IP 位址',
cpuShare: 'CPU 權重',
cpuShareHelper: '容器預設份額為 1024 CPU增大可使目前容器獲得更多的 CPU 時間',

View file

@ -789,6 +789,7 @@ const message = {
memUsage: '内存使用',
memTotal: '内存限额',
memCache: '缓存使用',
loadSize: '获取容器大小',
ip: 'IP 地址',
cpuShare: 'CPU 权重',
cpuShareHelper: '容器默认份额为 1024 CPU增大可使当前容器获得更多的 CPU 时间',

View file

@ -209,8 +209,7 @@
<el-descriptions-item :label="$t('container.memTotal')">
{{ computeSizeForDocker(row.memoryLimit) }}
</el-descriptions-item>
<el-descriptions-item>
<el-descriptions-item v-if="row.hasLoadSize">
<template #label>
{{ $t('container.sizeRw') }}
<el-tooltip :content="$t('container.sizeRwHelper')">
@ -219,7 +218,10 @@
</template>
{{ computeSize2(row.sizeRw) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('container.sizeRootFs')">
<el-descriptions-item
:label="$t('container.sizeRootFs')"
v-if="row.hasLoadSize"
>
<template #label>
{{ $t('container.sizeRootFs') }}
<el-tooltip :content="$t('container.sizeRootFsHelper')">
@ -229,6 +231,17 @@
{{ computeSize2(row.sizeRootFs) }}
</el-descriptions-item>
</el-descriptions>
<el-button
class="mt-2"
v-if="!row.hasLoadSize"
size="small"
link
type="primary"
@click="loadSize(row)"
>
{{ $t('container.loadSize') }}
</el-button>
</template>
</el-popover>
</div>
@ -384,6 +397,7 @@ import ContainerLogDialog from '@/components/log/container-drawer/index.vue';
import Status from '@/components/status/index.vue';
import { reactive, onMounted, ref, computed } from 'vue';
import {
containerItemStats,
containerListStats,
containerOperator,
inspect,
@ -588,6 +602,14 @@ const refresh = async () => {
}
};
const loadSize = async (row: any) => {
containerItemStats(row.containerID).then((res) => {
row.sizeRw = res.data.sizeRw || 0;
row.sizeRootFs = res.data.sizeRootFs || 0;
row.hasLoadSize = true;
});
};
const loadStats = async () => {
const res = await containerListStats();
let stats = res.data || [];