Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.git
.github
docs
*.md
!README.md
Dockerfile
data
82 changes: 82 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Multi-arch image → Docker Hub + GHCR.
# 需要:仓库 Variables 里设 DOCKERHUB_USER;Secrets 里设 DOCKERHUB_TOKEN(Docker Hub Access Token, Read & Write)
# PR 只构建不推送,用来验证 Dockerfile。
name: docker

on:
push:
tags: ["v*"]
pull_request:
paths:
- Dockerfile
- .dockerignore
- docker/**
- go.mod
- go.sum
- .github/workflows/docker.yml
workflow_dispatch:

jobs:
image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # 推 GHCR 用
env:
PUSH: ${{ github.event_name != 'pull_request' }}
steps:
- uses: actions/checkout@v4

- uses: docker/setup-buildx-action@v3

- if: env.PUSH == 'true'
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- if: env.PUSH == 'true'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- id: meta
uses: docker/metadata-action@v5
with:
images: |
docker.io/${{ vars.DOCKERHUB_USER || 'githubflyideas' }}/fogping
ghcr.io/${{ github.repository_owner }}/fogping
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=ref,event=pr

- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: ${{ env.PUSH == 'true' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: VERSION=${{ github.ref_type == 'tag' && github.ref_name || 'dev' }}
provenance: false # 避免部分平台(如 RouterOS)解析 manifest 时遇到 attestation 条目
cache-from: type=gha
cache-to: type=gha,mode=max

# 冒烟测试:amd64 单架构 load 到本地,起容器打 /api/version
- name: smoke test (amd64)
run: |
docker buildx build --load --platform linux/amd64 -t fogping:smoke --cache-from type=gha .
docker run -d --name fp -p 8518:8518 \
-e FOGPING_EDIT=1 -e FOGPING_USER=admin -e FOGPING_PASSWD=pw \
fogping:smoke
for i in $(seq 1 20); do
curl -fsS http://127.0.0.1:8518/api/version && break
sleep 1
done
curl -fsS http://127.0.0.1:8518/api/version | grep -q '"editable":true'
test "$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8518/api/targets)" = 401
docker logs fp
66 changes: 66 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# syntax=docker/dockerfile:1
# fogping multi-arch image: linux/amd64, linux/arm64, linux/arm/v7
#
# 与 json-ping 的区别:fogping 依赖 mattn/go-sqlite3(cgo),不能 CGO_ENABLED=0 交叉编译。
# 这里用 tonistiigi/xx:构建机原生架构跑 clang,链接目标架构的 musl,静态产出,不走 QEMU。

ARG GO_VERSION=1.24
ARG XX_VERSION=1.6.1

FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx

FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS build
COPY --from=xx / /
RUN apk add --no-cache clang lld
ARG TARGETPLATFORM
# 目标架构的 C 运行时与头文件(sqlite3.c 编译要用)
RUN xx-apk add --no-cache musl-dev gcc

WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .

ARG VERSION=dev
ENV CGO_ENABLED=1
# -extldflags -static:musl 全静态,运行层不需要任何 libc
# sqlite_omit_load_extension:静态二进制里 dlopen 无意义,顺便去掉链接警告
RUN xx-go build -trimpath -tags "timetzdata sqlite_omit_load_extension" \
-ldflags="-s -w -X main.version=${VERSION} -linkmode external -extldflags -static" \
-o /out/fogping . \
&& xx-verify --static /out/fogping

# busybox:musl 约 1MB:提供 sh(入口脚本)和 wget(健康检查)
FROM busybox:1.37-musl

LABEL org.opencontainers.image.title="fogping" \
org.opencontainers.image.description="SmokePing-like latency monitor: one binary, embedded SQLite, targets edited in the web UI" \
org.opencontainers.image.source="https://github.com/githubflyideas/fogping" \
org.opencontainers.image.licenses="Apache-2.0"

COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /out/fogping /usr/local/bin/fogping
COPY --chmod=0755 docker/entrypoint.sh /usr/local/bin/docker-entrypoint

# 程序以工作目录为根,数据库落在 ./data/fogping.db → 容器内 /data/data/fogping.db
RUN mkdir -p /data/data && chown -R 65532:65532 /data
WORKDIR /data

ENV TZ=UTC
VOLUME /data
EXPOSE 8518

# 默认非 root;bind mount 时推荐 --user $(id -u):$(id -g) 与宿主目录属主对齐。
# ICMP 用的是非特权 ping socket:Docker 20.10+ 默认在容器 netns 里放开 ping_group_range,无需 NET_RAW。
USER 65532:65532

# /api/version 不经过登录,开启 user=/passwd= 后健康检查依然有效
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
CMD wget -q -O /dev/null http://127.0.0.1:8518/api/version || exit 1

# 参数原样传给 fogping,例如:
# docker run ... githubflyideas/fogping --edit user=admin passwd=change-me
# 不方便改命令行的场景(Docker Desktop / NAS / RouterOS)用环境变量:
# FOGPING_EDIT=1 FOGPING_USER=admin FOGPING_PASSWD=change-me FOGPING_DAYS=90
ENTRYPOINT ["docker-entrypoint"]
CMD []
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,41 @@ Open `http://localhost:8518`, log in(user=admin passwd=admin1234 )
`./fogping --help` prints the common commands with copy-ready examples. The rest of
this page covers each step in detail.

## Docker

Images for linux/amd64, arm64 and arm/v7 (Raspberry Pi, NAS, RouterOS). Data lives in `/data`.

```bash
# first run: --edit so you can add targets in the web UI
docker run -d --name fogping --restart unless-stopped \
--user $(id -u):$(id -g) -p 8518:8518 \
-v ~/fogping:/data githubflyideas/fogping \
--edit user=admin passwd=change-me

# done editing: recreate without --edit (data in ~/fogping is kept)
docker rm -f fogping
docker run -d --name fogping --restart unless-stopped \
--user $(id -u):$(id -g) -p 8518:8518 \
-v ~/fogping:/data githubflyideas/fogping \
user=admin passwd=change-me
```

Everything after the image name is passed to fogping as-is (`--edit`, `--days=90`,
`user=` / `passwd=`). Where you can set environment variables but not a command
(Docker Desktop, NAS container managers, RouterOS), use these instead:

| Variable | Same as |
|---|---|
| `FOGPING_EDIT=1` | `--edit` |
| `FOGPING_USER=admin` / `FOGPING_PASSWD=change-me` | `user=admin passwd=change-me` (comma-separated for several) |
| `FOGPING_DAYS=90` | `--days=90` |

`--edit` without a login is refused, as on bare metal. PING targets work without
extra capabilities on Docker 20.10+; `--user` keeps the mounted directory writable by you.

## Contents

- [Docker](#docker)
- [Requirements](#requirements)
- [Install](#install)
- [Allow ICMP (ping) without root](#allow-icmp-ping-without-root)
Expand Down
19 changes: 19 additions & 0 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/sh
# Map FOGPING_* env vars to flags, for runners that can set env but not a command
# (Docker Desktop, NAS container managers, RouterOS /container).
# Command-line args are kept and come last, so they win over env on conflict.
set -e

case "${FOGPING_EDIT:-}" in
1|true|TRUE|yes|on) set -- --edit "$@" ;;
esac

if [ -n "${FOGPING_DAYS:-}" ]; then
set -- "--days=${FOGPING_DAYS}" "$@"
fi

if [ -n "${FOGPING_USER:-}" ] || [ -n "${FOGPING_PASSWD:-}" ]; then
set -- "user=${FOGPING_USER:-}" "passwd=${FOGPING_PASSWD:-}" "$@"
fi

exec fogping "$@"
Loading