Category: Linux

Chef 에 대해서 – Part 1

OC_Chef_Logo

서버환경이 분산화되면서 수많은 서버들을 관리해야 문제가 생긴다. 대략 1,000대 서버에 Apache 웹서버 설정을 변경하고 그것을 적용하라고 한다면 단시간내에 어떻게 할 것인가와 같은 문제들이다.

프로그램을 개발하고 그것을 서버에서 돌리기위해서는 기본적인 인프라 관리가 필수인데 최근에 글로벌 서비스가 많아지면서 분산된 대랭의 서버들을 어떻게 다룰것인가 하는 문제대한 해답으로 Chef, Puppet 과 같은 인프라스트럭쳐 자동화 프로그램들이 등장했다.

이 문서는 Chef 에 대한 기초적인 내용을 다룬다. 범위는 다음과 같다.

  • Chef 에 대해서
  • 설치
  • 레시피, 쿡북 작성
  • 다양한 예제들.

Chef 에 대해 

Chef 는 서버나 애플리케이션을 쉽게 배포하기 위한 시스템이나 클라우드 인프라 자동화 프레임워크이다. 여기서 중요한 것이 프레임워크(Framework) 라는 사실이다.

시스템이나 애플리케이션을 배포한다는 개념을 단순하게만 봤을때 그냥 관련 파일들을 배포하면 된다. 하지만 대량의 시스템은 다양한 변수들이 존재하고 이러한 변수들을 모두 수용하고 조작하기 위해서는 프로그래밍만한 방법도 없다.

Chef 는 Ruby 로 작성되었다. 따라서 Chef 를 확장하거나 커스터마이징을 하고 싶다면 Ruby 를 잘 알아야 한다. 특히나 특정 조건에 한해 특정 시스템이나 애플리케이션에만 적용되도록 무언가를 만들고 싶을때 Ruby 프로그래밍 지식은 많은 도움이 된다.

Chef 서버 구성도
Chef 서버 구성도

Chef 의 서버 구성은 다음과 같다.

Chef 는 이와같이 Server, Workstation, Client(Node) 로 나뉜다.  Client 는 Chef 에서는 Node 라 불리며 자동화를 시킬 서버 혹은 클라우드 시스템이다. Server 는 Chef 의 모든 것이다. 배포할 Node 목록과 이 Node 들에 대한 정책을 적용할 Rule, 각종 설정내용을 저장하는 저장소, 손쉽게 Node 를 추가하고 정책들을 적용하기 위한 웹 매니저 등을 포함한다. 물론 모든 작업은 Command-line 으로 가능하다. Workstation 은 배포할 애플리케이션에 대한 설정이나 명령어들을 만드는 서버다. Chef 에 이렇게 Node 적용하기 위한 것을 레시피(Recipe) 라고 부르며 레시피 묶음을 쿡북(Cookbook) 이라고 부른다.

당연한 이야기지만 위 서버들이 모두 필요한 것은 아니다. 단일 서버에 Chef 구성이 모두 포함될 수 있다. 아니면,  Server 와 Workstation 을 한 서버에서 모두 구성할 수 있다.

Chef 구성도
Chef 구성도. (출처:Chef 문서)

이러한 개념들은 다음과 같이 Chef 구성도로 나타낼 수 있다.  Chef 를 가지고 할 수 있는 주요한  것들을 나열해 보면 다음과 같다.

  1. 시스템에 명령어 보내고 출력값을 받기
  2. 각종 프로그램 설정 파일들 배포하기
  3. 서비스 프로그램 정지, 시작, 재시작하기
  4. 각종프로그램 설치 – 패키지 설치 or 컴파일 설치

서버나 클라우드 시스템을 다룰때에 대부분은 위 네가지 경우로 수렴된다. Chef 를 이용해 위와같은 작업을 할 경우에는, 첫째로 시스템을 등록하고(이걸 Node라 부른다) 두번째로 레시피를 작성한다. 셋째로 그 레시피를 쿡북에 반영시키고 넷째로 Node 에 명령어를 보내면 Node 는 쿡북을 다운받아 그것을 실행 시켜준다.

그래서 Chef 서버가 모두 구성되고 나서부터 대부분의 작업은 Workstation 에서 다 이루어진다고 보면 된다.

물론 인프라 관리 자동화 프로그램으로 Chef 만 있는 것이 아니다. Puppet 도 나름 꽤 유명하다. 하지만 내가 보기에 Chef 만큼이나 쉽고 간단한 것은 없는 것 같다. 용어들도 마치 시스템 관리를 요리에 빗대어 놓은것도 재미와함께 이해력을 높여준다. 실제로 Workstation 에서 뭔가를 할때에 쓰이는 도구가 나이프(knife)다. 요리를 할때 칼질은 기본이지!!

Chef 에 대해서 간단히 알아봤다. Chef 구성도를 보면 참 많은 것들이 눈에 들어오는데, 지금은 몰라도 된다. 왜냐하면 나도 모르니까. 차차 알아가면 될 문제이니 너무 서둘을 필요는 없을듯 싶다.

다음 시간에는 Chef Server 설치에 대해서 알아보자.

snmpd 로 인해 발생한 시스템 접속 불가 문제

제가 운영하는 시스템에는 snmpd 가 기본으로 서비스되고 있습니다. 각종 시스템의 상태를 파악하기 위해서 snmpd 를 활용하는 거지요. 그런데, 얼마전에 이 snmpd  때문에 아주 큰 문제가 된 일이 있었습니다.

이 문서는 그것이 무엇이며 왜 발생했고 어떻게 처리하는지에 대한 문서 입니다.

snmpd

snmpd 는 국제표준으로 IT기기에 각종 상태들을 네트워크를 통해서 제공할 수 있도록 설게된 SNMP를 제공하는 데몬이다. 수 많은 서버에 기본으로 탑재되어 있어 별다른 노력없이 사용이 가능하다.

리눅스 플랫폼에서 snmpd 를 사용하는데 있어 현재 인자값 사용에 문제가 있다. 인자값의 잘못된 사용으로 인해서 리눅스 전체 시스템에 미치는 영향은 치명적이다 못해 시스템을 재부팅한다하더라도 콘솔접속이 되지 않는한 문제를 해결될 수 없다.

Problems

대부분의 리눅스는 많은 배포판을 이룬다. 내가 테스트한 배포판은 Ubuntu 12.0.4 LTS 와 CentOS 6, 7 이다. 여기서는 Ubuntu 배포판을 기반으로 내용을 전개할 것이다. snmpd 의 문제만 체크하면 되는 것이기에 기타 하드웨어 스펙과 배포판에 설치된 각종 라이브러리등은 중요하지 않아 기술하지 않는다.

이제 문제에 접근해보자. Ubuntu 에서 snmpd 를 설치하면 다음과 같은 파일이 설치된다.

파일내용
/etc/default/snmpdsnmpd 의 init 파일에서 사용되는 snmpd 데몬 실행 옵션
/etc/init.d/snmpdsnmpd 데몬 init 스크립트

주목해야할 파일은 ‘/etc/default/snmpd’ 파일이며 이 파일에서 주목해야할 내용은 다음과 같다.

snmpd 데몬의 기본 운영을 바꾸기 위해서는 ‘/etc/default/snmpd’ 파일에서 위 내용을 편집하면 된다.

그럼, 거두절미하고 문제를 재현해보자. 미리 경고하지만 이는 반드시 콘솔접속이 되는 테스트 서버에서 해야한다. 오직 리모트로만 접속할 수 있는 서버이고 이 문제를 재현했다면 영원히 그 서버에 접속할 방법은 없다.

옵션을 다음과 같이 바꾼다.

자세히 보지 않으면 뭐가 바뀌었는지 알수 없게 된다. ‘-Lf’ 를 빠졌다. 이렇게 바꾸고 snmpd 를 재시작하면 문제가 재현된다.

증상

snmpd 를 재시작한 터미널에서는 별다른 증상을 알 수가 없다. 이제 다른 터미널을 열고 snmpd 를 재시작한 서버에 접속해보자. 아마 다음과 같이 나올 것이다.

root 계정이던 일반 계정이든 아무 상관이 없다. 바로 이게 문제다. 아무것도 접속할 수 없다.

