반응형

1.이슈 내용

- 파이썬 클래스의 멤버 변수 초기화시 특정 컬럼이 tuple 값으로 초기화되던 문제.

- tuple 로 초기화 된 값 때문에, external 테이블이 미리 정의한 컬럼 타입과 불일치 하여 조회에 이슈가 발생했었다.

2.이슈 원인

-내 손의 잘못.

-콤마. 파이썬은 원소 뒤에 콤마가 있으면 단일 튜플로 인식한다.

 

- 예제 재연

import json

ods_json_data ='''
{
    "log_seq" : "00001",
    "event_time" : 1656771413
}
'''

class ODSLog:
    def __init__(self,
                 log_Seq = None, 
                 event_time = None,
                 field_1 = None,
                 field_2 = None,
                 **kwargs):
        self.log_seq=log_Seq
        self.event_time=event_time
        self.field_1=field_1,
        self.field_2=field_2
        
def get_ods_transform_log():
    ods_data = json.loads(ods_json_data)
    ods_log = ODSLog(ods_data)
    print(ods_log.__dict__)
   
get_ods_transform_log()
# 출력 값 - 
{'log_seq': {'log_seq': '00001', 'event_time': 1656771413}, 'event_time': None, 'field_1': None, 'field_2': (None,)}

- ods_json_data 와 같은 방식으로 데이터가 들어오면, 클래스를 이용하여 필요한 ETL 처리후에 데이터를 출력 처리

- 실시간 데이터 수집의 경우 이상값이나 정의하지 않은 컬럼이 들어올 때를 예측해서 처리 로직을 추가해주면 좋다.

 

3.회고

콤마가 포함된 라인 하나를 발견하지 못해, 원천 데이터를 탓하며 일부러 타입 캐스팅을 하드코딩할 생각까지 했던 스스로를 반성한다. 

컬럼이 엄청 많은 로그를 처리할때 sublime text 나 엑셀을 이용해 프로그램 코드의 일부분을 일괄로 처리할 때가 있다. 그때 일부는 하나하나 찾아가면서 보정을 하게 되는데 그 과정에서 실수가 많이 발생하는 것 같다.

 

하반기에는 파이썬 공부도 해야 할 것 같다....

반응형
반응형

1. 문제 소개

  • 그리디, 정렬 문제에 해당한다.

 

2. 코드

import sys
input = sys.stdin.readline


# Ti = 처리시간
# Si = 마감시각

# Idea) 끝의 마감시각부터 처리시간을 빼가면서, 일 시작시각을 계산한다.

# 1) 마감시각 기준 정렬 
# 2) 일 업무(테스트 케이스)별로, 일 시작 시각 계산.

# 	prev_Si = 이전 업무 시작 시각 기준으로 처리하되 현재 마감 시각보다 크면 값 갱신: 마감시각 - 처리시각 

# 	IF prev_Si > Si:
# 		prev_Si = Si
# 	prev_Si -= Si 

def set_test_case():
    N =  int(input())
    N_CASE=[]
    
    for i in range(0,N):
        case = input().split(" ")
        set_case = (int(case[0]),int(case[1]))
        N_CASE.append(set_case)
    N_CASE.sort(key = lambda x : x[1],reverse=True)
    return N_CASE

def solution():
    work_array = set_test_case()
    prev_Si = work_array[0][1]
    for Si,Ti in work_array:
        if prev_Si > Ti :
            prev_Si = Ti
        prev_Si = prev_Si - Si

    if prev_Si < 0:
        prev_Si = -1

    print(prev_Si)
    
solution()

3. 코멘트

  • 끝에서 부터 업무 시작 시각을 계산하면, 답을 더 편하게 구할수 있다는 아이디어를 찬의님(스터디원) 리뷰를 보고 다시 풀었다.
  • 오늘도 하나 배웠다.
반응형
반응형

1. 문제 소개

  • 수학 유형 문제에 해당한다.

 

2. 코드 

import sys
input = sys.stdin.readline

# 1) 빨대 길이가 저장된 배열을 정렬
# 2) 가장 긴 쪽 부터 3개의 빨대로 삼각형 성립 조건 을 검사
# 3) IF 성립조건  = True  -> 삼각형 변의 합 계산 후 출력
# 4) IF 성립조건 = False -> 다음 크기의 빨대로 이동


def set_test_case():
    N = int(input())
    n_side = []
    for i in range(0,N):
        n_side += [int(input())]

    return n_side


def isValidTriangle(triangle):
    t_side = sorted(triangle,reverse=True)
    # 가장 긴 변 = C
    # 삼각형 성립 조건  C < A + B
    if t_side[0] < (t_side[1] + t_side[2]):
        return True
    else:
        return False
    
def solution():
    n_side = set_test_case()
    n_side = sorted(n_side)
    maximum_sum_triangle = -1
    for i in range(len(n_side)-1,1,-1):
        if isValidTriangle(n_side[ (i - 2) : (i + 1 )]):
            maximum_sum_triangle = sum(n_side[ (i - 2) : (i + 1 )])
            break
        
    print(maximum_sum_triangle)

solution()

3. 코멘트

  • 파이썬으로 제출 시 런타임에러가 발생한다면,  sys.stdin 패키지의 입력함수를 이용하길 권한다.
  • 오랜만에 유진님, 용범님과 재밌게 문제 풀이했다. 
반응형
반응형

1. 문제 소개

  • 수학 유형 문제에 해당한다.

 

2. 코드


def set_test_case():
    A = []
    B = []
    T = input() 
    for i in range(0,2):
        org = input().split(" ")
        if i == 0 :
            A = [int(x) for x in org]
        else:
            B = [int(x) for x in org]

    return A,B


def solution():
    A,B = set_test_case()
    S = 0 
    A = sorted(A)
    B_I = []  # B 배열의 크기 별 순위 인덱스
    MAP_B = {} # Key : B 인덱스의 값, avlue : B 인덱스의 위치

    for idx,va in enumerate(B):
        MAP_B[va] = idx  # 
        
    for i in sorted(B,reverse=True):
        B_I +=[MAP_B[i]] # B 배열의 각 요소별 크기 순번 저장
    
    for idx,va in enumerate(A):
        S_PART = va * B[B_I[idx]]
        S += S_PART

    print(S)



solution()

3. 코멘트

  • 수학을 이용해서 풀지 않았.. 

 

반응형
반응형

1. 문제 소개

  • 수학 유형 문제에 해당한다.

2. 코드 


def set_test_case():
    T = int(input())
    CASE = []
    for i in range (0,T):
        line = input().split(" ")  # 입력 문자열을 공백구분자로 분리 
        line = [int(x) for x in line]  # 연산을 위해 str -> int 형 변한
        CASE.append(line)  # 케이스들을 새로운 list 에 담음

    return CASE
        
# factorial 을 재귀로 간단히 구현
def factorial(n):
    if n <=1:
        return 1
    return n * factorial(n-1)


