Posted in: 프로그래밍

KT PC대수 제한 경험기(?) – 추가단말 서비스

최근에 웹소켓 서비스쪽에 작업중에 이상한 문제가 발생했다. 웹소켓을 제대로 접속을 못하는 문제가 발생한 것이다. 새로고침도 여러번 하고 브라우저도 바꾸어보아도 여전히 접속이 불가능했다. 다른분들께 부탁 하여 접속 가능여부를 체크 해 봤는데, 나에게만 발생하는 문제임을 확인했다. 그제서야 뭔가 문제가 있음을 느끼고 무슨일이 일어나고 있는지 살펴 보았다. WS 요청을 했는데 200이 온다고? 가장 먼저 개발자 도구를 열어보았다. 개발자 도구에서 네트워크 패널을 보면 브라우저가 어떻게 통신을 했는지 간단하게 볼 수 있다. 네트워크 패널을 이용하여 웹소켓 요청을 찍어봤는데, 응답 코드로 101이 아닌 200이 오는것을 확인하였다. 아니? 이게 무슨일인가! 비단 WS를 요청하면 101이 나와야 함이 정상인데 200이 온 것이다. 해당 웹소켓 서버는 내가 제작을 한 것이었는데, 기억에 분명 200을 리턴하는 코드는 작성한 적이 없었다. 혹시 특정 상황에서 내가 200을 리턴하도록 한 것이 있나? 해서 살펴 봤는데 전혀 없었다. 또한 응답 헤더에 있어서도 저런 헤더를 찍어내는 코드는 전혀 없었다. 웹소켓 백엔드 서버에서 발생하는 문제는 아닌것으로 판단하고, 그 앞단을 살펴 보기로 했다. 이 서버는 [유저 -> CDN -> WS서버] 의 접속 구조를 가지고 있다. WS서버의 문제는 아닌것을 확인했으니 CDN을 파 보아야 할 차례였다. 빠르게 해당 CDN을 경유하여 WS를 접속하는 다른 사이트를 찾아보았다.  결과는 정상이었다. 몇번을 시도해 보았고, 수신지 IP가 동일함 까지 확인해 보았다. 테스트 도중 위의 요청 헤더가 이상함을 한번 더 확인하였다. CDN 에서 요청을 내려줬으면 CDN 특유의 헤더가 찍혀야 하는데, 그런것이 전혀 없다. 일단 Response가 나온다는거면 아무리 CDN이 꼬여도 특정 헤더 하나정도는 찍혀야 마땅하다. 즉, 하나 정도는 묻혀 나와야 할 헤더가 없던 것이다. (예를 들어, Cloudflare라면 server: cloudflare 등의 헤더가 있어야 한다) 사실 처음에는 CDN 내부에서 오류가 발생했거나, GET / 요청이 캐싱되어서 GET ws://~~~/ 에서 캐싱된 200 페이지가 나오는것으로 생각했었다. 근데 그것도 아님이 확인되었다. 이제 내 PC에서 CDN으로 가는 사이에서 무슨 일이 벌어지고 있다고 생각했다. MITM 공격이라고 판단하여, 가장 먼저 내 PC에 바이러스나 악성 프로그램이 설치된것을 의심했다. 비슷한 환경의 사이트들을 접속해 보면서 여러 테스트를 해 보았다. 그냥 GET으로 접속하면 아무런 문제가 […]

Posted in: 프로그래밍

