在Docker环境下的kafka部署之三:ELK和filebeat

整体架构

现在可以基于Docker环境来构建一个完整的日志处理系统了,整体架构如下:

客户端 互联网 日志服务端
logfile => filebeat => SSL => kafka => logstash => ES => kibana

其中的日志处理部分就是以前说过的《用Docker+ELK集中处理日志》。

除去这部分,剩下的关键问题就是跨互联网的filebeat到kafka之间的连接。

生成证书

因为如架构所示,kafka是一个典型的同时需要支持内外网连接的情况:外网通过SSL接受来自filebeat的推送,内网对接logstash提供日志内容。

所以大致上可以基于前一篇的方式进行配置,但是因为filebeat的SSL配置与kafka客户端略有不同,所以证书的生成方面有少许不同,结合《 Setting Filebeat 5 with Kafka 0.10 over TLS》和《 TLS for Filebeat Kafka Output》及原来的证书脚本,重写了一个证书生成脚本如下:

#!/bin/bash
BASE_DIR=.  # 保存路径
DAYS_VALID=3650  # 证书有效期
PASSWORD=12345678  # 证书密码
NAME=domain_name  # 域名
DEPT=devops  # 部门名
COMPANY=your_company  # 公司名
CITY=Xiamen  # 城市
PROVINCE=Fujian  # 省份
COUNTRY=CN  # 国家

CERT_DIR="$BASE_DIR/ca"
SERVER_DIR="$BASE_DIR/secrets"
CLIENT_DIR="$BASE_DIR/client"
CA_CERT_NAME="$CLIENT_DIR/ca.crt"
CA_KEY_NAME="$CERT_DIR/ca.key"
PWD_NAME="$SERVER_DIR/password"
SERVER_KEYSTORE="$SERVER_DIR/server.keystore.jks"
SERVER_TRUSTSTORE="$SERVER_DIR/server.truststore.jks"
SERVER_CSR="$CERT_DIR/server.csr"
SERVER_CERT="$CERT_DIR/server.crt"
CLIENT_KEY="$CLIENT_DIR/client.key"
CLIENT_CSR="$CERT_DIR/client.csr"
CLIENT_CERT="$CLIENT_DIR/client.crt"
SUBJ="/C=$COUNTRY/ST=$PROVINCE/L=$CITY/O=$COMPANY/OU=$DEPT/CN=$NAME"
DNAME="CN=$NAME, OU=$DEPT, O=$COMPANY, L=$CITY, ST=$PROVINCE, C=$COUNTRY"

