Caddyソース読解(一)Run詳細
16906 ワード
Caddyソース読解(一)Run詳細
前言
今回のシリーズでは、caddyのライフサイクル全体にわたるソースコードについて説明します.
通常caddyはバイナリ配布ファイルを使用していますが、caddyのRun関数を分析します.最外層の論理から見て何をしたのか.
Caddy Run
Caddy Runにどのようなパッケージと操作が導入されているかを見てみましょう.Caddyの全体的な動作の概要
confの柔軟な設定
Log
最初の8つのflagオプションを見て、TLS構成を設定しています.CertMagicを使用していますが、作者もそれを他のGo言語プログラムに統合できるようにしています.自分のGoプログラムにTLS暗号化伝送を追加したいなら、それを使用しましょう.また、ACMEクライアント統合を使用している自動継続もサポートしています.それ自体はほとんど設定する必要はありません.デフォルト設定を使用してHTTPSを有効にします.証明書を取り消すためにCertMagicのCertificate revocation(please,only if private key is compromised)を呼び出すrevokeオプションについて説明します.
次に、
次に、環境変数の設定
これはプラグインを有効にするために使用されます.
versionとplugin情報
サーバの起動
caddyのrun関数を見て、プログラムの起動構成がどのようなものなのか、それに応じていくつかの問題があります.これらの問題を持ってソースコードを見続け、caddy内の論理を深く理解することができます. caddy.Inputの内部読取caddyfileの動作 Loaderもカスタマイズ可能であることがわかります.彼は実際にCaddyの重要な受信構成、インストール構成の操作部分に関連しています.tokenがpluginに構成するときにcontrollerに消費されることを読み出します.ここで多くの名詞はcaddyの構成部分 から来ています. caddy.EmitEventは、各caddyプラグインの をどのように起動するか caddy.ServerType HTTP以外に何がありますか(プラグインは対応するサーバに対応します) instanceはcaddyサーバの例であり、プラグインのアセンブリ能力を得るために具体的な設定ロジックが何であるかを示すことができる.彼はどのようにサーバーサービスとして 遠隔測定モジュールのアクセス(caddy 2は既に使用されていない) 他にも便利な選択肢があります.
アタッチメント
Run()関数ソース
Caddy(二)起動フローCaddyソース読解(三)Caddyfile解析by Loader&ParserCaddyソース読解(四)Pluggin&ControllerインストールプラグインCaddyソース読解(五)Instance&Server caddy-plugins(一)カスタムプラグインcaddy-plugins(二)caddy-grpcプラグインの一例
前言
今回のシリーズでは、caddyのライフサイクル全体にわたるソースコードについて説明します.
通常caddyはバイナリ配布ファイルを使用していますが、caddyのRun関数を分析します.最外層の論理から見て何をしたのか.
Caddy Run
Caddy Runにどのようなパッケージと操作が導入されているかを見てみましょう.Caddyの全体的な動作の概要
caddy/caddymain/run.go
まずinit関数を見てみましょう.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()
関数Run()関数の全文を見てみましょう.confの柔軟な設定
conf
Caddyfileのファイルパスを設定するために使用され、Stdinすなわち端末から構成を入力することができる.次の2つのinit()の関数で変更が呼び出されます. 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のプログラム内のstructureを表します.Log
log
ログ出力がどこにあるかを設定し、log-roll-mb
log-roll-compress
ログファイルサイズ制限を設定し、制限に達したときに古いログを放棄します.ファイルの圧縮オプションもあります // 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を有効にします.証明書を取り消すためにCertMagicのCertificate revocation(please,only if private key is compromised)を呼び出すrevokeオプションについて説明します.
// Check for one-time actions
if revoke != "" {
err := caddytls.Revoke(revoke)
if err != nil {
mustLogFatalf("%v", err)
}
fmt.Printf("Revoked certificate for %s
", revoke)
os.Exit(0)
}
telemetry 次に、
disabled-metrics
オプションで、不要な遠隔測定指標を閉じるために使用されます.ここのinitTelemetry()関数に注意してください.ここで入力したオプションを使用して遠隔測定のクローズを行います.// 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()
環境設定次に、環境変数の設定
env
、envfile
があり、それぞれ環境情報が印刷され、環境変数ファイルが設定可能な場所である// 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が起動できるサーバが多く、サーバのインタフェースを満たす限り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)
", 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を検出しますが、新しいサーバは起動しません.ここで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とJSONjson-to-caddyfile
およびcaddyfile-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内の論理を深く理解することができます.
アタッチメント
Run()関数ソース
// Run is Caddy's main() function.
func Run() {
flag.Parse()
module := getBuildModule()
cleanModVersion := strings.TrimPrefix(module.Version, "v")
caddy.AppName = appName
caddy.AppVersion = module.Version
certmagic.UserAgent = appName + "/" + cleanModVersion
// 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)
}
}
// 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)
}
}
// 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")
}
// Check for one-time actions
if revoke != "" {
err := caddytls.Revoke(revoke)
if err != nil {
mustLogFatalf("%v", err)
}
fmt.Printf("Revoked certificate for %s
", revoke)
os.Exit(0)
}
if version {
if module.Sum != "" {
// a build with a known version will also have a checksum
fmt.Printf("Caddy %s (%s)
", module.Version, module.Sum)
} else {
fmt.Println(module.Version)
}
os.Exit(0)
}
if plugins {
fmt.Println(caddy.DescribePlugins())
os.Exit(0)
}
// Check if we just need to do a Caddyfile Convert and exit
checkJSONCaddyfile()
// Set CPU cap
err := setCPU(cpu)
if err != nil {
mustLogFatalf("%v", err)
}
// Executes Startup events
caddy.EmitEvent(caddy.StartupEvent, nil)
// Get Caddyfile input
caddyfileinput, err := caddy.LoadCaddyfile(serverType)
if err != nil {
mustLogFatalf("%v", err)
}
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)
}
// Log Caddy version before start
log.Printf("[INFO] Caddy version: %s", module.Version)
// Start your engines
instance, err := caddy.Start(caddyfileinput)
if err != nil {
mustLogFatalf("%v", err)
}
// 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()
// Twiddle your thumbs
instance.Wait()
}
拡張読書Caddy(二)起動フローCaddyソース読解(三)Caddyfile解析by Loader&ParserCaddyソース読解(四)Pluggin&ControllerインストールプラグインCaddyソース読解(五)Instance&Server caddy-plugins(一)カスタムプラグインcaddy-plugins(二)caddy-grpcプラグインの一例