DevOps · Семейный Домашний проект
Junior DevOps Семейный сайт Ubuntu + Nginx

Привет, я junior DevOps-инженер

Я создал этот сайт для семьи и как практический проект: чистый UI, деплой на Ubuntu, Nginx, SSL и немного автоматизации.

Подробнее Мой DevOps-путь
Чистый кодHTML / CSS / JS без фреймворков
Ubuntu + Nginxпродакшн на VPS
Семья — главноеличное и тёплое
bash — devops@ubuntu: ~/project $ git push origin main Branch main → remote/main $ docker build -t app:latest . [+] Building 8.2s (7/8) DONE $ nginx -t && systemctl reload nginx nginx: configuration file test ok pm2 restart all [PM2] All processes restarted ● main UTF-8 ● online
Сайт работаетNginx + SSL + автообновление
CI/CD настраиваетсяGitHub Actions в процессе

О проекте

Семейная страница + практика DevOps в одном флаконе.
Учусь на практике

Зачем я это создал

  • Семейный уголок в сетиОдно место для наших моментов, заметок и воспоминаний.
  • Реальный проектЧистая разметка, адаптивный UI, деплой на боевой сервер.
  • DevOps-практикаРазвёртывание, автоматизация, структура — на настоящей странице.

Что дальше

  • Приватная панельJWT-авторизация + отдельный поддомен для семьи.
  • CI/CDGitHub Actions: lint → build → тесты → автодеплой по push.
  • МониторингPrometheus + Grafana для метрик сервера и логов Nginx.

Мой DevOps-путь

Что я изучаю и что уже применил на практике на этом сайте.
Строю навыки
99%
Uptime сервера за последние 30 дней
▲ Отлично
A+
Оценка SSL на ssllabs.com
Let's Encrypt
<1s
Среднее время ответа Nginx
▲ Быстро
v0.3
Текущая версия проекта
В разработке

Текущий стек

  • Linux (Ubuntu 22.04)процессы, сеть, права, systemd, bash-скрипты
  • Nginxreverse proxy, виртуальные хосты, SSL-терминация, gzip
  • Dockerконтейнеризация приложений, volumes, compose, образы
  • Git + GitHubветки, PR, таги, базовые Actions-пайплайны

Изучаю сейчас

Инструменты DevOps-инженера

Экосистема DevOps огромна. Вот ключевые инструменты — от разработки до мониторинга в продакшне.

CI/CD — примеры пайплайнов

