- Configurar o CARP
- Configurar o HAST
- Criando Unidades ZFS
- Failover (Complete Failover)
Corrigindo Desligamento
Montando discos AutoMágicamente
Definindo hast roles no boot com Scarpd - Instalado Plex, CouchPotato, Sonarr
- Referências
O NAS4FREE está instalado em um pendrive de 8GB com full install em ambos os nodes... Estou usando o FreeBSD 10.3.13 neste NAS4FREE.
Vamos ao que interessa:
Common Address Redundancy Protocol (CARP)
A primeira coisa à fazer é subir as interfaces CARP o cenário fica assim:Temos duas placas de rede em cada nó, a rede 13 está falando por um cabo crossover para evitar que o tráfego do HAST passe pelo switch.
A segunda interface de rede tem o CARP configurado falando pela subrede 192.168.101.0/27 e irá responder pelo switch com ip 192.168.101.29 quando for o CARP MASTER.
Uma analogia do CARP ao universo Linux sería o HeartBeat.
O nó master fica com 192.168.101.1 e o nó backup(slave) com 192.168.101.2, o ip do cluster no CARP será o 29.
O CARP é muito simples, não tem nenhum pulo do gato até aqui... o NAS4FREE ainda tem uma GUI que torna tudo literalmente orientado a click, pelo menos até esta parte... ;)
Como o NAS4FREE persiste as configurações em um arquivo xml por enquanto vou seguir o manual e acertar o CARP pela GUI.
Network --> Interface Management --> Carp
O virtual IP address é o ip do CARP, no caso o 29 a configuração é igual em ambos os nós, com exceção do advskew que deve ser menor no node master. O menor advskew prevalece como master. No meu caso está 1 pro MASTER e 100 para o BACKUP.
A cultura do click é estranha pra mim, então só pra deixar claro na prática oque esta acontecendo é basicamente isso:
#kldload carp
#ifconfig re0 inet vhid 1 pass mypassword advskew 1 alias 192.168.101.29/27 up
Cada node, carregando o modulo do carp e subindo interfaces virtuais com advskew diferentes. Para ser permanente devería ser editado em rc.conf e o módulo no loader.conf. Este é só um comparativo simples do que está acontecendo com estes cliques na GUI.
So far, so good.... Após aplicar estes passos em cada node o CARP está configurado...
High Avaiable Storage (HAST)
O DRDB do universo linux... Imagine "RAID1" pela rede...A arquitetura do cluster para o HAST está ilustrada aqui:
Cenário é simples mas héterogeneo.
O NODE-0 com 4 discos e NODE-1 com apenas 2 discos. Cada nó possui um disco ada0 com 1,5TB de capacidade que será espelhado a nível de bloco com o HAST e outro HD de 500GB.
O motivo de não ter espelhado todos os discos é simples... $$$. HD's são caros no Brasil e esses são os discos que tenho pra resolver o caso.
Também pode estar querendo saber porque raios o ada1 no NODE-1 não é o ada1 no NODE-0... Bom isso é prq eu estava com preguiça de abrir a máquina pra inverter a posição do cabo sata ;-P
Segui a GUI em : Services --> HAST --> Resources
lá criei dois resources... um com nome de midia e outro com nome share respectivamente ada0 e ada3.
Estou deixando o ada0 com nome de hast resource midia no NODE-0 será mapeado como ada0 em NODE-1 também.
O ada3 do NODE-0 será o ada1 no NODE-1.
resource midia {
on node0 {
local ada0
remote 13.13.13.2
}
on node1 {
local ada0
remote 13.13.13.1
}
}
resource share{
on node0 {
local ada3
remote 13.13.13.2
}
on node1{
local ada1
remote 13.13.13.1
}
}
WARNING!
Estou usando os IP's da rede crossover para o HAST pra evitar que o sync dos discos fique passando pelo switch.
PS: Se na hora de aplicar os resources tiver um erro ao criar o resource faça um wipe no disco antes de adiciona-los ao HAST.
Eu tenho um script que faz esse wipe:
Wipe disk
#!/bin/bash
echo "What disk do you want"
echo "to wipe? For example - ada1 :"
read disk
echo "OK, in 10 seconds I will destroy all data on $disk!"
echo "Press CTRL+C to abort!"
sleep 10
diskinfo ${disk} | while read disk sectorsize size sectors other
do
# Delete MBR, GPT Primary, ZFS(L0L1)/other partition table.
/bin/dd if=/dev/zero of=/dev/${disk} bs=${sectorsize} count=8192
# Delete GEOM metadata, GPT Secondary(L2L3).
/bin/dd if=/dev/zero of=/dev/${disk} bs=${sectorsize} oseek=$(expr $sectors - 8192) count=8192
done
Ok... agora tenho os discos configurados nos dois nós vou acertar o hast pela linha de comando.
NODE-0 MASTER
hastctl create midia
hastctl create share
service hastd onestart
hastctl role primary all
NODE-1 BACKUP(SLAVE)
hastctl create midia
hastctl create share
service hastd onestart
hastctl role secondary all
O status deles agora dever ser completePUTS! :/ o resource midia não esta subindo... matei tudo...
hastctl role init all
service hastd forcestop
Vou subir o hastd agora em modo ativo pra ver os logs do porque não foi pra frente no resource midia de 1,5TB
subindo hastd no-detach
$hastd -d
![]() |
Logs reclamado dos resources com tamanhos diferentes |
Agora entendi... ele nao quer syncar porque esta falando que meu disco de 1.5TB é alguns bytes maior que o outro... puts...
Vou recriar o device forçando o tamanho certo:
$hastctl create -m 1465137527k midia
Recriei o device de novo com o tamanho forçado nos dois nós...
Agora subi o hastd de novo e vamos ver o status...
$hastctl status
Opa... maravilha... agora esta tudo sincado...
Só um conferes do dump deles...
$hastctl dump -c /etc/hast.conf
Beleuza... tudo certo...
ZFS
Agora vamos formatar... precisamos de um filesystem... Eu optei por ZFS... Simplismente porque ZFS é muito massa... heheheheUma observação importante é que a formatação seja feita apenas no MASTER... o HAST trabalha à nível de bloco... então só precisa ser feito num lado, o lado primario do HAST. Os devices vão ter spawn em /dev/hast, então vão poder ser montados sempre sómente quando o nó for master com hast role primário.
Vamos seguir essa formatação pela GUI do NAS4FREE...
Mas antes... vamos importar os devices na GUI.
Importei os discos em:
Disks --> Management --> HDD Management --> Import Disks
Com os hasts devices mapeados na GUI vou formatar com zfs:
Disks --> Management --> HDD Format ... etc...
Depois só criar os virtual devices e os pools. Tenho tudo ok pela GUI.
Failover Complete
Agora chegou a hora de arregaçar as mangas e botar a mão na massa... Eu quero um FAILOVER COMPLETO. Quero desligar uma e ter meus serviços migrando e iniciando sózinhos sem nenhuma intervenção humana!
Meus testes de Failover em checklist devem funcionar nos seguintes cenários:
----------------------------------------------------------------------------------
1º IF NODE-0 == DOWN
THEN NODE-1 == BECOMES MASTER2º
IF NODE-0 == UP AGAIN
THEN NODE-1 == STIIL MASTER (net.inet.carp.preempt=0)
AND NODE-0 == BECOMES SLAVE
3º
IF NODE-1 == DOWN
THEN NODE-0 == BECOMES MASTER AGAIN
IF NODE-1 == UP
THEN NODE-1 == BACK TO SLAVE
4º Quando os dois forem ligados ao mesmo tempo o advskew deve agir
IF NODE-0 == DOWN
AND NODE-1 == DOWN
IF NODE-0 == UP
AND NODE-1 == UP
THEN NODE-0 == SE TORNA MASTER POR advksew hast primario pelo scarpd
THEN NODE-1 == SE TORNA SLAVE POR advskew hast secundario pelo scarpd
----------------------------------------------------------------------------------
Agora que as configurações do CARP e HAST já "estão ok". Vou começar meu checklist de failover DESLIGADNO o NODE-0 e nessa teoria o NODE-1 deverá se tornar o MASTER.
#shutdown -p now
OPS! :(
Simplismente não quer desliga... Depois de vários testes descobri que quando estou com as unidades ZFS HAST montadas o NAS4FREE não consegue desmontar e não desliga nunca... Fiz um teste pra ver se o watchdog entrava em ação, mas a máquina ficou na sequencia de shutdown por 7 horas seguidas e não desligou...
Essa falha de não desligar já entrou pro failover, já que vou ter de tratar o desligamento e a inicialização então depois de um tempo estudando porque não desliga, resolvi fazer algumas modificações no Nas4free...
Corrigindo o Desligamento do NAS4FREE com CARP+HAST+ZFS
Para corrigir o desligamento com ZFS + HAST preciso caçar o coelho na toca vendo oque o devd deve fazer quando a interface CARP mudar pro state DOWN. Mas como não desmonta os discos hasts, então não consegue deixar nunca a interface DOWN. E o problema de desligar começa a ficar grande...
Quando cria a interface carp no NAs4Free ele cria a configuração para a interface CARP em /etc/devd, nesse diretório deve heistir um arquivo carp_1@SUAcarpiface.conf.
notify 30 {
match "system" "CARP";
match "subsystem" "1@re0";
match "type" "MASTER";
action "/usr/local/sbin/carp-hast-switch master";
};
notify 30 {
match "system" "CARP";
match "subsystem" "1@re0";
match "type" "BACKUP";
action "/usr/local/sbin/carp-hast-switch slave";
};
Bom fica claro aí que a ação que ele deve chamar é o script carp-hast-switch com o case master ou slave.
Até aí nada demais...
Mas se formos depurar o script carp-hast-switch vamos descobrir que oque ele faz nada mais é que mudar a interface carp de BACKUP para MASTER e virar os discos hasts secundários para primarios...
Mas quem cuida de fato do HAST é o hastswitch... O hastswitch é chamado pelo carp-hast-switch para lidar com as unidades HAST detectando quem são qual o filesystem e qual tomada de acção. Tudo claro trazendo sempres os dados do /conf/config.xml que é onde o NAS4FREE guarda as configurações da GUI.
Ta ok... Detectei que no hastswitch no case shutdown ele não estava exportando os pools do zfs depois de desmontar então resolvi alterar no daemon do zfs deixando a função zfs_stop_main do arquivo /etc/rc.d/zfs assim:
zfs_stop_main()
{
zfs unshare -a
zfs unmount -a
resources=`hastctl dump -c /etc/hast.conf|grep resource|awk '{print $2}'`
for activepools in $resources
do
zpool export $activepools
done
}
Ps: Não era extremamente necessário modificar o daemon do zfs, podia ser tudo modificado no script hastswitch mas já que modifiquei lá resolvi documentar...
Modifiquei também a função shutdown acrescentando o stop do daemon zfs lá... E assim ficou sanado a primeira parte desse failover... O shutdown...
shutdown)
# state to backup
/usr/local/bin/xml sel -t \
-m "//vinterfaces/carp" \
-v "if" \
-n \
-b \
${configxml_file} | /usr/local/bin/xml unesc | \
while read _if; do
if [ "${PREEMPT}" = "0" -a -n "$_if" ]; then
/usr/bin/logger -p $log -t $name "Change ${_if} state to BACKUP."
/sbin/ifconfig "${_if}" state backup 2>/dev/null
/bin/sleep 1
elif [ "${PREEMPT}" != "0" -a -n "$_if" ]; then
/usr/bin/logger -p $log -t $name "Change ${_if} state to INIT."
/sbin/ifconfig "${_if}" down 2>/dev/null
/bin/sleep 1
fi
done
/bin/sleep 5
/etc/rc.d/zfs stop
;;
OK! :) Agora que a maquina desliga consigo observar que o NODE-1 realmente se tornou MASTER na interface CARP e os discos HAST estão aparecendo como primarios, porém ele não esta importando os pools...
Montando os discos automágicamente:
Quando o BACKUP se torna MASTER, você agora precisa que estes pools também estejam importados devidamente na GUI do nas4free então agora podemos trazer as configurações do zfs para este nó que ainda estava sem estes pools mapeados da seguinte forma:
Disks --> ZFS --> Configuration --> Synchronize
Aqui temos o problema do ovo e da galinha... O hastswitch start deve importar os pools, dos discos zfs... Porém ele se baseia no config.xml da gui pra fazer isso, então é provável que quando tente importar os pools aqui pela GUI não tenham pools sendo listandos porque não foram importados...
Então vamos resolver o problema do ovo...
$zpool import -f midia
$zfs import -f share
Agora os pools estão importados eles devem aparecer na GUI para serem sincronizados.
Eu acabei fazendo uma mudança significativa no hastswitch start para lidar com pools zfs como esse pedaço inteiro:
start)
# import ZFS pool
hast_pools=$(xml sel -t -m "//disk" --if "type='HAST'" --if "fstype='zfs'" -v name -n -b ${configxml_file})
for pools in $hast_pools
do
/usr/bin/logger -p $log -t $name "Import ZFS pool $pools."
/sbin/zpool import -f "$pools"
if [ $? -ne 0 ]
then
zfs mount $pools
fi
done
Estou deixando todos os scripts que modifiquei neste repositório do github.
Bom nesse momento com hastswitch modificado no start e shutdown eu posso fazer o segundo teste do Failover que será desligar os nós alternadamente, pra ver os discos sendo montados e desmontados de um pra outro enquanto eles se tornam MASTER e BACKUP(slave)...
Fiz várias vezes este failover de alternar entre elas, que seria o failover 1º,2º e 3º e tudo funciona muito bem...
Vou agora pro teste 4º, onde desligo as duas ao mesmo tempo e ligo de novo... Como não estou usando preempt isso pode dar um slpit brain... Mesmo com advskew diferentes...
Bom, oque aconteceu foi que o advskew agiu certo na interface CARP tornando uma master a outra SLAVE, mas... os discos HAST não formam montandos porque o devd só entra em ação quando o LINK sai de UP pra DOWN... mas nesse caso o devd não tomou partida durante a inicialização da maquina e nao rodou o script em action, com isso os discos ficarm sem role tanto no MASTER como no BACKUP e assim não monta os hasts...
Forçando uma ação pro HAST na inicialização (scarpd):
Eu não achei ninguem falando sobre esse caso... Então resolvi escrever um daemon que rode depois do HAST que verifique quem é MASTER e quem é SLAVE e defina o role pro resource do hast. Batizei esse serviço de scarpd.
#!/bin/sh
#
#
# Detectd whos is master nad whois slave and set hast initial roles
# this is failover util in case both nodes are shutdown and need to start at same time
# by: Igor Brandao
# PROVIDE: scarpd
# REQUIRE: NETWORKING syslogd hastd
# RCVAR: scarpd
. /etc/rc.subr
name="scarpd"
desc="Detects whos master and whos slave"
rcvar="${name}_enable"
load_rc_config "${name}"
start_cmd="${name}_start"
stop_cmd="${name}_stop"
carps=`/usr/local/bin/xml sel -t -m "//vinterfaces/carp" -v if -n -b /conf/config.xml`
hast_set_state(){
if [ ! -e /var/run/hastctl ]; then
sleep 2
if [ ! -e /var/run/hastctl ]; then
# hastd is not started
return 0
fi
fi
# get carp interfaces
carps=`/usr/local/bin/xml sel -t -m "//vinterfaces/carp" -v if -n -b /conf/config.xml`
STATE="unknown"
if [ -n "${carps}" ]; then
for if in ${carps}; do
STATE=`/sbin/ifconfig ${if} | grep "carp:" | awk '{ print tolower($2) }'`
done
fi
if [ $STATE == master ]
then
hastctl role primary all
/usr/local/sbin/hastswitch start
elif [ $STATE == backup ]
then
hastctl role secondary all
/usr/local/sbin/hastswitch stop
fi
}
scarpd_start()
{
hast_set_state
}
scarpd_stop()
{
hastctl role init all
load_rc_config $name
run_rc_command "$1"
O scarpd também esta no meu github. Você deve chamar ele em rc.conf com scarpd_enable="YES"
Bom nessa etapa... O Failover já é meu amigo... e tudo funciona perfeitamente, agora vem a diversão...
Instalando CouchPotato+Plex+Sonarr
Realmente não tem segredo nas próximas etapas, eu poderia criar jails, e até mesmo usar o finch para estes serviços, mas fui curto e grosso... intalei tudo direto:
1º Mudar o repositorio do /etc/pkg/FreeBSD.conf pra latest.
# url: "pkg+http://pkg.FreeBSD.org/${ABI}/quarterly",
url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest",
2º Instalando pacotes:
pkg install plexmediaserver sonarr python py27-sqlite3 fpc-libcurl docbook-xml git-lite
3º Instalando o couchpotato
git clone https://github.com/CouchPotato/CouchPotatoServer.git
ln -s /usr/local/bin/python /usr/bin/python
cp CouchPotatoServer/init/freebsd /usr/local/etc/rc.d/couchpotato&&chmod +x /usr/local/etc/rc.d/couchpotato
4º Linkar tudo em disco HAST
Não da pra equecer que por default sem jail as configuraçöes desses aplicativos ficam em /usr/local/ então linkar pra um disco hast os diretorios de configuração pode ser uma boa ideia...
mv /usr/local/couchpotato /mnt/hast/pool
ln -s /usr/local/couchpotato /mn/t/hast/pool/couchpotato
mv /usr/local/sonarr /mnt/hast/pool
ln -s /mnt/hast/pool/sonarr /usr/local/sonarr
mv plexdata /mnt/hast/pool
ln -s /mnt/pool/hast/plexdata /usr/local/plexdata
5º Adcionar todos eles no rc.conf
Blz... aqui ja está tudo rodando, Cluster,couch,potato,sonarr etc... Uma última observação é que os serviços devem ser sempre colocados com no hastswitch start e stop... assim o failover dos serviços sonarr, coucpotato e plex ficam funcionando também...
Enjoy :)
Bibliografias e Leituras úteis:
https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/carp.htmlhttps://www.freebsd.org/doc/handbook/carp.html
https://www.freebsd.org/doc/handbook/disks-hast.html
https://www.freebsd.org/cgi/man.cgi?query=devd.conf&sektion=5&apropos=0&manpath=FreeBSD+10.3-RELEASE+and+Ports
https://wiki.freebsd.org/HAST
https://forums.freebsd.org/threads/29639/
https://forums.freebsd.org/threads/38397/
http://forums.nas4free.org/viewtopic.php?t=1271
https://forums.freebsd.org/threads/49492/
http://blog.uni.cl/2015/06/freebsd-stockage-zfs-haute-disponibilite-avec-hast/
https://www.freebsd.org/doc/handbook/zfs-zpool.html
http://forums.nas4free.org/viewtopic.php?f=80&t=2333
https://lists.freebsd.org/pipermail/freebsd-fs/2013-October/018480.html
https://www.bsdinfo.com.br/artigos/FreeBSD_e_Alta_Disponibilidade_com_HAST+CARP+ZFS.pdf