"projects/git@developer.sourcefind.cn:tsoc/hg-misc-tools.git" did not exist on "16cd154d0011df2c73c0181ac73f43d02c8f100b"
Commit 8fe9b2d0 authored by Your Name's avatar Your Name
Browse files

fix 修改说明,添加hytop-wrapper

parent 592e7231
.idea .idea
go.sum go.sum
.vscode .vscode
.aider*
# Done
## tui库调研
- TUI库:https://github.com/charmbracelet/bubbletea ,用户定义TUI和运行组件
- TUI样式库:https://github.com/charmbracelet/lipgloss ,组件样式库,允许组合组件
- TUI组件库:https://github.com/NimbleMarkets/ntcharts ,TUI图表库
\ No newline at end of file
# Readme # Readme
这是一个类似Prometheus的监控工具,目前的想法是采集服务器上使用GPU/DCU的进程信息 这个仓库包含了使用golang读取dcu相关信息的库,同时也包含了hytop、hytop-wrapper等需要收集dcu信息的工具
主要包含两部分:
- 探针:部署在每个需要被监控的服务器上,暴露服务器中使用GPU/DCU进程的相关信息
- 收集器:收集各个探针的数据,并记录下来,且支持查询
写这个工具的目的是希望能快速快速找到哪些节点的GPU/DCU是空的,没有被使用的。 ## hytop
nvidia-dcgm-exporter和dcu-exporter收集的是一些数字指标信息,不能直观的体现GPU/DCU是否被占用
***本工具的另一个目的是希望能追查到进程的真实用户,以便统计每个人的GPU使用率*** 一个用于在shell终端里监控DCU的工具
位于/cmd/hytop下
## 关键组件选型 ## hytop-wrapper
数据库 hytop的包装程序,可以在远程ssh主机上执行hytop并将画面传回
位于/cmd/hytop-wrapper下
- prometheus/tsdb
序列化方法 ## pid_of_docker
- json 展示容器用卡情况的工具,支持DCU和Nvidia计算卡
- CBOR 在/bin/pid_of_docker下
## 如何确定谁使用了显卡、加速卡 ## opsflow
编写本工具的一个目的是想了解是哪些Linux系统用户在使用显卡 opsflow的探针工具
\ No newline at end of file
对于曙光环境,使用显卡的方式主要有两种
- docker容器
- 主机进程使用
对于docker容器,我们认为谁创建了容器,那么该容器中的进程使用了显卡,就认为是容器的创建者使用了显卡
对于主机进程,那就是进程的用户使用了显卡
复杂情况:
- sudo转换用户执行
- su转换用户
思路或方法:
- docker
- 使用audit审计docker命令、docker.sock文件
- 使用空壳脚本记录
- 代理和转发docker.sock文件
- 使用falco审计工具
- 监听/var/run/docker.sock
- 主机进程
- 直接审计使用显卡设备的进程
### docker监控
audit方案
```shell
auditctl -w /usr/bin/docker -p x -k docker-cmd
auditctl -w /var/run/docker.sock -p rwxa -k docker-daemon
ausearch -k docker-daemon | aureport -f -i
```
空壳shell方案: 编写一个空壳脚本替代原本的docker命令,在执行实际docker命令前记录相关信息,以下是空壳脚本的内容
```shell
#!/bin/bash
REAL_SUFFIX='-real'
BASE_NAME=$(basename "$0")
REAL_CMD="${BASE_NAME}$REAL_SUFFIX"
LOG_FILE="/var/log/${BASE_NAME}-call.log"
[[ -f "$LOG_FILE" ]] || (touch "$LOG_FILE" && chmod 666 "$LOG_FILE")
# 记录调用信息
{
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
echo "=== [${TIMESTAMP}] ==="
echo "Command : $0"
echo "All args : $*"
echo "PID : $$"
echo "Parent PID: $PPID"
PARENT_INFO=$(ps -fp $PPID)
echo "Parent Info:"
echo "$PARENT_INFO"
echo "--- Environment ---"
env
echo "--- Environment ---"
echo "----- Output -----"
} >> "$LOG_FILE"
(exec /usr/bin/"${REAL_CMD}" "$@") | tee "$LOG_FILE"
{
echo "----- Output -----"
echo ""
} >> "$LOG_FILE"
```
falco方案与audit的方案类似,本质上都是使用eBPF抓包,这里省略
### 针对sudo和su的方法
- 记录进程树
- 检查进程信息 /proc/\<PID\>/{status,sessionid,stat,environment}
- 使用who或last命令
- audit审计
- 使用pam会话记录
- acct记账
package main
import (
"crypto/md5"
_ "embed"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"log"
"math/rand/v2"
"os"
"strings"
"syscall"
"github.com/pkg/sftp"
"github.com/spf13/pflag"
"golang.org/x/crypto/ssh"
"golang.org/x/term"
)
//go:embed hytop
var hytop_bin []byte
var (
flagHost = pflag.String("host", "", "host to run hytop")
flagPort = pflag.Uint16P("port", "p", 22, "ssh port")
)
func main() {
pflag.Parse()
if flagHost != nil {
// 远程执行
run_remote()
} else {
// 本地执行
run_local()
}
}
func run_local() {
dir := fmt.Sprintf("/tmp/hytop-%s", get_rand_str())
err := os.Mkdir(dir, 0777)
if err != nil {
log.Fatal(err)
}
fp := fmt.Sprintf("%s/%s", dir, "hytop")
os.WriteFile(fp, hytop_bin, 0777)
syscall.Exec(fp, []string{"hytop"}, os.Environ())
}
func run_remote() {
home := os.Getenv("HOME")
pk, err := os.ReadFile(home + "/.ssh/id_rsa")
if err != nil {
log.Fatalf("read file ${HOME}/.ssh/id_rsa error")
}
signer, err := ssh.ParsePrivateKey(pk)
if err != nil {
log.Fatalf("unable to parse private key: %v", err)
}
// 1. SSH 客户端配置
config := &ssh.ClientConfig{
User: os.Getenv("USER"),
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
// 2. 连接服务器
client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", *flagHost, *flagPort), config)
if err != nil {
log.Fatal("连接失败: ", err)
}
defer client.Close()
binpath, err := send_file(client)
if err != nil {
log.Fatal("error send file: ", err)
}
// 3. 创建会话
session, err := client.NewSession()
if err != nil {
log.Fatal("创建会话失败: ", err)
}
defer session.Close()
// 4. 关键:请求 PTY (伪终端)
// 这让远程进程认为它运行在一个真实的终端窗口中
fd := int(os.Stdin.Fd())
state, err := term.MakeRaw(fd) // 将本地终端设为 Raw 模式以透传按键
if err != nil {
log.Fatal(err)
}
defer term.Restore(fd, state)
w, h, _ := term.GetSize(fd)
if err := session.RequestPty("xterm-256color", h, w, ssh.TerminalModes{
ssh.ECHO: 1,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}); err != nil {
log.Fatal("请求PTY失败: ", err)
}
// 5. 绑定输入输出
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
// 6. 启动持久命令(如 bash)
if err := session.Start(fmt.Sprintf(`PATH="${PATH}:/opt/hyhal/bin"; %s`, binpath)); err != nil {
log.Fatal("启动Shell失败: ", err)
}
// 等待命令结束
session.Wait()
del_file(client, binpath)
}
func get_rand_str() string {
i := rand.Uint64()
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, i)
src := md5.Sum(b)
return strings.Trim(strings.ReplaceAll(base64.StdEncoding.EncodeToString(src[:]), "/", "a"), "=")
}
func send_file(sshCli *ssh.Client) (string, error) {
sftp_client, err := sftp.NewClient(sshCli)
if err != nil {
return "", err
}
tmp_path := get_rand_str()
err = sftp_client.Mkdir(fmt.Sprintf("/tmp/hytop-%s", tmp_path))
if err != nil {
sftp_client.Close()
return "", err
}
res := fmt.Sprintf("/tmp/hytop-%s/hytop", tmp_path)
sftp_file, err := sftp_client.Create(res)
if err != nil {
sftp_client.Close()
return "", err
}
s, err := sftp_file.Write(hytop_bin)
if s != len(hytop_bin) || err != nil {
sftp_file.Close()
sftp_client.Close()
if err != nil {
return "", err
} else {
return "", errors.New("sftp send file size not equal with raw file")
}
}
sftp_file.Chmod(0777)
sftp_file.Close()
sftp_client.Close()
return res, nil
}
func del_file(sshCli *ssh.Client, filePath string) error {
sftp_client, err := sftp.NewClient(sshCli)
if err != nil {
return err
}
defer sftp_client.Close()
return sftp_client.Remove(filePath)
}
...@@ -9,12 +9,15 @@ require ( ...@@ -9,12 +9,15 @@ require (
github.com/lrstanley/bubblezone v0.0.0-20240914071701-b48c55a5e78e github.com/lrstanley/bubblezone v0.0.0-20240914071701-b48c55a5e78e
github.com/moby/moby/api v1.52.0-beta.2 github.com/moby/moby/api v1.52.0-beta.2
github.com/moby/moby/client v0.1.0-beta.2 github.com/moby/moby/client v0.1.0-beta.2
github.com/pkg/sftp v1.13.10
github.com/pquerna/otp v1.5.0 github.com/pquerna/otp v1.5.0
github.com/samber/mo v1.16.0 github.com/samber/mo v1.16.0
github.com/shirou/gopsutil/v3 v3.24.5 github.com/shirou/gopsutil/v3 v3.24.5
github.com/shirou/gopsutil/v4 v4.25.9 github.com/shirou/gopsutil/v4 v4.25.9
github.com/spf13/viper v1.21.0 github.com/spf13/viper v1.21.0
github.com/swaggo/swag v1.16.6 github.com/swaggo/swag v1.16.6
golang.org/x/crypto v0.46.0
golang.org/x/term v0.38.0
) )
require ( require (
...@@ -44,6 +47,7 @@ require ( ...@@ -44,6 +47,7 @@ require (
github.com/goccy/go-yaml v1.18.0 // indirect github.com/goccy/go-yaml v1.18.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
...@@ -64,7 +68,6 @@ require ( ...@@ -64,7 +68,6 @@ require (
go.uber.org/mock v0.5.0 // indirect go.uber.org/mock v0.5.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/arch v0.20.0 // indirect golang.org/x/arch v0.20.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/mod v0.31.0 // indirect golang.org/x/mod v0.31.0 // indirect
golang.org/x/net v0.48.0 // indirect golang.org/x/net v0.48.0 // indirect
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
......
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