git clone https://github.com/deviantony/docker-elk

nginx를 설치하고 docker 기반으로 ELK (elasticsearch, logstash, kibana)를 설치하고 nginx 로그를 filebeat를 설치하여 acces.log, error.log, syslog등을 전송해보자.

 

설치 

ELK를 도커에 설치하는 스크립트를 아래 github에 잘 정리되어 제공해주고 있다.
https://github.com/deviantony/docker-elk

ELK는 이걸로 설치하면 되는데 docker-compose로 nginx와 filebeat까지 함께 설치하기 위해서 아래 저장소에서 제공하는 nginx-filebeat 스크립트를 혼합해서 사용해보자.
https://github.com/spujadas/elk-docker/tree/master/nginx-filebeat

1. 우선 ELK 설치 스크립트를 가져오자.

git clone https://github.com/deviantony/docker-elk



2. 그리고 nginx-filebeat 파일을 다운 받아서 docker-elk 디렉토리 내부에 추가한다.

git clone https://github.com/spujadas/elk-docker
mv ./elk-docker/nginx-filebeat ./docker-elk



3. 그리고 nginx-filebeat까지 사용할 수 있도록 docker-compose.yml 스크립트를 수정해준다. 그리고 nginx.conf 파일을 쉽게 보고 생성된 log도 로컬에서 보기 위해서 mount를 로컬 폴더로 진행한다.

version: '3.2'

services:
  elasticsearch:
    container_name: "elasticsearch"
    build:
      context: elasticsearch/
      args:
        ELK_VERSION: $ELK_VERSION
    volumes:
      - type: bind
        source: ./elasticsearch/config/elasticsearch.yml
        target: /usr/share/elasticsearch/config/elasticsearch.yml
        read_only: true
      - type: volume
        source: elasticsearch
        target: /usr/share/elasticsearch/data
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      ES_JAVA_OPTS: "-Xmx256m -Xms256m"
      ELASTIC_PASSWORD: changeme
    networks:
      - mynet

  logstash:
    container_name: "logstash"
    build:
      context: logstash/
      args:
        ELK_VERSION: $ELK_VERSION
    volumes:
      - type: bind
        source: ./logstash/config/logstash.yml
        target: /usr/share/logstash/config/logstash.yml
        read_only: true
      - type: bind
        source: ./logstash/pipeline
        target: /usr/share/logstash/pipeline
        read_only: true
    ports:
      - "5000:5000"
      - "9600:9600"
    environment:
      LS_JAVA_OPTS: "-Xmx256m -Xms256m"
    networks:
      - mynet
    depends_on:
      - elasticsearch

  kibana:
    container_name: "kibana"
    build:
      context: kibana/
      args:
        ELK_VERSION: $ELK_VERSION
    volumes:
      - type: bind
        source: ./kibana/config/kibana.yml
        target: /usr/share/kibana/config/kibana.yml
        read_only: true
    ports:
      - "5601:5601"
    networks:
      - mynet
    depends_on:
      - elasticsearch

  nginx:
    container_name: "nginx"
    build:
      context: nginx-filebeat/
    volumes:
      - type: bind
        source: /Users/we/Documents/docker/nginx
        target: /etc/nginx
      - type: bind
        source: /Users/we/Documents/docker/nginx_log
        target: /var/log
    ports:
      - "8080:80"
    networks:
      - mynet

networks:
  mynet:
    driver: bridge

volumes:
  elasticsearch:

 

4. 마지막으로 filebeat.xml에서 ssl 통신을 하지 않기 때문에 ssl 부분을 제거해준다. (이유는 하단에 나온다.)

output:
  logstash:
    enabled: true
    hosts:
      - logstash:5044
    timeout: 15

filebeat:
  inputs:
    -
      paths:
        - /var/log/syslog
        - /var/log/auth.log
      document_type: syslog
    -
      paths:
        - "/var/log/nginx/*.log"
      document_type: nginx-access

 

