Commit 800b8f4d authored by liming6's avatar liming6
Browse files

fix 优化部分逻辑

parent 7622e62a
...@@ -22,19 +22,24 @@ import ( ...@@ -22,19 +22,24 @@ import (
var ( var (
MapIdDCU = sync.Map{} // 记录dcu信息 MapIdDCU = sync.Map{} // 记录dcu信息
ContainerInfo *docker.ContainersInfo
DockerPidInfo *DockerProcessMap = nil DockerPidInfo *DockerProcessMap = nil
User = "" User = ""
HostName = "" HostName = ""
) )
func init() { func init() {
ContainerInfo = docker.NewContainersInfo()
ContainerInfo.Update()
i, err := docker.ContainerInfo.GetProcessIdInDocker(false) i, err := docker.ContainerInfo.GetProcessIdInDocker(false)
if err != nil || i == nil {
DockerPidInfo = &DockerProcessMap{lock: sync.RWMutex{}, pids: make(map[int32]bool)} DockerPidInfo = &DockerProcessMap{lock: sync.RWMutex{}, pids: make(map[int32]bool)}
return if err == nil && i != nil {
for _, v := range i {
for _, pidInfo := range v {
DockerPidInfo.pids[pidInfo.Pid] = true
}
}
} }
DockerPidInfo = &DockerProcessMap{lock: sync.RWMutex{}, pids: maps.Clone(i)}
HostName, _ = os.Hostname() HostName, _ = os.Hostname()
uid := os.Getuid() uid := os.Getuid()
u, err := utils.GetSysUserById(uid) u, err := utils.GetSysUserById(uid)
...@@ -48,14 +53,14 @@ func init() { ...@@ -48,14 +53,14 @@ func init() {
type DCUInfo struct { type DCUInfo struct {
Id int Id int
Name string // full Name string // full
PerformanceLevel string // full PerformanceLevel string
Fan string // full Fan string // full
Temp float32 Temp float32
PwrAvg float32 PwrAvg float32
PwrCap float32 // full PwrCap float32
BusId string // full BusId string // full
MemTotal int MemTotal int // full
MemUsed int MemUsed int // full
MemUsedPerent float32 MemUsedPerent float32
Mig bool // full Mig bool // full
DCUUTil float32 DCUUTil float32
...@@ -212,15 +217,21 @@ func (dpm *DockerProcessMap) GetPidInfo() map[int32]bool { ...@@ -212,15 +217,21 @@ func (dpm *DockerProcessMap) GetPidInfo() map[int32]bool {
return maps.Clone(dpm.pids) return maps.Clone(dpm.pids)
} }
// Update 重新获取数据,这是一个耗时的操作 func (dpm *DockerProcessMap) Update(dinfo *docker.ContainersInfo) (map[int32]bool, sync.Locker) {
func (dpm *DockerProcessMap) Update() map[int32]bool { dpm.lock.Lock()
i, err := docker.ContainerInfo.GetProcessIdInDocker(true) clear(dpm.pids)
dpm.lock.Unlock()
rl := dpm.lock.RLocker()
i, err := dinfo.GetProcessIdInDocker(false)
if err != nil || i == nil { if err != nil || i == nil {
dpm.pids = make(map[int32]bool) return dpm.pids, rl
return make(map[int32]bool) }
for _, v := range i {
for _, pidInfo := range v {
DockerPidInfo.pids[pidInfo.Pid] = true
}
} }
dpm.pids = maps.Clone(i) return dpm.pids, rl
return maps.Clone(i)
} }
type DCUProcessInfo struct { type DCUProcessInfo struct {
......
No preview for this file type
package docker package docker
import ( import (
"get-container/utils"
"strconv" "strconv"
"context" "context"
"errors" "errors"
"fmt"
"os"
"regexp" "regexp"
"strings" "strings"
"sync" "sync"
...@@ -17,36 +14,32 @@ import ( ...@@ -17,36 +14,32 @@ import (
"github.com/moby/moby/client" "github.com/moby/moby/client"
) )
func init() {
_ = initContainerInfo()
}
/**
有两种方法获取进程属于哪个容器
1. 通过查询pid命名空间,仅在没有指定--pid参数时有效
2. 通过查询进程的cgroup
3. 使用docker top <container-id>匹配
*/
type FindCIDMethod string
const (
ByCgroup FindCIDMethod = "byCGroup"
ByPidNS FindCIDMethod = "byPidNS"
ByTop FindCIDMethod = "byTop"
)
var ( var (
ReDocker = regexp.MustCompile(`^.*docker[-/]([0-9a-z]*)(?:|.*)`) ReDocker = regexp.MustCompile(`^.*docker[-/]([0-9a-z]*)(?:|.*)`)
ContainerInfo *ContainersInfo = nil ContainerInfo *ContainersInfo = nil
) )
func NewContainersInfo() *ContainersInfo {
ContainerInfo = &ContainersInfo{
inspectInfo: make(map[string]container.InspectResponse),
listInfo: make(map[string]container.Summary),
topInfo: make(map[string]container.TopResponse),
time: time.Now(),
inspectLock: sync.RWMutex{},
topLock: sync.RWMutex{},
listLock: sync.RWMutex{},
}
return ContainerInfo
}
type ContainersInfo struct { type ContainersInfo struct {
lock sync.RWMutex // 读写锁,防止对Info的并发写
time time.Time // 记录写入Info的时间 time time.Time // 记录写入Info的时间
inspectInfo map[string]container.InspectResponse inspectInfo map[string]container.InspectResponse
inspectLock sync.RWMutex
listInfo map[string]container.Summary listInfo map[string]container.Summary
listLock sync.RWMutex
topInfo map[string]container.TopResponse topInfo map[string]container.TopResponse
topLock sync.RWMutex
} }
type ContainerPsInfo struct { type ContainerPsInfo struct {
...@@ -113,9 +106,15 @@ func ParsePsInfo(topInfo map[string]container.TopResponse) (map[string][]Contain ...@@ -113,9 +106,15 @@ func ParsePsInfo(topInfo map[string]container.TopResponse) (map[string][]Contain
} }
func (info *ContainersInfo) Update() error { func (info *ContainersInfo) Update() error {
info.lock.Lock() info.listLock.Lock()
defer info.lock.Unlock() info.topLock.Lock()
i, s, t, err := getContainerInfo() info.inspectLock.Lock()
defer func() {
info.inspectLock.Unlock()
info.topLock.Unlock()
info.listLock.Unlock()
}()
i, s, t, err := getRunningContainerInfo()
if err != nil { if err != nil {
return err return err
} }
...@@ -126,201 +125,39 @@ func (info *ContainersInfo) Update() error { ...@@ -126,201 +125,39 @@ func (info *ContainersInfo) Update() error {
return nil return nil
} }
func (info *ContainersInfo) Get() (map[string]container.InspectResponse, sync.Locker) { func (info *ContainersInfo) GetInspectInfo(update bool) (map[string]container.InspectResponse, sync.Locker) {
rl := info.lock.RLocker() if update {
rl.Lock() info.Update()
return info.inspectInfo, rl
}
func initContainerInfo() error {
inspect, lists, tops, err := getContainerInfo()
if err != nil {
return err
} }
ContainerInfo = &ContainersInfo{ rl := info.inspectLock.RLocker()
lock: sync.RWMutex{}, rl.Lock()
time: time.Now(), if info.inspectInfo == nil {
inspectInfo: inspect, return make(map[string]container.InspectResponse), rl
listInfo: lists,
topInfo: tops,
} }
return nil return info.inspectInfo, rl
} }
// GetProcessIdInDocker 获取所用容器的进程信息 // GetProcessIdInDocker 获取所用容器的进程信息
func (info *ContainersInfo) GetProcessIdInDocker(update bool) (map[int32]bool, error) { func (info *ContainersInfo) GetProcessIdInDocker(update bool) (map[string][]ContainerPsInfo, error) {
result := make(map[int32]bool)
if update { if update {
err := info.Update() err := info.Update()
if err != nil { if err != nil {
return result, err return nil, err
} }
} }
rl := info.lock.RLocker() rl := info.topLock.RLocker()
rl.Lock() rl.Lock()
i, err := ParsePsInfo(info.topInfo) i, err := ParsePsInfo(info.topInfo)
rl.Unlock() rl.Unlock()
rl = nil rl = nil
if err != nil {
return result, err
}
for _, v := range i {
for _, k := range v {
result[k.Pid] = true
}
}
return result, nil
}
// FindContainerIdByPid 根据pid获取该进程属于哪个docker容器,返回容器id,如果为nil,表示找不到容器id
func FindContainerIdByPid(pid uint64, method FindCIDMethod) (*string, error) {
switch method {
case ByPidNS:
return findContainerIdByNS(pid)
case ByCgroup:
return findContainerIdByCgroup(pid)
default:
return nil, fmt.Errorf("unknown method: %s", method)
}
}
func FindContainerIdByPidBatch(pids []uint64, method FindCIDMethod) (map[uint64]string, error) {
if len(pids) == 0 {
return nil, nil
}
switch method {
case ByPidNS:
return findContainerIdByNSBatch(pids)
case ByCgroup:
return findContainerIdByCgroupBatch(pids)
default:
return nil, fmt.Errorf("unknown method: %s", method)
}
}
// findContainerIdByPidCgroup 通过cgroup查询docker容器id
func findContainerIdByCgroup(pid uint64) (*string, error) {
content, err := os.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid))
if err != nil { if err != nil {
return nil, err return nil, err
} }
contentStr := strings.Trim(string(content), "\n") return i, nil
if len(contentStr) == 0 {
return nil, errors.New("process's cgroup not found")
}
lines := strings.Split(contentStr, "\n")
var target string
if len(lines) > 1 {
// 如果有多行,解析有pids的行
for _, line := range lines {
if strings.Contains(line, "pids") {
target = strings.TrimSpace(line)
break
}
}
if target == "" {
return nil, errors.New("process's cgroup not found pids line")
}
} else {
// 如果是单行,直接解析
target = strings.TrimSpace(lines[0])
}
target = strings.TrimSpace(target)
if !strings.Contains(target, "docker") {
return nil, errors.New("process's cgroup is not create by docker")
}
if ReDocker.MatchString(target) {
fields := ReDocker.FindStringSubmatch(target)
if len(fields) < 2 {
return nil, errors.New("process's cgroup is not create by docker")
}
cid := fields[1]
return &cid, nil
} else {
return nil, errors.New("process's cgroup is not create by docker")
}
}
func findContainerIdByCgroupBatch(pids []uint64) (map[uint64]string, error) {
results := make(map[uint64]string)
for _, pid := range pids {
str, err := findContainerIdByCgroup(pid)
if err != nil {
return nil, err
}
s := *str
results[pid] = s
}
return results, nil
}
// findContainerIdByNS 通过pid命名空间查询docker容器id
func findContainerIdByNS(pid uint64) (*string, error) {
ns, err := utils.GetPidNS(pid)
if err != nil {
return nil, err
}
if ContainerInfo == nil {
innerErr := initContainerInfo()
if innerErr != nil {
return nil, innerErr
}
} else {
if innerErr := ContainerInfo.Update(); innerErr != nil {
return nil, innerErr
}
}
info, lock := ContainerInfo.Get()
defer lock.Unlock()
for k, v := range info {
containerNs, innerErr := utils.GetPidNS(uint64(v.State.Pid))
if innerErr != nil {
continue
}
if containerNs == ns {
cid := k
return &cid, nil
}
}
return nil, nil
}
func findContainerIdByNSBatch(pids []uint64) (map[uint64]string, error) {
if ContainerInfo == nil {
innerErr := initContainerInfo()
if innerErr != nil {
return nil, innerErr
}
} else {
if innerErr := ContainerInfo.Update(); innerErr != nil {
return nil, innerErr
}
}
info, lock := ContainerInfo.Get()
defer lock.Unlock()
results := make(map[uint64]string)
ns2cid := make(map[uint64]string)
for k, v := range info {
containerNs, innerErr := utils.GetPidNS(uint64(v.State.Pid))
if innerErr != nil {
return nil, innerErr
}
ns2cid[containerNs] = k
}
for _, pid := range pids {
ns, err := utils.GetPidNS(pid)
if err != nil {
continue
}
if cid, ok := ns2cid[ns]; ok {
results[pid] = cid
}
}
return results, nil
} }
// getContainerInfo 获取所有正在运行的docker容器的详细信息 // getRunningContainerInfo 获取所有正在运行的docker容器的详细信息
func getContainerInfo() (map[string]container.InspectResponse, map[string]container.Summary, map[string]container.TopResponse, error) { func getRunningContainerInfo() (map[string]container.InspectResponse, map[string]container.Summary, map[string]container.TopResponse, error) {
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
......
...@@ -91,7 +91,7 @@ func TestDocker(t *testing.T) { ...@@ -91,7 +91,7 @@ func TestDocker(t *testing.T) {
func TestGetProcessIdInDocker(t *testing.T) { func TestGetProcessIdInDocker(t *testing.T) {
now := time.Now() now := time.Now()
err := initContainerInfo() err := ContainerInfo.Update()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
...@@ -100,6 +100,11 @@ func TestGetProcessIdInDocker(t *testing.T) { ...@@ -100,6 +100,11 @@ func TestGetProcessIdInDocker(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
t.Logf("%d", d.Nanoseconds()) t.Logf("%d ms", d.Milliseconds())
t.Logf("%+v", pids) for k, v := range pids {
t.Logf("======> container %s has %d processes", k, len(v))
for _, pidInfo := range v {
t.Logf(" %+v", pidInfo)
}
}
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment