17-bo‘lim

CI/CD va DevOps

Doimiy integratsiya va yetkazish, deploy strategiyalari, monitoring va infratuzilma kod sifatida.

🕑 14 daqiqa o‘qish 📄 975 so‘z 👁 8 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. DevOps nima?
  2. CI quvuri
  3. Deploy strategiyalari
  4. Funksiya kalitlari
  5. Muhitlar
  6. Infratuzilma kod sifatida
  7. Konteynerlar
  8. Monitoring
  9. Ogohlantirishlar
  10. Salomatlik tekshiruvi
  11. Xulosa

Kod yozildi va testdan o'tdi. Endi uni foydalanuvchiga yetkazish kerak - va buni tez, xavfsiz va takroran qilish lozim.

DevOps nima? #

Devor buzilishi Oldin Ishlab chiquvchilar devor Operatsiya jamoasi "Menda ishlaydi" / "Sizning kodingiz yomon" DevOps Bitta jamoa umumiy javobgarlik "Siz qurdingiz - siz ishlatasiz" DevOps - vosita emas, madaniyat va amaliyot
Asosiy g'oya - umumiy javobgarlik va avtomatlashtirish
CI, CD va CD
QisqartmaTo'liq nomiMa'nosi
CIContinuous IntegrationHar bir o'zgarish avtomatik qurilib testlanadi
CDContinuous DeliveryReliz istalgan paytda chiqarishga tayyor
CDContinuous DeploymentHar bir o'tgan o'zgarish avtomatik chiqariladi

Farq: Delivery da tugmani odam bosadi, Deployment da avtomatik.

CI quvuri #

To'liq quvur Kommit push Qurish bog'liqliklar Lint statik tahlil Testlar modul + integr. Xavfsizlik audit, skan Sinov staging Deploy prod Xato bo'lsa - quvur to'xtaydi, dasturchi xabardor qilinadi Maqsad: 10 daqiqadan kam Uzoq quvur - dasturchi kutmaydi va kontekstni yo'qotadi
Har bir bosqich oldingisidan qimmatroq - arzonlarini oldin bajaring
YAML
# .github/workflows/quvur.yml
name: CI/CD quvuri

on:
  push:
    branches: [main]
  pull_request:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  sifat:
    name: Kod sifati
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          coverage: none

      - name: Composer keshi
        uses: actions/cache@v4
        with:
          path: vendor
          key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }}

      - run: composer install --prefer-dist --no-progress

      - name: Kod uslubi
        run: vendor/bin/php-cs-fixer fix --dry-run --diff

      - name: Statik tahlil
        run: vendor/bin/phpstan analyse --error-format=github

      - name: Xavfsizlik auditi
        run: composer audit

  testlar:
    name: Testlar (PHP ${{ matrix.php }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        php: ['8.2', '8.3']

    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: sinov
          MYSQL_DATABASE: sinov_baza
        ports: ['3306:3306']
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-retries=5

    steps:
      - uses: actions/checkout@v4

      - uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}
          coverage: xdebug

      - run: composer install --prefer-dist --no-progress

      - name: Migratsiyalar
        run: php tools/migrate.php
        env:
          DB_HOST: 127.0.0.1
          DB_DATABASE: sinov_baza
          DB_PASSWORD: sinov

      - name: Testlar
        run: vendor/bin/phpunit --coverage-clover=coverage.xml

      - name: Qoplama hisoboti
        uses: codecov/codecov-action@v4
        with:
          files: coverage.xml

  deploy:
    name: Ishlab chiqarishga chiqarish
    needs: [sifat, testlar]
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production

    steps:
      - uses: actions/checkout@v4

      - name: Serverga chiqarish
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KALIT }}
          script: |
            cd /var/www/dokon
            ./deploy.sh ${{ github.sha }}
Quvurni tez saqlang
UsulTejaydi
Bog'liqliklarni keshlash1-3 daqiqa
Vazifalarni parallel bajarish50%+
Testlarni bo'lish (sharding)60%+
fail-fast - birinchi xatoda to'xtashKo'p
Faqat o'zgargan qismni testlashKatta loyihada
Docker qatlamlarini keshlash2-5 daqiqa

