chore: caddy config file(json)

nginx: proxy version api
This commit is contained in:
zhaoyupeng
2025-12-05 18:39:30 +08:00
parent f4f3590aec
commit c53c15fa8c
15 changed files with 1153 additions and 60 deletions

View File

@@ -1,15 +1,140 @@
package installer
import "context"
import (
"context"
"fmt"
func (i *installer) Check(ctx context.Context) error {
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/syscheck"
)
type CheckOption func(*checkOpt)
type checkOpt struct {
ignoreDisk bool
ignoreMemory bool
ignoreCPU bool
}
func WithIgnoreDiskCheck(ignore bool) CheckOption {
return func(o *checkOpt) {
o.ignoreDisk = ignore
}
}
func WithIgnoreMemoryCheck(ignore bool) CheckOption {
return func(o *checkOpt) {
o.ignoreMemory = ignore
}
}
func WithIgnoreCPUCheck(ignore bool) CheckOption {
return func(o *checkOpt) {
o.ignoreCPU = ignore
}
}
func (i *installer) Check(ctx context.Context, opts ...CheckOption) error {
var (
err error
o = &checkOpt{}
)
for _, fn := range opts {
fn(o)
}
logger.Info("☑️ installer.Check: Starting system checks...")
if err = i.targetOK(ctx); err != nil {
return err
}
// 1. Check disk space: >= 500GB
logger.Info("☑️ installer.Check: Checking disk space...")
diskSpaceResult, err := syscheck.CheckDiskSpace(ctx, i, 500)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check disk space: %v", err)
return fmt.Errorf("failed to check disk space: %w", err)
}
if !diskSpaceResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", diskSpaceResult.Name, diskSpaceResult.Message, diskSpaceResult.Expected, diskSpaceResult.Actual)
if !o.ignoreDisk {
return fmt.Errorf("disk space check failed: %s", diskSpaceResult.Message)
}
}
logger.Info("✅ %s: %s", diskSpaceResult.Name, diskSpaceResult.Actual)
// 2. Check disk performance: write >= 500MB/s, read >= 500MB/s
logger.Info("☑️ installer.Check: Checking disk performance...")
diskPerfResult, err := syscheck.CheckDiskPerformance(ctx, i, 500, 500)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check disk performance: %v", err)
return fmt.Errorf("failed to check disk performance: %w", err)
}
if !diskPerfResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", diskPerfResult.Name, diskPerfResult.Message, diskPerfResult.Expected, diskPerfResult.Actual)
if !o.ignoreDisk {
return fmt.Errorf("disk performance check failed: %s", diskPerfResult.Message)
}
}
logger.Info("✅ %s: %s", diskPerfResult.Name, diskPerfResult.Actual)
// 3. Check memory size: >= 15.5GB
logger.Info("☑️ installer.Check: Checking memory size...")
memResult, err := syscheck.CheckMemory(ctx, i, 15.5)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check memory: %v", err)
return fmt.Errorf("failed to check memory: %w", err)
}
if !memResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", memResult.Name, memResult.Message, memResult.Expected, memResult.Actual)
if !o.ignoreMemory {
return fmt.Errorf("memory check failed: %s", memResult.Message)
}
}
logger.Info("✅ %s: %s", memResult.Name, memResult.Actual)
// 4. Check CPU cores: >= 8
logger.Info("☑️ installer.Check: Checking CPU cores...")
cpuCoresResult, err := syscheck.CheckCPUCores(ctx, i, 8)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check CPU cores: %v", err)
return fmt.Errorf("failed to check CPU cores: %w", err)
}
if !cpuCoresResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", cpuCoresResult.Name, cpuCoresResult.Message, cpuCoresResult.Expected, cpuCoresResult.Actual)
if !o.ignoreCPU {
return fmt.Errorf("CPU cores check failed: %s", cpuCoresResult.Message)
}
}
logger.Info("✅ %s: %s", cpuCoresResult.Name, cpuCoresResult.Actual)
// 5. Check CPU frequency: >= 2GHz
logger.Info("☑️ installer.Check: Checking CPU frequency...")
cpuFreqResult, err := syscheck.CheckCPUFrequency(ctx, i, 2.0)
if err != nil {
logger.Debug("❌ installer.Check: Failed to check CPU frequency: %v", err)
return fmt.Errorf("failed to check CPU frequency: %w", err)
}
if !cpuFreqResult.Passed {
logger.Error("❌ %s: %s (Expected: %s, Actual: %s)", cpuFreqResult.Name, cpuFreqResult.Message, cpuFreqResult.Expected, cpuFreqResult.Actual)
if !o.ignoreCPU {
return fmt.Errorf("CPU frequency check failed: %s", cpuFreqResult.Message)
}
}
logger.Info("✅ %s: %s", cpuFreqResult.Name, cpuFreqResult.Actual)
logger.Info("✅ installer.Check: All system checks passed successfully!")
return nil
}

