EFK 스택 설치 · 13 min read · Dec 18, 2025
Ubuntu 22.04에 Elasticsearch, Fluentd 및 Kibana (EFK) 로깅 스택 설치하는 방법

로그 모니터링 및 분석은 서버 또는 컨테이너 인프라의 필수적인 부분이며 복잡한 애플리케이션을 처리할 때 유용합니다. 인기 있는 로깅 솔루션 중 하나는 Elasticsearch, Fluentd 및 Kibana (EFK) 스택입니다. 튜토리얼을 진행하기 전에 스택의 구성 요소에 대해 알아보겠습니다.
Elasticsearch는 전체 텍스트 검색 및 분석을 허용하는 실시간 분산형 확장 가능한 검색 엔진입니다. 대량의 데이터를 인덱싱하고 검색하는 데 사용됩니다. Kibana와 함께 배포되는 경우가 많으며, Kibana는 Elasticsearch를 위한 강력한 데이터 시각화 대시보드입니다. Kibana를 사용하면 Elasticsearch 로그 데이터를 탐색하고 대시보드 및 쿼리를 작성하여 애플리케이션에 대한 통찰력을 얻을 수 있습니다. Fluentd는 로그 데이터를 수집, 변환 및 Elasticsearch 백엔드로 전송합니다.
이 튜토리얼에서는 Ubuntu 22.04 머신에서 Docker를 사용하여 EFK 스택을 설치하고 Fluentd를 사용하여 필터링 및 변환한 후 컨테이너 로그를 Kibana로 전송합니다.
전제 조건
- 최소 6GB의 RAM을 가진 Ubuntu 22.04를 실행하는 서버.
- sudo 권한이 있는 비루트 사용자.
- 간단한 방화벽(UFW)이 활성화되어 실행 중입니다.
kibana.example.com과 같은 서버를 가리키는 완전한 도메인 이름(FQDN).- 모든 것이 업데이트되었습니다.
$ sudo apt update && sudo apt upgrade
1단계 - 방화벽 구성
패키지를 설치하기 전에 첫 번째 단계는 HTTP 및 HTTPS 연결을 허용하도록 방화벽을 구성하는 것입니다.
방화벽 상태를 확인합니다.
$ sudo ufw status
다음과 같은 내용을 볼 수 있어야 합니다.
상태: 활성
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
HTTP 및 HTTPS 포트를 허용합니다.
$ sudo ufw allow http
$ sudo ufw allow https
확인을 위해 상태를 다시 확인합니다.
$ sudo ufw status
상태: 활성
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:
# 배포는 빌드 프로세스 중에 자동으로 생성되는 사용자 정의 이미지를 사용합니다.
fluentd:
build: ./fluentd
volumes:
- ./fluentd/conf:/fluentd/etc
links: # 수신 로그를 Elasticsearch 컨테이너로 전송합니다.
- elasticsearch
depends_on:
- elasticsearch
ports: # 로그 집계를 위해 TCP 및 UDP 프로토콜 모두에서 포트 24224를 노출합니다.
- 24224:24224
- 24224:24224/udp
elasticsearch:
image: elasticsearch:8.7.1
expose:
- 9200
environment:
- discovery.type=single-node # 단일 노드로 실행됩니다.
- xpack.security.enabled=false
volumes: # Elasticsearch 데이터를 esdata Docker 볼륨에 로컬로 저장합니다.
- esdata:/usr/share/elasticsearch/data
kibana:
image: kibana:8.7.1
links: # Kibana 서비스를 Elasticsearch 컨테이너에 연결합니다.
- elasticsearch
depends_on:
- elasticsearch
ports:
- 5601:5601
environment: # 호스트 구성을 정의합니다.
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
# Elasticsearch 컨테이너를 위한 esdata라는 Docker 볼륨을 정의합니다.
volumes:
esdata:
파일을 저장하려면 Ctrl + X를 누르고 프롬프트가 나타나면 Y를 입력합니다. Fluentd, Elasticsearch 및 Kibana 각각에 대해 세 가지 서비스를 시작하도록 구성했습니다.
Fluentd의 경우, 준비된 이미지 대신 컨테이너를 빌드할 것입니다. Fluentd의 빌드 파일은 다음 단계에서 설정됩니다. 우리는 빌드 파일을 위한 디렉토리를 마운트하고 구성 파일을 위한 볼륨을 마운트했으며 로그 집계를 위해 TCP 및 UDP 프로토콜 모두에서 포트 24224를 노출했습니다.
다음 서비스는 Elasticsearch이며, 이 튜토리얼을 작성할 당시 사용 가능한 최신 버전을 사용하고 있습니다. 우리는 포트 9200을 통해 노출하고, 단일 노드 클러스터로 실행할 수 있도록 몇 가지 환경 변수를 설정했으며, 보안 기능을 비활성화했습니다. 이는 일반적으로 권장되지 않지만 보안을 활성화하는 것은 이 튜토리얼의 범위를 벗어납니다. 또한 Elasticsearch 데이터에 대한 로컬 볼륨을 마운트했습니다.
마지막으로 Kibana를 구성하고 대시보드에 접근하기 위해 포트 5601을 통해 노출합니다. Elasticsearch에 접근하기 위해 Kibana가 사용할 호스트를 구성하는 변수를 설정했습니다.
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
다음 코드를 붙여넣습니다.
# IP 0.0.0.0에서 Fluentd 바인딩
# 포트 24224
@type forward
port 24224
bind 0.0.0.0
# Elasticsearch에 로그 전송
# 호스트는 Elasticsearch
# 컨테이너 서비스와 일치해야 합니다.
@type copy
@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
@type stdout
파일을 저장하려면 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
다음 명령을 사용하여 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로 설정되어 있는지 확인합니다. 소스 필드는 자동으로 업데이트됩니다. Save data view to Kibana 버튼을 클릭하여 데이터 뷰 생성을 완료합니다.
다음으로 상단 메뉴(ellipsis)를 클릭하고 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 인증서를 설치하는 것입니다. SSL 인증서를 생성하기 위해 Certbot을 설치해야 합니다. Ubuntu의 리포지토리를 사용하여 Certbot을 설치하거나 Snapd 도구를 사용하여 최신 버전을 가져올 수 있습니다. 우리는 Snapd 버전을 사용할 것입니다.
Ubuntu 22.04에는 기본적으로 Snapd가 설치되어 있습니다. 다음 명령을 실행하여 Snapd 버전이 최신인지 확인합니다.
$ sudo snap install core && sudo snap refresh core
Certbot을 설치합니다.
$ sudo snap install --classic certbot
다음 명령을 사용하여 Certbot 명령이 /usr/bin 디렉토리에서 실행될 수 있도록 심볼릭 링크를 생성합니다.
$ 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 서비스의 환경 섹션 아래에 SERVER_PUBLICBASEURL=https://kibana.example.com 줄을 붙여넣습니다.
environment: # 호스트 구성 정의
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- SERVER_PUBLICBASEURL=https://kibana.example.com
파일을 저장하려면 Ctrl + X를 누르고 프롬프트가 나타나면 Y를 입력합니다.
컨테이너를 중지하고 제거합니다.
$ docker compose down --remove-orphans
업데이트된 구성으로 컨테이너를 다시 시작합니다.
$ docker compose up -d
Kibana 대시보드는 원하는 곳에서 https://kibana.example.com URL을 통해 접근할 수 있어야 합니다.
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
Welcome to nginx!
Welcome to nginx!
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.
For online documentation and support please refer to
nginx.org.
Commercial support is available at
nginx.com.
Thank you for using nginx.
또는 브라우저에서 URL http://을 열면 다음 페이지를 볼 수 있습니다.

Kibana 대시보드를 열고 왼쪽 사이드바 메뉴에서 Discover 링크를 클릭합니다. 상단 메뉴에서 + 기호를 클릭하여 Add filter 팝업을 표시합니다.

드롭다운에서 container_name 필드를 선택하고 연산자로 is를 선택한 후 필드 값으로 컨테이너 이름(nginx-fluentd-test)을 입력합니다.

Add filter 버튼을 클릭하여 Nginx 컨테이너의 데이터를 시각화합니다.

결론
이로써 Ubuntu 22.04 머신에 Elasticsearch, Fluentd 및 Kibana (EFK) 로깅 스택을 설치하는 튜토리얼이 마무리되었습니다. 질문이 있으시면 아래 댓글에 남겨주세요.
새 게시물을 받은 편지함에서 받기
스팸은 없습니다. 언제든지 구독 해지 가능합니다.