mkdir -p $CERT_DIR
mkdir -p $SERVER_DIR
mkdir -p $CLIENT_DIR
rm $CERT_DIR/*
rm $SERVER_DIR/*
rm $CLIENT_DIR/*

echo "1. Generate CA certificate and key..."
openssl req -new -x509 -keyout $CA_KEY_NAME -out $CA_CERT_NAME -days $DAYS_VALID \
    -passin pass:"$PASSWORD" -passout pass:"$PASSWORD" -subj "$SUBJ"

echo ""
echo "2. Generate server key store..."
keytool -genkey -keyalg RSA -keystore $SERVER_KEYSTORE -alias $NAME \
    -keysize 2048 -validity $DAYS_VALID -storepass $PASSWORD -keypass $PASSWORD \
    -dname "$DNAME"

echo ""
echo "3. Export server certificate signing request..."
keytool -certreq -keystore $SERVER_KEYSTORE -alias $NAME \
    -file $SERVER_CSR  -storepass $PASSWORD -keypass $PASSWORD -noprompt

echo ""
echo "4. Sign server certificate by CA..."
openssl x509 -req -CAcreateserial -CA $CA_CERT_NAME -CAkey $CA_KEY_NAME \
    -in $SERVER_CSR -out $SERVER_CERT -days $DAYS_VALID -passin pass:$PASSWORD

echo ""
echo "5. Import CA to server key store..."
keytool -import -keystore $SERVER_KEYSTORE -alias CARoot -file $CA_CERT_NAME \
    -storepass $PASSWORD -keypass $PASSWORD -noprompt

echo ""
echo "6. Import server certificate to server key store..."
keytool -import -keystore $SERVER_KEYSTORE -alias $NAME -file $SERVER_CERT \
    -storepass $PASSWORD -keypass $PASSWORD -noprompt

echo ""
echo "7. Import CA to server trust store..."
keytool -import -keystore $SERVER_TRUSTSTORE -alias CARoot -file $CA_CERT_NAME \
    -storepass $PASSWORD -keypass $PASSWORD -noprompt

echo ""
echo "8. Generate client key and certificate request..."
openssl req -nodes -new -keyout $CLIENT_KEY -out $CLIENT_CSR -days $DAYS_VALID \
    -subj "$SUBJ"

echo ""
echo "9. Sign client certificate by CA..."
openssl x509 -req -CAcreateserial -CA $CA_CERT_NAME -CAkey $CA_KEY_NAME \
    -in $CLIENT_CSR -out $CLIENT_CERT -days $DAYS_VALID -passin pass:$PASSWORD

echo ""
echo "10. Generate password file..."
echo "$PASSWORD" > $PWD_NAME
rm .srl

echo ""
echo "####### Done. #######"
echo "Following files were generated"
echo "Server password file: $PWD_NAME"
echo "Server java keystore: $SERVER_KEYSTORE"
echo "Server java truststore: $SERVER_TRUSTSTORE"
echo "Signed Client cert: $CLIENT_CERT"
echo "Client RSA private key: $CLIENT_KEY"
echo "Client PEM truststore: $CA_CERT_NAME"

按注释修改配置内容为你自己的相关信息,然后运行这个脚本即可生成所需要的所有证书。

需要注意的是:

OpenSSL 1.1.0f和1.1.0g生成的证书在ELK 6.4.1中使用会有问题,说是不是PKCS8格式,但OpenSSL又会提示说你现在用的是PKCS8,建议更新为PKCS12格式。按《SSL communication fails btw Filebeat (5.1.1) and Logstash (5.1.1)》所说,用如下命令转换后的结果与未转换是一样的,可见本来就是PKCS8。

openssl pkcs8 -in privatekey.key -topk8 -nocrypt -out privatekey.p8

所以保险起见还是用旧版一点的OpenSSL,比如1.0.2x。

如果不想重装系统中的OpenSSL,可以使用我的一个image:raptor/jdkssl,内置了所需要的keytool和openssl,生成的证书经实践是可用的。

用法如下:

docker pull raptor/jdkssl
docker run -it --rm -v /path_to_script:/root/bin raptor/jdkssl
# cd /root/bin
# ./setup_certs.sh
# exit

其中setup_certs.sh就是上面那个证书生成脚本。

配置kafka

基本配置方式如前一篇文章所说,docker-compose.yml如下:

version: '2'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper
    container_name: zookeeper
    mem_limit: 1024M
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
  kafka:
    image: confluentinc/cp-kafka
    container_name: kafka
    mem_limit: 1024M
    depends_on:
      - zookeeper
    ports:
      - 9093:9093
    volumes:
      - /home/raptor/path_to/secrets:/etc/kafka/secrets
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_LISTENERS: PLAINTEXT://:9092,SSL://:9093
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,SSL://domain_name:9093
      KAFKA_SSL_KEY_CREDENTIALS: password
      KAFKA_SSL_KEYSTORE_FILENAME: server.keystore.jks
      KAFKA_SSL_KEYSTORE_CREDENTIALS: password
      KAFKA_SSL_CLIENT_AUTH: required
      KAFKA_SSL_TRUSTSTORE_FILENAME: server.truststore.jks
      KAFKA_SSL_TRUSTSTORE_CREDENTIALS: password
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_HEAP_OPTS: "-Xmx512M -Xms16M"

注意其中的配置项,文件名根据新的脚本生成证书修改,然后用docker-compose up -d kafka启动即可。

启动完成后最好用consumer和producer测试一下,确保配置成功再进行下一步。

配置ELK

ELK的配置包括两个方面:一个是输入源配置为kafka,另一个是过滤时需要转换格式。

kafka的输入配置如下:

input {
  kafka {
    bootstrap_servers => "kafka:9092"
    topics => ["logstash"]
    consumer_threads => 1
    auto_offset_reset => "latest"
  }
}

很简单,通过内网不加密方式连接kafka,主题为logstash(这个可以自己任意命名),单线程,从最后开始取。

过滤配置则需要在filter的最前面加上一个转换:

filter {
    json {
        source => "message"
    }
... # 原来的过滤配置
}

这是因为filebeat发来的数据会被编码为JSON再存到kafka里,logstash读出来就是一个放在message字段里的字符串,需要用json插件解析到root,恢复成原本的格式供logstash解析。

最后把ELK也加到前面那个docker-compose.yml里:

...
  elk:
    image: sebp/elk
    container_name: elk
    depends_on:
      - kafka
    ports:
      - 127.0.0.1:5601:5601
      - 127.0.0.1:9200:9200
    volumes:
      - /home/raptor/path_to_logstash/conf.d:/etc/logstash/conf.d
      - /home/raptor/path_to_elk/data:/var/lib/elasticsearch

然后用docker-compose up -d elk启动。因为是通过内网连接kafka,也没有使用加密,一般没什么问题是很容易配通的。

配置filebeat

这个配置是关键点,搞了很久才搞通,当然,大部分坑在上面已经说过了,这里就说一下结论。

filebeat的配置中,主要就是output部分:

output.kafka:
    hosts: ["domain_name:9093"]
    topic: 'logstash'
    worker: 1
    ssl:
        certificate_authorities: "/etc/pki/tls/ca.crt"
        certificate: "/etc/pki/tls/client.crt"
        key: "/etc/pki/tls/client.key"

其中域名domain_name必须与kafka证书保持一致,并且解析到kafka的外网IP(如果本地测试,可以修改hosts文件实现),logstash是主题名,保持与ELK中的配置一致。剩下的就是三个客户端证书文件,即前面那个脚本生成的client文件夹里的三个文件,把它们放到/etc/pki/tls(或你自己指定的其它文件夹,只要和这边的配置一致即可)下。

配置无误,service filebeat start即可。

推送到[go4pro.org]