글쓴이 보관물: totoli78

Apache에서 gzip을 이용한 압축전송하기

웹서비스 최적화 방법중에 하나로 gzip 압축전송이 있습니다.
gzip은 파일 압축에 쓰이는 응용 소프트웨어로 GNU zip의 준말이며 무료 소프트웨어이다(출처: 위키백과)
이를 사용하기 위해서는 사용하는 브라우저가 지원을 해야합니다.
(브라우저에서 request headers > “Accept-Encoding”에 gzip이 포함되어 있어야 합니다.)

마찬가지로 서버에서도 gzip으로 전송해야 합니다.
(response headers > “Content-Encodding:gzip”)

배경

일반적인 Http Request & Response와 gzip사용시 차이점을 간단히 설명하면 다음과 같습니다.

1.브라우저에서 index.html을 서버에 요청합니다.

2.서버는 해당 파일을 찾습니다.

3.파일을 요청한 브라우저로 전송합니다(100KB).

4.브라우저는 해당 파일을 표시합니다.

 

 

1.브라우저에서 index.html을 서버로 요청시 gzip을 지원한다는 내용을 서버로 전달합니다.

2.서버는 해당 파일을 찾습니다.

3.gzip으로 파일을 압축해서 보냅니다(30KB).

4.브라우저에서 압축된 파일을 풀고 표시합니다.

테스트 환경

  • 서버 OS: Windows7
  • 웹서버: Apache 2.4
  • 브라우저: IE9, 크롬, Firefox, Safari
환경설정

1.Apache httpd.conf 파일 열기
2.LoadModule 영역에서 아래내용에 주석처리 되었으면 해제합니다.

LoadModule deflate_module modules/mod_deflate.so
LoadModule headers_module modules/mod_headers.so
LoadModule filter_module modules/mod_filter.so
3.적당한 위치에 아래 내용을 추가합니다.
<IfModule mod_deflate.c>

        AddOutputFilterByType DEFLATE text/plain
        AddOutputFilterByType DEFLATE text/html
        AddOutputFilterByType DEFLATE text/xml
        AddOutputFilterByType DEFLATE text/css
        AddOutputFilterByType DEFLATE application/xml
        AddOutputFilterByType DEFLATE application/xhtml+xml
        AddOutputFilterByType DEFLATE application/rss+xml
        AddOutputFilterByType DEFLATE application/javascript
        AddOutputFilterByType DEFLATE application/x-javascript

        DeflateCompressionLevel 9

        BrowserMatch ^Mozilla/4 gzip-only-text/html  # Netscape 4.xx에는 HTML만 압축해서 보냄
        BrowserMatch ^Mozilla/4\.0[678] no-gzip  # Netscape 4.06~4.08에는 압축해서 보내지 않음
        BrowserMatch \bMSIE !no-gzip !gzip-only-text/html  # 자신을 Mozilla로 알리는 MSIE에는 그대로 압축해서 보냄
        
        ##예외 설정
        SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|bmp|zip|gz|rar|7z)$ no-gzip dont-vary
        
        ####로그설정.
        ##DeflateFilterNote Input instream
        ##DeflateFilterNote Output outstream
        ##DeflateFilterNote Ratio ratio
        ##
        ##LogFormat '"%r" %{outstream}n/%{instream}n (%{ratio}n%%)' deflate
        ##CustomLog logs/deflate_log deflate

4.아파치를 restart 시킨 후 아래 사이트에서 gzip전송이 정상동작하는지 테스트 합니다.

http://www.whatsmyip.org/http-compression-test/

 

위 결과 페이지에서는 특정페이지가 84.5% 절감되고 있습니다.

Response Headers 내용은 아래와 같습니다.

 

 

 

 

 

※ 3번 “로그설정” 주석해제 후 apache logs폴더에 생성된 deflate_log파일을 보면 압축률을 확인할 수 있습니다.

.

"GET /nexacro14lib/framework/SystemBase.js HTTP/1.1" 23987/176486 (13%)
"GET /nexacro14lib/framework/Platform_HTML5.js HTTP/1.1" 18786/138984 (13%)
"GET /nexacro14lib/framework/ErrorDefine.js HTTP/1.1" 4886/18684 (26%)
"GET /nexacro14lib/framework/CssObjs.js HTTP/1.1" 25266/271916 (9%)
"GET /nexacro14lib/framework/BasicObjs.js HTTP/1.1" 9152/50003 (18%)
"GET /nexacro14lib/framework/Platform.js HTTP/1.1" 58726/370077 (15%)
"GET /nexacro14lib/framework/SystemBase_HTML5.js HTTP/1.1" 50261/334443 (15%)
"GET /nexacro14lib/component/CompBase/CompBase.js HTTP/1.1" 28574/167299 (17%)
"GET /nexacro14lib/component/CompBase/CompEventBase.js HTTP/1.1" 17397/255753 (6%)

미리 gzip으로 압축후 사용할 순 없을까?
gzip 사용시 단점은 요청시 gzip으로 압축하는 과정으로 인해
서버의 CPU사용량이 gzip을 사용하지 않을 때 보다 증가한다는 것이다.
그렇다면 거의 변할 일이 없는 file은 미리 압축해서 사용하는것도 괜찮지 않을까?
1.gzip으로 압축하기
Online YUI Compressor에 접속 후 아래그림 처럼 소스를 gz파일로 변환한다.
① 원하는 소스를 붙여넣기 한다.
② 결과는 “Redirect to gzipped output”을 선택한다.
③ “Compress”버튼을 누르면 잠시 후 파일을 저장할 수 있는 팝업창이 표시된다.
④ “gz”확장자로 원하는 위치와 파일명으로 저장합니다.

 

 

2.해당 파일을 영역안에 포함시킵니다.
<script type="text/javascript" src="./pkg/EcoBasic.js.gz"></script>
3.위와 같이 하고 해당페이지에 접속하면 아래처럼 log가 발생하고 정상적으로 load되지 않습니다(크롬).
Content-Encoding:gzip
Content-Type:application/x-gzip

Resource interpreted as Script but transferred with MIME type application/x-gzip: "http:// ~ /pkg/EcoBasic.js.gz". 
4.Apache httpd.conf 파일의 LoadModule 영역에서 아래내용에 주석처리 되었으면 해제합니다.
​LoadModule dir_module modules/mod_dir.so
  아래 2줄을 주석처리 되어있으면 해제합니다.
AddEncoding x-compress .Z
AddEncoding x-gzip .gz .tgz
  아래 2줄을 주석처리 합니다.
#AddType application/x-compress .Z
#AddType application/x-gzip .gz .tgz
  아래 내용도 추가해야 한다고 나와있었는데 실제로 해보니 적용하지 않아도 처리되었습니다(참고하세요).
  :gz 확장자에 대해서 강제로 content-encoding을 설정하는 내용입니다.
# root .
<Directory /~/EcoPkg>
    Options Indexes MultiViews FollowSymLinks
    #AllowOverride None
    #Order deny,allow
    #Deny from all
    #Allow from 127.0.0.0/255.0.0.0::1/128 

    AddEncoding gzip gz
    <FilesMatch "\.gz$">
      ForceType text/plain
      Header set Content-Encoding: gzip
    FilesMatch>
    
##   <FilesMatch "\.css\.gz$">
##        ForceType text/css
##        Header set Content-Encoding: gzip
##    FilesMatch>
Directory>
5.설정 후 아파치 restart후에 Response Headers를 확인하면 다음과 같습니다.

 

 

 

 

 

마치며…

※ RUNTIME(2014,9,3,1 버전 기준)에서는 지원하지 않습니다.
이미지와 PDF파일은 gzip으로 압축하지 않는다(이미 압축되어 있음).
gzip을 지원하는 브라우저 목록(출처: webmasters.stackexchange.com, 참고: schroepl.net)
  • Netscape 6+ (Netscape 4-5 does, but with some bugs).
  • Internet Explorer 5.5+ (July 2000) and IE 4 if set to HTTP/1.1.
  • Opera 5+ (June 2000)
  • Firefox 0.9.5+ (October 2001)
  • Chrome since forever
  • Safari since forever (as far as I can tell)
이 글에서는 Apache httpd.conf 파일을 이용한 예제가 작성되었지만 .htaccess 파일을 이용할 수 도 있다.

시놀로지 4TB디스크를 8TB디스크로 교체하기

DS918+와 4TB 하드디스크 한 개를 사용하고 있는데요. 8TB 하드디스크 네 개로 교체하고 싶어요.

  • 4TB 하드디스크의 공간이 가득 찼습니다. 8TB 하드디스크 4개 장착 후 raid5를 만들어서 24TB의 공간을 사용하고 싶습니다.
  • 나스는 24시간 계속 사용합니다. 8TB 하드디스크로 교체하는 동안에도 나스 접속이 끊기지 않아야 합니다.

8TB 하드디스크 4개를 raid5로 만듭니다.

DS920+는 핫스왑 기능을 제공하므로 나스를 계속 사용하면서 하드디스크를 교체할 수 있습니다. 8TB 하드디스크 4개를 사용하여 raid5가 완성되는 데는 5일 정도 소요 되었지만 실제로 작업한 시간(하드디스크 교체하기, 마우스 클릭하기 등)은 10분 정도밖에 되지 않습니다.

  • 현재는 1번 베이에 4TB 하드디스크가 장착되어 있고 나머지 베이는 비어 있습니다. 스토리지 풀은 shr방식입니다. 다음 과정을 거쳐서 8TB 하드디스크 4개로 raid5를 만듭니다.
  • 과정 1
    4TB 디스크 1개와 8TB 디스크 1개를 레이드1으로 만듭니다. 레이드1이 된 이후에는 볼륨의 크기가 4TB가 됩니다.
  • 과정 2
    4TB 디스크를 8TB 디스크로 교체 후 수리를 진행합니다. 수리가 끝나면 8TB 디스크 2개로 raid1이 되고 볼륨의 크기가 8TB가 됩니다.
  • 과정 3
    8TB 디스크 2개로 raid1 상태에서 8TB 디스크 2개를 추가하여 raid5로 변경합니다. raid5가 된 이후에는 볼륨의 크기가 24TB가 됩니다.

과정 1. 4TB와 8TB로 raid1 만들기.

  • 2번 베이에 8TB 디스크를 장착 후 “스토리지 풀 > 작업 > 드라이브 추가”를 클릭합니다. 드라이브 2를 선택합니다.
  • 스토리지 풀 화면에서 1번 베이의 4TB 디스크와 2번 베이의 8TB 디스크가 사용된 것을 볼 수 있습니다.
  • 스토리지 풀 확장하는 작업이 완료되면 스토리지 풀의 총 용량은 4TB, 볼륨의 총 용량도 4TB가 됩니다.
  • 스토리지 풀 확장이 완료되는데 13시간 정도 소요되었습니다.

과정 2. 4TB 교체 후, 8TB와 8TB로 raid1 만들기.

  • HDD/SSD 화면에서 4TB 하드디스크에 해당하는 드라이브(이번 사례에서는 드라이브 1)를 선택 후 “작업 > 비활성화”를 클릭합니다.
  • 드라이브 1 옆에 “비활성화됨”이 표시되면 4TB 하드디스크를 제거하고 새로운 8TB 디스크를 장착합니다. (기존 4TB 드라이브와 미러링 된 드라이브 외 새로운 드라이브 추가)
  • “스토리지 풀 > 작업 > 수리”를 클릭합니다. 드라이브 1을 선택합니다.
  • 2번 베이 하드디스크(Drive 2)의 내용을 1번 베이 하드디스크(Drive 1)로 복제하므로 Drive 2는 읽기작업이 발생하고 Drive 1은 쓰기작업이 발생하는 것을 리소스 모니터에서 확인할 수 있습니다.
  • 수리가 완료되는데 15시간 정도 소요되었습니다
  • 수리가 완료되면 스토리지 풀의 총 용량은 8TB로 늘어나지만, 사용한 용량은 여전히 4TB 그대로입니다. shr방식을 사용하는 경우에는 볼륨의 크기가 저절로 늘어나지 않기 때문입니다.        (DSM 6.2.3 부터는 자동으로 최대 크기로 늘어남)
  • 볼륨의 크기를 8TB로 늘립니다. “볼륨 > 작업 > 구성”을 클릭하고 “최대” 버튼을 클릭합니다. 그러면 “할당된 크기 수정(GB)” 칸의 숫자가 스토리지 풀이 사용할 수 있는 최대 용량으로 입력됩니다. “확인” 버튼을 클릭하면 10초 정도 후에 볼륨의 총 용량이 8TB로 변경됩니다.

과정 3. 8TB 디스크 2개 추가하기.

  • 3번 베이, 4번 베이에 8TB 디스크를 각각 장착한 다음 “스토리지 풀 > 작업 > 드라이브 추가”를 클릭합니다. 드라이브 3, 드라이브 4를 선택하고 다음을 클릭합니다. “설정 확인” 화면에서 총 용량이 21.82TB로 나오는지 확인합니다.
  • 스토리지 풀 확장이 완료되는데 90시간 정도 소요되었습니다.
  • 스토리지 풀 확장하는 작업이 완료되면 스토리지 풀의 총 용량은 24TB로 늘어나지만, 사용한 용량은 여전히 8TB 그대로입니다.  사용하지 않은 용량 16TB를 볼륨에 추가해야 합니다.
  • “볼륨 > 작업 > 구성”을 클릭하고 “최대” 버튼을 클릭합니다.
  • 최종적으로 볼륨의 크기가 24TB가 되었습니다.

8TB 디스크의 파티션 모습.

지금까지의 과정은 이벤트 로그와 알림에 기록되어 있습니다. 기록을 보면 “과정 3. 8TB 디스크 2개 추가하기”에서 동기화 작업이 네 번 나오는데요. 4TB 하드디스크로 스토리지 풀을 최초로 만들때 shr방식으로 설정했기 때문입니다. shr방식에서 더 큰 용량의 하드디스크로 교체하면 하드디스크의 파티션이 최초의 스토리지 풀 크기(4TB)와 나머지 크기(8TB에서 최초의 스토리지 풀 크기를 제외한 나머지)로  나누어집니다.  그래서 8TB디스크 4개 모두 두 개의 파티션으로 나누어져 있습니다. 또한 raid1에서 raid5로 변경하는 경우에 디스크 두 개를 한 번에 추가해도 변경작업(동기화 작업)은 한 개씩 이루어집니다. 그래서 다음과 같이 총 네 번의 동기화 작업이 진행됩니다.

  • 첫 번째 : 디스크 3개의 4TB  파티션을 동기화.
  • 두 번째 : 디스크 3개의 4TB  파티션을 제외한 부분 동기화.
  • 세 번째 : 디스크 4개의 4TB  파티션을 동기화.
  • 네 번째 : 디스크 4개의 4TB  파티션을 제외한 부분 동기화.

네 번의 동기화가 모두 끝나면 두 개의 raid5 장치가 생성됩니다. 하나는 4TB 크기 네 개로 raid5이므로 사용가능한 용량은 12TB가 되고, 다른 하나는 8TB에서 4TB를 제외한 크기(결국 4TB 크기) 네 개로 raid5이므로 마찬가지로 12TB가 됩니다. 그리고 두 개의 12TB 공간을 LVM을 사용하여 묶어서 총 24TB 크기의 스토리지 풀이 됩니다.

  • 지금까지의 과정을 “모든 알림”에서 볼 수 있습니다.
  • fdisk 명령어를 사용해서 파티션이 어떻게 나눠져 있는지 확인할 수 있습니다.
  • sda5, sdb5, sdc5, sdd5가 md2에 사용되고 sda6, sdb6, sdc6, sdd6이 md3에 사용되었습니다.

Docker로 Hadoop 테스트 환경 구축하기 – HDFS

Docker와 테스트 환경

Docker는 애플리케이션을 컨테이너화해서 이미지로 만들어 배치하고 실행할 수 있는 환경을 제공하는 컨테이너 도구1이다.
격리된 파일 시스템을 갖는 컨테이너 특성 덕분에, 운영 환경에 대한 프로비져닝 뿐만 아니라 Docker가 설치된 환경에서 컨테이너화된 애플리케이션을 즉시 구동시켜서 테스트할 수 있는 환경을 구성하는데에도 유용하게 쓰인다.

오픈소스를 포함한 다양한 애플리케이션을 로컬 환경에 설치하고 구성하면서 테스트하다보면(주로 개발자의 노트북), 어느새 로컬 환경은 지저분해지고 몇 해 전에 설치한 애플리케이션이 자동 재시작되어 리소스를 점유하게 된다. 환경 설정 파일은 언제 어떻게 바꿨는지도 모른다. VM 환경으로 OS를 완전히 격리시켜 구성하자니 구동하는데 시간이 오래 걸리고 구동한 이후 점유하는 리소스가 노트북이 감당하기에는 참 크다.

Docker는 이런 환경에서 최적의 도구이다. 컨테이너를 어떻게 구성할지 애플리케이션마다 설정 파일을 생성하고, 서로 상호작용하는 애플리케이션인 경우 다시 실행과 관련된 설정 파일을 생성해서 여러 개의 애플리케이션을 순식간에 실행하고 다시 종료시킬 수 있다.

이 포스트에서는 Hadoop HDFS를 Docker로 컨테이너화 헤서 로컬에 테스트 환경으로 구성하는 방법을 설명한다.

HDFS

하둡 분산 파일 시스템(HDFS)은 마스터 노드인 NameNode와 워커 노드인 DataNode로 이루어져있다. 각각 JVM 위에서 동작하는 자바 애플리케이션으로, 두 애플리케이션만 동작을 하면 HDFS를 구성해서 읽고 쓸 수 있다.
실행 환경은 일반적은 GNU/Linux 그리고 Windows 환경을 지원하며, Hadoop 2.7~2.x2 버전은 Java 7, 8을 지원한다.3

클라이언트에서 각 애플리케이션과 직접 통신하는 포트 정보는 다음 표와 같다.

애플리케이션 기본포트 프로토콜 상세설명 관련 설정 항목
NameNode 50070 HTTP HDFS 상태 및 파일 시스템 조회용 Web UI dfs.http.address
9000 IPC 파일 시스템 메타데이터 관련 작업용 fs.defaultFS
DataNode 50075 HTTP 상태 및 로그 조회용 Web UI dfs.datanode.http.address
50010 TCP 데이터 전송 dfs.datanode.address
표 1. 클라이언트에서 HDFS와 직접 통신할 때 사용하는 포트 정보

이 자료들을 가지고 이제 본격적으로 Docker 컨테이너화를 진행해보자.

Dockerize

애플리케이션을 Docker에서 사용하는 설정 파일인 Dockerfile 양식에 맞게 구성한다. 이 설정 파일이 있으면 Docker 이미지 빌드가 가능하고, 생성한 이미지를 어디에서든 당겨와서(pull) 실행할 수 있다.

사실 이미지를 생성하는 것은 이 설정 파일 없이 직접 기반이 되는 OS 이미지로부터 컨테이너를 실행해서 쉘로 접속, 필요한 작업을 하고 이미지를 커밋해도 된다.
하지만 이 방식은 다음과 같은 문제점이 있다:

  • 이미지가 어떻게 생성되었는지 기록이 남지 않음
  • 따라서 작업한 내용을 다시 되돌리거나, 몇 가지 설정을 변경해서 새로운 이미지를 생성하기 어려움
  • 그리고 생성된 이미지를 신뢰하기 어려움. 따라서 다른 사람과 공유하는데에도 적합하지 않음

블록을 쌓아 올리듯 이미지를 만드는 Docker 세계에서는, 처음에는 조금 번거로워 보이더라도 Dockerfile로 이미지를 만드는 것에 익숙해지는 것이 좋다.

Base

NameNode 이미지를 만들기 전에, 먼저 각 컴포넌트의 밑바탕이 되는 이미지를 구성한다. 밑바탕 이미지는 다음과 같은 내용을 포함헤야 할 것이다.

  • Hadoop binary
  • Java
  • Common configurations

여기에서는 현재 2019년 8월 기준 Hadoop 2버전의 최신 버전인 2.9.2 버전을 사용했다. Hadoop 2는 Java 7 이상을 지원하므로 Java 8을 선택했다. 마지막으로 공용 설정은 core-site.xml, hdfs-site.xml을 포함하도록 구성할 것이다.

먼저 다음과 같은 디렉토리 구조로 폴더와 파일을 생성한다.

.
└── hadoop
    ├── base
    │   ├── Dockerfile
    │   └── core-site.xml
    ├── namenode
    │   ├── Dockerfile
    │   ├── hdfs-site.xml
    │   └── start.sh
    └── datanode
        ├── Dockerfile
        ├── hdfs-site.xml
        └── start.sh

hadoop의 하위 폴더인 base, namenode, 그리고 datanode 폴더와 그 하위에 다시 위치한 개별 Dockerfile은 각 항목이 이미지로 빌드될 것임을 보여준다.

baes/Dockerfile의 내용을 다음과 같이 작성한다.

# 기반이 되는 이미지를 설정. 여기서는 ubuntu 18 기반인 zulu의 openjdk 8버전을 선택
FROM azul/zulu-openjdk:8

# 바이너리를 내려받기 위해 설치
RUN apt-get update && apt-get install -y curl 

ENV HADOOP_VERSION=2.9.2
ENV HADOOP_URL=http://mirror.apache-kr.org/hadoop/common/hadoop-$HADOOP_VERSION/hadoop-$HADOOP_VERSION.tar.gz

# Hadoop 2.9.2 버전을 내려받고 /opt/hadoop에 압축 해제
RUN curl -fSL "$HADOOP_URL" -o /tmp/hadoop.tar.gz \
    && tar -xvf /tmp/hadoop.tar.gz -C /opt/ \
    && rm /tmp/hadoop.tar.gz

# 데이터 디렉토리 생성 및 설정 폴더의 심볼릭 링크 생성
RUN ln -s /opt/hadoop-$HADOOP_VERSION /opt/hadoop \
    && mkdir /opt/hadoop/dfs \
    && ln -s /opt/hadoop-$HADOOP_VERSION/etc/hadoop /etc/hadoop \
    && rm -rf /opt/hadoop/share/doc

# 로컬의 core-site.xml 파일을 복제
ADD core-site.xml /etc/hadoop/

# 실행 환경에 필요한 환경 변수 등록
ENV HADOOP_PREFIX /opt/hadoop
ENV HADOOP_CONF_DIR /etc/hadoop
ENV PATH $HADOOP_PREFIX/bin/:$PATH
ENV JAVA_HOME /usr/lib/jvm/zulu-8-amd64

기반 이미지인 azul/zulu-openjdk:8는 ubuntu 18 기반이라서 이미지 크기가 315MB로 꽤 크다.
이 크기를 작게 줄이려고 alpine 기반으로 생성해서는 안된다. Hadoop은 일반적인 GNU/Linux 환경에서 동작할 수 있도록 개발되었기 때문이다.

base/core-site.xml은 다음과 같이 작성한다.

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://namenode:9000/</value>
    <description>NameNode URI
    </description>
  </property>  
</configuration>

fs.defaultFS 설정은 NameNode 위치를 찾는 중요한 설정으로, DataNode 뿐만 아니라 각종 애플리케이션에서 NameNode에 파일 읽기/쓰기 요청을 할 때 사용되는 항목이다.
URI의 호스트 이름이 namenode로 설정되었는데, NameNode 컨테이너의 호스트 이름을 namenode로 지정할 것이다.

이제 터미널에서 base 디렉토리로 이동해서 baes 이미지를 생성해보자.

cd base
docker build -t hadoop-base:2.9.2 .

현재 위치한 폴더의 Dockerfile을 사용해서 docker 이미지를 빌드하겠다는 명령어이다. Dockerfile을 작성한대로 각 라인이 하나의 빌드 단계가 되어 명렁어를 실행하는 로그가 출력된다.

빌드가 완료되면 다음 명렁어로 로컬에 생성된 이미지를 확인할 수 있다.

docker images

REPOSITORY        TAG                 IMAGE ID            CREATED             SIZE
hadoop-base       2.9.2               fab79eab7d71        2 mins ago          1.2GB

이 이미지는 다음과 같은 이미지 계층 구조를 갖는다. 간단한 구조를 갖지만 이 base 이미지를 통해 모든 Hadoop 컴포넌트의 기반으로 사용할 수 있다.

Hadoop base 이미지 계층그림 1. Hadoop base 이미지 계층 구조. OS → Runtime → App base의 간단한 구조이다.

NameNode

Java와 Hadoop binary를 가지고 있는 base 이미지를 생성했으니, 이제 NameNode 이미지를 생성해보자.
NameNode 구성시 고려해야할 부분은 다음과 같다.

  • FsImage, EditLog를 저장하는 로컬 파일 시스템 경로
  • NameNode용 hdfs-site.xml 설정
  • NameNode 최초 구동 확인 및 네임스페이스 포맷

먼저 hdfs-site.xml 파일을 다음과 같이 작성한다.
dfs.namenode.name.dir은 FsImage, EditLog 파일을 저장하는 경로이다. HDFS 파일 블록 크기를 결정하는 dfs.blocksize 항목은 바이트 수로 여기서는 테스트를 위해 기본 값보다 훨씬 작은 10MB로 설정했다. (기본값은 128MB) 이 외의 항목들은 hdfs-default.xml 파일을 참고한다.

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
  <property>
    <name>dfs.namenode.name.dir</name>
    <value>file:///opt/hadoop/dfs/name</value>
  </property>
  <property>
    <name>dfs.blocksize</name>
    <value>10485760</value>
  </property>
  <property>
    <name>dfs.client.use.datanode.hostname</name>
    <value>true</value>
  </property>
  <property>
    <name>dfs.namenode.rpc-bind-host</name>
    <value>0.0.0.0</value>
  </property>
  <property>
    <name>dfs.namenode.servicerpc-bind-host</name>
    <value>0.0.0.0</value>
  </property>
  <property>
    <name>dfs.namenode.http-bind-host</name>
    <value>0.0.0.0</value>
  </property>
  <property>
    <name>dfs.namenode.https-bind-host</name>
    <value>0.0.0.0</value>
  </property>
</configuration>

시작 스크립트인 start.sh에서는 NameNode의 네임스페이스가 포맷되었는지 확인하고, 포맷되기 전이라면 구동 전 포맷을 먼저 진행해야 한다.
다음과 같이 작성했다.

#!/bin/bash

# 네임스페이스 디렉토리를 입력받아서 
NAME_DIR=$1
echo $NAME_DIR

# 비어있지 않다면 이미 포맷된 것이므로 건너뛰고
if [ "$(ls -A $NAME_DIR)" ]; then
  echo "NameNode is already formatted."
# 비어있다면 포맷을 진행
else
  echo "Format NameNode."
  $HADOOP_PREFIX/bin/hdfs --config $HADOOP_CONF_DIR namenode -format
fi

# NameNode 기동
$HADOOP_PREFIX/bin/hdfs --config $HADOOP_CONF_DIR namenode

Dockerfile은 base 이미지 덕분에 간단하다.

# 방금 전 로컬에 생성한 base 이미지
FROM hadoop-base:2.9.2

# NameNode Web UI 응답 여부를 통해 Healthcheck
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD curl -f http://localhost:50070/ || exit 1

# 설정 파일 복제
ADD hdfs-site.xml /etc/hadoop/

# FsImage, EditLog 파일 경로를 volume으로 연결
RUN mkdir /opt/hadoop/dfs/name
VOLUME /opt/hadoop/dfs/name

# 실행 스크립트 복제
ADD start.sh /start.sh
RUN chmod a+x /start.sh

# NameNode의 HTTP, IPC 포트 노출
EXPOSE 50070 9000

# 시작 명령어 등록
CMD ["/start.sh", "/opt/hadoop/dfs/name"]

이제 이미지를 생성하자. 기본 명령어와 바이너리 파일을 다운로드 받는 등 시간이 오래 걸리는 작업은 이미 base 이미지에서 처리했기 때문에, 그 위에 다시 한번 계층을 쌓는 NameNode는 빌드 시간이 얼마 걸리지 않는 것을 볼 수 있다.

cd namenode
docker build -t hadoop-namenode:2.9.2 .

두 개의 이미지가 생성되었고 계층 구조에 하나의 이미지가 더해졌다.

Hadoop namenode 이미지 계층

NameNode 이미지를 생성했으니 이제 실제로 기동시켜볼 차례이다. 어차피 DataNode 이미지를 생성하면 docker compose를 구성하는 것이 훨씬 간편하므로 바로 compose 설정 파일을 구성하겠다.

먼저 root 디렉토리에 docker-compose.yml 파일을 생성한다. 디렉토리 구조를 보면 아래와 같다.

.
└── hadoop
    ├── base
    ├── namenode
    ├── datanode
    └── docker-compose.yml

