Rate Limiting with NGINX and NGINX Plus

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

아주 유용한 것중에 하나지만 종종 잘못 이해하고 잘못 설정하게되는 NGINX 기능이 속도 제한(rate limiting) 이다. 이것은 주어진 특정한 시간동안 HTTP 요청양을 제한할 수 있도록 한다. 하나의 요청은 웹사이트의 홈페이지를 위한 GET 요청이나 로그인 폼에 POST 요청이다.

속도 제한은 보안 목적을 위해서 사용되어 질 수 있는데, 에를들어 부르트 포스 패스워드 추정 공격의 속도를 낮출 수 있다. 이것은 실제 사용자에 대해 들어오는 요청 비율을 일반적인 값으로 제한하고 (로깅을 통해) 대상 URL을 식별함으로써 DDos 공격으로부터 보호하는데 도움이 될 수 있다. 더 일반적으로, 동시에 아주 많은 사용자 요청으로 인해 압도되어지는 업스트림 애플리케이션 서버를 보호하는데 사용되어진다.

이 블로그에서는 NGINX의 속도 제한의 기본사항과 더블어 고급 설정에 대해서 다룬다. 속도 제한은 NGINX Plus 에 같은 방법으로 동작한다.

NGINX 속도 제한은 어떻게 작동하나

NGINX 속도 제한은 leaky bucket algorithm 을 사용하는데, 이것은 대역폭이 제한된 상황에서 버스트를 다루기 위해 패킷 교환 컴퓨터 네트워크와 통신에 널리 사용된다. 비유를 하자면, 물이 상단에서 부어지고 바닥에서 물이 줄줄 세는 양동이와 같다. 물을 붓는 속도가 양동이에서 새는 속도를 초과하면 물통이 넘치게 된다. 이것을 요청처리로 다시 보면, 물은 클라이언트로부터 요청으로 볼수 있고 양동이는 FIFO 스케줄링 알고리즘에 따라 처리를 기다리는 요청에 대한 큐로 볼 수 있다. 새는 물은 서버에 의해서 처리하기 위한 버퍼에 존재하는 요청으로 볼수 있고 물통이 넘치는 것은 폐기되고 결코 서비스될 수 없는 요청으로 볼 수 있다.

기본적인 속도 제한 설정하기

속도 제한은 두개의 메인 지시문으로 설정되는데, 다음의 예제처럼 limit_req_zonelimit_req 이다.

limit_req_zone 은 속도 제한에 대한 파라메터를 정의하고 limit_req 는 나타나는 컨텍스트 내에(예를들어, /login/ 에 대한 모든 요청) 속도 제한을 활성화 한다.

limit_req_zone 지시문은 일반적으로 http 블럭에 정의하는데, 여러 컨텍스트에서 사용할 수 있다. 이것은 다음과 같이 세개의 파라메터를 가진다.

  • Key – 제한이 적용되는 요청 특징을 정의한다. 위 예에서 NGINX 변수 $binary_remote_addr 은 클라이언트 IP 주소의 이진 표현을 보유한다. 이것은 각각의 유니크한 IP 주소를 세걔의 파라메터로 정의한 요청 속도로 제한 한다. (우리가 이 변수를 사용하는 이유는 일반 문자열로 표현되는 클라이언트 IP 주소 $remote_addr 변수보다 적은 공간을 가지기 때문이다.)
  • Zone – 각 IP 주소에 상태를 저장하는 사용되는 공유 메모리 존과 어떻게 요청이 제한된 URL 에 접근하는지에 대해 정의한다. 공유 메모리에 정보를 유지하는 다는 것은 NGINX 워크 프로세스들 사이에 공유될 수 있다는 뜻이다. 이 정의는 두 파트를 가진다: 존 이름은 zone= 키워드로 구별되어지고 크기는 콜론(:) 뒤에 온다. 약 16,000 IP 주소에 대한 상태정보는 1 메가 크기를 가지며 위 예제에서는 약 160,000 주소를 저장할 수 있다. 만약 NGINX 가 새로운 엔트리(IP 주소) 를 추가할 필요가 있을때 스토리지가 꽉 찼다면, 가장 오래된 엔트리를 제거 한다. 여유 공간이 새로운 레코드를 수용하기에 충분하지 않는다면, NGINX 는 503 (Service Temporarily Unavailable) 코드 상태를 리턴 한다. 게다가, 메모리 고갈을 방지하기 위해서는 NGINX 가 새로운 엔트리를 만들때마다 이전 60초 동안 사용되지 않은 항목을 최대 2개까지 제거해야 한다.
  • Rate – 최대 요청 속도 지정. 위 예제에서, 속도는 초당 10 요청을 초과할 수 없다. NGINX 는 실제로 요청을 밀리초(millisecond) 초 단위로 추적하므로 이 제한은 100 밀리초(ms) 마다 1개 요청에 해당 한다. 이 예제에서 버스트를 허용하지 않았기 때문에 이전에 허용 된 요청 이후 100ms 미만에 도착하면 요청이 거부 된다.

limit_req_zone 지시문은 속도 제한과 공유 메모리 존에 대한 파라메터를 정의하지만 실제로 요청 속도를 제한하지 않는다. 요청 속도를 제한하기 위해서는 특정한 location 이나 server 블럭에 limit_req 지시문을 포함하는 제한 적용이 필요하다. 위 예제에서는 /login/ 에 대한 요청 속도를 제한하고 있다.

그래서 이제 각각의 유니크한 IP 주소는 /login/ 에 대해 초당 10 요청으로 제한되고, 좀 더 정확하게, 이전 요청의 100ms 안에 /login/ URL 에 대한 요청을 할 수 없다.

버스트 핸들링

위 예제에서, 서로 100ms 이내에 2개의 요청을 받으면 어떻게 되나? 두번째 요청에 대해 NGINX 는 클라이언트에 503 상태 코드를 리턴 한다. 아마도 이것은 우리가 원하는게 아닐 수도 있는데, 애플리케이션은 실제로는 버스티(bursty, 폭발적인) 경향이 있다. 대신 우리는 초과 요청을 버퍼하길 원하고 적시에 서비스를 원한다. 여기서 업데이트 된 구성에서와 같이 limit_req burst 파라메터를 사용 하다.

burst 파라메터는 얼마나 많은 요청을 존에서 지정한 속도를 초과해 클라이언트가 만들 수 있는지를 정의한다. (위 예제 mylimit 존에서, 속도 제한은 초당 10 요청 혹은 100ms 당 1개 다) 이전요청 이후에 100ms 보다 조금 빠르게 도착한 요청은 쿠에 넣는데 여기서 우리는 큐 크기를 20으로 지정했다.

만약 21 요청이 주어진 IP 주소로 동시에 도착한다면, NGINX 은 첫번째를 즉각 upstream 서버 그룹에 포워드하고 나머지는 20개는 대기열에 넣는다. 그런 다음 100ms 마다 큐된 요청을 포워드하고 들어오는 요청이 큐된 요청 개수가 20개가 넘어가게 될때에만 클라이언트에 503 을 리턴 한다.

No Delay 를 가지는 큐

burst 를 가지는 설정은 트래픽 흐름을 부드럽게 만들지만, 사이트가 느려져 보일 수 있기 때문에 그렇게 실용적이 않다. 우리의 예제에서, 큐에 20번째 패킷은 포워드되기 위해 2초를 기다리는데 이 시점에서 이에 대한 응답은 클라이언트에게 더 이상 유용하지 않을 수 있다. 이 상황을 해결하기 위해서는 burst 파라메터와 함께 nodelay 파라메터를 추가해야 한다.

nodelay 파라메터를 사용하면, NGINX 는 여전히 burst 파라메터에 따라 큐에 슬랏을 할당하고 설정된 속도 제한 부여하지만, 큐된 요청의 포워딩 간격을 두지 않는다. 대신, 요청이 아주 순간에 들어오면, NGINX 는 큐에 슬롯이 활용할 수 있는 한 즉시 포워드 된다. 이것은 “taken” 으로 슬롯을 표시하고(mark) 적절한 시간이 경과 할 때까지 다른 요청에 의해서 사용하도록 해제하지 않는다. (위 예제에서, 100ms 지난후)

이전과 마찬가지로, 20 슬롯 큐가 비어있고 21 요청이 주어진 IP 주소로부터 동시에 들어온다고 가정해 보자. NGINX 는 모든 21번째 요청을 즉각 포워드 하고 큐에 20 번 슬롯을 “taken” 으로 표시하고 나면 100ms 마다 1개 슬롯을 해제하게 된다. (만약 25개의 요청이 있다면, NGINX 는 그들중에 21번째를 즉각 포워드 하고 20번 슬롯을 “taken” 으로 표시하고 나머지 4개의 요청은 503 상태로 거절한다.)

이제 첫번째 요청 세트가 포워드된 이후 101ms 에 또 다른 20개의 요청이 동시에 도착한다고 생각해보자. 큐에서 오직 1 슬롯만이 해제된 상태다. 그래서 NGINX 는 1개 요청을 포워드 하고 다른 19개의 요청은 503 상태로 거절 한다. 대신 20개의 새로운 요청 전에 501ms 가 지났다면 5 개의 슬롯이 해제 된 상태이고 NGINX 는 즉각 5개의 요청을 포워드 하고 15개를 거절 한다.

이런 효과는 초당 10개의 속도 제한과 동일하다. nodelay 옵션은 요청들 사이에 허용되는 시간 간격을 제한하지 않고 속도 제한을 적용할경우에 유용하다.

Note: 대부분, 우리는 limit_req 지시문에 burst nodelay 파라메터를 포함하길 권장한다.

두단계 속도 제한

NGINX Plus R17 이나 NGINX 오픈 소스 1.15.7 에서, 전형적인 웹 브라우저 요청 패턴을 수용하기 위해 요청 버스트를 허용하도록 NGINX 를 설정할 수 있고, 추가적인 과도한 요청을 최대한 지점까지 제한하며 그 이상으로 추가적인 과도한 요청은 거부 된다. 두 단계 속도 제한은 limit_req 지시문에 delay 파라메터로 활성화 된다.

두 단계 속도 제한을 설명하기 위해, 여기서는 초당 5개 요청으로 속도를 제한함으로써 웹 사이트를 보호하도록 NGINX 를 설정한다. 웹사이트는 일반적으로 한 페이지당 4-6 리소스를 가지며 12 리소를 넘지 않는다. 이 설정은 최대 12 요청을 허용 하며 첫 8 요청은 지연(delay) 없이 처리 된다. 지연(delay) 는 5 r/s 한도를 적용하기 위해 8개 과도한 요청 이후에 추가 된다. 12개 과도한 요청 이후에, 모든 추가된 요청은 거부된다.

delay 파라메터는 정의된 속도 제한을 준수하기 위해 버스트 크기 내에서 과도한 요청을 조절(delayed) 하는 지점을 정의한다. 이 구성을 사용하면, 8r/s 에서 지속적인 요청 스트림을 만드는 클라이언트는 다음과 같은 동작을 경험하게 된다.

Illustration of rate‑limiting behavior with rate=5r/s burst=12 delay=8

첫 8 요청은 (delay 값) delay 없이 NGINX Plus 에 의해서 프록시 된다. 그 다음 4개 요청은 (burst – delay) 정의된 5 r/s 속도를 넘지 않도록 지연(delay) 된다. 그 다음 3개 요청은 거부되는데, 전체 버스트 크기를 넘어섰기 때문이다. 후속 요청은 지연된다.

고급 구성 예제

NGINX 의 다른 기능과 함께 기본적인 속도 제한 설정과 조합해, 아주 미묘하게 트래픽을 제한을 구현 할 수 있다.

Allowlisting

이 예제는 “allowlist” 에 없는 모든 요청에 대한 속도 제한을 어떻게 하는 보여준다. (역, allowlist 에 있는 IP 는 속도 제한을 하지 않는다.)

이 예제는 geo map 지시문을 사용해 구성했다. geo 블럭은 allowlist 에 IP 주소에 대해서 $limit 변수에 0의 값을 할당했고 다른 모든 것에는 1 의 값을 할당 했다. 그리고 나서 우리는 그러한 값을 키(key) 로 바꾸기 위해서 map 사용하는데, 그것은:

  • 만약 $limit0 이면, $limit_key 는 빈 문자열(empty string) 이 설정 된다.
  • 만약 $limit 1 이면, $limit_key 는 클라이언트의 IP 주소를 바이너리 포맷으로 설정 된다.

이 두가지를 조합해보면, $limit_key 는 허용된 IP 주소에 대해서는 빈 문자열로 설정되고, 그렇지 않으면 클라이언트 IP 주소가 설정 된다. limit_req_zone 지시문에 첫번째 파라메터가 빈 문자열이면, 제한이 적용되지 않는데, 허용된 IP 주소들 (10.0.0.0/8 과 192.168.0.0/24 서브넷) 은 제한이 걸리지 않는다. (역, 10.0.0.0/8 과 192.168.0.0/24 값은 0 이다) 다른 모든 IP 주소들은 초당 5 요청으로 제한이 걸린다.

limit_req 지시문은 제한을 / location 에 적용하고 지연 없는 포워딩으로 설정된 제한을 초과하여 최대 10패킷이 허용된다.

location 에 다중 limit_req 지시문 포함하기

하나의 location 에 다중 limit_req 지시문을 포함할 수 있다. 주어진 요청과 매칭되는 모든 제한은 적용되는데, meaing the most restrictive one is used.(가장 제한적인 것이 사용된다는 뜻이다.). 예를들어, 하나 이상의 지시문이 지연을 부과하는 경우 가장 긴 지연이 사용된다. 비슷하게, 설사 다른 지시분이 그것을 허용한다고 하더라도 어떤 지시문에 효과가 있다면 요청들은 거부 된다.

이전 예제를 확장해, 우리는 허용목록에 IP 주소들에 속도 제한을 적용할 수 있다.

허용된 목록에 IP 주소들은 첫번째 속도 제한(req_zone) 에 걸리지 않지만 두번째(req_zone_wl) 에 걸리며 초당 15 요청으로 제한된다. 허용목록에 없는 IP 주소드른 양쪽 모두 속도 제한에 걸리는데, 훨씬 제한적인 설정인 초당 5 요청이 적용 된다.

연관 기능 설정하기

Logging

기본적으로, NGINX 는 속도 제한으로 지연되거나 드랍된 요청을 기록한다. 다음과 같다.

로그 엔트리에 포함된 필드는 다음과 같다.

  • 2015/06/13 04:20:00 – 로그 엔트리에 기록된 날짜와 시간
  • [error] – 심각 수준
  • 120315#0 – NGINX 워커의 프로세스 ID 와 쓰레드 ID 인데 # 로 구분 된다.
  • *32086 – 속도가 제한된 프록시된 연결 ID
  • limiting requests – 로그 엔트리 기록이 속도 제한(rate limit) 라는 지시자
  • excess –
  • zone – 부과 된 속도 제한을 정의한 zone
  • client – 요청을 생성한 클라이언 IP 주소
  • server – 서버의 IP 주소 혹은 호스트이름
  • request – 클라이언트 의해서 생성한 실제 HTTP 요청
  • host – HTTP 헤더의 Host 값