5. 그럼 지금까지 수정한 내용을 이용해서 docker-compose up -d 통해 설치 진행해보자. 설치후 제거 하고 싶으면 (docker-compose -v down) 명령어를 통해 제거 할 수 있다.

docker-compose up -d

 

6. 설치가 완료되면 키바나에 접속해서 확인해보면 logstash, elasticsearch, kibana 모두 설치 된 것을 알 수 있다. 초기 계정은 elastic / changeme이다.

ELK와 nginx docker process

7. logstash pipeline을 만들어줘야하는데 kibana에서 management → logstash → pipeline에서 설정해주면된다. 간단하게 5044포트로 받고 nginx_log 인덱스로 넣게 설정한다.

input {
    beats {
        client_inactivity_timeout => 19909
        port => "5044"
        ssl => false
    }
}
filter {
  if [type] == "nginx-access" {
    grok {
      match => { "message" => "%{NGINXACCESS}" }
    }
  }
}
output {
    elasticsearch {
        hosts => ["elasticsearch:9200"]
        index => "nginx_log"
        user => "elastic"
        password => "changeme"
    }
   stdout { codec => rubydebug }
}

 

8. 그럼 filebeat가 정상적으로 동작하는지 확인해보자.

filebeat 실행 상태 확인

/etc/init.d/filebeat status

 

filebeat.xml 기준으로 설정 정상 적용 상태 확인

filebeat test config

 

filebeat.xml에 설정된 output 정상 여부 확인

filebeat test output

filebeat 테스트 결과

위에 테스트를 진행하면 위에 화면처럼 나오는게 나와야 정상이다. 정상적으로 가동된걸 확인했다.

그럼 실제 nginx에서 나온 로그가 filebeat로 수집되어 logstash > elasticsearch로 정상적으로 적재되는지 보자.
localhost:8080에 접속하여 로그 발생시킨 후 filebeat 로그를 확인했는데 왠걸 다음과 같은 오류가 발생했다.

[2019-10-15T06:12:48,844][INFO ][org.logstash.beats.BeatsHandler] [local: 172.28.0.4:5044, remote: 172.28.0.2:55946] Handling exception: org.logstash.beats.BeatsParser$InvalidFrameProtocolException: Invalid Frame Type, received: 1
[2019-10-15T06:12:48,845][WARN ][io.netty.channel.DefaultChannelPipeline] An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.handler.codec.DecoderException: org.logstash.beats.BeatsParser$InvalidFrameProtocolException: Invalid Frame Type, received: 1
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:459) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:392) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:359) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.handler.codec.ByteToMessageDecoder.channelInactive(ByteToMessageDecoder.java:342) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:245) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.channel.AbstractChannelHandlerContext.access$300(AbstractChannelHandlerContext.java:38) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.channel.AbstractChannelHandlerContext$4.run(AbstractChannelHandlerContext.java:236) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.util.concurrent.DefaultEventExecutor.run(DefaultEventExecutor.java:66) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858) [logstash-input-tcp-6.0.3.jar:?]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [logstash-input-tcp-6.0.3.jar:?]
at java.lang.Thread.run(Thread.java:834) [?:?]
Caused by: org.logstash.beats.BeatsParser$InvalidFrameProtocolException: Invalid Frame Type, received: 1
at org.logstash.beats.BeatsParser.decode(BeatsParser.java:92) ~[logstash-input-beats-6.0.0.jar:?]
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489) ~[logstash-input-tcp-6.0.3.jar:?]
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428) ~[logstash-input-tcp-6.0.3.jar:?]
... 10 more

이 문제는 logStash 또는 filebeat가 동시에 ssl을 사용하지 않는데 한쪽만 ssl 통신을 했을 때 발생되는 오류이다.

그래서 아까 위에 filebeat에 ssl 부문을 지우고 pipeline에 ssl 설정을 false로 지정한 것이다. 그럼 다시한번 localhost:8080에 들어가보자.

지금은 index.html을 만들어 놓지 않아서 404 에러가 발생된다.

