<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>끄적 끄적 로깅! 로깅!</title>
    <link>https://warpmemory.tistory.com/</link>
    <description>1. 가치있는 데이터 로깅
2. 소통과 정보 공유
3. 포트폴리오 관리
4. 스토리텔링</description>
    <language>ko</language>
    <pubDate>Sun, 2 Aug 2026 04:09:14 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>warpmemory</managingEditor>
    <image>
      <title>끄적 끄적 로깅! 로깅!</title>
      <url>https://t1.daumcdn.net/cfile/tistory/26421F4E525DF94B26</url>
      <link>https://warpmemory.tistory.com</link>
    </image>
    <item>
      <title>Claude Code 사용량(rate limit)을 statusline에 상시 표시하기 &amp;mdash; 과속 감지 바 만들기</title>
      <link>https://warpmemory.tistory.com/176</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며: 한도는 소진되고 나서야 보인다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code 구독 요금제에는 두 종류의 사용 한도가 있습니다. 하나는 &lt;b&gt;5시간 롤링 윈도우&lt;/b&gt;, 다른 하나는 &lt;b&gt;주간(7일) 한도&lt;/b&gt;입니다. 문제는 이 남은 양을 확인하는 내장 방법인 &lt;code&gt;/usage&lt;/code&gt; 명령이 대화형 세션에서 &lt;i&gt;내가 직접 쳐야만&lt;/i&gt; 보인다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업에 몰입하다 보면 정작 한도가 얼마나 남았는지 신경 쓸 겨를이 없습니다. 그러다 갑자기 &quot;한도에 도달했다&quot;는 메시지를 만나면, 흐름이 뚝 끊깁니다. 그래서 &lt;b&gt;매 순간 시야 안에 남은 한도가 들어와 있으면 좋겠다&lt;/b&gt;는 생각으로 statusline 스크립트를 만들었습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;아이디어: 바 두 개를 나란히, 그리고 &quot;과속 감지&quot;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;statusline에 두 개의 막대를 나란히 그립니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;사용률 바&lt;/b&gt; &amp;mdash; 지금까지 몇 %를 썼는가. 색으로 위험도를 표현합니다(초록 &amp;lt;70%, 노랑 &amp;lt;90%, 빨강 &amp;ge;90%).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;윈도우 경과 바&lt;/b&gt; &amp;mdash; 현재 윈도우에서 시간이 얼마나 흘렀는가(파랑 ⏱).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 이 스크립트의 핵심 아이디어가 나옵니다. &lt;b&gt;두 바를 같은 방향(경과율)으로 맞춰 놓으면, 두 값을 비교하는 것만으로 &quot;과속&quot; 여부를 한눈에 감지할 수 있다&lt;/b&gt;는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각해 보면 간단합니다. 5시간 동안 100%를 균등하게 쓴다면, 시간이 절반(50%) 지났을 때 사용률도 대략 50%여야 합니다. 그런데 만약 &lt;b&gt;사용률 &amp;gt; 경과율&lt;/b&gt;이라면? 지금 페이스라면 리셋 시각이 오기 전에 한도를 먼저 소진한다는 뜻입니다. 즉 &quot;과속&quot; 중입니다. 반대로 &lt;b&gt;사용률 &amp;lt; 경과율&lt;/b&gt;이면 여유가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 두 개를 눈으로 빼는 계산 없이, &lt;b&gt;사용률 바가 시간 바보다 길면 아껴 쓰라는 신호&lt;/b&gt;. 이게 이 스크립트를 오래 쓰게 만든 킬러 포인트입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;동작 원리: statusline은 stdin으로 JSON을 받는다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code의 statusline은 &lt;code&gt;settings.json&lt;/code&gt;에 커맨드를 등록하는 방식입니다. 등록된 커맨드는 실행될 때 &lt;b&gt;stdin으로 세션 상태 JSON을 전달&lt;/b&gt;받고, 표준출력으로 뱉은 한 줄이 statusline에 그대로 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 JSON 안에는 구독 계정 한정으로 &lt;code&gt;rate_limits&lt;/code&gt; 필드가 들어 있습니다. 구조는 대략 이렇습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;model&quot;: { &quot;display_name&quot;: &quot;Opus 4.8&quot; },
  &quot;rate_limits&quot;: {
    &quot;five_hour&quot;: { &quot;used_percentage&quot;: 52.0, &quot;resets_at&quot;: 1753859000 },
    &quot;seven_day&quot;: { &quot;used_percentage&quot;: 38.0, &quot;resets_at&quot;: 1754179740 }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;used_percentage&lt;/code&gt;는 사용률(%), &lt;code&gt;resets_at&lt;/code&gt;은 한도가 초기화되는 시각(epoch 초)입니다. 이 두 값만 있으면 사용률 바와 경과 바를 모두 그릴 수 있습니다. 필드 스펙은 &lt;a href=&quot;https://code.claude.com/docs/en/statusline.md&quot;&gt;공식 statusline 문서&lt;/a&gt;에 정리되어 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구현: 스크립트 전문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;~/.claude/statusline.sh&lt;/code&gt;에 저장할 스크립트 전체입니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash
# Claude Code statusline &amp;mdash; 모델명 + 5시간/주간 사용한도 바 그래프
# 사용률 바(초록&amp;lt;70 노랑&amp;lt;90 빨강&amp;ge;90) + 남은시간 바(파랑) + 리셋 시각
# stdin JSON의 rate_limits 필드 사용 (구독 계정, 첫 API 응답 이후에만 존재)
export PATH=&quot;/opt/homebrew/bin:$PATH&quot;

input=$(cat)

MODEL=$(echo &quot;$input&quot; | jq -r '.model.display_name // .model.id // &quot;?&quot;')
NOW=$(date +%s)

RESET=$'\033[0m'; GREEN=$'\033[32m'; YELLOW=$'\033[33m'; RED=$'\033[31m'
BLUE=$'\033[36m'; DIM=$'\033[2m'

bar() {  # $1=percent(0~100 정수) $2=width &amp;rarr; █/░ 바 문자열
  local pct=$1 width=$2 filled
  filled=$(( (pct * width + 50) / 100 ))
  [ &quot;$filled&quot; -gt &quot;$width&quot; ] &amp;amp;&amp;amp; filled=$width
  [ &quot;$filled&quot; -lt 0 ] &amp;amp;&amp;amp; filled=0
  local i out=&quot;&quot;
  for ((i=0; i&amp;lt;filled; i++)); do out+=&quot;█&quot;; done
  for ((i=filled; i&amp;lt;width; i++)); do out+=&quot;░&quot;; done
  printf '%s' &quot;$out&quot;
}

fmt_window() {  # $1=used_pct $2=resets_at $3=라벨 $4=윈도우초
  local pct=&quot;$1&quot; reset_at=&quot;$2&quot; label=&quot;$3&quot; win=&quot;$4&quot;
  [ -z &quot;$pct&quot; ] &amp;amp;&amp;amp; return
  local ipct; ipct=$(printf '%.0f' &quot;$pct&quot;)

  local color=$GREEN
  [ &quot;$ipct&quot; -ge 70 ] &amp;amp;&amp;amp; color=$YELLOW
  [ &quot;$ipct&quot; -ge 90 ] &amp;amp;&amp;amp; color=$RED

  # 윈도우 경과 시간 % 와 리셋 시각 (사용률과 같은 방향 &amp;mdash; 경과%보다 사용률이 크면 과속)
  local time_part=&quot;&quot;
  if [ -n &quot;$reset_at&quot; ] &amp;amp;&amp;amp; [ &quot;$reset_at&quot; != &quot;null&quot; ]; then
    local remain=$(( reset_at - NOW ))
    [ &quot;$remain&quot; -lt 0 ] &amp;amp;&amp;amp; remain=0
    [ &quot;$remain&quot; -gt &quot;$win&quot; ] &amp;amp;&amp;amp; remain=$win
    local rpct=$(( (win - remain) * 100 / win ))
    local rst_str
    if [ &quot;$win&quot; -gt 86400 ]; then
      rst_str=$(date -r &quot;$reset_at&quot; '+%m/%d %H:%M' 2&amp;gt;/dev/null)
    else
      rst_str=$(date -r &quot;$reset_at&quot; '+%H:%M' 2&amp;gt;/dev/null)
    fi
    time_part=$(printf ' %s⏱%s %s%%%s %s&amp;rarr;%s%s' \
      &quot;$BLUE&quot; &quot;$(bar &quot;$rpct&quot; 6)&quot; &quot;$rpct&quot; &quot;$RESET&quot; &quot;$DIM&quot; &quot;$rst_str&quot; &quot;$RESET&quot;)
  fi

  printf '%s %s%s %s%%%s%s' \
    &quot;$label&quot; &quot;$color&quot; &quot;$(bar &quot;$ipct&quot; 6)&quot; &quot;$ipct&quot; &quot;$RESET&quot; &quot;$time_part&quot;
}

FIVE_PCT=$(echo &quot;$input&quot; | jq -r '.rate_limits.five_hour.used_percentage // empty')
FIVE_RST=$(echo &quot;$input&quot; | jq -r '.rate_limits.five_hour.resets_at // empty')
WEEK_PCT=$(echo &quot;$input&quot; | jq -r '.rate_limits.seven_day.used_percentage // empty')
WEEK_RST=$(echo &quot;$input&quot; | jq -r '.rate_limits.seven_day.resets_at // empty')

OUT=&quot;[$MODEL]&quot;
F=$(fmt_window &quot;$FIVE_PCT&quot; &quot;$FIVE_RST&quot; &quot;5h&quot; $((5*3600)))
W=$(fmt_window &quot;$WEEK_PCT&quot; &quot;$WEEK_RST&quot; &quot;7d&quot; $((7*86400)))
[ -n &quot;$F&quot; ] &amp;amp;&amp;amp; OUT=&quot;$OUT $F&quot;
[ -n &quot;$W&quot; ] &amp;amp;&amp;amp; OUT=&quot;$OUT ${DIM}|${RESET} $W&quot;
[ -z &quot;$F$W&quot; ] &amp;amp;&amp;amp; OUT=&quot;$OUT ${DIM}한도정보 대기중${RESET}&quot;

echo &quot;$OUT&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 해설 ① bar() &amp;mdash; 정수 산술로 그리는 막대&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;bar()&lt;/code&gt;는 퍼센트와 너비를 받아 &lt;code&gt;█&lt;/code&gt;(채움)과 &lt;code&gt;░&lt;/code&gt;(빈칸)로 이루어진 막대 문자열을 반환합니다. 채울 칸 수 계산에서 눈여겨볼 부분은 &lt;code&gt;(pct * width + 50) / 100&lt;/code&gt;입니다. bash 산술은 정수 나눗셈만 하므로 그냥 나누면 항상 버림이 됩니다. 여기에 &lt;code&gt;+ 50&lt;/code&gt;(즉 100의 절반)을 더해 &lt;b&gt;반올림&lt;/b&gt; 효과를 냅니다. 이렇게 하면 51%가 6칸짜리 바에서 3칸으로 자연스럽게 반올림됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 해설 ② fmt_window() &amp;mdash; 경과율 계산&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과속 감지의 심장부입니다. &lt;code&gt;resets_at&lt;/code&gt;(리셋 epoch)에서 현재 시각 &lt;code&gt;NOW&lt;/code&gt;를 빼면 &lt;b&gt;남은 시간(remain)&lt;/b&gt;이 나옵니다. 윈도우 전체 길이 &lt;code&gt;win&lt;/code&gt;(5시간이면 18000초, 7일이면 604800초)에서 이 남은 시간을 빼면 &lt;b&gt;이미 흐른 시간&lt;/b&gt;이고, 그것을 %로 환산한 것이 경과율 &lt;code&gt;rpct&lt;/code&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;local remain=$(( reset_at - NOW ))
local rpct=$(( (win - remain) * 100 / win ))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 &lt;code&gt;rpct&lt;/code&gt;로 파랑 ⏱ 바를 그립니다. 그래서 사용률 색상 바와 시간 바가 &lt;b&gt;같은 축(0~100%)&lt;/b&gt; 위에서 비교되고, 앞에서 말한 &quot;사용률 &amp;gt; 경과율 = 과속&quot; 판단이 눈으로 바로 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;위 스크립트를 &lt;code&gt;~/.claude/statusline.sh&lt;/code&gt;로 저장합니다.&lt;/li&gt;
&lt;li&gt;실행 권한을 줍니다.
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;chmod +x ~/.claude/statusline.sh&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~/.claude/settings.json&lt;/code&gt;에 statusLine을 등록합니다.
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;statusLine&quot;: {
    &quot;type&quot;: &quot;command&quot;,
    &quot;command&quot;: &quot;~/.claude/statusline.sh&quot;,
    &quot;refreshInterval&quot;: 30
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록은 &lt;b&gt;다음 세션부터 적용&lt;/b&gt;될 수 있습니다. 이미 켜져 있는 세션이라면 새로 시작해 보세요. 참고로 스크립트는 &lt;code&gt;jq&lt;/code&gt;를 사용하므로 없다면 먼저 설치해야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함정 노트: 만들며 실제로 걸린 것들&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) rate_limits는 첫 API 응답 이후에만 온다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션을 막 시작한 직후에는 stdin JSON에 &lt;code&gt;rate_limits&lt;/code&gt; 필드가 &lt;b&gt;아직 없습니다&lt;/b&gt;. 이 필드는 그 세션에서 &lt;b&gt;첫 API 응답을 받은 뒤부터&lt;/b&gt; 채워집니다. 그래서 스크립트 마지막에 두 값이 모두 비어 있으면 &lt;code&gt;한도정보 대기중&lt;/code&gt;이라는 폴백 문구를 출력하도록 해 두었습니다. 이게 없으면 세션 초반에 statusline이 텅 비어 당황하게 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) ⏱ 바를 실시간으로 흐르게 하려면 refreshInterval이 필요하다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;statusline의 기본 갱신은 &lt;b&gt;이벤트 기반&lt;/b&gt;입니다(응답 도착 등, 300ms 디바운스). 즉 아무 일도 안 일어나는 유휴 상태에서는 statusline이 멈춰 있습니다. 그러면 시간이 흐르는데도 ⏱ 경과 바가 갱신되지 않습니다. 이걸 실시간으로 흐르게 하려고 &lt;code&gt;&quot;refreshInterval&quot;: 30&lt;/code&gt;(30초마다 재실행)을 넣었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 주의할 점: &lt;b&gt;사용률 %는 API 응답이 있어야만 갱신&lt;/b&gt;됩니다. 유휴 중에는 시간 바(⏱)만 흐르고, 사용률 바는 마지막으로 받은 값에 머물러 있습니다. 이건 데이터의 성질상 어쩔 수 없는 부분입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) date -r은 macOS 전용 &amp;mdash; 리눅스는 -d @&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트의 리셋 시각 포맷팅에 쓴 &lt;code&gt;date -r &quot;$reset_at&quot;&lt;/code&gt;은 &lt;b&gt;macOS(BSD date) 전용&lt;/b&gt; 문법입니다. 리눅스(GNU date)에서는 동작하지 않으니 다음처럼 바꿔야 합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OS&lt;/th&gt;
&lt;th&gt;epoch &amp;rarr; 시각 포맷&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;macOS (BSD)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;date -r &quot;$reset_at&quot; '+%H:%M'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux (GNU)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;date -d @&quot;$reset_at&quot; '+%H:%M'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리: 결과 화면&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완성된 statusline은 이런 한 줄로 표시됩니다(색상은 텍스트로 재현 불가하니 상상해 주세요).&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[Opus 4.8] 5h ███░░░ 52% ⏱██░░░░ 31% &amp;rarr;16:40 | 7d ██░░░░ 38% ⏱███░░░ 55% &amp;rarr;08/03 09:00&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽는 법:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;5h&lt;/b&gt;: 사용률 52% &amp;gt; 경과 31% &amp;rarr; &lt;b&gt;과속 중&lt;/b&gt;. 리셋(16:40)이 오기 전에 5시간 한도를 먼저 소진할 페이스입니다. 잠깐 속도를 늦출 신호.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;7d&lt;/b&gt;: 사용률 38% &amp;lt; 경과 55% &amp;rarr; &lt;b&gt;여유&lt;/b&gt;. 주간 한도는 시간 대비 덜 쓰고 있으니 걱정 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 &quot;몇 % 썼다&quot;를 넘어서, &lt;b&gt;지금 페이스가 지속 가능한지&lt;/b&gt;를 두 바의 길이 비교만으로 알려 준다는 점이 이 작은 스크립트의 가장 큰 효용이었습니다. 한도에 부딪혀 흐름이 끊기는 일이 눈에 띄게 줄었습니다. 터미널 한 줄에 넣어 두기 딱 좋은 투자입니다.&lt;/p&gt;</description>
      <category>유틸</category>
      <category>bash</category>
      <category>claude code</category>
      <category>StatusLine</category>
      <category>사용량</category>
      <category>터미널</category>
      <author>warpmemory</author>
      <guid isPermaLink="true">https://warpmemory.tistory.com/176</guid>
      <comments>https://warpmemory.tistory.com/176#entry176comment</comments>
      <pubDate>Thu, 30 Jul 2026 12:17:44 +0900</pubDate>
    </item>
    <item>
      <title>rsync 백업 중 메모리 사용률 급증, 원인은 두 가지: dirty page와 page cache</title>
      <link>https://warpmemory.tistory.com/175</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 서버에서 rsync 기반 백업이 도는 시간대에 메모리 사용률이 90%를 넘기며 알람이 뜨는 상황을 겪었다. 원인을 파고들어 보니 서로 다른 두 가지가 얽혀 있었다. 하나는 &lt;b&gt;dirty page 적체&lt;/b&gt;, 다른 하나는 &lt;b&gt;rsync가 만들어내는 page cache&lt;/b&gt;다. 둘 다 &quot;메모리 사용률 증가&quot;로 보이지만 성격과 대응법이 완전히 다르다. 나중에 또 만날 것 같아 정리해 둔다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. dirty page 적체로 인한 메모리 스파이크&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;증상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대량 파일 쓰기(백업, 미러링 등)가 일어나는 동안 메모리 사용률이 출렁이며 치솟는다. 쓰기가 일정 구간 누적되었다가 한 번에 디스크로 flush될 때 스파이크가 발생한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dirty page는 아직 디스크에 반영되지 않은, 메모리에만 존재하는 수정된 페이지다. 커널은 &lt;code&gt;vm.dirty_ratio&lt;/code&gt; / &lt;code&gt;vm.dirty_background_ratio&lt;/code&gt; 값에 따라 얼마나 쌓였을 때 flush를 시작할지 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함정은 일부 튜닝 프로파일에 있었다. 성능 지향 프로파일(예: &lt;code&gt;throughput-performance&lt;/code&gt;)을 적용하면 &lt;code&gt;vm.dirty_ratio&lt;/code&gt;가 기본 20%가 아니라 &lt;b&gt;40%로 올라간다.&lt;/b&gt; 즉 메모리의 40%까지 dirty page가 쌓이도록 허용되고, 그만큼 적체 후 일괄 flush 시 사용률 스파이크가 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 이 값이 &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; 같은 파일에는 &lt;b&gt;드러나 있지 않다&lt;/b&gt;는 것이다. 프로파일이 런타임에 덮어쓰기 때문에, sysctl 파일만 봐서는 왜 40%인지 알 수 없다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;현재 값 확인&lt;/h3&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;sysctl vm.dirty_ratio vm.dirty_background_ratio
# 적용 중인 튜닝 프로파일 확인
tuned-adm active&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결: 프로파일 통째로 바꾸지 말고 dirty 값만 오버라이드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능 프로파일의 다른 이점은 유지하면서 dirty 관련 값만 낮추는 게 핵심이다. 커스텀 프로파일을 만들어 기존 프로파일을 include한 뒤 dirty 값만 덮어쓴다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;mkdir -p /etc/tuned/custom-profile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;/etc/tuned/custom-profile/tuned.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;[main]
include=throughput-performance

[sysctl]
vm.dirty_ratio=20
vm.dirty_background_ratio=5&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;tuned-adm profile custom-profile
sysctl vm.dirty_ratio vm.dirty_background_ratio   # 20 / 5 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리가 매우 큰 장비라면 비율(%) 대신 절대량으로 상한을 거는 방식이 더 예측 가능하다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;[sysctl]
vm.dirty_bytes=1073741824          # 1GB
vm.dirty_background_bytes=268435456 # 256MB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(주의: &lt;code&gt;dirty_bytes&lt;/code&gt;를 설정하면 &lt;code&gt;dirty_ratio&lt;/code&gt;는 0으로 무시된다. 둘 중 하나만 쓴다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 한 가지 조정만으로 적체 후 일괄 flush로 인한 스파이크는 눈에 띄게 완화된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. rsync가 쌓는 page cache&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;증상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dirty 값을 조정했는데도 백업이 끝나갈 무렵 메모리 사용률이 높게 유지된다. 그런데 이건 성격이 다르다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rsync는 읽고 쓰는 파일을 page cache에 올린다. 백업처럼 한 번 읽고 다시 안 쓸 데이터까지 캐시에 차곡차곡 쌓이면서 &lt;code&gt;buff/cache&lt;/code&gt;가 부풀어 오른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 사실 &lt;b&gt;문제가 아닌 정상 동작&lt;/b&gt;이다. page cache는 reclaimable이라 다른 프로세스가 메모리를 필요로 하면 커널이 알아서 회수한다. &lt;code&gt;free -m&lt;/code&gt;에서 &lt;code&gt;available&lt;/code&gt; 값이 충분하면 실제로는 여유가 있는 상태다.&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;free -m
# total used free shared buff/cache available
# used가 아니라 available을 봐야 한다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 모니터링 알람이 &lt;code&gt;buff/cache&lt;/code&gt;까지 포함한 &quot;used 기준 사용률&quot;로 임계치를 잡는 경우다. 실제로는 회수 가능한데 알람만 울린다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 1: nocache로 캐시 적재 자체를 막기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일회성 백업처럼 캐시에 남길 필요가 없는 작업은 &lt;code&gt;nocache&lt;/code&gt;로 감싸면 된다. &lt;code&gt;LD_PRELOAD&lt;/code&gt;로 &lt;code&gt;open&lt;/code&gt;/&lt;code&gt;close&lt;/code&gt; 등을 후킹해서, 파일이 &lt;b&gt;닫힐 때마다&lt;/b&gt; 그 파일에 대해 &lt;code&gt;posix_fadvise(POSIX_FADV_DONTNEED)&lt;/code&gt;를 호출해 page cache에서 비운다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;nocache rsync -avuz /src/ /dst/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;끝에 한 번만 비우는 게 아니라 파일 단위로 비운다&lt;/b&gt;는 점이다. rsync가 파일 하나를 다 전송하고 &lt;code&gt;close()&lt;/code&gt; 하는 순간 그 파일이 차지하던 캐시가 즉시 해제된다. 그래서 수많은 파일을 도는 장시간 동기화에서도 캐시가 끝까지 누적되지 않고 일정 수준으로 유지된다. (쓰기 파일은 DONTNEED가 dirty page를 못 비우므로 nocache가 close 시 &lt;code&gt;fdatasync&lt;/code&gt;로 먼저 flush한 뒤 비운다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;nocache&lt;/code&gt;라는 이름 때문에 &quot;캐시를 아예 못 쓰게 해서 느려지는 것 아니냐&quot;는 오해가 있는데, 그렇지 않다. &lt;b&gt;처리가 끝난 파일의 페이지를 캐시에서 비워줄 뿐&lt;/b&gt;, 다른 프로세스나 시스템 전체의 캐시 사용을 막는 게 아니다. 또 열 때 이미 캐시에 있던 페이지는 건드리지 않고 자기가 새로 올린 페이지만 골라 비운다. 단발성 백업/복사처럼 어차피 재사용 안 할 데이터엔 안성맞춤이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 약점이 하나 있다. &lt;b&gt;하나의 거대한 파일을 오래 전송하는 경우&lt;/b&gt;, 그 파일은 close될 때까지 열려 있으므로 그동안 캐시가 쌓이고 닫힐 때 한꺼번에 해제된다. 작은~중간 파일이 많은 일반적인 백업에는 잘 맞지만, &quot;수십 GB짜리 파일 하나&quot;가 메인이면 효과가 제한적일 수 있다. 이럴 땐 아래 cgroup 방식이 더 확실하다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;설치 (EPEL 또는 소스 빌드)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;nocache&lt;/code&gt;는 RHEL/Rocky/CentOS 계열 &lt;b&gt;기본 저장소에는 없다.&lt;/b&gt; EPEL에 있거나, 없으면 소스로 빌드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EPEL이 가능하면:&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;yum install -y epel-release
yum install -y nocache&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EPEL을 못 쓰는 환경(폐쇄망 등)이면 소스 빌드가 깔끔하다. 의존성이 거의 없고 빌드도 가볍다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;# 빌드 도구
yum install -y git make gcc

