<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Akalanka Weerasooriya</title>
    <description>The latest articles on DEV Community by Akalanka Weerasooriya (@aawgit).</description>
    <link>https://dev.to/aawgit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F386970%2F761eed21-d946-41f2-abdd-09a8da96407c.png</url>
      <title>DEV Community: Akalanka Weerasooriya</title>
      <link>https://dev.to/aawgit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aawgit"/>
    <language>en</language>
    <item>
      <title>Building a (somewhat) intelligent agent</title>
      <dc:creator>Akalanka Weerasooriya</dc:creator>
      <pubDate>Sat, 09 Nov 2024 15:14:56 +0000</pubDate>
      <link>https://dev.to/aawgit/building-a-somewhat-intelligent-agent-ghe</link>
      <guid>https://dev.to/aawgit/building-a-somewhat-intelligent-agent-ghe</guid>
      <description>&lt;p&gt;This started on a Saturday night. If you are very social like me, you would know that there is no better time to do some coding than a peaceful Saturday night. So I opened up a pet project I've been working on and realized that it wasn't pushed to Github yet. I didn't remember the commands to set a remote repo and could have easily Googled or "ChatGPTed" it. But, wouldn't it be cooler to add another layer of abstraction and just tell the computer to "set this projects remote as such and such", specially in this era of intelligent agents? And wouldn't it be even cooler to build that agent?. And that's exactly what I did, instead of spending a few seconds on finding the commands to set the remote repo. &lt;/p&gt;

&lt;p&gt;I started solving the problem backwards. I would need a way to run shell commands from a program. That's easy, the subprocess module in Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import subprocess

def run_shell_command(command):
    try:
        # Run the shell command
        result = subprocess.run(command, shell=True, check=True, text=True, capture_output=True)
        # Return the command output
        return result.stdout
    except subprocess.CalledProcessError as e:
        # Return the error output if the command fails
        return e.stderr

print(run_shell_command('pwd'))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I need a way to decide what commands to run. That's where the intelligence comes in. It needs to take the natural language input and convert them to shell commands. Large Language Models (LLMs) are good at this sort of things. So I tried the following prompt on ChatGPT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I'm computer program created to assist a human. Currently the human is working on a DevOps task. He asked me to "ssh to the development server and install python". Please tell me what shell commands to run to do the activity. If you need more information please let me know what information you need. Please respond in the following manner and don't include anything else in the response.
Type: [Can be either "Commands" or "More information"]
Response: [List of shell commands or list of more information needed, separated by commas]

Example response 1:
Type: More information
Response: user id, key path

Example response 2:
Type: Commands
Response: ssh -i 'keyfile.pem' user1@server
It worked surprisingly well most of the time.

This was the response.

Type: More information
Response: user id, server IP or hostname, key path or password, operating system type (e.g., Ubuntu, CentOS)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after passing the inputs, it returned the list of commands as,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Type: Commands
Response: ssh -i 'key.pem' user1@192.168.1.8, sudo yum install python3 -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not exactly production ready, but this is a promising start. On this high note I had to stop for the day one, or rather hour one since I'm no longer the young man I once was and it was already 10 PM.&lt;/p&gt;

&lt;p&gt;A week later...&lt;/p&gt;

&lt;p&gt;Zooming out a little bit, "how would I use this?". I would open up a project on the terminal, and type " set the remote repo for this project as " . Then the agent will ask the LLM for the commands to run. If it needs more information, it will prompt me. After getting the information, it will send them to the LLM, for which the LLM will give the commands or ask for more information. This will repeat until a command runs. If the command is successful, it will stop. But if it returns errors the agent will prompt the LLM for commands to resolve the issue. Also, with each request to the LLM , the agent will send the conversation history in window with a suitable size. This will provide the context to the LLM. &lt;/p&gt;

