📎 Reference.
📌 Contents.
Docker 활용 ELK 구성 테스트
git clone
git clone https://github.com/deviantony/docker-elk.git
cd docker-elk
.env 파일에 패스워드 설정부분 참고
나의 경우, Nginx에 대한 컨테이너를 추가로 구성. 기본 페이지 접속시 Access 로그를 Logstash를 통해 Elasticsearch로 전달하는 구성으로 커스텀해보았다.
docker-compose.yml 변경점
version: '3.7'
services:
# The 'setup' service runs a one-off script which initializes users inside
# Elasticsearch — such as 'logstash_internal' and 'kibana_system' — with the
# values of the passwords defined in the '.env' file. It also creates the
# roles required by some of these users.
#
# This task only needs to be performed once, during the *initial* startup of
# the stack. Any subsequent run will reset the passwords of existing users to
# the values defined inside the '.env' file, and the built-in roles to their
# default permissions.
#
# By default, it is excluded from the services started by 'docker compose up'
# due to the non-default profile it belongs to. To run it, either provide the
# '--profile=setup' CLI flag to Compose commands, or "up" the service by name
# such as 'docker compose up setup'.
setup:
profiles:
- setup
build:
context: setup/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
init: true
volumes:
- ./setup/entrypoint.sh:/entrypoint.sh:ro,Z
- ./setup/lib.sh:/lib.sh:ro,Z
- ./setup/roles:/roles:ro,Z
environment:
ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
METRICBEAT_INTERNAL_PASSWORD: ${METRICBEAT_INTERNAL_PASSWORD:-}
FILEBEAT_INTERNAL_PASSWORD: ${FILEBEAT_INTERNAL_PASSWORD:-}
HEARTBEAT_INTERNAL_PASSWORD: ${HEARTBEAT_INTERNAL_PASSWORD:-}
MONITORING_INTERNAL_PASSWORD: ${MONITORING_INTERNAL_PASSWORD:-}
BEATS_SYSTEM_PASSWORD: ${BEATS_SYSTEM_PASSWORD:-}
networks:
- elk
depends_on:
- elasticsearch
- kibana
- logstash
elasticsearch:
build:
context: elasticsearch/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
volumes:
- ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro,Z
- elasticsearch:/usr/share/elasticsearch/data:Z
ports:
- 9200:9200
- 9300:9300
environment:
node.name: elasticsearch
ES_JAVA_OPTS: -Xms512m -Xmx512m
# Bootstrap password.
# Used to initialize the keystore during the initial startup of
# Elasticsearch. Ignored on subsequent runs.
ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
# Use single node discovery in order to disable production mode and avoid bootstrap checks.
# see: https://www.elastic.co/guide/en/elasticsearch/reference/current/bootstrap-checks.html
discovery.type: single-node
networks:
- elk
restart: unless-stopped
web:
image: nginx:latest
volumes:
- web-logs:/var/log/nginx:Z # Nginx 로그 디렉토리 공유
networks:
- elk
depends_on:
- logstash
restart: unless-stopped
ports:
- 8081:80
logstash:
build:
context: logstash/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
volumes:
- ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro,Z
- ./logstash/pipeline:/usr/share/logstash/pipeline:ro,Z
- ./logstash/input:/usr/share/logstash/input:rw,Z
- web-logs:/var/log/nginx:Z # 새로운 볼륨 추가
ports:
- 5044:5044
- 50000:50000/tcp
- 50000:50000/udp
- 9600:9600
environment:
LS_JAVA_OPTS: -Xms256m -Xmx256m
LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
networks:
- elk
depends_on:
- elasticsearch
restart: unless-stopped
kibana:
build:
context: kibana/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
volumes:
- ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml:ro,Z
ports:
- 5601:5601
environment:
KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
networks:
- elk
depends_on:
- elasticsearch
restart: unless-stopped
networks:
elk:
driver: bridge
volumes:
elasticsearch:
web-logs:
basic 라이센스 적용
xpack.license.self_generated.type: trial -> basic
elasticsearch.yml
수정된 logstash.conf
input {
beats {
port => 5044
}
tcp {
port => 50000
}
file {
path => "/usr/share/logstash/input/test-input.csv"
start_position => "beginning"
sincedb_path => "/usr/share/logstash/input/sincedb_file"
mode => "tail"
}
file {
path => "/var/log/nginx/host.access.log" # 새로운 로그 파일 경로
start_position => "beginning"
sincedb_path => "NUL"
}
}
output {
elasticsearch {
hosts => "elasticsearch:9200"
user => "elastic"
password => "${LOGSTASH_INTERNAL_PASSWORD}"
index => "test-input"
}
stdout {
codec => json_lines
}
}
docker-compose up -d
원래는 nginx access 로그를 활성화해야하는 내용까지 적용해야하겠지만, 일단 다이렉트로 nginx 컨테이너 접속 후 access 로그 활성화
docker exec -it docker-elk-web-1 /bin/bash
vi /etc/nginx/conf.d/default.conf
`===============================================
#access_log /var/log/nginx/host.access.log main;
===============================================`
# 위 내용 주석처리('#') 제거 후 저장
service nginx restart
이 후 localhost:8081 접속하여 host.access.log 파일에 액세스 로그 정상 확인.
localhost:5601 (키바나) 접속 → Stack Management → Index management 접속
test-input 인덱스 → console → GET test-input/_search?pretty
→ 액세스 로그 확인
✅ 테스트 성공
이제 위 테스트 경험을 바탕으로 다른 애플리케이션 Docker Compose에 결합 테스트할 차례. (Nginx Proxy Manager 관련 애플리케이션)
ELK 통합 완료
통합 시점 수정된 파일목록 일부

