Kubernetes 中使用consul-template渲染配置

Kubernetes 中使用consul-template渲染配置

當前公司使用consul來實現服務發現,如Prometheue配置中的target和alertmanager註冊都採用了consul服務發現的方式,以此來靈活應對服務的變更。但對於其他服務,是否也有一個通用的方式來使用consul管理配置文件?本文中描述如何使用consul-template來渲染配置文件。

使用方式

consul-template是hashicorp開發的一個模板渲染工具,它採用了Go template語法。可以將其配置爲守護進程模式,watch consul服務的變動,並將變動後的服務渲染到配置文件中。會雖然名字中帶了consul,但它還可以對 VaultNomad 進行渲染。

簡單使用方式如下,首先要創建一個模板in.tpl,在渲染時通過-template指定模板(in.tpl)和渲染結果(out.txt):

$ consul-template -consul-addr=<consul-address>:<consul-port> -template "in.tpl:out.txt"

SSL方式

生產環境中的consul通常會啓用ssl和ACL配置,這樣在連接consul的時候需要提供CA證書和token。命令行使用方式如下:

$ consul-template -log-level debug -consul-addr==<consul-address>:<consul-port> -consul-token=<token> -consul-ssl  -consul-ssl-verify=false -consul-ssl-ca-cert=<ca.crt> -template "in.tpl:out.txt"

也可以使用-consul-ssl-ca-path-consul-token-file分別指定CA證書和token文件的路徑。

獲取ca證書

官方提供了一個名爲consul-k8s的工具來獲取consul的CA證書,並提供了容器鏡像,使用方式如下:

$ consul-k8s-control-plane get-consul-client-ca -output-file=/tmp/tls.crt -server-addr=<consul-address> -server-port=<consul-port> 

可以將consul-k8s配置爲一個initcontainer,這樣在consul-template啓動之前就可以獲取到ca證書:

  initContainers:
    - command:
        - /bin/sh
        - '-ec'
        - |
          consul-k8s-control-plane get-consul-client-ca \
            -output-file=/consul/tls/client/ca/tls.crt \
            -server-addr=<consul-address> \
            -server-port=<consul-port> \
      image: docker pull hashicorp/consul-k8s-control-plane:0.36.0
      imagePullPolicy: IfNotPresent
      name: get-auto-encrypt-client-ca
      resources:
        limits:
          cpu: 50m
          memory: 50Mi
        requests:
          cpu: 50m
          memory: 50Mi
      volumeMounts:
        - mountPath: /consul/tls/client/ca
          name: consul-auto-encrypt-ca-cert
 volumes:
    - emptyDir:
        medium: Memory
      name: consul-auto-encrypt-ca-cert
獲取token

連接consul所使用的token可以以secret的形式部署在kubernetes集羣中,可以通過vault注入等方式來避免token泄露。

整個處理方式如下圖所示:

image
配置文件方式

上面通過命令行的方式(-template "in.tpl:out.txt")指定了模版和渲染結果,但這種方式只適用於渲染單個模板,如果需要渲染多個模板,可以採用配置文件的方式。

配置文件語法採用的是hcl,包含三部分:服務端(ConsulVaultNomad)、TemplatesModes,以及可選字段。服務端主要用於配置到服務端(Consul、Vault和Nomad)的連接;Templates可以指定多個模板(source)和渲染結果(destination);Modes用於配置consul-template的運行模式,通過once mode可以配置爲非守護進程模式,通過exec mode可以啓動額外的子進程。Modes字段可選,默認是守護進程模式。

配置文件的例子如下:

consul {
  address = "127.0.0.1:8500"

  auth {
    enabled = true
    username = "test"
    password = "test"
  }
}

log_level = "warn"

template {
  contents = "{{key \"hello\"}}"
  destination = "out.txt"
  exec {
    command = "cat out.txt"
  }
}

後續就可以通過consul-template -config <config>的方式運行。

編寫模板