기본적으로, 위 예제에서 [error] 를 본것처럼 NGINX 는 error 수준에서 거부된 요청들이 기록된다. (지연된 요청들은 한단계 낮은 수준으로, 기본적으로 warn, 기록된다. 로깅 수준을 바꾸기 위해, limit_req_log_level 지시문을 사용해라. 여기서, 우리는 거부된 요청은 warn 수준에서 기록되도록 지정했다.

클라이언트에 Error 코드 전송하기

기본적으로 NGINX 는 클라이언트가 속도 제한을 초과했을때에 503 (Service Temporarily Unavailable) 상태 코드를 응답한다. 다른 상태 코드를 지정하고 싶다면 limit_req_status 지시문을 사용해라.

특정 Location 에 모든 요청 거부하기

만약 특정 URL 에 대한 모든 요청들을 거부하기 원한다면, 단지 제한을 하는 말고, deny all 지시문을 포함한 location 블럭을 설정한다.

결론

우리는 HTTP 요청에 대해 서로 다른 location 에 요청 속도를 지정하고 burst와 nodelay 파라메터와 같은 속도 제한에 대한 추가적인 기능 설정등의 NGINX 와 NGINX Plus 가 제공하는 속도 제한에 많은 기능을 알아봤다. 우리는 또한 거부된 요청과 지연된 요청을 어떻게 기록하는지 설명했고, 허용되거나 거부된 클라이언트 IP 주소들에 대해 서로 다른 제한을 적용하는 등의 고급 설정에 대해서도 알아 봤다.

ElasticSearch 7.13 기반 책 리뷰

오래전에 “시작하세요! 엘라스틱서치” 라는 책을 구매해 본적이 있다. 지금도 소장하고 있는데, 엘라스틱서치에 대한 기본적인 개념을 익히는데 손색이 없는 책이라 평가하고 싶다. 단, 내가 볼때에 엘라스틱서치에 버전이 6.4 이다 보니 없어져버린 기능들은, 예를들어 펫싯, 볼 필요가 없었다.

오늘은 최신의 ElasticSearch 7.13 버전에서 이 책에 내용을 한번 살펴보고자 한다. 엘라스틱서치는 버전이 올라감에 따라 변경된 부분이 상당히 많아진다는 점을 생각해보면 꽤 도움이 될법한 내용들이라 생각한다.

엘라스틱서치의 데이터 구조 – 타입(Type) 없어짐

엘라스틱서치의 데이터 구조를 설명에서 인덱스(Index), 타입(Type), 도큐먼트(Document) 단위로 이루어진다고 설명했다. 하지만 엘라스틱서치 7.x 로 버전이 높아짐에 따라 타입은 이제 없다. 이에 대해서 엘라스틱서치 개발사에서 다음과 같은 페이지를 제공해 설명해 주고 있다.

위 문서를 보면 예를들어서 타입이 뭔지 설명하고 있으며 왜 없앴는지 타입을 이용했던 방법의 대안은 무엇인지 상세히 설명해 주고 있다.

그런데, 과연 타입(Type) 은 없어졌을까?

PUT 메소드 대신 POST 그리고 Content-Type 지정

책에 다음과 같은 예제가 나온다.

하지만 위와 같이 오류가 발생한다.

다음과 같이 Header 에 json 컨텐츠 타입을 지정해 줘야 한다.

앞서 매핑 타입이 없어졌다고 했지만 데이터를 입력하면 잘 들어 간다. GET 메소드를 이용한 조회도 잘 된다. 하지만 이것은 한가지 트릭과 같은데, 매핑에는 타입이 안나온다.

매핑 결과를 보면 타입(Type) 이 안나온다.

hotels 매핑 오류

책에는 hotels 인덱스 생성을 위해서 매핑(Mapping) 을 먼저 작성해 주는데 다음과 같다.

“Root mapping definition has unsupported parameters” 위와같이 오류가 발생 한다. 타입이 문제가 되는 경우라고 볼 수 있다. 위 내용을 수정하면 다음과 같다.

  • 매핑 타입 제거
  • string 타입을 제거하고 text 로 대체
  • not_analyzed 를 false 로 대체
  • dateOptionalTime 을 date_optional_time 으로 변경

성공적으로 생성 됐다.

Bulk 데이터 임포트

대량의 데이터를 엘라스틱서치에 입력할때에는 벌크(Bulk) api 를 이용한다. 매핑의 타입이 없기 때문에 타입을 제거해야 한다.

그리고 벌크 API 를 이용할때에는 다음과 같은 Content-Type 을 입력해줘야 한다.

위와같이 application/x-ndjson 을 지정해 줘야 한다.

systemd unit 편집기 바꾸기

systemd 는 이제 리눅스 시스템의 뼈대가 되는 기본운영 방법이 되었다. 기존에는 System V Init 이였지만 RHEL 7, Ubuntu 16.04부터 기본 시스템운영 프로그램이 되었다.

systemd 는 ‘ctl’ 로 끝나는 명령어들의 집합으로 제어가 가능하다. systemctl, journalctl, timedatectl, hostnamectl, loginctl 이 대표적이다. systemctl 의 경우에는 systemd unit 파일들에 대한 제어와 설정이 가능하다. systemd unit 파일은 기존의 Systemv V init Script 를 대체하는 것으로 일종의 시스템 데몬 프로그램들이라고 보면 된다. 시스템이 시작될때에 자동으로 시작되게 한다거나 종료하게 한다거나 하는것들을 가능하게 한다.

unit 파일은 텍스트 파일이기 때문에 언제든지 파일을 열어서 편한대로 편집이 가능한데, systemctl 명령어는 이런 unit 파일에 대한 편집기능을 제공 한다. unit 파일이 있는 곳으로 가서 텍스트 편집기를 열 필요가 없이 systemctl edit –full 명령어를 이용하면 가능하다.

문제는 systemctl edit — full 명령어를 입력했을 때에 실행되는 에디터가 nano 로 열리는 경우가 많은데, 이것을 바꿀 수 있다.

다음의 내용은 다음의 링크 내용을 정리한 것이다.

SYSTEMD_EDITOR 환경변수 설정

제일 쉬운 방법은 SYSTEMD_EDITOR 환경변수 설정이다. 대부분 Bash 를 사용하기 때문에 이것을 다음과 같이 쉘 환경변수로 설정 해준다.

매번 로그인 할때마다 입력하기 귀찮으면 ~/.bashrc 파일에 추가해주면 로그인할때마다 자동 적용된다.

만일 일반계정으로 로그인을 하고 sudo systemctl edit –full 명령어를 이용한다면 /etc/sudoers 파일에 다음을 추가해 줘야 한다.

sudo 를 실행하는 계정에 있는 환경 변수인 SYSTEMD_EDITOR 를 유지하라는 뜻이다.

update-alternatives 로 기본 에디터 변경

update-alternatives 는 alternative 가 가능한 여러 명령어들을 바꿔 주는 기능을 한다. 이것은 매우 유용한데, 예를들어 Python3 관련 버전이 여려개일 경우에 python3 에 기본 버전을 시스템적으로 지정해 줄 수 있다. update-alternatives –list 명령어를 입력하면 현재 등록된 대체가능한 명령어들이 보인다.

리눅스에서 편집기는 editor 라는 명령어로 되어 있고, 정확하게는 심볼릭 링크다, 이것을 update-alternatives 명령어로 바꿔주면 된다.

위에 내용을 보면 기본 에디터로 nano 가 설정되었다고 나온다. 이것을 바꿔주면 시스템적으로 기본 에디터가 변경되게 된다.

EDITOR 환경변수 지정

실행할때에마다 EDITOR 환경변수를 다음과 같이 지정해주면 된다.

WebLogic 12c: 노드 매니저(NodeManager) 설정하기

WebLogic 에서 노드매니저(NodeManger)는 웹로직을 운영할 머신(Machine) 에 설치되는 것으로 웹로직의 Admin 서버에 요청을 받아 처리해주는 역할을 한다. 노드매니저가 하는 일은 대략 다음과 같다.

  • 매니지드 서버 생성/삭제
  • 매니지드 서버 시작, 중지, 종료

웹로직을 설치하면 노드 매니저도 함께 설치가 된다. 기본적으로 터미널 상에서 노드매니저를 설정을 한 후에 Admin 콘솔에서 Admin 서버가 인식할 수 있도록 등록을 해줘야 한다.

여기서는 터미널 상에서 노드매니저 설정에 대해서 간단하게 다루어 본다.

노드매니저 설정

웹로직을 설치하고 난 후에 도메인(Domain) 을 생성하면 노드매니저 관련 내용도 함께 설치가 된다. 기본적으로 도메인 디렉토리에 nodemanager 디렉토리가 함께 존재하게 되고 여기에 파일들이 존재하게 되는데, 각 파일의 역할은 다음과 같다.

  • nodemanager/nodemanager.domains – 웹로직의 도메인. 파일 시스템상의 도메인의 풀 경로를 입력해준다.
  • nodemanager/nodemanager.properties – 노드 관리자 설정에 핵심 파일이다.
  • config/nodemanager/nm_password.properties – 노드매니저 사용자ID, 패스워드 정보를 담고 있는 파일. 암호화 되어 있지만 초기화가 가능하다.

핵심은 nodemanager.properties 인데 다음과 같은 3가지가 핵심적이 설정으로 보면 된다.

여기서 중요한 설정으로 SecureListener 를 들수 있다. 만일 웹로직 자체를 TLS 설정을 했다면 true 로 놔야 하겠지만 대부분 TLS 설정을 하지 않는다. TLS 설정이 없는 상태에서 이 설정을 변경하지 않고, 왜 안되는지 헤메는 경우가 많다.

이렇게 한 후에 Admin 콘솔에서 노드매니저 등록을 해줘야 한다. 이것은 환경 > 시스템 에서 할 수 있다.

WebLogic 12c Silent 설치

WebLogic 12c 사일런트(Silent) 설치에 대해서 다룹니다. 현재 WebLogic 은 12.2.1.4 버전이다.

WebLogic 12.2.1.4

WebLogic 은 자바 엔터프라이즈 서버(Java Enterprise Server) 이다. 자바를 필요로 하는데, 특징은 Oracle 자바 jdk8 이 반드시 필요하다. JEE 7 스펙을 만족한다. 최근에 Oracle Linux 8.x 에서 인증이 되어서 설치할 수 있게 되었다.

시스템 계정 생성

WebLogic 을 운영하기 위한 시스템 계정을 생성 한다.

디렉토리 생성

WebLogic 설치를 위한 디렉토리를 다음과 같이 생성해 준다.

oracle 계정의 Bash 환경변수 세팅

oracle 계정으로 운영할 WebLogic 을 위해서 환경변수를 다음과 같이 세팅해 준다.

Oracle Java 설치

oracle 계정으로 다음과 같이 Oracle 자바를 설치해 준다. Oracle 자바 1.8 버전을 다운받아서 설치 했다.

WebLogic Response File 작성

WebLogic 사일런스 설치를 하기 위해서 Response File 가 필요한데, 다음과 같이 작성해 준다.

Inventory 파일 작성

인벤토리를 위한 파일을 작성해 준다.

다운로드 WebLogic

다운로드를 할때에 주의해야할게 있다. WebLogic 이 발전하면서 Fusion Middleware Infrastructure 패키지화가 되었다. 다운로드 홈페이지에 가보면 두가지 타입으로 제공하고 있는데, “Generic Installer for Oracle WebLogic Server and Oracle Coherence:” 이것을 다운받아야 한다.

설치 확인

설치가 성공적으로 되었는지 다음과 같이 확인할 수 있다.

도메인 생성하기

WebLogic 은 도메인이라는 개념이 존재한다. 도메인은 하나의 WebLogic 서버처럼 여겨지는데, Admin 과 Managed 도메인 두가지 타입으로 나뉜다.

먼저 Admin 도메인 서버는 Managed 도메인 서버를 총괄한다. Managed 도메인 서버가 자바 애플리케이션을 실행하는 실제 서버라고 보면 되며 이 Managed 도메인 서버는 Admin 도메인 서버에 등록되어야만 동작할 수 있다.

도메인 생성에는 python 스크립트를 이용한다. 정확하게는 WebLogic 의 스크립트를 이용한다고 보면 된다. 이것은 스크립트뿐만 아니라 WLS 쉘을 이용해서도 할 수 있다.

다음과 같이 스크립트를 작성해 준다.

‘krbank_pay’ 가 도메인이 된다. selectTemplate 에 버전을 표시하고 있는데, 이는 $MW_HOME/wlserver/common/templates/wls/wls.jar 파일을 압축 해제하고 나온 파일중에 template-info.xml 을 보면 버전 정보가 나온다.

ServerStartMode 를 prod 로 하고 있으며, 아이디와 패스워드를 입력해주고 있다. writeDomain 으로 Admin 도메인을 어디에 생성할 것인지 디렉토리 정보를 넘겨주고 있다. 이렇게 작성을 다 했다면 다음의 명령어로 실행해 준다.

정상적으로 실행이 되었다면 /home/oracle/wls_domain/admin 디렉토리에 관련 파일들이 존재하게 된다. 디렉토리를 잘 보면 startWebLogic.sh 파일이 존하고 이것을 실행하면 Admin 도메인 서버가 실행 되게 되는데, 중간에 Username 과 password 를 입력받도록 되어 있다.

매번 서버를 시작할때마다 입력해주면 불편하니까, 이것을 다음과 같이 파일을 작성해 준다.

Admin 도메인 서버를 실행하면 입력한 내용은 암호화 된다. startWebLogic.sh 를 실행하고 브라우져에서 http://weblogic 서버 IP:7001/console 을 입력하면 관리자 화면이 나오게 된다.

Redmine 컴파일 설치

Redmine 컴파일 설치에 대해서 다룬다. 최신의 OS 를 가지고 하면 좋겠지만, CentOS 7 에서 설치를 진행 했다.

Mariadb 설치

MySQL 도 가능하지만, MariaDB 를 선택했다. 컴파일 설치가 가능하지만 패키지 설치로 설치한다.

각종 의존성이 함께 설치가 된다. CentOS 7 에서 MariaDB 버전은 5.5 이다. 현재 최선 버전은 10.4 이며 이것을 설치하고자 한다면 MariaDB 홈페이지에 공식 리파지토리를 설정하고 yum 명령어로 간단하게 설치할 수 있다.

my.cnf 파일을 다음과 같이 설정해 준다.

캐릭터 셋을(character-set) 설정하는 부분과 함께 mysql 콘솔에 대한 설정이 전부다. 이제 서버를 시작해주고 보안 설정을 해준다.

mysql_secure_installation 명령어를 이용하면 root 관리자 비밀번호와 간단한 보안설정도 함께 할 수 있다. 이제 redmine 을위한 데이터베이스를 생성하고 접속 계정을 만들어 준다.

로컬에서만 접속되도록 하였다. 이로서 redmine 설치를 위한 Mariadb 설치 설정은 모두 끝났다.

Ruby 2.7 컴파일 설치

Redmine 4.2 를 사용하기 위해서는 Ruby 2.7 이 필요로 한다. 컴파일 설치를 해야 하는데, 먼저 다음과 같이 컴파일 환경을 만들어 준다.

한가지, Ruby 는 jemalloc 를 사용하도록 할 것이다. CentOS 7 공식 패키지 저장소에는 이를 제공하지 않는다. EPEL 서드파티 레드햇 저장소를 활요하면 설치가 가능하다.

설치를 다하고나면 epel 저장소를 비활성화 해준다.

이제 Ruby 2.7 버전을 다운로드하고 다음과 같이 컴파일 설치 해준다.

Ruby 설치가 정상적으로 되었다면 이제 계정에서 Ruby 를 사용할 수 있도록 쉘 설정을 해준다.

이렇게 root 계정에서 ruby 가 인식이 된다.

Redmine 설치

Redmine 을 설치하기 위해서는 먼저 gem 을 설치해줘야 한다. gem 은 Ruby 에서 사용하는 플랫폼을 작성해주는 프로그램이다. 그런데, 여기서 또 생각해봐야 하는것이 이것을 이제 일반계정으로 만들것인지 아니면 전역적인 시스템에 설치할 것인지하는 것을 고려해야 한다.

나는 이것을 redmine 이라는 일반 계정을 생성해서 설치하기로 했다. ruby 는 전역적으로 설치했지만 redmine 은 계정을 생성해 시스템과 분리되도록 하였다.

gem 설치

이제 gem 을 설치해 준다. gem 은 Ruby 설치 디렉토리에 함께 설치 됨으로 root 계정으로 실행을 해준다.

gem 을 이용해서 다음과 같이 bundler, chef ruby-shadow 설치를 진행해 준다.

또, mysql2 확장을 설치해준다. 일종의 드라이버라고 보면 된다.

계정 생성

다음과 같이 redmine 계정을 생성해 준다.

redmine 계정도 ruby 를 인식 시켜준다.

redmine 설치

이제 redmine 계정에 redmine 4.2 를 다운로드하고 설치해 준다.

이제 구동에 필요한 패키지들을 설치해 준다.

쿠키 암호화를 위한 시크릿 토큰을 생성해 준다.

데이터베이스를 생성해 준다.

기본 언어를 한국어로 설정해 준다.

이제 모든게 완료 됐다. Redmine 을 다음과 같이 구동할 수 있다.

이제 브라우져에서 3000 포트로 접속을 시도해 본다. 만일 안된다면 CentOS 7 의 firewalld 서비스를 중지 시키고 다시해본다.

nginx 연동

redmine 의 자체 웹서버를 이용하는게 아니라 nginx 를 이용하는 방법이 있다. 이를 위해서 passenger 라는 프로그램이 필요한데, 이를 먼저 설치해 준다.

먼저 root 계정으로 gem 를 이용해서 passenger 를 설치해 준다.

이제 passenger 프로그램을 이용해서 passenger-nginx 모듈을 설치해주는데, 이게 passenger 가 알아서 nginx 를 다운로드 받아서 컴파일 설치까지 해준다.

명령어를 실행하면 이것저것 물어보는데, 설치 모듈에는 python 을 빼고 ruby 만 되도록 했고 설치는 커스텀이 아닌 그냥 1번으로 설치를 진행 했다. 디렉토리는 /opt/nginx 기본값을 사용했다.

컴파일이 모두 정상적으로 진행이 되면 위와같이 설정 안내가 간단하게 나온다.

nginx.conf 파일 설정

기본적인 설정만 되어 있어서 redmine 을 위한 설정을 다음과 같이 해준다.

systemd 등록

nginx 를 systemd 에 등록 해보자. 다음과 같이 파일을 작성 한다.

이제 systemd 유닛을 활성화 해주고 시작해준다.

Redmine 설치하기

Redmine 은 오픈소스 프로젝트 매니지먼트 프로그램이다. Ruby 로 제작되었기 때문에 설치 과정에서 Ruby 가 있어야 한다. 웹을 통해서 서비스를 하기 때문에 보통 웹서버와 연동을 하는 편이다.

환경

  • OS: CentOS 7
  • Arch: x86_64
  • Minimal Installation 상태

MariaDB 설치

MySQL 도 가능하지만, MariaDB 를 선택했다. 컴파일 설치가 가능하지만 패키지 설치로 설치한다.

각종 의존성이 함께 설치가 된다.

my.cnf 파일을 다음과 같이 설정해 준다.

다음과 같이 서버를 실행해 준다.

이제 기본적인 설정을 위해 mysql_secure_installation 을 실행해 준다.

root 패스워드를 설정하기 때문에 패스워드을 잃어서는 안된다.

이제 redmine 에서 사용할 계정을 생성해 준다.

이로서 redmine 설치를 위한 Mariadb 설치 설정은 모두 끝났다.

Redmine 설치

Redmine 4.2 버전을 설치하기 위해서는 다음과 같은 의존성이 필요하다.

  • Ruby – 4.2
  • Rails – 5.2

CentOS 7 에 Ruby 2.0 만을 패키지로 지원한다. 컴파일 설치를 해야한다.

컴파일을 위한 환경 구축

컴파일러와 라이브러리들을 함께 설치해 준다.

Passenger 와 Nginx 설치

Passenger 는 가벼운 웹 애플리케이션 서버로 Ruby 를 지원한다. 이것을 Nginx 와 통합시켜줄 것이다. 다음과 같이 epel 저장소를 활성화 해준다.

이제 Passenger 설치를 위한 저장소를 추가 준다.

redmine 시스템 계정 생성

다음과 같이 redmine 을 위한 시스템 계정을 생성해 준다.

RVM 을 이용한 Ruby 설치를 할 것이기 때문에 GPG 키를 먼저 임포트 해준다.

다음과 같이 RVM 을 설치 해준다.

마지막에 source 부분을 반드시 해준다. Ruby 2.7 을 설치해 준다.

mysql2 extension 을 다음과 같이 설치해 준다.

이제 Redmine 을 설치해 준다.

다음과 같이 데이베이스 접속 정보를 수정해 준다.

Redmine 의존성을 설치해 준다.

키를 생성하고 데이터베이스를 생성해 준다.

Nginx 설정

listener.ora, tnsnames.ora 생성하기

오라클 데이터베이스 19c 를 Silent 설치를 하고 나면 listener.ora, tnsnames.ora 가 생성되지 않는다. 어떻게 수동으로 이것을 생성하는지에 대해서 알아보고 차이에 대해서 간단히 설명한다.

신기하게도 오라클 데이터베이스 19c를 Silent 설치하고 난 후에 이것을 생성하지 않는다고 하더라도 원격 클라이언트 접속에는 아무런 문제가 되지 않는다. 하지만 접속 서비스를 할당하고 접속을 쪼개고 싶다면 tnsnames.ora 파일이 반드시 있어야 한다.

Oracle Net Listener

Listener 는 정확하게는 ‘Oracle Net Listener’ 라고 한다. 리스너는 하나 혹은 그 이상의 지원하는 서비스에 대한 정보, 프로토콜 주소들을 리스닝하도록 설정된다. 리스너의 설정 정보는 listener.ora 파일에 저장된다. 모든 설정 파라메터는 디폴트 값을 가지기 때문에 별도의 설정을 하지 않더라도 시작하는데 문제가 없다. 디폴트 리스너는 LISTENER 라는 이름을 가지고 시작시 아무런 서비스를 지원하지 않으며 다음과 같은 TCP/IP 프로토콜 주소만 리스닝한다.

리스너는 클라이언트 요청을 지원하는 서비스로 포워드 한다. 이러한 서비스들은 listener.ora 파일에 정적으로 설정되어지거나 리스너로 동적으로 등록되어질 수 있다. 이러한 등록 기능을 서비스 등록(service registration) 이라고 부른다. 등록은 PMON process 에 의해서 실행된다.

참고: Configuring and Administering Oracle Net Listener

생성하기

오라클 데이터베이스 19c 를 Silent 설치하게되면 리스너 설정 파일이 생성되지 않는다. 생성하기 위해서 netca 명령어를 이용할 텐데, 역시나 Silent 모드로 실행을 해준다. 그러기 위해서는 response 파일이 있어야 하는데, 다음과 같은 경로에 있다.

파일을 열어보면 좀 복잡해 보이는데, 주석 등을 제거하면 설정된 값은 다음과 같다.

내용들은 그냥 디폴트 값을 가지고 있다. 오라클 데이터베이스가 listener.ora 파일이 없이 잘 작동하는 이유가 위 파일의 값을 가지고 작동하기 때문인 것인데, 그렇다면 이것을 가지고 설정 파일을 만들어도 문제가 없다는 것과 같은 의미가 된다.

실행을 하게되면 listener.ora 파일이 생성되면서 리스너가 시작된다.

여기서 멀티테넌트 데이터베이스 구조일 경우에 PDB 에 따른 별도의 클라이언트 접속 요청을 처리해줄 필요가 있게된다. 리스너가 클라이언트 요청 파라메터에 PDB 의 SID 나 SERVICE 가 포함될 경우에 이를 받아서 처리해줘야 하는데, 그러기 위해서는 SID_LIST_LISTENER 엔트리를 사용해야 한다.

위 내용을 생성한 listener.ora 에 추가해 준다.

주요한 설정 내용은 다음과 같다.

  • SID_NAME – oracle System IDentifier 초기 설정 파라메터 파일에 INSTANCE_NAME 으로부터 SID 값을 얻을 수 있다.
  • GLOBAL_DBNAME – 데이터베이스 서비스다. 클라이언트 접속 요청을 처리하는 동안, 리스너는 클라이언트 접속 디스크립터에 SERVICE_NAME 파라메터 값과 이 파라메터의 값을 매치시킬려고 한다. 만약 클라이언트 접속 디스크립터가 SID 파라메터를 사용한다면 리스너는 더 이상 매칭 시도를 하지 않는다. 이 파라메터는 전용 서버에서 다이나믹 설정을 지원하지 않는 Oracle8 데이터베이스 설정을 위한 것이였다. 이 파라메터는 Oracle8i 나 그 이후에 데이터베이스 서비스 사용을 위해 필요할 수도 있다. 이 파라메터 값은 기본적으로 초기화 파라메터 파일에 DB_NAME 과 DB_DOMAIN 파라메터의 조합으로부터 (DB_NAME.DB_DOMAIN) 얻을 수 있다.
  • ORACLE_HOME – 오라클 인스턴스의 위치. 세팅하지 않으면 오라클 홈 디렉토리으로 가정한다. 리눅스와 유닉스에서 세팅은 옵션이지만 MS Windows 에서는 세팅을 무시하면 레지스트리 값 HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOMEID 를 사용한다.

오라클 데이터베이스의 컨테이너별 서비스를 알기위해서는 다음의 쿼리를 실행하면 된다.

Local Naming Prarameters in the tnsnames.ora 파일

tnsnames.ora 파일은 네트워크 서비스 이름을 접속 디스크립트로 매핑하거나, Net 서비스 이름을 리스너 프로토콜 주소로 맵핑을 포함한 설정 파일이다. Net 서비스 이름은 접속 디스크립터를 포함하는 데이터베이스 네트워크 주소와 연결된 별명(Alias) 이다. 접속 드스크립터는 프로토콜 주소를 통한 리스너의 위치와 연결을 위한 데이터베이스 서비스의 이름을 포함한다. 클라이언트와 서버는 애플리케이션에서 연결할대에 Net 서비스 이름을 사용한다.

오라클 멀티테넌트 데이터베이스에서 CDB, PDB 별로 접속을 하기 위해서는 tnsnames.ora 파일을 다음과 같이 생성해 준다.

참고: Local Naming Parameters in the tnsnames.ora File

listener.ora, tnsnames.ora 차이

접속하는 방법에 차이를 둔다. 원칙적으로 접속하는 방법은 다음과 같다.

@ 를 기준으로 뒤에 있는 호스트네임:포트/서비스 이다. 이것은 Direct 연결 방법이라고 하는데, 이 연결은 listener.ora 에 정의된 리스너 설정파일에 의해서 접속이 이루어진다.

이것을 짧게 별명만으로 지정할 수 있는데, 그것을 가능하게 하는 것이 tnsnames.ora 이다. pdb1 컨테이너에 접속하기 위해서는 PDB1 별명을 사용하기 때문에 다음과 같이 해도 된다.

PDB1 은 tnsnames.ora 파일에서 별명을 찾아서 접속 디스크립트에 값을 가져다 쓴다. 그리고 연결을 시도하고 이것을 오라클 리스너가 받아서 listener.ora 파일에 파라메터와 매핑해 접속이 이루어지게 된다.

멀티테넌트 데이터베이스 구조에서는 listener.ora 파일에서 SID_LIST_LISTENER 설정을 해줘야지만 PDB 에 접속이 가능해진다.

오라클 기본 정보 확인

오라클을 설치하고 나면 정보를 확인해야 한다. 오라클은 많은 테이블, 뷰, 동적쿼리등을 지원하는데 워낙 많다보니까 다 알수는 없다. 필요한 정보를 위한 간단한 쿼리들을 소개 한다.

데이터베이스 컴포넌트들의 상태 확인

오라클 데이터베이스는 다양한 컴포넌트들로 구성되는데, 이에 대한 상태를 확인할 수 있다.

만일 INVALID 컴포넌트가 있다면 다음과 같이 해준다.

데이터베이스 이름 확인

데이터베이스 이름이 뭔지를 확인할 수 있다.

CDB 가 YES 면 데이터베이스가 CDB 로 생성되었다는 것을 말한다. NO 라면 non-CDB 데이터베이스다.

DB_UNIQUE_NAME 은 쉘 환경변수에도 사용되고 있어 확인해 둘 필요가 있다. 데이터베이스 상태는 READ WRITE 상태여야 정상상태로 본다.

컨테이너 확인

오라클 12c 부터 도입된 개념이 멀티테넌트 컨테이너 데이터베이스다. 어떤 컨테이너들이 있는지 확인해 볼 필요가 있다.

v$containers 뷰는 CDB 안에 root 와 모든 PDB 를 포함한 모든 컨테이너에 대한 정보를 제공한다. 이것으로 PDB 이름을 확인해 볼 수 있다.

모든 CDB 는 다음의 컨테이너를 포함 한다.

  • 정확하기 한개의 root – root 는 오라클이 제공하는 메타데이터와 일반 유저들을 저장한다. 메타데이터는 오라클이 제공하는 PL/SQL 패키지드르이 소스코드 같은 것이다. 일반 유저는 모든 컨테이너가 알아야 하는 데이터베이스 사용자다. root 컨테이너의 이름은 CDB$ROOT 다.
  • 정확히 하나의 시드(seed) PDB – 시드 PDB 는 CDB 가 새로운 PDB를 생성하는데 사용할 수 있도록 하는 시스템이 제공하는 템플릿이다. 시드 PDB 이름은 PDB$SEED 다. PDB$SEED 를 추가하거나 수정할 수 없다.
  • 없거나 하나 이상의 사용자 생성 PDB – PDB는 사용자가 생성한 엔터티로 코드와 데이터를 포함한다. 예를들어, PDB는 인적자원(Human Resources) 나 판매 애플리케이션(Sales application) 과 같은 특정한 애플리케이션을 지원할 수있다. CDB 생성 시점시 PDB는 존재하지 않는다. PDB는 비지니스 요청에 따라 추가 된다.
Multitenant Container Database

위 그림은 멀티테넌트 컨테이너 데이터베이스 구조 이다. CDB 안에 PDB 가 존재하는 구조다. PDB 는 Pluggable Database 다.

root 컨테이너와 함께 오라클 데이터베이스는 자동으로 seed PDB(PDB$SEED) 를 생성 한다. 다음 그래프는 새로운 CDB 생성을 보여준다.

새로운 CDB 생성. seed PDB 도 함께 생성된다.

연결 상태 보기

CDB, PDB 구조에서 현재 내가 어느 곳에 연결되어 있는지 아는 건 중요 하다. 다음의 명령어로 확인해 볼 수 있다.

혹은 다음과 같은 쿼리문으로 확인 가능하다.

컨테이너 타입 확인하기

내가 접속한 컨테이너가 CDB 인지, PDB 인지 타입을 확인할 수 있다.

컨테이너 연결 세션 변경하기

일반 사용자(common user) 는 CDB, PDB 모두에 걸친 사용자임으로 연결 세션 변경을 통해서 PDB, CDB 를 교체할 수 있다.

CDB 와 연결된 PDB 상태 보기

CDB 와 연결된 PDB 상태는 다음과 같이 조회할 수 있다.

STATUS 값을 확인해 상태를 점검할 수 있다.

PDB 의 오픈 모드(Open Mode) 보기

이 쿼리를 통해서 마지막 오픈시간을 확인해 볼 수 있다. root 컨테이너에서는 모든 PDB 에 대한 정보를 보여주며, PDB 에 있다면 오직 하나의 PDB에 대한 정보만 보여준다.

컨테이터 데이터 오브젝트 확인

root 에서(root CDB), 컨테이터 데이터 오브젝트는 root 나 PDB에 포함된 데이터베이스 오브젝트에(테이블 이나 사용자) 대한 정보를 볼 수 있다.

CDB_ 뷰는 컨테이너 데이터 오브젝트들인데, 여기에는 CON_ID 컬럼이 있다. 이 컬럼은 각 PDB 의 컨테이터 ID 를 나타내는 것이며 DBA_PDBS 뷰에 쿼리하면 각 컨테이너 ID 에 대한 PDB 이름을 알 수 있다. 이 두개의 뷰를 조인해서 다음과 같이 데이터 오브젝트 확인이 가능하다.

p.PDB_ID > 2 조건을 준 이유는 root 와 seed 컨테이너를 제외하기 위함이다.

PDBS 에 있는 사용자들 보기

DBA_PDBS 와 CDB_USERS 를 조합하면 각 PDB에 사용자들을 확인해 볼 수 있다.

p.PDB_ID > 2 조건을 준 이유는 root 와 seed 컨테이너를 제외하기 위함이다.

각 PDB 의 데이터 파일 보기

DBA_PDBS 와 CDB_DATA_FILES 뷰를 조합하면 데이터 파일들을 확인할 수 있다.

혹은 다음과 같이 확인해 볼 수 있다.

CDB 에 임시 파일들 보기

CDB_TEMP_FILES 뷰를 활용하면 CDB 에 각 임시 파일 이름과 위치를 확인할 수 있다.

컨트롤 파일 확인

다음과 같이 컨트롤 파일들을 확인할 수 있다.

Redo Log 파일 확인

다음과 같이 RedoLog 파일을 확인할 수 있다.

PDB에 연결된 서비스 보기

CDB_SERVICES 뷰를 통해서 PDB 이름, 네트워크 이름, 컨테이너 ID 등을 확인해 볼 수 있다.

PDB 의 히스토리 보기

CDB_PDB_HISTORY 뷰는 PDB의 생성된 시간과 여러 히스토리 정보들을 보여준다.