Skip to content

Como configurar e usar um cluster de Apache Spark em sua rede local

Published:

In­tro­du­ção

Di­ga­mos que você tenha um com­pu­ta­dor an­tigo acu­mu­lando po­eira na sua casa. Que tal transformá-​lo em seu pró­prio ser­vi­dor com Apa­che Spark? De­cidi fazer isso com meu com­pu­ta­dor an­tigo e des­cre­ve­rei abaixo todos os pas­sos para ins­ta­lar e con­fi­gu­rar o Apa­che Spark para uti­li­za­ção em sua rede local.

Estou usando o Linux Man­jaro mais re­cente, o qual é ba­se­ado em Arch Linux. Por­tanto, ou­tras dis­tri­bui­ções ba­se­a­das em Arch (pro­va­vel­mente) podem uti­li­zar os mes­mos pas­sos sem ne­nhuma al­te­ra­ção. É pos­sí­vel adap­tar os pas­sos para ou­tras dis­tri­bui­ções Linux.

O que é Apa­che Spark?

O Apa­che Spark é um fra­mework de pro­ces­sa­mento de dados dis­tri­buído e es­ca­lá­vel, pro­je­tado para lidar com gran­des vo­lu­mes de dados. Ge­ral­mente o Spark opera em clus­ter, isto é, vá­rios ser­vi­do­res co­nec­ta­dos os quais pro­ces­sam as re­qui­si­ções de forma dis­tri­buída, ele­vando a ordem de mag­ni­tude da ve­lo­ci­dade e da quan­ti­dade de dados que podem ser pro­ces­sa­dos. Aqui, no en­tanto, con­fi­gu­ra­re­mos um stan­da­lone clus­ter, isto é, um clus­ter com ape­nas um ser­vi­dor. O Spark é uti­li­zado em di­ver­sos ce­ná­rios, desde pro­ces­sa­mento de gran­des quan­ti­da­des de dados até apren­di­zado de má­quina, e é uma fer­ra­mente im­por­tante para quem tra­ba­lha na área de dados. Além disso, ele per­mite a exe­cu­ção es­ca­lá­vel de que­ries SQL.

O Spark ofe­rece su­porte a di­fe­ren­tes lin­gua­gens, como Scala, Python e R. Ao final do post, fa­re­mos uma co­ne­xão uti­li­zando o pyspark, API do Spark para Python.

Con­fi­gu­rando o ser­vi­dor

A par­tir de agora, já cha­ma­re­mos o seu com­pu­ta­dor velho de ser­vi­dor! Pri­meiro, vamos con­fi­gu­rar al­gu­mas coi­sas.

SSH

Pri­meiro, co­necte um mouse, te­clado e tela ao seu com­pu­ta­dor an­tigo. Vamos ins­ta­lar e con­fi­gu­rar o OpenSSH. Isso per­mi­tirá a co­ne­xão re­mota ao ser­vi­dor (vamos cha­mar de ser­vi­dor daqui em di­ante). Tam­bém re­co­mendo ins­ta­lar um edi­tor de texto ade­quado ao ter­mi­nal. Aqui uti­lizo o ne­o­vim, mas sinta-​se à von­tade para uti­li­zar qual­quer um, in­clu­sive o nano (que já deve estar ins­ta­lado). Se ficar preso den­tro do vim/ne­o­vim, basta uti­li­zar o co­mando :q para sair (Ctrl+C para sair do modo de in­ser­ção e di­gi­tar co­man­dos).

sudo pacman -S openssh
sudo pacman -S neovim

Agora vamos gerar cha­ves SSH para o ser­vi­dor e ha­bi­li­tar o ser­viço do ser­vi­dor SSH.

sudo systemctl enable sshd.service
sudo systemctl start sshd.service

Vamos ins­ta­lar o jq e utilizá-​lo para obter o IP local da má­quina:

sudo pacman -S jq
ip -json route get 8.8.8.8 | jq -r '.[].prefsrc'

