Journey to find abnormal activities from logging data with periodic patterns
Background
Monitoring and configuring alerts for a new Single Sign-On service was done easily, but traditional metrics to find abnormal activities remains tough.
โข
Facebook Prophetย ์๊ณ์ด ์์ธก ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉ
โข
์ ๊ท SSO ์ธ์
๋ชจ๋ํฐ๋ง์ ์ํด ์์งํ ์๋น์ค๋ณ ์๊ณ์ด ์ธ์
์นด์ดํธ ํ์ฉ
โข
3์๊ฐ ์์ธก ๋ฐ์ดํฐ ์์ฑ: 1์๊ฐ๋ง๋ค ์คํ์์ 28์ผ๊ฐ ์ด์ ๋ฐ์ดํฐ ๊ธฐ๋ฐ์ผ๋ก ์ถ์ถ
โข
์ ์ผ ๋ง์ด ์ฌ์ฉํ๋ ๊ทธ๋ฃน์จ์ด ์ธ์
๋ง ์์ธก ์ ์ฉ
โข
๊ธฐ์กด Linear ํ ์ ๋๊ฐ์ผ๋ก๋ง ์๋ฆผ์ ์ค์ ํ๋ค๋ฉด, ์์ธก์ ํตํ ๊ฐ๋ณ์ ์ธ ์๋ฆผ ์ค์ ์ด ๊ฐ๋ฅ
Data Collection
๋ฐ์ดํฐ ์์ง ์ค๋น
โข
SSO ์ธ์
์ 1๊ฐ๋ง ์ ์ํ์ฌ Refresh Token ์ผ๋ก ์ต๋ํ ์ฌ์ฌ์ฉํ๊ณ ์๋ก์ด ์ธ์
์ ๋ฐ์์ค๋ ๋ก์ง
#!/bin/bash
REFRESH_TOKEN=`cat /var/opt/aerobase/dashboard/refresh_token`
RESULT=`curl --silent --location --request POST 'https://xxxx.co.kr/auth/realms/xxxx/protocol/openid-connect/token' --header 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=refresh_token' --data-urlencode 'client_id=view' --data-urlencode 'client_secret=************************' --data-urlencode 'refresh_token='$REFRESH_TOKEN''`
if echo ${RESULT} | grep -i "error"; then
echo "refresh failed, create a new session"
RESULT=`curl --silent --location --request POST 'https://xxxx.co.kr/auth/realms/xxxx/protocol/openid-connect/token' --header 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=client_credentials' --data-urlencode 'client_id=view' --data-urlencode 'client_secret=************************'`
fi
echo ${RESULT} | jq '.access_token' | sed -e 's/\"//g' > /var/opt/aerobase/dashboard/token
echo ${RESULT} | jq '.refresh_token' | sed -e 's/\"//g' > /var/opt/aerobase/dashboard/refresh_token
Bash
๋ณต์ฌ
๋ฐ์ดํฐ ์ถ์ถ, ์ ์ฒ๋ฆฌ, InfluxDB ๋ก ์ ์ฅ
#!/bin/bash
DB_NAME='app'
TABLE_NAME='sso-session'
TOKEN=`cat /var/opt/aerobase/dashboard/token`
# SSO API ์ธ์
์ ๋ณด ์์ฒญ
curl --silent --location --request GET 'https://xxxx.co.kr/auth/admin/realms/xxxx/client-session-stats' --header 'Authorization: Bearer '$TOKEN'' > /var/opt/aerobase/dashboard/result
# ์์ ๋ฐ์ดํฐ์์ ํ์ํ ๋ฐ์ดํฐ๋ง ์ถ์ถํ์ฌ ์ ์ฒ๋ฆฌ
RESULT=`cat /var/opt/aerobase/dashboard/result | jq '.[] | {(.clientId):.active}'`
DATA=`echo $RESULT | sed -e 's/\:/\=/g' | sed -e 's/{\| //g' | sed -e 's/\"//g' | sed -e 's/}/,/g' | sed -e 's/.$//'`
# InfluxDB ๋ก ์ ์ฅ ์์ฒญ
curl -s -i -XPOST http://xxxx:8086/write?db=${DB_NAME} --data-binary "${TABLE_NAME} ${DATA}" | grep 'HTTP/1.1 204' > /dev/null 2>&1
Bash
๋ณต์ฌ
์์ ) SSO API ํธ์ถ ํ ์์ ๋ฐ์ ์ํ ๋ฐ์ดํฐ
[{"offline":"0","clientId":"idm-client","active":"1","id":"2690c992-bb59-4891-8220-62f86d7a6d33"},{"offline":"0","clientId":"data","active":"28","id":"77cbc073-c6c4-4fd1-92f3-3e2b44ef7912"},{"offline":"0","clientId":"portal","active":"418","id":"9d7f3882-f9be-4584-b16d-1c6c573d7a9d"},{"offline":"0","clientId":"iam-admin","active":"22","id":"5c2edf26-60e7-4ffc-b7d6-4a9a7e4cea82"},{"offline":"0","clientId":"iam","active":"4","id":"309c8cfc-e49e-4303-964a-2e0dfe79e566"}]
Bash
๋ณต์ฌ
Facebook Prophet Docker ํ๊ฒฝ ์ค๋น
Dockerfile
FROM lppier/docker-prophet
RUN pip install -U pip influxdb tox workalendar
ENTRYPOINT ["python"]
Bash
๋ณต์ฌ
Docker image ์์ฑ
[server ~]$ sudo docker build -t prophet-influxdb-phil:202005081129 .
...
[server ~]$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
prophet-influxdb-phil 202005081129 55794cdad742 7 seconds ago 1.03GB
lppier/docker-prophet latest 2a09f62db268 15 months ago 1.01GB
Bash
๋ณต์ฌ
์์ธก ๋ฐ์ดํฐ ์์ฑ
Prophet - InfluxDB ์ฐ๋ํ์ฌ Forecast ์์ฑํ๋ ์์ค์ฝ๋
class InfluxDataFrameIO(DataframeIO):
def read(self, days: int = 7):
r = self.client.query(""" SELECT last(portal) as y FROM "{:1}" WHERE time >= now() - {:2}d GROUP BY time(1m) fill(null)""".format(self.influxdb_input_measurement, days))
df = pd.DataFrame(r.get(self.influxdb_input_measurement), columns=['y'])
df = df.tz_convert(None)
df.reset_index(inplace=True)
df.rename(inplace=True, columns={'index':'ds'})
return df[:-1]
def write(self, outputDataFrame: pd.DataFrame):
self.client.write_points(
outputDataFrame,
measurement = self.influxdb_output_measurement,
tags=self.influxdb_output_tags,
time_precision='m'
)
def forecast(df: pd.DataFrame):
m = Prophet(
holidays=get_holidays(),
holidays_prior_scale=100,
interval_width=0.99,
daily_seasonality=10,
weekly_seasonality=20,
)
m.fit(df)
# https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
future = m.make_future_dataframe(
periods=180,
freq='1T' # Minute
)
forecast = m.predict(future)
out = forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']]
o = out.set_index('ds')
return o
if __name__ == "__main__":
io = InfluxDataFrameIO(
client=DataFrameClient(
host='influxdb',
port=8086,
username='****',
password='****',
database='****'
),
influxdb_input_measurement='sso-session',
influxdb_output_measurement='sso-session',
influxdb_output_tags = {
'forecast': 'sso-prophet-20200508'
}
)
# 28์ผ ๋์์ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์ธก ๋ฐ์ดํฐ ์์ฑ
inputDataFrame = io.read(days=28)
outputDataFrame = forecast(inputDataFrame)
# ์์ฑ๋ ์์ธก ๋ฐ์ดํฐ๋ฅผ InfluxDB ์ ์ ์ฅ
io.write(outputDataFrame)
Python
๋ณต์ฌ
์คํ
[server ~]$ time sudo docker run -it --rm -v $(pwd):/app prophet-influxdb-phil:202005081129 ssoSessionForecast.py
INFO:fbprophet:Disabling yearly seasonality. Run prophet with yearly_seasonality=True to override this.
Initial log joint probability = -460.372
Iter log prob ||dx|| ||grad|| alpha alpha0 # evals Notes
99 38790.9 0.0447242 3752.01 0.8642 0.8642 111
...
...
Iter log prob ||dx|| ||grad|| alpha alpha0 # evals Notes
3720 41037.3 8.66095e-05 361.545 5.573e-07 0.001 4276 LS failed, Hessian reset
3778 41037.4 2.00873e-06 65.3976 0.8632 0.8632 4344
Optimization terminated normally:
Convergence detected: relative gradient magnitude is below tolerance
real 5m39.738s
user 0m0.052s
sys 0m0.071s
Bash
๋ณต์ฌ
๋ฐ์ดํฐ ๊ฐ๋ณ
๊ณตํด์ผ ๋ฐ ํ์ฌ ํด์ผ ์ ์ฉ
For instance, if you wanted to include Christmas Eve in addition to Christmas youโd includeย lower_window=-1,upper_window=0.If you wanted to use Black Friday in addition to Thanksgiving, youโd includeย lower_window=0,upper_window=1.You can also include a columnย prior_scaleย to set the prior scale separately for each holiday, as described below.
ํฌ๋ฆฌ์ค๋ง์ค ์ด๋ธ๋ฅผ ํฌ๋ฆฌ์ค๋ง์ค ๊ณตํด์ผ๋ก ์ธ์ํ๊ฒ ํ๊ณ ์ถ์ผ๋ฉด, -1์ ์ ์ฉํจ
๋ธ๋ ํ๋ผ์ด๋ฐ์ด๋ฅผ ์ถ์๊ฐ์ฌ์ ๋ก ์ธ์ํ๊ณ ์ถ์ผ๋ฉด upper_window ๋ฅผ 1 ์ ์ฉํจ (์ถ์๊ฐ์ฌ์ ์ ๋งค๋ฒ ๋ชฉ์์ผ์ด์ฌ์ ๋ธ๋ ํ๋ ์ด๋ฐ์ด๋ ๋ค์๋ ๊ธ์์ผ)
๊ณตํด์ผ ๋น์ผ๋ง ์ธ์ํ๊ฒ ํ๊ณ ์ถ์ผ๋ฉด, lower_window, upper_window ๋ฅผ ์
๋ ฅํ์ง ์์๋ ๋จ, ์๋๋ฉด ๋๋ค 0์ผ๋ก ์
๋ ฅ(๋น์ผ)
์ถํ ํ์ฌ ๊ณตํด์ผ์ ์๋ ์
๋ ฅ์ด ์๋๋ผ ๋ณ๋ ํ
์ด๋ธ์ ์ ์ฅํ์ฌ ๊ฐ์ ธ์ค๋๋ก ์ค์ ํด์ผํจ
def get_holidays():
from workalendar.asia import SouthKorea
cal = SouthKorea()
y = pd.DataFrame(cal.holidays(2020), columns=['ds', 'holiday'])
# y.insert(2, 'lower_window', 0)
# y.insert(3, 'upper_window', 1)
gsshop = pd.DataFrame({
'ds': pd.to_datetime(['2020-05-01']),
'holiday': 'company',
# 'lower_window': 0, # ์
๋ ฅํ ๋น์ผ๋ง ๊ณตํด์ผ๋ก ์ ์ธํ๊ณ ์ถ์ ๊ฒฝ์ฐ ํ์ ์์
# 'upper_window': 1, # ์
๋ ฅํ ๋น์ผ๋ง ๊ณตํด์ผ๋ก ์ ์ธํ๊ณ ์ถ์ ๊ฒฝ์ฐ ํ์ ์์
})
holiday = pd.concat((y, gsshop)) # ๊ณตํด์ผ ๋ฐ์ดํฐ์ ํฌํจ
return holiday
Python
๋ณต์ฌ
๊ณตํด์ผ ๋ฐ์ดํฐ ๊ฐ์ค์น ๋์
โข
ํ์์๋ฆฌ์ฆ ๋จ์:ย https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
โข
180๋ถ (3์๊ฐ)์ ์์ธก ๋ฐ์ดํฐ๋ฅผ ํฌํจํ์ฌ InfluxDB ์ ์
๋ ฅ
def forecast(df: pd.DataFrame):
"""
input: ์ ์ ๋ ์ค์ธก ๋ฐ์ดํฐ Dataframe
output: ์์ธก๋ Dataframe
"""
m = Prophet(
holidays=get_holidays(),
holidays_prior_scale=100, # 0.99 ์ด์๋๋ฐ 100์ผ๋ก ์ฌ๋ฆผ
interval_width=0.99,
daily_seasonality=10, # ์ฝ๋ฉํธ ๋์ด ์์๋๋ฐ ์ถ๊ฐํจ
weekly_seasonality=20, # ์ฝ๋ฉํธ ๋์ด ์์๋๋ฐ ์ถ๊ฐํจ
)
m.fit(df)
future = m.make_future_dataframe(
periods=180,
freq='1T'
)
forecast = m.predict(future)
out = forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']]
o = out.set_index('ds')
return o
Python
๋ณต์ฌ
Grafana ์ฐ๋
๊ธฐ์กด ๋ฐ์ดํฐ ๊ทธ๋ํ์ ์์ธก ๋ฐ์ดํฐ ์ถ๊ฐ
1.
yhat_lower (์์ธก ๋ฒ์ ํ์๊ฐ), yhat_upper (์์ธก ๋ฒ์ ์์๊ฐ), yhat (์์ธก ์ค์๊ฐ)
2.
ํ๋ท ์ธ์
์๊ฐ yhat_lower (์์ธก ๋ฒ์ ํ์๊ฐ๋ณด๋ค ์์ ๊ฒฝ์ฐ์๋ง ๋ฐ์ดํฐ ๋ณด์ฌ์ค)ย โ ์๋ฆผ์ ์ํ ๊ทธ๋ํ
3.
InfluxDB 1.4 ์ฌ์ฉ์ค (abs ๊ฐ์ function ์ ์ฌ์ฉ ์๋จ)
Visualization ์ค์
โข
์์ธก ์ค์๊ฐ์ ์๋ณด์ฌ์ค
โข
์์ธก ์ํ์ ๋ฒ์๋ ์ ์ด ์๋ ๋ฐฑ๊ทธ๋ผ์ด๋์ ๋ค๋ฅธ ์์ผ๋ก ํํ, ๋ ์ ๋์์๋ ๋ณด์ฌ์ฃผ์ง ์์
โข
์๋ฆผ์ ์ํ ๊ทธ๋ํ๋ ์๋ณด์ฌ์ค
์์ธก ์ ์ฉ ํ๋ฉด
์๋ฆผ ์ค์
โข
๊ทธ๋ฃน์จ์ด ์ธ์
์๊ฐ yhat_lower (์์ธก ๋ฒ์ ํ์๊ฐ) ๋ณด๋ค ์์ ๊ฒฝ์ฐ์ ์๋ฆผ
โข
๊ทธ๋ฃน์จ์ด ์ธ์
์๊ฐ 2000 ์ด์์ธ ๊ฒฝ์ฐ ์๋ฆผ (์ฌ์ 1000๋ช
๊ธฐ์ค)
โข
ํด๋น ์ค์ ์ด 5๋ถ๋์ ์ ์ง๋๋ ๊ฒฝ์ฐ ์๋ฆผ
Facebook Prophet ์ ํ๊ณ
๋๋ ทํ ์ฌ์ฉ ๋ชฉ์ ๋ฐ ์ ์ฉ ๋์
โข
์๊ณ์ด ๋ฐ์ดํฐ (์๊ฐ์ ํ๋ฆ): ์๊ฐ์ ํ๋ฆ์ ๋ฐ๋ผ ๊ฐํ ์์ฆ ํน์ง์ ๊ฐ๊ณ ์๋ ๊ณณ์ ์ ์ฉ ๊ฐ๋ฅ
โข
๊ฐํ ์์ฆ ํน์ง: ์๊ฐ์ ๋ฐ๋ฅธ ๋์ผํ ํจํด์ ๊ฐ๋ ๋ฐ์ดํฐย โย ์ฌ๋ด ๊ทธ๋ฃน์จ์ด ๋ก๊ทธ์ธ ํจํด, ์ถ์
์ฆ ์
๋ ฅ ํจํด, ์ถ๊ทผ ์๊ฐ๋ ํจํด
โข
๋ค์ด๋ฒ D2 ๋ค์ด๋ฒํ์ด ๊ฒฐ์ ์์คํ
์์ธก -ย https://d2.naver.com/helloworld/0065813
๊ณผ๊ฑฐ์ ์ฅ๊ธฐ์ ์ธ ์ถ์ธ๋ฅผ ๋ณด๊ณ ์ดํ์ ๋๋ต์ ์ธ ์ถ์ธ๋ฅผ ํ์ธํ๋ ์ฉ๋๋ก ์ฌ์ฉํ ๋ Prophet ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ๋ฅญํ ๊ฒฐ๊ณผ๋ฌผ์ ๋ณด์ฌ ์ค๋ค. ํนํ ๊ฐ์์ค๋ฐ ๋ฐ์ดํฐ์ ๋ณ๋์ด ์๋ ์ํฉ์์๋ ์ค์ ๋ฐ์ดํฐ์ ์์ฃผ ๊ทผ์ ํ ์์ธก ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ ์ค๋ค.ํ์ง๋ง ๋ช
์ ์ด๋ ๊ธฐํ ์ํฉ์ ์ํด ๊ฑฐ๋๋์ด ํ์์ ์ผ๋ก ํ๋ฝํ๊ฑฐ๋ ์ฆ๊ฐํ๋ ๊ฒฝ์ฐ, ์ฌ์
ํ๋ ๋ฑ์ผ๋ก ์ธํด ์ ๋ฐ์ ์ธ ๊ฑฐ๋๋ ์์ฒด๊ฐ ๋ณ๋๋๋ ๊ฒฝ์ฐ ๋ฑ์์๋ ์ ์ ํ ์์ธก ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ ์ฃผ์ง ๋ชปํ๋ ๋ฌธ์ ๊ฐ ์๋ค. ๊ณผ๊ฑฐ์ ์ถ์ธ๋ฅผ ๋ฐ๋ผ ์ฃผ๊ธฐ ํจ์๋ฅผ ๋ง๋ค๊ณ ์ดํ์ ๊ฑฐ๋๋ฅผ ์์ธกํ๋ ๋ชจ๋ธ์ ํ๊ณ ๋๋ฌธ์ด๋ค.
DB Active Session๊ณผ ํน์ ์ํ์ ํ๋งค ์ถ์ด๋ฅผ ๊ด๋ จ์์ผ ์์ธกํ๋ ๋ฐ ์ฌ์ฉํ๋ ๊ฒ์ ๊ฐ๋ฅํ๊ฐ?
๊ณ ๋ ค๋์ด์ผ ํ Factor๊ฐ ๋๋ฌด ๋ง๋ค!ย โย ๋ฐ์ดํฐ ๊ณผํ์ ๋ฐ AI ์ ์์ญ
โข
์์ผ: ์์์ผ์ ์ ํ๋ฆฌ๋ ์ ํ์ด ์ผ์์ผ์๋ ์ ํ๋ฆฌ๋?
โข
์๊ฐ: ์ ๋
8์์ ์ ํ๋ฆฌ๋ ์ ํ์ด ์ค์ 8์์๋ ์ ํ๋ฆฌ๋?
โข
ํด์ผ: ํ์ผ์ ์ ํ๋ฆฌ๋ ์ ํ์ด ํด์ผ์๋ ์ ํ๋ฆฌ๋?
โข
์ธ๋ถ ์์ธ: ๋ ์จ, ๊ณ์ , ์ฌํ์ ์ด์(๋ง์คํฌ)์ ๋ฐ๋ผ ์ ํ๋ฆฌ๊ณ ๋ ํ๋ฆฌ๋๋ฐ ์ด๋ป๊ฒ ๊ฐ์ค์น๋ฅผ ๋ ๊ฒ ์ธ๊ฐ?
DB Active Session๊ณผ Plain ์ํ ํ๋งค ์ถ์ด๋ง ์กฐํฉํ์ฌ ์์ธก ๋ชจ๋ธ์ ๋ง๋ค์ด ์ฌ์ฉํ๋ ๊ฒ์ ์๋ฏธ๊ฐ ์๋ ๊ฒ์ผ๊น?
๊ณ ๋ ค๋๋ Factor (์ฌ์ฉ๊ฐ๋ฅ)
โข
์์ผ
โข
์๊ฐ
โข
ํด์ผ
Active Session ์นด์ดํธ์ ์ฃผ๋ฌธ ์๋์ ์ด๋ป๊ฒ ์กฐํฉํด์ ์์ธก ๋ฐ์ดํฐ๋ฅผ ์์ฑํ ์ง ์๊ฐํด๋ณด์ (Prophet ์ผ๋ก ๊ฐ๋ฅํ๊ฐ?)
PoC
๊ทธ๋ ๋ค๋ฉด ์ฐ์ DB Active Session ๋ง ๊ฐ๊ณ ์์ธก์ ํด ๋ดค์๋ ์ด๋ ์ ๋ ๋ง๋์ง๋ฅผ ์ดํด๋ณด์
โข
DB Active Session ์นด์ดํธ์๋ง์ผ๋ก ์์ธก ๋ฐ์ดํฐ๋ฅผ ๋์
ํด๋ณด๊ณ ์ผ์ถ ๋ง๋์ง PoC ํด๋ณด์ย
โข
DB Active ์๊ณ์ด ๋ฐ์ดํฐ๋ฅผ InfluxDB ์ ์์งํ๋ค
โข
์? ๊ทธ๋๋ Static ํ ์๋ฆผ ํ๊ณ์น๋ฅผ ์ค์ ํด์ ํ๋ ๊ฒ ๋ณด๋ค ์ข ๋ Flexible ํ ์๋ฆผ ์์น๋ฅผ ์ ์ํด ์ค ์ ๋ ์๋ค.
WebDB Active User Session Count ์์ง ๋ฐ ์์ธก
์ค์๊ฐ์ ๋ฐํ์ผ๋ก ๊ณตํด์ผ ๋ฐ์ดํฐ ์ ๋ ฅํ์ฌ ์ ์ฉํ ๊ฒฐ๊ณผ
โข
๊ธฐ์กด ์ธ์
์๊ฐ ๋ฎ์ ๊ฒฝ์ฐ ์๋ฆผ์ ๋ฐ๋ ์ค์ ์ ์๋ค๊ณ ํจ
โข
์ธ์
์๋ฅผ 120์ ๋๊ฐ ๋๋ ๊ฒฝ์ฐ ์๋ฆผ์ ๋ฐ๊ณ ์๋ค๊ณ ํจ
โข
๋ฒ์๋ฅผ ๋ฒ์ด๋๋ ์ผ์ด์ค๋ฅผ ์ดํด๋ณด์
โฆ
๋งค์ผ ์๋ฒฝ 2์ ๋ฐฐ์น ์์
์ ์นด์ดํธ ํ
โช
Sub-Daily ์ต์
์ ํ์ฉํด์ Fitting ํด๋ณด์ย
โช
On/Off Seasonality ๋ฅผ ํ์ฉํด์ Fitting ํด๋ณด์
๋งค์ผ 02:03~02:06 ์ฌ์ด ๋ฐฐ์น์์ ์ ๋ฐ์ดํฐ๋ ๊ทธ๋ํ์์ ์๋ณด์ด๊ฒ ์ฒ๋ฆฌ
โข
์ฟผ๋ฆฌ์ ์กฐ๊ฑด์ ์ถ๊ฐํ ์ด์ : ์์ธ ์๊ฐ์ ๊ฐ๊ณ ์๋ ๋ฐ์ดํฐ๋ฅผ ํฌํจํ ์๋ฆฌ์ฆ์ ๋ฏธ๋ฆฌ ๋ฃ๊ณ Alert ์์ ์กฐ๊ฑด์ ๊ฑฐ๋ ์ฅ์์ ๋์ค์ ์ฌ์ฉ ์์ (๋ณผ ์๊ฐ ์์ผ๋ฉฐ, ํ์์ ์์ ํด์ผํ๋ฏ๋ก)
โข
์ถํ์๋ ์ฌ๋ฌ ์์
๋ค์ด ์๋ ์๊ฐ์ ๊ฒฝ์ฐ ๋ณ๋ ์๋ฆฌ์ฆ๋ก ๋ด๊ณ , ํด๋น Alert ์กฐ๊ฑด์ ํด๋น ์๊ฐ์๋ ์๋ฆผ ๋ณด๋ด์ง ์๋๋ก ์ค์ ํ๋ฉด ๊ฐ๋ฅ
โข
์ ์ด์ ๋ฐฐ์น ์์
์ ์ฌ์ฉ๋๋ ์ธ์
์ ์ ์ธํ๊ณ ๋ฐ์ดํฐ๋ฅผ ๋ด์ ์ ์๋์ง ํ์ธ ํ์ย โ ์์
์ค์ ์๋ชป๋๋ ๊ฒฝ์ฐ๋ ๋ด์ผํ๋ฏ๋ก ๋ฐ์ดํฐ ๋ณด์ ํด์ผํจ
โข
Grafana ์์๋ ํน์ ์๊ฐ๋์ ์๋ฆผ์ ์๋ณด๋ด๋ ๋ฐฉ์์ ์์ฒญ์ด ๋ง์์ง๋ง ์์ง ๊ฐ๋ฐ๋์ง ์์
โข
๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก, ํด๋น ์๊ฐ/๋ถ์ ๊ฐ์ ํ
์ด๋ธ์ ์
๋ ฅํ๋๋ก ํ์ฌ, query ์กฐ๊ฑด์ ์
๋ ฅํจ
SELECT max("qty") FROM "active_session_webdb" WHERE $timeFilter and (hour != 2 and minute < 6 and minute > 3) GROUP BY time($__interval) fill(linear)
SQL
๋ณต์ฌ
Redis ์ ๋ณด ์์ง ๋ฐ ์์ธก ๋ฐ์ดํฐ ์์ฑ - Data ๊ธฐ๋ฐ ์ฅ์ ์ด์์งํ ๊ฐ์ง PoC
โข
ํ์ฌ: Prometheus ๋ก ์์ง๋๊ณ ์๋ ์๋ฃ๋ฅผ Grafana์ ์ฐ๋ํ์ฌ ๋ชจ๋ํฐ๋ง ์งํ์ค
โข
Redis์ Key ์นด์ดํธ๊ฐ ์ฆ๊ฐ๋ง ํ๊ณ ์ค์ด๋ค์ง ์๋ ๊ฒฝ์ฐ ์ฅ์ ๋ฐ์ ํ ์ ์์ (๋ฉ๋ชจ๋ฆฌ ์์ง)
โข
Key ์นด์ดํธ๋ฅผ ๋ณด๋ฉด ํจํด์ด ๋ณด์ด๊ธฐ๋ ํ์ฌ ์ ์ฉ ๊ฐ๋ฅ์ฑ์ด ๋ณด์
โข
๊ฐ์ Category ์์๋ Load Balancing ์ด ๋์ด ์๋์ง ๊ฑฐ์ ์ผ์นํ๋ ํจํด์ ๋ณด์ (์์
์์ ์ ์ ์ธํ๋ฉด ๋ค๋ฅธ ํจํด์ ๋ณด์ด๋ ๊ฒฝ์ฐ ์๋ฆผ์ ์ ์ฉ ํ ์ ์์์ง๋...)
โข
Redis ๋ฅผ ๋ฐ๋ผ๋ณด๋ WAS์ ๋์๋ฅผ ์ผ์ถ ๋น์ทํ๊ฒ ๋๋ ๋๊ณ ์ฌ์ฉํ๋ ํค์ฌ์ ๊ฐ์ ์นดํ
๊ณ ๋ฆฌ์ ์๊ฐ ๋ฌ๋ผ๋ ์ด์ํ ๊ฒ์ด ์์
โข
Key Count ๋ ์ ์ธ: MC ๊ฐ์ธํ์ ๊ฒฝ์ฐ 0์ด์ฌ๋ ์ด์ํ ๊ฒ์ด ์๋, ๊ทธ๋ฅ DB ์ฉ๋๋ก ์ฌ์ฉํ์ฌ ์ ์ธ
Data ์ ์ฅ ๊ณํ: Prometheusย โ InfluxDB
โข
Redis ์ ๋ณด๋ Prometheus ๋ก ์ ์ฅ๋์ด ์์
โข
Prometheus ์ ์์ธก ๋ฐ์ดํฐ๋ฅผ ์
ํ๋ ์์
์ ์ถ์ฒ๋์ง ์์
โฆ
์์ ๋ฐ์ดํฐ, ๋ฏธ๋ ๋ฐ์ดํฐ๋ฅผ ์
๋ ฅํ๋ฉด ์ ๋์ํ์ง ์๋๋ค๊ณ ๋ค์
Data ์ถ์ถ
โข
Prometheus RestAPI ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถ
โข
Python Pandas ๋ฅผ ํตํด DataFrame ์ Merge ํ์ฌ ํ๊ฐ์ InfluxDB์ ์ฃผ์
โข
InfluxDB ์ฃผ์
์ Facebook Prophet ์์ธก ๋ฐ์ดํฐ๋ ํจ๊ป ์ถ๊ฐย โ Prometheus ์ InfluxDB ์ฑํฌ ํ๋ก์ธ์ค๋ฅผ ์๋์ผ๋ก ์์ฃผํ๊ณ , ๋ณ๋ ํ๋ก์ธ์ค๋ก ์์ธก ๋ฐ์ดํฐ ์์ฑ ๋ฐฐ์น๊ฐ ํ์ํจ
โข
Prometheus ์์ ์ถ์ถ์ ์๋ฆฌ์ฆ๋น 11,000 ํฌ์ธํธ ์ด์ ์ถ์ถ์ 400 ์๋ฌ ๋ฐ์ย โ ๋ฒ์๋ฅผ ์ข๊ฒ ๋๋ ๊ทธ๋ฃนํ ํด์ ํด์ผํจ
Data ํ์ธ
Local ํ๊ฒฝ์ InfluxDB ์คํ
docker run -p 8086:8086 -v influxdb:/var/lib/influxdb influxdb
Local ํ๊ฒฝ์ Chronograf ์คํ
docker run -it --rm -p 18888:8888 chronograf --influxdb-url=http://*.*.*.*:8086
Data ์ฃผ์
Crontab ์ค์
โข
Prometheusย โ InfluxDB ๋ก ์ฑํฌ๋ 1๋ถ ์ฃผ๊ธฐ๋ก ๋ง์ง๋ง 2๋ถ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ์
โข
์์ธก ๋ฐ์ดํฐ ์์ฑ์ 30์ผ์ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ฌ ์๊ฐ + 3์๊ฐ ์์ธก ๋ฐ์ดํฐ๋ฅผ ์๊ฐ๋จ์๋ก 10๋ถ์ ์์ํ์ฌ 3์ธํธ(๊ณตํต, ๋งค์ฅ, ๊ณตํต)์ ์์ธก ๋ฐ์ดํฐ๋ฅผ ์ฃผ์
(๋ฐ์คํฌํ์์ ์คํ์ 28๋ถ์ ๋ ์์ ๋จ)
* * * * * /home/phil/Documents/prophet/redisSync.sh >> /home/phil/Documents/prophet/log_redisSync.log 2>&1
30 * * * * /home/phil/Documents/prophet/forecastRedisKeyCount.sh >> /home/phil/Documents/prophet/log_redisForecast.log 2>&1
Bash
๋ณต์ฌ
Data ์ ์ฉ ์์ ๋ฒ์
โข
Key Count: ์๋ฃ
โข
Client
์๋ฆผ ์ค์ ์์ฉ ์์ ๋ฒ์
โข
์ํ์ ์์ธก ๊ฐ ๋ฒ์ด๋๋ ๊ฒ์ ๋ํด ์๋ฆผ (๊ฒฝ๊ณ )
โข
์ธํธ๋ก ๊ตฌ์ฑ๋ ์๋น์ค์ ๋ํด์๋ ๋ ๊ทธ๋ํ์ ์ฐจ์ด๊ฐ ํน์ ๊ฐ์ด์ ๋ฒ์ด์ง๋ ๊ฒฝ์ฐ ์๋ฆผ (๊ฒฝ๊ณ )
โฆ
์) ๊ณตํต 1 vs ๊ณตํต 2 ๊ฐ์ด ๊ฐ์๊ธฐ ๋ง์ด ์ฐจ์ด๋๋ ๊ฒฝ์ฐ (์์ธก๋ฒ์์์ ๋ฒ์ด๋๋ ๊ฒ์ผ๋ก๋ ์ปค๋ฒ๊ฐ ๋ ๋ฏ)