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-пайплайны
Изучаю сейчас
- 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-инженера — документация, инструменты, обучение.
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