View File

@@ -3,7 +3,11 @@ package installer
import (
"context"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
)
@@ -13,14 +17,118 @@ type installer struct {
target string
}
func (i *installer) targetOK(ctx context.Context) error {
if i.target == "" {
logger.Debug("🎯 installer.targetOK: target = self")
func (i *installer) buildCommand(ctx context.Context, cmds ...string) *exec.Cmd {
if len(cmds) == 0 {
return nil
}
// run ssh <target>, check if it's reachable, and it's root user
cmd := exec.CommandContext(ctx, "ssh", i.target, "whoami")
if i.target == "self" {
return exec.CommandContext(ctx, cmds[0], cmds[1:]...)
}
sshArgs := append([]string{i.target}, cmds...)
return exec.CommandContext(ctx, "ssh", sshArgs...)
}
// ExecuteCommand implements syscheck.CommandExecutor interface
func (i *installer) ExecuteCommand(ctx context.Context, cmds ...string) (string, error) {
cmd := i.buildCommand(ctx, cmds...)
if cmd == nil {
return "", fmt.Errorf("failed to build command")
}
output, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("command failed: %w, output: %s", err, string(output))
}
return string(output), nil
}
type CopyFileOption func(*copyFileOptions)
type copyFileOptions struct {
addExecutable bool
}
func withCopyFileExecutable() func(*copyFileOptions) {
return func(o *copyFileOptions) {
o.addExecutable = true
}
}
func (i *installer) copyFile(ctx context.Context, src, dst string, opts ...CopyFileOption) error {
logger.Debug("☑️ installer.copyFile: Copying file from %s to %s (target: %s)", src, dst, i.target)
var (
err error
o = &copyFileOptions{}
srcFile, dstFile *os.File
srcInfo os.FileInfo
)
for _, fn := range opts {
fn(o)
}
if i.target == "self" {
// Simply copy file locally
logger.Debug("Copying file locally: %s -> %s", src, dst)
// Open source file
if srcFile, err = os.Open(src); err != nil {
logger.Error("❌ Failed to open source file %s: %v", src, err)
return fmt.Errorf("failed to open source file: %w", err)
}
defer srcFile.Close()
// Create destination directory if needed
dstDir := filepath.Dir(dst)
if err = os.MkdirAll(dstDir, 0755); err != nil {
logger.Error("❌ Failed to create destination directory %s: %v", dstDir, err)
return fmt.Errorf("failed to create destination directory: %w", err)
}
// Create destination file
if dstFile, err = os.Create(dst); err != nil {
logger.Error("❌ Failed to create destination file %s: %v", dst, err)
return fmt.Errorf("failed to create destination file: %w", err)
}
defer dstFile.Close()
// Copy file content
if _, err = io.Copy(dstFile, srcFile); err != nil {
logger.Error("❌ Failed to copy file content: %v", err)
return fmt.Errorf("failed to copy file content: %w", err)
}
// Get source file permissions
if srcInfo, err = os.Stat(src); err == nil {
if err = os.Chmod(dst, srcInfo.Mode()); err != nil {
logger.Debug("⚠️ Failed to set file permissions: %v", err)
}
}
logger.Info("✅ File copied locally: %s -> %s", src, dst)
return nil
}
// Copy file via scp to remote target
logger.Debug("Copying file via scp: %s -> %s:%s", src, i.target, dst)
// Format: scp <src> <target>:<dst>
cmd := exec.CommandContext(ctx, "scp", src, fmt.Sprintf("%s:%s", i.target, dst))
output, err := cmd.CombinedOutput()
if err != nil {
logger.Error("❌ Failed to copy file via scp: %v, output: %s", err, string(output))
return fmt.Errorf("failed to copy file via scp: %w, output: %s", err, string(output))
}
logger.Info("✅ File copied via scp: %s -> %s:%s", src, i.target, dst)
return nil
}
func (i *installer) targetOK(ctx context.Context) error {
cmd := i.buildCommand(ctx, "whoami")
output, err := cmd.CombinedOutput()
if err != nil {
logger.Debug("❌ installer.targetOK: check target %s failed, err = %v", i.target, err)
@@ -36,5 +144,10 @@ func (i *installer) targetOK(ctx context.Context) error {
}
func NewInstaller(workdir, target string) *installer {
if target == "" {
logger.Warn("🎯 NewInstaller: target empty, set to default(self)")
target = "self"
}
return &installer{workdir: workdir, target: target}
}

View File

@@ -2,15 +2,19 @@ package installer
import (
"context"
"fmt"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"github.com/samber/lo"
)
type K0sOpt func(*k0sOpt)
type k0sOpt struct {
Type string // controller, worker
DisableWorker bool
WorkerTokenFile string
Type string // controller, worker
controllerAsWorker bool
WorkerTokenFile string
}
func WithK0sType(t string) K0sOpt {
@@ -22,9 +26,9 @@ func WithK0sType(t string) K0sOpt {
}
}
func WithoutK0sWorker() K0sOpt {
func WithK0sControllerAsWorker() K0sOpt {
return func(o *k0sOpt) {
o.DisableWorker = true
o.controllerAsWorker = true
}
}
@@ -40,9 +44,9 @@ func (i *installer) K0s(ctx context.Context, opts ...K0sOpt) error {
var (
err error
o = &k0sOpt{
Type: "controller",
DisableWorker: false,
WorkerTokenFile: "/etc/k0s/worker.token",
Type: "controller",
controllerAsWorker: false,
WorkerTokenFile: "/etc/k0s/worker.token",
}
)
@@ -54,5 +58,66 @@ func (i *installer) K0s(ctx context.Context, opts ...K0sOpt) error {
fn(o)
}
binaries := []string{
"dependency/bin/k0s",
"dependency/bin/k9s", "dependency/bin/kubectl", "dependency/bin/helm"}
if err = i.checkFiles(binaries...); err != nil {
return err
}
// check image tar files:
images := []string{
"dependency/image/k0s.apiserver-network-proxy-agent.tar",
"dependency/image/k0s.cni-node.tar",
"dependency/image/k0s.coredns.tar",
"dependency/image/k0s.kube-proxy.tar",
"dependency/image/k0s.kube-router.tar",
"dependency/image/k0s.metrics-server.tar",
"dependency/image/k0s.pause.tar",
}
if err = i.checkFiles(images...); err != nil {
return err
}
// copy binaries to /usr/local/bin and add executable permissions
if err = i.copyFile(ctx, "dependency/bin/k0s", "/usr/local/bin/k0s", withCopyFileExecutable()); err != nil {
return err
}
if err = i.copyFile(ctx, "dependency/bin/k9s", "/usr/local/bin/k9s", withCopyFileExecutable()); err != nil {
return err
}
if err = i.copyFile(ctx, "dependency/bin/kubectl", "/usr/local/bin/kubectl", withCopyFileExecutable()); err != nil {
return err
}
if err = i.copyFile(ctx, "dependency/bin/helm", "/usr/local/bin/helm", withCopyFileExecutable()); err != nil {
return err
}
i.ExecuteCommand(ctx, "k0s", "")
return nil
}
// checkBinaryFiles checks if the required binary files exist in the dependency/bin directory
func (i *installer) checkFiles(fileBaseName ...string) error {
logger.Info("☑️ installer.checkFiles: Checking files in %s...", i.workdir)
for _, file := range fileBaseName {
filename := filepath.Join(i.workdir, file)
logger.Debug("Checking file: %s", filename)
if _, err := os.Stat(filename); os.IsNotExist(err) {
logger.Error("❌ File not found: %s", filename)
return fmt.Errorf("file not found: %s", filename)
} else if err != nil {
logger.Error("❌ Failed to check file %s: %v", filename, err)
return fmt.Errorf("failed to check file %s: %w", filename, err)
}
logger.Info("✅ File found: %s", file)
}
logger.Info("✅ installer.checkBinaryFiles: All binary files verified successfully!")
return nil
}

View File

@@ -1,15 +1,184 @@
package installer
import "context"
import (
"context"
"fmt"
"gitea.loveuer.com/yizhisec/pkg3/logger"
)
func (i *installer) Prepare(ctx context.Context) error {
var (
err error
)
logger.Info("☑️ installer.Prepare: Starting system preparation...")
if err = i.targetOK(ctx); err != nil {
return err
}
// 1. Set timezone to Asia/Shanghai
logger.Info("☑️ installer.Prepare: Setting timezone to Asia/Shanghai...")
if err = i.setTimezone(ctx); err != nil {
logger.Debug("❌ installer.Prepare: Failed to set timezone: %v", err)
return fmt.Errorf("failed to set timezone: %w", err)
}
logger.Info("✅ installer.Prepare: Timezone set successfully")
// 2. Disable swap
logger.Info("☑️ installer.Prepare: Disabling swap...")
if err = i.disableSwap(ctx); err != nil {
logger.Debug("❌ installer.Prepare: Failed to disable swap: %v", err)
return fmt.Errorf("failed to disable swap: %w", err)
}
logger.Info("✅ installer.Prepare: Swap disabled successfully")
// 3. Load module: iscsi_tcp
logger.Info("☑️ installer.Prepare: Loading kernel module iscsi_tcp...")
if err = i.loadKernelModule(ctx, "iscsi_tcp"); err != nil {
logger.Debug("❌ installer.Prepare: Failed to load iscsi_tcp module: %v", err)
return fmt.Errorf("failed to load iscsi_tcp module: %w", err)
}
logger.Info("✅ installer.Prepare: iscsi_tcp module loaded successfully")
// 4. Load module: br_netfilter
logger.Info("☑️ installer.Prepare: Loading kernel module br_netfilter...")
if err = i.loadKernelModule(ctx, "br_netfilter"); err != nil {
logger.Debug("❌ installer.Prepare: Failed to load br_netfilter module: %v", err)
return fmt.Errorf("failed to load br_netfilter module: %w", err)
}
logger.Info("✅ installer.Prepare: br_netfilter module loaded successfully")
// 5. Apply sysctl settings
logger.Info("☑️ installer.Prepare: Applying sysctl settings...")
if err = i.applySysctlSettings(ctx); err != nil {
logger.Debug("❌ installer.Prepare: Failed to apply sysctl settings: %v", err)
return fmt.Errorf("failed to apply sysctl settings: %w", err)
}
logger.Info("✅ installer.Prepare: Sysctl settings applied successfully")
logger.Info("✅ installer.Prepare: System preparation completed successfully!")
return nil
}
// setTimezone sets the system timezone to Asia/Shanghai
func (i *installer) setTimezone(ctx context.Context) error {
// Check if timezone file exists
cmd := i.buildCommand(ctx, "test", "-f", "/usr/share/zoneinfo/Asia/Shanghai")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("timezone file /usr/share/zoneinfo/Asia/Shanghai not found")
}
// Remove old localtime link/file
cmd = i.buildCommand(ctx, "rm", "-f", "/etc/localtime")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to remove /etc/localtime: %v", err)
}
// Create symlink
cmd = i.buildCommand(ctx, "ln", "-s", "/usr/share/zoneinfo/Asia/Shanghai", "/etc/localtime")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to create symlink: %w", err)
}
return nil
}
// disableSwap disables all swap partitions and removes swap entries from /etc/fstab
func (i *installer) disableSwap(ctx context.Context) error {
// Turn off all swap
cmd := i.buildCommand(ctx, "swapoff", "-a")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to swapoff: %v (may be already off)", err)
}
// Comment out swap entries in /etc/fstab to make it persistent
cmd = i.buildCommand(ctx, "sed", "-i", "/swap/s/^/#/", "/etc/fstab")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to comment swap in /etc/fstab: %v", err)
}
return nil
}
// loadKernelModule loads a kernel module and ensures it's loaded on boot
func (i *installer) loadKernelModule(ctx context.Context, moduleName string) error {
// Load the module immediately
cmd := i.buildCommand(ctx, "modprobe", moduleName)
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to load module %s: %w", moduleName, err)
}
// Add to /etc/modules-load.d/ to load on boot
filePath := fmt.Sprintf("/etc/modules-load.d/%s.conf", moduleName)
cmd = i.buildCommand(ctx, "bash", "-c", fmt.Sprintf("echo '%s' > %s", moduleName, filePath))
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
logger.Debug("Failed to add module to modules-load.d: %v", err)
}
return nil
}
// applySysctlSettings applies required sysctl settings for Kubernetes
func (i *installer) applySysctlSettings(ctx context.Context) error {
const sysctlConfig = `# Kubernetes required settings
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
net.ipv4.conf.all.forwarding = 1
net.ipv6.conf.all.forwarding = 1
vm.swappiness = 0
vm.overcommit_memory = 1
vm.panic_on_oom = 0
fs.file-max = 1000000
fs.inotify.max_user_watches = 2099999999
fs.inotify.max_user_instances = 2099999999
fs.inotify.max_queued_events = 2099999999
net.ipv4.neigh.default.gc_thresh1 = 1024
net.ipv4.neigh.default.gc_thresh2 = 4096
net.ipv4.neigh.default.gc_thresh3 = 8192
`
// Write sysctl config file
cmd := i.buildCommand(ctx, "bash", "-c", fmt.Sprintf("cat > /etc/sysctl.d/99-kubernetes.conf << 'EOF'\n%sEOF", sysctlConfig))
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to write sysctl config: %w", err)
}
// Apply sysctl settings
cmd = i.buildCommand(ctx, "sysctl", "--system")
if cmd == nil {
return fmt.Errorf("failed to build command")
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to apply sysctl settings: %w", err)
}
return nil
}

