grpc poc working on k8s, using envoy as a sidecar in catalog api
This commit is contained in:
parent
db00159347
commit
591086956d
@ -89,6 +89,8 @@ services:
|
|||||||
- AzureStorageEnabled=False
|
- AzureStorageEnabled=False
|
||||||
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
|
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
|
||||||
- OrchestratorType=${ORCHESTRATOR_TYPE}
|
- OrchestratorType=${ORCHESTRATOR_TYPE}
|
||||||
|
- GRPC_PORT=81
|
||||||
|
- PORT=80
|
||||||
ports:
|
ports:
|
||||||
- "5101:80"
|
- "5101:80"
|
||||||
- "9101:81"
|
- "9101:81"
|
||||||
|
@ -240,7 +240,4 @@ services:
|
|||||||
- webhooks.api
|
- webhooks.api
|
||||||
|
|
||||||
envoy:
|
envoy:
|
||||||
image: envoy:v1
|
image: envoyproxy/envoy
|
||||||
build:
|
|
||||||
context: src/ApiGateways/Envoy
|
|
||||||
dockerfile: Dockerfile
|
|
@ -1,5 +1,17 @@
|
|||||||
{
|
{
|
||||||
"ReRoutes": [
|
"ReRoutes": [
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/{version}/{everything}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHostAndPorts": [
|
||||||
|
{
|
||||||
|
"Host": "catalog",
|
||||||
|
"Port": 5000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"UpstreamPathTemplate": "/api/{version}/c/{everything}",
|
||||||
|
"UpstreamHttpMethod": [ "GET" ]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/api/{version}/{everything}",
|
"DownstreamPathTemplate": "/api/{version}/{everything}",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
@ -9,9 +21,9 @@
|
|||||||
"Port": 80
|
"Port": 80
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"UpstreamPathTemplate": "/api/{version}/c/{everything}",
|
"UpstreamPathTemplate": "/grpc/{version}/c/{everything}",
|
||||||
"UpstreamHttpMethod": [ "GET" ]
|
"UpstreamHttpMethod": [ "GET" ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DownstreamPathTemplate": "/api/{version}/{everything}",
|
"DownstreamPathTemplate": "/api/{version}/{everything}",
|
||||||
"DownstreamScheme": "http",
|
"DownstreamScheme": "http",
|
||||||
@ -102,7 +114,7 @@
|
|||||||
"DownstreamHostAndPorts": [
|
"DownstreamHostAndPorts": [
|
||||||
{
|
{
|
||||||
"Host": "catalog",
|
"Host": "catalog",
|
||||||
"Port": 80
|
"Port": 5000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"UpstreamPathTemplate": "/catalog-api/{everything}",
|
"UpstreamPathTemplate": "/catalog-api/{everything}",
|
||||||
|
BIN
k8s/helm/catalog-api/envoycfg/_catalog.proto-descriptor.pb
Normal file
BIN
k8s/helm/catalog-api/envoycfg/_catalog.proto-descriptor.pb
Normal file
Binary file not shown.
56
k8s/helm/catalog-api/envoycfg/_envoy.yaml
Normal file
56
k8s/helm/catalog-api/envoycfg/_envoy.yaml
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
admin:
|
||||||
|
access_log_path: /tmp/admin_access.log
|
||||||
|
address:
|
||||||
|
socket_address: { address: 0.0.0.0, port_value: 9901 }
|
||||||
|
|
||||||
|
static_resources:
|
||||||
|
listeners:
|
||||||
|
- name: listener1
|
||||||
|
address:
|
||||||
|
socket_address: { address: 0.0.0.0, port_value: 51051 }
|
||||||
|
filter_chains:
|
||||||
|
- filters:
|
||||||
|
- name: envoy.http_connection_manager
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
|
||||||
|
stat_prefix: grpc_json
|
||||||
|
codec_type: AUTO
|
||||||
|
route_config:
|
||||||
|
name: local_route
|
||||||
|
virtual_hosts:
|
||||||
|
- name: local_service
|
||||||
|
domains: ["*"]
|
||||||
|
routes:
|
||||||
|
- match: { prefix: "/" }
|
||||||
|
route: { cluster: grpc, timeout: { seconds: 60 } }
|
||||||
|
http_filters:
|
||||||
|
- name: envoy.grpc_json_transcoder
|
||||||
|
config:
|
||||||
|
proto_descriptor: "/etc/envoy/catalog.proto-descriptor.pb"
|
||||||
|
services: ["CatalogApi.Catalog"]
|
||||||
|
print_options:
|
||||||
|
add_whitespace: true
|
||||||
|
always_print_primitive_fields: true
|
||||||
|
always_print_enums_as_ints: false
|
||||||
|
preserve_proto_field_names: false
|
||||||
|
- name: envoy.router
|
||||||
|
|
||||||
|
clusters:
|
||||||
|
- name: grpc
|
||||||
|
connect_timeout: 1.25s
|
||||||
|
type: logical_dns
|
||||||
|
lb_policy: round_robin
|
||||||
|
dns_lookup_family: V4_ONLY
|
||||||
|
http2_protocol_options: {}
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: grpc
|
||||||
|
endpoints:
|
||||||
|
- lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
# WARNING: "docker.for.mac.localhost" has been deprecated from Docker v18.03.0.
|
||||||
|
# If you're running an older version of Docker, please use "docker.for.mac.localhost" instead.
|
||||||
|
# Reference: https://docs.docker.com/docker-for-mac/release-notes/#docker-community-edition-18030-ce-mac59-2018-03-26
|
||||||
|
address: localhost
|
||||||
|
port_value: 5001
|
@ -73,10 +73,23 @@ spec:
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 80
|
containerPort: 5000
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
- name: grpc
|
||||||
|
containerPort: 5001
|
||||||
|
protocol: TCP
|
||||||
resources:
|
resources:
|
||||||
{{ toYaml .Values.resources | indent 12 }}
|
{{ toYaml .Values.resources | indent 12 }}
|
||||||
|
- name: envoy-proxy
|
||||||
|
image: envoyproxy/envoy
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- name: envoy
|
||||||
|
containerPort: 51051
|
||||||
|
protocol: TCP
|
||||||
|
volumeMounts:
|
||||||
|
- name: envoy-config
|
||||||
|
mountPath: /etc/envoy
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{ toYaml . | indent 8 }}
|
{{ toYaml . | indent 8 }}
|
||||||
@ -89,4 +102,13 @@ spec:
|
|||||||
tolerations:
|
tolerations:
|
||||||
{{ toYaml . | indent 8 }}
|
{{ toYaml . | indent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
volumes:
|
||||||
|
- name: envoy-config
|
||||||
|
configMap:
|
||||||
|
name: envoy-{{ $name }}
|
||||||
|
items:
|
||||||
|
- key: _envoy.yaml
|
||||||
|
path: envoy.yaml
|
||||||
|
- key: _catalog.proto-descriptor.pb
|
||||||
|
path: catalog.proto-descriptor.pb
|
||||||
|
|
||||||
|
16
k8s/helm/catalog-api/templates/envoy-cm.yaml
Normal file
16
k8s/helm/catalog-api/templates/envoy-cm.yaml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{{- $name := include "catalog-api.fullname" . -}}
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: "envoy-{{ $name }}"
|
||||||
|
labels:
|
||||||
|
app: {{ template "catalog-api.name" . }}
|
||||||
|
chart: {{ template "catalog-api.chart" .}}
|
||||||
|
release: {{ .Release.Name }}
|
||||||
|
heritage: {{ .Release.Service }}
|
||||||
|
data:
|
||||||
|
{{ (.Files.Glob "envoycfg/*.yaml").AsConfig | indent 2 }}
|
||||||
|
binaryData:
|
||||||
|
_catalog.proto-descriptor.pb: |-
|
||||||
|
{{ .Files.Get "envoycfg/_catalog.proto-descriptor.pb" | b64enc -}}
|
@ -14,6 +14,14 @@ spec:
|
|||||||
targetPort: http
|
targetPort: http
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
name: http
|
name: http
|
||||||
|
- port: {{ .Values.service.grpc }}
|
||||||
|
targetPort: grpc
|
||||||
|
protocol: TCP
|
||||||
|
name: grpc
|
||||||
|
- port: {{ .Values.service.envoy }}
|
||||||
|
targetPort: envoy
|
||||||
|
protocol: TCP
|
||||||
|
name: envoy
|
||||||
selector:
|
selector:
|
||||||
app: {{ template "catalog-api.name" . }}
|
app: {{ template "catalog-api.name" . }}
|
||||||
release: {{ .Release.Name }}
|
release: {{ .Release.Name }}
|
||||||
|
@ -9,7 +9,9 @@ image:
|
|||||||
|
|
||||||
service:
|
service:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 80
|
port: 5000
|
||||||
|
grpc: 5001
|
||||||
|
envoy: 80
|
||||||
|
|
||||||
|
|
||||||
resources: {}
|
resources: {}
|
||||||
@ -44,16 +46,20 @@ env:
|
|||||||
value: Development
|
value: Development
|
||||||
- name: OrchestratorType
|
- name: OrchestratorType
|
||||||
value: 'K8S'
|
value: 'K8S'
|
||||||
|
- name: PORT
|
||||||
|
value: "5000"
|
||||||
|
- name: GRPC_PORT
|
||||||
|
value: "5001"
|
||||||
probes:
|
probes:
|
||||||
liveness:
|
liveness:
|
||||||
path: /liveness
|
path: /liveness
|
||||||
initialDelaySeconds: 10
|
initialDelaySeconds: 10
|
||||||
periodSeconds: 15
|
periodSeconds: 15
|
||||||
port: 80
|
port: 5000
|
||||||
readiness:
|
readiness:
|
||||||
path: /hc
|
path: /hc
|
||||||
timeoutSeconds: 5
|
timeoutSeconds: 5
|
||||||
initialDelaySeconds: 90
|
initialDelaySeconds: 90
|
||||||
periodSeconds: 60
|
periodSeconds: 60
|
||||||
port: 80
|
port: 5000
|
||||||
|
|
||||||
|
@ -9,8 +9,10 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config
|
|||||||
{
|
{
|
||||||
public class CatalogOperations
|
public class CatalogOperations
|
||||||
{
|
{
|
||||||
|
// grpc call under REST must go trough port 80
|
||||||
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}";
|
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}";
|
||||||
public static string GetItemsById(IEnumerable<int> ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}";
|
// REST call standard must go through port 5000
|
||||||
|
public static string GetItemsById(IEnumerable<int> ids) => $":5000/api/v1/catalog/items?ids={string.Join(',', ids)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BasketOperations
|
public class BasketOperations
|
||||||
|
@ -24,7 +24,8 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
|
|||||||
|
|
||||||
public async Task<CatalogItem> GetCatalogItemAsync(int id)
|
public async Task<CatalogItem> GetCatalogItemAsync(int id)
|
||||||
{
|
{
|
||||||
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id));
|
var uri=_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id);
|
||||||
|
var stringContent = await _httpClient.GetStringAsync(uri);
|
||||||
|
|
||||||
return JsonConvert.DeserializeObject<CatalogItem>(stringContent);
|
return JsonConvert.DeserializeObject<CatalogItem>(stringContent);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.eShopOnContainers.Services.Catalog.API;
|
using Microsoft.eShopOnContainers.Services.Catalog.API;
|
||||||
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
|
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
|
||||||
using Microsoft.eShopOnContainers.Services.Catalog.API.Model;
|
using Microsoft.eShopOnContainers.Services.Catalog.API.Model;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using static CatalogApi.Catalog;
|
using static CatalogApi.Catalog;
|
||||||
|
|
||||||
@ -17,15 +18,17 @@ namespace Catalog.API.Grpc
|
|||||||
{
|
{
|
||||||
private readonly CatalogContext _catalogContext;
|
private readonly CatalogContext _catalogContext;
|
||||||
private readonly CatalogSettings _settings;
|
private readonly CatalogSettings _settings;
|
||||||
public CatalogService(CatalogContext dbContext, IOptions<CatalogSettings> settings)
|
private readonly ILogger _logger;
|
||||||
|
public CatalogService(CatalogContext dbContext, IOptions<CatalogSettings> settings, ILogger<CatalogService> logger)
|
||||||
{
|
{
|
||||||
_settings = settings.Value;
|
_settings = settings.Value;
|
||||||
_catalogContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
_catalogContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<CatalogItemResponse> GetItemById(CatalogItemRequest request, ServerCallContext context)
|
public override async Task<CatalogItemResponse> GetItemById(CatalogItemRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation($"Begin grpc call CatalogService.GetItemById for product id {request.Id}");
|
||||||
if (request.Id <=0)
|
if (request.Id <=0)
|
||||||
{
|
{
|
||||||
context.Status = new Status(StatusCode.FailedPrecondition, $"Id must be > 0 (received {request.Id})");
|
context.Status = new Status(StatusCode.FailedPrecondition, $"Id must be > 0 (received {request.Id})");
|
||||||
|
@ -75,11 +75,12 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API
|
|||||||
.UseConfiguration(configuration)
|
.UseConfiguration(configuration)
|
||||||
.ConfigureKestrel(options =>
|
.ConfigureKestrel(options =>
|
||||||
{
|
{
|
||||||
options.Listen(IPAddress.Any, 80, listenOptions =>
|
var ports = GetDefinedPorts(configuration);
|
||||||
|
options.Listen(IPAddress.Any, ports.httpPort, listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.Protocols = HttpProtocols.Http1;
|
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
|
||||||
});
|
});
|
||||||
options.Listen(IPAddress.Any, 81, listenOptions =>
|
options.Listen(IPAddress.Any, ports.grpcPort, listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.Protocols = HttpProtocols.Http2;
|
listenOptions.Protocols = HttpProtocols.Http2;
|
||||||
});
|
});
|
||||||
@ -107,38 +108,11 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API
|
|||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<(int portNumber, bool https)> GetDefinedPorts(IConfiguration config)
|
private static (int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config)
|
||||||
{
|
{
|
||||||
const string https = "https://";
|
var grpcPort = config.GetValue("GRPC_PORT", 5001);
|
||||||
const string http = "http://";
|
var port = config.GetValue("PORT", 80);
|
||||||
var defport = config.GetValue("ASPNETCORE_HTTPS_PORT", 0);
|
return (port, grpcPort);
|
||||||
if (defport != 0)
|
|
||||||
{
|
|
||||||
yield return (defport, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var urls = config.GetValue<string>("ASPNETCORE_URLS", null)?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (urls?.Any() == true)
|
|
||||||
{
|
|
||||||
foreach (var urlString in urls)
|
|
||||||
{
|
|
||||||
var uri = urlString.ToLowerInvariant().Trim();
|
|
||||||
var isHttps = uri.StartsWith(https);
|
|
||||||
var isHttp = uri.StartsWith(http);
|
|
||||||
if (!isHttp && !isHttps)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Url {uri} must start with https:// or http://");
|
|
||||||
}
|
|
||||||
|
|
||||||
uri = uri.Substring(isHttps ? https.Length : http.Length);
|
|
||||||
var lastdots = uri.LastIndexOf(':');
|
|
||||||
if (lastdots != -1)
|
|
||||||
{
|
|
||||||
var sport = uri.Substring(lastdots + 1);
|
|
||||||
yield return (int.TryParse(sport, out var nport) ? nport : isHttps ? 443 : 80, isHttps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IConfiguration GetConfiguration()
|
private static IConfiguration GetConfiguration()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user