def solution():
    case = set_test_case() # 입력 케이스 처리 
    for i in case :
        r,n = i # 강의 서쪽은 r, 동쪽은 n 에 대입
        p = factorial(n-r) * factorial(r)  # 조합의 구한 분모 값 계산
        c = factorial(n)  # 조합의 분자 값 계산
        print(c//p) # 정답 출력 

solution()

 

3. 코멘트

  • 아래 조합 공식을 이용하여 문제를 풀면 쉽다.

  • 너무 오랜만에 포스팅을 하는 것 같다. 요즘 회사 업무량이 많고, 주말에 여유가 없어 취미로 시작한 알고리즘 스터디도 좀처럼 참여가 어렵다. 그래도 꾸준히 해야겠지. +_+..
반응형
반응형

문제 해결이라기 보단, 회고에 가까움.

그라파나 자체 사용자 인증 기능을 사용하거나 sqlite 를 사용한다면 아래 이슈참고.

 

1.이슈 내용

  • grafana 사용자 view 권한 기능을 검증하려다, 모르고 organization 을 삭제 .. 정확하게는 신규 사용자에게 잘못 부여한 organization 을 제거한다는게, organization 을 삭제하는 작업을 진행해버림.
  • 갑자기 생성한 대시보드, 팀 모두 사라짐...  오잉 .. -_-??

2. 이슈 원인

  • grafana 기능 테스트 중 기존 조직(organization) 삭제..
  • 조직이 삭제되면서 연관된 팀과 대시보드가 자동으로 삭제 됨....

나랑 비슷한 실수를 한 사례들 조사

https://community.grafana.com/t/is-there-a-way-to-restore-an-accidentally-deleted-dashboard/23839

https://community.grafana.com/t/deleted-organization-how-to-recover/7189

 

 

3. 해결방법

 

결론은.. 주기적으로 백업하지 않으면 복원이 어려움. 

sqlite 에 진입해서 organization 레코드만 수동으로 만들어주면 되지 싶었으나, 연계된 데이터들 모두 삭제되서 행이 보이지 않았음..

 

 

 

일단 sqlite db 복제 스크립트를 등록 ...

## crontab 등록
0 0 * * * /usr/local/backup_script/backup_grafana_sqlitedb.sh
 
 
 
# 복제 스크립트
#!/bin/bash
GRAFANA_DATA=/usr/local/grafana/data
command cp ${GRAFANA_DATA}/grafana.db ${GRAFANA_DATA}/grafana_backup.db

 

 

데이터가 사라진건 아니지만, 너무 미안하게도 동료들이 작성한 대시보드, 데이터 소스들이 사라졌다. 

grafana 가 사용하는 meta db 의 백업은 필수로 하길 권장한다.

 

꼭..

반응형
반응형

1. 프로메테우스(prometheus) 란?

SoundCloud 사에서 만든 오픈 소스 기반 모니터링 솔루션

  • 많은 도큐먼트와 커뮤니티 보유

go 언어로 만들어짐

하드웨어 레벨 / 애플리케이션 모니터링이 가능

  • 마이크로 서비스
  • multiple language 지원(java, go, 등)
  • window/linux

pull 방식 아키텍쳐

  • push 방식은 패킷 손실. 수집처 서버의 CPU 사용율. 그리고 단일장애의 위험성이 있음
  • pull 방식은 tcp/udp 오버헤드가 적고, 집계가 push 보다 쉬워짐.
  • 부하가 높은 상황에서는 fail point 를 비교적 예방 할 수 있음

하지만 단점도? 

  • 수집 메트릭과 룰이 많아질수록 configuration 이 복잡해짐
  • 단일 노드 동작으로 스케일 아웃이 어려움

2. 왜 프로메테우스?

다양한 써드파티 Exporter 가 있다.

시계열 데이터 모델

PromQL 이라는 쉽고 간결한 자체 쿼리 언어 지원

단일 노드 사용하여 수집

많은 회사에서 사용하고 있고, kubernetes 에서도 prometheus 사용을 장려하고 있다.

3.프로메테우스 구조

시스템 구성도

prometheus server

  • main component
    • http 서버 : PromQL 쿼리를 받음
    • 시계열 데이터 베이스 storage : metric 저장
    • Worker : metric 반환

Exporter

  • fetch metric, convert to correct format, expose metric
  • 다양한 종류의 익스포터 지원
    • 해당 노드에서 발생하는 HW 메트릭 정보 수집

AlertManager

  • 알림이 발생하는 Rule 을 정의해서 부합하는 경우 알림 전송

Jobs

  • Single Application
  • 모니터 대상

** 역시 Pull Mechanism 이 가장 큰 특징 !!!

  • Data Retrieval Worker 가 pushgateway, prometheus targets으로 pull을 한다. 푸시가 아니라 pull 방식으로 수집한다.

동작 방식을 간단히 요약하면..

  • Jobs / Exporters 는 실제 메트릭을 수집하는 프로세스, 에이전트
  • Prometheus Server 가 Pull 방식으로 데이터를 가져갈 수 있게 HTTP 엔드포인트를 제공
  • GET 방식 요청을 날려 메트릭 정보를 수집하고, TSDB 에 저장.

4. 프로메테우스 설치 (docker-compose)

how? (구성 방법)

  • 아래 방법은 prod 수준이 아닌 빠른 구성이며, 모든 설정은 기본 값으로 되있다.
  • docker, docker-compose 가 미리 설치되어 있어야 한다.

a) 프로메테우스 구성

  • 가장 기본적인 구성은 프로메테우스 서버, 그라파나 조합이다.
  • 아래는 수집툴 node-exporter도 같이 띄워주는 설정이다
version: '3.7'

volumes:
    prometheus_data: {}
    grafana_data: {}

networks:
  front-tier:
  back-tier:
    driver: bridge

services:

  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus/:/etc/prometheus/
      - ./prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'
    ports:
      - 9090:9090
    links:
      - cadvisor:cadvisor
      - alertmanager:alertmanager
    depends_on:
      - cadvisor

    networks:
      - back-tier
      - front-tier
    restart: always

  node-exporter:
    image: prom/node-exporter
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command: 
      - '--path.procfs=/host/proc' 
      - '--path.sysfs=/host/sys'
      - --collector.filesystem.ignored-mount-points
      - "^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/)"
    ports:
      - 9100:9100
    networks:
      - back-tier
    restart: always
    deploy:
      mode: global

  alertmanager:
    image: prom/alertmanager
    ports:
      - 9093:9093
    volumes:
      - ./alertmanager/:/etc/alertmanager/
    networks:
      - back-tier
    restart: always
    command:
      - '--config.file=/etc/alertmanager/config.yml'
      - '--storage.path=/alertmanager'
      
    grafana:
    image: grafana/grafana
    user: "472"
    depends_on:
      - prometheus
    ports:
      - 3000:3000
    volumes:
      - ./grafana_data:/var/lib/grafana
      - ./grafana/provisioning/:/etc/grafana/provisioning/
    env_file:
      - ./grafana/config.monitoring
    networks:
      - back-tier
      - front-tier
    restart: always

b) process-exporter

  • 일반적으로 node-exporter 로 h/w 정보를 수집하긴하지만, 프로세스 수준으로 데이터를 수집이 필요할때 사용하면 좋다.
  • https://github.com/ncabatoff/process-exporter/releases/tag/v0.7.10
  • release 버전에 맞게 받을 수 있음
  • cofnig 설정방법
    • cat /proc/[processid]/cmline 의 정규식에 걸리는 애들
    process_names:
      - name: "{{.Comm}}"
        cmdline:
        - '.+'
  • 위 process-exporter 구성은 선택사항이다. 필요에 따라 구성해도 좋다. 

c) prometheus.yml 설정

  • 수집 서버 설정과, 방식, 룰 들을 셋팅하는 설정 파일이다
# 기본적인 전역 설정 
global:
  scrape_interval:     15s # 15초마다 매트릭을 수집한다. 기본은 1분이다.
  evaluation_interval: 15s # 15초마다 매트릭을 수집한다. 기본은 1분이다.
  # 'scrpae_timeout' 이라는 설정은 기본적으로 10초로 세팅되어 있다.
# Alertmanager 설정
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      # - alertmanager:9093

# 매트릭을 수집할 엔드포인트를 설정. 여기서는 Prometheus 서버 자신을 가리키는 설정을 했다.
scrape_configs:
  # 이 설정에서 수집한 타임시리즈에 'job=<job_name>'으로 잡의 이름을 설정한다.
  - job_name: 'prometheus'
    # 'metrics_path'라는 설정의 기본 값은 '/metrics'이고
    # 'scheme'라는 설정의 기본 값은 'http'이다.
    static_configs:
    - targets: ['localhost:9090']
    
 - job_name: 'node_exporter'
    static_configs:
      - targets: ['localhost:9100']
  - job_name: 'process_exporter'
    static_configs:
      - targets: ['localhost:9256']
  • prometheus 설정파일과 볼륨 마운팅 된 위치에 위 설정파일을 추가한다
  • 위 주석을 간단하게 달았지만, 수집 서버에 대한 정보가 scrape_configs 에 모두 기술되어야 한다
  • 모든 구성이 끝나면 docker-compsoe 명령을 이용해 컨테이너를 띄운다.