결합 후 수정된 파일 목록
주요 코드
▼ kibana.yml
server.name: kibana
server.host: 0.0.0.0
elasticsearch.hosts: [ http://elasticsearch:9200 ]
monitoring.ui.container.elasticsearch.enabled: true
monitoring.ui.container.logstash.enabled: true
elasticsearch.username: kibana_system
elasticsearch.password: ${KIBANA_SYSTEM_PASSWORD}
▼ logstash.conf
input {
file {
path => "/data/logs/modsec_audit.log"
start_position => "end"
sincedb_path => "/data/logs/.sincedb_modsec_audit"
type => "modsecurity-audit"
}
}
filter {
if [type] == "modsecurity-audit" {
grok {
match => {
"message" => "(?m)%{DATA:modsec_timestamp}.*%{DATA:transaction_id}.*%{GREEDYDATA:raw_data}"
}
}
date {
match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
}
}
}
output {
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "modsecurity-audit-%{+YYYY.MM.dd}"
user => "elastic"
password => "${LOGSTASH_INTERNAL_PASSWORD:-}"
ssl => false
ssl_certificate_verification => false
}
}
▼ docker-compose.yml
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
services:
setup:
profiles:
- setup
build:
context: ../elk/setup/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
init: true
volumes:
- ../elk/setup/entrypoint.sh:/entrypoint.sh:ro,Z
- ../elk/setup/lib.sh:/lib.sh:ro,Z
- ../elk/setup/roles:/roles:ro,Z
environment:
ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
METRICBEAT_INTERNAL_PASSWORD: ${METRICBEAT_INTERNAL_PASSWORD:-}
FILEBEAT_INTERNAL_PASSWORD: ${FILEBEAT_INTERNAL_PASSWORD:-}
HEARTBEAT_INTERNAL_PASSWORD: ${HEARTBEAT_INTERNAL_PASSWORD:-}
MONITORING_INTERNAL_PASSWORD: ${MONITORING_INTERNAL_PASSWORD:-}
BEATS_SYSTEM_PASSWORD: ${BEATS_SYSTEM_PASSWORD:-}
networks:
- nginx_proxy_manager
depends_on:
- elasticsearch
- kibana
- logstash
npm:
image: nginxproxymanager:dev
..(생략)..
volumes:
- npm_data:/data
- le_data:/etc/letsencrypt
- ../backend:/app
- ../frontend:/app/frontend
- ../global:/app/global
depends_on:
- db
working_dir: /app
db:
..(생략)..
elasticsearch:
build:
context: ../elk/elasticsearch/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
init: true
volumes:
- ../elk/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro,Z
- elasticsearch_data:/usr/share/elasticsearch/data:Z # !
container_name: elasticsearch
environment:
node.name: elasticsearch
ES_JAVA_OPTS: -Xms512m -Xmx512m
ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
discovery.type: single-node
ports:
- 9200:9200
networks:
- nginx_proxy_manager
logstash:
build:
context: ../elk/logstash/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
container_name: logstash
volumes:
- ../elk/logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro,Z
- ../elk/logstash/pipeline:/usr/share/logstash/pipeline:ro,Z
- npm_data:/data:rw
ports:
- 5044:5044
networks:
- nginx_proxy_manager
depends_on:
- elasticsearch
environment:
LS_JAVA_OPTS: -Xms256m -Xmx256m
LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
kibana:
build:
context: ../elk/kibana/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
container_name: kibana
volumes:
- ../elk/kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml:ro,Z
ports:
- 5601:5601
environment:
KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
networks:
- nginx_proxy_manager
depends_on:
- elasticsearch
volumes:
npm_data:
name: npm_core_data
le_data:
name: npm_le_data
db_data:
name: npm_db_data
elasticsearch_data:
driver: local
name: elk_es_data
networks:
nginx_proxy_manager:
name: npm_network
driver: bridge
최종 결합 완료 및 실행
docker compose -f docker-compose.yml --profile setup up 명령 실행 결과:



(몇가지 시행착오가 있었지만 최종적으로 정상 실행 완료된 로그)
Nginx Proxy Manager 관리자 페이지:
http://localhost:3081 → ✅ 정상


Kibana 페이지:
http://localhost:5601 → ✅ 정상


→ ✅ logstash를 통해 modsec log 수집 성공
키바나에서 로그 확인을 위한 추가설정 가이드
- 키바나 접속(http://localhost:5601)초기 계정 정보
ID:
elasticPASSWORD:
changeme
- 좌측 네비게이션 >
Stack Management>Data view>Create Data View
인덱스 명: (아무 이름)
인덱스 패턴:
modsecurity-audit-*시간 필드:
@timestamp
→ ✅ 인덱스 생성 완료
Kibana 초기 화면 Discover 메뉴에서 http://localhost:3080 접속에 대한 로그 확인 및 Visualize 메뉴에서 차트 구성 가능
Comments
진행 간에 로그인 페이지 없는 ELK 구성은 빨리 구현할 수 있었다. 하지만 위와 같이 로그인 페이지를 붙이는 과정은 조금 달랐다.
“1️⃣Docker 컨테이너 활용 + 2️⃣ 다른 애플리케이션에 결합”하는 조건과 동일하게 진행된 레퍼런스를 찾지 못했고, 이 때문에 직접 이런저런 소스 분석하며 결합해보고 테스트 하느라 시간이 제법 소요된 작업이었다. ✅ 아무튼 성공.