Categories
CICD Development Docker Infrastructure Linux

Databases with dokku

This is part 2 of a couple of blog posts about dokku, a amazing little Heroku clone.

In the previous post I showed how to set up Dokku on a DigitalOcean droplet, and deployed a little hello-world container with a single git push. The reason why I wanted dokku thoug was the need of a database. As said – hosting comes cheap, databases usually come either expensive or with limited flexibility, or just too annoying configuration effort.

Dokku is the perferct middle ground. Let’s see why.

For me it was the existing postgres plugin which you can simply install and use. The whole process is incredibly easy, takes wbout two commands, and looks like this (let’s assume our “hello world” container uses a database):

$ sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres

That’s it, again.

$ dokku postgres:create hello-world

dokku postgres:create hello-world
Waiting for container to be ready
Creating container database Securing connection to database
=====> Postgres container created: hello-world
=====> Container Information
       Config dir: /var/lib/dokku/services/postgres/hello-world/config
       Data dir: /var/lib/dokku/services/postgres/hello-world/data
       Dsn: postgres://postgres:bd6b0725d710bb5a662bb628eee787b1@dokku-postgres-hello-world:5432/hello_world
       Exposed ports: -
       Id: 785ef252c748ed85739d1d6ad375a1e1bd66e925ac79358e9ffaa30ab852d6c0 
       Internal ip: 172.17.0.9
       Links: -
       Service root: /var/lib/dokku/services/postgres/hello-world
       Status: running
       Version: postgres:10.2

$ docker ps

CONTAINER ID   IMAGE                      COMMAND                  CREATED         STATUS         PORTS      NAMES
cc99cccacf2c   dokku/hello-world:latest   "/bin/sh -c 'php-fpm…"   2 minutes ago   Up 2 minutes   80/tcp     hello-world.web.1
785ef252c748   postgres:10.2              "docker-entrypoint.s…"   5 minutes ago   Up 5 minutes   5432/tcp   dokku.postgres.hello-world
[...]

This creates a database container with postgres 10.2, as you can see. You can influence a lot of behavior by using environment variables, see the GitHub page for more info.

Then you link the container to the running app:

$ dokku postgres:link hello-world hello-world
-----> Setting config vars
       DATABASE_URL: postgres://postgres:bd6b0725d710bb5a662bb628eee787b1@dokku-postgres-hello-world:5432/hello_world
-----> Restarting app hello-world
-----> Releasing hello-world (dokku/hello-world:latest)...
-----> Deploying hello-world (dokku/hello-world:latest)...
-----> Attempting to run scripts.dokku.predeploy from app.json (if defined)
-----> No Procfile found in app image
-----> DOKKU_SCALE file found (/home/dokku/hello-world/DOKKU_SCALE)
=====> web=1
-----> Attempting pre-flight checks 
       For more efficient zero downtime deployments, create a file CHECKS. 
       See http://dokku.viewdocs.io/dokku/deployment/zero-downtime-deploys/ for examples 
       CHECKS file not found in container: Running simple container check...
-----> Waiting for 10 seconds ...
-----> Default container check successful!
-----> Running post-deploy
-----> Configuring hello-world.my-paas.for-myself.com...(using built-in template)
-----> Creating http nginx.conf
-----> Running nginx-pre-reload Reloading nginx
-----> Setting config vars DOKKU_APP_RESTORE: 1
-----> Found previous container(s) (14c349cb496d) named hello-world.web.1
=====> Renaming container (14c349cb496d) hello-world.web.1 to hello-world.web.1.1535285386
=====> Renaming container (cc99cccacf2c) serene_bassi to hello-world.web.1
-----> Attempting to run scripts.dokku.postdeploy from app.json (if defined)
-----> Shutting down old containers in 60 seconds
=====> 14c349cb496d95cc4be1833f2e7f6ef2bef099a37c2a22cd4dcdb542f09bea0f
=====> Application deployed:
       http://hello-world.my-paas.for-myself.com

And done.

What happened? You have now the environment variable $DATABASE_URL set in the hello-world app, that’s why the restart was necessary (which you can postpone, if you want, but you probably need it now, right?).

Let’s check:

$ docker exec -ti hello-world.web.1 /bin/sh 

[now in the container]

# env | grep DATABASE 
DATABASE_URL=postgres://postgres:bd6b0725d710bb5a662bb628eee787b1@dokku-postgres-hello-world:5432/hello_world 

That’s it. Super easy. Now if you’re using Django, you could use kennethreitz/dj-database-url to automatically parse and use it, and you’re done. (Probably every framework has something similar, so just have a look).

Categories
CICD Development Docker Infrastructure

Build your own PaaS with Dokku

I was looking for some “play” deployment method for a couple of things I want to try out. Most of them require a database. And it should be cheap, cause I don’t have any load on them and don’t earn any money, so I don’t want to spend basically no money if possible. The usual suspects are too expensive – AWS, Heroku, etc.

