Как запустить публичный сайт на телефоне или экономим на спичках
- пятница, 7 февраля 2025 г. в 00:00:15
Сейчас вас научу "плохому" — будем поднимать наше веб-приложение на телефоне и прикрутим к нему публичный домен, что бы все могли пользоваться нашим классным сервисом.
Для этой цели я накидал приложение на go, которое определяет IP адрес, вычисляет город, отправляет запрос во внешний сервис и отдает страницу с данными о погоде в вашей локации. Я не стал упарываться - он просто нужен для демонстрации. https://github.com/itcaat/what-is-the-weather-now. Также там есть кеширование погоды и городов, но скорее всего хабраэффект его положит.
В статье мы просто развлекаемся и вообще это не продакшен-реди решение ;) Но я почти уверен, что для себя вы найдете то, что сможете использовать в работе или пет-проекте.
Что нам нужно:
Само веб-приложение.
Установленный UserLAnd https://userland.tech/ ( root не потребуется )
GitHub Actions чтобы собрать приложение.
Бесплатный аккаунт на CloudFlare с нашим подключенным доменом — у меня будет devopsbrain.ru.
Итак, качаем UserLAnd можно с play market. В списке операционных систем выбираем Ubuntu (Minimal → Terminal)
и сразу там сделаем sudo passwd userland
чтобы установить пароль пользователя userland. У меня не пускало удаленно по ssh если просто passwd сделать. Я не стал разбираться, вроде на github есть какое то issue на эту тему. Поэтому едем дальше.
Теперь посмотрим в настройках wifi свой IP-адрес и подключимся с компа по ssh (порт 2022) и сразу установим пакетики.
ssh userland@192.168.1.75 -p2022
sudo apt update && apt install ca-certificates nano jq unzip -y
Само приложение и его сборка у меня уже готовы. Я собираю сразу под все платформы и архитектуры и качу релиз из main бранчи. Хотя нам нужен только arm64 для нашего эксперимента.
name: Build, Tag, and Release
on:
push:
branches:
- main
env:
appName: what-it-the-weather-now
jobs:
version:
outputs:
app_version: ${{ steps.version.outputs.new_tag }}
changelog: ${{ steps.version.outputs.changelog }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Bump version and push tag
id: version
uses: mathieudutour/github-tag-action@v6.2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
build:
runs-on: ubuntu-latest
strategy:
matrix:
goos: [linux, windows, darwin]
goarch: [amd64, arm64]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
- name: Set environment variables
run: |
echo "GOOS=${{ matrix.goos }}" >> $GITHUB_ENV
echo "GOARCH=${{ matrix.goarch }}" >> $GITHUB_ENV
- name: Install dependencies
run: go mod download
- name: Build
run: |
go build -o ${{ env.appName }}-${{ matrix.goos }}-${{ matrix.goarch }}
- name: Package binary into a ZIP file
run: |
zip -j ${{ env.appName }}-${{ matrix.goos }}-${{ matrix.goarch }}.zip ${{ env.appName }}-${{ matrix.goos }}-${{ matrix.goarch }}
- name: List generated files
run: ls -lh ${{ env.appName }}-*.zip
- name: Upload ZIP artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.appName }}-${{ matrix.goos }}-${{ matrix.goarch }}
path: ${{ env.appName }}-${{ matrix.goos }}-${{ matrix.goarch }}.zip
release:
needs:
- version
- build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
pattern: ${{ env.appName }}-*
path: my-artifact
merge-multiple: true
- name: Create GitHub Release
id: create_release
uses: ncipollo/release-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag: ${{ needs.version.outputs.app_version }}
name: Release ${{ needs.version.outputs.app_version }}
body: ${{ needs.version.outputs.changelog }}
generateReleaseNotes: true
- name: List downloaded files
run: ls -lh my-artifact
- name: Upload all release artifacts
run: |
for file in ./my-artifact/*.zip; do
echo "Uploading $file"
gh release upload ${{ needs.version.outputs.app_version }} "$file"
done
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Деплоить на телефон мы будем максимально просто - сделаем скрипт, который будет находить последний релиз и разворачивать в userland.
#!/bin/bash
REPO="itcaat/what-it-the-weather-now"
INSTALL_DIR="$HOME/what-it-the-weather-now"
BIN_NAME="what-it-the-weather-now-linux-arm64"
ZIP_FILE="$BIN_NAME.zip"
PID_FILE="$INSTALL_DIR/app.pid"
VERSION_FILE="$INSTALL_DIR/version"
FORCE_UPDATE=false
# Check if --force is used
if [[ "$1" == "--force" ]]; then
FORCE_UPDATE=true
echo "Force update enabled. Killing all instances and reinstalling..."
fi
# Function to stop the application
stop_application() {
if [[ -f "$PID_FILE" ]]; then
APP_PID=$(cat "$PID_FILE")
if ps -p "$APP_PID" > /dev/null 2>&1; then
echo "Stopping running application (PID: $APP_PID)..."
kill "$APP_PID"
sleep 2
fi
rm -f "$PID_FILE"
fi
}
# Force stop all instances if --force is enabled
if [[ "$FORCE_UPDATE" == true ]]; then
pkill -f "$BIN_NAME" 2>/dev/null
echo "Killed all running instances of $BIN_NAME."
rm -f "$PID_FILE" "$VERSION_FILE"
fi
# 1. Get the latest release tag from GitHub API
LATEST_RELEASE=$(curl -s "https://api.github.com/repos/$REPO/releases/latest" | jq -r '.tag_name')
if [[ -z "$LATEST_RELEASE" || "$LATEST_RELEASE" == "null" ]]; then
echo "Error: Failed to fetch the latest release."
exit 1
fi
echo "Latest release: $LATEST_RELEASE"
# 2. Check if the installed version is already the latest
if [[ -f "$VERSION_FILE" && "$FORCE_UPDATE" == false ]]; then
INSTALLED_VERSION=$(cat "$VERSION_FILE")
if [[ "$INSTALLED_VERSION" == "$LATEST_RELEASE" ]]; then
echo "You already have the latest version ($INSTALLED_VERSION). Exiting."
exit 0
fi
fi
# 3. Stop the running application if needed
stop_application
# 4. Construct the download URL
ASSET_URL="https://github.com/$REPO/releases/download/$LATEST_RELEASE/$ZIP_FILE"
echo "Downloading: $ASSET_URL"
# 5. Create the installation directory if it doesn't exist
mkdir -p "$INSTALL_DIR"
# 6. Download and extract the new version
curl -L "$ASSET_URL" -o "$INSTALL_DIR/$ZIP_FILE"
if [[ $? -ne 0 ]]; then
echo "Error: Failed to download the file."
exit 1
fi
echo "Extracting to $INSTALL_DIR"
unzip -o "$INSTALL_DIR/$ZIP_FILE" -d "$INSTALL_DIR"
chmod +x "$INSTALL_DIR/$BIN_NAME"
# 7. Remove the ZIP file after extraction
rm "$INSTALL_DIR/$ZIP_FILE"
# 8. Store the new version number
echo "$LATEST_RELEASE" > "$VERSION_FILE"
# 9. Start the updated application in the background
echo "Starting the updated application..."
nohup "$INSTALL_DIR/$BIN_NAME" > "$INSTALL_DIR/output.log" 2>&1 &
echo $! > "$PID_FILE"
echo "Application is running in the background (PID: $(cat $PID_FILE)). Logs: $INSTALL_DIR/output.log"
Можно передать параметр --force
, который убьет все процессы нашего приложения и заново скачает и запустит все. Также если скрипт обнаружит новый релиз, то также стопнет текущие процессы нашего приложения и раскатит новую версию.
Пробовал - не хватило памяти запуститься раннеру
Теперь просто кладем его в домашний каталог и запускаем. Он найдет последний релиз, скачает его под нашу платформу arm64 и запустит в фоне приложение. Оно у нас вешается на порт 8080.
Дальше остается просто добавить туннель в cloudflare zerotrust
. При активации вас попросит вбить карту - можно скипнуть этот шаг и сразу настроить туннель cloudflared.
По сути нам надо просто выделить либо корневой домен, либо какой то поддомен. Мы сделаем для https://weather.devopsbrain.ru. Обслуживание домена у вас должно быть в cloudflare.
Далее нам нужно выбрать нужную архитектуру и операционную систему. В нашем случае debian arm64 и запустить команду для установки cloudflared.
После установки зароутим трафик в туннель на localhost:8080.
По итогу туннель будет запущен и можно открывать наш супер сайт https://weather.devopsbrain.ru, который хостится прямо на нашем телефоне. SSL также будет из коробки выдан cloudflare. При желании можете в rules настроить редиректы http -> https.
Ну и как вы понимаете, запустить в принципе можно все что хотите (даже с бд-шками) при достаточном количестве памяти. Исходники приложения, скрипты и файлы для сборки лежат в репе https://github.com/itcaat/what-is-the-weather-now.
А на этом все - спасибо за внимание. При желании заглядывайте в тележку https://t.me/devopsbrain. Всем отличного настроения.