MyBatis와 싸우면서 얻은 것들

Java로 간단한 console application을 만들다가 이상한 문제들을 마주하게 되었다.  그 중 하나는 DB 연결이 계속 끊어지는 현상. 현재 시점에서도 DB에 가해지는 부하는 정말 작은 수준이었기 때문에 특별히 tuning할 이유는 없었고 퍼포먼스와 관련해서는 기본 설정값으로 사용하고 있었는데 끊어지는 이유를 알 수가 없었다. (MySQL 5.5의 default wait_timeout은 8시간)

1. 환경

Application 동작의 흐름은 DB select, 특정 행위, DB update 순서였는데 특정한 기능을 수행하는게 길게는 20분 정도 혹은 그 이상 소요되는 경우가 있었고 편의를 위해 MyBatis를 사용했으며 pool을 사용하도록 설정해 두었다. 연결이 끊어지는 현상을 보고 poolPingQuery, poolPingEnabled를 설정해 봤는데 마찬가지.

2. Azure에 idle timeout이 있다

우선은 MyBatis 설정과 별개로 왜 DB연결이 끊어지는지 궁금해졌다. DB와의 연결은 어차피 socket을 사용하기 때문에 DB와 application 사이의 통신에 영향을 줄 수 있는 것들은 많다. MyBatis 아래의 JDBC, Kernel의 socket timeout, 공유기 설정 등을 살펴봤는데 특이한 내용은 발견할 수 없었다.

다음으로 Azure에 어떤 특성이 있지 않을까 싶어서 알아보니 따로 idle timeout이 있다고 한다. 기본값이 4분이라고 하는데 테스트한 내용과 정확히 맞는다. (강제로 sleep 시간을 늘려가며 테스트해보니 3분까지는 문제가 없었고 4분부터 연결이 끊김을 확인) 이런 내용은 Azure 문서에 잘 보이지 않고 설정 또한 UI로는 불가능하다. PowerShell로만 가능한데 현재 열려있는 endpoint를 확인해보면 공백으로 표시되고 있고 powershell로 IdleTimeoutInMinutes 값을 30분까지 늘릴 수 있다고 한다.

3. Pool ping에 대한 잘못된 소문

Azure의 idle timeout과 별개로 MyBatis의 poolPing에 대해서 살펴보기로 했다. Pool ping 설정 중에 poolPingConnectionsNotUsedFor 가 있는데 문서 상으로는 마치 설정된 시간에 한번씩 DB에 ping으로 설정된 쿼리를 날려서 연결을 유지해줄 것처럼 보인다. 국내의 대부분 블로그 등에서도 비슷하게 설명하고 있다. 하지만 이 내용은 공식 문서가 잘못된 것인지 예상한대로 동작하지 않는다. 거의 동일한 의문을 가진 해외 사용자의 글을 보기도 했는데 소스를 보면 connection pool에서 실제 connection을 꺼내오거나 넣을 때만 connection이 유효한지 확인을 하는데 이 때 pool ping과 관련된 작업을 수행한다. poolPingConnectionsNotUsedFor에 설정된 값은 connection 유효성을 확인할 때만 영향을 주기 때문에 pool에서 connection을 꺼내오고 집어넣는 시점 이외에는 전혀 영향을 주지 않는다.

4. 결론

Azure나 기타 환경에 대한 timeout 값을 늘리는 것은 좋은 방법은 아니라고 본다. DB connection을 오래도록 유지해야만 하는 상황이라면 별도의 장치를 두어서 connection을 유지하도록 조치하거나 아니면 아예 비용이 소모되더라도 필요한 상황에서만 connection을 맺고 끊는 게 나을 것 같다..

5. 참고

(1) MyBatis 공식문서

http://www.mybatis.org/mybatis-3/configuration.html

(2) MyBatis 참고 소스

https://github.com/mybatis/mybatis-3/blob/mybatis-3.3.1/src/main/java/org/apache/ibatis/datasource/pooled/PooledDataSource.java

https://github.com/mybatis/mybatis-3/blob/mybatis-3.3.1/src/main/java/org/apache/ibatis/datasource/pooled/PooledConnection.java

덕만이 잠든 시간에 – python socket으로 IP 얻기

지난번에 만들어놓은게 고작 IP를 얻기 위해서 package를 별도로 설치해야 한다는게 마음에 걸렸다. 어차피 그 package도 IP 정보를 얻기위해 네이티브 API를 사용하지 않을까 싶어서 방법을 찾아보기로 했다. 사실 가장 좋은 방법은 그 package 소스를 보고 분석하는 것이겠지만 시간이 꽤 걸릴 것 같기도 해서 우선은 간단히.

1. socket.gethostbyname()

원래 처음에 사용했던 방법은 socket의 gethostbyname 함수를 사용하는 것이었다. 이 함수는 hostname을 ip address로 변환해주는데 hostname을 얻기 위해서 gethostname() 이나 getfqdn()을 사용해봤다. OS X에서는 특별한 문제가 없어서 그냥 사용하려고 했는데 raspberry pi로 넘어가니 IP가 자꾸 ‘127.0.0.1’로 나온다. /etc/hosts와 관련이 있는데 예전에 Java로 했을 때도 비슷한 현상이 있었던 것 같다.

아무튼 hostname과 관련된 다른 작업을 해주지 않으면 gethostbyname은 사용하기 어려울 것 같다는게 결론.

2. netifaces

이름처럼 network interface와 관련된 기능을 구현해놓은 package로 설치만하면 정말 간단하게 interface 별로 원하는 정보를 얻을 수 있다. 예를 들어 interfaces 함수를 호출하면 존재하는 모든 interface가 다 튀어나오고 ifaddresses 함수를 사용하면 해당 interface가 가지고 있는 address 정보를 던져준다.

하지만 설치가 귀찮아서…

3. socket.connect() / socket.getsockname()

그래서 최종적으로 사용한 방법은 socket으로 어딘가에 연결한 후에 연결된 socket 정보로부터 IP를 얻어오는 방법이다.

socket_connect

그림처럼 socket을 생성한 후에 아무곳이나 연결한다. 아무곳이라는게 그래도 연결이 가능한 곳이어야 하니 google이 망하지 않는한 유지될 것 같은 걔네 DNS에 연결해본다. socket.connect() 함수는 address를 받게 되어있는데 AF_INET family로 socket을 생성했으니 (host, port)로 이루어진 tuple을 넣는다. port는 아무거나 해도 관계없겠지만 사용되지 않을 것 같은 1을 넣어줬다. (0을 넣을 경우 OS X에서는 오류가 발생함)

연결된 이후에는 socket의 주소를 받아본다. ip와 port가 넘어오니 그 중에 ip 정보만 받으면 끝. (port는 실제 8.8.8.8의 1번 포트와 연결을 맺은 임의의 port 번호가 날라옴)

 

덕만이 잠든 시간에 – Tell me your IP

Raspberry pi를 가지고 놀다보니 불편한 점이 하나 있었다. 작아서 휴대성이 좋기는 한데 새로운 환경에서 전원을 켰을 때 접속 정보를 알기 위해서 번거로운 작업을 해야 한다는 것이다. HDMI 케이블, USB 혹은 무선 키보드/마우스를 들고 가서 다 연결한 후에야 사용 가능한데 주변기기들이 훨씬 크기 때문에 여간 불편한게 아니다. 특히 나의 경우엔 ssh로 붙어서 작업하는 빈도가 높고 연결된 모니터를 직접 보며 작업하는 일은 거의 없기 때문에 위의 과정이 좀 무의미했다.

그래서 인터넷 연결이 되면 IP 정보만 telegram으로 전달하는 모듈을 만들어보기로 했다.

1. Github

https://github.com/blurblah/tell_me_your_ip

위 저장소에 올려두고 대략적인 설명을 써넣었으니 참조.

2. Telegram bot의 제약사항

예전에 telegram bot을 만들어봤을 때 한가지 제약 혹은 유의할 점을 하나 알았는데 하나의 token을 가진 bot은 한 곳에서만 동작해야 한다는 점이다. 두 군데 이상 동작하려고 하면 하나는 오류를 뱉고 실행되지 않는다. 위 저장소에 있는 소스는 device에서 직접 동작하게 되어있기 때문에 telegram bot을 직접 생성해서 token을 넣게 되어있다. 여러개의 device를 대상으로 활용하기 위해서는 방식을 변경해야 할 것 같다. 특정 서버 한 곳에서 bot이 동작하게 하고 device를 등록한 후에 관리되게 만들어야 할 것 같은데 나중에 필요성이 생기면 작업해봐야겠다.

3. 동작 결과