# 소스 받아 빌드
git clone https://github.com/Feh/nocache.git
cd nocache
make
make install        # 기본 PREFIX=/usr/local &amp;rarr; /usr/local/bin/nocache

# 확인
which nocache
nocache --help&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폐쇄망이면 인터넷 되는 곳에서 tarball(또는 git clone)만 받아 반입한 뒤 &lt;code&gt;make &amp;amp;&amp;amp; make install&lt;/code&gt; 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 같은 데이터를 반복적으로 읽어 캐시 효과가 중요한 쪽(서비스가 읽는 원본 경로 등)에는 적용하지 않는 게 좋다. 백업의 목적지(dest) 쪽이나 일회성 작업에 거는 게 안전하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 2: cgroup으로 메모리 상한 걸기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 작업이 쓸 수 있는 메모리(캐시 포함) 자체를 제한하고 싶다면 cgroup으로 스코프를 묶는다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;systemd-run --scope -p MemoryHigh=3G -p MemoryMax=4G rsync -avuz /src/ /dst/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;MemoryHigh&lt;/code&gt;를 넘으면 커널이 해당 스코프의 캐시를 적극적으로 회수하며 압박하고, &lt;code&gt;MemoryMax&lt;/code&gt;는 하드 리밋이다. 시스템 전체 메모리 사용률이 백업 때문에 튀는 걸 근본적으로 묶어둘 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;현상&lt;/th&gt;
&lt;th&gt;원인&lt;/th&gt;
&lt;th&gt;핵심 대응&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;쓰기 중 사용률 스파이크&lt;/td&gt;
&lt;td&gt;dirty page 적체 후 일괄 flush&lt;/td&gt;
&lt;td&gt;dirty_ratio 낮춤 (커스텀 tuned 프로파일)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;백업 후 사용률 높게 유지&lt;/td&gt;
&lt;td&gt;rsync page cache 누적&lt;/td&gt;
&lt;td&gt;nocache 또는 cgroup 메모리 상한&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;교훈 세 가지.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;&quot;메모리 사용률 높음&quot;을 한 덩어리로 보면 안 된다.&lt;/b&gt; dirty page(쓰기 적체)와 page cache(읽기 캐시)는 원인도 대응도 다르다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;튜닝 프로파일이 sysctl 값을 몰래 바꾼다.&lt;/b&gt; 설정 파일만 보지 말고 &lt;code&gt;tuned-adm active&lt;/code&gt;로 적용 프로파일을 확인하자. 그리고 프로파일을 통째로 갈아엎기보다 include + 필요한 값만 오버라이드하는 게 깔끔하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;page cache 증가는 대부분 정상이다.&lt;/b&gt; &lt;code&gt;available&lt;/code&gt;을 보고 판단하고, 알람 임계치가 &lt;code&gt;buff/cache&lt;/code&gt;를 포함하는지 점검하자.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>운영체제</category>
      <category>dirty_ratio</category>
      <category>Linux</category>
      <category>nocache</category>
      <category>Page Cache</category>
      <category>rsync</category>
      <category>tuned</category>
      <author>warpmemory</author>
      <guid isPermaLink="true">https://warpmemory.tistory.com/175</guid>
      <comments>https://warpmemory.tistory.com/175#entry175comment</comments>
      <pubDate>Fri, 26 Jun 2026 11:36:00 +0900</pubDate>
    </item>
    <item>
      <title>[linux] cron.d 설정으로 haproxy 로그 logrotate 적용</title>
      <link>https://warpmemory.tistory.com/172</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1분 주기로 haproxy 로그 확인해서 100M 넘으면 로테이트해서 500개 까지 보관(crontab 사용하지 않고, /etc/cron.d 설정)&lt;/p&gt;
