grpc poc working on k8s, using envoy as a sidecar in catalog api

This commit is contained in:
eiximenis 2019-06-27 16:16:11 +02:00
parent db00159347
commit 591086956d
13 changed files with 148 additions and 49 deletions

View File

@ -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"

View File

@ -240,7 +240,4 @@ services:
- webhooks.api - webhooks.api
envoy: envoy:
image: envoy:v1 image: envoyproxy/envoy
build:
context: src/ApiGateways/Envoy
dockerfile: Dockerfile

View File

@ -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}",

View 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

View File

@ -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

View 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 -}}

View File

@ -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 }}

View File

@ -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

View File

@ -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

View File

@ -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);
} }

View File

@ -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})");

View File

@ -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()