Deploy strategiyalari #

Uch asosiy strategiya To'g'ridan-to'g'ri v1 v2 To'xtash vaqti bor Sodda, kichik loyihalar uchun Ko'k-yashil Ko'k (v1) faol Yashil (v2) Balanser To'xtashsiz, bir zumda qaytish Ikki barobar resurs kerak Kanareyka v1 - 95% trafik v2 5% trafik Xavf minimal, o'lchash mumkin Murakkab infratuzilma Qaysi birini tanlash? Kichik loyiha, tungi deploy → to'g'ridan-to'g'ri To'xtash qabul qilinmaydi → ko'k-yashil Katta yuk, xavfli o'zgarish → kanareyka
Har bir strategiya o'z narxiga ega
Terminal
#!/bin/bash
# deploy.sh - atomik deploy
set -euo pipefail

RELIZ=$1
ILDIZ=/var/www/dokon
RELIZLAR=$ILDIZ/relizlar
YANGI=$RELIZLAR/$RELIZ

echo "==> Reliz tayyorlanmoqda: $RELIZ"
mkdir -p "$YANGI"
git archive "$RELIZ" | tar -x -C "$YANGI"

echo "==> Bog'liqliklar"
cd "$YANGI"
composer install --no-dev --optimize-autoloader --no-interaction

echo "==> Umumiy kataloglar"
ln -sfn "$ILDIZ/umumiy/.env" "$YANGI/.env"
ln -sfn "$ILDIZ/umumiy/yuklamalar" "$YANGI/public/yuklamalar"
rm -rf "$YANGI/storage" && ln -sfn "$ILDIZ/umumiy/storage" "$YANGI/storage"

echo "==> Migratsiyalar"
php "$YANGI/tools/migrate.php"

echo "==> Salomatlik tekshiruvi"
php "$YANGI/tools/salomatlik.php" || { echo "Tekshiruv o'tmadi"; exit 1; }

echo "==> Almashtirish"
ln -sfn "$YANGI" "$ILDIZ/joriy"
sudo systemctl reload php8.3-fpm

echo "==> Eski relizlarni tozalash (oxirgi 5 tasi qoladi)"
cd "$RELIZLAR" && ls -1t | tail -n +6 | xargs -r rm -rf

echo "==> Tayyor: $RELIZ"
Atomik almashtirish

Simvolik havolani almashtirish - bir zumda bajariladi.

Foydalanuvchi hech qachon yarim ko'chirilgan fayllarni ko'rmaydi.

Orqaga qaytish ham bir zumda:

Terminal
ln -sfn /var/www/dokon/relizlar/OLDINGI /var/www/dokon/joriy
sudo systemctl reload php8.3-fpm

Funksiya kalitlari #

PHP
final class FunksiyaKalitlari
{
    public function __construct(
        private KeshInterfeysi $kesh,
        private SozlamaRepozitoriyi $repozitoriy,
    ) {}

    public function yoqilganmi(string $nom, ?int $foydalanuvchiId = null): bool
    {
        $kalit = $this->kesh->eslab("kalit:{$nom}", 60,
            fn () => $this->repozitoriy->oling($nom));

        if ($kalit === null || !$kalit->faol) {
            return false;
        }

        if ($kalit->foiz === 100) {
            return true;
        }

        if ($foydalanuvchiId === null) {
            return false;
        }

        // Bir foydalanuvchi har doim bir xil guruhga tushsin
        $xesh = crc32("{$nom}:{$foydalanuvchiId}") % 100;

        return $xesh < $kalit->foiz;
    }
}
PHP
if ($kalitlar->yoqilganmi('yangi_savat', $foydalanuvchi->id)) {
    return $this->yangiSavat->korsating();
}

return $this->eskiSavat->korsating();
Deploy va reliz ni ajrating

Deploy - kodni serverga chiqarish (texnik amal). Reliz - imkoniyatni foydalanuvchiga ochish (biznes qarori).

Funksiya kalitlari bilan siz kodni har kuni deploy qilib, imkoniyatni kerakli paytda ochasiz.

Bu tugallanmagan ishni ham main ga merge qilish imkonini beradi.

Kalitlarni tozalab turing