Anote esse en­de­reço IP, ele será uti­li­zado em di­ver­sos mo­men­tos. Sem­pre que hou­ver <IP> em um co­mando, subs­ti­tua pelo en­de­reço IP do seu ser­vi­dor, a menos que es­pe­ci­fi­que outra coisa. Em outro com­pu­ta­dor, tente conectar-​se ao ser­vi­dor uti­li­zando usuá­rio e senha por meio do ssh:

ssh <IP>

Se con­se­guiu se co­nec­tar, está tudo certo. Agora, ainda na sua má­quina prin­ci­pal, ge­ra­re­mos uma chave SSH para conectar-​se ao ser­vi­dor. É re­co­men­dá­vel que essa chave seja usada ex­clu­si­va­mente para esse fim. Nos lo­cais onde co­loco <nome_do_arquivo>, subs­ti­tua pelo ca­mi­nho com­pleto até o nome do ar­quivo da chave pri­vada. Su­giro salvá-​la na pasta ~/.ssh.

ssh-keygen -t ed25519 -C "<nome_da_chave>"
ssh-add ~/.ssh/<nome_do_arquivo>

Copie todo o con­teúdo da chave pú­blica que ge­ra­mos (ar­quivo .pub), conecte-​se no­va­mente o ser­vi­dor e adi­ci­one todo o con­teúdo co­pi­ado ao ar­quivo ~/.ssh/authorized_keys:

cat ~/.ssh/<nome_do_arquivo>.pub

ssh <IP>
mkdir ~/.ssh
nvim ~/.ssh/authorized_keys

Desconecte-​se do ser­vi­dor e tente conectar-​se no­va­mente. Dessa vez, o login deve acon­te­cer sem senha, por meio da sua chave SSH. Vamos então de­sa­bi­li­tar a co­ne­xão re­mota uti­li­zando senha por mo­ti­vos de se­gu­rança. No­va­mente, co­nec­tado ao ser­vi­dor pelo ter­mi­nal:

sudo nvim /etc/ssh/sshd_config

Adi­ci­one as se­guin­tes li­nhas:

PasswordAuthentication no
AuthenticationMethods publickey

Desconecte-​se do ser­vi­dor. Edite o ar­quivo con­fig do SSH para atri­buir um nome ao IP do seu ser­vi­dor. Adi­ci­one as se­guin­tes li­nhas ao ar­quivo ~/.ssh/config (pode ser ne­ces­sá­rio criar o ar­quivo) da sua má­quina local, subs­ti­tuindo os cam­pos in­di­ca­dos:

Host <nome_do_servidor>
    HostName <IP>
    User <seu_usuário_no_servidor>

Ro­te­a­dor

Agora re­serve um IP para nosso ser­vi­dor no ro­te­a­dor. Isso é im­por­tante para que o ser­vi­dor sem­pre se co­necte uti­li­zando o mesmo IP local, fa­ci­li­tando a co­ne­xão uti­li­zando SSH.

Uti­lize o se­guinte co­mando e anote o IP que apa­rece no campo gateway:

ip -json route get 8.8.8.8

Agora, vá até esse IP no na­ve­ga­dor, faça login no seu ro­te­a­dor (cada marca pos­sui um par de usuá­rio e senha pa­drão, ve­ri­fi­que no seu ro­te­a­dor). In­fe­liz­mente, como há mui­tas mar­cas dis­po­ní­veis, essa etapa varia bas­tante. Em li­nhas ge­rais, basta ir à seção de rede local, lo­ca­li­zar o seu ser­vi­dor pelo IP e adi­ci­o­nar uma nova re­serva de IP para aquele dis­po­si­tivo.

Apa­che Ha­doop

O Apa­che Spark pode rodar sem o Apa­che Ha­doop, no en­tanto, al­gu­mas fun­ci­o­na­li­da­des de­pen­dem do Ha­doop, por isso ele será ins­ta­lado e con­fi­gu­rado pri­meiro. O Apa­che Ha­doop é um fra­mework tam­bém para pro­ces­sa­mento pa­ra­lelo e es­ca­lá­vel de grande quan­ti­da­des dados. Um dos seus prin­ci­pais com­po­nen­tes é o Ha­doop Dis­tri­bu­ted File System (HDFS), um sis­tema de ar­qui­vos dis­tri­buído que dis­tri­bui e re­plica os dados em di­fe­ren­tes nós do clus­ter. O Apa­che Spark uti­liza o HDFS para ge­ren­ciar o ar­ma­ze­na­mento de ar­qui­vos de forma dis­tri­buída, bem como o YARN (outro com­po­nente do Ha­doop) para alo­ca­ção de re­cur­sos do clus­ter.

