:: CloudSHell2 - Installation d'Archlinux ::
Introduction
Toutes les opérations décrites ci-dessous ont été faites à partir d'un portable sous Archlinux et du reader/writer eMMC se branchant sur le port SD (via un adaptateur micro-SD).
La commande lsblk
permet de récupérer le nom du périphérique, ici /dev/sdb
:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
[...]
sdb 8:16 1 116,5G 0 disk
Préparation du module eMMC
Remise à zéro
$ sudo dd if=/dev/zero of=/dev/sdb bs=1M count=8
Partitionnement
$ sudo fdisk /dev/sdb
Créer une nouvelle partition primaire vide, en tapant les commandes suivantes :
- o : crée une nouvelle table de partition DOS
- n : crée une nouvelle partition avec les paramètres suivants :
- p : type de partition primaire
- ENTER : numéro de partition (1 par défaut)
- 4096 : premier secteur
- ENTER : dernier secteur (par défaut)
- w : écrit la table de partition et quitte
Création du système de fichiers ext4
$ sudo mkfs.ext4 /dev/sdb1
Montage du système de fichiers
$ mkdir root
$ sudo mount /dev/sdb1 root
Téléchargement et extraction du système de fichier root
Téléchargement
$ wget http://os.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz
$ wget http://os.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz.md5
Vérification
$ md5sum -c ArchLinuxARM-odroid-xu3-latest.tar.gz.md5
ArchLinuxARM-odroid-xu3-latest.tar.gz: Réussi
Extraction
$ sudo bsdtar -xpf ArchLinuxARM-odroid-xu3-latest.tar.gz -C root
Flash du bootloader
$ cd root/boot/
$ sudo ./sd_fusing.sh /dev/sdb
/dev/sdb reader is identified.
[...]
U-boot image is fused successfully.
Démontage du système de fichiers
$ cd ../..
$ sudo umount root
Premier démarrage du système
- Insérer le module eMMC dans son emplacement sur la carte XU4
- Connecter le cable ethernet et brancher l'alimentation
Attendre la fin du démarrage du système puis se connecter en ssh avec le user alarm et le mot de passe alarm
$ ssh alarm@cloudshell.domaine.lan
Ouvrir une session root avec le mot de passe root
$ su - root
Gestion de paquets
Pacman
Initialisation du trousseau de clés
# pacman-key --init
Chargement du trousseau de clés
# pacman-key --populate archlinuxarm
Mise à jour du système
# pacman -Syu
Installation de paquets
# pacman -S vim sudo man-pages man-db bash-completion gptfdisk lvm2 git fakeroot binutils unzip wget highlight perl-json-xs fortune-mod uboot-tools
Arch User Repository (AUR) - Facultatif
Attention : Aucun assistant AUR n'est officiellement supporté par les développeurs d'Arch Linux pour la simple raison qu'AUR est un dépôt de paquets non officiellement supportés.
Trizen
trizen est un client pour AUR, léger et écrit en Perl.
Installation de trizen
$ git clone https://aur.archlinux.org/trizen.git
$ cd trizen
$ makepkg -si
Localisation
Fuseau horaire
# ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime
Synchronisation de l'horloge matérielle sur l'horloge système
# hwclock --systohc
Info : cette commande permet de générer le fichier /etc/adjtime
, cf. hwclock(8) section "The Adjtime File" pour plus d'information sur ce fichier.
Génération des locales
# vi /etc/locale.gen
Décommenter la ligne fr_FR.UTF-8 UTF-8
, puis générer les locales :
# locale-gen
Generating locales...
fr_FR.UTF-8... done
Generation complete.
Configuration des locales
cf. Locale Variables - ArchWiki pour plus de détails sur les différentes variables supportées.
# vi /etc/locale.conf
LANG=fr_FR.UTF-8
Clavier
# vi /etc/vconsole.conf
KEYMAP=fr
Configuration réseau
Nom d'hôte
# vi /etc/hostname
cloudshell
# vi /etc/hosts
127.0.0.1 localhost
::1 localhost
127.0.1.1 cloudshell.domaine.lan cloudshell
Adresse IP
Comme il s'agit d'un serveur installé dans mon réseau local, j'utilise DHCP pour lui assigner une adresse IP (bail statique sur le routeur) et pour diffuser différentes options. Par sécurité, un profil statique est créé dans le cas où le routeur ne serait pas disponible.
# vi /etc/dhcpcd.conf
Ajouter/modifier les lignes suivantes :
# Allow users of this group to interact with dhcpcd via the control socket.
controlgroup wheel
# Inform the DHCP server of our hostname for DDNS.
hostname
# Persist interface configuration when dhcpcd exits.
persistent
# vendorclassid is set to blank to avoid sending the default of
# dhcpcd-<version>:<os>:<machine>:<platform>
vendorclassid
# A list of options to request from the DHCP server.
option domain_name_servers, domain_name, domain_search
option classless_static_routes
# Respect the network MTU. This is applied to DHCP routes.
option interface_mtu
# Most distributions have NTP support.
option ntp_servers
# Rapid commit support.
option rapid_commit
# A ServerID is required by RFC2131.
require dhcp_server_identifier
# Generate Stable Private IPv6 Addresses based from the DUID
slaac private
noipv4ll
# Static profile
profile static_eth0
static ip_address=192.168.0.2/24
static routers=192.168.0.254
static domain_name_servers=192.168.0.254 1.1.1.1
# Fallback to static profile
interface eth0
fallback static_eth0
Mise à jour du firmware JSM561
Téléchargement de l'application de mise à jour
# wget https://wiki.odroid.com/_media/accessory/add-on_boards/xu4_cloudshell2/jms561_fw_updater_onxu4.tgz
Extraction et lancement de la mise à jour
# tar xvzf jms561_fw_updater_onxu4.tgz
# cd JMS561/
On met à jour le firmware pour chaque disque connecté, ici /dev/sda et /dev/sdb :
# ./JMS561FwUpdate -d /dev/sda -f ./jms561_Hardkernel_v158.001.000.004.bin -b ./backup.bin
# ./JMS561FwUpdate -d /dev/sdb -f ./jms561_Hardkernel_v158.001.000.004.bin -b ./backup.bin
Vérification de la version actualisée
# ./JMS561FwUpdate -d /dev/sda -v
Bridge Firmware Version: v158.1.0.4
# ./JMS561FwUpdate -d /dev/sdb -v
Bridge Firmware Version: v158.1.0.4
Installation du gestionnaire de RAID du controleur JMS56X
# wget https://wiki.odroid.com/_media/accessory/add-on_boards/xu4_cloudshell2/raidmgr_static_cloudshell2.zip
# unzip raidmgr_static_cloudshell2.zip
# chmod a+x raidmgr_static
# ./raidmgr_static
JMS56X HW RAID Manager V8.0.0.1
(C) 2008~2011 JMicron Tech, Corp. Command Line Interface RAID Manager For JMS56X.
JMS56X>
Valid commands set are:
=======================
GC ------------------------------------- Get avail JMS56X
DC C[n] -------------------------------- Display controller info
SR C[n] -------------------------------- Show avail RAID info
SS C[n] -------------------------------- Show avail SATA info
SM C[n] D[n] --------------------------- Show disk S.M.A.R.T. info
CR C[n] D[0,..,2] R0|R1|JBOD ----------- Create RAID
DR C[n] R[n] --------------------------- Delete RAID
SF C[n] -------------------------------- Show firmware version
SA C[n] -------------------------------- Set alarm mute
GR C[n] R[n] --------------------------- Get rebuilding percentage
ID C[n] D[n] --------------------------- Identify disk
ST C[n] R[n] timer --------------------- Set standby timer
AS C[n] D[n] R[n] ---------------------- Add spare disk
DS C[n] D[n] R[n] ---------------------- Delete spare disk
EX ------------------------------------- Exit
JMS56X>
Ecran LCD
Activation de l'écran
# echo "options fbtft_device name=hktft9340 busnum=1 rotate=270" > /etc/modprobe.d/cloudshell.conf
# echo "spi_s3c64xx" >> /etc/modules-load.d/fbtft_device.conf
# echo "spidev" >> /etc/modules-load.d/fbtft_device.conf
# echo "fbtft_device" >> /etc/modules-load.d/fbtft_device.conf
Script d'affichage d'infos sur l'écran LCD
Note : ce script est un exemple, adaptez-le à votre besoin
$ sudo vim /bin/cloudshell-lcd
Coller les lignes suivantes :
#!/bin/bash
## Configuration
export TERM="linux"
# console font (see /usr/share/kbd/consolefonts)
# export CONSOLE_FONT=""
# Output Console (ttyX)
export OUTPUT_CONSOLE="1"
# Network Interface: eth0, wlan0, ....
export NETIF="eth0"
# SATA HDD mount
export SATA="/dev/sd"
# Mem Card mount
export MEMCARD="/dev/mmcblk0p1"
# Screen refresh in seconds
export REFRESH="3"
# CPU Temperature in C or F
export TEMPERATURE_FORMAT="C"
# External IP Refresh counts
# The time to update the ip in counts is acquired by using the following formula
# seconds_to_refresh = EXT_IP_REFRESH * REFRESH
export EXT_IP_REFRESH="10"
# Message
#export MESSAGE=`/usr/bin/fortune`
export MESSAGE="La simplicité est la sophistication suprême - Léonard De Vinci"
get_external_ip() {
export EXTERNAL_IP=`/usr/bin/drill myip.opendns.com @resolver1.opendns.com | awk '/^myip/ {print $5}'`
}
get_full_date() {
export DATE=`/usr/bin/date +"%d-%m-%Y %H:%M:%S"`
}
get_hostname() {
export HOSTNAME=`/usr/bin/getent ahosts | awk '/127.0.1.1/ {print $2}'`
}
get_internal_ip() {
export INTERNAL_IP=`/usr/bin/ip -o -4 a | awk '/eth0/ {print $4}'`
}
get_net_tx_rx_realtime() {
net_txrx=`/usr/bin/sar -n DEV 1 1 | awk '/Moyenne.*eth0/ {gsub(",",".",$0);print $5" "$6}'`
# in MB/s
_tx=`echo $net_txrx | awk '{printf $1}'`
_rx=`echo $net_txrx | awk '{printf $2}'`
export NET_TX=`echo "scale=1; x=$_tx/1024; if(x<1) print 0; x" | bc -l`
export NET_RX=`echo "scale=1; x=$_rx/1024; if(x<1) print 0; x" | bc -l`
}
get_disk_info() {
t=`/usr/bin/df -h | grep $SATA`
export DISK_SIZE=`echo $t | awk '{printf $2}'`
export DISK_USED=`echo $t | awk '{printf $3}'`
export DISK_FREE=`echo $t | awk '{printf $4}'`
export DISK_USED_PCT=`echo $t | awk '{printf $5}'`
}
get_memcard_info() {
t=`/usr/bin/df -h | grep $MEMCARD`
export MEMCARD_SIZE=`echo $t | awk '{printf $2}'`
export MEMCARD_USED=`echo $t | awk '{printf $3}'`
export MEMCARD_FREE=`echo $t | awk '{printf $4}'`
export MEMCARD_USED_PCT=`echo $t | awk '{printf $5}'`
}
get_memory_info() {
# in Mbytes
export MEM_FREE=$(/usr/bin/free -h | awk '/Mem:/ {print $7}')
export MEM_TOTAL=$(/usr/bin/free -h | awk '/Mem:/ {print $2}')
export MEM_USED=$(/usr/bin/free -h | awk '/Mem:/ {print $3}')
}
get_cpu_usage() {
cpufree=`mpstat 1 1 | awk '/Moyenne/ {gsub(",",".",$0); print $12}'`
export CPU_USAGE=`echo "scale=1; x=100-$cpufree; if(x<1) print 0; x" | bc -l`
}
get_cpu_temperature() {
_t=$[`cat /sys/class/thermal/thermal_zone0/temp` / 1000]
if [ "$TEMPERATURE_FORMAT" = "C" ]; then
export CPU_TEMP="$_t"C
else
_t1=$[ $_t * 9 / 5 + 32 ]
export CPU_TEMP="$_t1"F
fi
}
get_ssh_connections() {
export SSH_CONNECTIONS=$(/usr/bin/who | wc -l)
}
get_process_count() {
export PROCESS_COUNT=`ps xa | wc -l`
}
# local variables
ext_ip_refresh_c=0
COFF=$(tput sgr0)
CGREEN=$(tput setaf 2)
CRED=$(tput setaf 1)
CBLUE=$(tput setaf 6)
oc="/dev/tty$OUTPUT_CONSOLE"
# font setup
#setfont $CONSOLE_FONT > $oc
# Ensure that we are in the right TTY
chvt $OUTPUT_CONSOLE
# infinite loop
while true; do
# Ensure that we are in the right TTY
chvt $OUTPUT_CONSOLE
# check if EXT_IP_REFRESH
if (( ($ext_ip_refresh_c % $EXT_IP_REFRESH) == 0 )); then
get_external_ip
fi
# increment $ext_ip_refresh_c
ext_ip_refresh_c=$[$ext_ip_refresh_c+1]
# get data
get_internal_ip
get_hostname
get_disk_info
get_memcard_info
get_full_date
get_net_tx_rx_realtime
get_memory_info
get_cpu_usage
get_cpu_temperature
get_ssh_connections
get_process_count
# clear the screen every loop
# we only wipe the screen when we are ready to write data to it
clear > $oc
# format the data on screen
echo "" > $oc
echo -e "$CBLUE$HOSTNAME $COFF: $DATE" > $oc
echo "" > $oc
# line CPU Usage
echo -e "CPU Usage: $CBLUE$CPU_USAGE%$COFF CPU Temp: $CBLUE$CPU_TEMP$COFF" > $oc
# Line Memory
echo -e "Memory Free: $CBLUE$MEM_FREE$COFF Used: $CBLUE$MEM_USED$COFF" > $oc
# Line IP Addresses
echo -e "IP: $CBLUE$INTERNAL_IP$COFF Rem: $CBLUE$EXTERNAL_IP$COFF" > $oc
# Line network usage
echo -e "TX: $CBLUE$NET_TX MB/s$COFF RX: $CBLUE$NET_RX MB/s$COFF" > $oc
# Line Disk Space
echo -e "Disk Used: $CBLUE$DISK_USED$COFF ($CBLUE$DISK_USED_PCT$COFF) Free: $CBLUE$DISK_FREE$COFF" > $oc
# Line Memcard Space
echo -e "Mem Card Used: $CBLUE$MEMCARD_USED$COFF ($CBLUE$MEMCARD_USED_PCT$COFF) Free: $CBLUE$MEMCARD_FREE$COFF" > $oc
# Line SSH
echo -e "SSH Connections: $CBLUE$SSH_CONNECTIONS$COFF" > $oc
# line Processes
echo -e "Processes Running: $CBLUE$PROCESS_COUNT$COFF" > $oc
# Custom Message
echo -e "$CRED$MESSAGE$COFF" > $oc
# sleep
sleep $REFRESH
done
On rend le script exécutable :
$ sudo chmod a+x /bin/cloudshell-lcd
Service systemd
$ sudo vim /etc/systemd/system/cloudshell-lcd.service
Coller les lignes suivantes :
[Unit]
Description="ODROID Cloudshell LCD Info"
DefaultDependencies=no
Requires=sysinit.target
After=sysinit.target
[Service]
Type=simple
ExecStart=/bin/cloudshell-lcd
[Install]
WantedBy=basic.target
WantedBy=sysinit.target
Maintenant on active le service et on le démarre :
$ sudo systemctl enable cloudshell-lcd.service
$ sudo systemctl start cloudshell-lcd.service
Ventilateur
Script de contrôle du ventilateur
$ sudo vim /bin/cloudshell-fan
Coller les lignes suivantes :
#!/bin/bash
REFRESH="1"
DISK_TEMP_THRESHOLD="45"
CPU_TEMP_THRESHOLD="60"
FAN_CHANGED="*"
get_disk_dev_info() {
# Pull disk info from /dev/sd*
fdisk -l > disks.txt 2>/dev/null
SATA=($(awk '/^\/dev\/sd/ {printf "%s ", $1}' disks.txt))
rm disks.txt
}
get_disk_temperature() {
for i in "${!SATA[@]}"
do
# declare and assign variable seperately to avoid masking return value
DISK_TEMP[$i]=" (IDLE)"
local t
t=$(smartctl -a "${SATA[$i]}" -d sat | grep "Temp")
if (( $? == 0 ))
then
local temp=$(echo $t | awk '{print $10}')
DISK_TEMP[$i]="$temp"
else
DISK_TEMP[$i]=""
fi
done
}
get_cpu_temperature() {
for i in {0..4}
do
_t=$(($(</sys/class/thermal/thermal_zone${i}/temp) / 1000))
CPU_TEMP[$i]="$_t"
done
}
fan_on() {
i2cset -y 1 0x60 0x05 0x00
}
fan_off() {
i2cset -y 1 0x60 0x05 0x05
}
handle_fan() {
for i in "${!DISK_TEMP[@]}"
do
if (( "${DISK_TEMP[$i]}" > "${DISK_TEMP_THRESHOLD}" ))
then
if [ "${FAN_CHANGED}" != "1" ]
then
echo "Turning fan on because disk $i has hit the threshold"
fi
FAN_CHANGED="1"
fan_on
return
fi
done
for i in "${!CPU_TEMP[@]}"
do
if (( "${CPU_TEMP[$i]}" > "${CPU_TEMP_THRESHOLD}" ))
then
if [ "${FAN_CHANGED}" != "1" ]
then
echo "Turning fan on because CPU $i has hit the threshold"
fi
FAN_CHANGED="1"
fan_on
return
fi
done
# No fuss, fan is off
if [ "${FAN_CHANGED}" != "0" ]
then
echo "All temps nominal, turning fan off"
FAN_CHANGED="0"
fi
fan_off
}
while true; do
get_disk_dev_info
get_disk_temperature
get_cpu_temperature
handle_fan
sleep ${REFRESH}
done
On rend le script exécutable :
$ sudo chmod a+x /bin/cloudshell-fan
Service systemd
$ sudo vim /etc/systemd/system/cloudshell-fan.service
Coller les lignes suivantes :
[Unit]
Description="ODROID Cloudshell Fan Control"
DefaultDependencies=no
Requires=sysinit.target
After=sysinit.target
[Service]
Type=simple
ExecStart=/bin/cloudshell-fan
[Install]
WantedBy=basic.target
WantedBy=sysinit.target
Maintenant on active le service et on le démarre :
$ sudo systemctl enable cloudshell-fan.service
$ sudo systemctl start cloudshell-fan.service
Configuration uboot
Afin de supprimer les messages susceptibles d'apparaitre à l'écran une fois le système redémarré, on modifie les fichier /boot/boot.txt :
$ sudo vim /boot/boot.txt
Modifier la ligne suivante en ajoutant les paramètres quiet et splash à la fin de la ligne :
setenv bootargs "console=tty1 console=ttySAC2,115200n8 root=PARTUUID=${uuid} rw rootwait smsc95xx.macaddr=${macaddr} ${videoconfig} quiet splash"
Mettre à jour le fichier boot.scr :
$ cd /boot/
$ sudo ./mkscr
Redémarrage du système
# reboot
Finalisation de la configuration du système
Se connecter en ssh avec le user alarm et le mot de passe alarm
$ ssh alarm@cloudshell.domaine.lan
Ouvrir une session root avec le mot de passe root
$ su - root
Utilisateurs
Définir un mot de passe root
# passwd
Définir un mot de passe pour l'utilisateur alarm et le renommer
# passwd alarm
# usermod -l new_user alarm
# usermod -m -d /home/new_user new_user
Configuration des disques
Le CloudShell2 permet de connecter deux disques SATA au format 3.5" par défaut (et 2.5" avec adaptateur), et supporte les technologies RAID0, RAID1, SPAN ainsi que l'accès direct (mode PM).
Dans ma configuration, j'ai branché deux disques SATA 2To en 2.5" et configuré le 'DIP switch' en accès direct (mode PM). Ces deux disques seront configurés via LVM pour former un volume logique unique de 4To.
Note : Sur un serveur on devrait dans l'idéal créer un volume RAID1 avec deux disques, mais ici on est limité au niveau matériel, en effet le CLoudshell2 connecte les deux disques SATA en USB via UART et à l'heure actuelle la synchronisation des deux disques provoque de nombreuses erreurs fatales, jusqu'à rendre USB indisponible. Les solutions de contournement consistent à limiter la taille et la bande passante allouée à la synchronisation, ce qui induit des pertes de performances non négligeables. Je préfère donc pour le moment sauvegarder les données de manière régulière sur un autre mini-serveur de mon réseau dédié à la sauvegarde (un raspberry Pi avec un disque externe de 4To).
Préparation des disques
$ sudo gdisk /dev/sda
Supprimer toutes les données GPT et MBR du disque, en tapant les commandes suivantes :
- x : accéder aux fonctionnalités avancées
- z : détruire les données GPT et MBR
Partitionnement
$ sudo gdisk /dev/sda
Créer une nouvelle partition de type 'Linux LVM' (8e00) sur le disque, en tapant les commandes suivantes :
- n : créer une nouvelle partition avec les paramètres suivants :
- Partition number (1-128, default 1):
- First sector (34-3907029134, default = 2048) or {+-}size{KMGTP}:
- Last sector (2048-3907029134, default = 3907029134) or {+-}size{KMGTP}:
- Hex code or GUID (L to show codes, Enter = 8300): 8e00
- Partition number (1-128, default 1):
- w : appliquer les changements et quitter
Répéter l'opération sur le 2ème disque (/dev/sdb).
Vérification
$ sudo fdisk -l
[...]
Disk /dev/sda: 1.82 TiB, 2000398934016 bytes, 3907029168 sectors
[...]
Device Start End Sectors Size Type
/dev/sda1 2048 3907029134 3907027087 1.8T Linux LVM
Disk /dev/sdb: 1.82 TiB, 2000398934016 bytes, 3907029168 sectors
[...]
Device Start End Sectors Size Type
/dev/sdb1 2048 3907029134 3907027087 1.8T Linux LVM
Création des volumes physiques
$ sudo pvcreate /dev/sda1 /dev/sdb1
Physical volume "/dev/sda1" successfully created.
Physical volume "/dev/sdb1" successfully created.
Création du groupe de volume
$ sudo vgcreate system /dev/sda1 /dev/sdb1
Volume group "system" successfully created
Création du volume logique
Formatage de la partition
# mkfs.ext4 /dev/sda1
Synchronisation du contenu de /home
# mkdir /mnt/data
# mount /dev/sda1 /mnt/data
# rsync -avz /home/ /mnt/data/
Une fois les données synchronisées (et vérifiées !), on peut nettoyer le répertoire /home :
# rm -rf /home/*
Montage de la partition et génération du fstab
# mount /dev/sda1 /home
# genfstab -p -U / > /etc/fstab
Récepteur infrarouge
Références
- ODROID-XU4
- Cloudshell2
- ODROID-XU4 | Arch Linux ARM
- pacman - ArchWiki
- Arch User Repository - ArchWiki
- LVM - ArchWiki
- Locale - ArchWiki