consul-template使用的Go template的語法,除此之外,它還提供了豐富的內置方法,用於支持Consul(文章中搜索關鍵字Query Consul )、Vault(文章中搜索關鍵字Query Vault )和Nomad(文章中搜索關鍵字Query Nomad ),以及一些公共函數(如trimregexMatchreplaceAll等)。

模板語法中比較重要的兩點:

  • 在模板文本中,一切動態的內容和判斷代碼塊均使用 {{}} 包括起來,在 {{}} 之外的文本均會被原封不動地拷貝到輸出中
  • 爲了方便格式化模板源代碼,還額外提供了 {{--}} 兩種語法,可以將代碼塊前或代碼塊後的空白字符均移除。空白字符包括空格符、換行符、回車符、水平製表符。

舉例

下面是logstash的output配置,用於將logstash處理的消息發送到elasticsearch.hosts中。如果hosts中的節點發生變動(如擴縮容),此時就需要聯動修改logstash的配置:

output {
    elasticsearch {
        hosts => ['dev-logging-elkclient000001.local:9200', 'dev-logging-elkclient000002.local:9200', 'elkclient000003.local:9200']
        index => "logstash-infra-%{es_index}"
        resurrect_delay => 2
        retry_max_interval => 30
        sniffing => false
        action => "create"
    }
}

爲了避免上述修改,可以通過consul-template對該配置進行渲染(當然前提是hosts中的節點都已經註冊到了Consul中)。

{{- $nodes := "" }}
{{ range service "elasticsearch" }} {{- $node := .Node }} {{- $port := .Port }} 
{{ if $node | regexMatch "(dev|prd)-logging-elkclient.*" }}
{{ if eq $nodes "" }}
{{$nodes = (printf "'%s:%d'" $node $port)}}
{{else}}
{{$nodes = (printf "%s,'%s:%d'" $nodes $node $port)}}
{{- end -}}
{{- end -}}
{{- end }}

output {
    elasticsearch {
        hosts => [{{$nodes}}]
        index => "logstash-%{es_index}"
        resurrect_delay => 2
        retry_max_interval => 30
        sniffing => false
        action => "create"
    }
}
  1. 首先定義一個變量$nodes,用於保存最終的結果
  2. 遍歷consul的service elasticsearch,獲取Node字段(如dev-logging-elkclient000001.local)和Port字段(本例中只有9200)
  3. 通過內置方法regexMatchelasticsearch的節點中過濾所需的節點
  4. 通過printf方法拼接字符串,並將結果保存到$nodes
  5. 最後在output.elasticsearch.hosts中使用上面的結果$nodes即可,由於$nodes是動態輸出,因此需要加上雙大括號{{$nodes}}

參考

Tips

  • 有時候一個文件因爲要經過多個服務的渲染而添加了多個模板,例如先使用vault注入secrets,再使用consul注入services。這樣就會導致vault在處理時候會嘗試解析consul的模板,但由於vault缺少連接consul所需的配置,會導致vault一直嘗試連接consul。可以通過將其他服務的模版作爲raw string的方式規避該問題,這樣在vault解析模板的時候就會輸出consul的模板:

    {{- $consulTemplate := `
    {{- $nodes := "" }}
    {{ range service "elasticsearch" }} {{- $node := .Node }} {{- $port := .Port }} 
    {{ if $node | regexMatch "(dev|prd)-logging-elkclient.*" }}
    {{ if eq $nodes "" }}
    {{$nodes = (printf "'%s:%d'" $node $port)}}
    {{else}}
    {{$nodes = (printf "%s,'%s:%d'" $nodes $node $port)}}
    {{- end -}}
    {{- end -}}
    {{- end }}
    
    output {
        elasticsearch {
            hosts => [{{$nodes}}]
            index => "logstash-%{es_index}"
            resurrect_delay => 2
            retry_max_interval => 30
            sniffing => false
            action => "create"
        }
    }
    ` }}
    
    {{ $consulTemplate }}
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章