Как запустить публичный сайт на телефоне или экономим на спичках

от автора

Сейчас вас научу «плохому» — будем поднимать наше веб-приложение на телефоне и прикрутим к нему публичный домен, что бы все могли пользоваться нашим классным сервисом.

Для этой цели я накидал приложение на go, которое определяет IP адрес, вычисляет город, отправляет запрос во внешний сервис и отдает страницу с данными о погоде в вашей локации. Я не стал упарываться — он просто нужен для демонстрации. https://github.com/itcaat/what-is-the-weather-now. Также там есть кеширование погоды и городов, но скорее всего хабраэффект его положит.

Дисклеймер

В статье мы просто развлекаемся и вообще это не продакшен-реди решение 😉 Но я почти уверен, что для себя вы найдете то, что сможете использовать в работе или пет-проекте.

image.png

Наша погода

Что нам нужно:

  1. Само веб-приложение.

  2. Установленный UserLAnd https://userland.tech/ ( root не потребуется )

  3. GitHub Actions чтобы собрать приложение.

  4. Бесплатный аккаунт на 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 для нашего эксперимента.

.github/workflows/release.yml
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.

/deploy/install_and_run.sh
#!/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, который убьет все процессы нашего приложения и заново скачает и запустит все. Также если скрипт обнаружит новый релиз, то также стопнет текущие процессы нашего приложения и раскатит новую версию.

Почему не github actions для деплоя

Пробовал — не хватило памяти запуститься раннеру

Теперь просто кладем его в домашний каталог и запускаем. Он найдет последний релиз, скачает его под нашу платформу arm64 и запустит в фоне приложение. Оно у нас вешается на порт 8080.

image.png

image.png

Дальше остается просто добавить туннель в cloudflare zerotrust. При активации вас попросит вбить карту — можно скипнуть этот шаг и сразу настроить туннель cloudflared.

По сути нам надо просто выделить либо корневой домен, либо какой то поддомен. Мы сделаем для https://weather.devopsbrain.ru. Обслуживание домена у вас должно быть в cloudflare.

image.png

Создание нового туннеля

Далее нам нужно выбрать нужную архитектуру и операционную систему. В нашем случае debian arm64 и запустить команду для установки cloudflared.

image.png

Создание нового туннеля

После установки зароутим трафик в туннель на localhost:8080.

image.png

Создание нового туннеля

По итогу туннель будет запущен и можно открывать наш супер сайт https://weather.devopsbrain.ru, который хостится прямо на нашем телефоне. SSL также будет из коробки выдан cloudflare. При желании можете в rules настроить редиректы http -> https.

image.png

Наша погода

Ну и как вы понимаете, запустить в принципе можно все что хотите (даже с бд-шками) при достаточном количестве памяти. Исходники приложения, скрипты и файлы для сборки лежат в репе https://github.com/itcaat/what-is-the-weather-now.

А на этом все — спасибо за внимание. При желании заглядывайте в тележку https://t.me/devopsbrain. Всем отличного настроения.


ссылка на оригинал статьи https://habr.com/ru/articles/879818/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *