Asosiy qismga o'tish

CD va qaytarish: orqaga yo'l qancha vaqt oladi

32-bob 9 kod bloki 1673 so'z

Bu bobdagi kod shu fayllar bilan ishlaydio'z kompyuteringizda ishga tushiring

Yong'in chiqqanda eshik qidirilmaydi

Har binoda zaxira chiqish eshigi bor. U kamdan-kam ishlatiladi — ko'p odam uning qayerdaligini ham bilmaydi.

Lekin eshik yong'in paytida qurilmaydi. U oldindan quriladi, belgilanadi va sinaladi. Chunki yong'in paytida qurishga vaqt yo'q.

31-bobda darvoza qurdik — u yomon modelni chiqishdan oldin to'xtatadi. Yaxshi.

Endi noqulay savol: darvozadan o'tib ketgan muammo-chi?

Chunki o'tadi. Darvoza F1 ni tekshiradi, lekin tezlikni tekshirmaydi. Sxemani tekshiradi, lekin haqiqiy foydalanuvchi xulqini tekshirmaydi. Bir kuni ishlab chiqarishda nimadir buziladi va siz buni faqat o'sha yerda bilib olasiz.

O'shanda muhim savol bitta bo'ladi: orqaga qancha vaqtda qaytasiz?

Ikki xil jamoa

Bir xil muammo, ikki xil kun.

Birinchi jamoa. "Tuzataylik" deydi. Sabab qidiriladi, kod yoziladi, test qilinadi, CI kutiladi, chiqariladi. Eng yaxshi holatda bir soat. Odatda uch.

Bu vaqt davomida foydalanuvchilar buzilgan tizimdan foydalanadi.

Ikkinchi jamoa. "Qaytaraylik" deydi. Bitta buyruq. Ikki daqiqa. Tizim ishlayapti.

Keyin tinchgina o'tirib sababni qidiradi — endi shoshilmasdan, kimdir ustingizda turmagan holda.

Farq texnikada emas, tayyorgarlikda. Ikkinchi jamoa qaytish yo'lini oldindan qurgan.

Eng muhim ko'rsatkich — tuzatish emas, qaytish vaqti

