Migrationsstrategie vom alten QNAP TS-412 zum GlusterFS

Nachdem ich jetzt mehrere Wochen mit dem GlusterFS rumgespielt habe, und auch reichlich Erkenntnise sammeln konte; wird es Zeit die Daten von meinem alten QNAP TS-412 zum GlusterFS zu verschieben.

Das klingt auf den ersten Moment sehr einfach, ist aber durch einige Randbedingungen nicht ganz so trivial, wie es klingt.

Vorraussetzungen/Bedarfsbeschreibung:

  • im QNAP stecken 4x 4TB HDD, die sollen im GlusterFS zum Einsatz kommen
  • ich habe nicht beliebig viel Geld, will also nur gerade soviel hinzu kaufen, wie umbedingt notwendig. 😉
  • die neue SpeicherkapazitĂ€t soll 7x 4TB sein
  • ich spendiere zwei spare HDDs

Sehen wir uns den bisherigen QNAP TS-412 im Detail an.

Ausgangssituation, die bisherige Datensenke

Das Zielbild im GlusterFS soll wie folgt sein.

Zielbild der neuen Datensenke

In meinem selbstgebauten GlusterFS Chassis sollen zukĂŒnftig 9 active Nodes stecken und ein spare Node (Brick), fĂŒr den Fall das ich mal einen defekten Node durchtauschen muss. Die oben rechts hell grau hinterlegten Nodes sollen die beiden redundanten Nodes darstellen, ist nur zur Visualisierung (im GlusterFS kann und will man, in dem von mir angestgrebten Dispersed-Modus, die Funktion nicht direkt pro Node festlegen). Diese beiden Nodes mĂŒssen von vorherein also immer anwesend sein. Der Spare Brick kommt spĂ€ter, wenn der ganze GlusterFS lĂ€uft und dient nur als Cold-Spare, sollte einer der Nodes die GrĂ€tsche machen.

Jetzt kommt die erste TĂŒcke eines GlusterFS im Dispersed-Modus: die Anzahl der Nodes ist nicht VerĂ€nderbar! Ich kann also nicht zur Laufzeit von 7 dispersed Nodes und 2 redundand Nodes auf sagen wir 10 dispersed Nodes und 2 redundand Nodes skalieren. Was geht ist, dass man neben einen dispersed GlusterFS einen weiteren (baugleichen) GlusterFS stellt und daraus einen Distributed Dispersed macht, aber die Gedanken an solches Konstrukt sind mir noch zu weit in der Ferne.. 🙂

Was wiederum geht, ist die HDDs in einem Dispered GlusterFS zu vergrössern, wobei immer die kleinste HDD die Menge des zur VerfĂŒgung stehenden Speichers angibt. Also in einem GlusterFS mit 7 x 4TB HDD dispersed + 2 x 4TB HDD replicated errechnet sich der Speicher nach den dispersed Nodes, entsprechend 7 x 4TB = 28TB.

Jetzt zeigt sich mein Dilemma, theoretisch brĂ€uchte ich neben den 9 x Odroid-HC2 ja nur 5 x 4TB HDDs kaufen, um die Ziel BestĂŒckung zu erhalten. Doch wohin in der Phase des Umbaus mit den Daten?

Da es ist unwarscheinlich ist, dass ich diese HDDS fĂŒr die Dauer von 1-2 Monaten kostenlos geliehen bekomme, muss ich mich diesem Problem anders nĂ€hern…

Zum GlĂŒck habe ich aus der vorherigen BestĂŒckung des QNAP TS-412 noch 4 x 2TB HDD rumliegen. Fand diese immer fĂŒr zu schade zum Wegwerfen, haben ja auch mal Geld gekostet!! Daraus ergibt sich folgende Option zu BestĂŒckung.

Schritt 1 des neuen GlusterFS

FĂŒr den ersten Schritt benötige ich also 9 x Odroid-HC2 (zu je ~65€ = 585€) und zusĂ€tzlich 5 x 4 TB HDDs (zu je ~190€ = 950€). Zusammen nicht ganz billig die Datensenke, aber dafĂŒr sollte die nĂ€chste Jahre/Jahrzehnte ruhe sein bei der Frage, wohin mit den ganzen Filmen aus den Mediatheken der öffentlich rechtlichen Fernsehsender.

ZurĂŒck zu meinem Dilemma. Mit der BestĂŒckung des GlusterFS in Schritt 1 mit den alten 2TB HDDs ist die SpeicherkapazitĂ€t also 7 x 2TB = 14TB

Also genau 2TB kleiner das was ich benötige… knurr…

Und hier geht der Eiertanz dann auch los.

Ich kann also nachdem ich Schritt 1 aufgesetzt habe die Daten von JBOD1 oder JBOD2 im ganzen von alt nach neu verschieben. Nehmen wir jetzt einfach mal JBOD2 und verschieben dann die Daten auf der GlusterFS.

Dann sollte nach dem Kopieren der Daten, von der initial zur VerfĂŒgung gestandenen SpeicherkapazitĂ€t 14TB – 8TB = 6TB, also noch 6TB vorhanden sein. Ich kann dann also JBOD2 auflösen und die HDD3 und HDD4 aus dem QNAP TS-412 ziehen, platt machen und durch diese beiden gebrauchten 4TB HDDs den Gluster ein StĂŒck umbauen – also folgendes Szenario ist das Ziel fĂŒr Schritt 2.

Schritt 2, nach dem Auflösen von JBOD2

Durch das Verschieben der zwei HDDs sind jetzt zwei kleine HDDs frei geworden, also 2 x 2TB HDDs. Diese brauchen wir im nÀchsten Datenverschiebezyklus.

Jetzt sind in JBOD1 noch bummelig 8TB auf 6TB unter zu bringen, was natĂŒrlich nicht geht..

Die Krux aus dieser Situation ist, dass ich 6 TB auf das GlusterFS kopiere und die verbleibenden 2TB auf eine der beiden ĂŒbrig gebliebenen 2 TB HDDs. Wenn dann die letzten beiden 4TB HDDs in der QNAP TS-412 frei sind, kommt Schritt 3 des Umbaus.

Zielzustand ist erreicht

Nachdem 3 Umbau ist der Zielzustand erreicht und die Daten von der ĂŒbrig gebliebenen 2TB HDD werden auch auf das GlusterFS kopiert.

…Tadaa…

Soweit zu meiner Planung – ob es klappt, ich werde berichten.

Installing GlusterFS on Odroid-HC2

Das GlusterFS lebt davon, dass es unterscheiden kann welches VerĂ€nderung im Filesystem zu welchem Zeitpunkt, auf welchem Node zu erst stattgefunden hat. Andernfalls könnte es ja nicht die AktualitĂ€t beurteilen. Aus diesem Grund ist es, laut Anleitung, sehr wichtig ein durchgehend zeitsynchrones Setup zu pflegen. An der Installation und Nutzung von NTP fĂŒhrt also kein Weg vorbei. Also zu erst mal NTP installieren und aktivieren.

---
- name: All hosts install ntp
  hosts: glusterfs
  become: yes
  
  tasks:
    - name: install ntp
      pacman:
        name: ntp
        update_cache: yes

Und ausrollen, mittlerweile traue ich mich es auch gleich auf den ganzen Node-Stapel auszurollen.

[ansible@barney ~]$ ansible-playbook ntp.yml

PLAY [All hosts install ntp] *******************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]

TASK [install ntp] *****************************************************************************************************************************************
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.xx               : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.xx               : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.xx               : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.xx               : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.xx               : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.xx               : ok=2    changed=1    unreachable=0    failed=0   

[ansible@barney ~]$ 

Der nĂ€chste Schritt ist also NTP Server fĂŒr die interne Uhr der Gluster-Nodes zu nutzen. Ich hab mir folgendes Playbook zusammen geschrieben.

---
- name: Build NTP config
  hosts: glusterfs
  become: yes
  
  tasks:
    - name: Stop service ntpd, if started
      service:
        name: ntpd
        state: stopped

    - name: delete orginal config
      file:
        path: /etc/ntp.conf
        state: absent

    - name: Touch on /etc/ntp.conf
      file:
        path: /etc/ntp.conf
        state: touch
        mode: "u=rw,g=r,o=r"

    - name: insert ntp.conf
      blockinfile:
        path: /etc/ntp.conf
        block: |
          #Associate to PTB's NTP pool
          server 0.arch.pool.ntp.org
          server 1.arch.pool.ntp.org
          server 2.arch.pool.ntp.org
          server 3.arch.pool.ntp.org

          # By default, the server allows:
          # - all queries from the local host
          # - only time queries from remote hosts, protected by rate limiting and kod
          restrict default kod limited nomodify nopeer noquery notrap
          restrict 127.0.0.1
          restrict ::1

          # Location of drift file
          driftfile /var/lib/ntp/ntp.drift

    - name: Start service ntpd, if not started
      service:
        name: ntpd
        state: started

    - name: Enable service ntpd, and not touch the state
      service:
        name: ntpd
        enabled: yes

    - name: set timezone to Europe/Berlin
      timezone:
        name: Europe/Berlin

Ein Kontrolle, mit einigen Minuten abstand zum ausgefĂŒhrten Playbook, zeigt auch, dass die NTP Server erreicht werden. Leider war es mir nicht möglich die Zeitserver der PTB in Deutschland zu nutzen. Auf deren Website standen zwar die FQDNs, welche aber weder auf NTP Anfragen noch auf Ping reagierten..

[ansible@GFS1 ~]$ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
+217.79.179.106  192.53.103.108   2 u   38   64  177   12.028   -1.624   5.113
+eta.h6g-server. 205.46.178.169   2 u   39   64  177   15.882   -0.296   5.146
*89.163.128.33 ( 192.53.103.104   2 u   37   64  177   13.343   -6.665   5.282
+mx.pingless.com 130.149.17.21    2 u   35   64  177    8.285   -6.129   4.892
[ansible@GFS1 ~]$ 

Die Uhrzeit sollte also ĂŒber alle Nodes dann Synchron sein, nachdem ich das Playbook auf alle Gluster Nodes geworfen habe. Was ich gleich mal mache.

[ansible@barney ~]$ ansible-playbook ntp-config.yml

PLAY [Build NTP config] ************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]

TASK [Stop service ntpd, if started] ***********************************************************************************************************************
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]
changed: [192.168.2.xx]
ok: [192.168.2.xx]

TASK [delete orginal config] *******************************************************************************************************************************
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]

TASK [Touch on /etc/ntp.conf] ******************************************************************************************************************************
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]

TASK [insert ntp.conf] *************************************************************************************************************************************
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]

TASK [Start service ntpd, if not started] ******************************************************************************************************************
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]

TASK [Enable service ntpd, and not touch the state] ********************************************************************************************************
ok: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]

TASK [set timezone to Europe/Berlin] ***********************************************************************************************************************
ok: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]
changed: [192.168.2.xx]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.xx               : ok=8    changed=5    unreachable=0    failed=0   
192.168.2.xx               : ok=8    changed=6    unreachable=0    failed=0   
192.168.2.xx               : ok=8    changed=6    unreachable=0    failed=0   
192.168.2.xx               : ok=8    changed=6    unreachable=0    failed=0   
192.168.2.xx               : ok=8    changed=6    unreachable=0    failed=0   
192.168.2.xx               : ok=8    changed=6    unreachable=0    failed=0   

[ansible@barney ~]$ 

Nachdem nun die Uhrzeit klar ist, geht es weiter mit dem aktivieren des GlusterfS Daemon, ohne welchen ein GlusterFS nicht funktioniert.

DafĂŒr habe ich mir folgendes Playbook ausgedacht.

---
- name: Start GlusterFS Daemon
  hosts: glusterfs
  become: yes
  
  tasks:
    - name: Start GlusterFS Daemon, if not started
      service:
        name: glusterd
        state: started

    - name: Enable GlusterFS Daemon service, and not touch the state
      service:
        name: glusterd
        enabled: yes

Dieses Playbook lief ebenfalls durch ohne Fehlermeldungen. Ich hab mit den GlusterFS Boardmitteln mal lokal auf Node GFS2 probiert, ob die anderen Nodes im Gluster-Service erreichbar sind.

[ansible@GFS2 ~]$ sudo gluster peer probe gfs1
peer probe: success. 

[ansible@GFS2 ~]$ sudo gluster peer probe gfs2                    
peer probe: success. Probe on localhost not needed

[ansible@GFS2 ~]$ sudo gluster peer probe gfs3
peer probe: success. 

[ansible@GFS2 ~]$ sudo gluster peer probe gfs4
peer probe: success. 

[ansible@GFS2 ~]$ sudo gluster peer probe gfs5
peer probe: success. 

[ansible@GFS2 ~]$ sudo gluster peer probe gfs6
peer probe: success. 

[ansible@GFS2 ~]$ 

Auf allen Nodes wurde der GlusterFS Daemon erfolgreich gestartet. Da ich von Hause aus sehr mistrauisch bin, habe ich den selben Test nach einem reboot ebenso nochmal durchgefĂŒhrt. Das Ergebnis war das selbe.

