├── .github
└── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── docker-compose.yml
├── load-balancing-http.gif
├── load-balancing.gif
├── load-balancing.png
├── nginx
├── nginx.conf
├── proxy.conf
└── ssl
│ ├── localhost.cer
│ ├── localhost.crt
│ └── localhost.key
└── src
├── .dockerignore
├── Dockerfile
├── MyWebApi.sln
├── MyWebApi
├── Controllers
│ └── WeatherForecastController.cs
├── MyWebApi.csproj
├── Program.cs
├── Startup.cs
├── WeatherForecast.cs
├── appsettings.Development.json
└── appsettings.json
└── README.md
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: changhuixu
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
332 | CSharpLabs/RunExeFromWebApi/WebApi/App_Data/
333 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Changhui Xu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Host an ASP.NET Core App with Nginx and Docker: SSL and Load Balancing
2 |
3 |
4 |
5 | ## [Medium Article](https://codeburst.io/load-balancing-an-asp-net-core-web-app-using-nginx-and-docker-66753eb08204)
6 |
7 | Following the two articles, _[Configure ASP.NET Core to work with proxy servers and load balancers](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#configure-nginx)_ and _[Host ASP.NET Core on Linux with Nginx](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1)_, in Microsoft Docs, I created a demo application using Docker Compose, which orchestrates an Nginx reverse proxy server and an ASP.NET Core Web API app. The following screen recording shows the demo app.
8 |
9 | 
10 |
11 | ## Load Balancing
12 |
13 | ```bash
14 | docker-compose build
15 | docker-compose up --scale api=4 --build
16 | docker-compose up
17 | ```
18 |
19 | ## SSL
20 |
21 | ### Generate an OpenSSL certificate
22 |
23 | On Windows, if you have _Git for Windows_ installed, then you can use the `openssl` command directly. Otherwise, the official page: [OpenSSL.Wiki: Binaries](https://wiki.openssl.org/index.php/Binaries) contains useful URLs for downloading and installation guides.
24 |
25 | ```bash
26 | openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout localhost.key -out localhost.crt -passin pass:YourSecurePassword
27 | ```
28 |
29 | This command will generate two files: `localhost.crt` and `localhost.key`.
30 |
31 | ## License
32 |
33 | Feel free to use the code in this repository as it is under MIT license.
34 |
35 |
36 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | nginx:
4 | image: nginx:alpine
5 | hostname: 'nginx'
6 | volumes:
7 | - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
8 | - ./nginx/proxy.conf:/etc/nginx/proxy.conf:ro
9 | - ./nginx/ssl/localhost.crt:/etc/ssl/certs/localhost.crt:ro
10 | - ./nginx/ssl/localhost.key:/etc/ssl/certs/localhost.key:ro
11 | - ./nginx/logs/:/var/log/nginx/
12 | ports:
13 | - '80:80'
14 | - '443:443'
15 | depends_on:
16 | - api
17 | restart: always
18 |
19 | api:
20 | build: ./src
21 | ports:
22 | - '5000'
23 | restart: always
24 |
--------------------------------------------------------------------------------
/load-balancing-http.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-labs/NginxLoadBalancer/7ee1699ba219ff3358ebc0a9a60a13808f8665cc/load-balancing-http.gif
--------------------------------------------------------------------------------
/load-balancing.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-labs/NginxLoadBalancer/7ee1699ba219ff3358ebc0a9a60a13808f8665cc/load-balancing.gif
--------------------------------------------------------------------------------
/load-balancing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-labs/NginxLoadBalancer/7ee1699ba219ff3358ebc0a9a60a13808f8665cc/load-balancing.png
--------------------------------------------------------------------------------
/nginx/nginx.conf:
--------------------------------------------------------------------------------
1 | user nginx;
2 |
3 | worker_processes auto;
4 |
5 | events { worker_connections 1024; }
6 |
7 | http {
8 | log_format upstream_time '$remote_addr $http_x_forwarded_for - $remote_user [$time_local] '
9 | '$ssl_protocol "$request" $status $body_bytes_sent '
10 | '"$http_referer" "$http_user_agent"'
11 | 'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"';
12 | access_log /var/log/nginx/access.log upstream_time;
13 |
14 | include /etc/nginx/proxy.conf;
15 | include /etc/nginx/mime.types;
16 | limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
17 | server_tokens off;
18 |
19 | sendfile on;
20 | keepalive_timeout 29; # Adjust to the lowest possible value that makes sense for your use case.
21 | client_body_timeout 10; client_header_timeout 10; send_timeout 10;
22 |
23 | upstream webapi {
24 | server api:5000;
25 | }
26 |
27 | server {
28 | listen *:80 default_server;
29 | add_header Strict-Transport-Security max-age=15768000;
30 | return 301 https://$host$request_uri;
31 | }
32 |
33 | server {
34 | listen 443 ssl;
35 | server_name $hostname;
36 | ssl_certificate /etc/ssl/certs/localhost.crt;
37 | ssl_certificate_key /etc/ssl/certs/localhost.key;
38 | ssl_protocols TLSv1.1 TLSv1.2;
39 | ssl_prefer_server_ciphers on;
40 | ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
41 | ssl_ecdh_curve secp384r1;
42 | ssl_session_cache shared:SSL:10m;
43 | ssl_session_tickets off;
44 | # ssl_stapling on; #ensure your cert is capable
45 | # ssl_stapling_verify on; #ensure your cert is capable
46 |
47 | add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always;
48 | add_header X-Frame-Options DENY;
49 | add_header X-Content-Type-Options nosniff;
50 | add_header X-Frame-Options "SAMEORIGIN";
51 |
52 | location / {
53 | proxy_pass http://webapi;
54 | limit_req zone=one burst=10 nodelay;
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/nginx/proxy.conf:
--------------------------------------------------------------------------------
1 | proxy_redirect off;
2 | proxy_http_version 1.1;
3 | proxy_set_header Upgrade $http_upgrade;
4 | proxy_cache_bypass $http_upgrade;
5 | proxy_set_header Connection keep-alive;
6 | proxy_set_header Host $host;
7 | proxy_set_header X-Real-IP $remote_addr;
8 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
9 | proxy_set_header X-Forwarded-Proto $scheme;
10 | proxy_set_header X-Forwarded-Host $server_name;
11 | client_max_body_size 10m;
12 | client_body_buffer_size 128k;
13 | proxy_connect_timeout 90;
14 | proxy_send_timeout 90;
15 | proxy_read_timeout 90;
16 | proxy_buffers 32 4k;
--------------------------------------------------------------------------------
/nginx/ssl/localhost.cer:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-labs/NginxLoadBalancer/7ee1699ba219ff3358ebc0a9a60a13808f8665cc/nginx/ssl/localhost.cer
--------------------------------------------------------------------------------
/nginx/ssl/localhost.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDWzCCAkOgAwIBAgIUEAdjIV+t/aaackYTTUB3Dkvz+9IwDQYJKoZIhvcNAQEL
3 | BQAwPTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklBMSEwHwYDVQQKDBhJbnRlcm5l
4 | dCBXaWRnaXRzIFB0eSBMdGQwHhcNMjAwNjI0MjExNjQ2WhcNMjEwNjI0MjExNjQ2
5 | WjA9MQswCQYDVQQGEwJVUzELMAkGA1UECAwCSUExITAfBgNVBAoMGEludGVybmV0
6 | IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
7 | AKy4cXduNWM9zYWWYUAMcsV0U6Vp1vIWcv5J1u79OOVIsYPNwbd59HIfBIwwS6di
8 | 4SHzkInEv0ZnhjBh2XMpD4zCOKwdbcaYd1hI/lfjA5Snq88Zcktpl/KcDve64wGQ
9 | l754TW7U545EU43hAH+72+zodtWm3xd1Nfj/bUM+SHACObPiNPUD+WaT6e7xcIIl
10 | uaXPasMeLVvQpAP9N65LTtvFDY5zQ+cK3ON5kAnV9lKYj66nJBTUDNCJfHOTh6iG
11 | OyyqU+k5XeeDNl6YHineYH9HpgVW0w0EcrZJOxTgxd3ZxKXYIaqUttRKYsN7973b
12 | XPzERtl85byqsHZ5ND7CyIECAwEAAaNTMFEwHQYDVR0OBBYEFKlV+2A6A4blPa5V
13 | 1mZ5L180YUVsMB8GA1UdIwQYMBaAFKlV+2A6A4blPa5V1mZ5L180YUVsMA8GA1Ud
14 | EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACYO1douwUt9qd8cTozUqUln
15 | pqlm+do6th/DDeeBUJi5pecgRy70edFO0NERCTEINanyBbEJpLcSZ9m03q253rHM
16 | wUErSQCvkehV2RtQIeRK1z+hjsq1JF0T3pBygSaYnsBSgc7CEu1G6ugJMvhVjI+9
17 | 6JuVWnctBj2wuShW1+b9R7Rf78FqI4fwdkc0xhPihT8KydYzFVFZhk4/6Qxmlxzj
18 | p7F8K/sxX71h/DThcokNx1JZQAGXIRQvlAkDGMzsvOHwh1i/Tgsj7nFMtxHr6hYW
19 | KyoXUanJQfp/22pM9jYtDbCOSsfFqh1OMyDTd5vxcY9O1nzxZM8ujH3u9IxVhg4=
20 | -----END CERTIFICATE-----
21 |
--------------------------------------------------------------------------------
/nginx/ssl/localhost.key:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCsuHF3bjVjPc2F
3 | lmFADHLFdFOladbyFnL+Sdbu/TjlSLGDzcG3efRyHwSMMEunYuEh85CJxL9GZ4Yw
4 | YdlzKQ+MwjisHW3GmHdYSP5X4wOUp6vPGXJLaZfynA73uuMBkJe+eE1u1OeORFON
5 | 4QB/u9vs6HbVpt8XdTX4/21DPkhwAjmz4jT1A/lmk+nu8XCCJbmlz2rDHi1b0KQD
6 | /TeuS07bxQ2Oc0PnCtzjeZAJ1fZSmI+upyQU1AzQiXxzk4eohjssqlPpOV3ngzZe
7 | mB4p3mB/R6YFVtMNBHK2STsU4MXd2cSl2CGqlLbUSmLDe/e921z8xEbZfOW8qrB2
8 | eTQ+wsiBAgMBAAECggEAV1TXQmz+H5TTQzV7LLhem8oqtRuj7Do/oKXTQHnW98/e
9 | ueciiNPoLn/Se9O7nQIZFWKi9ZX+pOmZZwNCwMDFNQXLQ/OJWv4icghOWj/VwqdY
10 | 7tlN+iWFB82yn73mJBdpMox/koeiIUQY/0cCSpPBKrOLypGagbDpPmJga7ylQcR5
11 | pCzL2FE2rWGtMfU3niodCIADgiNLQ3Pxs4Df6RLIWynrTAEQI5UaJF1G0s41fW/F
12 | 56m+OpIvhcqc+MokFmDjfmqdWJ5/SZQNnfGwHvBfRSGICv56L3bwXllFIlse81HG
13 | qlHMYGd9SCsnYHuBXBHMAuJok91nL7sMMCouG1tMAQKBgQDTwvoSPn6wNJWXGv+5
14 | h2fRlez9CfXBYA4K4YfC+meDoBmLv8pgqzvIp2CQV0roReDSahTEze+XijIgTN+W
15 | e9gArJ3qUZvkHrXSG7/Bp2LRXMDXiYA99XP2QwP6CAJasb/o4otBrNXBMlIomqO6
16 | zIET/nys5DKgKSXlkFsebmu+YQKBgQDQzYs0ks4hNhHdV14FmVVBWz0aT1HthtFO
17 | bO+ZwS4YLfR+emkK/+FcFgfmnLH5Q1m3ofHmG5KDUeXGEovTEmv5wBmMM2DBBUpV
18 | ruBRcSKVN/taKCLW5fky6q2+2vHUmUueWCrK2kd4uL+wD68CHcnfJgwegp7hj5Ow
19 | HuOvn9v+IQKBgQDEhtE6mVFOCp9B60alUmshcXjNpa60VobxxjkO1QG4gJ70uXwC
20 | lygWom/VxY25XxC1dsnrlAvlodW/cFiY+jqO9aRg0HhTD8VCR4qBGO12zBQUdIHV
21 | /rQuOie4pjI5JXmrkoQW4WZHtIHXORz2wdCRU781XVs2lLmdAYnvBh9sQQKBgFdw
22 | zEv+8o2b7A1KShiEJtzQzZzX/NJAtCRooEo+SQRdMldJAfZWQ9ThcPuVQT7GgqrR
23 | CdE4WLnfsR6qo52HyGn1lX1OAsWyZnfNXSkd/MKKiBswVa6y7hHxj0Muwzkp3S35
24 | dCoxEN8wVy3u03ZKbUCi2U5pmwmQq+OEfAsPIfyhAoGBAKLzTUlQzSqS6ofbyOqS
25 | Z1ik0PPNyWWGU9l2pNMt/VNda9LVV60ixX7Shw7A3MzEqp4QRiwqOdOxVKNxopir
26 | eRaAsGvNe6i4/Id11Kafcw0tK2d4GPCExnd0dA8WmuwAr4aAs0RPMGWKdVCDDi8h
27 | T7sYDBa+mdNjBbnLKp7rcd0E
28 | -----END PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/src/.dockerignore:
--------------------------------------------------------------------------------
1 | # directories
2 | **/bin/
3 | **/obj/
4 | **/out/
5 | **/.git/
6 | **/node_modules/
7 |
8 | # files
9 | Dockerfile*
10 | **/*.md
--------------------------------------------------------------------------------
/src/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1-alpine AS build
2 | WORKDIR /app
3 |
4 | COPY *.sln .
5 | COPY MyWebApi/*.csproj ./MyWebApi/
6 | RUN dotnet restore
7 |
8 | COPY MyWebApi/. ./MyWebApi/
9 | WORKDIR /app/MyWebApi
10 | RUN dotnet publish -c Release -o /out --no-restore
11 |
12 |
13 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine AS runtime
14 | WORKDIR /app
15 | COPY --from=build /out ./
16 | ENV ASPNETCORE_URLS http://*:5000
17 | ENTRYPOINT ["dotnet", "MyWebApi.dll"]
--------------------------------------------------------------------------------
/src/MyWebApi.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29926.136
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyWebApi", "MyWebApi\MyWebApi.csproj", "{AD4920AB-E6F2-4B85-9EAA-EBE03DC802C8}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {AD4920AB-E6F2-4B85-9EAA-EBE03DC802C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {AD4920AB-E6F2-4B85-9EAA-EBE03DC802C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {AD4920AB-E6F2-4B85-9EAA-EBE03DC802C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {AD4920AB-E6F2-4B85-9EAA-EBE03DC802C8}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {76612D6C-55D2-460C-AE88-7E4C25BC6011}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/src/MyWebApi/Controllers/WeatherForecastController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace MyWebApi.Controllers
8 | {
9 | [ApiController]
10 | [Route("[controller]")]
11 | public class WeatherForecastController : ControllerBase
12 | {
13 | private static readonly string[] Summaries = new[]
14 | {
15 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
16 | };
17 |
18 | private readonly ILogger _logger;
19 |
20 | public WeatherForecastController(ILogger logger)
21 | {
22 | _logger = logger;
23 | }
24 |
25 | [HttpGet]
26 | public IEnumerable Get()
27 | {
28 | _logger.LogInformation($"Host Name: { Environment.MachineName}");
29 | var rng = new Random();
30 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast
31 | {
32 | Date = DateTime.Now.AddDays(index),
33 | TemperatureC = rng.Next(-20, 55),
34 | Summary = Summaries[rng.Next(Summaries.Length)]
35 | })
36 | .ToArray();
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/MyWebApi/MyWebApi.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/MyWebApi/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.Hosting;
8 | using Microsoft.Extensions.Logging;
9 |
10 | namespace MyWebApi
11 | {
12 | public class Program
13 | {
14 | public static void Main(string[] args)
15 | {
16 | CreateHostBuilder(args).Build().Run();
17 | }
18 |
19 | public static IHostBuilder CreateHostBuilder(string[] args) =>
20 | Host.CreateDefaultBuilder(args)
21 | .ConfigureWebHostDefaults(webBuilder =>
22 | {
23 | webBuilder.UseStartup();
24 | });
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/MyWebApi/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Net;
4 | using System.Net.Sockets;
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.AspNetCore.Http;
8 | using Microsoft.AspNetCore.Http.Extensions;
9 | using Microsoft.AspNetCore.HttpOverrides;
10 | using Microsoft.Extensions.Configuration;
11 | using Microsoft.Extensions.DependencyInjection;
12 | using Microsoft.Extensions.Hosting;
13 |
14 | namespace MyWebApi
15 | {
16 | public class Startup
17 | {
18 | public Startup(IConfiguration configuration)
19 | {
20 | Configuration = configuration;
21 | }
22 |
23 | public IConfiguration Configuration { get; }
24 |
25 | // This method gets called by the runtime. Use this method to add services to the container.
26 | public void ConfigureServices(IServiceCollection services)
27 | {
28 | services.AddControllers();
29 | services.Configure(options =>
30 | {
31 | options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
32 | });
33 | }
34 |
35 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
36 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
37 | {
38 | app.UseForwardedHeaders(); // Forwarded Headers Middleware can run after diagnostics and error handling, but it must be be run before calling UseHsts.
39 | if (env.IsDevelopment())
40 | {
41 | app.UseDeveloperExceptionPage();
42 | }
43 |
44 | app.UseRouting();
45 |
46 | app.UseAuthorization();
47 |
48 | app.UseEndpoints(endpoints =>
49 | {
50 | endpoints.MapControllers();
51 | endpoints.MapGet("/", async context =>
52 | {
53 | context.Response.ContentType = "text/plain";
54 |
55 | // Host info
56 | var name = Dns.GetHostName(); // get container id
57 | var ip = Dns.GetHostEntry(name).AddressList.FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork);
58 | Console.WriteLine($"Host Name: { Environment.MachineName} \t {name}\t {ip}");
59 | await context.Response.WriteAsync($"Host Name: {Environment.MachineName}{Environment.NewLine}");
60 | await context.Response.WriteAsync(Environment.NewLine);
61 |
62 | // Request method, scheme, and path
63 | await context.Response.WriteAsync($"Request Method: {context.Request.Method}{Environment.NewLine}");
64 | await context.Response.WriteAsync($"Request Scheme: {context.Request.Scheme}{Environment.NewLine}");
65 | await context.Response.WriteAsync($"Request URL: {context.Request.GetDisplayUrl()}{Environment.NewLine}");
66 | await context.Response.WriteAsync($"Request Path: {context.Request.Path}{Environment.NewLine}");
67 |
68 | // Headers
69 | await context.Response.WriteAsync($"Request Headers:{Environment.NewLine}");
70 | foreach (var (key, value) in context.Request.Headers)
71 | {
72 | await context.Response.WriteAsync($"\t {key}: {value}{Environment.NewLine}");
73 | }
74 | await context.Response.WriteAsync(Environment.NewLine);
75 |
76 | // Connection: RemoteIp
77 | await context.Response.WriteAsync($"Request Remote IP: {context.Connection.RemoteIpAddress}");
78 | });
79 | });
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/MyWebApi/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace MyWebApi
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/MyWebApi/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/MyWebApi/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | # Build an ASP.NET Core Web App on Docker
2 |
3 | ```bash
4 | # in this directory
5 | docker build . -t aspnetapp
6 | docker run -it -p 5000:5000 aspnetapp
7 | ```
8 |
--------------------------------------------------------------------------------