Har bir kalit - kodda qo'shimcha shart.

100 ta eski kalit - o'qib bo'lmaydigan kod.

Qoida: kalit 100% ga yetgach, 2 hafta ichida uni va eski kodni o'chiring.

Muhitlar #

MuhitMaqsadMa'lumot
LokalIshlab chiqishSun'iy
CIAvtomatik testlarSun'iy, har safar toza
StagingQo'lda sinov, demoAnonimlashtirilgan nusxa
ProductionFoydalanuvchilarHaqiqiy
Ishlab chiqarish ma'lumotini staging ga nusxalash

Bu keng tarqalgan, lekin xavfli amaliyot:

  • Shaxsiy ma'lumotlar himoyasiz muhitda
  • Tasodifan haqiqiy mijozlarga email yuborilishi mumkin
  • Qonun buzilishi

To'g'ri yondashuv - anonimlashtirish:

SQL
UPDATE mijozlar SET
    ism      = CONCAT('Sinov', id),
    email    = CONCAT('sinov', id, '@example.test'),
    telefon  = CONCAT('+99890', LPAD(id, 7, '0')),
    manzil   = 'Sinov manzili';

Va staging da tashqi xizmatlarni o'chirib qo'ying: email, SMS, to'lov.

Terminal
# .env.staging
APP_MUHIT=staging
EMAIL_YUBORILSINMI=false
SMS_YUBORILSINMI=false
TOLOV_REJIMI=sinov

Infratuzilma kod sifatida #

HCL
# infratuzilma/asosiy.tf
resource "digitalocean_droplet" "veb" {
  count  = 2
  name   = "dokon-veb-${count.index + 1}"
  region = "fra1"
  size   = "s-2vcpu-4gb"
  image  = "ubuntu-24-04-x64"

  ssh_keys = [var.ssh_kalit_id]

  tags = ["dokon", "veb", "production"]
}

resource "digitalocean_database_cluster" "mysql" {
  name       = "dokon-mysql"
  engine     = "mysql"
  version    = "8"
  size       = "db-s-2vcpu-4gb"
  region     = "fra1"
  node_count = 2
}

resource "digitalocean_loadbalancer" "asosiy" {
  name   = "dokon-balanser"
  region = "fra1"

  forwarding_rule {
    entry_port     = 443
    entry_protocol = "https"
    target_port     = 80
    target_protocol = "http"
    certificate_name = "dokon-uz"
  }

  healthcheck {
    port     = 80
    protocol = "http"
    path     = "/salomatlik"
  }

  droplet_ids = digitalocean_droplet.veb[*].id
}
Terminal
terraform plan     # nima o'zgarishini ko'rsatadi
terraform apply    # qo'llaydi
IaC nima beradi?
  1. Takrorlanuvchanlik - bir xil muhitni qayta yaratish
  2. Versiyalash - infratuzilma o'zgarishi Git da
  3. Tekshiruv - PR orqali muhokama
  4. Hujjat - kod o'zi hujjat
  5. Falokatdan tiklanish - bir buyruq bilan qayta qurish

"Qo'lda sozlangan server" - bilim faqat bir odam boshida degani.

Konteynerlar #

DOCKERFILE
# Ko'p bosqichli qurish
FROM composer:2 AS bogliqliklar
WORKDIR /ilova
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --optimize-autoloader

FROM php:8.3-fpm-alpine
RUN apk add --no-cache libzip-dev \
    && docker-php-ext-install pdo_mysql opcache zip

COPY --from=bogliqliklar /ilova/vendor /var/www/vendor
COPY . /var/www

WORKDIR /var/www

RUN addgroup -g 1000 ilova && adduser -u 1000 -G ilova -D ilova \
    && chown -R ilova:ilova /var/www/storage
USER ilova

HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
  CMD php -r "exit(@file_get_contents('http://localhost:9000/salomatlik') ? 0 : 1);"

EXPOSE 9000
CMD ["php-fpm"]
YAML
# docker-compose.yml
services:
  ilova:
    build: .
    depends_on:
      mysql:
        condition: service_healthy
    environment:
      DB_HOST: mysql
    volumes:
      - ./storage:/var/www/storage

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PAROL}
      MYSQL_DATABASE: dokon
    volumes:
      - mysql_malumot:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      retries: 5

  nginx:
    image: nginx:alpine
    ports: ["80:80"]
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on: [ilova]

