14 | I'm Lucas, a freelance software developer, working professionaly since 2004.
15 | My goal is to build well designed, well tested and properly documented applications that exceed your expectations.
16 |
17 |
18 |
19 | I work mostly in PHP and Javascript, but I'm learning other languages as well. For more info check my expertise.
20 |
21 |
22 |
23 | Besides being an engineer I'm also organizer of DomCode, a non-profit foundation with 1200+
24 | members that organizes events like the annual DomCode conference and
25 | monthly DomCode meetups where (inter)national speakers share their knowledge on
26 | software development.
27 | Since I value knowledge sharing a lot I became an occasional speaker myself.
28 |
29 |
30 |
31 | I love to work with other freelancers or companies. Contact me if you are interested.
32 |
33 |
34 |
--------------------------------------------------------------------------------
/source/_posts/draft-splitting-your-in-a-front-end-and-back-end-containers.md:
--------------------------------------------------------------------------------
1 | ---
2 | draft: true
3 | title: Spliting your app in a back and front end service
4 | tags:
5 | - nginx
6 | - docker
7 | - php
8 | categories:
9 | - software-development
10 | ---
11 |
12 | Splitting an application into separate backend and front end services has the following advantages:
13 | - Both services will be simpler since they have a single responsibility.
14 | - Both services can be scaled individually
15 |
16 | # Caveats
17 |
18 | # Resolving backend from the frontend
19 |
20 | While (at least in Docker Compose/Docker Swarm setups) services can be resolved by their name this doesn't always work out of the box.
21 | External domain names seem to have precedence over internal service names.
22 | Most of the time this is not a problem.
23 | Until the name of a service is an existing top level domain like [`app`](https://icannwiki.org/.app).
24 | Nginx will try to resolve `app` which results in a conflict.
25 | This can be resolved by telling Nginx to use the [internal Docker DNS](https://docs.docker.com/engine/userguide/networking/configure-dns/).
26 |
27 | An example is:
28 |
29 |
30 |
31 | Docker service names
32 | TODO check this ^
33 |
34 | Make sure frontend knows how determine which backend to use.
35 |
36 | When nginx resolves it's upstream it doesn't use the Docker DNS by default.
37 |
38 |
39 |
--------------------------------------------------------------------------------
/source/blog/tags/tag.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: Tag Archive
4 | generator: [posts_tag_index, pagination]
5 | pagination:
6 | provider: page.tag_posts
7 |
8 | ---
9 |
10 | {% block head_meta %}
11 |
12 |
13 | {% endblock %}
14 |
15 | {% block title %}{{ page.title }} "{{ page.tag }}"{% endblock %}
16 | {% block content %}
17 | {% set year = '0' %}
18 |
"{{ page.tag }}"
19 | {% for post in page.pagination.items %}
20 | {% set this_year %}{{ post.date | date("Y") }}{% endset %}
21 | {% if year != this_year %}
22 | {% set month = '0' %}
23 | {% set year = this_year %}
24 | {% endif %}
25 | {% set this_month %}{{ post.date | date("F") }}{% endset %}
26 | {% if month != this_month %}
27 | {% set month = this_month %}
28 |
13 | Because of my work for DomCode I got interested in public speaking
14 | myself. Below you can see some of my recent talks, workshops etc. I mostly speak about software but occasionally
15 | other topics like welding (which is one of my favorite things to do) pop up too. I'm currently planning new talks/workshops for 2017.
16 |
12 | This is some of the work I did at my former employers.
13 |
14 |
15 |
16 |
17 |
18 | Mijndomein.nl (at Ibuildings.nl)
19 |
20 |
21 | Mijndomein is a major player in consumer web hosting and domain registration. They have been working hard on
22 | modernizing their platform to offer new features to their customers and to release them often and with ease.
23 | As part of the Ibuildings team at Mijndomein I have trained employees in working with modern Javascript and PHP frameworks and testing tools.
24 | I helped them to build a (mobile) web shop as well.
25 |
26 |
27 | Furthermore I have worked on carving out parts of their application to micro services and connecting them via HTTP APIs
28 | and message queues controlled by a central process manager and setting up a (OAuth 2 based) single sign on solution
29 | for authentication between all services and the existing platform
30 | applications. I have initiated the use of (Docker) containers which is now broadly adopted for production usage
31 | accross the entire platform.
32 |
33 |
34 |
35 |
36 | SURFnet.nl (at Ibuildings.nl)
37 |
38 |
39 | SURFnet provides IT infrastructure and online services to Dutch universities and research institutes.
40 | One of the services SURFnet offers is the SAML based OpenConext platform which offers a 'collaborative infrastructure' that
41 | allows students, teachers and researchers in the Netherlands to collaborate using online services.
42 | As part of the Ibuildings team at SURFnet I did a lot of maintenance and worked on modernizing various
43 | components of the platform, improve test and automating deployment and provisioning using Ansible.
44 |
49 | Verzekeringssite.nl offers comparing and taking insurance policies.
50 | As part of the team at Verzekeringssite I developed a service that aggregated data retrieved from 3rd party APIs
51 | and calculated the most suitable insurance policies based on user input in realtime.
52 |
53 |
54 |
55 |
Other clients
56 |
57 | While I mainly focus on backend (web) applications I have done a lot of
58 | (campaign) website, CMS and advertisement work for clients like: Vakantieveilingen.nl,
59 | FC Utrecht, Fietswereld/Profile, Heineken/Amstel, WebAds and Adrime (now Weborama).
60 |
32 | Categories:
33 | {% for category in page.categories %}
34 | {{ category }}{% if not loop.last %}, {% endif %}
35 | {% endfor %}
36 |
37 | {% endif %}
38 | {% if page.tags %}
39 |
40 | Tags:
41 | {% for tag in page.tags %}
42 | {{ tag }}{% if not loop.last %}, {% endif %}
43 | {% endfor %}
44 |
45 | {% endif %}
46 |
47 | {% if page.previous_post or page.next_post %}
48 |
58 | {% endif %}
59 |
60 |
61 | {% if site.disqus.shortname and site.disqus.shortname != '' %}
62 |
63 |
83 |
86 | {% endif %}
87 |
88 | {% endblock %}
89 |
--------------------------------------------------------------------------------
/source/projects/electric-discovery.md:
--------------------------------------------------------------------------------
1 | ---
2 | draft: true
3 | layout: "default"
4 | name: "Electric Land Rover Discovery project"
5 |
6 | ---
7 |
8 | [Component Overview](/projects/electric-discovery/component-overview)
9 |
10 |
11 |
12 |
13 |
14 |
15 | I'm working on converting my a Land Rover Discovery from diesel to electric.
16 |
17 | It's a 1997 300Tdi model.
18 | This model originally had a 100 inch wheelbase but it has been [stretched to ~124 inch]().
19 | Both body and chassis have been [hot dip galvanized]()
20 |
21 | One of my examples is the [electric Volvo Amazon](https://www.oudevolvo.nl/ev-combi/) project by [Lars Rengersen](https://twitter.com/larsrengersen)
22 |
23 | I'll be using the same (Siemens 1PV5153 4WS-14) 3 phase induction motor as Lars [put it in his Volvo](https://www.oudevolvo.nl/blog/2015/08/13/m400-versnellingsbak-aan-elektromotor-gemaakt-en-ingebouwd/)
24 | with a different controller: the [Sevcon gen 4 size 8](http://www.sevcon.com/products/high-voltage-controllers/gen4-s8/)
25 |
26 | The Siemens motor was originally developed for the [electric Ford Transit Connect](https://en.wikipedia.org/wiki/Azure_Transit_Connect_Electric)
27 | but came available to the DIY market after the bankruptcy of [Azure Dynamics](https://en.wikipedia.org/wiki/Azure_Dynamics).
28 |
29 | In the Ford Transit Connect the Siemens motor was used in combination with the [DMOC 645](http://store.evtv.me/proddetail.php?prod=dmoc645) controller however
30 | it becomes harder to obtain both the motor and the controller from the Azure Dynamics bankruptcy sales.
31 |
32 | Also to get it road legal in the Netherlands you need a certificate which proves the [electromagnetic compatibility](https://en.wikipedia.org/wiki/Electromagnetic_compatibility) of the motor controller combination for the Dutch market.
33 | This reduces both the number of motors/controllers you can use as well as the suplliers you can get them from.
34 |
35 | Luckily [New Electric](http://www.newelectric.nl/drive-train/) has already done the necessary EMC tests and they can supply a motor/controller combination with the required certificated.
36 | Furthermore they aquire the motors directly from Siemens rather
37 |
38 | The rest of the driveline stay the same and I'll be [keeping the gearbox]() albeit with a few small changes.
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 | Other people using Tesla Model S batteries:
81 | - http://evbimmer325i.blogspot.nl/2017/06/tesla-battery-pack-bus-bar-steel.html?m=1
82 | - http://www.diyelectriccar.com/forums/showthread.php?t=177489
83 | - https://www.oudevolvo.nl/ev-combi/
84 |
--------------------------------------------------------------------------------
/source/_posts/draft-dealing-with-unavailable-services-on-application-start-up.md:
--------------------------------------------------------------------------------
1 | ---
2 | draft: true
3 | title: Dealing with unavailable services on application start up
4 | categories:
5 | software-development
6 | tags:
7 | - docker
8 | - docker-festival
9 | ---
10 |
11 | Containers are volatile, I've written before on how that creates issues when
12 | [proxying ingress traffic](/blog/2017/06/25/accessing-your-docker-app-via-a-domain-name-using-traefik/)
13 |
14 | Another area where this is creating problems is when processes have (a chain of) dependencies on each other.
15 |
16 | Many containerized applications are composed of multiple processes like for example a web server a run time a relational database, a key/value store.
17 | These processes will most likely have (a chain of) dependencies other services.
18 |
19 | TODO: health check/parallelism
20 |
21 |
22 | # Example 1, check if a web application is ready to serve requests
23 |
24 | A database is a good example since it touches multiple issues like connections, state.
25 |
26 | A simple dependency chain might look like this
27 |
28 | In development:
29 | - In order to start the application it's database should be migrated to the latest state
30 | - In order to execute database migrations vendor libraries must be installed
31 | - In order to execute database migrations the database needs to be available
32 | - In order to be available the database must be started.
33 |
34 | In production:
35 | - In order to add a newly deployed application to the pool it must be [healthy]
36 | - In order for an application to be healthy it must be started.
37 | - In order to start the application it's database should be migrated to the latest state
38 | - In order to execute database migrations the database needs to be available
39 |
40 |
41 | TODO: forward compatible migrations/config
42 |
43 | /***
44 |
45 | Note that if your happen to be using Doctrine ORM there's a caveat:
46 | You need to installl vendor libraries with composer to execute DB polling
47 | However composer install also triggers a clear cache script which in turn tries to generate proxies
48 | for each database entity. This of course fails horribly when there's no database connection yet.
49 | An easy solution is to have Doctrine generating proxies on demand.
50 |
51 | In Symfony this can be configured as follows:
52 |
53 | ```yaml
54 | doctrine:
55 | orm:
56 | # This essentially allows CLI scripts to run when there is no database available (yet)
57 | # If disabled Doctrine will attempt to generate the proxy cache on cache warmup.
58 | auto_generate_proxy_classes: true
59 | ```
60 |
61 | ***/
62 |
63 |
64 |
65 | Since all of these any of these can fail how do you make sure your application handles these correctly?
66 |
67 | Let's solve the last problem first:
68 |
69 | # Example 2: start a database when an application starts
70 | This is mainly about development/test environments where the database is also a containerized process whereas
71 | in a production the database is most likely running as an external service.
72 |
73 | I often use the [official Docker MySQL image](https://hub.docker.com/_/mysql/) for development and testing.
74 | The easiest way to make sure the database is started when you run an application (or test suite!)
75 | depending is to use docker compose `depends_on` option.
76 | To trigger starting the database when the app starts configure something like:
77 | feature:
78 |
79 | ```yaml
80 | version: "2"
81 |
82 | services:
83 | app:
84 | ...
85 | depends_on:
86 | - db
87 | db:
88 | ...
89 | ```
90 |
91 | # Example 2: Waiting until the database is ready
92 |
93 | Starting a database might take some time, in most cases it takes longer than starting the application depending on it.
94 | So how to determine when the database is ready?
95 | While I'm a fan of event driven systems. The easiest way to determine this is plain old polling.
96 | A.k.a: just attempt to connect every second or so.
97 | This is preferably done using via the application database abstraction layer.
98 | This way one can be sure that not only the database is ready but also the application is correctly configured
99 | to make a connection.
100 |
101 | Since most of my applications are written in PHP and based on the Symfony framework
102 | I often use the [LIIP Monitor Bundle](https://github.com/liip/LiipMonitorBundle).
103 | This library is able to check various kinds of pre conditions.
104 | Testing database connections is just one of the many things it can do.
105 | There is probably a similar solution for your language/framework of choice.
106 |
107 | `wait-for-services`
108 |
109 | ```bash
110 | #!/usr/bin/env sh
111 |
112 | DIR=`dirname $(readlink -f $0)`
113 |
114 | until ${DIR}/console monitor:health --all; do
115 | >&2 echo "Waiting for all services to become available"
116 | sleep 1
117 | done
118 |
119 | ```
120 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | SHELL=/bin/bash
2 | .DELETE_ON_ERROR:
3 |
4 | TARGET=$@
5 |
6 | export HOST_UID=$(shell id -u)
7 | export HOST_GID=$(shell id -g)
8 |
9 | PLATFORM := $(shell uname -s)
10 | ifeq ($(PLATFORM),Darwin)
11 | export DOCKER_HOST_IP_OR_NAME = docker.for.mac.localhost
12 | else ifeq ($(PLATFORM),Linux)
13 | export DOCKER_HOST_IP_OR_NAME=$(shell ip -f inet addr show docker0 | grep -Po 'inet \K[\d.]+')
14 | endif
15 |
16 |
17 | include make/travis.mk
18 |
19 | all: docker/app/.built
20 |
21 | export COMPOSE_PROJECT_NAME=lucasvanlierop-website
22 | export CI_FILE='env/ci/docker-compose.yml'
23 |
24 | ~/.composer:
25 | $(TARGET_MARKER_START)
26 | mkdir -p ~/.composer
27 | $(TARGET_MARKER_END)
28 |
29 | vendor: \
30 | docker/sculpin/.built \
31 | ~/.composer \
32 | composer.json \
33 | composer.lock
34 | $(TARGET_MARKER_START)
35 | docker-compose -f $(CI_FILE) run --rm sculpin composer install
36 | $(TARGET_MARKER_END)
37 |
38 | .PHONY:
39 | clean:
40 | $(TARGET_MARKER_START)
41 | rm -rfv source/css/*
42 | rm -rfv output_prod/*
43 | $(TARGET_MARKER_END)
44 |
45 | source/css/pygments.css: docker/sculpin/.built
46 | $(TARGET_MARKER_START)
47 | docker-compose -f $(CI_FILE) run --rm sculpin sh \
48 | bin/generate-pygments-css
49 | $(TARGET_MARKER_END)
50 |
51 | docker/sass/.built: \
52 | $(shell find docker/sass/* | grep .built)
53 | $(TARGET_MARKER_START)
54 | docker-compose -f $(CI_FILE) build sass
55 | touch $@
56 | $(TARGET_MARKER_END)
57 |
58 | docker/sculpin/.built: docker/sculpin/*
59 | $(TARGET_MARKER_START)
60 | docker-compose -f $(CI_FILE) build sculpin
61 | touch $@
62 | $(TARGET_MARKER_END)
63 |
64 | output_dev:
65 | $(TARGET_MARKER_START)
66 | mkdir -p $(TARGET)
67 | $(TARGET_MARKER_END)
68 |
69 | # todo fix source/*
70 | output_prod: \
71 | $(shell find source/) \
72 | source/css \
73 | docker/sculpin/.built \
74 | vendor
75 | $(TARGET_MARKER_START)
76 | docker-compose -f $(CI_FILE) run --rm sculpin vendor/bin/sculpin generate \
77 | --env=prod
78 | $(TARGET_MARKER_END)
79 |
80 | source/css: \
81 | docker/sass/.built \
82 | source/css/pygments.css \
83 | $(shell find source/scss/ | grep .built)
84 | $(TARGET_MARKER_START)
85 | docker-compose -f $(CI_FILE) run --rm sass --update /app/source/scss:/app/source/css
86 | touch $@
87 | $(TARGET_MARKER_END)
88 |
89 | docker/app/.built: \
90 | docker/app/* \
91 | output_prod
92 | $(TARGET_MARKER_START)
93 | docker-compose -f $(CI_FILE) build app
94 | touch $@
95 | $(TARGET_MARKER_END)
96 |
97 | .PHONY: up
98 | up: output_dev
99 | docker-compose up
100 |
101 | .PHONY: down
102 | down:
103 | docker-compose down
104 |
105 | .PHONY: test
106 | test: docker/app/.built
107 | $(TARGET_MARKER_START)
108 | docker-compose -f $(CI_FILE) up -d --no-build --force-recreate --remove-orphans
109 | tests/smoke-test.sh
110 | tests/validate-html.sh
111 | docker-compose -f $(CI_FILE) stop
112 | $(TARGET_MARKER_END)
113 |
114 | DOCKER_TUNNEL_CONTAINER=docker_swarm_ssh_tunnel
115 | DOCKER_TUNNEL_PORT=12374
116 | DOCKER_SWARM_HOST=lucasvanlierop.nl
117 | DEPLOY_USER=deploy
118 | DOCKER_STACK_FILE=env/prod/docker-compose.yml
119 | DOCKER_STACK_NAME=lucasvanlierop-website
120 |
121 | .PHONY: deploy
122 | .SILENT: deploy
123 | deploy: \
124 | traefik-production-certificate-store \
125 | traefik-production-config \
126 | tunnel-to-production-docker-socket
127 | $(TARGET_MARKER_START)
128 | docker \
129 | -H localhost:$(DOCKER_TUNNEL_PORT) \
130 | stack deploy \
131 | --with-registry-auth \
132 | -c $(DOCKER_STACK_FILE) \
133 | --prune \
134 | $(DOCKER_STACK_NAME)
135 | $(TARGET_MARKER_END)
136 |
137 | .PHONY: traefik-production-certificate-store
138 | .SILENT: traefik-production-certificate-store
139 | traefik-production-certificate-store:
140 | $(TARGET_MARKER_START)
141 | # Create file Where Traefik can store it's certificates
142 | ssh $(DEPLOY_USER)@$(DOCKER_SWARM_HOST) "(umask 600; touch /opt/traefik/acme.json)"
143 | $(TARGET_MARKER_END)
144 |
145 | .PHONY: traefik-production-config
146 | .SILENT: traefik-production-config
147 | traefik-production-config:
148 | $(TARGET_MARKER_START)
149 | # Copy Traefik config file (Todo: this should be Swarm secret since this does not trigger a restart)
150 | scp env/prod/traefik.toml $(DEPLOY_USER)@$(DOCKER_SWARM_HOST):/opt/traefik/traefik.toml
151 | $(TARGET_MARKER_END)
152 |
153 | .SILENT: tunnel-to-production-docker-socket
154 | tunnel-to-production-docker-socket:
155 | $(TARGET_MARKER_START)
156 | # Create SSH tunnel to Docker Swarm cluster
157 | @(docker ps | grep $(DOCKER_TUNNEL_CONTAINER)) || docker run \
158 | -d \
159 | --name $(DOCKER_TUNNEL_CONTAINER) \
160 | -p $(DOCKER_TUNNEL_PORT):$(DOCKER_TUNNEL_PORT) \
161 | -v $(SSH_AUTH_SOCK):/ssh-agent \
162 | kingsquare/tunnel \
163 | *:$(DOCKER_TUNNEL_PORT):/var/run/docker.sock \
164 | $(DEPLOY_USER)@$(DOCKER_SWARM_HOST)
165 |
166 | timeout 10 $(MAKE) wait-for-tunnel-to-production-docker-socket
167 |
168 | $(TARGET_MARKER_END)
169 |
170 | wait-for-tunnel-to-production-docker-socket:
171 | until docker -H localhost:$(DOCKER_TUNNEL_PORT) version 2>/dev/null 1>/dev/null > /dev/null; do \
172 | echo "Waiting for docker tunnel"; \
173 | sleep 1; \
174 | done
175 |
--------------------------------------------------------------------------------
/source/_posts/draft-tools-from-the-golden-years-an-introduction-to-make.md:
--------------------------------------------------------------------------------
1 | ---
2 | draft: true
3 | title: Tools from the Golden Years, an introduction to (GNU) Make
4 | categories:
5 | software-development
6 | tags:
7 | - make
8 | image: /images/blog/software/empowered-by-gnu.svg
9 | ---
10 |
11 | One of the tools I use almost every day is (GNU) Make.
12 |
13 | Since I will use Make in some of my following posts I'd thought I should give a very basic introduction.
14 | This post is also meant to share my love for it and hopefully give you an incentive to try it.
15 |
16 | If you find yourself creating cache directories, installing vendor libraries, compiling assets/artefacts, running tests, deploying using different scripts keep reading!
17 |
18 | What I wanted was having to worry about only a very limited set of commands for all of the above.
19 | Most of them aren't things you need to execute directly but mostly a requirement of some other command.
20 | Make is very good in glueing all kinds of scripts and their requirements together.
21 |
22 |
23 | ## But Make is very old, how can it still be useful?
24 |
25 | Yes Make is very old. I was really surprised when I found out how old it really was,
26 | I knew it was from #backinthedays but [according to wikipedia](https://en.wikipedia.org/wiki/Make_(software))
27 | it first occured in April 1976!
28 | At the moment of writing that's exactly __42(!)__ years ago (and still going strong).
29 |
30 | The seventies really were [the Golden Years](https://www.youtube.com/watch?v=JUuRGRcY9O0) of Unix
31 |
32 | a few years after [the dawn of time](https://en.wikipedia.org/wiki/Unix_time))!
33 |
34 | ## What does Make even do?
35 | Make was (and still is) meant to compile C source files.
36 | However it can be used for many other applications too.
37 | That said it's important to keep in mind it's original purpose.
38 |
39 | What make does is running shell commands.
40 | In case you wonder why would I need a tool to run shell commands if I already have a shell?
41 |
42 | Well what makes make powerful is how it can orchestrate a (large) amount of separate calls in a specific order.
43 |
44 | Especially with containers Make is a great fit since it's very good at abstracting the boiler plate of running containers.
45 |
46 | # Use cases
47 |
48 | In my Job was web application developer I have many use cases for Make, think of:
49 |
50 | - Building Docker images
51 | - Ensuring cache directories exist
52 | - Installing vendor libraries (using PHP's Composer, Node's yarn)
53 | - Running test & inspection tools (PHPunit etc.)
54 | - Booting the application in development modus
55 | - Creating dist(ribution) versions of the application
56 | - Deploying the application
57 |
58 | Even while most of these actions run in containers there are always difference between
59 | host operating systems, mainly Linux and OSX in my case.
60 | Make is capable of adapting to various environments in a simple but effective way: setting variables containing paths,
61 | command flags etc.
62 |
63 | ## The anatomy of a Make target
64 |
65 | A make goal consists of a target, optional pre-requisites and a rule
66 |
67 | Where a target is a file that should be created.
68 | The pre-requisites are other files that should be created first in order to create the target
69 | The rule is a
70 |
71 | The are declared as follows:
72 |
73 | ```makefile
74 | a-target: a-pre-requisite
75 | a-rule
76 | ```
77 |
78 | Alternatively it's also possible to run commands that do not result in a target.
79 | These are called [Phony targets](https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html).
80 | Phony targets can be used to run tests, clean actions etc.
81 |
82 | ```makefile
83 | .PHONY: logs
84 | logs:
85 | tail -f /var/log/some.log
86 | ```
87 |
88 | ## How I started using Make the wrong way
89 | I intially started using Make as an alternative for Apache Ant
90 | which one of my previous employers used for running tests and generating deployable artefacts.
91 | When I got into using (Docker) containers I Discovered GNU Make via [Jessie Frazelle](https://twitter.com/jessfraz)
92 |
93 | While it's syntax is a bit quirky Make made so much more sense to me than the bulky XML files I was used to.
94 | I started out with (ab)using Make as a dumb task runner like: (`PHONY` galore)
95 |
96 | ```makefile
97 |
98 | .PHONY: do-something
99 | do-something:
100 | shell-command
101 | ```
102 |
103 | Using `.PHONY` is almost like a code smell to me now.
104 | In many cases it's better to use real target or [empty targets](https://www.gnu.org/software/make/manual/html_node/Empty-Targets.html)
105 |
106 | ...overlooked
107 |
108 | ## The power of dependency resolving.
109 | Dependency (or pre-requisites as Make likes to call them) resolving might be the single most powerful feature of make.
110 | ...Make will create a graph of
111 |
112 | ## Debugging
113 | ...--debug, silent
114 |
115 |
116 | ## Example
117 |
118 | A very common scenrar
119 |
120 |
121 | ----------------------------- Next post
122 |
123 | ## Advanced, running things in parallel
124 | Depending on the resources available Make can execute it's targets more efficiently by running them in parallel (using the `-j` argument)
125 |
126 | ```bash
127 | make -j --output-sync=recurse target-1 target-2 (... target-n)
128 | ```
129 |
130 | The `--output-sync=recurse` buffers the output created by a given target until it has finished.
131 | In most cases this is what you want because mixed output from multiple targets is pretty unreadble.
132 |
133 |
134 |
135 | Make isn't really good in Idempotency
136 |
137 | ---
138 | Tnx Jessie, Jan
139 |
--------------------------------------------------------------------------------
/source/scss/main.scss:
--------------------------------------------------------------------------------
1 | @import "modules/_colors";
2 |
3 | html {
4 | height: 100%;
5 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
6 | }
7 |
8 | html, body {
9 | min-height: 100% !important;
10 | width: 100% !important;
11 | margin: 0;
12 | padding: 0;
13 | color: #2C3E50;
14 | }
15 |
16 | a {
17 | color: $palette-petrol;
18 | text-decoration: none;
19 | }
20 |
21 | body {
22 | line-height: 1.4;
23 | background-color: white;
24 | }
25 |
26 | header.top {
27 | color: #eee;
28 | padding: 20px;
29 |
30 | a {
31 | text-decoration: none;
32 | color: white;
33 | float: left;
34 | }
35 | }
36 |
37 | span.top-title h1, h2, h3 {
38 | letter-spacing: .6px;
39 | font-weight: 300;
40 | }
41 |
42 | span.top-title {
43 | margin: 0;
44 | font-size: 200%;
45 | }
46 |
47 | span.top-title .payoff {
48 | font-size: 50%;
49 | display: block;
50 | }
51 |
52 | header.top {
53 | nav {
54 | margin-top: 12px;
55 | float: right;
56 | ul {
57 | margin: 0;
58 | padding: 0;
59 | display: inline;
60 |
61 | li {
62 | margin: 0;
63 | padding: 0;
64 | float: left;
65 | list-style: none;
66 | padding: 0 10px;
67 | text-transform: uppercase;
68 | border-right: 1px solid white;
69 |
70 | &:last-child {
71 | border: none;
72 | }
73 | }
74 | }
75 |
76 | a {
77 | color: white;
78 | }
79 | }
80 | }
81 |
82 | main, header.top, footer {
83 | clear: both;
84 | border-bottom: solid 1px white;
85 | padding: 10px 20px;
86 | }
87 |
88 | header.top:after {
89 | visibility: hidden;
90 | display: block;
91 | font-size: 0;
92 | content: " ";
93 | clear: both;
94 | height: 0;
95 | }
96 |
97 | section h2 {
98 | text-transform: uppercase;
99 | }
100 |
101 | .about-me h2 {
102 | display: none;
103 | }
104 |
105 | a.button {
106 | font-size: 200%;
107 | font-weight: bold;
108 | padding: 5px 10px;
109 | border-radius: 4px;
110 | color: white;
111 | }
112 |
113 | .block {
114 | list-style: none;
115 | padding: 20px;
116 | margin-top: 40px;
117 | background: rgba(50, 50, 50, .05);
118 | border: solid 1px lightgrey;
119 | border-radius: 3px;
120 |
121 | h3 {
122 | margin-top: 0;
123 |
124 | .fa {
125 | font-size: 200%;
126 | margin-right: 10px;
127 | }
128 | .fa-plus-circle {
129 | font-size: 100%;
130 | margin: 0;
131 | }
132 | }
133 | }
134 |
135 | .block-list {
136 | margin: 0;
137 | padding: 0;
138 | }
139 |
140 | .main {
141 | background-color: #eee;
142 | padding: 20px;
143 | height: 100%;
144 | margin: 0;
145 | }
146 |
147 | .photo {
148 | float: right;
149 | height: auto;
150 | max-width: 100%;
151 | margin-left: 1em;
152 | margin-bottom: 1em;
153 | }
154 |
155 | .contact-links {
156 | margin: 0;
157 | padding: 0;
158 |
159 | li {
160 | list-style: none;
161 | margin: 10px 0 0 0;
162 |
163 | i {
164 | font-size: 20px;
165 | }
166 | }
167 | }
168 |
169 | footer {
170 | background-color: #ddd;
171 | }
172 |
173 | abbr[title] {
174 | cursor: help;
175 | border-bottom: 1px dashed $palette-grey;
176 | text-decoration: none;
177 | }
178 |
179 | @media screen and (min-width: 640px) {
180 | main, header.top, footer {
181 | padding: 20px 20%;
182 | }
183 | }
184 |
185 | @media screen and (max-width: 640px) {
186 | body {
187 | font-size: 1.3em;
188 | }
189 | .block {
190 | margin-top: 20px;
191 | }
192 | span.top-title .payoff {
193 | display: none;
194 | }
195 | header.top {
196 | a {
197 | text-align: center;
198 | float: none;
199 | }
200 | nav {
201 | float: none;
202 |
203 | ul {
204 | display: block;
205 | margin: 0 auto;
206 |
207 | li {
208 | float: none;
209 | display: inline-block;
210 | }
211 | }
212 | }
213 | }
214 | abbr[title] {
215 | border: none;
216 | }
217 | abbr[title]:before {
218 | content: attr(title) " ("
219 | }
220 | abbr[title]:after {
221 | content: ")"
222 | }
223 | }
224 |
225 | figure.code-highlight-figure {
226 | margin: 0;
227 | overflow-x: scroll;
228 |
229 | .highlight {
230 | padding: 10px;
231 | }
232 | }
233 |
234 | ul.categories {
235 | margin: 0;
236 | padding: 0;
237 | display: inline;
238 |
239 | li {
240 | margin: 0;
241 | padding: 0;
242 | display: inline;
243 | list-style: none;
244 | padding: 0 10px 0 0;
245 | text-transform: uppercase;
246 |
247 | &:last-child {
248 | border: none;
249 | }
250 | }
251 | }
252 |
253 | article {
254 | img {
255 | max-width: 100%;
256 | }
257 | }
258 |
259 | .blog {
260 | h3 {
261 | clear: both;
262 |
263 | .image {
264 | display: block;
265 | height: 100px;
266 | width: 100px;
267 | background-repeat: no-repeat;
268 | background-position: center;
269 | background-size: cover;
270 | float: left;
271 | margin-right: 20px;
272 | }
273 | }
274 | }
275 |
276 |
277 | code {
278 | background-color: #f6f8fa;
279 | border: solid 1px darkgray;
280 | }
281 |
--------------------------------------------------------------------------------
/source/_posts/draft-testing-containerized-web-applications-with-docker-part-1-smoke-tests.md:
--------------------------------------------------------------------------------
1 | ---
2 | draft: true
3 | title: Testing containerized web applications with Docker part 1: Smoke tests
4 | categories:
5 | - software-development
6 | tags:
7 | - testing
8 | - docker
9 | image: /images/blog/software/smoke-and-ashes.jpg
10 | imageAlt: Smoke and ashes
11 | ---
12 |
13 | _This article is part of a [series of blog posts](/blog/tags/docker-festival/) related to the
14 | [Docker Festival](https://twitter.com/hashtag/dockerfestival?src=hash) style of workshops
15 | [Matthias Noback](https://twitter.com/matthiasnoback) are doing._
16 |
17 | One of the great promises of containerized infrastructure is being able to run same stack (Operating system + runtime + application etc) in multiple environments.
18 | However it seems not everyone uses this to it's full potential.
19 |
20 | - eyeopener, run app, not just test code but exact stack that will be deplo artefact
21 | verify what will be deployed
22 |
23 |
24 | While it's fantastic to have (almost) no differences between development and production it's even better to have no differences between development, __CI__ and production.
25 | Before the container era many CI environments were provisioned in a different way than development and production where tools like Ansible, Puppet etc. were used.
26 |
27 | By far the biggest issue with tools like these is that are meant to update mutable infrastructure.
28 | After a while both local (e.g. Vagrant based) development environments as well as production (like) environments are more or less up to date.
29 | This means that provisioning almost never runs from scratch and breaking changes might be introduced without being noticed.
30 | Until a new server is added or a new employee joins the team that is.
31 |
32 | I've seen many projects we're
33 | Because building a container image and starting it is a lot simpler than running a provisioning tool against a CI environment
34 | it's now possible to not only run unit/integration tests but also real system tests.
35 |
36 | This article will be an introduction to system testing a containerized application in a test/CI environment.
37 |
38 | ## What is smoke testing?
39 | Smoke testing is the most basic form of [system testing](https://en.wikipedia.org/wiki/System_testing).
40 | Essentially it should be very simple tests that still cover a lot of the application and the stack it runs on.
41 | When a smoke test fails there's something broken which should be fixed before doing further (system) testing.
42 |
43 | What about the smoke?, well Wikipedia [explains system testing software](https://en.wikipedia.org/wiki/Smoke_testing)
44 | as "trying the major functions of software before carrying out formal testing".
45 | The explanation for system testing electrical items is informative though:
46 | "looking for smoke when powering electrical items for the first time"
47 |
48 | A small typo or misconfiguration can cause an entire (web) application to fail (even when all unit tests etc. have passed).
49 | Many of these errors can be caught by testing something trivial as "can the homepage of the web application be accessed?"
50 |
51 | Since it's easy (and fast) to start containerized (web) applications in a test environment smoke testing might be more powerful than ever.
52 |
53 | ## Configuring the application to run in a CI
54 |
55 | ## Example #1, test if an application responds at all.
56 |
57 | Since applications are started 'just in time' for testing
58 | I've wrote earlier on how that raises challenges when trying to
59 | [proxy ingress traffic to web services](/blog/2017/06/25/accessing-your-docker-app-via-a-domain-name-using-traefik/).
60 |
61 | Instead of dealing with specific container instances it's better to handle the processes as named services and use them that way
62 |
63 | Another area where this raises challenges is when directly accessing the service (not a specific container), for example during testing.
64 | When running system tests against a web service, chances are the service hasn't finished starting yet or maybe it's waiting on another service.
65 |
66 | Especially in a CI environment services are started just in time and most likely in the background.
67 | So how to determine if web service is ready for use?
68 |
69 | ## Polling
70 |
71 | The simple answer is: poll it.
72 | While event based systems are very nice, the easiest way by far to check if a web service is ready to handle requests is do a request.
73 |
74 | A very simple tool which is available almost everywhere is: `curl`.
75 | If it's called with the `--fail` argument it will fail (almost) silently on all server errors.
76 | *(To make it completely silent also add the `--silent` argument)*
77 |
78 | So as longs as `curl` requests keep failing the web service is not ready yet.
79 |
80 | The following example tests a given url every 0.5 seconds:
81 |
82 | ```bash
83 | #!/bin/sh
84 |
85 | set -e
86 |
87 | until test_output=`curl --silent --fail ${1}`; do
88 | echo "Waiting for the web service to become available ${1}"
89 | sleep 0.5;
90 | done
91 | ```
92 |
93 | ## Make sure polling doesn't continue forever.
94 |
95 | Of course it's always possible that something is broken and the service will never return a success status code.
96 | To prevent the script from running forever it's advised let it time out after a while.
97 |
98 | This can be done using the shell `timeout` function which can be prefixed to a command.
99 | Assuming the poll script above can be found at `poll-web-service` an example of 'poll until timeout` looks like this:
100 |
101 | ```bash
102 | timeout -t 60 poll-web-service {url-to-test}
103 | ```
104 |
105 | To prevent issues with different shell versions on different systems it's safer to run this in a container too.
106 | The following example assumes there's a Docker Compose based setup with a (development)
107 | service named `app` and service named `web` which are in the same network.
108 |
109 | ```bash
110 | docker-compose run --rm app sh -c 'timeout -t 60 poll-web-service http://web'
111 | ```
112 |
113 | In a next article more advanced system tests will be covered
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/source/index.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: "default"
3 | name: "Lucas van Lierop, freelance software developer #php #symfony #docker"
4 |
5 | generator: pagination
6 | pagination:
7 | max_per_page: 3
8 | use:
9 | - posts
10 | ---
11 |
12 |
13 |
14 |
15 |
About me
16 |
17 |
18 |
19 |
20 | I'm a freelance software developer based in Utrecht, the Netherlands. I'm specialized in creating, refactoring and containerizing
21 | (web) applications.
22 |
55 |
56 | You want to build a modern (web) application
57 |
58 |
59 | I know my way around modern PHP and can help you with building a web application, API,
60 | micro service or middleware with proper documentation and tests.
61 |
62 |
63 |
64 |
65 |
66 |
67 | You own a legacy application which needs improvement
68 |
69 |
70 | Many software projects turn into legacy projects over time.
71 | Reasons being the use of obsolete technologies, lack of tests, tight coupling and high complexity.
72 | When your application is still valuable to your company I can modernize your codebase,
73 | set up a testing strategy and write documentation.
74 |
75 |
76 |
77 |
78 |
79 |
80 | You would like to be able to deploy your application at any given time but your current setup doesn't
81 | let you do that
82 |
83 |
84 | The era of waiting hours, days or even months before deploying an update of your application
85 | to production are over. Current technology and practices let us deploy fast and often.
86 | I can help you with continuous integration and deployment.
87 |
88 |
89 |
90 |
91 |
92 |
93 | You often experience that your application which worked perfectly on development doesn't work in
94 | production.
95 |
96 |
97 | Many issues with applications are caused by differences in the dev/test/production pipeline.
98 | This can be prevented by making sure the stack of operating system + web server + runtime + the
99 | application itself is consistent in every stage. I can help you set Linux containers (Docker),
100 | which solves this issue entirely.
101 |
102 |
103 |
104 |
105 |
106 |
107 | You want your (junior) developers to be trained
108 |
109 |
110 | Junior developers are often very eager to learn. I can teach them modern PHP practices,
111 | test-driven development and coach them in their webdevelopment careers.
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | You own an application and you want to know how well it's built
120 |
121 |
122 |
123 | If you have an application and you're unsure about its quality I can do an audit and give you an
124 | overview of possible (security)issues, point to areas that need your attention and suggest improvements.
125 |
126 |
127 |
128 |
129 |
130 |
131 |
161 |
--------------------------------------------------------------------------------
/source/_posts/draft-history-of-the-electric-discovery.md:
--------------------------------------------------------------------------------
1 | ---
2 | draft: true
3 | title: Starting the EV conversion project for the Land Rover
4 | tags:
5 | - land rover
6 | - ev conversion
7 | - project-discover-e
8 | categories:
9 | - cars
10 | ---
11 |
12 | # Vlog
13 |
14 | This is the story of My Land Rover discovert and how I converted it. I'm Lucas, professional software engineer by and I hobby mechanic. In this vlog I'll tell about my adventures in building and driving it. By now I feel bad that I didn't start vlogging about this project years ago like one of my examples Jehu Garcia, but at least I still have the pictures. Hopefully the project raises some questions so I can do a Q&A video. For now I will vlog the entire 9 years I've owned the car in one pilot episode
15 |
16 | 2008
17 | * Why I bought this vehicle?
18 | Well, I was rebuilding my house for over 2 years {insert flash: reconstuction pictures}. My parents – who always help me! - helped me out getting all kinds of building materials { flash picutres } and also helped me transporting all kinds of trash to the recyling station {insert pictures}
19 |
20 | However, sometimes I needed something when my parents weren't there and so I had to go by bike. I can tell you that's really hard {flash pictures} , sometimes I had to carry loads of more than 100 kg (picture}. At at given moment my boss bought a brand new Land Rover Discovery (flash picture + options ALLTHETHINGS + 80K+}. Then I made a decision I thougt: “this is enough, I'm not doing this anymore, I need to get myself a ride too!” So I also bought a Land Rover Discovery. It was also a diesel, it was also green (the color at least), 4x4, heavy etc. The -only- difference was that that was over 10 years old, dented, rusty, (Picutre of first ride|. But hey, I suddenly had my frst car, and it was a still a pretty cool car to drive.
21 |
22 | So now I had the car I could do my own hauling {insert hauling pictures}, I made new friends I could go to meetings {insert prikkersdag, andere rittten}l could drive up to the North Cape {insert pictures}. I could even drive to the Sahara {insert pictures, more on that later} And off couse it was an old car – English {insert funny English pictures, so it needed lot's of maintanance. I noticed repairs at the garagage we're hugely expensive {insert pictures of bills } so I bough a lot of tools. {insert pictures of tools }
23 |
24 | 2009
25 |
26 | After a while I found out it needed more than just maintainence, new parts and fluids. It needed welding {insert rusty picture} My dad – who knows me very well – said something like “Son welding is hard, try do not to do everything yourself {do all the things }. Why not lot let a professional do it?” So I decided to take it to a professional. After some research I though I had found a -'professional'-. Boy was I wrong... I hadso much debate with these guys and still ended up having to pay 3000 euro's { insert WTF?, pictures of money } But at least it was fixed now right, right? No… it wasn't , when I had the Vehicle checked a few days later it (which you have to do every year in the Netherlands) it failed...on rusty spots which needed welding {insert picture }. So the garage fixed the rusted spot and made sure it was ok again
27 |
28 | That was the time I made my second decision, from now on I will DIY everything I can, including welding. So I did a welding course {insert picture of certificate}. And I started welding on the car, sometimes at my parents place (insert pictures}, sometimes on the street at home {insert picture}. The welding light attracted many people walking by, mostly friendly neighors asking what I was up to {insert picture NICE } but also a guy (apparently a welder himself) who came up to me and said: 'you know what we call this at my work?, We call this SHIT” {insert picture, yeah, thanks buddy…}. I chose to ignore him but folks, if you're watching this: please never do this, if someone is trying to learn something wether professionally or just as an amateur. Wether it's welding, programming (I'm a programmer), cooking or whatever. Help them out, give them advice, {insert picture} ask a question.{ insert picture } Or, if you really don't care/think it's shit STFU {insert picture}.
29 |
30 | At that time I started thinking about how awesome it would be to convert the Discovery to a (hybrid) electric system. My plan was to attach a big generator { insert picture } to the engine {insert picture}, add some batteries {insert picture} and then replace the entire heavy, maintainence requiring driveline { insert picture} with just 4 wheels with built in hub motors which then seemed to be the furure { insert pictures }. However I did lot's of research and had discussions { insert pictures } and soon learned the time wasn't yet right for this both because of the costs and the availability of parts. So this plan was skipped. {insert meme skipped }
31 |
32 | 2010
33 |
34 | Anyway as I continued welding the car I realized it should be done properly, and that's something you can only do when the car is dismantled. Why: because it's hard to reach certain spots for welding, because if you CAN weld on one side, you can't always reach the other side to fix the burnt pain causing it to rust again but most important because you set the thing on fire {insert been there done that picture } SO another decision (insert pricture } :rip this thing apart and start fixing it properly….And have it treated afterwards. {insert pictures of treating }
35 |
36 | Also, while people started laughing at me when I said the car really wasn't that big it actually wasn't. It was maybe little wider than average cars althoug not that much. It was certainly higher, but not vey long. Actually it had the same wheelbase as my parents Renault Megane. And if I were to place passenger seats in it not many cargo space would be left. At least not enough for traveeling or hauling. So the plan was to make it bigger somehow, maybe just stretch it?! {insert picture stetch meme} But first: on the road again.
37 |
38 | So another trip. This time we would leave our continent for the first time, we would be heading for Morocco {insert picture} – or even cooler we're going to Africa! {insert picture of Africa}
39 | So I in preparation I had bought new tires, Hi-Jack lift {inset pictures, a snorkel, extra jerry cans, double battery system, working light etc. we drove up to Spain, took the boat to Tanger and drove around. It was an AMAZING trip{insert picture and music Don't go loose it baby }
40 |
41 | We returned home in octobober and I had
42 |
43 | 2011
44 |
45 |
46 |
47 | Bad things:
48 | * bills
49 | * People saying: “you're doing it wrong”
50 | * Cooling fluid
51 | * Fresh painted Hood getting loose
52 |
53 |
--------------------------------------------------------------------------------
/source/_posts/2017-06-25-accessing-your-docker-app-via-a-domain-name-using-traefik.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Accessing your Docker app via a domain name using Træfɪk"
3 | categories:
4 | - software-development
5 | tags:
6 | - docker
7 | - docker-festival
8 | - traefik
9 | image: images/blog/software/traefik-header-image.png
10 | imageAlt: "Træfɪk architecture, courtesy of traefik.io"
11 | ---
12 |
13 | _This article is part of a [series of blog posts](/blog/tags/docker-festival/) related to the
14 | [Docker Festival](https://twitter.com/hashtag/dockerfestival?src=hash) workshop
15 | [Matthias Noback](https://twitter.com/matthiasnoback) and I did at
16 | [Dutch PHP Conference 2017](https://www.phpconference.nl/)_
17 |
18 | ## Running a (read: just one) web application in Docker is easy.
19 |
20 | So you're developing or running web applications in Docker but how do you expose them to the outside world?
21 |
22 | When you have just one application the most common option when you're starting with Docker is to just bind a port to the host the container runs on like:
23 |
24 | ```bash
25 | docker run -p 80:80 some/image
26 | ```
27 |
28 | Now you can reach the application by `http://localhost`.
29 | That would work for development or even for production if you have just one web service per host.
30 |
31 | ## But what if you want more?
32 |
33 | - You want run multiple web services?
34 | - You want to run multiple instances of a service?
35 | - You want to run services on multiple hosts?
36 | - You just want a more descriptive hostname than 'http://localhost'?
37 |
38 | ## Reverse proxy to the rescue!
39 |
40 | A very nice approach to these problems is to use a [reverse proxy](https://en.wikipedia.org/wiki/Proxy_server#Reverse_proxies).
41 | Reverse proxy servers have existed for ages, their job is to distribute incoming traffic (so called ingress) to application(s).
42 |
43 | So you might have heard or even use [Nginx](https://nginx.org/) or [HAProxy](http://www.haproxy.org/) who have been serving the web for quite a while.
44 |
45 | However since we're dealing with containers now there is one big difference:
46 | In comparison to 'classic' servers/virtual machines containers tend to be very volatile.
47 | Each time a service is deployed or scaled new containers come and old ones go.
48 | The proxy needs to be able to keep track of all these changes which means it's not possible to rely on manual configuration.
49 | Instead some automated reconfiguration is required.
50 |
51 | ## Enter Træfɪk
52 |
53 | [Træfɪk](https://traefik.io/) is an proxy that does just that.
54 | It reconfigures itself continuously by listening to events on the system the containers are running on.
55 | When using Docker (Swarm) it listens to the Docker socket (`/var/run/docker.sock`)
56 |
57 | When a container is started it will be automatically be accesible via Træfɪk.
58 | When a container stops (either intended or unintended) it will be removed from the proxy config again.
59 |
60 | You can now let Træfɪk listen to a given (number of) port number(s) like `80` or `443` and let it decided which traffic should go to which containers based on hostname.
61 | Containers as in plural? yes it does load balancing! It can also do SSL termination, maybe more on that later.
62 |
63 | I can only say this is absolutely _awesome_.
64 | I'm using it for quite a while now and I'm still wondering how I could work without it.
65 | And yes there are other products too but this is so simple yet so powerful.
66 |
67 | ## How does Træfɪk work?
68 |
69 | So I said Træfɪk configures itself automatically?, Well almost.
70 | You have to add some configuration to your application to help Træfɪk understand how it should proxy requests to it.
71 | To be able to do this you'll need to understand the basics of how Træfɪk works.
72 | Træfɪk works with the concepts frontend and backend and makes sure traffic from a given frontend (accessible from the web) goes to a given backend (running on the orchestrator).
73 |
74 | 
75 |
76 | _^Image is courtesy of traefik.io_
77 |
78 | ## Configuring Træfɪk
79 | Træfɪk can be configured via labels, these labels can be set either as part of the container image or by an orchestration tool.
80 | I prefer the latter since I like decoupling in general because it allows different settings for different environments.
81 |
82 | At minimum Træfɪk needs to know the following things of your web application:
83 |
84 | - To which backend it belongs
85 | - On which domain it should be reachable from outside
86 | - On which port it is running
87 | - Via which network it can be reached
88 |
89 | Furthermore I choose to explicitly enable services for Træfɪk.
90 | This way Træfɪk is not bothered by containers I don't want to expose like backend applications and databases.
91 | Also this keeps the UI a lot cleaner.
92 |
93 | Configuration is done by setting labels on the container.
94 |
95 | A minimum configuration looks like this:
96 |
97 | _Note 1: In the examples below I use `docker-compose` but Træfɪk supports many other systems too._
98 |
99 | _Note 2: I left out all the usual stuff_
100 |
101 | ```yaml
102 | version: '2'
103 |
104 | networks:
105 | traefik:
106 | external:
107 | name: traefik_webgateway
108 |
109 | services:
110 | app:
111 | ...
112 | networks:
113 | - traefik
114 | labels:
115 | - "traefik.enable=true"
116 | - "traefik.backend=lucasvanlierop-web"
117 | - "traefik.frontend.rule=Host:lucasvanlierop.nl.localhost"
118 | - "traefik.port=80"
119 | - "traefik.docker.network=traefik_webgateway"
120 | ```
121 |
122 | For a more complete example see the [`docker-compose.yml` of this site](https://github.com/lucasvanlierop/website/blob/e0f9d60bdfda1adbba7f41077df9870d57860688/docker-compose.yml)
123 |
124 | _Note if you use Docker Compose files to deploy to a Docker Swarm Cluster the `labels` configuration goes under `deploy` rather than directly under the service._
125 |
126 | ```yaml
127 | version: '3'
128 |
129 | networks:
130 | traefik:
131 | external:
132 | name: traefik_webgateway
133 |
134 | services:
135 | app:
136 | ...
137 | networks:
138 | - traefik
139 | deploy:
140 | labels:
141 | - "traefik.enable=true"
142 | - "traefik.backend=lucasvanlierop-web"
143 | - "traefik.frontend.rule=Host:lucasvanlierop.nl.localhost"
144 | - "traefik.port=80"
145 | - "traefik.docker.network=traefik_webgateway"
146 | ```
147 |
148 | For the sake of completeness an example of how you could configure this directly in a `Dockerfile`
149 | ```dockerfile
150 | LABEL "traefik.enable=true" \
151 | "traefik.backend=lucasvanlierop-web" \
152 | "traefik.frontend.rule=Host:lucasvanlierop.nl.localhost" \
153 | "traefik.port=80" \
154 | "traefik.docker.network=traefik_webgateway"
155 | ```
156 |
157 | ## Running Træfɪk
158 | While Træfɪk itself is a Go binary you - off course - run it as a Docker container.
159 | There's even an [example Docker Compose configuration](https://docs.traefik.io/#docker).
160 |
161 | Note that I prefer to explicitly enable services to be proxies by Træfɪk rather than having it autodetect all containers.
162 | This can be achieved by running it with `--docker.exposedbydefault=false`.
163 |
164 | For all other options: Træfɪk has pretty good [documentation](https://docs.traefik.io/)
165 |
166 | ## And now a quick look at the user interface
167 |
168 | The user interface is pretty basic but shows the info you need about front and back ends, hosts, http protocols, load balancing protocols etc.
169 |
170 | _Note it also has a tab where you can get some stats about application health but that's out of the scope of this article_
171 |
172 | 
173 |
174 | ## And now try it yourself!
175 |
176 | I hope Træfɪk will be as valuable to you as it is to me, let me know how it worked out for you!
177 |
178 | If you want to need more, take a look at the [documentation](https://docs.traefik.io/).
179 |
180 |
--------------------------------------------------------------------------------
/source/_posts/2017-06-28-running-cli-tools-in-docker-part-1-composer.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Running CLI tools In Docker Part 1; Composer
3 | categories:
4 | software-development
5 | tags:
6 | - docker
7 | - docker-festival
8 | image: /images/blog/software/composer-in-docker.png
9 | imageAlt: Composer logo with containers in the background
10 | ---
11 |
12 | _This article is part of a [series of blog posts](/blog/tags/docker-festival/) related to the
13 | [Docker Festival](https://twitter.com/hashtag/dockerfestival?src=hash) workshop
14 | [Matthias Noback](https://twitter.com/matthiasnoback) and I did at
15 | [Dutch PHP Conference 2017](https://www.phpconference.nl/)_
16 |
17 | ## When your (web) application runs in a container but tools still run on your host
18 |
19 | I've encountered 'hybrid' combinations where people run their (web) applications in a container but still run tools directly on their host.
20 |
21 | There are however good reasons to run all tools in a container too:
22 |
23 | - It guarantees the tools run on the __exact same software__ as the application itself.
24 | - It guarantees all users of the use the __exact same software__
25 | - It does __not require installing extra software__ on your host
26 |
27 | In this post the [PHP package manager Composer](https://getcomposer.org/) will be taken as an example on how to run a CLI tool in a container.
28 | Composer was chosen since I have used it for almost every project the last 5 years.
29 | Furthermore since it has quite a few requirements which makes it an interesting case.
30 |
31 | ## There are a few things you need to understand about running processes in Docker.
32 |
33 | ### Processes in Docker run as root by default!
34 | If that does not ring a bell yet: It's advisable to run processes as a non-root user especially when they generate files.
35 |
36 | __This might sound like a contradiction but by default a user would create files that are NOT owned by that user! It would even require sudo rights to remove those files.__
37 |
38 | ### Some processes [can't be stopped](https://www.youtube.com/watch?v=lP4Nnek6DCo)
39 | The first process that is started in Docker is considered (by Linux) the [init](https://en.wikipedia.org/wiki/Init) process.
40 | Linux is designed to keep that process running whatever happens.
41 | The only way to stop that process (aside from letting Docker `kill` that container) is to let the process itself listen to so called ['signals'](https://en.wikipedia.org/wiki/Unix_signal).
42 |
43 | Most non 'server' processes are not designed to handle signals properly.
44 | In practice this means that you can't stop a process once you've started it.
45 |
46 | This can be resolved by:
47 |
48 | - Declaring a [`STOPSIGNAL`](https://docs.docker.com/engine/reference/builder/#stopsignal) in your `Dockerfile`
49 | - Using an init process such as [Tini](https://github.com/krallin/tini)
50 | - Wrapping the command in a [bash `trap`](http://redsymbol.net/articles/bash-exit-traps/)
51 |
52 | I have personally used Tini a lot since that also solved the [Zombie reaper problem](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/).
53 | If I remember correctly this problem has been solved by now.
54 |
55 | ### (Most) processes expect configuration
56 | Most processes expect some form of configuration either in the form of files or environment variables.
57 | By default processes running in a container do not have access to anything outside the container either fail or behave different than expected.
58 |
59 | Composer for example expects a `.composer` dir to be present in the home dir (which you can specify).
60 | Since this directory not only contains configuration but also cache files there's an enormous speed benefit in 'reusing' that dir for the container.
61 |
62 | ### Some processes expect devices to be present
63 | Just like variables of file systems (almost) no devices are shared with the container.
64 |
65 | In the case of Composer chances are it expects a SSH socket to be present.
66 | As mentioned earlier it's very likely Composer has to download some GIT repositories which is often done via SSH.
67 |
68 | That's a bit of a problem since that would sharing the SSH socket with the container.
69 | While that's possible it's a bit more work.
70 | Also sharing power of SSH with an isolated process which only needs a access a limited set of resources does not fit the 'least access principle' of containerized processes.
71 |
72 | An easier approach in many cases is to use HTTP(S) instead.
73 | In case access to private repositories is required make sure an access token is set up:
74 |
75 | For GitHub this can be done like:
76 | ```bash
77 | composer config -g github-oauth.github.com
78 | ```
79 |
80 | See also the [Composer docs on this](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens)
81 |
82 | For GitLab this can be done like:
83 | ```bash
84 | composer config your.gitlab.domain
85 | ```
86 |
87 | ## Now lets's containerize Composer
88 |
89 | With that out of the way let's see what is required to run Composer in a container
90 |
91 | ### Install
92 |
93 | First install Composer itself.
94 | Since most of the dependencies Composer is likely to install are either in zip or git 'format' support for both is required too.
95 |
96 | ```dockerfile
97 | # Note this assumes you have already create a base image for you application containing
98 | # PHP and all required libraries.
99 | # In this case the base image is expected to be based on Alpine Linux.
100 | FROM your/application:base
101 |
102 | RUN apk update \
103 |
104 | # Install init system for running tasks as pid 1
105 | && apk add --no-cache tini \
106 |
107 | # Install Composer + Git + Zip so it can fetch from various sources
108 | && apk add \
109 | --no-cache \
110 | git \
111 | zlib-dev \
112 | && php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
113 | && php composer-setup.php \
114 | && php -r "unlink('composer-setup.php');" \
115 | && chmod +x composer.phar \
116 | && mv composer.phar /usr/local/bin/composer
117 |
118 | # Run tini as init process
119 | ENTRYPOINT ["/sbin/tini", "--"]
120 | ```
121 |
122 | ### Configure
123 |
124 | Then share the required configuration (in `docker-compose` format).
125 | The following configuration will cause composer to:
126 |
127 | - Run as a normal user (it's possible to automate by exporting these values using the `id` program)
128 | - Look for it's configuration in /home/.composer
129 | - Run all commands in the project dir that has been shared with the container
130 |
131 | _Note: I left out all the usual stuff_
132 |
133 | ```yaml
134 | services:
135 | app:
136 | ...
137 | user: user-id:group-id
138 | working_dir: /your-project-dir
139 | environment:
140 | HOME: /home
141 | volumes:
142 | - ./:/your-project-dir
143 | - ~/.composer:/home/.composer
144 | ```
145 |
146 | ### Run
147 |
148 | Now it's time to run the process.
149 | To prevent having to use docker commands all the time it's nice to have a wrapper script.
150 | The example below runs composer and passes all scripts arguments on to composer.
151 |
152 | ```bash
153 | #!/usr/bin/env bash
154 | docker-compose run --rm app composer "$@"
155 | ```
156 |
157 | You could run the above like for example:
158 |
159 | ```bash
160 | ./composer install --ansi --no-dev --optimize-autoloader
161 | ```
162 |
163 | ## Protip: Add platform requirements
164 | Since compose now runs on the same stack (Composer calls it 'platform') as the application it's possible to add specific requirements of the stack itself
165 | without the risk of false positives or negatives.
166 | For example you can require a specific PHP version or extensions to be installed like:
167 |
168 | ```bash
169 | ./composer require "ext-zip" "*"
170 | ```
171 |
172 | ## Recap
173 | Running CLI tools in Docker is pretty doable but you have to be aware of certain pitfalls.
174 |
175 | I hope I have encouraged you to run your developer tools on the same stack your application runs on.
176 |
--------------------------------------------------------------------------------
/source/_posts/2017-12-31-truly-immutable-deployments-with-docker-or-kubernetes.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Truly Immutable deployments with Docker or Kubernetes.
3 | categories:
4 | - software-development
5 | tags:
6 | - docker
7 | - docker-compose
8 | - docker-festival
9 | - immutability
10 | - kubernetes
11 | - security
12 | image: /images/blog/software/locked-door.jpg
13 | imageAlt: "Just a random picture of a locked door"
14 | ---
15 |
16 | __Target audience:__ developers who (are planning to) run containers in production.
17 |
18 | TL;DR Deploy your containers with an immutable file system, its more secure and predictable!
19 |
20 | ---
21 |
22 | ## Deploying should be boring a.k.a. predictable, what does that look like?
23 |
24 | For me boring means it's predictable, which means the deployment process provides some guarantees, such as:
25 |
26 | - it's obvious which code has been deployed *(e.g. can be related to a commit in version control)*
27 | - deployed code behaves as expected *(asserted by tests)*
28 | - deployed code is fully compatible with the stack it runs on *(asserted by tests)*.
29 | - all of the above do not change once deployed
30 |
31 | For now I'd like to focus on the last item, I'll probably write an article on setting up an automated build and test pipeline later.
32 |
33 | ## How predictable are deployment processes nowadays?
34 |
35 | Deployment practices have come along way since 'the old days' where developers used to edit (hack) files directly on servers without any form of testing.
36 |
37 | In today's container era all of above guarantees can be provided by CI pipelines that build, test and deploy code + infrastructure automatically from version control.
38 | This does not mean they ARE always provided, in fact many deployment processes guarantee:
39 |
40 | - it's obvious which code has been deployed
41 | - deployed code behaves as expected
42 | - deployed code is fully compatible with the stack it runs on.
43 |
44 | But still don't guarantee:
45 |
46 | - all of the above do not change once deployed
47 |
48 | ## How to make a deployment even more predictable with immutable containers
49 |
50 | Let's take a look a some reasons that can cause changes in code or configuration after a deployment.
51 |
52 | - Hacking attempts that change the application or even download scripts
53 | - Bugs in the application that write files to an incorrect path
54 | - Frameworks that generate code for caching purposes (common practice with non compiled languages)
55 | - Automatic updates
56 |
57 | Even if some of these changes are desired, they are not tested and therefore unpredictable. Any guarantees provided by the build/test/deployment process can no longer be trusted. If possible these should be part of the build process rather than happen after deployment. Especially updates since they would be gone when a new version is deployed anyway.
58 |
59 | The good news is that changes can be prevented fairly easy by just starting the container with an immutable (read only) file system.
60 | In practice this means an application, its configuration and basically everything else that's in the container can NOT be changed.
61 | Let's see some examples on how to configure this in Docker & Kubernetes.
62 |
63 | ## Example #1 Configuring immutable containers in Docker Compose (version 3) and Docker Swarm
64 |
65 | With Docker all you have to do is mark the service as 'read only'.
66 |
67 | *Note that this example is for version 3 of the Docker Compose file format which can also be used for deploying to Docker Swarm.
68 | For the 'classic' Docker Compose version 2 format check the example below.*
69 |
70 |
71 | ```yaml
72 | version: '3'
73 |
74 | services:
75 | app:
76 | ...
77 | read_only: true
78 | ```
79 |
80 | The example above assumes the application does not require any changes to the file system.
81 | In practice that might not always be possible because some processes require some files or directories to be writable.
82 | For example a temporary directory for storing file uploads might be necessary.
83 | Of course the final uploaded files should be stored in a persistent volume.
84 | Another example might be processes like Apache HTTP server that want to store their process id in a [`pid` file](https://linux.die.net/man/3/pidfile).
85 |
86 | This of course could be solved by mounting a writable file or directory from the local file system into the container.
87 | However since there's no need to persist these files they can be written to memory instead of disk.
88 |
89 | Docker Compose supports temporary volumes in memory.
90 | In this example a writable directory named `/var/run` will be available in the container.
91 | *Note: it goes without saying that these kind of exclusions should be used as little as possible!*
92 |
93 | ```yaml
94 | version: '3'
95 |
96 | volumes:
97 | apache-run:
98 | driver: local
99 | driver_opts:
100 | type: tmpfs
101 | device: tmpfs
102 |
103 | services:
104 | app:
105 | ...
106 | read_only: true
107 | volumes:
108 | - apache-run:/var/run
109 | ```
110 |
111 | ## Example #2 Configuring immutable containers in the classic Docker Compose version 2 format
112 |
113 | The Docker Compose version `2` file format is a bit different and has a separate `tmpfs` configuration that creates a temporary volume in memory.
114 |
115 | *Note you have to specify at least the `uid` (user id) that should own the volume (unless the process runs as root which is not recommended for security reasons!).*
116 |
117 |
118 | ```yaml
119 | version: "2"
120 |
121 | app:
122 | read_only: true
123 | tmpfs:
124 | - /var/run:uid={uid-of-the-process}
125 | ```
126 |
127 | ## Example #3 Configuring immutable file systems in Kubernetes (v1.7+)
128 |
129 | In Kubernetes an immutable file system can be configured as part of a deployment security context.
130 | Instead of Docker's tmpfs a volume of the type `emptyDir` must be configured.
131 |
132 | *Note: as Slava points out in the comments Kubernetes even supports [enforcing the use of read only file systems](https://kubernetes.io/docs/concepts/policy/pod-security-policy/#volumes-and-file-systems)*
133 |
134 | ```yaml
135 | apiVersion: apps/v1beta1
136 | kind: Deployment
137 | metadata:
138 | ...
139 | spec:
140 | ...
141 | template:
142 | ...
143 | spec:
144 | containers:
145 | - name: app
146 | ...
147 | securityContext:
148 | readOnlyRootFilesystem: true
149 | volumeMounts:
150 | - mountPath: /var/run
151 | name: apache-run
152 | volumes:
153 | - name: apache-run
154 | emptyDir: {}
155 |
156 | ```
157 |
158 | ## Rounding up
159 |
160 | - *Do all containers need to have an immutable file system?*
161 | Of course this is up to you. I would recommend that at least containers where scripts/binaries can be executed should be immutable. So web and application servers and maybe even database servers.
162 |
163 | - *Should development containers have an immutable file system too?*
164 | Not necessarily since it's a mainly security measure for production situations.
165 | However it's better to have a similar setup across all environments so possible issues can be spotted before going to production.
166 |
167 |
168 | *FYI: The examples above are taken from a legacy WordPress project. Since I'm not a WordPress expert at all I intentionally run it in an immutable container to reduce the attack vector and
169 | to prevent unplanned automatic updates from happening. For some more context check the full Docker Compose config at the time of writing for both
170 | [development](https://github.com/allihoppa/allihoppa.nl/blob/4e061496f8d489a00c0d1cf32725d90e376eb426/environment/dev/docker-compose.yml#L28) and
171 | [production](https://github.com/allihoppa/allihoppa.nl/blob/4e061496f8d489a00c0d1cf32725d90e376eb426/environment/prod/docker-compose.yml#L43)*
172 |
173 | ---
174 |
175 | *Thanks
176 | [Jeroen](https://twitter.com/n0x13),
177 | [Annelies](https://twitter.com/alli_hoppa) and
178 | [Caroline](https://twitter.com/erzitkaktussen)
179 | for reviewing this post*
180 |
--------------------------------------------------------------------------------
/source/expertise.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: "default"
3 | name: "Lucas van Lierop's Expertise"
4 | ---
5 |
6 |
7 |
8 |
275 | Besides being an engineer I'm also organizer of DomCode, a non-profit foundation with 1200+
276 | members that organizes events like the annual DomCode conference and
277 | monthly DomCode meetups where (inter)national speakers share their knowledge on
278 | software development.
279 | Since I value knowledge sharing a lot I became an occasional speaker myself.
280 |
281 |
282 |
283 |
284 |
285 |
286 | None work related expertise
287 |
288 |
289 |
Caring for my family
290 |
Event organizing
291 |
General construction work
292 |
MIG welding
293 |
Car maintenance
294 |
Electronics
295 |
296 |
297 |
298 |
299 |
--------------------------------------------------------------------------------
/source/_posts/draft-car-hot-dip-galvanizing.md:
--------------------------------------------------------------------------------
1 | ---
2 | draft: true
3 | title: Galvanize car parts, a manual
4 | categories:
5 | - cars
6 | tags:
7 | - hot dip galvanizing
8 | - cars
9 | - restoration
10 | ---
11 | This manual is based on [an article published earlier on a Land Rover forum](http://www.laroprik.nl/viewtopic.php?f=3&t=503973)
12 | ---
13 |
14 | Since I've have seen many questions regarding hot dip galvanizing of chassis and body parts I've
15 |
16 | Aangezien er regelmatig vragen over thermisch verzinken komen dacht ik waarom niet een handleiding maken voor mensen die er aan willen beginnen. Onderstaand een stuk uit een mail die ik ooit eens gestuurd heb naar iemand (ook weer deels op basis van info van Andrew Colville) die mij weer op weg geholpen heeft. Wie helpt er even mee hier een compleet verhaal van te maken wat we ergens centraal kunnen plaatsen (eigenlijk zou ik toch graag weer een Land Rover wiki willen)
17 | Ontlakken:
18 | Veel mensen laten het stralen, ik ben van mening dat dat niet goed genoeg werkt, aan de buitenkant ziet het er keurig uit maar aan de binnenkant kan het zo vol het tectyl, verf, zand etc zitten en daar hecht geen zink op, zal je zien dat het juist van binnenuit weer gaat roesten, zonde van je geld dus. En ja het gaat door een aantal baden maar die zijn niet bedoeld om dat er allemaal uit te krijgen. Ik heb daarom mijn chassis thermisch laten ontlakken en daarna met compressor het resterende (droge) vuil er uit geblazen.
19 | Gaten boren:
20 | Tip wees niet zuinig boor gewoon overal waar lucht er uit moet / zink er in moet gaten en ook groot genoeg. Alle verhalen over kromtrekken zijn overdreven maar het is wel zaak dat het zink snel genoeg door kan lopen. Denk ook niet “ach dit kleine hoekje, dat zal toch wel gaan?” bij mij bleek zink achter in bijvoorbeeld de veerhand en de outriggers. Afgezien van dat dit geld kost en gewicht verhoogt is het soms ook heel onhandig omdat iets niet meer past (in mijn geval o.a. een schroefveer…).
21 | Neem als stelregel dat alles wat een 3 dimensionale vorm heeft doorboord moet worden dus komen er 3 stukken plaat bij elkaar in een hoek of maakt het chassis een bolling, boor er een gat in! Verder nog overal boren waar stukken plaat doorgelast op gelkaar zitten, denk dan aan dwarsbalken in je chassis, dat was in ieder geval bij mij zo. Er zal namelijk altijd, lucht, vuil etc tussen de 2 stukken zitten en dat moet weg kunnen. Bij mij zag je naderhand op dat soort plekken echt klonten donkere prut, waarvan ik gok dat het verroest staal en zink is die tussen de dubbelingen 'uitgeploft is. Dit was bij de body meer dan bij het chassis dus bij jou wellicht bij het schutbord.
22 | Neem flink tijd voor het gaten boren (uren werk!), koop goede harde boren, koel ze met olie, boor netjes van klein naar groot en loop stukje voor stukje je chassis na of het zink overal door kan stromen. Als het zink ergens niet kan komen is dat zonde van de investering en bovendien gevaarlijk want de lucht die opgesloten raakt zet enorm uit door de hitte en zal het chassis doen exploderen.
23 | Achteraf schilderen:
24 | Ga je het ook nog schilderen (zink roest niet maar lost langzaam op) zoja bedenk dan op voorhand wat/hoe je er op wilt smeren, de meeste dingen die op staal hechten doen dat niet op zink, bovendien moet je het ook serieus opschuren of laten aanstralen en in de primer zetten. Poedercoating wordt door veel mensen afgeraden. Ik heb zelf uiteindelijk Brantho Korrux gekozen puur omdat ik dat bij lage temperatuur kan aanbrengen.
25 | Laat je niet bang maken:
26 | Bovenstaande is niet om je bang te maken, dat zullen anderen wel doen. Dan bedoel ik mensen die liever tectyleren of de verzinkerij zelf die zal zeggen dat het kromtrekt. Nee het is bedoeld om je te motiveren er echt iets moois van te maken.
27 | Groet
28 | Rapporteren
29 | Anonymous avatar
30 | Justus Christus
31 | 21-02-2013 11:00
32 | Hoi Lucas,
33 | Prima post!
34 | Erg handig.
35 | Heb je ook tips voor bijvoorbeeld een schutbord?
36 | Mogelijk kunnen we een keer een paar fotos ritselen met de benodigde gaten zichtbaar!
37 | Groet,
38 | Justus!
39 | Rapporteren
40 | Anonymous avatar
41 | Lucas van Lierop
42 | 21-02-2013 11:17
43 | Hoi Justus,
44 | Ik heb geen los schutbord zoals een Def of een Serie maar er zijn wellicht mensen die daar foto's van hebben. Echter als je deze poster volgt zit je altijd goed.
45 | Succes
46 | Rapporteren
47 | Anonymous avatar
48 | Geert Oude Luttighuis
49 | 21-02-2013 11:27
50 | Lucas, mooie aanzet om vragen vóór te zijn/blijven. Toch ook een vraag nav je post: heb je tips voor het thermisch ontlakken? Of is dit in één run gegaan bij je verzinkerij?
51 | Rapporteren
52 | Anonymous avatar
53 | Lucas van Lierop
54 | 21-02-2013 11:36
55 | Ik heb eigenlijk maar een tip, zorg dat ze NIET je loodjes er af knippen omdat ze ‘denken’ dat die ‘toch smelten’. Verder is het gewoon een kwestie van afleveren en ophalen. Ik heb het 1,5 jaar voor het verzinken gedaan zodat ik daar tussenin fijn kon lassen.
56 | Gr
57 | Rapporteren
58 | Anonymous avatar
59 | Geert Oude Luttighuis
60 | 21-02-2013 11:46
61 | Hahaha, goeie tip ja… Maar ik doelde eigenlijk meer op tips als adressen, kosteninzicht e.d., aangezien ze niet overal zomaar thermisch kunnen ontlakken (een pyrolyse-oven is een behoorlijke investering).
62 | Rapporteren
63 | Anonymous avatar
64 | Lucas van Lierop
65 | 21-02-2013 11:54
66 | Ah ok, Ik ben bij TCI in tilburg geweest, voor een complete carrosserie en chassis was ik daar aardig wat geld kwijt (1700 euro). Wellicht is dat met alleen chassis en of schutbord al stuk goedkoper omdat ze er dan makkelijker andere dingen bij kunnen zetten om de prijs te drukken. De volgende keer zou ik daar beter over onderhandelen… Voordeel was wel dat het daarna superfijn werken was zonder allerlei verf tectyl etc. Ik kon overal zo lassen (wel nog roest wegpoetsen). En het verzinken ging dus ook heel goed omdat het zo mooi kaal was, het verzinken zelf is veel goedkoper dan het ontlakken (bv 1,50 per kilo)
67 | Gr
68 | Rapporteren
69 | Anonymous avatar
70 | Nick
71 | 21-02-2013 12:45
72 | Foto`s van een verzinkt schutbord staan op mijn “mijnalbums” pagina. Achteraf gezien had ik het laswerk mooier willen afwerken, maar dit was toen ter tijd bij wijze van proef, die achteraf érg goed gelukt is. Schutbord is gewoon gemonteerd geworden (+/- 3 jaar geleden) en ziet er nog steeds perfect uit, nergens roest te bekennen.
73 | Als ik een keer tijd heb zal ik een poging doen om hier een stukje over te schrijven.
74 | Rapporteren
75 | Anonymous avatar
76 | Andrew Colville
77 | 22-02-2013 01:09
78 | Lucas, Ik wil even toe voegen dat kromtrekken wel kan, maar niet op een manier waardoor het onbruikbaar wordt, Je chassis en schutbord vouwen zich niet dubbel:D de koets van de Classic die ik gedaan heb had hier en daar wel bolstaand plaatwerk en dat valt mijn inziens onder krom trekken het blijft eigen risico, en de voor behandeling het ontlakken en gaten boren zijn heel belangerijk.
79 | Misschien kun je dat aan je verhaaltje toevoegen?
80 | Verder kun je te verzinken oud staal (geverfd/tectyl etc ) ook laten “Loogen” ontlakken op basis van chemicaliën, ook dit is prijzig €1000 euro voor de koets en chassis destijds ( 2006 uit mijn hoofd ) Maar dan zit er niks meer behalve staal, het staal is helemaal schoon ook tussen de naden en in kokers en plakken waar je niet aan denkt.
81 | je kunst werk begint direct te roesten ( vlieg roest genoemd ) dat is net erg voor het zink procces, zelfs beter voor de aanhechting van het zink, maar minder handig als je buiten werkt aan het project en nog veel moet lassen voor je naar de verzinkerijk kunt, je kunt het roesten tegen gaan door het staal in te vetten, dit vet wordt bij de verzinkerij weer verwijdert in de voor behandelings procces.
82 | de Loog chemicaliën hebben ook geen invloed op het zink, maar wel wanner je direct het staal gaat lakken en niet eerst verzinken, de chemicaliën kunnen je lak opvreten.
83 | Rapporteren
84 | Anonymous avatar
85 | Lucas van Lierop
86 | 22-02-2013 07:40
87 | Dank voor je toeoeging, ik ga er een een mooi verhaal van maken. Bij mij stond er idd ook wel wat bol maar niks zichtbaars. verder ook een stukje krom maar dat is echt mijn schuld omdat ik daar de wing half verbouwd had ivm grotere wielen en een snorkel. Ach ja dat is ook geen punt, voordeel bij land rover is dat je bijna alles met alu plaatwerk afdekt
88 | Rapporteren
89 | 1
90 |
91 | 2
92 |
93 | het gevaar van kromtrekken is inderdaad niet zo groot. Er is wat bol gaan staan inderdaad. maar niet erg. Er was meer beschadigd doordat ze de boel waarschijnlijk hebben laten vallen of tijdens het hijsen tegen elkaar hebben laten stoten. Maar ook dat was geen probleem.
94 | Voor versteviging had ik de punten van het schutbord met een hoeklijn en bouten aan elkaar bevestigd. Helaas had ik daar in mijn onwetendheid bouten voor gebruikt die verzinkt waren. Ik kreeg alles netjes terug in onderdelen. De hoeklijn en het schutbord hadden ze van elkaar af gehaald en afzonderlijk verzinkt. :( Gelukkig was er niets krom geworden. Maar gebruik dus geen verzinkte bouten om steunstrips te bevestigen.
95 | Arnold.
96 | Rapporteren
97 | Anonymous avatar
98 | mirza.denhond@hotmail.com
99 | 01-03-2013 19:39
100 | Hallo.
101 | Ook een goed alternatief voor thermisch verzinken, en ook als alternatief voor electrolytisch verzinken ,is het behandelen met ZINGA. Dit is een coating/verf van bijna pure zink dat koudgalvanisatie noemt. Ik heb destijds, meer dan 20 jaar geleden, delen van mijn 109 schutbord behandeld en het roest komt er nog niet door. Dit produkt dat met borstel of spuitpistool wordt aangebracht hecht op lichte roest, en zelfs op licht vochtige ondergrond. Wel is het best dat het metaal opgeschuurd of gezandstraald is. Op verf heeft het een slechte hechting, maar daarentegen hecht bijna iedere verf op de ZINGA. Op het internet is er wel meer terug te vinden.
102 | Rapporteren
103 | Anonymous avatar
104 | Lucas van Lierop
105 | 02-03-2013 19:32
106 | Afgezien van het feit dat alles wat je erop smeert een alternatief is beschouw ik dit niet echt als een alternatief. Bij thermisch verzinken komt het zink nl. Ook IN de constructie en zelfs voor een deel In de naden. Dit bereik je alleen met dippen in een bad.
107 | Rapporteren
108 |
--------------------------------------------------------------------------------
/source/scss/normalize.scss:
--------------------------------------------------------------------------------
1 | /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /**
4 | * 1. Change the default font family in all browsers (opinionated).
5 | * 2. Correct the line height in all browsers.
6 | * 3. Prevent adjustments of font size after orientation changes in
7 | * IE on Windows Phone and in iOS.
8 | */
9 |
10 | /* Document
11 | ========================================================================== */
12 |
13 | html {
14 | font-family: sans-serif; /* 1 */
15 | line-height: 1.15; /* 2 */
16 | -ms-text-size-adjust: 100%; /* 3 */
17 | -webkit-text-size-adjust: 100%; /* 3 */
18 | }
19 |
20 | /* Sections
21 | ========================================================================== */
22 |
23 | /**
24 | * Remove the margin in all browsers (opinionated).
25 | */
26 |
27 | body {
28 | margin: 0;
29 | }
30 |
31 | /**
32 | * Add the correct display in IE 9-.
33 | */
34 |
35 | article,
36 | aside,
37 | footer,
38 | header,
39 | nav,
40 | section {
41 | display: block;
42 | }
43 |
44 | /**
45 | * Correct the font size and margin on `h1` elements within `section` and
46 | * `article` contexts in Chrome, Firefox, and Safari.
47 | */
48 |
49 | h1 {
50 | font-size: 2em;
51 | margin: 0.67em 0;
52 | }
53 |
54 | /* Grouping content
55 | ========================================================================== */
56 |
57 | /**
58 | * Add the correct display in IE 9-.
59 | * 1. Add the correct display in IE.
60 | */
61 |
62 | figcaption,
63 | figure,
64 | main { /* 1 */
65 | display: block;
66 | }
67 |
68 | /**
69 | * Add the correct margin in IE 8.
70 | */
71 |
72 | figure {
73 | margin: 1em 40px;
74 | }
75 |
76 | /**
77 | * 1. Add the correct box sizing in Firefox.
78 | * 2. Show the overflow in Edge and IE.
79 | */
80 |
81 | hr {
82 | box-sizing: content-box; /* 1 */
83 | height: 0; /* 1 */
84 | overflow: visible; /* 2 */
85 | }
86 |
87 | /**
88 | * 1. Correct the inheritance and scaling of font size in all browsers.
89 | * 2. Correct the odd `em` font sizing in all browsers.
90 | */
91 |
92 | pre {
93 | font-family: monospace, monospace; /* 1 */
94 | font-size: 1em; /* 2 */
95 | }
96 |
97 | /* Text-level semantics
98 | ========================================================================== */
99 |
100 | /**
101 | * 1. Remove the gray background on active links in IE 10.
102 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
103 | */
104 |
105 | a {
106 | background-color: transparent; /* 1 */
107 | -webkit-text-decoration-skip: objects; /* 2 */
108 | }
109 |
110 | /**
111 | * Remove the outline on focused links when they are also active or hovered
112 | * in all browsers (opinionated).
113 | */
114 |
115 | a:active,
116 | a:hover {
117 | outline-width: 0;
118 | }
119 |
120 | /**
121 | * 1. Remove the bottom border in Firefox 39-.
122 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
123 | */
124 |
125 | abbr[title] {
126 | border-bottom: none; /* 1 */
127 | text-decoration: underline; /* 2 */
128 | text-decoration: underline dotted; /* 2 */
129 | }
130 |
131 | /**
132 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
133 | */
134 |
135 | b,
136 | strong {
137 | font-weight: inherit;
138 | }
139 |
140 | /**
141 | * Add the correct font weight in Chrome, Edge, and Safari.
142 | */
143 |
144 | b,
145 | strong {
146 | font-weight: bolder;
147 | }
148 |
149 | /**
150 | * 1. Correct the inheritance and scaling of font size in all browsers.
151 | * 2. Correct the odd `em` font sizing in all browsers.
152 | */
153 |
154 | code,
155 | kbd,
156 | samp {
157 | font-family: monospace, monospace; /* 1 */
158 | font-size: 1em; /* 2 */
159 | }
160 |
161 | /**
162 | * Add the correct font style in Android 4.3-.
163 | */
164 |
165 | dfn {
166 | font-style: italic;
167 | }
168 |
169 | /**
170 | * Add the correct background and color in IE 9-.
171 | */
172 |
173 | mark {
174 | background-color: #ff0;
175 | color: #000;
176 | }
177 |
178 | /**
179 | * Add the correct font size in all browsers.
180 | */
181 |
182 | small {
183 | font-size: 80%;
184 | }
185 |
186 | /**
187 | * Prevent `sub` and `sup` elements from affecting the line height in
188 | * all browsers.
189 | */
190 |
191 | sub,
192 | sup {
193 | font-size: 75%;
194 | line-height: 0;
195 | position: relative;
196 | vertical-align: baseline;
197 | }
198 |
199 | sub {
200 | bottom: -0.25em;
201 | }
202 |
203 | sup {
204 | top: -0.5em;
205 | }
206 |
207 | /* Embedded content
208 | ========================================================================== */
209 |
210 | /**
211 | * Add the correct display in IE 9-.
212 | */
213 |
214 | audio,
215 | video {
216 | display: inline-block;
217 | }
218 |
219 | /**
220 | * Add the correct display in iOS 4-7.
221 | */
222 |
223 | audio:not([controls]) {
224 | display: none;
225 | height: 0;
226 | }
227 |
228 | /**
229 | * Remove the border on images inside links in IE 10-.
230 | */
231 |
232 | img {
233 | border-style: none;
234 | }
235 |
236 | /**
237 | * Hide the overflow in IE.
238 | */
239 |
240 | svg:not(:root) {
241 | overflow: hidden;
242 | }
243 |
244 | /* Forms
245 | ========================================================================== */
246 |
247 | /**
248 | * 1. Change the font styles in all browsers (opinionated).
249 | * 2. Remove the margin in Firefox and Safari.
250 | */
251 |
252 | button,
253 | input,
254 | optgroup,
255 | select,
256 | textarea {
257 | font-family: sans-serif; /* 1 */
258 | font-size: 100%; /* 1 */
259 | line-height: 1.15; /* 1 */
260 | margin: 0; /* 2 */
261 | }
262 |
263 | /**
264 | * Show the overflow in IE.
265 | * 1. Show the overflow in Edge.
266 | */
267 |
268 | button,
269 | input { /* 1 */
270 | overflow: visible;
271 | }
272 |
273 | /**
274 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
275 | * 1. Remove the inheritance of text transform in Firefox.
276 | */
277 |
278 | button,
279 | select { /* 1 */
280 | text-transform: none;
281 | }
282 |
283 | /**
284 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
285 | * controls in Android 4.
286 | * 2. Correct the inability to style clickable types in iOS and Safari.
287 | */
288 |
289 | button,
290 | html [type="button"], /* 1 */
291 | [type="reset"],
292 | [type="submit"] {
293 | -webkit-appearance: button; /* 2 */
294 | }
295 |
296 | /**
297 | * Remove the inner border and padding in Firefox.
298 | */
299 |
300 | button::-moz-focus-inner,
301 | [type="button"]::-moz-focus-inner,
302 | [type="reset"]::-moz-focus-inner,
303 | [type="submit"]::-moz-focus-inner {
304 | border-style: none;
305 | padding: 0;
306 | }
307 |
308 | /**
309 | * Restore the focus styles unset by the previous rule.
310 | */
311 |
312 | button:-moz-focusring,
313 | [type="button"]:-moz-focusring,
314 | [type="reset"]:-moz-focusring,
315 | [type="submit"]:-moz-focusring {
316 | outline: 1px dotted ButtonText;
317 | }
318 |
319 | /**
320 | * Change the border, margin, and padding in all browsers (opinionated).
321 | */
322 |
323 | fieldset {
324 | border: 1px solid #c0c0c0;
325 | margin: 0 2px;
326 | padding: 0.35em 0.625em 0.75em;
327 | }
328 |
329 | /**
330 | * 1. Correct the text wrapping in Edge and IE.
331 | * 2. Correct the color inheritance from `fieldset` elements in IE.
332 | * 3. Remove the padding so developers are not caught out when they zero out
333 | * `fieldset` elements in all browsers.
334 | */
335 |
336 | legend {
337 | box-sizing: border-box; /* 1 */
338 | color: inherit; /* 2 */
339 | display: table; /* 1 */
340 | max-width: 100%; /* 1 */
341 | padding: 0; /* 3 */
342 | white-space: normal; /* 1 */
343 | }
344 |
345 | /**
346 | * 1. Add the correct display in IE 9-.
347 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
348 | */
349 |
350 | progress {
351 | display: inline-block; /* 1 */
352 | vertical-align: baseline; /* 2 */
353 | }
354 |
355 | /**
356 | * Remove the default vertical scrollbar in IE.
357 | */
358 |
359 | textarea {
360 | overflow: auto;
361 | }
362 |
363 | /**
364 | * 1. Add the correct box sizing in IE 10-.
365 | * 2. Remove the padding in IE 10-.
366 | */
367 |
368 | [type="checkbox"],
369 | [type="radio"] {
370 | box-sizing: border-box; /* 1 */
371 | padding: 0; /* 2 */
372 | }
373 |
374 | /**
375 | * Correct the cursor style of increment and decrement buttons in Chrome.
376 | */
377 |
378 | [type="number"]::-webkit-inner-spin-button,
379 | [type="number"]::-webkit-outer-spin-button {
380 | height: auto;
381 | }
382 |
383 | /**
384 | * 1. Correct the odd appearance in Chrome and Safari.
385 | * 2. Correct the outline style in Safari.
386 | */
387 |
388 | [type="search"] {
389 | -webkit-appearance: textfield; /* 1 */
390 | outline-offset: -2px; /* 2 */
391 | }
392 |
393 | /**
394 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
395 | */
396 |
397 | [type="search"]::-webkit-search-cancel-button,
398 | [type="search"]::-webkit-search-decoration {
399 | -webkit-appearance: none;
400 | }
401 |
402 | /**
403 | * 1. Correct the inability to style clickable types in iOS and Safari.
404 | * 2. Change font properties to `inherit` in Safari.
405 | */
406 |
407 | ::-webkit-file-upload-button {
408 | -webkit-appearance: button; /* 1 */
409 | font: inherit; /* 2 */
410 | }
411 |
412 | /* Interactive
413 | ========================================================================== */
414 |
415 | /*
416 | * Add the correct display in IE 9-.
417 | * 1. Add the correct display in Edge, IE, and Firefox.
418 | */
419 |
420 | details, /* 1 */
421 | menu {
422 | display: block;
423 | }
424 |
425 | /*
426 | * Add the correct display in all browsers.
427 | */
428 |
429 | summary {
430 | display: list-item;
431 | }
432 |
433 | /* Scripting
434 | ========================================================================== */
435 |
436 | /**
437 | * Add the correct display in IE 9-.
438 | */
439 |
440 | canvas {
441 | display: inline-block;
442 | }
443 |
444 | /**
445 | * Add the correct display in IE.
446 | */
447 |
448 | template {
449 | display: none;
450 | }
451 |
452 | /* Hidden
453 | ========================================================================== */
454 |
455 | /**
456 | * Add the correct display in IE 10-.
457 | */
458 |
459 | [hidden] {
460 | display: none;
461 | }
462 |
--------------------------------------------------------------------------------
/source/_posts/2018-02-02-deploying-dockerized-applications-via-an-ssh-tunnel.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Deploying Dockerized applications via an SSH tunnel.
3 | categories:
4 | software-development
5 | tags:
6 | - continuous-integration
7 | - continuous-deployment
8 | - docker
9 | - docker-festival
10 | - docker-swarm
11 | image: /images/blog/software/tunnel.jpg
12 | ---
13 |
14 | __Target audience:__ Developers who want to (continuously) deploy their Dockerized application.
15 |
16 | *TL;DR: SSH can be used as an alternative to deploying Dockerized applications, this works best with Docker Swarm.*
17 | ---
18 |
19 | Many Docker projects start life as a Docker Compose based development setup.
20 | At a given moment these projects need to be deployed to production.
21 | This article will explain an alternative way deploying to a remote Docker server/cluster using the Docker socket and SSH instead of the Docker HTTP API.
22 |
23 | ## Why deploy Dockerized applications over SSH instead of directly via the API?
24 | *Disclaimer this article doesn't say you MUST but you COULD deploy over SSH.*
25 |
26 | Docker offers a secured API which can be used for deployments.
27 | However... not everyone (or one's system administrator) wants to expose yet another port to the outside world.
28 | Also the Docker API requires setting up some TLS certificates.
29 | Docker has a tool named ['Machine'](https://docs.docker.com/machine/) that can manage those certificates however Machine comes with its own configuration which along with the certificates isn't very portable.
30 |
31 | An alternative solution is to use the Docker socket and let 'come to you' over a secure connection that in most cases is already present: SSH. Or more specific: an SSH tunnel.
32 | Since SSH supports public key authentication granting deployment access to a co-worker or CI server is just a matter of copying their public key to the server/cluster.
33 |
34 | By using an [SSH tunnel](https://www.ssh.com/ssh/tunneling/) it's possible to 'trick' Docker by letting it think
35 | it's deploying to a local port which is actually a Docker socket on a remote system.
36 |
37 | ## But should I expose SSH at all?
38 |
39 |
40 |
41 | Well David has a fair point to consider. In this case I suggest NOT to use SSH to make changes to the server
42 | - [#immutabilityFTW](https://twitter.com/search?q=immutabilityFTW&src=typd) -
43 | but ONLY for deployments.
44 |
45 | ## Prerequisites
46 | - A server with [Docker installed](https://docs.docker.com/engine/installation/).
47 | - A Docker Image registry like [Docker Hub](https://hub.docker.com/).
48 | - A way to (preferably an automated CI pipeline) build Docker images and push them to the registry.
49 | *(If you're unsure how to set this up, let me know in the comments. I might write an article about it!).*
50 | - Knowledge of writing [Docker Compose v3 files](https://docs.docker.com/compose/compose-file/)
51 |
52 | ## What needs to be done to deploy Dockerized applications via SSH?
53 | - Define which services should run e.g. in a (Docker stack deploy compatible) `docker-compose.yml` V3 file, which is probably similar to the development setup.
54 | - Define environment specific config either the `environment` section of a `docker-compose.yml` file or in a separate `*.env` file.
55 | - Tell the server to start/update these services with the specified config.
56 |
57 | ## Why Docker Swarm should be used for production rather than Docker Compose
58 | Earlier in this article Docker Swarm is mentioned a few times, why not use Docker Compose?
59 | A common first approach to deploy application to a production server is to use Docker Compose since that is often the tool used during development.
60 | While using Docker Compose for production is possible, Docker has a better alternative built in named: [Swarm](https://docs.docker.com/get-started/part4/).
61 | Swarm - *from a deployment perspective* - works very similar to Docker Compose but has more advanced orchestration capabilities.
62 |
63 | Compared to Docker Compose, Docker Swarm has the following advantages:
64 |
65 | - It doesn't require installing Docker Compose on the server
66 | - It doesn't require files like `docker-compose.yml`, `*.env` to be copied to the remote server
67 | - It supports multiple servers for high availability (or just to provide more resources)
68 | - It supports storing [secrets](https://docs.docker.com/engine/swarm/secrets/) for e.g. credentials that cannot be provided as an environment variable
69 |
70 | Alternatively to Docker Swarm there are of course orchestrators like
71 | [Kubernetes](https://kubernetes.io/)
72 | or [DC/OS Marathon](https://mesosphere.com/blog/marathon-production-ready-containers/)
73 | but Docker Swarm still is the easiest production ready Docker orchestration tool to start with.
74 |
75 | So let's start!
76 |
77 | ## Step 1: preparing a server for use with Docker Swarm
78 |
79 | To converting an existing Docker (1.13+) server into a Swarm node all that needs to be done is run the following command on the server:
80 | ```bash
81 | docker swarm init
82 | ```
83 |
84 | *Note: When a multi server ('node' in Docker Swarm speak) setup is desired a bit more is required -> read the [docs](https://docs.docker.com/get-started/part4/).*
85 |
86 |
87 | *Note: if containers started by `docker(-compose)` are already running on the host the need to be stopped and restarted via `docker stack deploy`.*
88 |
89 | ## Step 2: setting up a tunnel to the Docker socket
90 |
91 | As explained earlier the goal is to tunnel the remote Docker socket to the local system
92 | There's one caveat though: this only works on SSHv6.7+ while most older OSes are stuck on SSHv6.
93 | However there's no need for updating since you can run an [SSH tunnel in Docker](https://hub.docker.com/r/kingsquare/tunnel/).
94 |
95 | *Note: while it's possible to tunnel a remote __socket__ to a local __socket__ in this example the remote Docker __socket__ is tunneled to a locale __port__.
96 | This prevents having to deal with file permissions of the socket.*
97 |
98 | ```bash
99 | DOCKER_TUNNEL_CONTAINER=docker_swarm_ssh_tunnel
100 | DOCKER_TUNNEL_PORT=12374
101 | DOCKER_SWARM_HOST={public-ip-or-hostname-of-a-swarm-master}
102 | DEPLOY_USER={user-that-can-connect-via-ssh}
103 |
104 | docker run \
105 | -d \
106 | --name ${DOCKER_TUNNEL_CONTAINER} \
107 | -p ${DOCKER_TUNNEL_PORT}:${DOCKER_TUNNEL_PORT} \
108 | -v ${SSH_AUTH_SOCK}:/ssh-agent \
109 | kingsquare/tunnel \
110 | *:${DOCKER_TUNNEL_PORT}:/var/run/docker.sock \
111 | ${DEPLOY_USER}@${DOCKER_SWARM_HOST}
112 | ```
113 |
114 | *Note: Above setup is known to work on Debian like distros,
115 | Red Hat like distros do not seem to have a `SSH_AUTH_SOCK` environment variable.
116 | Suggestions to make this work on Red Hat like distros are welcome.*
117 |
118 | ## Step 3: waiting until the tunnel is established
119 | Now the tunnel has been started (in the background) any further commands should wait until it's actually usable.
120 | An easy way to do that is to poll the tunnel by executing a simple docker command like `docker version`.
121 |
122 | ```bash
123 | until docker -H localhost:${DOCKER_TUNNEL_PORT} version 2>/dev/null 1>/dev/null > /dev/null; do
124 | echo "Waiting for docker tunnel";
125 | sleep 1;
126 | done
127 | ```
128 |
129 | *Note: if the connection cannot be established for some reason the `until` loop will run forever.
130 | If desired a [`timeout`](https://ss64.com/bash/timeout.html) can be added to stop the polling after a given amount of time.
131 | This requires wrapping the loop in a separate bash script (or make target)*
132 |
133 | ## Step 4: deploying with `docker stack deploy`
134 |
135 | Once a SSH tunnel has been established Docker can use it to deploy the stack to a remote Swarm node:
136 |
137 | ```bash
138 | DOCKER_STACK_FILE={path/to/docker-compose.yml}
139 | DOCKER_STACK_NAME={name-that-will-be-prefixed-to-each-server}
140 |
141 | docker \
142 | -H localhost:${DOCKER_TUNNEL_PORT} \
143 | stack deploy \
144 | --with-registry-auth \
145 | -c ${DOCKER_STACK_FILE} \
146 | --prune \
147 | ${DOCKER_STACK_NAME}
148 | ```
149 |
150 | ## Step 5: closing the tunnel again
151 | After the deploying has either succeeded or failed the tunnel should be closed again.
152 |
153 | ```bash
154 | docker stop ${DOCKER_TUNNEL_CONTAINER}
155 | docker rm ${DOCKER_TUNNEL_CONTAINER}
156 | ```
157 |
158 | ## A final overview of the total script
159 |
160 | *Note: this example is a simplified version of the deployment setup of [this website](https://lucasvanlierop.nl/) which relies heavily [GNU Make](https://www.gnu.org/software/make/manual/make.html) for orchestrating testing, building and deploying.
161 | If you're not experience with GNU Make yet please give it a try, especially the [pre requisites](https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html) are very powerful once you grasp the concept.*
162 |
163 | ```bash
164 | #!/usr/bin/env bash
165 |
166 | #...docker build, docker login, docker push etc.
167 |
168 | DOCKER_TUNNEL_CONTAINER=docker_swarm_ssh_tunnel
169 | DOCKER_TUNNEL_PORT=12374
170 | DOCKER_SWARM_HOST={public-ip-or-hostname-of-a-swarm-master}
171 | DEPLOY_USER={user-that-can-connect-via-ssh}
172 | DOCKER_STACK_FILE={path/to/docker-compose.yml}
173 | DOCKER_STACK_NAME={name-that-will-be-prefixed-to-each-server}
174 |
175 | docker run \
176 | -d \
177 | --name ${DOCKER_TUNNEL_CONTAINER} \
178 | -p ${DOCKER_TUNNEL_PORT}:${DOCKER_TUNNEL_PORT} \
179 | -v ${SSH_AUTH_SOCK}:/ssh-agent \
180 | kingsquare/tunnel \
181 | *:${DOCKER_TUNNEL_PORT}:/var/run/docker.sock \
182 | ${DEPLOY_USER}@${DOCKER_SWARM_HOST}
183 |
184 | until docker -H localhost:${DOCKER_TUNNEL_PORT} version 2>/dev/null 1>/dev/null > /dev/null; do
185 | echo "Waiting for docker tunnel";
186 | sleep 1;
187 | done
188 |
189 | docker \
190 | -H localhost:${DOCKER_TUNNEL_PORT} \
191 | stack deploy \
192 | --with-registry-auth \
193 | -c ${DOCKER_STACK_FILE} \
194 | --prune \
195 | ${DOCKER_STACK_NAME}
196 |
197 | docker stop ${DOCKER_TUNNEL_CONTAINER}
198 | docker rm ${DOCKER_TUNNEL_CONTAINER}
199 | ```
200 |
201 | Enjoy deploying your applications!
202 |
203 | To see this in more context check [the deploy setup of this site at the time of writing this article](https://github.com/lucasvanlierop/website/blob/ed0483d5f6b12335a735da0e3d8b6859aacfe8f4/Makefile#L112)
204 |
205 | ---
206 |
207 | *Thanks
208 | [Robin](https://twitter.com/fruitl00p) for creating this awesome [Docker SSH Tunnel image](https://hub.docker.com/r/kingsquare/tunnel/)
209 | and [Bram](https://twitter.com/Brammm) for triggering to finally write this article.*
210 |
211 | *Thanks
212 | [Annelies](https://twitter.com/alli_hoppa) and
213 | [Bram](https://twitter.com/Brammm)
214 | for reviewing this post*
215 |
--------------------------------------------------------------------------------
/source/images/blog/software/empowered-by-gnu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------