(단일) 실패지점

 서버를 운영하다 보면 서버 장에 대응에 대한 고민이 생긴다. 서버란 어찌되었건 “끊김이 없어야 하는 것”이다. 단 몇분이라도 서버에 장애가 발생하면 사용자는 불편을 느낄 뿐더러 신뢰성을 잃을수 조차 있다. 쇼핑몰 서버에서 피크 타임시 발생하는 장애는 하루 매출의 반 이상을 잃게 할 수도 있다. 특정 목적의 서비스 중이라면 SLA에 따라 보상금까지 물어야 할 수도 있다. 이해가 쉽도록 “서버”를 “방송”으로 바꾸어보자. 방송사의 장비에 불량이 생겨서 몇 분 동안 검은 화면만 송출되었다고 생각하면 얼마나 큰 일인지 생각할 수 있을거다. 자체 방송중에 검은화면만 나와도 큰 일인데, 광고 타임에 검은 화면만 나왔다면 (계약에 따라) 손해배상을 해야 할 수도 있다. 증상을 조기에 알아낸다고 해도 수정·패치 작업에는 또 긴 시간이 필요하다. IT운영 인력이 부족할 경우 ‘증상이 있음’을 알아내는것 조차 어려울 수 있다. 그렇기에 가장 중요한것은 애초에 장애가 나지 않도록 하는 것이다. 어느 호스팅사에서 월 1만원짜리 웹 호스팅을 받고있다고 하자. 당장 보이는건 해당 호스팅 업체에 있는 내 사이트밖에 없다. 하지만 그 아래에는 서버의 위치 (가정집, 사내, IDC)와 회선 정보, 그리고 서버 관리능력과 안정성이 깔려있다. 사실 이런 걱정을 모두 호스팅사에 넘기기 위해 (Managed) 웹 호스팅을 쓰는것은 맞지만, 어쨋든 장애가 나면 손해는 내가 보는것이다. 단순한 웹서버라고 해도 안을 까보면 상당히 많은 구성요소가 깔려있다. 당장 글을 쓰면서 생각나는것만 해도 서버의 지리적 위치, 인터넷(회선·대역폭), 내부 장비(스위치등 일체), 전기, 무정전 시스템, 네임 시스템 서버, 물리적인 서버, 그리고 그 안에 설치된 소프트웨어 Nginx·Apache등 웹 서버 프로그램, DBMS […]

Posted in: 프로그래밍

유챗 봇 제작 #2 – 접속정보 처리

 이전까지의 글에서는 유챗 서버와의 통신에 필요한 맨 아랫 부분을 다루어 보았다. 여기서 부터는 밑바닥에 대한 부분에서 벗어나서 실질적으로 봇을 이용하기 위한 (일종의) 클래스를 구현해 볼 것이다.  글을 쓰는 도중에 tokio가 1.0으로 버전이 올라갔다. 아직 종속된 라이브러리들이 1.0을 지원하지 않아서 0.3버전을 바탕으로 글을 작성하였다. 1.0 버전으로 올리는 과정에 대해서는 따로 글을 쓸 것 같지는 않으니, 필요가 있으면 직접 Github의 리포를 확인해야 할 것이다.  이전글에서는 아래와 같은 코드를 이용해서 직접적으로 접속 정보를 전송하였다:  하지만 위의 코드로는 접속정보를 바꾸기가 쉽지 않다. 여러개의 방에 접속하기 위해서는 매번 접속정보를 생성 해 줘야 하며, 인증 토큰을 필요로 하는 방에 접속하려면 해줄것이 너무 많아진다. 물론 안될 것은 없겠지만 이것들을 직접 구워먹고자 하면 상당히 번거롭고 귀찮은 방법이다. 그러므로 앞으로 접속정보를 관리하는 구조체를 만들어서 관련된 작업은 모두 거기서 이루어 지게 한다.  우선 접속정보를 담아낼 수 있는 구조체를 만든다. cache_token과 profile_image는 uchat.js를 뜯어보면서 ‘이런게 있는구나~’ 를 알아냈다. 실제로는 어떻게 사용되는지 까지는 모르겠다. 겉으로는 드러나지 않지만 계속해서 내부에선 업데이트가 진행되는듯 한 것으로 보아, 언젠가 기능 구현에 쓰일지도 모르겠다. uchat.js를 뜯어봤더니, 무조건 있어야 하는 값은 방 ID 하나 뿐이었다. 접속 인증을 사용하지 않는 방은 token이 필요 없다. nick이 없으면 알아서 손님1234 같은 닉네임을 만들어 준다. auth 또한 Option으로 뺄려면 뺄 수 있으나 구현에 있어서 한단계 추가 할 것이 생겨서 UChatAuthLevel이라는 enum에 None을 추가해 놓는것으로 타협하였다. 값을 설정하는 방식으로는 builder 패턴을 이용하였다. getter / setter를 만들 필요도 없을것 같아서 저 수준에서 마감을 했다. (어짜피 필요하면 구조체 내에 접근 가능하기도 하다)  Builder 패턴을 모르는 사람을 위해 설명하자면, 값을 설정하는 함수를 실행하면 자신을 리턴하여 계속 이어서 값 설정을 가능하도록 하는 패턴이다. 아래와 같이 setter를 사용할 수도 있지만, 빌더 페턴에 비해서는 해야 할 것이 더 많다: 빌더 패턴을 이용하면 위의 코드를 더 간단하게 호출할 수 있다. nick()은 닉네임 설정후 빌더(자신)를 그대로 돌려주고, id()도 마찬가지로 자신을 돌려주고 하기 때문에 가능하다. 값이 설정된 빌더를 계속 주고 받고 한다고 생각하면 된다. 이제 필요한 데이터는 모두 모았다. 이제 이것을 실제 접속 정보로 바꾸는 부분이 필요하다. 우선 유챗에서 채팅방 접속 토큰을 어떻게 생성하는지 보자: 여기서 우리가 봐야할 부분은 uchat_array2data() 이다. 한줄한줄 해석해 보자. $arr[‘time’] = time();– 접속토큰 변조 방지를 위해 […]