&lt;p&gt;We would need to make the queries to LLM a little abstract to make the agent handle a wider range of tasks. After all, it wouldn't be very useful if its only capable of setting remote repo URLs. At the same time, we need to clearly defile its scope. In this case it would be an agent for running shell commands. To help handling a range of commands, we can parameterize the prompt. Those parameters would be,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The natural language input from the human.&lt;/li&gt;
&lt;li&gt;Context: This is little tricky, I will use the conversation history for now.&lt;/li&gt;
&lt;li&gt;Any errors returned by running a command.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In addition to that we will have to maintain the state such as executing a command or getting more info. &lt;/p&gt;

&lt;p&gt;Let's code it. I've changed the LLMs output to a JSON format string since it's easier to write the processing part that way. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9i69pzwigitro00aqpw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9i69pzwigitro00aqpw.png" alt="Image description" width="800" height="689"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I tested it with a few simple commands and they worked as expected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcrhv9ufosx4a5o1hec5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcrhv9ufosx4a5o1hec5.png" alt="Image description" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seems alright. Let's try another one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclns04rb0imoqncq3d9w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclns04rb0imoqncq3d9w.png" alt="Image description" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's not what I asked for. May be we need to be more specific.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwn1y6uqcoi31sipri0k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwn1y6uqcoi31sipri0k.png" alt="Image description" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's more like it. Although I should definitely add a mechanism to verify the commands before running them. That should prevent the agent from doing something crazy. Also, explaining a command before it runs would be a good feature - but not for now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;answer = input(f" Shall I run '{command}'? (Yes/ No) ")
                if answer.lower()=='yes': # Execute the command
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, it kind of works, but we need to make it easily accessible. Creating an alias did the trick. I added the following to ~/.bashrc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias shelly='/home/akalanka/projects/shelly/venv/bin/python3 /home/akalanka/projects/shelly/main.py'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see how well "Shelly" fulfills her purpose. First I told Shelly to create the remote repo, but it did't work because it was trying to setup gh CLI tools authentication, which was too complex for a simple tool like this. So I created the remote repo and then asked to set it as the origin of the local repo, which also failed the first time. But after improving the prompt template, I asked her to correct the mistake, which actually worked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnad08k7z5n06ae9u2cb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnad08k7z5n06ae9u2cb.png" alt="Image description" width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I went ahead and asked her to commit and push her own code, which also was done nicely enough (ignoring the fact that she ignored the instruction about the commit message).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvxzvbob6sner1p8ub67.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvxzvbob6sner1p8ub67.png" alt="Image description" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's not much useful for commands I use frequently, which I remember, because it's quicker and more reliable to run the shell command directly. But for other cases this actually seem to help.&lt;/p&gt;

&lt;p&gt;So about a week later, I was finally able to set the remote repo for the project. Great success!. What a way to spend weekend evenings!. &lt;/p&gt;

&lt;p&gt;Obviously, a lot can be done to improve this. To start, some way of persisting the user inputs between the invocations could smooth things up. Using LangChain could be a good idea. Let me know what you think. Also feel free to check out the &lt;a href="https://github.com/aawgit/shelly" rel="noopener noreferrer"&gt;source code&lt;/a&gt; and open a PR to make it more intelligent. It could use some help. Hey, you can use the Shelly to push your feature, hopefully.&lt;/p&gt;

&lt;p&gt;P.S. This was entirely written by a human. Absolutely no intelligence - artificial or otherwise was involved in the writing. &lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>ai</category>
      <category>shell</category>
      <category>bash</category>
    </item>
    <item>
      <title>Building a Spark cluster with two PCs and a Raspberry Pi.</title>
      <dc:creator>Akalanka Weerasooriya</dc:creator>
      <pubDate>Thu, 28 May 2020 18:39:16 +0000</pubDate>
      <link>https://dev.to/aawgit/building-a-spark-cluster-with-two-pcs-and-a-raspberry-pi-3fk2</link>
      <guid>https://dev.to/aawgit/building-a-spark-cluster-with-two-pcs-and-a-raspberry-pi-3fk2</guid>
      <description>&lt;p&gt;I read &lt;a href="https://towardsdatascience.com/a-journey-into-big-data-with-apache-spark-part-1-5dfcc2bccdd2" rel="noopener noreferrer"&gt;this &lt;/a&gt; brilliant post by Ashley Broadley which explains how to set up a Spark cluster with docker compose. It inspired me to try out something a little bit different, to use different devices in the same LAN as nodes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn4zm61q1pq53lhb702t7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn4zm61q1pq53lhb702t7.jpg" alt="Alt Text" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post describes how to set up a cluster in Spark Standalone mode, which is easier in comparison to using other cluster managers.&lt;br&gt;
