Browse Category: linux

Gitlab out of date

Da will man mall in der besinnlichen Zeit sein Gitlab Server neu aufsetzen und… boing, da ist das Arch Paket aus dem Repository out of date und weigert sich wegen Ruby Problemen zu Ende zu Installieren.

Naja, warte ich bis ins neue Jahr 2020.

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.

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

GrĂ¶ĂŸe einer .vmdk unter Virtualbox anpassen

Nun war es soweit, meine .vmdk war bis zum letzten GByte voll gelaufen und leider kann man unter VirtualBox, anders als unter VMware, nicht einfach ein resize der .vmdk machen. Das geht bei VirtualBox nur ĂŒber einen kleinen Umweg.

Am besten ist es, in der VirtualBox zuerst die .vmdk in der Gastsystem zu trennen/detach. Anschließend wird die .vmdk in eine .vdi gecloned:

vboxmanage clonehd ./DISK1.vmdk clone.vdi –format vdi

Sollte er dabei zu reichlich Fehlermeldungen in der Bash folgender Art kommen:

VBoxManage: error: Cannot register the hard disk ‘/DISK1.vmdk’ {730daceb-67b6-4e5f-993a-9808fghijklmnopq because a hard disk ‘/DISK1.vmdk’ with UUID {730daceb-67b6-4e5f-993a-9808fghijklmnopq} already exists
VBoxManage: error: Details: code NS_ERROR_INVALID_ARG (0x80070057), component VirtualBoxWrap, interface IVirtualBox, callee nsISupports
VBoxManage: error: Context: “OpenMedium(Bstr(pszFilenameOrUuid).raw(), enmDevType, enmAccessMode, fForceNewUuidOnOpen, pMedium.asOutParam())” at line 179 of file VBoxManageDisk.cpp

Muss die UUID ĂŒberschrieben werden, das geht mit einem internen Befehl bei VirtualBox:

vboxmanage internalcommands sethduuid ./DISK1.vmdk

Und dann sollte das Clonen wie folgt flutschen:

vboxmanage clonehd ./DISk1.vmdk clone.vdi –format vdi

0%…10%…20%…30%…40%…50%…60%…70%…80%…90%…100%
Clone medium created in format ‘vdi’. UUID: 24605153-4237-46e5-b41f-zz1k32a38

Bei einer .vdi kann VirtualBox die GrĂ¶ĂŸe anpassen. Ich wollte 100GByte haben also lautete das bei mir wie folgt:

VBoxManage modifyhd ./clone.vdi –resize 102400

Auch hier folgte eine kleine Beschwerde auf der Bash:

VBoxManage: error: Cannot register the hard disk ‘./clone.vdi’ {24605153-4237-46e5-b41f-zz1k32a38} because a hard disk ‘/home/eric/VirtualBox VMs/Computacenter/clone.vdi’ with UUID {24605153-4237-46e5-b41f-zz1k32a38} already exists
VBoxManage: error: Details: code NS_ERROR_INVALID_ARG (0x80070057), component VirtualBoxWrap, interface IVirtualBox, callee nsISupports
VBoxManage: error: Context: “OpenMedium(Bstr(pszFilenameOrUuid).raw(), enmDevType, enmAccessMode, fForceNewUuidOnOpen, pMedium.asOutParam())” at line 179 of file VBoxManageDisk.cpp

Also erneut die UUID setzen, diesmal halt bei der .vdi Datei..

vboxmanage internalcommands sethduuid ./clone.vdi

Mir ist zwar nicht klar, warum das fĂŒr die VirtualBox jedesmal in “rum Trompeten” endet… aber nun ja. Jetzt lĂ€ĂŸt sich bei der .vdi endlich die GrĂ¶ĂŸe anpassen. In meinem Falle wie gesagt sollte die .vdi auf 100GByte erhöht werden ohne den Inhalt zu verĂ€ndern. Das geht nun endlich mittels:

VBoxManage modifyhd ./clone.vdi –resize 102400

0%…10%…20%…30%…40%…50%…60%…70%…80%…90%…100%

Kurzer Check ob alles da ist:

ls -la

insgesamt 146302296
drwx—— 4 eric users 4096 11. Jul 12:09 .
drwx—— 3 eric users 4096 11. Jul 10:33 ..
-rw——- 1 eric users 74624991232 11. Jul 12:09 DISK1.vmdk
-rw——- 1 eric users 8454 11. Jul 10:33 Arbeitsbox.vbox
-rw——- 1 eric users 75190239232 11. Jul 12:13 clone.vdi

Sieht soweit gut aus, jetzt wieder zurĂŒck von der .vdi zu einer .vmdk.

vboxmanage clonehd ./clone.vdi ./DISK2.vmdk –format vmdk

0%…10%…20%…30%…40%…50%…60%…70%…80%…90%…100%
Clone medium created in format ‘vmdk’. UUID: 8f315836-5209-48f5-a3c9-zzk3@@alpha

Die fertige .vmdk nun noch in der Arbeits.vbox an den virtuellen SATA Controller gebunden und fertig ist das Resize. Hat gute 2h gedauert… Noch aufrĂ€umen auf der HDD:

rm clone.vdi

rm DISK1.vmdk

Linux Arch Netctl

Ich hab mich von Zeit zu Zeit, also eigentlich immer nach einem Softwareupdate per pacman, darĂŒber Ă€rgern dĂŒrfen, dass das Ethernet Interface nicht mit statischer Adresse hoch kam. Es sah so aus, als ob das Interface auf dem Switch einfach zu spĂ€t seinen Link aktiviert kriegt. Das Problem lĂ€ĂŸt sich fixen, in dem man in der netctl.config des Interfaces einfach folgendes mit einfĂŒgt “SkipNoCarrier=yes” und siehe da, das Interface rappelt sich einfach etwas spĂ€ter aber mit statischer Adresse.

Terratec Noxon Radio2IPod / MPD Stream

auch kleine Erfolge bereiten mir ja immer freude. Nachdem das Setup fĂŒr den MPD/Icecast gefunden ist lĂ€uft der Stream jetzt auch problemlos in der KĂŒche auf dem guten Terratec Noxon Radio2IPod, wird auch sauber aks 256KBps Stream ĂŒber Wlan angezeigt.

Stellt sich nur noch die Frage, was stelle ich eigentlich ins Bad?

  • 1
  • 2