Ins­ta­lando o Apa­che Ha­doop

É ne­ces­sá­rio ins­ta­lar o Java Run­time En­vi­ron­ment ver­são 11. Nesse ponto, você deve con­se­guir conectar-​se fa­cil­mente ao ser­vi­dor uti­li­zando o SSH. Co­nec­tado ao ser­vi­dor, rode os se­guin­tes co­man­dos:

sudo pacman -S jre11-openjdk
wget https://dlcdn.apache.org/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz
tar -xzf hadoop-3.3.6.tar.gz
mv hadoop-3.3.6 ~/hadoop
cd ~/hadoop

Adi­ci­one as se­guin­tes li­nhas ao ar­quivo etc/hadoop/hadoop-env.sh:

export JAVA_HOME=/lib/jvm/default

Aten­ção: o di­re­tó­rio JAVA_HOME varia con­forme sua dis­tri­bui­ção Linux. Ele deve con­ter o ca­mi­nho até a im­ple­men­ta­ção Java a ser uti­li­zada pelo Ha­doop.

Em se­guida:

source etc/hadoop/hadoop-env.sh

Adi­ci­one as se­guin­tes li­nhas ao ar­quivo etc/hadoop/core-site.xml, subs­ti­tuindo a tag <configuration> vazia.

<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://localhost:9000</value>
    </property>
</configuration>

Adi­ci­one as se­guin­tes li­nhas ao ar­quivo etc/hadoop/hdfs-site.xml, subs­ti­tuindo a tag <HOME> pelo ca­mi­nho com­pleto até sua pasta home (algo como /home/<nome_do_usuario>).

<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>
    <property>
        <name>dfs.data.dir</name>
  <value><HOME>/hadoop_data</value>
    </property>
</configuration>

Note que con­fi­gu­ra­mos um fator de re­pli­ca­ção de 1. Em am­bi­en­tes de pro­du­ção, isso não é re­co­men­dado dado que pode acar­re­tar perda de dados em caso de falha do hard­ware. Como es­ta­mos em um am­bi­ente local, uti­li­za­mos 1 para eco­no­mi­zar re­cur­sos da má­quina.

Agora pre­ci­sa­mos che­car se con­se­gui­mos conectar-​se ao localhost sem exi­gir senha. Pro­va­vel­mente o co­mando a se­guir re­sul­tará em um erro ou exi­gir senha:

ssh localhost

Aten­ção: rode esse co­mando co­nec­tado ao ser­vi­dor pelo ter­mi­nal via SSH!

Caso o co­mando re­sulte em um erro ou peça senha — o que é bem pro­vá­vel — rode os co­man­dos a se­guir:

ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys

Agora tente conectar-​se no­va­mente ao localhost e dessa vez o co­mando deve fun­ci­o­nar!

ssh localhost

Adi­ci­one a se­guinte linha ao ar­quivo etc/hadoop/hadoop-env.sh:

export HADOOP_CONF_DIR=$HOME/etc/hadoop

Adi­ci­o­nando per­mis­sões ao fi­rewall (op­ci­o­nal)

De­pen­dendo da sua dis­tri­bui­ção linux, pode ser ne­ces­sá­rio adi­ci­o­nar per­mis­sões ao fi­rewall tanto da sua má­quina quanto do ser­vi­dor. Os co­man­dos abaixo fun­ci­o­nam em Arch Linux com o pa­cote firewalld. Ba­si­ca­mente, per­mi­ti­mos co­ne­xão por todas as por­tas entre o ser­vi­dor e a nossa má­quina. Isso pode tra­zer ris­cos de se­gu­rança se você não puder con­fiar na se­gu­rança da sua rede local ou no seu ser­vi­dor. Rode o mesmo co­mando tanto no ser­vi­dor quanto na sua má­quina. No ser­vi­dor, in­sira o IP da sua má­quina no lugar de <IP> e vice-​versa. Se ne­ces­sá­rio, ins­tale firewalld con­forme as ins­tru­ções da sua dis­tri­bui­ção, há mais in­for­ma­ções aqui.

