개요

Istio Ingress Gateway는 Istio에서 외부 요청을 내부 시스템으로 전달하는 역할을 하는 구성 요소다. 일반적으로 Kubernetes에서 사용되는 Ingress 리소스와 유사한 개념이지만, Istio에서는 더 많은 기능과 flexibility를 제공한다. 

 

- 클러스터의 관문 역할

- outside → inside traffic의 인입 처리 담당

- Security 가 중요함.

- 인입을 위한 룰 매칭 등을 처리

- 인입 후에는 inside 서비스로 라우팅도 담당

 

약어는 다음과 같다.

 

gw : gateway

vs : virtual service

VIP : virtual IP

 

나는 minikube를 사용하기에 tunnel을 이용해서 외부 진입점을 생성해야 한다. 그러면 {EXTERNAL-IP}:{PORT}로 접근이 가능하다. 

 

나의 경우 127.0.0.1로 여러 개의 포트가 떠있다.

 

namespace istio-system에 deploy가 떠있으므로 아래의 명령어를 척 보면 pilot-agent, envoy가 떠있다. 

kubectl -n istio-system exec \ deploy/istio-ingressgateway -- ps

 

kubectl describe svc istio-ingressgateway -n istio-system

 

이제 service를 살펴보자. 

 

client -> NodePort -> Port -> Target Port 순으로 생각해 주면 되는데 예를 들어서 http2를 보자.

NodePort: 30238이기에 127.0.0.1:30238로 요청하면 Port인 80으로 가고 그게 Target Port인 8080으로 전달되는 구조다. 

 

리스터, 라우트

Envoy 프록시에서 설정된 리스너(listener) 구성 정보를 조회하는 데 사용된다. 

istioctl proxy-config listener deploy/istio-ingressgateway -n istio-system
ADDRESSES PORT MATCH DESTINATION
0.0.0.0 8443 ALL Route: https.443.https.test-istio-gateway.default
0.0.0.0 15021 ALL Inline Route: /healthz/ready* // 헬스 체크
0.0.0.0 15090 ALL Inline Route: /stats/prometheus* // 지표 수집

 

 

Istio의 Envoy 프락시에서 정의된 라우트(route) 구성을 보여주는 데 사용된다. 

istioctl proxy-config route deploy/istio-ingressgateway -n istio-system
NAME VHOST NAME DOMAINS MATCH VIRTUAL SERVICE
https.443.https.test-istio-gateway.default *:443 * /test* test-istio.default
backend * /stats/prometheus*
backend * /healthz/ready*

 

Gateway에 명세를 하나 추가해 보자.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: test-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "test.io"

 

이제 리스너와 라우트를 확인해 보면 다음과 같다. 

/리스너
ADDRESSES PORT MATCH DESTINATION
0.0.0.0 8080 ALL Route: http.8080
0.0.0.0 8443 ALL Route: https.443.https.test-istio-gateway.default
0.0.0.0 15021 ALL Inline Route: /healthz/ready*
0.0.0.0 15090 ALL Inline Route: /stats/prometheus*
/라우트
NAME VHOST NAME DOMAINS MATCH VIRTUAL SERVICE
https.443.https.test-istio-gateway.default *:443 * /test* test-istio.default
http.8080 blackhole:80 * /* 404
backend * /stats/prometheus*
backend * /healthz/ready*

 

라우트에서는 vs를 찾을 수 없어서 404를 나타내는 것을 볼 수 있다. vs를 추가한 후에 살펴보자.

 

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: test-gw
spec:
hosts:
- "test.io"
gateways:
- test-gateway
http:
- route:
- destination:
host: server-service
port:
number: 80

 

그 후에 라우트를 확인해 보면 vs가 정상 동작하는 것을 볼 수 있다.

NAME VHOST NAME DOMAINS MATCH VIRTUAL SERVICE
https.443.https.test-istio-gateway.default *:443 * /test* test-istio.default
http.8080 test.io:80 test.io /* test-gw.default
backend * /stats/prometheus*
backend * /healthz/ready*

 

HTTPS 통신하기

우선 private_key.pem과 certificate.pem를 만들자. 

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl req -new -key private_key.pem -out csr.pem // 마음대로 입력하지만 common name은 위에서 설정한 domain
openssl x509 -req -in csr.pem -signkey private_key.pem -out certificate.pem -days 365

 

kubectl get secret -n istio-system
NAME TYPE DATA AGE
test-cred kubernetes.io/tls 2 11s

 

이제 이전에 생성한 gateway에 부착해 보자.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: test-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "test.io"
tls:
httpsRedirect: true // https로 리다이렉트
- port: // 여기서부터 추가
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: test-cred
hosts:
- "test.io"

 

이제 호출하면 아래와 같이 오류가 뜬다. 

 

curl -v -H "Host: test.io" https://127.0.0.1/api/test
* Trying 127.0.0.1:443...
* Connected to localhost (127.0.0.1) port 443 (#0)
* ALPN: offers h2
* ALPN: offers http/1.1
* CAfile: /etc/ssl/cert.pem
* CApath: none
* (304) (OUT), TLS handshake, Client hello (1):
* LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to 127.0.0.1:443
* Closing connection 0
curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to 127.0.0.1:443

 

이거는 사설 인증서가 없기 때문에 발생하는 오류다. certificate.pem를 체인으로 추가해 주자.

curl -v -H "Host: test.io" https://127.0.0.1:443/api/test \
--cacert certificate.pem
* Trying 127.0.0.1:443...
* Connected to 127.0.0.1 (127.0.0.1) port 443 (#0)
* ALPN: offers h2
* ALPN: offers http/1.1
* CAfile: certificate.pem
* CApath: none
* (304) (OUT), TLS handshake, Client hello (1):
* LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to 127.0.0.1:443
* Closing connection 0
curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to 127.0.0.1:443

 

test.io 도메인이 아닌 127.0.0.1으로 호출해서 문제가 발생한다. resolve를 강제로 추가해 주자.

 

curl -v https://test.io/api/test \
--cacert certificate.pem \
--resolve test.io:443:127.0.0.1
## 중략
* Connection #0 to host test.io left intact
test

 

이제 위의 과정을 각각의 원하는 Virtual Service에 적용해 주면 된다. 

 

이렇게 간단하게 gateway와 virtual service를 생성하고 부착, HTTPS 설정까지 해봤다 다음에는 Plan TCP와 gateway를 분리하는 방법에 대해서 알아보려고 한다.