서버를 만들고, 여러 테스트하는 것 중에 영구 접속이 끊어지는 상황을 재현하는 것은 쉽지 않을 수 있다. 디버거를 이용하여 간단히 종료시켜 보는 법을 알아 본다.

python으로 돌고 있는 데몬(pid 11688)이 있다고 하고, 데몬의 DB 접속은 영구 접속에 해당하는데, 이 접속을 강제로 종료시키는 것을 가정한다.

$ lsof -n -p 11688

COMMAND   PID   USER   FD   TYPE   DEVICE  SIZE/OFF      NODE NAME
python  11688 pynoos  cwd    DIR    202,3        80   1636949 /misc/django_projects/app/broadcast
python  11688 pynoos  rtd    DIR    202,3      4096       128 /
python  11688 pynoos  txt    REG    202,3      7216     53035 /usr/bin/python
python  11688 pynoos  mem    REG    202,3     12408 134524267 /usr/lib64/python/lib-dynload/grpmodule.so
python  11688 pynoos  mem    REG    202,3     13520 134538649 /usr/lib64/python/lib-dynload/_bisectmodule.so
python  11688 pynoos  mem    REG    202,3     48240 134524376 /usr/lib64/python/lib-dynload/arraymodule.so
python  11688 pynoos  mem    REG    202,3    694224 134524317 /usr/lib64/python/lib-dynload/unicodedata.so
python  11688 pynoos  mem    REG    202,3     24176 134524160 /usr/lib64/python/lib-dynload/zlibmodule.so
python  11688 pynoos  mem    REG    202,3     14496  69180222 /usr/lib64/libutil-2.17.so
...
... (생략)
...
python  11688 pynoos  mem    REG    202,3     19288  69048848 /usr/lib64/libdl-2.17.so
python  11688 pynoos  mem    REG    202,3    142232  69048881 /usr/lib64/libpthread-2.17.so
python  11688 pynoos  mem    REG    202,3   1847496  67226793 /usr/lib64/libpython.so.1.0
python  11688 pynoos  mem    REG    202,3    163400  69048840 /usr/lib64/ld-2.17.so
python  11688 pynoos    0r   CHR      1,3       0t0      1028 /dev/null
python  11688 pynoos    1u   CHR    136,4       0t0         7 /dev/pts/4
python  11688 pynoos    2u   CHR    136,4       0t0         7 /dev/pts/4
python  11688 pynoos    3w   REG    202,3   3009102 136203946 /misc/log/projects/app.log
python  11688 pynoos    4r   CHR      1,9       0t0      1033 /dev/urandom
python  11688 pynoos    5w   REG    202,3 266251952 135014754 /misc/log/projects/app.sql
python  11688 pynoos    6u  IPv4 26138940       0t0       UDP *:9413
python  11688 pynoos    7u  IPv4 26137372       0t0       TCP 10.1.1.1:33148->10.1.1.2:mysql (ESTABLISHED)

먼저 열려 있는 파일들의 리스트(list of open files; lsof)를 확인한다. 위의 예에서는 7u로 되어 있는 값이 mysql 커넥션 디스크립터다. 이 디스크립터를 종료하면 mysql과의 접속이 종료되는 것이다.

$ gdb /usr/bin/python  11688
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-115.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/bin/python...Reading symbols from /usr/bin/python...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Attaching to program: /usr/bin/python, process  11688
Reading symbols from /lib64/libpython.so.1.0...Reading symbols from /lib64/libpython.so.1.0...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libpython.so.1.0
Reading symbols from /lib64/libpthread.so.0...(no debugging symbols found)...done.
[New LWP 12995]
[New LWP 12994]
[New LWP 12992]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Loaded symbols for /lib64/libpthread.so.0
Reading symbols from /lib64/libdl.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libdl.so.2
...
... (생략)
...
Loaded symbols for /usr/lib64/python/lib-dynload/arraymodule.so
Reading symbols from /usr/lib64/python/lib-dynload/_bisectmodule.so...Reading symbols from /usr/lib64/python/lib-dynload/_bisectmodule.so...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /usr/lib64/python/lib-dynload/_bisectmodule.so
Reading symbols from /usr/lib64/python/lib-dynload/grpmodule.so...Reading symbols from /usr/lib64/python/lib-dynload/grpmodule.so...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /usr/lib64/python/lib-dynload/grpmodule.so
0x00007f0de576b953 in select () from /lib64/libc.so.6
(gdb) call close(7)
$1 = 0