Posted in: 프로그래밍

유챗 봇 만들기 #1 – Tokio 적용

이전 글에서 유챗이 어떤 프로토콜·구조를 가지고 통신을 하는지 알아 보았다. 이번 글에서는 Tokio를 이용해서 프로그램의 기본적인 구조를 짜 볼 것이다. 또한 tokio-tungstenite 라이브러리까지 결합하여 웹소켓 접속까지 다루어 볼 것이다. Rust 프로젝트 생성 가장 먼저 Rust를 설치해야 한다. Rust는 rustup으로 간단한게 설치 할 수 있다. rustup은 Rust를 사용하기 위해 필요한 툴체인들을 설치하도록 도와주는 공식 프로그램이다. rustup은 https://rustup.rs/ 에서 다운로드 및 설치 할 수 있다. 설치가 완료되었다면 cargo, rustup 등의 프로그램을 사용 할 수 있게 된다. cargo나 rustup가 바로 실행되지 않는다면 쉘(터미널)을 재시작 해야 한다. /usr/bin등이 아닌 /root/.cargo/bin 와 같은 별도의 폴더에 프로그램이 설치되기 때문에 $PATH를 업데이트 해야 하기 때문이다. 쉘을 재시작 하면 정상적으로 $PATH가 다시 읽혀저서 사용이 가능할 것이다. 그래도 안된다면 폴더를 $PATH에 집어넣든 ln -s $/.cargo/bin /usr/local/bin 등으로 적절하게 기존 $PATH에 집어넣든 해서 조치를 취하자. rustup까지 설정이 완료되었으면 Rust 프로젝트를 위한 작업공간을 만들어야 한다. Rust 프로젝트는 Cargo 라는 패키지 관리 프로그램으로 생성 할 수 있다. (npm 같은것이라고 생각하면 된다) cargo new 또는 cargo init를 사용하면 된다. cargo new 이름 을 실행하면 현재 위치 아래에 새로 이름에 해당하는 폴더가 생길것이다. 이미 있는 폴더에 프로젝트를 만들고 싶다면 해당 폴더안에서 바로 cargo init를 실행해도 된다. 정상적으로 실행 됐다면 위와 같은 파일 구조를 가질것이다. 여기서 Cargo.toml은 프로젝트의 설정 및 의존 라이브러리 지정에 사용된다. 실제 프로그램의 코드는 src/ 폴더 아래에 들어가게 된다. 이미 src/ 폴더 아래에는 main.rs가 존재하는데, src/main.rs 파일이 프로그램의 진입점이 된다. Tokio 라이브러리 사용 Tokio는 비동기 작업 스케줄링 라이브러리이다. tokio를 이용하면 빠르고 간편하게 프로그램을 비동기로 짤 수 있다. 우선 우리 프로젝트에 tokio를 적용해 보자. 먼저 Cargo.toml을 열어서 [dependencies] 하단에 tokio = { version = “0.3”, features = [“full”, “io-util”, “net”] […]

Posted in: 프로그래밍