So I looked around and found Dokku.

Dokku is a set of – hang on – shell scripts – which basically emulate Heroku on a machine of your own. It’s integrated with Digital Ocean droplets out of the box, if you want it. And the whole thing is 5 € / month, which is perfect. It also integrates with a Dockerfile based deployment, so you do git push and everything just works.

It’s amazing.

This is how you get started. But before you can get started, you need a domain you control, either on AWS or any other hoster. This is for routing traffic to your deployments later. You also need a public SSH key, or better a public / private key pair. Once you have both you can …

  1. create a Digital Ocean account, and …
  2. add your SSH public key to your account, and …
  3. in that account, create a new droplet with a “Dokku” image preinstalled.
  4. Wait until the droplet finished provisioning.

While the droplet is being created, you can also create a project locally to test it later:

$ mkdir dokku-test
$ cd dokku-test
$ git init
$ echo "FROM tutum/hello-world" > Dockerfile
$ git add Dockerfile
$ git commit -m "Initial commit"

In this little test project we only create a Dockerfile from an hello-world image which displays “Hello world” in a browser so we can verify it worked.

Once the droplet is done, you can start setting up your personal little PaaS. First, you have to configure your DNS. We will set up a wildcard entry for our deployments, and a non-wildcard entry for git. Let’s assume your domain is for-myself.com, then you would add …

  • my-paas.for-myself.com , type “A” (or “AAAA” if you are IPv6) to your droplet IP
  • *.my-paas.for-myself.com just the same

Then you SSH into your droplet, and create your dokku project. (This is something you have to do for every project). All you have to do for this is:

$ ssh root@DROPLET_IP
~# dokku apps:create hello-world
-----> Creating hello-world... done
~# _

Done.

Now you configure a git remote URL for your project, and push it:

$ git remote add dokku dokku@my-paas.for-myself.com:hello-world

Again – done. If you push your project now (assuming DNS is already set), everything should happen automagically:

$ git push --set-upstream dokku master
X11 forwarding request failed
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 241 bytes | 241.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
-----> Cleaning up...
-----> Building hello-world from dockerfile...
remote: build context to Docker daemon  2.048kB
Step 1/1 : FROM tutum/hello-world
latest: Pulling from tutum/hello-world
658bc4dc7069: Pulling fs layer
[... TRUNCATED ...]
983d35417974: Pull complete
Digest: sha256:0d57def8055178aafb4c7669cbc25ec17f0acdab97cc587f30150802da8f8d85
Status: Downloaded newer image for tutum/hello-world:latest
 ---> 31e17b0746e4
Successfully built 31e17b0746e4
Successfully tagged dokku/hello-world:latest
-----> Setting config vars
       DOKKU_DOCKERFILE_PORTS:  80/tcp
-----> Releasing hello-world (dokku/hello-world:latest)...
-----> Deploying hello-world (dokku/hello-world:latest)...
-----> Attempting to run scripts.dokku.predeploy from app.json (if defined)
-----> No Procfile found in app image
-----> DOKKU_SCALE file not found in app image. Generating one based on Procfile...
-----> New DOKKU_SCALE file generated
=====> web=1
-----> Attempting pre-flight checks
       For more efficient zero downtime deployments, create a file CHECKS.
       See http://dokku.viewdocs.io/dokku/deployment/zero-downtime-deploys/ for examples
       CHECKS file not found in container: Running simple container check...
-----> Waiting for 10 seconds ...
-----> Default container check successful!
-----> Running post-deploy
-----> Creating new /home/dokku/hello-world/VHOST...
-----> Setting config vars
       DOKKU_PROXY_PORT_MAP:  http:80:80
-----> Configuring hello-world.my-paas.for-myself.com...(using built-in template)
-----> Creating http nginx.conf
-----> Running nginx-pre-reload
       Reloading nginx
-----> Setting config vars
       DOKKU_APP_RESTORE:  1
=====> Renaming container (14c349cb496d) amazing_snyder to hello-world.web.1
-----> Attempting to run scripts.dokku.postdeploy from app.json (if defined)
=====> Application deployed:
       http://hello-world.my-paas.for-myself.com

To my-paas.for-myself.com:hello-world
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'dokku'.

And if you open your URL now (which is hello-world.my-paas.for-myself.com) you should see this image:

Now, for 5 € / month you get:

  • A heroku-like, no-nonsense, fully automated, git-based deployment platform
  • A server which you control (and have to maintain, okay, but on which you can deploy …)
  • A database (or many of them – dokku provides great integration for databases btw; more on that in another post)
  • Publicly reachable deployments (for customers, testing, whatever)
  • Let’s Encrypt certificates (dokku provides support for these as well, again more in a later post)
  • And for 1 € more (it’s always 20% of the base price) you get backups of your system)

That’s absolutely incredible. Oh, and did I mention that the maintainers are not only friendly, but also super responsive and incredibly helpful on Slack?