<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="nl">
	<id>https://hackerspacenijmegen.nl/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mrngm</id>
	<title>Hackerspace Nijmegen Wiki - Gebruikersbijdragen [nl]</title>
	<link rel="self" type="application/atom+xml" href="https://hackerspacenijmegen.nl/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mrngm"/>
	<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/Speciaal:Bijdragen/Mrngm"/>
	<updated>2026-05-25T03:59:27Z</updated>
	<subtitle>Gebruikersbijdragen</subtitle>
	<generator>MediaWiki 1.39.2</generator>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Categorie:Projects&amp;diff=319</id>
		<title>Categorie:Projects</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Categorie:Projects&amp;diff=319"/>
		<updated>2020-01-21T22:22:11Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: Nieuwe pagina aangemaakt met &amp;#039;This page gives an overview of all present and past projects at Hackerspace Nijmegen&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page gives an overview of all present and past projects at Hackerspace Nijmegen&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=318</id>
		<title>Hackerspace Nijmegen</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=318"/>
		<updated>2020-01-21T22:20:08Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
&lt;br /&gt;
= Hackerspace Nijmegen =&lt;br /&gt;
&lt;br /&gt;
We&#039;re located at [https://www.openstreetmap.org/node/2826874262 Elzenstraat 4a], 6523EV Nijmegen. [tel:+31244200242 Call us] (+31244200242) when we&#039;re open and the gate or entrance is closed.&lt;br /&gt;
&lt;br /&gt;
Join us on [irc://irc.snt.utwente.nl:6669/hsnijmegen IRC] (IRCnet: #hsnijmegen), [https://twitter.com/Hackerspace024 twitter], or the mailinglist: [[File:listemail.png]]!&lt;br /&gt;
&lt;br /&gt;
Enjoyed your visit at our hackerspace and want more? Become a [[Contributing|contributor]]!&lt;br /&gt;
&lt;br /&gt;
[[About_Hackerspace_Nijmegen|Here&#039;s our general contact page]]&lt;br /&gt;
&lt;br /&gt;
== Open when? ==&lt;br /&gt;
&lt;br /&gt;
The space is open:&lt;br /&gt;
* every Tuesday. Ask on IRC regarding the opening time.&lt;br /&gt;
* every Monday for &amp;quot;synth and stompbox night&amp;quot;&lt;br /&gt;
* when our [http://www.hackerspacenijmegen.nl spacestate (bottom of the page)] says so!&lt;br /&gt;
&lt;br /&gt;
== Projects ==&lt;br /&gt;
&lt;br /&gt;
{{Special:PrefixIndex/Projects:}}&lt;br /&gt;
* [[HSNWiki:Migration_from_Gitwiki|Wiki migration status]]&lt;br /&gt;
&lt;br /&gt;
== Events (past and present) ==&lt;br /&gt;
{{Special:PrefixIndex/Event:}}&lt;br /&gt;
&lt;br /&gt;
== Contributing to the wiki ==&lt;br /&gt;
&lt;br /&gt;
* [[Speciaal:GebruikerAanmaken|Request an account]]&lt;br /&gt;
* One of the wiki admins processes your request, and usually approves it.&lt;br /&gt;
* Edit away!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;It&#039;s a wiki&#039;&#039;: improve things! Join us in the conversation in #hsnwiki on IRCnet (same network as #hsnijmegen) if you have questions!&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=317</id>
		<title>Hackerspace Nijmegen</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=317"/>
		<updated>2020-01-21T22:18:49Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: /* Hackerspace Nijmegen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
&lt;br /&gt;
= Hackerspace Nijmegen =&lt;br /&gt;
&lt;br /&gt;
We&#039;re located at [https://www.openstreetmap.org/node/2826874262 Elzenstraat 4a], 6523EV Nijmegen. [tel:+31244200242 Call us] (+31244200242) when we&#039;re open and the gate or entrance is closed.&lt;br /&gt;
&lt;br /&gt;
Join us on [irc://irc.snt.utwente.nl:6669/hsnijmegen IRC] (IRCnet: #hsnijmegen), [https://twitter.com/Hackerspace024 twitter], or the mailinglist: [[File:listemail.png]]!&lt;br /&gt;
&lt;br /&gt;
Enjoyed your visit at our hackerspace and want more? Become a [[Contributing|contributor]]!&lt;br /&gt;
&lt;br /&gt;
== Open when? ==&lt;br /&gt;
&lt;br /&gt;
The space is open:&lt;br /&gt;
* every Tuesday. Ask on IRC regarding the opening time.&lt;br /&gt;
* every Monday for &amp;quot;synth and stompbox night&amp;quot;&lt;br /&gt;
* when our [http://www.hackerspacenijmegen.nl spacestate (bottom of the page)] says so!&lt;br /&gt;
&lt;br /&gt;
== Projects ==&lt;br /&gt;
&lt;br /&gt;
{{Special:PrefixIndex/Projects:}}&lt;br /&gt;
* [[HSNWiki:Migration_from_Gitwiki|Wiki migration status]]&lt;br /&gt;
&lt;br /&gt;
== Events (past and present) ==&lt;br /&gt;
{{Special:PrefixIndex/Event:}}&lt;br /&gt;
&lt;br /&gt;
== Contributing to the wiki ==&lt;br /&gt;
&lt;br /&gt;
* [[Speciaal:GebruikerAanmaken|Request an account]]&lt;br /&gt;
* One of the wiki admins processes your request, and usually approves it.&lt;br /&gt;
* Edit away!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;It&#039;s a wiki&#039;&#039;: improve things! Join us in the conversation in #hsnwiki on IRCnet (same network as #hsnijmegen) if you have questions!&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=316</id>
		<title>Hackerspace Nijmegen</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=316"/>
		<updated>2020-01-21T22:15:43Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: /* Hackerspace Nijmegen */ opening times&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
&lt;br /&gt;
= Hackerspace Nijmegen =&lt;br /&gt;
&lt;br /&gt;
We&#039;re located at [https://www.openstreetmap.org/node/2826874262 Elzenstraat 4a], 6523EV Nijmegen. [tel:+31244200242 Call us] (+31244200242) when we&#039;re open and the gate is closed.&lt;br /&gt;
&lt;br /&gt;
Join us on [irc://irc.snt.utwente.nl:6669/hsnijmegen IRC] (IRCnet: #hsnijmegen), [https://twitter.com/Hackerspace024 twitter], or the mailinglist: [[File:listemail.png]]!&lt;br /&gt;
&lt;br /&gt;
Enjoyed your visit at our hackerspace and want more? Become a [[Contributing|contributor]]!&lt;br /&gt;
&lt;br /&gt;
== Open when? ==&lt;br /&gt;
&lt;br /&gt;
The space is open:&lt;br /&gt;
* every Tuesday. Ask on IRC regarding the opening time.&lt;br /&gt;
* every Monday for &amp;quot;synth and stompbox night&amp;quot;&lt;br /&gt;
* when our [http://www.hackerspacenijmegen.nl spacestate (bottom of the page)] says so!&lt;br /&gt;
&lt;br /&gt;
== Projects ==&lt;br /&gt;
&lt;br /&gt;
{{Special:PrefixIndex/Projects:}}&lt;br /&gt;
* [[HSNWiki:Migration_from_Gitwiki|Wiki migration status]]&lt;br /&gt;
&lt;br /&gt;
== Events (past and present) ==&lt;br /&gt;
{{Special:PrefixIndex/Event:}}&lt;br /&gt;
&lt;br /&gt;
== Contributing to the wiki ==&lt;br /&gt;
&lt;br /&gt;
* [[Speciaal:GebruikerAanmaken|Request an account]]&lt;br /&gt;
* One of the wiki admins processes your request, and usually approves it.&lt;br /&gt;
* Edit away!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;It&#039;s a wiki&#039;&#039;: improve things! Join us in the conversation in #hsnwiki on IRCnet (same network as #hsnijmegen) if you have questions!&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=315</id>
		<title>Hackerspace Nijmegen</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=315"/>
		<updated>2020-01-21T22:08:07Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: /* Contributing to the wiki */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
&lt;br /&gt;
= Hackerspace Nijmegen =&lt;br /&gt;
&lt;br /&gt;
We&#039;re located at [https://www.openstreetmap.org/node/2826874262 Elzenstraat 4a], 6523EV Nijmegen. [tel:+31244200242 Call us] (+31244200242) when we&#039;re open and the gate is closed.&lt;br /&gt;
&lt;br /&gt;
Join us on [irc://irc.snt.utwente.nl:6669/hsnijmegen IRC] (IRCnet: #hsnijmegen), [https://twitter.com/Hackerspace024 twitter], or the mailinglist: [[File:listemail.png]]!&lt;br /&gt;
&lt;br /&gt;
Enjoyed your visit at our hackerspace and want more? Become a [[Contributing|contributor]]!&lt;br /&gt;
&lt;br /&gt;
== Projects ==&lt;br /&gt;
&lt;br /&gt;
{{Special:PrefixIndex/Projects:}}&lt;br /&gt;
* [[HSNWiki:Migration_from_Gitwiki|Wiki migration status]]&lt;br /&gt;
&lt;br /&gt;
== Events (past and present) ==&lt;br /&gt;
{{Special:PrefixIndex/Event:}}&lt;br /&gt;
&lt;br /&gt;
== Contributing to the wiki ==&lt;br /&gt;
&lt;br /&gt;
* [[Speciaal:GebruikerAanmaken|Request an account]]&lt;br /&gt;
* One of the wiki admins processes your request, and usually approves it.&lt;br /&gt;
* Edit away!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;It&#039;s a wiki&#039;&#039;: improve things! Join us in the conversation in #hsnwiki on IRCnet (same network as #hsnijmegen) if you have questions!&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=314</id>
		<title>Hackerspace Nijmegen</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=314"/>
		<updated>2020-01-21T22:07:50Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: /* Contributing to the wiki */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
&lt;br /&gt;
= Hackerspace Nijmegen =&lt;br /&gt;
&lt;br /&gt;
We&#039;re located at [https://www.openstreetmap.org/node/2826874262 Elzenstraat 4a], 6523EV Nijmegen. [tel:+31244200242 Call us] (+31244200242) when we&#039;re open and the gate is closed.&lt;br /&gt;
&lt;br /&gt;
Join us on [irc://irc.snt.utwente.nl:6669/hsnijmegen IRC] (IRCnet: #hsnijmegen), [https://twitter.com/Hackerspace024 twitter], or the mailinglist: [[File:listemail.png]]!&lt;br /&gt;
&lt;br /&gt;
Enjoyed your visit at our hackerspace and want more? Become a [[Contributing|contributor]]!&lt;br /&gt;
&lt;br /&gt;
== Projects ==&lt;br /&gt;
&lt;br /&gt;
{{Special:PrefixIndex/Projects:}}&lt;br /&gt;
* [[HSNWiki:Migration_from_Gitwiki|Wiki migration status]]&lt;br /&gt;
&lt;br /&gt;
== Events (past and present) ==&lt;br /&gt;
{{Special:PrefixIndex/Event:}}&lt;br /&gt;
&lt;br /&gt;
== Contributing to the wiki ==&lt;br /&gt;
&lt;br /&gt;
* [[Speciaal:GebruikerAanmaken|Request an account]]&lt;br /&gt;
* One of the wiki admins processes your request, and usually approves it.&lt;br /&gt;
* Edit away!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;It&#039;s a wiki&#039;&#039;&#039;: improve things! Join us in the conversation in #hsnwiki on IRCnet (same network as #hsnijmegen) if you have questions!&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=313</id>
		<title>Hackerspace Nijmegen</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Hackerspace_Nijmegen&amp;diff=313"/>
		<updated>2020-01-21T22:03:46Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: Rework!&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
&lt;br /&gt;
= Hackerspace Nijmegen =&lt;br /&gt;
&lt;br /&gt;
We&#039;re located at [https://www.openstreetmap.org/node/2826874262 Elzenstraat 4a], 6523EV Nijmegen. [tel:+31244200242 Call us] (+31244200242) when we&#039;re open and the gate is closed.&lt;br /&gt;
&lt;br /&gt;
Join us on [irc://irc.snt.utwente.nl:6669/hsnijmegen IRC] (IRCnet: #hsnijmegen), [https://twitter.com/Hackerspace024 twitter], or the mailinglist: [[File:listemail.png]]!&lt;br /&gt;
&lt;br /&gt;
Enjoyed your visit at our hackerspace and want more? Become a [[Contributing|contributor]]!&lt;br /&gt;
&lt;br /&gt;
== Projects ==&lt;br /&gt;
&lt;br /&gt;
{{Special:PrefixIndex/Projects:}}&lt;br /&gt;
* [[HSNWiki:Migration_from_Gitwiki|Wiki migration status]]&lt;br /&gt;
&lt;br /&gt;
== Events (past and present) ==&lt;br /&gt;
{{Special:PrefixIndex/Event:}}&lt;br /&gt;
&lt;br /&gt;
== Contributing to the wiki ==&lt;br /&gt;
&lt;br /&gt;
* [[Speciaal:GebruikerAanmaken|Request an account]]&lt;br /&gt;
* One of the wiki admins &lt;br /&gt;
* Edit away!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;It&#039;s a wiki&#039;&#039;&#039;: improve things! Join us in the conversation in #hsnwiki on IRCnet (same network as #hsnijmegen) if you have questions!&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Projects:Music_at_HSN&amp;diff=312</id>
		<title>Projects:Music at HSN</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Projects:Music_at_HSN&amp;diff=312"/>
		<updated>2020-01-21T21:30:05Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: +categorie:projects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We have a Music Player (MPD) at Hackerspace Nijmegen. If you&#039;d like some music,&lt;br /&gt;
you can turn it on.&lt;br /&gt;
&lt;br /&gt;
To use the commands listed on this page, you need to be connected to the HS-NMGN&lt;br /&gt;
wifi network or be otherwise connected to the HSN network.&lt;br /&gt;
&lt;br /&gt;
Questions? Ask Sjors!&lt;br /&gt;
&lt;br /&gt;
== Starting it ==&lt;br /&gt;
&lt;br /&gt;
Find out MPD&#039;s status by running `mpc -h barometer status`. The output may look&lt;br /&gt;
something like:&lt;br /&gt;
&lt;br /&gt;
 Maduk - Liquicity Yearmix 2016 (Mixed By Maduk)&lt;br /&gt;
 [playing] #1/1   2:37/64:15 (4%)&lt;br /&gt;
 volume: 77%   repeat: off   random: off   single: off   consume: on &lt;br /&gt;
&lt;br /&gt;
Play state is on the second line. If it says &#039;playing&#039;, and you don&#039;t hear&lt;br /&gt;
anything, check audio settings on `barometer@bar-o-meter` :-(&lt;br /&gt;
&lt;br /&gt;
If it says &#039;paused&#039; or &#039;stopped&#039;, run `mpc -h barometer play`. If that doesn&#039;t&lt;br /&gt;
help, restart it.&lt;br /&gt;
&lt;br /&gt;
If it&#039;s not running, or broken in some way, you can `ssh barometer@bar-o-meter`&lt;br /&gt;
and run `./startmpd.sh`. That&#039;ll stop it if it&#039;s running, then restart it. It&lt;br /&gt;
starts mpd, but also ashuffle, a tool to keep the playlist always filled.&lt;br /&gt;
&lt;br /&gt;
== Stopping it ==&lt;br /&gt;
&lt;br /&gt;
Done with the music? Last person to leave the space? `mpc -h barometer pause`.&lt;br /&gt;
&lt;br /&gt;
== Adding your own music ==&lt;br /&gt;
&lt;br /&gt;
`barometer` is running an NFS mount. Try:&lt;br /&gt;
&lt;br /&gt;
 sudo mkdir -p /media/music&lt;br /&gt;
 sudo mount -t nfs barometer:/media/music /media/music&lt;br /&gt;
&lt;br /&gt;
Make your own directory on it, and add your stuff! Please don&#039;t touch other&lt;br /&gt;
people&#039;s stuff, and don&#039;t download any music from it, get it somewhere legally!&lt;br /&gt;
&lt;br /&gt;
Currently there&#039;s only about 128 GB of space, so don&#039;t upload your entire music&lt;br /&gt;
database; if it fills up further, I&#039;ll probably add a disk or something.&lt;br /&gt;
&lt;br /&gt;
== Queueing your own music ==&lt;br /&gt;
&lt;br /&gt;
Take the path to your file, then run:&lt;br /&gt;
&lt;br /&gt;
 mpc -h barometer add &#039;yourname/Path To Your Music/Some Artist - His Track (Someones Remix).mp3&#039;&lt;br /&gt;
&lt;br /&gt;
== TODO ==&lt;br /&gt;
&lt;br /&gt;
Not in any particular order:&lt;br /&gt;
&lt;br /&gt;
* It sometimes doesn&#039;t start with the correct audio interface. Why?&lt;br /&gt;
* Nicer speakers&lt;br /&gt;
* Automatic `mpc pause` when the space state is switched to closed, and *perhaps* &amp;quot;mpc play&amp;quot; when switched to open&lt;br /&gt;
* Revbank plugin for mpd!&lt;br /&gt;
* A (matrix LED?) screen that displays what song is currently playing, perhaps some nice animations&lt;br /&gt;
* A Something like https://github.com/notandy/ympd on a screen for people who don&#039;t care for CLIs&lt;br /&gt;
* A fair queueing system&lt;br /&gt;
* Force consume mode on (perhaps by patching mpd? Or just a cronjob every minute)&lt;br /&gt;
&lt;br /&gt;
[[Categorie:Projects]]&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Projects:Kubernetes&amp;diff=311</id>
		<title>Projects:Kubernetes</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Projects:Kubernetes&amp;diff=311"/>
		<updated>2020-01-21T21:29:48Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: +categorie:projects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;According to Wikipedia, [[Wikipedia:Kubernetes|Kubernetes]] (commonly stylized as k8s) is an open-source container orchestration system for automating application deployment, scaling, and management. I&#039;ve heard a lot about it, and it seems to solve some problems I&#039;m encountering sometimes, so I&#039;d like to get to know it better. While doing so, I wanted to make a write-up of what I found out.&lt;br /&gt;
&lt;br /&gt;
This page is not intended as a tutorial, but it will link to various tutorials I found useful. In this way, I hope that it can be used as a reference for someone setting up Kubernetes to be able to learn about it more quickly.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Disclaimer: I&#039;m a beginner in the Kubernetes area, so this tutorial will contain errors. Please correct errors where you find them, and if possible, write some explanation! The &amp;quot;I&amp;quot; in this article is Sjors, but parts are possibly written by other people.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Problem =&lt;br /&gt;
&lt;br /&gt;
We start with a Linux desktop, running some version of Arch Linux. Someone wants to run an application on it for which only binaries are available, but those binaries were compiled on Ubuntu. Now, system libraries are different between Arch Linux and Ubuntu, and while it&#039;s possible to create binaries that run independent of system libraries (called &amp;quot;static binaries&amp;quot;), in this particular situation let&#039;s assume the binaries aren&#039;t static, but you still want to run them on your Arch installation.&lt;br /&gt;
&lt;br /&gt;
You can get a second machine and run Ubuntu on it, or similarly you could use a Virtual Machine. But, there&#039;s a simpler and more efficient solution: Docker allows you to install, within your Linux distribution (the &amp;quot;host&amp;quot;), another Linux distribution (let&#039;s call it &amp;quot;guest&amp;quot; for now). The host and the guest may be completely different – technically, only the kernel of the host is used, and of course the host and guest must have compatible processor architectures. You run the guest environment within Docker (a &amp;quot;Docker Container&amp;quot;) and run that binary in it.&lt;br /&gt;
&lt;br /&gt;
This is how easy that is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sjors@somebox:~$ lsb_release -a&lt;br /&gt;
LSB Version: 1.4&lt;br /&gt;
Distributor ID: Arch&lt;br /&gt;
Description: Arch Linux&lt;br /&gt;
Release: rolling&lt;br /&gt;
Codename: n/a&lt;br /&gt;
sjors@somebox:~$ docker run -ti ubuntu:bionic bash&lt;br /&gt;
root@a5b210d251c2:/# apt-get update &amp;amp;&amp;amp; apt-get -y install lsb-release&lt;br /&gt;
[....]&lt;br /&gt;
root@a5b210d251c2:/# lsb_release -a&lt;br /&gt;
No LSB modules are available.&lt;br /&gt;
Distributor ID:	Ubuntu&lt;br /&gt;
Description:	Ubuntu 18.04.1 LTS&lt;br /&gt;
Release:	18.04&lt;br /&gt;
Codename:	bionic&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;-it&amp;lt;/code&amp;gt; flag makes this an interactive container that can run &amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;; just as easily, you can run detached containers running webservers, and use the integrated port-forwarding features to make them accessible to the outside.&lt;br /&gt;
&lt;br /&gt;
Now, Docker has some problems of its own:&lt;br /&gt;
&lt;br /&gt;
* You start Docker containers by accessing the Docker daemon; the daemon runs containers as root and allows you to start a container with a bind-mount of &amp;quot;/&amp;quot;. Basically, having access to the Docker daemon means you have root on the system, but you need access to do anything. It&#039;s all-or-nothing.&lt;br /&gt;
* When your Docker machine goes down, all containers are gone. You&#039;ll have to either restart all containers manually, or have boot-scripts that set them up, but there&#039;s no automatic restart mechanism.&lt;br /&gt;
* When you want to run more Docker containers than fit on one machine, there&#039;s no horizontal scaling mechanism built-in.&lt;br /&gt;
* When you want to run a service multiple times, e.g. for redundancy, you need to schedule them manually multiple times and roll your own method of load-balancing them.&lt;br /&gt;
&lt;br /&gt;
Kubernetes provides a solution for these problems, while otherwise looking very much like Docker. In fact, when you&#039;re familiar with Docker some of the commands below will also be very familiar to you.&lt;br /&gt;
&lt;br /&gt;
= Concepts =&lt;br /&gt;
&lt;br /&gt;
In this section, I&#039;ll explain some of Kubernetes&#039; concepts quickly, and add links if you want to know more.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Container&#039;&#039;&#039;: Like with Docker, this is one &#039;guest environment&#039; in which you can run anything. Usually, Kubernetes containers are, in fact, Docker containers.&lt;br /&gt;
* &#039;&#039;&#039;[https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/ Pod]&#039;&#039;&#039;: The basic unit you actually schedule in Kubernetes. Usually, a Pod contains one Container, but a Pod can consist of multiple Containers which can be a very useful feature. More on that later. A Pod isn&#039;t durable: when anything causes a Pod to stop (e.g. the machine it runs on has a power outage), it won&#039;t be restarted automatically.&lt;br /&gt;
* &#039;&#039;&#039;[https://kubernetes.io/docs/concepts/workloads/controllers/deployment/ Deployment]&#039;&#039;&#039;: An indication of &amp;quot;desired state&amp;quot; of the cluster in terms of the pods you always expect to have. The Kubernetes system will always try to match these Deployments to what it&#039;s actually running. Basically, a Deployment is the way to start a Pod and ensure it stays running.&lt;br /&gt;
* &#039;&#039;&#039;[https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/ Job]&#039;&#039;&#039;: An indication that you want to run some command to completion. When the cluster has a Job, it will keep re-creating its Pods until a given number of them succeed successfully. Basically, a Job is the way to start a Pod and ensure it finishes once.&lt;br /&gt;
* &#039;&#039;&#039;[https://kubernetes.io/docs/concepts/storage/volumes/ Volume]&#039;&#039;&#039;: As in Docker, changes to containers are temporary and will be gone when the container stops. If you want to keep those changes after a restart, like in Docker, you make a Volume. They are also useful to share data between containers. In Kubernetes, Volumes are kept over restarts of Containers, but not over restarts of Pods, unless they are Persistent. More on that later.&lt;br /&gt;
* &#039;&#039;&#039;[https://kubernetes.io/docs/concepts/services-networking/service/ Service]&#039;&#039;&#039;: When your Pod contains some application, such as a webserver, you can make its TCP port available as a Service so that people (inside or outside the cluster) can connect to it. For an application you want to run redundantly, multiple Pods can be started; you&#039;ll configure them to share the same Service. This way, when you connect to the Service, you&#039;ll get one of the running Pods behind it. Instant redundancy!&lt;br /&gt;
* &#039;&#039;&#039;[https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ Namespace]&#039;&#039;&#039;: Kubernetes supports running multiple &amp;quot;virtual clusters&amp;quot; on the infrastructure of one &amp;quot;physical cluster&amp;quot;. Those virtual clusters are called &amp;quot;namespaces&amp;quot;, and you can restrict access to certain namespaces. Normally, you&#039;re only working with the &amp;quot;default&amp;quot; namespace.&lt;br /&gt;
&lt;br /&gt;
Those are some concepts that allow you to use a Kubernetes cluster. In this guide, we&#039;ll also be setting up the infrastructure behind that:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Node&#039;&#039;&#039;: A machine that actually runs the Containers. Can be bare-metal or a virtual machine, or even an embedded IoT device. A Node runs a process called &amp;quot;Kubelet&amp;quot; which interacts with the Docker daemon (usually) to set everything up, but normally you never communicate directly with it.&lt;br /&gt;
* &#039;&#039;&#039;Control Plane&#039;&#039;&#039;: A set of some applications (API server, etcd, controller manager, scheduler...) that make sure the cluster is &amp;quot;healthy&amp;quot;. For example, it starts Pods when you request it to, but also when a Node goes down that was running Pods for some Deployment, it restarts those Pods elsewhere. These master applications themselves also run in Pods, in a separate namespace called &amp;quot;kube-system&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;Master node&#039;&#039;&#039;: Otherwise a normal Node, but it runs the Control Plane applications. By default, a Master node will only run Pods for these applications, but you can configure it to allow normal Pods too. There can be multiple Master nodes, for redundancy of the cluster.&lt;br /&gt;
&lt;br /&gt;
= Understanding networking =&lt;br /&gt;
&lt;br /&gt;
Networking within a Kubernetes cluster isn&#039;t difficult, but requires some specific explanation. Applications within Kubernetes containers need to be able to access each other, regardless of whether they run on the same Node or not. To make it worse, Nodes may be running behind firewalls or may be running in different subnets. Luckily, Kubernetes has some mechanisms that make it very robust against this.&lt;br /&gt;
&lt;br /&gt;
When setting up a Kubernetes cluster, there are two important internal IP ranges throughout the cluster:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;The Pod network range&#039;&#039;&#039;. This internal range is automatically split over Nodes, and Pods get individual addresses from it.&lt;br /&gt;
** For example, you can set this to 10.123.0.0/16; the master node will likely get 10.123.0.0/24 and the second Node you add after that gets 10.123.1.0/24 and so on. A Pod running on this second node may have 10.123.1.55 as an IP address. (If the Pod has multiple containers, all of them will have the same IP address.)&lt;br /&gt;
* &#039;&#039;&#039;The service network range&#039;&#039;&#039;. When you register a Service, such as &amp;quot;my-fun-webserver&amp;quot;, it automatically gets an IP address within this range. An application called the &#039;kube-proxy&#039;, running automatically on every Node, then takes care that any communication with this IP address is forwarded to one of the actual Pods providing that service (by configuring iptables). Fun fact: the Kubernetes API server registers itself as a service and is always available at the first host address of the range you chose.&lt;br /&gt;
** For example, your service network range may be 10.96.0.0/16; the Kubernetes API service makes itself available at 10.96.0.1. When you communicate with this IP address, the communication is automatically translated (by iptables) to be sent to the Pod IP address of the Kubernetes API service, e.g. 10.123.1.55.&lt;br /&gt;
&lt;br /&gt;
It&#039;s important that these ranges don&#039;t overlap, and they also both shouldn&#039;t overlap with any relevant IP ranges within your existing network! The Kubernetes folks suggest you use something within 10.0.0.0/8 if your local network range is within 192.168.0.0/16 and vice-versa.&lt;br /&gt;
&lt;br /&gt;
Since there is no one-size-fits-all solution to networking between Nodes, Kubernetes allows that to be done by specific networking plugins, called CNI. There are a number of such plugins, but a friend of mine experienced with running Kubernetes clusters explained why you should probably go for Weave:&lt;br /&gt;
&lt;br /&gt;
* It uses the vxlan standard. vxlan allows tunneling traffic between nodes automatically. When a packet should travel from one Node to another, a small UDP/IP header is prepended to it, so it is sent to the right node, where the header is taken off and routing continues.&lt;br /&gt;
* This method allows it to cross almost all difficult network setups as long as you can have one UDP port forwarded between the machines.&lt;br /&gt;
* Weave is smart enough to figure out the most efficient way to use vxlan given your Linux kernel version.&lt;br /&gt;
* It&#039;s also pretty simple: just a single Go binary.&lt;br /&gt;
&lt;br /&gt;
Kubernetes takes care that the pod network range and service network range is not only usable within pods, but also on the nodes. So, using the example values above, `https://10.96.0.1/` will reach the Kubernetes API server within pods and on nodes, also highly-available if you have multiple masters, which is pretty convenient.&lt;br /&gt;
&lt;br /&gt;
Some more important features of Kubernetes networking:&lt;br /&gt;
* A Kubernetes cluster automatically runs a &amp;quot;CoreDNS&amp;quot; pod, which provides DNS to all other pods. It forwards requests outside the cluster to an upstream DNS server, but most importantly, provides an internal `cluster.local` DNS zone that you can use to look up other pods or services. For example, `kubernetes.default.svc.cluster.local` resolves to 10.96.0.1, as above. (In that hostname, &#039;kubernetes&#039; is the service name, &#039;default&#039; is the namespace.)&lt;br /&gt;
* When a pod is listening on some TCP port, you don&#039;t need to use Services to reach them externally: &amp;lt;code&amp;gt;kubectl port-forward pod/foobarbaz 8080:80&amp;lt;/code&amp;gt; forwards local port 8080 to port 80 of a pod called &#039;foobarbaz&#039;, and for this to work your &amp;lt;code&amp;gt;kubectl&amp;lt;/code&amp;gt; can run on any machine with credentials to access the API server, it doesn&#039;t need to be part of the cluster.&lt;br /&gt;
&lt;br /&gt;
= Setting it all up =&lt;br /&gt;
&lt;br /&gt;
With that all behind us, let&#039;s start setting up our first cluster!&lt;br /&gt;
&lt;br /&gt;
For a useful cluster, you&#039;ll need at least one machine to be the master, but of course we&#039;ll use at least two so we can call it an actual cluster. The master node is strongly recommended to have at least two CPUs and 2 GB of RAM. The other nodes can be a bit smaller.&lt;br /&gt;
&lt;br /&gt;
For my tests, I&#039;ve used four machines:&lt;br /&gt;
&lt;br /&gt;
* Kubetest1 - 1 CPU, 1 GB RAM - 145.131.6.177 - Ubuntu 16.04&lt;br /&gt;
* Kubetest2 - 1 CPU, 1 GB RAM - 145.131.8.75  - Ubuntu 16.04&lt;br /&gt;
* Kubetest3 - 1 CPU, 1 GB RAM - 145.131.5.151 - Ubuntu 16.04&lt;br /&gt;
* Kubetest4 - 2 CPU, 2 GB RAM - 145.131.6.179 - Ubuntu 16.04&lt;br /&gt;
&lt;br /&gt;
(I initially wanted a three-machine cluster, and forced the master on a 1 CPU/1 GB RAM machine, but processes were being killed because of going out-of-memory. So, kubetest4 became a bit bigger and will be my master.)&lt;br /&gt;
&lt;br /&gt;
Now, the machines in your cluster should be able to access each other on all ports used by Kubernetes (see [https://kubernetes.io/docs/setup/independent/install-kubeadm/#check-required-ports this list here]). &#039;&#039;&#039;Important note&#039;&#039;&#039;: the machines should also be able to access themselves on their external IP address! I was initially having problems because of this, where pods on a worker node could reach the master normally, but pods on the master node couldn&#039;t. It was because the external IP address of the master were used in some communications, which was ironically impossible just on the master itself.&lt;br /&gt;
&lt;br /&gt;
So, once you have your machines up, and you know they can reach each other, we&#039;ll start installation.&lt;br /&gt;
&lt;br /&gt;
* First, we choose our Pod network range and Service range. See previous section. I used 10.107.0.0/16 for the network range and 10.16.0.0/16 for the service range.&lt;br /&gt;
* Optionally, we disable swap on the machine, as Kubernetes prefers to run without it. To do this, check &amp;lt;code&amp;gt;/proc/swaps&amp;lt;/code&amp;gt; for your swap locations and run &amp;lt;code&amp;gt;swapoff&amp;lt;/code&amp;gt; on them, then remove the entries from &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt;. Or, leave them enabled and later disable the check in Kubernetes.&lt;br /&gt;
* We install the Docker daemon. On Ubuntu, &amp;lt;code&amp;gt;sudo apt-get install docker.io&amp;lt;/code&amp;gt; does it.&lt;br /&gt;
* We install the basic Kubernetes tools: &amp;lt;code&amp;gt;kubeadm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kubelet&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;kubecfg&amp;lt;/code&amp;gt;. [https://kubernetes.io/docs/setup/independent/install-kubeadm/ Here&#039;s a nice guide for it].&lt;br /&gt;
* We use &amp;lt;code&amp;gt;kubeadm&amp;lt;/code&amp;gt; to create our cluster. [https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/ Just follow this guide], until the &amp;quot;Installing a pod network add-on&amp;quot; step.&lt;br /&gt;
** I used: &amp;lt;code&amp;gt;kubeadm init --pod-network-cidr &amp;quot;10.107.0.0/16&amp;quot; --service-cidr &amp;quot;10.16.0.0/16&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
** After this, &amp;lt;code&amp;gt;kubectl get nodes&amp;lt;/code&amp;gt; should already respond with your master node, and &amp;lt;code&amp;gt;kubectl get pods --all-namespaces&amp;lt;/code&amp;gt; should mention the &amp;lt;code&amp;gt;kube-system&amp;lt;/code&amp;gt; pods that make up the Control Plane as discussed above!&lt;br /&gt;
* We install a pod network add-on. As discussed before, we use Weave, but YMMV. See [https://www.weave.works/docs/net/latest/kubernetes/kube-addon/ this guide on Weave setup], with an important note: if you don&#039;t use Kubernetes&#039; default Pod network CIDR, pay attention to the exact &amp;lt;code&amp;gt;kubectl apply&amp;lt;/code&amp;gt; step.&lt;br /&gt;
** I used: &amp;lt;code&amp;gt;kubectl apply -f &amp;quot;https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d &#039;\n&#039;)&amp;amp;env.IPALLOC_RANGE=10.107.0.0/16&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
** After this, &amp;lt;code&amp;gt;kubectl get pods --all-namespaces&amp;lt;/code&amp;gt; should show some additional pods. Most importantly, it should now show that CoreDNS is running.&lt;br /&gt;
* Now, we can join our other nodes. &amp;lt;code&amp;gt;kubeadm init&amp;lt;/code&amp;gt; will show a &amp;lt;code&amp;gt;kubeadm join&amp;lt;/code&amp;gt; command at the end of its output, which you can run on the other nodes. If you don&#039;t have that output anymore, see [https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-join/#token-based-discovery-with-ca-pinning this page on how to recreate a valid join command].&lt;br /&gt;
** I also copied the &amp;lt;code&amp;gt;/etc/kubernetes/admin.conf&amp;lt;/code&amp;gt; from the master to the other nodes (root-readable only), and set &amp;lt;code&amp;gt;KUBECONFIG=/etc/kubernetes/admin.conf&amp;lt;/code&amp;gt;, so I could run &amp;lt;code&amp;gt;kubectl&amp;lt;/code&amp;gt; on them. But that&#039;s up to you!&lt;br /&gt;
&lt;br /&gt;
Hopefully, after understanding all the concepts discussed earlier, this process was a matter of mere minutes!&lt;br /&gt;
&lt;br /&gt;
Your node list should be complete now:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
root@kubetest4:~# kubectl get nodes&lt;br /&gt;
NAME        STATUS   ROLES    AGE    VERSION&lt;br /&gt;
kubetest1   Ready    &amp;lt;none&amp;gt;   2d2h   v1.13.4&lt;br /&gt;
kubetest2   Ready    &amp;lt;none&amp;gt;   2d2h   v1.13.4&lt;br /&gt;
kubetest3   Ready    &amp;lt;none&amp;gt;   2d5h   v1.13.4&lt;br /&gt;
kubetest4   Ready    master   2d5h   v1.13.4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
By the way, you can run &amp;lt;code&amp;gt;kubectl&amp;lt;/code&amp;gt; commands from your own machine as well by copying &amp;lt;code&amp;gt;/etc/kubernetes/admin.conf&amp;lt;/code&amp;gt; from the master to your &amp;lt;code&amp;gt;$HOME/.kube/config&amp;lt;/code&amp;gt;, and possibly after some firewall config to allow communications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TO DO: It should be possible to generate user credentials instead of taking admin credentials. But how?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Creating some basic pods =&lt;br /&gt;
&lt;br /&gt;
You&#039;ll see your cluster has no pods in the default namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl get pods&lt;br /&gt;
No resources found.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The simplest thing we can do, now that we have a cluster, is run a random image (which is a Docker image, in this case &amp;lt;code&amp;gt;ubuntu:bionic&amp;lt;/code&amp;gt;, pulled from Docker Hub) with a random command, like before:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl run --restart=Never -ti --image=ubuntu:bionic bash&lt;br /&gt;
If you don&#039;t see a command prompt, try pressing enter.&lt;br /&gt;
root@bash:/# apt-get update &amp;amp;&amp;amp; apt-get -y install lsb-release&lt;br /&gt;
[....]&lt;br /&gt;
root@bash:/# lsb_release -a&lt;br /&gt;
No LSB modules are available.&lt;br /&gt;
Distributor ID:	Ubuntu&lt;br /&gt;
Description:	Ubuntu 18.04.1 LTS&lt;br /&gt;
Release:	18.04&lt;br /&gt;
Codename:	bionic&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indeed, your pod will be shown now:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl get pods&lt;br /&gt;
NAME   READY   STATUS    RESTARTS   AGE&lt;br /&gt;
bash   1/1     Running   0          56s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the command above, we gave &amp;lt;code&amp;gt;--restart=Never&amp;lt;/code&amp;gt;. There are three parameters to this option:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Never&amp;lt;/code&amp;gt;: When the pod exits, it will not be recreated. If this is given, &amp;lt;code&amp;gt;kubectl&amp;lt;/code&amp;gt; will just create a Pod.&lt;br /&gt;
* &amp;lt;code&amp;gt;OnFailure&amp;lt;/code&amp;gt;: When the pod exits with failure, it will be recreated, otherwise not. In other words, &amp;lt;code&amp;gt;kubectl&amp;lt;/code&amp;gt; will start a Job to create the Pod. (If that doesn&#039;t make sense, quickly check up on Jobs in the Concepts section above!)&lt;br /&gt;
* &amp;lt;code&amp;gt;Always&amp;lt;/code&amp;gt;: When the pod exits, it will be recreated. You guessed it: in this case, &amp;lt;code&amp;gt;kubectl&amp;lt;/code&amp;gt; will create a Deployment. (If you didn&#039;t guess it, re-check the Concepts section!)&lt;br /&gt;
&lt;br /&gt;
The default is &amp;lt;code&amp;gt;--restart=Always&amp;lt;/code&amp;gt; so you&#039;ll see the container is recreated like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl run -ti --image=ubuntu:bionic bash &lt;br /&gt;
If you don&#039;t see a command prompt, try pressing enter.&lt;br /&gt;
root@bash-58654c7f4b-9bhcq:/# touch foobarbaz&lt;br /&gt;
root@bash-58654c7f4b-9bhcq:/# exit&lt;br /&gt;
Session ended, resume using &#039;kubectl attach bash-58654c7f4b-9bhcq -c bash -i -t&#039; command when the pod is running&lt;br /&gt;
&lt;br /&gt;
[...wait for a bit until the pod comes back up...]&lt;br /&gt;
&lt;br /&gt;
$ kubectl attach bash-58654c7f4b-9bhcq -c bash -i -t&lt;br /&gt;
If you don&#039;t see a command prompt, try pressing enter.&lt;br /&gt;
root@bash-58654c7f4b-9bhcq:/# ls foobarbaz&lt;br /&gt;
ls: cannot access &#039;foobarbaz&#039;: No such file or directory&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, the container restarted, as &amp;lt;code&amp;gt;/foobarbaz&amp;lt;/code&amp;gt; did not exist anymore when re-attaching after the &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;. Any state in the filesystem of the container/pod will be gone upon restart.&lt;br /&gt;
&lt;br /&gt;
If you tried this, you can check and remove the deployment like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl get deployments      &lt;br /&gt;
NAME   READY   UP-TO-DATE   AVAILABLE   AGE&lt;br /&gt;
bash   1/1     1            1           3s&lt;br /&gt;
$ kubectl delete deployment bash&lt;br /&gt;
deployment.extensions &amp;quot;bash&amp;quot; deleted&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Storage using Volumes ==&lt;br /&gt;
&lt;br /&gt;
So let&#039;s try adding a Volume to our pod, to see if we can make some changes persistent. Kubernetes supports [https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes many types of volumes]; in this case we use &amp;lt;code&amp;gt;[https://kubernetes.io/docs/concepts/storage/volumes/#emptydir emptyDir]&amp;lt;/code&amp;gt; which is just a locally stored disk (initially empty).&lt;br /&gt;
&lt;br /&gt;
There is no command-line parameter to &amp;lt;code&amp;gt;kubectl run&amp;lt;/code&amp;gt; to add volumes. Internally, &amp;lt;code&amp;gt;kubectl run&amp;lt;/code&amp;gt; translates your commandline to a JSON request to the Kubernetes API server; we&#039;d have to add any additional requests directly into the JSON. This can be done with the &amp;lt;code&amp;gt;--overrides&amp;lt;/code&amp;gt; flag, but at this point, it is probably easier to switch to sending those commands ourselves. We can use JSON for this too, but many users use YAML for this, so we will too.&lt;br /&gt;
&lt;br /&gt;
The command above, &amp;lt;code&amp;gt;kubectl run --restart=Never -ti --image=ubuntu:bionic bash&amp;lt;/code&amp;gt;, translates to the following YAML:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: Pod&lt;br /&gt;
metadata:&lt;br /&gt;
  name: bash&lt;br /&gt;
spec:&lt;br /&gt;
  containers:&lt;br /&gt;
  - name: bash&lt;br /&gt;
    image: ubuntu:bionic&lt;br /&gt;
    stdin: true&lt;br /&gt;
    stdinOnce: true&lt;br /&gt;
    tty: true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, this is a Pod named &amp;quot;bash&amp;quot; and with one container, also called &amp;quot;bash&amp;quot;, running &amp;lt;code&amp;gt;ubuntu:bionic&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To create this Pod and attach to it, we write the code above to &amp;lt;code&amp;gt;bash.yaml&amp;lt;/code&amp;gt; and run these commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl create -f bash.yaml&lt;br /&gt;
pod/bash created&lt;br /&gt;
$ kubectl attach -ti bash -c bash&lt;br /&gt;
If you don&#039;t see a command prompt, try pressing enter.&lt;br /&gt;
root@bash:/# exit 0&lt;br /&gt;
exit&lt;br /&gt;
$ kubectl delete pod bash&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, we will recreate the pod with an &amp;lt;code&amp;gt;emptyDir&amp;lt;/code&amp;gt; volume mounted at &amp;lt;code&amp;gt;/foo&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat bash.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: Pod&lt;br /&gt;
metadata:&lt;br /&gt;
  name: bash&lt;br /&gt;
spec:&lt;br /&gt;
  volumes:&lt;br /&gt;
  - name: testing-volume&lt;br /&gt;
    emptyDir: {}&lt;br /&gt;
  containers:&lt;br /&gt;
  - name: bash&lt;br /&gt;
    image: ubuntu:bionic&lt;br /&gt;
    stdin: true&lt;br /&gt;
    stdinOnce: true&lt;br /&gt;
    tty: true&lt;br /&gt;
    volumeMounts:&lt;br /&gt;
    - mountPath: /foo&lt;br /&gt;
      name: testing-volume&lt;br /&gt;
$ kubectl create -f bash.yaml&lt;br /&gt;
pod/bash created&lt;br /&gt;
$ kubectl attach -ti bash -c bash&lt;br /&gt;
If you don&#039;t see a command prompt, try pressing enter.&lt;br /&gt;
root@bash:/# mount | grep foo&lt;br /&gt;
/dev/mapper/ubuntu--vg-root on /foo type ext4 (rw,relatime,errors=remount-ro,data=ordered)&lt;br /&gt;
root@bash:/# exit 0&lt;br /&gt;
exit&lt;br /&gt;
$ kubectl delete pod bash&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Of course, this volume isn&#039;t really persistent just yet; restarts of the pod will cause it to be recreated (the Volume has the &amp;quot;lifetime of the Pod&amp;quot;) so it actually doesn&#039;t serve our purpose.&lt;br /&gt;
&lt;br /&gt;
There&#039;s two ways to get around this:&lt;br /&gt;
&lt;br /&gt;
* Instead of using an emptyDir volume, use a volume type that stores its contents somewhere&lt;br /&gt;
* Or, alternatively, make a PersistentVolume that exists outside our Pod, and then mount it.&lt;br /&gt;
&lt;br /&gt;
== A volume type with persistency ==&lt;br /&gt;
&lt;br /&gt;
As seen before, Kubernetes supports [https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes many volume types], some of which are naturally persistent because they store the data on an external service.&lt;br /&gt;
&lt;br /&gt;
The quickest way to set up persistent storage is to set up a NFS server. (Remember that in production, you&#039;ll want to go for something redundant, such as Ceph or Gluster or clustered NFS.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
root@kubetest4:~# apt-get install -y nfs-kernel-server&lt;br /&gt;
[...]&lt;br /&gt;
root@kubetest4:~# mkdir /persistent&lt;br /&gt;
root@kubetest4:~# chmod 0777 /persistent&lt;br /&gt;
root@kubetest4:~# cat &amp;lt;&amp;lt;EOF &amp;gt;&amp;gt;/etc/exports&lt;br /&gt;
/persistent *(rw,sync,no_subtree_check)&lt;br /&gt;
EOF&lt;br /&gt;
root@kubetest4:~# exportfs -ra&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure the other nodes can access it, by checking that &amp;lt;code&amp;gt;mkdir /persistent &amp;amp;&amp;amp; mount -t nfs &amp;lt;ipaddress&amp;gt;:/persistent /persistent&amp;lt;/code&amp;gt; works. Also, create a file &amp;lt;code&amp;gt;/persistent/helloworld.txt&amp;lt;/code&amp;gt; with some contents.&lt;br /&gt;
&lt;br /&gt;
Now we can go ahead and create our pod with this volume (replacing &amp;lt;ipaddress&amp;gt; with the address of your NFS host again):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat bash.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: Pod&lt;br /&gt;
metadata:&lt;br /&gt;
  name: bash&lt;br /&gt;
spec:&lt;br /&gt;
  volumes:&lt;br /&gt;
  - name: testing-volume&lt;br /&gt;
    nfs:&lt;br /&gt;
      path: /persistent&lt;br /&gt;
      server: &amp;lt;ipaddress&amp;gt;&lt;br /&gt;
  containers:&lt;br /&gt;
  - name: bash&lt;br /&gt;
    image: ubuntu:bionic&lt;br /&gt;
    stdin: true&lt;br /&gt;
    stdinOnce: true&lt;br /&gt;
    tty: true&lt;br /&gt;
    volumeMounts:&lt;br /&gt;
    - mountPath: /foo&lt;br /&gt;
      name: testing-volume&lt;br /&gt;
$ kubectl create -f bash.yaml&lt;br /&gt;
pod/bash created&lt;br /&gt;
$ kubectl attach -ti bash -c bash&lt;br /&gt;
If you don&#039;t see a command prompt, try pressing enter.&lt;br /&gt;
root@bash:/# mount | grep foo&lt;br /&gt;
145.131.6.179:/persistent on /foo type nfs4 (rw,relatime,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=145.131.6.179,local_lock=none,addr=145.131.6.179)&lt;br /&gt;
root@bash:/# cat /foo/helloworld.txt&lt;br /&gt;
Hello world!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Make a PersistentVolume with a claim ==&lt;br /&gt;
&lt;br /&gt;
The Pod YAML file above has persistency, but with the downside that the definition describes where the files should be stored. Most of the time, definitions don&#039;t care about where the files are stored, but only what properties the storage has:&lt;br /&gt;
&lt;br /&gt;
* How much storage can we use?&lt;br /&gt;
* Is it optimized for small files or for large files?&lt;br /&gt;
* Is it highly available?&lt;br /&gt;
* Is it backed up?&lt;br /&gt;
&lt;br /&gt;
To more accurately implement this use-case, Kubernetes has two object types called [https://kubernetes.io/docs/concepts/storage/persistent-volumes/ PersistentVolume and PersistentVolumeClaim]. The idea is that a cluster administrator creates PersistentVolumes (abbreviated &amp;lt;code&amp;gt;pv&amp;lt;/code&amp;gt;) that know what kind of storage they represent and where to find it; then, users create PersistentVolumeClaim (abbreviated &amp;lt;code&amp;gt;pvc&amp;lt;/code&amp;gt;) asking for storage with constraints like &amp;quot;at least 10 GB&amp;quot;. When a PVC is created, it is matched to its closest PV and a link is created. If no PV is available to fulfill a PVC, the PVC stays in &amp;quot;Pending&amp;quot; state. In a way, PersistentVolumes are like Nodes in the sense that they provide capacity, and PersistentVolumeClaims are like Pods in the sense that they use that capacity if it is available anywhere in the cluster.&lt;br /&gt;
&lt;br /&gt;
In this section, we&#039;ll rewrite our Pod from before to use a PersistentVolumeClaim with its constraints, and then have that PersistentVolumeClaim automatically matched to a PersistentVolume that provides the same NFS share. The first thing we&#039;ll do is create the PersistentVolumeClaim:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat testing-pvc.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: PersistentVolumeClaim&lt;br /&gt;
metadata:&lt;br /&gt;
  name: testing-pvc&lt;br /&gt;
spec:&lt;br /&gt;
  accessModes:&lt;br /&gt;
    - ReadWriteOnce&lt;br /&gt;
  resources:&lt;br /&gt;
    requests:&lt;br /&gt;
      storage: 2Gi&lt;br /&gt;
$ kubectl apply -f testing-pvc.yaml &lt;br /&gt;
persistentvolumeclaim/testing-pvc created&lt;br /&gt;
$ kubectl get pvc&lt;br /&gt;
NAME             STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE&lt;br /&gt;
testing-pvc      Pending                                                     6s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you see, the &amp;lt;code&amp;gt;testing-pvc&amp;lt;/code&amp;gt; PVC is created, but is Pending, because haven&#039;t supplied any PersistentVolumes to the cluster yet that can fulfill it. So, we create a PersistentVolume:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat nfs-storage.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: PersistentVolume&lt;br /&gt;
metadata:&lt;br /&gt;
  name: nfs-storage&lt;br /&gt;
spec:&lt;br /&gt;
  capacity:&lt;br /&gt;
    storage: 10Gi&lt;br /&gt;
  accessModes:&lt;br /&gt;
    - ReadWriteOnce&lt;br /&gt;
  nfs:&lt;br /&gt;
    path: /persistent&lt;br /&gt;
    server: &amp;lt;IP address&amp;gt;&lt;br /&gt;
$ kubectl apply -f nfs-storage.yaml&lt;br /&gt;
$ kubectl get pv        &lt;br /&gt;
NAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   REASON   AGE&lt;br /&gt;
nfs-storage   10Gi       RWO            Retain           Bound    default/testing-pvc                              18s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It might take a moment for the PVC to be bound, but sure enough, after a bit, it is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl get pvc&lt;br /&gt;
NAME             STATUS   VOLUME        CAPACITY   ACCESS MODES   STORAGECLASS   AGE&lt;br /&gt;
testing-pvc      Bound    nfs-storage   10Gi       RWO                           3m15s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that we asked for only 2 GB, but since the volume provides 10 GB, we got the lowest capacity volume that could be bound.&lt;br /&gt;
&lt;br /&gt;
And let&#039;s create a Pod to use it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat bash-pvc.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: Pod&lt;br /&gt;
metadata:&lt;br /&gt;
  name: bash&lt;br /&gt;
spec:&lt;br /&gt;
  volumes:&lt;br /&gt;
  - name: testing-volume&lt;br /&gt;
    persistentVolumeClaim:&lt;br /&gt;
      claimName: testing-pvc&lt;br /&gt;
  containers:&lt;br /&gt;
  - name: bash&lt;br /&gt;
    image: ubuntu:bionic&lt;br /&gt;
    stdin: true&lt;br /&gt;
    stdinOnce: true&lt;br /&gt;
    tty: true&lt;br /&gt;
    volumeMounts:&lt;br /&gt;
    - mountPath: /foo&lt;br /&gt;
      name: testing-volume&lt;br /&gt;
$ kubectl create -f bash.yaml&lt;br /&gt;
pod/bash created&lt;br /&gt;
$ kubectl attach -ti bash -c bash&lt;br /&gt;
If you don&#039;t see a command prompt, try pressing enter.&lt;br /&gt;
root@bash:/# mount | grep foo&lt;br /&gt;
145.131.6.179:/persistent on /foo type nfs4 (rw,relatime,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=145.131.6.179,local_lock=none,addr=145.131.6.179)&lt;br /&gt;
root@bash:/# cat /foo/helloworld.txt&lt;br /&gt;
Hello world!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This might seem like an insignificant victory, since we&#039;re getting the same end result, but it&#039;s a crucial step forward: our Pods, Jobs and Deployments don&#039;t need to care anymore where their storage comes from, only that it is persistent.&lt;br /&gt;
&lt;br /&gt;
There&#039;s some more benefits of Persistent Volume Claims:&lt;br /&gt;
&lt;br /&gt;
* If a PVC is deleted while there is still a Pod using it, it will switch state to &amp;lt;code&amp;gt;Terminating&amp;lt;/code&amp;gt; but will not disappear until the Pods are gone, too.&lt;br /&gt;
* PVCs have a reclaim policy that can be set to &amp;lt;code&amp;gt;Delete&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Recycle&amp;lt;/code&amp;gt;, allowing the PV&#039;s contents to be deleted as well.&lt;br /&gt;
* When StorageClasses are used, PVC&#039;s can only be bound to a PV with the same Storage Class. When no free PV exists within the Storage Class, while a PVC is waiting to be bound, a PV can be automatically created. This is called [https://kubernetes.io/docs/concepts/storage/persistent-volumes/#provisioning Dynamic provisioning].&lt;br /&gt;
* It is possible to make a PV with a &amp;quot;Node Affinity&amp;quot;, causing any Pods using that PV to run on a specific node. This combines very well with the HostPath volume type, as this allows a bind-mount of some directory on a Node to be accessible within a Pod.&lt;br /&gt;
** But, it should also be obvious that this is a security risk if you allow untrusted users to create PV&#039;s and use them in Pods. TO DO: Add a section on protecting this.&lt;br /&gt;
&lt;br /&gt;
== Running a Deployment using this volume ==&lt;br /&gt;
&lt;br /&gt;
Now that we know how to persistently store data and access it from Pods, it&#039;s time to create an actual Deployment with a real-world application in it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat nginx.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: PersistentVolume&lt;br /&gt;
metadata:&lt;br /&gt;
  name: nfs-html-storage&lt;br /&gt;
spec:&lt;br /&gt;
  capacity:&lt;br /&gt;
    storage: 10Gi&lt;br /&gt;
  accessModes:&lt;br /&gt;
    - ReadWriteOnce&lt;br /&gt;
  nfs:&lt;br /&gt;
    path: /persistent/html&lt;br /&gt;
    server: &amp;lt;IP of nfs server&amp;gt;&lt;br /&gt;
---&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: PersistentVolumeClaim&lt;br /&gt;
metadata:&lt;br /&gt;
  name: nginx-webfiles&lt;br /&gt;
spec:&lt;br /&gt;
  accessModes:&lt;br /&gt;
    - ReadWriteOnce&lt;br /&gt;
  resources:&lt;br /&gt;
    requests:&lt;br /&gt;
      storage: 2Gi&lt;br /&gt;
---&lt;br /&gt;
apiVersion: apps/v1&lt;br /&gt;
kind: Deployment&lt;br /&gt;
metadata:&lt;br /&gt;
  name: nginx-deployment&lt;br /&gt;
spec:&lt;br /&gt;
  selector:&lt;br /&gt;
    matchLabels:&lt;br /&gt;
      app: nginx&lt;br /&gt;
  replicas: 2&lt;br /&gt;
  template:&lt;br /&gt;
    metadata:&lt;br /&gt;
      labels:&lt;br /&gt;
        app: nginx&lt;br /&gt;
    spec:&lt;br /&gt;
      volumes:&lt;br /&gt;
      - name: webfiles&lt;br /&gt;
        persistentVolumeClaim:&lt;br /&gt;
          claimName: nginx-webfiles&lt;br /&gt;
      containers:&lt;br /&gt;
      - name: nginx&lt;br /&gt;
        image: nginx:1.7.9&lt;br /&gt;
        ports:&lt;br /&gt;
        - containerPort: 80&lt;br /&gt;
        volumeMounts:&lt;br /&gt;
        - mountPath: /usr/share/nginx/html&lt;br /&gt;
          name: webfiles&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, before starting this deployment, make an example web page by creating &amp;lt;code&amp;gt;/persistent/html/index.html&amp;lt;/code&amp;gt; with something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&amp;lt;h1&amp;gt;It&#039;s working!&amp;lt;/h1&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, start the PV, PVC and deployment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl apply -f nginx.yaml&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A note about &amp;lt;code&amp;gt;kubectl apply&amp;lt;/code&amp;gt; (&amp;quot;declarative management&amp;quot;) versus &amp;lt;code&amp;gt;kubectl create&amp;lt;/code&amp;gt; (&amp;quot;imperative management&amp;quot;): in this case, &amp;lt;code&amp;gt;apply&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;create&amp;lt;/code&amp;gt; would do the same thing as the Deployment described in &amp;lt;code&amp;gt;nginx.yaml&amp;lt;/code&amp;gt; doesn&#039;t exist yet. However, would you change &amp;lt;code&amp;gt;nginx.yaml&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;kubectl create&amp;lt;/code&amp;gt; again, you&#039;d get an error. &amp;quot;Imperative management&amp;quot; (create, delete, replace) means you&#039;re telling kubectl what action is necessary, while &amp;quot;declarative management&amp;quot; means you&#039;re telling kubectl what the state of the cluster should be, and it will perform the correct action for you. Both are fine in a production context; from now on, this page will be using &amp;lt;code&amp;gt;apply&amp;lt;/code&amp;gt; where possible since that seems to be the community consensus in tutorials.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s check if the deployment has been created and the pods as well:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl get deployments&lt;br /&gt;
NAME               READY   UP-TO-DATE   AVAILABLE   AGE&lt;br /&gt;
nginx-deployment   1/2     2            1           11h&lt;br /&gt;
$ kubectl get pods                                      &lt;br /&gt;
NAME                                READY   STATUS              RESTARTS   AGE&lt;br /&gt;
nginx-deployment-58b6c946d5-fnqr6   1/1     Running             0          49s&lt;br /&gt;
nginx-deployment-58b6c946d5-p2nlm   0/1     ContainerCreating   0          49s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
One pod has been created, the other one is still in &amp;lt;code&amp;gt;ContainerCreating&amp;lt;/code&amp;gt; state. Let&#039;s check why...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl describe pod nginx-deployment-58b6c946d5-p2nlm&lt;br /&gt;
[....]&lt;br /&gt;
  Warning  FailedMount  15s  kubelet, kubetest1  (combined from similar events): MountVolume.SetUp failed for volume &amp;quot;websource&amp;quot; : mount failed: exit status 32&lt;br /&gt;
Mounting command: systemd-run&lt;br /&gt;
Mounting arguments: --description=Kubernetes transient mount for /var/lib/kubelet/pods/fac670f9-47d7-11e9-a977-001dd8b7660c/volumes/kubernetes.io~nfs/websource --scope -- mount -t nfs 145.131.6.179:/persistent/html /var/lib/kubelet/pods/fac670f9-47d7-11e9-a977-001dd8b7660c/volumes/kubernetes.io~nfs/websource&lt;br /&gt;
Output: Running scope as unit run-r368fd7089b0a46139882e708a89f8926.scope.&lt;br /&gt;
mount: wrong fs type, bad option, bad superblock on 145.131.6.179:/persistent/html,&lt;br /&gt;
       missing codepage or helper program, or other error&lt;br /&gt;
       (for several filesystems (e.g. nfs, cifs) you might&lt;br /&gt;
       need a /sbin/mount.&amp;lt;type&amp;gt; helper program)&lt;br /&gt;
&lt;br /&gt;
       In some cases useful info is found in syslog - try&lt;br /&gt;
       dmesg | tail or so.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In other words, it is failing to start because the volume &amp;lt;code&amp;gt;websource&amp;lt;/code&amp;gt; (our NFS mount) cannot be started. You can see from the output that this is running on the kubelet for &amp;lt;code&amp;gt;kubetest1&amp;lt;/code&amp;gt;, and the error comes from &amp;lt;code&amp;gt;mount -t nfs 145.131.6.179:/persistent/html/...&amp;lt;/code&amp;gt;. The error is correct: we need an NFS mount helper tool that isn&#039;t installed on kubetest1. I run &amp;lt;code&amp;gt;apt-get install nfs-common&amp;lt;/code&amp;gt; on it, and sure enough, the pod is soon running:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl get pods&lt;br /&gt;
NAME                                READY   STATUS    RESTARTS   AGE&lt;br /&gt;
nginx-deployment-58b6c946d5-fnqr6   1/1     Running   0          6m42s&lt;br /&gt;
nginx-deployment-58b6c946d5-p2nlm   1/1     Running   0          6m42s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Making services accessible over TCP =&lt;br /&gt;
&lt;br /&gt;
== Accessing Pods and Deployment using port-forward ==&lt;br /&gt;
&lt;br /&gt;
Now, we&#039;d like to see nginx in action! The pod is listening on an internal port 80 (according to its configuration and the default nginx config). If we want to access this from outside the cluster, there&#039;s three ways:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl port-forward&amp;lt;/code&amp;gt;, which listens to a local port on the machine where you run &amp;lt;code&amp;gt;kubectl port-forward&amp;lt;/code&amp;gt;, and then forwards all connections to a pod. (I&#039;ve explained above how to configure kubectl so it can run on your own machine when the cluster runs elsewhere.)&lt;br /&gt;
* Creating a Service, in the next section&lt;br /&gt;
* Creating an Ingress, in the section after that&lt;br /&gt;
&lt;br /&gt;
Let&#039;s try creating a port-forward to the pod first:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl port-forward pod/nginx-deployment-58b6c946d5-fnqr6 8080:80&lt;br /&gt;
Forwarding from 127.0.0.1:8080 -&amp;gt; 80&lt;br /&gt;
Forwarding from [::1]:8080 -&amp;gt; 80&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, visit &amp;lt;code&amp;gt;http://127.0.0.1:8080&amp;lt;/code&amp;gt; in your browser and presto!&lt;br /&gt;
&lt;br /&gt;
You can also do a port-forward to a deployment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl port-forward deployment/nginx-deployment 8080:80&lt;br /&gt;
Forwarding from 127.0.0.1:8080 -&amp;gt; 80&lt;br /&gt;
Forwarding from [::1]:8080 -&amp;gt; 80&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you start a port-forward to a deployment, the deployment is resolved to a random one of the &amp;lt;code&amp;gt;Running&amp;lt;/code&amp;gt; pods in the deployment. So, in this case, you&#039;ll get a response from &#039;&#039;either&#039;&#039; of the two pods; if one of them is down, you&#039;ll get it from the one that&#039;s up. (If both are down, you&#039;re out of luck.) However, when that resolved pod goes down for whatever reason, the port-forward is &#039;&#039;&#039;not&#039;&#039;&#039; restarted to another pod in the deployment. You can try this out by running &amp;lt;code&amp;gt;kubectl delete pod nginx-deployment-...&amp;lt;/code&amp;gt; on your pods: the deployment will cause them to be restarted, but the port-forward will cease to work once you&#039;ve deleted the right one.&lt;br /&gt;
&lt;br /&gt;
== Accessing a deployment using a Service ==&lt;br /&gt;
&lt;br /&gt;
So, what should we do if we want the application to be reachable even if its Pods go down? The first method is to create a [https://kubernetes.io/docs/concepts/services-networking/service/ Service]. A Service describes how an application should be accessible. There&#039;s multiple types of Services, corresponding with multiple interpretations of &amp;quot;accessible&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ClusterIP&amp;lt;/code&amp;gt; is a service type indicating that the application should be only internally accessible using a &amp;quot;virtual service IP&amp;quot; (as described above). This service IP will be allocated by Kubernetes and distributed to all nodes and pods, so that a connection to the virtual service IP on the correct port will automatically end up on one of its running Pods.&lt;br /&gt;
* &amp;lt;code&amp;gt;NodePort&amp;lt;/code&amp;gt; is a service type indicating that the application should be externally accessible using a &amp;quot;service port&amp;quot; on all Nodes. The service port will be allocated by Kubernetes (you can choose it, but that&#039;s not recommended) and distributed to all nodes, so that a connection to any node on the service port will automatically end up on one of its running Pods. A NodePort service also automatically gets a ClusterIP, so you can use that, too.&lt;br /&gt;
* &amp;lt;code&amp;gt;LoadBalancer&amp;lt;/code&amp;gt; is a service type indicating that the application should be externally accessible using a provided load balancer. By default, this works like the &amp;lt;code&amp;gt;NodePort&amp;lt;/code&amp;gt; but on specific cloud providers you&#039;ll also get an allocated external IP address, on which a wanted port is listening and end up on one of the running Pods. I&#039;m running this on my own cluster, not one hosted by a cloud provider, so I won&#039;t create a &amp;lt;code&amp;gt;LoadBalancer&amp;lt;/code&amp;gt; service. If you&#039;d like to, [https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer this page] explains how they work.&lt;br /&gt;
** There is a controller that allows you to use &amp;lt;code&amp;gt;LoadBalancer&amp;lt;/code&amp;gt; services on your own bare-metal cluster that doesn&#039;t run on a cloud provider. It&#039;s called [https://metallb.universe.tf/ MetalLB] and it works by having a pool of external routable IP addresses to choose from; when allocating from that pool, it starts announcing that IP address via either ARP or BGP onto an arbitrary Node, so that traffic to that IP ends up there. If the Node goes down, MetalLB elects a new leader node and re-announces the IP there, so that the service is moved.&lt;br /&gt;
* &amp;lt;code&amp;gt;ExternalName&amp;lt;/code&amp;gt; doesn&#039;t actually set up any forwarding, but allows you to register an internal name that forwards to a given name in DNS elsewhere. This allows migration to/from Kubernetes.&lt;br /&gt;
* Not a service type, but if your service uses HTTP, you can use Ingress instead of Service to make your service externally accessible. More on that later.&lt;br /&gt;
&lt;br /&gt;
Since we want our service to be externally accessible, we&#039;ll make a NodePort service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat service.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: Service&lt;br /&gt;
metadata:&lt;br /&gt;
  name: nginx-service&lt;br /&gt;
spec:&lt;br /&gt;
  selector:&lt;br /&gt;
    app: nginx&lt;br /&gt;
  ports:&lt;br /&gt;
    - name: nginx&lt;br /&gt;
      port: 80&lt;br /&gt;
      protocol: TCP&lt;br /&gt;
  type: NodePort&lt;br /&gt;
$ kubectl apply -f service.yaml&lt;br /&gt;
$ kubectl get services&lt;br /&gt;
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE&lt;br /&gt;
kubernetes      ClusterIP   10.16.0.1       &amp;lt;none&amp;gt;        443/TCP        8d&lt;br /&gt;
nginx-service   NodePort    10.16.247.178   &amp;lt;none&amp;gt;        80:31106/TCP   4s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When we define the Service, we provide a Selector which defines the Pods supplying this Service. You can supply arbitrary labels to Pods; the &amp;lt;code&amp;gt;app=nginx&amp;lt;/code&amp;gt; label was initially set in our Deployment (see the &amp;lt;code&amp;gt;metadata&amp;lt;/code&amp;gt; section) and is inherited by all Pods created by it, so the Service will use them directly.&lt;br /&gt;
&lt;br /&gt;
As you can see, our &amp;lt;code&amp;gt;nginx-service&amp;lt;/code&amp;gt; service is now up and it got external port 31106. Indeed, when we go to &amp;lt;code&amp;gt;http://&amp;lt;IP&amp;gt;:31106/&amp;lt;/code&amp;gt; (replacing &amp;lt;IP&amp;gt; with the IP of any of our nodes) we can see the page again! Also, when we &amp;lt;code&amp;gt;kubectl delete pod ...&amp;lt;/code&amp;gt; arbitrary pods within the deployment, they are restarted automatically, and accesses to the external IP/port keep working as long as at least one pod is &amp;lt;code&amp;gt;Running&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
There is an alternative way to make a Service externally reachable that can be convenient: you can set an &amp;lt;b&amp;gt;externalIP&amp;lt;/b&amp;gt; on a &amp;lt;code&amp;gt;ClusterIP&amp;lt;/code&amp;gt; type Service and any Node with that IP will listen on the indicated Service Port -- but that comes with a big fat warning: it introduces a single point of failure into your Service, as it will be unreachable if that Node is down! Yet, it can be very convenient especially for bare-metal clusters, so I&#039;ll show you how to do it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat service-externalip.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: Service&lt;br /&gt;
metadata:&lt;br /&gt;
  name: nginx-service-externalip&lt;br /&gt;
spec:&lt;br /&gt;
  selector:&lt;br /&gt;
    app: nginx&lt;br /&gt;
  ports:&lt;br /&gt;
    - name: nginx&lt;br /&gt;
      port: 80&lt;br /&gt;
      protocol: TCP&lt;br /&gt;
  type: ClusterIP&lt;br /&gt;
  externalIPs:&lt;br /&gt;
  - &amp;quot;145.131.6.177&amp;quot; # Replace this with one of your node&#039;s IPs, of course!&lt;br /&gt;
$ kubectl apply -f service-externalip.yaml&lt;br /&gt;
$ kubectl get services&lt;br /&gt;
NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE&lt;br /&gt;
kubernetes                 ClusterIP   10.16.0.1       &amp;lt;none&amp;gt;          443/TCP        19d&lt;br /&gt;
nginx-service              NodePort    10.16.247.178   &amp;lt;none&amp;gt;          80:31106/TCP   11d&lt;br /&gt;
nginx-service-externalip   ClusterIP   10.16.165.143   145.131.6.177   80/TCP         3s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sure enough, if you visit your external IP on port 80, you should see the same page served by Nginx appear! As described before, you can have a similar approach without having a fixed Node to connect to; the controller [https://metallb.universe.tf/ MetalLB] chooses a node randomly then uses ARP or BGP to announce an IP address on it. But, this sort of setup only works in a controlled subnet so I can&#039;t try it on this cluster.&lt;br /&gt;
&lt;br /&gt;
== A summary so far ==&lt;br /&gt;
&lt;br /&gt;
We&#039;ve talked about:&lt;br /&gt;
&lt;br /&gt;
* Nodes, the machines (usually physical or VMs) that together form the Kubernetes cluster&lt;br /&gt;
** Master nodes are nothing special, except they (also) run Pods that together form the Kubernetes Control Plane&lt;br /&gt;
* Pods, the basic unit of scheduling; they run on Nodes and consist of at least one Container running an actual Docker Image&lt;br /&gt;
** Pods have an IP address within the Pod networking range&lt;br /&gt;
* Deployments, which are a way to tell Kubernetes to always have some type of Pod running&lt;br /&gt;
* Jobs, which are a way to tell Kubernetes to keep running some type of Pod until it finishes successfully&lt;br /&gt;
* Services, which are a way to make some application in Pods accessible over TCP (inside and/or outside the cluster)&lt;br /&gt;
** Services can have a &#039;virtual&#039; IP address within the Service networking range, they can have a NodePort all Nodes listen on, and/or they can have an external IP statically or dynamically provided by a LoadBalancer.&lt;br /&gt;
* Volumes, which provide various kinds of storage to Pods&lt;br /&gt;
** Persistent Volumes are provided by the cluster administrator to allow storage&lt;br /&gt;
** Persistent Volume Claims claim such volumes for some user&lt;br /&gt;
** Pods can have a Persistent Volume Claim attached to them, making the contents of the volume actually usable&lt;br /&gt;
&lt;br /&gt;
== Accessing a Deployment using an Ingress ==&lt;br /&gt;
&lt;br /&gt;
[https://kubernetes.io/docs/concepts/services-networking/ingress/ Ingresses] are like Services, but for HTTP only. This specialisation allows adding a number of additional features, such as having multiple applications behind one URL or hostname (e.g. micro-services), SSL termination and splitting load between different versions of the same service (canarying).&lt;br /&gt;
&lt;br /&gt;
Ingress is currently in beta (v1beta1), meaning that the feature is well-tested and will continue to exist, but details may change. Consider this before using it in production.&lt;br /&gt;
&lt;br /&gt;
Like LoadBalancer Services, creating an Ingress does not immediately change anything in the cluster. You need to have an Ingress Controller for anything to change in the cluster after you create an Ingress. There&#039;s many [https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/ Ingress Controller plugins] to choose from; I will try [https://github.com/containous/traefik Traefik] since it supports Let&#039;s Encrypt out of the box. (Some cloud providers may provide an Ingress Controller out of the box.)&lt;br /&gt;
&lt;br /&gt;
First of all, we set up Traefik. For this, we&#039;ll need to create some service types we haven&#039;t seen before: service accounts, cluster role bindings and config maps. Bear with me for a bit while we set up Traefik:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat traefik-account.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: ServiceAccount&lt;br /&gt;
metadata:&lt;br /&gt;
  name: traefik-ingress-controller&lt;br /&gt;
  namespace: kube-system&lt;br /&gt;
---&lt;br /&gt;
kind: ClusterRole&lt;br /&gt;
apiVersion: rbac.authorization.k8s.io/v1beta1&lt;br /&gt;
metadata:&lt;br /&gt;
  name: traefik-ingress-controller&lt;br /&gt;
rules:&lt;br /&gt;
  - apiGroups:&lt;br /&gt;
      - &amp;quot;&amp;quot;&lt;br /&gt;
    resources:&lt;br /&gt;
      - services&lt;br /&gt;
      - endpoints&lt;br /&gt;
      - secrets&lt;br /&gt;
    verbs:&lt;br /&gt;
      - get&lt;br /&gt;
      - list&lt;br /&gt;
      - watch&lt;br /&gt;
  - apiGroups:&lt;br /&gt;
      - extensions&lt;br /&gt;
    resources:&lt;br /&gt;
      - ingresses&lt;br /&gt;
    verbs:&lt;br /&gt;
      - get&lt;br /&gt;
      - list&lt;br /&gt;
      - watch&lt;br /&gt;
---&lt;br /&gt;
kind: ClusterRoleBinding&lt;br /&gt;
apiVersion: rbac.authorization.k8s.io/v1beta1&lt;br /&gt;
metadata:&lt;br /&gt;
  name: traefik-ingress-controller&lt;br /&gt;
roleRef:&lt;br /&gt;
  apiGroup: rbac.authorization.k8s.io&lt;br /&gt;
  kind: ClusterRole&lt;br /&gt;
  name: traefik-ingress-controller&lt;br /&gt;
subjects:&lt;br /&gt;
- kind: ServiceAccount&lt;br /&gt;
  name: traefik-ingress-controller&lt;br /&gt;
  namespace: kube-system&lt;br /&gt;
$ kubectl apply -f traefik-account.yaml&lt;br /&gt;
serviceaccount/traefik-ingress-controller created&lt;br /&gt;
clusterrole.rbac.authorization.k8s.io/traefik-ingress-controller created&lt;br /&gt;
clusterrolebinding.rbac.authorization.k8s.io/traefik-ingress-controller created&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we create a ConfigMap for Traefik&#039;s configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat traefik-configmap.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: ConfigMap&lt;br /&gt;
metadata:&lt;br /&gt;
  name: traefik-configmap&lt;br /&gt;
  namespace: kube-system&lt;br /&gt;
data:&lt;br /&gt;
  traefik.toml: |&lt;br /&gt;
    defaultEntryPoints = [&amp;quot;http&amp;quot;, &amp;quot;https&amp;quot;]&lt;br /&gt;
    insecureSkipVerify = true&lt;br /&gt;
&lt;br /&gt;
    [entryPoints]&lt;br /&gt;
      [entryPoints.http]&lt;br /&gt;
        address = &amp;quot;:80&amp;quot;&lt;br /&gt;
      [entryPoints.https]&lt;br /&gt;
        address = &amp;quot;:443&amp;quot;&lt;br /&gt;
        [entryPoints.https.tls]&lt;br /&gt;
      [entryPoints.admin]&lt;br /&gt;
        address = &amp;quot;:8080&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    [kubernetes]&lt;br /&gt;
      [kubernetes.ingressEndpoint]&lt;br /&gt;
        publishedService = &amp;quot;kube-system/traefik-ingress-service-external&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    [api]&lt;br /&gt;
    entryPoint = &amp;quot;admin&amp;quot;&lt;br /&gt;
$ kubectl apply -f traefik-configmap.yaml&lt;br /&gt;
configmap/traefik-configmap created&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That being done, we now start the Traefik deployment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat traefik.yaml&lt;br /&gt;
apiVersion: extensions/v1beta1&lt;br /&gt;
kind: Deployment&lt;br /&gt;
metadata:&lt;br /&gt;
    name: traefik-ingress&lt;br /&gt;
    namespace: kube-system&lt;br /&gt;
    labels:&lt;br /&gt;
        k8s-app: traefik-ingress-lb&lt;br /&gt;
spec:&lt;br /&gt;
    replicas: 1&lt;br /&gt;
    selector:&lt;br /&gt;
        matchLabels:&lt;br /&gt;
            k8s-app: traefik-ingress-lb&lt;br /&gt;
    template:&lt;br /&gt;
        metadata:&lt;br /&gt;
            labels:&lt;br /&gt;
                k8s-app: traefik-ingress-lb&lt;br /&gt;
                name: traefik-ingress-lb&lt;br /&gt;
        spec:&lt;br /&gt;
            volumes:&lt;br /&gt;
            - name: traefik-configmap&lt;br /&gt;
              configMap:&lt;br /&gt;
                name: traefik-configmap&lt;br /&gt;
            serviceAccountName: traefik-ingress-controller&lt;br /&gt;
            terminationGracePeriodSeconds: 60&lt;br /&gt;
            containers:&lt;br /&gt;
            - image: traefik&lt;br /&gt;
              name: traefik-ingress-lb&lt;br /&gt;
              ports:&lt;br /&gt;
              - name: web&lt;br /&gt;
                containerPort: 80&lt;br /&gt;
              - name: https&lt;br /&gt;
                containerPort: 443&lt;br /&gt;
              - name: admin&lt;br /&gt;
                containerPort: 8080&lt;br /&gt;
              volumeMounts:&lt;br /&gt;
              - mountPath: &amp;quot;/config&amp;quot;&lt;br /&gt;
                name: &amp;quot;traefik-configmap&amp;quot;&lt;br /&gt;
              args:&lt;br /&gt;
              - --loglevel=INFO&lt;br /&gt;
              - --configfile=/config/traefik.toml&lt;br /&gt;
$ kubectl apply -f traefik.yaml&lt;br /&gt;
deployment.extensions/traefik-ingress created&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What did this do?&lt;br /&gt;
* We created the service account and privileges Traefik needs to find Ingresses, Services and Endpoints.&lt;br /&gt;
* We created a ConfigMap, a hard-coded type of Volume that is commonly used to supply configuration inside Pods. This ConfigMap causes Traefik to listen on ports 80, 443 and 8080.&lt;br /&gt;
* Then, we created a Deployment that runs the Traefik image with the given configmap and service account.&lt;br /&gt;
* Note that you won&#039;t find these deployments and pods using the normal &amp;lt;code&amp;gt;kubectl get pods&amp;lt;/code&amp;gt; (etc) commands unless you give &amp;lt;code&amp;gt;-n kube-system&amp;lt;/code&amp;gt; to select the kube-system namespace.&lt;br /&gt;
&lt;br /&gt;
You should see a &amp;lt;code&amp;gt;traefik-ingress-...&amp;lt;/code&amp;gt; pod with status &amp;lt;code&amp;gt;Running&amp;lt;/code&amp;gt; when you run &amp;lt;code&amp;gt;kubectl get pods -n kube-system&amp;lt;/code&amp;gt;; if that&#039;s not the case, you should stop here and investigate what&#039;s wrong.&lt;br /&gt;
&lt;br /&gt;
To use Traefik, we&#039;ll configure two things:&lt;br /&gt;
* External connections end up at it&lt;br /&gt;
* It reads the hostname and path of requests, and sends them onwards to the correct Service&lt;br /&gt;
&lt;br /&gt;
The first thing we&#039;ve already discussed before: it requires setting up a LoadBalancer Service if you&#039;re running on a cloud provider; if you&#039;re not, like me, you can set up a ClusterIP Service with an ExternalIP and the side-note of a single-point-of-failure applies here as well. (Note that we expose only ports 80 and 443, not 8080; this is the administrator port of Traefik.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat traefik-service-external.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: Service&lt;br /&gt;
metadata:&lt;br /&gt;
  name: traefik-ingress-service-external&lt;br /&gt;
  namespace: kube-system&lt;br /&gt;
spec:&lt;br /&gt;
  selector:&lt;br /&gt;
    k8s-app: traefik-ingress-lb&lt;br /&gt;
  ports:&lt;br /&gt;
    - protocol: TCP&lt;br /&gt;
      port: 80&lt;br /&gt;
      name: web&lt;br /&gt;
    - protocol: TCP&lt;br /&gt;
      port: 443&lt;br /&gt;
      name: https&lt;br /&gt;
  externalIPs:&lt;br /&gt;
  - &amp;quot;145.131.8.75&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;externalIPs&amp;lt;/code&amp;gt; mentioned here should be the external IP of one of your Nodes. At this point you can also create a record in DNS to point to this IP address if you want; I created &amp;lt;code&amp;gt;kubetest.sjorsgielen.nl IN A 145.131.8.75&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Having this set up should cause &amp;lt;code&amp;gt;http://kubetest.sjorsgielen.nl/&amp;lt;/code&amp;gt; to end up within Traefik. It will give a &amp;quot;404 page not found&amp;quot; result, as Traefik doesn&#039;t know about any Ingresses yet to forward your request to.&lt;br /&gt;
&lt;br /&gt;
You can check the Traefik dashboard to see that it&#039;s up. Currently, we&#039;ll need a port-forward for that:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl port-forward -n kube-system deployment/traefik-ingress 8080:8080&lt;br /&gt;
Forwarding from [::1]:8080 -&amp;gt; 8080&lt;br /&gt;
Forwarding from 127.0.0.1:8080 -&amp;gt; 8080&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, visit &amp;lt;code&amp;gt;http://localhost:8080/&amp;lt;/code&amp;gt; and you should see the Traefik dashboard. It will show no frontends and no backends, as we haven&#039;t created any Ingresses yet for it to route. So let&#039;s create one for our Nginx service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat ingress.yaml&lt;br /&gt;
apiVersion: extensions/v1beta1&lt;br /&gt;
kind: Ingress&lt;br /&gt;
metadata:&lt;br /&gt;
    name: nginx-ingress&lt;br /&gt;
    annotations:&lt;br /&gt;
        traefik.frontend.rule.type: PathPrefixStrip&lt;br /&gt;
spec:&lt;br /&gt;
    rules:&lt;br /&gt;
    - host: kubetest.sjorsgielen.nl&lt;br /&gt;
      http:&lt;br /&gt;
        paths:&lt;br /&gt;
        - path: /nginx&lt;br /&gt;
          backend:&lt;br /&gt;
            serviceName: nginx-service&lt;br /&gt;
            servicePort: 80&lt;br /&gt;
$ kubectl apply -f nginx.yaml&lt;br /&gt;
ingress.extensions/nginx-ingress created&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So what does this mean?&lt;br /&gt;
&lt;br /&gt;
* It&#039;s an Ingress type, meaning it&#039;s a message to the cluster/Traefik that we want to have a Service externally accessible over HTTP.&lt;br /&gt;
* The service will be reachable on the Host &amp;lt;code&amp;gt;kubetest.sjorsgielen.nl&amp;lt;/code&amp;gt; -- this acts like a sort of virtual server in Apache, where different hosts can serve different content.&lt;br /&gt;
* The request Path must begin with &amp;lt;code&amp;gt;/nginx&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;traefik.frontend.rule.type: PathPrefixStrip&amp;lt;/code&amp;gt; annotation will cause the &amp;lt;code&amp;gt;/nginx&amp;lt;/code&amp;gt; prefix to be stripped off before the request is forwarded.&lt;br /&gt;
* The requests will be forwarded to the &amp;lt;code&amp;gt;nginx-service&amp;lt;/code&amp;gt; service on port 80.&lt;br /&gt;
&lt;br /&gt;
In other words, http://kubetest.sjorsgielen.nl/nginx/index.html will be forwarded to http://nginx-service/index.html. And indeed, it shows the same Nginx page again! Also, if you go to the Traefik dashboard again, you&#039;ll see the frontend and backend have appeared and also you&#039;ll be able to see the average response time on the Health tab.&lt;br /&gt;
&lt;br /&gt;
Now, you could replace your port-forward to the Traefik dashboard with a Service and an Ingress so you can make it externally accessible on your hostname (or a different one) as well. I&#039;ll leave that as an exercise to you!&lt;br /&gt;
&lt;br /&gt;
== Let&#039;s encrypt this ==&lt;br /&gt;
&lt;br /&gt;
There&#039;s one very nice feature of Traefik I didn&#039;t want you to miss out on. It of course supports TLS, and it can automatically get your certificates through any ACME provider such as Let&#039;s Encrypt.&lt;br /&gt;
&lt;br /&gt;
For this, we change our ConfigMap to include a &amp;lt;code&amp;gt;[acme]&amp;lt;/code&amp;gt; section and also to auto-forward all HTTP requests to HTTPS:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat traefik-configmap.yaml&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: ConfigMap&lt;br /&gt;
metadata:&lt;br /&gt;
  name: traefik-configmap&lt;br /&gt;
  namespace: kube-system&lt;br /&gt;
data:&lt;br /&gt;
  traefik.toml: |&lt;br /&gt;
    defaultEntryPoints = [&amp;quot;http&amp;quot;, &amp;quot;https&amp;quot;]&lt;br /&gt;
    insecureSkipVerify = true&lt;br /&gt;
&lt;br /&gt;
    [entryPoints]&lt;br /&gt;
      [entryPoints.http]&lt;br /&gt;
        address = &amp;quot;:80&amp;quot;&lt;br /&gt;
        [entryPoints.http.redirect]&lt;br /&gt;
          entryPoint = &amp;quot;https&amp;quot;&lt;br /&gt;
      [entryPoints.https]&lt;br /&gt;
        address = &amp;quot;:443&amp;quot;&lt;br /&gt;
        [entryPoints.https.tls]&lt;br /&gt;
      [entryPoints.admin]&lt;br /&gt;
        address = &amp;quot;:8080&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    [acme]&lt;br /&gt;
    email = &#039;your e-mail address&#039;&lt;br /&gt;
    storage = &amp;quot;acme.json&amp;quot;&lt;br /&gt;
    caServer = &amp;quot;https://acme-v01.api.letsencrypt.org/directory&amp;quot;&lt;br /&gt;
    entryPoint = &amp;quot;https&amp;quot;&lt;br /&gt;
    onDemand = true&lt;br /&gt;
      [acme.httpChallenge]&lt;br /&gt;
      entryPoint = &amp;quot;http&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    [kubernetes]&lt;br /&gt;
      [kubernetes.ingressEndpoint]&lt;br /&gt;
        publishedService = &amp;quot;kube-system/traefik-ingress-service-external&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    [api]&lt;br /&gt;
    entryPoint = &amp;quot;admin&amp;quot;&lt;br /&gt;
$ kubectl apply -f traefik-configmap.yaml&lt;br /&gt;
configmap/traefik-configmap configured&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, unfortunately, changing ConfigMaps doesn&#039;t automatically update the Pods that use it. So, we can destroy our Pod and the Deployment will recreate it with the correct configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl get pods -n kube-system | grep traefik&lt;br /&gt;
traefik-ingress-6dcd896c78-7w2k6       1/1     Running   0          8d&lt;br /&gt;
$ kubectl delete pod traefik-ingress-6dcd896c78-7w2k6 -n kube-system&lt;br /&gt;
$ kubectl get pods -n kube-system | grep traefik&lt;br /&gt;
traefik-ingress-6dcd896c78-8gl9t       1/1     Running   0          15s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Traefik will start requesting a TLS certificate when the first TLS request comes in. That may take a minute for the LetsEncrypt challenge to resolve, but after this, you should be able to access your hostname via HTTPS and it should present a valid certificate. In my case, https://kubetest.sjorsgielen.nl/nginx gives the same working page! Also, we&#039;ve configured the http forward, so http://kubetest.sjorsgielen.nl/nginx just forwards there. Hassle-free TLS, done!&lt;br /&gt;
&lt;br /&gt;
= Creating your own images =&lt;br /&gt;
&lt;br /&gt;
So far, we&#039;ve usually set up the standard container &amp;lt;code&amp;gt;ubuntu:bionic&amp;lt;/code&amp;gt;. It&#039;s pulled from the Docker Hub at https://hub.docker.com/_/ubuntu. Docker Hub is a central registry for images. In the same way you can pull many images from there, such as the minimal Linux image &amp;lt;code&amp;gt;alpine&amp;lt;/code&amp;gt; or the image running in our Traefik pod, &amp;lt;code&amp;gt;traefik&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
But, if we want to run our own Docker images inside Kubernetes, it will need to be able to pull them as well. This can be done by uploading our images to Docker Hub, but for our own experimentation, let&#039;s set up our own registry and plug Kubernetes into it.&lt;br /&gt;
&lt;br /&gt;
To begin with, the registry will need storage for its images. True to our earlier experiments, we start by creating a persistent volume claim. (I&#039;ll assume there&#039;s a persistent volume to fulfill it; if not, check above how to create one yourself.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: PersistentVolumeClaim&lt;br /&gt;
metadata:&lt;br /&gt;
  name: registry-files&lt;br /&gt;
spec:&lt;br /&gt;
  storageClassName: default&lt;br /&gt;
  accessModes:&lt;br /&gt;
    - ReadWriteOnce&lt;br /&gt;
  resources:&lt;br /&gt;
    requests:&lt;br /&gt;
      storage: 20Gi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The registry deployment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apiVersion: apps/v1&lt;br /&gt;
kind: Deployment&lt;br /&gt;
metadata:&lt;br /&gt;
  name: registry&lt;br /&gt;
spec:&lt;br /&gt;
  selector:&lt;br /&gt;
    matchLabels:&lt;br /&gt;
      app: registry&lt;br /&gt;
  replicas: 1&lt;br /&gt;
  template:&lt;br /&gt;
    metadata:&lt;br /&gt;
      labels:&lt;br /&gt;
        app: registry&lt;br /&gt;
    spec:&lt;br /&gt;
      volumes:&lt;br /&gt;
      - name: registrystorage&lt;br /&gt;
        persistentVolumeClaim:&lt;br /&gt;
          claimName: registry-files&lt;br /&gt;
      containers:&lt;br /&gt;
      - name: registry&lt;br /&gt;
        image: registry:2&lt;br /&gt;
        ports:&lt;br /&gt;
        - containerPort: 5000&lt;br /&gt;
        volumeMounts:&lt;br /&gt;
        - mountPath: /var/lib/registry&lt;br /&gt;
          name: registrystorage&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And a Service + Ingress to make it accessible on a new hostname. I found that Docker doesn&#039;t support accessing a registry with a path prefix, so we have to give it its own hostname. Luckily, with Traefik, it&#039;s easy to route; you&#039;ll only have to add a record in DNS.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: Service&lt;br /&gt;
metadata:&lt;br /&gt;
  name: registry-service&lt;br /&gt;
spec:&lt;br /&gt;
  selector:&lt;br /&gt;
    app: registry&lt;br /&gt;
  ports:&lt;br /&gt;
    - name: registry&lt;br /&gt;
      port: 5000&lt;br /&gt;
      protocol: TCP&lt;br /&gt;
---&lt;br /&gt;
apiVersion: extensions/v1beta1&lt;br /&gt;
kind: Ingress&lt;br /&gt;
metadata:&lt;br /&gt;
  name: registry-ingress&lt;br /&gt;
spec:&lt;br /&gt;
  rules:&lt;br /&gt;
  - host: kuberegistry.sjorsgielen.nl&lt;br /&gt;
    http:&lt;br /&gt;
      paths:&lt;br /&gt;
      - path: /&lt;br /&gt;
        backend:&lt;br /&gt;
          serviceName: registry-service&lt;br /&gt;
          servicePort: 5000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After a minute, as before, https://kuberegistry.sjorsgielen.nl/v2/ (replace with your own hostname) should return 200 OK with a page content of &amp;quot;{}&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To test whether it&#039;s working, let&#039;s take the Ubuntu Docker image and push it onto our registry, as per [https://docs.docker.com/registry/ more or less these instructions]. Here, it&#039;s important that the registry is well-reachable over HTTPS, as Docker will only allow non-SSL HTTP communication over localhost! (Although you could get around this with a &amp;lt;code&amp;gt;kubectl port-forward&amp;lt;/code&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ docker pull ubuntu&lt;br /&gt;
$ docker image tag ubuntu kuberegistry.sjorsgielen.nl/myubuntu&lt;br /&gt;
$ docker push kuberegistry.sjorsgielen.nl/myubuntu&lt;br /&gt;
[...] Retrying in 10 seconds&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That seems to fail. As before, we can figure out the root cause by getting the logs of the Registry pod:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ kubectl logs registry-6bf4dbcfb-9csf5&lt;br /&gt;
[...]&lt;br /&gt;
time=&amp;quot;2019-03-28T21:44:04.465658668Z&amp;quot; level=error msg=&amp;quot;response completed with error&amp;quot; err.code=unknown err.detail=&amp;quot;filesystem: mkdir /var/lib/registry/docker: permission denied&amp;quot; err.message=&amp;quot;unknown error&amp;quot; go.version=go1.11.2 http.request.host=kuberegistry.sjorsgielen.nl http.request.id=c00f2785-30b0-469d-bcff-70a12c0f604b http.request.method=POST http.request.remoteaddr=10.107.160.0 http.request.uri=&amp;quot;/v2/myubuntu/blobs/uploads/&amp;quot; http.request.useragent=&amp;quot;docker/18.06.1-ce go/go1.10.4 git-commit/e68fc7a kernel/4.4.0-112-generic os/linux arch/amd64 UpstreamClient(Docker-Client/18.06.1-ce \(linux\))&amp;quot; http.response.contenttype=&amp;quot;application/json; charset=utf-8&amp;quot; http.response.duration=125.482304ms http.response.status=500 http.response.written=164 vars.name=myubuntu &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A &amp;quot;permission denied&amp;quot; error in &amp;quot;mkdir /var/lib/registry/docker&amp;quot;. Now, we may not know the PersistentVolume behind whatever is mounted in the registry, but we can quickly find out by checking &amp;lt;code&amp;gt;kubectl describe deployment registry&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kubectl get pvc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;kubectl describe pv registry-storage&amp;lt;/code&amp;gt;. In my case, it&#039;s because root squashing is enabled on my NFS mount and the directory is being accessed by root, therefore by an anonymous uid/gid, which doesn&#039;t have rights in the directory. It&#039;s easily fixed and now the push works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ docker push kuberegistry.sjorsgielen.nl/myubuntu&lt;br /&gt;
The push refers to repository [kuberegistry.sjorsgielen.nl/myubuntu]&lt;br /&gt;
b57c79f4a9f3: Pushed &lt;br /&gt;
d60e01b37e74: Pushed &lt;br /&gt;
e45cfbc98a50: Pushed &lt;br /&gt;
762d8e1a6054: Pushed &lt;br /&gt;
latest: digest: sha256:f2557f94cac1cc4509d0483cb6e302da841ecd6f82eb2e91dc7ba6cfd0c580ab size: 1150&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s make our own Docker image, push it, and start it in a Pod!&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example Dockerfile that runs a tiny Perl-based webserver that always responds with its own hostname:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat Dockerfile&lt;br /&gt;
FROM ubuntu:bionic&lt;br /&gt;
&lt;br /&gt;
RUN apt-get update \&lt;br /&gt;
 &amp;amp;&amp;amp; apt-get install -y libmojolicious-perl \&lt;br /&gt;
 &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;br /&gt;
&lt;br /&gt;
# Normally, you&#039;d use COPY here, but I wanted to keep this in one file&lt;br /&gt;
RUN echo &amp;quot;#!/usr/bin/env perl&amp;quot;                       &amp;gt;&amp;gt;/app.pl \&lt;br /&gt;
 &amp;amp;&amp;amp; echo &amp;quot;use Mojolicious::Lite;&amp;quot;                    &amp;gt;&amp;gt;/app.pl \&lt;br /&gt;
 &amp;amp;&amp;amp; echo &amp;quot;get &#039;/&#039; =&amp;gt; sub {&amp;quot;                          &amp;gt;&amp;gt;/app.pl \&lt;br /&gt;
 &amp;amp;&amp;amp; echo &amp;quot;  shift-&amp;gt;render(text =&amp;gt; &#039;Hello World!&#039;); &amp;quot; &amp;gt;&amp;gt;/app.pl \&lt;br /&gt;
 &amp;amp;&amp;amp; echo &amp;quot;};&amp;quot;                                        &amp;gt;&amp;gt;/app.pl \&lt;br /&gt;
 &amp;amp;&amp;amp; echo &amp;quot;app-&amp;gt;start;&amp;quot;                               &amp;gt;&amp;gt;/app.pl \&lt;br /&gt;
 &amp;amp;&amp;amp; chmod +x /app.pl&lt;br /&gt;
&lt;br /&gt;
EXPOSE 3000&lt;br /&gt;
CMD [&amp;quot;/app.pl&amp;quot;, &amp;quot;daemon&amp;quot;, &amp;quot;-l&amp;quot;]&lt;br /&gt;
$ docker build -t kuberegistry.sjorsgielen.nl/helloworld:latest .&lt;br /&gt;
$ docker push kuberegistry.sjorsgielen.nl/helloworld:latest&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At this point, you should be able to write a Deployment, Service and Ingress for this application, using the examples above. &amp;lt;code&amp;gt;kubectl apply&amp;lt;/code&amp;gt; should then start the Pod, Traefik should route the service and whatever host/path you configured should quickly be reachable and respond with &amp;quot;Hello World&amp;quot;. We&#039;ve created our own image and ran it on your cluster!&lt;br /&gt;
&lt;br /&gt;
= To do =&lt;br /&gt;
&lt;br /&gt;
* Kubectl set image for rolling release?&lt;br /&gt;
* Kubernetes Dashboard&lt;br /&gt;
* Attempt Kubernetes upgrade from 1.13 to 1.14&lt;br /&gt;
** https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade-1-14/&lt;br /&gt;
** First, do a normal apt upgrade (the kubernetes packages are held and will not be modified)&lt;br /&gt;
** Then, unhold the kubeadm package on the master, upgrade it to the right version, then re-hold it&lt;br /&gt;
*** This only worked for me after unholding the kubelet and upgrading it as well.&lt;br /&gt;
** On the master, &amp;quot;kubeadm upgrade plan&amp;quot;, then &amp;quot;kubeadm upgrade apply v1.14.x&amp;quot;&lt;br /&gt;
** Upgrade CNI controller by re-running the same &amp;lt;code&amp;gt;kubectl apply&amp;lt;/code&amp;gt; as earlier&lt;br /&gt;
** Unhold the kubelet and kubectl packages on the master, upgrade them and re-hold them, then restart the kubelet&lt;br /&gt;
** For each worker, unhold the kubeadm package, upgrade it, rehold it; cordon (drain) the node; upgrade the node config; install the new kubelet version and restart it; uncordon the node.&lt;br /&gt;
*** Here too, this only worked for me after unholding the kubelet and upgrading it as well.&lt;br /&gt;
* Try getting information on a pod from inside it using the Kubernetes API&lt;br /&gt;
** https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-api/#accessing-the-api-from-a-pod&lt;br /&gt;
** &amp;lt;code&amp;gt;wget --ca-certificate=/run/secrets/kubernetes.io/serviceaccount/ca.crt -qO- https://kubernetes.default.svc.cluster.local/api/&amp;lt;/code&amp;gt;&lt;br /&gt;
** Doesn&#039;t need using the Kubernetes API, can be done using env vars: https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/&lt;br /&gt;
* Play with native cronjobs&lt;br /&gt;
* Play with Statefulset / Daemonset&lt;br /&gt;
* Security Contexts&lt;br /&gt;
** Refuse pods with host networking&lt;br /&gt;
** Refuse PVs with hostpath mounts&lt;br /&gt;
** Allow K8s API communication from a pod, but only to receive information about itself&lt;br /&gt;
** Basically: Make it impossible to root a node even with &amp;quot;broad&amp;quot; privileges on the Kubernetes API server&lt;br /&gt;
** https://kubernetes.io/docs/concepts/policy/pod-security-policy/&lt;br /&gt;
* Limiting pods in memory, CPU, I/O&lt;br /&gt;
* Limiting pods in network communication&lt;br /&gt;
&lt;br /&gt;
[[Categorie:Projects]]&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Projects:Knitting_Machine_to_laptop&amp;diff=310</id>
		<title>Projects:Knitting Machine to laptop</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Projects:Knitting_Machine_to_laptop&amp;diff=310"/>
		<updated>2020-01-21T21:29:26Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: +categorie:projects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;There you go. My objective for the rest of the year is getting my knitting &lt;br /&gt;
machine connected to my laptop (serial to usb), so i can create and replace the &lt;br /&gt;
knitting patterns and create new ones. &lt;br /&gt;
&lt;br /&gt;
Ultimately, the idea is to knit bitmaps.&lt;br /&gt;
&lt;br /&gt;
Model: Brother KH-930e. It works but the electronics are old.&lt;br /&gt;
&lt;br /&gt;
Next steps: &lt;br /&gt;
&lt;br /&gt;
* create the serial to usb cable&lt;br /&gt;
* install stuff (python)&lt;br /&gt;
&lt;br /&gt;
First encounter to the matter was [Electro-knit&lt;br /&gt;
Hacking the Brother KH-930e knitting machine](https://learn.adafruit.com/electroknit/software)&lt;br /&gt;
&lt;br /&gt;
Specs and updates will follow.&lt;br /&gt;
&lt;br /&gt;
W&lt;br /&gt;
&lt;br /&gt;
[[Categorie:Projects]]&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Projects:Anonycaster&amp;diff=309</id>
		<title>Projects:Anonycaster</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Projects:Anonycaster&amp;diff=309"/>
		<updated>2020-01-21T21:29:10Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: +categorie:projects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The page of this project is located on dsprenkels&#039; etherpad: https://etherpad.dsprenkels.com/p/anonycaster&lt;br /&gt;
&lt;br /&gt;
[[Categorie:Projects]]&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Projects:40W_Lasercutter&amp;diff=308</id>
		<title>Projects:40W Lasercutter</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Projects:40W_Lasercutter&amp;diff=308"/>
		<updated>2020-01-21T21:28:47Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: +categorie:projects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Getting started ==&lt;br /&gt;
&lt;br /&gt;
First you need some tools to work with the lasercutter.&lt;br /&gt;
For starters you need a drawing program like [https://inkscape.org/ Inkscape] that is capable of making 2d vector drawings.&lt;br /&gt;
&lt;br /&gt;
The you need [https://visicut.org/ Visicut] as a program to hook the 2 together.&lt;br /&gt;
&lt;br /&gt;
Visicut has a plugin for inkscape so you can press &#039;Extensions &#039;&#039;&#039;&amp;gt;&#039;&#039;&#039; Lasercut path &#039;&#039;&#039;&amp;gt;&#039;&#039;&#039; Open in VisiCut&#039; from Inkscape :)&lt;br /&gt;
&lt;br /&gt;
For Visicut you need a definition file (xml) that contains the laser settings, you can get a rudimentary one here: [[Bestand:hsnmgnlaser.tar.gz]]&lt;br /&gt;
&lt;br /&gt;
Import this using Options -&amp;gt; Lasercutters, it is a good idea to change the job prefix under edit -&amp;gt; job to your own name so you can find your files again.&lt;br /&gt;
&lt;br /&gt;
== Basic operation ==&lt;br /&gt;
* Turn on the machine AND the air assist compressor thingy.&lt;br /&gt;
* Send the file using visicut (i&#039;m not including an entire inkscape/visicut tutorial, plenty online to be found).&lt;br /&gt;
* Turn on the power to the tube with the &#039;Engraving On&#039; button, this only works if all lids are closed.&lt;br /&gt;
* Using the Black&amp;amp;Blue dial (by pressing and rotating it) navigate to Play -&amp;gt; sd/ -&amp;gt; &amp;lt;your prefix&amp;gt;&amp;lt;number&amp;gt;.gcode.&lt;br /&gt;
* The program now starts.&lt;br /&gt;
&lt;br /&gt;
Using the emergency stop you can turn off the entire machine if something bad happens.&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
* Laser power very low/none, check if the &#039;Power regulator&#039; isn&#039;t turned way down, you can check with the &#039;Test&#039; button.&lt;br /&gt;
&lt;br /&gt;
== Materials it can cut/engrave? ==&lt;br /&gt;
* Wood -&amp;gt; most types it can cut and engrave, up to about 5mm.&lt;br /&gt;
* Plastics PLA/ABS/PET/PS are fine, PVC is a REALLY bad idea, cut and engrave up to 5mm.&lt;br /&gt;
* Cardboard/Paper -&amp;gt; see wood, paper can catch fire, which is a lot better with airassist.&lt;br /&gt;
* Metals -&amp;gt; No, but engraving is possible by removing a coating of $material such as paint.&lt;br /&gt;
* Cookies -&amp;gt; Engrave Work fine, still eatable afterwards.&lt;br /&gt;
* Chocolate/Sugary stuff engraves fine.&lt;br /&gt;
* Wet foods -&amp;gt; No.&lt;br /&gt;
&lt;br /&gt;
== History: ==&lt;br /&gt;
&lt;br /&gt;
== Why is there suddenly a lasercutter ==&lt;br /&gt;
It was donated to hackerspace nijmegen by hack42 during the [https://hack42.nl/wiki/Hack42_on_Tour Hack42 on Tour] weekend.&lt;br /&gt;
&lt;br /&gt;
Short backstory: A #42 member saved it from the dumpster, it was &#039;&#039;in working order&#039;&#039; but since this is a crappy old machine it was replaced.&lt;br /&gt;
&lt;br /&gt;
Hack42 already has a very similar machine, rebuild into a nice useable one and no real intrest in haveing two, so it was donated.&lt;br /&gt;
&lt;br /&gt;
== So what does &#039;&#039;in working order&#039;&#039; mean? ==&lt;br /&gt;
That it should work as designed, but the design is horrid.&lt;br /&gt;
&lt;br /&gt;
== What todo do with it ==&lt;br /&gt;
In my (SA007) opinion the steps should be.&lt;br /&gt;
&lt;br /&gt;
* Verify the &#039;&#039;in working order&#039;&#039; claim&lt;br /&gt;
** Make sure the 230V wiring is usable, i&#039;ve seen it with a negligible amount or actual metal in the wires. (Done 12-11-18, wires aren&#039;t great but useable)&lt;br /&gt;
** Connect the ground to the ground terminal of the power connector. (Done 12-11-18, casing and HV supply are now grounded via the power connector)&lt;br /&gt;
** Make the cooling system work (pump, bucket, hoses, see if there are leaks and if the flow is usable). (In progress, there seems to be a blocked pipe) (13-11-18, pipe is unblocked, flow seems good, there is now some cleaner in it to remove crud) (3-12-18, Cooling &#039;done&#039; for now, has 4L cooling circulating nicely)&lt;br /&gt;
** Power the laser up, put some wood under the head and hit test, it should burn the wood, if not, check the alignment. (Very short test done, tube isn&#039;t broken, yay)&lt;br /&gt;
** Optional: Actually hook it up to an old windows pc and install the supplies software. (Board removed, no longer an option).&lt;br /&gt;
* Add loads of safety&#039;s, there aren&#039;t any...&lt;br /&gt;
** Switches on the covers, so it can&#039;t turn on the high voltage/laser if a cover is opened.&lt;br /&gt;
** Emergency stop switch. (SA007: Done 19-11-18)&lt;br /&gt;
** Temperature/water flow sensors are also nice, also ordered.&lt;br /&gt;
** Covers for the bit holes in the casing (most are needed for alignment, but can have removable covers. (13-11-2018 In progress using ducktape&#039;d alufoil, laser can&#039;t cut trough the alufoil) (Done 3-12-18, all relevant slots have been covered)&lt;br /&gt;
** Covers for the slits and gaps in the main/all covers (you should not be able to directly look into the laser bed, with exception of the window. (Done 3-12-18, all relevant slots have been covered)&lt;br /&gt;
* Replace electronics&lt;br /&gt;
** There are several options for the main electronics, such as smoothieboard (or clone), laos and several 3d-printerboard are usable as well. The one at hack42 is a laos one, from experience it is &#039;really meh&#039; so I would pick something else (mks / smoothieboard clone and wires ordered by themba paid by crowdfunding).&lt;br /&gt;
** Replace a lot of the wiring. (12-11-2018 Not as much needed as expected)&lt;br /&gt;
** Probably upgrade the endstops (more range, better accuracy)&lt;br /&gt;
** Add display? (Also ordered, not crowdfunded)&lt;br /&gt;
** Add timer? (laser tubes have a limited life, you want to keep track and/or make people pay for usage)&lt;br /&gt;
* Upgrade cooling&lt;br /&gt;
** Keeping the tube cool is important.&lt;br /&gt;
** A &#039;closed&#039; system is great, a chiller even better.&lt;br /&gt;
** Temps need to be below 30 celcius, 18 is best for long life.&lt;br /&gt;
* Upgrade ventilation&lt;br /&gt;
** The exhaust needs to go outside (maybe via the ventilation slits) (Outside airvent is installed)&lt;br /&gt;
** The fan sucks air from the wrong place (alignment sucks) (Alignment is improved)&lt;br /&gt;
** Maybe a (carbon)filter&lt;br /&gt;
* Added features&lt;br /&gt;
** Air assist is really awesome, cutting results greatly improve, you can build a cheap one from a fridge compressor.&lt;br /&gt;
** Usable switchable light, 25W bulb is useless.&lt;br /&gt;
** Switched/monitored cooling/ventilation, so you don&#039;t run without it.&lt;br /&gt;
** Honeycomb bed&lt;br /&gt;
** Is someone gets creative, a focus adjustment would be nice.&lt;br /&gt;
* Calibrate/adjust the machine&lt;br /&gt;
** Align/clean the mirrors.&lt;br /&gt;
** Calibrate the axes, more accuracy.&lt;br /&gt;
** More to come.&lt;br /&gt;
&lt;br /&gt;
== Sources for information ==&lt;br /&gt;
We have a SF40 laser, very similar to K40, which has been modified extensively.&lt;br /&gt;
&lt;br /&gt;
Ask SA007 on IRC/Mail/Whatever&lt;br /&gt;
&lt;br /&gt;
== Costs? ==&lt;br /&gt;
It will probably be around 200 euro to get parts such as replacement electronics, safety gear etc.&lt;br /&gt;
&lt;br /&gt;
Current costs:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Item                &lt;br /&gt;
! Price (Paid by)  &lt;br /&gt;
|-&lt;br /&gt;
| Lasercutter         &lt;br /&gt;
| Free             &lt;br /&gt;
|-&lt;br /&gt;
| [https://www.aliexpress.com/item/3D-Printer-Parts-Controller-Board-MKS-SBASE-V1-3-32-bit-Open-Source-Platform-Smoothieboard-Compatible/32853515280.html Electronics]         &lt;br /&gt;
| 40,34 (Themba)   &lt;br /&gt;
|-&lt;br /&gt;
| [https://www.aliexpress.com/item/3d-printing-touch-screen-RepRap-controller-panel-MKS-TFT28-V1-2-display-color-TFT-support-WIFI/32808421021.html Display]             &lt;br /&gt;
| 24,43 (Themba)   &lt;br /&gt;
|-&lt;br /&gt;
| EStop               &lt;br /&gt;
| Free             &lt;br /&gt;
|-&lt;br /&gt;
| Shielding materials &lt;br /&gt;
| 11,50 (SA007)    &lt;br /&gt;
|-&lt;br /&gt;
| [https://www.groku.de/fileadmin/web/pdf/EZE5600_en.pdf Bucket] &lt;br /&gt;
| Free &lt;br /&gt;
|-&lt;br /&gt;
| Subtotal            &lt;br /&gt;
| 76,27            &lt;br /&gt;
|-&lt;br /&gt;
| Coolant             &lt;br /&gt;
| 8,70 (SA007)     &lt;br /&gt;
|-&lt;br /&gt;
| Exhaust hose        &lt;br /&gt;
| 6,95 (SA007)     &lt;br /&gt;
|-&lt;br /&gt;
| Wiring/plugs        &lt;br /&gt;
| 5ish (SA007)     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Currently funded: 50 euro.&lt;br /&gt;
&lt;br /&gt;
There is a donation box on top of the lasercutter.&lt;br /&gt;
&lt;br /&gt;
== Looking for materials: ==&lt;br /&gt;
* Fridge compressor (anyone an old fridge thats going to be dumped?)&lt;br /&gt;
* Watercooling items (radiator+fans, hoses, pump etc)&lt;br /&gt;
&lt;br /&gt;
== Small tasks list: ==&lt;br /&gt;
* Add/wire interlock switches.&lt;br /&gt;
* Extend Y motor wire&lt;br /&gt;
* Change plugs on endstops.&lt;br /&gt;
* Find / connect PWM signal.&lt;br /&gt;
* Hook pcb up to power.&lt;br /&gt;
* Mount temperature sensor somewhere properly to measure coolant temp.&lt;br /&gt;
* Fix/connect led strip and find/mount a psu for them.&lt;br /&gt;
&lt;br /&gt;
== More specs? ==&lt;br /&gt;
It is &#039;40W&#039; laser tube, more realisticly it is 30-35W it runs on really high voltage (~2kV).&lt;br /&gt;
&lt;br /&gt;
It is a CO2 laser, they run at 10600nm, which is really really infrared.&lt;br /&gt;
&lt;br /&gt;
== Firmware == &lt;br /&gt;
The laser is currently running smoothieware version:&lt;br /&gt;
edge a06bb6a Nov 30 2018 20:45:58.&lt;br /&gt;
This is a cnc specific firmware version. &lt;br /&gt;
The current configuration file is attached below.&lt;br /&gt;
&lt;br /&gt;
To update firmware place the firmware file in the root of the sd card and name the file firmware.bin. Then power up the machine whith the card in the slot (reboot is needed). If the firmware update is succesfull the file will be renamed to firmware.cur. The firmware expects to find a config.txt also in the root of the sd card.&lt;br /&gt;
&lt;br /&gt;
=== config.txt ===&lt;br /&gt;
 # Smoothieboard configuration file, see http://smoothieware.org/configuring-smoothie&lt;br /&gt;
 # NOTE Lines must not exceed 132 characters, and &#039;#&#039; characters mean what follows is ignored&lt;br /&gt;
 ## Robot module configurations : general handling of movement G-codes and slicing into moves&lt;br /&gt;
 &lt;br /&gt;
 # this turn G28 back to what people from 3d printing are used to&lt;br /&gt;
 grbl_mode                                    false&lt;br /&gt;
 &lt;br /&gt;
 # Basic motion configuration&lt;br /&gt;
 default_feed_rate                            2000             # Default speed (mm/minute) for G1/G2/G3 moves&lt;br /&gt;
 default_seek_rate                            2000             # Default speed (mm/minute) for G0 moves&lt;br /&gt;
 mm_per_arc_segment                           0.0              # Fixed length for line segments that divide arcs, 0 to disable&lt;br /&gt;
 #mm_per_line_segment                         5                # Cut lines into segments this size&lt;br /&gt;
 mm_max_arc_error                             0.01             # The maximum error for line segments that divide arcs 0 to disable&lt;br /&gt;
                                                               # note it is invalid for both the above be 0&lt;br /&gt;
                                                               # if both are used, will use largest segment length based on radius&lt;br /&gt;
 &lt;br /&gt;
 # Arm solution configuration : Cartesian robot. Translates mm positions into stepper positions&lt;br /&gt;
 # See http://smoothieware.org/stepper-motors&lt;br /&gt;
 alpha_steps_per_mm                           158               # Steps per mm for alpha ( X ) stepper&lt;br /&gt;
 beta_steps_per_mm                            158               # Steps per mm for beta ( Y ) stepper&lt;br /&gt;
 gamma_steps_per_mm                           1600             # Steps per mm for gamma ( Z ) stepper&lt;br /&gt;
 &lt;br /&gt;
 # Planner module configuration : Look-ahead and acceleration configuration&lt;br /&gt;
 # See http://smoothieware.org/motion-control&lt;br /&gt;
 acceleration                                 3000             # Acceleration in mm/second/second.&lt;br /&gt;
 #z_acceleration                              500              # Acceleration for Z only moves in mm/s^2, 0 uses acceleration which is the default. DO NOT SET ON A DELTA&lt;br /&gt;
 junction_deviation                           0.05             # See http://smoothieware.org/motion-control#junction-deviation&lt;br /&gt;
 #z_junction_deviation                        0.0              # For Z only moves, -1 uses junction_deviation, zero disables junction_deviation on z moves DO NOT SET ON A DELTA&lt;br /&gt;
 &lt;br /&gt;
 # Cartesian axis speed limits&lt;br /&gt;
 x_axis_max_speed                             30000            # Maximum speed in mm/min&lt;br /&gt;
 y_axis_max_speed                             30000            # Maximum speed in mm/min&lt;br /&gt;
 z_axis_max_speed                             300              # Maximum speed in mm/min&lt;br /&gt;
 &lt;br /&gt;
 # Stepper module configuration &lt;br /&gt;
 # Pins are defined as  ports, and pin numbers, appending &amp;quot;!&amp;quot; to the number will invert a pin&lt;br /&gt;
 # See http://smoothieware.org/pin-configuration and http://smoothieware.org/pinout&lt;br /&gt;
 alpha_step_pin                               2.0              # Pin for alpha stepper step signal&lt;br /&gt;
 alpha_dir_pin                                0.5!              # Pin for alpha stepper direction, add &#039;!&#039; to reverse direction&lt;br /&gt;
 alpha_en_pin                                 0.4              # Pin for alpha enable pin&lt;br /&gt;
 alpha_current                                0.5              # X stepper motor current&lt;br /&gt;
 alpha_max_rate                               20000.0          # Maximum rate in mm/min&lt;br /&gt;
 &lt;br /&gt;
 beta_step_pin                                2.1              # Pin for beta stepper step signal&lt;br /&gt;
 beta_dir_pin                                 0.11             # Pin for beta stepper direction, add &#039;!&#039; to reverse direction&lt;br /&gt;
 beta_en_pin                                  0.10             # Pin for beta enable&lt;br /&gt;
 beta_current                                 0.5              # Y stepper motor current&lt;br /&gt;
 beta_max_rate                                20000.0          # Maxmimum rate in mm/min&lt;br /&gt;
 &lt;br /&gt;
 gamma_step_pin                               2.2              # Pin for gamma stepper step signal&lt;br /&gt;
 gamma_dir_pin                                0.20             # Pin for gamma stepper direction, add &#039;!&#039; to reverse direction&lt;br /&gt;
 gamma_en_pin                                 0.19             # Pin for gamma enable&lt;br /&gt;
 gamma_current                                0.5              # Z stepper motor current&lt;br /&gt;
 gamma_max_rate                               300.0            # Maximum rate in mm/min&lt;br /&gt;
 &lt;br /&gt;
 ## Extruder module configuration&lt;br /&gt;
 # See http://smoothieware.org/extruder&lt;br /&gt;
 extruder.hotend.enable                          false          # Whether to activate the extruder module at all. All configuration is ignored if false&lt;br /&gt;
 extruder.hotend.steps_per_mm                    140           # Steps per mm for extruder stepper&lt;br /&gt;
 extruder.hotend.default_feed_rate               600           # Default rate ( mm/minute ) for moves where only the extruder moves&lt;br /&gt;
 extruder.hotend.acceleration                    500           # Acceleration for the stepper motor mm/sec²&lt;br /&gt;
 extruder.hotend.max_speed                       50            # Maximum speed in mm/s&lt;br /&gt;
 &lt;br /&gt;
 extruder.hotend.step_pin                        2.3           # Pin for extruder step signal&lt;br /&gt;
 extruder.hotend.dir_pin                         0.22          # Pin for extruder dir signal ( add &#039;!&#039; to reverse direction )&lt;br /&gt;
 extruder.hotend.en_pin                          0.21          # Pin for extruder enable signal&lt;br /&gt;
 &lt;br /&gt;
 # Extruder offset&lt;br /&gt;
 #extruder.hotend.x_offset                        0            # X offset from origin in mm&lt;br /&gt;
 #extruder.hotend.y_offset                        0            # Y offset from origin in mm&lt;br /&gt;
 #extruder.hotend.z_offset                        0            # Z offset from origin in mm&lt;br /&gt;
 &lt;br /&gt;
 # Firmware retract settings when using G10/G11, these are the defaults if not defined, must be defined for each extruder if not using the defaults&lt;br /&gt;
 #extruder.hotend.retract_length                  3            # Retract length in mm&lt;br /&gt;
 #extruder.hotend.retract_feedrate                45           # Retract feedrate in mm/sec&lt;br /&gt;
 #extruder.hotend.retract_recover_length          0            # Additional length for recover&lt;br /&gt;
 #extruder.hotend.retract_recover_feedrate        8            # Recover feedrate in mm/sec (should be less than retract feedrate)&lt;br /&gt;
 #extruder.hotend.retract_zlift_length            0            # Z-lift on retract in mm, 0 disables&lt;br /&gt;
 #extruder.hotend.retract_zlift_feedrate          6000         # Z-lift feedrate in mm/min (Note mm/min NOT mm/sec)&lt;br /&gt;
 &lt;br /&gt;
 delta_current                                    0.5          # First extruder stepper motor current&lt;br /&gt;
 &lt;br /&gt;
 # Second extruder module configuration&lt;br /&gt;
 #extruder.hotend2.enable                         true         # Whether to activate the extruder module at all. All configuration is ignored if false&lt;br /&gt;
 #extruder.hotend2.steps_per_mm                   140          # Steps per mm for extruder stepper&lt;br /&gt;
 #extruder.hotend2.default_feed_rate              600          # Default rate ( mm/minute ) for moves where only the extruder moves&lt;br /&gt;
 #extruder.hotend2.acceleration                   500          # Acceleration for the stepper motor, as of 0.6, arbitrary ratio&lt;br /&gt;
 #extruder.hotend2.max_speed                      50           # mm/s&lt;br /&gt;
 &lt;br /&gt;
 #extruder.hotend2.step_pin                       2.8          # Pin for extruder step signal&lt;br /&gt;
 #extruder.hotend2.dir_pin                        2.13         # Pin for extruder dir signal ( add &#039;!&#039; to reverse direction )&lt;br /&gt;
 #extruder.hotend2.en_pin                         4.29         # Pin for extruder enable signal&lt;br /&gt;
 &lt;br /&gt;
 #extruder.hotend2.x_offset                       0            # x offset from origin in mm&lt;br /&gt;
 #extruder.hotend2.y_offset                       25.0         # y offset from origin in mm&lt;br /&gt;
 #extruder.hotend2.z_offset                       0            # z offset from origin in mm&lt;br /&gt;
 &lt;br /&gt;
 #epsilon_current                                 1.5          # Second extruder stepper motor current&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ## Laser module configuration&lt;br /&gt;
 # See http://smoothieware.org/laser&lt;br /&gt;
 laser_module_enable                           true            # Whether to activate the laser module at all&lt;br /&gt;
 laser_module_pwm_pin                          2.5             # This pin will be PWMed to control the laser. &lt;br /&gt;
                                                               # Only pins 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 1.18, 1.20, 1.21, 1.23, 1.24, 1.26, 3.25 and 3.26&lt;br /&gt;
                                                               # can be used since laser requires hardware PWM, see http://smoothieware.org/pinout&lt;br /&gt;
 #laser_module_ttl_pin 	                      1.30            # This pin turns on when the laser turns on, and off when the laser turns off.&lt;br /&gt;
 #laser_module_maximum_power                   1.0             # This is the maximum duty cycle that will be applied to the laser&lt;br /&gt;
 #laser_module_minimum_power                   0.0             # This is a value just below the minimum duty cycle that keeps the laser&lt;br /&gt;
                                                               # active without actually burning.&lt;br /&gt;
 #laser_module_default_power                   0.8             # This is the default laser power that will be used for cuts if a power has not been specified.  The value is a scale between&lt;br /&gt;
                                                               # the maximum and minimum power levels specified above&lt;br /&gt;
 laser_module_pwm_period                      2              # This sets the pwm frequency as the period in microseconds&lt;br /&gt;
 &lt;br /&gt;
 ## Temperature control configuration&lt;br /&gt;
 # See http://smoothieware.org/temperaturecontrol&lt;br /&gt;
 &lt;br /&gt;
 # First hotend configuration&lt;br /&gt;
 temperature_control.hotend.enable            false             # Whether to activate this ( &amp;quot;hotend&amp;quot; ) module at all.&lt;br /&gt;
 temperature_control.hotend.thermistor_pin    0.23             # Pin for the thermistor to read&lt;br /&gt;
 temperature_control.hotend.heater_pin        2.7              # Pin that controls the heater, set to nc if a readonly thermistor is being defined&lt;br /&gt;
 temperature_control.hotend.thermistor        EPCOS100K        # See http://smoothieware.org/temperaturecontrol#toc5&lt;br /&gt;
 #temperature_control.hotend.beta             4066             # Or set the beta value&lt;br /&gt;
 temperature_control.hotend.set_m_code        104              # M-code to set the temperature for this module&lt;br /&gt;
 temperature_control.hotend.set_and_wait_m_code 109            # M-code to set-and-wait for this module&lt;br /&gt;
 temperature_control.hotend.designator        T                # Designator letter for this module&lt;br /&gt;
 #temperature_control.hotend.max_temp         300              # Set maximum temperature - Will prevent heating above 300 by default&lt;br /&gt;
 #temperature_control.hotend.min_temp         0                # Set minimum temperature - Will prevent heating below if set&lt;br /&gt;
 &lt;br /&gt;
 # Safety control is enabled by default and can be overidden here, the values show the defaults&lt;br /&gt;
 # See http://smoothieware.org/temperaturecontrol#runaway&lt;br /&gt;
 #temperature_control.hotend.runaway_heating_timeout      900  # How long it can take to heat up, max is 2040 seconds.&lt;br /&gt;
 #temperature_control.hotend.runaway_cooling_timeout        0  # How long it can take to cool down if temp is set lower, max is 2040 seconds&lt;br /&gt;
 #temperature_control.hotend.runaway_range                20   # How far from the set temperature it can wander, max setting is 63°C&lt;br /&gt;
 &lt;br /&gt;
 # PID configuration &lt;br /&gt;
 # See http://smoothieware.org/temperaturecontrol#pid&lt;br /&gt;
 #temperature_control.hotend.p_factor         13.7             # P ( proportional ) factor&lt;br /&gt;
 #temperature_control.hotend.i_factor         0.097            # I ( integral ) factor&lt;br /&gt;
 #temperature_control.hotend.d_factor         24               # D ( derivative ) factor&lt;br /&gt;
 &lt;br /&gt;
 #temperature_control.hotend.max_pwm          64               # Max pwm, 64 is a good value if driving a 12v resistor with 24v.&lt;br /&gt;
 &lt;br /&gt;
 # Second hotend configuration&lt;br /&gt;
 #temperature_control.hotend2.enable            true           # Whether to activate this ( &amp;quot;hotend&amp;quot; ) module at all.&lt;br /&gt;
 #temperature_control.hotend2.thermistor_pin    0.25           # Pin for the thermistor to read&lt;br /&gt;
 #temperature_control.hotend2.heater_pin        1.23           # Pin that controls the heater&lt;br /&gt;
 #temperature_control.hotend2.thermistor        EPCOS100K      # See http://smoothieware.org/temperaturecontrol#thermistor&lt;br /&gt;
 ##temperature_control.hotend2.beta             4066           # or set the beta value&lt;br /&gt;
 #temperature_control.hotend2.set_m_code        104            # M-code to set the temperature for this module&lt;br /&gt;
 #temperature_control.hotend2.set_and_wait_m_code 109          # M-code to set-and-wait for this module&lt;br /&gt;
 #temperature_control.hotend2.designator        T1             # Designator letter for this module&lt;br /&gt;
 &lt;br /&gt;
 #temperature_control.hotend2.p_factor          13.7           # P ( proportional ) factor&lt;br /&gt;
 #temperature_control.hotend2.i_factor          0.097          # I ( integral ) factor&lt;br /&gt;
 #temperature_control.hotend2.d_factor          24             # D ( derivative ) factor&lt;br /&gt;
 &lt;br /&gt;
 #temperature_control.hotend2.max_pwm          64              # Max pwm, 64 is a good value if driving a 12v resistor with 24v.&lt;br /&gt;
 &lt;br /&gt;
 temperature_control.bed.enable               false             # Whether to activate this ( &amp;quot;hotend&amp;quot; ) module at all.&lt;br /&gt;
 temperature_control.bed.thermistor_pin       0.24             # Pin for the thermistor to read&lt;br /&gt;
 temperature_control.bed.heater_pin           2.5              # Pin that controls the heater&lt;br /&gt;
 temperature_control.bed.thermistor           Honeywell100K    # See http://smoothieware.org/temperaturecontrol#thermistor&lt;br /&gt;
 #temperature_control.bed.beta                3974             # Or set the beta value&lt;br /&gt;
 temperature_control.bed.set_m_code           140              # M-code to set the temperature for this module&lt;br /&gt;
 temperature_control.bed.set_and_wait_m_code  190              # M-code to set-and-wait for this module&lt;br /&gt;
 temperature_control.bed.designator           B                # Designator letter for this module&lt;br /&gt;
 &lt;br /&gt;
 # Bang-bang ( simplified ) control&lt;br /&gt;
 # See http://smoothieware.org/temperaturecontrol#bang-bang&lt;br /&gt;
 #temperature_control.bed.bang_bang           false            # Set to true to use bang bang control rather than PID&lt;br /&gt;
 #temperature_control.bed.hysteresis          2.0              # Set to the temperature in degrees C to use as hysteresis&lt;br /&gt;
 &lt;br /&gt;
 ## Switch modules&lt;br /&gt;
 # See http://smoothieware.org/switch&lt;br /&gt;
 &lt;br /&gt;
 # Switch module for fan control&lt;br /&gt;
 switch.fan.enable                            false             # Enable this module&lt;br /&gt;
 switch.fan.input_on_command                  M106             # Command that will turn this switch on&lt;br /&gt;
 switch.fan.input_off_command                 M107             # Command that will turn this switch off&lt;br /&gt;
 switch.fan.output_pin                        2.6              # Pin this module controls&lt;br /&gt;
 switch.fan.output_type                       pwm              # PWM output settable with S parameter in the input_on_comand&lt;br /&gt;
 #switch.fan.max_pwm                          255              # Set max pwm for the pin default is 255&lt;br /&gt;
 &lt;br /&gt;
 #switch.misc.enable                          true             # Enable this module&lt;br /&gt;
 #switch.misc.input_on_command                M42              # Command that will turn this switch on&lt;br /&gt;
 #switch.misc.input_off_command               M43              # Command that will turn this switch off&lt;br /&gt;
 #switch.misc.output_pin                      2.4              # Pin this module controls&lt;br /&gt;
 #switch.misc.output_type                     digital          # Digital means this is just an on or off pin&lt;br /&gt;
 &lt;br /&gt;
 ## Temperatureswitch&lt;br /&gt;
 # See http://smoothieware.org/temperatureswitch&lt;br /&gt;
 # Automatically toggle a switch at a specified temperature. Different ones of these may be defined to monitor different temperatures and switch different swithxes&lt;br /&gt;
 # Useful to turn on a fan or water pump to cool the hotend&lt;br /&gt;
 #temperatureswitch.hotend.enable              true            #&lt;br /&gt;
 #temperatureswitch.hotend.designator          T               # first character of the temperature control designator to use as the temperature sensor to monitor&lt;br /&gt;
 #temperatureswitch.hotend.switch              misc            # select which switch to use, matches the name of the defined switch&lt;br /&gt;
 #temperatureswitch.hotend.threshold_temp      60.0            # temperature to turn on (if rising) or off the switch&lt;br /&gt;
 #temperatureswitch.hotend.heatup_poll         15              # poll heatup at 15 sec intervals&lt;br /&gt;
 #temperatureswitch.hotend.cooldown_poll       60              # poll cooldown at 60 sec intervals&lt;br /&gt;
 &lt;br /&gt;
 ## Endstops&lt;br /&gt;
 # See http://smoothieware.org/endstops&lt;br /&gt;
 endstops_enable                              true             # The endstop module is enabled by default and can be disabled here&lt;br /&gt;
 #corexy_homing                               false            # Set to true if homing on a hbot or corexy&lt;br /&gt;
 alpha_min_endstop                            1.24^            # Pin to read min endstop, add a ! to invert if endstop is NO connected to ground&lt;br /&gt;
 #alpha_max_endstop                           1.25^            # Pin to read max endstop, uncomment this and comment the above if using max endstops&lt;br /&gt;
 alpha_homing_direction                       home_to_min      # Or set to home_to_max and set alpha_max and uncomment the alpha_max_endstop&lt;br /&gt;
 alpha_min                                    0                # This gets loaded as the current position after homing when home_to_min is set&lt;br /&gt;
 alpha_max                                    200              # This gets loaded as the current position after homing when home_to_max is set&lt;br /&gt;
 beta_min_endstop                             1.26^            # Pin to read min endstop, add a ! to invert if endstop is NO connected to ground&lt;br /&gt;
 #beta_max_endstop                            1.27^            # Pin to read max endstop, uncomment this and comment the above if using max endstops&lt;br /&gt;
 beta_homing_direction                        home_to_min      # Or set to home_to_max and set alpha_max and uncomment the alpha_max_endstop&lt;br /&gt;
 beta_min                                     0                # This gets loaded as the current position after homing when home_to_min is set&lt;br /&gt;
 beta_max                                     200              # This gets loaded as the current position after homing when home_to_max is set&lt;br /&gt;
 gamma_min_endstop                            1.28^            # Pin to read min endstop, add a ! to invert if endstop is NO connected to ground&lt;br /&gt;
 #gamma_max_endstop                           1.29^            # Pin to read max endstop, uncomment this and comment the above if using max endstops&lt;br /&gt;
 gamma_homing_direction                       home_to_min      # Or set to home_to_max and set alpha_max and uncomment the alpha_max_endstop&lt;br /&gt;
 gamma_min                                    0                # This gets loaded as the current position after homing when home_to_min is set&lt;br /&gt;
 gamma_max                                    200              # This gets loaded as the current position after homing when home_to_max is set&lt;br /&gt;
 &lt;br /&gt;
 alpha_max_travel                             500              # Max travel in mm for alpha/X axis when homing&lt;br /&gt;
 beta_max_travel                              500              # Max travel in mm for beta/Y axis when homing&lt;br /&gt;
 gamma_max_travel                             500              # Max travel in mm for gamma/Z axis when homing&lt;br /&gt;
 &lt;br /&gt;
 # Optional enable limit switches, actions will stop if any enabled limit switch is triggered&lt;br /&gt;
 #alpha_limit_enable                          false            # Set to true to enable X min and max limit switches&lt;br /&gt;
 #beta_limit_enable                           false            # Set to true to enable Y min and max limit switches&lt;br /&gt;
 #gamma_limit_enable                          false            # Set to true to enable Z min and max limit switches&lt;br /&gt;
 &lt;br /&gt;
 # Endstops home at their fast feedrate first, then once the endstop is found they home again at their slow feedrate for accuracy&lt;br /&gt;
 alpha_fast_homing_rate_mm_s                  50               # Alpha/X fast homing feedrate in mm/second&lt;br /&gt;
 alpha_slow_homing_rate_mm_s                  25               # Alpha/X slow homing feedrate in mm/second&lt;br /&gt;
 beta_fast_homing_rate_mm_s                   50               # Beta/Y  fast homing feedrate in mm/second&lt;br /&gt;
 beta_slow_homing_rate_mm_s                   25               # Beta/Y  slow homing feedrate in mm/second&lt;br /&gt;
 gamma_fast_homing_rate_mm_s                  4                # Gamma/Z fast homing feedrate in mm/second&lt;br /&gt;
 gamma_slow_homing_rate_mm_s                  2                # Gamma/Z slow homing feedrate in mm/second&lt;br /&gt;
 &lt;br /&gt;
 alpha_homing_retract_mm                      5                # Distance to retract from the endstop after it is hit for alpha/X&lt;br /&gt;
 beta_homing_retract_mm                       5                # Distance to retract from the endstop after it is hit for beta/Y&lt;br /&gt;
 gamma_homing_retract_mm                      1                # Distance to retract from the endstop after it is hit for gamma/Z&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 # Optional enable limit switches, actions will stop if any enabled limit switch is triggered (all are set for delta)&lt;br /&gt;
 alpha_limit_enable                          true            # Set to true to enable X min and max limit switches&lt;br /&gt;
 beta_limit_enable                           true            # Set to true to enable Y min and max limit switches&lt;br /&gt;
 #gamma_limit_enable                          false            # Set to true to enable Z min and max limit switches&lt;br /&gt;
 &lt;br /&gt;
 # Optional order in which axis will home, default is they all home at the same time,&lt;br /&gt;
 # If this is set it will force each axis to home one at a time in the specified order&lt;br /&gt;
 #homing_order                                 XYZ              # X axis followed by Y then Z last&lt;br /&gt;
 #move_to_origin_after_home                    false            # Move XY to 0,0 after homing&lt;br /&gt;
 #endstop_debounce_count                       100              # Uncomment if you get noise on your endstops, default is 100&lt;br /&gt;
 #endstop_debounce_ms                          1                # Uncomment if you get noise on your endstops, default is 1 millisecond debounce&lt;br /&gt;
 #home_z_first                                 true             # Uncomment and set to true to home the Z first, otherwise Z homes after XY&lt;br /&gt;
 &lt;br /&gt;
 # End of endstop config&lt;br /&gt;
 # Delete the above endstop section and uncomment next line and copy and edit Snippets/abc-endstop.config file to enable endstops for ABC axis&lt;br /&gt;
 #include abc-endstop.config&lt;br /&gt;
 &lt;br /&gt;
 ## Z-probe&lt;br /&gt;
 # See http://smoothieware.org/zprobe&lt;br /&gt;
 zprobe.enable                                false           # Set to true to enable a zprobe&lt;br /&gt;
 zprobe.probe_pin                             1.28!^          # Pin probe is attached to, if NC remove the !&lt;br /&gt;
 zprobe.slow_feedrate                         5               # Mm/sec probe feed rate&lt;br /&gt;
 #zprobe.debounce_ms                          1               # Set if noisy&lt;br /&gt;
 zprobe.fast_feedrate                         100             # Move feedrate mm/sec&lt;br /&gt;
 zprobe.probe_height                          5               # How much above bed to start probe&lt;br /&gt;
 #gamma_min_endstop                           nc              # Normally 1.28. Change to nc to prevent conflict,&lt;br /&gt;
 &lt;br /&gt;
 # Levelling strategy&lt;br /&gt;
 # Example for 3-point levelling strategy, see wiki documentation for other strategies&lt;br /&gt;
 #leveling-strategy.three-point-leveling.enable         true        # a leveling strategy that probes three points to define a plane and keeps the Z parallel to that plane&lt;br /&gt;
 #leveling-strategy.three-point-leveling.point1         100.0,0.0   # the first probe point (x,y) optional may be defined with M557&lt;br /&gt;
 #leveling-strategy.three-point-leveling.point2         200.0,200.0 # the second probe point (x,y)&lt;br /&gt;
 #leveling-strategy.three-point-leveling.point3         0.0,200.0   # the third probe point (x,y)&lt;br /&gt;
 #leveling-strategy.three-point-leveling.home_first     true        # home the XY axis before probing&lt;br /&gt;
 #leveling-strategy.three-point-leveling.tolerance      0.03        # the probe tolerance in mm, anything less that this will be ignored, default is 0.03mm&lt;br /&gt;
 #leveling-strategy.three-point-leveling.probe_offsets  0,0,0       # the probe offsets from nozzle, must be x,y,z, default is no offset&lt;br /&gt;
 #leveling-strategy.three-point-leveling.save_plane     false       # set to true to allow the bed plane to be saved with M500 default is false&lt;br /&gt;
 &lt;br /&gt;
 ## Panel&lt;br /&gt;
 # See http://smoothieware.org/panel&lt;br /&gt;
 # Please find your panel on the wiki and copy/paste the right configuration here&lt;br /&gt;
 panel.enable                                 true             # Set to true to enable the panel code&lt;br /&gt;
 &lt;br /&gt;
 # Example for reprap discount GLCD&lt;br /&gt;
 # on glcd EXP1 is to left and EXP2 is to right, pin 1 is bottom left, pin 2 is top left etc.&lt;br /&gt;
 # +5v is EXP1 pin 10, Gnd is EXP1 pin 9&lt;br /&gt;
 panel.lcd                                   reprap_discount_glcd     #&lt;br /&gt;
 #panel.spi_channel                           0                 # SPI channel to use  ; GLCD EXP1 Pins 3,5 (MOSI, SCLK)&lt;br /&gt;
 #panel.spi_cs_pin                            0.16              # SPI chip select     ; GLCD EXP1 Pin 4&lt;br /&gt;
 panel.encoder_a_pin                         3.25!^            # Encoder pin         ; GLCD EXP2 Pin 3&lt;br /&gt;
 panel.encoder_b_pin                         3.26!^            # Encoder pin         ; GLCD EXP2 Pin 5&lt;br /&gt;
 panel.encoder_resolution                    4&lt;br /&gt;
 panel.click_button_pin                      1.30!^            # Click button        ; GLCD EXP1 Pin 2&lt;br /&gt;
 panel.buzz_pin                              1.31              # Pin for buzzer      ; GLCD EXP1 Pin 1&lt;br /&gt;
 #panel.back_button_pin                       2.11!^            # Back button         ; GLCD EXP2 Pin 8&lt;br /&gt;
 &lt;br /&gt;
 panel.menu_offset                            0                 # Some panels will need 1 here&lt;br /&gt;
 &lt;br /&gt;
 panel.alpha_jog_feedrate                     6000              # X jogging feedrate in mm/min&lt;br /&gt;
 panel.beta_jog_feedrate                      6000              # Y jogging feedrate in mm/min&lt;br /&gt;
 panel.gamma_jog_feedrate                     200               # Z jogging feedrate in mm/min&lt;br /&gt;
 &lt;br /&gt;
 panel.hotend_temperature                     185               # Temp to set hotend when preheat is selected&lt;br /&gt;
 panel.bed_temperature                        60                # Temp to set bed when preheat is selected&lt;br /&gt;
 &lt;br /&gt;
 ## Custom menus : Example of a custom menu entry, which will show up in the Custom entry.&lt;br /&gt;
 # NOTE _ gets converted to space in the menu and commands, | is used to separate multiple commands&lt;br /&gt;
 custom_menu.power_on.enable                true              #&lt;br /&gt;
 custom_menu.power_on.name                  Power_on          #&lt;br /&gt;
 custom_menu.power_on.command               M80               #&lt;br /&gt;
 &lt;br /&gt;
 custom_menu.power_off.enable               true              #&lt;br /&gt;
 custom_menu.power_off.name                 Power_off         #&lt;br /&gt;
 custom_menu.power_off.command              M81               #&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ## Network settings&lt;br /&gt;
 # See http://smoothieware.org/network&lt;br /&gt;
 network.enable                               true             # Enable the ethernet network services&lt;br /&gt;
 network.webserver.enable                     true             # Enable the webserver&lt;br /&gt;
 network.telnet.enable                        true             # Enable the telnet server&lt;br /&gt;
 network.ip_address                           auto             # Use dhcp to get ip address&lt;br /&gt;
 # Uncomment the 3 below to manually setup ip address&lt;br /&gt;
 #network.ip_address                           192.168.3.222   # The IP address&lt;br /&gt;
 #network.ip_mask                              255.255.255.0   # The ip mask&lt;br /&gt;
 #network.ip_gateway                           192.168.3.1     # The gateway address&lt;br /&gt;
 #network.mac_override                         xx.xx.xx.xx.xx.xx  # Override the mac address, only do this if you have a conflict&lt;br /&gt;
 &lt;br /&gt;
 ## System configuration&lt;br /&gt;
 # Serial communications configuration ( baud rate defaults to 9600 if undefined )&lt;br /&gt;
 # For communication over the UART port, *not* the USB/Serial port&lt;br /&gt;
 uart0.baud_rate                              115200           # Baud rate for the default hardware ( UART ) serial port&lt;br /&gt;
 &lt;br /&gt;
 second_usb_serial_enable                     false            # This enables a second USB serial port&lt;br /&gt;
 #leds_disable                                true             # Disable using leds after config loaded&lt;br /&gt;
 #play_led_disable                            true             # Disable the play led&lt;br /&gt;
 &lt;br /&gt;
 # Kill button maybe assigned to a different pin, set to the onboard pin by default&lt;br /&gt;
 # See http://smoothieware.org/killbutton&lt;br /&gt;
 kill_button_enable                           true             # Set to true to enable a kill button&lt;br /&gt;
 kill_button_pin                              2.12             # Kill button pin. default is same as pause button 2.12 (2.11 is another good choice)&lt;br /&gt;
 &lt;br /&gt;
 #msd_disable                                 false            # Disable the MSD (USB SDCARD), see http://smoothieware.org/troubleshooting#disable-msd&lt;br /&gt;
 #dfu_enable                                  false            # For linux developers, set to true to enable DFU&lt;br /&gt;
 &lt;br /&gt;
 # Only needed on a smoothieboard&lt;br /&gt;
 # See http://smoothieware.org/currentcontrol&lt;br /&gt;
 currentcontrol_module_enable                 true             # Control stepper motor current via the configuration file&lt;br /&gt;
&lt;br /&gt;
[[Categorie:Projects]]&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
	<entry>
		<id>https://hackerspacenijmegen.nl/index.php?title=Gebruiker:Mrngm&amp;diff=307</id>
		<title>Gebruiker:Mrngm</title>
		<link rel="alternate" type="text/html" href="https://hackerspacenijmegen.nl/index.php?title=Gebruiker:Mrngm&amp;diff=307"/>
		<updated>2020-01-21T21:06:33Z</updated>

		<summary type="html">&lt;p&gt;Mrngm: Nieuwe pagina aangemaakt met &amp;#039;You may know me from [https://wiki.sha2017.org/w/User:Sha2017.org-mrngm sha2017], ohm2013, or har2009, [https://twitter.com/mrngm this blue bird], or perhaps my non...&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;You may know me from [https://wiki.sha2017.org/w/User:Sha2017.org-mrngm sha2017], ohm2013, or har2009, [https://twitter.com/mrngm this blue bird], or perhaps my non-exhaustive [https://moeilijklastig.nl/verloopjes/ list of funky adapters].&lt;/div&gt;</summary>
		<author><name>Mrngm</name></author>
	</entry>
</feed>