caddy(四)Run詳解

caddy(四)Run詳解

前言

平時咱們使用 caddy 都是使用 它的 二進制 分發文件,咱們如今來分析 caddy 的 Run 函數。
從最外層抽象的看它都作了些什麼。git

Caddy Run

咱們來看看 Caddy Run 中引入了哪些包和操做,對 Caddy 的整體行爲作一個概覽
caddy/caddymain/run.go
首先看 init 函數github

func init() {
	caddy.TrapSignals()

	flag.BoolVar(&certmagic.Default.Agreed, "agree", false, "Agree to the CA's Subscriber Agreement")
	flag.StringVar(&certmagic.Default.CA, "ca", certmagic.Default.CA, "URL to certificate authority's ACME server directory")
	flag.StringVar(&certmagic.Default.DefaultServerName, "default-sni", certmagic.Default.DefaultServerName, "If a ClientHello ServerName is empty, use this ServerName to choose a TLS certificate")
	flag.BoolVar(&certmagic.Default.DisableHTTPChallenge, "disable-http-challenge", certmagic.Default.DisableHTTPChallenge, "Disable the ACME HTTP challenge")
	flag.BoolVar(&certmagic.Default.DisableTLSALPNChallenge, "disable-tls-alpn-challenge", certmagic.Default.DisableTLSALPNChallenge, "Disable the ACME TLS-ALPN challenge")
	flag.StringVar(&certmagic.Default.Email, "email", "", "Default ACME CA account email address")
	flag.DurationVar(&certmagic.HTTPTimeout, "catimeout", certmagic.HTTPTimeout, "Default ACME CA HTTP timeout")
	flag.StringVar(&revoke, "revoke", "", "Hostname for which to revoke the certificate")

  flag.StringVar(&disabledMetrics, "disabled-metrics", "", "Comma-separated list of telemetry metrics to disable")
	
  flag.StringVar(&conf, "conf", "", "Caddyfile to load (default \""+caddy.DefaultConfigFile+"\")")
	flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
	flag.BoolVar(&printEnv, "env", false, "Enable to print environment variables")
	flag.StringVar(&envFile, "envfile", "", "Path to file with environment variables to load in KEY=VALUE format")
	
  flag.BoolVar(&fromJSON, "json-to-caddyfile", false, "From JSON stdin to Caddyfile stdout")
	flag.BoolVar(&toJSON, "caddyfile-to-json", false, "From Caddyfile stdin to JSON stdout")
	flag.BoolVar(&plugins, "plugins", false, "List installed plugins")
	flag.StringVar(&logfile, "log", "", "Process log file")
	flag.IntVar(&logRollMB, "log-roll-mb", 100, "Roll process log when it reaches this many megabytes (0 to disable rolling)")
	flag.BoolVar(&logRollCompress, "log-roll-compress", true, "Gzip-compress rolled process log files")
	flag.StringVar(&caddy.PidFile, "pidfile", "", "Path to write pid file")
	flag.BoolVar(&caddy.Quiet, "quiet", false, "Quiet mode (no initialization output)")
	flag.StringVar(&serverType, "type", "http", "Type of server to run")
	flag.BoolVar(&version, "version", false, "Show version")
	flag.BoolVar(&validate, "validate", false, "Parse the Caddyfile but do not start the server")

	caddy.RegisterCaddyfileLoader("flag", caddy.LoaderFunc(confLoader))
	caddy.SetDefaultCaddyfileLoader("default", caddy.LoaderFunc(defaultLoader))
}
複製代碼

而後咱們來看 Run() 函數 json

conf 的靈活設置

conf 用來設置 Caddyfile 的文件路徑,是能夠從 Stdin 即終端輸入配置的。
會在如下兩個在 init() 中的函數調用更改promise

caddy.RegisterCaddyfileLoader("flag", caddy.LoaderFunc(confLoader))
	caddy.SetDefaultCaddyfileLoader("default", caddy.LoaderFunc(defaultLoader))
複製代碼

而注意到這裏使用的 Loader ,能夠用來自定義 caddyfile 的裝載方式。服務器

