Goal
Deploying/hosting a blazor-hosted (.Net Core 3 Preview 6) app on a Ubuntu server with a reserve-proxy (nginx) and docker (all containerized).
Config Files
The dockerfile which got generated by Visual Studio:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.0-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:3.0-buster AS build
WORKDIR /src
COPY ["Website.Server/Website.Server.csproj", "Website.Server/"]
COPY ["Website.Client/Website.Client.csproj", "Website.Client/"]
RUN dotnet restore "Website.Server/Website.Server.csproj"
COPY . .
WORKDIR "/src/Website.Server"
RUN dotnet build "Website.Server.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "Website.Server.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Website.Server.dll"]
The docker-compose.yml file which is used to launch the application:
version: '3'
services:
nginx:
image: nginx:1.17-alpine
volumes:
- ./data/nginx:/etc/nginx/conf.d
ports:
- "80:80"
- "443:443"
restart: always
wesbite:
image: ImageToTheDockerFileAbove
expose:
- "80"
- "443"
restart: always
And finally my nginx config:
server {
listen 80;
server_name domain;
location / {
return 301
https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name domain;
location / {
proxy_pass http://website:80;
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;
}
}
Startup.cs of my ASP.Net Core app
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddNewtonsoftJson();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBlazorDebugging();
}
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseClientSideBlazorFiles<Client.Startup>();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
}
}
Problems
Actually there is just no content served at all and I am just getting a 404, but
the link between the container does exist. Since I can see the following output as soon as I type in my domain in the browser:
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET domain
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'Fallback {*path:nonfile}'
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'Fallback {*path:nonfile}'
Regarding to MSDN you are not required to add the following lines to the config of nginx, since the blazor app sits behind a normal ASP.Net Core application.
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html =404;
But when reading the error this still seems to me as a routing issue or similar.
Related
I have troubles with the api endpoints called by the spa application when is running in docker. The project is developed in c# with minimal apis and fallback to index.html, when I compile and use docker to compile the spa and back application.
When the front calls to an api endpoint, the response is the html file
# this is the program cs
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error");
app.UseHsts();
}
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Bookclub App"));
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.RegisterBaseRoutes();
app.RegisterBookRoutes();
app.RegisterPollRoutes();
app.MapHealthChecks();
app.MapFallbackToFile("{*path:regex(^(?!api).*$)}", "index.html");
app.Run();
# Dockerfile
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:7.0-alpine AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
ENV DOTNET_RUNNING_IN_CONTAINER=true
RUN apk add --no-cache icu-libs krb5-libs libgcc libintl libssl1.1 libstdc++ zlib
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
FROM node:18-alpine AS build-node
WORKDIR /app
COPY ["src/Lanre.Web/clientapp/package.json", "./package.json"]
RUN npm i
COPY ["src/Lanre.Web/clientapp/", "./"]
RUN npm run build
FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine AS build
WORKDIR /src
COPY ["src/Lanre.Web/Lanre.Web.csproj", "src/Lanre.Web/"]
COPY ["src/Lanre.Infrastructure/Lanre.Infrastructure.csproj", "src/Lanre.Infrastructure/"]
COPY ["src/Lanre.Module.Library/Lanre.Module.Library.csproj", "src/Lanre.Module.Library/"]
COPY ["src/Lanre.Module.Poll/Lanre.Module.Poll.csproj", "src/Lanre.Module.Poll/"]
RUN dotnet restore "src/Lanre.Web/Lanre.Web.csproj"
COPY . .
WORKDIR "/src/src/Lanre.Web"
RUN dotnet build "Lanre.Web.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "Lanre.Web.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish/certs/devcert.pfx /root/.aspnet/https/devcert.pfx
RUN chmod 644 /root/.aspnet/https/devcert.pfx && update-ca-certificates
COPY --from=publish /app/publish .
COPY --from=build-node /app/build/ ./wwwroot
ENTRYPOINT ["dotnet", "Lanre.Web.dll"]
I have a dotnet project and when I ran it inside docker container the build goes right and the program starts (it logs that everything is fine), but when I make a request in postman, I got a socket hang up error. what can be a reason? When I manually start a server by dotnet run command it works fine.
Here is my dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build
WORKDIR /src
COPY ["kisc.csproj", ""]
RUN dotnet restore "./kisc.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "kisc.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "kisc.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
EXPOSE 5000
ENTRYPOINT ["dotnet", "kisc.dll"]
And the containers logs
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Unable to bind to http://localhost:5000 on the IPv6 loopback interface: 'Cannot assign requested address'.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
Did you set the default url in your app?
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>();
webBuilder.UseUrls("http://localhost:5000", "https://localhost:8001");
});
}
Or Set Enviroment With port in your dockerfile
ENV ASPNETCORE_URLS = "http://localhost:5000"
Look Here
I have a dockerized asp.net Core application trying to connect to a mySql Database. Both are running inside a docker-compose. When I test the connection to a local Database without Docker, my code is working fine, but when I deploy it on a Vm inside docker-compose and that I call one of my controller, I get this error : System.ArgumentNullException: Value cannot be null. (Parameter 'connectionString') .
Here is my docker-compose :
version: '3'
services:
dbgil:
container_name: dbgil
image: mysql
restart: always
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: root
volumes:
- dbdata:/var/lib/mysql
webserver:
depends_on:
- dbgil
image: lionelquirynen/gillesquirynensys:latest
ports:
- "8021:80"
links:
- dbgil
volumes:
dbdata:
And here is my startup in my asp.net core application :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GillesQuirynenSys.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace GillesQuirynenSys
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<MySqlDbContext>(options =>
options.UseMySql("server=localhost;port=3306;database=test;user=root;password=root;convert zero datetime=True"));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, MySqlDbContext context)
{
context.Database.EnsureCreated();
var databaseCreator = context.GetService<IRelationalDatabaseCreator>();
databaseCreator.CreateTables();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
I first had my connection string in my appsettings.json but I was getting the same error so I tried to hardcode it but it did not change anything. Has anyone any ideas? It seems like my configuration is working fine (at least, it is locally without Docker).
PS: Here is the DockerFile of my asp.net Core application before pushing it to DockerHub :
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["GillesQuirynenSys/GillesQuirynenSys.csproj", "GillesQuirynenSys/"]
RUN dotnet restore "GillesQuirynenSys/GillesQuirynenSys.csproj"
COPY . .
WORKDIR "/src/GillesQuirynenSys"
RUN dotnet build "GillesQuirynenSys.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "GillesQuirynenSys.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "GillesQuirynenSys.dll"]
I'm glad my answer helped.
So basicaly in the docker container, the server name of MySQL db is the same as the service name declared in docker-compose.yml file.
One more note to your code:
Instead of this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<MySqlDbContext>(options =>
options.UseMySql("server=localhost;port=3306;database=test;user=root;password=root;convert zero datetime=True"));
}
-> where you hard-code your connection string
Try this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<MySqlDbContext>(options =>
options.UseMySql(Configuration.GetConnectionString("DefaultConnection")));
}
And adjust your appsettings.json file - for production (docker container) build, you will have:
{
"ConnectionStrings": {
"DefaultConnection": "server=dbgil;port=3306;database=test;user=root;password=root;convert zero datetime=True"
}
...
}
And in appsettings.Development.json, you will have a connection string for Development mode (your localhost):
{
"ConnectionStrings": {
"DefaultConnection": "server=localhost;port=3306;database=test;user=root;password=root;convert zero datetime=True"
}
...
}
Try using this, it works:
"ConnectionStrings": {
"ShopOnlineConnection": "server:(localdb)\\MSSQLLocalDB;database=ShopOnline;Trusted_Connection:True;"
},
I'm just getting started with Docker and have installed docker for windows.
The basic setup of docker is correct and i have been able to debug a simple Asp.Net Core app which is deployed to a container from within Visual studio (using the standard 'Run' command targeting docker).
The problem i'm having is being able to hit the endpoint hosted from within the container without using localhost i.e. using the IP of the container. I need this as i'm intending to hit the endpoint from a xamarin app.
After doing some reading, it seems i need to 'publish' the port that the application is running, in this case port 5000, but i can't seem to find where to configure visual studio to do this.
Using postman or a web browser to hit the endpoint results in the same response Empty_Response error.
I'm hoping someone can point me in the right direction
My Dockerfile:
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base
WORKDIR /app
EXPOSE 5000
ENV ASPNETCORE_URLS http://<container ip>:5000
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
WORKDIR /src
COPY ["ItemCheckout/ItemCheckout.csproj", "ItemCheckout/"]
RUN dotnet restore "ItemCheckout/ItemCheckout.csproj"
COPY . .
WORKDIR "/src/ItemCheckout"
RUN dotnet build "ItemCheckout.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "ItemCheckout.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "ItemCheckout.dll"]
Startup.cs:
public class Startup
{
private static readonly LoggerFactory _loggerFactory = new LoggerFactory(new []{new DebugLoggerProvider()});
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ItemCheckoutDbContext>(o =>
{
o.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
o.UseLoggerFactory(_loggerFactory);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
program.cs:
public class Program
{
public static async Task Main(string[] args)
{
await CreateWebHostBuilder(args).Build().RunAsync();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseUrls("http://<container ip>:5000")
.UseStartup<Startup>();
}
Output when running:
Hosting environment: Development
Content root path: /app
Now listening on: http://<container ip>:5000
Application started. Press Ctrl+C to shut down.
EDIT: Updated program.cs as per #MindSwipe's suggestion, however i am still getting the same result
Port mapping must be specified during docker run command. EXPOSE command within dockerfile normally used for documentation purposes.
Solution: in your Visual Studio project file add the following:
<PropertyGroup>
<DockerfileRunArguments>-p 5000:5000</DockerfileRunArguments>
</PropertyGroup>
References:
https://learn.microsoft.com/en-us/visualstudio/containers/container-msbuild-properties?view=vs-2019
https://docs.docker.com/engine/reference/builder/#expose
OK - from what I see - you're confusing the port on the container with the port on the host.
You have 2 sides to port mapping in Docker - the host side and the client/container side.
There's a couple of ways to solve the issue that you can't connect.
With the container running (F5 debug, command line) - execute the following command:
"docker ps -a"
You will see a list of running containers; there's a column called "Ports". Find the entry for your container - probably the solutionname:dev. Look at the ports column, you will see AA -> BB.
AA is the port you need in your browser - that is your host port - so http://localhost:AA, or http://IP-Address:AA/
BB is the listening port on the container - in your case 5000.
Does this help?
EDIT:
change this line: ENV ASPNETCORE_URLS http://:5000
to ENV ASPNETCORE_URLS http://+:5000
This is relative to the container - not the host.
I have .net core app running in docker, when I deploying image to amazon Elastic Beanstalk, status is ok, however when I am trying access the page i am getting "502 Bad Gateway nginx/1.14.1"
DockerFile:
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
WORKDIR /src
COPY ["MyApp.csproj", "."]
RUN dotnet restore "MyApp.csproj"
COPY . .
RUN dotnet build "MyApp.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "MyApp.csproj" -c Release -o /app
FROM base AS final
WORKDIR . /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "MyApp.dll"]
Dockerrun.aws.json
{
"AWSEBDockerrunVersion": "1",
"Image": {
"Name": "xxxx/myapp",
"Update": "true"
},
"Ports": [
{
"ContainerPort": "80"
}
]
}
log:
2019/07/16 10:55:34 [error] 13553#0: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 185.32.153.2, server: , request: "GET / HTTP/1.1", upstream: "http://172.17.0.3:80/", host: "myapp-env11.us-east-1.elasticbeanstalk.com"
What Port should I open in order for this to run?
So I was having this same issue earlier today. It turns out it was an issue with my application failing to start so, the nginx couldn't reach it.
And after going through the logs. I found out that it was a connection string issue, because my application was starting locally and when I deployed to IIS but not when its deployed to AWS elastic-beanstalk.
My application wasn't a dockerized application but this same issue almost gave me a terrible day.
So, here is what I will advise, download your logs and look through the logs for any application specific error, in my case I had to check the web.stdout logs.
So I fixed my connection string issue.
From:
"DefaultConnection": "Server=dbserverURL,1433;Database=DbName;User Id=dbuser;Password=dbPass;Trusted_Connection=True;MultipleActiveResultSets=true"
To:
"DefaultConnection": "Server=dbserverURL,1433;Database=DbName;Integrated Security=false;User Id=dbuser;Password=dbPass;MultipleActiveResultSets=true"