GET nginx_log/_search
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "nginx_log",
        "_type" : "_doc",
        "_id" : "vc04zm0BZBO8vgBrBmV7",
        "_score" : 1.0,
        "_source" : {
          "ecs" : {
            "version" : "1.1.0"
          },
          "@version" : "1",
          "tags" : [
            "beats_input_codec_plain_applied"
          ],
          "message" : """2019/10/15 07:00:31 [error] 25#25: *16 "/etc/nginx/html/index.html" is not found (2: No such file or directory), client: 172.28.0.1, server: , request: "GET / HTTP/1.1", host: "localhost:8080"""",
          "host" : {
            "name" : "a31d3333d22b"
          },
          "agent" : {
            "type" : "filebeat",
            "version" : "7.4.0",
            "hostname" : "a31d3333d22b",
            "id" : "cc4eb582-e09c-4a83-bb2e-9721c39ee508",
            "ephemeral_id" : "a5918a0b-4688-458f-bbc2-4eb49a3fff03"
          },
          "log" : {
            "file" : {
              "path" : "/var/log/nginx/error.log"
            },
            "offset" : 8524
          },
          "@timestamp" : "2019-10-15T07:00:38.443Z"
        }
      },
      {
        "_index" : "nginx_log",
        "_type" : "_doc",
        "_id" : "vs04zm0BZBO8vgBrBmV8",
        "_score" : 1.0,
        "_source" : {
          "ecs" : {
            "version" : "1.1.0"
          },
          "@version" : "1",
          "tags" : [
            "beats_input_codec_plain_applied"
          ],
          "message" : """172.28.0.1 - - [15/Oct/2019:07:00:31 +0000] "GET / HTTP/1.1" 404 555 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"""",
          "host" : {
            "name" : "a31d3333d22b"
          },
          "agent" : {
            "type" : "filebeat",
            "version" : "7.4.0",
            "hostname" : "a31d3333d22b",
            "id" : "cc4eb582-e09c-4a83-bb2e-9721c39ee508",
            "ephemeral_id" : "a5918a0b-4688-458f-bbc2-4eb49a3fff03"
          },
          "log" : {
            "file" : {
              "path" : "/var/log/nginx/access.log"
            },
            "offset" : 8528
          },
          "@timestamp" : "2019-10-15T07:00:38.443Z"
        }
      }
    ]
  }
}

 

정상적으로 적재가 잘되는것을 확인할 수 있다.

이로써 docker로 구성된 elk로 로그 적재를 진행해봤다. 

 

관련 패키지는 github에 올려놓았다.

https://github.com/weduls/elk_with_nginx

docker를 사용하면서 그 편리함을 느끼고 있었다. 그리고 요 근래 it회사에서 docker와 kubernets를 이용하여 인프라를 운영을 하는 것을 많이 들었다.

나는 그런 환경을 접해보지는 못했기 때문에 정확하게 kuberntes가 무엇인지 잘 모른다. 그래서 이번 기회에 kubernets(이하 쿠버네티스)에 대한 기본 개념을 정리하고 설치해서 공부를 위한 초석을 닦아보자.

 

쿠버네티스 (kubernetes)

쿠버네티스는 도커 컨테이너 운영을 자동화 하기위한 오케스트레이션 도구이다. 구글에서 만들었으며 컨테이너를 운영하고 다루기 위한 api와 cli등을 제공한다. 컨테이너 배포 이외에도 효율적인 컨테이너 배치 및 스케일링, 로드밸런싱, 헬스 체크, secure등의 기능을 제공한다.

AWS ECS 와 GClound에서 도커 관리 기능을 제공하면서 컨테이너를 사용한 애플리케이션 개발이 점차 보급 되었다.  기존에도 docker에 스웜(swarm)이라는 기능이 있었으나 사용해 봤을 때 불편한 점이 많았는데 쿠버네티스가 더 편리하게 이 점이 보완되었다. 

 

쿠버네티스 설치 (MacOs)