설정 파일의 내용을 작성한다. services에는 기동할 컨테이너 정보를, volumes에는 마운트할 volume 정보를, networks에는 컨테이너 간 어떤 네트워크 구성을 사용할지 지정하는 항목이다.
volumes 이하에 정의된 각 항목은 다음과 같은 의미를 갖고 있다.

  • namenode:/opt/hadoop/dfs/namenamenode volume을 컨테이너의 /opt/hadoop/dfs/name 경로에 마운트
  • /tmp:/tmp: 컨테이너가 기동되는 호스트의 /tmp 경로를 컨테이너의 /tmp 경로로 마운트. 호스트와 컨테이너 간 파일을 쉽게 공유하기 위해서 지정함

콜론으로 구분된 설정 값의 뒷 부분이 컨테이너에 해당한다는 것을 기억하면 쉽게 이해할 수 있다.

version: "3"

services:
  namenode:
    image: hadoop-namenode:2.9.2
    container_name: namenode
    hostname: namenode
    ports:
      - "50070:50070"
      - "9000:9000"
    volumes:
      - namenode:/opt/hadoop/dfs/name
      - /tmp:/tmp
    networks:
      - bridge

volumes:
  namenode:

networks:
  bridge:

이제 이 설정 파일을 바탕으로 NameNode 컨테이너를 기동할 수 있다.

# docker-compose.yml 파일이 위치한 경로로 이동
cd hadoop

# 컨테이너를 백그라운드에서 실행
#  종료할 때에는 docker-compose down
docker-compose up -d
Creating namenode        ... done

# 기동중인 컨테이너 프로세스 확인
docker ps
CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS                            PORTS               622e4f642435
hadoop-namenode:2.9.2          "/start.sh /opt/hado…"   10 seconds ago      Up 6 seconds (health: starting)   0.0.0.0.:9000->9000/tcp, 0.0.0.0.:50070->50070/tcp 

# 생성된 bridge 네트워크 확인
docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
0d9989bd9f78        bridge              bridge              local
9ae1ae84360f        hadoop_bridge       bridge              local
62a94ebd58b7        host                host                local

# 생성된 volume 확인
docker volume ls
DRIVER              VOLUME NAME
local               hadoop_namenode

# namenode 컨테이너의 로그 확인
docker logs -f namenode

docker compose로 기동한 후 마지막 명령어로 로그를 살펴보면, 네임스페이스를 포맷한 로그와 NameNode를 기동한 로그를 확인할 수 있다.
브라우저에서 http://localhost:50070 으로 접속하면 아래와 같은 NameNode Web UI에 접근한다.

NameNode Web UI

또한 NameNode가 기동되었으니, 아직 DataNode를 띄우지 않더라도 폴더의 생성과 삭제, 그리고 파일 목록 조회가 가능하다.
NameNode 컨테이너 내부에 있는 Hadoop 클라이언트를 실행해서 테스트해보자.

# namenode 이름의 컨테이너의 hadoop 클라이언트를 실행. 파일 시스템의 root 디렉토리를 모두 조회한다.
docker exec namenode /opt/hadoop/bin/hadoop fs -ls -R /

# 명령어 등록
alias hadoop="docker exec namenode /opt/hadoop/bin/hadoop"

# 폴더 생성/조회/삭제
hadoop fs -mkdir -p /tmp/test/app
hadoop fs -ls -R /tmp
hadoop fs -rm -r /tmp/test/app

docker exec [컨테이너 이름] [명령어]를 입력하면 동작 중인 컨테이너 내에서 [명령어]에 해당하는 명령어를 실행한 결과의 표준 출력을 현재 쉘에 출력한다.
컨테이너 내 자주 실행하는 프로그램응 alias로 등록해두면 편리하다. 여기에서는 NameNode의 bin/hadoop 프로그램을 hadoop으로 alias 지정했다.

DataNode

먼저 작성했던 JVM과 하둡 바이너리를 포함하는 base 이미지로부터 DataNode 이미지를 생성해보자. 고려해야할 부분은 두 가지이다.

  • 파일 블록을 저장하는 로컬 파일 시스템 경로
  • DataNode용 hdfs-site.xml 설정
    파일 블록 저장 경로는 $HADOOP_PREFIX/dfs/data로 설정할 것이다. 이 내용은 hdfs-site.xml 파일을 datanode 디렉토리 이하에 생성하고 내용을 다음과 같이 작성한다.
<configuration>
  <property>
    <name>dfs.datanode.data.dir</name>
    <value>file:///opt/hadoop/dfs/data</value>
  </property>
  <property>
    <name>dfs.blocksize</name>
    <value>10485760</value>
  </property>  
  <property>
    <name>dfs.datanode.use.datanode.hostname</name>
    <value>true</value>
  </property>
</configuration>

시작 스크립트는 start.sh에 다음과 같이 작성한다.

#!/bin/sh

$HADOOP_PREFIX/bin/hdfs --config $HADOOP_CONF_DIR datanode

hdfs 프로그램을 datanode 옵션으로 시작하면 DataNode 프로세스가 기동한다.

마지막으로 Dockerfile 이다. HEALTHCHECK에 Web UI 주소가 입력된 것과 EXPOSE에 명시한 포트를 눈여겨 본다.

FROM hadoop-base:2.9.2

# DataNode Web UI 응답 여부를 통해 Healthcheck
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3  CMD curl -f http://localhost:50075/ || exit 1

RUN mkdir /opt/hadoop/dfs/data
VOLUME /opt/hadoop/dfs/data

ADD start.sh /start.sh
RUN chmod a+x /start.sh

# WebUI, 데이터전송
EXPOSE 50075 50010

CMD ["/start.sh"]

NameNode 이미지를 구성할 때와 크게 다르지 않다. 이제 생성한 파일들을 가지고 이미지를 생성한다.

cd datanode
docker build -t hadoop-datanode:2.9.2 .

이제 컨테이너 실행 환경 정보를 담고 있는 docker-compose.yml 파일에 DataNode를 추가한다. 로컬 환경이지만 세 개의 DataNode를 띄울 것이다.

version: "3.4"

# 이미지와 네트워크 정보에 대한 base service를 지정
x-datanode_base: &datanode_base
  image: hadoop-datanode:2.9.2
  networks:
    - bridge

services:
  namenode:
    image: hadoop-namenode:2.9.2
    container_name: namenode
    hostname: namenode
    ports:
      - 50070:50070
      - 9000:9000
    volumes:
      - namenode:/opt/hadoop/dfs/name
      - /tmp:/tmp
    networks:
      - bridge

  datanode01:
    <<: *datanode_base
    container_name: datanode01
    hostname: datanode01
    volumes:
      - datanode01:/opt/hadoop/dfs/data
  
  datanode02:
    <<: *datanode_base
    container_name: datanode02
    hostname: datanode02
    volumes:
      - datanode02:/opt/hadoop/dfs/data

  datanode03:
    <<: *datanode_base
    container_name: datanode03
    hostname: datanode03
    volumes:
      - datanode03:/opt/hadoop/dfs/data

volumes:
  namenode:
  datanode01:
  datanode02:
  datanode03:

networks:
  bridge:

파일을 수정하고 docker-compose up -d를 다시 실행하면 세 개의 DataNode 컨테이너를 추가로 띄우게 된다. DataNode는 NameNode로 heartbeat을 보내면서 NameNode에 등록된다.

Elastic Search Index and Shade Split point

여러개의 샤드 vs 인덱스 쪼개기

다나와 서비스의 상품갯수는 급격하게 늘어나고 있습니다. 몇달전 4~5억건이었던 상품이 7억건에 육박하고 있는데요. 현재 사용하고 있는 패스트캣 검색엔진에서는 대량의 문서들을 여러개의 인덱스로 나누어 색인하고 있습니다.

검색할때는 나눈 인덱스들을 하나로 합쳐서 검색하고 있는데요. 엘라스틱서치에도 여러 인덱스를 하나로 합쳐서 한번에 검색하는 기능이 있어서 지금처럼 인덱스를 쪼개서 사용하는 것도 가능한 시나리오입니다. 참고로 얘기하면, 이렇게 데이터를 범위나 카테고리로 나누어 관리하는 기법을 파티셔닝이라고 합니다.

먼저 인덱스를 하나로 가져가든 여러개로 나누든, 샤드 하나의 크기는 비슷하게 설정해야 할텐데요, 적절한 샤드의 크기를 먼저 정해야 할 것 같습니다. 몇차례 구글링을 한결과 엘라스틱서치의 공식블로그에서 도움이 될만한 글를 찾을수 있었습니다.

TIP: 작은 샤드는 작은 세그먼트를 만들며 부하를 증가시킵니다. 평균 샤드 크기를 최소한 수 GB와 수십 GB 사이를 유지하세요. 시간 기반 데이터를 사용한 과거 사례를 보면, 20GB ~ 40GB 정도의 사이즈가 적당합니다.

샤드크기는 수GB에서 수십GB 사이가 적당하며, 경험상 시계열 데이터의 경우 20GB~40GB가 적당하다고 합니다. 그리고 아래에서 또다른 팁을 발견할수 있었습니다.

TIP: 하나의 노드에 저장할 수 있는 샤드의 개수는 가용한 힙의 크기와 비례하지만, Elasticsearch에서 그 크기를 제한하고 있지는 않습니다. 경험상 하나의 노드에 설정한 힙 1GB 당 20개 정도가 적당합니다. 따라서 30GB 힙을 가진 노드는 최대 600개 정도의 샤드를 가지는 것이 가능하지만, 이 보다는 적게 유지하는 것이 더 좋습니다.

힙메모리 30GB로 엘라스틱서치를 구동시 샤드는 최대 600개 정도라고 합니다. 이미 32GB 를 힙에 할당할 생각을 하고 있으므로, 활성화된 인덱스가 50개라고 한다면, 인덱스당 600샤드 / 50 인덱스 = 12 로 인덱스당 최대 12개의 샤드를 설정할수 있을 겁니다. 일단 샤드갯수의 최대치는 넉넉한것 같으므로, 많아서 문제가 될것같지는 않습니다.

하나의 샤드를 20GB~40GB로 설정한다면, 7억건의 상품은 몇개의 샤드로 나눠지게 될까요? 다나와 상품기준으로는 15개의 샤드로 나눠지게 됩니다. 물론 엘라스틱 서치 블로그에서는 시계열 데이터에 대한 팁이라서 상품과는 문서의 특성이 다릅니다. 대부분은 웹서버나 DB 로그데이터 일텐데요, 로그스태시의 파싱모듈을 확인해보면 한 로그내의 필드수가 10개 내외로 매우 적습니다. 반면에 상품의 필드는 수십개는 기본입니다.

이러한 문서특성의 차이로 검색과 색인시 한개 문서에 들어가는 리소스와 시간이 더 투여됩니다. 그러므로, 상품문서는 하나의 샤드를 20GB 보다는 조금 더 작게 나누면 여러 서버에 더 잘게 분산이 되어 전체적인 응답시간은 더 빨라질겁니다. 우리는 샤드당 약 5GB로 설정하고 테스트 했는데, 빠른 응답속도를 얻을 수 있었습니다.

그럼, 인덱스를 나누는것과 샤드를 나누는것 어떤것이 적합할까요? 어치피 샤드는 여러 서버에 분산되어 병렬 및 병행 (참고: https://soy.me/2015/01/03/concurrent/)으로 검색이 되므로, 인덱스가 같던 다르던 상관은 없습니다. 검색의 기본 단위는 샤드이기 때문이죠. 따라서 인덱스를 나누는 것은 운영의 편의성을 고려할때 선택하는 방법입니다. 장애없는 운영의 측면에서 생각해보면, 큰 덩어리 하나를 다루는 것은 부담스러워도, 작은 덩어리 여러개를 다루는 것이 번거롭긴해도 그만큼 장애 가능성을 낮추는 방법이 됩니다.

전체색인을 빠르게 하려면

전체색인을 할 경우 인덱스 하나가 7억건이라면 색인이 모두 끝날때까지 약 3-4시간이 소요될것이고, 검색에 노출되기 까지 이시간을 고스란히 기다려야 합니다. 더 문제가 되는것은 전체색인 도중에도 상품의 가격은 계속해서 변하게 되는데 이 변경분을 전체색인후에 일괄적용을 해야하며, 대기시간이 길수록 일괄적용시간도 늘어나게 됩니다. 그러므로, 최대한 전체색인을 빠르게 해야하는데, 이를 위해서는 문서를 입력하는 REST 클라이언트를 멀티스레드로 여러개 생성하여 동시입력량을 늘려야 합니다. 이때 Bulk Insert 를 쓰는것은 기본이구요.

하지만, 엘라스틱서치의 Write IO도 고려해야 합니다. 동시입력량을 늘리면, 서버의 CPU와 Disk IO중 둘중 하나는 최대치에 이를것이고, 더이상 색인속도는 늘어나지 않게 됩니다. 우리가 경험한 최대 색인속도는 500KB 상품문서 기준으로 16000건/초 였습니다. 이때 CPU는 32코어로 90%를 사용했으니 활용도는 매우 높다고 할수 있습니다. (참고: https://danawalab.github.io/elastic/2020/07/06/Elasticsearch-Index.html)

결국은 더 빠른 색인을 위해서는 하나의 인덱스를 여러개로 나눠야 합니다. 통으로 4시간이 걸리는 문서를 10개의 인덱스로 나누면 색인시간이 서버를 공유하므로 정확히 1/10이 될수는 없지만, 그대로 각각 40분정도로 병행완료가 됩니다.

상품DB의 특성상 우리는 카테고리군별로 인덱스를 나누고 있습니다. 카테고리별로 인덱스를 나눌때의 장점은 특정카테고리만 검색할때 해당 인덱스만 검색하면 되므로, 검색부하가 현저히 감소하게 됩니다. 우리는 인덱스도 나누고 샤드도 2-3개로 나누어 전체적인 검색응답시간을 최대한 단축하는 방향으로 설계했습니다.

레플리카 갯수는 몇개로?

고가용성을 추구할때 빼놓을 수 없는 것이 레플리카인데요, 레플리카는 레플리카 샤드를 줄여서 얘기하는 것으로, 프라이머리 샤드에 종속됩니다. 우리말로는 복제본 이라고도 하죠. 복제본은 분산 데이터 시스템에서는 동일한 역할을 담당하는데요, 하드웨어 장애를 극복하고 검색과 같은 읽기처리량을 향상시키는 것입니다.

그런데 읽기성능이 좋아진다는 것은 쓰기성능이 낮아진다는 얘기도 됩니다. 왜냐하면, 복제본을 여러개 만들기 위해서는 그만큼 문서색인시에 쓰기작업도 복제본 갯수만큼 발생하기 때문이죠. 하나의 서버만 본다면 1이지만, 전체클러스터 입장에서는 복제본이 열개면 10의 쓰기가 일어나는 것입니다. 문서가 색인될때 구지 모든 서버의 IO발생하게 만들 필요는 없을겁니다.

또한 복제본의 갯수는 동시에 장애가 발생할 노드를 몇개 까지 허용하는지에 달려있습니다. 복제본이 1개라면 하나의 노드가 죽어도 검색서비스는 유지됩니다. 하지만, 이제 복제본은 0개가 되므로, SA가 빨리 노드를 복구하지 않으면, 다른 또하나의 노드가 죽을때 검색서비스에는 장애가 생깁니다.

만약 회사내에 장애 복구시스템이 잘 갖춰져 있다고 하면, 복제본은 1개로도 가능합니다. 복제본이 2라면 서버가 연속 2개 죽어도 상관이 없으므로, 마음을 느긋하게 가질수 있습니다. 하지만 전체 클러스터에서 서버가 2개 빠지고도 부하를 충분히 견딜수 있게 서버를 배치해 놓아야 합니다. 우리는 카테고리별 인덱스의 읽기성능을 고려해서 부하가 높은 인덱스는 복제본 2를 나머지는 1을 설정할 계획입니다.

결론

nginx로그와 같은 로그성 문서는 색인을 하고 나면 수정이 필요없는 정적인 컨텐츠인 반면에, 상품문서는 색인이 끝나도 계속 갱신이 되어야 하는 살아있는 컨텐츠입니다. 그렇기 때문에, 동적색인이 원활하고 장애도 대비할수 있으며, 검색성능도 높은 설계가 필요합니다. 그리고 엘라스틱서치를 사용하여 검색시스템을 설계할때 운영자가 할수 있는 최선의 방법은 문서와 샤드를 잘 관리하는 것입니다. 따라서 견고한 검색시스템을 위해 인덱스와 샤드를 어떻게 설정해야 하는지에 대해 살펴봤습니다.

참고

https://www.elastic.co/kr/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster

https://medium.com/kariyertech/elasticsearch-cluster-sizing-and-performance-tuning-42c7dd54de3

https://www.elastic.co/guide/en/elasticsearch/reference/current/scalability.html

Kubernetes Virtual Storage (Ceph) Installation

설치에 필요한 도구로 Rook을 사용할 예정입니다. Rook은 오픈소스 클라우드 네이티브 스토리지 오케스트레이터로, 클라우드 네이티브 환경과 기본적으로 통합할 수 있는 다양한 스토리지 솔루션 세트에 대한 플랫폼, 프레임 워크 및 지원합니다. Rook을 통해 Ceph 가상스토리지를 구성하고 공유 파일 시스템을 적용하도록 하겠습니다.

Rook 구성도

Rook은 쿠버네티스 POD에서 실행되며, Ceph, EdgeFS등 가상솔루션을 POD로 배포하여 관리하는 도구입니다. agent를 통해 Ceph, EdgeFS등 가상솔루션을 관리하고, OSD를 통해 데이터를 영구저장합니다.

/images/2020-01-28-kubernetes-rook-ceph/Untitled.png

Ceph vs EdgeFS

Rook에서 제공하는 가상스토리지는 EgdeFS와 Ceph 외에도 다양하게 지원하지만 안정적인 버전은 아래 두가지만 지원합니다.

  • EdgeFS: 데이터베이스처럼 대용량 스토리지가 필요할때 사용됩니다.
  • Ceph: 공유 확장에 특화되어 있는 스토리지가 필요할때 사용됩니다.

공유나 확장에 특화되어 있는 Ceph를 설치하도록 하겠습니다.

Ceph 스토리지 유형

Ceph는 Block, Object, File 기반으로 데이터를 사용할 수 있습니다. 각 유형에 따라서 사용하는 기능에 차이가 있습니다.

  • Block Stroage: 단일 POD에 storage 제공합니다.
  • Object Storage: 애플리케이션이 쿠버네티스 클러스터 내부 또는 외부에서 액세스 할수있느 데이터를 IO 할수있고, S3 API를 스토리지 클러스터에 노출을 제공합니다.
  • Shared Stroage: 여러 POD에서 공유할 수있는 파일 시스템기반 스토리지입니다.

Shared Storage 구성도

이번에 설치해볼 Shared Storage 구성입니다. Ceph 클러스터에 데이터를 저장하고 Ceph를 통해 POD들과 데이터가 공유가 됩니다.

/images/2020-01-28-kubernetes-rook-ceph/Untitled%201.png

Ceph 설치

테스트 환경

쿠버네티스가 설치된 환경이 필요합니다. 설치방법은 쿠버네티스 고가용성 클러스터 구성 블로그를 통해 설치진행 바랍니다.

OS: ubuntu:18.04
Kubernetes: 1.16.4

Ceph 소스 다운로드 및 POD 배포

Rook은 별도의 설치가 없고 github으로 Ceph, EdgeFS 가상스토리지 솔루션을 쿠버네티스에 배포할 수 있도록 제공하고 있습니다.

$ git clone --single-branch --branch release-1.2 https://github.com/rook/rook.git

아래 경로에 보면 common.yaml, operator.yaml이 있습니다. kubectl 명령어를 통해 배포 합니다.

$ kubectl apply -f rook/cluster/examples/kubernetes/ceph/common.yaml
$ kubectl apply -f rook/cluster/examples/kubernetes/ceph/operator.ymal
$ kubectl apply -f rook/cluster/examples/kubernetes/ceph/cluster.yaml

cluster.yaml을 배포하게 되면 POD 중 OSD가 Running 상태인지 확인이 필요합니다. 변경되는 시간이 약 1 ~ 3 분 정도 소요됩니다.
테스트 환경에 따라 OSD 갯수가 차이가 있을 수 있습니다.

$ kubectl get pod -n rook-ceph

/images/2020-01-28-kubernetes-rook-ceph/Untitled%202.png

OSD Running 상태까지가 ceph 클러스터 구성이 완료되었고, Shared Storage 유형을 적용해보겠습니다.

Shared Storage 유형 적용

아래 명령어를 실행하게 되면 MDS POD가 배포가 됩니다. 약 1 ~ 3분 정도 소요됩니다. Shared Storage 옵션을 변경할 수 있는데 자세한 내용은 ceph-filesystem-crd 통해 확인바랍니다.

$ kubectl apply -f rook/cluster/examples/kubernetes/ceph/filesystem.yaml

StorageClass Driver 적용

StorageClass는 POD에서 Ceph 데이터 풀에 접근하기 위해 사용되는 드라이버입니다. kubectl 명령어를 통해 적용합니다.

$ kubectl apply -f rook/cluster/examples/kubernetes/ceph/csi/cephfs/storageclass.yaml

Shared Storage 확인

정상적으로 적용이 되었는지 확인하는 방법압니다. 아래 결과 처럼 myfs가 생성되어 있습니다.

$ kubectl get CephFileSystem -A

결과)

/images/2020-01-28-kubernetes-rook-ceph/Untitled%203.png

Shared Storage 테스트

kube-registry를 배포하면 PVC, docker-registry POD가 배포됩니다. 컨테이너 내부에 접근하여 /var/lib/registry 디렉터리에 dummy 파일을 생성 하여 다른 POD에서 공유되어 보여지는지 확인합니다.

$ kubectl apply -f rook/cluster/examples/kubernetes/ceph/csi/cephfs/kube-registry.yaml

정리

Rook이라는 도구를 이용해서 Ceph를 설치해보았습니다. Ceph를 관리하려면 러닝커브가 있지만 Rook이라는 도구를 이용해서 Ceph 관리와 쿠버네티스와 연동을 쉽게 할 수 있습니다.

Configuring Kubernetes High Availability Clusters

소개

쿠버네티스는 마스터 노드에 내부적으로 API-SERVER를 가지고 있어 마스터 노드를 통해 워커 노드로 통신이 전달됩니다. 대량에 통신량이 발생하게 되면 마스터 노드는 부하를 많이 받아 장애 발생 가능성이 커지게 됩니다. 마스터 노드를 고가용성 클러스터로 연결하게 되면 통신량을 분산시킬 수 있고, 일부가 마스터 노드에서 장애가 발생시 워커 노드의 운영 시스템에는 영향을 줄일 수 있습니다.

구성 환경

아래 사양으로 총 3대 서버를 준비합니다.

OS: CentOS 7
CPU: 2
RAM: 4
Storage: 100G

구성목표

  1. 마스터 노드 3개를 클러스터구성
  2. 가상 네트워크(weave) 적용
  3. ceph storage 적용

도커&쿠버네티스 설치

CentOS 패키지 업데이트와 방화벽을 정지 및 스왑을 종료합니다.

$ sudo yum update -y
$ sudo systemctl disable firewalld && sudo systemctl stop firewalld
$ sudo selinux disabled
$ sudo setenforce 0
$ sudo iptables piv4 forward
$ sudosudo bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
$ sudo swapoff -a
$ sudo sed -i '/swap/d' /etc/fstab

운영 환경에서는 아래 포트를 오픈하여 진행합니다.

노드 포트 TCP/UDP
마스터 6443, 2379-2380, 10250, 10251, 10252 TCP
마스터, 워커 6783 TCP
마스터, 워커 6783, 6784 UDP
워커 10250, 30000-32767 TCP
로드 밸런서 26443 TCP

호스트네임 설정

호스트네임은 서로 다르게 지정해야합니다.

$ sudo hostnamectl set-hostname node1

$ sudo hostnamectl set-hostname node2

$ sudo hostnamectl set-hostname node3

도커 설치

도커 레파지토리를 등록하여 yum 으로 설치 합니다.

$ sudo yum install -y yum-utils \
    device-mapper-persistent-data \
    lvm2

$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

$ sudo yum install docker-ce docker-ce-cli containerd.io

$ sudo systemctl start docker

설치 후 daemon.json을 추가하여 cgroupdriver를 systemd 옵션으로 사용할 수 있도록 합니다.

$ sudo cat <<EOF > /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
EOF

$ sudo systemctl daemon-reload
$ sudo systemctl enable docker
$ sudo systemctl restart docker

쿠버네티스 설치

쿠버네티스의 레파지토리 저장소를 등록하여 yum 으로 kubelet, kubeadm, kubectl을 설치합니다.

  • kubeadm: 클러스터 관련 작업시 사용됩니다.
  • kubectl: 클러스터의 서비스 및 pod 관리시 사용됩니다.
  • kubelet: 클러스터 에이전트역할을 합니다.
$ sudo cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=0
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg [https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg](https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg)
EOF

$ sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

$ sudo systemctl enable kubelet
$ sudo systemctl restart kubelet

node1, node2, node3에 도커와 쿠버네티스가 설치를 하였다면 아래 명령어를 통해 확인해볼 수 있습니다.

$ sudo docker verison
$ kubectl version

로드밸런서 설치

공식문서에서 사용되고 있는 HAProxy를 사용하도록 하겠습니다.
테스트환경이기 때문에 keepalive및 failover 기능없이 진행하도록 하겠습니다.

노드1에만 haproxy 를 설치하도록 하겠습니다.

$ sudo yum install haproxy -y

haproxy 로드밸런싱 설정

node1 IP의 26443포트로 전달받은 데이터를 node1 ~ node3의 6443 포트로 포워드 시켜줍니다.
라운드로빈으로 순차적으로 접근하도록 하겠습니다.

$ sudo cat <<EOF >> /etc/haproxy/haproxy.cfg
frontend kubernetes-master-lb
bind 0.0.0.0:26443
option tcplog
mode tcp
default_backend kubernetes-master-nodes

backend kubernetes-master-nodes
mode tcp
balance roundrobin
option tcp-check
option tcplog
server node1 <node1 IP>:6443 check
server node2 <node2 IP>:6443 check
server node3 <node3 IP>:6443 check
EOF

$ sudo systemctl restart haproxy

haproxy 확인

해당 포트가 오픈되어 있는지 확인합니다.
포트가 오픈되어 있어도 접근이 안될때는 방화벽을 확인해야 합니다.

netstat -an|grep 26443

클러스터 생성

클러스터 생성할때 –upload-certs, –control-plane-endpoint 플래그를 추가해야 인증서가 자동 배포되고, 마스터 노드 조인 명령어가 출력됩니다.

$ sudo kubeadm init --control-plane-endpoint "<노드1 DNS/IP or LoadBalancer DNS/IP>:26443" \
                --upload-certs \
                --pod-network-cidr "10.244.0.0/16"

config 생성

kubectl 사용하기 위해서는 사용자 디렉토리 하위에 .kube/config 가 필요합니다. kubeadm init 명령어를 진행하고 마지막에 로그를 잘보면 config를 생성하는 명령어가 적혀있습니다.

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

클러스터 연결

노드1에서 클러스터 생성시 kubeadm join 명령어가 출력되는데 –control-plane –certificate-key 플래그 포함하여 명령어를 실행하면 마스터 노드로 연결이 되고, 플래그는 추가하지 않고 사용하게 되면 워커노드로 연결됩니다.

노드2 클러스터 연결

$ sudo kubeadm join 192.168.248.251:26443 --token 06glbc.s0yaqonyajs95ez3 \
    --discovery-token-ca-cert-hash sha256:379ff0daa2cffa3f6581ae25c96aa3d6e4a9af43df92b8d0ac5a4dbb4c7e5547 \
    --control-plane --certificate-key 8eb7fa9a38ca1579c3fb61e0a1106935c4e9dfd81cbad259121f223f0adf555f

config 생성

노드2의 사용자에서도 kubectl 명령어를 사용하기 위해선 config를 생성해야 합니다.

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

노드3 클러스터 연결

노드3에서도 노드2와 동일하게 연결을 진행합니다.

$ sudo kubeadm join 192.168.248.251:26443 --token 06glbc.s0yaqonyajs95ez3 \
    --discovery-token-ca-cert-hash sha256:379ff0daa2cffa3f6581ae25c96aa3d6e4a9af43df92b8d0ac5a4dbb4c7e5547 \
    --control-plane --certificate-key 8eb7fa9a38ca1579c3fb61e0a1106935c4e9dfd81cbad259121f223f0adf555f

config 생성

노드3에서도 동일하게 config를 생성해야합니다.

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

클러스터 구성 확인

노드가 연결이 되었다면 아래 명령어를 실행시 노드가 전부 보여야합니다.

$ kubectl get node

마스터 노드에는 기본설정으로 pod 배포가 안됩니다. taint명령어를 통해 pod 배포가 가능하도록 하겠습니다.

$ kubectl taint nodes --all node-role.kubernetes.io/master-

가상 네트워크(weave) 적용

Calico, Canal, Clilum, Flannel, weave 등 다양한 가상네트워크가 있지만 공식문서에서 제공하는 weave를 적용하도록 하겠습니다.

$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

가상네트워크 적용 확인

최초 클러스터를 진행하게 되면 노드의 상태는 NotReady 입니다. 가상네트워크를 적용하게 되면 약 1 ~ 3분 소요되고 Ready 상태로 변경되게 됩니다. 이것으로 가상네트워크 적용이 완료 되었습니다.

$ kubectl get node

nginx 배포 해보기

nginx pod 3개를 배포하는 명령어입니다.

kubectl run --image=nginx --port 80 --replicas=3 nginx-app

아래 명령어를 통해 nginx-app 배포된 노드를 보면 node1 ~ 3 골고루 배포되어 있는걸 확인할 수 있습니다.

kubectl get pod -o wide

정리

이번에 쿠버네티스 고가용성 클러스터를 서버에 구축해보았습니다. 쿠버네티스 버전 1.6 이후 부터는 인증서배포 자동화, 클러스터 연결하는 방법이 간편하게 변경되었습니다. 이전 버전에서는 클러스터 구성하기위해선 복잡한 과정과 설정파일을 만들어야 했지만 이제는 간단하게 명령어 한줄만 입력하면 클러스터에 연결/해지를 하도록 변경되었습니다. 자유로운 노드 할당은 자원 낭비없이 효율적으로 관리하는데 장점이 될 것으로 보여집니다.

참고문서

Get alarms to Telegram in Grafana

1. 알람 채널 추가

  • Alerting > Notification channels 메뉴를 선택합니다.

    /images/2021-01-26-Common-Grafana-Alert/alert_01.png

  • New channel 을 클릭합니다. /images/2021-01-26-Common-Grafana-Alert/alert_02.png
  • Type 을 Telegram 으로 선택합니다.

    /images/2021-01-26-Common-Grafana-Alert/alert_03.png

    /images/2021-01-26-Common-Grafana-Alert/alert_04.png

    • Name, Default : 모든 알람 받기
    • Include image : 이미지 포함 여부
    • Disable Resolve Message : 해결 알람 받지 않기
    • Send reminders : 알림 상기시키기
    • Telegram API settings (BOT API Token, Chat ID) : 텔레그램에 봇을 추가하여 해당 토큰과 사용자의 챗 ID를 기입

    설정하고 Save 버튼을 눌러서 저장합니다.

  • 추가된 Channel 입니다. /images/2021-01-26-Common-Grafana-Alert/alert_05.png

2. 대시보드 패널에 알람 추가

  • 초당 요청량과 평균 응답속도 패널에 특정 수치로 알람을 설정 해보겠습니다. /images/2021-01-26-Common-Grafana-Alert/alert_06.png
  • 추가 하고싶은 패널의 수정 버튼을 누릅니다. 하단의 Alert 설정에서 Create Alert 버튼을 누릅니다. /images/2021-01-26-Common-Grafana-Alert/alert_07.png
  • 그래프 오른쪽 하트를 위 아래로 드래그하여 알람수치를 조정 할 수 있습니다. /images/2021-01-26-Common-Grafana-Alert/alert_08.png
  • 알람 이름과 규칙을 정합니다.

    Evaluate every 10s For 30s

    30초 동안 10초 마다 체크

  • Conditions은 여러개를 추가 할 수 있습니다.

    WHEN avg () OF query (A, 10s, now) IS ABOVE 400

    패널에 추가된 쿼리 A 수치가 10초 전부터 현재까지 평균 값이 400을 넘는 상태 /images/2021-01-26-Common-Grafana-Alert/alert_09.png

  • 평균 응답속도에 대해서도 알람을 설정하겠습니다.

    Evaluate every 10s For 0

    10초 마다 체크 하도록 설정했습니다.

    WHEN max () OF query (B, 10s, now) IS ABOVE 0.03

    OR max () OF query (C, 10s, now) IS ABOVE 0.03

    쿼리 B, C 중에서 10초 전부터 현재까지 최대값이 0.03초를 넘는 상태에 대해서 알람 설정을 했습니다. /images/2021-01-26-Common-Grafana-Alert/alert_10.png

    Conditions 에는 다양한 값으로 조건을 설정 할 수 있습니다. /images/2021-01-26-Common-Grafana-Alert/alert_10_1.png

3. 알람 확인 및 활용

  • 알림 리스트를 대시보드에 패널로 추가할 수 있습니다. /images/2021-01-26-Common-Grafana-Alert/alert_11.png
  • 대시보드에 알람 및 리스트가 추가된 모습입니다. /images/2021-01-26-Common-Grafana-Alert/alert_13.png
  • Telegram 알람 확인 앞서 Notification Channel 에서 Include image 를 설정하였기 때문에 이미지와 함께 알람이 오게 됩니다.

    초당 요청량은 ‘Evaluate every 10s For 30s’ 30초 동안 10초 마다 확인 하도록 설정 하였기 때문에 PENDING(보류중) 알람이 오지않고 보류 상태가 됩니다.

    평균 응답 속도에는 ‘Evaluate every 10s For 0’ 10초 마다 상태를 확인해서 보내도록 설정하여 보류하지 않고 바로 알람이 오게 됩니다.

    • 초록색 : OK
    • 주황색 : PENDING
    • 빨간색 : ALERTING

    /images/2021-01-26-Common-Grafana-Alert/alert_14.png

  • 알람 설정에서 State history 버튼을 눌러서 상태 히스토리를 확인 할 수 있습니다. /images/2021-01-26-Common-Grafana-Alert/alert_15.png
  • Alerting 메뉴에서 Alert Rules 을 선택하면 알람 설정 수정으로 바로가거나 시작 및 일시중지 제어 할 수 있습니다. /images/2021-01-26-Common-Grafana-Alert/alert_16.png

 

참고 자료

  • https://grafana.com/docs/grafana/latest/alerting/

Configuring Monitoring Dashboards Through Grafana

그라파나?

  • 그라파나는 데이터를 시각화하여 분석 및 모니터링을 용이하게 해주는 오픈소스 분석 플랫폼입니다. 여러 데이터 소스를 연동하여 사용 할 수 있으며 시각화 된 데이터들을 대시보드로 만들 수 있습니다.

    LIVE DEMO

  • 앞서 설치한 그라파나와 프로메테우스의 메트릭 정보를 조회하여 시각화 하는 방법에 대해 알아보겠습니다.

구성 방법

1. 데이터 소스 생성

  • Create a data source 메뉴를 선택합니다. /images/2020-03-17-Common-Dashborad/grafana_1.PNG
  • 데이터 소스로 프로메테우스를 선택합니다. /images/2020-03-17-Common-Dashborad/grafana_2.PNG
  • 프로메테우스 서버 정보를 입력한 후 하단의 Save&Test를 클릭합니다.
    • 연결테스트 후 이상이 없다면 저장됩니다.

/images/2020-03-17-Common-Dashborad/grafana_3.PNG

2. 대시 보드 생성

  • 홈으로 돌아와 Build a dashboard 메뉴를 선택합니다.

/images/2020-03-17-Common-Dashborad/grafana_4.PNG

  • 패널 선택장이 나옵니다. 대시보드는 이 패널들을 구성하고 배치하면 됩니다.
    • 메트릭 조회를 위해 Add Query를 선택합니다

/images/2020-03-17-Common-Dashborad/grafana_5PNG

  • Query 선택창에서 앞에 생성한 Data Source를 불러옵니다.
  • Metric에선 노드익스포터에서 수집한 항목을 선택합니다.

/images/2020-03-17-Common-Dashborad/grafana_7PNG

잠시 돌아와서 위에 선택한 메트릭 항목은 아래와 같은 구조를 가지고 있습니다.
따라서 Metric name으로만 조회시 프로메테우스 서버가 수집한 같은 이름 항목 전부를 조회 합니다. Label name으로 구분하여 조회 할 수 있습니다.
  • Prometheus Metric

/images/2020-03-17-Common-Dashborad/metrics.PNG

  • 이처럼 프로메테우스 서버에서도 메트릭을 조회할 수 있습니다.
  • node_cpu_seconds_total{job=”kube2”,mode=”system”}
    • ex) kube2 host에서 system 영역 cpu 사용률

