Word Count: Approximately 4kEstimated Reading Time: 20 min
Background
Since I last impulsively set up the operating system on my Raspberry Pi, I have not figured out how to use it specifically, and it has just been lying in the drawer gathering dust for a year.
It came to my mind again on a Sunday afternoon: I received a reminder that the cloud server I bought from Tencent Cloud was about to expire. It’s a 4-core 8G memory Ubuntu, mainly used as a development machine, assisting in debugging code and compiling services, which is still very smooth.
However, after logging into the console and seeing the renewal price, the same performance is more than 200 more expensive than last year (388 last year, 646 this year). Comparing prices with several domestic cloud vendors, they are similar, so I gave up the renewal and thought about whether there is another way to create an independent development environment?
Keeping my desktop computer running at home is one method. Windows’ WSL is already very complete and can completely serve as a Linux development machine.
However, the services I need to run currently do not require the performance of the desktop computer, is there a lighter way to run it?
So, it’s just right, I can finally wake up the long-dormant Raspberry Pi. Although the performance of 4U4G is not exactly abundant, it is still enough to run a few services.
Content of This Article
-
Setting up the development environment (docker, various development languages, code server)
-
File server (samba)
-
Monitoring (prometheus + grafana + node exporter)
-
Remote desktop connection
-
Smart switch
-
Future prospects
System Preparation
My Raspberry Pi is installed with Ubuntu 22, using the Raspberry Pi Imager[1] (the official system flashing tool) to flash the system and install it. Specific installation can be referred to in the blog I wrote last year.
Raspberry Pi Practice Episode 1
Li Lili, WeChat Official Account: Xiao Lizi’s Daily Raspberry Pi Practice Episode 1
Installing the Development Environment
The development model I am familiar with: docker + various language development environments + vscode, installed as is on the Raspberry Pi.
Docker
The purpose of installing docker is mainly to quickly start containers that are isolated from the host environment and compile services. Especially when compiling C++ services, the system images like centos and ubuntu often come with versions of gcc and glibc that do not meet the requirements and need to be upgraded. However, upgrading glibc[2] can easily affect the system itself if not done carefully, so it is recommended to compile related services in containers.
# Install docker using Aliyun source
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh --mirror Aliyun
After installation, check the version:
❯ docker -v
Docker version 24.0.7, build afdd53b
Regarding pulling container images in China, the acceleration methods vary for different image repositories, such as docker hub official images[3], registry.k8s.io (the image repository for Kubernetes-related services, the old domain is k8s.gcr.io[4]), quay.io (Red Hat image repository), etc.
# Original way to pull images
docker pull mysql:8.0.31
docker pull registry.k8s.io/kube-apiserver:v1.17.3
docker pull quay.io/dexidp/dex:v2.28.1
# Pulling images using repository proxy
# Docker hub official images: set up the USTC mirror accelerator
# Reference: https://mirrors.ustc.edu.cn/help/dockerhub.html
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}
EOF
# Restart docker and then pull
docker pull mysql:8.0.31
# registry.k8s.io
docker pull registry.aliyuncs.com/google_containers/kube-apiserver:v1.17.3
# quay
docker pull quay.mirrors.ustc.edu.cn/dexidp/dex:v2.28.1
Development Environments for Various Languages
There are many online tutorials on how to install environments for various programming languages, and the process usually involves downloading and extracting installation packages, configuring environment variables and source configurations. Once familiar with the operations, it becomes quick, but every time the system is reinstalled or a development environment is installed based on a base image, it can be quite troublesome to repeat the process.
I have organized the installation scripts for commonly used languages and services in the shell-tools[5] repository, which can help me install with a single command, and I can also specify the version to install.
For example, to install go, java, python, and nodejs:
# go_version: specify version, default version is defined in Makefile.vars.version
# NET=CN: go installation package will be downloaded from a domestic source and set GOPROXY to goproxy.cn after installation
NET=CN go_version=1.21.4 make golang
# Install jdk1.8 (default development environment JAVA_HOME), jdk17 (used by vscode java plugin), maven, gradle
NET=CN make java
# Install conda, python 3.12
NET=CN python3_version=3.12 make python3
# Install nodejs 18.16.1
NET=CN nodejs_version=v18.16.1 make nodejs
To upgrade gcc and glibc, you can also use this script:
# Upgrade gcc to 12.3.0
NET=CN gcc_version=12.3.0 make gcc
# Upgrade glibc to 2.35
NET=CN glibc_version=2.35 make glibc
# The default installation path for glibc is /usr/local, so you need to set the environment variable to put /usr/local/lib in front
# Reference: https://unix.stackexchange.com/a/67783
export LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64:/lib
Code Server
Regarding vscode, I previously wrote an introduction to commonly used plugins. In remote development mode, install vscode locally, connect to the remote server via remote ssh, and then install various language plugins, the experience is still very smooth.
local architecture
The architecture is shown in the above image[6]: the frontend UI framework running locally on vscode, the backend server automatically runs the vscode server, responsible for running specific projects and language plugins, and providing debugger, terminal, and other functions.
Furthermore, we can also directly install code server[7] on the remote server. It is the online version of vscode, meaning the UI service also runs on the server, and can be opened directly through the browser.
The comparison between vscode and code server is shown in the image above[8], the difference is that nodejs (the UI) also runs on the server, and the local machine only needs to open the browser.
The installation and configuration method of code server on Raspberry Pi is as follows:
# Install code server 4.19.0 via shell-tools
code_server_version=4.19.0 make code-server
# Configuration: /opt/modules/code-server-4.18.0-linux-arm64/config.yaml
bind-addr: 0.0.0.0:8080
auth: password
password: codeserver
cert: falseliz
# Start
nohup /opt/modules/code-server-4.19.0-linux-arm64/bin/code-server --config /opt/modules/code-server-4.19.0-linux-arm64/config.yaml > /dev/null 2>&1 &
Then open http://RaspberryPiInternalIP:8080 to start enjoying the web version of vscode.
code server effect
Extension: If you want to provide code server for your friends to use together, the configuration will be more complicated, requiring an additional layer of oauth proxy[9] or other proxies to support multi-user login. Jupyterhub[10] is also a choice, and I can write specifically how to do it if I have the opportunity in the future.
File Server
Besides being used for development, the Raspberry Pi can also be installed with samba and serve as a file server. Later, I can mount disks on the Raspberry Pi to store videos, music, and other media, and it can also serve as a local media library.
Samba
The samba service corresponds to SMB[11] (Server Message Block), which is a protocol for sharing files in a local area network. I will directly explain how to install it on the Linux system.
# Install samba and add samba access users (note that it is different from system users)
apt -y install samba samba-common-bin
# Add a user for logging into samba: pi, and set a password
smbpasswd -a pi
# Activate the user
smbpasswd -e pi
After installation, configure the directory for open access in /etc/samba/smb.conf.
# vim /etc/samba/smb.conf
[music]
path = /data/music
writeable=Yes
valid users = pi
create mask=0777
directory mask=0777
public=yes
browseable=yes
Mounting Mechanical Hard Drive
When trying to mount the mechanical hard drive I bought earlier (as a media data disk) to the Raspberry Pi, I encountered a small problem: the hard drive is already in NTFS format and has some data on it, but the Linux system cannot read it directly and needs to install the ntfs-3g[12] driver.
apt -y install ntfs-3g
And set it to auto-mount at boot in /etc/fstab, reference:
/dev/sdb1 /data/music ntfs-3g defaults,nofail,uid=1000,gid=1000,umask=0007,x-systemd.device-timeout=5 0 0
Remote Desktop
For Linux systems, usually, when developing, writing code, and typing commands, there is little need to access the desktop. However, sometimes if you want to experience Linux applications (like QQ), you can still install a remote desktop to have a go.
The common remote desktop protocols are xrdp and vnc. Here we will configure it using xrdp:
# Install xrdp
apt -y install xrdp
# Install lxde cinnamon desktop
apt -y install lxde cinnamon-desktop-environment
# xsession is executed after the user logs into the desktop system and can be used to customize the desktop
# Reference: https://unix.stackexchange.com/a/47426
# Use cinnamon as the desktop
echo cinnamon > ~/.xsession
# Modify /etc/xrdp/startwm.sh and add 3 lines between "fi" and "test -x"
unset DBUS_SESSION_BUS_ADDRESS
unset XDG_RUNTIME_DIR
startlxde
# Start xrdp
systemctl start xrdp
# Or: /etc/init.d/xrdp restart
# Enable auto-start on boot
systemctl enable xrdp
The xrdp port 3389 can be viewed and modified in /etc/xrdp/xrdp.ini.
Then connect using Microsoft Remote Desktop:
Desktop
A very clean desktop.
Then we can install the new version of QQ adapted for Linux, downloaded from the official website[13].
# Download
wget https://dldir1.qq.com/qqfile/qq/QQNT/2355235c/linuxqq_3.1.1-11223_arm64.deb
# Install
dpkg -i linuxqq_3.1.1-11223_arm64.deb
Monitoring
After the previous steps, the development environment we set up on the Raspberry Pi is basically ready, and we can happily build images and run services on it. However, feeling the CPU slightly heating up, concerns arise: wouldn’t it be better to have a nice panel to see the current status of the Raspberry Pi and do some monitoring?
Let’s not say much, let’s start setting up the dashboard.
Getting Performance Metrics from Raspberry Pi
First, let’s look at how to get the CPU temperature from the command line:
Reference – Use the vcgencmd command to view the CPU temperature, operating speed, and voltage of Raspberry Pi[14].
vcgencmd measure_temp | grep -o -E '[[:digit:]].*'
CPU Temperature
The vcgencmd command is based on the Raspberry Pi’s kernel firmware[15] and can obtain information related to system resources and hardware status, such as clock frequency, voltage, memory, etc.
However, obtaining parameters through commands requires additional scripts, which is still not very convenient compared to the exporter method. So, is there a native exporter that can directly obtain temperature data?
Node Exporter
The system commands mentioned earlier only provide a rough look at CPU metrics. To monitor continuously, we need to collect data through prometheus and the exporter mechanism that provides system metrics, exposing relevant metrics in advance.
Node exporter[16] provides system-related monitoring metrics. After deploying its ARM version on the Raspberry Pi, we can obtain the temperature through the node_thermal_zone_temp metric, with data sourced from the system file /sys/class/thermal/thermal_zone0/temp, which directly provides the CPU temperature data from the system.
Deploying Prometheus, Node Exporter, and Grafana
Continue to use shell-tools to install with one command.
# Install prometheus and node exporter
prometheus_version=2.45.0 node_exporter_version=1.6.1 NET=CN make prometheus
# Install grafana
grafana_version=10.2.0 NET=CN make grafana
# Start
/opt/modules/prometheus/prometheus --web.enable-lifecycle --config.file=/opt/modules/prometheus/prometheus.yml --web.listen-address=:3001
/opt/modules/grafana/bin/grafana-server --homepath /opt/modules/grafana --config /opt/modules/grafana/conf/custom.ini
/opt/modules/node_exporter/node_exporter --web.listen-address=":9100"
After installation, add the local address of the node exporter to the prometheus configuration file to start collecting metrics:
# vim /opt/modules/prometheus/prometheus.yml
scrape_configs:
- job_name: "node_exporter"
static_configs:
- targets: ["localhost:9100"] # node exporter address
Prometheus Integration with Grafana
After collecting metrics, the key step is to use the “cool” grafana dashboard to display it, allowing us to see system status in real time.
The official dashboard for node exporter is node exporter full[17], which is very detailed. Import the JSON file for the dashboard and add the prometheus data source (localhost:3001) to get started.
Import Dashboard
Select Prometheus Data Source
Dashboard Effect
Auto Start Services on Boot
Sometimes the Raspberry Pi needs to restart, and we certainly want our installed services to start automatically after the system restarts. For services managed by systemctl like docker, simply execute systemctl enable docker. For manually installed code server and prometheus, how should we configure them?
There are many methods, common ones include: rc.local, init.d, crontab @reboot, etc. They each have different execution timings.
Reference – Five Methods to Set Applications to Start on Boot on Raspberry Pi[18].
When to Trigger Execution
After the Linux system starts, the /sbin/init[19] script initializes a series of system services. We can export the order of startup using systemd-analyze plot > startup_order.svg. Some auto-start scripts have the following startup order:
/etc/init.d: The first batch of system services to initialize.
rc.local: After the network service starts.
crontab @reboot: Before network starts.
Why mention the startup order? Because some services depend on network-related basic services (like network card initialization), if using /etc/init.d or crontab @reboot, it is likely to fail to start. In comparison, rc.local has a later startup order, ensuring it starts after the basic system services. Additionally, from a configuration perspective, systemctl and /etc/init.d require a certain format specification, such as service name, dependencies, start and stop commands, etc., while rc.local simply requires writing the startup commands for the services directly.
Now let’s see how to configure rc.local.
Adding Service Startup Commands to rc.local
Edit the /etc/rc.local file as the root user and add the startup commands for the services:
# vim /etc/rc.local
#!/bin/bash
# Start prometheus
nohup /opt/modules/prometheus/prometheus --web.enable-lifecycle --config.file=/opt/modules/prometheus/prometheus.yml --web.listen-address=:3001 > /dev/null 2>&1 &
# Start grafana
nohup /opt/modules/grafana/bin/grafana-server --homepath /opt/modules/grafana --config /opt/modules/grafana/conf/custom.ini > /dev/null 2>&1 &
# Start node exporter
nohup /opt/modules/node_exporter/node_exporter --web.listen-address=":9100" > /dev/null 2>&1 &
Then add executable permission with chmod +x /etc/rc.local, restart the Raspberry Pi, and log in again to find that the relevant services are already running.
Another point to note is that the commands in rc.local are executed serially. If an earlier command fails, the subsequent ones will not execute, so it is recommended to start services in the background using nohup.
Smart Socket
Given the limited performance, as we deploy more services on the Raspberry Pi, it is inevitable to encounter resource exhaustion, causing it to freeze and become unresponsive.
Several times I attempted to remotely connect to my Raspberry Pi while it was running several resource-intensive services and it froze, leaving me with no option but to restart it. So, I decided to get a Xiaomi smart socket to experience the real convenience brought by the Internet of Things.
Only enough space for one plug, slightly insufficient
Future Prospects
After more than a month of tinkering, my Raspberry Pi can finally run as a development server for the long term. Compared to the cloud server mentioned at the beginning, its performance is slightly weaker, but it also brings the benefits of being able to interface with local data and restart anytime from my phone.
What services can I run on it in the future, or what interactions can I have with the real world? I have not thought of anything particularly yet, but I will roughly list some scattered ideas to see if I have time to continue tinkering later.
NAS: I really didn’t expect that someone would actually use Raspberry Pi as NAS. According to[20], the bottleneck in data transfer speed should still be quite obvious, and it would be more reasonable to use it as a small file server.
Local Document Library + Media Library: Archive all my music, albums, videos, and e-books.
Sensors: Set up an air quality detector, reference blog[21].
Conclusion
This not-so-hardcore technical article is dedicated to the source of motivation that inspires my creativity, always possessing a quirky charm. Happy wedding to the most unique man in the world today!
References
Raspberry Pi Imager: https://www.raspberrypi.com/news/raspberry-pi-imager-imaging-utility
[2]glibc: https://zh.wikipedia.org/wiki/GNU_C%E5%87%BD%E5%BC%8F%E5%BA%93
[3]Docker Hub Official Images: https://hub.docker.com/search?image_filter=official
[4]k8s.gcr.io: https://kubernetes.io/blog/2022/11/28/registry-k8s-io-faster-cheaper-ga
[5]shell-tools: https://github.com/smiecj/shell-tools
[6]Above Image: https://code.visualstudio.com/docs/remote/faq#_how-do-the-remote-development-extensions-work
[7]Code Server: https://coder.com/docs/code-server/latest
[8]Above Image: https://insujang.github.io/2019-11-10/code-server
[9]OAuth Proxy: https://coder.com/docs/code-server/latest/guide#external-authentication
[10]JupyterHub: https://github.com/jupyterhub/jupyter-server-proxy
[11]SMB: https://baike.baidu.com/item/samba/455025
[12]ntfs-3g: https://zh.wikipedia.org/wiki/NTFS-3G
[13]Official Website: https://im.qq.com/linuxqq/index.shtml
[14]Reference – Using vcgencmd Command to View Raspberry Pi’s CPU Temperature, Operating Speed, and Voltage Information: https://blog.csdn.net/lakeheart879/article/details/77336393
[15]Firmware: https://github.com/raspberrypi/firmware
[16]Node Exporter: https://github.com/prometheus/node_exporter
[17]Node Exporter Full: https://grafana.com/grafana/dashboards/1860-node-exporter-full
[18]Reference – Five Methods to Set Applications to Start on Boot on Raspberry Pi: https://shumeipai.nxez.com/2023/05/13/run-a-program-on-your-raspberry-pi-at-startup.html
[19]/sbin/init: https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/4/html/reference_guide/s2-boot-init-shutdown-init
[20]Reference: https://post.smzdm.com/p/a4p6d8r8
[21]Reference Blog: https://www.cirmall.com/articles/35968