Nachdem also der GlusterFS Daemon sauber lĂ€uft, geht es daran die einzelnen Nodes als Peers per Playbook bekannt zu machen. Das Playbook dafĂŒr habe ich wie folgt formuliert.

---
- name: Start GlusterFS Daemon
  hosts: glusterfs
  become: yes
  
  tasks:
    - name: Create a trusted storage pool
      gluster_peer:
            state: present
            nodes:
                 - GFS1
                 - GFS2
                 - GFS3
                 - GFS4
                 - GFS5
                 - GFS6

Beim Ausrollen ist mir aufgefallen, dass es scheinbar nur notwendig ist, es gegen einen GlusterFS Node laufen zu lassen, da dieser dann wohl die Peers untereinander informiert. Anders kann ich mir das erfolgreiche Setup trotz der vielen Fehlermeldungen nicht recht erklÀren. Sei es drum, das Playbook ist durchgelaufen.

[ansible@barney ~]$ ansible-playbook gluster-peers.yml 

PLAY [Start GlusterFS Daemon] ******************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]
ok: [192.168.2.xx]

TASK [Create a trusted storage pool] ***********************************************************************************************************************
fatal: [192.168.2.xx]: FAILED! => {"changed": false, "msg": "peer probe: failed: GFS1 is either already part of another cluster or having volumes configured\n", "rc": 1}
fatal: [192.168.2.xx]: FAILED! => {"changed": false, "msg": "peer probe: failed: GFS1 is either already part of another cluster or having volumes configured\n", "rc": 1}
fatal: [192.168.2.xx]: FAILED! => {"changed": false, "msg": "peer probe: failed: GFS1 is either already part of another cluster or having volumes configured\n", "rc": 1}
fatal: [192.168.2.xx]: FAILED! => {"changed": true, "msg": "peer probe: failed: GFS3 is either already part of another cluster or having volumes configured\n", "rc": 1}
fatal: [192.168.2.xx]: FAILED! => {"changed": false, "msg": "peer probe: failed: GFS1 is either already part of another cluster or having volumes configured\n", "rc": 1}
changed: [192.168.2.xx]
        to retry, use: --limit @/home/ansible/gluster-peers.retry

PLAY RECAP *************************************************************************************************************************************************
192.168.2.xx               : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.xx               : ok=1    changed=0    unreachable=0    failed=1   
192.168.2.xx               : ok=1    changed=0    unreachable=0    failed=1   
192.168.2.xx               : ok=1    changed=0    unreachable=0    failed=1   
192.168.2.xx               : ok=1    changed=0    unreachable=0    failed=1   
192.168.2.xx               : ok=1    changed=0    unreachable=0    failed=1   

[ansible@barney ~]$

Aber wie bereits geschrieben geht es den Peers gut, denn die Abfrage des Glsuer Peer Status sieht richtig gut aus. Einmal auf GFS:

[ansible@GFS1 ~]$ sudo gluster peer status
Number of Peers: 5

Hostname: GFS2
Uuid: a8e86d86-d13d-45d6-97a1-b46619a4dfea
State: Peer in Cluster (Connected)

Hostname: GFS3
Uuid: a9696e90-0707-4de4-8430-479e7d3af8ac
State: Peer in Cluster (Connected)

Hostname: GFS4
Uuid: e01c2d0a-6491-4250-b531-5895be4788f0
State: Peer in Cluster (Connected)

Hostname: GFS5
Uuid: c26b3841-ff62-456a-b1d9-8e2842291535
State: Peer in Cluster (Connected)

Hostname: GFS6
Uuid: 67604b4f-85ba-47d5-96e4-a9dee66569ae
State: Peer in Cluster (Connected)

[ansible@GFS1 ~]$ 

Und einmal zur Sicherheit auch noch auf GFS6:

[ansible@GFS6 ~]$ sudo gluster peer status
Number of Peers: 5

Hostname: GFS1
Uuid: 48851509-5126-4d70-918d-841fc185ce21
State: Peer in Cluster (Connected)

Hostname: GFS2
Uuid: a8e86d86-d13d-45d6-97a1-b46619a4dfea
State: Peer in Cluster (Connected)

Hostname: GFS3
Uuid: a9696e90-0707-4de4-8430-479e7d3af8ac
State: Peer in Cluster (Connected)

Hostname: GFS4
Uuid: e01c2d0a-6491-4250-b531-5895be4788f0
State: Peer in Cluster (Connected)

Hostname: GFS5
Uuid: c26b3841-ff62-456a-b1d9-8e2842291535
State: Peer in Cluster (Connected)

[ansible@GFS6 ~]$ 

Die Peers sehen sich und haben eine Verbindung untereinander. Weiter geht es mit dem eigentlichen interessanten Teil, das Aufsetzen des Node ĂŒbergreifenden GlusterFWS Volumes.

Leider gelingt mir dieses zur Zeit nicht mit Ansible, was eigentlich sehr schade ist – da ich es umbedingt nutzen wollte. Auf der anderen Seite ist es nicht notwendigerweise mit Ansible einfacher, da die gesammte GlusterFS Konfiguration nur auf einem Node erfolgt und der verteilt es dann an die anderen Node Clustermember. Hier jetzt also die Zeilen zum Aufsetzen des GlusterFS Volumes auf der Bash von gfs1:

[ansible@GFS1 ~]$ sudo gluster volume create GV0 disperse 4 redundancy 2 transport tcp gfs1:/export/sda1 gfs2:/export/sda1 gfs3:/export/sda1 gfs4:/export/sda1 gfs5:/export/sda1 gfs6:/export/sda1 force 
volume create: GV0: success: please start the volume to access data

[ansible@GFS1 ~]$ 

Da mich nach so einem Befehl immer brennend die Auswirkung interessiert, hier die Kontrolle des Ergebnis.

[ansible@GFS1 ~]$ sudo gluster volume info
 
Volume Name: GV0
Type: Disperse
Volume ID: a917ef77-f247-4460-a3bb-bf4a7cb1fad9
Status: Created
Snapshot Count: 0
Number of Bricks: 1 x (4 + 2) = 6
Transport-type: tcp
Bricks:
Brick1: gfs1:/export/sda1
Brick2: gfs2:/export/sda1
Brick3: gfs3:/export/sda1
Brick4: gfs4:/export/sda1
Brick5: gfs5:/export/sda1
Brick6: gfs6:/export/sda1
Options Reconfigured:
transport.address-family: inet
nfs.disable: on

[ansible@GFS1 ~]$

Solange das Volume nur angelegt (Created) ist, findet noch keine Replikation zwischen den Nodes statt. DafĂŒr muss das Volume erst gestartet werden.

[ansible@GFS1 ~]$ sudo gluster volume start GV0  
volume start: GV0: success
[ansible@GFS1 ~]$

Anschließend Ă€ndert sich der Status von Created zu Started. Jetzt sollte das GlusterFS Volume erreichbar sein.

[ansible@GFS1 ~]$ sudo gluster volume info
 
Volume Name: GV0
Type: Disperse
Volume ID: a917ef77-f247-4460-a3bb-bf4a7cb1fad9
Status: Started
Snapshot Count: 0
Number of Bricks: 1 x (4 + 2) = 6
Transport-type: tcp
Bricks:
Brick1: gfs1:/export/sda1
Brick2: gfs2:/export/sda1
Brick3: gfs3:/export/sda1
Brick4: gfs4:/export/sda1
Brick5: gfs5:/export/sda1
Brick6: gfs6:/export/sda1
Options Reconfigured:
transport.address-family: inet
nfs.disable: on

[ansible@GFS1 ~]$ sudo gluster volume status
Status of volume: GV0
Gluster process                             TCP Port  RDMA Port  Online  Pid
------------------------------------------------------------------------------
Brick gfs1:/export/sda1                     49152     0          Y       1746 
Brick gfs2:/export/sda1                     49152     0          Y       916  
Brick gfs3:/export/sda1                     49152     0          Y       678  
Brick gfs4:/export/sda1                     49152     0          Y       681  
Brick gfs5:/export/sda1                     49152     0          Y       671  
Brick gfs6:/export/sda1                     49152     0          Y       679  
Self-heal Daemon on localhost               N/A       N/A        Y       1767 
Self-heal Daemon on GFS2                    N/A       N/A        Y       937  
Self-heal Daemon on GFS3                    N/A       N/A        Y       699  
Self-heal Daemon on GFS4                    N/A       N/A        Y       702  
Self-heal Daemon on GFS5                    N/A       N/A        Y       692  
Self-heal Daemon on GFS6                    N/A       N/A        Y       700  
 
Task Status of Volume GV0
------------------------------------------------------------------------------
There are no active volume tasks
 
[ansible@GFS1 ~]$ 

Ich hab das GlusterFS Volume GV0 mal probeweise gemountet. Das geht sehr einfach von einem anderen Linux Arch, welcher nicht als Peer defineriert sein muss. Okay, ich habe jetzt auch keine Security features aktiviert. Was wichtig ist, dass der Client der in den GlusterFS mounten möchte die Nodes per DNS genauso auflösen können muss, wie die GlusterFS Peers untereinander. Dann geht das mounten auch recht einfach von der Hand.

[eric@Wintermute ~]$ sudo mount -t glusterfs gfs6:GV0 /media/GV0/

[eric@Wintermute ~]$ df -aH /media/GV0
Dateisystem    GrĂ¶ĂŸe Benutzt Verf. Verw% EingehĂ€ngt auf
gfs6:GV0         10T    111G  9,9T    2% /media/GV0

[eric@Wintermute ~]$ ls -la /media/GV0/
insgesamt 4
drwxr-xr-x  3 root root   24  1. Apr 20:10 .
drwxr-xr-x 10 root root 4096  1. Apr 21:13 ..

[eric@Wintermute ~]$ 

Ich hoffe, ich konnte etwas Licht in das GlusterFS Thema bringen.

:o)

Odroid-HC2 mit Ansible fĂŒr das GlusterFS vorbereiten

Nachdem Ansible nun auf die Nodes zugreifen kann, folgt der nĂ€chste Schritt. Ich werde mich StĂŒck fĂŒr StĂŒck an die notwendigen Bestandteile eines Playbooks an nĂ€hern. Ziel ist es, ein Playbook zu erhalten, welches mir die Nodes so Aufbaut, dass sie in das GlusterFS integriert werden können.

Zuerst schaue ich mir mal an, wie das von https://archlinuxarm.org/platforms/armv7/samsung/odroid-hc2 erzeugte Setup aussieht. Mich interessiert vor allem, wie das Arm Team die microSD Karte gemountet hat und wo diese und die eingebaute HDD im /dev Baum sitzt.

[ansible@alarm ~]$ cat /etc/fstab 
# Static information about the filesystems.
# See fstab(5) for details.

# <file system> <dir> <type> <options> <dump> <pass>
[ansible@alarm ~]$ sudo cat /etc/fstab
# Static information about the filesystems.
# See fstab(5) for details.

# <file system> <dir> <type> <options> <dump> <pass>
[ansible@alarm ~]$ 

Okay, kann man so machen.. seufz, damit hĂ€lt man sich natĂŒrlich alle Freiheitsgrade offen, indem man die Medien gar nicht erst mountet.

Bin ich persönlich jetzt keine Freund davon etwas ungemountet zu booten, aber ich kann es nachvollziehen, dass das Team es so gemacht hat. Dadurch können die drei verschiedenen Arten von Mount-Points nach dem ersten Booten bedient werden. (/dev/sdX versus Label versus UUID). Ich selber nutze sehr gerne zum Mounten die Label, weil meiner Meinung nach dadurch in einem spĂ€teren Update sich verĂ€ndernde Mount-Points a’la /dev/sda1 abgefangen werden und diese schwer handlebaren UUIDs umgangen werden können. Aber wie schon erwĂ€hnt, was fĂŒr mich war ist, mag noch lange nicht fĂŒr jedes Szenario gelten.

Na dann schau ich mir mal die Optionen zum Mounten im /dev/disk Baum genauer an.

[ansible@alarm ~]$ sudo tree /dev/disk/
/dev/disk/
|-- by-id
|   |-- ata-SAMSUNG_HD203WI_S1UYJ1KZ100183 -> ../../sda
|   |-- ata-SAMSUNG_HD203WI_S1UYJ1KZ100183-part1 -> ../../sda1
|   |-- mmc-SC16G_0xb48f4515 -> ../../mmcblk0
|   |-- mmc-SC16G_0xb48f4515-part1 -> ../../mmcblk0p1
|   |-- wwn-0x50024e9002b89270 -> ../../sda
|   `-- wwn-0x50024e9002b89270-part1 -> ../../sda1
|-- by-label
|   `-- ROOT -> ../../mmcblk0p1
<snip>

Am Ende steht mein gesetztes Label ROOT fĂŒr die mmcblk0p1 Partition. Diese Partition möchte ich jetzt auch gerne fest in der /etc/fstab stehen haben, damit nach spĂ€teren Software-Updates nichts ins Schlingern gerĂ€t.

Ich baue mir das Zielplaybook aus einzelnen kleinen Playbook-Configlets zusammen, deshalb prĂŒfe ich jetzt erstmal, ob das Mounten von einem / auf einem Node geht. Das Playbook-Configlets habe ich mir wie folgt ausgedacht.