(구성된 이미지나 스크린샷들은.. 나중에 추가..)

 

 

5) 소감..?

  • 이번에 회사에서 프로메테우스 신규 구성을 맡아 진행해봤는데, 역시 공부할게 아직도 많다. 
  • 그라파나에서 완성된 대시보드를 import 하면 수집쪽 셋팅 후 정말 손쉽게 구성할 수 있다.
  • 데이터 엔지니어에게 있어 모니터링은 정말 중요한 작업이라는걸 다시 느꼈다. 

 

반응형
반응형

1.문제 소개

  • 구현, 시뮬레이션 유형 문제에 해당한다.

https://www.acmicpc.net/problem/8911

2. 코드

import sys
input = sys.stdin.readline

def set_test_case():
    command_line = int(input())
    commands=[]
    for i in range(0,command_line):
        commands+=[input()]

    return commands

def move_turtle(location,direction,move):
    # location[0] -> X 축 location[1] -> Y 축
    if direction ==3 or direction ==2:
        move = move * -1
        
    if direction == 0 or direction ==2:
        location[1] = location[1] + move
    elif direction == 1 or direction == 3:
        location[0] = location[0] + move
    else:
        pass

    return location

def move_direction(current, direction):
    # 0 : up , 1 : right , 2 : down , 3: left
    if direction == 'R':
        current = (current+1)%4
    elif direction == 'L':
        current = (current-1)%4
    else:
        pass
    return current

def solution():
    commands = set_test_case()
    
    for command in commands:
        turtle_direction = 0 # default direction -> UP
        turtle_location=[0,0] # default location
        maximum_location=[0,0,0,0] # UP , RIGHT, DOWN, LEFT 
        
        for c in command:

            # set location by command
            if c == 'L' or c=='R':
                turtle_direction = move_direction(turtle_direction,c)
            elif c == 'F':
                turtle_location = move_turtle(turtle_location,turtle_direction,1)
            elif c == 'B':
                turtle_location = move_turtle(turtle_location,turtle_direction,-1)
            
           # save maximum location of turtle  
            if c == 'F' or c == 'B':
                if turtle_location[0] > maximum_location[1]:
                    maximum_location[1]=turtle_location[0]                    
                    
                elif turtle_location[1] > maximum_location[0]:
                    maximum_location[0]=turtle_location[1]
                    
                elif turtle_location[0] < maximum_location[3]:
                    maximum_location[3] = turtle_location[0]
                
                elif turtle_location[1] < maximum_location[2]:
                    maximum_location[2] = turtle_location[1]
            
        # result print
        if (maximum_location[1] == 0 and maximum_location[3] ==0) or (maximum_location[0] == 0 and maximum_location[2] ==0): 
            print(0)
        else:
            print((abs(maximum_location[0])+abs(maximum_location[2])) * (abs(maximum_location[1])+abs(maximum_location[3])))
            

if __name__=='__main__':
    solution()

3. 코멘트

  • IF 문이 많아 좋은 코드로 만들진 못했지만, 요약하자면 move_direction,turtle 로 거북이의 방향과 위치를 명령어기반으로 갱신해주고, 동서남북 방면의 최대값을 저장해 정답을 출력했다.
  • 최근 폼도 떨어지기도 했고, 매트릭스 좌표 계산이 머릿속으로 빨리 빨리 안되서 속상했다.
  • 다른 스터디원의 풀이 방식을 보니, 내 로직이 쓸데없이 더 복잡해보였지만.. 뭐어쩌겠나.. ㅇㅅㅇ.. 공부해야지. 
