Browse Source
* Added container proxy for talking to the host - This change introduces a container proxy which makes it possible for docker containers can talk to host services using container networking. These proxies will not show up in the dashboard as they are "infrastructure" containers. - Added Private and NetworkAlias to DockerRunInfo but did not expose these configuration. - All container communication is done using host names. - Fix host shutdown again - Bind to all interfaces on linuxdavidfowl/build-earlier
committed by
GitHub
27 changed files with 682 additions and 38 deletions
@ -0,0 +1,7 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace ApplicationA |
|||
{ |
|||
public class Program |
|||
{ |
|||
public static void Main(string[] args) |
|||
{ |
|||
CreateHostBuilder(args).Build().Run(); |
|||
} |
|||
|
|||
public static IHostBuilder CreateHostBuilder(string[] args) => |
|||
Host.CreateDefaultBuilder(args) |
|||
.ConfigureWebHostDefaults(webBuilder => |
|||
{ |
|||
webBuilder.UseStartup<Startup>(); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
{ |
|||
"iisSettings": { |
|||
"windowsAuthentication": false, |
|||
"anonymousAuthentication": true, |
|||
"iisExpress": { |
|||
"applicationUrl": "http://localhost:2755", |
|||
"sslPort": 44369 |
|||
} |
|||
}, |
|||
"profiles": { |
|||
"IIS Express": { |
|||
"commandName": "IISExpress", |
|||
"launchBrowser": true, |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
}, |
|||
"ApplicationA": { |
|||
"commandName": "Project", |
|||
"launchBrowser": true, |
|||
"applicationUrl": "https://localhost:5001;http://localhost:5000", |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace ApplicationA |
|||
{ |
|||
public class Startup |
|||
{ |
|||
// This method gets called by the runtime. Use this method to add services to the container.
|
|||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
} |
|||
|
|||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
|||
{ |
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
|
|||
app.UseRouting(); |
|||
|
|||
app.UseEndpoints(endpoints => |
|||
{ |
|||
endpoints.MapGet("/", async context => |
|||
{ |
|||
await context.Response.WriteAsync("Hello from Application A " + Environment.GetEnvironmentVariable("APP_INSTANCE") ?? Environment.GetEnvironmentVariable("HOSTNAME")); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"Logging": { |
|||
"LogLevel": { |
|||
"Default": "Information", |
|||
"Microsoft": "Warning", |
|||
"Microsoft.Hosting.Lifetime": "Information" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
{ |
|||
"Logging": { |
|||
"LogLevel": { |
|||
"Default": "Information", |
|||
"Microsoft": "Warning", |
|||
"Microsoft.Hosting.Lifetime": "Information" |
|||
} |
|||
}, |
|||
"AllowedHosts": "*" |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace ApplicationB |
|||
{ |
|||
public class Program |
|||
{ |
|||
public static void Main(string[] args) |
|||
{ |
|||
CreateHostBuilder(args).Build().Run(); |
|||
} |
|||
|
|||
public static IHostBuilder CreateHostBuilder(string[] args) => |
|||
Host.CreateDefaultBuilder(args) |
|||
.ConfigureWebHostDefaults(webBuilder => |
|||
{ |
|||
webBuilder.UseStartup<Startup>(); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
{ |
|||
"iisSettings": { |
|||
"windowsAuthentication": false, |
|||
"anonymousAuthentication": true, |
|||
"iisExpress": { |
|||
"applicationUrl": "http://localhost:19251", |
|||
"sslPort": 44343 |
|||
} |
|||
}, |
|||
"profiles": { |
|||
"IIS Express": { |
|||
"commandName": "IISExpress", |
|||
"launchBrowser": true, |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
}, |
|||
"ApplicationB": { |
|||
"commandName": "Project", |
|||
"launchBrowser": true, |
|||
"applicationUrl": "https://localhost:5001;http://localhost:5000", |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace ApplicationB |
|||
{ |
|||
public class Startup |
|||
{ |
|||
// This method gets called by the runtime. Use this method to add services to the container.
|
|||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
} |
|||
|
|||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
|||
{ |
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
|
|||
app.UseRouting(); |
|||
|
|||
app.UseEndpoints(endpoints => |
|||
{ |
|||
endpoints.MapGet("/", async context => |
|||
{ |
|||
await context.Response.WriteAsync("Hello from Application B " + Environment.GetEnvironmentVariable("APP_INSTANCE") ?? Environment.GetEnvironmentVariable("HOSTNAME")); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"Logging": { |
|||
"LogLevel": { |
|||
"Default": "Information", |
|||
"Microsoft": "Warning", |
|||
"Microsoft.Hosting.Lifetime": "Information" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
{ |
|||
"Logging": { |
|||
"LogLevel": { |
|||
"Default": "Information", |
|||
"Microsoft": "Warning", |
|||
"Microsoft.Hosting.Lifetime": "Information" |
|||
} |
|||
}, |
|||
"AllowedHosts": "*" |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00 |
|||
# Visual Studio 15 |
|||
VisualStudioVersion = 15.0.26124.0 |
|||
MinimumVisualStudioVersion = 15.0.26124.0 |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApplicationA", "ApplicationA\ApplicationA.csproj", "{5A9DC239-55BB-4951-B081-35931BF8C867}" |
|||
EndProject |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApplicationB", "ApplicationB\ApplicationB.csproj", "{AE1F10D3-BFAE-4D23-ADCF-06770237285D}" |
|||
EndProject |
|||
Global |
|||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|||
Debug|Any CPU = Debug|Any CPU |
|||
Debug|x64 = Debug|x64 |
|||
Debug|x86 = Debug|x86 |
|||
Release|Any CPU = Release|Any CPU |
|||
Release|x64 = Release|x64 |
|||
Release|x86 = Release|x86 |
|||
EndGlobalSection |
|||
GlobalSection(SolutionProperties) = preSolution |
|||
HideSolutionNode = FALSE |
|||
EndGlobalSection |
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Debug|x64.ActiveCfg = Debug|Any CPU |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Debug|x64.Build.0 = Debug|Any CPU |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Debug|x86.ActiveCfg = Debug|Any CPU |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Debug|x86.Build.0 = Debug|Any CPU |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Release|x64.ActiveCfg = Release|Any CPU |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Release|x64.Build.0 = Release|Any CPU |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Release|x86.ActiveCfg = Release|Any CPU |
|||
{5A9DC239-55BB-4951-B081-35931BF8C867}.Release|x86.Build.0 = Release|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Debug|x64.ActiveCfg = Debug|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Debug|x64.Build.0 = Debug|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Debug|x86.ActiveCfg = Debug|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Debug|x86.Build.0 = Debug|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Release|x64.ActiveCfg = Release|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Release|x64.Build.0 = Release|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Release|x86.ActiveCfg = Release|Any CPU |
|||
{AE1F10D3-BFAE-4D23-ADCF-06770237285D}.Release|x86.Build.0 = Release|Any CPU |
|||
EndGlobalSection |
|||
EndGlobal |
|||
@ -0,0 +1,25 @@ |
|||
server { |
|||
listen 80; |
|||
|
|||
location /A { |
|||
proxy_pass http://appA/; |
|||
proxy_http_version 1.1; |
|||
proxy_set_header Upgrade $http_upgrade; |
|||
proxy_set_header Connection keep-alive; |
|||
proxy_set_header Host $host; |
|||
proxy_cache_bypass $http_upgrade; |
|||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
|||
proxy_set_header X-Forwarded-Proto $scheme; |
|||
} |
|||
|
|||
location /B { |
|||
proxy_pass http://appB/; |
|||
proxy_http_version 1.1; |
|||
proxy_set_header Upgrade $http_upgrade; |
|||
proxy_set_header Connection keep-alive; |
|||
proxy_set_header Host $host; |
|||
proxy_cache_bypass $http_upgrade; |
|||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
|||
proxy_set_header X-Forwarded-Proto $scheme; |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
services: |
|||
- name: nginx |
|||
image: nginx |
|||
bindings: |
|||
- protocol: http |
|||
volumes: |
|||
- source: nginx.conf |
|||
target: /etc/nginx/conf.d/default.conf |
|||
- name: appA |
|||
project: ApplicationA/ApplicationA.csproj |
|||
bindings: |
|||
replicas: 2 |
|||
- name: appB |
|||
project: ApplicationB/ApplicationB.csproj |
|||
bindings: |
|||
replicas: 2 |
|||
@ -0,0 +1,22 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Bedrock.Framework" Version="0.1.38-alpha.gd25d5b37ad" /> |
|||
<PackageReference Include="System.IO.Pipelines" Version="4.7.0" /> |
|||
</ItemGroup> |
|||
|
|||
<!-- Include *.deps.json and *.runtimeconfig.json in ContentWithTargetPath so they will be copied to the output folder of projects |
|||
that reference this one. --> |
|||
<Target Name="AddRuntimeDependenciesToContent" Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp'" BeforeTargets="GetCopyToOutputDirectoryItems"> |
|||
<ItemGroup> |
|||
<ContentWithTargetPath Include="$(ProjectDepsFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectDepsFileName)" /> |
|||
|
|||
<ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectRuntimeConfigFileName)" /> |
|||
</ItemGroup> |
|||
</Target> |
|||
</Project> |
|||
@ -0,0 +1,135 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.IO.Pipelines; |
|||
using System.Linq; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
using System.Threading.Tasks; |
|||
using Bedrock.Framework; |
|||
using Microsoft.AspNetCore.Connections; |
|||
using Microsoft.AspNetCore.Connections.Features; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace Microsoft.Tye.Proxy |
|||
{ |
|||
public class Program |
|||
{ |
|||
static async Task Main(string[] args) |
|||
{ |
|||
var serviceName = Environment.GetEnvironmentVariable("APP_INSTANCE"); |
|||
var containerHost = Environment.GetEnvironmentVariable("CONTAINER_HOST"); |
|||
|
|||
using var host = new HostBuilder() |
|||
.ConfigureLogging(logging => |
|||
{ |
|||
logging.AddConsole(); |
|||
logging.SetMinimumLevel(LogLevel.Debug); |
|||
}) |
|||
.ConfigureServer(server => |
|||
{ |
|||
var logger = server.ApplicationServices.GetRequiredService<ILogger<Program>>(); |
|||
|
|||
logger.LogInformation("Received connection information {Host}:{Port}", containerHost, Environment.GetEnvironmentVariable("PROXY_PORT")); |
|||
|
|||
static (int Port, int ExternalPort) ResolvePort(string portValue) |
|||
{ |
|||
var pair = portValue.Split(':'); |
|||
return (int.Parse(pair[0]), int.Parse(pair[1])); |
|||
} |
|||
|
|||
var ports = Environment.GetEnvironmentVariable("PROXY_PORT")?.Split(';').Select(ResolvePort) ?? Enumerable.Empty<(int, int)>(); |
|||
|
|||
server.UseSockets(sockets => |
|||
{ |
|||
foreach (var mapping in ports) |
|||
{ |
|||
sockets.Listen(IPAddress.Any, mapping.Port, o => |
|||
{ |
|||
// o.UseConnectionLogging("Microsoft.Tye.Proxy");
|
|||
|
|||
o.Run(async connection => |
|||
{ |
|||
var notificationFeature = connection.Features.Get<IConnectionLifetimeNotificationFeature>(); |
|||
|
|||
NetworkStream? targetStream = null; |
|||
|
|||
try |
|||
{ |
|||
var target = new Socket(SocketType.Stream, ProtocolType.Tcp) |
|||
{ |
|||
NoDelay = true |
|||
}; |
|||
|
|||
logger.LogDebug("Attempting to connect to {ServiceName} listening on {Port}:{ExternalPort}", serviceName, mapping.Port, mapping.ExternalPort); |
|||
|
|||
await target.ConnectAsync(containerHost, mapping.ExternalPort); |
|||
|
|||
logger.LogDebug("Successfully connected to {ServiceName} listening on {Port}:{ExternalPort}", serviceName, mapping.Port, mapping.ExternalPort); |
|||
|
|||
targetStream = new NetworkStream(target, ownsSocket: true); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
logger.LogDebug(ex, "Proxy error for service {ServiceName}", serviceName); |
|||
|
|||
if (targetStream is object) |
|||
{ |
|||
await targetStream.DisposeAsync(); |
|||
} |
|||
|
|||
connection.Abort(); |
|||
return; |
|||
} |
|||
|
|||
try |
|||
{ |
|||
logger.LogDebug("Proxying traffic to {ServiceName} {Port}:{ExternalPort}", serviceName, mapping.Port, mapping.ExternalPort); |
|||
|
|||
// external -> internal
|
|||
var reading = Task.Run(() => connection.Transport.Input.CopyToAsync(targetStream, notificationFeature.ConnectionClosedRequested)); |
|||
|
|||
// internal -> external
|
|||
var writing = Task.Run(() => targetStream.CopyToAsync(connection.Transport.Output, notificationFeature.ConnectionClosedRequested)); |
|||
|
|||
await Task.WhenAll(reading, writing); |
|||
} |
|||
catch (ConnectionResetException) |
|||
{ |
|||
// Connection was reset
|
|||
} |
|||
catch (IOException) |
|||
{ |
|||
// Reset can also appear as an IOException with an inner SocketException
|
|||
} |
|||
catch (OperationCanceledException ex) |
|||
{ |
|||
if (!notificationFeature.ConnectionClosedRequested.IsCancellationRequested) |
|||
{ |
|||
logger.LogDebug(0, ex, "Proxy error for service {ServiceName}", serviceName); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
logger.LogDebug(0, ex, "Proxy error for service {ServiceName}", serviceName); |
|||
} |
|||
finally |
|||
{ |
|||
await targetStream.DisposeAsync(); |
|||
} |
|||
|
|||
// This needs to reconnect to the target port(s) until its bound
|
|||
// it has to stop if the service is no longer running
|
|||
}); |
|||
|
|||
}); |
|||
} |
|||
}); |
|||
}) |
|||
.Build(); |
|||
|
|||
await host.RunAsync(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue