Installazione di Apache Kafka su Docker Desktop

Visto che di recente ho installato Kafka su Docker Desktop, condivido una semplice guida per arrivare ad averlo funzionante in qualche minuto.

Installazione di Kafka tramite Docker Compose

  1. Installare Docker Desktop per Windows.
  2. Registrarsi a Docker Hub se non si ha già un account.
  3. Effettuare tramite Docker Desktop il login su Docker Hub (clic destro sull’icona nella system tray).
  4. Per evitare che si verifichino errori durante la build in ambiente Windows, dalla git bash eseguire:
git config --global core.autocrlf false
  1. Effettuare con git il clone del repository https://github.com/wurstmeister/kafka-docker.git.
  2. Aprire il file docker-compose-single-broker.yml e impostare nella variabile d’ambiente KAFKA_ADVERTISED_HOST_NAME il valore kafkaserver.
  3. Nel file host di Windows aggiungere la riga:
127.0.0.1 kafkaserver
  1. Con la powershell di Windows (per aprirla tasto Windows  + x) posizionarsi nella cartella git clonata ed eseguire il comando:
docker-compose -f .\docker-compose-single-broker.yml up

Attendere che il processo di installazione e avvio termini.

Test dell’installazione

  1. Scaricare https://downloads.apache.org/kafka/2.6.0/kafka_2.12-2.6.0.tgz ed estrarre il contenuto.
  2. Portarsi nella cartella kafka_2.12-2.6.0\bin\windows sempre con la Powershell ed eseguire il comando per creare un topic:
.\kafka-topics.bat --create --topic test-events --bootstrap-server localhost:9092

Nel caso vi ritorni l’errore: “The input line is too long. The syntax of the command is incorrect.” accorciare il path rinominando le cartelle.

  1. Eseguire il comando per creare messaggi nel topic creato:
.\kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic test-events --from-beginning

La shell si bloccherà in attesa di messaggi dal producer

  1. Aprire un’altra Powershell nella stessa cartella ed eseguire il comando:
.\kafka-console-producer.bat --bootstrap-server localhost:9092 --topic test-events

Scrivere del testo e premere invio. Nell’altra shell del consumer si vedranno comparire i messaggi appena scritti dal producer.

Installazione di Apache OpenWhisk su Docker Desktop

Negli ultimi tempi mi sono interessato alle architetture serverless, grazie ad alcuni interessanti post di Michele Sciabarrà su Linkedin. Ho quindi deciso di giocare un po’ con Apache OpenWhisk, che è il software open source alla base ad esempio di IBM Cloud Functions e Nimbella. In questo articolo spiegherò come installarlo su Docker Desktop in locale.

Setup di Docker Desktop/Kubernetes

  1. Scaricare e installare Docker Desktop per Windows
  2. Andare nei settings di Docker Desktop e abilitare Kubernetes
  3. Installare Chocolatey, che è un gestore di pacchetti per Windows:
    1. Avviare la PowerShell come amministratore (tasto Windows + x e selezionare Windows PowerShell (Admin))
    2. Eseguire: Set-ExecutionPolicy AllSigned
    3. Eseguire: Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
  4. Installare helm tramite Chocolatey, un gestore di pacchetti per Kubernetes, con il comando choco install kubernetes-helm

Deploy di Apache OpenWhisk

Ora che abbiamo helm installato, possiamo procedere all’installazione di Openwhisk:

  1. All’interno di una cartella clonare il repository git con il comando: git clone https://github.com/apache/openwhisk-deploy-kube.git
  2. Eseguire il comando sempre dalla PowerShell kubectl describe nodes e annotarsi il valore di InternalIP
  3. All’interno della cartella creata in precedenza con il clone del repository creare il file mycluster.yaml, mettendo il valore di InternalIP nel parametro apiHostName:

whisk:
ingress:
type: NodePort
apiHostName: 192.168.65.3
apiHostPort: 31001

nginx:
httpsNodePort: 31001

  1. Eseguire kubectl label nodes --all openwhisk-role=invoker
  2. Creare il namespace openwhisk all’interno di Kubernetes: openwhisk kubectl create ns openwhisk
  3. Effettuare il deploy di Openwhisk su Kubernetes: helm install owdev ./helm/openwhisk -n openwhisk -f mycluster.yaml
  4. Scaricare il binario della CLI di Openwhisk dalla pagina . Aggiungere l’eseguibile cli.exe alla variabile PATH di Windows.
  5. Eseguire il comando: wsk property set --apihost localhost:31001
  6. Eseguire il comando: wsk property set --auth 23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP

Verifica dell’installazione

Il comando di deploy di Openwhisk eseguito poco sopra effettuerà l’installazione di tutti i componenti necessari su Kubernetes in qualche minuto. Una volta terminato il processo, il comando kubectl get all -n openwhisk -o wide, se non ci sono stati errori, dovrà dare un output di questo tipo:

NAME                                         READY   STATUS      RESTARTS   AGE    IP          NODE             NOMINATED NODE   READINESS GATES
pod/owdev-alarmprovider-6f584689bf-j5tgz     1/1     Running     0          43d    10.1.1.13   docker-desktop   <none>           <none>
pod/owdev-apigateway-6d864b779d-lvgs2        1/1     Running     0          43d    10.1.1.20   docker-desktop   <none>           <none>
pod/owdev-controller-0                       1/1     Running     0          43d    10.1.1.16   docker-desktop   <none>           <none>
pod/owdev-couchdb-647d5945ff-6kww2           1/1     Running     0          43d    10.1.1.15   docker-desktop   <none>           <none>
pod/owdev-gen-certs-2hdlq                    0/1     Completed   0          43d    10.1.0.15   docker-desktop   <none>           <none>
pod/owdev-init-couchdb-jmfp8                 0/1     Completed   0          43d    10.1.0.13   docker-desktop   <none>           <none>
pod/owdev-install-packages-rjh9r             0/1     Completed   0          43d    10.1.0.10   docker-desktop   <none>           <none>
pod/owdev-invoker-0                          1/1     Running     0          43d    10.1.1.10   docker-desktop   <none>           <none>
pod/owdev-kafka-0                            1/1     Running     0          43d    10.1.1.12   docker-desktop   <none>           <none>
pod/owdev-kafkaprovider-795b455d7-rfgds      1/1     Running     0          43d    10.1.1.9    docker-desktop   <none>           <none>
pod/owdev-nginx-85f9fdc44-pvbdm              1/1     Running     0          43d    10.1.1.21   docker-desktop   <none>           <none>
pod/owdev-redis-9bf746976-w5t47              1/1     Running     0          43d    10.1.1.11   docker-desktop   <none>           <none>
pod/owdev-wskadmin                           1/1     Running     0          43d    10.1.1.19   docker-desktop   <none>           <none>
pod/owdev-zookeeper-0                        1/1     Running     0          43d    10.1.1.14   docker-desktop   <none>           <none>
pod/wskowdev-invoker-00-1-prewarm-nodejs10   1/1     Running     0          6h4m   10.1.1.24   docker-desktop   <none>           <none>
pod/wskowdev-invoker-00-2-prewarm-nodejs10   1/1     Running     0          6h4m   10.1.1.25   docker-desktop   <none>           <none>

NAME                       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE   SELECTOR
service/owdev-apigateway   ClusterIP   10.99.44.8       <none>        8080/TCP,9000/TCP            43d   name=owdev-apigateway
service/owdev-controller   ClusterIP   10.96.47.134     <none>        8080/TCP                     43d   name=owdev-controller
service/owdev-couchdb      ClusterIP   10.96.176.173    <none>        5984/TCP                     43d   name=owdev-couchdb
service/owdev-kafka        ClusterIP   None             <none>        9092/TCP                     43d   name=owdev-kafka
service/owdev-nginx        NodePort    10.103.112.186   <none>        80:30302/TCP,443:31001/TCP   43d   name=owdev-nginx
service/owdev-redis        ClusterIP   10.106.4.19      <none>        6379/TCP                     43d   name=owdev-redis
service/owdev-zookeeper    ClusterIP   None             <none>        2181/TCP,2888/TCP,3888/TCP   43d   name=owdev-zookeeper

NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS      IMAGES                          SELECTOR
deployment.apps/owdev-alarmprovider   1/1     1            1           43d   alarmprovider   openwhisk/alarmprovider:2.2.0   name=owdev-alarmprovider
deployment.apps/owdev-apigateway      1/1     1            1           43d   apigateway      openwhisk/apigateway:1.0.0      name=owdev-apigateway
deployment.apps/owdev-couchdb         1/1     1            1           43d   couchdb         apache/couchdb:2.3              name=owdev-couchdb
deployment.apps/owdev-kafkaprovider   1/1     1            1           43d   kafkaprovider   openwhisk/kafkaprovider:2.1.0   name=owdev-kafkaprovider
deployment.apps/owdev-nginx           1/1     1            1           43d   nginx           nginx:1.17                      name=owdev-nginx
deployment.apps/owdev-redis           1/1     1            1           43d   redis           redis:4.0                       name=owdev-redis

NAME                                             DESIRED   CURRENT   READY   AGE   CONTAINERS      IMAGES                          SELECTOR
replicaset.apps/owdev-alarmprovider-6f584689bf   1         1         1       43d   alarmprovider   openwhisk/alarmprovider:2.2.0   name=owdev-alarmprovider,pod-template-hash=6f584689bf
replicaset.apps/owdev-apigateway-6d864b779d      1         1         1       43d   apigateway      openwhisk/apigateway:1.0.0      name=owdev-apigateway,pod-template-hash=6d864b779d
replicaset.apps/owdev-couchdb-647d5945ff         1         1         1       43d   couchdb         apache/couchdb:2.3              name=owdev-couchdb,pod-template-hash=647d5945ff
replicaset.apps/owdev-kafkaprovider-795b455d7    1         1         1       43d   kafkaprovider   openwhisk/kafkaprovider:2.1.0   name=owdev-kafkaprovider,pod-template-hash=795b455d7
replicaset.apps/owdev-nginx-85f9fdc44            1         1         1       43d   nginx           nginx:1.17                      name=owdev-nginx,pod-template-hash=85f9fdc44
replicaset.apps/owdev-redis-9bf746976            1         1         1       43d   redis           redis:4.0                       name=owdev-redis,pod-template-hash=9bf746976

NAME                                READY   AGE   CONTAINERS   IMAGES
statefulset.apps/owdev-controller   1/1     43d   controller   openwhisk/controller:71b7d56
statefulset.apps/owdev-invoker      1/1     43d   invoker      openwhisk/invoker:71b7d56
statefulset.apps/owdev-kafka        1/1     43d   kafka        wurstmeister/kafka:2.12-2.3.1
statefulset.apps/owdev-zookeeper    1/1     43d   zookeeper    zookeeper:3.4

NAME                               COMPLETIONS   DURATION   AGE   CONTAINERS         IMAGES                       SELECTOR
job.batch/owdev-gen-certs          1/1           2m8s       43d   gen-certs          openwhisk/ow-utils:71b7d56   controller-uid=4aeeffc8-836b-4ee4-a55c-17a08fe2bdc7
job.batch/owdev-init-couchdb       1/1           3m2s       43d   init-couchdb       openwhisk/ow-utils:71b7d56   controller-uid=39e41fbc-715d-44a7-9cc5-d1cb6d83e077
job.batch/owdev-install-packages   1/1           13m        43d   install-packages   openwhisk/ow-utils:71b7d56   controller-uid=8e3ca341-2d06-497b-a97e-282a6db3d465

Se qualche componente permanesse in uno stato di init o errore, allora vorrebbe dire che Openwhisk non può funzionare correttamente.

Durante la prima installazione avevo riscontrato sul pod del controller lo stato “ImageInspectError”. Ho risolto installando una versione più vecchia di Docker Desktop e ripetendo l’installazione. Qui trovate la issue che avevo aperto su Github.

Generare con PHP una lista di anagrafiche random con codice fiscale

In un progetto di performance testing mi è capitata la necessità di avere a disposizione un grande numero di anagrafiche distinte (nell’ordine delle migliaia), con relativo codice fiscale. Queste anagrafiche dovevano essere su un file csv, che sarebbe stato utilizzato come input dai virtual user di Loadrunner per simulare uno scenario di inserimento anagrafica. Il tracciato del file csv era il seguente:

nome,cognome,stato di nascita,sesso,provincia di nascita,comune di nascita,data,codice fiscale

Per creare il file csv ho sviluppato uno script PHP, nel quale ho usato una delle tante classi che si trovano sul web per generare il codice fiscale. Nel mio caso ho utilizzato questa, disponibile su PHPclasses.org.

Di seguito il codice dello script:

require_once 'codicefiscale.class.php';

class CodiceFiscale extends CodiceFiscale_Abstract {

    

    private $_db;

    

    public function leggiCatastali($filename) {

        $this->_db = unserialize(file_get_contents($filename));

    }

    

    protected function _calcolaCatastale() {

        if (!$this->_db) {

            throw new CodiceFiscale_Exception('impossibile caricare il file dei catastali');

        }

        $comune    = strtoupper($this->comune);

        $provincia = strtoupper($this->provincia);

        if (!isset($this->_db[$provincia][$comune])) {

            $message = sprintf("codice catastale non trovato (provincia: %s, comune: %s)", $provincia, $comune);

            throw new CodiceFiscale_Exception($message);

        }

        return $this->_db[$provincia][$comune];

    }

}



function random_name( $length = 8 ) {

    $chars = "abcdefghijklmnopqrstuvwxyz";

    $name = substr( str_shuffle( $chars ), 0, $length );

    return $name;

}



//funzione di comodità per generare un array da passare alla classe

function componi($nome, $cognome, $data, $comune, $provincia, $sesso) {

    return [

       'nome'      => $nome,

       'cognome'   => $cognome,

       'data'      => $data,

       'comune'    => $comune,

       'provincia' => $provincia,

       'sesso'     => $sesso 

    ];

}



// istanzio la classe e carico il db dei catastali

$cf = new CodiceFiscale();

$cf->leggiCatastali('catastali.txt');

$dati = array();

$fp = fopen('elenco_vf.csv', 'w');

$pool_citta = array('roma', 'torino', 'milano', 'trieste');

$pool_province = array('roma' => 'rm', 'torino' => 'to', 'milano' => 'mi', 'trieste' => 'ts');



//Genero 4000 anagrafiche random con relativo codice fiscale

for($i=0;$i<4000;$i++)

{

    $int= rand(157762800,567990000);

    $nome = random_name();

    $cognome = random_name();

    if((rand(1,100)%2) == 0) 

        $sesso = "m";

    else $sesso = "f";

    $citta_temp = $pool_citta[rand(0,3)];

    $dati[] = componi($nome , $cognome, date("d-m-Y",$int), $citta_temp, $pool_province[$citta_temp], $sesso);

    

}



//Creo l'intestazione del file csv

$testata = array('nome','cognome','statoDiNascita','sesso','provinciaDiNascita','comuneDiNascita','data','cf','anagrafica');

fputcsv($fp,$testata);

//per ogni anagrafica scrivo una riga sul file csv

foreach($dati as $persona) {

    try {

        $cf->importa($persona);

        fputcsv($fp, array_map('strtoupper',array($persona['nome'], $persona['cognome'], 'Italia', $persona['sesso'], $persona['provincia'], $persona['comune'], date_format(date_create($persona['data']), 'Y-m-d'),$cf->calcola(), '')));

    } catch (CodiceFiscale_Exception $e) {

        print "Errore: ". $e->getMessage() . "\n";

        continue;

    }

}



fclose($fp);

Spero sia utile, perchè online si trovano solamente soluzioni per generare il singolo codice fiscale.