/images/2020-03-17-Common-Dashborad/grafana_9.PNG

  • 다시 돌아와서 생성한 패널 쿼리에 적용해보겠습니다.

/images/2020-03-17-Common-Dashborad/grafana_10.PNG

  • node_cpu_seconds_total는 계속 누적이 되고 있는 값이기 때문에 rate함수를 사용합니다.
  • rate(node_cpu_seconds_total{job=”kube2”,mode=”system”}[10m])
    • 10분동안 CPU 사용율 변화수치를 초당으로 변환

/images/2020-03-17-Common-Dashborad/grafana_11.PNG

  • CPU 코어의 평균을 구하여 해당 서버의 CPU 평균 사용률을 표시합니다. -avg(rate(node_cpu_seconds_total{job=”kube2”,mode=”system”}[10m])) by (job)

/images/2020-03-17-Common-Dashborad/grafana_12.PNG

  • 설정에서 그래프 종류를 선택할 수 있습니다.

/images/2020-03-17-Common-Dashborad/grafana_6.PNG

이처럼 자신의 필요한 메트릭 정보를 집계하여 패널로 만든 후 대시보드를 구성하면 됩니다.

외부 대시보드 포맷 사용

  • 매트릭 정보를 직접 집계하여 사용하거나 필요한 데이터를 정의하는게 나름 번거로운 작업이라 그라파나 홈페이지에 다른 여러 사용자들이 구성해놓은 대시보드 포맷을 사용하면 더 쉽게 대시보드를구현할 수 있습니다.

