mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 09:49:28 +08:00
improve podman support (#211)
This commit is contained in:
@@ -73,7 +73,7 @@ func (a *Agent) Run(pubKey []byte, addr string) {
|
|||||||
a.initializeSystemInfo()
|
a.initializeSystemInfo()
|
||||||
a.initializeDiskInfo()
|
a.initializeDiskInfo()
|
||||||
a.initializeNetIoStats()
|
a.initializeNetIoStats()
|
||||||
a.dockerManager = newDockerManager()
|
a.dockerManager = newDockerManager(a)
|
||||||
|
|
||||||
// initialize GPU manager
|
// initialize GPU manager
|
||||||
if os.Getenv("GPU") == "true" {
|
if os.Getenv("GPU") == "true" {
|
||||||
|
@@ -25,18 +25,23 @@ type dockerManager struct {
|
|||||||
apiContainerList *[]container.ApiInfo // List of containers from Docker API
|
apiContainerList *[]container.ApiInfo // List of containers from Docker API
|
||||||
containerStatsMap map[string]*container.Stats // Keeps track of container stats
|
containerStatsMap map[string]*container.Stats // Keeps track of container stats
|
||||||
validIds map[string]struct{} // Map of valid container ids, used to prune invalid containers from containerStatsMap
|
validIds map[string]struct{} // Map of valid container ids, used to prune invalid containers from containerStatsMap
|
||||||
|
goodDockerVersion bool // Whether docker version is at least 25.0.0 (one-shot works correctly)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add goroutine to the queue
|
// Add goroutine to the queue
|
||||||
func (d *dockerManager) queue() {
|
func (d *dockerManager) queue() {
|
||||||
d.sem <- struct{}{}
|
|
||||||
d.wg.Add(1)
|
d.wg.Add(1)
|
||||||
|
if d.goodDockerVersion {
|
||||||
|
d.sem <- struct{}{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove goroutine from the queue
|
// Remove goroutine from the queue
|
||||||
func (d *dockerManager) dequeue() {
|
func (d *dockerManager) dequeue() {
|
||||||
<-d.sem
|
|
||||||
d.wg.Done()
|
d.wg.Done()
|
||||||
|
if d.goodDockerVersion {
|
||||||
|
<-d.sem
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns stats for all running containers
|
// Returns stats for all running containers
|
||||||
@@ -75,6 +80,7 @@ func (dm *dockerManager) getDockerStats() ([]*container.Stats, error) {
|
|||||||
go func() {
|
go func() {
|
||||||
defer dm.dequeue()
|
defer dm.dequeue()
|
||||||
err := dm.updateContainerStats(ctr)
|
err := dm.updateContainerStats(ctr)
|
||||||
|
// if error, delete from map and add to failed list to retry
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dm.containerStatsMutex.Lock()
|
dm.containerStatsMutex.Lock()
|
||||||
delete(dm.containerStatsMap, ctr.IdShort)
|
delete(dm.containerStatsMap, ctr.IdShort)
|
||||||
@@ -89,11 +95,10 @@ func (dm *dockerManager) getDockerStats() ([]*container.Stats, error) {
|
|||||||
// retry failed containers separately so we can run them in parallel (docker 24 bug)
|
// retry failed containers separately so we can run them in parallel (docker 24 bug)
|
||||||
if len(failedContainters) > 0 {
|
if len(failedContainters) > 0 {
|
||||||
slog.Debug("Retrying failed containers", "count", len(failedContainters))
|
slog.Debug("Retrying failed containers", "count", len(failedContainters))
|
||||||
// time.Sleep(time.Millisecond * 1100)
|
|
||||||
for _, ctr := range failedContainters {
|
for _, ctr := range failedContainters {
|
||||||
dm.wg.Add(1)
|
dm.queue()
|
||||||
go func() {
|
go func() {
|
||||||
defer dm.wg.Done()
|
defer dm.dequeue()
|
||||||
err = dm.updateContainerStats(ctr)
|
err = dm.updateContainerStats(ctr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Error getting container stats", "err", err)
|
slog.Error("Error getting container stats", "err", err)
|
||||||
@@ -201,12 +206,13 @@ func (dm *dockerManager) deleteContainerStatsSync(id string) {
|
|||||||
delete(dm.containerStatsMap, id)
|
delete(dm.containerStatsMap, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new http client for Docker API
|
// Creates a new http client for Docker or Podman API
|
||||||
func newDockerManager() *dockerManager {
|
func newDockerManager(a *Agent) *dockerManager {
|
||||||
dockerHost := "unix:///var/run/docker.sock"
|
dockerHost, exists := os.LookupEnv("DOCKER_HOST")
|
||||||
if dockerHostEnv, exists := os.LookupEnv("DOCKER_HOST"); exists {
|
if exists {
|
||||||
slog.Info("DOCKER_HOST", "host", dockerHostEnv)
|
slog.Info("DOCKER_HOST", "host", dockerHost)
|
||||||
dockerHost = dockerHostEnv
|
} else {
|
||||||
|
dockerHost = getDockerHost()
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedURL, err := url.Parse(dockerHost)
|
parsedURL, err := url.Parse(dockerHost)
|
||||||
@@ -251,11 +257,15 @@ func newDockerManager() *dockerManager {
|
|||||||
Transport: transport,
|
Transport: transport,
|
||||||
},
|
},
|
||||||
containerStatsMap: make(map[string]*container.Stats),
|
containerStatsMap: make(map[string]*container.Stats),
|
||||||
|
sem: make(chan struct{}, 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure sem is initialized
|
// If using podman, return client
|
||||||
concurrency := 200
|
if strings.Contains(dockerHost, "podman") {
|
||||||
defer func() { dockerClient.sem = make(chan struct{}, concurrency) }()
|
a.systemInfo.Podman = true
|
||||||
|
dockerClient.goodDockerVersion = true
|
||||||
|
return dockerClient
|
||||||
|
}
|
||||||
|
|
||||||
// Check docker version
|
// Check docker version
|
||||||
// (versions before 25.0.0 have a bug with one-shot which requires all requests to be made in one batch)
|
// (versions before 25.0.0 have a bug with one-shot which requires all requests to be made in one batch)
|
||||||
@@ -273,9 +283,22 @@ func newDockerManager() *dockerManager {
|
|||||||
|
|
||||||
// if version > 24, one-shot works correctly and we can limit concurrent operations
|
// if version > 24, one-shot works correctly and we can limit concurrent operations
|
||||||
if dockerVersion, err := semver.Parse(versionInfo.Version); err == nil && dockerVersion.Major > 24 {
|
if dockerVersion, err := semver.Parse(versionInfo.Version); err == nil && dockerVersion.Major > 24 {
|
||||||
concurrency = 5
|
dockerClient.goodDockerVersion = true
|
||||||
|
} else {
|
||||||
|
slog.Info(fmt.Sprintf("Docker %s is outdated. Upgrade if possible. See https://github.com/henrygd/beszel/issues/58", versionInfo.Version))
|
||||||
}
|
}
|
||||||
slog.Debug("Docker", "version", versionInfo.Version, "concurrency", concurrency)
|
|
||||||
|
|
||||||
return dockerClient
|
return dockerClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test docker / podman sockets and return if one exists
|
||||||
|
func getDockerHost() string {
|
||||||
|
scheme := "unix://"
|
||||||
|
socks := []string{"/var/run/docker.sock", "/run/user/1000/podman/podman.sock", "/run/podman/podman.sock"}
|
||||||
|
for _, sock := range socks {
|
||||||
|
if _, err := os.Stat(sock); err == nil {
|
||||||
|
return scheme + sock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scheme + socks[0]
|
||||||
|
}
|
||||||
|
@@ -74,6 +74,7 @@ type Info struct {
|
|||||||
DiskPct float64 `json:"dp"`
|
DiskPct float64 `json:"dp"`
|
||||||
Bandwidth float64 `json:"b"`
|
Bandwidth float64 `json:"b"`
|
||||||
AgentVersion string `json:"v"`
|
AgentVersion string `json:"v"`
|
||||||
|
Podman bool `json:"p,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final data structure to return to the hub
|
// Final data structure to return to the hub
|
||||||
|
@@ -86,6 +86,13 @@ async function getStats<T>(collection: string, system: SystemRecord, chartTime:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dockerOrPodman(str: string, system: SystemRecord) {
|
||||||
|
if (system.info.p) {
|
||||||
|
str = str.replace("docker", "podman").replace("Docker", "Podman")
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
export default function SystemDetail({ name }: { name: string }) {
|
export default function SystemDetail({ name }: { name: string }) {
|
||||||
const direction = useStore($direction)
|
const direction = useStore($direction)
|
||||||
const { _ } = useLingui()
|
const { _ } = useLingui()
|
||||||
@@ -385,7 +392,7 @@ export default function SystemDetail({ name }: { name: string }) {
|
|||||||
<ChartCard
|
<ChartCard
|
||||||
empty={dataEmpty}
|
empty={dataEmpty}
|
||||||
grid={grid}
|
grid={grid}
|
||||||
title={t`Docker CPU Usage`}
|
title={dockerOrPodman(t`Docker CPU Usage`, system)}
|
||||||
description={t`Average CPU utilization of containers`}
|
description={t`Average CPU utilization of containers`}
|
||||||
cornerEl={containerFilterBar}
|
cornerEl={containerFilterBar}
|
||||||
>
|
>
|
||||||
@@ -406,8 +413,8 @@ export default function SystemDetail({ name }: { name: string }) {
|
|||||||
<ChartCard
|
<ChartCard
|
||||||
empty={dataEmpty}
|
empty={dataEmpty}
|
||||||
grid={grid}
|
grid={grid}
|
||||||
title={t`Docker Memory Usage`}
|
title={dockerOrPodman(t`Docker Memory Usage`, system)}
|
||||||
description={t`Memory usage of docker containers`}
|
description={dockerOrPodman(t`Memory usage of docker containers`, system)}
|
||||||
cornerEl={containerFilterBar}
|
cornerEl={containerFilterBar}
|
||||||
>
|
>
|
||||||
<ContainerChart chartData={chartData} chartName="mem" dataKey="m" unit=" MB" />
|
<ContainerChart chartData={chartData} chartName="mem" dataKey="m" unit=" MB" />
|
||||||
@@ -447,8 +454,8 @@ export default function SystemDetail({ name }: { name: string }) {
|
|||||||
>
|
>
|
||||||
<ChartCard
|
<ChartCard
|
||||||
empty={dataEmpty}
|
empty={dataEmpty}
|
||||||
title={t`Docker Network I/O`}
|
title={dockerOrPodman(t`Docker Network I/O`, system)}
|
||||||
description={t`Network traffic of docker containers`}
|
description={dockerOrPodman(t`Network traffic of docker containers`, system)}
|
||||||
cornerEl={containerFilterBar}
|
cornerEl={containerFilterBar}
|
||||||
>
|
>
|
||||||
{/* @ts-ignore */}
|
{/* @ts-ignore */}
|
||||||
@@ -649,7 +656,7 @@ function ChartCard({
|
|||||||
<CardDescription>{description}</CardDescription>
|
<CardDescription>{description}</CardDescription>
|
||||||
{cornerEl && <div className="relative py-1 block sm:w-44 sm:absolute sm:top-2.5 sm:end-3.5">{cornerEl}</div>}
|
{cornerEl && <div className="relative py-1 block sm:w-44 sm:absolute sm:top-2.5 sm:end-3.5">{cornerEl}</div>}
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<div className="ps-0 w-[calc(100%-1.6em)] h-52 relative">
|
<div className="ps-0 w-[calc(100%-1.5em)] h-52 relative">
|
||||||
{<Spinner msg={empty ? t`Waiting for enough records to display` : undefined} />}
|
{<Spinner msg={empty ? t`Waiting for enough records to display` : undefined} />}
|
||||||
{isIntersecting && children}
|
{isIntersecting && children}
|
||||||
</div>
|
</div>
|
||||||
|
2
beszel/site/src/types.d.ts
vendored
2
beszel/site/src/types.d.ts
vendored
@@ -34,6 +34,8 @@ export interface SystemInfo {
|
|||||||
b: number
|
b: number
|
||||||
/** agent version */
|
/** agent version */
|
||||||
v: string
|
v: string
|
||||||
|
/** system is using podman */
|
||||||
|
p?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SystemStats {
|
export interface SystemStats {
|
||||||
|
Reference in New Issue
Block a user