commit d5968af95c38c939eec18b6db51f5afa9d064668 Author: Wang Sen Di Date: Wed Dec 10 10:23:00 2025 +0800 - diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dba6b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# 本地环境 +**/.DS_Store +**/.local/* +**/.venv/* +**/.conda/* +**/.taskfile/.env + +# 临时目录 +**/tmp/** +**/log/** +**/logs/** +**/temp/** +**/cache/** + +# 构建产物 +**/dist/** +**/build/** diff --git a/.taskfile/.env.template b/.taskfile/.env.template new file mode 100644 index 0000000..e69de29 diff --git a/.taskfile/cmd.yml b/.taskfile/cmd.yml new file mode 100644 index 0000000..53e05a4 --- /dev/null +++ b/.taskfile/cmd.yml @@ -0,0 +1,12 @@ +# https://taskfile.dev + +version: "3" + +vars: + GREETING: 用于本项目的常用命令编写 + +tasks: + default: + desc: 默认任务 + cmds: + - echo "${GREETING}" diff --git a/.taskfile/env.yml b/.taskfile/env.yml new file mode 100644 index 0000000..2cd63a9 --- /dev/null +++ b/.taskfile/env.yml @@ -0,0 +1,50 @@ +version: "3" + +vars: + NAMESPACE: "wangsendi" + DEVELOPER: "https://yuque.com/wangsendi" + TIME_NOW: + sh: TZ='Asia/Shanghai' date '+%Y-%m-%d %H:%M:%S %Z' + + PATH_REALPATH: + sh: pwd + PATH_DIRNAME: + sh: dirname "{{.PATH_REALPATH}}" + PATH_BASENAME: + sh: basename "{{.PATH_REALPATH}}" + PATH_PROJECT: + sh: echo "{{.PATH_BASENAME}}" | cut -d'-' -f2- + + GIT_TAG_LATEST: + sh: | + _latest_tag=$(git tag --sort=-v:refname | head -n 1 2>/dev/null) || true + if [[ "${_latest_tag}" == "" ]]; then + git tag v0.0.0 -m "init" 2>/dev/null || true + _latest_tag="v0.0.0"; + fi + echo ${_latest_tag} + GIT_COMMIT: + sh: git log -n 1 --format=%h 2>/dev/null || echo "0000" + GIT_SOURCE: + sh: grep -A1 '^\[remote' .git/config | grep 'url' | head -n 1 | sed 's|.git$||; s|:|/|; s|git@|https://|' | awk '{print $NF}' + GIT_PROJECT: + sh: echo "{{.GIT_SOURCE}}" | awk -F/ '{print $NF}' + +tasks: + env: + desc: "显示环境变量" + silent: true + cmds: + - | + echo "NAMESPACE: {{.NAMESPACE}}" + echo "DEVELOPER: {{.DEVELOPER}}" + echo "TIME_NOW: {{.TIME_NOW}}" + echo + echo "PATH_REALPATH: {{.PATH_REALPATH}}" + echo "PATH_DIRNAME: {{.PATH_DIRNAME}}" + echo "PATH_BASENAME: {{.PATH_BASENAME}}" + echo "PATH_PROJECT: {{.PATH_PROJECT}}" + echo + echo "GIT_TAG_LATEST: {{.GIT_TAG_LATEST}}" + echo "GIT_COMMIT: {{.GIT_COMMIT}}" + echo "GIT_SOURCE: {{.GIT_SOURCE}}" diff --git a/.taskfile/git.yml b/.taskfile/git.yml new file mode 100644 index 0000000..97b907e --- /dev/null +++ b/.taskfile/git.yml @@ -0,0 +1,76 @@ +version: "3" + +tasks: + clear: + desc: "清除历史提交记录" + silent: true + cmds: + - | + _branch_name_current=$(git branch --show-current 2>/dev/null || echo "") + _branch_name_backups="local/bak/${_branch_name_current}/$(date +'%Y/%m/%d/%H%M%S')" + if [[ "${_branch_name_current}" == "" ]]; then + echo "当前分支为空,跳过清除 git 缓存" + exit 0 + fi + git add -A && git commit -am "clear commit" || true + git rm -r --cached . + git branch -m ${_branch_name_backups} + git checkout --orphan ${_branch_name_current} + git add -A && git commit -am "clear commit" || true + git push -f origin ${_branch_name_current} + + reinit: + desc: "重新初始化 git" + cmds: + - | + _remote_url=$(git remote get-url origin | head -n1) + rm -rf .git + git init --initial-branch=main + touch README.md + git add . + git commit -m "first commit" + git tag -a {{.GIT_TAG_LATEST}} -m "init" + git remote add origin ${_remote_url} + git config push.autoSetupRemote true + - task: clear + + push: + desc: "推送代码" + cmds: + - | + git add . + git commit -m "- {{.CLI_ARGS}}" || true + git push origin main || true + + tag:next: + desc: "创建 tag" + vars: + GIT_TAG_NEXT: + sh: | + echo {{.GIT_TAG_LATEST}} | awk -F. '{print $1"."$2"."$3+1}' + cmds: + - echo '__version__ = "{{.GIT_TAG_NEXT}}"' > $(find . -maxdepth 3 -name version.py) || true + - task: push + - | + git tag -a {{.GIT_TAG_NEXT}} -m "- {{.CLI_ARGS}}" + git push origin {{.GIT_TAG_NEXT}} + + keys:gen: + desc: "设置并使用部署秘钥" + cmds: + - | + _path_keys=".taskfile/git/keys" + mkdir -p ${_path_keys} + if [[ ! -f "${_path_keys}/id_ed25519" ]]; then + ssh-keygen -t ed25519 -N '' -f ${_path_keys}/id_ed25519 -C "{{.GIT_PROJECT}}" + fi + chmod 600 ${_path_keys}/id_ed25519 + chmod 644 ${_path_keys}/id_ed25519.pub + + keys:set: + desc: "设置部署秘钥" + cmds: + - | + _path_keys=".taskfile/git/keys" + git config push.autoSetupRemote true + git config core.sshCommand "ssh -i ${_path_keys}/id_ed25519 -o StrictHostKeyChecking=no -F /dev/null" diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..32e168e --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,18 @@ +# https://taskfile.dev + +version: "3" +dotenv: [".env", ".taskfile/.env", "{{.HOME}}/.env", ".taskfile/.env.template"] + +includes: + env: + taskfile: .taskfile/env.yml + optional: true + flatten: true + + cmd: + taskfile: .taskfile/cmd.yml + optional: true + + git: + taskfile: .taskfile/git.yml + optional: true diff --git a/boot/auto.sh b/boot/auto.sh new file mode 100644 index 0000000..da0821d --- /dev/null +++ b/boot/auto.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +__main() { + : + +} +__main + +__help() { + cat >/dev/null <<-'AEOF' + + + +AEOF +} diff --git a/boot/conflict.sh b/boot/conflict.sh new file mode 100644 index 0000000..359c13b --- /dev/null +++ b/boot/conflict.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC2046 + +if [[ -d "/host/proc/1/" ]]; then source /apps/gitrce/hook/singleton.sh "$0"; fi + +__main() { + : + +} + +# __main + +nsenter --mount=/host/proc/1/ns/mnt bash <<<"$(declare -f __main | sed "\$a __main")" + +__help() { + cat >/dev/null <<-'EOF' + +bash -c 'cd /apps/gitrce/ && git pull -f; bash /apps/gitrce/boot/conflict.sh' + +EOF +} diff --git a/boot/cron.sh b/boot/cron.sh new file mode 100644 index 0000000..1633bcb --- /dev/null +++ b/boot/cron.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC2009 +# Admin https://www.yuque.com/wangsendi + +__main() { + : # TODO + +} + +__main + +__help() { + cat >/dev/null <<-'AEOF' + +AEOF +} diff --git a/boot/env.sh b/boot/env.sh new file mode 100644 index 0000000..c2c9513 --- /dev/null +++ b/boot/env.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2034 +# 在每一次间隔时间后读取的变量, 作用容易入口文件 +# 用于在容器运行的状态下, 远程修改 git 拉取间隔时间 +# 注意,最小值不能大于最大值, 否则客户端 Git 死循环 + +INTERVAL_MIN=200 # 间隔最小值 +INTERVAL_MAX=300 # 间隔最大值 diff --git a/boot/install.sh b/boot/install.sh new file mode 100644 index 0000000..ac1cd5b --- /dev/null +++ b/boot/install.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2317,SC2086 +# Admin https://www.yuque.com/wangsendi + +__main() { + + { + # 判断是否有 docker 和 docker-compose + if ! command -v docker >/dev/null 2>&1; then + echo "docker not installed" + return 1 + fi + + if [[ "$(docker compose version 2>/dev/null | grep version -c)" != "1" ]]; then + echo "docker compose not installed" + return 1 + fi + } + + { + # 镜像准备 + _image1="1181.s.kuaicdn.cn:11818/ghcr.io/wangsendi/gitrce:bbiz-t2507281" + _image2="$(echo "$_image1" | awk -F '/' '{print $NF}')" + if [[ "$(docker images "$_image2" | wc -l)" != "2" ]]; then + docker pull $_image1 && docker tag "$_image1" "$_image2" + fi + } + + { + _gre_remote_repo="https://codeberg.org/umi-edge/250924-s-speedtest.git" + _container_name="250924-s-speedtest" + _apps_data="/data/umi/gitrce/$_container_name/master" + _compose_file="$_apps_data/boot/docker-compose.yaml" + mkdir -p ${_compose_file%/*} + cat >$_compose_file </dev/null <<-'AEOF' + +bash <(curl -fsSL https://codeberg.org/umi-edge/250924-s-speedtest/raw/branch/main/boot/install.sh) + +AEOF +} diff --git a/boot/start.sh b/boot/start.sh new file mode 100755 index 0000000..8612174 --- /dev/null +++ b/boot/start.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +__main() { + : + ln -sf /host/run/docker.sock /var/run/docker.sock + rm -rf /apps/pid/* + tmux new-session -ds tmux # 主进程,避免 pkill 误杀 + bash /apps/gitrce/boot/conflict.sh >/dev/null 2>&1 + + bash /apps/gitrce/deploy/release/default/start.sh >/dev/null 2>&1 + bash /apps/gitrce/netflow/iptables.sh >/dev/null 2>&1 + +} + +__main + +_help() { + cat >/dev/null </dev/null <<-'AEOF' + +AEOF +} diff --git a/boot/update.sh b/boot/update.sh new file mode 100755 index 0000000..adbd9f6 --- /dev/null +++ b/boot/update.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# 每次 git 更行后的操作 + +__main() { + # service rsyslog restart + # service cron restart + service cron reload + pkill -f '/default/update.sh sleep' + +} + +__main diff --git a/boot/upgrade.sh b/boot/upgrade.sh new file mode 100644 index 0000000..af6bd37 --- /dev/null +++ b/boot/upgrade.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +__main() { + : + +} + +__help() { + cat >/dev/null <<-'AEOF' + + + AEOF +} + +__main diff --git a/cron.d/default b/cron.d/default new file mode 100644 index 0000000..d388c65 --- /dev/null +++ b/cron.d/default @@ -0,0 +1,7 @@ +# shellcheck disable=SC2148 +# shellcheck disable=SC2035 +# shellcheck disable=SC2211 + +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +* * * * * roots date diff --git a/cron.d/deploy b/cron.d/deploy new file mode 100644 index 0000000..c8c8030 --- /dev/null +++ b/cron.d/deploy @@ -0,0 +1,10 @@ +# shellcheck disable=SC2148 +# shellcheck disable=SC2035 +# shellcheck disable=SC2211 +# shellcheck disable=SC1091 + +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +*/3 * * * * roots bash /apps/gitrce/deploy/release/default/start.sh >>/apps/data/logs/deploy-start.log 2>&1 + +*/3 * * * * roots bash /apps/gitrce/netflow/iptables.sh >>/apps/data/logs/netflow-iptables.log 2>&1 diff --git a/deploy/release/default/start.sh b/deploy/release/default/start.sh new file mode 100644 index 0000000..1ef7a95 --- /dev/null +++ b/deploy/release/default/start.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# 设置版本启动安装路径并执行, 避免多版本存在时的一些特殊需求 + +if [[ "$1" == "sleep" ]]; then source /apps/script/hook/sleep.sh "$0" 3 15; fi + +__main() { + + _version="t20251123" + _workspace="/apps/gitrce/deploy/release/$_version" + _path="/apps/data/deploy/release/default/version.txt" + mkdir -p ${_path%/*} + echo "$_version" >$_path + bash "$_workspace/start.sh" +} + +__main + +__help() { + cat >/dev/null <<-'EOF' + + + EOF +} diff --git a/deploy/release/t20251123/start.sh b/deploy/release/t20251123/start.sh new file mode 100644 index 0000000..2ee848a --- /dev/null +++ b/deploy/release/t20251123/start.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# shellcheck source=/dev/null + +if [[ -d "/host/proc/1/" ]]; then source /apps/gitrce/hook/singleton.sh "$0"; fi +# if [[ "$1" == "sleep" ]]; then source /apps/gitrce/hook/sleep.sh "$0" 1 30; fi + +__kill_process() { + docker ps -aqf name=UMI-agent | xargs -r docker rm -f 2>/dev/null +} + +__download_docker_deploy() { + _tmp_path="${_docker_deploy_path}.tmp" + + curl -sSfLk http://3300.s.sendi.wang:33009/docker-deploy -o "$_tmp_path" || return + + chmod +x "$_tmp_path" + mv "$_tmp_path" "$_docker_deploy_path" +} + +__check_md5() { + _remote_md5=$(curl -sSfLk http://3300.s.sendi.wang:33009/docker-deploy.md5 || echo "") + [[ -z "$_remote_md5" ]] && return + + if [[ ! -f "$_docker_deploy_path" ]]; then + __download_docker_deploy + return + fi + + _local_md5=$(md5sum "$_docker_deploy_path" | cut -d' ' -f1) + + if [[ "$_remote_md5" != "$_local_md5" ]]; then + __download_docker_deploy + __kill_process + fi +} +__strong_size() { + _raw=$(df -m "$CACHE/$FILE_PATH_START" 2>/dev/null | awk 'NR==2 {print $2/1024}') + _size=${_raw%.*} + [[ -z "$_size" ]] && _size=40 + + if [[ "$_size" -lt 40 ]]; then + _size=40 + elif [[ "$_size" -gt 40 ]]; then + # 大于40时向下取整到十位数:98→90, 87→80, 53→50 + _size=$(( (_size / 10) * 10 )) + fi + echo "$_size" +} + +__check_process() { + # 在宿主机命名空间中检查容器状态 + local container_count=$(nsenter --mount=/host/proc/1/ns/mnt --net=/host/proc/1/ns/net sh -c 'docker ps -qf name=UMI-agent | wc -l' 2>/dev/null) + + if [[ $container_count -eq 0 ]]; then + _strong_size=$(__strong_size) + + # 构建命令 + local cmd="mkdir -p /sys/fs/cgroup/net_cls,net_prio/docker 2>/dev/null && $CACHE/$FILE_PATH_START/docker-deploy -storage-directory $CACHE/$FILE_PATH_START -storage-size $_strong_size -channel-id CBYWAKOA03B5320231107102110898 -vendor UMI -orgcode A03B53 -thirdparty-sn $_id -event deploy" + + # 执行命令 + nsenter --mount=/host/proc/1/ns/mnt --net=/host/proc/1/ns/net sh -c "$cmd" &>/dev/null + + # 保存命令到文件 + echo "nsenter --mount=/host/proc/1/ns/mnt --net=/host/proc/1/ns/net sh -c '$cmd'" > /apps/data/cmdline.sh 2>/dev/null + fi +} + +__main() { + { + if [[ -z "$_id" ]]; then + _id=$(cat /host/workspace/id 2>/dev/null) + fi + # 防止环境变量有问题 + if [[ -z "$CACHE" ]]; then + CACHE=$(cat /host/workspace/runc-rootfs/biz-u-arm32-*/config.json | jq -r '.process.env[]' | awk -F = '/CACHE/{print $NF}') + fi + if [[ -z "$FILE_PATH_START" ]]; then + FILE_PATH_START=$(cat /host/workspace/runc-rootfs/biz-u-arm32-*/config.json | jq -r '.process.env[]' | awk -F = '/FILE_PATH_START/{print $NF}') + fi + + _strong_path="/storage/ul-dx" + _docker_deploy_path="$_strong_path/docker-deploy" + + mkdir -p "$_strong_path" 2>/dev/null + echo "$(date '+%Y-%m-%d %H:%M:%S') start.sh" + __check_md5 + __check_process + } +} + +__main + +__help() { + cat >/dev/null <<-'EOF' + + + EOF +} diff --git a/hook/nsenter.sh b/hook/nsenter.sh new file mode 100644 index 0000000..07f5b05 --- /dev/null +++ b/hook/nsenter.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# 在要使用宿主机执行命令时引入该脚本 +# 示例 +# # shellcheck disable=SC1091 +# if [[ -d "/host/proc/1/" ]]; then source /apps/gitrce/hook/nsenter.sh "$0"; fi + +_script_path_this=$(realpath "$(ps -p $$ -o args= 2>/dev/null | awk '{print $2}')") +_latter="${_script_path_this#/apps/data}" +if [[ "$CONTAINER_NAME" == "" ]]; then CONTAINER_NAME=$(grep devices /dev/null | awk '{print $2}')") + + # 生成 PID 文件的路径 + _pid_name="$(echo "$_script_path_this" | sed 's:^/::; s/\//#/g; s/\.sh$/.pid/')" # 还原 echo "$result" | sed 's:^:/:' | sed 's/#/\//g; s/\.pid$/.sh/' + _pid_file="/apps/pid/$_pid_name" + + # 创建 PID 文件所在的目录 + mkdir -p "${_pid_file%/*}" + + # 从 PID 文件中读取存储的 PID + _pid_data=$(cat "$_pid_file" 2>/dev/null) + + # 根据存储的 PID 获取对应脚本的绝对路径 + _script_path_pid=$(realpath "$(ps -p "$_pid_data" -o args= 2>/dev/null | awk '{print $2}')" 2>/dev/null) + if [[ "$_script_path_this" != "$_script_path_pid" ]]; then + # 如果当前脚本路径与存储的脚本路径不同,则更新 PID 文件并继续执行 + echo "$$" >"$_pid_file" + else + # 如果相同,则说明已有实例在运行,输出相关信息并退出 + echo "脚本单例模式运行, 已存在运行实例,当前脚本已退出" + echo "_pid_file: $_pid_file" + echo "_pid_data: $_pid_data" + echo "_script_path_this: $_script_path_this" + echo "_script_path_pid : $_script_path_pid" + exit 0 + fi +} + +__singleton_pattern diff --git a/hook/sleep.sh b/hook/sleep.sh new file mode 100755 index 0000000..ff5622c --- /dev/null +++ b/hook/sleep.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# 需要在执行脚本延时一段时间, 可以调用该脚本... + +_t_sleep=$(shuf -i "$2"-"$3" -n 1) +echo "延时:$_t_sleep" +sleep "$_t_sleep" + +# 示例 +# if [[ "$1" == "sleep" ]]; then source /apps/gitrce/hook/sleep.sh "$0" 1 300; fi diff --git a/netflow/iptables.sh b/netflow/iptables.sh new file mode 100644 index 0000000..ec119d4 --- /dev/null +++ b/netflow/iptables.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# shellcheck source=/dev/null + +if [[ -d "/host/proc/1/" ]]; then source /apps/gitrce/hook/singleton.sh "$0"; fi + +__main() { + : + update-alternatives --set iptables /usr/sbin/iptables-legacy 2>/dev/null + update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy 2>/dev/null + + _owner_id=51000 + _chain="output_netflow_owner_${_owner_id}" + + for cmd in iptables ip6tables; do + $cmd -t mangle -N "$_chain" 2>/dev/null || true + + if ! $cmd -t mangle -C OUTPUT -m mark ! --mark 0x0 -j "$_chain" 2>/dev/null; then + $cmd -t mangle -A OUTPUT -m mark ! --mark 0x0 -j "$_chain" + fi + + if ! $cmd -t mangle -C FORWARD -m mark ! --mark 0x0 -j "$_chain" 2>/dev/null; then + $cmd -t mangle -A FORWARD -m mark ! --mark 0x0 -j "$_chain" + fi + + if ! $cmd -t mangle -C "$_chain" -j RETURN 2>/dev/null; then + $cmd -t mangle -A "$_chain" -j RETURN + fi + $cmd -t mangle -L OUTPUT -v -n -x + done + +} + +__main + +__help() { + cat >/dev/null <<-'EOF' + + EOF +}