&lt;pre id=&quot;code_1697788929064&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir /etc/logrotate.hourly

cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/logrotate.hourly/haproxy
/var/log/haproxy/haproxy.log
/var/log/haproxy/haproxy-*.log {
  hourly
  missingok
  nocompress
  notifempty
  size 100M
  rotate 500
  dateext
  dateformat -%Y%m%d%H-%s
  #copytruncate
  postrotate
    /usr/bin/pgrep rsyslogd | xargs -I{} kill -s HUP {}
  endscript
}
EOF

mkdir /etc/cron.minutely

cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/cron.minutely/0logrotate.hourly
/usr/sbin/logrotate  /etc/logrotate.hourly/haproxy
EOF

chmod 750 /etc/cron.minutely/0logrotate.hourly
 
cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/cron.d/0minutely
# Run the hourly jobs
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
* * * * * root run-parts /etc/cron.minutely
EOF

chmod 640 /etc/cron.d/0minutely&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>운영체제</category>
      <category>cron.d</category>
      <category>cron.minutely</category>
      <category>Linux</category>
      <author>warpmemory</author>
      <guid isPermaLink="true">https://warpmemory.tistory.com/172</guid>
      <comments>https://warpmemory.tistory.com/172#entry172comment</comments>
      <pubDate>Fri, 20 Oct 2023 17:12:59 +0900</pubDate>
    </item>
    <item>
      <title>Linux에서 삭제된 파일 물고 있는 프로세스 찾는 방법</title>
      <link>https://warpmemory.tistory.com/171</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;lsof +L1&amp;nbsp;&lt;/p&gt;</description>
      <category>운영체제</category>
      <author>warpmemory</author>
      <guid isPermaLink="true">https://warpmemory.tistory.com/171</guid>
      <comments>https://warpmemory.tistory.com/171#entry171comment</comments>
      <pubDate>Thu, 8 Jun 2023 20:48:43 +0900</pubDate>
    </item>
    <item>
      <title>git에서 시크릿 파일 삭제 방법</title>
      <link>https://warpmemory.tistory.com/170</link>
      <description>&lt;p&gt;&lt;span&gt;시크릿 데이터가 원격 저장소에 저장되었을때 삭제 방법입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;git filter-branch --force --tree-filter 'rm -f etc/*' --prune-empty HEAD&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;git push --force origin master&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍</category>
      <author>warpmemory</author>
      <guid isPermaLink="true">https://warpmemory.tistory.com/170</guid>
      <comments>https://warpmemory.tistory.com/170#entry170comment</comments>
      <pubDate>Mon, 29 Mar 2021 15:39:17 +0900</pubDate>
    </item>
    <item>
      <title>Linux에서 tcpdump 시간 단위로 pcap 파일 생성</title>
      <link>https://warpmemory.tistory.com/169</link>
      <description>&lt;p&gt;tcpdump.sh 스크립트 생성&lt;/p&gt;
