<?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: Sergio Pino</title>
    <description>The latest articles on DEV Community by Sergio Pino (@spino327).</description>
    <link>https://dev.to/spino327</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%2F444864%2F70c2ee53-de77-4639-a72f-f850a2b67da8.jpeg</url>
      <title>DEV Community: Sergio Pino</title>
      <link>https://dev.to/spino327</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/spino327"/>
    <language>en</language>
    <item>
      <title>Calling GitHub Copilot Models from OpenHands using LiteLLM Proxy</title>
      <dc:creator>Sergio Pino</dc:creator>
      <pubDate>Thu, 03 Apr 2025 19:16:06 +0000</pubDate>
      <link>https://dev.to/spino327/calling-github-copilot-models-from-openhands-using-litellm-proxy-1hl4</link>
      <guid>https://dev.to/spino327/calling-github-copilot-models-from-openhands-using-litellm-proxy-1hl4</guid>
      <description>&lt;p&gt;Integrating GitHub Copilot models into your development workflow can significantly enhance productivity. By leveraging LiteLLM Proxy alongside OpenHands, you can seamlessly connect to various large language models (LLMs), including GitHub's offerings. This guide will walk you through setting up LiteLLM Proxy and OpenHands using Docker Compose, enabling you to call GitHub Copilot models effectively.&lt;a href="https://docs.all-hands.dev/modules/usage/llms" rel="noopener noreferrer"&gt;openhands backends&lt;/a&gt;, &lt;a href="https://docs.all-hands.dev/modules/usage/llms/litellm-proxy" rel="noopener noreferrer"&gt;litellm-proxy&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before proceeding, ensure you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker and Docker Compose&lt;/strong&gt;: Installed on your system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Personal Access Token (PAT)&lt;/strong&gt;: Required for authenticating with GitHub's API.&lt;a href="https://docs.github.com/en/rest/authentication/authenticating-to-the-rest-api?apiVersion=2022-11-28" rel="noopener noreferrer"&gt;authenticating to the rest api&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Obtain a GitHub Personal Access Token
&lt;/h2&gt;

&lt;p&gt;To authenticate with GitHub's API, you'll need to create a Personal Access Token (PAT):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to your GitHub account.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Developer settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the left sidebar, click on &lt;strong&gt;Personal access tokens&lt;/strong&gt; &amp;gt; &lt;strong&gt;Tokens (classic)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Generate new token (classic)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Provide a descriptive note, set an expiration, and select the necessary scopes.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Generate token&lt;/strong&gt; and &lt;strong&gt;copy&lt;/strong&gt; the token for later use.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: Treat your PAT like a password. Store it securely and do not share it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Configure LiteLLM Proxy
&lt;/h2&gt;

&lt;p&gt;LiteLLM Proxy serves as a gateway to various LLM providers, including GitHub. To configure it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a Configuration File&lt;/strong&gt;: Save the following content as &lt;code&gt;litellm_config.yaml&lt;/code&gt;:&lt;a href="https://docs.litellm.ai/docs/proxy/docker_quick_start" rel="noopener noreferrer"&gt;quick_start&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="na"&gt;model_list&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-gpt-4o-mini&lt;/span&gt;
       &lt;span class="na"&gt;litellm_params&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github/gpt-4o-mini&lt;/span&gt;
         &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;os.environ/GITHUB_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration specifies the GitHub Copilot model and retrieves the API key from the environment variable &lt;code&gt;GITHUB_API_KEY&lt;/code&gt;. There are several more models to choose from &lt;a href="https://github.com/marketplace?type=models" rel="noopener noreferrer"&gt;marketplace&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set Environment Variables&lt;/strong&gt;: Ensure the &lt;code&gt;GITHUB_API_KEY&lt;/code&gt; environment variable is set with your GitHub PAT.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 3: Set Up Docker Compose
&lt;/h2&gt;

&lt;p&gt;To orchestrate the deployment of LiteLLM Proxy and OpenHands, use Docker Compose with the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;litellm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/berriai/litellm:main-latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4000:4000"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./litellm_config.yaml:/app/config.yaml&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GITHUB_API_KEY=${GITHUB_API_KEY}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;LITELLM_MASTER_KEY=somekey&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;UI_USERNAME=someuser&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;UI_PASSWORD=somepassword&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--config /app/config.yaml --detailed_debug&lt;/span&gt;

  &lt;span class="na"&gt;openhands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.all-hands.dev/all-hands-ai/openhands:0.23&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openhands-app&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.24-nikolaik&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WORKSPACE_MOUNT_PATH=${HOME}/OH&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;LOG_ALL_EVENTS=true&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${HOME}/OH:/opt/worspace_base&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${HOME}/.openhands-state:/.openhands-state&lt;/span&gt;
    &lt;span class="na"&gt;extra_hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host.docker.internal:host-gateway"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;LiteLLM Service&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mounts the &lt;code&gt;litellm_config.yaml&lt;/code&gt; configuration file.&lt;/li&gt;
&lt;li&gt;Exposes port 4000.&lt;/li&gt;
&lt;li&gt;Sets environment variables, including &lt;code&gt;GITHUB_API_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;OpenHands Service&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Depends on LiteLLM Proxy.&lt;/li&gt;
&lt;li&gt;Exposes port 3000.&lt;/li&gt;
&lt;li&gt;Configures necessary environment variables and volume mounts.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Deploy the Services
&lt;/h2&gt;

&lt;p&gt;With the configuration in place:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ensure your &lt;code&gt;GITHUB_API_KEY&lt;/code&gt; environment variable is set:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GITHUB_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_personal_access_token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Start the services using Docker Compose:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will pull the necessary images and start both services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Configure OpenHands to Use LiteLLM Proxy
&lt;/h2&gt;

&lt;p&gt;Once the services are running:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Access the OpenHands UI at &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;Advanced&lt;/strong&gt; options.&lt;/li&gt;
&lt;li&gt;Set the following configurations:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom Model&lt;/strong&gt;: &lt;code&gt;litellm_proxy/github-gpt-4o-mini&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base URL&lt;/strong&gt;: &lt;code&gt;http://litellm:4000&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Key&lt;/strong&gt;: &lt;code&gt;somekey&lt;/code&gt; (matches &lt;code&gt;LITELLM_MASTER_KEY&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These settings direct OpenHands to route requests through LiteLLM Proxy to access the GitHub Copilot model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By following these steps, you've integrated GitHub Copilot models into your development environment using LiteLLM Proxy and OpenHands.&lt;/p&gt;

</description>
      <category>litellm</category>
      <category>openhands</category>
      <category>githubcopilot</category>
      <category>docker</category>
    </item>
    <item>
      <title>Building a Raspberry Pi Cluster</title>
      <dc:creator>Sergio Pino</dc:creator>
      <pubDate>Tue, 01 May 2018 17:00:00 +0000</pubDate>
      <link>https://dev.to/spino327/building-a-raspberry-pi-cluster-3o8i</link>
      <guid>https://dev.to/spino327/building-a-raspberry-pi-cluster-3o8i</guid>
      <description>&lt;p&gt;I came across a presentation by Ray Tsang et al. that showcased a Raspberry Pi cluster with Docker and Kubernetes support &lt;sup id="fnref1"&gt;1&lt;/sup&gt;. It is very useful to have a personal Raspberry Pi cluster to explore concepts of distributed systems and create proof of concept prototypes. This post will be updated with my experiences on how to create and maintain a Raspberry Pi cluster, so I will add content over time. &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Parts
&lt;/h3&gt;

&lt;p&gt;My cluster is based on Raspberry Pi 3 Model B. I tried to follow the list of parts presented in &lt;sup id="fnref1"&gt;1&lt;/sup&gt;. Below I list the parts and links to each part in amazon (USA).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raspberry Pi 3 Model B Motherboard&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/gp/product/B01CD5VC92/ref=oh_aui_detailpage_o07_s00?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anker 60W 6-Port USB Wall Charger&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/gp/product/B00P936188/ref=oh_aui_detailpage_o07_s00?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Samsung 32GB 95MB/s (U1) MicroSD EVO Select Memory Card&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/gp/product/B06XWN9Q99/ref=oh_aui_detailpage_o06_s00?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cat 6 Ethernet Cable 5 ft (5 PACK) Flat Internet Network Cable&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/gp/product/B017R12IJA/ref=oh_aui_detailpage_o03_s00?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NETGEAR N300 Wi-Fi Router with High Power 5dBi External Antennas (WNR2020v2)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/NETGEAR-Router-External-Antennas-WNR2020v2/dp/B00MRVJYEI/ref=sr_1_1?ie=UTF8&amp;amp;qid=1526432473&amp;amp;sr=8-1&amp;amp;keywords=netgear+wnr2020" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kingston Digital USB 3.0 Portable Card Reader for SD, microSD.&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/gp/product/B00KX4TORI/ref=oh_aui_detailpage_o08_s01?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I 3D printed the cluster rack using the files &lt;a href="https://www.thingiverse.com/thing:2371586" rel="noopener noreferrer"&gt;Raspberry Cluster Frame&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fspino327.github.io%2Fassets%2Fimg%2Fc_pi_setup_small.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fspino327.github.io%2Fassets%2Fimg%2Fc_pi_setup_small.jpg" alt="setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Operating System
&lt;/h3&gt;

&lt;p&gt;I selected Hypriot OS since it already has support for docker. Also, with HypriotOS 1.7 and up, it is possible to use cloud-init to automatically change some settings on first boot. The version of &lt;code&gt;cloud-init&lt;/code&gt; that it seems to support is &lt;a href="https://cloudinit.readthedocs.io/en/0.7.9/index.html" rel="noopener noreferrer"&gt;v0.7.9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since my cluster is based on Raspberry PI 3 model B which includes a &lt;em&gt;Quad Core 1.2GHz Broadcom BCM2837 64bit CPU&lt;/em&gt; &lt;sup id="fnref2"&gt;2&lt;/sup&gt;, then I use the releases of Hypriot OS provided by the github repo &lt;code&gt;DieterReuter/image-builder-rpi64&lt;/code&gt; (64 bit distribution) instead of the ones provided by the repo &lt;code&gt;hypriot/image-builder-rpi&lt;/code&gt; (32-bit distribution) &lt;sup id="fnref3"&gt;3&lt;/sup&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.1. Flashing the OS
&lt;/h4&gt;

&lt;p&gt;First, I decided to use the command line script developed by hypriot and hosted on github &lt;code&gt;hypriot/flash&lt;/code&gt; &lt;sup id="fnref4"&gt;4&lt;/sup&gt;. The advantage of this over a more simple method such as &lt;code&gt;sudo dd if=image.img of=/dev/rdisk2 bs=1m&lt;/code&gt;. After installing the dependencies as described in the &lt;code&gt;hypriot/flash&lt;/code&gt; repo, I installed the command line utility in &lt;code&gt;$HOME/bin&lt;/code&gt;.  &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ # get the release
$ curl -O https://raw.githubusercontent.com/hypriot/flash/master/flash
$ # add executable permissions
$ chmod +x flash
$ # move to ~/bin
$ mv flash $HOME/bin/
$ # export $HOME/bin to the path
$ export PATH=$HOME/bin:$PATH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Second, I got the OS image from &lt;code&gt;DieterReuter/image-builder-rpi64&lt;/code&gt;&lt;sup id="fnref5"&gt;5&lt;/sup&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wget https://github.com/DieterReuter/image-builder-rpi64/releases/download/v20180429-184538/hypriotos-rpi64-v20180429-184538.img.zip
$ wget https://github.com/DieterReuter/image-builder-rpi64/releases/download/v20180429-184538/hypriotos-rpi64-v20180429-184538.img.zip.sha256
$ # Verify the image with sha-256
$ shasum -a 256 hypriotos-rpi64-v20180429-184538.img.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Flashing the sd-card:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ # Setting nodeX and with the user-data.yml
$ flash --hostname nodeX --userdata ./user-data.yml hypriotos-rpi64-v20180429-184538.img
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can find an example of the &lt;a href="https://gist.github.com/spino327/d514212f9f782d786cedf0487854c6f9" rel="noopener noreferrer"&gt;user-data.yml&lt;/a&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  2.1. Network configuration (using a router)
&lt;/h4&gt;

&lt;p&gt;I decided to configure the network interface using static IPs. The idea is to modify the file for &lt;code&gt;eth0&lt;/code&gt; which is located at &lt;code&gt;sudo vim /etc/network/interfaces.d/eth0&lt;/code&gt;. As an example this is the setup for &lt;em&gt;node1&lt;/em&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;allow-hotplug eth0
iface eth0 inet static
    address 192.168.2.11
    network 192.168.2.0
    netmask 255.255.255.0
    broadcast 192.168.2.255
    gateway 192.168.2.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I have a small Netgear WNR2020 wireless router dedicated to the Pi cluster, which is connected to another router that provides internet to it. I found useful to modify the DNS routes by adding the google DNS servers. Thus, I modified the file &lt;code&gt;/etc/resolv.conf&lt;/code&gt; to look like (you need &lt;code&gt;sudo&lt;/code&gt; to write to it):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat /etc/resolv.conf
  nameserver 8.8.8.8
  nameserver 8.8.4.4
  nameserver 192.168.2.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h5&gt;
  
  
  2.1.1 Network configuration (using a switch and head node with NAT)
&lt;/h5&gt;

&lt;p&gt;Recently, I updated the cluster by removing the router. Thus, now the configuration uses a switch to communicate internally with the cluster's nodes.&lt;br&gt;
Also, I configured the head node to be a NAT. Basically, the head node connects to my wireless network using &lt;code&gt;wlan0&lt;/code&gt; and serves as NAT through the ethernet interface &lt;code&gt;eth0&lt;/code&gt;.&lt;/p&gt;
&lt;h6&gt;
  
  
  2.1.1.1 Head node
&lt;/h6&gt;

&lt;p&gt;First, tell the kernel to enabled IP forwarding by either &lt;code&gt;echo 1 &amp;gt; /proc/sys/net/ipv4/ip_forward&lt;/code&gt; or by changing the line &lt;code&gt;net.ipv4.ip_forward=1&lt;/code&gt; in the file &lt;code&gt;/etc/sysctl.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Second, make the head node to act as NAT:  &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
$ sudo iptables -A FORWARD -i wlan0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
$ sudo iptables -A FORWARD -i eth0 -o wlan0 -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can save the iptable rules and restore them at boot by:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo iptables-save &amp;gt; /etc/cluster_ip_rules.fw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Third, I added the configuration for the wireless interface as:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat /etc/network/interfaces.d/wlan0 
auto wlan0
iface wlan0 inet dhcp
    wpa-ssid "Your ssid"
    wpa-psk "your password"
    gateway 192.168.1.1
    post-up iptables-restore &amp;lt; /etc/cluster_ip_rules.fw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h6&gt;
  
  
  2.1.1.2. Slave nodes
&lt;/h6&gt;

&lt;p&gt;In the other nodes, I changed the &lt;code&gt;eth0&lt;/code&gt; configuration by adding the head node IP as the gateway. So it looks like:  &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat /etc/network/interfaces.d/eth0
allow-hotplug eth0
#iface eth0 inet dhcp
iface eth0 inet static
    address 192.168.2.13
    network 192.168.2.0
    netmask 255.255.255.0
    broadcast 192.168.2.255
    gateway 192.168.2.11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;make sure that the file &lt;code&gt;/etc/resolv.conf.head&lt;/code&gt; has the line &lt;code&gt;nameserver 192.168.1.1&lt;/code&gt; or pointing to your wifi router's IP address.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.2. Setting up passwordless
&lt;/h4&gt;

&lt;p&gt;I followed the guide by Mathias Kettner &lt;sup id="fnref6"&gt;6&lt;/sup&gt; to setup ssh login without password. Basically, this is a two part process. First, create the key in your local machine.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ # generating a key that has not the default filename
$ ssh-keygen -t rsa
 Enter file in which to save the key (&amp;lt;user_path&amp;gt;/.ssh/id_rsa): &amp;lt;user_path&amp;gt;/.ssh/pic_id_rsa
 Enter passphrase (empty for no passphrase):
 Enter same passphrase again:
 Your identification has been saved in &amp;lt;user_path&amp;gt;/.ssh/pic_id_rsa.
 Your public key has been saved in &amp;lt;user_path&amp;gt;/.ssh/pic_id_rsa.pub.
 The key fingerprint is:
 ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Second, setup the public key on the remote host.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ # Create .ssh folder in remote machine
$ ssh ruser@remote 'mkdir -p ~/.ssh'
 ruser@remote's password:
$ # Send key.pub to remote machine
$ cat ~/.ssh/pic_id_rsa.pub | ssh ruser@remote 'cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys'
 ruser@remote's password:
$ # Changing permissions in the remote machine of both .ssh and .ssh/authorized_keys
$ ssh ruser@remote 'chmod 700 ~/.ssh'
$ ssh ruser@remote 'chmod 640 ~/.ssh/authorized_keys'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, you can ssh to &lt;code&gt;remote&lt;/code&gt; without password. I'm using a Mac laptop so I had to add the key to the ssh agent by &lt;code&gt;$ ssh-add $HOME/.ssh/pic_id_rsa&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Docker
&lt;/h3&gt;

&lt;p&gt;There are several docker images for &lt;strong&gt;arm64v8&lt;/strong&gt; in the &lt;a href="https://hub.docker.com/u/arm64v8/" rel="noopener noreferrer"&gt;docker registry&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Kubernetes
&lt;/h3&gt;

&lt;p&gt;In the following section, I assume that you're logged as root. If not, then you will need to insert &lt;code&gt;sudo&lt;/code&gt; in the commands. The end result should be the same.&lt;/p&gt;

&lt;p&gt;First, add the encryption key for the packages  &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Second, add repository  &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" &amp;gt;&amp;gt; /etc/apt/sources.list.d/kubernetes.list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Third, in each node install kubernetes&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apt-get install -y kubelet kubeadm kubectl kubernetes-cni
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  4.1. Configuring the master node
&lt;/h4&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubeadm init --pod-network-cidr 10.244.0.0/16 --apiserver-advertise-address 192.168.2.11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To start using your cluster, you need to run the following as a regular user:  &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  4.2. Configuring the network with Flannel
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/coreos/flannel" rel="noopener noreferrer"&gt;Flannel&lt;/a&gt; is a simple and easy way to configure a layer 3 network fabric designed for Kubernetes.\&lt;br&gt;
You just need to apply it to your cluster using &lt;code&gt;kubectl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.3. Configuring the worker nodes
&lt;/h4&gt;

&lt;p&gt;We just need to join our worker nodes using the &lt;code&gt;kubeadm join&lt;/code&gt; command and passing the token and discovery-token-ca-cert-hash that&lt;br&gt;
was provided by &lt;code&gt;kubeadm init&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubeadm &lt;span class="nb"&gt;join &lt;/span&gt;192.168.2.11:6443 &lt;span class="nt"&gt;--token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;token&amp;gt; &lt;span class="nt"&gt;--discovery-token-ca-cert-hash&lt;/span&gt; &amp;lt;sha256:...&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.4. Accessing the cluster
&lt;/h4&gt;

&lt;p&gt;To access the cluster from a remote machine with &lt;code&gt;kubectl&lt;/code&gt; you can use &lt;code&gt;kubectl proxy&lt;/code&gt;. In my case, the master node in the raspberry pi&lt;br&gt;
cluster has two network interfaces, and I wanted to connect from a machine that is in the external network (external network is &lt;code&gt;192.168.1.0/24&lt;/code&gt; and the internal network is &lt;code&gt;192.168.2.0/24&lt;/code&gt;). Thus, running &lt;code&gt;kubectl proxy&lt;/code&gt; in the master node and passing parameters to use the&lt;br&gt;
external ip of the master node (in my case &lt;code&gt;--address=192.168.1.32&lt;/code&gt;) and passing a list of allowed hosts for the IPs in the external network (&lt;code&gt;--accept-hosts="^192.168.1"&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl proxy &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8080 &lt;span class="nt"&gt;--address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.1.32 &lt;span class="nt"&gt;--accept-hosts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"^192.168.1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you can create or add into your desired KUBECONFIG file the following information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;clusters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://192.168.1.32:8080&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rpi-cluster&lt;/span&gt;
&lt;span class="na"&gt;contexts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rpi-cluster&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rpi&lt;/span&gt;
&lt;span class="na"&gt;current-context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rpi&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Config&lt;/span&gt;
&lt;span class="na"&gt;preferences&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  References
&lt;/h3&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/blog/2015/11/creating-a-raspberry-pi-cluster-running-kubernetes-the-shopping-list-part-1" rel="noopener noreferrer"&gt;R. Tsang et al. Creating a Raspberry Pi cluster running Kubernetes, the shopping list (Part 1). Kubernetes Blog. 2015.&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://www.raspberrypi.org/products/raspberry-pi-3-model-b/" rel="noopener noreferrer"&gt;Raspberry PI 3 model B specs.&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://blog.hypriot.com/post/cloud-init-cloud-on-hypriot-x64/" rel="noopener noreferrer"&gt;Bootstrapping a Cloud with Cloud-Init and HypriotOS (64-bit).&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://github.com/hypriot/flash" rel="noopener noreferrer"&gt;flash: Command line script to flash SD card images of any kind.&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;&lt;a href="https://github.com/DieterReuter/image-builder-rpi64/releases" rel="noopener noreferrer"&gt;Releases for image-builder-rpi64.&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;&lt;a href="http://www.linuxproblem.org/art_9.html" rel="noopener noreferrer"&gt;M. Kettner. SSH login without password.&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>raspberrypi</category>
      <category>cluster</category>
      <category>docker</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
