本页内容
- 先决条件
- 第 1 步 - 配置防火墙
- 第 2 步 - 安装 Docker 和 Docker Compose
- 第 3 步 - 创建 Docker Compose 文件
- 第 4 步 - 配置 Fluentd 构建文件
- 第 5 步 - 运行 Docker 容器
- 第 6 步 - 配置 Kibana
- 第 7 步 - 安装 Nginx
- 第 8 步 - 安装 SSL
- 第 9 步 - 配置 Nginx
- 第 10 步 - 使用 Fluentd 日志驱动运行 Docker 容器
- 结语
日志监控与分析是服务器或容器基础设施的重要组成部分,在处理复杂应用时尤为有用。流行的日志解决方案之一是 Elasticsearch、Fluentd 和 Kibana(EFK)技术栈。在深入了解本教程之前,让我们先了解一下该技术栈的组成部分。
Elasticsearch 是一个实时、分布式且可扩展的搜索引擎,支持全文搜索和分析。它用于对大量数据进行索引和检索。它通常与 Kibana 一同部署,Kibana 是 Elasticsearch 强大的数据可视化仪表盘。Kibana 允许你探索 Elasticsearch 的日志数据,并构建仪表盘和查询以深入了解你的应用。Fluentd 负责收集、转换日志数据并将其发送至 Elasticsearch 后端。
在本教程中,我们将在 Ubuntu 22.04 机器上使用 Docker 安装 EFK 技术栈,并在使用 Fluentd 过滤和转换容器日志后,将其发送至 Kibana。
先决条件
- 一台运行 Ubuntu 22.04 的服务器,至少具备 6GB 内存。
- 一个具有 sudo 权限的非 root 用户。
- uncomplicated Firewall(UFW)已启用并正在运行。
- 一个指向该服务器的完全限定域名(FQDN),例如 kibana.example.com 。
- 所有软件包均已更新。$ sudo apt update && sudo apt upgrade
一台运行 Ubuntu 22.04 的服务器,至少具备 6GB 内存。
一个具有 sudo 权限的非 root 用户。
uncomplicated Firewall(UFW)已启用并正在运行。
一个指向该服务器的完全限定域名(FQDN),例如 kibana.example.com 。
所有软件包均已更新。
$ sudo apt update && sudo apt upgrade
第 1 步 - 配置防火墙
在安装任何软件包之前,第一步是配置防火墙以允许 HTTP 和 HTTPS 连接。
检查防火墙的状态。
$ sudo ufw status
你应该会看到类似下面的输出。
Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6)
允许 HTTP 和 HTTPS 端口。
$ sudo ufw allow http
$ sudo ufw allow https
再次检查状态以确认。
$ sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
80/tcp ALLOW Anywhere
443 ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
80/tcp (v6) ALLOW Anywhere (v6)
443 (v6) ALLOW Anywhere (v6)
第 2 步 - 安装 Docker 和 Docker Compose
添加 Docker 的官方 GPG 密钥。
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker.gpg
运行以下命令以添加 Docker 仓库。
$ echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
更新系统以加入 Docker 仓库。
$ sudo apt update
安装 Docker 和 Docker Compose 插件。
$ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
本教程将使用 Docker Compose v2 插件,而非旧版的二进制文件。因此,运行命令已从 docker-compose 改为 docker compose,本文中均体现这一点。
Docker 以较高权限运行,因此你需要频繁使用 sudo 来执行命令。更好的做法是将你的 Linux 用户账户加入到 docker 用户组。
$ sudo usermod -aG docker ${USER}
${USER} 变量会获取当前登录的系统账户。如果你不是以想要赋予权限的用户登录的,请将 ${USER} 替换为该用户名。
要使新的用户组成员身份生效,请注销服务器后重新登录,或者使用以下命令。系统会提示你输入该用户的密码。
$ su - ${USER}
第 3 步 - 创建 Docker Compose 文件
首先,创建 EFK 项目的目录。
$ mkdir ~/efk
切换到该目录。
$ cd ~/efk
创建并打开 docker-compose.yml 文件进行编辑。
$ nano docker-compose.yml
在其中粘贴以下代码。
services:
# Deploy using the custom image automatically be created during the build process.
fluentd:
build: ./fluentd
volumes:
- ./fluentd/conf:/fluentd/etc
links: # Sends incoming logs to the elasticsearch container.
- elasticsearch
depends_on:
- elasticsearch
ports: # Exposes the port 24224 on both TCP and UDP protocol for log aggregation
- 24224:24224
- 24224:24224/udp
elasticsearch:
image: elasticsearch:8.7.1
expose:
- 9200
environment:
- discovery.type=single-node # Runs as a single-node
- xpack.security.enabled=false
volumes: # Stores elasticsearch data locally on the esdata Docker volume
- esdata:/usr/share/elasticsearch/data
kibana:
image: kibana:8.7.1
links: # Links kibana service to the elasticsearch container
- elasticsearch
depends_on:
- elasticsearch
ports:
- 5601:5601
environment: # Defined host configuration
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
# Define the Docker volume named esdata for the Elasticsearch container.
volumes:
esdata:
按 Ctrl + X 保存文件,并在提示时输入 Y 确认。我们已配置启动三个服务,分别为 Fluentd、Elasticsearch 和 Kibana 各一个。
对于 Fluentd,我们将构建容器而非使用现成镜像。Fluentd 的构建文件将在下一步中设置。我们已为其构建文件挂载了一个目录,并为配置文件挂载了卷,同时开放了 TCP 和 UDP 两种协议的 24224 端口用于日志聚合。
下一个服务是 Elasticsearch,我们使用的是撰写本教程时可用的最新版本。我们已通过 9200 端口将其开放,并设置了若干环境变量,以便以单节点集群方式运行,同时禁用了安全功能。通常不建议这样做,但启用安全功能超出了本教程的范围。我们还为 Elasticsearch 数据挂载了本地卷。
最后,我们配置 Kibana 并通过 5601 端口开放,该端口将用于访问仪表盘。我们还设置了一个变量来配置供其访问的 Elasticsearch 主机。
第 4 步 - 配置 Fluentd 构建文件
创建 Fluentd 及配置目录。
$ mkdir fluentd/conf -p
运行 tree 命令以验证目录结构。
$ tree
它应当类似下面的样子。