volumes:
  mysql_malumot:
Konteyner qoidalari
  1. Kichik obraz - alpine yoki slim
  2. Ko'p bosqichli qurish - qurish vositalari yakuniy obrazda qolmasin
  3. root sifatida ishlamang
  4. Sirlarni obrazga qo'ymang - muhit o'zgaruvchisi orqali
  5. HEALTHCHECK qo'shing
  6. .dockerignore yozing
  7. Versiyani qotiring: php:8.3-fpm-alpine, latest emas

Monitoring #

Kuzatuvchanlikning uch ustuni Metrikalar Raqamlar vaqt bo'ylab • So'rovlar soni • Javob vaqti (p50, p95, p99) • Xato darajasi • CPU, xotira, disk Jurnallar Hodisalar tafsiloti • Xato tafsilotlari • Foydalanuvchi amallari • Tashqi chaqiruvlar • Nosozlik tafsiloti Izlar So'rov yo'li • Qaysi xizmatdan o'tdi • Har birida qancha vaqt • Qayerda sekinlashdi • Bog'liqliklar zanjiri Metrikalar - "muammo bor" Jurnallar va izlar - "muammo qayerda"
Uchalasi birga to'liq manzarani beradi
PHP
// Tuzilmali jurnal
$this->jurnal->info('buyurtma_yaratildi', [
    'buyurtma_id' => $buyurtma->id,
    'mijoz_id'    => $buyurtma->mijozId,
    'summa'       => $buyurtma->jami()->tiyin,
    'elementlar'  => count($buyurtma->elementlar()),
    'davomiylik_ms' => $davomiylik,
    'sorov_id'    => $sorovId,
]);
JSON
{
  "vaqt": "2026-08-15T14:22:31.482+05:00",
  "daraja": "info",
  "hodisa": "buyurtma_yaratildi",
  "buyurtma_id": 1042,
  "mijoz_id": 7,
  "summa": 75000000,
  "elementlar": 3,
  "davomiylik_ms": 187,
  "sorov_id": "req_8f3a1d0e"
}
Tuzilmali jurnal - qidirish uchun
Natija
YOMON: "Buyurtma 1042 mijoz 7 uchun yaratildi, 187ms"
YAXSHI: {"hodisa":"buyurtma_yaratildi","buyurtma_id":1042,...}

Ikkinchisini filtrlash mumkin:

Natija
hodisa="buyurtma_yaratildi" AND davomiylik_ms > 1000
Jurnalga nimani yozmaslik kerak
  • Parollar va tokenlar
  • Karta raqamlari
  • Shaxsiy hujjat ma'lumotlari
  • To'liq so'rov tanasi (sirlar bo'lishi mumkin)
PHP
$maxfiy = ['parol', 'token', 'karta_raqami', 'cvv', 'api_kalit'];

$tozalangan = array_map(
    fn ($kalit, $qiymat) => in_array($kalit, $maxfiy, true) ? '***' : $qiymat,
    array_keys($malumot), $malumot,
);

Ogohlantirishlar #

YAML
# prometheus/qoidalar.yml
groups:
  - name: dokon
    rules:
      - alert: YuqoriXatoDarajasi
        expr: |
          sum(rate(http_sorovlar_total{kod=~"5.."}[5m]))
          / sum(rate(http_sorovlar_total[5m])) > 0.05
        for: 5m
        labels:
          jiddiylik: kritik
        annotations:
          xulosa: "Xato darajasi 5% dan yuqori"
          tavsif: "Oxirgi 5 daqiqada {{ $value | humanizePercentage }}"

      - alert: SekinJavob
        expr: |
          histogram_quantile(0.95,
            rate(http_davomiylik_seconds_bucket[5m])) > 2
        for: 10m
        labels:
          jiddiylik: ogohlantirish
        annotations:
          xulosa: "95-protsentil javob vaqti 2 soniyadan yuqori"

      - alert: BazaUlanishlariTolgan
        expr: mysql_ulanishlar_faol / mysql_ulanishlar_maks > 0.8
        for: 5m
        labels:
          jiddiylik: ogohlantirish
Ogohlantirish charchashi

Kuniga 50 ta ogohlantirish kelsa, jamoa ularni e'tiborsiz qoldiradi. Keyin haqiqiy muammo ham o'tkazib yuboriladi.

Qoidalar:

  1. Ogohlantirish harakat talab qilsin - aks holda u metrika
  2. Kritik va ogohlantirish ni ajrating
  3. Kritik - tunda uyg'otishga arziydigan darajada
  4. Muntazam ishlaydigan ogohlantirishni tuzating yoki o'chiring
  5. Oyiga bir marta qoidalarni qayta ko'rib chiqing

Salomatlik tekshiruvi #

PHP
// public/salomatlik.php
$tekshiruvlar = [
    'baza' => function (): bool {
        try {
            return (bool) app(PDO::class)->query('SELECT 1')->fetchColumn();
        } catch (Throwable) {
            return false;
        }
    },
    'kesh' => function (): bool {
        try {
            app(KeshInterfeysi::class)->saqlang('salomatlik', '1', 10);
            return app(KeshInterfeysi::class)->oling('salomatlik') === '1';
        } catch (Throwable) {
            return false;
        }
    },
    'disk' => fn (): bool => disk_free_space('/') > 1024 * 1024 * 500,
];

$natijalar = [];
$sogmi = true;

foreach ($tekshiruvlar as $nom => $tekshiruv) {
    $natija = $tekshiruv();
    $natijalar[$nom] = $natija ? 'ok' : 'xato';
    $sogmi = $sogmi && $natija;
}

http_response_code($sogmi ? 200 : 503);
header('Content-Type: application/json');

echo json_encode([
    'holat'     => $sogmi ? 'sog' : 'nosoz',
    'versiya'   => getenv('APP_VERSIYA'),
    'tekshiruvlar' => $natijalar,
], JSON_UNESCAPED_UNICODE);
Ikki xil tekshiruv
  • Liveness - "jarayon tirikmi?" Yo'q bo'lsa - qayta ishga tushiring
  • Readiness - "so'rov qabul qila oladimi?" Yo'q bo'lsa - trafik yubormang

Ishga tushish paytida ilova tirik, lekin tayyor emas.

Amaliy topshiriq
  1. Loyihangiz uchun CI quvurini yozing (lint, test, xavfsizlik).
  2. Bog'liqliklarni keshlang va vaqt farqini o'lchang.
  3. Vazifalarni parallel bajarishga o'tkazing.
  4. Atomik deploy.sh skriptini yozing.
  5. Orqaga qaytarish skriptini yozing.
  6. Funksiya kalitlari tizimini amalga oshiring.
  7. Dockerfile va docker-compose.yml yozing.
  8. Tuzilmali jurnal yozuvini qo'shing.
  9. /salomatlik endpointini yozing.
  10. Uchta ogohlantirish qoidasini yozing.

Xulosa #

  • DevOps - madaniyat va umumiy javobgarlik, vosita emas.
  • CI - har bir o'zgarish avtomatik qurilib testlanadi.
  • CD (Delivery) - reliz tayyor; CD (Deployment) - avtomatik chiqadi.
  • Quvur 10 daqiqadan kam bo'lsin; arzon bosqichlarni oldin bajaring.
  • Atomik deploy - simvolik havolani almashtirish.
  • Funksiya kalitlari deploy va relizni ajratadi.
  • Kalitlarni tozalab turing - eskilari kodni chalkashtiradi.
  • Staging da ma'lumotni anonimlashtiring, tashqi xizmatlarni o'chiring.
  • Infratuzilma kod sifatida - takrorlanuvchanlik va versiyalash.
  • Konteynerda root sifatida ishlamang, versiyani qotiring.
  • Kuzatuvchanlik: metrikalar, jurnallar, izlar.
  • Tuzilmali jurnal qidirish va filtrlash imkonini beradi.
  • Ogohlantirish charchashi - haqiqiy muammoni yashiradi.

Keyingi bo'limda xavfsizlikni ko'ramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.