// confLoader loads the Caddyfile using the -conf flag.
func confLoader(serverType string) (caddy.Input, error) {
	if conf == "" {
		return nil, nil
	}

	if conf == "stdin" {
		return caddy.CaddyfileFromPipe(os.Stdin, serverType)
	}

	var contents []byte
	if strings.Contains(conf, "*") {
		// Let caddyfile.doImport logic handle the globbed path
		contents = []byte("import " + conf)
	} else {
		var err error
		contents, err = ioutil.ReadFile(conf)
		if err != nil {
			return nil, err
		}
	}

	return caddy.CaddyfileInput{
		Contents:       contents,
		Filepath:       conf,
		ServerTypeName: serverType,
	}, nil
}

複製代碼

注意到這裏返回的 caddy.Input 類型,它表明 caddyfile 在程序中的 structureapp

Log

log 設置 日誌 輸出在什麼地方,log-roll-mb log-roll-compress 是設置 log 文件大小限制,達到限制的時候會放棄舊的日誌。還有 文件的壓縮選項函數

// Set up process log before anything bad happens
	switch logfile {
	case "stdout":
		log.SetOutput(os.Stdout)
	case "stderr":
		log.SetOutput(os.Stderr)
	case "":
		log.SetOutput(ioutil.Discard)
	default:
		if logRollMB > 0 {
			log.SetOutput(&lumberjack.Logger{
				Filename:   logfile,
				MaxSize:    logRollMB,
				MaxAge:     14,
				MaxBackups: 10,
				Compress:   logRollCompress,
			})
		} else {
			err := os.MkdirAll(filepath.Dir(logfile), 0755)
			if err != nil {
				mustLogFatalf("%v", err)
			}
			f, err := os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
			if err != nil {
				mustLogFatalf("%v", err)
			}
			// don't close file; log should be writeable for duration of process
			log.SetOutput(f)
		}
	}

複製代碼

證書處理

咱們看前 8 個 flag 選項,都是設定 TLS 配置的。使用的是 CertMagic ,同時做者也把它放出來能夠被其餘的 Go 語言程序集成,若是想要爲本身的 Go 程序添加 TLS 加密傳輸,就使用它吧。他還支持自動續期,自己是使用的 ACME 客戶端集成。
自己大部分不須要設置,會使用默認設置幫助你啓用 HTTPS 。
介紹一下 revoke 選項,這裏會調用 CertMagic 的 Certificate revocation (please, only if private key is compromised)來撤銷證書ui

// Check for one-time actions
	if revoke != "" {
		err := caddytls.Revoke(revoke)
		if err != nil {
			mustLogFatalf("%v", err)
		}
		fmt.Printf("Revoked certificate for %s\n", revoke)
		os.Exit(0)
	}
複製代碼

telemetry

而後是 disabled-metrics 選項,用來關閉一些不須要的 遙測指標
注意到這裏的 initTelemetry() 函數,其中會使用 這裏輸入的選項進行遙測的關閉。this

// initialize telemetry client
	if EnableTelemetry {
		err := initTelemetry()
		if err != nil {
			mustLogFatalf("[ERROR] Initializing telemetry: %v", err)
		}
	} else if disabledMetrics != "" {
		mustLogFatalf("[ERROR] Cannot disable specific metrics because telemetry is disabled")
	}

複製代碼

而後會在 Run() 的最後部分進行相應遙測模塊的 設置加密

// Begin telemetry (these are no-ops if telemetry disabled)
	telemetry.Set("caddy_version", module.Version)
	telemetry.Set("num_listeners", len(instance.Servers()))
	telemetry.Set("server_type", serverType)
	telemetry.Set("os", runtime.GOOS)
	telemetry.Set("arch", runtime.GOARCH)
	telemetry.Set("cpu", struct {
		BrandName  string `json:"brand_name,omitempty"`
		NumLogical int    `json:"num_logical,omitempty"`
		AESNI      bool   `json:"aes_ni,omitempty"`
	}{
		BrandName:  cpuid.CPU.BrandName,
		NumLogical: runtime.NumCPU(),
		AESNI:      cpuid.CPU.AesNi(),
	})
	if containerized := detectContainer(); containerized {
		telemetry.Set("container", containerized)
	}
	telemetry.StartEmitting()
複製代碼

環境設置

而後是環境變量的設置 envenvfile,分別是打印出環境信息和能夠設置環境變量文件的位置

// load all additional envs as soon as possible
	if err := LoadEnvFromFile(envFile); err != nil {
		mustLogFatalf("%v", err)
	}

	if printEnv {
		for _, v := range os.Environ() {
			fmt.Println(v)
		}
	}
複製代碼

cpu

cpu 設定 cpu 使用限制