gdb는 디버깅 중인 프로세스의 입장으로 간단한 시스템 콜을 호출 할 수 있는데, 마지막 명령처럼 "call close(7)" 을 호출하면, 디스크립터를 종료하게 된다. 그리고, ^D를 눌러 gdb를 종료하면 프로세스는 실행을 재개하며, 영문도 모르는 채 DB 접속이 끊어지는 상황을 만나게 된다.

Happy Debugging.

django로 만든 웹 서버가 메모리 릭이 있는 것 같아서, 임시로 클라이언트 요청 수에 제한을 걸고 새로 실행되도록 설정했다. 임시가 길어져 1년이 돼가는 동안 잊고 있다가 개발자의 자존심을 건드는 설정인지라 다시 봐야했다.

이 글은 메모리 릭을 잡았다는 것이 아니다.

서버들 중 적당히 스왑을 사용하고 있는 녀석을 골라서 들어갔다. gunicorn 들 중에 제일 pid가 큰 것을 골라낸 후, 해당 PID 에 strace 를 걸어서 GET/POST와 URL PATH에 해당하는 것만 출력하도록 awk 파이프 질을 했다. 동시에 "ps -efl" 로 해당 PID만 골라낸 다음 awk로 적당히 메모리 사용량 부분만 출력한다.

대략 512 개의 요청만 하면 사라지는 gunicorn의 PID이므로, 위 두 작업을 백그라운드로 실행시켜 놓고 fork되어 나간 bash 스크립트의 PID를 저장해 놓는다. 그리고 예의 주시 중인 PID를 1초에 한 번씩 kill -0로 죽었는지를 확인한다. 죽었다면 fork된 녀석들을 죽이고 종료하면 된다.

매우 민첩하게 위 작업들을 생각의 순서대로 뱉어 낸다. 물론 처음에는 바로 쉘상에서 작업하지만, 조금 커질 것을 알고 a.sh 정도로 vim 스크립트로 편집을 하고 실행시키도록 한다.

메모리가 증가되는 순간의 URL을 몇 개 수집한다. 수 분을 기다린다. 화면은 그저 URL과 메모리 사용량만 계속 올라간다.

아뿔싸, 접속한 서버가 오토스케일 정책에 따라 반납되면서 접속이 끊긴다. 뭐야 이거, a.sh 다시작성하기 귀찮은데... 아, 1년만에 들어온 생각인데...

터미널 버퍼를 위로 올려 봐도 vim으로 작업한 것은 남아 있지 않다. 그저 화면에 스크립트의 결과물만 장황하게 넘어간다.

당황하지 말고, terminal에는 vim, less 등이 사용 될 땐 alternative screen으로 작성된다는 것을 알고 있다. ansi terminal이 제공하는 매우 오래된 기능이다.

"ansi show alternative screen" 로 검색을 한다. 