Raspberry pi를 새로운 곳에서 LAN 케이블을 연결하고 전원을 올리면 곧 아래와 같은 메세지가 telegram으로 전달된다. 모든 network interface의 정보를 다 뿌리게 되어있는데 필요한 정보만 선별해서 다듬은 후에 전송할 필요가 있어보인다.

telegram

덕만이 잠든 시간에 – Raspberry pi를 CCTV로 활용하기

Raspberry pi로 뭘 해볼까 하다가 요즘 진행중인 프로젝트도 있고 해서 CCTV로 활용해보기로 했다.

어차피 카메라 모듈은 있고 기본 제공되는 툴로 스틸사진은 찍어본 적이 있으니 가볍게 접근할 수 있을거라 생각했다.

1. Motion 사용

검색을 해보니 motion이라는 툴을 CCTV로 활용할 때 많이 사용하는 것 같았다.

MJPEG으로 스트리밍 비슷하게 해주고 별도의 클라이언트 없이 브라우저로 가볍게 영상확인도 가능한 것 같아서 선택.

OS로 데비안 계열의 Raspbian을 사용하고 있으므로 apt-get으로 간단하게 설치할 수 있고 /etc/motion/motion.conf 파일에서 각종 설정을 변경할 수 있다.

원격으로 영상 확인과 설정을 제어하기 위해 webcam_localhost와 control_localhost를 변경하고 restart를 했는데 (default로 localhost에서만 접근 가능하도록 설정되어 있음) 영상이 나오질 않았다.

이상해서 시스템 로그를 확인해보니

fail_to_open_video_devicedevice를 열 수 없다고 한다.

예전에 카메라 모듈 장착하고 raspi-config에서 enable 설정도 해줬는데 실제 /dev에 보니 video0이 없었고 motion의 설정 파일은 video capture를 위해 default로 /dev/video0를 읽도록 되어있었다.

2. modprobe

로딩되어 있는 모듈들을 살펴보기로 했다.

lsmod그림처럼 이런저런 모듈들은 보이지만 video 관련된건 없다.