Você pode pular essa etapa. Caso não con­siga abrir URLs que apon­tem para o ser­vi­dor pos­te­ri­or­mente, volte para esse ponto e adi­ci­one per­mis­sões de fi­rewall con­forme as ins­tru­ções da sua dis­tri­bui­ção Linux.

sudo firewall-cmd --add-source=<IP> --zone=trusted

For­ma­tando o sis­tema de ar­qui­vos Ha­doop

Mais in­for­ma­ções aqui. Subs­ti­tua <USERNAME> pelo seu nome de usuá­rio do sis­tema.

bin/hdfs namenode -format
sbin/start-dfs.sh
bin/hdfs dfs -mkdir /user
bin/hdfs dfs -mkdir /user/<USERNAME>

Mais con­fi­gu­ra­ções do Ha­doop

Adi­ci­one às se­guin­tes li­nhas ao ar­quivo etc/hadoop/mapred-site.xml no lugar da tag <configuration> vazia.

<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
    <property>
        <name>mapreduce.application.classpath</name>
        <value>$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/lib/*</value>
    </property>
</configuration>

Adi­ci­one as se­guin­tes li­nhas ao ar­quivo etc/hadoop/yarn-site.xml no lugar da tag <configuration> vazia.

<configuration>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
        <name>yarn.nodemanager.env-whitelist</name>
        <value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_HOME,PATH,LANG,TZ,HADOOP_MAPRED_HOME</value>
    </property>
</configuration>

Ve­ri­fi­car se o Ha­doop está ro­dando

Nesse ponto, o Apa­che Ha­doop deve estar ro­dando no seu ser­vi­dor! Ve­ri­fi­que se con­se­gue aces­sar o se­guinte en­de­reço no na­ve­ga­dor da sua má­quina prin­ci­pal.

http://<IP>:9870/

Em que <IP> é o en­de­reço IP local do seu ser­vi­dor. Caso veja uma pá­gina com in­for­ma­ções do Ha­doop, ve­ri­fi­que o campo DFS Re­mai­ning — deve haver pelo menos al­guma me­mó­ria livre. Caso a con­fi­gu­ra­ção do Ha­doop tenha algum pro­blema, é pos­sí­vel que não haja ne­nhum es­paço livre de­tec­tado.

Apa­che Spark

Ins­ta­lando o Spark

cd ~
wget https://dlcdn.apache.org/spark/spark-3.4.1/spark-3.4.1-bin-hadoop3.tgz
tar -xzf spark-3.4.1-bin-hadoop3.tgz
mv spark-3.4.1-bin-hadoop3 ~/spark

Con­fi­gu­rando o Spark

De­pen­dendo do seu shell, adi­ci­o­na­re­mos li­nhas a ar­qui­vos di­fe­ren­tes. Este ar­quivo é ~/.bashrc caso use bash e ~/.zshrc caso use zsh. Ve­ri­fi­que qual Shell está em uso com o co­mando:

ps -p $$

Na co­luna CMD es­tará o nome do Shell em uso.

Adi­ci­one as se­guin­tes li­nhas:

export SPARK_HOME=$HOME/spark
export PATH=$PATH:$SPARK_HOME/bin:$SPARK_HOME/sbin

Rode o se­guinte co­mando para as novas li­nhas terem efeito na ses­são atual do Shell (aqui as­su­mindo que bash está em uso):

source ~/.bashrc

Vá até à pasta ~/spark. Adi­ci­one as se­guin­tes li­nhas ao ar­quivo conf/spark-env.sh.template em que <IP> é o IP local do seu ser­vi­dor.

SPARK_MASTER_HOST=<IP>
HADOOP_CONF_DIR=$HOME/hadoop/etc/hadoop

Re­no­meie o ar­quivo:

mv conf/spark-env.sh.template conf/spark-env.sh

Ini­ci­ando uma ses­são do Spark

Subs­ti­tua <IP> pelo IP local do seu ser­vi­dor.

cd bin
start-master.sh
start-worker.sh spark://<IP>:7077

Na sua má­quina prin­ci­pal, vá até o en­de­reço http://<IP>:8080/ (no­va­mente, subs­ti­tua a tag IP con­forme dito acima). Uma pá­gina com o tí­tulo “Spark Mas­ter” deve car­re­gar. Ve­ri­fi­que na ses­são “Wor­kers” se há um wor­ker ro­dando. Se sim, o Spark está ro­dando nor­mal­mente. Vamos parar o Spark.

stop-worker.sh
stop-master.sh

Cri­ando um ser­viço para rodar o Spark au­to­ma­ti­ca­mente

Essa etapa é op­ci­o­nal. Com os co­man­dos acima é pos­sí­vel ini­ciar seu ser­vi­dor Spark sem­pre que ne­ces­sá­rio. Aqui in­te­gra­mos o Spark ao systemd para poder gerenciá-​lo como um ser­viço ro­dando ao fundo do sis­tema sem­pre que li­ga­mos o ser­vi­dor.

Crie o ar­quivo /etc/systemd/system/spark-master.service e adi­ci­one as se­guin­tes li­nhas (ne­ces­sá­rio abrir o edi­tor com sudo). Lembre-​se de subs­ti­tuir <HOME> pelo ca­mi­nho com­pleto até a pasta home do seu usuá­rio!

[Unit]
Description=Apache Spark Master
After=network.target

[Service]
Type=forking
User=root
Group=root
ExecStart=<HOME>/spark/sbin/start-master.sh
ExecStop=<HOME>/spark/sbin/stop-master.sh

[Install]
WantedBy=multi-user.target

Crie o ar­quivo /etc/systemd/system/spark-worker.service e adi­ci­one as se­guin­tes li­nhas (ne­ces­sá­rio abrir o edi­tor com sudo). Lembre-​se de subs­ti­tuir <IP> pelo IP local do seu ser­vi­dor e <HOME> pelo ca­mi­nho com­pleto até a pasta home do seu usuá­rio!

[Unit]

Description=Apache Spark Worker

After=network.target

[Service]
Type=forking
User=root
Group=root
ExecStart=<HOME>/spark/sbin/start-worker.sh spark://<IP>:7077
ExecStop=<HOME>/spark/sbin/stop-worker.sh

[Install]
WantedBy=multi-user.target

Vamos ati­var ambos os ser­vi­ços para serem ini­ci­a­dos com o sis­tema e iniciá-​los:

sudo systemctl enable spark-master spark-worker
sudo systemctl start spark-master spark-worker

Es­pere al­guns se­gun­dos e, no­va­mente, ve­ri­fi­que o en­de­reço http://<IP>:8080/. Se a mesma pá­gina de antes car­re­gar com 1 wor­ker ativo, o Spark está ro­dando nor­mal­mente.

Conectando-​se ao ser­vi­dor Spark re­mo­ta­mente via PySpark

É ne­ces­sá­rio ter o pyspark ins­ta­lado em sua má­quina local para conectar-​se ao ser­vi­dor Spark. Siga as ins­tru­ções do site ofi­cial para ins­ta­lar o pyspark. Note que é ne­ces­sá­rio ins­ta­lar o Java JDK e con­fi­gu­rar a va­riá­vel de am­bi­ente JAVA_HOME tam­bém na sua má­quina local (como fi­ze­mos acima). Nesse caso, é pos­sí­vel de­fi­nir a va­riá­vel JAVA_HOME ma­nu­al­mente ou inseri-​la no ar­quivo ~/.bashrc.

O có­digo abaixo conecta-​se ao ser­vi­dor Spark (lembre-​se de subs­ti­tuir a tag <IP>), cria e exibe um Da­ta­Frame.

from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .appName("SparkTest") \
    .master("spark://<IP>:7077") \
    .getOrCreate()

# Create a sample dataframe
data = [("Alice", 1), ("Bob", 2), ("Charlie", 3)]
df = spark.createDataFrame(data, ["Name", "Age"])

df.show()

Se tudo cor­rer bem, o Da­ta­Frame será exi­bido.

Spark Con­nect

Nessa ses­são ve­re­mos como con­fi­gu­rar a fun­ci­o­na­li­dade Spark Con­nect: uma ar­qui­te­tura cliente-​servidor de­sa­co­plada que per­mite co­nec­ti­vi­dade re­mota a clus­ters Spark usando a API de Da­ta­Frame. A se­pa­ra­ção entre cli­ente e ser­vi­dor per­mite que o Spark seja apro­vei­tado de qual­quer lugar. Ele pode ser in­cor­po­rado em apli­ca­ções de dados mo­der­nas, em IDEs, No­te­bo­oks e lin­gua­gens de pro­gra­ma­ção.

Con­fi­gu­rando o ser­viço Spark Con­nect

Crie o ar­quivo /etc/systemd/system/spark-connect.service e adi­ci­one as se­guin­tes li­nhas (ne­ces­sá­rio abrir o edi­tor com sudo). Lembre-​se de subs­ti­tuir <IP> pelo IP local do seu ser­vi­dor e <HOME> pelo ca­mi­nho com­pleto até a pasta home do seu usuá­rio!

[Unit]

Description=Apache Spark Connect

After=network.target

[Service]
Type=forking
User=root
Group=root
ExecStart=<HOME>/spark/sbin/start-connect-server.sh --packages org.apache.spark:spark-connect_2.12:3.4.1
ExecStop=<HOME>/spark/sbin/stop-connect-server.sh

[Install]
WantedBy=multi-user.target

Ative o ser­viço para ser ini­ci­ado com o sis­tema e iniciá-​lo:

sudo systemctl enable spark-connect
sudo systemctl start spark-connect

Essa fun­ci­o­na­li­dade re­quer al­gu­mas de­pen­dên­cias ex­tras. Para ga­ran­tir que elas es­te­jam ins­ta­la­das no seu com­pu­ta­dor (não é ne­ces­sá­rio no ser­vi­dor), ins­tale o pyspark com as de­pen­dên­cias op­ci­o­nais do con­nect:

pip install "pyspark[connect]"

Tes­tando a co­ne­xão com Spark Con­nect

O có­digo abaixo conecta-​se ao ser­vi­dor Spark uti­li­zando o Spark Con­nect (lembre-​se de subs­ti­tuir a tag <IP>), cria e exibe um Da­ta­Frame.

from pyspark.sql import SparkSession

spark = SparkSession.builder.remote("sc://<IP>:15002").getOrCreate()

data = [("Alice", 1), ("Bob", 2), ("Charlie", 3)]
df = spark.createDataFrame(data, ["Name", "Age"])

df.show()

Con­clu­são

Pa­ra­béns! Você ins­ta­lou e con­fi­gu­rou o Apa­che Spark! Há di­ver­sos ser­vi­ços de cloud que ofe­re­cem clus­ters com Apa­che Spark ou até mesmo am­bi­en­tes com­ple­tos in­te­gra­dos ao Spark (como o Da­ta­bricks). No en­tanto, esses ser­vi­ços são bas­tante caros, o que pra­ti­ca­mente in­vi­a­bi­liza seu uso para apren­der a uti­li­zar o Apa­che Spark fora do am­bi­ente cor­po­ra­tivo. O ob­je­tivo desse post é per­mi­tir que qual­quer um com um com­pu­ta­dor so­brando possa rodar o Apa­che Spark e apren­der a utilizá-​lo sem pagar nada.

Há mui­tos tó­pi­cos sobre Apa­che Spark que esse post não sobre, como, por exem­plo, suas mui­tas con­fi­gu­ra­ções. Po­de­mos ex­plo­rar mais as­sun­tos em ou­tros posts, por hora fi­ca­mos por aqui. A do­cu­men­ta­ção do pyspark é uma boa fonte de co­nhe­ci­mento para co­me­çar a uti­li­zar seu ser­vi­dor Spark.


Previous Post
Como criptografar seu computador com LUKS e TPM + senha
Next Post
Survival analysis with Cox reggression - heart failure data