&lt;pre id=&quot;code_1609293227189&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#/bin/bash
export PATH=$PATH:/usr/bin:/usr/sbin
DUMP_NAME=&quot;api&quot;
DUMP_DIR=&quot;/root&quot;
DUMP_FILE=&quot;${DUMP_NAME}_%Y-%m-%d_%H:%M.pcap&quot;
find $DUMP_DIR -name &quot;${DUMP_NAME}_*.pcap&quot; -mtime +2 -delete
ps -ef |egrep &quot;tcpdump .* ${DUMP_DIR}/${DUMP_FILE}&quot; |grep -v grep |awk '{print $2}' |xargs kill -3 &amp;gt; /dev/null 2&amp;gt;&amp;amp;1
timeout 24h tcpdump -i eth0 port 80 -G 3600 -w ${DUMP_DIR}/${DUMP_FILE} &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;crontab -e 로 스케줄러 등록&lt;/p&gt;
&lt;pre id=&quot;code_1609293299175&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# restart tcpdump
0 0 * * * /root/tcpdump.sh &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>운영체제</category>
      <author>warpmemory</author>
      <guid isPermaLink="true">https://warpmemory.tistory.com/169</guid>
      <comments>https://warpmemory.tistory.com/169#entry169comment</comments>
      <pubDate>Wed, 30 Dec 2020 10:57:44 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL 테이블 Vacuum 모니터링</title>
      <link>https://warpmemory.tistory.com/168</link>
      <description>&lt;pre id=&quot;code_1595211671621&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT datname, age(datfrozenxid) FROM pg_database ORDER BY age(datfrozenxid) desc;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1595211756647&quot; class=&quot;html xml&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;         datname         |    age     
-------------------------+------------
 aaaaaaaaaaaaaaaa        | 1485485076
 bbbbbbbbbbbbbbbbbbbbbbb | 1481342364
 cccccccccccccccccccc    | 1461333757
 dddddddddddddddddd      | 1330037484
 postgres                | 1329674313
 fffffffff               | 1156326849
 ggggggggggggggggggggggg |  100464636
 template1               |     382402
 template0               |     378203
(9 rows)​&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;데이터베이스의 수명이 20억 트랜잭션 ID에 도달하면, 트랜잭션 ID(XID) 랩어라운드가 발생하고 데이터베이스는 읽기 전용이 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>엔지니어링</category>
      <author>warpmemory</author>
      <guid isPermaLink="true">https://warpmemory.tistory.com/168</guid>
      <comments>https://warpmemory.tistory.com/168#entry168comment</comments>
      <pubDate>Mon, 20 Jul 2020 11:25:09 +0900</pubDate>
    </item>
    <item>
      <title>타임라인 및 검색 시스템 설계</title>
      <link>https://warpmemory.tistory.com/167</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;사전 지식&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용기술&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Elastic Load Balancing(ELB)&lt;/li&gt;
&lt;li&gt;Elastic Computing(EC2)&lt;/li&gt;
&lt;li&gt;ElastiCache(Redis Cluster)&lt;/li&gt;
&lt;li&gt;Elasticsearch(ELK)&lt;/li&gt;
&lt;li&gt;EC2 Autoscailing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Linux
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CentoOS&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Nginx
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UWSGI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Python
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Django&lt;/li&gt;
&lt;li&gt;Celery&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;Kafka&lt;/li&gt;
&lt;li&gt;Storm&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발 사전 체크&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기획 내용의 검토&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Timeline 시나리오 설계 및 구축&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;특정 유저가 다른 유저 혹은 인플루언서를 팔로우할 경우 모아보기를 지원해야 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Only DB Query로 구축은 지양하고, 확장성 및 성능을 고려한 설계 필요(DB로 설계했을 때 방안도 고려)&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;AWS ELK를 활용한 검색 시나리오 설계 및 구축&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;사용자가 작성한 글/크롤링한 데이터/커뮤니티 검색 지원&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결하고자 하는 문제&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확장성 및 성능을 고려한 Timeline 시스템 구축&lt;/li&gt;
&lt;li&gt;데이터 크롤링 및 검색 시스템 구축&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발 범위&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Timeline 시스템
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 post 시 팔로워 timeline에 post 추가&lt;/li&gt;
&lt;li&gt;Only DB Query 구축 지양, 확장성 및 성능을 만족&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis 클러스터를 이용한 캐시 시스템&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검색 시스템
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;크롤링 시스템
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URL, Keyword 등 설정에 따른 크롤링&lt;/li&gt;
&lt;li&gt;크롤링 사이트, 속도 제한&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;파이프라인 시스템
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 버퍼링&lt;/li&gt;
&lt;li&gt;데이터 유실 없어야 하고&lt;/li&gt;
&lt;li&gt;장애 내성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스트림 프로세싱
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파싱&lt;/li&gt;
&lt;li&gt;필터링&lt;/li&gt;
&lt;li&gt;인덱싱&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검색 시스템
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 저장&lt;/li&gt;
&lt;li&gt;검색&lt;/li&gt;
&lt;li&gt;검색 결과 랭크&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;통계 시스템
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스 통계&lt;/li&gt;
&lt;li&gt;검색 통계&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;캐시 구축
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 결과 캐시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;API 구축&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색&lt;/li&gt;
&lt;li&gt;검색 결과 랭크&lt;/li&gt;
&lt;li&gt;post 등록 시 검색 인덱스 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 프로세스 및 시나리오&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시스템 구성 및 프로세스&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNJD7i/btqFiOGJfvK/eWFQMGrayy883tfHbLTKw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNJD7i/btqFiOGJfvK/eWFQMGrayy883tfHbLTKw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNJD7i/btqFiOGJfvK/eWFQMGrayy883tfHbLTKw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNJD7i%2FbtqFiOGJfvK%2FeWFQMGrayy883tfHbLTKw1%2Fimg.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 로직&lt;/h2&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;Timeline 시스템&lt;/h3&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;MySQL 구성&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Relational Database Service(RDS)가 EC2 기반으로 서비스됨&lt;/li&gt;
&lt;li&gt;비용 측면에서 RDS 사용하지 않고, EC2에 직접 DBMS 구축해서 사용해도 됨&lt;/li&gt;
&lt;li&gt;DB 이중화 및 주기적인 백업 시스템 필수&lt;/li&gt;
&lt;li&gt;멀티 마스터 구조 또는 Master - Slaves 구성인 경우 라이브러리 단에서&amp;nbsp; Read/Write 구분하여 사용&lt;/li&gt;
&lt;li&gt;데이터가 많아지면 데이터 베이스 샤딩/파티셔닝 고려&lt;/li&gt;
&lt;li&gt;운영 이슈가 심각해지면 RDS로 이전 고려&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;MySQL 스키마&lt;/h4&gt;
&lt;pre class=&quot;n1ql&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(256),
  `name` varchar(256),
  `influencer` char(1) DEFAULT 'f',
  `created_at` datetime DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`),
  UNIQUE KEY `IDX_user_email` (`email`),
  KEY `IDX_user_name` (`name`),
  KEY `IDX_user_influencer` (`influencer`),
  KEY `IDX_user_created_at` (`created_at`)
) ENGINE=INNODB DEFAULT CHARSET='utf8';&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE `follower` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(10) unsigned,
  `follower_user_id` int(10) unsigned,
  `created_at` datetime DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`),
  KEY `IDX_follower_user_id` (`user_id`),
  KEY `IDX_follower_created_at` (`created_at`),
  FOREIGN KEY (`user_id`) 
  REFERENCES user(`id`) ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=INNODB DEFAULT CHARSET='utf8';&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE `post` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(10) unsigned NOT NULL,
  `contents` text NOT NULL,
  `tags` text,
  `locale` varchar(256) NOT NULL DEFAULT '',
  `created_at` datetime DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`),
  KEY `IDX_post_user_id` (`user_id`),
  KEY `IDX_post_locale` (`locale`),  
  KEY `IDX_post_created_at` (`created_at`),
  FOREIGN KEY (`user_id`) 
  REFERENCES user(`id`) ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=INNODB DEFAULT CHARSET='utf8';&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;MySQL 타임라인 조회&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자의 팔로워가 많아지면 많아질수록 부하 문제 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;n1ql&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT
    *
FROM
    post
WHERE
    user_id in (
        SELECT
            user_id
        FROM
            follower
        WHERE
            follower_user_id = {user_id}
    )
    OR user_id = {user_id}
ORDER BY created_at DESC LIMIT 20;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;MySQL 사용자가 팔로우하는 인플루언서 조회&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT
    u.id, u.influencer
FROM
    follower AS f JOIN user AS u ON u.id=f.user_id
WHERE
    f.follower_user_id={user_id} AND u.influencer='t';&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;Redis 스키마&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;키 : 벨류&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;ada&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;USER_{user_id}_INFLUENCERS : Sets(1, 3, 5, 7)
USER_{user_id}_FOLLOWINGS : Sets(2, 4, 6, 8)
USER_{user_id}_POSTS : Lists[1, 2, 3, 7, 8, 9]
POST_{post_id} : Strings '{
    &quot;user_id&quot;: 1,
    &quot;user_name&quot;: &quot;사용자1&quot;,
    &quot;contents&quot;: &quot;포스트1&quot;,
    &quot;created_at&quot;: &quot;2020-07-02 10:09:10&quot;
}'&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;Redis 타임라인 조회&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Redis 클러스터에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;액티브(2주&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;안에 접속한) 사용자에 대한 타임라인을 미리 생성&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;저장하여&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;서비스&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;캐시가 없는 경우에는 MySQL에서 가져와서, 캐시 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;ttl 시간을 2주로 설정, 해당 키 접근 시 마다 ttl 시간이 2주가 되도록 설정&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인플루언서의 post가 아닌 경우는, post 등록 즉시 팔로워들의 POSTS 캐시에 바로 추가(Push 방식)&lt;/li&gt;
&lt;li&gt;인플루언서의 post인 경우는, 팔로워가 접속했을 때, 팔로워의 POSTS 캐시에&lt;span&gt;&amp;nbsp;&lt;/span&gt;인플루언서&lt;span&gt;&amp;nbsp;&lt;/span&gt;포스트를 가져와 병합(Pull 방식)&lt;/li&gt;
&lt;li&gt;MySQL과 최종 정합성 고려&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;API 서버&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EC2로 구축
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Nginx : 웹 서버&lt;/li&gt;
&lt;li&gt;uWSGI(Web Server Gateway Interface) : Nginx서버와 Python 애플리케이션 간 통신 인터페이스 모듈&lt;/li&gt;
&lt;li&gt;Django REST framework(DRF) : Django RESTful 프레임워크&lt;/li&gt;
&lt;li&gt;REST Swagger : API 문서 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Amazon EC2 Autoscailing 사용하여, 서버 부하에 대응&lt;/li&gt;
&lt;li&gt;RESTful 조건에 만족하도록 개발&lt;/li&gt;
&lt;li&gt;GET /api/v1/posts
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;user_id&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;POST /api/v1/posts
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;user_id&lt;/li&gt;
&lt;li&gt;contents&lt;/li&gt;
&lt;li&gt;tags&lt;/li&gt;
&lt;li&gt;locale&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;Celery 서버&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비동기 태스크 큐로 post가 들어왔을 때, Redis 클러스터에 Timeline Cache 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;검색 시스템&lt;/h3&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;수집(Crawler)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 대상 URL 및 검색 키워드 설정에 따라 데이터 수집&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수집 대상 설정 가져오는 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;crawler 속도 제한 기능 필수&lt;/li&gt;
&lt;li&gt;robots.txt 확인하여, crawler 제한된 사이트 제외&lt;/li&gt;
&lt;li&gt;사이트 도메인별로 Kafka 토픽 데이터 저장(스트림 프로세싱 단순화)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Twitter&lt;/li&gt;
&lt;li&gt;Facebook&lt;/li&gt;
&lt;li&gt;YouTube&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;파이프라인(Kafka 클러스터)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EC2로 Kafka 클러스터(+Zookeeper) 파이프라인 구축
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Consumer가 broker로 부터 메시지를 pull 하는 방식
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Consumer가 처리 가능할 때 메시지를 가져와 처리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자원 효율적 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메시지를 disk에 저장 하기 때문에 이미 처리한 큐도, 다시 가져와서 처리가 가능 함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Consumer 장애 시에도 데이터 누락 없이 처리 할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메시지를 쌓아 두었다가 한번에 처리하는 배치 처리도 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;클러스터로 장애 내성을 갖음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 규모에 따라서 클러스터 규모 산정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최소 설정은 3개 클러스터에 복제 1개 이상&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수집 데이터 보관 기간 설정(log.retention.hours=72)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수집 용량 및 스트림 스로세싱 시스템 장애등을 고려해서 조정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;스트림 프로세싱(Storm 클러스터)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;EC로 Storm 클러스터(+Zookeeper) 스트림 프로세싱 구성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분산 처리 프레임워크로 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;파이프라인으로 들어온 데이터 가공
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필터링
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중복 데이터 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;파싱&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인 및 데이터 타입별로 데이터 파싱&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;필터링
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중복 데이터 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인덱싱/태깅
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메타데이터 생성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;날자&lt;/li&gt;
&lt;li&gt;지역&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;검색 시스템(Elasticsearch 클러스터)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스트림 프로세싱으로 가공된 검색 데이터 저장
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;url&lt;/li&gt;
&lt;li&gt;title&lt;/li&gt;
&lt;li&gt;summary&lt;/li&gt;
&lt;li&gt;content_type&lt;/li&gt;
&lt;li&gt;created_at&lt;/li&gt;
&lt;li&gt;locale&lt;/li&gt;
&lt;li&gt;tags&lt;/li&gt;
&lt;li&gt;click_count&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검색 정보 저장
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;keyword&lt;/li&gt;
&lt;li&gt;locale&lt;/li&gt;
&lt;li&gt;created_at&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Query 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;통계 시스템(Kibana)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스 통계 데이터 제공 및 시각화&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;캐시 시스템(Redis 클러스터)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 결과를 캐시&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;API 서버&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Timeline API 서버 구성 사용&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;Celery 서버&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비동기 태스크 큐 처리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 키워드 저장&lt;/li&gt;
&lt;li&gt;검색 결과 클릭 카운팅&lt;/li&gt;
&lt;li&gt;검색 결과 캐시 생성&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;신규 포스트 데이터 검색 시스템에 저장&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;모니터링 시스템&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;처리량 모니터링
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부하 발생 하거나, 유입량 보다 처리량이 낮으면 노티&lt;/li&gt;
&lt;li&gt;스케일 아웃 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;에러 모니터링
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;에러 발생시 노티&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로젝트</category>
      <author>warpmemory</author>
      <guid isPermaLink="true">https://warpmemory.tistory.com/167</guid>
      <comments>https://warpmemory.tistory.com/167#entry167comment</comments>
      <pubDate>Thu, 2 Jul 2020 14:01:24 +0900</pubDate>
    </item>
    <item>
      <title>Linux find 명령어로 날자별 디렉토리 파일 이동 및 복원</title>
      <link>https://warpmemory.tistory.com/166</link>
      <description>&lt;div class=&quot;colorscripter-code&quot; style=&quot;color:#010101;font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace !important; position:relative !important;overflow:auto&quot;&gt;&lt;table class=&quot;colorscripter-code-table&quot; style=&quot;margin:0;padding:0;border:none;background-color:#fafafa;border-radius:4px;&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;&lt;tr&gt;&lt;td style=&quot;padding:6px;border-right:2px solid #e5e5e5&quot;&gt;&lt;div style=&quot;margin:0;padding:0;word-break:normal;text-align:right;color:#666;font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace !important;line-height:130%&quot;&gt;&lt;div style=&quot;line-height:130%&quot;&gt;1&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;2&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;3&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;4&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;5&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;6&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;7&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;8&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;9&lt;/div&gt;&lt;div style=&quot;line-height:130%&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td style=&quot;padding:6px 0;text-align:left&quot;&gt;&lt;div style=&quot;margin:0;padding:0;color:#010101;font-family:Consolas, 'Liberation Mono', Menlo, Courier, monospace !important;line-height:130%&quot;&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&lt;span style=&quot;color:#999999&quot;&gt;#&amp;nbsp;날자별&amp;nbsp;디렉토리&amp;nbsp;이동&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;find&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;home&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;hosting&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;webanalyzer&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;data&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;db&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;*&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;maxdepth&amp;nbsp;&lt;span style=&quot;color:#0099cc&quot;&gt;1&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;type&amp;nbsp;d&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;name&amp;nbsp;&lt;span style=&quot;color:#63a35c&quot;&gt;&quot;`date&amp;nbsp;+'%Y%m'&amp;nbsp;-d&amp;nbsp;'1&amp;nbsp;year&amp;nbsp;ago&amp;nbsp;1&amp;nbsp;month&amp;nbsp;ago'`&quot;&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#066de2&quot;&gt;exec&lt;/span&gt;&amp;nbsp;bash&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;c&amp;nbsp;&lt;span style=&quot;color:#63a35c&quot;&gt;'basedir=$(dirname&amp;nbsp;{})&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;mkdir&amp;nbsp;-p&amp;nbsp;/home/_trash$basedir&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;mv&amp;nbsp;{}&amp;nbsp;/home/_trash$basedir'&lt;/span&gt;&amp;nbsp;\;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;find&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;home&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;hosting&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;webanalyzer&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;data&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;db&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;*&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;maxdepth&amp;nbsp;&lt;span style=&quot;color:#0099cc&quot;&gt;1&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;type&amp;nbsp;d&amp;nbsp;\(&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;regex&amp;nbsp;&lt;span style=&quot;color:#63a35c&quot;&gt;'.*2018[01][0-9]$'&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;or&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;regex&amp;nbsp;&lt;span style=&quot;color:#63a35c&quot;&gt;'.*20190[1-5]$'&lt;/span&gt;&amp;nbsp;\)&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#066de2&quot;&gt;exec&lt;/span&gt;&amp;nbsp;bash&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;c&amp;nbsp;&lt;span style=&quot;color:#63a35c&quot;&gt;'basedir=$(dirname&amp;nbsp;{})&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;mkdir&amp;nbsp;-p&amp;nbsp;/home/_trash$basedir&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;mv&amp;nbsp;{}&amp;nbsp;/home/_trash$basedir'&lt;/span&gt;&amp;nbsp;\;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&lt;span style=&quot;color:#999999&quot;&gt;#&amp;nbsp;이동된&amp;nbsp;디렉토리&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;find&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;home&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;_trash&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;home&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;hosting&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;webanalyzer&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;data&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;db&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;*&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;maxdepth&amp;nbsp;&lt;span style=&quot;color:#0099cc&quot;&gt;1&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;type&amp;nbsp;d&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;name&amp;nbsp;&lt;span style=&quot;color:#63a35c&quot;&gt;&quot;`date&amp;nbsp;+'%Y%m'&amp;nbsp;-d&amp;nbsp;'1&amp;nbsp;year&amp;nbsp;ago&amp;nbsp;1&amp;nbsp;month&amp;nbsp;ago'`&quot;&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#066de2&quot;&gt;exec&lt;/span&gt;&amp;nbsp;bash&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;c&amp;nbsp;&lt;span style=&quot;color:#63a35c&quot;&gt;'basedir=$(dirname&amp;nbsp;{})&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;targetdir=&quot;${basedir#/*/*/}&quot;&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;&amp;nbsp;mv&amp;nbsp;{}&amp;nbsp;/$targetdir'&lt;/span&gt;&amp;nbsp;\;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;padding:0 6px; white-space:pre; line-height:130%&quot;&gt;find&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;home&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;_trash&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;home&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;hosting&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;webanalyzer&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;data&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;db&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;*&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;maxdepth&amp;nbsp;&lt;span style=&quot;color:#0099cc&quot;&gt;1&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;type&amp;nbsp;d&amp;nbsp;\(&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;regex&amp;nbsp;&lt;span style=&quot;color:#63a35c&quot;&gt;'.*2018[01][0-9]$'&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;or&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;regex&amp;nbsp;&lt;span style=&quot;color:#63a35c&quot;&gt;'.*20190[1-5]$'&lt;/span&gt;&amp;nbsp;\)&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#066de2&quot;&gt;exec&lt;/span&gt;&amp;nbsp;bash&amp;nbsp;&lt;span style=&quot;color:#0086b3&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color:#a71d5d&quot;&gt;-&lt;/span&gt;c&amp;nbsp;&lt;span style=&quot;color:#63a35c&quot;&gt;'basedir=$(dirname&amp;nbsp;{})&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;targetdir=&quot;${basedir#/*/*/}&quot;&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;&amp;nbsp;mv&amp;nbsp;{}&amp;nbsp;/$targetdir'&lt;/span&gt;&amp;nbsp;\;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td style=&quot;vertical-align:bottom;padding:0 2px 4px 0&quot;&gt;&lt;a href=&quot;http://colorscripter.com/info#e&quot; target=&quot;_blank&quot; style=&quot;text-decoration:none;color:white&quot;&gt;&lt;span style=&quot;font-size:9px;word-break:normal;background-color:#e5e5e5;color:white;border-radius:10px;padding:1px&quot;&gt;cs&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;</description>
      <category>운영체제</category>
      <author>warpmemory</author>
      <guid isPermaLink="true">https://warpmemory.tistory.com/166</guid>
      <comments>https://warpmemory.tistory.com/166#entry166comment</comments>
      <pubDate>Mon, 15 Jun 2020 18:20:32 +0900</pubDate>
    </item>
    <item>
      <title>웹 사이트 SSL/TLS 지원 프로토콜 확인</title>
      <link>https://warpmemory.tistory.com/165</link>
      <description>&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2020년 상반기에 &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;4대 주요 웹브라우저 회사인 &lt;/span&gt;Google, Microsoft, Apple, Mozilla의 브라우저에서 TLS 1.0 및 1.1 지원을 종료한다고 합니다. 현재 TLS 프로토콜은 TLS 1.0, 1.1, 1.2, 1.3의 4가지 버전이 있지만, 1.0, 1.1은 POODLE 및 DEAST와 같은 공격에 취약한 것으로 알려져 있습니다. 이에 OpenSSL을 명령어로 프로토콜에 따라서 접속이 가능한지 확인 하는 방법을 소개합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;OpenSSL로 프로토콜 접속 테스트&lt;/h2&gt;
&lt;p&gt;openssl의 s_client 명령어와 프로토콜 옵션을 넣어서, 특정 프로토콜로 접속이 가능한지 확인할 수 있습니다. 정상적으로 접속이되면, 인증서 정보 및 접속 정보가 출력되고 종료 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1585117959248&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# openssl s_client -connect naver.com:443 -tls1  
CONNECTED(00000003)
depth=3 /C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
verify return:1
depth=2 /C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
verify return:1
depth=1 /C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Organization Validation Secure Server CA
verify return:1
depth=0 /C=KR/postalCode=13561/ST=Gyeonggi-do/L=Seongnam-si/streetAddress=6, Buljeong-ro/O=NAVER Corp./OU=Information Security Team/OU=Hosted by Korea Information Certificate Authority, Inc./OU=PremiumSSL Wildcard/CN=*.www.naver.com
verify return:1
---
Certificate chain
 0 s:/C=KR/postalCode=13561/ST=Gyeonggi-do/L=Seongnam-si/streetAddress=6, Buljeong-ro/O=NAVER Corp./OU=Information Security Team/OU=Hosted by Korea Information Certificate Authority, Inc./OU=PremiumSSL Wildcard/CN=*.www.naver.com
   i:/C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Organization Validation Secure Server CA
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Organization Validation Secure Server CA
   i:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
 2 s:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
 3 s:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIHnTCCBoWgAwIBAgIRAPG4tSHPCotEtG1VzRODH4owDQYJKoZIhvcNAQELBQAw
gZUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE9MDsGA1UE
AxM0U2VjdGlnbyBSU0EgT3JnYW5pemF0aW9uIFZhbGlkYXRpb24gU2VjdXJlIFNl
cnZlciBDQTAeFw0xOTA0MTAwMDAwMDBaFw0yMTA0MjgyMzU5NTlaMIIBFjELMAkG
A1UEBhMCS1IxDjAMBgNVBBETBTEzNTYxMRQwEgYDVQQIEwtHeWVvbmdnaS1kbzEU
MBIGA1UEBxMLU2VvbmduYW0tc2kxFzAVBgNVBAkTDjYsIEJ1bGplb25nLXJvMRQw
EgYDVQQKEwtOQVZFUiBDb3JwLjEiMCAGA1UECxMZSW5mb3JtYXRpb24gU2VjdXJp
dHkgVGVhbTFAMD4GA1UECxM3SG9zdGVkIGJ5IEtvcmVhIEluZm9ybWF0aW9uIENl
cnRpZmljYXRlIEF1dGhvcml0eSwgSW5jLjEcMBoGA1UECxMTUHJlbWl1bVNTTCBX
aWxkY2FyZDEYMBYGA1UEAwwPKi53d3cubmF2ZXIuY29tMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAri825vCxtjFOswvvlJ0Nm1UEY80kt33LbKmjqFnM
S9CiB7VODBp7e3Sw09F4Q2gu98L3BNEhR245O06ClFUj5mq9JJnGrRGcmjp/PhTJ
puYnPxlHdRXOsAYK/j97GJTpM05Xzzm94N/6Shr6h+LDvtZ7MDc9tNHv5ZquxBxx
tRvfnYAiZp0+zzWocCp2bj2ghvRlhXp7MRhT1S8BV1SwrN1aOjaua2hmjmBC9jav
6VOc72K5jNBhAsPQ0iFYmc1cFCCch6LFQaGXwssDfmVVRjSeRwRwIcJbiyVShaqY
gk9FtgSln3tc1Z60/89PsGgOItpIVoWwk/rgkzSAIkQvRQIDAQABo4IDYjCCA14w
HwYDVR0jBBgwFoAUF9nWJSdn+THCSUPZMDZEjGypT+swHQYDVR0OBBYEFLh6Zc2P
k8619JQ6JoHvw+9/CdsyMA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0G
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBKBgNVHSAEQzBBMDUGDCsGAQQB
sjEBAgEDBDAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3RpZ28uY29tL0NQUzAI
BgZngQwBAgIwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2NybC5zZWN0aWdvLmNv
bS9TZWN0aWdvUlNBT3JnYW5pemF0aW9uVmFsaWRhdGlvblNlY3VyZVNlcnZlckNB
LmNybDCBigYIKwYBBQUHAQEEfjB8MFUGCCsGAQUFBzAChklodHRwOi8vY3J0LnNl
Y3RpZ28uY29tL1NlY3RpZ29SU0FPcmdhbml6YXRpb25WYWxpZGF0aW9uU2VjdXJl
U2VydmVyQ0EuY3J0MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNv
bTApBgNVHREEIjAggg8qLnd3dy5uYXZlci5jb22CDXd3dy5uYXZlci5jb20wggF9
BgorBgEEAdZ5AgQCBIIBbQSCAWkBZwB1ALvZ37wfinG1k5Qjl6qSe0c4V5UKq1Lo
GpCWZDaOHtGFAAABagYQtsUAAAQDAEYwRAIgcvnYKlDj5iiMyXUXDZaL+3klOjFl
1kpDV8smCZmgrsUCIFLq43RudmteYHp/Vz13MfOjpeQ1spviyauCe/zA9XVzAHYA
RJRlLrDuzq/EQAfYqP4owNrmgr7YyzG1P9MzlrW2gagAAAFqBhC2xAAABAMARzBF
AiAGC+BNdF49ESvhqGhprDN0SndEXXp8Bvu8lCgPpJGkJgIhANuhe20ajyfGk0m5
e07meCD6NP5roWv/kdtA9yESZsITAHYAXNxDkv7mq0VEsV6a1FbmEDf71fpH3KFz
lLJe5vbHDsoAAAFqBhC27gAABAMARzBFAiB4z/lDFMMQDEIR7HuYO5m2oVFVgsyG
slTJz9S9OUVhmQIhAO2S+srpfoOTAYLYXStlidzGdOql4RUuCUOUyhJ1/eybMA0G
CSqGSIb3DQEBCwUAA4IBAQAOYLCmqVASHh1ybN8AMGpTvnVglf4G7EOZVffQ0A/b
EZzfKT1amG6Ha24P5REPZs81/nHH0biLTN5LusRU9wPWzxpnxyZKAsyuPP9zxc9K
2h0quJIEZMTNy6JDP1BU6BHnGlz7TeuLfmcm/H3EeKcU6m5LiyB+W8q5c7ND5Sv6
/nIuwZolhF2EeLjVZ+gTFjvqEaBtnXLWyNr7XIV5xpqSXyFDOeHcRfTVA7UZH3j/
JmmmIlwcdsvUAZ1nNXxFX/xQ1C5aiqe0fJuLTa8glfPzYvZwjG88Kx0KFSvcbFEF
rCcMKOi42bm/30le9Ul2rHH4wLaGoZemkjz0uxDbanpR
-----END CERTIFICATE-----
subject=/C=KR/postalCode=13561/ST=Gyeonggi-do/L=Seongnam-si/streetAddress=6, Buljeong-ro/O=NAVER Corp./OU=Information Security Team/OU=Hosted by Korea Information Certificate Authority, Inc./OU=PremiumSSL Wildcard/CN=*.www.naver.com
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Organization Validation Secure Server CA
---
No client certificate CA names sent
---
SSL handshake has read 6174 bytes and written 413 bytes
---
New, TLSv1/SSLv3, Cipher is AES128-SHA
Server public key is 2048 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : AES128-SHA
    Session-ID: 6BCE041B1F64BC3876272307D8B4AA679C5319FF7592FF97A5CBE75137CAFBFA
    Session-ID-ctx: 
    Master-Key: 819D24CDBECE180F26286CEE10AF96BBB34BDF98D1A0A5F24638CB7D117DE795A39639ABB5FD37AA63EEB2CB0B0B2BF4
    Key-Arg   : None
    Krb5 Principal: None
    Start Time: 1585117802
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---
closed&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;프로토콜 옵션은 다음과 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Option&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-ssl2&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;just&amp;nbsp;use&amp;nbsp;SSLv2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-ssl3&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;just&amp;nbsp;use&amp;nbsp;SSLv3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-tls1_2&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;just&amp;nbsp;use&amp;nbsp;TLSv1.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-tls1_1&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;just&amp;nbsp;use&amp;nbsp;TLSv1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-tls1&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;just&amp;nbsp;use&amp;nbsp;TLSv1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;-dtls1&lt;span style=&quot;color: #333333;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;just&amp;nbsp;use&amp;nbsp;DTLSv1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>엔지니어링</category>
      <category>OpenSSL</category>
      <category>Protocol</category>
      <category>s_client</category>
      <category>version</category>
      <author>warpmemory</author>
      <guid isPermaLink="true">https://warpmemory.tistory.com/165</guid>
      <comments>https://warpmemory.tistory.com/165#entry165comment</comments>
      <pubDate>Wed, 25 Mar 2020 19:50:52 +0900</pubDate>
    </item>
  </channel>
</rss>