https://grafana.com/grafana/dashboards

/images/2020-03-17-Common-Dashborad/grafana_13.PNG

  • import 화면에서 다운받은 JSON 파일을 upload하거나 COPY ID를 입력하여 적용합니다.

/images/2020-03-17-Common-Dashborad/grafana_14.PNG

결론

프로메테우스, 그라파나를 통해 비교적 쉽게 모니터링을 위한 프로세스들을 만들 수 있었습니다. 여러 익스포터를 통해 필요한 매트릭 수집을 확장할 수 있다는게 큰 장점인 것 같으며 PULL 방식이라 부하에 따른 장애나 성능감소를 걱정하지 않아도 된다고 합니다. 그라나파를 통해 매트릭 데이터를 보기 쉽게 만들 수 있었으며 다양한 데이터 소스를 지원하는 만큼 여러 분야에서 활용하면 좋을 것 같습니다.

Installing Prometheus and Grafana Using Docker-Compose

프로메테우스?

  • 프로메테우스는 오픈소스기반의 모니터링 솔루션이며 모니터링 대상이 되는 metric 정보를 pull 방식으로 수집합니다. 공식적으로 제공하는 exporter뿐만 아니라 여러 Third-pary exporter를 통하여 다양한 플랫폼을 모니터링 할 수 있는 장점이 있습니다.