실습을 진행할 컴퓨터가 mac os이기 때문에 mac에서 설치하는 방법을 알아보자. 우선 docker에서 preference에 들어가면 kubernetes 탭이 존재한다. 여기서 아래와 같이 체크하고 Apply를 누르면 설정이 우선 완료된다.

그리고 cli로 쿠버네티스를 다루기 위한 도구인 kubectl을 설치한다.

curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl
chmod +x kubectl
mv kubectl /usr/local/bin

위에 명령어를 통해 kubectl을 명령어로 등록해주는데 자세한 방법은 홈페이지에 잘 나와있다.

https://kubernetes.io/docs/tasks/tools/install-kubectl/

설치가 완료된 후 버전을 확인해보면 출력되면 정상적으로 설치가 된 것 이다.

 

단일 노드 쿠버네티스 클러스터 Minikube 설치

쿠버네티스 클러스터에는 여러 노드들이 존재하지만 실습을 위해서 단일 노드로 되어 있는 Minikube를 설치해서 사용한다. 맥 기준으로 brew로 설치하면 되고 자세한 방법은 아래를 참조하면 된다.

brew cask install minikube

https://kubernetes.io/ko/docs/tasks/tools/install-minikube/

 

쿠버네티스 구성요소

쿠버네티스로 실행되는 애플리케이션은 노드, 네임스페이스, 파드, 레플리카세트, 디플로이먼트 등 다양한 리소스가 함께 연동해 동작한다. 

그 구성 요소들에 대해 간단하게 정리해보자.

1. 노드

-> 노드는 쿠버네티스 클러스터의 관리 대상으로 등록된 도커 호스트로 컨테이너가 배치되는 대상이다. 노드는 마스터와 일반 노드들로 구성되는데 쿠버네티스 클러스터 전체를 관리하는 서버에는 마스터가 적어도 하나 이상 있어야 한다. 마스터는 클러스터를 상호 조정하는 장치이고 노드는 애플리케이션이 실제로 돌아가는 곳이다. 노드에는 여러 파드들이 위치할 수 있고 그 파드에는 컨테이너들이 존재한다.

구글 이미지 출처

또한 쿠버네티스는 노드의 리소스 사용 현황 및 배치 전략을 근거로 컨테이너를 적절하게 배치한다. 클러스터에 배치된 노드 수, 노드의 사양 등에 따라서 노드에 배치할 수 있는 컨테이너의 수를 결정한다. 클러스터의 처리 능력은 노드에 의해서 결정된다.

kubectl get nodes

 

 현재 노드는 아까 설치한 단일 노드 Minikubes가 마스터로 위치해 있는 걸 볼 수 있다.

 

2. 네임스페이스

쿠버네티스 클러스터 내부에는 여러개의 가상 클러스터를 만들 수 있는데 이 네임스페이스는 하나의 공간으로써 사람또는 상황에 따라서 나눠서 사용할 수 있다. 기본적으로는 default로 되어있다.

 

3. 파드 (pod)

 pod는 컨테이너가 모인 집합체로써 하나 이상의 컨테이너로 이루어 진다. nginx, was처럼 서로간의 연결고리가 있는 컨테이너들은 하나로 묶어 일괄배포한다. pod는 노드내에 배치되고 같은 파드는 여러 노드에 배치할 수도 있고 한 노드에 여러개 배치할 수도 있다. pod는 하나 또는 그 이상의 애플리케이션 컨테이너 그룹을 나타내는 쿠버네티스의 추상적 개념으로 컨테이너에 대해 서로 자원을 공유한다.

  • 볼륨과 같은 공유 스토리지
  • 클러스터 IP 주소와 같은 네트워킹
  • 컨테이너 이미지 버전 또는 사용할 특정포트와 같은 각 컨테이너가 동작하는 방식에 대한 정보

 

apiVersion: v1
kind: Pod
metadata:
  name: sample-echo
spec:
  containers:
  - name: nginx
    image: ddd/nginx:latest
    env:
    - name: BACKEND_HOST
      value: localhost:8080
    ports:
    - containerPort: 80
  - name: echo
    image: dd/echo:latest
    ports:
    - containerPort: 8080
    
 # 파드 yaml 예제

 

4. 레플리카 세트

파드를 하나만 사용하여 가동할 경우에 실제 가용성이 떨어지기 때문에 레플리카 세트를 만들어서 여러 파드를 함께 구성해준 것이다.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: echo
  labels:
    app: echo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: echo
  template: # template하단은 pad.yaml과 같음

 

5. 서비스 (servce, svc)

서비스를 이용해서 각 Pod에 있는 애플리케이션을 외부에서 접근하게 할 수있다.

쿠버네티스에서 서비스는 하나의 논리적인 파드 셋과 그 파드들에 대한 접근할 수 있는 정책을 정의하는 추상적인 개념이다. 서비스는 종속적인 파드들 사이를 느슨하게 결합되도록 도와준다. 서비스는 모든 쿠버네티스 오브젝트들과 같이 yaml로써 정의 할 수있다. 각 서비스가 되는 대상을 labelSelector를 통해 지정할 수 있다.

각 파드들이 고유의 IP를 갖고 있기는 하지만, 그 IP들은 서비스의 도움없이 클러스터의 외부로 노출될 수 없다. 서비스들은 애플리케이션들에 트래픽이 실릴 수 있도록 허용해준다. 서비스들은 ServiceSpec에서 type을 지정함으로써 다양한 방식들로 노출시킬 수 있다.

clusterIp(기본값) - 클러스터 내에서 내부 IP에 대해 서비스를 노출해준다. 이 방식은 오직 클러스터 내에서만 서비스가 접근될 수 있도록 해준다.

NodePort - NAT가 이용되는 클러스터 내에서 각각 선택된 노드들의 동일한 포트에 서비스를 노출시켜준다.
<NodeIP>:<NodePort>를 이용하여 클러스터 외부로부터 서비스가 접근할 수 있도록 해준다. CluserIP의 상위 집합이다.

LoadBalancer - (지원 가능한 경우) 기존 클라우드에서 외부용 로드밸런서를 생성하고 서비스에 고정된 공인 IP를 할당해준다. NodePort의 상위 집합이다.

ExternalName - 이름으로 CNAME 레코드를 반환함으로써 임의의 이름(스펙에서 externalName으로 명시)을 이용하여 서비스를 노출시켜준다. 프록시는 사용되지 않는다. 이 방식은 kube-dns 버전 1.7 이상에서 지원 가능하다.

 

6. 디플로이먼트 (deployment)

서비스, 파드, 레플리카세트의 집합체로써 애플리케이션의 기초가 되는 단위이다. 디플로이먼트 내부에는 서비스, 파드, 레플리카세트 등을 한번에 구성하여 적용 시킬 수 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo
  labels:
    app: echo
spec:
  replicas: 3
  selector:
    matchLabels
      app: echo
  template: # template는 pod와 동일

 

서비스와 디플로이먼트에 차이는 서비스는 내부에 여러 pod들을 외부와 연결해주는 역할을 담당하고 디플로이먼트는 쿠버네티스에서 돌아가는 pod들의 상태를 확인하면서 재시작 등등을 진행하는 담당을 한다.

다음 시간에는 실제 애플리케이션 하나를 이미지로 빌드하고 그것을 minikube에 디플로이먼트를 통해 파드로 배포를 진행해본다. 그리고 스케일적용과 이미지 변경 시 자동으로 새로 배포되는 블루그린 배포도 적용해보자.

 

참고 싸이트

https://kubernetes.io/ko/docs/tasks/administer-cluster/highly-available-master/
https://kubernetes.io/ko/docs/tutorials/kubernetes-basics/deploy-app/deploy-intro/
https://kubernetes.io/ko/docs/concepts/overview/working-with-objects/labels/
https://devopscube.com/kubernetes-deployment-tutorial/

엘라스틱서치 7.0이 출시했다.