그래서 문서를 좀 찾아보니 camera는 v4l2 driver를 사용하도록 되어있고 (https://www.raspberrypi.org/documentation/hardware/camera.md) 사용중인 raspberry pi가 broadcom의 bcm2835 chip이 박혀 있어서 bcm2835-v4l2라는 모듈을 raspbian에 넣어둔 것으로 보인다.

modprobe 명령으로 bcm2835-v4l2를 로딩하고 나서 확인해보면 아래 그림처럼 video 관련 모듈이 뜬다.

lsmod_after_modprobe모듈을 로드한 후 motion을 restart 하니 문제없이 영상을 확인할 수 있었는데 modprobe는 일시적으로 모듈을 올려주는 것이기 때문에 boot 이후에 항상 사용하기 원한다면 /etc/modules 파일에 모듈 이름을 추가해야 한다.

3. LED off

Video가 정상으로 나오는건 확인했는데 카메라 모듈에 달려있는 적색 LED가 항상 켜져있는게 마음에 걸렸다.

상당히 밝아서 눈에 거슬리고 작지만 쓸데없이 파워를 소모하긴 싫으니 꺼보기로 했다.

관련 문서가 있을 것 같아서 찾아보니 역시…

https://www.raspberrypi.org/documentation/configuration/config-txt.md

문서에 의하면 Raspberry PI는 BIOS 대신 /boot/config.txt 파일에서 각종 설정을 변경할 수 있도록 해뒀다고 한다.

/boot/config.txt에 disable_camera_led = 1 을 추가해주고 reboot 하면 카메라는 동작중이지만 LED는 켜지지 않는 걸 확인할 수 있다.

4. 결론

결국 해놓고 보니 뚝뚝 끊어지긴 하지만 훌륭한 퀄리티를 원했던 것은 아니니까 그냥 넘기기로 했음

(framerate 조절해도 별 차이 없었음)

Azure VM의 ssh connection refused 해결방법

MS Azure에서 Ubuntu VM을 생성해 사용하던 중에 어느 시점부터 ssh 연결이 되지 않는 경우가 생겼다.

Portal dashboard 상에서는 running 상태로 표시되고 있고 실제 DNS나 IP는 제대로 할당된 것으로 보였는데 ssh 접속을 시도하면 connection refused라고 표시되었는데 verbose option으로 메세지를 봐도 특별한 내용은 없었다.

1. Reset remote access

해결을 위해 Azure에서 제시하고 있는 방법은 아래 링크와 같다.

https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-troubleshoot-ssh-connections/

가장 손쉬운 방법은 remote access를 reset하는 것인데 별 문제가 아니라면 이 기능으로 해결이 되는 것 같다.

예전 portal (https://manage.windowsazure.com)에서 VM dashboard를 보면 해당 기능에 대한 링크가 있고 누르면 새로운 portal로 이동해서 진행하도록 되어있다.

reset_remote_access

대략 10분 이내에 reset이 되는데 이걸로 해결이 안된다면 아래의 방법을 참조한다.

2. Disk만 분리해서 문제해결 후 VM을 재생성하는 방법

VM에 접속이 안되니 문제를 확인할 수도, 추측되는 문제의 원인을 해결할 수도 없다.

국내 IDC에 있거나 근처에 있는 장비라면 직접 가서 상태를 확인할 수도 있겠지만 클라우드라 그럴 수도 없다.

이 방법은 VM의 disk만 따로 떼어내서 원인 확인 및 해결 후에 다시 VM에 연결해서 살리는 방법이다.

참조한 링크문서는 아래와 같다.

https://social.msdn.microsoft.com/Forums/en-US/54c600c0-f4d6-4b20-ad87-1358fa10d27a/linux-vm-ssh-connection-refused?forum=WAVirtualMachinesforWindows

(0) 사전에 유의할 내용

Disk를 attach, detach 하거나 VM 삭제하면서 남겨둔 disk 정보가 리스트에 늦게 반영되는 경우가 있었다.

관련 작업을 하면서 disk가 보이지 않는다면 몇 분 정도 후에 다시 시도해보면 확인할 수 있다.

임시로 사용할 VM은 삭제하는 VM과 동일한 storage account를 사용하고 있어야 한다.

Disk는 storage account 별로 동일한 공간에 위치하기 때문이다. (물리적으로는 다른 영역일 수 있지만)

(1) VM 삭제

우선 VM을 삭제해야 하는데 삭제할 때 관련 disk는 남겨둔다. (select “Keep the attached disks”)

VM에 attach되어 있는 disk는 다른 VM에 붙일 수 없게 되어있기 때문이다.

(2) 새로운(임시) VM에서 attach disk / mount

새로운 VM을 생성하거나 이미 사용중인 VM에서 attach disk 버튼을 눌러 (1) 과정에서 남겨둔 disk를 붙여준다.

attach_disk정상적으로 붙었다면 dashboard에 표시되지만 임시 VM에서 확인하면 보이지 않는다.

하지만 /var/log/syslog를 SCSI로 grep을 해보면 새롭게 추가된 disk를 확인할 수 있다.

grep_scsi

일반적인 경우라면 /dev/sda1가 root filesystem으로 /dev/sdb1이 붙어있는 상태이고 신규로 추가한 disk는 sdc로 연결된다.

VM에서 mount할 임시 경로를 만들고 /dev/sdc1을 (sdc인 경우) 생성한 경로에 mount 해준다.

mount /dev/sdc1 mount_point

(3) 문제 분석 / 해결

위 과정까지 문제가 없었다면 ssh 연결이 되지 않던 VM의 disk 내용을 고스란히 볼 수 있다.

boot log나 syslog, dmesg 등을 mount 된 경로에서 확인하고 문제의 원인을 분석한다.

나의 경우에는 fstab에서 webdav로 mount 하고 있는 외부 disk 연결에 문제가 발생하는게 원인이었던 것으로 보여 fstab에서 그 부분을 주석처리 해버렸다.

(fstab에서 사용한 option을 좀 수정해야 하겠지만 우선은 VM을 살리는게 목적이었으니 그냥 진행)

(4) Detach disk / VM 생성

다시 booting을 해도 문제가 없을 것처럼 손을 봤으면 임시 VM에서 disk를 umount하고 portal dashboard에서도 detach 해준다.

그 이후에는 실제로 사용할 VM (과거에 제거한 VM과 동일하게 해도 문제없음. Cloud service만 같이 제거한 상태라면)을 생성하는데 detach된 disk를 가지고 생성해야 한다. (VM 생성시에 Gallery의 MY DISKS 항목에서 수정된 disk를 선택)

사전 유의사항에도 명시한 것처럼 disk list가 갱신되는데 시간이 좀 걸릴 수 있으니 list에 disk가 보이지 않으면 얼마 후에 다시 시도해야 한다.