ESC [ Ps ;...; Ps h             Set Mode
ESC [ Ps ;...; Ps l             Reset Mode
      Ps = 4            (A)     Insert Mode
           20           (A)     ‘Automatic Linefeed’ Mode.
           34                   Normal Cursor Visibility
           ?1           (V)     Application Cursor Keys
           ?3           (V)     Change Terminal Width to 132 columns
           ?5           (V)     Reverse Video
           ?6           (V)     ‘Origin’ Mode
           ?7           (V)     ‘Wrap’ Mode
           ?9                   X10 mouse tracking
           ?25          (V)     Visible Cursor
           ?47                  Alternate Screen (old xterm code)
           ?1000        (V)     VT200 mouse tracking
           ?1047                Alternate Screen (new xterm code)
           ?1049                Alternate Screen (new xterm code)

Set은 h로 끝나며, Ps로는 "?47"이면 되겠다. 내 로컬 console은 zsh이니까 몇가지 escape 처리해서 echo 해주면 되겠다. 될까?

echo -e \\e\[\?47h

으허허 보인다. 반납된 서버에 잠시 삽질했던 코드를 다시 치고 싶지 않은 그 임시 코드가 보인다.

#!/bin/bash

P=$(ps -ef | grep guni | grep python2 | sort -k2 -n | awk '($3 != 1) {print $2}' | head -1)
strace -tt -s 100 -p $P 2>&1 | stdbuf -o0 egrep "recvfrom\(.*(GET|POST)" | stdbuf -o0 awk '{print substr($3,2), $4}' &
PID1=$!
while true; do ps -efl | grep gunicorn | grep -w $P | awk '{print $10}'; sleep .5; done &
PID2=$!

trap "kill $PID1 $PID2; exit" SIGINT
while true
do
        if kill -0 $P; then
                sleep 1
                continue
        fi
        kill $PID1 $PID2
        exit
done

귀찮다... 이 시간에 이거 할 게 아닌데.

늪, 사람은 뭔가에 빠지기 마련이다. 하다 못해 게으름에 빠지기도 한다. 그 순간조차 그 상태를 유지하고 싶다. 너무 오래 머물면 안될 것을 알면서도 계속 유지하다가 적응하게 된다.

늪을 나오기 위해, 용기란 것이 가끔 필요한 것임에도, 그 생각이 드는 순간조차 그 속에서 뭔가 더 할 수 있을 것 같은 생각이 동시에 든다. 이것은 늪 속에서 주기적으로 엄습하는 목소리와의 결말 없는 전쟁같은 것이다.

늪, 이 점성 고형물이 꿀인지 진흙인지는 상관이 없다. 결국엔 그것외엔 할 수 없는 것이 그 안에서 사는 모습이다.

새로운 것을 추구하자, 죽기 전에 꼭 해야할 일을 시작하자, 돈을 많이 벌어야지 하고 싶은 것만 할 수 없지 않느냐. 이런 고민은 30대에 끝날 줄 알았지만, 지금보니 늙어서 죽는다고 해도 계속 될 것 같다.

발버둥치면 더 깊은 곳으로 들어간다. 차라리 아직 목 윗부분이 공기를 마시고 있을 때, 가만히 기다려 보는 것이 좋을지도 모르겠다. 혹시 아나 나를 잡아 당길 사람이 나타날지.

상민과 나의 밥집 평가에 대한 CORRELATION 이 -0.8527이 나온다. -1에 가까우므로 반대 방향의 데이터가 예상된다, 즉 취향이 반대라는 얘기인데, 점수 메긴 숫자로 보면 취향이 반대 같아 보이진 않는다. 내가 평가안한 것(빈칸)에 대해서는 CORREL 함수가 제외하고 계산하는 것은 분명하다. 그리고 내가 평가한 것이 6개 이므로 자유도 5로 계산했을 때 수작업으로 계산해도 같은 값이 나온다.(당연하지 컴퓨터인데)

뭔가, 상관계수로 취향을 분석하는 것은 저렇게만하면 안될것 같다. 당연하지..모두 식당평가를 같은 4점, 3.9점을 주었고 나머지 둘이 달랐으니, 이 들을 관통하는그래프가 예상 밖이 되는데.. 아.. 이런 멍청한.

Network Card의 PPS를 측정하는 아주 간단한 스크립트

linux, NIC eth0 의 PPS를 1초마다 측정한다고 가정하면,

#!/bin/bash

while true
do
    echo $(cat /sys/class/net/eth0/statistics/tx_packets) $(cat /sys/class/net/eth0/statistics/rx_packets)
    sleep 1
done | awk '{DT=$1-TPREV; DR=$2-RPREV; if(TPREV) print strftime("%Y-%m-%d %H:%M:%S", systime()), "TX:", DT, "RX:", DR; TPREV=$1; RPREV=$2; }'

 

HHI 지수를 조금 체감하기 위해 숫자놀이를 해보자면,

일단, HHI 란 퍼센트로 표현한 점유율의 제곱 합 이다.

  • 1개의 회사가 전 시장을 다 장악하는 경우 100% 이므로, 제곱합은 그저 100의 제곱인 100000 이다. 이값이 HHI의 최대값을 나타낸다.
  • 2개의 회사가 시장을 반씩 양분하고 있는경우 50%의 제곱합이므로 2500 * 2 = 5000
  • 5개의 회사가 시장을 5등분하고 있는 경우 20%의 제곱합이므로 400 * 5 = 2000
  • 10개의 회사가 시장을 10등분하고 있는 경우 10%의 제곱합이므로 100 * 10 = 1000

어떤 산업이 1000~2000 정도의 HHI 지수를 가지고 있다는 것의 예는 이렇다.

어떤 회사의 최대 점유율이 20% 인 산업군을 하나 만든다면,

  • 20%, 20%, 20%, 10%, 10%, 10%, 10% = 400 + 400 + 400 + 100 + 100 + 100 + 100 = 1600

제 1 점유율이 30%인 산업군의 예는,

  • 30 10 10 10 10 10 10 10 = 900 + 100 * 7 = 1600

제 1 점유율이 40%인 산업군의 예는,

  • 40 5 5 5 5 5 5 5 5 5 5 5 5 = 1600 * 25 * 12 = 1600 + 300 = 1900

훔... 마지막의 경우 두 회사가 합병해서 10인 회사가 생긴다면

  • 40 10 5 5 5 5 5 5 5 5 5 5 = 1600 + 100 + 25 * 10 = 1700 + 250 = 1950

HHI 지수를 보건데, 시장을 독점한다면 그 값의 최대값은 10000 이다. 두 회사가 시장을 양분하고 있으면, 대략 5000 언저리, 2~5개의 회사가 치고 박고 싸우고 있으면 2000 ~ 5000, 5개 이상의 회사가 매우 시끄러우면 2000 이하.

잘 놀았습니다..

GS(링크)를 끌어다가 즐겨찾기에 떨어 뜨려 저장 후, 구글 검색 결과 창에서 누르면 주소를 정리해주는 북마클릿이다. 해당 내용은 아래와 같다. 만약 링크가 나오지 않는다면, 아무 북마크나 만들고 아래 내용으로 주소를 복사/붙여넣기를 하여 만든다.

javascript:location.href=location.href.split('?').map(x=>x.split('&').filter(a=>a.startsWith('http')||(['q','hl','tbm','start'].indexOf(a.split('=')[0])!=-1))).map(b=>b.join('&')).filter( a => a).join('?')

구글 검색을 한 다음 나오는 페이지를 공유하고 싶으나, 그 URL에는 불필요한 정보가 실려 있다. 예를 들어 "real user monitoring"이라는 키워드를 검색한다고하면,

https://www.google.com/search?q=real+user+monitoring&oq=real+user+monitoring&aqs=chrome..9i57j69i64j69i60.475j0j9&sourceid=chrome&ie=UTF-8

real user monitor가 q에 있으며 그 외에 다른 내용이 추가 되어 있다. oq 는 old query 인 것 같고, aqs, sourceid, ie 등등이 있지만, 내가 넣은 키워드는 아니다.

https://www.google.com/search?q=real+user+monitoring

위 정도면 충분하지 않은가?

https://www.google.com/search?q=real+user+monitoring&newwindow=1&sxsrf=LeKk03jPSDAZORglcmjxCXz04IBrb_OLA:1601697916568&ei=Ph3X7KYIs77wQOb2ZeQBg&start=10&sa=N&ved=ahUKEwiyxuraxZfsAhXOfXAKHZvsBWIQ8tMDegQIHRA2&biw=1664&bih=856

이것은 두번째 페이지를 선택했을때 나오는 URL이다. 여기에서는 의미 있는 인자로는 start=10으로 보인다. 또한 이미지 검색탭도 중요한 공유지점이므로 한 번 더 확인해 보면,

https://www.google.com/search?q=real+user+monitoring&newwindow=1&hl=ko&sxsrf=LeKk03rjnn29_rlZpdwCneoGI-E2l1abA:1601698337343&source=lnms&tbm=isch&sa=X&ved=ahUKEwic0ryjx5fsAhUQBogKHausBGsQ_AUoAXoECB0QAw&biw=1664&bih=856

tbm=isch 가 그 역할을 하는 것으로 보여진다. 따라서, 위 URL을 정리하는 간단한 스크립트를 만들수 있고, 북마크에서 해당 자바스크립트가 실행되는 URL로 만들어 두면 유용할 것이다. 단계는 다음과 같다.

  1. "?"로 나눈다.
  2. 나눈 각각을 "&"로 나눈다.
  3. 나눈 각각이 http로 시작하면 남긴다.
  4. 나눈 각각을 '='으로 나눈 첫번째 단어가 'q', 'hl', 'tbm', 'start' 이면 남긴다.
  5. 남은 각각을 역순으로 합친다.
location.href.split('?').map(
    x => x.split('&').filter(
        a => a.startsWith('http') || ( ['q','hl','tbm','start'].indexOf( a.split('=')[0] ) !=-1 )
    )
).map( b => b.join('&')).filter( a => a ).join('?')

이렇게 정리되는 주소로 재설정하는 것으로 마무리하여 북마클릿을 만든다.

지극히 사소한 개인 로그라서, 읽지 마시기를 권함.

집안 청소를 대략 두 달에 걸쳐 했다. 지난 8월 15일 전후로 아내의 휴가에 맞춰서, 여행 갈 일도 없는 코로나 시국에서 집 방 바꾸기나 해보자고 시작한 것이, 얼추 끝나는 것은 한 달이 걸렸으며, 그 한 달 이후로 꾸준히 뭔가를 바꾸고 있다.

  • 첫째가 쓰던 방을 내가 쓰고, 둘째가 쓰던 방을 아내가 쓰고, 큰 방은 둘째에게 줬다. 그리고 학교앞에서 자취하는 첫째는 가끔 오면 적당히 잔다.
  • 10수 년 전 동네 도서 대여점이 문을 닫을 때 우연히 지나가다가 책장을 헐값에 수거해 왔고(17개), 집안 곳곳에 배치를 해뒀으나, 집정리가 끝난 지금 7개를 대형폐기물 업체에 넘겼다. 수거해 온 이후 몇 년 전 2개는 버렸기 때문에, 집에는 8개가 남아 있다.

첫째의 방

  • 이제는 내 방
  • 첫째의 방에 딸려 있던 부속방엔 피아노가 있었고, 각종 안쓰던 옛날 물건들이 그곳에 있었던 죽은 공간이었다. 피아노를 조금 밀고, 잡동사니를 드러내고 80cm 넓이의 작은 책상을 하나 사서 그곳에 내 공간을 만들었다.
  • 피아노 방으로가는 길을 만들기 위해 싱글침대를 길게 써야 했으며, 그러기 위해선 큰 책상을 분해하여 접어 두었다.
  • 싱글침대를 길게 쓰면서 생긴 한쪽 벽은 거실에 있던 다섯 개의 도서 대여점 책장을 이동시켜 배치했고, 거실의 벽면이 드디어 벽지를 자연스럽게 드러내고 있다. (거실 한쪽 벽이 그간 너무 가려 있었다)
  • 피아노 방에 책상을 조그맣게 들여 자리를 잡아 안정감은 있으나, 뒤에서 누가 오는느낌이 쎄해서(왜?) 거울을 하나 배치함.
  • 동네 꼬마녀석들이 노는 소리가 너~무 시끄럽다. (얼른 자라서 조용하게 놀아라.)

둘째의 방

  • 이제는 아내의 방
  • 이 방은 공부하는 2층침대를 두어 침대와 책상이 한 공간을 쓸 수 있게 되어 있었다.
  • 머리에 뭔가가 있는 느낌이 싫다해서 책상공간에는 시스템 책장들을 다 몰아 넣고, 조금 떨어져 나온 곳에 조그만 사각형 책상을 방 가운데 두어 사용한다.
  • 도서대여점 책장 두 개를 넣어 큰 방에 있던 책들과, 거실에 있던 문학전집 (200권)을 가지고 들어갔고, 원래 아늑한 곳이 더 아늑해졌다.
  • 시스템 책장은 책장이라기보다는 사물함에 가깝다.

큰 방

  • 이제는 둘째의 방
  • 2in1 에어콘 하나가 들어 있는 방인지라, 원격 수업이 잦은 둘째가 집에 있을 때 사용하라고 둘째 공간을 만들었다.
  • 첫째의 옷과 간단한 책장도 여기에 배치하여 가끔 왔을 때 사용할 수 있게 하려는게 목적인 방이 됐다.

주방

  • 집이 오래되다보니, 렌지후드가 말썽이다. 스위치 박스가 잘 안눌렸는데, 수리신청을 하여 수리완료.
  • 렌지후드 철망이 둘인데, 그 중 하나 교체 했고 불을 켜면 나머지 것과 너무 비교 된다. 그 동안 수시로 세척해 왔는데도 관리가 너무 어렵다.
  • 전자레인지, 에어 프라이어 위치 변경하여 조리 공간 확보.

그 밖

  • 현관 센서등이 고장나서 새로 주문하여 교체
  • 화장실 앞에 센서등 하나 (충전식) 설치했다가 불량이라 교환신청.
  • 거실 중앙 형광등 갈아 끼움
  • 베란다 창고 털어 안쓰는 블라인드 버림
  • 워킹 트레드밀 버림

아직 남은 것이라면, 내 방 옷장정리... 등등등.

모든 게 멈추었다.

급한 불 앞에선 불끄는 것외엔 집중할 여유가 없다. 불끄다 잠시 눈돌려서 할 수 있는 것이라고는 눈에 들어오는 것에 시선을 잠시 고정시켜 따라가는 것일 뿐.

문득문득 나이듦이 머리를 들고, 잃어버린듯한 기회에 대한 아쉬움도 머리를 들고, 할 수 있을 것 같은 자신감은 외면을 한다.

 

지킬 박사와 하이드씨

지킬이 정상이고 하이드가 살인자, 우리 말 어감상 지킬이 강해 보이므로 이름을 한국식으로 바꾼다.

박재권과 하익두씨라고 하면 좀 더 강한 것 같기도 하고...

+ Recent posts