콘솔에서 접속해도 다음과 같은 증상이 나온다.

원인분석

원인은 ‘/etc/default/snmpd’ 에서 정의한 snmpd 의 시작 옵션에 있다. snmpd 는 다음과 같이 커맨드라인에서 시작할 수 있다.

문제는 snmpd 에서 옵션은 반드시 ‘-L’, ‘-a’ 등과 같이 – 로 시작한다. 이것이 없이 그냥 값을 주게되면 그것은 바로 [LISTENING ADDRESSES] 가 된다. 여기서 중요한 것이 [LISTENING ADDRESSES]는 어떤값들이 올수 있느냐인데 이는 snmpd 맨페이지(man)를 통해서 확인할 수 있다.

결론만 말하면 <transport-address> 없이 그냥 문자열을 주게되면 snmpd 는 그것을 Unix Domain Socket 으로 인식하게 된다. 위 맨페이지에서 마지막에 나온 설명이 이것을 말해준다.

문제가 된 옵션을 다시보면

‘/dev/null’ 은 snmpd 에서 Unix Domain Socket 으로 인식하게 된다.

/dev/null

/dev/null 은 major 1, Minor 3 값을 가지는 Char device 파일이다. 이 파일은 특수한 장치 파일인데, 실제로는 없는 존재하지 않는 커널에서 제공하는 장치파일로서 Input/Output 을 empty 화시켜준다.

이 파일들은 아주 많은 프로그램에서 사용되지는데 대표적인 것으로 sshd 가 있다.

위에서 아래쪽을 보면 TYPE 이 CHR 이며 DVICE 1,3 나오는 ‘/dev/null’ 이 보인다.

그런데, 이것을 위에 문제처럼 snmpd 의 Unix Domain Socket 으로 하게되면 /dev/null 는 더 이상 장치파일이 아닌 소켓 파일로 변경되어 이 장치를 사용하는 모든 프로그램에 문제가 발생하게 된다. 다음과 같이 이전에 장치파일이 아닌 그냥 일반파일로 변경된것을 알 수 있다.

그리고 netstat 로 보면 snmpd 가 /dev/null 을 Unix Domain Socket 으로 사용하고 있다는 것이 보인다.

해결방법

가장 좋은 방법은 아직 연결이 유지되고 있는 터미널에서 ‘/etc/default/snmpd’ 의 옵션을 고쳐주는 것이다. 그리고 반드시 재부팅을 해줘야 한다.

그런데, 재부팅을 해서는 안되는 서버라면 어떻게 해야 할까?

첫째, ‘/etc/default/snmpd’ 에서 옵션을 변경하고 snmpd 를 정지시킨다. 재시작해줘도 /dev/null 파일이 장치로 복구가 안된다.

둘째, /dev/null Unix Domain Socket 파일을 삭제한다. 더이상 장치 파일이 아니기 때문에 과감히 삭제한다.

셋째, /dev/null 파일을 다시 만들어 준다.

위와같이 장치파일을 생성해주자 마자 바로 sshd 가 복구된다.

문제의 핵심

문제는 /dev/null 를 Domain Unix Socket 이던 무엇이던간에 프로그램에서 파일이 속성을 변경되도록 했다는데 있다. 애초에 snmpd 인자로 /dev/null 을 주었던게 문제였고 그 이전에 오타발생이 문제였다.

비단 snmpd 만 인자값으로 Unix Domain Socket 을 사용한다고 생각하지 않는다. 리눅스에는 수많은 프로그램들이 존재하고 snmpd 와 같이 옵션으로 Unix Domain Socket 을 받는 경우도 분명 존재한다. 그것도 하필이면 /dev/null 를 준다면 똑같은 문제가 발생할 수 있다.

위의 snmpd 문제로 영향받는 프로그램들

위 문제(snmpd 로 발생되는 문제) 로 영향받는 프로그램들은 /dev/null 장치를 사용하는 프로그램이라면 다 영향을 받는다.

  • sshd
  • nginx ← 동작에는 문제가 없어 보이지만 재시작하면 다음과 같이 나오면서 시작되지 않는다.

  • proftpd ← 재시작하면 다음과 같이 나오면서 시작이되지만 정상적으로 동작이 되지 않는다.

  • Tomcat ← 재시작하면 다음과 같이 나오면서 시작되지만 접속하면 아무것도 안된다. 이것은 아마도 bash 이 오류를 내면서 톰캣 시작시 세팅되는 환경변수들이 셋업되지 않았기 때문인것으로 보인다.

/dev/null 이 없어짐으로 인해서 bash 가 제대로 동작하지 않아 쉘스크립트로 작성된 대부분의 init 스크립트가 제대로 동작하지 못해 대부분의 프로그램들에 문제가 발생한다.

CentOS 6,7

운이 좋게도 CentOS 7 은 배포판 패키징으로 옵션 설정이 없다.

CentOS 에서는 ‘/etc/sysconfig/snmpd’ 에 옵션 설정을 할 수 있다.

여기서 CentOS 7 에서 상태가 심각했다. CentOS 7 에서는 Init System 이 systemd 로 변경되었다. 사실 systemd 는 Init System 프로그램을 넘어서 CentOS 7 배포판의 중추신경계역활을 하게됨에 따라 지대한 영향을 미친다.

이에 따라서 systemd를 제어하는 systemctl 명령어로 그 어떤 것도 실행되지 않았다. 거기다 /dev/null 를 새로 생성하더라도 systemd 가 복구되지 않았다. 무조건 재부팅이 요구된다.

CentOS 6 에서는 ubuntu 처럼 다음과 같이 옵션이 나온다.

역시 옵션은 ‘/etc/sysconfig/snmpd’ 에서 설정할 수 있으며 주석으로 다음과 같이 되어 있다.

CentOS 6과 7의 차이

아마도 개인적인 추측인데, CentOS 7 로 넘어오면서 기본 옵션이 변경된데에는 systemd 의 도입때문으로 보인다. systemd 의 도입으로 각 데몬 프로그램들은 systemd 가 상태, 로깅들을 담당하게 되어 이전 CentOS 6 과는 다르게 각 데몬 프로그램들 각자가 로깅을 할 필요가 없게 되었다.

최종결론

어찌되었든 /dev/null 파일을 삭제하거나 일반파일로 바꾸거나 해서는 안된다. 그렇게되면 sshd 는 접속을 거부할 것이다. 혹시나 다른 방법으로 슈퍼유저인 root 로 커맨드를 날릴 수 있다면 당황하지 말고 다음과 같이하면 적어도 sshd 는 돌아온다.

그리고 ubuntu 에서 ‘/etc/default/snmpd’ 를 수정할때는 오타 를 항상 조심해야 한다.

SSH 포트 포워딩.

많은 IT 종사자들은 회사 보안 때문에 특정 서버에 포트를 오직 SSH 와 서비스를 위한 포트만을 열어둔 경우가 많다. 그런데 서버를 관리하다보면 특정 서비스 체크를 위한 매니징 서비스에 접속을 해야하는 경우가 발생한다. 이럴 경우 사내 보안팀에 매니징 서비스 접속을 위해서 포트를 개방해 줄것을 요구할 수 있지만 이럴때에 SSH 의 포트 포워딩(Port Forwarding)을 이용하면 쉽게 해결 할 수 있다.

SSH 포트 포워딩에도 다음과 같이 세가지 종류가 있다.

  • Local Port Forwarding
  • Remote Port Forwarding
  • Dynamic Port Forwarding

SSH 포트 포워딩은 SSH 서버를 Gateway 나 Proxy 서버처럼 활용해 외부 접속을 하는 것이여서 터널링(Tunneling)이라고 부르기도 한다. 각 포트 포워딩 설명을 예제상황을 가정해 설명하도록 하겠다.

192.168.96.6 서버에는 Tomcat 서버가 가동중이고 Web 접속 포트는 8180 이며 서버 상태를 체크하기 위한 JMX 가 활성화 되어 있고 이 포트는 8190 이다.

현재 이 서버는 테스트 서버여서 자체 방화벽으로 22번 포트만 개방되어 있고 Web 접속 포트와 JMX 포트가 개방되어 있지 않다.

