This is an experimental home project, Trying to build infrasturture for home project, Down the line I plan to add 2 more 8gb Raspberry pi-5 and create a small cluster with persistant storage. I will add more if nessary dependant on the scale of my personal project. The techstack I plan to use is: Anisble, Docker, K3's, GitLab Actions, Ubuntu Server LTS, Grafana, nginx, PostgresDB Prometheus and Harbor. Will add more if scaling issues occur. Will document Hardware if needed as the next stage will be a switch and two raspberry pi's to add as worker nodes.
- Open Raspberry Pi Imager.
- Choose the 'OS' you want to install from the list. I used the LTS of Ubuntu Server
- Insert your SD card/M.2 nvme and select it in the 'Storage' section.
- Before writing, click on the cog icon for advanced settings.
- Set the hostname to your desired value, e.g.,
RP1
. - Enable SSH and select the "allow public-key authorization only" option.
- Set the hostname to your desired value, e.g.,
- Click on 'Write' to begin the flashing process.
- Insert the flashed SD card/M.2 nvme into the Raspberry Pi and power it on.
- On the first boot, ssh into the Pi to perform initial configuration
- Run the following commands to update the package list and upgrade the installed packages:
sudo apt update && sudo apt upgrade
sudo vim /etc/wpa_supplicant/wpa_supplicant.conf
Add the following lines to the file:
network={
ssid=""
key_mgmt=NONE
}
Disable the Wi-Fi interface:
sudo apt install net-tools
sudo ifconfig wlan0 down
Block the Wi-Fi module using rfkill
:
sudo apt install rfkill
sudo rfkill block wifi
Prevent the Wi-Fi module from loading at boot:
sudo vim /etc/modprobe.d/raspi-blacklist.conf
Add the following line:
blacklist brcmfmac
Reboot your Raspberry Pi:
sudo reboot
- Check the status of ufw
sudo ufw status
- Allow SSH, HTTP & HTTPS:
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
- Enable ufw
sudo ufw enable
- Recheck the status if ufw:
sudo ufw status verbose
- Reload ufw
sudo ufw reload
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Create the docker group:
sudo groupadd docker
Add your user to the docker group:
sudo usermod -aG docker $USER
Activate the changes to group memberships:
newgrp docker
docker --version
docker ps
-
Login into your router
-
In WAN settings enable port forwarding for SSH
-
Enter the following:
"Service Name": "GitHub Actions" "Protocol": "TCP" "External Port": 22 "Internal Port": 22 "Internal IP": <Your Pi Private IP>
- Generate Private and Public keys on your local machine:
ssh-keygen -t ed25519 -C "github-actions" -f ~/.ssh/github-actions
ssh-copy-id -i ~/.ssh/github-actions.pub <UserName>@<Your-Pi's-IP>
- Add the following Secrets ( -> Settings -> Security -> Secrets and Variables -> Actions):
- SSH-Private-Key = cat .ssh/github-actions
"DOCKER_USERNAME": <UserName>
"DOCKER_PASSWORD": <PassWord>
"DOCKER_PROJECT": <Docker-Hub-Project-Name>
"PI_K3_SSH_PRIVATE_KEY": <SSH-Private-Key>
"SSH_IP": <Your Public IP>
"SSH_USERNAME": <Your Pi's Username>
- Make the following:
mkdir .github/workflows
touch deploy.yml
- In
deploy.yml
Add the following:
name: Build, Push, and Deploy Docker Image
on:
push:
branches:
- main
workflow_dispatch:
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_PROJECT: ${{ secrets.DOCKER_PROJECT }}
PI_K3_SSH_PRIVATE_KEY: ${{ secrets.PI_K3_SSH_PRIVATE_KEY }}
SSH_USERNAME: ${{ secrets.SSH_USERNAME }}
SSH_IP: ${{ secrets.SSH_IP }}
jobs:
docker_build_and_push:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Log in to Docker Hub
run: echo "${{ env.DOCKER_PASSWORD }}" | docker login -u "${{ env.DOCKER_USERNAME }}" --password-stdin
- name: Build Docker Image
run: |
docker build -t ${{ env.DOCKER_USERNAME }}/${{ env.DOCKER_PROJECT }}:latest .
- name: Push Docker Image
run: |
docker push ${{ env.DOCKER_USERNAME }}/${{ env.DOCKER_PROJECT }}:latest
rpi_deploy:
runs-on: ubuntu-latest
needs: docker_build_and_push
steps:
- name: Set up SSH
uses: webfactory/[email protected]
with:
ssh-private-key: ${{ env.PI_K3_SSH_PRIVATE_KEY }}
- name: Pull and Run Docker Container on Raspberry Pi
run: |
ssh -o StrictHostKeyChecking=no ${{ env.SSH_USERNAME }}@${{ env.SSH_IP }} "
docker stop ${{ env.DOCKER_PROJECT }} || true && \
docker rm ${{ env.DOCKER_PROJECT }} || true && \
docker pull ${{ env.DOCKER_USERNAME }}/${{ env.DOCKER_PROJECT }}:latest
"
curl -sSL https://install.pi-hole.net | bash
Question 1: OK
Question 2: OK
Question 3: Continue
Question 4: eth0 -> Select
Question 5: Yes -> Continue
Question 6: OK
Question 7: Google DNS -> Yes
Question 8: Yes
Question 9: Yes
Question 10: Yes
Question 11: 0 -> Continue
Question 12 Take down the information -> OK
sudo pihole -a -p
sudo vim /etc/lighttpd/lighttpd.conf
server.port = 8080
sudo ufw allow 8080
sudo systemctl restart lighttpd
http://<Raspberry-Pi_IP>:8080/admin/index.php
- Settings -> DNS -> Tick the IPv6 Google DNS -> Save
sudo apt install nginx
sudo systemctl start nginx
sudo systemctl enable nginx
sudo systemctl status nginx
- Create A class and CNAMES on DNS Provider
A Class = {
Type : A
Name : <subdomain>
Data : <raspberry-pi-ip>
TTL : 600
}
CNAME Class = {
Type : CNAME
Name : www.<subdomain>
Data : <subdomain>.<raspberry-pi-ip>
TTL : 600
}
sudo vim /etc/hosts
- Add IP and Host name to the
/etc/hosts
file.
<raspberry-pi-ip> domain.name.com
- Create a configuration file:
vim /etc/nginx/sites-available/<subdomain>.<domain.com>
- Configure the Reverse Proxy:
server {
listen 80;
server_name subdomain.domain.com www.subdomain.domain.com;
location / {
proxy_pass http://<raspberry-pi-ip>:<application_port>;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Optional: Set up basic authentication if you want to restrict access
# auth_basic "Restricted Access";
# auth_basic_user_file /etc/nginx/.pihole_htpasswd;
}
sudo ln -s /etc/nginx/sites-available/<subdomain>.<domain.com> /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
http://www.<subdomain>.<domain>.com
- On the Master Node (Primary Pi), run:
curl -sfL https://get.k3s.io | sh -
sudo cat /var/lib/rancher/k3s/server/node-token
sudo kubectl get nodes
curl -sfL https://get.k3s.io | K3S_URL=https://<master-ip>:6443 K3S_TOKEN=<node-token> sh -
sudo kubectl get nodes
-Create a Kubernetes Deployment YAML: .deployment/deployment.yml
for deploying your Docker container to the cluster:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: your-docker-username/your-image-name:tag
ports:
- containerPort: 80
- Apply the Deployment:
kubectl apply -f .deployment/deployment.yml
- To access the deployed container within your Pi network, expose it using
.deployment/service.yml
file for a NodePort service:
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
type: NodePort
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 123
nodePort: 30000 # Optional, any available port in the 30000-32767 range
This way, external requests to your Raspberry Pi on port 30000 (or whichever nodePort you choose) will be correctly routed to port 123 in your app container.
- Apply the Service:
kubectl apply -f .deployment/service.yml
- Start k3s with --write-kubeconfig-mode:
- Edit k3s Service and Add the
--write-kubeconfig-mode
Flag
sudo vim /etc/systemd/system/k3s.service
ExecStart=/usr/local/bin/k3s server --write-kubeconfig-mode 644
- Reload and Restart k3s:
sudo systemctl daemon-reload
sudo systemctl restart k3s
- Verfify Changes:
kubectl get nodes
**** WILL UPDATE ONCE FIXED ****