Răsfoiți Sursa

Add nginx-aspnet-mysql implementation (#119)

* Added nginx-aspnet-mysql implementation

Signed-off-by: GitHub <noreply@github.com>

* Updated nginx+aspnet+mysql README.md

Signed-off-by: GitHub <noreply@github.com>

* Added db healthcheck

Signed-off-by: GitHub <noreply@github.com>
Esteban Solano Granados 4 ani în urmă
părinte
comite
eece4feb98

+ 68 - 0
nginx-aspnet-mysql/README.md

@@ -0,0 +1,68 @@
+## Compose sample application
+### ASP.NET server with an Nginx proxy and a MySQL database
+
+Project structure:
+```
+.
+├── backend
+│   ├── Dockerfile
+│   ├── aspnet.csproj
+│   └── Program.cs
+├── db
+│   └── password.txt
+├── docker-compose.yaml
+├── proxy
+│   ├── conf
+│   └── Dockerfile
+└── README.md
+```
+
+[_docker-compose.yaml_](docker-compose.yaml)
+```
+services:
+  backend:
+    build: backend
+    ...
+  db:
+    image: mysql:8.0.19
+    ...
+  proxy:
+    build: proxy
+    ports:
+    - 80:80
+    ...
+```
+The compose file defines an application with three services `proxy`, `backend` and `db`.
+When deploying the application, docker-compose maps port 80 of the proxy service container to port 80 of the host as specified in the file.
+Make sure port 80 on the host is not already being in use.
+
+## Deploy with docker-compose
+
+```
+$ docker-compose up -d
+```
+
+## Expected result
+
+Listing containers must show three containers running and the port mapping as below:
+```
+$ docker ps
+CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
+8906b14c5ad1        nginx-aspnet-mysql_proxy     "nginx -g 'daemon of…"   2 minutes ago       Up 2 minutes        0.0.0.0:80->80/tcp    nginx-aspnet-mysql
+l_proxy_1
+13e0e0a7715a        nginx-aspnet-mysql_backend   "/server"                2 minutes ago       Up 2 minutes        8000/tcp              nginx-aspnet-mysq
+l_backend_1
+ca8c5975d205        mysql:5.7                    "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        3306/tcp, 33060/tcp   nginx-aspnet-mysql
+l_db_1
+```
+
+After the application starts, navigate to `http://localhost:80` in your web browser or run:
+```
+$ curl localhost:80
+["Blog post #0","Blog post #1","Blog post #2","Blog post #3","Blog post #4"]
+```
+
+Stop and remove the containers
+```
+$ docker-compose down
+```

+ 17 - 0
nginx-aspnet-mysql/backend/Dockerfile

@@ -0,0 +1,17 @@
+
+FROM mcr.microsoft.com/dotnet/aspnet:5.0 as base
+WORKDIR /app
+
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
+COPY . /src
+WORKDIR /src
+RUN ls
+RUN dotnet build "aspnetapp.csproj" -c Release -o /app/build
+
+FROM build AS publish
+RUN dotnet publish "aspnetapp.csproj" -c Release -o /app/publish
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "aspnetapp.dll"]

+ 84 - 0
nginx-aspnet-mysql/backend/Program.cs

@@ -0,0 +1,84 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.Primitives;
+using MySql.Data;
+using MySql.Data.MySqlClient;
+
+class Program
+{
+    public static void Main(string[] args)  => WebHost.CreateDefaultBuilder(args)
+        .Configure(async app =>
+        {
+            app.UseRouting();
+                    
+            string password = File.ReadAllText("/run/secrets/db-password");
+            string connectionString = $"server=db;user=root;database=example;port=3306;password={password}";
+
+            app.UseEndpoints(e =>
+            {
+                e.MapGet("/",  context => {
+                    using MySqlConnection connection = new MySqlConnection(connectionString);
+                    var titles = new List<string>();
+
+                    try
+                    {
+                        Console.WriteLine("Connecting to MySQL...");
+                        connection.Open();
+
+                        string sql = "SELECT title FROM blog";
+                        using var cmd = new MySqlCommand(sql, connection);
+                        using MySqlDataReader reader = cmd.ExecuteReader();
+
+                        while (reader.Read())
+                        {
+                            titles.Add(reader.GetString(0));
+                        }
+                        reader.Close();
+                    }
+                    catch (Exception ex)
+                    {
+                        Console.WriteLine(ex.ToString());
+                        context.Response.StatusCode = 500;
+                        return Task.CompletedTask;
+                    }
+                    connection.Close();
+                    
+                    context.Response.StatusCode = 200;
+                    context.Response.WriteAsJsonAsync(titles);
+
+                    return Task.CompletedTask;
+                });
+            });
+            Prepare(connectionString);
+
+        }).Build().Run();
+
+        private static void Prepare(string connectionString)
+        {
+            using MySqlConnection connection = new MySqlConnection(connectionString);
+
+            connection.Open();
+            using var transation = connection.BeginTransaction();
+
+            using MySqlCommand cmd1 = new MySqlCommand("DROP TABLE IF EXISTS blog", connection, transation);
+            cmd1.ExecuteNonQuery();
+
+            using MySqlCommand cmd2 = new MySqlCommand("CREATE TABLE IF NOT EXISTS blog (id int NOT NULL AUTO_INCREMENT, title varchar(255), PRIMARY KEY (id))", connection, transation);
+            cmd2.ExecuteNonQuery();
+            
+            for (int i = 0; i < 5; i++)
+            {
+                using MySqlCommand insertCommand = new MySqlCommand( $"INSERT INTO blog (title) VALUES ('Blog post #{i}');", connection, transation);
+                insertCommand.ExecuteNonQuery();
+	        }
+            transation.Commit();
+            connection.Close();
+        }
+}

+ 8 - 0
nginx-aspnet-mysql/backend/aspnetapp.csproj

@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+  <PropertyGroup>
+    <TargetFramework>net5.0</TargetFramework>
+  </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="MySql.Data" Version="8.0.23" />
+  </ItemGroup>
+</Project>

+ 1 - 0
nginx-aspnet-mysql/db/password.txt

@@ -0,0 +1 @@
+db-q5n2g

+ 41 - 0
nginx-aspnet-mysql/docker-compose.yaml

@@ -0,0 +1,41 @@
+version: "3.7"
+services:
+  backend:
+    build: backend
+    secrets:
+      - db-password
+    depends_on: 
+      - db
+    environment:
+      - ASPNETCORE_URLS=http://+:8000
+    depends_on:
+      db:
+        condition: service_healthy
+  db:
+    image: mysql:8.0.19
+    command: '--default-authentication-plugin=mysql_native_password'
+    restart: always
+    healthcheck:
+      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "--silent"]
+      interval: 3s
+      retries: 5
+      start_period: 30s
+    secrets:
+      - db-password
+    volumes:
+      - db-data:/var/lib/mysql
+    environment:
+      - MYSQL_DATABASE=example
+      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db-password
+
+  proxy:
+    build: proxy
+    ports:
+      - 80:80
+    depends_on: 
+      - backend
+volumes:
+  db-data:
+secrets:
+  db-password:
+    file: db/password.txt

+ 2 - 0
nginx-aspnet-mysql/proxy/Dockerfile

@@ -0,0 +1,2 @@
+FROM nginx:1.13-alpine
+COPY conf /etc/nginx/conf.d/default.conf

+ 8 - 0
nginx-aspnet-mysql/proxy/conf

@@ -0,0 +1,8 @@
+server {
+    listen       80;
+    server_name  localhost;
+    location / {
+        proxy_pass   http://backend:8000;
+    }
+
+}