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

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

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

Подробнее Мой DevOps-путь
Чистый кодHTML / CSS / JS без фреймворков
Ubuntu + Nginxпродакшн на VPS
Семья — главноеличное и тёплое
Семья в саду — летние воспоминания
stages: - build - test - deploy docker-build: stage: build image: docker $ docker build -t app . $ nginx -t && reload git push origin main $ pm2 restart all
Сайт работает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-пайплайны

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

  • KubernetesPod, Deployment, Service, Ingress, ConfigMap, Helm
  • Prometheus + Grafanaметрики, alerting, дашборды для инфраструктуры
  • БезопасностьJWT-авторизация, CSP-заголовки, rate-limiting, fail2ban
  • Terraform / AnsibleInfrastructure as Code, идемпотентный деплой

Инструменты 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_NAME: my-app
  REGISTRY: ghcr.io/${{ github.repository_owner }}

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-and-push:
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/main'
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - name: Log in to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{{ github.actor }}
          password: ${{{ secrets.GITHUB_TOKEN }}
      - name: Build & push
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: ${{{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest

  deploy:
    runs-on: ubuntu-latest
    needs: build-and-push
    environment: production
    steps:
      - name: Deploy via SSH
        uses: appleboy/ssh-action@v1.0.3
        with:
          host:     ${{{ secrets.SSH_HOST }}
          username: ${{{ secrets.SSH_USER }}
          key:      ${{{ secrets.SSH_KEY }}
          script: |
            docker pull $IMAGE
            docker stop my-app || true
            docker rm   my-app || true
            docker run -d --name my-app \
              --restart unless-stopped \
              -p 3000:3000 $IMAGE
            docker image prune -f
GitLab CI Пайплайн с кешированием, Docker-in-Docker, деплой через SSH и Kubernetes
.gitlab-ci.yml
image: docker:24

services:
  - docker:24-dind

variables:
  DOCKER_TLS_CERTDIR: "/certs"
  IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
  IMAGE_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
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

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

deploy_prod:
  stage: deploy
  image: alpine:latest
  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 -
    - mkdir -p ~/.ssh && chmod 700 ~/.ssh
    - ssh-keyscan $SSH_HOST >> ~/.ssh/known_hosts
  script:
    - ssh $SSH_USER@$SSH_HOST "
        docker pull $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"
  only: [ main ]
  when:  on_success
Gitea Actions Self-hosted CI на Gitea — синтаксис совместим с GitHub Actions
.gitea/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [ main ]

jobs:
  build-test-deploy:
    runs-on: ubuntu-latest  # runner: act_runner

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install & Test
        run: |
          npm ci
          npm test

      - name: Build Docker image
        run: |
          docker build -t ${{ secrets.REGISTRY }}/app:${{ gitea.sha }} .
          docker tag ${{ secrets.REGISTRY }}/app:${{ gitea.sha }} \
                     ${{ secrets.REGISTRY }}/app:latest

      - name: Push to Gitea Registry
        run: |
          echo ${{ secrets.REGISTRY_TOKEN }} | \
            docker login ${{ secrets.REGISTRY }} -u ${{ secrets.REGISTRY_USER }} --password-stdin
          docker push ${{ secrets.REGISTRY }}/app:latest

      - name: Deploy via SSH
        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 -f /opt/app/docker-compose.yml up -d --no-deps app
            docker image prune -f

      - name: Notify on failure
        if: failure()
        run: |
          curl -s -X POST ${{ secrets.TELEGRAM_URL }} \
            -d "text=❌ Deploy failed: ${{ gitea.repository }} @ ${{ gitea.sha }}"
Jenkins Declarative Pipeline: параллельные стадии, Docker, деплой и Slack/Telegram уведомления
Jenkinsfile
// Declarative Pipeline — Jenkins
pipeline {
  agent { label 'docker' }

  environment {
    REGISTRY   = 'registry.example.com'
    IMAGE      = "${REGISTRY}/my-app"
    TAG        = "${env.BUILD_NUMBER}-${env.GIT_COMMIT[0..6]}"
    DEPLOY_HOST= credentials('deploy-host')
    SSH_CRED   = credentials('deploy-ssh-key')
  }

  options {
    timeout(time: 30, unit: 'MINUTES')
    buildDiscarder(logRotator(numToKeepStr: '10'))
    disableConcurrentBuilds()
  }

  stages {
    stage('Checkout') {
      steps {
        checkout scm
        sh 'echo "Branch: $GIT_BRANCH, Commit: $GIT_COMMIT"'
      }
    }

    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('Unit Tests') {
          steps {
            sh '''
              docker run --rm -v $PWD:/app -w /app node:20-alpine sh -c \
                "npm ci && npm test -- --coverage --reporters=default --reporters=jest-junit"
            '''
          }
          post {
            always {
              junit 'junit.xml'
            }
          }
        }
      }
    }

    stage('Build & Push') {
      when { branch 'main' }
      steps {
        script {
          docker.withRegistry("https://${REGISTRY}", 'registry-creds') {
            def img = docker.build("${IMAGE}:${TAG}")
            img.push()
            img.push('latest')
          }
        }
      }
    }

    stage('Deploy to Production') {
      when { branch 'main' }
      input {
        message "Deploy ${TAG} to production?"
        ok 'Deploy'
      }
      steps {
        sshagent(['deploy-ssh-key']) {
          sh """
            ssh -o StrictHostKeyChecking=no ${DEPLOY_HOST} '
              docker pull ${IMAGE}:latest
              docker stop app 2>/dev/null || true
              docker rm   app 2>/dev/null || true
              docker run -d --name app --restart unless-stopped \\
                -p 3000:3000 ${IMAGE}:latest
              docker image prune -f
            '
          """
        }
      }
    }
  }

  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

Как код попадает из редактора на боевой сервер — шаг за шагом.
Автоматизация
💻
РазработкаКод в IDE, локальный тест
📤
Push / PRgit push → GitHub
🔍
Lint & TestGitHub Actions: проверка кода
📦
BuildDocker image, минификация
🚀
DeploySSH → сервер → Nginx reload
📊
MonitorPrometheus + Grafana alert

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
Быстрые ссылки: GitHub, Telegram, LinkedIn.

Ресурсы

Полезные ссылки для DevOps-инженера — документация, инструменты, обучение.
Полезное