접속하고자 하는 사용자의 PC는 리눅스를 사용한다.

위 말을 도식화 하면 다음과 같다.

Local Port Forwarding 상황

Local Port Forwarding

접속은 SSH 포트인 22번만 열려있고 Tomcat 관련 포트는 리눅스 서버의 로컬 방화벽으로 막혀 있는 상황이라고 가정하자.

이제 Tomcat 서비스에 접속을 하고 싶다면 어떻게 해야하는 걸까? 이럴때 사용하는 것이 바로 Local Port Forwarding 이다. 형식은 다음과 같다.

ssh 의 ‘-L’ 옵션이 바로 Local Port Forwarding 을 하도록 해주는 것이다. <local port> 는 접속하는 클라이언트에서 사용할 포트이며 <Remote Server> 는 접속할 서버(여기서는 192.168.96.6) 를 Gateway 로 이용해 접속할 서버, <Remote Port>는 Remote Server 의 포트이다.

여기서 한가지 주목해야할 것이 [SSH 서버] 는 Gateway 역활을 할 뿐이라는 사실이다. 예를 들어 [SSH 서버] 에서는 외부로 모든 접속이 가능하다라고 가정했을때에 yahoo.com 의 80 포트로 Local Port Forwarding 은 다음과 같다.

위와같이 한후에 클라이언트 PC(여기서는 왼쪽에 있는 컴퓨터)에서 웹 브라우져를 켜고 주소창에 ‘http://localhost:10030’ 이라고 하면 yahoo.com 이 열리게 된다.

다시 가정한 상황으로 돌아오면 Gateway 서버(여기서는 192.168.96.6서버) 외부가 아닌 그 자체의 서비스들을 포워딩할 것이기에 다음과 같이 하면 된다.

이렇게 한 후에 웹 브라우져를 실행하고 주소창에 ‘http://localhost:10030’ 이라고 입력하면 192.168.96.6 서버의 Tomcat 서버 포트인 8180 에 연결되고 톰캣 페이지가 보이게 된다.

Local Port Forwarding 이기 때문에 웹 브라우져에서의 접속 서버명은 항상 localhost 가 된다.

Remote Port Forwarding 

이것은 필자가 아직 다루어보지 못했기에 설명을 생략한다.

Dynamic Port Forwarding

이 포워딩은 접속하는 서버를 SOCKS Proxy 서버로 동작하도록 한다. Local Port Forwarding 에서 접속하는 서버는 Gateway 서버로 동작을 했지만 이 포워딩은 접속하는 서버를 Proxy 서버로 그것도 SOCKS 동작하게 한다.

Proxy 서버이기 때문에 클라이언트에서 접속할때에는 항상 실제 접속을 하는 서버와 포트를 사용하면 된다.

JDK 1.7 이상부터는 JMC(Java Mission Control) 이 함께 설치된다. 이걸 이용하면 Java 애플리케이션의 각종 정보를 볼 수 있는데, 물론 JMX 에 접속도 가능하다. 위 예제 상황에서 Dynamic Port Forwarding 을 이용해 접속해보자.

사용방법은 간단하다.

위와같이 하게되면 192.168.96.6 서버가 포트 10030 으로 Proxy 서버로 역활을 하게된다. 이제 클라이언트에서 JMC 를 구동하고 다음과 같이 Network 설정을 해준다.

JMC Socks Network 설정

그리고 나서 JMX 접속 서버와 포트는 실제 접속을 하기 위한 192.168.96.6 과 8190 으로 해주면 접속이 이루어진다.

JMC 접속 완료

디바이스를 이용한 화면전송

리눅스에 접속되어 있는 상태에서 상대방에게 화면을 전송하고 싶다면 디바이스를 이용한 화면전송 을 이용하면 됩니다. 방법은 아주 간단합니다.

우선 텔넷이나 ssh로 두분이 같은 시스템에 접속 합니다.

저의 디바이스(가상 터미널)명을 찾아야 합니다.
#tty 이 명령어를 입력하시면 다음과 같은 가상터미널이 나타납니다.

그럼, 함께 접속된 상대방의 디바이스(가상터미널)명을 또 찾아야 합니다.
#w 이 명령어를 입력하시고 고객이 잡고 있는 가상터미널을 찾거나
만일 어떤 가상 터미널을 잡고 있는지 잘 모르겠으면 고객에게
tty 명령어를 입력하여 나타나게 되는 가상터미널 번호를 알아
내시면 됨니다. 예) /dev/pts/2 이 나타났다고 가정 하구요.

그럼, 제가 접속한 화면에서 아래와 같은 명령어를 입력하시면 같은 시스템
에 접속된 상대방의 화면이 저의 제어권으로 넘어 오게 됨니다.

자주 사용하지는 않아도 아주 가끔 사용하는 것이오니 참고 하시라구 장황
하게 몇자 적습니다.

그리고, 종료 하실려면 exit 입력하시면 됨니다.

Proxy 서버 개념

Forward Proxy

forward proxy
Forward proxy

포워드 프락시(Forward Proxy)는 클라이언트가 타켓서버(목표서버)의 주소를 받아서 타켓서버로 연결을 시켜준다. 클라이언트는 타켓서버의 주소를 요청하면 프락시 서버는 뒷단에 타켓서버의 주소를 가진 서버에 연결을 포워딩 한다. 프락시 서버는 타켓서버의 주소를 가지고 있지 않다.

Reverse Proxy

Reverse Proxy
Reverse Proxy

리버스 프락시(Reverse Proxy)는 타켓서버의 주소가 아닌 프락시 서버가 타켓서버의 주소를 가지고 있고 프락시 서버가 이를 받아서 뒷단에 실제 타켓 서버로 연결을 시켜준다. 그러니까 타켓서버의 주소를 프락시서버가 가지고 있어야 하며 클라이언트는 타켓서버의 주소로 요청을 하면 프락시 서버가 응답을 하게 된다.

High Availability – Cold, Warm, Hot

이글은 다음의 내용을 번역한 것입니다.

클러스터링(Clustering) 은 소프트웨어, 하드웨어, 데이터 이중화 도입이 필요한 어떤 서비스에 대해 고가용성(High Availability) 구현을 위한 가장 일반적인 기술이다. 실패한 클러스터링 소프트웨어에서는 즉각적으로 관리자 개입이 필요없이 대기(standby) 시스템에 애플리케이션이 시작된다. 소프트웨어 이중화의 타입에 따라서 고가용성이 제공되어질 클러스터들은 다음과 같은 설정들로 구성되어질 수 있다.

  • Cold Standby: 보조 노드는 다른 동일한 기존 시스템의 백업처럼 동작 한다. 이 보조 노드는 처음으로 기존 노드가 고장이 발생했을때만 설치되어지고 구성되어진다. 그리고 나서, 기존 노드가 실패할때에 보조 노도는 켜지고 마지막으로 실패된 컨포넌트가 시작되기 이전 데이터로 복구되어진다. 기존 시스템의 데이터는 스토리지 시스템으로 백업되어질 수 있고 필요할때마다 보조 시스템으로 복구되어질 수 있다. 이것은 일반적으로 몇 시간의 복구 시간을 제공 한다.
  • Warm Standby: 소프트웨어 컨포넌트는 보조 노드에 설치되어지고 활용되어진다. 보조 노드는 켜지고 운영된다. 기존 노드가 실패할때에 이러한 소프트웨어 컨포넌트들은 보조 노드에서 시작되어진다. 이 과장은 일반적으로 클러스터 매니저를 사용해 자동화된다. 데이터는 정기적으로 공유 디스크나 디스크 기반 리플레케이션을 사용해 보조 노드 시스템에 미러(mirror) 된다. 이것은 일반적으로 몇분의 복구 시간을 제공한다.
  • Hot Standby: 소프트웨어 컨포넌트들은 기존 노드와 보조 노드 양쪽 모두에 설치되어지고 활용되어 진다. 보조 시스템의 소프트웨어 컨포넌트들은 켜져 있지만 데이터를 처리하거나 요청을 처리하지 않는다. 데이터는 거의 실시간으로 미러(mirror) 되고 양쪽 시스템은 동일한 데이터를 갖는다. 데이터 리플리케이션은 전형적으로 소프트웨어의 기능을 통해서 이루어진다. 이것은 일반적으로 몇초의 복구 시간을 제공한다.
  • Active-Active (Load Balanced): 이 방법에서는 기본과 보조 시스템들은 Active 이고 병령로 요청을 처리중이다. 데이터 리플레케이션은 소프트웨어 기능을 통해서 행해지고 양방향이 될 수 있다. 이것은 일반적으로 순간적 복구 시간을 제공 한다.

 