/images/2020-03-16-Common-Prometheus/architecture.PNG

  • 여기서는 도커 컴포즈를 통하여 프로메테우스와 그라파나를 설치하고 Node-Exporter를 통해 받아온 Metric 정보로 그라파나 대시보드를 구성해보겠습니다.

설치 및 구성

1. 구성 환경

CENTOS 7.8

DOCKER 19.03.5, build 633a0ea

DOCKER-COMPOSE 1.25.3, build d4d1b42b

2. docker-compose.yml 작성

  • 공식적으로 설정된 포트들이 이미 사용중임에 따라 다른 포트로 대체하였습니다.
version: '3.7'
services:

  # 직접 설치로 대체
  #node:
  #  image: prom/node-exporter
  #  container_name: node-exporter
  #  ports:
  #    - 9101:9100
  #  networks:
  #    - prometheus-network

  prometheus:
    image: prom/prometheus:v2.16.0
    container_name: prometheus
    network_mode: "host" 
    command:
      - '--web.listen-address=0.0.0.0:9099'
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--storage.tsdb.retention.time=200h'
      - '--web.enable-lifecycle'
    volumes:
      - /data/prometheus:/etc/prometheus
      - /data/prometheus/data:/prometheus
    ports:
      - 9099:9099
    networks:
	  - prometheus-network

  grafana:
    container_name: grafana
    image: grafana/grafana:6.6.2
    environment:
      - GF_SECURITY_ADMIN_USER=user1
      - GF_SECURITY_ADMIN_PASSWORD=user1
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - /data/grafana/data:/var/lib/grafana
      - /data/grafana/provisioning:/etc/grafana/provisioning
    ports:
      - 9900:3000
    depends_on:
      - prometheus
    networks:
      - prometheus-network

networks:
  prometheus-network:

부가 설명

  • 도커에서 노드 익스포터를 실행하는 것은 권장하지 않는다고 합니다. 이유는 도커는 머신의 내부동작과 컨테이너를 격리시키려고 하기 때문에 노드 익스포터에서 내부 동작 결과가 잘동작하지 않을 수 있기 때문입니다.
- #node:
  #  image: prom/node-exporter
  #  container_name: node-exporter
  #  ports:
  #    - 9101:9100
  #  networks:
  #    - prometheus-network
  • 컨테이너에서 호스트에 떠있는 프로세스로 접근이 안되어 NETWORK MODE를 HOST로 설정하였습니다.
network_mode: "host" 
  • 기존 구성 환경에서는 이미 기본포트가 쓰이고 있기 때문에 별도 설정을 통해 포트를 변경하였으며 config 파일 경로를 지정하였습니다.
command:
      - '--web.listen-address=0.0.0.0:9099'
      - '--config.file=/etc/prometheus/prometheus.yml'
  • 컨테이너 종료시에 데이터가 삭제될 수 있으므로 HOST의 볼륨 경로를 지정하였습니다.
volumes:
      - /data/prometheus:/etc/prometheus
      - /data/prometheus/data:/prometheus

3. prometheus.config 작성

  • 환경설정 파일을 작성합니다
  • 매트릭 수집 주기 설정
# my global config
global:
  scrape_interval:     15s # 15초 주기로 매트릭 수집
  • 익스포터 설정
  • 추가 될 때 마다 해당 형식으로 설정 파일내에 추가합니다.
# Exporter 설정
  - job_name: 'kube1'    # 사용할 이름
    scrape_interval: 10s # 주기
    static_configs:      # 익스포트 프로세스 설정
    - targets: ['prom1.danawa.com:9110','prom1.danawa.com:9111']

prometheus.config

# my global config
global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"


# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  - job_name: 'prometheus'

    static_configs:
    - targets: ['127.0.0.1:9099']

  # Exporter 설정
  - job_name: 'prom1'
    scrape_interval: 10s
    static_configs:
    - targets: ['prom1.danawa.com:9110','prom1.danawa.com:9111']

  - job_name: 'prom2'

    static_configs:
    - targets: ['prom2.danawa.com:9110']

  - job_name: 'prom3'

    static_configs:
    - targets: ['prom3.danawa.com:9110']

4. docker-compose 실행

docker-compose up -d

결과
Creating network "prometheus_prometheus-network" with the default driver
Creating node-exporter ... done
Creating prometheus    ... done
Creating grafana       ... done

5. node-exporter 설치

## 사용버전 0.18.1
## node-exporter의 기본 포트는 9100이지만 구성 환경에서 이미 사용중인 포트이므로 임의의 포트로 설정하여 기동하였습니다.

1. wget https://github.com/prometheus/node_exporter/releases/download/v0.18.1/node_exporter-0.18.1.linux-amd64.tar.gz

2. tar xvfz node_exporter-0.18.1.linux-amd64.tar.gz

3. mv node_exporter-0.18.1.linux-amd64/node_exporter /usr/local/bin/

4. /usr/local/bin/node_exporter --web.listen-address=:9110
  • 확인 : http://prom1.danawa.com:9110/metrics – 기본포트 9100

/images/2020-03-16-Common-Prometheus/node_exporter.PNG

6. prometheus 확인

http://prom1.danawa.com:9099 – 기본포트 9090

  • 익스포터로 수집된 메트릭정보를 조회해 볼 수 있습니다.
  • PROMQL을 사용해 데이터를 집계 할 수 있습니다.

/images/2020-03-16-Common-Prometheus/prometheus1.PNG

http://prom1.danawa.com:9099/targets

  • 익스포터의 상태를 확인 할 수 있습니다.

/images/2020-03-16-Common-Prometheus/prometheus1_2.PNG

6. grafana 확인

http://prom1.danawa.com:9900 – 기본포트 3000 /images/2020-03-16-Common-Prometheus/grafana.PNG

/images/2020-03-16-Common-Prometheus/grafana2.PNG

Ubuntu에 minikube설치하기

내가 가진 리눅스 환경에 쿠버네티스 실습 환경을 만들기 위해 minikube를 설치합니다.

 

이 포스트는 우분투 20.04 LTS버젼 기준으로 작성했습니다. 설치 전에 버전을 꼭 확인 해보세요.

미리 설치해야 하는 것 – Docker, VirtualBox (가상환경 내에 설치시에는 필요 없음)

Docker 설치

도커를 설치 해줍니다. Docker가 설치 되어 있다면 도커 설치는 넘어갑니다.

sudo apt-get update
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
sudo apt-get install docker-ce docker-ce-cli containerd.io

여기까지가 Docker설치 명령입니다. 단순히 docker-ce만 설치 한다고 되는 것은 아닙니다. 위 단계를 모두 거쳐야 합니다.

 

 

설치가 완료된 후 docker라고 입력 했을 때 위와 같이 나오면 잘 설치 된 것입니다.

버추얼 박스 설치

minikube를 설치 하려면 OS에 맞는 하이퍼바이저를 설치 해주어야 합니다. 저는 virtualbox를 설치 했습니다.

sudo apt-get install virtualbox

위 버추얼박스를 설치 해줍니다.

Minikube설치

아래 명령어로 minikube를 다운로드 받습니다.

curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 \
  && chmod +x minikube
sudo mkdir -p /usr/local/bin/
sudo install minikube /usr/local/bin/

아래 명령어로 minikube를 실행 합니다.

minikube start

 

 

위와 같이 나오면 잘 설치되고 실행된 것입니다.

minikube start –vm-driver=none   (가상환경 내에 구동시 필요)

저 처럼 amd를 사용해서 에러가 나시는 분들은 위 명령어로 실행을 해보시기 바랍니다.

 

 

위와같이 GUEST_MISSING_CONNTRACK 에러가 나는 경우

sudo apt-get install -y conntrack

위 명령어로 conntrack을 설치 하시고 실행 하시기 바랍니다.

Kubectl설치

쿠버네티스 api에 명령을 전송 하려면 kubectl을 이용해야 합니다.

apt-get을 이용해 kubectl을 설치 합니다.

sudo apt-get update && sudo apt-get install -y apt-transport-https gnupg2
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl

 

Nginx띄워보기

잘 설치되었으면 Nginx를 띄워보겠습니다.

kubectl run webserver –image=nginx:1.14 –port 80

 

 

kubectl get po

위 명령어를 이용해 webserver가 Running상태인지 확인 합니다.

 

end.