> For the complete documentation index, see [llms.txt](https://kde6260.gitbook.io/dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kde6260.gitbook.io/dev/nginx-and-wsgi-pod-service.md).

# Nginx & WSGI pod를 service로 노출하기

최초작성일 \[2020.03.18]

아래와 같은 구조로 쿠버네티스의 service를 생성하려고 한다. (replicaset 또는 deployment를 미리 생성해야 한다.)

![](/files/-M2i8be9KIiq8Sw80ffq)

### 1. WSGI pod의 service 생성하기

WSGI 프로세스는 아래의 configuration으로 작동한다. 포트번호 9090을 통해 트래픽을 받아 flask 애플리케이션으로 전달한다.&#x20;

```
[uwsgi]
wsgi-file=app/app.py
# flask app's name
callable=flask_app

# bind to the specified UNIX/TCP socket using default protocol
socket=:9090
# set internal http socket timeout
http-timeout=5

# enable master process
master = 1
# spawn the specified number of workers/processes
processes = 4

enable-threads=true

# serialize accept(), AKA Thundering Herd
# https://uwsgi-docs.readthedocs.io/en/latest/articles/SerializingAccept.html
thunder-lock=true
```

WSGI는 nginx를 통해 들어오는 트래픽만을 허용하므로 service 타입은 `ClusterIP`로 선택한다. `ClusterIP` 타입은 pod를 클러스터 내부에 한정하여 노출하려고 할 때 사용한다.

```yaml
apiVersion: v1

kind: Service

metadata:
  name: service-flask-wsgi

spec:
  type: ClusterIP
  selector:
    app: flask-wsgi
  ports:
    - name: port-flask-wsgi
      port: 9090
      targetPort: 9090
```

| attribute        | usage                                                                                                            |
| ---------------- | ---------------------------------------------------------------------------------------------------------------- |
| selector         | 외부로 노출시키려는 pod의 라벨을 식별한다. ([label selector](https://kde6260.gitbook.io/dev/k8s-replicaset#2-label-selector) 참고.) |
| ports.port       | 서비스가 생성되면 서비스는 클러스터 내부에서만 사용할 수 있는 고유한 IP를 가지게 된다. 서비스의 IP에 접근할 때 `ports.port`에 해당하는 값을 포트번호로 쓴다.                |
| ports.targetPort | selector에 정의된 라벨에 해당하는 pod들이 사용하는 포트번호. replicaset 템플릿에서 지정한`containerPort`의 값과 같아야 한다.                          |

위에서 작성한 템플릿 `service-app.yml` 을 클러스터에 적용한다.

```
$ kubectl apply -f service-app.yml
service/service-flask-wsgi created

$ kubectl get service
NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes           ClusterIP   100.64.0.1       <none>        443/TCP          4d23h
service-flask-wsgi   ClusterIP   100.66.110.185   <none>        9090/TCP         121m
```

`metadata.name`의 값에 맞게 `service-flask-wsgi`라는 서비스가 생성되었다. `CLUSTER-IP` 필드의 IP주소는 클러스터 내부에서 해당 서비스에 접근하는 용도로 쓰인다. 즉, 클러스터 외부에서는 `CLUSTER-IP`필드의 주소로 접근할 수 없다.

`kubernetes`라는 서비스가 이미 있는 것을 볼 수 있다. `kubernetes`서비스는 pod 내부에서 쿠버네티스의 API에 접근하기 위한 용도로 쓰인다.

### 2. Nginx pod의 service 생성하기

`nginx.conf`는 아래와 같다. Nginx가 업스트리밍하는 서버의 주소가 위에서 생성한 서비스의 이름에 해당하 `service-flask-wsgi`인 것을 알 수 있다.  쿠버네티스 애플리케이션에는 service나 pod와 같은 리소스를 쉽게 찾을 수 있도록 리소스의 이름을 레코드로 관리하는 내부 DNS가 있다. 그리고 pod들은 이 DNS에 주소를 질의하기 때문에 service의 이름을 주소로 사용할 수 있다.

```
user nginx;
worker_processes auto;
...
http {
    ...
    upstream flask_server {
        server service-flask-wsgi:9090;
        keepalive 512;
        keepalive_timeout 5;
    }
    server {
        listen 80;
        server_name service-flask-wsgi;

        location / {
            include /etc/nginx/uwsgi_params;
            uwsgi_pass flask_server;
        }
    }
}
```

Nginx pod를 클러스터 외부에 노출하기 위해 아래와 같은 템플릿을 작성한다. `NodePort` 타입은 모든 노드의 특정 포트를 개방하여 외부에서 서비스에 접근하도록 허용한다. 아래의 service는 모든 노드에 포트번호 80번을 개방한다.

```yaml
apiVersion: v1

kind: Service

metadata:
  name: service-my-nginx

spec:
  type: NodePort
  selector:
    app: my-nginx
  ports:
    - name: port-my-nginx
      port: 8080
      targetPort: 80
```

위에서 작성한 템플릿 `service-nginx.yml`을 클러스터에 생성한다.

```
$ kubectl apply -f service-nginx.yml
service/service-my-nginx created

$ kubectl get services
NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes           ClusterIP   100.64.0.1       <none>        443/TCP          4d22h
service-flask-wsgi   ClusterIP   100.66.110.185   <none>        9090/TCP         60m
service-my-nginx     NodePort    100.68.131.17    <none>        8080:31085/TCP   4s
```

`service-my-nginx`라는 이름으로 service가 생성된 것을 확인할 수 있다. service 타입이 `NodePort`였음에도 `CLUSTER-IP`가 만들어진 것을 볼 수 있는데, `NodePort` 타입의 service가 `ClusterIP` 타입의 기능을 포함하기 때문이다. 따라서 `NodePort` 타입으로 생성한 서비스는&#x20;

* 클러스터 내부에서 `CLUSTER-IP` 또는 service 이름을 통해 접근할 수 있고
* 클러스터 외부에서 노드의 IP를 통해 접근할 수 있다.

`PORT(S)`의 값은 `<클러스터 내부에서 접근할 때 쓰는 포트번호>:<클러스터 외부에서 접근할 때 쓰는 포트번호>`로 되어있다.&#x20;

클러스터 내부에서 `service-my-nginx` 에 접근해보자. 클러스터 내부의 워커 노드 중 하나에 접속하여 `curl`을 실행했다. IP주소는 `CLUSTER-IP`에 해당하는 `100.68.131.17`이고 포트번호 `8080`으로 접근한다.

```
admin@ip-172-20-40-1:~$ curl 100.68.131.17:8080
Hello, This is DaEun Kim.
```

이번에는 클러스터 외부에서 `service-my-nginx`에 접근한다. 워커 노드의 퍼블릭IP주소와 함께 포트번호는 `31085`으로 접근한다. 내/외부 IP주소를 포함한 노드의 정보를 조회하고 싶다면 아래의 커맨드를 실행한다.&#x20;

```
$ kubectl get nodes -o wide  
```

&#x20;위 커맨드에서 조회한 노드의 퍼블릭 IP주소로 curl을 실행한다.

```
$ curl 13.125.164.232:31085
Hello, This is DaEun Kim. 
```

&#x20;

참고

* [Nginx Upstream 성능최적화](https://brunch.co.kr/@alden/11)
* [Nginx config 정리](http://bong8nim.com/post/programming/etc/nginx-config-manual/)
* [Nginx full example](https://www.nginx.com/resources/wiki/start/topics/examples/full/)
* [시작하세요! 도커/쿠버네티스](https://search.kyobobook.co.kr/web/search?vPstrKeyWord=%25EC%258B%259C%25EC%259E%2591%25ED%2595%2598%25EC%2584%25B8%25EC%259A%2594%21%2520%25EB%258F%2584%25EC%25BB%25A4%252F%25EC%25BF%25A0%25EB%25B2%2584%25EB%2584%25A4%25ED%258B%25B0%25EC%258A%25A4\&orderClick=LEA)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://kde6260.gitbook.io/dev/nginx-and-wsgi-pod-service.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