Реальные конфиги для GitHub Actions, GitLab CI, Gitea, Jenkins.
Для резюме
GitHub ActionsПолный пайплайн: lint → тесты → Docker build → деплой на VPS через SSHДокументация ↗
.github/workflows/deploy.yml
name: CI/CD Deploy

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  IMAGE: ghcr.io/${{ github.repository_owner }}/my-app

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Lint Dockerfile
        uses: hadolint/hadolint-action@v3.1.0
        with:
          dockerfile: Dockerfile

  test:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: npm ci && npm test

  build-push:
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/main'
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{{ github.actor }}
          password: ${{{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v5
        with:
          push: true
          tags: ${{{ env.IMAGE }}:latest

  deploy:
    runs-on: ubuntu-latest
    needs: build-push
    environment: production
    steps:
      - uses: appleboy/ssh-action@v1.0.3
        with:
          host:     ${{{ secrets.SSH_HOST }}
          username: ${{{ secrets.SSH_USER }}
          key:      ${{{ secrets.SSH_KEY }}
          script: |
            docker pull ${{ env.IMAGE }}:latest
            docker stop app || true && docker rm app || true
            docker run -d --name app --restart unless-stopped -p 3000:3000 $IMAGE:latest
            docker image prune -f
GitLab CIDocker-in-Docker, кеширование, деплой и KubernetesДокументация ↗
.gitlab-ci.yml
image: docker:24
services:
  - docker:24-dind

variables:
  IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
  LATEST: $CI_REGISTRY_IMAGE:latest

stages: [ lint, test, build, deploy ]

lint:
  stage: lint
  image: node:20-alpine
  cache:
    key: $CI_COMMIT_REF_SLUG
    paths: [ node_modules/ ]
  script: [ npm ci, npm run lint ]

test:
  stage: test
  image: node:20-alpine
  coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
  script: [ npm ci, npm test -- --coverage ]

build:
  stage: build
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build --cache-from $LATEST -t $IMAGE -t $LATEST .
    - docker push $IMAGE && docker push $LATEST
  only: [ main ]

deploy_prod:
  stage: deploy
  image: alpine
  environment:
    name: production
    url:  https://projectforfamilystablelife.top
  before_script:
    - apk add --no-cache openssh-client
    - eval $(ssh-agent -s) && echo "$SSH_PRIVATE_KEY" | ssh-add -
    - ssh-keyscan $SSH_HOST >> ~/.ssh/known_hosts
  script:
    - ssh $SSH_USER@$SSH_HOST "docker pull $LATEST && docker compose up -d"
  only: [ main ]
Gitea ActionsSelf-hosted CI — синтаксис совместим с GitHub ActionsДокументация ↗
.gitea/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [ main ]

jobs:
  build-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: npm
      - name: Test
        run: npm ci && npm test
      - name: Build & Push Docker
        run: |
          echo ${{ secrets.REGISTRY_TOKEN }} | docker login ${{ secrets.REGISTRY }} -u ${{ secrets.REGISTRY_USER }} --password-stdin
          docker build -t ${{ secrets.REGISTRY }}/app:latest .
          docker push ${{ secrets.REGISTRY }}/app:latest
      - uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{{ secrets.SSH_HOST }}
          username: ${{{ secrets.SSH_USER }}
          key: ${{{ secrets.SSH_KEY }}
          script: docker pull ${{ secrets.REGISTRY }}/app:latest && docker compose up -d
      - name: Notify on fail
        if: failure()
        run: curl -s -X POST ${{ secrets.TELEGRAM_URL }} -d "text=❌ Deploy failed"
JenkinsDeclarative Pipeline: параллельные стадии, Docker, Telegram уведомленияДокументация ↗
Jenkinsfile
// Declarative Pipeline
pipeline {
  agent { label 'docker' }
  environment {
    REGISTRY    = 'registry.example.com'
    IMAGE       = "${REGISTRY}/my-app"
    DEPLOY_HOST = credentials('deploy-host')
  }
  options {
    timeout(time: 30, unit: 'MINUTES')
    buildDiscarder(logRotator(numToKeepStr: '10'))
  }
  stages {
    stage('Lint & Test') {
      parallel {
        stage('Lint') {
          steps { sh 'docker run --rm -v $PWD:/app -w /app node:20-alpine npm ci && npm run lint' }
        }
        stage('Test') {
          steps { sh 'docker run --rm -v $PWD:/app -w /app node:20-alpine sh -c "npm ci && npm test"' }
          post { always { junit 'junit.xml' } }
        }
      }
    }
    stage('Build & Push') {
      when { branch 'main' }
      steps {
        script {
          docker.withRegistry("https://${REGISTRY}", 'registry-creds') {
            def img = docker.build("${IMAGE}:${env.BUILD_NUMBER}")
            img.push(); img.push('latest')
          }
        }
      }
    }
    stage('Deploy') {
      when { branch 'main' }
      input { message "Deploy to production?"; ok 'Deploy' }
      steps {
        sshagent(['deploy-ssh-key']) {
          sh "ssh $DEPLOY_HOST 'docker pull ${IMAGE}:latest && docker compose up -d'"
        }
      }
    }
  }
  post {
    success { sh """curl -s "$TELEGRAM_URL" -d "text=✅ ${JOB_NAME} #${BUILD_NUMBER} deployed" """ }
    failure { sh """curl -s "$TELEGRAM_URL" -d "text=❌ ${JOB_NAME} #${BUILD_NUMBER} failed" """ }
    always  { cleanWs() }
  }
}

CI/CD Pipeline

Как код попадает из редактора на боевой сервер — шаг за шагом.
Автоматизация

Infrastructure as Code

  • Terraformописание инфраструктуры в HCL-файлах, версионирование состояния
  • Ansible Playbooksидемпотентная настройка серверов, роли и инвентари

Мониторинг и алерты

  • Prometheusсбор метрик с сервера, Nginx, Docker-контейнеров
  • Grafana + Alertmanagerдашборды, оповещения в Telegram при падении

Безопасность

  • Fail2ban + UFWблокировка брутфорса, файрвол с белыми списками
  • HTTP Security HeadersCSP, HSTS, X-Frame-Options через Nginx-конфиг

Семья

Небольшая галерея тёплых семейных моментов.
Наши моменты

Контакты

Напишите — всегда рад общению.
Связаться
Давайте общаться

Email: homestablelifehello@gmail.com

homestablelifehello@gmail.com
Telegram
LinkedIn
GitHub
roadmap.sh/devops

Ресурсы

Полезные ссылки для DevOps-инженера.
Полезное