Ishlab chiqarishda ikki raqam bor:

  • MTTR (o'rtacha tiklanish vaqti) — tizim qancha vaqt buzuq turdi;
  • MTBF (buzilishlar orasidagi vaqt) — qanchalik kam buziladi.

Ko'p jamoa ikkinchisini yaxshilashga urinadi: "boshqa hech qachon buzilmasin".

Bu imkonsiz. Har doim buziladi.

Yaxshi jamoalar birinchisiga qaraydi: buzilsa ham, tez tiklanamiz. Shuning uchun ular tez-tez, kichik qismlarda chiqaradi — chunki qaytish arzon.

Teg: latest — qaytishning dushmani

Qaytish uchun qayerga qaytishni bilish kerak. Demak har versiya nomlanishi shart.

bash19 qator
python3 -c "
import sys, pathlib; sys.path.insert(0, 'src')
if not pathlib.Path('model.onnx').exists():
    import pandas as pd
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.model_selection import train_test_split
    from quvur.xususiyat import XUSUSIYATLAR, tayyorla
    from quvur.format import onnx_ga
    d = pd.read_csv('../datasets/ijara.csv')
    x, y = tayyorla(d), d['broker']
    Xtr, _, ytr, _ = train_test_split(x, y, test_size=0.25, stratify=y, random_state=42)
    m = RandomForestClassifier(n_estimators=150, min_samples_leaf=3,
                               random_state=42, n_jobs=1).fit(Xtr, ytr)
    onnx_ga(m, len(XUSUSIYATLAR), 'model.onnx')
print('model tayyor')
"
docker images mlops-servis:v1 -q | grep -q . || docker build -q -f Dockerfile.servis -t mlops-servis:v1 . > /dev/null 2>&1
docker tag mlops-servis:v1 mlops-servis:v2 2>/dev/null
docker images mlops-servis --format '{{.Repository}}:{{.Tag}}  {{.ID}}'
Chiqish (qaytish kodi 0, 3215 ms)
model tayyor
mlops-servis:v1  2b7d80f0ae48
mlops-servis:v2  2b7d80f0ae48

`:latest` bilan qaytib bo'lmaydi

Eng keng tarqalgan xato: rasmni mlops-servis:latest deb belgilash.

Muammo shundaki, latest — bu nom, versiya emas. U doim eng oxirgisini ko'rsatadi.

Ishlab chiqarishda nima ishlayotganini so'rasangiz, javob "latest" bo'ladi. Lekin bu hech narsa demaydi: kecha ham latest edi, bugun ham latest — ichi boshqa.

Va eng yomoni: qaytmoqchi bo'lsangiz, qayerga qaytasiz? Eski rasm nomi yo'q.

Qoida: har chiqarish o'zgarmas teg oladi — v1, v2, yoki git commit xeshi (a3f9c2e). latest faqat qulaylik uchun, hech qachon ishlab chiqarishda emas.

Bu 23-bobdagi model_final_v2_YANGI.pkl muammosining aynan o'zi, faqat rasmlarda.

Chiqarish skripti

shu faylni yaratingchiqar.sh

bash45 qator
#!/usr/bin/env bash
# Yangi versiyani chiqaradi. Soglik tekshiruvidan otmasa - AVTOMATIK qaytaradi.
set -u

TEG="${1:?teg kerak: ./chiqar.sh v2}"
PORT="${2:-18130}"
NOM="mlops-joriy"
KUTISH=25

# hozirgi versiyani eslab qolamiz - qaytish uchun
ESKI=$(docker inspect "$NOM" --format '{{index .Config.Labels "versiya"}}' 2>/dev/null || echo "")

ishga_tushir() {
  local teg="$1"
  docker rm -f "$NOM" > /dev/null 2>&1
  docker run -d --name "$NOM" -p "${PORT}:8000" \
    --label "versiya=$teg" "mlops-servis:$teg" > /dev/null 2>&1
}

sogmi() {
  for _ in $(seq 1 "$KUTISH"); do
    [ "$(curl -s -o /dev/null -w %{http_code} "http://127.0.0.1:${PORT}/ready" 2>/dev/null)" = "200" ] \
      && return 0
    sleep 1
  done
  return 1
}

echo "chiqarilmoqda: $TEG (eski: ${ESKI:-yoq})"
ishga_tushir "$TEG"

if sogmi; then
  echo "OK: $TEG soglom, chiqarildi"
  exit 0
fi

echo "XATO: $TEG soglik tekshiruvidan otmadi"
if [ -n "$ESKI" ]; then
  echo "QAYTARILMOQDA -> $ESKI"
  ishga_tushir "$ESKI"
  sogmi && echo "qaytarildi: $ESKI ishlayapti" || echo "OGOHLANTIRISH: qaytarish ham muvaffaqiyatsiz"
else
  echo "qaytadigan versiya yoq (birinchi chiqarish)"
fi
exit 1

Skriptda uchta qaror bor:

Eski versiya eslab qolinadi — --label versiya=... orqali. Qaytish uchun manzil kerak, va u avtomatik olinadi, qo'lda yozilmaydi.

Sog'liq tekshiruvi kutiladi — 27-bobdagi /ready. Konteyner ishga tushgani yetarli emas (28-bob), u tayyor bo'lishi kerak.

Qaytish avtomatik — odam qaror qilmaydi. Tungi soat 3 da odam sekin qaror qiladi.

Birinchi chiqarish

bash7 qator
chmod +x chiqar.sh
./chiqar.sh v1 18130
echo "chiqish kodi: $?"
echo ""
echo -n "javob: "; curl -s http://127.0.0.1:18130/ready
echo ""
docker inspect mlops-joriy --format 'ishlayotgan versiya: {{index .Config.Labels "versiya"}}'
Chiqish (qaytish kodi 0, 3117 ms)
chiqarilmoqda: v1 (eski: yoq)
OK: v1 soglom, chiqarildi
chiqish kodi: 0

javob: {"holat":"tayyor","sorovlar":0,"chegara":0.5}
ishlayotgan versiya: v1

Buzilgan versiya chiqaramiz

Endi v2 ni ataylab buzamiz — model faylini olib tashlaymiz. 27-bobdan bilamizki, bunda servis ishga tushadi, lekin /ready 503 qaytaradi.

shu faylni yaratingDockerfile.buzuq

matn4 qator
FROM mlops-servis:v1
USER root
RUN rm -f /app/model.onnx
USER servis
bash9 qator
docker build -q -f Dockerfile.buzuq -t mlops-servis:v2 . > /dev/null 2>&1
echo "v2 qurildi (model faylisiz)"
echo ""
t0=$SECONDS
./chiqar.sh v2 18130
KOD=$?
echo ""
echo "chiqish kodi: $KOD"
echo "jami vaqt: $((SECONDS - t0)) s"
Chiqish (qaytish kodi 0, 32005 ms)
v2 qurildi (model faylisiz)

chiqarilmoqda: v2 (eski: v1)
XATO: v2 soglik tekshiruvidan otmadi
QAYTARILMOQDA -> v1
qaytarildi: v1 ishlayapti

chiqish kodi: 1
jami vaqt: 31 s

Qaytish ishladimi

bash10 qator
echo -n "servis javob beryaptimi: "
curl -s http://127.0.0.1:18130/ready
echo ""
docker inspect mlops-joriy --format 'hozirgi versiya: {{index .Config.Labels "versiya"}}'
echo ""
echo -n "bashorat ishlayaptimi: "
curl -s -X POST http://127.0.0.1:18130/bashorat -H "Content-Type: application/json" -d '{
  "xonalar": 2, "maydon": 55, "qavat": 3, "jami_qavat": 9, "narx_mln": 4.2,
  "tavsif_uzunligi": 60, "rasmlar_soni": 2, "telefon_takrorlanishi": 18,
  "kunlik_elonlar": 9, "egasi_tasdiqlangan": 0}'
Chiqish (qaytish kodi 0, 53 ms)
servis javob beryaptimi: {"holat":"tayyor","sorovlar":0,"chegara":0.5}
hozirgi versiya: v1

bashorat ishlayaptimi: {"broker_ehtimoli":0.9179,"broker":true,"chegara":0.5,"ms":3.88}

Natijani o'qing: chiqarish muvaffaqiyatsiz tugadi (chiqish kodi 1), lekin servis ishlayapti — chunki eski versiyaga qaytdi.

Foydalanuvchi buzilishni umuman ko'rmadi. U faqat bir necha soniya kutdi.

Mana CD ning asosiy va'dasi: chiqarish xavfsiz bo'lishi uchun u qaytariladigan bo'lishi kerak.

Sog'liq tekshiruvi yetarlicha chuqur bo'lsin

Bizning /ready model yuklanganini tekshiradi. Bu yaxshi, lekin hamma narsani ushlamaydi.

Tasavvur qiling: model yuklandi, /ready 200 qaytardi, lekin model noto'g'ri model — masalan boshqa loyihaniki. Sog'liq tekshiruvi buni sezmaydi.

Shuning uchun chuqurroq tekshiruv foydali: /ready ichida bitta ma'lum e'lon bo'yicha bashorat qiling va natija kutilgan oraliqda ekanini tekshiring. Bu 25-bobdagi minimal funksionallik testining ishga tushish paytidagi varianti.

Sekinroq, lekin u "ishlayapti, faqat noto'g'ri" holatini ushlaydi.

CD quvuri: to'liq rasm

python19 qator
diagramma(
    {
        "Birlashtirildi\n(main)": (0, 1.8, 2.2, 1.0, "manba"),
        "CI darvozasi\n(31-bob)": (3.0, 1.8, 2.2, 1.0, "qayta"),
        "Rasm qurish\n+ teg": (6.0, 1.8, 2.1, 1.0, "qayta"),
        "Chiqarish\n+ sog'liq": (8.9, 1.8, 2.1, 1.0, "kuzatuv"),
        "Ishlayapti": (11.8, 3.1, 2.0, 0.9, "xizmat"),
        "QAYTARISH": (11.8, 0.5, 2.0, 0.9, "manba"),
    },
    [
        ("Birlashtirildi\n(main)", "CI darvozasi\n(31-bob)"),
        ("CI darvozasi\n(31-bob)", "Rasm qurish\n+ teg"),
        ("Rasm qurish\n+ teg", "Chiqarish\n+ sog'liq"),
        ("Chiqarish\n+ sog'liq", "Ishlayapti", "sog'lom"),
        ("Chiqarish\n+ sog'liq", "QAYTARISH", "sog'liq yo'q"),
    ],
    nom="cd_quvuri.png", kenglik=15,
    izoh="Qaytarish - alohida jarayon emas, chiqarishning bir qismi.",
)

Qaytarish - alohida jarayon emas, chiqarishning bir qismi.

Qaytarish - alohida jarayon emas, chiqarishning bir qismi.

Diqqat qiling: qaytarish alohida reja emas. U chiqarish jarayonining ichida, avtomatik.

Agar qaytarish "kerak bo'lsa qo'lda qilamiz" degan narsa bo'lsa, u kerak bo'lgan paytda ishlamaydi — chunki hech kim uni sinab ko'rmagan.

Modelni qaytarish: boshqa masala

Yuqorida kod qaytarildi. Lekin model buzilgan bo'lsa-chi?

28-bobdagi tanlovni eslang. Agar model rasm ichida bo'lsa, eski rasmga qaytish modelni ham qaytaradi — bitta harakat.

Agar model tashqarida bo'lsa (registrda), kod qaytarish modelni qaytarmaydi. U yerda 23-bobdagi alias ishlatiladi: production ni eski versiyaga ko'chirasiz.

python10 qator
import pandas as pd
show(pd.DataFrame([
    {"Buzilgan narsa": "Kod", "Qaytarish usuli": "Eski rasm tegi", "Vaqti": "Soniyalar"},
    {"Buzilgan narsa": "Model (rasm ichida)", "Qaytarish usuli": "Eski rasm tegi",
     "Vaqti": "Soniyalar"},
    {"Buzilgan narsa": "Model (registrda)", "Qaytarish usuli": "Alias ko'chirish (23-bob)",
     "Vaqti": "Soniyalar"},
    {"Buzilgan narsa": "Ma'lumot sxemasi", "Qaytarish usuli": "Oson emas — migratsiya kerak",
     "Vaqti": "Soatlar"},
]))
Buzilgan narsaQaytarish usuliVaqti
KodEski rasm tegiSoniyalar
Model (rasm ichida)Eski rasm tegiSoniyalar
Model (registrda)Alias ko'chirish (23-bob)Soniyalar
Ma'lumot sxemasiOson emas — migratsiya kerakSoatlar

Oxirgi qatorga e'tibor bering: hamma narsa ham qaytarilmaydi. Ma'lumotlar bazasiga qo'shilgan ustun yoki o'zgartirilgan sxema — ularni orqaga qaytarish ancha qiyin.

Shuning uchun qoida: ma'lumot o'zgarishi va kod o'zgarishi bir chiqarishda bo'lmasin. Avval sxemani qo'shing (eski kod ham ishlaydigan qilib), keyin kodni chiqaring.

Tozalash

bash4 qator
docker rm -f mlops-joriy > /dev/null 2>&1
docker rmi mlops-servis:v2 > /dev/null 2>&1
echo "tozalandi"
docker ps --format '{{.Names}}' | grep -v k3d
Chiqish (qaytish kodi 0, 3646 ms)
tozalandi
novavivo-postgres
novavivo-redis
stratix-med-frontend-1

Xulosa

Tushuncha Asosiy fikr
Darvoza yetarli emas Ba'zi muammo faqat ishlab chiqarishda ko'rinadi
Asosiy savol "Buzilmasinmi" emas — "qancha tez qaytamiz"
MTTR Tiklanish vaqti — MTBF dan muhimroq
:latest Versiya emas, nom — qaytish imkonini yo'q qiladi
O'zgarmas teg v1, v2 yoki commit xeshi
Avtomatik qaytarish Odam emas, skript qaror qilsin
Sog'liq tekshiruvi /ready, va u yetarlicha chuqur bo'lsin
Qaytarish sinaladi Sinovdan o'tmagan qaytarish — qaytarish emas
Ma'lumot sxemasi Qaytarib bo'lmaydi — kod bilan alohida chiqaring

Talaba ishi

  1. chiqar.sh ni ikki marta ketma-ket buzuq teg bilan ishlating. Ikkinchisida nima bo'ldi?
  2. /ready ga bashorat tekshiruvini qo'shing (ma'lum e'lon → kutilgan oraliq). Endi "noto'g'ri model" holatini ushlaydimi?
  3. KUTISH ni 3 soniya qiling. Sog'lom versiya ham qaytarilib ketdimi? Bu qanday xato deb ataladi?
  4. Skriptga chiqarish jurnalini qo'shing: qachon, qaysi teg, natija. Uni faylga yozing.
  5. Model registrda bo'lgan holatni yozing: alias ko'chirish bilan qaytarish. Kod qaytarish bilan farqi nimada?

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar