[Kubernetes] ๐ gRPC๊ฐ .NET์์๋ง ์ ๋๋ค: HTTP/2ยทTLSยทscheme ๋ฌธ์ ํด๊ฒฐ
์ฟ ๋ฒ๋คํฐ์ค ๊ฒ์ดํธ์จ์ด ๋ค์์ .NET gRPC๋ง ์ฐ๊ฒฐ์ด ๋๊ธฐ๋ ๋ฌธ์ ๋ฅผ HTTP/2(h2c)์ Kestrel์ AllowAlternateSchemes ์ค์ ์ผ๋ก ํด๊ฒฐํ๋ ๊ณผ์ ์ ์ ๋ฆฌํ์ต๋๋ค.
์ฟ ๋ฒ๋คํฐ์ค์ gRPC ์๋น์ค๋ฅผ ์ฌ๋ ธ๋๋ฐ Python ์๋ฒ๋ ๋ถ๊ณ .NET(Kestrel) ์๋ฒ๋ง ๊ณ์ ๋๊ธฐ๋ ๋ฌธ์ ๋ฅผ ๊ฒช์์ต๋๋ค. ์ด ๊ธ์์๋ ๊ทธ ์์ธ์ด โ ๊ฒ์ดํธ์จ์ด๊ฐ ๋ฐฑ์๋๋ก HTTP/1.1์ ๋ณด๋ด๋ ๋ฌธ์ ์ โก TLS ์ข
๋ฃ ํ์๋ :scheme=https ๋ผ๋ฒจ์ด ๋จ์ Kestrel์ด ๊ฑฐ๋ถํ๋ ๋ฌธ์ , ๋ ๊ฐ์ ์๋ก ๋ค๋ฅธ ๊ณ์ธต์ ์์์์ ์ถ์ ํ๊ณ h2c + AllowAlternateSchemes๋ก ํด๊ฒฐํ๋ ๊ณผ์ ์ ๋จ๊ณ๋ณ๋ก ๋ค๋ฃน๋๋ค.
์ด ์๋ฆฌ์ฆ๋ ์ฟ ๋ฒ๋คํฐ์ค์ gRPC ์๋น์ค๋ฅผ ์ฌ๋ฆฌ๋ค ๋งํ ์ฝ์ง์์ ์ถ๋ฐํด, HTTP/2ยทTLSยท๋ก๋๋ฐธ๋ฐ์ยทGateway APIยท์ธ์ฆ์๊น์ง ํ๋์ฉ ํ์ด๊ฐ๋ ๊ธฐ๋ก์ ๋๋ค. 1ํธ(์ด ๊ธ)์ ๋ฌธ์ ์ ํด๊ฒฐ์ ๋๋ค.
๐จ ์ฆ์: ๊ฐ์ gRPC์ธ๋ฐ ํ์ชฝ๋ง ์ ๋๋ค
์ฟ ๋ฒ๋คํฐ์ค์ gRPC ์๋น์ค๋ฅผ ์ฌ๋ ธ์ต๋๋ค. ๊ฒ์ดํธ์จ์ด ์ค์ ์ ๋์ผํ๋ฐ ๊ฒฐ๊ณผ๊ฐ ๊ฐ๋ ธ์ต๋๋ค.
| ๋ฐฑ์๋ | ํฌํธ 80 (ํ๋ฌธ) | ํฌํธ 443 (TLS) |
|---|---|---|
| Python gRPC ์๋ฒ | โ ์ ์ | โ ์ ์ |
| .NET(Kestrel) gRPC ์๋ฒ | โ ์ ์ | โ ์ฐ๊ฒฐ ๋๊น |
Kestrel ๋ก๊ทธ์๋ ๋ค์๊ณผ ๊ฐ์ ์๋ฌ๊ฐ ์ฐํ์ต๋๋ค.
1
The request :scheme header 'https' does not match the transport scheme 'http'.
์ฒ์ ๋ ์๊ฐ์ โHTTP/2๊ฐ ๋ฌธ์ ๋๊น HTTP/2๋ฅผ ํผํด๋ณด์โ์์ต๋๋ค. ๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด ๊ทธ๊ฑด ๋ถ๊ฐ๋ฅํฉ๋๋ค. gRPC๋ HTTP/2 ์์์๋ง ๋์ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. HTTP/2๋ฅผ ๋นผ๋ ์๊ฐ ๊ทธ๊ฑด ๋ ์ด์ gRPC๊ฐ ์๋๋๋ค.
๊ทธ๋์ ๋ฐฉํฅ์ ๋ฐ๊ฟจ์ต๋๋ค. โํผํ์โ๊ฐ ์๋๋ผ โ์ด๋์, ์ ๊นจ์ง๋์งโ๋ฅผ ์ ํํ ์ฐพ์๋ก์.
๐ ํต์ฌ ์ ์ ๋ ๊ฐ์ง
๋ณธ๊ฒฉ์ ์ผ๋ก ๋ค์ด๊ฐ๊ธฐ ์ ์ ๋ ๊ฐ์ง๋ง ๊น๊ณ ๊ฐ๋๋ค. (์์ธํ ๊ฐ๋ ์ ๋ค์ ํธ์์ ๋ค๋ฃน๋๋ค.)
- gRPC = HTTP/2. ์ฒ์๋ถํฐ ๋๊น์ง HTTP/2๋ฅผ ์๊ตฌํฉ๋๋ค.
- โํต์ ๋ฐฉ์(HTTP ๋ฒ์ )โ๊ณผ โ์ํธํ(TLS)โ๋ ๋ณ๊ฐ์ ๋๋ค. ์ด๊ฒ ๋ชจ๋ ํผ๋์ ๊ทผ์์ด์์ต๋๋ค.
ํนํ ํท๊ฐ๋ ธ๋ ๊ฑด h2c๋ผ๋ ๋จ์ด์์ต๋๋ค.
h2์ h2c๋ ๋ฌด์์ธ๊ฐ?
h2๋ TLS ์์์ ๋์ํ๋ HTTP/2, h2c๋ ์ํธํ ์์ด(ํ๋ฌธ) ๋์ํ๋ HTTP/2๋ฅผ ๊ฐ๋ฆฌํต๋๋ค(c = cleartext). โHTTP/2์ธ๋ฐ ํ๋ฌธโ์ด๋ผ๋ ๋ง์ด ๋ชจ์์ฒ๋ผ ๋ค๋ฆฌ์ง๋ง, HTTP/2์ TLS๊ฐ ๋ณ๊ฐ์ ๊ณ์ธต์ด๋ผ๋ ๊ฒ์ ์๊ณ ๋๋ฉด ์์ฐ์ค๋ฝ์ต๋๋ค.
| ํ๊ธฐ | ํ๋กํ ์ฝ | ์ํธํ | ์ผ๋ฐ์ ์ธ ํฌํธ |
|---|---|---|---|
h2 | HTTP/2 | TLS ์์ | 443 |
h2c | HTTP/2 | ํ๋ฌธ | 80 |
๐ ์์ธ โ โ ๊ฒ์ดํธ์จ์ด๊ฐ HTTP/1.1๋ก ๋ง์ ๊ฑธ๊ณ ์์๋ค
์ฒซ ๋ฒ์งธ ์์ธ์ ๊ฒ์ดํธ์จ์ด๊ฐ ๋ฐฑ์๋๋ก HTTP/1.1์ ๋ณด๋ด๊ณ ์์๋ ๊ฒ์ ๋๋ค. Kestrel์ HTTP/2 ์ ์ฉ์ผ๋ก ์ค์ ํด ๋๋ฉด ๊ทธ ํฌํธ๋ HTTP/2๋ง ์์๋ฃ์ต๋๋ค. ๊ทธ๋ฐ๋ฐ ๊ฒ์ดํธ์จ์ด๋ ๋ณ๋ ์ง์ ์ด ์์ผ๋ฉด ๋ฐฑ์๋๋ก HTTP/1.1์ ๋ณด๋ ๋๋ค. โ์ธ์ดโ๊ฐ ์ ๋ง์ผ๋ Kestrel์ด ๊ฑฐ๋ถํฉ๋๋ค.
ํด๊ฒฐ โ -a: ๊ฒ์ดํธ์จ์ด๊ฐ h2c๋ก ๋ณด๋ด๊ฒ ํ๋ค
Service์ appProtocol์ ๋ช
์ํ๋ฉด, ๊ฒ์ดํธ์จ์ด๊ฐ ๋ฐฑ์๋๋ก ํ๋ฌธ HTTP/2๋ฅผ ๋ณด๋ด์ผ ํ๋ค๋ ๊ฒ์ ์๊ฒ ๋ฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
# Service
apiVersion: v1
kind: Service
metadata:
name: my-grpc-svc
spec:
ports:
- name: grpc
port: 8080
targetPort: 8080
appProtocol: kubernetes.io/h2c # ํ๋ฌธ HTTP/2๋ก ์ ๋ฌํ๋ผ
Contour๋ฅผ ์ฌ์ฉํ๋ค๋ฉด HTTPProxy์์ ๋ฐฑ์๋ ํ๋กํ ์ฝ์ ์ง์ ์ง์ ํ ์๋ ์์ต๋๋ค.
1
2
3
4
5
6
7
# Contour HTTPProxy
spec:
routes:
- services:
- name: my-grpc-svc
port: 8080
protocol: h2c
ํด๊ฒฐ โ -b: Kestrel์ HTTP/2 ์ ์ฉ์ผ๋ก ๋๋ค
ํ๋ฌธ ํฌํธ์๋ ํ๋กํ ์ฝ ์๋ ํ์(ALPN)์ด ์์ด์, Http1AndHttp2๋ก ๋๋ฉด ํ์ ๋จ๊ณ ์์ด HTTP/1.1๋ก ๋จ์ด์ง๋๋ค. ๋ฐ๋ผ์ ํ๋ฌธ gRPC ํฌํธ๋ Http2๋ก ๊ณ ์ ํด์ผ ํฉ๋๋ค.
1
2
3
4
5
6
7
8
// appsettings.json
{
"Kestrel": {
"EndpointDefaults": {
"Protocols": "Http2"
}
}
}
โ ๏ธ
Http1AndHttp2๋ TLS(ALPN)๊ฐ ์์ ๋๋ง HTTP/2๋ก ํ์๋ฉ๋๋ค. TLS ์ข ๋ฃ๊ฐ ๊ฒ์ดํธ์จ์ด์์ ์ผ์ด๋๋ ์ฟ ๋ฒ๋คํฐ์ค ํ๊ฒฝ์์๋ ํ๋ฌธ ํฌํธ์ด๋ฏ๋กHttp2๋ก ๋ช ์ํด์ผ ํฉ๋๋ค.
๐ ์์ธ โก โ scheme ๋ผ๋ฒจ ๋ถ์ผ์น (.NET๋ง ๋งํ๋ ์ง์ง ์ด์ )
๋ ๋ฒ์งธ ์์ธ์ด์ .NET๋ง ๋งํ ์ง์ง ์ด์ ๋ :scheme ๋ผ๋ฒจ๊ณผ ์ค์ ์ ์ก ๋ฐฉ์์ ๋ถ์ผ์น์
๋๋ค. ๊ฒ์ดํธ์จ์ด๊ฐ TLS๋ฅผ ์ข
๋ฃ(๋ณตํธํ)ํด ํ๋ฌธ์ผ๋ก ๋ฐ๊ฟ ๋ณด๋ด๋ฉด์๋, ์์ฒญ์ ๋ถ์ :scheme ๊ฐ์ ์๋๋๋ก https ๋ก ๋จ๊ฒจ ๋ก๋๋ค. ๊ทธ๋ฐ๋ฐ Kestrel์ ๋์ฐฉํ ์ฐ๊ฒฐ์ ํ๋ฌธ์
๋๋ค. Kestrel์ โscheme์ https๋ผ๋๋ฐ ์ฐ๊ฒฐ์ ํ๋ฌธ์ด๋ค? ์ ๋ง์์โ๋ผ๋ฉฐ ๊ฑฐ๋ถํฉ๋๋ค. ์ด๊ฒ ๋ก๊ทธ์ ์ฐํ ๊ทธ ์๋ฌ์ ์ ์ฒด์์ต๋๋ค.
1
The request :scheme header 'https' does not match the transport scheme 'http'.
Python gRPC ์๋ฒ๋ ์ด scheme ๊ฒ์ฆ์ ํ์ง ์์์ ๊ทธ๋ฅ ํต๊ณผํ๊ณ , Kestrel๋ง ์๊ฒฉํ๊ฒ ๊ฒ์ฌํ๋ ๊ฒ์ ๋๋ค.
ํด๊ฒฐ โก: Kestrel์๊ฒ ๊ทธ scheme์ ํ์ฉํ๋ผ๊ณ ์๋ ค์ค๋ค
AllowAlternateSchemes ์ต์
์ ์ผ๋ฉด Kestrel์ด :scheme๊ณผ ์ ์ก ๋ฐฉ์์ด ๋ฌ๋ผ๋ ํ์ฉํฉ๋๋ค.
1
2
3
4
builder.WebHost.ConfigureKestrel(options =>
{
options.AllowAlternateSchemes = true;
});
๋ค์ผ๋ก ์ด ์ต์
์ ์ผ๋ฉด HttpRequest.Scheme์ด ์๋ ๊ฐ(https)์ผ๋ก ์กํ, ์ฑ์ด ๋ฆฌ๋ค์ด๋ ํธยท์ ๋๊ฒฝ๋ก URL์ ๋ง๋ค ๋๋ ์ฌ๋ฐ๋ฅด๊ฒ ๋์ํฉ๋๋ค.
Tip:
AllowAlternateSchemes๋ .NET 6 ์ด์์์ ์ฌ์ฉํ ์ ์์ต๋๋ค. scheme ๊ฐ ์์ฒด๋ ์ฌ์ ํ ์ ํจํ ํ์์ด์ด์ผ ํฉ๋๋ค.
๐งฉ ๋ ์ค์ ์ โ์ค๋ณตโ์ด ์๋๋ผ โ์๋ก ๋ค๋ฅธ ์ธตโ
์ฒ์์ โ๋ ๋ค h2c ๊ด๋ จ ์๋๊ฐ?โ ์ถ์๋๋ฐ, ์ญํ ์ด ์์ ํ ๋ค๋ฆ ๋๋ค.
| ์ค์ | ๋ด๋นํ๋ ๊ณ์ธต | ์๋ฏธ |
|---|---|---|
Service์ appProtocol: h2c | ํต์ ๋ฐฉ์(์ธ์ด) | โHTTP/2๋ก ๋ณด๋ด๋ผโ |
Kestrel AllowAlternateSchemes | ํค๋(๋ผ๋ฒจ) ๊ฒ์ฆ | โhttps ๋ผ๋ฒจ์ ํ๋ฌธ ์์์๋ ํ์ฉํด๋ผโ |
๊ทธ๋์ ํ๋๋ง ๋นผ๋, ๋ ๋ค ๋นผ๋ ํต์ ์ด ์ ๋ฉ๋๋ค. ๋ ๋ค ๋ง์ถฐ์ผ ํฉ๋๋ค.
โ ์ฆ์์ด ์ ๋ถ ์ค๋ช ๋๋ค
๋ ์์ธ์ ์๊ณ ๋๋ฉด ์ฒ์์ ์ฆ์ ํ๊ฐ ๋ชจ๋ ์ค๋ช ๋ฉ๋๋ค.
์ 80์ ๋๊ณ 443์ ์ ๋๋? 80(ํ๋ฌธ)์ผ๋ก ๋ค์ด์ค๋ฉด ๊ฒ์ดํธ์จ์ด๊ฐ scheme
http๋ก ์ ๋ฌ โ ์ค์ ๋ ํ๋ฌธ โ ์ผ์น โ OK. 443(TLS)์ schemehttps๋ฅผ ํ๋ฌธ ์๋ก ์ ๋ฌ โ ๋ถ์ผ์น โ ์คํจ.์ Python์ ๋๊ณ .NET์ ์ ๋๋? Python ์๋ฒ๋ scheme ๋ผ๋ฒจ์ ๊ฒ์ฆํ์ง ์์๊ณ , Kestrel๋ง ์๊ฒฉํ๊ฒ ๊ฒ์ฌํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
๐ ํ ์ค ์์ฝ
.NET gRPC๊ฐ ๊ฒ์ดํธ์จ์ด ๋ค์์ ์ ๋๋ ๊ฑด ๋ ๊ฐ์ง์์ต๋๋ค.
- ๊ฒ์ดํธ์จ์ด๊ฐ HTTP/1.1๋ก ๋ง์ ๊ฑธ์ด์ โ Service
appProtocol: h2c+ KestrelProtocols: Http2 - TLS๋ฅผ ํผ ๋ค์๋
:scheme=https๋ผ๋ฒจ์ด ๋จ์ Kestrel์ด ๊ฑฐ๋ถํด์ โAllowAlternateSchemes = true
๋์ ๋ค๋ฅธ ์ธต์ ๋ฌธ์ ๋ผ ๋ ๋ค ๋ง์ถฐ์ผ ํฉ๋๋ค.
โ ์์ฃผ ๋ฌป๋ ์ง๋ฌธ
Q. gRPC๋ฅผ HTTP/1.1๋ก ์ธ ์๋ ์๋์?
์์ต๋๋ค. gRPC๋ ์คํธ๋ฆฌ๋ฐ๊ณผ ๋ฉํฐํ๋ ์ฑ์ ์ํด HTTP/2๋ฅผ ์ ์ ๋ก ์ค๊ณ๋์ด, HTTP/2๋ฅผ ๋นผ๋ฉด ๋ ์ด์ gRPC๊ฐ ์๋๋๋ค.
Q. h2์ h2c์ ์ฐจ์ด๋ ๋ฌด์์ธ๊ฐ์?
๋ ๋ค HTTP/2์
๋๋ค. h2๋ TLS ์์์(์ํธํ), h2c๋ ํ๋ฌธ์์ ๋์ํฉ๋๋ค. ์ฟ ๋ฒ๋คํฐ์ค์์ ๊ฒ์ดํธ์จ์ด๊ฐ TLS๋ฅผ ์ข
๋ฃํ๋ฉด ๋ฐฑ์๋๋ก๋ h2c(ํ๋ฌธ HTTP/2)๊ฐ ์ ๋ฌ๋ฉ๋๋ค.
Q. Kestrel์ Http1AndHttp2๋ก ๋๋ฉด ์ ๋๋์?
ํ๋ฌธ ํฌํธ์์๋ ALPN ํ์์ด ์์ด HTTP/1.1๋ก ๋จ์ด์ง๋๋ค. TLS ์ข
๋ฃ๊ฐ ๊ฒ์ดํธ์จ์ด์์ ์ผ์ด๋๋ ํ๊ฒฝ์์๋ ํ๋ฌธ gRPC ํฌํธ๋ฅผ Http2๋ก ๊ณ ์ ํด์ผ ํฉ๋๋ค.
Q. AllowAlternateSchemes๋ ์ด๋ค ์ํฉ์์ ํ์ํ๊ฐ์?
๊ฒ์ดํธ์จ์ด/๋ก๋๋ฐธ๋ฐ์๊ฐ TLS๋ฅผ ์ข
๋ฃํ ๋ค :scheme์ ์๋ ๊ฐ(https)์ผ๋ก ์ ์งํ ์ฑ ํ๋ฌธ์ผ๋ก ๋ฐฑ์๋์ ์ ๋ฌํ ๋ ํ์ํฉ๋๋ค. ์ด ์ต์
์ด ์์ผ๋ฉด Kestrel์ด scheme๊ณผ ์ ์ก ๋ฐฉ์ ๋ถ์ผ์น๋ก ์ฐ๊ฒฐ์ ๊ฑฐ๋ถํฉ๋๋ค.
๐ ์ฐธ๊ณ
- Allow alternate schemes in Kestrel requests ยท PR #34013 (dotnet/aspnetcore)
- GRPC :scheme pseudo-header causes ConnectionAbortedException ยท Issue #30532 (dotnet/aspnetcore)
- gRPC issue when Kestrel set to Http1AndHttp2 with unsecure urls ยท Issue #979 (grpc/grpc-dotnet)
- Kubernetes Service appProtocol ๋ฌธ์
- Contour HTTPProxy: gRPC / h2c upstream
</content> </invoke>