Docker部署Elasticsearch SSL加密集群:TLS证书生成与Kibana对接实战

发布时间:2026/10/15 6:54:27
Docker部署Elasticsearch SSL加密集群:TLS证书生成与Kibana对接实战 1. 为什么单机跑ES还要折腾SSL证书很多人第一次接触Elasticsearch的时候都是在本地或者测试环境里裸奔——HTTP明文传输谁都能连图的就是一个省事。但只要你把ES往稍微正式一点的环境里放哪怕只是一台内网服务器安全扫描那一关就过不去。我见过太多项目在交付前一周被安全团队打回来原因就是ES的9200端口开着HTTP没有任何加密。这个问题的本质在于Elasticsearch从7.x版本开始安全功能已经内置在免费的基础许可里了。也就是说你不需要买白金版就能用上TLS加密和认证。但官方文档在这块的描述比较分散加上证书生成那套流程涉及不少概念很多人一看就头大干脆就跳过了。我写这篇东西的目的很直接用Docker把ES的SSL环境快速搭起来让你在十分钟内有一个能跑、能连、能验证的加密集群。不管你是要做本地开发联调还是要给测试环境加一层防护这套流程都能直接复用。涉及的关键词包括Docker部署、Elasticsearch安全配置、TLS证书生成、单节点集群、Kibana对接这些我都会一步步拆开讲。先说一下适用场景。如果你是在做日志收集、数据分析、或者搜索服务的开发需要本地有一套跟生产环境安全配置接近的ES实例那这篇就是给你写的。如果你只是想随便跑个Demo不在乎安全那确实没必要折腾证书直接关掉安全模块就行。但只要你需要模拟真实环境或者公司有合规要求那这套流程迟早要过一遍。我自己的经验是与其等到部署到服务器上再手忙脚乱地配证书不如一开始就在Docker里把安全环境搭好。这样开发阶段就能发现各种连接问题比如客户端证书验证失败、Kibana连不上、curl命令报SSL错误等等。这些问题在本地解决掉比在生产环境上排查要轻松得多。2. 证书生成这一步到底在做什么2.1 为什么不能直接用自签名的随便一个证书很多人觉得SSL证书不就是个文件嘛随便生成一个不就行了。但Elasticsearch对证书是有要求的不是随便一个自签名证书就能用。它需要证书里包含正确的Subject Alternative Name也就是SAN字段。这个字段决定了证书对哪些主机名或IP地址有效。如果你生成的证书里没有写SAN或者写的是localhost但你是通过IP访问的那连接的时候就会报证书验证失败。这个错误信息通常不太直观很多人会以为是证书本身有问题其实是SAN没配对。Elasticsearch提供了一个专门的证书工具叫elasticsearch-certutil这个工具封装了OpenSSL的很多复杂操作让你用几条命令就能生成符合要求的证书。我建议直接用这个工具不要自己去手撸OpenSSL命令除非你对证书体系非常熟悉。2.2 用elasticsearch-certutil生成CA和节点证书整个证书体系分两层一个CA根证书用来签发其他证书然后是各个节点或者服务的证书由CA签发。这样做的好处是你只需要把CA证书分发给所有需要通信的组件它们就能互相验证。具体操作上我习惯在Docker容器里直接跑certutil命令。先启动一个临时的ES容器然后进去生成证书再把证书拷出来。这样生成的证书跟ES版本是匹配的不会出现兼容性问题。# 启动一个临时容器用来生成证书 docker run --rm -it \ -v $(pwd)/certs:/usr/share/elasticsearch/config/certs \ docker.elastic.co/elasticsearch/elasticsearch:8.12.0 \ bash进去之后先生成CA# 生成CA证书 bin/elasticsearch-certutil ca \ --out config/certs/elastic-stack-ca.p12 \ --pass 这里我把密码设为空方便后续自动化处理。生产环境当然要设密码但本地开发环境图个方便空密码能省去很多交互步骤。接着用CA签发节点证书# 生成节点证书 bin/elasticsearch-certutil cert \ --ca config/certs/elastic-stack-ca.p12 \ --ca-pass \ --out config/certs/elastic-certificates.p12 \ --pass 这两条命令跑完certs目录下就有两个p12文件了。但p12格式在某些场景下不太方便比如Kibana配置或者curl命令验证的时候用PEM格式更直观。所以还需要把p12转成PEM。# 从p12提取CA证书 openssl pkcs12 -in config/certs/elastic-stack-ca.p12 \ -cacerts -nokeys -out config/certs/ca.crt -passin pass: # 从p12提取节点证书和私钥 openssl pkcs12 -in config/certs/elastic-certificates.p12 \ -nocerts -nodes -out config/certs/elastic-cert.key -passin pass: openssl pkcs12 -in config/certs/elastic-certificates.p12 \ -clcerts -nokeys -out config/certs/elastic-cert.crt -passin pass:这样你就得到了ca.crt、elastic-cert.crt和elastic-cert.key三个文件。后面配置ES和Kibana的时候都会用到。注意生成证书的时候一定要确认SAN字段包含了你要用的所有主机名和IP。如果后面发现漏了只能重新生成没法直接修改。2.3 证书文件的权限问题这里有个很容易踩的坑证书文件的权限。Elasticsearch在启动的时候会检查证书文件的权限如果权限太开放它会拒绝启动并报错。特别是在Docker环境里宿主机和容器内的用户ID可能不一致导致权限混乱。我的做法是在生成证书之后统一把证书文件的属主改成容器内ES进程的用户。在官方镜像里这个用户的UID通常是1000。你可以用chown命令改一下sudo chown -R 1000:1000 ./certs chmod 600 ./certs/*.key chmod 644 ./certs/*.crt私钥文件权限设成600证书文件设成644这是比较标准的做法。如果你在docker-compose里挂载了certs目录记得在启动前确认权限没问题否则ES会一直重启。3. 用Docker Compose编排ES和Kibana3.1 配置文件的结构设计我不太喜欢把所有配置都塞在docker-compose.yml的environment字段里那样文件会变得很长而且不好维护。更好的做法是把ES的配置抽到一个独立的elasticsearch.yml文件里然后通过volume挂载进去。目录结构大概是这样project/ ├── docker-compose.yml ├── certs/ │ ├── ca.crt │ ├── elastic-cert.crt │ └── elastic-cert.key ├── es-config/ │ └── elasticsearch.yml └── kibana-config/ └── kibana.yml这样每个组件的配置都是独立的改起来清楚也方便版本管理。3.2 elasticsearch.yml的关键配置项ES的配置文件里跟SSL相关的核心配置有这几项cluster.name: docker-es-cluster node.name: es-node-01 network.host: 0.0.0.0 discovery.type: single-node xpack.security.enabled: true xpack.security.transport.ssl.enabled: true xpack.security.transport.ssl.verification_mode: certificate xpack.security.transport.ssl.key: /usr/share/elasticsearch/config/certs/elastic-cert.key xpack.security.transport.ssl.certificate: /usr/share/elasticsearch/config/certs/elastic-cert.crt xpack.security.transport.ssl.certificate_authorities: /usr/share/elasticsearch/config/certs/ca.crt xpack.security.http.ssl.enabled: true xpack.security.http.ssl.key: /usr/share/elasticsearch/config/certs/elastic-cert.key xpack.security.http.ssl.certificate: /usr/share/elasticsearch/config/certs/elastic-cert.crt xpack.security.http.ssl.certificate_authorities: /usr/share/elasticsearch/config/certs/ca.crt这里有两个SSL配置块transport.ssl管的是节点之间的通信加密http.ssl管的是客户端到ES的通信加密。单节点环境下transport其实可以简化但为了跟多节点环境保持一致我还是把它配上。verification_mode设成certificate表示验证证书但不验证主机名。如果你设成full那就要求证书里的SAN必须跟实际访问的主机名完全匹配本地开发很容易在这里翻车。所以我建议本地环境用certificate模式生产环境再根据实际情况调整。3.3 docker-compose.yml的编排细节docker-compose文件里ES和Kibana的服务定义要处理好依赖关系和网络配置version: 3.8 services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0 container_name: es-node environment: - ES_JAVA_OPTS-Xms1g -Xmx1g - ELASTIC_PASSWORDyour_strong_password volumes: - ./es-config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml - ./certs:/usr/share/elasticsearch/config/certs - es-data:/usr/share/elasticsearch/data ports: - 9200:9200 - 9300:9300 networks: - es-net kibana: image: docker.elastic.co/kibana/kibana:8.12.0 container_name: kibana volumes: - ./kibana-config/kibana.yml:/usr/share/kibana/config/kibana.yml - ./certs:/usr/share/kibana/config/certs ports: - 5601:5601 depends_on: - elasticsearch networks: - es-net volumes: es-data: networks: es-net: driver: bridgeELASTIC_PASSWORD这个环境变量是用来设置elastic超级用户的密码的。如果你不设ES第一次启动的时候会自动生成一个随机密码打印在日志里。但那个密码找起来麻烦不如自己指定一个。内存方面ES默认会吃掉很多内存本地开发设成1G就够了。如果你机器内存大可以适当调大但不要超过物理内存的一半。3.4 kibana.yml的对接配置Kibana要连上带SSL的ES配置也不复杂server.host: 0.0.0.0 server.name: kibana elasticsearch.hosts: [https://elasticsearch:9200] elasticsearch.username: kibana_system elasticsearch.password: your_kibana_password elasticsearch.ssl.certificateAuthorities: [/usr/share/kibana/config/certs/ca.crt] elasticsearch.ssl.verificationMode: certificate这里有个关键点Kibana连接ES用的不是elastic用户而是kibana_system用户。这个用户的密码需要你手动设置。在ES启动之后用elastic用户执行一条命令来设置curl -X POST https://localhost:9200/_security/user/kibana_system/_password \ -u elastic:your_strong_password \ -H Content-Type: application/json \ -d {password:your_kibana_password} \ --cacert certs/ca.crt如果你忘了设这个密码Kibana启动后会一直报认证失败日志里能看到401错误。这个坑我踩过不止一次每次都是因为急着启动Kibana忘了设密码。4. 启动之后怎么验证环境真的通了4.1 用curl验证HTTPS连接环境起来之后第一件事是用curl确认ES的HTTPS端口能通curl -X GET https://localhost:9200 \ --cacert certs/ca.crt \ -u elastic:your_strong_password如果返回一个JSON里面有cluster_name和version信息说明HTTPS配置成功了。如果报SSL证书错误检查一下ca.crt的路径对不对以及证书是不是过期了。如果报401那就是密码不对。如果报连接拒绝检查容器是不是正常启动了端口映射有没有问题。4.2 检查集群健康状态和证书信息确认基本连接没问题之后可以看一下集群的健康状态curl -X GET https://localhost:9200/_cluster/health?pretty \ --cacert certs/ca.crt \ -u elastic:your_strong_password单节点集群的健康状态通常是yellow因为副本分片没法分配。这是正常的不用管它。如果你看到red那说明有分片分配失败了需要检查磁盘空间或者索引设置。还可以看一下SSL证书的具体信息curl -X GET https://localhost:9200/_ssl/certificates?pretty \ --cacert certs/ca.crt \ -u elastic:your_strong_password这个接口会返回当前使用的证书的详细信息包括颁发者、有效期、SAN字段等。可以用来确认证书是不是你期望的那张。4.3 Kibana的验证和常见报错Kibana启动之后打开浏览器访问http://localhost:5601应该能看到登录页面。用elastic用户登录如果能正常进入Kibana界面说明整条链路都通了。如果Kibana报Unable to connect to Elasticsearch先看Kibana的日志docker logs kibana --tail 50常见的错误有几种一是kibana_system密码没设或者设错了二是ca.crt路径不对三是ES还没完全启动Kibana就急着连了。第三种情况最常见因为ES启动比较慢Kibana又不会自动重试太多次。解决办法就是等ES完全启动之后再重启Kibana容器。提示docker-compose的depends_on只能保证容器启动顺序不能保证服务就绪。如果你经常遇到这个问题可以考虑用healthcheck配合depends_on的condition字段。5. 那些让我折腾半天的坑5.1 证书SAN字段缺失导致的连接失败这个问题我遇到过好几次每次都是因为生成证书的时候没注意SAN字段。具体表现是curl命令加--cacert能通但Java客户端或者Kibana连的时候报No subject alternative names present。原因就是证书里没有SAN扩展。elasticsearch-certutil默认生成的证书是不带SAN的你需要用--dns和--ip参数指定bin/elasticsearch-certutil cert \ --ca config/certs/elastic-stack-ca.p12 \ --ca-pass \ --out config/certs/elastic-certificates.p12 \ --pass \ --dns localhost \ --ip 127.0.0.1 \ --ip 172.18.0.2这里的IP地址要填你Docker网络里ES容器的实际IP。如果你不确定可以先启动容器用docker inspect查一下。或者干脆用--dns把可能用到的主机名都写上。5.2 文件权限不对导致ES拒绝启动这个坑在Linux环境下特别常见。你把证书文件从宿主机挂载到容器里宿主机上的文件属主可能是你的用户但容器里ES进程用的是UID 1000的用户。如果权限不匹配ES启动时会报Config file is not readable或者类似的错误。解决办法就是在启动之前把证书目录的属主改成1000sudo chown -R 1000:1000 ./certs如果你用的是Mac或者WindowsDocker Desktop的文件共享机制会处理好权限问题一般不会遇到这个坑。但如果你是在Linux服务器上部署这个步骤绝对不能省。5.3 内存不足导致的容器反复重启ES是个内存大户默认的JVM堆内存是1G加上Lucene的堆外内存一个ES容器至少要2G内存才能跑得比较稳。如果你在内存比较小的机器上跑比如2G内存的云主机ES很容易因为OOM被系统杀掉。表现就是容器启动之后跑一会儿就挂了docker ps看不到容器docker logs也只能看到被kill之前的日志。解决办法是在docker-compose里限制ES的JVM内存environment: - ES_JAVA_OPTS-Xms512m -Xmx512m同时确保你的机器至少有2G可用内存。如果实在不够可以考虑把Kibana停掉只跑ES。5.4 Kibana连不上ES的排查思路Kibana连不上ES的时候不要急着重启先按这个顺序排查确认ES的HTTPS端口能从Kibana容器内部访问到。可以进Kibana容器用curl试一下。检查kibana.yml里的elasticsearch.hosts地址是不是写对了。在Docker网络里应该用服务名而不是localhost。确认kibana_system用户的密码已经设置过了。检查ca.crt文件是不是挂载到了Kibana容器里路径跟配置里写的是不是一致。看Kibana日志里具体的错误信息是SSL握手失败还是认证失败这两种情况的处理方式完全不同。我自己的习惯是每次改完配置先不急着重启而是用docker exec进容器手动跑一下curl命令确认网络和证书都没问题再重启服务。这样能省很多来回折腾的时间。6. 把这套环境用起来的几个实际场景6.1 本地开发联调时的客户端配置如果你是用Java或者Python客户端连这套ES需要把CA证书配置到客户端里。以Python的elasticsearch库为例from elasticsearch import Elasticsearch es Elasticsearch( hosts[https://localhost:9200], basic_auth(elastic, your_strong_password), ca_certscerts/ca.crt, verify_certsTrue ) print(es.info())Java客户端的话需要把ca.crt导入到JVM的truststore里或者直接在代码里指定SSLContext。具体做法可以参考官方文档但核心就是让客户端信任你自建的CA。6.2 用这套环境测试索引生命周期管理有了安全环境之后你可以测试一些在裸奔环境下不太好模拟的场景。比如索引生命周期管理你可以创建一个策略让索引在达到一定大小或者时间之后自动滚动和删除。这个过程涉及到集群的很多内部操作在安全环境下测试能发现一些权限相关的问题。curl -X PUT https://localhost:9200/_ilm/policy/my_policy \ --cacert certs/ca.crt \ -u elastic:your_strong_password \ -H Content-Type: application/json \ -d { policy: { phases: { hot: { actions: { rollover: { max_size: 1gb } } }, delete: { min_age: 7d, actions: { delete: {} } } } } }6.3 模拟多节点集群的扩展路径虽然这篇讲的是单节点部署但证书体系是支持多节点的。你只需要用同一个CA签发多个节点证书然后在每个节点的配置里指定对应的证书文件就行了。transport.ssl的配置在多节点环境下会自动完成节点间的证书验证。如果你后面要扩展到多节点记得把discovery.type改成多播或者单播配置并且确保所有节点的证书都是由同一个CA签发的。否则节点之间会因为证书验证失败而无法组成集群。我自己的做法是在本地用Docker Compose起三个ES节点模拟一个最小规模的生产集群。这样可以在本地测试分片分配、副本同步、节点故障转移这些行为比单节点环境更接近真实情况。7. 关于证书轮换和长期维护的一点经验证书是有有效期的elasticsearch-certutil默认生成的证书有效期是三年。三年听起来很长但如果你在一个项目里长期使用这套环境迟早会遇到证书过期的问题。证书一过期整个集群就起不来了所有节点都会报证书验证失败。我的建议是在生成证书的时候就把有效期设长一点比如十年。虽然安全性上稍微打点折扣但省去了很多维护的麻烦。命令里加一个--days参数就行bin/elasticsearch-certutil cert \ --ca config/certs/elastic-stack-ca.p12 \ --ca-pass \ --out config/certs/elastic-certificates.p12 \ --pass \ --days 3650另外把生成证书的命令写成一个脚本放在项目里。这样下次需要重新生成证书的时候直接跑脚本就行不用再去翻文档回忆命令。脚本里把SAN字段、有效期、输出路径都写死保证每次生成的证书都是一致的。还有一点证书文件不要提交到代码仓库里。虽然自签名的证书泄露了风险也不大但这是个好习惯。把certs目录加到.gitignore里然后在README里写清楚怎么生成证书。新同事拉下代码之后跑一下脚本就能把环境搭起来。这套Docker加SSL的ES环境我从第一次折腾到现在大概经历了五六次重建。每次都是因为换了机器或者升级了版本需要重新配一遍。现在这套流程已经比较稳定了基本上十分钟内能搞定。希望这些经验能帮你少走点弯路。