View File

@@ -100,6 +100,16 @@ kubectl create configmap ssl-client-key --namespace hsv2 --from-file=client.key=
kubectl create configmap ssl-client-ca-crt --namespace hsv2 --from-file=client.ca.crt=./ssl_client_ca.crt --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-client-ca-key --namespace hsv2 --from-file=client.ca.key=./ssl_client_ca.key --dry-run=client -o yaml | kubectl apply -f -
kubectl create configmap ssl-web-crt --namespace hsv2 --from-file=web.server.crt=./ssl_web.crt --dry-run=client -o yaml | kubectl apply -f -
`
_version_yaml = `
apiVersion: v1
kind: ConfigMap
metadata:
name: config-version
namespace: hsv2
data:
version.txt: |
__version__
`
)
@@ -208,6 +218,12 @@ kubectl create configmap ssl-web-crt --namespace hsv2 --from-file=web.server.crt
}
logger.Debug("✅ maker.ConfigMap: 写入 ssl_client_ca.key 文件: %s 成功", filepath.Join(dir, "ssl_client_ca.key"))
if err = os.WriteFile(filepath.Join(dir, "version.yaml"), []byte(_version_yaml), 0644); err != nil {
logger.Debug("❌ maker.ConfigMap: 写入 version.yaml 文件: %s 失败, 错误: %v", filepath.Join(dir, "version.yaml"), err)
return err
}
logger.Debug("✅ maker.ConfigMap: 写入 version.yaml 文件: %s 成功", filepath.Join(dir, "version.yaml"))
// upsert configmap
logger.Debug("☑️ maker.ConfigMap: 执行 upsert 脚本: %s", filepath.Join(dir, "upsert.sh"))
if err = os.WriteFile(filepath.Join(dir, "upsert.sh"), []byte(upsert), 0755); err != nil {

View File

@@ -2,31 +2,18 @@ package maker
import (
"context"
"encoding/json"
"os"
"path/filepath"
"gitea.loveuer.com/yizhisec/pkg3/logger"
"yizhisec.com/hsv2/forge/pkg/downloader"
"yizhisec.com/hsv2/forge/pkg/model"
)
func (m *maker) Proxy(ctx context.Context) error {
const (
binURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv2/bin/caddy"
caddyfileTpl = `{
layer4 {
:8443 {
route {
proxy __UPSTREAMS_8443__
}
}
:443 {
route {
proxy __UPSTREAMS_443__
}
}
}
}`
binURL = "https://artifactory.yizhisec.com:443/artifactory/filestore/hsv2/bin/caddy"
systemdSvc = `[Unit]
Description=YiZhiSec Caddy Reverse Proxy
After=network.target
@@ -34,7 +21,7 @@ After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/caddy run --config /etc/caddy/Caddyfile
ExecStart=/usr/local/bin/caddy run --config /etc/caddy/caddy.json
StandardOutput=journal
StandardError=journal
Nice=-20
@@ -68,12 +55,85 @@ WantedBy=multi-user.target`
}
logger.Debug("✅ maker.Proxy: 下载 caddy 成功, url = %s", binURL)
logger.Debug("☑️ maker.Proxy: 写入 Caddyfile 文件..., dest = %s", filepath.Join(location, "Caddyfile"))
if err := os.WriteFile(filepath.Join(location, "Caddyfile"), []byte(caddyfileTpl), 0644); err != nil {
logger.Debug("❌ maker.Proxy: 写入 Caddyfile 失败, dest = %s, err = %v", filepath.Join(location, "Caddyfile"), err)
logger.Debug("☑️ maker.Proxy: 写入 caddy.json 文件..., dest = %s", filepath.Join(location, "caddy.json"))
caddyConfig := model.CaddyConfig{
"apps": &model.CaddyApp{
Layer4: &model.CaddyLayer4{
Servers: map[string]*model.CaddyServer{
"proxy_8443": {
Listen: []string{":8443"},
Routes: []*model.CaddyRoute{
{
Handle: []*model.CaddyHandle{
{
Handler: "proxy",
Upstreams: []*model.CaddyUpstream{
{Dial: []string{"__ip_1__:32443"}},
{Dial: []string{"__ip_2__:32443"}},
},
HealthChecks: &model.CaddyHealthCheck{
Active: &model.CaddyActive{
Interval: "10s",
Timeout: "2s",
Port: 32443,
},
Passive: &model.CaddyPassive{
FailDuration: "30s",
MaxFails: 2,
},
},
LoadBalancing: &model.CaddyLoadBalancing{
Selection: &model.CaddySelection{
Policy: "round_robin",
},
},
},
},
},
},
},
"proxy_443": {
Listen: []string{":443"},
Routes: []*model.CaddyRoute{
{
Handle: []*model.CaddyHandle{
{
Handler: "proxy",
Upstreams: []*model.CaddyUpstream{
{Dial: []string{"__ip_1__:31443"}},
{Dial: []string{"__ip_2__:31443"}},
},
HealthChecks: &model.CaddyHealthCheck{
Active: &model.CaddyActive{
Interval: "10s",
Timeout: "2s",
Port: 31443,
},
Passive: &model.CaddyPassive{
FailDuration: "30s",
MaxFails: 2,
},
},
LoadBalancing: &model.CaddyLoadBalancing{
Selection: &model.CaddySelection{
Policy: "round_robin",
},
},
},
},
},
},
},
},
},
},
}
bs, _ := json.MarshalIndent(caddyConfig, "", " ")
if err := os.WriteFile(filepath.Join(location, "caddy.json"), []byte(bs), 0644); err != nil {
logger.Debug("❌ maker.Proxy: 写入 Caddyfile 失败, dest = %s, err = %v", filepath.Join(location, "caddy.json"), err)
return err
}
logger.Debug("✅ maker.Proxy: 写入 Caddyfile 文件成功, dest = %s", filepath.Join(location, "Caddyfile"))
logger.Debug("✅ maker.Proxy: 写入 Caddyfile 文件成功, dest = %s", filepath.Join(location, "caddy.json"))
logger.Debug("☑️ maker.Proxy: 写入 caddy.service 文件..., dest = %s", filepath.Join(location, "caddy.service"))
if err := os.WriteFile(filepath.Join(location, "caddy.service"), []byte(systemdSvc), 0644); err != nil {