mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-30 02:36:18 +08:00
feat: 容器增加资源使用、端口显示 (#786)
This commit is contained in:
parent
05e7506f61
commit
2d31c5b005
7 changed files with 122 additions and 38 deletions
|
|
@ -22,6 +22,10 @@ type ContainerInfo struct {
|
|||
State string `json:"state"`
|
||||
RunTime string `json:"runTime"`
|
||||
|
||||
CPUPercent float64 `json:"cpuPercent"`
|
||||
MemoryPercent float64 `json:"memoryPercent"`
|
||||
Ports []string `json:"ports"`
|
||||
|
||||
IsFromApp bool `json:"isFromApp"`
|
||||
IsFromCompose bool `json:"isFromCompose"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
|
|
@ -97,27 +98,45 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
|||
records = list[start:end]
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(records))
|
||||
for _, container := range records {
|
||||
IsFromCompose := false
|
||||
if _, ok := container.Labels[composeProjectLabel]; ok {
|
||||
IsFromCompose = true
|
||||
}
|
||||
IsFromApp := false
|
||||
if created, ok := container.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
IsFromApp = true
|
||||
}
|
||||
backDatas = append(backDatas, dto.ContainerInfo{
|
||||
ContainerID: container.ID,
|
||||
CreateTime: time.Unix(container.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
Name: container.Names[0][1:],
|
||||
ImageId: strings.Split(container.ImageID, ":")[1],
|
||||
ImageName: container.Image,
|
||||
State: container.State,
|
||||
RunTime: container.Status,
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
})
|
||||
go func(item types.Container) {
|
||||
IsFromCompose := false
|
||||
if _, ok := item.Labels[composeProjectLabel]; ok {
|
||||
IsFromCompose = true
|
||||
}
|
||||
IsFromApp := false
|
||||
if created, ok := item.Labels[composeCreatedBy]; ok && created == "Apps" {
|
||||
IsFromApp = true
|
||||
}
|
||||
|
||||
var ports []string
|
||||
for _, port := range item.Ports {
|
||||
if port.IP == "::" || port.PublicPort == 0 {
|
||||
continue
|
||||
}
|
||||
ports = append(ports, fmt.Sprintf("%v:%v/%s", port.PublicPort, port.PrivatePort, port.Type))
|
||||
}
|
||||
cpu, mem := loadCpuAndMem(client, item.ID)
|
||||
backDatas = append(backDatas, dto.ContainerInfo{
|
||||
ContainerID: item.ID,
|
||||
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
|
||||
Name: item.Names[0][1:],
|
||||
ImageId: strings.Split(item.ImageID, ":")[1],
|
||||
ImageName: item.Image,
|
||||
State: item.State,
|
||||
RunTime: item.Status,
|
||||
CPUPercent: cpu,
|
||||
MemoryPercent: mem,
|
||||
Ports: ports,
|
||||
IsFromApp: IsFromApp,
|
||||
IsFromCompose: IsFromCompose,
|
||||
})
|
||||
wg.Done()
|
||||
}(container)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return int64(total), backDatas, nil
|
||||
}
|
||||
|
|
@ -270,16 +289,16 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainterStats, erro
|
|||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
res.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
res.Body.Close()
|
||||
var stats *types.StatsJSON
|
||||
if err := json.Unmarshal(body, &stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data dto.ContainterStats
|
||||
previousCPU := stats.PreCPUStats.CPUUsage.TotalUsage
|
||||
previousSystem := stats.PreCPUStats.SystemUsage
|
||||
data.CPUPercent = calculateCPUPercentUnix(previousCPU, previousSystem, stats)
|
||||
data.CPUPercent = calculateCPUPercentUnix(stats)
|
||||
data.IORead, data.IOWrite = calculateBlockIO(stats.BlkioStats)
|
||||
data.Memory = float64(stats.MemoryStats.Usage) / 1024 / 1024
|
||||
if cache, ok := stats.MemoryStats.Stats["cache"]; ok {
|
||||
|
|
@ -301,18 +320,26 @@ func stringsToMap(list []string) map[string]string {
|
|||
}
|
||||
return lableMap
|
||||
}
|
||||
func calculateCPUPercentUnix(previousCPU, previousSystem uint64, v *types.StatsJSON) float64 {
|
||||
var (
|
||||
cpuPercent = 0.0
|
||||
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage) - float64(previousCPU)
|
||||
systemDelta = float64(v.CPUStats.SystemUsage) - float64(previousSystem)
|
||||
)
|
||||
|
||||
func calculateCPUPercentUnix(stats *types.StatsJSON) float64 {
|
||||
cpuPercent := 0.0
|
||||
cpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage) - float64(stats.PreCPUStats.CPUUsage.TotalUsage)
|
||||
systemDelta := float64(stats.CPUStats.SystemUsage) - float64(stats.PreCPUStats.SystemUsage)
|
||||
|
||||
if systemDelta > 0.0 && cpuDelta > 0.0 {
|
||||
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CPUStats.CPUUsage.PercpuUsage)) * 100.0
|
||||
cpuPercent = (cpuDelta / systemDelta) * float64(len(stats.CPUStats.CPUUsage.PercpuUsage)) * 100.0
|
||||
}
|
||||
return cpuPercent
|
||||
}
|
||||
func calculateMemPercentUnix(memStats types.MemoryStats) float64 {
|
||||
memPercent := 0.0
|
||||
memUsage := float64(memStats.Usage - memStats.Stats["cache"])
|
||||
memLimit := float64(memStats.Limit)
|
||||
if memUsage > 0.0 && memLimit > 0.0 {
|
||||
memPercent = (memUsage / memLimit) * 100.0
|
||||
}
|
||||
return memPercent
|
||||
}
|
||||
func calculateBlockIO(blkio types.BlkioStats) (blkRead float64, blkWrite float64) {
|
||||
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(bioEntry.Op) {
|
||||
|
|
@ -363,3 +390,25 @@ func pullImages(ctx context.Context, client *client.Client, image string) error
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadCpuAndMem(client *client.Client, container string) (float64, float64) {
|
||||
res, err := client.ContainerStats(context.Background(), container, false)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
res.Body.Close()
|
||||
return 0, 0
|
||||
}
|
||||
res.Body.Close()
|
||||
var stats *types.StatsJSON
|
||||
if err := json.Unmarshal(body, &stats); err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
CPUPercent := calculateCPUPercentUnix(stats)
|
||||
MemPercent := calculateMemPercentUnix(stats.MemoryStats)
|
||||
return CPUPercent, MemPercent
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,9 @@ export namespace Container {
|
|||
createTime: string;
|
||||
state: string;
|
||||
runTime: string;
|
||||
|
||||
cpuPercent: number;
|
||||
memoryPercent: number;
|
||||
ports: Array<string>;
|
||||
isFromApp: boolean;
|
||||
isFromCompose: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { ResPage, SearchWithPage } from '../interface';
|
|||
import { Container } from '../interface/container';
|
||||
|
||||
export const searchContainer = (params: Container.ContainerSearch) => {
|
||||
return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params);
|
||||
return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params, 400000);
|
||||
};
|
||||
export const createContainer = (params: Container.ContainerCreate) => {
|
||||
return http.post(`/containers`, params, 3000000);
|
||||
|
|
|
|||
|
|
@ -446,6 +446,7 @@ const message = {
|
|||
lastHour: 'Last Hour',
|
||||
last10Min: 'Last 10 Minutes',
|
||||
newName: 'New name',
|
||||
source: 'Resource rate',
|
||||
|
||||
user: 'User',
|
||||
command: 'Command',
|
||||
|
|
|
|||
|
|
@ -463,6 +463,7 @@ const message = {
|
|||
lastHour: '最近 1 小时',
|
||||
last10Min: '最近 10 分钟',
|
||||
newName: '新名称',
|
||||
source: '资源使用率',
|
||||
|
||||
user: '用户',
|
||||
command: '命令',
|
||||
|
|
|
|||
|
|
@ -71,20 +71,42 @@
|
|||
min-width="80"
|
||||
prop="imageName"
|
||||
/>
|
||||
<el-table-column :label="$t('commons.table.status')" min-width="50" prop="state" fix>
|
||||
<el-table-column :label="$t('commons.table.status')" min-width="60" prop="state" fix>
|
||||
<template #default="{ row }">
|
||||
<Status :key="row.state" :status="row.state"></Status>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('container.upTime')" min-width="80" prop="runTime" fix />
|
||||
<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') }}:
|
||||
{{ row.memoryPercent.toFixed(2) }}%
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('container.port')" min-width="80" prop="ports" fix>
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.ports">
|
||||
<div v-for="(item, index) in row.ports" :key="index">
|
||||
<div v-if="row.expand || (!row.expand && index < 3)">
|
||||
<el-tag class="tagMargin">{{ item }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!row.expand && row.ports.length > 3">
|
||||
<el-button type="primary" link @click="row.expand = true">
|
||||
{{ $t('commons.button.expand') }}...
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="createTime"
|
||||
:label="$t('commons.table.date')"
|
||||
:formatter="dateFormat"
|
||||
:label="$t('container.upTime')"
|
||||
min-width="70"
|
||||
show-overflow-tooltip
|
||||
prop="runTime"
|
||||
fix
|
||||
/>
|
||||
<fu-table-operations
|
||||
width="370px"
|
||||
width="300px"
|
||||
:ellipsis="10"
|
||||
:buttons="buttons"
|
||||
:label="$t('commons.table.operate')"
|
||||
|
|
@ -117,7 +139,6 @@ import TerminalDialog from '@/views/container/container/terminal/index.vue';
|
|||
import CodemirrorDialog from '@/components/codemirror-dialog/codemirror.vue';
|
||||
import Status from '@/components/status/index.vue';
|
||||
import { reactive, onMounted, ref } from 'vue';
|
||||
import { dateFormat } from '@/utils/util';
|
||||
import { ContainerOperator, inspect, loadDockerStatus, searchContainer } from '@/api/modules/container';
|
||||
import { Container } from '@/api/interface/container';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
|
|
@ -324,3 +345,9 @@ onMounted(() => {
|
|||
loadStatus();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tagMargin {
|
||||
margin-top: 2px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue