개요
- application server 및 ELK를 docker container로 올려두고 로깅해본다.
구조
- 개요
- 상세설명
- api
- golang, gonic-gin framework를 이용하여 현재 한국 시간을 리턴하는 api를 배포하는 서버
- api 서버 내에 filebeat를 설치하여 gin.log를 logstash로 일정 시간마다 전송한다.
- front
- nginx 웹서버를 실행시켜 main.html 페이지를 배포하는 서버
- main.html에서는 현재 한국 시간을 얻어오는 api를 api 서버로부터 호출한다.
- front 서버 내에 filebeat를 설치하여 access.log와 error.log를 logstash로 일정 시간마다 전송한다.
- logstash
- api 및 front의 filebeat로부터 데이터를 전송 받으면 지정된 filter 처리를 한 후
elasticsearch로 전송한다.
- api 및 front의 filebeat로부터 데이터를 전송 받으면 지정된 filter 처리를 한 후
- elasticsearch
- logstash에서 받은 데이터를 재색인한다.
- 이를 통해 쉽게 로그 검색을 할 수 있게 된다.
- kibana
- GUI로 elasticsearch의 많은 기능을 이용할 수 있게 된다.
- kibana query를 이용하여 간단하게 로그 검색할 수 있다.
- api
- 덧붙이는 말
- 각 application server에 filebeat를 직접 설치하는 대신
filebeat container에서 docker logs를 가져와 logstash로 보내는 방법도 있다. 참고
- 각 application server에 filebeat를 직접 설치하는 대신
예제 소스코드
docker network 생성
- 개요
- 컨테이너 간 통신을 하려면 docker network를 생성하고
해당 컨테이너들을 생성한 docker network에 연결해야한다. - docker network에 연결된 컨테이너들은 사설 IP(private IP)를 받게 된다.
- 컨테이너 간 통신을 할때 사설 IP를 사용하거나 컨테이너 이름을 hostname으로 쓸 수 있다.
- 컨테이너 간 통신을 하려면 docker network를 생성하고
- 실행
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
# es-net이라는 docker network 생성 $ docker network create es-net # 연결된 컨테이너 확인(최초 생성 시에는 아무 것도 없다) $ docker network inspect es-net # 결과 [ { "Name": "es-net", "Id": "2af31f476054b06841ffbb72903bc07793e860d0b677e886990ec24c4512637f", "Created": "2021-10-05T06:53:46.1699186Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": {}, "Options": {}, "Labels": {} } ]
Elasticsearch
- 컨테이너 생성 설명
- 9200번, 9300번 포트로 컨테이너와 실제환경(도커를 돌리고 있는 윈도우)를 연결한다.
- docker network, es-net에 연결한다.
- -e 명령을 통해 컨테이너 내부의 환경변수를 설정할 수 있다.
- elasticsearch는 환경변수를 통해 몇 가지 설정을 할 수 있다.
- action.auto_create_index:true
- 새로운 데이터가 들어왔을 때 인덱스를 자동으로 생성하겠다는 설정
- discovery.type=single-node
- 싱글 노드만 쓰겠다는 설정
- 다중 노드를 쓴다면 slave 노드를 93xx 포트에 연결한다.
- action.auto_create_index:true
- –rm을 지정하면 컨테이너 종료 시 컨테이너를 삭제한다.
- elasticsearch은 7.15.0 버전을 사용한다.
- !주의 - 컨테이너 이름을 정할 때 “_“나 “.”을 사용하지 말자
logstash에서 hosts를 지정할 때 “_“나 “.”가 포함되면 에러를 내보닌다.
- 실행
1 2 3 4 5 6 7 8
> docker run \ --name es-c-es \ --network es-net \ --rm \ -p 9200:9200 -p 9300:9300 \ -e "action.auto_create_index:true" \ -e "discovery.type=single-node" \ docker.elastic.co/elasticsearch/elasticsearch:7.15.0
- 실행 확인
Logstash
- 설정
- logstash 설정은 /usr/share/logstash/pipeline/(이하 pipeline) 하위에서 할 수 있다.
- logstash 도커 컨테이너는 pipeline 하위의 logstash로 시작하는 파일을 설정파일로 인식한다.
- pipeline 하위의 patterns 디렉토리에 logstash 설정에서 grok 정규식 사전 파일들을 저장한다.
- 실제 환경에서 설정파일을 생성한 뒤 컨테이너의 pipeline 디렉토리에 mount 하는 방식으로 설정한다.
- logstash 설정은 /usr/share/logstash/pipeline/(이하 pipeline) 하위에서 할 수 있다.
- 설정파일
- patterns
- config에서 log를 파싱할 때 grok 정규식을 사용하는데,
이때 사용할 패턴을 미리 파일로 만들어둔다. 1 2 3 4 5 6 7
# nginx NGINXACCESS %{IPORHOST:[nginx][access][remote_ip]} - %{DATA:[nginx][access][user_name]} \[%{HTTPDATE:[nginx][access][time]}\] \"%{WORD:[nginx][access][method]} %{DATA:[nginx][access][url]} HTTP/%{NUMBER:[nginx][access][http_version]}\" %{NUMBER:[nginx][access][response_code]} %{NUMBER:[nginx][access][body_sent][bytes]} \"%{DATA:[nginx][access][referrer]}\" \"%{DATA:[nginx][access][agent]}\" NGINXERROR %{DATA:[nginx][error][time]} \[%{DATA:[nginx][error][level]}\] %{NUMBER:[nginx][error][pid]}#%{NUMBER:[nginx][error][tid]}: (\*%{NUMBER:[nginx][error][connection_id]} )?%{GREEDYDATA:[nginx][error][message]} # gin GIN_TIMESTAMP %{YEAR}/%{MONTHNUM}/%{MONTHDAY} - %{HOUR}:%{MINUTE}:%{SECOND} GINLOG \[%{DATA:[gin][type]}\] %{GIN_TIMESTAMP:[gin][time]} \| %{NUMBER:[gin][response_status]} \| \s*%{DATA:[gin][response_time]}\s* \| \s*%{IPORHOST:[gin][remote_ip]}\s* \| \s*%{DATA:[gin][method]}\s* \"%{DATA:[gin][url]}\"
- config에서 log를 파싱할 때 grok 정규식을 사용하는데,
- logstash.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
# 5044 포트로 filebeat에서 전송하는 데이터를 받겠다는 의미 input { beats { port => 5044 } } # 전송 받은 비정형 데이터를 filter에서 정형화 시킨다. # 사전에서 정의한 grok 패턴을 통해 비정헝 데이터를 파싱하고 # mutate, date 등의 명령을 통해 정제한다. filter { if "nginx-access" in [tags] { grok { patterns_dir => ["/usr/share/logstash/pipeline/patterns"] match => {"message" => ["%{NGINXACCESS}"]} remove_field => "message" } mutate { add_field => { "read_timestamp" => "%{@timestamp}" } } date { match => [ "[nginx][access][time]", "dd/MMM/YYYY:H:m:s Z" ] remove_field => "[nginx][access][time]" } useragent { source => "[nginx][access][agent]" target => "[nginx][access][user_agent]" remove_field => "[nginx][access][agent]" } } else if "nginx-error" in [tags] { grok { patterns_dir => ["/usr/share/logstash/pipeline/patterns"] match => { "message" => ["%{NGINXERROR}"] } remove_field => "message" } mutate { rename => { "@timestamp" => "read_timestamp" } } date { match => [ "[nginx][error][time]", "YYYY/MM/dd H:m:s" ] remove_field => "[nginx][error][time]" } } else if "gin" in [tags] { grok { patterns_dir => ["/usr/share/logstash/pipeline/patterns"] match => {"message" => ["%{GINLOG}"]} remove_field => "message" remove_field => "[gin][type]" } mutate { add_field => { "read_timestamp" => "%{@timestamp}" } } date { match => [ "[gin][time]", "yyyy/MM/dd - HH:mm:ss" ] remove_field => "[gin][time]" } } } # stdout을 추가함으로써 처리과정을 사용자가 볼 수 있다. # ES_IP 환경변수에 담긴 hostname을 통해 elasticsearch로 정제한 데이터를 송부한다. # 매일 인덱스를 새로 생성하는 것을 알 수 있다. output { stdout { codec => rubydebug } elasticsearch { action => "index" hosts => ["${ES_IP:=localhost}:9200"] manage_template => false index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}" } }
- patterns
- 컨테이너 생성 설명
- docker network, es-net에 연결한다.
- –rm을 지정하면 컨테이너 종료 시 컨테이너를 삭제한다.
- -e를 통해 elasticsearch 컨테이너 이름을 logstash 컨테이너의 ES_IP 환경변수로 설정한다.
- -v를 통해 실제환경의 config/logstash 디렉토리를
logstash 컨테이너의 /usr/share/logstash/pipeline 디렉토리에 mount한다. - logstash 버전은 7.15.0를 사용한다.
- 실행
1 2 3 4 5 6 7
> docker run \ --name es-c-logstash \ --rm \ --network es-net \ -e ES_IP=es-c-es \ -v %cd%/config/logstash/:/usr/share/logstash/pipeline/ \ docker.elastic.co/logstash/logstash:7.15.0
API
- 개요
- golang, gonic-gin framework를 이용하여 간단한 API를 생성한다.
- 현재 한국 시간을 알려주는 API로 endpoint는 /now이다.
- gonic-gin 로그는 stdout 대신 파일에 남도록 설정한다.
- api/main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
import ( "io" "log" "net/http" "os" "time" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) // handleNow: 현재 한국 시간 획득 func handleNow(c *gin.Context) { loc, _ := time.LoadLocation("Asia/Seoul") now := time.Now().In(loc) c.JSON(http.StatusOK, gin.H{"data": now}) } func main() { // gonic-gin 로그가 stdout 대신 파일에 남도록 설정 gin.DisableConsoleColor() logFilePath := "gin.log" f, fileError := os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.FileMode(0644)) if fileError != nil { log.Fatal(fileError) } defer f.Close() gin.DefaultWriter = io.MultiWriter(f) // Gin mode를 release로 설정 gin.SetMode(gin.ReleaseMode) // CORS 설정, front 서버 host는 localhost:8000이다. router := gin.Default() corsConf := cors.New(cors.Config{ AllowOrigins: []string{"http://localhost:8000"}, }) router.Use(corsConf) // 현재 한국 시간 획득하는 API router.GET("/now", handleNow)
- 로컬환경에서 실행
- filebeat.yml
- 개요
- 로그 파일을 읽어 logstash로 보내므로 filebeat.inputs: type은 filestream을 사용한다.
- logstash에서 파일을 구분할 수 있도록 태그를 달아준다.
- gin.log는 gin으로 달아준다.
- 파일
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
filebeat.inputs: - type: filestream enabled: true paths: - /root/api/gin.log tags: ["gin"] output.logstash: hosts: ["${LOGSTASH_IP:=localhost}:5044"] logging.level: debug logging.to_stderr: true logging.to_syslog: true
- 개요
- Dockerfile 작성
- 개요
- api 서버 내에 filebeat도 설치해야하기 때문에
기본 golang docker image에서 filebeat를 설치하는 Dockerfile를 작성하여 커스텀 이미지 생성한다. - Dockerfile 명령어 중 entrypoint라는 명령어가 있다.
- entrypoint는 컨테이너를 생성할 때마다 실행되는 명령어를 지정하는 명령어이다.
- 우리는 gonic-gin 뿐만아니라 filbeat도 함께 실행해야하므로
이를 실행하는 스크립트 파일을 생성하여 entrypoint로 실행시킨다.
- api 서버 내에 filebeat도 설치해야하기 때문에
- api_script.sh
1 2 3 4 5 6
#!/usr/bin/env bash # filebeat를 background로 실행하고, stderr는 stdout으로 변경하여 filebeat.log에 기록 nohup filebeat -e -c /etc/filebeat/filebeat.yml > filebeat.log 2>&1 & # gonic-gin 서버 실행 go run main.go
- Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
FROM golang:1.16 WORKDIR /root/api COPY . /root/api # apt 저장소를 카카오미러로 변경 RUN cp /etc/apt/sources.list sources.list_backup RUN sed -i -e 's/archive.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list RUN sed -i -e 's/security.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list # filebeat 설치 및 서비스 등록 # 참고: https://www.elastic.co/guide/en/beats/filebeat/current/setup-repositories.html RUN wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add - RUN apt-get install apt-transport-https -y RUN echo "deb https://artifacts.elastic.co/packages/oss-7.x/apt stable main" | tee -a /etc/apt/sources.list.d/elastic-7.x.list RUN apt-get update && apt-get install filebeat -y COPY ./filebeat.yml /etc/filebeat/filebeat.yml RUN update-rc.d filebeat defaults 90 10 # api_script.sh 실행 ENTRYPOINT bash api_script.sh
- 개요
- Dockerfile 기반으로 docker image 생성
1 2
> cd api > docker build -t es-c-api .
- docker image를 기반으로 컨테이너 생성
- 설명
- docker network es-net에 연결한다.
- -e를 이용하여 logstash 컨테이너 이름을 api 컨테이너 LOGSTASH_IP 환경변수로 지정한다.
- 8080번 포트로 컨테이너와 실제환경을 연결한다.
- -d는 컨테이너를 detached하라는 의미로
컨테이너 stdout이 실제환경 쉘에 노출되지 않으며 background 실행처럼 돌아간다. - 가장 끝의 es-c-api는 컨테이너의 기반이 되는 이미지의 이름을 의미한다.
- 실행
1 2 3 4 5 6
> docker run \ --name es-c-api \ --network es-net -e LOGSTASH_IP=es-c-logstash \ -p 8080:8080 \ -d es-c-api
- 실행 확인
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
> docker exec -it es-c-api bash $ tail -f /root/filebeat.log # 결과 DEBUG [input.filestream] filestream/filestream.go:131 End of file reached: /root/api/gin.log; Backoff now. {"id": "DD9DC931427F9DD0", "source": "filestream::.global::native::187961-147", "path": "/root/api/gin.log", "state-id": "native::187961-147"} INFO [file_watcher] filestream/fswatch.go:137 Start next scan DEBUG [file_watcher] filestream/fswatch.go:204 Found 1 paths ... DEBUG [processors] processing/processors.go:203 Publish event: { "@timestamp": "2021-10-06T06:31:50.050Z", "@metadata": { "beat": "filebeat", "type": "_doc", "version": "7.15.0" }, "input": { "type": "filestream" }, "ecs": { "version": "1.11.0" }, "host": { "name": "2af34ed1fa1d" }, "agent": { "ephemeral_id": "6a3d70c7-da4d-4bda-8662-c1c9728ec367", "id": "4798c380-2dd3-4926-899f-d2006bcb4f4b", "name": "2af34ed1fa1d", "type": "filebeat", "version": "7.15.0", "hostname": "2af34ed1fa1d" }, "log": { "file": { "path": "/root/api/gin.log" }, "offset": 435 }, "message": "[GIN] 2021/10/06 - 06:31:47 | 200 | 94µs | 172.18.0.1 | GET \"/now\"", "tags": [ "gin" ] }
- 설명
Front
- 개요
- nginx 웹서버를 실행시켜 main.html 페이지를 배포한다.
- main.html에는 버튼이 하나 있으며 버튼을 클릭하면
현재 한국 시간을 얻어오는 api를 api 서버로부터 호출하여 렌더한다.
- main.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="./js/request.js" defer></script> <script src="./js/time.js" defer></script> <title>TEST FRONT WEB</title> </head> <body> <div> <button onclick="updateKoreanTime()">Update</button> <div>Current time(Korea)</div> <div id="korea-time-wrapper"> <div id="korea-time"></div> <div id="korea-time-error"></div> </div> </div> </body> </html>
- js/request.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// CommonRequest : 공용 Request 함수 function CommonRequest() { this.baseURL = "//localhost:8080" } // requestGetKoreanTime : 한국 시간 요청 function requestGetKoreanTime(successFunc, failFunc) { CommonRequest.call(this) const url = `${baseURL}/now` fetch(url) .then(resp => resp.json()) .then(respJSON => successFunc(respJSON)) .catch(error => failFunc(error)) }
- js/time.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
// updateKoreanTime : 한국 시간 업데이트 function updateKoreanTime() { const elemMap = {} const ids = ["korea-time", "korea-time-error"] for (let i=0; i<ids.length; i++) { const id = ids[i] const elem = document.querySelector(`#${id}`) if (elem === null) { console.warn(`A element of ${id} is not exists.`) return } elemMap[id] = elem } function successFunc(jespJSON) { console.log("successFunc", jespJSON) const koreaTime = jespJSON["data"] if (jespJSON["data"] !== "undefined") { elemMap["korea-time"].innerText = koreaTime } } function failFunc(error) { console.log("error", error) if (typeof error !== "undefined") { elemMap["korea-time-error"].innerText = error } } if (typeof requestGetKoreanTime !== "undefined") { requestGetKoreanTime(successFunc, failFunc) } }
- 로컬환경에서 실행
- filebeat.yml
- 개요
- 로그 파일을 읽어 logstash로 보내므로 filebeat.inputs: type은 filestream을 사용한다.
- logstash에서 파일을 구분할 수 있도록 태그를 달아준다.
- access.log는 nginx-access로, error.log는 nginx-error로 달아준다.
- 파일
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
filebeat.inputs: - type: filestream enabled: true paths: - /var/log/nginx/access.log tags: ["nginx-access"] - type: filestream enabled: true paths: - /var/log/nginx/error.log tags: ["nginx-error"] output.logstash: hosts: ["${LOGSTASH_IP:=localhost}:5044"] logging.level: debug logging.to_stderr: true logging.to_syslog: true
- 개요
- Dockerfile 작성
- 개요
- front 서버 내에 filebeat도 설치해야하기 때문에
기본 nginx image에서 filebeat를 설치하는 Dockerfile을 작성하여 커스텀 이미지 생성한다. - 우리는 nginx 뿐만아니라 filbeat도 함께 실행해야하므로
이를 실행하는 스크립트 파일을 생성하여 entrypoint로 실행시킨다.
- front 서버 내에 filebeat도 설치해야하기 때문에
- front_script.sh
1 2 3 4 5 6 7
#!/usr/bin/env bash # filebeat를 background로 실행하고, stderr는 stdout으로 변경하여 filebeat.log에 기록 nohup filebeat -e -c /etc/filebeat/filebeat.yml > filebeat.log 2>&1 & # docker container를 detached로 실행할 경우, nginx를 daemon으로 실행하면 안된다. nginx -g 'daemon off;'
- Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
FROM nginx:1.21.3 WORKDIR /root # front 파일 복사 RUN mkdir -p /usr/share/nginx/html/js COPY ./main.html /usr/share/nginx/html/ COPY ./js /usr/share/nginx/html/js # apt 저장소 카카오미러로 변경 RUN cp /etc/apt/sources.list sources.list_backup RUN sed -i -e 's/archive.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list RUN sed -i -e 's/security.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list RUN apt-get update && apt-get upgrade -y RUN apt-get install wget gnupg -y # nginx 도커 이미지에서는 docker logs에 로깅할 수 있도록 # access.log는 stdout으로, error.log는 stderr로 symbolic link를 만들어두었다. # 우리는 파일 기반으로 처리할 것이므로 symbolic link를 해제한다. RUN rm -f /var/log/nginx/access.log RUN rm -f /var/log/nginx/error.log # filebeat 다운로드 및 실행 # 참고: https://www.elastic.co/guide/en/beats/filebeat/current/setup-repositories.html RUN wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add - RUN apt-get install apt-transport-https -y RUN echo "deb https://artifacts.elastic.co/packages/oss-7.x/apt stable main" | tee -a /etc/apt/sources.list.d/elastic-7.x.list RUN apt-get update && apt-get install filebeat -y COPY ./filebeat.yml /etc/filebeat/filebeat.yml RUN update-rc.d filebeat defaults 90 10 # front_script.sh 실행 COPY ./front_script.sh /root/ ENTRYPOINT bash front_script.sh
- 개요
- Dockerfile 기반으로 docker image 생성
1 2
> cd front > docker build -t es-c-front .
- docker image를 기반으로 컨테이너 생성
- 설명
- docker network es-net에 연결한다.
- -e를 이용하여 logstash 컨테이너 이름을 api 컨테이너 LOGSTASH_IP 환경변수로 지정한다.
- 컨테이너의 80번 포트를 실제환경의 8000번 포트로 연결한다.
이는 nginx의 기본 포트가 80번이기 때문이다. - -d는 컨테이너를 detached하라는 의미로
컨테이너 stdout이 실제환경 쉘에 노출되지 않으며 background 실행처럼 돌아간다. - 가장 끝의 es-c-front는 컨테이너의 기반이 되는 이미지의 이름을 의미한다.
- 실행
1 2 3 4 5 6
> docker run \ --name es-c-front \ --network es-net -e LOGSTASH_IP=es-c-logstash \ -p 8000:80 \ -d es-c-front
- 실행확인
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
> docker exec -it es-c-front bash $ tail -f /root/filebeat.log # 결과 DEBUG [logstash] logstash/async.go:120 connect INFO [publisher] pipeline/retry.go:219 retryer: send unwait signal to consumer INFO [publisher] pipeline/retry.go:223 done ... DEBUG [processors] processing/processors.go:203 Publish event: { "@timestamp": "2021-10-06T06:26:05.525Z", "@metadata": { "beat": "filebeat", "type": "_doc", "version": "7.15.0" }, "log": { "offset": 2225, "file": { "path": "/var/log/nginx/access.log" } }, "message": "172.18.0.1 - - [06/Oct/2021:06:26:05 +0000] \"GET /main.html HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36\" \"-\"", "tags": [ "nginx-access" ], "input": { "type": "filestream" }, "ecs": { "version": "1.11.0" }, "host": { "name": "562db3883fa7" }, "agent": { "version": "7.15.0", "hostname": "562db3883fa7", "ephemeral_id": "19325ad6-c59c-4013-82c8-33b040c3d187", "id": "d97701a2-949e-4610-b98f-f9441f35555a", "name": "562db3883fa7", "type": "filebeat" } }
- 설명
Kibana
- 개요
- kibana로 GUI 이용하여 elasticsearch 검색을 이용할 수 있다.
- 컨테이너 생성 설명
- –rm을 지정하면 컨테이너 종료 후 컨테이너가 삭제된다.
- docker network, es-net에 연결한다.
- 컨테이너 5601번과 실제환경 5601을 연결한다.
이를 통해 실제환경에서 localhost:5601로 접속하면 컨테이너의 키바나로 접속할 수 있다. - -e를 통해 elasticsearch와 연결한다.
- kibana는 7.15.0 버전을 사용한다.
- 실행
1 2 3 4 5 6 7 8 9 10 11 12
> docker run \ --rm \ --name es-c-kibana \ --net es-net \ -p 5601:5601 \ -e "ELASTICSEARCH_HOSTS=http://es-c-es:9200" \ docker.elastic.co/kibana/kibana:7.15.0 # 브라우저를 켜고 localhost:5601로 접속 # 좌상단 햄버거 메뉴 클릭 # > Analytics 하위의 Discover 클릭 # > 인덱스 패턴이 없다면 filebeat*로 인덱스 패턴 생성 # > query 입력창에 원하는 kibana query 입력
- 검색 예시
실험 소감
- 이 예시에서는 로그를 컨테이너에 저장하기 때문에 컨테이너 삭제 시 로그가 날아간다.
- 실제 환경이었다면 Application server(front, api)에서 남는 로그를 s3에 저장하고,
로그 분석이 필요한 경우, filebeat, logstash, elasticsearch, kibana 컨테이너를 생성하고
filebeat input을 s3에서 불러와 로그분석을 할 것 같다. - 실험에는 총 5개의 컨테이너가 생성되었는데,
내 pc에서 해당 컨테이너들을 돌리는데 소비하는 램은 ddr3 12gb였다. - 나중에는 docker-compose.yml 파일로 만들어두는 것이 편할 것 같다.
- kubernetes 위에 올려서 쓰는 경우도 많은 것 같으니 실험해보면 재밌을 듯.