// Set CPU cap
	err := setCPU(cpu)
	if err != nil {
		mustLogFatalf("%v", err)
	}
複製代碼

分發事件

這是用來啓用插件的。

// Executes Startup events
	caddy.EmitEvent(caddy.StartupEvent, nil)
複製代碼

服務器類型

type 設定 caddy 啓動的服務器類型,這是由於 caddy 能啓動的服務器不少,只要知足了 Server 的接口就可使用 caddy。
這裏調用的 LoadCaddyfile 能夠認爲是 Caddy 配置最重要的部分。

// Get Caddyfile input
	caddyfileinput, err := caddy.LoadCaddyfile(serverType)
	if err != nil {
		mustLogFatalf("%v", err)
	}
複製代碼

pidfile 設置寫入 pid 文件的路徑, quiet 設置 caddy 不輸出初始化信息的啓動 。

version 和 plugin 信息

version 用來顯示 caddy 版本,plugins 用來列出已經安裝的 插件

if version {
		if module.Sum != "" {
			// a build with a known version will also have a checksum
			fmt.Printf("Caddy %s (%s)\n", module.Version, module.Sum)
		} else {
			fmt.Println(module.Version)
		}
		os.Exit(0)
	}
	if plugins {
		fmt.Println(caddy.DescribePlugins())
		os.Exit(0)
	}

複製代碼

validate 驗證 caddyfile 文件

validate 是驗證 caddyfile 的可行性,他會檢測 caddyfile 可是不會啓動一個新的 Server。注意到在這裏調用了 os.Exit(0) 退出。

if validate {
		err := caddy.ValidateAndExecuteDirectives(caddyfileinput, nil, true)
		if err != nil {
			mustLogFatalf("%v", err)
		}
		msg := "Caddyfile is valid"
		fmt.Println(msg)
		log.Printf("[INFO] %s", msg)
		os.Exit(0)
	}
複製代碼

caddyfile 與 JSON

json-to-caddyfilecaddyfile-to-json 從 JSON 文件中讀取 caddyfile 的配置文件,或者將 caddyfile 文件輸出爲 JSON 格式。

// Check if we just need to do a Caddyfile Convert and exit
	checkJSONCaddyfile()
複製代碼

根據 flag 選項進行 Caddyfile  和 JSON 直接的轉換。

func checkJSONCaddyfile() {
	if fromJSON {
		jsonBytes, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Read stdin failed: %v", err)
			os.Exit(1)
		}
		caddyfileBytes, err := caddyfile.FromJSON(jsonBytes)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Converting from JSON failed: %v", err)
			os.Exit(2)
		}
		fmt.Println(string(caddyfileBytes))
		os.Exit(0)
	}
	if toJSON {
		caddyfileBytes, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Read stdin failed: %v", err)
			os.Exit(1)
		}
		jsonBytes, err := caddyfile.ToJSON(caddyfileBytes)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Converting to JSON failed: %v", err)
			os.Exit(2)
		}
		fmt.Println(string(jsonBytes))
		os.Exit(0)
	}
}
複製代碼

Start

啓動服務器

// Start your engines
	instance, err := caddy.Start(caddyfileinput)
	if err != nil {
		mustLogFatalf("%v", err)
	}
複製代碼

總結

看完 caddy 的 run 函數,瞭解了程序的啓動配置了哪些東西,相應的會有一些問題。帶着這些問題繼續看源碼,可以深刻了解 caddy 內的邏輯。

  • caddy.Input 的內在讀取 caddyfile 的操做
  • Loader 能夠看出也是能夠自定義的,他實際涉及到 Caddy 較爲重要的 接收配置,安裝配置的操做部分。它會讀取 token 給 plugin 配置時 給 controller 消費,這裏多的名詞是來自於 caddy 的配置部分
  • caddy.EmitEvent 是怎樣啓動各 caddy 插件的
  • caddy.ServerType 除了 HTTP 還有什麼(插件會對應相應的服務器的)
  • instance 能夠看出是 caddy 服務器的實例,它具體的設置邏輯是什麼來得到 插件裝配的能力的。他是怎樣做爲服務器服務的
  • 遙測模塊的接入
  • 其餘的就是一些方便的選項了。

拓展閱讀

caddy(一)源碼全覽
caddy(二)自定義插件
caddy(三)caddy-grpc 一個插件實例

相關文章
相關標籤/搜索