SNMP Extend

snmpd 는 시스템의 자원들에 대해서 SNMP 프로토콜을 통해서 볼 수 있는 기능을 제공 한다. 그런데, 알고 싶은 자원들에 대한 목록들을 조회하기 위해 MIB 값들이 할당되어 있으며 자주 사용되는 운영체제, 라우터, 스위치 같은 장비들에 대한 MIB 값들을 미리 할당되어 전 세계적으로 표준으로 재정해 사용하고 있다.

그런데, 표준으로 재정된 MIB 값외에 사용자가 필요한 자원들을 조회하기 위한 MIB 값을 등록해야 하는데 이때 사용하는 것이 SNMP Extend 를 사용하면 된다.

SNMP Extend 는 ‘.1.3.6.1.4.1.2021.8.10’ 같은 MIB 에 조회하고자하는 값들을 등록하면 된다.

예를들면 다음과 같다.

이렇게 하면 ‘.1.3.6.1.4.1.2021.8.10.101’ 를 뿌리로하는 MIB 를 조회하면 iostate 으로 조회된 값이 등록되어 SNMP 프로토콜을 이용해서 원격에서도 조회가 가능하다.

테스트를 해보면 snmpwalk 명령어를 이용해서 다음과 같이 해보자

‘-v 1’ 은 snmpd 의 version 1 을 지정해주는 것이다. 만일 이게 안된다면 ‘-v2c’ 로 지정해서 해보자

이렇게해서 무언가 나온다면 정상이다.

만일 ‘Timeout’ 에러 메시지를 만단다면 다음과 같이 퍼미션을 조정해주자.

흥미롭게도 snmpd 는 /etc/hosts.allow, /etc/hosts.deny 두개의 파일을 참조하는데, 퍼미션이 없다면 ‘Timeout’ 이 오류를 내다.

HyperText Transfer Protocol

HyperText Transfer Protocol 은 인터넷 미디어를 전송하기 위한 규약이다. 인터넷 미디어라는 단어를 쓰기는 했지만, 요즘에는 인터넷을 통해서 못하는게 없을 정도로 인터넷 미디어를 규정하는 매체의 경계는 없다. 따라서 “인터넷 미디어를 전송하기 위한 규약”은 구시대적인 정의이며 차라리 “인터넷을 이용한 웹을 사용할 경우에 쓰이게되는 규약” 정도가 더 잘 맞는듯 하다.

HyperText 는 텍스트를 가지는 노드사이에 지역적인 링크(이걸 HyperLink라 한다)를 사용하는 구조적 텍스트를 말한다. 쉽게 말해서 웹사이트에서 링크를 클릭하면서 텍스트를 불러오는 텍스트 구조를 말하는 것이다.

History

Tim Berners-Lee
Tim Berners-Lee

1989년 CERN (유럽 입자 물리학 연구소) 에 근무하던  Tim Berners-Lee 는 각국에서 온 연구원들과 수많은 논문들을 컴퓨터를 이용해 공유할 방법을 궁리하고 있었다. 박사들의 논문들은 간단한 텍스트와 수식이 전부였는데, 이것을 컴퓨터로 표현하기 위한 손쉬운 방법을 궁리했는데, 그건 프로그래머 수준의 교육을 받은자가 아닌 어떤 규칙성만 알면 손쉽게 만들수 있도록 하는데 초점이 있었다.

여러가지 가능성을 연구하던중에 마크업(MarkUp)  언어로 간단한 규칙만으로 논문들을 공유할 수 있도록 HTML 을 만들어낸다.

문제는 주석이였다. 각종 논문에는 글의 중간중간에 어려운 단어의 해석을 위해서 주석을 붙이곤 했고 논문에 뒷부분에는 참고문헌들을 나열하여 추가적으로 필요한 자료들을 찾아보도록 되어 있었다.

어떻게하면 이러한 수많은 정보들을 손쉽게 접근할 수 있을까라는 생각 끝에 HyperLink 라는 개념이 나오고 이러한  HyperLink 를 포함하는 구조적인 텍스트로서 HTML 이 만들어졌으며 이를 전송하기위한 프로토콜로서 HTTP 를 만들어낸다.

Tim Berners-Lee 는 HTML, HTTP, 그리고 이것을 이용할 오늘날의 웹 브라우저를 만들어냈다. 그것도 1990년대 일이였으며 이렇게해서 제작한 것을 CERN 연구소에서 1991년 8월 6일날 첫번째 웹 문서가 온라인에 게재된다. 이때 이용한 HTTP 의 버전이 바로 0.9 버전이다.

그가 만든 HTML, HTTP 웹 브라우저는 로열티도 없는 무료였으며 많은 회사들이 이에 관심을 가지고 웹(web)의 발전에 기여하도록 만든다. 이러한 그의 노력은 World Wide Web 이라는 단어를 세상에 알리는 계기가 되면서 1994년에 W3C (World Wide Web Consortium) 이 창립되게 이른다.

HTTP 버전.

  • 1991년 HTTP 0.9
  • 1996년 HTTP 1.0
  • 1999년 HTTP 1.1

HTTP 특징.

HTTP 의 가장 큰 특징은 다음과 같다.

  1. 클라이언트(Client) 로부터 요청이 있어야만 한다.
  2. 모든 필요한 HyperText 를 서버가 클라이언트로 전송이 끝나면 서버와의 연결은 해제된다.

HTTP 은 클라이언트가 서버로 연결을 이루고 필요한 HyperText 를 전송받고 연결을 해제하는 세가지 방법으로 동작한다. 필요한 HyperText 를 다 전송하고 나면 연결을 해제한다는 것, 즉 연결지향이 아닌 비연결지향이라고 해서 “Connectionless Protocol” 이라고도 한다.

클라이언트가 서버로 연결을 요청해야만 무언가를 할 수 있다는 것도 중요한데, 이는 서버가 클라이언트를 먼저 연결 요청을 할 수 없다는 것이다.

또, HTTP 는 TV 방송처럼 서버에서 동작하는 것을 보기위한 TV 같은 것이 아니라 서버에서 필요한 자원들을 전부 다운로드 받아서 클라이언트의 웹 브라우저에서 플레이를 해주는 것이다.

따라서 웹(Web)이라는 것은  HTTP 는 서버로부터 HTML 을 구성하는 자원들을 모두 클라이언트에 모두 가지고 와서 클라이언트가 웹 브라우저를 이용해서 보여주는 것이다.

HTTP 구조.

HTTP 는 HTML 을 전송하기 위한 프로토콜이다. 이것을 만들었을 당시에 대상이 연구 논문이였다는 점을 감안하면 최대한 단순한 구조로 만들어져있다는 건 놀라운 일이 아니다.

HTTP 는 Header 와 Body 로 구분되어진다.