[ansible@barney ~]$ cat label_root_mount.yml 
---
- name: All mount / by label ROOT
  hosts: glusterfs
  become: yes

  tasks:
   - name: generate LABEL=ROOT entry
     mount:
       path: /
       src: LABEL=ROOT
       fstype: ext4
       opts: defaults,noatime
       state: present

[ansible@barney ~]$ 

Damit ich das Playbook jetzt nicht gleich gegen alle Nodes lasse, habe ich mir die /etc/ansible/host aufgeteilt, so daß ich einzelne Nodes gezielt ansprechen kann. Die /etc/ansible/host Textdatei sieht wie folgt aus.

[root@barney ansible]# cat /etc/ansible/hosts
[glusterfs]
192.168.2.143
192.168.2.144
192.168.2.145
192.168.2.146
192.168.2.25

[g1]
192.168.2.143

[g2]
192.168.2.144

[g3]
192.168.2.145

[g4]
192.168.2.146

[g5]
192.168.2.25
[root@barney ansible]# 

Ich vermute, in grĂ¶ĂŸeren Installationen wird wohl eher mit dem DNS Namen der Nodes hantiert als mit der IPv4 Adresse, ist mir jetzt aber erstmal Wurst..

Okay, dann lassen wir mal das Playbook-Configlets gegen den Node g5 laufen und schauen uns an, wie das Ergebnis aussieht.

[ansible@barney ~]$ ansible-playbook label_root_mount.yml --limit g5

PLAY [All mount / by label ROOT] ***************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.25]

TASK [generate LABEL=ROOT entrie] **************************************************************************************************************************
changed: [192.168.2.25]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   

[ansible@barney ~]$ 

Spannend war es jetzt fĂŒr mich, da ich mir nicht ganz sicher war, was der Node denn nun wirklich als Ergebnis in der /etc/fstab stehen hat. Ich seh mal nach.

[ansible@alarm ~]$ cat /etc/fstab 
# Static information about the filesystems.
# See fstab(5) for details.

# <file system> <dir> <type> <options> <dump> <pass>
LABEL=ROOT / ext4 defaults,noatime 0 0
[ansible@alarm ~]$ 

Sieht gut aus, allerdings gibt es noch ein Delta zwischen /etc/fstab und gelebter /etc/mtab. DafĂŒr werde ich den Node jetzt mach rebooten und dann sollten die beiden symmetrisch sein.

[ansible@barney ~]$ ansible-playbook reboot.yml --limit g5

PLAY [All hosts reboot] ************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.25]

TASK [Unconditionally reboot the machine with all defaults] ************************************************************************************************
changed: [192.168.2.25]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   

[ansible@barney ~]$ 

Nach dem kleinen Klaps sieht es jetzt so aus, wie ich es haben wollte. Die microSD Karte ist ĂŒber das Label gemountet.

[ansible@alarm ~]$ lsblk -Tf
NAME        FSTYPE LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
sda                                                                          
`-sda1      xfs          03df1da5-89d0-4fd3-8bf4-69cbbe8d87ce                
mmcblk0                                                                      
`-mmcblk0p1 ext4   ROOT  5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81   12.4G    10% /

Nun wird es Zeit, die verbaute HDD der Nodes fĂŒr die Nutzung im GlusterFS einzurichten. DafĂŒr verwerfe ich das auf der HDD existierende Setup erst einmal weg.

Ich hab jetzt eine gute Stunde gebraucht, um festzustellen, dass auf meinen Nodes alle Befehle vom Modul parted nicht ausgefĂŒhrt werden. Ich stell da mal eine steile Hypothese auf, das Modul parted braucht auf dem Nodes das Programm parted. Ich werde das also mal Installieren und gucken ob das Playbook dann sauber durch lĂ€uft.

Also schraub ich mir ein Playbook zum Ausrollen von parted.

[ansible@barney ~]$ cat parted.yml
---
- name: All hosts install parted
  hosts: glusterfs
  become: yes
  
  tasks:
    - name: install parted
      pacman:
        name: parted
        update_cache: yes
[ansible@barney ~]$ 

Und lÀuft es durch?

[ansible@barney ~]$ ansible-playbook parted.yml --limit g5

PLAY [All hosts install parted] ****************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.25]

TASK [install parted] **************************************************************************************************************************************
changed: [192.168.2.25]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   
[ansible@barney ~]$

Okay, jetzt ist auf Node g5 also das Programm parted ausgerollt und jetzt lass ich nochmal das Playbook zum Löschen der Partition auf Node g5 los.

---
- name: All remove all partitions on sda
  hosts: glusterfs
  become: yes

  tasks:
   - parted:
       device: /dev/sda
       number: 1
       state: absent

Diese Partition stammt aus meinen Versuchen mit unterschiedlichen Softgware Defined Storages (Ceph/Hadoop/GlusterFs). FĂŒr mich stellt sich GlusterFS als am leichtesten handlebar dar, auch hier mag jeder zu seiner eigenen Sichtweisekommen auf Basis seines Bedarfs.

Nun gut, was macht denn nun das Playbok zum Löschen der Partion #1 auf SDA?

[ansible@barney ~]$ ansible-playbook remove_all_partitions_on_hdd.yml --limit g5

PLAY [All remove all partitions on sda] ********************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.25]

TASK [parted] **********************************************************************************************************************************************
changed: [192.168.2.25]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   

[ansible@barney ~]$ 

Jetzt noch flott eon prĂŒfender Blick direkt ins Filesystem des Node g5.

