14 β Docker image CI va GHCR¶
β¬ οΈ Oldingi: 13 β Test va build pipeline Β· π README Β· Keyingi: 15 β Avtomatik deploy β‘οΈ
Bu bobda: 09-bobda image'ni qo'lda
docker build/docker tag/docker pushqilgan edik β endi shu jarayonni butunlay avtomatlashtiramiz: hargit pushva har release'da GitHub Actions image'ni o'zi qurib, tag qo'yib, GHCR'ga (ghcr.io/<owner>/<image>) yetkazadi;permissionsbilanGITHUB_TOKEN'gapackages: writeruxsatini beramiz,docker/login-actionbilan loginni,docker/setup-buildx-actionbilan zamonaviy builder'ni,docker/metadata-actionbilan tag/label (sha, semver, faqat main'dalatest) avtomatik generatsiyasini,docker/build-push-actionbilancache-from/cache-to: ghakeshli qurish va push'ni sozlaymiz, va nihoyat Trivy bilan har build'da zaiflik skanini qo'yib,critical/hightopilsa deploy'ni to'xtatuvchi xavfsizlik gate'ini va SARIF natijani GitHub Security'ga yuklashni β supply-chain xavfsizligining birinchi qadamini β o'rganamiz.
Muammo: har push'da qo'lda push qilishdan charchadik¶
09-bobda image'ni registry'ga chiqarishni o'rgandik. Eslang, oqim shunday edi:
docker build -t vazifalar-api:multi .
docker tag vazifalar-api:multi ghcr.io/ioqil/vazifalar-api:1.2.3
echo "$GHCR_TOKEN" | docker login ghcr.io -u ioqil --password-stdin
docker push ghcr.io/ioqil/vazifalar-api:1.2.3
Bu ishlaydi. Lekin har kichik o'zgarishdan keyin buni qo'lda takrorlash kerak. Muammolar darrov boshlanadi:
- Unutib qolasiz. Kodni push qildingiz, lekin image'ni qurishni unutdingiz β server eski versiyani ishlatib turaveradi.
- Tag chalkashadi. Bu safar
1.2.3deb tag qo'ydingiz, keyingi safar1.2.4deyishni unutib,latest'ni qaytadan yozdingiz. Qaysi image qaysi koddan ekani noaniq. - "Mening mashinamda ishlaydi". Image sizning noutbukingizda qurildi β sizning
nodeversiyangiz, sizning keshingiz bilan. Hamkasbingiznikida boshqacha chiqishi mumkin. - Xavfsizlik tekshirilmaydi. Image ichida
criticalzaiflik bo'lsa ham, hech kim sezmasdan production'ga ketadi.
Yechim β buni CI'ga topshirish. Git & GitHub kitobida va 12-13-boblarda GitHub Actions bilan tanishdik: har push'da kod testdan o'tib, build bo'ladi. Endi shu pipeline'ga yana bitta bosqich qo'shamiz β image'ni Actions runner'ida qurib, GHCR'ga avtomatik push qilish. Bir marta sozlaysiz, keyin har push o'zi qiladi.
π Asosiy g'oya: image'ni hech qachon o'z mashinangizda qo'lda qurib push qilmang. Manba haqiqati (source of truth) β git repository. CI har commit'dan toza, takrorlanadigan image quradi va uni izlanadigan tag bilan registry'ga qo'yadi.
GHCR nega CI uchun qulay¶
09-bobda ikki registry'ni ko'rgandik: Docker Hub (docker.io) va GHCR (ghcr.io). CI uchun GHCR'ning bitta katta afzalligi bor: u sizning GitHub repo'ngiz bilan birga keladi va Actions ichida login uchun alohida parol sozlash shart emas.
Har bir GitHub Actions ishi (workflow run) avtomatik ravishda GITHUB_TOKEN degan vaqtinchalik token oladi. Bu token shu run davomida amal qiladi, run tugagach o'chadi. Unga GHCR'ga push qilish huquqini berish uchun bittagina narsa kerak β workflow'da to'g'ri permissions belgilash. Tashqi Personal Access Token (PAT) yaratish, uni secrets'ga qo'yish β bularning hech biri kerak emas.
βΉοΈ Docker Hub yoki boshqa tashqi registry'ga push qilmoqchi bo'lsangiz, u yerda GITHUB_TOKEN ishlamaydi β o'sha registry'ning hisobi uchun PAT/parol yaratib, GitHub repo'ngizning Settings β Secrets'iga (masalan DOCKERHUB_TOKEN) qo'shasiz va login-action'ga shu secret'ni berasiz. GHCR esa bularsiz, "ichki" ishlaydi.
Ruxsatlar: permissions blokini to'g'ri qo'yish¶
Standart holatda GITHUB_TOKEN faqat o'qish huquqiga ega bo'lishi mumkin. Image'ni GHCR'ga push qilish β bu "package yozish" amali, shuning uchun token'ga aniq ruxsat berish kerak. Buni workflow yoki job darajasida permissions bilan beramiz:
permissions:
contents: read # repo kodini o'qish (checkout uchun)
packages: write # GHCR'ga (package) image push qilish
contents: readβactions/checkout'ga repo kodini o'qishga ruxsat.packages: writeβ eng muhimi: GHCR'ga image (GitHub terminologiyasida "package") yozish ruxsati.
π Eng kam imtiyoz tamoyili (principle of least privilege): token'ga faqat kerakli ruxsatni bering, ortig'ini emas. Bu yerda bizga kodni o'qish va package yozishdan boshqa hech narsa kerak emas β shuning uchun write-all kabi keng ruxsat bermaymiz. Token o'g'irlansa ham, qila oladigan zarari shu ikki narsadan oshmaydi.
β οΈ permissions blokini umuman yozmasangiz, repo'ngizning standart sozlamasiga tushib qolasiz β u esa juda cheklangan (push ishlamaydi) yoki aksincha juda keng bo'lishi mumkin. Shuning uchun har doim aniq yozing.
To'liq workflow: qurish va GHCR'ga push¶
Endi bo'laklarni birlashtiramiz. Quyidagi workflow .github/workflows/docker-publish.yml ichida turadi. U har main'ga push'da, har v*.*.* tag'da va har release'da ishlaydi:
name: Docker image qurish va GHCR'ga push
on:
push:
branches: [main]
tags: ['v*.*.*']
release:
types: [published]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Kodni checkout qilish
uses: actions/checkout@v6
- name: GHCR'ga login
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Buildx sozlash
uses: docker/setup-buildx-action@v4
- name: Tag va label generatsiya (metadata)
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha
type=semver,pattern={{version}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Image qurish va push
uses: docker/build-push-action@v7
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Bir oz uzun ko'rinadi, lekin har bir bosqich aniq bir ish qiladi. Endi qadam-baqadam ko'rib chiqamiz.
env: takrorlanadigan qiymatlar¶
github.repository β bu egasi/repo-nomi ko'rinishida keladi (masalan ioqil/vazifalar-api). Demak image'ning to'liq nomi ghcr.io/ioqil/vazifalar-api bo'ladi β aynan 09-bobdagidek. Bu qiymatlarni env'ga chiqarib, pastda bir necha marta takrorlashdan saqlanamiz.
Login: docker/login-action@v4¶
- name: GHCR'ga login
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
Bu 09-bobdagi docker login ghcr.io -u ... --password-stdin'ning Actions'dagi muqobili.
registry: ghcr.ioβ qaysi registry'ga kirayotganimiz.username: ${{ github.actor }}β workflow'ni ishga tushirgan foydalanuvchi nomi.password: ${{ secrets.GITHUB_TOKEN }}β yuqorida aytgan avtomatik token. Buni biz yaratmaymiz β GitHub har run uchun o'zi beradi.
π‘ ${{ secrets.GITHUB_TOKEN }} β bu yagona "avtomatik" secret. Boshqa hech qanday secret sozlamasdan GHCR'ga push qila olamiz, chunki permissions'da packages: write berdik.
Buildx: docker/setup-buildx-action@v4¶
Buildx β Docker'ning zamonaviy builder'i (BuildKit ustida). U keshlash, parallel qurish va multi-platform image'larni qo'llab-quvvatlaydi. Keyingi bosqichdagi cache-from/cache-to: gha (GitHub Actions keshi) aynan shu builder bilan ishlaydi. Bitta qator β keyingi hamma narsa shuning ustida ishlaydi.
Metadata: docker/metadata-action@v6 β tag'larni avtomatik o'ylab topadi¶
Bu β workflow'ning eng "aqlli" qismi. 09-bobda tag'larni qo'lda yozardik (docker tag ...:1.2.3, ...:sha-a1b2c3d). metadata-action shu tag'larni kontekstdan kelib chiqib o'zi generatsiya qiladi:
- name: Tag va label generatsiya (metadata)
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha
type=semver,pattern={{version}}
type=raw,value=latest,enable={{is_default_branch}}
tags: ostidagi har bir qator β bitta qoida:
type=shaβ git commit SHA'sidan tag yasaydi (masalansha-a1b2c3d). Bu immutable β aynan qaysi koddan qurilgani har doim aniq.type=semver,pattern={{version}}β agar pushv1.2.3ko'rinishidagi git tag'i bo'lsa, undan1.2.3image tag'ini yasaydi. Oddiy branch push'da bu qoida hech narsa qo'shmaydi.type=raw,value=latest,enable={{is_default_branch}}βlatesttag'ini faqat default branch (main) bo'lganda qo'shadi. Boshqa branch'dalatesto'zgarmaydi.
π Mana, 09-bobdagi tag strategiyamiz avtomatlashdi: sha (immutable), semver (reliz), latest (faqat main). id: meta muhim β keyingi bosqich natijaga steps.meta.outputs.tags orqali murojaat qiladi.
Bundan tashqari metadata-action foydali label'lar ham yasaydi (OCI standart): qaysi commit, qaysi vaqt, qaysi repo'dan qurilgani image'ning o'ziga yoziladi. Bu keyinchalik "bu image qayerdan keldi?" degan savolga javob beradi.
Qurish va push: docker/build-push-action@v7¶
- name: Image qurish va push
uses: docker/build-push-action@v7
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Bu bitta bosqich docker build + docker push'ni birlashtiradi:
context: .β Dockerfile va kod repo ildizida (09-bobdagi multi-stage Dockerfile).push: trueβ qurgandan keyin GHCR'ga push qil. (falseqilsangiz faqat quradi, push qilmaydi β buni Trivy bilan ishlaganda ko'ramiz.)tags/labelsβmetadata-actionyasagan tag va label'larni shu yerda ishlatamiz. Bitta build, lekin bir nechta tag β xuddi 09-bobdagi "bitta image, uchta tag".cache-from: type=gha/cache-to: type=gha,mode=maxβ build keshini GitHub Actions keshida saqlaydi (gha= GitHub Actions). Keyingi build o'zgarmagan qatlamlarni keshdan oladi β image qurish ancha tezlashadi.mode=maxβ barcha oraliq qatlamlarni ham keshlaydi (multi-stage uchun foydali).
π‘ cache-to: gha birinchi run'da hech narsani tezlashtirmaydi (kesh bo'sh), lekin ikkinchi run'dan boshlab β agar package.json o'zgarmagan bo'lsa β npm install qatlami keshdan olinadi va build bir necha barobar tez tugaydi. Aynan 09-bobdagi "kam o'zgaradigan narsani avval ko'chir" qoidasining CI'dagi davomi.
β οΈ Push faqat siz repo'ga haqiqatan egalik qilganingizda va permissions: packages: write to'g'ri qo'yilganda ishlaydi. Birinchi push'dan keyin image GitHub repo'ngizning "Packages" bo'limida ko'rinadi. U standart holatda private bo'ladi (09-bobda aytganimizdek) β public qilmoqchi bo'lsangiz, package sozlamalaridan o'zgartirasiz.
βΉοΈ Illustrativ: yuqoridagi workflow GHCR'ga haqiqiy push qiladi, bu esa real GitHub repo, real run va internet talab qiladi. Biz buni lokalda ishga tushira olmaymiz β buni o'z repo'ngizda .github/workflows/'ga qo'yib sinab ko'rasiz. Lekin biz workflow YAML'ini struktura va sintaksis jihatidan tekshirdik, va o'sha Dockerfile'ni lokalda haqiqatan qurib ishlashiga ishonch hosil qildik (bob oxiridagi tekshiruvga qarang).
Tashqi registry uchun secret (Docker Hub misoli)¶
GHCR uchun secret kerak emas edi. Lekin Docker Hub yoki boshqa registry'ga push qilsangiz, uning hisobi uchun token kerak. Avval GitHub repo'da Settings β Secrets and variables β Actions ostida secret qo'shasiz (masalan DOCKERHUB_TOKEN), keyin:
- name: Docker Hub'ga login
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
registry'ni yozmasak, standart docker.io (Docker Hub) bo'ladi.
π Secret'lar log'da hech qachon to'liq ko'rinmaydi (GitHub ularni avtomatik *** bilan yashiradi). Parolni hech qachon workflow YAML'iga to'g'ridan-to'g'ri yozmang β har doim secrets.'dan oling. Docker Hub'da parol o'rniga Access Token ishlating (Docker Hub Account Settings β Security'dan yaratiladi).
Supply-chain xavfsizligi: Trivy bilan zaiflik skani¶
Endi eng muhim qadamlardan biriga keldik. Image qurildi, push qilinmoqda β lekin uning ichida ma'lum zaifliklar (CVE) bormi? Eski openssl, zaif kutubxona, base image'da tuzatilmagan teshik? 09-bobda docker scout/trivyni qisqa ko'rgandik. Endi uni pipeline'ga qo'yamiz, shunda har build avtomatik skanerlanadi.
Bu β supply-chain xavfsizligining birinchi qadami. Supply-chain (ta'minot zanjiri) β sizning kodingizdan tashqari, siz ishlatadigan hamma narsa: base image, npm paketlari, ularning bog'liqliklari. Bularning birortasida zaiflik bo'lsa, u sizning production image'ingizga ham o'tadi. Trivy aynan shuni topadi.
aquasecurity/trivy-action bilan image'ni skanerlaymiz va critical/high zaiflik topilsa, pipeline'ni to'xtatamiz (fail). Mantiq quyidagicha: avval image'ni quramiz (lekin hali push qilmaymiz), skanerlaymiz, faqat toza bo'lsa push qilamiz.
name: Docker image + Trivy zaiflik skani
on:
push:
branches: [main]
pull_request:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-scan-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
security-events: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Buildx sozlash
uses: docker/setup-buildx-action@v4
- name: Metadata (tag/label)
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha
type=raw,value=latest,enable={{is_default_branch}}
- name: Image qurish (lokal, hali push emas)
uses: docker/build-push-action@v7
with:
context: .
load: true
push: false
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Trivy skan (gate)
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: '1'
ignore-unfixed: true
- name: SARIF natijani Security'ga yuklash
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
- name: GHCR'ga login
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Skan o'tdi - endi push
uses: docker/build-push-action@v7
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
Yangi bo'laklar:
permissions: security-events: writeβ qo'shildi, chunki skan natijasini (SARIF) GitHub'ning Security bo'limiga yuklaymiz.- Avval
load: true, push: falseβ image'ni runner'ning lokal Docker'iga yuklaymiz, lekin GHCR'ga push qilmaymiz. Avval tekshirilsin. - Trivy gate:
severity: CRITICAL,HIGHβ faqat shu darajadagi zaifliklarga e'tibor.exit-code: '1'β agar shunday zaiflik topilsa, qadam xato (fail) bilan tugaydi va butun pipeline to'xtaydi β demak push bo'lmaydi. Bu β xavfsizlik gate'i.ignore-unfixed: trueβ hali tuzatilmagan (fix yo'q) zaifliklarni e'tiborga olmaydi β aks holda siz qila oladigan hech narsasi yo'q xatolardan pipeline behuda yiqiladi.format: sarif,output: trivy-results.sarifβ natijani standart SARIF formatida faylga yozadi.- SARIF'ni yuklash (
github/codeql-action/upload-sarif) βif: always()bilan, ya'ni skan yiqilsa ham natijani yuklaydi. Topilgan zaifliklar GitHub repo'ngizning Security β Code scanning bo'limida chiroyli ro'yxat bo'lib ko'rinadi.
π Tartib muhim: build β scan β (toza bo'lsa) push. Agar avval push qilib, keyin skanerlasangiz, zaif image allaqachon registry'da turgan bo'ladi. Shuning uchun gate'ni push'dan oldin qo'yamiz.
βΉοΈ Illustrativ: Trivy jonli ishlaganda internetdan eng yangi CVE bazasini yuklab oladi va aniq raqamlar sizning image'ingizga, o'sha kungi CVE bazasiga bog'liq. Masalan, hisobot taxminan shunday namuna ko'rinishda bo'ladi (bu β illyustratsiya, haqiqiy natija emas):
Agar CRITICAL yoki HIGH topilsa, exit-code: '1' tufayli qadam qizil bo'ladi va push bosqichigacha yetib bormaydi. Biz bu workflow'ni real GitHub'da ishga tushira olmaymiz (real run/CVE baza kerak), lekin YAML'ni struktura va sintaksis bo'yicha tekshirdik.
π‘ Zaiflikni kamaytirishning eng samarali yo'li (09-bobda aytganimizdek) β kichik base (-slim/distroless) va base image'ni muntazam yangilab turish. Trivy faqat ko'rsatadi; tuzatish β base'ni yangilash yoki zaif paketni almashtirish.
Hammasini birlashtirib: bitta git pushdan production-ready image¶
Endi katta rasmni ko'ramiz. Siz lokalda kod yozasiz, test qilasiz, keyin:
Shu bitta git pushdan keyin GitHub Actions o'zi:
- Kodni checkout qiladi.
- Buildx'ni sozlaydi.
metadata-actionbilan tag'larni hisoblaydi (sha-<commit>,latest, agar reliz bo'lsa1.2.3).- Image'ni quradi (keshdan foydalanib, tez).
- Trivy bilan zaiflikka skanerlaydi β
critical/highbo'lsa to'xtaydi. - Toza bo'lsa β GHCR'ga push qiladi.
Endi serveringizdagi yoki Kubernetes'dagi ish β shunchaki ghcr.io/ioqil/vazifalar-api:sha-<commit>'ni pull qilish. Buni avtomatik qilish β keyingi (15-bob, avtomatik deploy) mavzu. Hozircha pipeline image'ni tayyor, tag'langan va skanerlangan holda GHCR'ga yetkazadi.
π Bu β DevOps'ning markaziy g'oyasi: manba git, qolgani avtomatik. Inson qo'li faqat kodga tegadi; build, tag, skan, push β hammasi takrorlanadigan va kuzatiladigan (har run log'da ko'rinadi).
14-bob mashqlari¶
Workflow YAML'larini lokalda
python -c "import yaml; ..."vapython -m yamllint -d relaxed <fayl>bilan tekshirishingiz mumkin.docker build(push'siz) lokalda ishlaydi. Real GHCR push / Actions run / jonli Trivy skan β GitHub hisobi va internet talab qiladi (illustrativ β o'z repo'ngizda sinang).
Oson
- GHCR'ga push qiladigan workflow'da
permissionsbloki nima uchun kerak va qaysi ikki ruxsat yoziladi? Har birini bir jumlada izohlang. docker/login-actionda GHCR uchunpasswordsifatida nima beriladi? Bu qiymatni siz yaratasizmi yoki GitHub o'zi beradimi?metadata-action'dagitype=raw,value=latest,enable={{is_default_branch}}qatori nima qiladi? Negalatest'ni faqatmain'da qo'shamiz?cache-from: type=ghavacache-to: type=ghanimani tezlashtiradi? Birinchi run'da ham tez bo'ladimi?- Image'ning to'liq GHCR nomini yozing: foydalanuvchi
aziz, repoblog-api.${{ github.repository }}bu yerda nimaga teng bo'ladi?
O'rta
- Quyidagi workflow bo'lagidagi xatoni toping va tuzating (image push qilinmoqda, lekin nega ishlamaydi?):
jobs:
push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v7
with:
push: true
tags: ghcr.io/aziz/blog-api:latest
Yechim
permissions bloki yo'q. GITHUB_TOKEN standart holatda GHCR'ga (packages) yozish huquqiga ega bo'lmaydi, shuning uchun push: true "denied" xatosi beradi. Job'ga ruxsat qo'shamiz:
jobs:
push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v6
# ... qolgani o'zgarmaydi
(Ixtiyoriy: context: . ham qo'yilsa aniqroq bo'ladi, lekin u standart ..)
metadata-action'siz, qo'lda uchta tag (sha, semver, latest) yozmoqchisiz.build-push-action'ningtags:maydoniga ularni qanday yozasiz (foydalanuvchiaziz, imageblog-api, shaa1b2c3d, versiya2.0.0)?
Yechim
- uses: docker/build-push-action@v7
with:
context: .
push: true
tags: |
ghcr.io/aziz/blog-api:sha-a1b2c3d
ghcr.io/aziz/blog-api:2.0.0
ghcr.io/aziz/blog-api:latest
Bir nechta tag β har biri alohida qatorda (| blok-skalyari). Bitta build, uch tag β barchasi bir Image ID'ga ishora qiladi (09-bobdagidek). metadata-action aynan shuni avtomatlashtirgan edi: qo'lda yozish xatoga moyil va commit SHA'ni har safar nusxalash kerak.
type=shatag (immutable) bilanlatest(mutable) farqini production deploy nuqtai nazaridan tushuntiring. Server qaysisinipullqilgani ma'qul va nega?
Yechim
type=sha (sha-a1b2c3d) β immutable: bir marta push qilingach hech qachon o'zgarmaydi, aynan qaysi commit'dan qurilgani aniq. latest β mutable: har push'da boshqa image'ga ko'chadi. Production server sha-... (yoki semver 1.2.3) ni pull qilgani ma'qul, chunki: (1) qaysi versiya ishlayotgani aniq, (2) rollback oson (oldingi sha'ga qaytasiz), (3) ikki server bir vaqtda bir xil image oladi. latest'ga tayanish β "qaysi versiya ekani noaniq" muammosini keltiradi (09-bobda ko'rgandik).
- Trivy gate'ida
exit-code: '1'vaseverity: CRITICAL,HIGHnima qiladi?ignore-unfixed: truenega foydali?
Yechim
severity: CRITICAL,HIGH β Trivy faqat shu ikki darajadagi zaiflikka e'tibor beradi (medium/low'ni hisobotda ko'rsatsa-da, gate uchun hisobga olmaydi). exit-code: '1' β agar shunday zaiflik topilsa, qadam xato bilan tugaydi va pipeline to'xtaydi β demak push bo'lmaydi (xavfsizlik gate). ignore-unfixed: true β hali tuzatuvi (fix) chiqmagan zaifliklarni e'tiborsiz qoldiradi; aks holda siz hech narsa qila olmaydigan CVE'lar tufayli pipeline behuda yiqiladi. Bu amaliy muvozanat: jiddiy va tuzatib bo'ladigan muammoda to'xta.
- Nega Trivy skanini push'dan oldin qo'yamiz, push'dan keyin emas? Workflow'da bu qanday tartibda ifodalanadi?
Yechim
Agar avval push qilib, keyin skanerlasak, zaif image allaqachon GHCR'da turadi β uni boshqalar pull qilib olishi mumkin. Gate ma'nosini yo'qotadi. Shuning uchun tartib: build (load: true, push: false) β Trivy skan (gate) β faqat toza bo'lsa push (push: true). Skan yiqilsa, push qadami umuman ishga tushmaydi (oldingi qadam fail bo'lgani uchun).
Qiyin
- 09-bobdagi namuna
vazifalar-api(Express) uchun GHCR'ga push qiluvchi to'liq workflow yozing: triggermain'ga push vav*.*.*tag;permissionsto'g'ri; login, buildx, metadata (sha + semver + latest faqat main), build-push (kesh bilan). YAML'niyamllint -d relaxedbilan tekshiring.
Yechim
.github/workflows/docker-publish.yml:
name: Docker image qurish va GHCR'ga push
on:
push:
branches: [main]
tags: ['v*.*.*']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v6
- uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/setup-buildx-action@v4
- id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha
type=semver,pattern={{version}}
type=raw,value=latest,enable={{is_default_branch}}
- uses: docker/build-push-action@v7
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Tekshirish (lokal):
python -c "import yaml,sys; list(yaml.safe_load_all(open(sys.argv[1]))); print('parse OK')" docker-publish.yml
python -m yamllint -d relaxed docker-publish.yml
Bu workflow main push'da (sha + latest) va v1.2.3 tag push'da (semver) image'ni quradi va GHCR'ga push qiladi. (Real push GitHub repo va internet talab qiladi β illustrativ.)
- 11-mashqdagi workflow'ga Trivy zaiflik gate'ini qo'shing: image avval
push: falsebilan qurilsin, TrivyCRITICAL,HIGH'daexit-code: '1'bilan skanerlasin, SARIF natija GitHub Security'ga yuklansin, keyin (toza bo'lsa) push bo'lsin.permissions'ga nima qo'shiladi?
Yechim
permissions'ga security-events: write qo'shiladi (SARIF yuklash uchun). Build qadamiga load: true, push: false, so'ng Trivy va SARIF yuklash, oxirida push qadami:
permissions:
contents: read
packages: write
security-events: write
steps:
- uses: actions/checkout@v6
- uses: docker/setup-buildx-action@v4
- id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha
type=raw,value=latest,enable={{is_default_branch}}
- name: Qurish (push'siz)
uses: docker/build-push-action@v7
with:
context: .
load: true
push: false
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Trivy skan
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: '1'
ignore-unfixed: true
- name: SARIF yuklash
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
- uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push (skan o'tgach)
uses: docker/build-push-action@v7
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
if: always() SARIF yuklashda muhim β skan yiqilsa ham natija Security bo'limiga tushadi. (Illustrativ β jonli Trivy CVE bazasini yuklaydi.)
- Docker Hub'ga (GHCR emas) push qilmoqchisiz. GHCR variantidan nimasi farq qiladi?
secretsqaysi,login-actionqanday o'zgaradi,permissions: packages: writekerakmi?
Yechim
Farqlar:
- Secret kerak: Docker Hub'da GITHUB_TOKEN ishlamaydi. Docker Hub Account Settings β Security'dan Access Token yaratib, GitHub repo Settings β Secrets'iga (masalan DOCKERHUB_TOKEN, DOCKERHUB_USERNAME) qo'shasiz.
- login-action: registry'ni yozmaysiz (standart docker.io), username/password'ni secret'lardan olasiz:
- uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
permissions: packages: writekerak emas: u faqat GHCR (GitHub packages) uchun. Docker Hub'ga push autentifikatsiya secret token orqali bo'ladi, GitHub token ruxsatiga bog'liq emas.- Image nomi:
aziz/blog-api(yokidocker.io/aziz/blog-api) βghcr.io/prefiksisiz.
- Workflow ishladi, lekin
docker/build-push-action"denied: permission_denied: write_package" xatosi bilan to'xtadi. Ehtimoliy ikkita sababni va yechimni ayting.
Yechim
Ikkita asosiy sabab:
permissions: packages: writeyo'q (yokicontents: reado'rniga noto'g'ri yozilgan). Yechim: job/workflow'ga aniqpermissionsblokini qo'shish.- Repo darajasidagi standart ruxsat cheklangan. GitHub repo
Settings β Actions β General β Workflow permissionsda "Read repository contents permission" tanlangan bo'lsa, hatto workflow'da yozsangiz ham cheklov bo'lishi mumkin. Yechim: "Read and write permissions"ni yoqish yoki ko'pincha workflow'dagi aniqpermissionsbloki yetarli.
Qo'shimcha: image nomi ${{ github.repository }}'ga mos kelmasa (boshqa egaga push) ham denied bo'ladi β egasi sizning hisobingiz/tashkilotingiz bo'lishi kerak.
- Bir jamoadosh "har push'da
latest'ni serverga pull qilamiz, shunda doim eng yangisi bo'ladi" deydi. Bu yondashuvning xavfini tushuntiring va CI tag strategiyasi bilan qanday to'g'ri qilishni taklif qiling.
Yechim
Xavf: latest β mutable, har push'da boshqa image'ga ko'chadi. Server pull latest qilganda qaysi versiya kelishi noaniq; ikki server turli vaqtda turli image olishi mumkin; muammo chiqsa rollback qilib bo'lmaydi (oldingi latest qayerda β bilinmaydi). To'g'ri yondashuv: CI har commit'ga immutable sha-<commit> (yoki reliz uchun semver 1.2.3) tag qo'yadi (metadata-action buni avtomatik qiladi); server aynan o'sha aniq tagni pull qiladi. latest'ni qulaylik uchun yonida saqlash mumkin, lekin deploy unga tayanmasin. Shunda har deploy aniq, takrorlanadigan va rollback oson (oldingi sha-tagga qaytasiz). Avtomatik deploy'ni 15-bobda ko'ramiz.
β¬ οΈ Oldingi: 13 β Test va build pipeline Β· π README Β· Keyingi: 15 β Avtomatik deploy β‘οΈ