-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathslides_deploy-to-paas.html
More file actions
515 lines (498 loc) · 40.2 KB
/
slides_deploy-to-paas.html
File metadata and controls
515 lines (498 loc) · 40.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Slides for
Deploy to PAAS — Ruby on Rails Guides
</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" data-turbo-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print">
<link rel="stylesheet" type="text/css" href="stylesheets/highlight.css" data-turbo-track="reload">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="stylesheets/reset.css">
<link rel="stylesheet" href="stylesheets/reveal.css">
<link rel="stylesheet" href="stylesheets/myslide.css" id="theme">
<link rel="stylesheet" href="stylesheets/code.css">
<script src="javascripts/clipboard.js" data-turbo-track="reload"></script>
<script src="javascripts/slides.js" data-turbo-track="reload"></script>
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section>
<h1>Deploy to PAAS</h1><p>Platform as a Service - PAAS - is one
way to deploy web apps.</p><p>After reading it you should</p>
<ul>
<li>know different cloud service models</li>
<li>know some of the 12 factors of the 12factor App</li>
<li>be able to deploy to dokku</li>
<li>be able to deploy to heroku (warning: you need a credit card and have to pay)</li>
</ul>
<p><small>Slides - use arrow keys to navigate, esc to return to page view, f for fullscreen</small></p>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-0'>▻</a>
<h2 id="what-is-cloud-computing-questionmark"><a class="anchorlink" href="#what-is-cloud-computing-questionmark"><span>1</span> What is Cloud Computing?</a></h2><p>When you want to publish your web application you can use your own
computer, connect it to the internet permanently, and do all the work
of maintaining the computer and the internet connection yourself.</p><p>Or you can use a service provider that takes care of part of the job.
Depending on how much you do yourself these services have different names.</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-1'>▻</a>
<p>This diagram <a href="https://www.redhat.com/en/topics/cloud-computing/what-is-paas">from redhat</a> shows four different scenarios:</p><p><img src="images/iaas-paas-saas.png" alt="comparison of on premise, iaas, paas, saas"></p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-2'>▻</a>
<ul>
<li><strong>On-site</strong>, also called <strong>on premise</strong>, means running your own hardware in your own room with your own internet connection.</li>
</ul>
<p>The following three scenarios all fall under the term "Cloud Computing":</p>
<ul>
<li><p>With <strong>Infrastructure as a Service</strong> the server is housed in a server room at the service
provider. Hardware, internet connecion, power are all taken care of. You rent a virtual
machine, sometimes with operating system preinstalled, and take care of everything form
the operating system upwards.</p></li>
<li><p><strong>Platform as a Service</strong> also takes care of the operating system (e.g. Linux),
running databases and other services and providing the interpreter or runtime for
the programming language. You rent space for your app plus the database(es) you need.</p></li>
<li><p>With <strong>Software as a Service</strong> there is no app to deploy, the whole stack is in
the hands of the service provider.</p></li>
</ul>
<p>For a company, moving from on premise to *aas means employing
fewer sysadmins and buying less hardware. It also means
paying a lot of money to the service provider.</p><p>Which alternative is feasibly and which one is cheaper for a
specific project depends on many factors.</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-3'>▻</a>
<h2 id="why-use-cloud-computing-questionmark"><a class="anchorlink" href="#why-use-cloud-computing-questionmark"><span>2</span> Why use Cloud Computing?</a></h2><p>The <a href="https://csrc.nist.gov/publications/detail/sp/800-145/final">NIST Definition of Cloud Computing</a>
lists five essential characteristics of cloud computing. The short version is:</p>
<ul>
<li><strong>On-demand self-service</strong>. A consumer can configure computing capabilities (storage, cpus,...) as needed, without human interaction with each service provider.</li>
<li><strong>Broad network access</strong>. Capabilities are available over the network and accessed through standard mechanisms.</li>
<li><strong>Resource pooling</strong>. The provider’s computing resources serve multiple consumers. Different physical and virtual resources are dynamically assigned and reassigned according to demand.</li>
<li><strong>Rapid elasticity</strong>. Capabilities can be elastically provisioned and released. To the consumer, the capabilities available for provisioning appear to be unlimited.</li>
<li><strong>Measured service</strong>. Resource usage can be monitored, controlled, and reported, providing transparency for both the provider and consumer of the utilized service.</li>
</ul>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-4'>▻</a>
<h2 id="public-vs-private-cloud"><a class="anchorlink" href="#public-vs-private-cloud"><span>3</span> Public vs. Private Cloud</a></h2><p>Any cloud infrastructure that is open for use by the general public is called a <strong>public cloud</strong>.
Public does not mean free, just that any paying customer can use it.
It exists on the premises of the cloud provider.</p><p>A <strong>private cloud</strong> would be iaas, paas, or saas that is run for the
exclusive use of one organisation. To be called "cloud" it should
still exhibit the five characteristics above.</p><p>In this guide we will look at one example of a public paas (heroku)
und one example of a private paas (dokku).</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-5'>▻</a>
<h2 id="using-heroku"><a class="anchorlink" href="#using-heroku"><span>4</span> Using Heroku</a></h2><p>you need to</p>
<ul>
<li>create an account on <a href="http://heroku.com">heroku</a></li>
<li>give them your credit card number, this will cost money!</li>
<li>and install the <a href="https://devcenter.heroku.com/articles/heroku-cli">heroku command line interface (cli)</a></li>
<li>add your public key to heroku by running <code>heroku keys:add</code></li>
</ul>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-6'>▻</a>
<h2 id="using-dokku-with-ssh"><a class="anchorlink" href="#using-dokku-with-ssh"><span>5</span> Using Dokku with ssh</a></h2><p>if you are reading this as part of your course at Fachhochschule Salzburg
you already have an account on our local dokku install.</p><p>If you have your own linux VM that can work as a server, you can <a href="https://dokku.com/docs/getting-started/installation/">install dokku</a> on it, it is open source.</p><p>To send commands to dokku you can use ssh. For example:</p><div class="interstitial code">
<pre><code class="highlight plaintext">ssh -p PORT dokku@HOSTNAME config appname # will output the evironment variables
ssh -p PORT dokku@HOSTNAME logs appname # will output the last part of the logfile
ssh -p PORT dokku@HOSTNAME run --force-tty appname bin/rails console # interactive rails console
</code></pre>
<button class="clipboard-button" data-clipboard-text="ssh -p PORT dokku@HOSTNAME config appname # will output the evironment variables
ssh -p PORT dokku@HOSTNAME logs appname # will output the last part of the logfile
ssh -p PORT dokku@HOSTNAME run --force-tty appname bin/rails console # interactive rails console
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-7'>▻</a>
<h2 id="using-dokku-with-the-client"><a class="anchorlink" href="#using-dokku-with-the-client"><span>6</span> Using Dokku with the client</a></h2><p>To install the dokku <strong>client</strong> on your development machine: if it's a mac:</p><div class="interstitial code">
<pre><code class="highlight plaintext">brew install dokku/repo/dokku
</code></pre>
<button class="clipboard-button" data-clipboard-text="brew install dokku/repo/dokku
">Copy</button>
</div>
<p>If it's ubuntu:</p><div class="interstitial code">
<pre><code class="highlight plaintext">git clone https://github.com/dokku/dokku.git ~/.dokku
# Add this line to ~/.bashrc or ~/.zshrc
echo "alias dokku='bash \$HOME/.dokku/contrib/dokku_client.sh'" >> ~/.bashrc
</code></pre>
<button class="clipboard-button" data-clipboard-text="git clone https://github.com/dokku/dokku.git ~/.dokku
# Add this line to ~/.bashrc or ~/.zshrc
echo "alias dokku='bash \$HOME/.dokku/contrib/dokku_client.sh'" >> ~/.bashrc
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-8'>▻</a>
<h2 id="the-12factor-app"><a class="anchorlink" href="#the-12factor-app"><span>7</span> The 12factor App</a></h2><p>In 2011 the document "the 12factor app" was published by developers by
the company heroku. It describes how to prepare a web app
for running on a paas.</p><p>Many of these 12 factors have become commonplace for web development -
you probably have been doing some of it all along. But let's look
at each one in turn. A link to the original document is
always provided in the first sentence, please read the original first!</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-9'>▻</a>
<h3 id="i-codebase"><a class="anchorlink" href="#i-codebase"><span>7.1</span> I. Codebase</a></h3><p><a href="https://12factor.net/codebase">One codebase tracked in revision control, many deploys</a></p><p>Today, git is used for all new projects.</p><p>"Multiple apps sharing the same code"
is explicitly discouraged for a 12 factor app. Here there is no industry consensus.
Some companys use one app per repo, some others use Monorepos that contain many
different apps and libraries, most famously Google. (If you have as much code
as google git will not suffice for a monorepo)</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-10'>▻</a>
<h3 id="ii-dependencies"><a class="anchorlink" href="#ii-dependencies"><span>7.2</span> II. Dependencies</a></h3><p><a href="https://12factor.net/dependencies">Explicitly declare and isolate dependencies</a></p><p>Dependency declaration through a file like <code>Gemfile</code>, <code>package.json</code>, <code>composer.json</code>
is available in all modern languages for the web.</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-11'>▻</a>
<h3 id="iii-config"><a class="anchorlink" href="#iii-config"><span>7.3</span> III. Config</a></h3><p><a href="https://12factor.net/config">Store config in the environment</a></p><p>You can see the environment variables in both dokku and heroku by
running the <code>config</code> command:</p><div class="interstitial code">
<pre><code class="highlight plaintext">$ dokku config
=====> joe19 env vars
DATABASE_URL: postgres://postgres:a051e0ff74c1@dokku-postgres-jobboarddemo-db:5432/jobboarddemo_db
DOKKU_APP_RESTORE: 1
DOKKU_APP_TYPE: dockerfile
DOKKU_PROXY_PORT: 80
DOKKU_PROXY_SSL_PORT: 443
GIT_REV: 9d6a962ec7cf0070a395f1f58cf10dc6e65afed3
RAILS_MASTER_KEY: cf22a74f420883ef254e3a7c023966eb
REDIS_URL: redis://:feb28989602d9b296c812da56@dokku-redis-jobboarddemo-redis:6379
</code></pre>
<button class="clipboard-button" data-clipboard-text="$ dokku config
=====> joe19 env vars
DATABASE_URL: postgres://postgres:a051e0ff74c1@dokku-postgres-jobboarddemo-db:5432/jobboarddemo_db
DOKKU_APP_RESTORE: 1
DOKKU_APP_TYPE: dockerfile
DOKKU_PROXY_PORT: 80
DOKKU_PROXY_SSL_PORT: 443
GIT_REV: 9d6a962ec7cf0070a395f1f58cf10dc6e65afed3
RAILS_MASTER_KEY: cf22a74f420883ef254e3a7c023966eb
REDIS_URL: redis://:feb28989602d9b296c812da56@dokku-redis-jobboarddemo-redis:6379
">Copy</button>
</div>
<p>or for another app:</p><div class="interstitial code">
<pre><code class="highlight plaintext">$ heroku config
=== obscure-springs-61542 Config Vars
DATABASE_URL: postgres://erout:1baa7f8@ec2-34-200-106-49.compute-1.amazonaws.com:5432/d3jjlc4
LANG: en_US.UTF-8
RACK_ENV: production
RAILS_ENV: production
RAILS_LOG_TO_STDOUT: enabled
RAILS_SERVE_STATIC_FILES: enabled
SECRET_KEY_BASE: 39a9fdb1549bfc2042b1d227682d3a2a5eebd26377461a479c727f6f0ef
</code></pre>
<button class="clipboard-button" data-clipboard-text="$ heroku config
=== obscure-springs-61542 Config Vars
DATABASE_URL: postgres://erout:1baa7f8@ec2-34-200-106-49.compute-1.amazonaws.com:5432/d3jjlc4
LANG: en_US.UTF-8
RACK_ENV: production
RAILS_ENV: production
RAILS_LOG_TO_STDOUT: enabled
RAILS_SERVE_STATIC_FILES: enabled
SECRET_KEY_BASE: 39a9fdb1549bfc2042b1d227682d3a2a5eebd26377461a479c727f6f0ef
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-12'>▻</a>
<h3 id="iv-backing-services"><a class="anchorlink" href="#iv-backing-services"><span>7.4</span> IV. Backing services</a></h3><p><a href="https://12factor.net/backing-services">Treat backing services as attached resources</a></p><p><img src="images/dokku-app.svg" alt="dokku app"></p><p>If you look back at the environment variables above
you can see the variable <code>DATABASE_URL</code>. This contains
all the information needed to connect to the database -
type of database, username, password, host, port, name of the database.</p><div class="interstitial code">
<pre><code class="highlight plaintext">DATABASE_URL: postgres://erout:1baa7f8@ec2-34-200-106-49.compute-1.amazonaws.com:5432/d3jjlc4
</code></pre>
<button class="clipboard-button" data-clipboard-text="DATABASE_URL: postgres://erout:1baa7f8@ec2-34-200-106-49.compute-1.amazonaws.com:5432/d3jjlc4
">Copy</button>
</div>
<p>Here you can also see that Heroku uses Amazon AWS to run the database server.
To Heroku buys iaas from aws and sells paas to us.</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-13'>▻</a>
<h3 id="v-build-release-run"><a class="anchorlink" href="#v-build-release-run"><span>7.5</span> V. Build, release, run</a></h3><p><a href="https://12factor.net/build-release-run">Strictly separate build and run stages</a></p><p>Here we first meet a limitation of the 12 factor app when
we compare it to deploying PHP via SFTP:
It is impossible to make changes to the code at runtime.</p><p>If you change the code, you need to commit, push to the paas, where the app it will be built. It will take a minute for the new version of the app to be deployed.</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-14'>▻</a>
<h3 id="vi-processes"><a class="anchorlink" href="#vi-processes"><span>7.6</span> VI. Processes</a></h3><p><a href="https://12factor.net/processes">Execute the app as one or more stateless processes</a></p><p>In the beginning, your app will run as one process.</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-15'>▻</a>
<h3 id="vii-port-binding"><a class="anchorlink" href="#vii-port-binding"><span>7.7</span> VII. Port binding</a></h3><p><a href="https://12factor.net/port-binding">Export services via port binding</a></p><p>Each app offers a HTTP server to the outside world. You will need to declare the port your HTTP server is running on. For example for Ruby on Rails this is typically port 3000.</p><p><img src="images/dokku-port.svg" alt="dokku app"></p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-16'>▻</a>
<h3 id="viii-concurrency"><a class="anchorlink" href="#viii-concurrency"><span>7.8</span> VIII. Concurrency</a></h3><p><a href="https://12factor.net/concurrency">Scale out via the process model</a></p><p>In the beginning, your app will run as one process. Later on you might need to scale up and run 2, 3, 4 processes in parallel.
The PAAS will take care of routing different requests to different processes of your app.</p><p><img src="images/dokku-scale-app.svg" alt="dokku app"></p><p>The app itself needs to be stateless.</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-17'>▻</a>
<h3 id="ix-disposability"><a class="anchorlink" href="#ix-disposability"><span>7.9</span> IX. Disposability</a></h3><p><a href="https://12factor.net/disposability">Maximize robustness with fast startup and graceful shutdown</a></p><p>Each app process is disposable - it can be shut down quickly, and nothing is lost. Data is only stored permanently in backing services like a database, or in a storage service or volume.</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-18'>▻</a>
<h3 id="x-dev-prod-parity"><a class="anchorlink" href="#x-dev-prod-parity"><span>7.10</span> X. Dev/prod parity</a></h3><p><a href="https://12factor.net/dev-prod-parity">Keep development, staging, and production as similar as possible</a></p><p>Yes.</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-19'>▻</a>
<h3 id="xi-logs"><a class="anchorlink" href="#xi-logs"><span>7.11</span> XI. Logs</a></h3><p><a href="https://12factor.net/logs">Treat logs as event streams</a></p><p>You can see the log in both dokku and heroku by
running the <code>logs</code> command:</p><div class="interstitial code">
<pre><code class="highlight plaintext">$ dokku logs
2025-11-24T20:30:46.315963919Z app[web.1]: [816d2f43-c4c0-4521-89a0-ffc2f243596f] Started GET "/jobs/2" for 194.166.209.208 at 2025-11-24 20:30:46 +0000
2025-11-24T20:30:46.318252143Z app[web.1]: [816d2f43-c4c0-4521-89a0-ffc2f243596f] Processing by JobsController#show as HTML
2025-11-24T20:30:46.318290592Z app[web.1]: [816d2f43-c4c0-4521-89a0-ffc2f243596f] Parameters: {"id" => "2"}
2025-11-24T20:30:46.487038122Z app[web.1]: [816d2f43-c4c0-4521-89a0-ffc2f243596f] Rendered layout layouts/application.html.erb (Duration: 34.1ms | GC: 0.1ms)
2025-11-24T20:30:46.487290093Z app[web.1]: [816d2f43-c4c0-4521-89a0-ffc2f243596f] Completed 200 OK in 169ms (Views: 35.3ms | ActiveRecord: 99.4ms (2 queries, 0 cached) | GC: 0.5ms)
2025-11-24T20:30:46.490611938Z app[web.1]: {"time":"2025-11-24T20:30:46.490419862Z","level":"INFO","msg":"Request","path":"/jobs/2","status":200,"dur":187,"method":"GET","req_content_length":0,"req_content_type":"","resp_content_length":2270,"resp_content_type":"text/html; charset=utf-8","remote_addr":"194.166.209.208","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:146.0) Gecko/20100101 Firefox/146.0","cache":"miss","query":"","proto":"HTTP/1.1"}
2025-11-24T20:30:46.589852933Z app[web.1]: {"time":"2025-11-24T20:30:46.589414199Z","level":"INFO","msg":"Unable to proxy request","path":"/assets/application-bfcdf840.js","error":"context canceled"}
2025-11-24T20:30:46.589891496Z app[web.1]: {"time":"2025-11-24T20:30:46.58952369Z","level":"INFO","msg":"Request","path":"/assets/style-d769b782.css","status":200,"dur":5,"method":"GET","req_content_length":0,"req_content_type":"","resp_content_length":690,"resp_content_type":"text/css","remote_addr":"194.166.209.208","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:146.0) Gecko/20100101 Firefox/146.0","cache":"miss","query":"","proto":"HTTP/1.1"}
2025-11-24T20:30:46.589903889Z app[web.1]: {"time":"2025-11-24T20:30:46.589438843Z","level":"INFO","msg":"Unable to proxy request","path":"/assets/stimulus.min-4b1e420e.js","error":"context canceled"}
</code></pre>
<button class="clipboard-button" data-clipboard-text="$ dokku logs
2025-11-24T20:30:46.315963919Z app[web.1]: [816d2f43-c4c0-4521-89a0-ffc2f243596f] Started GET "/jobs/2" for 194.166.209.208 at 2025-11-24 20:30:46 +0000
2025-11-24T20:30:46.318252143Z app[web.1]: [816d2f43-c4c0-4521-89a0-ffc2f243596f] Processing by JobsController#show as HTML
2025-11-24T20:30:46.318290592Z app[web.1]: [816d2f43-c4c0-4521-89a0-ffc2f243596f] Parameters: {"id" => "2"}
2025-11-24T20:30:46.487038122Z app[web.1]: [816d2f43-c4c0-4521-89a0-ffc2f243596f] Rendered layout layouts/application.html.erb (Duration: 34.1ms | GC: 0.1ms)
2025-11-24T20:30:46.487290093Z app[web.1]: [816d2f43-c4c0-4521-89a0-ffc2f243596f] Completed 200 OK in 169ms (Views: 35.3ms | ActiveRecord: 99.4ms (2 queries, 0 cached) | GC: 0.5ms)
2025-11-24T20:30:46.490611938Z app[web.1]: {"time":"2025-11-24T20:30:46.490419862Z","level":"INFO","msg":"Request","path":"/jobs/2","status":200,"dur":187,"method":"GET","req_content_length":0,"req_content_type":"","resp_content_length":2270,"resp_content_type":"text/html; charset=utf-8","remote_addr":"194.166.209.208","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:146.0) Gecko/20100101 Firefox/146.0","cache":"miss","query":"","proto":"HTTP/1.1"}
2025-11-24T20:30:46.589852933Z app[web.1]: {"time":"2025-11-24T20:30:46.589414199Z","level":"INFO","msg":"Unable to proxy request","path":"/assets/application-bfcdf840.js","error":"context canceled"}
2025-11-24T20:30:46.589891496Z app[web.1]: {"time":"2025-11-24T20:30:46.58952369Z","level":"INFO","msg":"Request","path":"/assets/style-d769b782.css","status":200,"dur":5,"method":"GET","req_content_length":0,"req_content_type":"","resp_content_length":690,"resp_content_type":"text/css","remote_addr":"194.166.209.208","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:146.0) Gecko/20100101 Firefox/146.0","cache":"miss","query":"","proto":"HTTP/1.1"}
2025-11-24T20:30:46.589903889Z app[web.1]: {"time":"2025-11-24T20:30:46.589438843Z","level":"INFO","msg":"Unable to proxy request","path":"/assets/stimulus.min-4b1e420e.js","error":"context canceled"}
">Copy</button>
</div>
<p>or for another app:</p><div class="interstitial code">
<pre><code class="highlight plaintext">$ heroku logs
2020-11-17T23:07:37.373849+00:00 heroku[router]: at=info method=GET path="/stylesheets/application.css" host=obscure-springs-61542.herokuapp.com request_id=d3e9ae87-bf4f-4c7e-b640-9663a184b8b5 fwd="193.171.53.146" dyno=web.1 connect=0ms service=3ms status=404 bytes=1902 protocol=https
2020-11-17T23:07:39.869495+00:00 app[web.1]: I, [2020-11-17T23:07:39.869410 #4] INFO -- : [11aade3c-23be-4927-a1fb-73432521b203] Started GET "/drinks" for 193.171.53.146 at 2020-11-17 23:07:39 +0000
2020-11-17T23:07:39.870216+00:00 app[web.1]: I, [2020-11-17T23:07:39.870132 #4] INFO -- : [11aade3c-23be-4927-a1fb-73432521b203] Processing by DrinksController#index as HTML
2020-11-17T23:07:39.871120+00:00 app[web.1]: I, [2020-11-17T23:07:39.871051 #4] INFO -- : [11aade3c-23be-4927-a1fb-73432521b203] Rendering drinks/index.html.erb within layouts/application
2020-11-17T23:07:39.873153+00:00 app[web.1]: D, [2020-11-17T23:07:39.873081 #4] DEBUG -- : [11aade3c-23be-4927-a1fb-73432521b203] Drink Load (0.8ms) SELECT "drinks".* FROM "drinks"
2020-11-17T23:07:39.873890+00:00 app[web.1]: I, [2020-11-17T23:07:39.873815 #4] INFO -- : [11aade3c-23be-4927-a1fb-73432521b203] Rendered drinks/index.html.erb within layouts/application (Duration: 2.6ms | Allocations: 288)
2020-11-17T23:07:39.874513+00:00 app[web.1]: I, [2020-11-17T23:07:39.874442 #4] INFO -- : [11aade3c-23be-4927-a1fb-73432521b203] Completed 200 OK in 4ms (Views: 2.8ms | ActiveRecord: 0.8ms | Allocations: 885)
2020-11-17T23:07:39.875779+00:00 heroku[router]: at=info method=GET path="/drinks" host=obscure-springs-61542.herokuapp.com request_id=11aade3c-23be-4927-a1fb-73432521b203 fwd="193.171.53.146" dyno=web.1 connect=0ms service=7ms status=200 bytes=1937 protocol=https
</code></pre>
<button class="clipboard-button" data-clipboard-text="$ heroku logs
2020-11-17T23:07:37.373849+00:00 heroku[router]: at=info method=GET path="/stylesheets/application.css" host=obscure-springs-61542.herokuapp.com request_id=d3e9ae87-bf4f-4c7e-b640-9663a184b8b5 fwd="193.171.53.146" dyno=web.1 connect=0ms service=3ms status=404 bytes=1902 protocol=https
2020-11-17T23:07:39.869495+00:00 app[web.1]: I, [2020-11-17T23:07:39.869410 #4] INFO -- : [11aade3c-23be-4927-a1fb-73432521b203] Started GET "/drinks" for 193.171.53.146 at 2020-11-17 23:07:39 +0000
2020-11-17T23:07:39.870216+00:00 app[web.1]: I, [2020-11-17T23:07:39.870132 #4] INFO -- : [11aade3c-23be-4927-a1fb-73432521b203] Processing by DrinksController#index as HTML
2020-11-17T23:07:39.871120+00:00 app[web.1]: I, [2020-11-17T23:07:39.871051 #4] INFO -- : [11aade3c-23be-4927-a1fb-73432521b203] Rendering drinks/index.html.erb within layouts/application
2020-11-17T23:07:39.873153+00:00 app[web.1]: D, [2020-11-17T23:07:39.873081 #4] DEBUG -- : [11aade3c-23be-4927-a1fb-73432521b203] Drink Load (0.8ms) SELECT "drinks".* FROM "drinks"
2020-11-17T23:07:39.873890+00:00 app[web.1]: I, [2020-11-17T23:07:39.873815 #4] INFO -- : [11aade3c-23be-4927-a1fb-73432521b203] Rendered drinks/index.html.erb within layouts/application (Duration: 2.6ms | Allocations: 288)
2020-11-17T23:07:39.874513+00:00 app[web.1]: I, [2020-11-17T23:07:39.874442 #4] INFO -- : [11aade3c-23be-4927-a1fb-73432521b203] Completed 200 OK in 4ms (Views: 2.8ms | ActiveRecord: 0.8ms | Allocations: 885)
2020-11-17T23:07:39.875779+00:00 heroku[router]: at=info method=GET path="/drinks" host=obscure-springs-61542.herokuapp.com request_id=11aade3c-23be-4927-a1fb-73432521b203 fwd="193.171.53.146" dyno=web.1 connect=0ms service=7ms status=200 bytes=1937 protocol=https
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-20'>▻</a>
<h3 id="xii-admin-processes"><a class="anchorlink" href="#xii-admin-processes"><span>7.12</span> XII. Admin processes</a></h3><p><a href="https://12factor.net/admin-processes">Run admin/management tasks as one-off processes</a></p><p>You can run other processes in both dokku and heroku by
using the <code>run</code> command:</p><div class="interstitial code">
<pre><code class="highlight plaintext">dokku run rails db:migrate
</code></pre>
<button class="clipboard-button" data-clipboard-text="dokku run rails db:migrate
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight plaintext">heroku run rails db:migrate
</code></pre>
<button class="clipboard-button" data-clipboard-text="heroku run rails db:migrate
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-21'>▻</a>
<h2 id="first-steps-with-dokku"><a class="anchorlink" href="#first-steps-with-dokku"><span>8</span> First steps with dokku</a></h2><p>To deploy a Ruby on Rails app to dokku you need to:</p>
<ol>
<li>on the server: create an app on dokku, plus postgres as the backing service</li>
<li>on your development machine: add a new <code>git remote</code>. you have been using <code>origin</code>, the remote for dokku is called <code>dokku</code></li>
<li>make sure in <code>config/database.yml</code> the link to the production database is set correctly</li>
<li>set the environment variable for the master.key</li>
<li>commit</li>
<li>push the main branch to dokku using git</li>
<li>run <code>rails db:migrate</code>, and maybe <code>rails db:seed</code> on the paas</li>
</ol>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-22'>▻</a>
<h3 id="step-1-create-an-app-and-a-backing-service"><a class="anchorlink" href="#step-1-create-an-app-and-a-backing-service"><span>8.1</span> Step 1: Create an app and a backing service</a></h3><p>This is done on the dokku server on the commandline. Here an example for an app called <code>jobpichu</code>:</p><div class="interstitial code">
<pre><code class="highlight plaintext">dokku apps:create jobpichu
dokku domains:set jobpichu jobpichu.projects.ct.fh-salzburg.ac.at
dokku postgres:create jobpichu-db
dokku postgres:link jobpichu-db jobpichu
dokku storage:ensure-directory jobpichu
dokku storage:mount jobpichu /var/lib/dokku/data/storage/jobpichu:/app/storage
dokku nginx:set jobpichu client-max-body-size 5m
dokku proxy:build-config jobpichu
dokku letsencrypt:enable jobpichu
echo '========= now you should deploy to ssh://dokku@projects.ct.fh-salzburg.ac.at:5412/jobpichu =========='
echo '========== your app will be available as https://jobpichu.projects.ct.fh-salzburg.at =========='
</code></pre>
<button class="clipboard-button" data-clipboard-text="dokku apps:create jobpichu
dokku domains:set jobpichu jobpichu.projects.ct.fh-salzburg.ac.at
dokku postgres:create jobpichu-db
dokku postgres:link jobpichu-db jobpichu
dokku storage:ensure-directory jobpichu
dokku storage:mount jobpichu /var/lib/dokku/data/storage/jobpichu:/app/storage
dokku nginx:set jobpichu client-max-body-size 5m
dokku proxy:build-config jobpichu
dokku letsencrypt:enable jobpichu
echo '========= now you should deploy to ssh://dokku@projects.ct.fh-salzburg.ac.at:5412/jobpichu =========='
echo '========== your app will be available as https://jobpichu.projects.ct.fh-salzburg.at =========='
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-23'>▻</a>
<h3 id="step-2-add-a-new-remote"><a class="anchorlink" href="#step-2-add-a-new-remote"><span>8.2</span> Step 2: Add a new remote</a></h3><p>A Git remote is essentially a link to another copy of your Git repository, usually hosted on a server somewhere on the internet.</p><p>You have used a remote called <code>origin</code> before - it is set up every time you clone a repository, for example from github.com.</p><p>Every time you push you specify which remote to push to:</p><div class="interstitial code">
<pre><code class="highlight plaintext"># git push <remote> <branch>
git push origin main
</code></pre>
<button class="clipboard-button" data-clipboard-text="# git push <remote> <branch>
git push origin main
">Copy</button>
</div>
<p>This pushes the local branch main to the remote origin, and the branch main there.</p><p>To deploy to dokku, you also use git push. But first you have to tell git the address of the repository to push to.
For example if your address is <code>ssh://dokku@projects.ct.fh-salzburg.ac.at:5412/jobpichu</code> you should configure it like this:</p><div class="interstitial code">
<pre><code class="highlight plaintext">git remote add dokku ssh://dokku@projects.ct.fh-salzburg.ac.at:5412/jobpichu
</code></pre>
<button class="clipboard-button" data-clipboard-text="git remote add dokku ssh://dokku@projects.ct.fh-salzburg.ac.at:5412/jobpichu
">Copy</button>
</div>
<p>If this works, you can push to dokku (later)</p><div class="interstitial code">
<pre><code class="highlight plaintext">git push dokku main
</code></pre>
<button class="clipboard-button" data-clipboard-text="git push dokku main
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-24'>▻</a>
<h3 id="step-3-check-the-database-configuration"><a class="anchorlink" href="#step-3-check-the-database-configuration"><span>8.3</span> Step 3. Check the database configuration</a></h3><p>Have a look at the file <code>config/database.yml</code>. There you will find the configuration of the database for the three
different environments: development, test and production. The entry for production should look like this:</p><div class="interstitial code">
<pre><code class="highlight plaintext">production:
url: <%= ENV["DATABASE_URL"] %>
</code></pre>
<button class="clipboard-button" data-clipboard-text="production:
url: <%= ENV["DATABASE_URL"] %>
">Copy</button>
</div>
<p>Make sure to get the indentation right! The file format is yaml (yet another markup language). It does not use
parenthesis or curly brackets to define the structure, only indentation.</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-25'>▻</a>
<p>In a terminal inside your project folder run the command</p><div class="interstitial code">
<pre><code class="highlight plaintext">dokku config
</code></pre>
<button class="clipboard-button" data-clipboard-text="dokku config
">Copy</button>
</div>
<p>You should see an entry like</p><div class="interstitial code">
<pre><code class="highlight plaintext">DATABASE_URL: postgres://postgres:a051e0ff74c1@dokku-postgres-jobpichu-db:5432/jobpichu_db
</code></pre>
<button class="clipboard-button" data-clipboard-text="DATABASE_URL: postgres://postgres:a051e0ff74c1@dokku-postgres-jobpichu-db:5432/jobpichu_db
">Copy</button>
</div>
<p>This information will be used to connect to the database. It looks like a URL and can be parsed like one:</p>
<ul>
<li>it is a postgres database</li>
<li>the username is postgres</li>
<li>the password is a051e0ff74c1</li>
<li>the hostname to connect to is dokku-postgres-jobpichu-db</li>
<li>the port number is 5432</li>
<li>the database to connect to is named jobpichu_db</li>
</ul>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-26'>▻</a>
<h3 id="step-4-set-the-environment-variable-for-the-master-key"><a class="anchorlink" href="#step-4-set-the-environment-variable-for-the-master-key"><span>8.4</span> Step 4: set the environment variable for the master.key</a></h3><p>Rails let's you stores secrets (like API keys) in an encrypted file. Actually it has already
created that encrypted file, and added it to git.</p>
<ul>
<li>the encrypted file <code>config/credentials.yml.enc</code> was added to git</li>
<li>the key for decryption was stored in <code>config/master.key</code> and not added to git, but to .gitignore</li>
</ul>
<p>You could edit the encrypted file like this:</p><div class="interstitial code">
<pre><code class="highlight plaintext">VISUAL="code --wait" bin/rails credentials:edit
</code></pre>
<button class="clipboard-button" data-clipboard-text="VISUAL="code --wait" bin/rails credentials:edit
">Copy</button>
</div>
<p>On your local machine <code>config/master.key</code> is used. On dokku, this information needs to be stored in an environment variable.
This is how you set the variable:</p><div class="interstitial code">
<pre><code class="highlight plaintext">dokku config:set --no-restart RAILS_MASTER_KEY=**********************
</code></pre>
<button class="clipboard-button" data-clipboard-text="dokku config:set --no-restart RAILS_MASTER_KEY=**********************
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-27'>▻</a>
<h3 id="step-5-commit"><a class="anchorlink" href="#step-5-commit"><span>8.5</span> Step 5: commit</a></h3><p>Check if you have changes in your local code base (with <code>git status</code>). If there are any, make sure
to commit. Only committed code will be deployed!</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-28'>▻</a>
<h3 id="step-6-push-the-main-branch-to-dokku-using-git"><a class="anchorlink" href="#step-6-push-the-main-branch-to-dokku-using-git"><span>8.6</span> Step 6: push the main branch to dokku using git</a></h3><div class="interstitial code">
<pre><code class="highlight plaintext">git push dokku main
</code></pre>
<button class="clipboard-button" data-clipboard-text="git push dokku main
">Copy</button>
</div>
<p>You will see a lot of output: you can watch how the app is being built.
At the end of the process you hopefully see:</p><div class="interstitial code">
<pre><code class="highlight plaintext">=====> Application deployed:
http://jobpichu.projects.ct.fh-salzburg.ac.at
https://jobpichu.projects.ct.fh-salzburg.ac.at
</code></pre>
<button class="clipboard-button" data-clipboard-text="=====> Application deployed:
http://jobpichu.projects.ct.fh-salzburg.ac.at
https://jobpichu.projects.ct.fh-salzburg.ac.at
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-29'>▻</a>
<h3 id="step-7-migrations-and-seeds"><a class="anchorlink" href="#step-7-migrations-and-seeds"><span>8.7</span> Step 7: Migrations and Seeds</a></h3><p>Now your app is deployed, it has a connection to a database, but the database is empty.</p><p>If you had this situation on your local machine, you would run</p><div class="interstitial code">
<pre><code class="highlight plaintext">rails db:migrate
</code></pre>
<button class="clipboard-button" data-clipboard-text="rails db:migrate
">Copy</button>
</div>
<p>You can also do this on the dokku server, inside the deployed app, by adding <code>dokku run</code> before the command:</p><div class="interstitial code">
<pre><code class="highlight plaintext">dokku run rails db:migrate
</code></pre>
<button class="clipboard-button" data-clipboard-text="dokku run rails db:migrate
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-30'>▻</a>
<h3 id="problems-questionmark"><a class="anchorlink" href="#problems-questionmark"><span>8.8</span> Problems?</a></h3><p>Are there still problems with your app? Have a look at the log files by running</p><div class="interstitial code">
<pre><code class="highlight plaintext">dokku logs
</code></pre>
<button class="clipboard-button" data-clipboard-text="dokku logs
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-31'>▻</a>
<h2 id="using-dokku"><a class="anchorlink" href="#using-dokku"><span>9</span> Using dokku</a></h2><p>Take a minute to look back at all the steps:</p>
<ul>
<li>which steps will be necessary when you want to deploy a new version of your app?</li>
<li>which steps will be necessary if a new team member wants to set up the development environment on their own machine?</li>
</ul>
</section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-32'>▻</a>
<h2 id="using-dokku-for-other-types-of-apps"><a class="anchorlink" href="#using-dokku-for-other-types-of-apps"><span>10</span> Using dokku for other types of apps</a></h2><p>Dokku is not connected to Ruby on Rails. You can also deploy php, node.js, python projects, or anything that
can be built and run with a Dockerfile</p></section>
<section><a class='slide_break' href='deploy-to-paas.html#slide-33'>▻</a>
<h2 id="see-also"><a class="anchorlink" href="#see-also"><span>11</span> See Also</a></h2>
<ul>
<li><a href="https://csrc.nist.gov/publications/detail/sp/800-145/final">NIST Definition of Cloud Computing</a></li>
<li><a href="https://xenitab.github.io/blog/2022/02/23/12factor/">Anders Qvist: Twelve-factor app anno 2022</a></li>
</ul>
</div></section>
</div>
</div>
<!-- End slides. -->
<!-- Required JS files. -->
<script src="javascripts/reveal.js"></script>
<script src="javascripts/search.js"></script>
<script src="javascripts/markdown.js"></script>
<script>
// Also available as an ES module, see:
// https://revealjs.com/initialization/
Reveal.initialize({
controls: false,
progress: true,
center: false,
hash: true,
// The "normal" size of the presentation, aspect ratio will
// be preserved when the presentation is scaled to fit different
// resolutions. Can be specified using percentage units.
width: 1000,
height: 600,
disableLayout: false,
// Factor of the display size that should remain empty around
// the content
margin: 0.05,
// Bounds for smallest/largest possible scale to apply to content
minScale: 0.2,
maxScale: 10.0,
keyboard: {
27: () => {
// do something custom when ESC is pressed
var new_url = window.location.pathname.replace('slides_', '') + window.location.hash.replace('/','slide-');
window.location = new_url;
},
191: 'toggleHelp',
13: 'next', // go to the next slide when the ENTER key is pressed
},
// Learn about plugins: https://revealjs.com/plugins/
plugins: [ RevealSearch, RevealMarkdown ]
});
</script>
</body>
</html>