'nginx'에 해당하는 글 3건

aws-nlb

 

AWS 로드 밸런서 컨트롤러로부터 생성/관리 되는 로드 밸런서: NLB / ALB

 

NLB(Network Load Balancing)

 

  • 네트워크 트래픽을 OSI 모델의 L4 에서 로드 밸런싱
  • k8s 에서 LoadBalancer 타입의 Service 생성시 프로비저닝됨
  • 초당 수백만 개의 요청 처리 가능
  • 고정 IP 등록 가능
  • IP / Port 지정으로 타겟그룹 등록 가능

 

ALB(Application Load Balancing)

 

  • 어플리케이션 트래픽을 OSI 모델의 L7 에서 로드 밸런싱
  • k8s 에서 ingress 리소스 생성시 프로비저닝됨
  • 여러 도메인, 호스트, 경로 기반의 라우팅 가능
  • 한 URL에서 다른 URL로 요청을 리디렉션 가능

 

NLB / ALB 서브넷 설정

 

  • 방법1. Service 구성시 서브넷 ID 명시
  • 방법2. private/public 서브넷에 태그 설정
    - public : kubernetes.io/cluster/cluster-name : shared, kubernetes.io/role/elb : 1
    - private : kubernetes.io/cluster/cluster-name : shared, kubernetes.io/role/internal-elb : 1

 

 

NLB 구동 테스트

 

NLB 를 사용할 계획은 아니지만 샘플이 간단해서 기록해 본다. NLB 를 통해 nginx 의 첫화면을 확인하는 샘플. 어플리케이션 정상 배포 확인하고, NLB 정상 구동 확인하고, 브라우저에서 nginx 확인하고...

 

 

1. Sample namespace

 

$ kubectl create namespace nlb-sample-app

& 해당 namespace 로 fargate 프로파일도 작성

 

 

2. nginx 어플리케이션 배포

 

$ vi sample-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nlb-sample-app
  namespace: nlb-sample-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: public.ecr.aws/nginx/nginx:1.21
          ports:
            - name: tcp
              containerPort: 80
$ kubectl apply -f sample-deployment.yaml
Deployment/nlb-sample-app created

 

 

3. NLB 생성

 

로드밸런서의 타입인 Instance 와 IP 중 Fargate 는 IP 타겟만 지정 가능하다. Instance 는 node 로, IP 는 pod 로 라우팅된다. 아래 스크립트는 선택기가 app=nginx 인 pod 로 라우팅하는 NLB 를 생성한다.

 

$ vi sample-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nlb-sample-service
  namespace: nlb-sample-app
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: external
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  type: LoadBalancer
  selector:
    app: nginx
$ kubectl apply -f sample-service.yaml
Service/nlb-sample-service created

 

 

4. 서비스 확인

 

$ kubectl get svc nlb-sample-service -n nlb-sample-app
NAME            TYPE           CLUSTER-IP         EXTERNAL-IP                                                                    PORT(S)        AGE
sample-service  LoadBalancer   10.100.240.137   k8s-nlbsampl-nlbsampl-xxxxxxxxxx-xxxxxxxxxxxxxxxx.elb.us-west-2.amazonaws.com   80:32400/TCP   16h

 

여기까지 진행하였으면, AWS 관리콘솔에서 [로드밸런서] 와 [대상 그룹] 이 정상적으로 구동되었는지 확인한다.

  • 로드밸런서 : DNS 이름 / network 유형 / internet-facing 체계
  • 대상그룹 : 80 port / TCP protocal / IP target / Health status

 

nlb

 

 

5. 결과 확인

 

상단 EXTERNAL-IP 의 DNS 주소로 curl 이나 browser 를 사용하여 nginx 첫 페이지를 확인한다.

 

$ curl k8s-nlbsampl-nlbsampl-xxxxxxxxxx-xxxxxxxxxxxxxxxx.elb.us-west-2.amazonaws.com

 

nginx

 

 

* NLB 에서 80 port 접속시 container 의 8080 port 로 라우팅 하려면...

  • Service port:80, targetPort: 8080
  • Deployment containerPort: 8080

 


WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

Google Cloud Platform 에서 쿠버네티스를 운영하기 위한 방법으로 Kubernetes Engine 과 Kubernetes API 를 사용하여 어플리케이션을 배포 및 관리 할 수 있다. 간단하게 쿠버네티스를 이용하여 어플리케이션을 테스트 해보자.


GKE(Google Kubernetes Engine) 를 사용하여 쿠버네티스 클러스터 생성

kubectl 를 사용하여 Docker 컨테이너를 배포 및 관리

쿠버네티스의 Deployment 와 Service 를 사용하여 마이크로 서비스로 어플리케이션 분리



1. 구성 연결


GCP 콘솔의 APIs & services 에서 Kubernetes Engine API 와 Container Registry API 이 활성화 되어 있는지 체크하고, Cloud Shell 이나 Cloud SDK 를 사용하여 GCP 계정, 프로젝트, zone 설정 등을 확인한다.


> gcloud config list



2. 클러스터-노드 생성


> gcloud container clusters create bootcamp --num-nodes 5 --scopes "https://www.googleapis.com/auth/projecthosting,storage-rw"


NAME      LOCATION       MASTER_VERSION  MASTER_IP     MACHINE_TYPE   NODE_VERSION   NUM_NODES  STATUS

bootcamp  us-central1-a  1.11.7-gke.12   35.202.79.78  n1-standard-1  1.11.7-gke.12  5          RUNNING


위의 명령으로 클러스터(bootcamp) 와 node 가 5개 생성된다. --scopes 인수는 나중에 사용할 프로젝트 호스팅과 Google Cloud Storage API 에 대한 액세스를 제공한다.


> kubectl get nodes

NAME                                      STATUS    ROLES     AGE       VERSION

gke-bootcamp-default-pool-654208f7-2pc5   Ready     <none>    8m        v1.11.7-gke.12

gke-bootcamp-default-pool-654208f7-4hbc   Ready     <none>    8m        v1.11.7-gke.12

gke-bootcamp-default-pool-654208f7-9wxw   Ready     <none>    8m        v1.11.7-gke.12

gke-bootcamp-default-pool-654208f7-qf9k   Ready     <none>    8m        v1.11.7-gke.12

gke-bootcamp-default-pool-654208f7-x9vz   Ready     <none>    8m        v1.11.7-gke.12


GCP 콘솔에서 확인하고 싶다면 클러스터는 [Kubernetes Engine > Cluster], 노드는 [Compute Engine > VM Instances] 에서 확인할 수 있다. 아래 예제처럼 GCP 콘솔에서 직접 클러스터를 생성할 수도 있다.






3. 어플리케이션 배포


> kubectl create deployment nginx --image=nginx:1.10.0

deployment.apps/nginx created


> kubectl get pods -o wide

NAME                     READY   STATUS    RESTARTS  IP          NODE

nginx-7d79bd4478-86289   1/1     Running   0         10.48.1.5   gke-bootcamp-default-pool-654208f7-4hbc


위 명령으로 nginx 컨테이너를 담은 파드가 생성되고 노드에 배치됐다.



4. 서비스 생성 (외부IP 노출)


> kubectl expose deployment nginx --port 80 --type LoadBalancer

service/nginx exposed


> kubectl get services

NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE

kubernetes   ClusterIP      10.51.240.1     <none>          443/TCP        18m

nginx        LoadBalancer   10.51.250.242   34.66.113.107   80:32693/TCP   1m


위 명령으로 LoadBalancer 타입의 서비스를 생성하여 nginx 컨테이너를 외부에서 접근할 수 있게 한다. 생성된 External-IP 와 port 는 서비스가 유지되는 동안만 유효하다. (External-IP 생성에 일정 시간이 소요될 수 있음)



5. pod 수 확장


> kubectl scale deployment nginx --replicas 3

deployment.extensions/nginx scaled


> kubectl get pods -o wide

NAME                     READY   STATUS    RESTARTS  IP          NODE                                      NOMINATED NODE

nginx-7d79bd4478-86289   1/1     Running   0         10.48.1.5   gke-bootcamp-default-pool-654208f7-4hbc

nginx-7d79bd4478-j6wql   1/1     Running   0         10.48.2.4   gke-bootcamp-default-pool-654208f7-9wxw

nginx-7d79bd4478-zznhd   1/1     Running   0         10.48.3.5   gke-bootcamp-default-pool-654208f7-2pc5


위 명령은 생성해 놓은 nginx 배포(deployment) 의 pod 수를 3 개로 늘린다. 마스터는 요청에 따라 적절한 노드를 찾아 2개의 파드를 각각 추가로 배치한다. 트래픽이 많거나 적을 경우 위와 같이 파드 수를 늘리거나 줄일 수 있다.



6. 어플리케이션 테스트


> curl http://<External IP>:80





7. 삭제


파드나 서비스를 더 이상 사용하지 않을 경우 아래와 같이 삭제한다.


> kubectl delete deployment nginx

deployment.extensions "nginx" deleted


> kubectl get pods

No resources found.


> kubectl delete service nginx




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

NGINX vs MPM-PREFORK vs MPM-EVENT


Ubuntu 14.04 에서 apache 2.4 MPM prefork 를 사용중이다.

prefork 방식은 안정적이고 다른 소프트웨어와 호환성이 좋다고 알려져 있다.

동시 접속에 대해서는 쓰레드 방식의 worker / event / nginx 가 좋다고 알려져 있다.

그리고 나는 이것들이 실제 지금 우리 서버에도 해당되는지 테스트를 해봤다.

더불어 현재 설치된 prefork 방식의 한계도 알아보려고 한다.

현재 서버는 AWS r1.large 이다.



Prefork setting


# vi /etc/apache2/mods-enabled/mpm_prefork.conf
<IfModule mpm_prefork_module>
        StartServers              5
        MinSpareServers           5
        MaxSpareServers          10
        MaxRequestWorkers       150
        MaxConnectionsPerChild    0
</IfModule>
cs


최초 서버 시작시 프로세스는 5개로 시작하고, 여분의 프로세스 범위는 5~10 이고 요청을 처리할 수 있는 최대 프로세스 수는 150 개로 설정되어 있다.

하나의 프로세스당 하나의 요청을 받으니까 150개의 요청을 한번에 받고나면 150개가 클라이언트에 응답을 돌려줄 때까지는 더 이상의 요청을 받을 수 없다.

응답을 빨리 한 프로세스는 또 다른 요청을 받을 수 있을 것이고... 그것은 페이지의 용량 등 여러가지 리소스에 따라 달라질 것이다.


프로세스 확인


웹서버를 시작하고 나서 apache2 프로세스를 검색하면 


18625 ?        Ss     0:01 /usr/sbin/apache2 -k start
18630 ?        S      0:01 /usr/sbin/apache2 -k start
18642 ?        S      0:01 /usr/sbin/apache2 -k start
18652 ?        S      0:01 /usr/sbin/apache2 -k start
18673 ?        S      0:00 /usr/sbin/apache2 -k start
18711 ?        S      0:00 /usr/sbin/apache2 -k start
cs


부모 프로세스인 Ss 를 제외하면 프로세스가 5개 맞다. 



Prefork 과부하 테스트


Request page size : 52kb

Number of Threads : 최대 500

Ramp-Up Period : 1

Loop Count : 1


웹 서비스 성능 테스트 툴은 JMeter 를 사용하였다. (Apache Bench 도 결과적으로는 거의 흡사했다.)

테스트 PC 의 CPU 가 100% 가 되면 요청 Thread 가 동시에 전달될 수 없으므로, 요청 Thread(동시접속)는 500 정도까지 테스트 해보았다.

요청 페이지는 DB 연결도 필요하고 CDN 으로부터 적지 않은 이미지들도 불러오는 일반 쇼핑몰 메인 페이지 수준의 페이지이다.


Thread 1~10 은 응답속도가 0.4 초 나왔다. (전혀 문제 없음)

Thread 20 은 응답속도가 0.4~0.8 초 나왔다. 평균 0.6 (거의 문제 없음)

Thread 30 은 응답속도가 0.4~1.4 초 나왔다. 평균 1.0 (이정도는 문제 없음)

Thread 40 은 응답속도가 0.4~3.0 초 나왔다. 평균 1.6 (벌써 이러면 좀 걱정 됨)

Thread 50 은 응답속도가 0.4~3.0 초 나왔다. 평균 1.9 (최대 응답 속도는 이전과 동일)

Thread 60 은 응답속도가 0.6~3.9 초 나왔다. 평균 2.4 (벌어지기 시작함)

Thread 70 은 응답속도가 0.6~4.7 초 나왔다. 평균 2.8 (...)

Thread 80 은 응답속도가 0.6~5.8 초 나왔다. 평균 3.3

Thread 90 은 응답속도가 0.9~6.4 초 나왔다. 평균 3.9

Thread 100 은 응답속도가 0.8~7.4 초 나왔다. 평균 4.4

Thread 110 은 응답속도가 0.5~7.9 초 나왔다. 평균 4.6

Thread 120 은 응답속도가 0.5~8.9 초 나왔다. 평균 5.2

Thread 130 은 응답속도가 0.5~9.9 초 나왔다. 평균 5.8

Thread 140 은 응답속도가 0.6~10.4 초 나왔다. 평균 6.3

Thread 150 은 응답속도가 0.6~11.2 초 나왔다. 평균 6.9 


--(MaxRequestWorkers 설정값이 150 이라서 그런건지 뭔지 여기까지는 에러가 없다.


Thread 160 은 응답속도가 0.6~12.2 초 나왔다. 평균 7.2

Thread 200 은 응답속도가 0.9~15.6 초 나왔다. 평균 9.3

Thread 250 은 응답속도가 0.9~19.6 초 나왔다. 평균 11.4

Thread 300 은 응답속도가 0.6~22.8 초 나왔다. 평균 13.6

Thread 350 은 응답속도가 0.8~26.2 초 나왔다. 평균 15.4 (error 5.14%)

Thread 400 은 응답속도가 0.6~29.0 초 나왔다. 평균 16.9 (error 8.50%)

Thread 500 은 응답속도가 5.4~24.2 초 나왔다. 평균 17.5 (error 35.8%)


각 테스트는 프로세스가 안정화 되고나서 테스트를 하였다.

MaxRequestWorkers 설정 값이 150 인 상태에서 52kb 짜리 테스트 페이지는 동시 요청을 300 개 정도까지는 에러 없이 응답하였다.

발생한 에러는 콘솔에 툴 에러로 나왔는데 어찌됐든 응답 받지 못했으니 대충 에러로 인식.

RAM 사용량은 thread 500 요청시 8GB 중 apahce 에서 최대 1GB 정도만을 사용했다. CPU 도 25% 정도까지만 올라갔다. (서버 리소스는 완전 널널하다는...)

사실 3초 이상 브라우저의 로딩 화면을 바라보고 있는 사람은 거의 없다.

최대 3초 까지 방문 유저가 참아 준다고 치면 현재 상태에서의 동접은 50 정도까지가 안정적인 것으로 보인다.

매우 실망스러운 수치이다.

성능 테스트를 직접 해본것은 처음이였지만 내가 알고 있던 성능 테스트 결과와는 사뭇 달랐다.

이때까지만 해도 일단 페이지 자체가 가볍지 않고, 튜닝도 전혀 하지 않은 기본 설정 상태라서 그럴것이라고 생각했다.


그렇다면 MaxRequestWorkers 값을 늘리면 응답 속도가 늘어날까? (150->256)


Thread 50 은 응답속도가 0.7~3.2 초 나왔다. 평균 2.0 (비슷)

Thread 80 은 응답속도가 0.8~6.0 초 나왔다. 평균 3.4 (비슷)

Thread 100 은 응답속도가 0.9~7.5 초 나왔다. 평균 4.3 (비슷)

Thread 120 은 응답속도가 0.9~8.9 초 나왔다. 평균 5.0 (비슷)

Thread 150 은 응답속도가 1.0~11.4 초 나왔다. 평균 6.6 (비슷)

Thread 200 은 응답속도가 0.8~15.0 초 나왔다. 평균 8.5 (비슷)

Thread 300 은 응답속도가 0.9~22.5 초 나왔다. 평균 13.3 (비슷)

Thread 350 은 응답속도가 0.9~26.1 초 나왔다. 평균 15.8 (error 11.11%)


프로세스를 150 에서 256 으로 올려도 속도면에서는 거의 차이가 없었고 에러도 동일한 구간에서 발생하기 시작했다.

그렇다면 현재 상황에서는 프로세스 수를 1024 정도로 올리더라도 응답속도나 에러는 동일할 것으로 판단되었다.

그래도 확실하게 테스트.


MaxRequestWorkers 구성은 ServerLimit 에서 지정한 값보다 클 수 없는데, serverLimit 의 기본값은 256 이다.

MaxRequestWorkers 를 256 보다 크게 설정하려면 ServerLimit 값을 256보다 크게 설정해야 한다.

ServerLimit 는 여분의 메모리에 할당되므로 이 값을 너무 크게 설정하면 시스템이나 apache 가 불안정해 질 수 있다고 한다.

ServerLimit 는 200000 까지 설정할 수 있으며, 이 보다 더 큰 값을 적용시키려면 MPM 소스 파일에 MAX_SERVER_LIMIT 값을 수정하고 apache 를 재컴파일해야 한다.


MaxRequestWorkers 256->1024


<IfModule mpm_prefork_module>
        ServerLimit            1024
        StartServers              5
        MinSpareServers           5
        MaxSpareServers          10
        MaxRequestWorkers      1024
        MaxConnectionsPerChild    0
</IfModule>
cs


Thread 50 은 응답속도가 0.5~3.4 초 나왔다. 평균 2.1 (비슷)

Thread 100 은 응답속도가 0.9~7.7 초 나왔다. 평균 4.4 (비슷)

Thread 200 은 응답속도가 0.9~15.7 초 나왔다. 평균 9.0 (비슷)

Thread 300 은 응답속도가 1.0~23.5 초 나왔다. 평균 13.6 (비슷)

Thread 350 은 응답속도가 0.9~25.3 초 나왔다. 평균 15.3 (error 6.00%)


달라진게 없다. 하하~ ...prefork...

포기할 수 없다. 마지막으로 1024->100000 까지 올려봤다.


Thread 350 은 응답속도가 1.7~26.6 초 나왔다. 평균 16.0 (error 1.43%)


확실히 알게 되었다. prefork 튜닝은 다 부질 없음을...ㅋ

어짜피 첫 페이지에 대한 동시접속만 알아보려는 것이므로 Timeout, KeepAlive 등의 테스트는 하지 않았다.



Multi threads


NGINX vs MPM-WORKER vs MPM-EVENT


프로세스 방식의 mpm-prefork, 멀티 스레드 방식의 nginx / mpm-worker / mpm-event.

prefork 의 실망감을 감추지 못한채 큰 기대감을 갖고 위 3가지에 대해 prefork 와 동일하게 테스트 해보았다.

prefork 와 동일한 환경에서의 결과는...


...

...

...

...

...


따로 적기도 짜증날 정도로 prefork / nginx / mpm-worker / mpm-event 의 결과는 비슷했다. 스레드 수를 늘려도 마찬가지.

순간 네트워크의 속도가 약간씩 영향을 주는 것을 감안할 때 무엇이 더 좋다고 딱히 말할 수 없을 정도로 큰 차이가 없었다.

또한 AWS 에서 가장 낮은 사양의 t2.micro 에서도 동일했다. 하하.

(phpinfo 정도의 가벼운 페이지는 모두 1000 개의 동시접속을 1,2 초 내에 처리했다.)



나의 결론은 이렇다.


1. Apache 를 쓰던 Nginx 를 쓰던 속도나 처리 상에 큰 속도 차이는 없다.

2. 웹서버의 사양도 속도에 큰 차이를 보이지 않는다. (서버 사양을 늘리느니 서버를 한 대 더 추가하는게 나을듯...)

3. 튜닝한다고 프로세스, 쓰레드 수를 늘려도 처리 속도는 차이가 없다.


난 결국 이미 깔려 있는, 그리고 이 중 가장 안정적이라는 prefork 를 유지하기로 마음 먹었다.


끝!



WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,