はじめに #
Elastic Cloud on Kubernetes(ECK)を使っていると、物理機器やVMのlogなどECK外部のlogを収集したいことがあると思います。
この場合ECK内部のAgentは外部公開されていないので、ECK外部にAgentを構築 + Fleetに登録するのですが、非常に難しく様々なIssueが存在します。
- ECK外のAgentを登録する際に証明書の問題が起きるが、ドキュメントがないので混乱する旨のIssue
- FleetServerをIngressで公開した時にAgentがUnHealthyになる問題
- FleetServerをIngressで公開した時にAgentがUnHealthyになる原因について言及のあるIssue
今回はこの問題と、対応方法を紹介します。
1. 問題 #
なぜ外部のElastic AgentがECKのfleet-serverに登録できないかは、以下のIssueに原因が全て記載されています。
https://github.com/elastic/elastic-agent/issues/2762
2. 対応方法 #
原因に軽く触れつつ、解決方法を紹介します。
1. Ingressでfleet-serverを外部公開
外部のAgentがECKのfleet-serverにアクセスするためには、なんらかの手段でfleetを外部公開する必要があります。 この時ECK in production environmentにもあるように、ECKはTLSで公開することが推奨になるので、Ingressを使ってElasticSearch,Kibana,Fleetを公開するケースが多いと思います。
従ってIngressを使ってECKにHTTPSでアクセスできるようにします。
# SAMPLE (nginx ingress controller + cert-manager + let's encrypt)
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kibana-ingress
namespace: elastic-stack
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/backend-protocol: HTTPS
nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
ingressClassName: nginx
tls:
- hosts:
- "*.k8s.hirohirolab.com"
secretName: quickstart-example-tls # https://cert-manager.io/docs/tutorials/acme/nginx-ingress/
rules:
- host: elastic.k8s.hirohirolab.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: elasticsearch-es-http
port:
number: 9200
- host: kibana.k8s.hirohirolab.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kibana-kb-http
port:
number: 5601
- host: fleet.k8s.hirohirolab.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fleet-server-agent-http
port:
number: 8220
- path: /
)を選んでしまうと、redirectの関係から404 Not Found
になるようです。Github-issue: Document edge cases around Fleet/Agent setups with Ingress
2. Fleetの通信をIngress経由に変更
FleetをIngressで公開しましたが、defaultの設定ではhttps://elasticsearch-es-http.elastic-stack.svc:9200
とSerivce経由でアクセスするため、外部のAgentはアクセス不可になります。
従ってfleet-serverの通信をIngress経由に変更します。
- before
# https://github.com/elastic/cloud-on-k8s/blob/c25ae520dbba3d83231b96fb9e10dc081546ff39/deploy/eck-stack/examples/agent/fleet-agents.yaml#L40
xpack.fleet.agents.elasticsearch.hosts: ["https://elasticsearch-es-http.elastic-stack.svc:9200"]
xpack.fleet.agents.fleet_server.hosts: ["https://fleet-server-agent-http.elastic-stack.svc:8220"]
- after
# THIS IS A SAMPLE. INPUT YOUR FLEET AND ELASTIC URL.
xpack.fleet.agents.elasticsearch.hosts: ["https://elastic.k8s.hirohirolab.com"]
xpack.fleet.agents.fleet_server.hosts: ["https://fleet.k8s.hirohirolab.com"]
すると以下のようにIngress経由でのFleet通信に切り替わります。
3. Agentの証明書管理(問題)
これによりECK外部のAgentは登録できるようになりますが、今度はECK内部のAgentは以下のような証明書エラーになります。
この原因はECK内部のAgentは、ECKが生成した独自の自己証明書をFLEET_CA
に格納するためです。
root@v0-dev-03:~/project/kubernetes-ingress# kubectl exec -it eck-stack-with-fleet-eck-agent-agent-84thl -n elastic-stack -- /bin/bash
root@v0-k8s-03:/usr/share/elastic-agent# export | grep CA
declare -x FLEET_CA="/mnt/elastic-internal/fleetserver-association/elastic-stack/fleet-server/certs/ca.crt" # Insert By elastic-operator
fleet-serverをIngress経由のアクセスに変更したためFLEET_CA
の証明書ではなく、Ingressに登録した証明書でのアクセスになります。この結果、証明書検証ができずエラーになります。
4 FLEET_CA
, FLEET_URL
の上書き
上記解決にはFLEET_CA=""
と上書きをすれば、一般の証明書(Lets Encryptなど外部のCA認証済み)を解決できるようになります。公式doc参照
またFLEET_URL
もIngress経由のURLになってないので、変更する必要があります。
root@v0-k8s-03:/usr/share/elastic-agent# export | grep FLEET_URL
declare -x FLEET_URL="https://fleet-server-agent-http.elastic-stack.svc:8220"
これら環境変数はelastic-operator
によって設定されてしまいますが、公式docによると環境変数の上書きができるようです。
公式のhelmでは、daemonSet
の欄に追加する形になります。
# https://github.com/elastic/cloud-on-k8s/blob/c25ae520dbba3d83231b96fb9e10dc081546ff39/deploy/eck-stack/examples/agent/fleet-agents.yaml#L98
daemonSet:
podTemplate:
spec:
serviceAccountName: elastic-agent
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
automountServiceAccountToken: true
securityContext:
runAsUser: 0
# OVERRIDE ENV VARS
containers:
- name: agent
env:
- name: FLEET_CA
value: ""
- name: FLEET_URL
value: "https://fleet.k8s.hirohirolab.com" # THIS IS A SAMPLE. INPUT YOUR FLEET URL.
5. 完了
この状態でdeployすれば、以下のように環境変数が上書きされて展開され、statusもHEALTHYになります。
root@v0-dev-01:~/project/elasticsearch/cloud-on-k8s# kubectl exec -it eck-stack-with-fleet-eck-agent-agent-84thl -n elastic-stack -- /bin/bash
root@v0-k8s-01:/usr/share/elastic-agent# export | grep -E "FLEET_CA|FLEET_URL"
declare -x FLEET_CA=""
declare -x FLEET_URL="https://fleet.k8s.hirohirolab.com"
root@v0-k8s-01:/usr/share/elastic-agent# elastic-agent status
┌─ fleet
│ └─ status: (HEALTHY) Connected
└─ elastic-agent
└─ status: (HEALTHY) Running
おわりに #
これでECK外部のAgentも、ECK内部のAgentもIngress経由でfleet-serverと接続することができるようになりました。
TLS通信しなくてもOKだったり、Ingressを用意するのが大変な場合は、fleet-serverをLoadBalancerやNodePortで公開して、FLEET_INSECURE=1
で上書きしても解決できると思います。(未検証ですが)
おまけ #
Elastic AgentではなくBeats/LogstashでECK外部のlogを収集する場合は簡単で、公式のhelmにBeatsが外部公開されるexampleが既に用意されています。
# https://github.com/elastic/cloud-on-k8s/blob/c25ae520dbba3d83231b96fb9e10dc081546ff39/deploy/eck-stack/examples/logstash/basic-eck.yaml#L105C1-L114C31
services:
- name: beats
service:
spec:
type: ClusterIP
ports:
- port: 5044
name: "filebeat"
protocol: TCP
targetPort: 5044
またpurestorage(物理ストレージ)のSyslogをECKに転送する方法の紹介もあります。link