반응형
반응형

1. 문제 소개

  • 수학 유형 문제에 해당한다.

2. 코드

import sys
input = sys.stdin.readline

def set_test_case():
    F = int(input())    # 다친 손가락 위치
    M = int(input())    # 다친 손가락의 최대 사용 횟수 
    return F,M

def solution():
    F,M = set_test_case()
    result = 0 

    if F==1:
        result = 8*M
    elif F==5:
        result = 8 * M +4
    else:
        if M % 2 == 0 :
            result = 4 * M + (F-1)
        else:
            result = 4 * M + (5-F)
            
    print(result)
            
if __name__=='__main__':
    solution()

 

3.코멘트 

  • 손가락 위치 별 별로 최대사용 횟수 이후 셀 수 있는 숫자를 직접 하나 하나 구해서, 손가락 위치 별 규칙을 구한다.
  • 다친 손가락이 1 / 5 / 2 ~ 4 일 때 아래의 규칙을 발견할 수 있고, 코드로 작성했다.

  • 처음엔 작성한 알고리즘에 구멍이 있었는데, 금일 함께 스터디했던 유진님의 도움으로 코드를 보정할 수 있었다.
  • 역시 공부는 꾸준히 해야 한다...  
반응형
반응형

1. 문제 소개

  • 그리디 유형 문제에 해당한다.

https://www.acmicpc.net/problem/1343

2.코드

# 1. valid_check 
#   - 폴리오미노 유효성 검사 
#   - x 가 짝수개수인지 체크 
# 2. custom_split
#   - X와 . 을 분리 
# 3. convert
#   - AAAA , BB 우선순위로 폴리오미노 문자 조합

def set_test_case():
    T = input()
    return T

def valid_check(s):
    result = True
    for case in s.split('.'):
        case_len = len(case)
        if case_len %2 != 0:
            result = False
            return result

    return result

def custom_split(input_str):
    split_list = []

    last_dot_point=-1
    cursor = 0 

    while (cursor < len(input_str)):

        # 첫번째 문자가 닷(.) 이 아닌 경우 ,  연속적인 닷(.)이 아닌경우 
        if input_str[cursor] == '.':
            if cursor > 0 and not (input_str[cursor-1] == '.'):
                split_list.append(input_str[last_dot_point+1:cursor])
                last_dot_point = cursor
            else:
                last_dot_point = cursor
            split_list.append('.')
        
        # 마지막 커서 도달 시 잔여 문자 처리 
        if cursor == len(input_str)-1 and cursor != last_dot_point:
            split_list.append(input_str[last_dot_point+1:])
            
        cursor +=1
    return split_list

def convert(split_list):
    result ='' 
    for case in split_list:
        if case[0] =='X':
            cursor = 0 
            while(cursor < len(case)):
                if len(case[cursor:]) >= 4:
                    result +='AAAA'
                    cursor +=4
                else:
                    result +='BB'
                    cursor+=2
        else:
            result+=case
    
    return result

def solution():
    #s = '...'
   # s = '..XXXX..XX.'
#    s= 'XX.XXXXXXXXXX..XXXXXXXX...XXXXXX'
    s = set_test_case()
    split_str = custom_split(s)
    if not valid_check(s):
        result = '-1'
    else:
        result = convert(split_str)
    print(result)

if __name__=='__main__':
    solution()

3. 코멘트

  • custom_split() 에서는 입력 문자열의 문자를 하나 하나 순회하면서, 닷(.) 과 X를 분리하고, convert() 에서는 사전 순 ordering을 위해 AAAA, BB 순서로 문자를 치환하는 방식이다.
  • 최근에 이직한 새 회사에 적응하느라 스터디를 많이 못하기도 했고..  뇌가 굳은건지.. 간단한 로직이라 생각했는데 구현이 빨리 빨리 안되서 속상하다.
  • 스터디 새 멤버도 충원됐고, 앞으로 꾸준히 공부해야겠다. :D 

 

반응형

+ Recent posts