Following devices were used as nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Worker 1: A PC running on Windows, with Docker installed.&lt;/li&gt;
&lt;li&gt;Worker 2: A PC running on Ubuntu, with Docker installed.&lt;/li&gt;
&lt;li&gt;Master: A Raspberry Pi 3 model B running on Raspbian.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Steps are pretty simple and straight forward. Here we go…&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up Spark in Raspberry Pi and starting the Master
&lt;/h2&gt;

&lt;p&gt;I used SSH to log in to the Raspberry Pi and used it in headless mode just to avoid keeping another monitor and a keyboard. But if you don’t mind that, skip the SSH set up and continue with JDK installation on RPi terminal.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up SSH server and opening up port 22
&lt;/h3&gt;

&lt;p&gt;The SSH server of the RPi is not enabled by default. There are broadly two options for enabling it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Placing a ‘ssh’ file in the SD card from another machine.&lt;/li&gt;
&lt;li&gt;Using RPi desktop (Yes, for this you need to plug in a monitor once).
RPi documentation explains these two options under 2, and 3.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To test the SSH connection, first find the IP address of the RPi using ifconfig. Then from another machine in the same network enter the command&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;ssh &amp;lt;username&amp;gt;@&amp;lt;ip address of the RPi&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;If the IP address is correct and SSH server is running, you will get a prompt for the password. Type in the login password of the RPi for the user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiex04lvzr4d1heueury1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiex04lvzr4d1heueury1.png" alt="Alt Text" width="734" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;However, there are security issues involved with allowing remote login, even if you have set a password. &lt;a href="https://askubuntu.com/questions/1107987/connect-two-computers-with-ssh-in-a-home-lan/1108044#1108044" rel="noopener noreferrer"&gt;This&lt;/a&gt; answer suggests that a key based authentication method should be used.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing JDK
&lt;/h3&gt;

&lt;p&gt;Spark runs on Java. So, we need to have Java installed on the RPi. Yet, most RPis used today come with JDK installed on Raspbian. In that case, this step is not necessary. Otherwise, execute following commands from the RPi, to install the Java Runtime Environment.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo apt-get update&lt;br&gt;
sudo apt-get install openjdk-8-jre&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Spark on the RPi and starting the master
&lt;/h3&gt;

&lt;p&gt;From the Spark documentation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To install Spark Standalone mode, you simply place a compiled version of Spark on each node on the cluster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Execute following commands to install Spark.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wget https://downloads.apache.org/spark/spark-2.4.5/spark-2.4.5-bin-hadoop2.7.tgz&lt;br&gt;
tar -xzf spark-2.4.5-bin-hadoop2.7.tgz &amp;amp;&amp;amp; \&lt;br&gt;
    mv spark-2.4.5-bin-hadoop2.7 /spark &amp;amp;&amp;amp; \&lt;br&gt;
    rm spark-2.4.5-bin-hadoop2.7.tgz&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;To start the master, use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/spark/bin/spark-class org.apache.spark.deploy.master.Master --port 7077 --webui-port 8080&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;This tells Spark to start a master and listen on port 7077, and also use port 8080 for displaying the web User Interface.&lt;br&gt;
If everything goes well, you should see a bunch of logs running on the screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz6ts1qfoqczr90dsnr0o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz6ts1qfoqczr90dsnr0o.png" alt="Alt Text" width="725" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, you should be able to see the web UI of the master. If you have a monitor for the RPi, UI can be accessed at localhost:8080, or else point a browser to :8080on any other PC in the LAN.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fchfwezewi8uds7ddck2l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fchfwezewi8uds7ddck2l.png" alt="Alt Text" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seems like the master is running fine. Lets fire up some workers and see what happens.&lt;/p&gt;
&lt;h2&gt;
  
  
  Starting the worker nodes using Docker
&lt;/h2&gt;

&lt;p&gt;I used the same Dockerfile as in Ashley’s article, and updated the Spark download link. Here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM openjdk:8-alpine
RUN apk --update add wget tar bash
RUN wget https://downloads.apache.org/spark/spark-2.4.5/spark-2.4.5-bin-hadoop2.7.tgz
RUN tar -xzf spark-2.4.5-bin-hadoop2.7.tgz &amp;amp;&amp;amp; \
    mv spark-2.4.5-bin-hadoop2.7 /spark &amp;amp;&amp;amp; \
    rm spark-2.4.5-bin-hadoop2.7.tgz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will build a docker image with Java and Spark installed. Build the image, start the container, and open its shell using following commands:&lt;br&gt;
Set the environment variable MYNAME by&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;MYNAME=&amp;lt;your name&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 on Ubuntu Terminal or by&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;set MYNAME=&amp;lt;your name&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 on Windows CMD. Also, you may need to execute following with sudo on Ubuntu.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker build -t $MYNAME/spark:latest .&lt;br&gt;
docker run -it --rm $MYNAME/spark:latest /bin/sh&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Then start the worker on docker container with following:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;spark/bin/spark-class org.apache.spark.deploy.worker.Worker --webui-port 8080 spark://&amp;lt;ip-of-master&amp;gt;:7077&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;This tells Spark to start a worker and connect it with the master at the given IP. Lets go back to the UI of the master:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1rdg9zwlawr7c4lxyudn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1rdg9zwlawr7c4lxyudn.png" alt="Alt Text" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes! The master has accepted us.&lt;br&gt;
Since I had another laptop laying around I added that to the cluster as well. — The more the merrier.&lt;br&gt;
Adding another worker is no different from the above.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can build a docker image on the second machine from the above docker file, or use a copy of the one built on the first machine. Use&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;sudo docker save -o &amp;lt;some name&amp;gt;.tar $MYNAME/spark:latest&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 to build a tar with the image, copy it to the second machine, and, use&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;docker load -i &amp;lt;path to image tar file&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 to load the image.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiuv1ofpyl4vscoxi6uls.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiuv1ofpyl4vscoxi6uls.jpeg" alt="Alt Text" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Submitting a job
&lt;/h2&gt;

&lt;p&gt;I used one of the examples come with the Spark installation, which calculates the value of Pi. Execute following from the RPi to submit the job.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/spark/bin/spark-submit --master spark://&amp;lt;master-ip-address&amp;gt;:7077 --class org.apache.spark.examples.SparkPi /spark/examples/jars/spark-examples_2.11–2.4.5.jar 1000&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;org.apache.spark.examples.SparkPi is the entry point of our application, and /spark/examples/jars/spark-examples_2.11–2.4.5.jar is the path to the jar containing the application and dependencies. Since our application is a one comes shipped with the Spark installation, its available on all nodes of the cluster. 1000 is an application argument which in this case is related to the number of partitions to which the data set is being distributed.&lt;br&gt;
You can check the job status on the UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fase0w3j7qg95xk7k44lc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fase0w3j7qg95xk7k44lc.png" alt="Alt Text" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There will also be some log entries in the master and worker terminals. After successful completion of the job, it will show the result in the terminal where the job was submitted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fccmgfhih461wobb6iu51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fccmgfhih461wobb6iu51.png" alt="Alt Text" width="726" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>spark</category>
      <category>hadoop</category>
      <category>bigdata</category>
      <category>raspberrypi</category>
    </item>
  </channel>
</rss>