유챗 봇 만들기 #0 – 프로토콜 분석

 유챗에서 작동하는 간단한 봇을 만드려는 중에, 관련된 자료가 검색해도 보이지 안길레 적어본다. 어디까지나 내가 코드를 분석하면서 찾은것을 기반으로 작성한 글이기에 부정확한 내용이 있을 수도 있다. 우선 유챗의 통신 내용을 들여다보면 아래와 같이 나온다: 모든 통신은 웹소켓을 기반으로 동작하고 있음을 볼 수 있다. 특히 웹소켓중 바이너리 타입으로 데이터를 송수신 함을 볼 수 있다. 하나의 웹소켓 패킷(바이너리 타입 메세지) 안에서는 \n을 기준으로 여러개의 메세지를 담을 수 있다. 메세지는 중간에 끊어짐이 없다. 각 줄의 메세지는 다시 \x02, \x03, \x04, \x05, \x06, \x07 등으로 내용 구분된다.   다시 말하자면, 우선 바이너리 타입의 웹소켓 패킷으로 메세지의 덩어리가 온다. 들어온 메세지 덩어리는 우선 \n 단위로 나눠진다. 그렇게 나눠진 한줄 한줄 나눠진 메세지는 다시 \x02~\x07을 구분자로 해서 내용이 나뉘어진다. 맨 앞에는 메세지의 타입(기능)을 구분해 놓은것 인 것 같고, 나머지는 자신의 데이터 맨 앞에 있는 구분자에 의해 해당 내용(데이터)의 타입이 정해지는 것 같았다. 메세지1에 \x03 ESukmean 이라고 있으면, 이는 문자열 “ESukmean”임을 뜻한다. \x04 1이 있으면 이는 true를 의미한다. 내용들이 구분자로 (\x03 ~ \x07, \n등) 쪼개지기에 프로그램이 구분자와 일반 데이터를 구분하는것이 매우 중요하다. 누군가의 채팅 내용에 \n이 있었는데, 그것을 메세지의 분리로 인식하면 프로그램의 버그가 발생할 것이다. 이를 방지하기 위해 혹시라도 구분자에 해당되는 데이터가 들어온다면 그 앞에 \을  붙여서 escape하는 것 같았다. 예를 들어, 채팅 내용이 “가나다라 \n 아자차카” 라면, 우리가 받는 메세지는 “가나다라\\\n아자차카”가 되는것이다. “1234 \x06 5678″ 이라는 데이터는 “1234 \\\x06 5678″로 들어올 것이다. UChat.js를 보면 아래와 같은 구분자 타입이 존재함을 알 수 있다. […]

Posted in: 프로그래밍

프로그램을 조금더 빠르게 – AoS vs SoA 프로그램 비교

저번 글에서 AoS와 SoA의 개념에 대해 글을 써 보았다. AoS와 SoA 이야기를 논하려면 데이터가 메모리에 가지런히 모여있다는 전재를 해야한다. 배열을 만들었으나 실제로는 데이터가 연속적으로 모여있지 않고 뿔뿔히 흩어저 있으면 메모리 캐싱이 될 리가 만무하다. 즉, AoS와 SoA는 메모리 구조가 시스템적일수록 더욱 효율적으로 동작한다. 배열의 저장과 그 외 기능의 동작이 군더더기 없이 딱 필요한 기능만 동작될 […]

Posted in: 프로그래밍

Docker-Compose로 PHP + MySQL 묶기

 지금 이 블로그도 그렇듯 서버내 모든 서비스가 도커 내에서 돌아가고 있다. 서버 맨 앞에 nginX를 두고 적절한 도커 컨테이너로 연결을 중계해 준다. 이때 기본 nginX로 다루기 껄끄러운 커넥션은 다시 Rust를 통해 다시 핸들링한다. 이런 구조속에서 각 컨테이너간 서로 통신해야할 일이 생길수 있다. 나로 같으면 PHP와 MySQL를 서로 연결해줘야 하는 상황이 생긴것이다. 짜피 Docker-Compose를 쓰기에 PHP와 […]

Posted in: 프로그래밍

tokio rs 사용 팁 – Bytes편

Rust로 비동기 프로그래밍을 하다 보면 tokio 라는 런타임(라이브러리)를 접하게 될 것이다. Tokio-rs를 사용하면서 알면 좋을만한 팁들을 정리해 보았다. Bytes 라이브러리를 잘 사용하자 단순히 [u8;] 배열을 여기저기 옮겨다니면서 메모리 풀을 사용하는것도 좋지만, Bytes 라이브러리를 활용하는 것도 좋은 방법이다. 사실 왠만한 상황에서 bytes만큼 괜찮은 메모리 풀을 찾기도, 만들기도 쉽지 않을것이다. BytesMut 활용  IO 작업을 할 때 읽기/쓰기용 […]

Posted in: 프로그래밍

메모리 Reallocation (재할당)

 메모리 재할당은 “필요한 메모리의 양이 바뀌어서 다시 메모리를 할당 받는것”이다. 코딩을 하다보면 메모리를 재할당 받아야 할 때가 종종 찾아온다. 사용자가 직접 메모리 재할당을 할 때도 있고, Python처럼 언어에서 보이지 않게 알아서 재할당을 진행할 때도 있다.  위의 그림을 해석해 보자. 기존에는 데이터를 저장할 때 8Byte의 공간을 필요로 했으나, 어떤 사정에 의해 14Byte가 필요해 졌다. 그래서 메모리 […]