Junior DevOps
Семейный сайт
Ubuntu + Nginx
Привет, я junior DevOps-инженер
Я создал этот сайт для семьи и как практический проект: чистый UI, деплой на Ubuntu, Nginx, SSL и немного автоматизации.
Чистый кодHTML / CSS / JS без фреймворков
Ubuntu + Nginxпродакшн на VPS
Семья — главноеличное и тёплое
Сайт работает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/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-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/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"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
Как код попадает из редактора на боевой сервер — шаг за шагом.
РазработкаКод в 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-конфиг
Семья
Небольшая галерея тёплых семейных моментов.
Загружаем фото...
Контакты
Напишите — всегда рад общению.
Давайте общаться
homestablelifehello@gmail.com
Telegram
LinkedIn
GitHub
roadmap.sh/devops
Ресурсы
Полезные ссылки для DevOps-инженера.
DevOps Roadmap
Полная карта навыков DevOps-инженера от начала до продакшна.
↗ roadmap.sh
Docker Docs
Контейнеры, образы, volumes, compose и лучшие практики.
↗ docs.docker.com
Kubernetes Docs
Концепции, объекты, руководства и примеры для оркестрации.
↗ kubernetes.io
Nginx Docs
Директивы, модули, конфиги для reverse proxy и веб-сервера.
↗ nginx.org
Prometheus
Мониторинг, метрики, alerting и интеграция с Grafana.
↗ prometheus.io
Grafana Docs
Дашборды, визуализация метрик, плагины и облачная версия.
↗ grafana.com
Terraform Docs
Infrastructure as Code: провайдеры, модули, state-файлы.
↗ terraform.io
GitHub Actions
CI/CD пайплайны, воркфлоу, авторы экшенов и маркетплейс.
↗ github.com/actions
The Twelve-Factor App
12 правил для надёжных облачных приложений. Обязательно к прочтению.
↗ 12factor.net