728x90

Elasticsearch를 Docker Compose + Wolfi 이미지로 구성한다는 건, 결국 이 조합입니다.
- 기반
- Docker / Docker Compose 환경
- Elastic에서 제공하는 공식 Docker Compose 템플릿 (3노드 ES + Kibana)
- 이미지
- 기본:
docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} - Wolfi Hardened:
docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION}
- 기본:
- 구성 파일
.env: 비밀번호, 버전, 포트, 메모리 등docker-compose.yml: ES 3노드 + Kibana 서비스 정의
- 보안/운영 포인트
- 포트 노출 최소화(ES_PORT, KIBANA_PORT)
- X-Pack Security / TLS / 권한 분리
- Rootless/최소 권한, 이미지 서명/스캔, 모니터링/로그 수집
Wolfi 이미지란? 왜 쓰는가?
1. Wolfi 개념
- Chainguard에서 만든 컨테이너 전용 미니멀 리눅스 배포판
- 특징
- 매우 작은 패키지셋, 불필요 도구 제거
- 지속적으로 업데이트되는 패키지로 CVE를 최소화
- “컨테이너+보안”에 초점이 맞춰져 있음
2. Elasticsearch Wolfi 이미지
Elastic 공식 문서에서 이렇게 설명합니다.
- Hardened Docker images: Wolfi 기반으로 빌드된 ES 이미지
- 사용하려면 Docker 20.10.10 이상 필요
- 사용법: 이미지 태그에
-wolfi를 추가- 예
# 최신(문서 기준 예시) docker pull docker.elastic.co/elasticsearch/elasticsearch-wolfi:9.2.2
- 예
- 나머지 설정(환경변수, compose 구조)은 일반 ES Docker와 거의 동일

보안 입장에서는 “기본 이미지보다 CVE가 적은 하드닝 이미지” 정도의 컨셉으로 이해하시면 됩니다.
사전 준비: Docker / Compose / 시스템 설정
1. Docker & Compose 설치
- OS: Linux / macOS / Windows 어디든 가능.
- Docker Desktop 환경이면 Compose 포함.
- 공식 문서에서는 최소 4GB 메모리를 Docker에 할당하라고 안내합니다.
2. 시스템 설정 (리눅스 호스트 기준 자주 쓰는 것)
# mmap 영역 증가 (ES 필수 튜닝 중 하나)
sudo sysctl -w vm.max_map_count=262144
# 영구 적용 (예)
echo "vm.max_map_count=262144" | sudo tee /etc/sysctl.d/99-elasticsearch.conf
sudo sysctl --system
Compose 템플릿만으로도 로컬 개발은 돌아가지만, 운영 전에는 이런 튜닝을 권장합니다.
(Read Medium articles with AI)
디렉터리 및 파일 구조
1. 프로젝트 디렉터리 생성
mkdir es-wolfi-cluster
cd es-wolfi-cluster
2. 공식 템플릿 다운로드
Elastic 문서에서는 해당 디렉터리에 .env와 docker-compose.yml을 저장하라고 되어 있습니다.
- 직접 복사하거나,
- 사내 Git 리포지토리로 관리하는 것을 추천드립니다.
300x250
.env 파일 구성 (보안/운영 핵심)
공식 문서 예제를 바탕으로, Wolfi와 상관 없이 공통으로 사용하는 .env 구조입니다.
# Password for the 'elastic' user (at least 6 characters)
ELASTIC_PASSWORD=Elastic123 # 예시 (알파벳+숫자만)
# Password for the 'kibana_system' user (at least 6 characters)
KIBANA_PASSWORD=Kibana123
# Version of Elastic products
STACK_VERSION=9.2.2
# Cluster name
CLUSTER_NAME=wolfi-docker-cluster
# License type: basic or trial
LICENSE=basic
#LICENSE=trial
# Port to expose Elasticsearch HTTP API to the host
#ES_PORT=9200 # 0.0.0.0:9200 (위험)
ES_PORT=127.0.0.1:9200 # 보안상 권장
# Port to expose Kibana to the host
KIBANA_PORT=5601
#KIBANA_PORT=80
# Memory limit for Elasticsearch containers (in bytes)
MEM_LIMIT=2147483648 # 2GB
# Project namespace (optional)
#COMPOSE_PROJECT_NAME=my-es-wolfi
1. 비밀번호 규칙 주의
ELASTIC_PASSWORD,KIBANA_PASSWORD는 알파벳+숫자만 사용 (특수문자 사용 시 bash 스크립트 동작 문제 발생)- 운영에서는
- 이 값들을 Git에 커밋하지 않고,
- CI/CD / Vault / Secret Manager와 연동하는 방식을 권장.
2. ES 포트 노출 제한 (매우 중요)
- 문서에서도 “포트 9200이 외부에 노출되지 않도록
ES_PORT=127.0.0.1:9200로 설정하라”고 명시합니다. - 서비스 프로바이더 관점
- ES를 직접 인터넷에 노출하는 건 웬만하면 피하고,
- 내부망 + WAF/Reverse Proxy(Kibana, NGINX) 구조로 두는 것이 안전합니다.
docker-compose.yml 구성 & Wolfi 적용
공식 Compose는 크게 4 서비스로 구성됩니다.
setup(초기 TLS/사용자 설정 스크립트 수행)es01,es02,es03(3 노드 ES)kibana
여기서 Wolfi 적용 포인트는 단 하나
Elasticsearch 서비스의 image를 elasticsearch-wolfi:${STACK_VERSION} 로 바꾸는 것
1. 예시 docker-compose.yml (핵심 부분만)
version: "2.2"
services:
setup:
image: docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION}
container_name: es-setup
environment:
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
- KIBANA_PASSWORD=${KIBANA_PASSWORD}
- STACK_VERSION=${STACK_VERSION}
volumes:
- certs:/usr/share/elasticsearch/config/certs
user: "0"
command: >
bash -c '
if [ x${ELASTIC_PASSWORD} == x ]; then
echo "Set the ELASTIC_PASSWORD environment variable in the .env file";
exit 1;
elif [ x${KIBANA_PASSWORD} == x ]; then
echo "Set the KIBANA_PASSWORD environment variable in the .env file";
exit 1;
fi;
# CA, 노드 인증서 생성 스크립트 (공식 예제 그대로)
if [ ! -f config/certs/ca.zip ]; then
echo "Creating CA";
bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
unzip config/certs/ca.zip -d config/certs;
fi;
if [ ! -f config/certs/certs.zip ]; then
echo "Creating certs";
# es01, es02, es03, kibana 용 인증서 생성...
# (공식 예제의 instances.yml 생성 부분)
fi;
echo "Setting file permissions"
chown -R root:root config/certs;
find . -type d -exec chmod 750 {} \;;
find . -type f -exec chmod 640 {} \;;
echo "Setup done."
'
es01:
image: docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION}
container_name: es01
depends_on:
- setup
environment:
- node.name=es01
- cluster.name=${CLUSTER_NAME}
- discovery.seed_hosts=es01,es02,es03
- cluster.initial_master_nodes=es01,es02,es03
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
- xpack.security.enabled=true
- xpack.security.http.ssl.enabled=true
- xpack.security.http.ssl.key=certs/es01/es01.key
- xpack.security.http.ssl.certificate=certs/es01/es01.crt
- xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
- xpack.security.transport.ssl.enabled=true
- xpack.security.transport.ssl.key=certs/es01/es01.key
- xpack.security.transport.ssl.certificate=certs/es01/es01.crt
- xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
- xpack.security.transport.ssl.verification_mode=certificate
- ES_JAVA_OPTS=-Xms1g -Xmx1g
volumes:
- certs:/usr/share/elasticsearch/config/certs
- esdata01:/usr/share/elasticsearch/data
ports:
- "${ES_PORT}:9200"
ulimits:
memlock:
soft: -1
hard: -1
mem_limit: ${MEM_LIMIT}
es02:
image: docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION}
container_name: es02
depends_on:
- setup
environment:
- node.name=es02
- cluster.name=${CLUSTER_NAME}
- discovery.seed_hosts=es01,es02,es03
- cluster.initial_master_nodes=es01,es02,es03
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
# 나머지 xpack/security, TLS 설정은 es01과 동일 패턴
volumes:
- certs:/usr/share/elasticsearch/config/certs
- esdata02:/usr/share/elasticsearch/data
es03:
image: docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION}
container_name: es03
depends_on:
- setup
environment:
- node.name=es03
- cluster.name=${CLUSTER_NAME}
- discovery.seed_hosts=es01,es02,es03
- cluster.initial_master_nodes=es01,es02,es03
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
volumes:
- certs:/usr/share/elasticsearch/config/certs
- esdata03:/usr/share/elasticsearch/data
kibana:
image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
container_name: kibana
depends_on:
- es01
environment:
- ELASTICSEARCH_HOSTS=https://es01:9200
- ELASTICSEARCH_USERNAME=kibana_system
- ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
- SERVER_PUBLICBASEURL=http://localhost:${KIBANA_PORT}
- SERVER_NAME=kibana
- SERVER_PORT=5601
- SERVER_HOST=0.0.0.0
- ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=/usr/share/kibana/config/certs/ca/ca.crt
ports:
- "${KIBANA_PORT}:5601"
volumes:
- certs:/usr/share/kibana/config/certs
volumes:
certs:
esdata01:
esdata02:
esdata03:
위 예시는 공식 템플릿 구조를 Wolfi 이미지에 맞게 바꾼 예시입니다.
실제 공식 docker-compose.yml의 스크립트/instances.yml 생성 부분을 그대로 사용하시면 됩니다.
클러스터 기동 & 확인
1. 클러스터 기동
docker-compose up -d
setup→ 인증서 생성 후 종료es01,es02,es03→ 클러스터 형성kibana→ ES에 연결 후 UI 제공
2. Kibana 접속
http://localhost:5601
- ID:
elastic - PW:
.env의ELASTIC_PASSWORD값
(문서에서도elastic/ELASTIC_PASSWORD로 로그인하라고 안내합니다.
3. curl로 헬스 체크 예시
TLS On일 때
curl -k -u elastic:${ELASTIC_PASSWORD} \
https://localhost:9200/_cluster/health?pretty
TLS Off(테스트용)일 때
curl -u elastic:${ELASTIC_PASSWORD} \
http://localhost:9200/_cluster/health?pretty
종료
# 컨테이너/네트워크만 정지 및 삭제 (데이터는 유지)
docker-compose down
# 컨테이너/네트워크/볼륨(데이터까지) 모두 삭제
docker-compose down -v
공식 문서에서도 같은 방식으로 안내합니다.
보안 점검 포인트 정리
Wolfi 이미지 + ES Docker Compose 구성을 내부에 소개하려면, 아래 항목들을 “보안 가이드 또는 체크리스트”로 만들면 좋습니다.
1. 이미지 및 취약점 관리
- 이미지 출처
docker.elastic.co/elasticsearch/elasticsearch-wolfi:${STACK_VERSION}만 사용
- 사내 레지스트리/프록시
- Pull 후 사내 레지스트리에 재저장
- Trivy, Grype 등으로 정기 CVE 스캔 수행
- 버전 정책
STACK_VERSION고정 (예: 9.2.2)- ES 전체 스택(ES, Kibana, Beats 등) 버전 일치 필요
2. 네트워크 & 접근 통제
.env에서ES_PORT=127.0.0.1:9200설정- 운영망 구성
- ES는 내부망 전용 / VPN 또는 Bastion 호스트에서만 접근
- Kibana, API Gateway, NGINX를 통해 외부 사용자 접근 통제
- Docker 네트워크
- ES/Kibana 전용
docker network사용 (외부 컨테이너와 분리)
- ES/Kibana 전용
3. 인증/권한 (X-Pack Security)
xpack.security.enabled=true필수- 역할/계정 분리
- Superadmin:
elastic(최소 사용) - Kibana:
kibana_system(자동 설정) - 애플리케이션 계정: 특정 인덱스/alias만 read/write
- 모니터링 계정: read/monitor 전용 role
- Superadmin:
- API 키
- 장기 토큰 대신 API Key 기반 연동을 기본으로 가이드
4. TLS/암호화
- 공식 Compose는 CA + 노드 인증서 자동 생성 스크립트를 포함
→setup서비스가 이를 수행 - 운영 시에는
- 사내 PKI/CA와 연동하거나,
- ACME/Let’s Encrypt + Reverse Proxy 사용 고려
- 내부 전송(Transport)도 TLS 적용되어 있음 (기본 스크립트가 처리)
5. 권한/Rootless
- Wolfi 이미지와 잘 맞는 패턴
- Podman Rootless 혹은 Docker
--user옵션 활용
- Podman Rootless 혹은 Docker
- 데이터 디렉터리
- ES UID/GID에만 RW 권한 부여
- 컨테이너 Capabilities 최소화
- 필요 시
cap_drop: [ALL]+ 필요한 Cap만cap_add하는 패턴 고려
- 필요 시
6. 로그 & 모니터링 연동
- ES 로그
- stdout/파일로 수집 → Fluentd/Logstash/Beats/Wazuh 등으로 중앙수집
- 중요 이벤트 모니터링
- 인증 실패, cluster health RED/ORANGE, 노드 join/leave, shard relocation
- Prometheus
- Wolfi 기반 exporter와 연계해 메트릭 수집 가능 (Chainguard의 Exporter 또는 Elastic의 Exporter 활용)
다음 단계 제안
정리하면
- 구성 방식은 공식 Docker Compose 예제를 그대로 이용하되,
Elasticsearch 이미지 이름만 Wolfi 하드닝 이미지로 교체하는 것이 핵심입니다. .env에서- 비밀번호(알파벳+숫자),
STACK_VERSION,ES_PORT=127.0.0.1:9200
만 잘 잡아주면 로컬/테스트 환경은 바로 기동 가능합니다.
- 보안 입장에서는
- 이미지 소스 및 CVE 관리,
- 포트 노출/네트워크 구조,
- 계정/권한 분리,
- TLS/로그/모니터링
을 정책 문서+체크리스트로 정리해두면, 이후 프로젝트/서비스에서 재사용하기 좋습니다.
728x90
그리드형(광고전용)
댓글