[도커/쿠버네티스] 8장_인그레스

ingress 는 일반적으로 외부에서 내부로 향하는 것을 지칭한다.
쿠버네티스 인그레스는, 외부 요청을 어떻게 처리할 것인지 네트워크 7 계층 레벨에서 정의하는 오브젝트이다.

사용 이유

어플리케이션이 3 개의 디플로이먼트로 생성되어 있다고 하자.
디플로이먼트를 외부에 노출하려면 위 그림 처럼,
NodePort or LoadBalancer 타입의 서비스 3 개를 각각 디플로이먼트에 연결해주면 된다.
그런데, 이 방식은 서비스마다 세부적인 설정을 할 때 추가적인 복잡성이 발생한다.
예를 들면, 아래와 같은 내용을 구현하려면, 서비스와 디플로이먼트에 대해 일일이 설정해야한다.

  1. SSL / TLS 보안 연결
  2. 접근 도메인 및 클라이언트 상태에 기반한 라우팅

그래서 인그레스가 필요하다.
인그레스 오브젝트를 이용하면, URL 엔드포인트를 단 하나만 생성해서 이런 번거로움을 해결할 수 있다.

위 그림처럼, 3 개의 서비스에 대해 3 개 URL 이 각각 존재하는 것이 아니라
인그레스에 접근하기 위한 하나의 URL 만 존재한다.
핵심은, 라우팅 정의나 보안 연결 같은 세부 설정은 서비스와 디플로이먼트가 이니라 인그레스에 의해 수행

구조

ingress-example.yaml 파일을 작성하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-example
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: jko.example.com # 이 도메인으로 접근하는 요청에 대한 처리 규칙 정의
http:
paths:
- path: /echo-hostname # 이 경로로 들어온 요청을 backend 에 정의한 서비스로 전달
backend:
serviceName: hostname-service # 위 path 로 들어온 요청이 전달될 서비스의 이름
servicePort: 80

그리고, 인그레스 오브젝트를 생성하자.

1
kubuctl apply -f ingress-example.yaml

하지만, 이것만으로 아무일도 일어나지 않는다.
인그레스는 요청 처리 규칙을 정의한 선언적인 오브젝트일 뿐, 외부 요청을 받는 서버가 아니기 때문이다.
그래서, 인그레스 컨트롤러라는 특수한 서버에 이 규칙을 적용해서 생성해야한다.
인그레스 컨트롤러의 종류로는 다음 것들이 있다.

  1. Kong API Gateway : https://konghq.com/kong
  2. GKE (Google Kubernetes Engine) : https://cloud.google.com/kubernetes-engine
  3. Nginx 웹서버 인그레스 컨트롤러 : https://github.com/kubernetes/ingress-nginx

아래와 같이,
쿠버네티스에서 공식적으로 개발되고 있는 Nginx 웹서버 인그레스 컨트롤러를 설치하자.

1
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.41.2/deploy/static/provider/aws/deploy.yaml

설치하면,

  1. ingress-nginx 네임스페이스에 디플로이먼트와 포드를 확인하면, Nginx 웹 서버가 생성되어 있다.
  2. 그리고, 외부에서 Nginx 인그레스 컨트롤러에 접근하기 위한 서비스도 생성되어 있다.

이제, 인그레스의 종착점이 될 테스트용 디플로이먼트와 서비스를 아래와 같이 생성해보자.
Nginx 인그레스 컨트롤러로 들어오는 요청은 이 디플로이먼트의 포드들로 분산될 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
name: hostname-deployment
spec:
replicas: 3
selector:
matchLabels:
app: webserver
template:
metadata:
name: my-webserver
labels:
app: webserver
spec:
containers:
- name: my-webserver
image: alicek106/ingress-annotation-test:0.0
ports:
- containerPort: 5000
name: flask-port
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: hostname-service
spec:
ports:
- name: web-port
port: 80
targetPort: flask-port
selector:
app: webserver
type: ClusterIP
1
2
kubuctl apply -f hostname-deployment.yaml
kubuctl apply -f hostname-service.yaml

이제, Nginx 인그레스 컨트롤러의 /echo-hostname 으로 요청을 전송해보자.
AWS 를 사용하고 있기 때문에, DNS 이름으로 요청한다.

1
curl a20...3.elb.amazonaws.com/echo-hostname

404 Not Found 에러가 반환될 것이다. 왜그럴까 ?
Nginx 인그레스 컨트롤러가 jko.example.com 으로 접근할 때만 응답을 처리하도록 설정해서이다.
위에서 작성한 ingress-example.yaml 파일을 수정해보자.

1
2
3
4
5
6
7
kubectl edit ingress ingress-example

...
spec:
rules:
- host: a20...3.elb.amazonaws.com # 이 도메인으로 접근하는 요청에 대한 처리 규칙 정의
...

다시 아래와같이 요청하면, Nginx 인그레스 컨트롤러가 해당 요청을 처리할 것이다.

1
curl a20...3.elb.amazonaws.com/echo-hostname

인그레스 컨트롤러 동작 원리

지금까지의 과정은 위와 같다. 정리하면,

  1. 공식 github 에서 제공하는 YAML file 로 Nginx 인그레스 컨트롤러 생성
  2. Nginx 인그레스 컨트롤러를 외부로 노출하기 위한 서비스 생성
  3. 요청 처리 규칙을 정의하는 인그레스 오브젝트 생성
  4. Nginx 인그레스 컨트롤러로 들어온 요청은 인그레스 규칙에 따라 적절한 서비스로 전달

인그레스 리소스의 상태

그런데 위의 3번에서 인그레스 오브젝트를 생성만 하면,
인그레스 컨트롤러는 자동으로 인그레스를 로드해서 Nginx 웹 서버에 적용한다.
어떻게 이게 가능할까 ?
Nginx 인그레스 컨트롤러는 항상 인그레스 리소스의 상태를 지켜보고 있기 때문이다.

bypass

특정 경로와 호스트 이름으로 유입된 요청은 인그레스에 의해 정의된 규칙에 따라 서비스로 전달된다.
위에서 정의한, ingress-example.yaml 파일을 다시 보면,

1
2
3
4
5
6
7
8
9
10
...
spec:
rules:
- host: jko.example.com # 이 도메인으로 접근하는 요청에 대한 처리 규칙 정의
http:
paths:
- path: /echo-hostname # 이 경로로 들어온 요청을 backend 에 정의한 서비스로 전달
backend:
serviceName: hostname-service # 위 path 로 들어온 요청이 전달될 서비스의 이름
servicePort: 80

/echo-hostname 으로 유입된 요청이 hostname-service 라는 서비스로 전달되도록 설정되어 있다.
하지만 실제로는, 요청이 실제로 hostname-service 로 전달되지 않고
Nginx 인그레스 컨트롤러는 서비스에 의해 생성된 엔드 포인트로 요청을 직접 전달한다.
이러한 동작을 bypass 라고 한다.


시작하세요! 도커/쿠버네티스 <용찬호>

Comments