엘라스틱서치 7.0에는 kibana UI변경과 multi mapping type 제거 등의 이슈가 있다.

우선 달라진점을 확인하기 위해 docker에 설치해보자.

설치

elasticsearch

docker run --name elastic7.0 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.0.0

 

kibana

docker run -d --rm --link elastic7.0:elastic-url -e "ELASTICSEARCH_HOSTS=http://elastic-url:9200" -p 5601:5601 --name kibana7.0 docker.elastic.co/kibana/kibana:7.0.0

설치가 끝나고 프로세스를 확인해보면 elastic과 kibana가 올라가 있는 것을 확인할 수있다.

 

kibana UI 확인

키바나에 접속해 보니 엄청 깨끗하다. 몬가 되게 플랫해진 요새 디자인을 하고 있다. 5부터 6까지 변할때도 몬가 키바나가 멋있어졌다고 느꼈는데 이번에 7버전은 더 멋진 것 같다.

그리고 더 대단한 건 요새 대세인 다크모드가 지원된다.

다크모드는 setting > kibana > advanced settings > dark mode 위치에 가면 다음과 같은 설정 하는 부분이 있다.

설정 후 새로고침을 하면 적용되는데 화면이 멋있다.

 

그리고 멀티 맵핑 타입이 제거되고 등등 다른 변경사항이 있는데 아래 링크를 확인하면 된다.

근데 릴리즈하면서 마지막 글에 보면 기존에 elastic을 6버전 이상 사용하고 있으면 굳이 업데이트 할 필요는 없다고 한다 ㅋㅋ

 

변경사항

https://www.elastic.co/blog/elastic-stack-7-0-0-released

집에서 개인적으로 사용할 nas를 구입했다.


간단하게 파일 저장하고 공유하고 docker를 사용해서 mysql, redis등을 사용하기 위해서 구입했다.


위메프에서 ds118 20만원에 구입하고 wd 1tb nas용 하드를 구입했다. 구입은 맥을 사면서 적립되었던 포인트를 사용해서 실질적으로 구매비용은 10만원정도 들었다.


사실 하드를 nas용으로 사야하나 싶었으나 인터넷에 알아보니 nas에 일반 하드를 설치하면 고장날 확률이 많다고 한다. 대표적으로 nas에 경우 24시간 켜져있고 진동이 하드에 전달되기 때문에 고장난 확률이 높아진다고 한다. 그래서 nas 하드를 위메프에서 7만원에 구매했다.


박스는 안전하게 포장되어 왔다.

생각보다 가볍다. 비싼 제품인데 봉인 씰이 없는건 아쉽다.


내부를 뜯어보면 어댑터와 랜선 그리고 본체가 전부이다. ㅋㅋ

정말 별거없는데 드럽게 비싸다.


이제 이 제품들을 이용해서 nas를 구축해보자.

우선 같이 구입한 1TB NAS용 하드디스크를 뜯어보자.

아주 튼튼하다. 오랜만이다 맨날 2.5인치 ssd만 보다가 오랜만에 3.5인치 하드를 보니 낯설기도 하다 ㅋㅋ

순서에 맞게 하드를 자리에 꼽아준다.


같이 동봉된 나사를 각면마다 3개씩 꼽아준다.

설치가 완료되면 어댑터랑 랜선을 꼽고 자리에 배치해준다. 크기는 생각보다 작아서 책상위에 올려놓아도 아담하고 소음도 전혀없다.

이제 설정을 위해서 에이전트 화면에 접속해야하는데 같은 망에서 아래 기재되어 있는  url에 접속한다.



find.synology.com에 접속하게 되면 현재 같은 망에 연결되어있는 NAS 장비가 보여지고 DSM(synology os)를 설치할 수 있게 가이드가 주어진다.


하드디스크를 선택하고 설치를 진행한다.


설치는 사람들은 10분정도 걸린다고 하는데 나는 2분도 안걸린 것 같다.