[ansible@alarm ~]$ lsblk -Tf
NAME        FSTYPE LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
sda                                                                          
mmcblk0                                                                      
`-mmcblk0p1 ext4   ROOT  5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81   12.4G    10% /
[ansible@alarm ~]$ 

Es hat funktioniert, die Partition sda1 ist gelöscht. Sicherlich geistern die Testdaten jetzt noch auf der HDD rum, aber was solls.

Als nĂ€chstes möchte ich jetzt eine Partition auf /dev/sda anlegen mit den Label=BRICK. Das Filesystem dieser BRICK Partition muss XFS sein. Das Playbook dafĂŒr habe ich mir wiefolgt vorgestellt.

[ansible@barney ~]$ cat make_sda1_partition.yml 
---
- name: All make one partition on sda1 with xfs
  hosts: glusterfs
  become: yes

  tasks:
   - name: Create sda1 partition
     parted:
       device: /dev/sda
       label: gpt
       number: 1
       state: present
       part_start: 0%
       part_end: 100%

   - name: Create a xfs filesystem on /dev/sda1
     filesystem:
       fstype: xfs
       dev: /dev/sda1
       opts: -i size=512

   - name: Label sda1 partition with LABEL=BRICK
     command: "xfs_admin -L BRICK /dev/sda1"
     become: true

[ansible@barney ~]$ 

Der Knackpunkt in diesem Ablauf ist, dass ich es auf Biegen und Brechen nicht hinbekommen habe mit den standard Ansible Modulen ein Label fĂŒr die neue Partition zu setzen. Deshalb steht da im Playbook so ein hĂ€ssliches Bash Command drin, aber nun ja. Dann lass ich mal mein erstes Playbook mit drei Tasks ausrollen. Mal sehen, ob das so geht, wie ich es mir vorstelle.

[ansible@barney ~]$ ansible-playbook make_sda1_partition.yml --limit g5

PLAY [All make one partition on sda1 with xfs] *************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.25]

TASK [Create sda1 partition] *******************************************************************************************************************************
changed: [192.168.2.25]

TASK [Label sda1 partition with LABEL=BRICK] ***************************************************************************************************************
changed: [192.168.2.25]

TASK [Create a xfs filesystem on /dev/sda1] ****************************************************************************************************************
ok: [192.168.2.25]

PLAY RECAP *************************************************************************************************************************************************
192.168.2.25               : ok=4    changed=2    unreachable=0    failed=0   

[ansible@barney ~]$

Ausgerollt ist es jedenfalls. Bei FilesystemverÀnderungen sehe ich lieber genau nach, bevor ich weiter mache.

[ansible@alarm ~]$ ls -l /dev/disk/by-label/
total 0
lrwxrwxrwx 1 root root 10 Mar 31 15:52 BRICK -> ../../sda1
lrwxrwxrwx 1 root root 15 Mar 31 09:56 ROOT -> ../../mmcblk0p1


[ansible@alarm ~]$ lsblk -Tf
NAME        FSTYPE LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
sda                                                                          
`-sda1      xfs    BRICK cdc27797-15eb-4e4d-b58e-3e6ad3315112                
mmcblk0                                                                      
`-mmcblk0p1 ext4   ROOT  5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81   12.4G    10% /

[ansible@alarm ~]$ sudo xfs_db -c info /dev/sda1
meta-data=/dev/sda1              isize=512    agcount=4, agsize=122091705 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=0
data     =                       bsize=4096   blocks=488366820, imaxpct=5
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=238460, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

ansible@alarm ~]$ 

Das sieht doch nach einem gĂŒltigen Versuch aus. Es gibt eine Partition /dev/sda1 mit dem Label BRICK. Die orginale Anleitung zum GlusterFS sieht vor, dass die XFS Storage-Partitionen nach /export/sdX1 gemountet wird. Also lassen wir Ansible mal ein Verzeichnis /export erzeugen, worin danach /export/sda1 erzeugt wird. das Playbook dafĂŒr stelle ich mir wie folgt vor.

[ansible@barney ~]$ cat mkdir_export_mount_label_brick.yml 
---
- name: Generate /export/sdb1 and mount into fstab
  hosts: glusterfs
  become: yes

  tasks:
  - name: Creates directory and subdirectory
    file:
      path: /export/sda1
      state: directory

  - name: generate LABEL=BRICK entry
    mount:
      path: /export/sda1
      src: LABEL=BRICK
      fstype: xfs
      opts: defaults
      state: present

[ansible@barney ~]$ 

Wenn ich mir das Ergebnis des Playbooks ansehe, bin ich zufrieden. Nach einem Reboot sollte dann die Partition sauber gemountet sein.

[ansible@alarm ~]$ tree -dfugA /export/
/export
└── [root     root    ]  /export/sda1

1 directory

[ansible@alarm ~]$ cat /etc/fstab 
# Static information about the filesystems.
# See fstab(5) for details.

# <file system> <dir> <type> <options> <dump> <pass>
LABEL=ROOT / ext4 defaults,noatime 0 0
LABEL=BRICK /export/sda1 xfs defaults 0 0

[ansible@alarm ~]$ 

Also steht als letzes fĂŒr die Vorbereitung der GlusterFS installation noch ein Reboot an, dafĂŒr nehme ich das oben bereits beschriebene Playbook reboot.yml nochmal zur Hand und lasse es auf Node g5 los und sehe mir nachdem Reboot das Ergebnis auf dem Node g5 an.

[ansible@alarm ~]$ lsblk -Tf 
NAME        FSTYPE LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
sda                                                                          
`-sda1      xfs    BRICK cdc27797-15eb-4e4d-b58e-3e6ad3315112    1.8T     0% /export/sda1
mmcblk0                                                                      
`-mmcblk0p1 ext4   ROOT  5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81   12.4G    10% /

[ansible@alarm ~]$ 

Das sieht genau so aus, wie ich es haben wollte. Damit sind die Vorbereitung des Dateisystems und der Partitonen abgeschlossen. Die einzelnen Code-Schnipsel baue ich jetzt noch in ein zusammenhĂ€ngendes Playbook zusammen und lasse es dann ĂŒber das gesammte Setup laufen.

Das finale Plabook lief fast zufriedenstellend durch. 😉

[ansible@barney ~]$ ansible-playbook preinstallation_glusterfs.yml

PLAY [Vorbereiten der Nodes vor der GlusterFS installation] ************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.2.146]
ok: [192.168.2.144]
ok: [192.168.2.145]
ok: [192.168.2.143]
ok: [192.168.2.25]
ok: [192.168.2.137]

TASK [Installiere parted] **********************************************************************************************************************************
ok: [192.168.2.25]
changed: [192.168.2.145]
changed: [192.168.2.144]
changed: [192.168.2.143]
changed: [192.168.2.146]
ok: [192.168.2.137]

TASK [Installiere glusterfs] *******************************************************************************************************************************
changed: [192.168.2.25]
changed: [192.168.2.144]
changed: [192.168.2.145]
changed: [192.168.2.146]
changed: [192.168.2.143]
ok: [192.168.2.137]

TASK [Erzeuge einen FSTAB Eintrag fĂŒr LABEL=ROOT] **********************************************************************************************************
changed: [192.168.2.145]
changed: [192.168.2.143]
changed: [192.168.2.144]
ok: [192.168.2.25]
changed: [192.168.2.146]
ok: [192.168.2.137]

TASK [Lösche existierende Partitionen SDA1 auf SDA] ********************************************************************************************************
fatal: [192.168.2.25]: FAILED! => {"changed": false, "err": "Warning: Partition /dev/sda1 is being used. Are you sure you want to continue?\n", "msg": "Error while running parted script: /usr/bin/parted -s -m -a optimal /dev/sda -- rm 1", "out": "", "rc": 1}
changed: [192.168.2.143]
changed: [192.168.2.144]
changed: [192.168.2.146]
changed: [192.168.2.145]
fatal: [192.168.2.137]: FAILED! => {"changed": false, "err": "Warning: Partition /dev/sda1 is being used. Are you sure you want to continue?\n", "msg": "Error while running parted script: /usr/bin/parted -s -m -a optimal /dev/sda -- rm 1", "out": "", "rc": 1}

TASK [Erzeuge eine SDA1 Partition] *************************************************************************************************************************
changed: [192.168.2.143]
changed: [192.168.2.144]
changed: [192.168.2.145]
changed: [192.168.2.146]

TASK [Formatiere SDA1 mit XFS] *****************************************************************************************************************************
changed: [192.168.2.143]
changed: [192.168.2.145]
changed: [192.168.2.144]
changed: [192.168.2.146]

TASK [Umlabeln der SDA1 mit LABEL=BRICK] *******************************************************************************************************************
changed: [192.168.2.145]
changed: [192.168.2.146]
changed: [192.168.2.144]
changed: [192.168.2.143]

TASK [Erzeuge Verzeichnis /export/sda1] ********************************************************************************************************************
changed: [192.168.2.143]
changed: [192.168.2.144]
changed: [192.168.2.146]
changed: [192.168.2.145]

TASK [Erzeuge einen FSTAB Eintrag fĂŒr LABEL=BRICK] *********************************************************************************************************
changed: [192.168.2.143]
changed: [192.168.2.144]
changed: [192.168.2.145]
changed: [192.168.2.146]

TASK [Durchbooten um die FSTAB EintrÀge zu aktivieren] *****************************************************************************************************
changed: [192.168.2.144]
changed: [192.168.2.146]
changed: [192.168.2.143]
changed: [192.168.2.145]
        to retry, use: --limit @/home/ansible/preinstallation_glusterfs.retry

PLAY RECAP *************************************************************************************************************************************************
192.168.2.137              : ok=4    changed=0    unreachable=0    failed=1   
192.168.2.143              : ok=11   changed=10   unreachable=0    failed=0   
192.168.2.144              : ok=11   changed=10   unreachable=0    failed=0   
192.168.2.145              : ok=11   changed=10   unreachable=0    failed=0   
192.168.2.146              : ok=11   changed=10   unreachable=0    failed=0   
192.168.2.25               : ok=4    changed=1    unreachable=0    failed=1   

[ansible@barney ~]$ 

Ansible auf Linux Arch installieren

Die Installation von Ansible auf Linux Arch ist weitestgehend ein No-Brainer. FĂŒr das Ansible selber ist das Paket “ansible” notwendig. Wer, wie ich, seine Playbooks checken lassen möchte, installiert das Paket “ansible-lint” einfach mit.

[root@barney ansible]# pacman -S ansible ansible-lint
Löse AbhÀngigkeiten auf...
Suche nach in Konflikt stehenden Paketen...

Pakete (3) python-ruamel-yaml-0.15.89-1  ansible-2.7.9-1  ansible-lint-4.1.0-1

GesamtgrĂ¶ĂŸe des Downloads:            0,33 MiB
GesamtgrĂ¶ĂŸe der installierten Pakete:  81,56 MiB

:: Installation fortsetzen? [J/n] 
:: Empfange Pakete...
 python-ruamel-yaml-0.15.89-1-x86_64                                                                                                                                  279,1 KiB  5,80M/s 00:00 [#######################################################################################################################] 100%
 ansible-lint-4.1.0-1-any                                                                                                                                              60,9 KiB  0,00B/s 00:00 [#######################################################################################################################] 100%
(3/3) PrĂŒfe SchlĂŒssel im SchlĂŒsselring                                                                                                                                                         [#######################################################################################################################] 100%
(3/3) ÜberprĂŒfe Paket-IntegritĂ€t                                                                                                                                                               [#######################################################################################################################] 100%
(3/3) Lade Paket-Dateien                                                                                                                                                                       [#######################################################################################################################] 100%
(3/3) PrĂŒfe auf Dateikonflikte                                                                                                                                                                 [#######################################################################################################################] 100%
(3/3) ÜberprĂŒfe verfĂŒgbaren Festplattenspeicher                                                                                                                                                [#######################################################################################################################] 100%
:: Verarbeite PaketÀnderungen...
(1/3) Installiere ansible                                                                                                                                                                      [#######################################################################################################################] 100%
Optionale AbhĂ€ngigkeiten fĂŒr ansible
    sshpass: for ssh connections with password
    python-passlib: crypt values for vars_prompt
    python-pyopenssl: openssl modules
    python-netaddr: for the ipaddr filter
    python-systemd: log to journal
    python-pywinrm: connect to Windows machines
    python-dnspython: for dig lookup
    python-ovirt-engine-sdk: ovirt support
    python-boto3: aws_s3 module
    python-jmespath: json_query support
    acme-tiny: openssl_certificate module
(2/3) Installiere python-ruamel-yaml                                                                                                                                                           [#######################################################################################################################] 100%
(3/3) Installiere ansible-lint                                                                                                                                                                 [#######################################################################################################################] 100%
:: Starte post-transaction hooks...
(1/1) Arming ConditionNeedsUpdate...
[root@barney ansible]# 

Nachdem Installieren ist es wichtig seine zu managenden Nodes in der /etc/ansible/hosts einzutragen oder wer eine erheblich grĂ¶ĂŸere Infrastruktur hat, nutzt die Variante mit den Unterverzeichnissen. FĂŒr mich reicht die Variante mit den Textdateien allemale aus. Na dann trage ich mal die Nodes ein.

[root@barney bak]# nano /etc/ansible/hosts 

[glusterfs]
192.168.2.143
192.168.2.144
192.168.2.145
192.168.2.146
192.168.2.25

Damit sind die fĂŒnf Nodes des zu bauenden GlusterFS eingetragen und ich prĂŒfe auch gleich mal, ob sie fĂŒr Ansible erreichbar sind, in dem in den klassichen PING Test mache.

[ansible@barney ~]$ ansible all -m ping
192.168.2.144 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.2.146 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.2.143 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.2.145 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.2.25 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
[ansible@barney ~]$ 

LĂ€uft ja wie geschmiert. Alle fĂŒnf Odroid-HC2 melden sich, ergo kann sich Ansible ĂŒber SSH einloggen und Python Befehle absetzen. Dann werde ich den fĂŒnf mal ein kleines Playbook vorspielen lassen, was sie zum Software-Update animieren sollte. Das Playbook ist ganz kurz und stĂ¶ĂŸt nur lokal den Update selbst an.

[ansible@barney ~]$ cat syu.yml 
---
- name: All hosts up-to-date
  hosts: glusterfs
  become: yes
  
  tasks:
    - name: full system upgrade
      pacman:
        update_cache: yes
        upgrade: yes

[ansible@barney ~]$ 

Fertig ist das Playbook. Ich habe das Playbook einmal mittels ansible-lint geprĂŒft.

[ansible@barney ~]$ ansible-lint syu.yml 
[201] Trailing whitespace
syu.yml:5
  

[ansible@barney ~]$

Ja okay, es sind hinter dem Text tatĂ€schlich noch Leerzeichen drin, kann ich mir verkneifen – ist aber funktional irrelevant. Na dann mal sehen, was die Nodes zu dem Playbook sagen..

[ansible@barney ~]$ ansible-playbook syu.yml

PLAY [All hosts up-to-date] ******************************************************

TASK [Gathering Facts] ***********************************************************
ok: [192.168.2.146]
ok: [192.168.2.143]
ok: [192.168.2.144]
ok: [192.168.2.145]
ok: [192.168.2.25]

TASK [full system upgrade] **************************************************************************************************************************************************************************************************************************************************************************************************
changed: [192.168.2.146]
changed: [192.168.2.144]
changed: [192.168.2.143]
changed: [192.168.2.145]
changed: [192.168.2.25]

PLAY RECAP ******************************************************************************************************************************************************************************************************************************************************************************************************************
192.168.2.143              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.144              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.145              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.146              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   

[ansible@barney ~]$ 

Ich freue mich, dass alle fĂŒnf sich neue Software geholt haben und der Software Update gut durch lief. NatĂŒrlich kann man da noch deutlich mehr steuern, aber fĂŒr den ersten Test reicht mir das aus. Nach dem Software Update wollte ich auch noch probieren, ob ich alle fĂŒnf gleichzeitig Rebooten kann. DafĂŒr habe ich ebenso ein Playbook geschrieben. Auch kein weltbewegendes Zeug.

[ansible@barney ~]$ cat reboot.yml 
---
- name: All hosts reboot
  hosts: glusterfs
  become: yes
  
  tasks:
    - name: Unconditionally reboot the machine with all defaults
      reboot:

[ansible@barney ~]$ 

Und das Playbook werfe ich jetzt den fĂŒnf Nodes mal vor die FĂŒĂŸe und wenn ich das richtig Verstanden habe, rebooten alle fĂŒnf Nodes.

[ansible@barney ~]$ ansible-playbook reboot.yml 

PLAY [All hosts reboot] **********************************************************

TASK [Gathering Facts] ***********************************************************
ok: [192.168.2.146]
ok: [192.168.2.143]
ok: [192.168.2.144]
ok: [192.168.2.145]
ok: [192.168.2.25]

TASK [Unconditionally reboot the machine with all defaults] **********************
changed: [192.168.2.143]
changed: [192.168.2.145]
changed: [192.168.2.146]
changed: [192.168.2.144]
changed: [192.168.2.25]

PLAY RECAP ***********************************************************************
192.168.2.143              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.144              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.145              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.146              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.25               : ok=2    changed=1    unreachable=0    failed=0   

[ansible@barney ~]$ 

Ist schon ein merkwĂŒrdiges GefĂŒhl, wenn da fĂŒnf Nodes gleichzeitig rebooten und man das Anlaufen ihrer HDDs hören kann.

FĂŒr meinen ersten Ansible Test bin ich zufrieden. In dem nĂ€chsten Beitrag werde ich mich StĂŒck fĂŒr StĂŒck an das automatische Aufsetzen des GlusterFS machen. Denn wenn spĂ€ter weitere Nodes dazu kommen, möchte ich das ja genau mit Playbooks ausfĂŒhren.

Linux Arch Installation auf den SDS Odroid-HC2 Nodes

Nachdem das PowerSupply und das Chassis fĂŒr die Odroid-HC2 Bricks fertig ist, habe ich mich jetzt dem Software Defined Storage selbst zugewendet. Beim Formatieren der 10 microSD Karten viel mir sofort auf, das ist ein Anwendungsfall fĂŒr eine Automatisierung. Leider ist das Installieren bis zum Aufsatzpunkt einer Automatisierung sehr Zeit intensiv.

Also fange ich mal an 10 microSD Karten platt zu machen, damit sie jungfrÀulich an den Start gehen.

[eric@Wintermute ~]$ sudo dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=8
8+0 DatensÀtze ein
8+0 DatensÀtze aus
8388608 bytes (8,4 MB, 8,0 MiB) copied, 0,131692 s, 63,7 MB/s
[eric@Wintermute ~]$ sync

Als nĂ€chstes hole ich mir ein Linux Arch Image fĂŒr die kleinen Bricks. Dankenswerterweise hat sich das Team von Arch Linux ARM die MĂŒhe gemacht, Images fĂŒr die Installation von Linux Arch im monitorless Mode zu bauen, so daß ich die Installation nicht von Hand machen muss. Das hat bei Linux Arch immer etwas von TierquĂ€lerei..

Das Image und die genaue ErlÀuterung in englisch findet ihr unter:

https://archlinuxarm.org/platforms/armv7/samsung/odroid-hc2

Allerdings werde ich die 10 microSD Karten nicht einzeln nach dem Verfahren generieren und dann fertig machen bis Ansible sich einloggen kann. Da sitze ich ja in Tagen noch hier. Ich werde eine microSD Karte fertig machen und diese dann clonen. Also hole ich mir mal ein aktuelles Linux Arch Image fĂŒr eine Installation ohne Monitor!

[eric@Wintermute ~]$ wget http://os.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz
--2019-03-25 19:00:32--  http://os.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz
Auflösen des Hostnamens os.archlinuxarm.org (os.archlinuxarm.org)
 50.116.36.110
Verbindungsaufbau zu os.archlinuxarm.org (os.archlinuxarm.org)|50.116.36.110|:80 
 verbunden.
HTTP-Anforderung gesendet, auf Antwort wird gewartet 
 302 Found
Platz: http://de4.mirror.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz [folgend]
--2019-03-25 19:00:32--  http://de4.mirror.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz
Auflösen des Hostnamens de4.mirror.archlinuxarm.org (de4.mirror.archlinuxarm.org)
 213.202.193.253
Verbindungsaufbau zu de4.mirror.archlinuxarm.org (de4.mirror.archlinuxarm.org)|213.202.193.253|:80 
 verbunden.
HTTP-Anforderung gesendet, auf Antwort wird gewartet 
 200 OK
LĂ€nge: 405576169 (387M) [application/octet-stream]
Wird in »ArchLinuxARM-odroid-xu3-latest.tar.gz« gespeichert.

ArchLinuxARM-odroid-xu3-latest.tar.gz 100%[======================================================================>] 386,79M  5,65MB/s    in 69s      

2019-03-25 19:01:42 (5,59 MB/s) - »ArchLinuxARM-odroid-xu3-latest.tar.gz« gespeichert [405576169/405576169]

Jetzt noch flott checken, ob mir da niemand MĂŒll angedreht hat. FĂŒr mich reicht da ein md5 check, wer es genauer mag und ganz sicher sein möchte, findet auf dem Server auch gpg .tar.gz.sig File zu dem Archiv.

[eric@Wintermute ~]$ wget http://os.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz.md5
--2019-03-25 19:08:44--  http://os.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz.md5
Auflösen des Hostnamens os.archlinuxarm.org (os.archlinuxarm.org)
 50.116.36.110
Verbindungsaufbau zu os.archlinuxarm.org (os.archlinuxarm.org)|50.116.36.110|:80 
 verbunden.
HTTP-Anforderung gesendet, auf Antwort wird gewartet 
 302 Found
Platz: http://de6.mirror.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz.md5 [folgend]
--2019-03-25 19:08:44--  http://de6.mirror.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz.md5
Auflösen des Hostnamens de6.mirror.archlinuxarm.org (de6.mirror.archlinuxarm.org)
 2a01:4f8:c17:5ade::f00, 138.201.244.244
Verbindungsaufbau zu de6.mirror.archlinuxarm.org (de6.mirror.archlinuxarm.org)|2a01:4f8:c17:5ade::f00|:80 
 verbunden.
HTTP-Anforderung gesendet, auf Antwort wird gewartet 
 200 OK
LĂ€nge: 72 [application/octet-stream]
Wird in »ArchLinuxARM-odroid-xu3-latest.tar.gz.md5« gespeichert.

ArchLinuxARM-odroid-xu3-latest.tar.gz 100%[======================================================================>]      72  --.-KB/s    in 0s      

2019-03-25 19:08:44 (4,79 MB/s) - »ArchLinuxARM-odroid-xu3-latest.tar.gz.md5« gespeichert [72/72]

Jetzt noch eben md5sum gegen das Archiv laufen lassen und auf ein OK warten.

[eric@Wintermute ~]$ md5sum -c ArchLinuxARM-odroid-xu3-latest.tar.gz.md5
ArchLinuxARM-odroid-xu3-latest.tar.gz: OK
[eric@Wintermute ~]$ 

Md5sum hat den gepackten Tarball fĂŒr verdaulich erachtet. Es kann also weiter gehen mit der Installation auf einer microSD Karte. Diese werde ich dann wie gesagt clonen.

Ich richte mich dabei nach der orginalen Anleitung, auf der oben verlinkten Website. Dann formatiere ich also mal die microSD Karte.

[eric@Wintermute ~]$ sudo fdisk /dev/mmcblk0

Willkommen bei fdisk (util-linux 2.33.1).
Änderungen werden vorerst nur im Speicher vorgenommen, bis Sie sich
entscheiden, sie zu schreiben.
Seien Sie vorsichtig, bevor Sie den Schreibbefehl anwenden.

GerÀt enthÀlt keine erkennbare Partitionstabelle.
Eine neue DOS-Festplattenbezeichnung 0x5c6a4f4f wurde erstellt.

Befehl (m fĂŒr Hilfe): o
Eine neue DOS-Festplattenbezeichnung 0xf4792cc4 wurde erstellt.

Befehl (m fĂŒr Hilfe): p
Festplatte /dev/mmcblk0: 14,9 GiB, 15931539456 Bytes, 31116288 Sektoren
Einheiten: Sektoren von 1 * 512 = 512 Bytes
SektorgrĂ¶ĂŸe (logisch/physikalisch): 512 Bytes / 512 Bytes
E/A-GrĂ¶ĂŸe (minimal/optimal): 512 Bytes / 512 Bytes
Festplattenbezeichnungstyp: dos
Festplattenbezeichner: 0xf4792cc4

Befehl (m fĂŒr Hilfe): n
Partitionstyp
   p   PrimÀr (0 primÀr, 0 erweitert, 4 frei)
   e   Erweitert (Container fĂŒr logische Partitionen)
WĂ€hlen (Vorgabe p): p
Partitionsnummer (1-4, Vorgabe 1): 1
Erster Sektor (2048-31116287, Vorgabe 2048): 4096
Last sector, +/-sectors or +/-size{K,M,G,T,P} (4096-31116287, Vorgabe 31116287): 

Eine neue Partition 1 des Typs „Linux“ und der GrĂ¶ĂŸe 14,9 GiB wurde erstellt.

Befehl (m fĂŒr Hilfe): w
Die Partitionstabelle wurde verÀndert.
ioctl() wird aufgerufen, um die Partitionstabelle neu einzulesen.
Festplatten werden synchronisiert.

[eric@Wintermute ~]$ 

Sehen wir uns das mal genauer an, was FDisk da draus gemacht hat. Meine Erwartung ist ein nicht gemountet Blockdevice unter /dev/mmcblk0 mit einer Partition unter /dev/mmcblk0p1.

[eric@Wintermute ~]$ lsblk -TJ /dev/mmcblk0
{
   "blockdevices": [
      {"name":"mmcblk0", "maj:min":"179:0", "rm":false, "size":"14,9G", "ro":false, "type":"disk", "mountpoint":null,
         "children": [
            {"name":"mmcblk0p1", "maj:min":"179:1", "rm":false, "size":"14,9G", "ro":false, "type":"part", "mountpoint":null}
         ]
      }
   ]
}

Schaut soweit gut aus, die Partition ist genau da, wo ich sie erwartet habe. Dann geht es jetzt daran ein Filesystem auf der Partition zu installieren. Nach der Anleitung wird es ein ext4fs, womit ich fĂŒr “/” leben kann. Allerdings arbeite ich persönlich lieber mit Labeln bei den Partitionen anstelle ihrer Position im /dev Baum. Meine Root Partion bekommt folglich auch das Label ROOT.

[eric@Wintermute ~]$ sudo mkfs.ext4 -v /dev/mmcblk0p1 -L ROOT
mke2fs 1.45.0 (6-Mar-2019)
Dateisystemtypen fĂŒr das AufschlĂŒsseln von mke2fs.conf: 'ext4'
GerÀteblöcke werden verworfen: erledigt                        
Dateisystembezeichnung=ROOT
OS-Typ: Linux
BlockgrĂ¶ĂŸe=4096 (log=2)
FragmentgrĂ¶ĂŸe=4096 (log=2)
Stride=0 Blöcke, Stripebreite=0 Blöcke
972944 Inodes, 3889024 Blöcke
194451 Blöcke (5.00%) reserviert fĂŒr den Superuser
Erster Datenblock=0
Maximale Dateisystem-Blöcke=2151677952
119 Blockgruppen
32768 Blöcke pro Gruppe, 32768 Fragmente pro Gruppe
8176 Inodes pro Gruppe
UUID des Dateisystems: 5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81
Superblock-Sicherungskopien gespeichert in den Blöcken: 
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208

beim Anfordern von Speicher fĂŒr die Gruppentabellen: erledigt                        
Inode-Tabellen werden geschrieben: erledigt                        
Das Journal (16384 Blöcke) wird angelegt: erledigt
Die Superblöcke und die Informationen ĂŒber die Dateisystemnutzung werden
geschrieben: erledigt

[eric@Wintermute ~]$ 

Na das lief doch wie gewĂŒnscht gut durch, es gibt jetzt also auf /dev/mmcblk0 eine Partition /dev/mmcblk0p1 mit ext4 Filesystem. Dann mounten wir die mal ins bestehende Filesystem hinein, um Daten darauf schreiben zu können.

[eric@Wintermute ~]$ mkdir root
[eric@Wintermute ~]$ sudo mount /dev/mmcblk0p1 root

Ist ohne Wiederworte eingehÀngt. Jetzt kommt der Teil mit dem Daten aus dem gezippten Tarball auspacken. Dabei ist es wichtig wirklich ROOT zu sein, ein sudo hilft hier nicht.

[eric@Wintermute ~]$ su
Passwort: ALIGATOR3 

root@Wintermute eric]# bsdtar -vxpf ArchLinuxARM-odroid-xu3-latest.tar.gz -C root
x ./bin
x ./boot/
x ./boot/tzsw.bin
x ./boot/dtbs/
x ./boot/initramfs-linux.img
x ./boot/sd_fusing.sh
x ./boot/mkscr
x ./boot/boot.txt
x ./boot/zImage
x ./boot/u-boot.bin
x ./boot/bl2.bin
<snip>
x ./var/db/nscd/
x ./var/db/Makefile
x ./var/log/lastlog
x ./var/log/wtmp
x ./var/log/journal/
x ./var/log/audit/
x ./var/log/btmp
x ./var/log/private/
x ./var/log/old/
x ./var/log/journal/remote/
[root@Wintermute eric]# 

Nicht erschrecken, wenn du das selber machst, aus dem Tarball plumpst ein ganzes Linuxsystem raus. 🙂

Damit der Odroid-HC2 von der microSD Karte booten kann, ist noch etwas Bootcode im Bootsektor der microSD Karte notwendig. Den schaufeln wir da per mitgeliefertem Script hin.

[root@Wintermute eric]# cd root
[root@Wintermute root]# cd boot

[root@Wintermute boot]# pwd
/home/eric/root/boot

[root@Wintermute boot]# sh sd_fusing.sh /dev/mmcblk0
/dev/mmcblk0 reader is identified.
BL1 fusing
30+0 DatensÀtze ein
30+0 DatensÀtze aus
15360 bytes (15 kB, 15 KiB) copied, 0,0379681 s, 405 kB/s
BL2 fusing
28+1 DatensÀtze ein
28+1 DatensÀtze aus
14592 bytes (15 kB, 14 KiB) copied, 0,111764 s, 131 kB/s
u-boot fusing
1123+1 DatensÀtze ein
1123+1 DatensÀtze aus
575173 bytes (575 kB, 562 KiB) copied, 1,65022 s, 349 kB/s
TrustZone S/W fusing
512+0 DatensÀtze ein
512+0 DatensÀtze aus
262144 bytes (262 kB, 256 KiB) copied, 0,574115 s, 457 kB/s
U-boot image is fused successfully.
[root@Wintermute boot]# 

Selbst nach etlichen Jahren mit Linux mag ich so ein Bootcode rumgeschraube nicht wirklich..

Nach dem Bootcode Übertragen ist das Linux Arch auf der microSD Karte drauf und ich nehme sie wieder aus dem Filesystem raus.

[root@Wintermute boot]# cd ../..
[root@Wintermute eric]# pwd
/home/eric
[root@Wintermute eric]# umount root
[root@Wintermute eric]# rmdir root
[root@Wintermute eric]# 

Noch ein letzter prĂŒfender Blick auf die microSD Karte.

[root@Wintermute eric]# lsblk -f /dev/mmcblk0
NAME        FSTYPE LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
mmcblk0                                                                      
└─mmcblk0p1 ext4   ROOT  5ab9cbdc-0a5f-4fe8-8910-afdda9eafb81                
[root@Wintermute eric]# 

Es sieht gut aus und die microSD Karte kann in einen Odroid-HC2, um dann das Linux Arch von der Karte das erstemal zu Booten und dem System alle notwendigen Pakete fĂŒr eine weiteres Setup mit Ansible zu verpassen.

Nachdem das Lampenspiel an dem Odroid-HC2 darauf schließen liess, das er online ist, habe ich auf meinem DHCP Server mal nachgesehen und tatsĂ€chlich, da ist eine 192.168.2.146 aufgetaucht. Eine kurze Kontrolle, ob die IPv4 Adresse auch lebt.

[root@Wintermute eric]# ping 192.168.2.146
PING 192.168.2.146 (192.168.2.146) 56(84) bytes of data.
64 bytes from 192.168.2.146: icmp_seq=1 ttl=64 time=1.44 ms
64 bytes from 192.168.2.146: icmp_seq=2 ttl=64 time=0.566 ms
64 bytes from 192.168.2.146: icmp_seq=3 ttl=64 time=0.733 ms
64 bytes from 192.168.2.146: icmp_seq=4 ttl=64 time=0.619 ms
^X^C
--- 192.168.2.146 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 24ms
rtt min/avg/max/mdev = 0.566/0.840/1.442/0.352 ms
[root@Wintermute eric]# 

Es scheint dem Odroid-HC2 gut zu gehen, zumindest antwortet jemand auf das Geklopfe an der Netzwerkkarte. Laut Anleitung ist er per SSH unter dem User alarm, mit dem Passwort alarm erreichbar. PrĂŒfen wir das mal und sehen auch gleich mal nach der Linux Version und den CPU Kernen des Odroid-HC2.

[root@Wintermute eric]# ssh -l alarm 192.168.2.146
alarm@192.168.2.146's password: alarm

[alarm@alarm ~]$ cat /proc/version
Linux version 4.14.102-1-ARCH (builduser@leming) (gcc version 8.2.1 20181127 (GCC)) #1 SMP PREEMPT Sat Feb 23 19:59:09 UTC 2019

[alarm@alarm ~]$ cat /proc/cpuinfo 
processor       : 0
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 90.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 1
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 90.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 2
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 90.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 3
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 90.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 4
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

processor       : 5
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

processor       : 6
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

processor       : 7
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

Hardware        : SAMSUNG EXYNOS (Flattened Device Tree)
Revision        : 0100
Serial          : 0000000000000000
[alarm@alarm ~]$ 

Es ist schon sehr verblĂŒffend, dass dieses kleine Board in dem Odroid-HC2 mit 8 Kernen daher kommt. Eine wirklich sehr potente Plattform!

Weiter geht es mit dem Anlegen und Abgleichen des Pacman SchlĂŒsselrings, in welchem die VertrauenswĂŒrdigkeiten zu den Softwareentwicklern liegen.

[alarm@alarm ~]$ pacman-key --init
==> ERROR: pacman-key needs to be run as root for this operation.

[alarm@alarm ~]$ su
Password: root

[root@alarm alarm]# pacman-key --init
gpg: /etc/pacman.d/gnupg/trustdb.gpg: trustdb created
gpg: no ultimately trusted keys found
gpg: starting migration from earlier GnuPG versions
gpg: porting secret keys from '/etc/pacman.d/gnupg/secring.gpg' to gpg-agent
gpg: migration succeeded
gpg: Generating pacman keyring master key...
gpg: key 2AC1C92126FEB3C8 marked as ultimately trusted
gpg: directory '/etc/pacman.d/gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/etc/pacman.d/gnupg/openpgp-revocs.d/3189C9A002F783227C42CD222AC1C92126FEB3C8.rev'
gpg: Done
==> Updating trust database...
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u

[root@alarm alarm]# pacman-key --populate archlinuxarm
==> Appending keys from archlinuxarm.gpg...
==> Locally signing trusted keys in keyring...
  -> Locally signing key 69DD6C8FD314223E14362848BF7EEF7A9C6B5765...
  -> Locally signing key 02922214DE8981D14DC2ACABBC704E86B823CD25...
  -> Locally signing key 9D22B7BB678DC056B1F7723CB55C5315DCD9EE1A...
==> Importing owner trust values...
gpg: setting ownertrust to 4
gpg: inserting ownertrust of 4
gpg: setting ownertrust to 4
==> Updating trust database...
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   3  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: depth: 1  valid:   3  signed:   1  trust: 0-, 0q, 0n, 3m, 0f, 0u
gpg: depth: 2  valid:   1  signed:   0  trust: 1-, 0q, 0n, 0m, 0f, 0u
[root@alarm alarm]# 

Wenn ich schonmal auf dem Odroid-HC2 bin, mache ich auch gleich einen Softwareupdate. Vorweg sei gesagt, dass ich erstaunt bin wie wenig er sich aus dem Repository abholt. Die Images scheinen sehr gut gepflegt zu werden.

[root@alarm alarm]# pacman -Syu
:: Synchronizing package databases...
 core                                                            215.4 KiB  2.50M/s 00:00 [####################################################] 100%
 extra                                                             2.2 MiB  5.96M/s 00:00 [####################################################] 100%
 community                                                         5.1 MiB  4.70M/s 00:01 [####################################################] 100%
 alarm                                                           117.3 KiB  3.18M/s 00:00 [####################################################] 100%
 aur                                                               5.2 KiB  0.00B/s 00:00 [####################################################] 100%
:: Starting full system upgrade...
resolving dependencies...
looking for conflicting packages...

Packages (15) bash-5.0.002-1  ca-certificates-mozilla-3.43-1  coreutils-8.31-1  e2fsprogs-1.45.0-1  glib2-2.60.0-1  gnupg-2.2.14-1
              iana-etc-20190308-1  iproute2-5.0.0-1  libgpg-error-1.36-1  libseccomp-2.4.0-1  libssh2-1.8.1-1  linux-firmware-20190313.efd2c1c-1
              linux-odroid-xu3-4.14.107-1  s-nail-14.9.13-2  texinfo-6.6-1

Total Download Size:   107.30 MiB
Total Installed Size:  559.27 MiB
Net Upgrade Size:        1.78 MiB

:: Proceed with installation? [Y/n] 
:: Retrieving packages...
 iana-etc-20190308-1-any                                         

<snip>

==> Creating gzip-compressed initcpio image: /boot/initramfs-linux.img
==> Image generation successful
(2/7) Reloading system manager configuration...
(3/7) Creating temporary files...
(4/7) Reloading device manager configuration...
(5/7) Arming ConditionNeedsUpdate...
(6/7) Updating the info directory file...
(7/7) Rebuilding certificate stores...
[root@alarm alarm]# 

Damit Ansible zum Fliegen kommt, benötige ich noch die Pakete Python und Sudo. Welche ich gleich installiere, dabei wird Pacman die AbhÀngigkeiten automatisch Auflösen und die notwendigen weiteren Pakete selbststÀndig nachinstallieren.

[root@alarm alarm]# pacman -S sudo python
resolving dependencies...
looking for conflicting packages...

Packages (3) libnsl-1.2.0-1  python-3.7.2-3  sudo-1.8.27-1

Total Download Size:    16.42 MiB
Total Installed Size:  111.02 MiB

:: Proceed with installation? [Y/n] 
:: Retrieving packages...
 sudo-1.8.27-1-armv7h                                            715.7 KiB  10.9M/s 00:00 [####################################################] 100%
 libnsl-1.2.0-1-armv7h                                            51.2 KiB  2.50M/s 00:00 [####################################################] 100%
 python-3.7.2-3-armv7h                                            15.7 MiB  12.2M/s 00:01 [####################################################] 100%
(3/3) checking keys in keyring                                                            [####################################################] 100%
(3/3) checking package integrity                                                          [####################################################] 100%
(3/3) loading package files                                                               [####################################################] 100%
(3/3) checking for file conflicts                                                         [####################################################] 100%
(3/3) checking available disk space                                                       [####################################################] 100%
:: Processing package changes...
(1/3) installing sudo                                                                     [####################################################] 100%
(2/3) installing libnsl                                                                   [####################################################] 100%
(3/3) installing python                                                                   [####################################################] 100%
Optional dependencies for python
    python-setuptools
    python-pip
    sqlite [installed]
    mpdecimal: for decimal
    xz: for lzma [installed]
    tk: for tkinter
:: Running post-transaction hooks...
(1/2) Creating temporary files...
(2/2) Arming ConditionNeedsUpdate...
[root@alarm alarm]# 

Ich prĂŒfe lieber nach, was fĂŒr ein Python da genau installiert wurde. Dabei geht es mir darum, ob es sich um Python2 oder Python3 handelt.

[root@alarm alarm]# ls -la /usr/bin/python*
lrwxrwxrwx 1 root root    7 Jan 13 15:56 /usr/bin/python -> python3
lrwxrwxrwx 1 root root   14 Jan 13 15:56 /usr/bin/python-config -> python3-config
lrwxrwxrwx 1 root root    9 Jan 13 15:56 /usr/bin/python3 -> python3.7
lrwxrwxrwx 1 root root   16 Jan 13 15:56 /usr/bin/python3-config -> python3.7-config
-rwxr-xr-x 2 root root 5540 Jan 13 15:56 /usr/bin/python3.7
lrwxrwxrwx 1 root root   17 Jan 13 15:56 /usr/bin/python3.7-config -> python3.7m-config
-rwxr-xr-x 2 root root 5540 Jan 13 15:56 /usr/bin/python3.7m
-rwxr-xr-x 1 root root 3180 Jan 13 15:56 /usr/bin/python3.7m-config
[root@alarm alarm]# 

Sehr schön, es ist ein aktuelles Python3 geworden, was mich beruhigt – denn ich mag einem neuen System auch gerne ein frisches Python3. 😉

Damit Ansible auch auf dem System rumfuhrwerken kann, bedarf es eines Users mit sudo-Rechten ohne Passwort abfrage. Ich fĂŒr meinen Teil nehme gerne den User ansible und verteile den SSH SchlĂŒssel des Users ansible vom Control Server (master) auf die zu betreuenden Systeme (nodes), aber da gibt es verschiedene AnsĂ€tze.

[root@alarm alarm]# useradd -m -g users -s /bin/bash ansible
       
[root@alarm alarm]# passwd ansible
New password: SUPERGEHEIM 
Retype new password: SUPERGEHEIM

passwd: password updated successfully
[root@alarm alarm]# 

Okay, fĂŒr den ein oder anderen stellt sich die Frage wie kriege ich diese SSH-Keys vom Master zu den Nodes. Hier eine kurze Darstellung des Ablaufs auf dem Master, ab der Stelle… es gibt keinen User ansible auf der Master. 😉

eric@barney ~]$ sudo useradd -m -g users -s /bin/bash ansible
[sudo] Passwort fĂŒr eric: 

[eric@barney ~]$ sudo passwd ansible
Geben Sie ein neues Passwort ein: SUPERGEHEIM 
Geben Sie das neue Passwort erneut ein: SUPERGEHEIM
passwd: Passwort erfolgreich geÀndert

[ansible@barney ~]$ ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/ansible/.ssh/id_ed25519): 
Created directory '/home/ansible/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/ansible/.ssh/id_ed25519.
Your public key has been saved in /home/ansible/.ssh/id_ed25519.pub.
The key fingerprint is:
SHA256:B4A2txMe0ho2TcF0S4gMH3BIxJhvrIjra1JpuWF4WYw ansible@barney
The key's randomart image is:
+--[ED25519 256]--+
<snip>
+----[SHA256]-----+


[ansible@barney ~]$ ls -la .ssh/
insgesamt 16
drwx------ 2 ansible users 4096 25. MĂ€r 19:58 .
drwx------ 3 ansible users 4096 25. MĂ€r 19:57 ..
-rw------- 1 ansible users  411 25. MĂ€r 19:58 id_ed25519
-rw-r--r-- 1 ansible users   96 25. MĂ€r 19:58 id_ed25519.pub

[ansible@barney ~]$ ssh-copy-id -i ~/.ssh/id_ed25519.pub ansible@192.168.2.146
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/ansible/.ssh/id_ed25519.pub"
The authenticity of host '192.168.2.146 (192.168.2.146)' can't be established.
ECDSA key fingerprint is SHA256:tatuetata.
Are you sure you want to continue connecting (yes/no)? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
ansible@192.168.2.146's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'ansible@192.168.2.146'"
and check to make sure that only the key(s) you wanted were added.

[ansible@barney ~]$ ssh ansible@192.168.2.146

[ansible@alarm ~]$ ls -la
total 24
drwx------ 3 ansible users 4096 Mar 25 20:01 .
drwxr-xr-x 4 root    root  4096 Mar 25 19:50 ..
-rw-r--r-- 1 ansible users   21 Mar 16 15:27 .bash_logout
-rw-r--r-- 1 ansible users   57 Mar 16 15:27 .bash_profile
-rw-r--r-- 1 ansible users  141 Mar 16 15:27 .bashrc
drwx------ 2 ansible users 4096 Mar 25 20:01 .ssh
[ansible@alarm ~]$ 

Der User ansible kann sich ohne Passwort vom Master im Node, mittels SSH einloggen. Jetzt fehlen nur noch die sudo Rechte fĂŒr den User ansible, damit der Ansible Master auf dem Node auch Änderungen am System vornehmen kann.

[ansible@alarm ~]$ su
Password: GEHEIM

[root@alarm ansible]# visudo

ansible ALL=(ALL) NOPASSWD: ALL

[root@alarm ansible]# visudo -c    
/etc/sudoers: parsed OK
[root@alarm ansible]#       

Wie immer erfolgt danach eine kurze Kontrolle, ob der User sich einloggen und auch tatsĂ€chlich ohne Passwortabfrage sudo ausfĂŒhren darf.

[ansible@barney ~]$ ssh ansible@192.168.2.146
Last login: Mon Mar 25 20:15:11 2019 from 192.168.2.10

[ansible@alarm ~]$ ls -lah /root/
ls: cannot open directory '/root/': Permission denied

[ansible@alarm ~]$ sudo ls -lah /root/
total 16K
drwxr-x---  3 root root 4.0K Mar 25 19:55 .
drwxr-xr-x 16 root root 4.0K Mar 25 18:57 ..
-rw-------  1 root root  305 Mar 25 20:15 .bash_history
drwx------  3 root root 4.0K Mar  9 01:57 .gnupg
[ansible@alarm ~]$ 

Des gewĂŒnschte Setup ist erreicht – JUPIEEE.

Die Replikation von der nun fertigen microSD Karte auf die restlichen neuen Karten kann starten. Fahren wir also sanft unseren ersten Node runter.

[ansible@alarm ~]$ sudo shutdown -P now
Connection to 192.168.2.146 closed by remote host.
Connection to 192.168.2.146 closed.
[ansible@barney ~]$ 

Anschließend ziehe ich mir von der microSD Karte ein Image mittels dd. Der Ouput beim Image ziehen sieht dann wiefolgt aus.

[root@Wintermute GlusterFS-Image]# sudo dd if=/dev/mmcblk0 of=20190325_GlusterFS-Node.img bs=4M status=progress
8824815616 bytes (8,8 GB, 8,2 GiB) copied, 110 s, 80,2 MB/s
[root@Wintermute GlusterFS-Image]#

Auf eine leere microSD Karte Schreiben sieht dann auch nicht wesentlich anders aus. Nur dann halt neunmal nach einander.

[root@Wintermute GlusterFS-Image]# sudo dd if=20190325_GlusterFS-Node.img of=/dev/mmcblk0 bs=4M status=progress
4970250240 bytes (5,0 GB, 4,6 GiB) copied, 6 s, 825 MB/s
3798+1 DatensÀtze ein
3798+1 DatensÀtze aus
15931539456 bytes (16 GB, 15 GiB) copied, 1163,94 s, 13,7 MB/s
[root@Wintermute GlusterFS-Image]#

Das einzig Blöde ist nur, dass diese microSD Karten nicht gerade Raketen beim Schreiben sind. Am Ende dauert es pro microSD Karte ~20Minuten. Zeit etwas Grand Tour zu sehen und nebenbei die ganzen Karten fertig zu machen. Mehr zum Aufsetzen des GlusterFS kommt dann im nÀchsten Beitrag.

Chassis fĂŒr ein SoftwareDefinedStorage (SDS)

Nachdem ich das Netzteil fĂŒr den SDS fertig gestellt hatte. War die Zeit fĂŒr die Verpackung der Odroid-HC2 gekommen. Diese Bilden im Verbund dann ein Speichersystem. Getest habe ich das auf der Werkbank ohne Chassis und es funktionierte erstaunlich gut. Ich werde weitere Posts zum Aufsetzen und Betrieb des Clusters schreiben, doch in diesem Post soll es erst einmal um das Chassis selbst gehen. Das Chassis soll maximal 10 StĂŒck Odroid-HC2 beherbergen, was der Strommenge nach (pro Odroid-HC2 2 Ampere) die maximale Last ist, welches mein Netzteil erzeugen kann. Ich hab mir zuerst einmal die Odroid-HC2 hingestellt und diese dann ausgemessen. Auf dem Beipackzettel zu den Odroid-HC2 stehen zwar Maße drauf, aber ich glaube nur was ich selber sehe.. bin zu lange in der IT.

FĂŒnf StĂŒck Odroid-HC2 warten auf ihr Chassis

Zu den ermittelten Maßen habe ich jeweils 2mm in jede Dimension hinzu addiert, damit etwas Spiel im Chassis vorhanden ist (und meine handwerklichen Ungenauigkeiten etwas kaschiert werden). Zuerst habe mich mir die tragenden Außenteile aus 16mm Tischlerplatte zugesĂ€gt und anschließend in die stehenden Teile 6mm Nuten geschlitzt, fĂŒr die Multiplex-Bodeplatten jedes Odroid-HC2-Faches. (Alternativ zu den Böden hatte ich ĂŒberlegt die Odroid-HC2 nur auf Leisten zu legen, was thermisch sicher vorteilhaft gewesen wĂ€re, aber die StabilitĂ€t des Chassis stark beeintrĂ€chtigt hĂ€tte).

Nachdem ich die tragenden Teile des Chassis zugeschnitten hatte, habe ich die Bodenplatten aus 6mm Birke-Multiplex ausgesĂ€gt und dabei vorne, mittels einer Ausklinkung, Platz fĂŒr die Verkabelung gelassen.

Um zu sehen wieviel Luft die einzelnen Odroid-HC2 im Chassis haben, stellte ich diese kurzerhand in das wacklige und nicht verleimte Chassis hinein. Alles etwas kippelig, aber es ging.

Lose Kontrolle der Abmaße

Die Spaltmaße und Platzreserven reichten aus, also habe ich das Chassis zusammengeschraubt. Ich habe mich bewußt gegen Fingerzinken oder Verleimung mittels einer Gehrung entschieden, da ich ggf. noch Platz zum Versetzen der SeitenwĂ€nden haben wollte.

Verschraubtes Chassis vor dem Einleimen der FÀcherböden.

Die Fachböden gleiteten gut in die Nuten hinein, so dass ich die Fachböden einleimen konnte. Nachdem Einleimen der Fachböden ist das Chassis sehr Verwindungsteif.

Eingeleimte Fachböden

Die Odroid-HC2 brauchen in dem Chassis eine Zwangsbeatmung durch LĂŒfter, andernfalls ist mit deren baldigem Ableben zu rechnen. FĂŒr die BelĂŒftung hatte ich mir 4 StĂŒck Enermax T.B. Silence 12cm (UCTB12) bestellt, welche einer Halterung/Aufnahme bedĂŒrften. Die LĂŒfter habe ich mir passend zur 12Volt Stromversorgung des Chassis ausgesucht, da ich ein weiteres Netzteil nur fĂŒr die LĂŒfter vermeiden wollte.

Ich hatte fĂŒr das Chassis vorn und hinten jeweils einen Deckel vorgesehen, welcher auf der einen Seite einmal Platz fĂŒr die LĂŒfter bieten und zum anderen als Sicht- oder Zugriffschutz des Chassis dienen sollte. Jeder Deckel wird dabei ĂŒber das Chassis gestĂŒlpt, wie der Deckel eine Schuhkartons und in diesen Deckel werden dann auf einer Seite die LĂŒfter eingelassen. Ich habe mir die Leisten der Deckel aus 10mm Birken-Multiplex und die inneren Halterungen/Querverstrebungen aus 4mm Birke-Multiplex zugesĂ€gt und die LĂŒfter eingepaßt.

Nachdem die LĂŒfter in einen Deckel eingpaßt waren habe ich probeweise mal 12Volt angelegt, um die Laufrichtung der LĂŒfter zu kontrollieren. Waren natĂŒrlich verkehrt herum eingebaut. Die LĂŒfter sollten Luft in das Chassis blasen und sie saugten…. :o)

Das die LĂŒfter saugten war nicht schlimm, da ich vor hatte das Chassis zu Lackieren und dafĂŒr sowieso alle beweglichen Teile ausgebaut werden mussten.

LĂŒfter verkehrt herum eingebaut.

In der Zwischenzeit war der Leim an den Fachböden ausgehÀrtet und ich konnte erneut die Passkontrolle der Odroid-HC2 testen. Dabei viel mir auf, dass ich zuviel Spiel hatte und die Odroid-HC2 locker durch die FÀcher schieben konnte. Aus diesem Grund habe ich hinten im Chassis Stop-Klötzchen in jedem Fach verleimt.

Stop-Klötzchen sollen das Durchschieben der Odroid-HC2 verhindern.

Nachdem der Leim nun ein paar Tage durchgetrocknet war, habe ich das Chassis aussen und die Deckel mit matt schwarzem Autolack aus der SprĂŒhdose lackiert. Sicherlich nicht die beste Variante, aber fĂŒr meine Zwecke ausreichend – bin ja kein Möbelbauer. :o)

Um den besagten Zugriffschutz in den Deckel zu integrieren, hatte ich mir aus dem Baumarkt einen 0,8mm Starken gelochten Alublechstreifen gekauft. Dieses Alublech erinnert entfernt an die Optik des alten Apple PowerPC. Das dĂŒnne Alublech hat den Vorteil, dass es sich gut mit einer Blechschere verarbeiten lĂ€ĂŸt – bei Stahlblech wĂ€re es ein ganz schlimmes gewĂŒrge geworden. Das Blech habe ich in zwei StĂŒck 30 x 30 cm mit der Blechschere geschnitten und auf die Deckel mit CA Kleber verklebt. Ich wollte hier keine Schrauben aus dem Deckel heraus stehen haben. Den durch die Löcher quellenden ĂŒberschĂŒssigen CA Kleber habe ich erst mit einem Tuch aufgenommen und das ausgehĂ€rtete Zeug spĂ€ter mit 200er Sandpapier vom Alublech abgeschliffen.

Der ganze SDS Turm ist mit Verteiler-Switch 51 cm hoch.

Fertiger SDS Turm

VSFTPD auf Linux Arch geht nicht mehr..

Wer von euch auch einen VSFTPd nutzt auf seiner NextCloud zum Ablegen von Scans von seinem MultiFunktionsPrinter/Scanner benötigt seit dem letzten Update eine eigene Datei in /etc/pam.d/vsftpd

%PAM-1.0
auth required pam_unix.so
account required pam_unix.so
password required pam_unix.so
session required pam_unix.so

In der /etc/vsftpd.conf muss dazu noch ein Eintrag gemacht werden, damit der VSFTPd dann auch die PAM nutzt.

pam_service_name=vsftpd

Powersupply fĂŒr ein SoftwareDefinedStorage (SDS)

FĂŒr mein nĂ€chstes Projekt benötige ich ein PowerSupply (PS), welches mir die Spannung und den Strom möglichst direkt anzeigt. Da schnelle, wechselnde Schwankungen auf digitalen Anzeigen immer sehr schwer ablesbar sind, habe ich mich fĂŒr analoge Drehspulinstrumente entschieden. Das Netzteil habe ich nicht selber gebaut, ich habe auf ein fertiges Netzteil zurĂŒck gegriffen, da die heutigen Wirkungsgrade bei Schaltnetzteilen einen Eigenbau weitestgehend absurd machen. FĂŒr meinen Zweck reicht ein 12Volt 20Ampere Netzteil aus. Dieses PS soll am Ende den SDS, welcher aus initial aus fĂŒnf Odroid-HC2 besteht, antreiben.

Ich hab alle Bestandteile des PS auf der Werkbank nebeneinander gelegt und mir ĂŒberlegt, welches Teil ich wo im Chassis haben möchte.

Bestandteile des PoerSupplys

Ich hab dann aus einfachem Kiefernrestholz eines bekannten schwedischen Möbelhauses ein Chassis gebaut. Leider ist das Holz sehr stark ausgerissen / gesplittert. Beim nÀchsten Chassis werde ich wieder auf Multiplex setzen, dass ist wesentlich ausrissfreier.

Auf der Bodenplatte des Chassis habe ich mir markiert, wie weit die SeitenwĂ€nde in die Bodeplatte hineinragen. Über dem Netzteilmodul haben ich den Deckel des Chassis gelocht, damit die WĂ€rme nach oben abziehen kann. Das Modul habe ich auf der Bodenplatte positioniert, damit ich die Bodenplatte auch fĂŒr den WĂ€rmefluss lochen konnte.

Chassisdeckel und Netzteilmodul auf der Bodenplatte

In das Frontpanel habe ich die Drehspulinstrumente und den KaltgerÀteanschluss, welcher mit einem Schalter und einer Sicherung daher kommt, eingepasst. Der Schalter ist ein 2-poliger Schalter, so dass bei Problemen die Spannungsfreiheit sicher gestellt ist.


Frontplatte des PS Chassis

Nach dem Positionieren der einzelnen Teile im Chassis, und dem befestigen dieser, ging es an das Verkabeln der Komponenten. Ich werde hier nicht im Detail drauf eingehen und verweise darauf, dass dieses nur durch geschulte Personen gemacht werden darf! Hier wird mit 230Volt Netzspannung gearbeitet, dass ist kein Kindergeburtstag!! Ausserdem möchte ich daran erinnern, dass bei den erwarteten StromstĂ€rken von mehr als 10 Ampere, der Leitungsquerschnitt sauber berechnet werden muss und Lötverbindungen sich ausschließen – also immer schön mit Kabelschuhen arbeiten und sauber Quetschen.

Das Chassis ist somit fertig fĂŒr die Betrieb des SDS.

Stiefelknecht

Mein Freundin Maike hat mich ĂŒberredet, fĂŒr einen guten Zweck, etwas aus Holz zu Tischerln. Ich muss zugeben, dass ich mich da nicht lange ĂŒberreden lassen mußte. Wir haben hier bei uns im Dorf einmal im Jahr ein Lichterfest, auf dem man seine Sachen privat verkaufen kann. Das Geld, welches wir da in 2019 einnehmen werden, wollen wir der Bodelschwinghsche Stiftungen Bethel spenden. Nun habe ich mir ein paar Gedanken gemacht, was zum Zeitpunkt des Lichtermarktes (Herbst) wohl so von Kaufinteresse sein könnte und bin dabei zuerst auf den Stiefelknecht gekommen, nebst einiger anderer Dinge – welche so ĂŒber das Jahr hier auch gebaut und dann gepostet werden.

Wir soll so ein Stiefknecht den nun Aussehen war eine meiner ersten Fragen. Nach geraumer Zeit im Googeluniversum kam ich auf folgendes, zugegeben sehr simples, Design.

Designidee des Stiefelknechtes

Aber hey, warum etwas kompliziertes Bauen, wenn es doch “nur” beim Stiefelausziehen helfen soll ohne viel TĂŒdelĂŒt..

Als komplizierter wie erwartet haben sich die vielen spitzen Winkel im Design erwiesen. Ich hab mit hier eine Vorrichtung fĂŒr die KreissĂ€ge gebaut, damit da meine Finger nicht zu Dicht am SĂ€geblatt vorbei mussten. Ausserdem ermöglichte mir die Vorrichtung auch, dass seitliche Stehen neben der SĂ€ge, was sich bei den Kickbacks der spitzen Winkel als zwingend notwendig herausstellte.

Ich hab also zuerst einmal 10 Bretter mit den Maßen 47cm x 13,5cm aus einer 28mm Tischlerplatte heraus gesĂ€gt und mir das Design aufgemalt und probweise mit einem StĂŒck Leiste auf die Werkbank gestellt (Bild oben). Dabei kam mit die hintere Auflage auf der Brettkante als ungeeignet vor, denn das gibt bestimmt ein wackeliges GefĂŒhl beim Ausziehen der Stiefel. Also hab ich diesen Stelle abgeflacht an der KreissĂ€ge.

Abflachen der hinteren AuflageflÀche

So Stand der Stiefelknecht schon viel stabiler und das kippelige GefĂŒhl war weg.

Steht schon stabiler auf der Werkbank

Damit das Ding nicht ganz so wie ein plumpes Brett aussieht, habe ich die SÀume zur AuflageflÀche hin enger zulaufen lassen. Das waren dann die nÀchsten spitzen Winkel, welche auf der KreissÀge angefertigt wurden.

Das BesÀumen der Kannten im spitzen Winkel

Nach dem BesÀumen sah das Brett schon etwas gefÀlliger und halt auch langsam nach Stiefelknecht aus.

langsam entwickelt sich der Knecht

Die Aussparung zum Ausziehen des Stiefels hatte ich nach dem kleinsten mit ĂŒber den Weg laufenden Stiefel vorgesehen, also dem Schuh meiner LĂŒtten. Ein 51mm Lochbohrer passte ganz gut zu der GrĂ¶ĂŸe der Ferse des Stiefels. Ich hab die Bretter fĂŒr einen sicheren Halt in die Vorderzange meiner Werkbank eingespannt und ausgebohrt. Anschließend habe ich den Keil fĂŒr die grĂ¶ĂŸeren Schuhe ausgesĂ€gt.

Da die abgeflachte Stelle hinten sÀgerauh war, wie das gesammte Brett, habe ich die ganzen rauhen Stellen erstmal abgeschliffen und die Kanten mit dem AbrundfrÀser gefÀllig abgerundet. Nicht das sich spÀter beim Schuheausziehen noch jemand einen Splitter zieht.

Der Stapel der Bretter benötigte seine Zeit und wuchs auf einen kleinen Berg an. Nachdem Abschleifen und AbfrĂ€sen habe ich mir aus meinem Restholz eine Latte aus Douglasie mit 35mm x 35mm raus gesucht und den Klotz fĂŒr die vordere Auflage davon abgesĂ€gt und einen Probe Stiefelknecht fĂŒr Maike fertig gemacht. Da Maike neben ihrer Arbeit bei die Pinnebergerin auch im Rettungsdienst tĂ€tig ist, wo ja genau diese Knobelbecher getragen werden, fĂŒr die man einen Stiefelknecht gebrauchen kann und ihr zur Probe vorbei gebracht.

Der Stiefelknecht wurde von ihr fĂŒr gut befunden, was dann die fertigstellung der anderen Knechte bedeutete, nebst entsprechender Hartwachsölung, damit das Holz nicht beim ersten Wassertropfen anfĂ€ngt zu arbeiten.

Reithalle mit StÀllen

Mitte November 2018 fragte mich mein Kind, ob ich nicht fĂŒr ihre Schleich-Pferde eine Reithalle mit StĂ€llen bauen könnte. Da ich mir nicht ganz sicher war, was es meinte sind wir kurzer Hand rĂŒber zum Fuchshof gegangen, wo das Kind in der Pony Akademie Hasloh mit Feuereifer Reiten lernt, und da wurde mir dann erklĂ€rt, was alles dabei sein sollte. Die Liste wurde lang und lĂ€nger..

Zu Hause habe ich dann mal erstmal recherchiert, wie die echten Abmaße einer Reithalle sind (60m x 40m) und wie groß die Pferdeboxen der orginal Schleich-Pferde sind. Darauf folgte dann der vorsichtige Blick ins Kinderzimmer, wieviel Stauraum denn fĂŒr dieses Unterfangen zur VerfĂŒgung stehen wĂŒrde. Es zeichnete sich als Parkplatz fĂŒr dieses Möbel nur die AblageflĂ€che oben auf dem Kleiderschrank ab (100cm x 55cm), was mir im Folgenden fĂŒr die Findung der Zielmaße dienen sollte.

In Abstimmung mit dem Kind wurde die Anzahl der Boxen etwas lockerer formuliert und nicht orginal getreu vom Fuchshof ĂŒbernommen. Auch der Zugriff sollte von oben erfolgen und nicht durch die TĂŒren mit langen Arm. Nach einiger Abstimmung war der Grundriss gefunden und alles wurde ohne Dach geplant.

Grundriss der Reithalle mit StÀllen

Da ich nicht so gut im Umgang mit SketchUp bin, plane ich das WerkstĂŒck immer auf Papier und baue es von Innen nach Außen auf, so dass Ungenauigkeiten sich nach Außen Verlaufen.

Zu erst habe ich also die WĂ€nde der Reithalle angefertigt, was bei einem Maßstab von 100:1 bedeutete, dass die WĂ€nde ein Rechteck von 60cm x 36cm (eigentlich 40cm) ergeben sollten.

Als nĂ€chstes stand die Anfertigung der Pferdeboxen auf der Liste. Die Abmaße sind jetzt nicht die der echten Pferdeboxen, da ich nicht weiß ob und wie die Spielzeugpferde von Schleich sich mit den Boxen (Maßstab 100cm:1cm) im VerhĂ€ltnis stehen. Ich hab einfach die Abmaße der Spielzeugboxen genommen. Die sind ~12cm Breit und und ~15cm Tief. Die SeitwĂ€nde wollte ich etwas abgerundet und gefĂ€llig zum Spielen fĂŒr die KinderhĂ€nde haben. Also erstmal einen Prototypen angezeichnet. Da ich keine BandsĂ€ge besitze, habe ich die erste Trennwand ausgesĂ€gt und als Schablone fĂŒr die weiteren WĂ€nde genommen. Die habe ich zusammen gezwingt und dann BĂŒndig geschliffen. Ist jetzt nicht 100% identisch, aber ich finde darauf kommt es bei Handgemacht nicht wirklich an!

Die TrennwÀnde habe ich probeweise an die Reithalle gestellt, um einen Eindruck vom gesammt Bild zu bekommen.

Reithalle mit angestellten Pferdeboxen-TrennwÀnden

Um die Maße der Bodenplatte zu ermitteln, habe ich die SeitwĂ€nde angelehnt und ausgemessen, was mein tatsĂ€chliches Maß ist. Ich hatte fĂŒr die Bodenplatte 10mm oder 8mm Birke-Multiplex im Baumarkt ausgesucht. Bei den tatsĂ€chlichen Maßen habe ich mich lieber fĂŒr 8mm entschieden, sonst hĂ€tte ich wegen des gesammt Gewichtes vermutlich gleich einen Flaschenzug mit planen können..

Nach dem SĂ€gen der Bodeplatte kam das obligatorische Abschleifen, wobei ich persönlich bei Kinderspielzeug bei 180 Körnung abschließe. Soll ja kein hochglanz Schleiflack-Schrank werden.

Abgeschliffene Bodenplatte mit den ersten Pferden zu Besuch

In der Reithalle selber habe ich aus Douglasienholz die Trittschutzplatten der orginalen Reithalle nachgeahmt und den ganzen Aufbau das erstemal fliegend auf die jetzt existierende Bodenplatte gestellt. Die beiden Besucher habe ich zum GrĂ¶ĂŸenvergleich mal in zwei Boxen gestellt.

Die auf Gehrung gesĂ€gten Trittschutzleisten wurde nun innen in der Reithalle bodenbĂŒndig eingeleimt und mit ordentlich Druck verzwingt. Kleine Ungenauigkeiten habe ich dann mit Schleifpapier korrigiert.

Nachdem der Leim gut durchgetrocknet war, habe ich die Reithalle auf der Bodenplatte ausgerichtet und verleimt. Da ich keine Möglichkeit besitze eine Konstruktion mit riesen Zwingen zu Verpressen, habe ich den notwendigen Anpressdruck mit Gewichten realisiert, welche ich zur besseren Druckverteilung auf Brettern aus meiner Restholzkiste aufgelegt habe.

Beim fliegenden Aufstellen der WĂ€nde viel mir auf, dass die Aussenkanten doch recht scharf ausfallen wĂŒrden, wenn diese rechtwinklig geschnitten wĂŒrden. Das sah mir nach zuviel Verletzungspotential beim Spielen aus, also hab ich da lieber einer Radius dran gesĂ€gt und natĂŒrlich abgeschliffen. Die WĂ€nde selber haben beim erneuten Aufstellen eindeutig zu wenig AuflageflĂ€che gezeigt, um sie stabil zu Verleimen. Also hab ich 10mm x 10mm Leisten geschnitten und diese an die WĂ€nde geleimt und verpresst. Meine Erkenntnis aus dem Projekt ist ganz klar: man kann nicht genug Zwingen in der Werkstatt haben!!

Um die WÀnde beim Verleimen sicher Senkrecht ausrichten zu können, habe ich mir aus einer 22mm dicken OSB Platte Abstandsbrettchen gesÀgt. Das stellte sich im Laufe des Tages als sehr arbeitsbringende Idee heraus..

Wie sich zeigte, fanden die Kinder die OSB Platte mit ihrer Stroh Ă€hnlichen Textur so prima, dass ich mein Kind nur zum Nachbarskind sagen hörte: “ja ja, der Boden wird in jeder Box so und in der Reithalle natĂŒrlich auch, damit die Pferde weich Laufen”.

Ich fragte mich in diesem Augenblick, warum ich nicht gleich eine OSB Platte fĂŒr den Boden genommen habe, aber nun ja sei es drum. Ich fuhr also zum Baumarkt meiner Wahl und habe mir eine möglichst dĂŒnne OSB Platte (12mm) fĂŒr den Boxen- und Hallenboden raus gesucht, damit das nicht so stark auftrĂ€gt. Die OSB Platte hab ich dann fĂŒr die Reithalle und die Boxen zugesĂ€gt, die Kanten ordentlich abgerundet (wegen der Splittergefahr) und verleimt.

WĂ€hrend ich die WĂ€nde befestigte, geisterte mir die ganze Zeit ĂŒber der Gedanke durch Kopf, wie ich wohl die TĂŒren öffnen und schließen könnte in der Konstruktion. Ich fuhr also erneut zum Baumarkt meines Vertrauens und schlĂ€ndert lange durch die Reihen bis ich endlich passende Messing-Scharniere fĂŒr diese kleinen TĂŒren fand.

TĂŒrscharniere, eigentlich fĂŒr SchmuckkĂ€stchen

Die Scharniere sollen ja bei allen TĂŒren an der selben Stelle sitzen, wofĂŒr ich mir eine Bohrschablone baute. Den Stapel StallwĂ€nde und TĂŒren habe ich dann mit dieser Bohrschablone gebohrt, wobei ich die Seitenwelle meiner DekupiersĂ€ge nutzte, um den 2mm Bohrer sicher Einspannen und die Löcher gewichtsfrei Bohren zu können.

Die BoxentĂŒren aus 6mm Birke-Multiplex stellten sich als zu dĂŒnn heraus, um hier mit Schrauben zu hantieren. Das wĂŒrde innerhalb kĂŒrzester Zeit beim Spielen ausreißen. Metall auf Holz zu kleben erschien mir ebenfalls als sehr ungeeignet, da das warscheinlich ebenso schnell abgehen wĂŒrde. Mir fiel fĂŒr die bessere Kraftverteilung ein, dass eine Besfestigung mit Silberdraht und das Verlöten dieser DrĂ€hte wohl eine praktikabelere Lösung sein könnte. Ich hab das Probeweise bei einem Scharnier versucht und es klappte ganz gut. Zum Einsatz kam 0,6mm Silberdraht.

Die so erstellten TĂŒr-Seitenteil-Elemente habe ich dann mit 1-2mm Spiel zueinander ausgerichtet und verleimt.

Die SeitenwĂ€nde an den Waschboxen, vor dem Eingang zur Reithalle, habe ich am Boden ebenfalls mit Stabilisierungsleisten verleimt, damit hier auch langfristig kein Abbrechen/-reißen der WĂ€nde droht, und fluchtend verpresst.

Nach dem Anbringen der TĂŒr zur Reithalle, kamen noch die notwendigen Markierungen in der Reithalle selbst. Hier erklĂ€rte mir mein Kind dann genau, wo welche Marke hin gehört. Sicherheitshalber hab ich es mir aber nochmal aufgeschrieben. 🙂

Damit die Bodenplatte nicht gleich das Laminat ruiniert, habe ich noch FilzfĂŒĂŸe drunter geklebt.

FilzfĂŒĂŸe unter der Bodenplatte

Final kamen noch als Aufkleber die Schilder des Fuchshof und der Pony Akademie Hasloh auf das Möbel. Die Kinderaugen leuchteten bei der Übergabe und seitdem wandert das Möbel regelmĂ€ĂŸig zwischen Schrankdeckel (Parkplatz) und Zimmerboden hin und her. 🙂