切换到 Fluentd 目录。
$ cd fluentd
创建并打开 Dockerfile 进行编辑。
$ nano Dockerfile
在其中粘贴以下代码。这段代码拉取 Fluentd Debian Docker 镜像,并安装用于 Elasticsearch 的 Fluentd 插件。
# fluentd/Dockerfile FROM fluent/fluentd:v1.16-debian-1 USER root RUN ["gem", "install", "fluent-plugin-elasticsearch", "--no-document", "--version", "5.3.0"] USER fluent
按 Ctrl + X 保存文件,并在提示时输入 Y 确认。
切换到配置目录。
$ cd conf
创建并打开 fluentd.conf 文件进行编辑。
$ nano fluentd.conf
在其中粘贴以下代码。
# bind fluentd on IP 0.0.0.0
# port 24224
<source>
@type forward
port 24224
bind 0.0.0.0
</source>
# sendlog to the elasticsearch
# the host must match to the elasticsearch
# container service
<match *.**>
@type copy
<store>
@type elasticsearch_dynamic
hosts elasticsearch:9200
logstash_format true
logstash_prefix fluentd
logstash_dateformat %Y%m%d
include_tag_key true
tag_key @log_name
include_timestamp true
flush_interval 30s
</store>
<store>
@type stdout
</store>
</match>
按 Ctrl + X 保存文件,并在提示时输入 Y 确认。
上面的 source 指令使用 forward 插件,该插件将 Fluentd 变成一个 TCP 端点以接受 TCP 数据包。
match 指令查找带有匹配标签的事件,在此处表示匹配所有事件。我们将使用 elasticsearch_dynamic 插件进行存储,该插件允许动态指定配置值。hosts 字段指定了 Elasticsearch 应用的主机名,即 Docker Compose 文件中的服务名。logstash_format 设置为 true,意味着 Fluentd 使用 logstash-%Y.%m.%d 这一惯例命名格式。写入事件的索引前缀名称设置为 fluend。include_tag_key 设置为 true,会以 JSON 格式添加 Fluentd 标签。tag_key 是用于提取标签的字段名。将变量 include_timestamp 设置为 true 会为日志添加一个时间戳字段。flush_interval 指定了数据刷写的间隔。我们还使用了 stdout 插件将事件/日志打印到标准输出。
第 5 步 - 运行 Docker 容器
切换回 EFK 目录。
$ cd ~/efk
使用以下命令启动容器。
$ docker compose up -d
检查运行中容器的状态。
$ docker ps
b3780c311154 efk-fluentd "tini -- /bin/entryp…" 9 seconds ago Up 8 seconds 5140/tcp, 0.0.0.0:24224->24224/tcp, 0.0.0.0:24224->24224/udp, :::24224->24224/tcp, :::24224->24224/udp efk-fluentd-1
5a48f0a9ade1 kibana:8.7.1 "/bin/tini -- /usr/l…" 9 seconds ago Up 7 seconds 0.0.0.0:5601->5601/tcp, :::5601->5601/tcp efk-kibana-1
dab3a0ab0312 elasticsearch:8.7.1 "/bin/tini -- /usr/l…" 9 seconds ago Up 8 seconds 9200/tcp, 9300/tcp efk-elasticsearch-1
你也可以使用以下命令来实现。
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
efk-elasticsearch-1 elasticsearch:8.7.1 "/bin/tini -- /usr/l…" elasticsearch 37 seconds ago Up 36 seconds 9200/tcp, 9300/tcp
efk-fluentd-1 efk-fluentd "tini -- /bin/entryp…" fluentd 37 seconds ago Up 36 seconds 5140/tcp, 0.0.0.0:24224->24224/tcp, 0.0.0.0:24224->24224/udp, :::24224->24224/tcp, :::24224->24224/udp
efk-kibana-1 kibana:8.7.1 "/bin/tini -- /usr/l…" kibana 37 seconds ago Up 36 seconds 0.0.0.0:5601->5601/tcp, :::5601->5601/tcp
运行以下命令检查 EFK 构建过程的日志。
$ docker logs efk-fluentd-1
$ docker logs efk-kibana-1
$ docker logs efk-elasticsearch-1
检查 Elasticsearch 容器。它会打印出该容器的详细配置。
$ docker inspect efk-elasticsearch-1
你可以注意到分配给该容器的 IP 地址。
[
{
"Id": "dab3a0ab03120d3a7192045e1ea84fdd0f8fdb7819cc6d6780e05109d61e0b66",
"Created": "2023-05-04T09:58:00.256169904Z",
"Path": "/bin/tini",
"Args": [
"--",
"/usr/local/bin/docker-entrypoint.sh",
"eswrapper"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 23619,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-05-04T09:58:00.563700803Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:59075530be34d3a06866f894ae9735f6d739a7a751ad45efb86dec3c9bd16836",
"ResolvConfPath": "/var/lib/docker/containers/dab3a0ab03120d3a7192045e1ea84fdd0f8fdb7819cc6d6780e05109d61e0b66/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/dab3a0ab03120d3a7192045e1ea84fdd0f8fdb7819cc6d6780e05109d61e0b66/hostname",
"HostsPath": "/var/lib/docker/containers/dab3a0ab03120d3a7192045e1ea84fdd0f8fdb7819cc6d6780e05109d61e0b66/hosts",
"LogPath": "/var/lib/docker/containers/dab3a0ab03120d3a7192045e1ea84fdd0f8fdb7819cc6d6780e05109d61e0b66/dab3a0ab03120d3a7192045e1ea84fdd0f8fdb7819cc6d6780e05109d61e0b66-json.log",
"Name": "/efk-elasticsearch-1",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "docker-default",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "efk_default",
"PortBindings": {},
"RestartPolicy": {
"Name": "",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"ConsoleSize": [
0,
0
],
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "private",
"Dns": null,
"DnsOptions": null,
"DnsSearch": null,
"ExtraHosts": [],
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": null,
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": null,
"DeviceCgroupRules": null,
"DeviceRequests": null,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": null,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"Mounts": [
{
"Type": "volume",
"Source": "efk_esdata",
"Target": "/usr/share/elasticsearch/data",
"VolumeOptions": {}
}
],
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/ee03648cf34e03601848b1769569b4d3bb7192db118102ca050215ba87060bbf-init/diff:/var/lib/docker/overlay2/51d6cfcb59e473a3f163e68984a1ba1325a2c816ed7925c4dffdefcf2e104d11/diff:/var/lib/docker/overlay2/b9c096454bda31f1cb2ea33f108be8b29b2e94827ebe94cc17563eb596b7cab1/diff:/var/lib/docker/overlay2/effe604c5b015ba02cf3b7a238bd3ff5dad7970a72e689ef5275fcf03fd0bcd1/diff:/var/lib/docker/overlay2/72fbf23251467ea2f6af8d9458c7fdd8fa3ef716eeafd9319ceff59d07d96788/diff:/var/lib/docker/overlay2/02094ec9e4ebb04371f782744a3a46852a00bf6fd7e8820d466a3576aeb9d5fc/diff:/var/lib/docker/overlay2/ce364cdd636b67e10c879aa152360d965d08fe456663ed8fbe78c3bd37bde6c7/diff:/var/lib/docker/overlay2/33bf44b475ea5ea249845b7eed75ded47dd9dc7877b9231fa4195b4753071945/diff:/var/lib/docker/overlay2/4f19bd8089599ef879075012c710ec464d8e0446fc0a0813850657dddd23a5dc/diff:/var/lib/docker/overlay2/a39a61b12d8565c6d5b33c17a04d47c8bd47609a787e0548fbac0d47d00eecc8/diff:/var/lib/docker/overlay2/cbd9d77eb9ed6b600511f9a676aab511d2aa2b3dbd18d5403559699558546996/diff",
"MergedDir": "/var/lib/docker/overlay2/ee03648cf34e03601848b1769569b4d3bb7192db118102ca050215ba87060bbf/merged",
"UpperDir": "/var/lib/docker/overlay2/ee03648cf34e03601848b1769569b4d3bb7192db118102ca050215ba87060bbf/diff",
"WorkDir": "/var/lib/docker/overlay2/ee03648cf34e03601848b1769569b4d3bb7192db118102ca050215ba87060bbf/work"
},
"Name": "overlay2"
},
"Mounts": [
{
"Type": "volume",
"Name": "efk_esdata",
"Source": "/var/lib/docker/volumes/efk_esdata/_data",
"Destination": "/usr/share/elasticsearch/data",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
"Config": {
"Hostname": "dab3a0ab0312",
"Domainname": "",
"User": "elasticsearch:root",
"AttachStdin": false,
"AttachStdout": true,
"AttachStderr": true,
"ExposedPorts": {
"9200": {},
"9200/tcp": {},
"9300/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"xpack.security.enabled=false",
"discovery.type=single-node",
"PATH=/usr/share/elasticsearch/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"ELASTIC_CONTAINER=true"
],
"Cmd": [
"eswrapper"
],
"Image": "elasticsearch:8.7.1",
"Volumes": null,
"WorkingDir": "/usr/share/elasticsearch",
"Entrypoint": [
"/bin/tini",
"--",
"/usr/local/bin/docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"com.docker.compose.config-hash": "51c818791aa87ea7eccc389578c76ec4d596265eba8baefb8833bf5df13777e3",
"com.docker.compose.container-number": "1",
"com.docker.compose.depends_on": "",
"com.docker.compose.image": "sha256:59075530be34d3a06866f894ae9735f6d739a7a751ad45efb86dec3c9bd16836",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "efk",
"com.docker.compose.project.config_files": "/home/navjot/efk/docker-compose.yml",
"com.docker.compose.project.working_dir": "/home/navjot/efk",
"com.docker.compose.service": "elasticsearch",
"com.docker.compose.version": "2.17.3",
"org.label-schema.build-date": "2023-04-27T04:33:42.127815583Z",
"org.label-schema.license": "Elastic-License-2.0",
"org.label-schema.name": "Elasticsearch",
"org.label-schema.schema-version": "1.0",
"org.label-schema.url": "https://www.elastic.co/products/elasticsearch",
"org.label-schema.usage": "https://www.elastic.co/guide/en/elasticsearch/reference/index.html",
"org.label-schema.vcs-ref": "f229ed3f893a515d590d0f39b05f68913e2d9b53",
"org.label-schema.vcs-url": "https://github.com/elastic/elasticsearch",
"org.label-schema.vendor": "Elastic",
"org.label-schema.version": "8.7.1",
"org.opencontainers.image.created": "2023-04-27T04:33:42.127815583Z",
"org.opencontainers.image.documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/index.html",
"org.opencontainers.image.licenses": "Elastic-License-2.0",
"org.opencontainers.image.ref.name": "ubuntu",
"org.opencontainers.image.revision": "f229ed3f893a515d590d0f39b05f68913e2d9b53",
"org.opencontainers.image.source": "https://github.com/elastic/elasticsearch",
"org.opencontainers.image.title": "Elasticsearch",
"org.opencontainers.image.url": "https://www.elastic.co/products/elasticsearch",
"org.opencontainers.image.vendor": "Elastic",
"org.opencontainers.image.version": "8.7.1"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "bf47cd7764585766349085d35100611e086cf233fc9fc655c6eb9e086f1cd59a",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"9200/tcp": null,
"9300/tcp": null
},
"SandboxKey": "/var/run/docker/netns/bf47cd776458",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {
"efk_default": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"efk-elasticsearch-1",
"elasticsearch",
"dab3a0ab0312"
],
"NetworkID": "1bc8ac0185982b84a24a201852f2cddc0432a3ffff1a2bd4008074875f696cac",
"EndpointID": "e1c67199e679f350d1da47f0b1e208ec6a7767eb57d60f773ba08b88a6962dcf",
"Gateway": "172.23.0.1",
"IPAddress": "172.23.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:17:00:02",
"DriverOpts": null
}
}
}
}
]
如你所见,该容器获得了 172.23.0.2 作为 IP 地址。运行以下命令以验证 Elasticsearch 是否正常工作。
$ curl 172.23.0.2:9200
{
"name" : "dab3a0ab0312",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "gldMFBtQSxS5sL93rBAdzA",
"version" : {
"number" : "8.7.1",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "f229ed3f893a515d590d0f39b05f68913e2d9b53",
"build_date" : "2023-04-27T04:33:42.127815583Z",
"build_snapshot" : false,
"lucene_version" : "9.5.0",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}
第 6 步 - 配置 Kibana
既然 EFK 技术栈已经部署完成,现在可以配置 Kibana 了。在浏览器中打开 URL http://

点击 Explore on my own 按钮进入 Kibana 仪表盘。

点击 Stack Management 链接以设置 Kibana 数据视图。从左侧边栏选择 Kibana >> Data Views 选项,打开数据视图页面。

点击 Create data view 按钮继续。

输入数据视图名称和索引模式 fluentd-* 。确保 Timestamp 字段设置为 @timestamp 。source 字段会自动更新。点击 Save data view to Kibana 按钮完成数据视图的创建。
接下来,点击顶部菜单(省略号),再点击 Discover 选项以显示日志监控。

你会看到以下页面,确认你的配置运行正常。这些日志全部取自 Elasticsearch,并由 Fluentd 日志聚合发送。

第 7 步 - 安装 Nginx
Ubuntu 22.04 自带的 Nginx 版本较旧。你需要下载官方 Nginx 仓库以安装最新版本。
导入 Nginx 的签名密钥。
$ curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
添加 Nginx 稳定版的仓库。
$ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg arch=amd64] \
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
更新系统仓库。
$ sudo apt update
安装 Nginx。
$ sudo apt install nginx
验证安装。
$ nginx -v
nginx version: nginx/1.24.0
启动 Nginx 服务器。
$ sudo systemctl start nginx
第 8 步 - 安装 SSL
第一步是安装 Let's Encrypt 的 SSL 证书。我们需要安装 Certbot 来生成 SSL 证书。你既可以使用 Ubuntu 的仓库安装 Certbot,也可以通过 Snapd 工具获取最新版本。我们将使用 Snapd 版本。
Ubuntu 22.04 默认已安装 Snapd。运行以下命令以确保你的 Snapd 版本是最新的。
$ sudo snap install core && sudo snap refresh core
安装 Certbot。
$ sudo snap install --classic certbot
使用以下命令,通过创建指向 /usr/bin 目录的符号链接,确保 Certbot 命令可以运行。
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
为域名 kibana.example.com 生成 SSL 证书。
$ sudo certbot certonly --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m
[email protected]
-d kibana.example.com
上述命令会将证书下载到你服务器上的 /etc/letsencrypt/live/kibana.example.com 目录。
生成 Diffie-Hellman 组证书。
$ sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096
检查 Certbot 续期调度服务。
$ sudo systemctl list-timers
你会发现 snap.certbot.renew.service 是计划运行的服务之一。
NEXT LEFT LAST PASSED UNIT ACTIVATES ------------------------------------------------------------------------------------------------------------------------------------ Mon 2023-05-06 13:37:57 UTC 3h 45min left Mon 2023-05-01 07:20:42 UTC 2h 31min ago ua-timer.timer ua-timer.service Mon 2023-05-06 14:39:29 UTC 4h 47min left Sat 2023-02-04 16:04:18 UTC 2 months ago motd-news.timer motd-news.service Mon 2023-05-06 15:53:00 UTC 6h left n/a n/a snap.certbot.renew.timer snap.certbot.renew.service
对该过程进行一次干运行,以检查 SSL 续期是否正常工作。
$ sudo certbot renew --dry-run
如果你没有看到任何错误,说明一切就绪。你的证书将自动续期。
第 9 步 - 配置 Nginx
创建并打开用于 Kibana 的 Nginx 配置文件。
$ sudo nano /etc/nginx/conf.d/kibana.conf
在其中粘贴以下代码。将 IP 地址替换为你 Elasticsearch 服务器的私有 IP 地址。
server {
listen 80; listen [::]:80;
server_name kibana.example.com;
return 301 https://$host$request_uri;
}
server {
server_name kibana.example.com;
charset utf-8;
listen 443 ssl http2;
listen [::]:443 ssl http2;
access_log /var/log/nginx/kibana.access.log;
error_log /var/log/nginx/kibana.error.log;
ssl_certificate /etc/letsencrypt/live/kibana.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/kibana.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/kibana.example.com/chain.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
resolver 8.8.8.8;
ssl_stapling on;
ssl_stapling_verify on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
location / {
proxy_pass http://localhost:5601;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
按 Ctrl + X 保存文件,并在提示时输入 Y 确认。
打开 /etc/nginx/nginx.conf 文件进行编辑。
$ sudo nano /etc/nginx/nginx.conf
在 include /etc/nginx/conf.d/*.conf; 这一行之前添加以下行。
server_names_hash_bucket_size 64;
按 Ctrl + X 保存文件,并在提示时输入 Y 确认。
验证配置。
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
重启 Nginx 服务。
$ sudo systemctl restart nginx
还需要最后一步。打开 Docker Compose 文件进行编辑。
$ nano ~/docker-compose.yml
在 Kibana 服务下的 environment 部分添加如下一行 SERVER_PUBLICBASEURL=https://kibana.example.com 。
environment: # Defined host configuration
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- SERVER_PUBLICBASEURL=https://kibana.example.com
完成后按 Ctrl + X 保存文件,并在提示时输入 Y 确认。
停止并移除容器。
$ docker compose down --remove-orphans
使用更新后的配置重新启动容器。
$ docker compose up -d
你的 Kibana 仪表盘应当可以通过 URL https://kibana.example.com 在任何位置访问。
第 10 步 - 使用 Fluentd 日志驱动运行 Docker 容器
现在,我们将运行一个带有 Fluentd 日志驱动器的 Docker 容器,使其自动将日志发送到该技术栈。我们将使用 Nginx 容器进行测试。
从 Docker Hub 仓库拉取 Nginx 镜像。我们使用 alpine 版本,因为它是体积最小的镜像。
$ docker pull nginx:alpine
运行以下命令以创建并启动 Nginx 容器。我们将日志驱动设置为 Fluentd,并将端口设为 8080,因为默认的 80 端口已被代理模式下的 Nginx 服务器占用。
$ docker run --name nginx-fluentd-test -d --log-driver=fluentd -p 8080:80 nginx:alpine
检查容器状态。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
038c43e4e1a3 nginx:alpine "/docker-entrypoint.…" 12 seconds ago Up 11 seconds 0.0.0.0:8080->80/tcp, :::8080->80/tcp nginx-fluentd-test
a94ca706bd0c efk-fluentd "tini -- /bin/entryp…" 8 hours ago Up 8 hours 5140/tcp, 0.0.0.0:24224->24224/tcp, 0.0.0.0:24224->24224/udp, :::24224->24224/tcp, :::24224->24224/udp efk-fluentd-1
0cf04a446425 kibana:8.7.1 "/bin/tini -- /usr/l…" 8 hours ago Up 8 hours 0.0.0.0:5601->5601/tcp, :::5601->5601/tcp efk-kibana-1
7c7ad8f9b123 elasticsearch:8.7.1 "/bin/tini -- /usr/l…" 8 hours ago Up 8 hours 9200/tcp, 9300/tcp efk-elasticsearch-1
运行以下命令访问 Nginx 容器并生成访问日志。
$ curl localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
或者,你可以在浏览器中打开 URL http://

打开 Kibana 仪表盘,点击左侧边栏菜单中的 Discover 链接。点击顶部菜单中的 + 号,弹出 Add filter 对话框。

从下拉列表中选择 container_name 字段,以 is 作为运算符,并将容器名称(nginx-fluentd-test)填入字段值。

点击 Add filter 按钮以可视化来自 Nginx 容器的数据。

结语
至此,我们关于在 Ubuntu 22.04 机器上安装 Elasticsearch、Fluentd 和 Kibana(EFK)日志技术栈的教程就结束了。如果你有任何疑问,请在下方评论中留言。