Structure of a request message
Structure of a request message.(http://www.icodeguru.com/dotnet/core.c.sharp.and.dot.net/0131472275/ch17lev1sec1.html)

Header 에는 메소드(Method), URI, HTTP 프로토콜과 버전을 비롯한 각종 정보들을 포함하고 Body 에는 클라이언트가 서버에 요청할 내용이 들어간다. 메소드가 GET 일 경우에 QueryString 의 경우가 바로 Body 다.

HTTP 구조에서 Header 들어가는 내용은 HTTP 버전별로 차이가 있다. 주요한 차이는 다음과 같다.

분류HTTP 1.0HTTP 1.1
Pragma지원미지원
Cache-Control미지원지원
Transfer-Encoding미지원지원
Content-Language미지원지원
Connection미지원지원
Accept미지원지원
Accept-Charset미지원지원
Accept-Encoding미지원지원
Accept-Language미지원지원
Host미지원지원
GET지원지원
POST지원지원
HEAD지원지원
OPTIONS미지원지원
PUT미지원지원
DELETE미지원지원
TRACE미지원지원

HTTP 1.0 과 HTTP 1.1 이 가장 큰 차이점이라면 바로 Connection 헤더이며 이 값이 Keep-Alive 를 지원 여부이다. HTTP 1.1 은 Keep-Alive 를 지원해 다수 접속의 요청과 연결을 줄이고 한번의 연결로 다수의 데이터를 전송받을 수 있게 해준다.

이러한 HTTP 는 웹을 보여주는 웹 브라우저와 서버간의 통신이며 기본적으로 메타데이터를, 이는 텍스트 데이터여서 사람이 눈으로 읽을 수 있다, 주고받는 것이여서 이것을 캡쳐해서 볼 수 있다.

HTTP 통신 들여다 보기.

HTTP 통신은 웹 브라우저와 서버간의 통신으로 이를 들여다 볼 수 있다. 여기서는 Telnet 을 통해서 서버에 요청을 보내고 받아보는 방법을 소개한다.

이것은 브라우저가 해야하는 것을 Telnet 이 대신하는 것이라고 보면 되며 실제 웹 브라워져와 서버와의 통신이 어떻게 이루어지는지를 극명하게 보여주는 실제 예라 할 수 있다.

먼저 Telnet 을 이용해서 서버에 다음과 같이 접속을 한다.

linux.systemv.pe.kr 를 했지만 아무 웹 사이트나 상관이 없다. 중요한 것은  “직접입력”,”Enter 쳐준다” 같이 직접 해준 것이다. 이 부분이 바로 웹 브라우져가 웹 서버에 요청을 하는 아주 간단한 방법이다.  위 방법에는 body 가 없다. 그런데도 서버로의 요청이 전달되고 응답도 정상으로 나온다.

응답은 정확하게 Header 와 Body 로 나뉘어 나온다.  주목해야 하는 것은 바로 Header 이다.

응답 Header 는 HTTP 버전과 함께 HTTP 응답 코드로부터 시작한다. 그리고 응답하는 컨텐츠가 어떤 타입인지, 텍스트 인지 그림파일인지 아니면 바이너리 파일인지등, 을 명시하고 있으며 body 의 길이도 보여주고 있으며 현재 연결 상태가 어떤것인지도 나타내고 있다.

Telnet 말고 다른 툴들도 있는데, 여기서 몇개를 소개한다.

Internet Explorer 11 개발자 도구

Windows 운영체제에서 가장 많이 사용하는 Internet Explorer 11 에는 개발자 도구라는 것이 있다. 정확하게는 웹 개발자 도구인데, 단축키 F12 이며 이것을 누르면 브라우져 아래에서 창이 올라온다.

IE11의 개발자 도구
IE11의 개발자 도구

여기에서 “네트워크” 메뉴를 클릭하면 위 화면과 같은 상태가 된다. 이제 왼쪽에 초록색 플레이 단추를 누르고 주소창에 웹 사이트 주소를 입력해서 엔터를 쳐보자.

IE11 개발자도구 네트워크 요약
IE11 개발자도구 네트워크 요약

그러면 위와 같이 웹 브라우저가 입력한 웹 사이트로부터 내려받은 웹 컨텐츠들에 대한 정보와함께 보여준다. 내려받은 웹 컨텐츠의 정보로는 유형, 받은 용량, 받는데 걸린 시간등이다.

이제 위 내려받은 컨텐츠중에 하나를 클릭하고 왼쪽에 ‘자세히’를 클릭하면 아래와 같이 HTTP 정보가 나온다.

IE11 개발자도구 네트워크 상세 정보
IE11 개발자도구 네트워크 상세 정보

아주 보기 편하도록 각각 필요한 정보들을 분류하고 내용들을 잘 보여준다. 이쯤되면 유료 소프트웨어 못지 않다.

Safari

Safari 웹 브라우저에서도  IE11 과 같은 툴을 제공한다. Safari 를 실행하고 “웹 속성보기”혹은 마우스 오른쪽 버튼을 클릭해 요소검사를 실행하면 아래쪽에 IE11 과같이 창이 올라온다.

Safari HTTP 보기
Safari HTTP 보기

여기서 왼쪽에 “타임라인” 클릭하고 페이지를 다시 한번 릴로드 하거나 주소창에 웹 사이트 주소를 입력하고 엔터를 친다. 그러면 타임라인에 시간들이 그려지고 왼쪽에 컨텐츠들이 나오며 그것을 클릭하고 오른쪽에 “리소스” 를 클릭하면 이 컨텐츠의 상세한 정보가 나오면서 HTTP 이 내용을 볼 수 있다.

FireFox

FireFox 웹 브라우저에도 “요소검사” 를 통해서 각 웹 컨텐츠별로 HTTP 의 내용을 아래와 같이 살펴볼 수 있다. “네트워크” 탭을 클릭한 후에 주소창에 웹 사이트 주소를 입력해 엔터를 치면 왼쪽에 웹 사이트의 컨텐츠들이 표시되며 클릭을하면 HTTP의 상세한 정보를 보여준다.

Firefox HTTP 분석
Firefox HTTP 분석

 

Fiddler

Fiddler 는 Web Debugger 이며 독립된 소프트웨어 이다. 무료여서 누구나 사용할 수 있다. 설치를한 후에 실행을 하면 곧바로 HTTP 들을 캡쳐하기 시작하며 어떤 웹 브라우져를 사용하던지간에 HTTP를 캡쳐해준다.

Fiddler
Fiddler

전송된 컨텐츠에 대해서 매우 상세한 정보를 보여주는 전문적인 툴이다.

HTTP 프로토콜이 무엇인지부터해서 이것을 눈으로 직접확인해 볼수 있는 방법까지 간단하게 살펴봤다. 위 내용이 전부가 아니며 아주 기초적인 내용에 불과할 뿐이라는 사실을 상기할 필요가 있다. HTTP 는 아주 간단하지만 위에서 기술한 것보다 훨씬 다양하고 체계적인 내용들이 다수 포함되어 있다.

웹 개발자이거나 웹 서버를 다루는 사람이라면 이러한 툴들을 가까이하는 것이 좋다. 주로 보안 쿠키를 검사하거나 보이지 않은 헤더의 변조가능성등을 살펴봐야하는 때에는 이러한 방법들이 기초적인 추적의 실마리를 제공할 수도 있다.

 

[번역] 바쁜 리눅스 서버에서 TCP TIME-WAIT 상태 대처하기.

이 문서는 원작자의 동의를 얻어 다음이 포스트를 발 번역한 것 입니다.

그리고 이 글의 라이센스는 “CC BY-NC-SA 3.0” 입니다. 글 아래의 라이센스는 이 글에서는 해당되지 않음을 알려 드립니다. 다시 한번 원작자 Vincent Bernat 씨에게 감사 합니다.

net.ipv4.tcp_tw_recycle 를 활성화하지 말라!

리눅스 커널 문서는 net.ipv4.tcp_tw_recycle 이 무엇인지에 대해서 별다른 도움이 안된다.

TIME-WAIT 소켓의 빠른 재사용을 활성화. 기본값은 0이다. 기술적인 전문가의 충고나 요구없이 함부로 바꿔서는 안된다.

이와 비슷한, net.ipv4.tcp_tw_reuse 는 좀더 문서화되었지만 하는 말은 똑같다.

프로토콜 입장에서 안전한때에 새로운 접속에 대해 TIME-WAIT 소켓의 재사용을 허용하라. 기본값은 0 이다.기술적인 전문가의 충고나 요구없이 함부로 바꿔서는 안된다.

이렇게 부족한 문서로 인해서 우리는 TIME-WAIT 숫자를 줄이기위해서 양쪽다 모두 1로 세팅하라고 충고하는 수많은 튜닝 가이드를 찾게된다. 그러나 tcp(7) 메뉴얼 페이지에 의해하면, net.ipv4.tcp_tw_recycle 옵션은 같은 NAT 장치 뒤에 있는 서로 다른 컴퓨터들로부터 접속을 핸들링할 수 없는것처럼, 이는 찾기도 힘들고 그것이 당신을 집어삼킬거다, 대중적인 서버들에게 아주 많은 문제가 있다.

TIME-WAIT 소켓의 빠른 재사용의 활성하기. 이 옵션의 활성화는 NAT(Network Address Translation) 을 가지고 동작할때에 문제를 발생시킬수 있어서 권장하지 않는다.

나는 인터넷에 잘못된 정보를 주는 사람들을 위해서 좀 더 상세한 설명을 여기서 제공할 것이다.

참고로, 이름에 ipv4를 사용함에도, net.ipv4.tcp_tw_recycle 은 IPv6 에함께 적용되어 제어된다, 우리는 리눅스의 TCP 스택에서 찾고 있다는 것을 유념해야 한다. This is completely unrelated to Netfilter connection tracking which may be tweaked in other ways

TIME_WAIT 상태에 대해.

TIME-WAIT 상태를 찾아보고 기억해보자. 이것이 무엇일까? 아래의 TCP 상태 다이어그램을 보라.

TCP State Diagram
TCP State Diagram

오직 처음으로 접속을 차단하는 끝부분에서 TIME-WAIT 상태에 도달한다. 다른 끝단은 빠르게 접속을 태우는것을 허용되는 경로를 따를것이다.

여러분은 ss -tan 를통해서 현재 접속 상태를 살펴볼 수 있다.

목적 TIME-WAIT 상태에 대한 목적은 두가지가 있다.

  • 아주 잘 알고 있듯이 지연된 세그먼트(Delayed Segments)를 같은 네쌍둥이(소스 주소, 소스 포트, 목적지 주소, 목적지 포트) 에 의존하는 뒤늦은 접속을 받아들일려고 하는 접속을 차단하는 것이다. 순차적인 번호(sequence number) 는 또한 접속을 받을 수 있는 어떤 구간에 있어야 한다. 이것은 아주 작은 문제를 야기하지만 여전히 특히 아주 큰 윈도우즈를 받는 빠른 접속에서는 존재하게 된다. RFC 1337 은 TIME-WAIT 상태가 부족해지면 무슨일이 벌어지는지를 상세히 설명하고 있다. Here is an example of what could be avoided if the TIME-WAIT state wasn’t shortened
duplicate-segment
Due to a shortened TIME-WAIT state, a delayed TCP segment has been accepted in an unrelated connection.
  •  또 다른 목적은 원격 끝에 접속을 닫을 수 있도록 한다. 마지막 ACK 를 잃었을때, 원격 끝은 LAST-ACK 상태에 머문다. TIME-WAIT 이 없다면 원격 끝이 여전히 이전 접속이 유효하다고 생각하는데도 연결은 재개될 수 있다. SYN 세그먼트를 받았을때에 이것은 세그먼트와 같은 것을 기대하지 않는 것처럼 RST 를 응답하게된다. 새로운 연결은 에러와 함께 중단된다.
last-ack
If the remote end stays in LAST-ACK state because the last ACK was lost, opening a new connection with the same quadruplet will not work.

RFC 793 은 적어도 2MSL 시간에 TIME-WAIT 상태가 필요하다. 리눅스에서 이 기간은 튜닝할수 없으며 include/net/tcp.h 에 정의된 것처럼 1분이다.

이 값을 튜닝하도록 하는 제안들이 있었지만, 실제 현장에서는 TIME-WAIT이 좋은 것이기에 거절되어져 왔다.

문제점

이제, 이 상태가 많은 접속을 핸들링하는 서버에서 왜 성가신 존재일수 있는지를 보자. 세가지 측면에서 문제점이 있다.

  • 슬롯(slot)은 같은 종류의 새로운 연결을 차단하기 위해서 연결테이블에 가져다 놓는다.
  • 메모리(Memory)는 커널의 소켓 구조(socket structure) 의 의해서 소모되며,
  • 이에 더해서 CPU도 사용된다.

‘ss -tan state time-wait | wc -l’ 의 결과는 문제가 아니다.

Connection table slot

TIME-WAIT 상태에 접속은 접속 테이블에서 1분동안 유지된다. 이것은 같은 네 쌍둥이(소스 주소, 소스 포트, 목적지 주소, 목적지 포트)에 다른 연결은 존재할 수 없다는 것을 뜻한다.

웹서를 예를들면, 목적지 주소와 목적지 포트는 상수와 같다.(변하지 않는 값이라는 뜻) 만약 웹 서버가 L7 로드 밸런서 뒤에 있다면 소스 주소 또한 상수 일수 있다. 리눅스에서 클라이언트 포트는 기본적으로 30,000 개의 포트 범위에서 할당된다. (이것은 net.ipv4.ip_local_port_range 를 튜닝함으로써 바꿀 수 있다) 이것은 오직 웹 서버와 로드 밸런서 사이에 연결이 매분당 30,000 개, 약 초당 500 개 연결이 맺어질수 있다는 것을 의미한다.

만약 TIME-WAIT 소켓이 클라이언트측에 존재한다면 이러한 상황은(30,000개 포트를 모두 할당한 나머지 더 이상 접속을 받을 수 없는 상황) 쉽게 찾을 수 있다. connect() 시스템 콜을 호출하면 EADDRNOTAVAIL 을 리턴하고 애플리케이션은 그것에 대한 어떤 에러메시지를 기록할 것이다. 서버측에서 이것은 로그도 없고 계산할 수 있는 카운터도 없어서 좀 더 복잡하다. 이상하게도, 당신은 사용되어지는 네 쌍둥이의 숫자 리스트에서 판단할 수 있는 무언가 오기를 노력해야 한다.

이에 대한 해결책은 좀 더 많은 네 쌍둥이들이다. 이것은 몇가지 방법으로 이룰 수 있다. (설정하는데 어려운 순서대로)

  • net.ipv4.ip_local_port_range 를 좀 더 넓게 세팅함으로써 좀 더 많은 클라이언트 포트를 사용.
  • 리스닝 하기위한 웹서버에게 몇개의 추가적인 포트(81,82,83,…)를 할당함으로써 좀 더 많은 서버 포트를 사용.
  • 로드 밸런서에 아이피를 추가하거나 라운드 로빈 기능으로 그들을 사용하게 함으로써 좀 더 많은 클라이언트 아이피를 사용.
  • 웹 서버에 추가적인 아이피를 추가함으로써 좀 더 많은 서버 아이피 사용.

물론, 마지막 해결책은 net.ipv4.tcp_tw_reuse 나 net.ipv4.tcp_tw_recycle 를 트윅하는 거다. 아직 하지는 말자. 우리는 뒤에서 그것들의 세팅에 대해서 이야기할 할 거다.

메모리

핸들링을 위해서 많은 접속을 가지는 경우에, leaving a socket open for one additional minute may cost your server some memory. 예를들어, 만약 당신이 약 초당 10,000 개의 새로운 접속을 핸들링하길 원한다면 당신은 TIME-WAIT 상태에 약 600,000 소켓을 가지게 될 것이다. 그러면 얼마나 많은 메모리를 소모할까? 그렇게 많지 않다.

첫째로, 애플리케이션 입장에서, TIME-WAIT 소켓은 그 어떤 메모리도 소모하지 않는다. 소켓은 닫혔다. 커널에서 TIME-WAIT 소켓은 세개의 다른 목적에 대해 세개의 구조로 제공된다.

  1. 다른 상태의 연결이 포함되어 있음에도 “TCP established hash table” 로 불리우는 접속 해쉬 테이블(A hash table of connection) 은, 예를들어 새로은 세그먼트를 받아들일때에 존재하는 연결을 위치시키는 장소 사용되어진다.이 해쉬테이블의 각각의 버킷(bucket)은 TIME-WAIT 상태에서 접속 리스트와 정규적인 활성 접속 리스트가 포함 된다. 이 해쉬테이블의 크기는 시스템 메모리에 의존적이며 부팅시에 볼수 있다.

    이것은 thash_entries 파라메터를 가지는 커널 명령어 라인에 숫자 엔트리를 지정함으로써 값을 재지정할 수 있다. TIME-WAIT 상태에서 접속 리스트의 각 요소들은 ‘struct tcp_timewait_sock’ 이며, 다른 상태들을 위한 타입은 ‘struct tcp_sock’ 이다.
  2. 접속 리스트의 집합은, “death row” 로 불리우는, TIME-WAIT 상태에서 접속 만료(expire)를 위해서 사용되어진다. 그들은 만료되기전에 얼마나 시간이 남았는지에 따라서 정렬되어진다. 접속 해쉬 테이블에 엔트리처럼 같은 메모리 공간을 사용한다. 이것은 ‘struct inet_timewait_sock’ 의 ‘struct hlist_node tw_death_node’ 멤버이다.
  3. 바운드 포트 해쉬 테이블은, 로컬 바운드 포트들과 연관된 파라메터들을 들고 있는, 주어진 포드가 리슨(Listen)하기위해 안전한지 혹은 다이나믹 바운드 케이스의 경우에 자유 포트를 찾을수 있는지를 결정하는데 사용되어진다. 이 해쉬 테이블의 크기는 접속 해쉬테이블의 크기와 똑같다.

    각 요소들은 ‘struct inet_bind_socket’ 이다. 그것은 각 로컬적인 바운드 포트당 하나의 요소다. 웹 서버에 TIME-WAIT 접속은 로컬적으로 80포트에 바운드 되고 TIME-WAIT 접속들의 형제들(sibling) 처럼 같은 엔트리들을 공유한다. 다시 말해서, 원격 서비스에대한 하나의 접속은 로컬적으로 어떤 랜덤 포트에 바운드 되고 그 엔트리는 공유되지 않는다.

그래서, 우리는 오직 ‘struct tcp_timewait_sock’ 과 ‘struct inet_bind_socket’ 에 의해서 소모되는 용량만을 생각하면 된다. 하나의 ‘struct tcp_timewait_sock’ 은 TIME-WAIT 상태에서 각 접속에대한, inbound or outbound, 것이다. 하나의 전용의 ‘struct inet_bind_socket’ 은 각 outbound 접속에 대한 것이며 inboud 접속에 대한 것은 없다.

하나의 ‘struct tcp_timewait_sock’ 은 168 bytes 인 반면에 ‘struct inet_bind_socket’ 은 48 bytes 이다.

그래서, 만약 약 40,000 개의 TIME-WAIT 상태의 inboud 접속을 가지고 있다면 10MB 보다 적은 메모리를 소모한다. 만약 dir 40,000개의 TIME-WAIT 상태의 outbound 접속을 가지고 있다면 2.5MB 추가적인 메모리를 위해 계산이 필요하다. ‘slabtop’의 출력을 보고 체크해보자. 여기에 TIME-WAIT 상태에서 약 50,000 개의 접속과 약 45,000개의 outbound 접속을 가지는 서버의 결과가 있다.

여기서 바뀐 것은 아무것도 없다. TIME-WAIT 접속으로 인해 사용되어지는 메모리는 정말 작다. 만약 당신의 서버가 초당 수천개의 새로운 접속을 처리해야 해야 한다면, 클라이언트에게 데이터를 효과적으로 전송하기 위한 좀 더 많은 메모리가 필요할 뿐이다. TIME-WAIT 접속 오버헤드는 무시해도 좋다.

CPU

CPU 측면에서 free local port 를 찾는 것은 조금 비싼작업일 수 있다. 이 작업은 락(lock)을 사용하고 free port 를 찾을때까지 로컬 바운드 포트들을 반복하는 ‘inet_csk_get_port’ 함수에 의해서 이루러진다. 이 해쉬 테이블의 아주 많은 엔트리들은 TIME-WAIT 상태의 outbound 연결을 가졌다면 일반적으로 문제가 되지 않는다. 접속들은 보통 같은 프로파일을 공유하고 함수들은 그들을 순차적으로 반복함으로써 아주 빠르게 free port 를 찾는다.

다른 해결법

만약 여전히 이전 섹션을 읽었는데도 TIME-WAIT 접속들이 문제가 있다라고 생각한다면 이 문제를 해결하기 위해 세 가지 추가적인 해결법이 있다.

  • socket lingering 비활성.
  • net.ipv4.tcp_tw_reuse.
  • net.ipv4.tcp_tw_recycle

Socket lingering

close() 가 호출되면, 커널 버퍼에 남아있는 데이터는 백그라운드로 보내질 것이고 소켓(Socket)은 최종적으로 TIME-WAIT 상태로 변한다. 애플리케이션은 즉각적으로 계속 일을 할 수 있고 모든 데이터는 안전하게 분배되도록 다루어진다.(주, 소켓이 빨리 닫힘에따라 애플리케이션은 소켓관련 작업을 끝내고 다른 일을 할 수 있다는 말)

그러나 애플리케이션은 이러한 행동을 하지못하도록 선택할 수 있는데, 그것이 바로 Socket lingering 이다. 거기에는 두가지 특징이 있다.

  1. 첫째로, 어떤 남아있는 데이터는 폐기처분되고 일반적인 4번의 패킷 접속 차단 시퀀스를 갖는 차단 접속 대신에 연결은 RST 를 가지고 닫힐 것이며 즉각적으로 파괴될 것이다.
  2. 두번째로, 여전히 소켓의 보내는 버퍼에 데이터가 남아있다면 프로세스는 close() 가 호출될때에 모든 데이터 보내지고 접속자로부터 응답을 수신받거나 설정된 링거 타임머가 만료될때까지 sleep 될 것이다. 이것은 소켓이 non-blocking 세팅되었을때에 프로세스가 sleep 되지 않을 가능성이 있다. 이것은 설정된 타임아웃 동안 보내어질 수 있는 데이터가 남아있는것을 허용하지만 만약 데이터가 성공적으로 보내지면, 일반적인 차단 시퀀스는 실행되고 TIME-WAIT 상태를 얻게된다. 그리고 다른 게이스로, RST 를 가지는 접속 차단을 얻을 것이고 남은 데이터는 폐기되어진다.

양쪽의 경우에, 비활성 Socket lingering 은 one-size-fits-all 해결방법이 아니다. 이것은 상위 프로토콜 지점으로부터 사용하는 것이 안전한 HAProxy 나 Nginx 처럼 몇몇 애플리케이션에서 사용되어질 수 있다. (주, Nginx 의 경우 lingering 관련 옵션들이 존재한다.) There are good reasons to not disable it unconditionnaly

net.ipv4.tcp_tw_reuse

TIME-WAIT 은 별 상관없는 접속을 받는 지연된 세그먼트를 차단한다. 그러나, 어떤 조건에서, 새로운 접속의 세그먼트가 오래된 접속의 세그먼로 잘못 해석할 수 없도록 해준다.

RFC 1323 은 고대역폭 경로를 넘는 성능향상을 위한 TCP 확장 셋을 제공한다. 다른 것들 사이에서, 이것은 두개의 4 바이트 타임 스탬프 필드를 전달하는 새로운 TCP 옵션을 정의한다.  하나는 TCP 보내기 옵션의 현재 타임스탬프 값이며 다른 하나는 원격 호스트로부터 아주 최근에 받은 타임스탬프 이다.

net.ipv4.tcp_tw_reuse 를 활성화 하면, 리눅스는 새롭게 외부로나가는 접속을 위해 새로운 타임스탬프가 이전 접속에서 기록된 최근의 타임스탬프보다 현격하게 크다면 TIME-WAIT 상태에서 존재하는 접속을 재사용한다: TIME-WAIT 상태에서 외부로나가는 접속은 정확히 1초 후에 재사용되어질 수 있다.

어떻게 이게 안전한가? TIME-WAIT 의 첫번째 목적은 똑같은 세그먼트가  아무런 연관이 없는 접속을 받는 것을 피하는 것이다. 타임스탬프 사용 덕분에, 이러한 똑같은 세그먼트들은 시간이 벗어난 타임스탬프로부터 오게되었고 결국 폐기된다.

두번째 목적은 원격 끝점이 마지막 ACK 를 잃었을때에 발생되는 LAST-ACK 상태가 안되게 해준다. 원격 끝점은 다음의 경우가 있을때까지 FIN 세그먼트를 재전송한다.

  1. 포기하고 접속이 해제될때까지
  2. 기다리던 중에 ACK 를 수신하고 접속이 해제될때까지
  3. RST 를 수신하고 접속이 해제될때까지

만약 FIN 세그먼트가 제때에 수신이 되면 로컬 소켓은 여전히 TIME-WAIT 상태에 있을 것이고 기대한 ACK 세그먼트를 보낼 것이다.

한번 새로운 접속이 TIME-WAIT 엔트리로 바뀌면, 새로운 접속의 SYN 세그먼트는 timestamps 덕분에 무시되어지고 RST 에 의한 응답은 없지만 FIN 세그먼트의 재전송에 의한 응답만 있다. FIN 세그먼트는 LAST-ACK 상태 중 전환을 허용할 RST 를 가진 응답이 있을 것이다.( 왜냐하면 로컬 접속은 SYN-SENT 상태이다) 응답이 없기 때문에 초기 SYN 세그먼트는 결국 (일초 후) 재전송되고 연결이 약간의 지연을 제외하고, 명백한 오류없이 설정됩니다.

If the remote end stays in LAST-ACK state because the last ACK was lost, the remote connection will be reset when the local end transition to the SYN-SENT state.
If the remote end stays in LAST-ACK state because the last ACK was lost, the remote connection will be reset when the local end transition to the SYN-SENT state.

접속이 재사용될때에 TWRecycled 카운트가 증가하는거에 주목해야 한다.

net.ipv4.tcp_tw_recycle

이 메커니즘은 타임스탬프 옵션에 의존하지만 서버는 일반적으로 먼저 연결을 종료할 때 incoming 과 outgoing 접속 모두에 영향을 미친다.

TIME-WAIT  상태는 곧 만료 될 예정이다: 이것은 RTT와 그 편차로부터 산출되는 재송신 타임아웃 기간 후에 제거되어진다. 여러분은 ss 명령어를 통해서 살아있는 접속에 대한 알맞은 값들을 발견 할 수 있다.

만료 타이머를 감소시키면서, 접속이 TIME-STATE 상태로 들어갈때, TIME-WAIT 상태가 제공하는 것과 동일한 보장을 유지하려면 맨 마지막 타임스탬프는 이전에 알고있는 목적지에 대해서 다양한 매트릭을 포함하는 전용구조에 기억되어 진다. 그리고, 리눅스는 TIME-WAIT 상태가 만료되지 않는 한, 최종적으로 기록된 타임스탬프보다 훨씬 큰 타임스탬프가 아닌 리모트 호스트로부터 모든 세그먼트를 드랍(Drop) 할 것이다.

리모트 호스트는 NAT 장치에 있을때, 타임스탬프 조건은 1분 동안 연결된 NAT 장치 뒤에 하나를 제외하고 모든 호스트는 금지되기 때문에 그들은 같은 타임스탬프 클럭을 공유할 수 없다. 이상하게도, 이것은 문제를 진단하고 감지하기 힘든다면 이 옵션을 비활성화 하는것이 훨씬 좋다.

LAST-ACK 상태는 net.ipv4.tcp_tw_recycle 와 같이 정확하게 같은 방법으로 다루어진다.

요약

보편적인 해결책은 더 많은 서버 포트들을 사용하는  네 쌍둥이 숫자를 증가시키는 것이다. 이것은 TIME-WAIT 엔트리들을 가진 가능한 접속들을 고갈되지 않도록 해준다.

서버측에서, NAT 장치들이 가지고 있지 않는지 확신이 서지 않는다면 net.ipv4.tcp_tw_recycle 를 활성화하지 마라. net.ipv4.tcp_tw_reuse 활성화는 들어오는 접속에 대해서는 쓸모가 없다.

클라이언트 측에서, net.ipv4.tcp_tw_reuse 활성화는 아주 안전한 해결책이다. net.ipv4.tcp_tw_reuse 와함께 net.ipv4.tcp_tw_recycle 활성화는 대부분 쓸모가 없다.

마지막으로 Unix Network Programming 을 쓴 W.Richard Stevens 의 말을 인용한다.

TIME-WAIT 상태는 우리의 친구이고 우리에 도움을 준다. (i.e, to let old duplicate segments expire in the network). 이 상태를 피하기 노력하기 보다는 그것을 이해해야 한다.

리눅스 시스템 자원 제한.

아주 접속이 많은 웹 서버를 운영하던중에 다음과 같은 종류의 메시지를 접하는 경우가 종종 있다.

너무나 많은 파일을 열었다는 건데, 도대체 무슨 파일을 열었다는건지 서비스하는 웹 페이지의 파일 개수도 많지도 않은데 말이다.

이 문서는 이러한 의문을 가지는 사람들 위한 것이다.

모든게 파일!

리눅스 시스템은 모든것이 파일이다. 장치도 파일이다. 그 유명한 /dev/ 디렉토리에 있는 팡리들이 바로 장치들이다. 저 파일들에게 cat 명령어로 데이터를 던져주면 그것을 알아먹는 장치는 동작하게 된다.

또, 리눅스에서 프로그램의 실행은 혼자 동작하는 것이 아니다. C 프로그램을 해본사람이라면 라이브러리(Library) 개념을 알고 있을 텐데 프로그램이 동작할때에는 이러한 라이브러리 파일도 함께 로딩(Loading)된다. 예를들어 sshd 라는 명령어가 어떤 라이브러리들고 로딩되는지는 다음과 같이 알수 있다.

sshd 가 실행중인데, 현재 이 프로그램은 아주 많은 파일들을 사용하고 있다. 프로그램 실행을 위한 라이브러리 파일뿐만 아니라 sshd 설정파일들도 눈에 보인다.

그런데, 리눅스 시스템에서 각 프로그램에게 64개 이상의 파일을 사용하지 못하도록 설정되어 있다면 어떻게 될까? 그것이 바로 위에서 만나게 되는 메시지이다.

리눅스 시스템 자원 제어하기.

리눅스 시스템은 위에서 설명한 것처럼 열어야할 파일 개수뿐만아니라 다양한 자원들에게 대해서 제어를 할 수 있도록 해놨다. 이를 위해서 ulimit 라는 명령어를 제공하고 이를 통해서 각각의 자원들을 제어할 수 있다.

위에 나열한 것처럼 cpu, login, file open, file size 등 다양한 것에 대해서 제한(limit)을 둘 수 있다.

또, 각각의 제한은 리눅스 시스템 계정 사용자별, 그룹별, 프로세스별로 줄 수 있다. 그렇다면 현재 로그인한 상태에서 제한은 다음과 같이 확인이 가능하다.

만일 슈퍼 유저(혹은 root 유저) 라면 다른 사용자의 제한도 다음과 같이 확인할 수 있다.

 

Soft and Hard Limit

시스템 리소스 제한에는 Soft Limit 와 Hard Limit 가 있다. Hard Limit 는 Soft Limit 의 최대임계치로서 이 제한에 걸리면 곧바로 문제가 된다. 하지만 Soft Limit 는 최대 임계치는 아니여서 이것을 벗어나더라도 Hard Limit 까지는 문제가 없다. 대신에 Soft Limit 를 벗어났을때에 메일을 보낸다든지 경고 메시지를 내보내는등 조만간에 최대 임계치에 도달할 수 있다는 것을 미리 알려줄 수 있다.

사용자별, 자원별 제한

사용자별, 자원별로 제한을 둘 수 있다. 이는 /etc/security/limits.conf 파일에 명시하면 사용자별, 자원별로 제한이 된다. 파일을 열어보면 자원별은 item 으로 부르고 있는데 제한을 두고 싶은 시스템 자원에 대한 지시어를 찾아서 적으면 된다.

예를들어 systemv 사용자에 대해 최대 파일 열기 디스크립터 개수를 제한고 싶다면 다음과 같이 해주면 된다.

이 내용을 적은 이후부터 systemv 로 시스템에 로그인 하는 사용자는 바로 적용이 되고 이 사용자 계정으로 실해되는 데몬 프로그램은 재시작을 해주면 바로 적용된다.

각 프로세스당 자원에 대한 제한을 알고 싶다면 PID 를 알아낸뒤에 다음과 같이 해주면 된다.