설치가 완료되면 보안 작업을 해줘야하는데 대표적으로 DSM 포트변경, 접속 지역 제한, 알려진 포트 (22, 21....) 변경 및 차단, admin 계정 비활성화 등등 진행해야한다.


생각보다 좋다. 이거보다 더 느린 nas인 ds119j나 ds218j등을 사용했으면 큰일날뻔했다. 이것도 그렇게 빠르지 않아서 ㅋㅋㅋ 


아직 낯설고 어색하지만 잘 써보자.


  1. Favicon of https://kyome.tistory.com BlogIcon KYOME 2019.07.11 16:35 신고

    혹시 도커 설치하셨나요? ds118 글보고 도커 설치 할 수있겠다 싶어서 구매했는데..도저히 방법을 모르겠네요 도와주세요 ㅜㅜ

  2. Favicon of https://kyome.tistory.com BlogIcon KYOME 2019.07.11 16:38 신고

    방법이 없네요 ㅠㅠ 118로 우분투 돌릴 수 있는 방법은 없겠죠??

    • Favicon of https://wedul.site BlogIcon 위들 wedul 2019.07.11 16:38 신고

      어려울거에요. 저도 요새들어 더 좋은 나스 살걸 후회하고 있어요 ㅠ

  3. Favicon of https://kyome.tistory.com BlogIcon KYOME 2019.07.11 16:41 신고

    할 수 있는게 너무 제한적이네요..ㅜㅜ 알겠습니다 고마워요

Elasticsearch를 Spring Boot에서 작업을 하는 간단한 정리를 해보자.


1. Library 추가

Elasticsearch를 사용하기 위해서는 spring-data-elasticsearch 라이브러리가 추가되어야 한다. 

gradle에 추가해보자.

1
2
3
4
5
6
7
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    compileOnly "org.projectlombok:lombok:1.16.16"
}
 
cs


spring-data-elasticsearch 버전별로 호환되는 elasticsearch가 상이하니 참고

spring data elasticsearchelasticsearch
3.2.x6.5.0
3.1.x6.2.2
3.0.x5.5.0
2.1.x2.4.0
2.0.x2.2.0
1.3.x1.5.2


2. Configuration

Elasticsearch에 접속하기 위한 Configuration을 정의해준다.

Elasticsearch  접속을 위해서는 host, port, cluster name이 필요하다. cluster name을 알아야 하는데 docker에 설치 한 경우 여기서 확인하면 된다.

우선 docker exec -it elastic bash로 콘솔에 접속한 후에 elasticsearch.yml에 적혀있는 cluster name을 확인한다.

그리고 application.properties에 설정 내용을 적어준다.

1
2
3
4
elasticsearch.host=127.0.0.1
elasticsearch.port=9300
elasticsearch.cluster_name=docker-cluster
spring.main.allow-bean-definition-overriding=true
cs

그리고 EnableElasticsearchRepositories 애노테이션을 설정한 Configuration 클래스를 만들어준다.

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
package com.elasticsearch.study.configuration;
 
import org.springframework.beans.factory.annotation.Value;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
 
import java.net.InetAddress;
 
/**
 * Elasticsearch Configuration
 *
 * @author wedul
 * @since 2019-02-09
 **/
@EnableElasticsearchRepositories
@Configuration
public class ElasticConfiguration {
 
  @Value("${elasticsearch.host}")
  private String host;
 
  @Value("${elasticsearch.port}")
  private int port;
 
  @Value("${elasticsearch.cluster_name")
  private String clusterName;
 
  @Bean
  public Client client() throws Exception {
    Settings settings = Settings.builder().put("cluster.name", clusterName).build();
 
    TransportClient client = new PreBuiltTransportClient(settings);
    client.addTransportAddress(new TransportAddress(InetAddress.getByName(host), port));
    return client;
  }
 
  @Bean
  public ElasticsearchOperations elasticsearchTemplate() throws Exception {
    return new ElasticsearchTemplate(client());
  }
 
}
 
cs


3. DTO 생성

Elasticsearch에서 Document 내용을 담을 DTO를 만들어주고 @Document 애노테이션을 달고 index name과 type을 정의해준다.

@Id 어노테이션이 붙은 필드는 각 Doucument에 붙어있는 _id 값이다.

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
package com.elasticsearch.study.dto;
 
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
 
/**
 * studyFor
 *
 * @author wedul
 * @since 2019-02-09
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Document(indexName = "wedul_play", type = "story")
public class WedulPlay {
 
  @Id
  private String id;
  private String title;
  private String user;
  private long startAt;
  private long endAt;
 
}
 
cs


4. Repository

JPA를 사용하면 익숙할 패턴으로 Elasticsearch에서도 ElasticsearchRepository가 존재한다. 사용방법은 JPA와 동일하게 저장할 때는 save, 조회할 때는 find(), findByUser()등으로 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.elasticsearch.study.repository;
 
import com.elasticsearch.study.dto.WedulPlay;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
 
/**
 * study
 *
 * @author wedul
 * @since 2019-02-09
 **/
@Repository("wedulPlayRepository")
public interface WedulPlayRepository extends ElasticsearchRepository<WedulPlay, String> {
 
  WedulPlay findByUser(String user);
  
}
 
cs


5. Service

지금 테스트 하는 부분에서는 크게 비즈니스 로직에 들어갈 소스가 없다.

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
package com.elasticsearch.study.service;
 
import com.elasticsearch.study.dto.WedulPlay;
import com.elasticsearch.study.repository.WedulPlayRepository;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
/**
 * study
 *
 * @author wedul
 * @since 2019-02-09
 **/
@AllArgsConstructor
@NoArgsConstructor
@Service
public class WedulPlayService {
 
  private WedulPlayRepository wedulPlayRepository;
 
  public void save(WedulPlay play) {
    wedulPlayRepository.save(play);
  }
 
  public List<WedulPlay> findAll() {
    return Lists.newArrayList(wedulPlayRepository.findAll());
  }
 
  public WedulPlay findByUser(String user) {
    return wedulPlayRepository.findByUser(user);
  }
 
}
 
cs


6. Test 코드

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
76
77
78
package com.elasticsearch.study.wedulplay;
 
import com.elasticsearch.study.dto.WedulPlay;
import com.elasticsearch.study.repository.WedulPlayRepository;
import com.elasticsearch.study.service.WedulPlayService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
 
import java.util.List;
 
import org.hamcrest.core.IsNull;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
 
/**
 * wedul play document 조회
 *
 * @author wedul
 * @since 2019-02-09
 **/
@RunWith(SpringRunner.class)
@SpringBootTest
public class WedulPlayTest {
 
  WedulPlayService wedulPlayService;
 
  @Autowired
  @Qualifier("wedulPlayRepository")
  WedulPlayRepository wedulPlayRepository;
 
  @Before
  public void setup() {
    wedulPlayService = new WedulPlayService(wedulPlayRepository);
  }
 
  @Test
  public void whenValidParameter_thenSuccessFind() {
    List<WedulPlay> list = wedulPlayService.findAll();
 
    assertNotNull(list);
  }
 
  @Test
  public void whenValidParameter_thenSuccessSave() {
    Exception ex = null;
 
    try {
      wedulPlayService.save(WedulPlay.builder().title("안녕 이건 테스트야").user("위들").startAt(1242421424).endAt(23214124).build());
    } catch (Exception exception) {
      ex = exception;
    }
 
    assertTrue(null == ex);
  }
 
  @Test
  public void whenValidParameter_thenSuccessFindByUser() {
    Exception ex = null;
 
    try {
      WedulPlay play = wedulPlayService.findByUser("위들");
 
      assertThat(play, is(IsNull.notNullValue()));
    } catch (Exception exception) {
      ex = exception;
    }
 
    assertTrue(null == ex);
  }
 
 
}
 
cs


설정이 간단하다. 

나중에 이용해 먹어야지


자세한 소스코드는 여기 참조

https://github.com/weduls/spring_elastic

+ Recent posts