Packaging болон Shipping Code

Кодыг хүссэнээрээ ажиллуулах нь хэцүү; харин тэрхүү кодыг өөрийнхөөсөө өөр компьютер дээр ажиллуулах нь ихэнхдээ бүр илүү хэцүү байдаг.

Кодыг хүргэх (shipping code) гэдэг нь таны бичсэн кодыг өөр нэг хэрэглэгч таны компьютерын тохиргоог яг таг хуулбарлах шаардлагагүйгээр ажиллуулах боломжтой, ашиглахад бэлэн хэлбэрт шилжүүлэхийг хэлнэ. Код хүргэх нь олон хэлбэртэй бөгөөд програмчлалын хэл, системийн сангууд, үйлдлийн систем зэрэг олон хүчин зүйлээс хамаардаг. Мөн таны юу бүтээж байгаагаас хамаарна: програм хангамжийн сан (library), командын мөрийн хэрэгсэл (CLI), эсвэл вэб үйлчилгээ (web service) зэрэг нь тус бүртээ өөр өөр шаардлага, байршуулах (deployment) алхмуудтай байдаг. Гэсэн хэдий ч эдгээр бүх тохиолдолд ижил төстэй тал бий: бид бэлэн болох бүтээгдэхүүн буюу артефакт (artifact) гэж юу болох, мөн түүнийг ажиллахад орчин тойрноос нь юу шаардлагатайг тодорхойлох хэрэгтэй болдог.

Энэхүү лекцээр бид дараах сэдвүүдийг авч үзэх болно:

Бид ойлгоход хялбар болгох үүднээс эдгээр ойлголтуудыг Python экосистемийн жишээнүүдээр тайлбарлах болно. Бусад програмчлалын хэлний экосистемд ашиглагддаг хэрэгслүүд өөр өөр байдаг ч үндсэн концепцууд нь ерөнхийдөө ижил байна.

Хамаарлууд ба орчин

Орчин үеийн програм хангамжийн хөгжүүлэлтэд хийсвэрлэлийн түвшнүүд хаа сайгүй байдаг. Програмууд өөрсдийн логикийг бусад сан эсвэл үйлчилгээнүүдэд даатгах нь түгээмэл. Гэхдээ энэ нь таны програм болон түүнийг ажиллахад шаардлагатай сангуудын хооронд хамаарал (dependency) үүсгэдэг. Жишээлбэл, Python дээр вэбсайтын агуулгыг татаж авахын тулд бид ихэвчлэн дараах кодыг бичдэг:

import requests

response = requests.get("https://missing.csail.mit.edu")

Гэвч requests сан нь Python ажиллах орчинтой цуг ирдэггүй тул хэрэв бид requests-ийг суулгахгүйгээр энэ кодыг ажиллуулахыг оролдвол Python алдаа заах болно:

$ python fetch.py
Traceback (most recent call last):
  File "fetch.py", line 1, in <module>
    import requests
ModuleNotFoundError: No module named 'requests'

Энэхүү санг ашиглахын тулд бид эхлээд pip install requests тушаалыг ажиллуулж суулгах шаардлагатай. pip нь Python програмчлалын хэлнээс багцуудыг суулгахад зориулан гаргасан командын мөрийн хэрэгсэл юм. pip install requests тушаалыг ажиллуулахад дараах дарааллаар үйлдлүүд хийгдэнэ:

  1. Python-ийн багцын сангаас (PyPI) requests-ийг хайна.
  2. Бидний ажиллаж буй платформд тохирох артефактыг хайна.
  3. Хамаарлуудыг шийдвэрлэнэ — requests сан нь өөрөө бусад багцуудаас хамаардаг тул суулгагч нь тэдгээр дамжих хамаарлуудын тохирох хувилбаруудыг олж, эхэлж суулгах ёстой.
  4. Артефактуудыг татаж аваад, дараа нь тэдгээрийг задалж, манай файлын системийн зөв байршлууд руу хуулна.
$ pip install requests
Collecting requests
  Downloading requests-2.32.3-py3-none-any.whl (64 kB)
Collecting charset-normalizer<4,>=2
  Downloading charset_normalizer-3.4.0-cp311-cp311-manylinux_x86_64.whl (142 kB)
Collecting idna<4,>=2.5
  Downloading idna-3.10-py3-none-any.whl (70 kB)
Collecting urllib3<3,>=1.21.1
  Downloading urllib3-2.2.3-py3-none-any.whl (126 kB)
Collecting certifi>=2017.4.17
  Downloading certifi-2024.8.30-py3-none-any.whl (167 kB)
Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests
Successfully installed certifi-2024.8.30 charset-normalizer-3.4.0 idna-3.10 requests-2.32.3 urllib3-2.2.3

Үүнээс бид requests сан нь certifi болон charset-normalizer гэх мэт өөрийн гэсэн хамаарлуудтай бөгөөд requests-ийг суулгахаасоо өмнө тэдгээр нь суух шаардлагатайг харж болно. Нэгэнт суусан бол Python ажиллах орчин уг санг импортлох үед олох боломжтой болно.

$ python -c 'import requests; print(requests.__path__)'
['/usr/local/lib/python3.11/dist-packages/requests']

$ pip list | grep requests
requests        2.32.3

Програмчлалын хэлнүүд нь сангуудыг суулгах, нийтлэх тал дээр өөр өөр хэрэгсэл, стандарт болон арга барилуудыг ашигладаг. Rust гэх мэт зарим хэлэнд хэрэгслүүд нь нэгдсэн байдаг — cargo нь угсрах, тест хийх, хамаарлыг удирдах, болон нийтлэх үйлдлийг бүгдийг хариуцдаг. Харин Python зэрэг зарим хэлэнд нэгтгэл нь стандартын түвшинд хийгддэг — ганц хэрэгсэл ашиглахын оронд багцлах үйл явцыг тодорхойлсон стандартууд байх бөгөөд үүний ачаар даалгавар тус бүрд өрсөлдөхүйц олон хэрэгслийг ашиглах боломж бүрддэг (pip-ийн эсрэг uv, setuptools-ийн эсрэг hatch эсвэл poetry). Мөн LaTeX зэрэг зарим экосистемд TeX Live эсвэл MacTeX зэрэг багцууд нь олон мянган бэлэн суулгасан багцтайгаа цуг ирдэг.

Хамаарлуудыг нэмэх нь хамаарын зөрчилдөөнүүдийг дагуулж байдаг. Зөрчилдөөн нь програмууд ижил хамаарын хоорондоо үл нийцэх хувилбаруудыг шаардах үед үүсдэг. Жишээлбэл, хэрэв tensorflow==2.3.0 нь numpy>=1.16.0,<1.19.0-ийг шаардаж, pandas==1.2.0 нь numpy>=1.16.5-ийг шаардвал numpy>=1.16.5,<1.19.0 нөхцөлийг хангах ямар ч хувилбар тохирно. Гэвч хэрэв таны төслийн өөр нэг багц numpy>=1.19-ийг шаардвал бүх хязгаарлалтыг зэрэг хангах боломжит хувилбар олдохгүй зөрчилдөөн үүснэ.

Олон багцууд дундын хамаарлуудынхаа хоорондоо үл нийцэх хувилбаруудыг шаарддаг энэхүү нөхцөл байдлыг ихэвчлэн dependency hell (хамаарын там) гэж нэрлэдэг. Зөрчилдөөнийг шийдэх нэг арга бол програм бүрийн хамаарлыг өөрийнх нь тусдаа орчинд (environment) тусгаарлах явдал юм.

Python дээр бид дараах байдлаар виртуал орчин (virtual environment) үүсгэдэг:

$ which python
/usr/bin/python
$ pwd
/home/missingsemester
$ python -m venv venv
$ source venv/bin/activate
$ which python
/home/missingsemester/venv/bin/python
$ which pip
/home/missingsemester/venv/bin/pip
$ python -c 'import requests; print(requests.__path__)'
['/home/missingsemester/venv/lib/python3.11/site-packages/requests']

$ pip list
Package Version
------- -------
pip     24.0

Орчин гэдгийг тухайн хэлний ажиллах орчны бие даасан хувилбар бөгөөд дотроо өөрийн гэсэн суулгасан багцуудтай гэж ойлгож болно. Энэхүү виртуал орчин буюу venv нь суулгасан хамаарлуудыг системийн үндсэн Python суулгацаас тусгаарладаг. Төсөл бүрд өөрийн гэсэн шаардлагатай хамаарлуудыг агуулсан виртуал орчин үүсгэж хэвших нь зөв дадал юм.

Орчин үеийн олон үйлдлийн системүүд Python зэрэг програмчлалын хэлний ажиллах орцтой цуг ирдэг ч тэдгээрийг өөрчлөх нь эрсдэлтэй. Учир нь үйлдлийн систем өөрийн ажиллах явцад тэдгээр дээр тулгуурладаг байж болзошгүй тул үргэлж тусдаа орчин үүсгэж ашиглахыг илүүд үзээрэй.

Зарим хэлэнд суулгах үйл явц нь тодорхой хэрэгслээр бус, харин стандартаар тодорхойлогддог. Python-д PEP 517 нь угсралтын системийн интерфэйсийг тодорхойлж, PEP 621 нь төслийн мета-өгөгдлийг pyproject.toml файлд хэрхэн хадгалахыг зааж өгдөг. Энэ нь хөгжүүлэгчдэд pip-ийг улам сайжруулж, илүү хурдан ажилладаг uv гэх мэт хэрэгслүүдийг бүтээх боломжийг олгосон. uv-ийг суулгахын тулд pip install uv гэж ажиллуулахад л хангалттай.

pip-ийн оронд uv ашиглах нь ижил интерфэйстэй боловч хамаагүй хурдан байдаг:

$ uv pip install requests
Resolved 5 packages in 12ms
Prepared 5 packages in 0.45ms
Installed 5 packages in 8ms
 + certifi==2024.8.30
 + charset-normalizer==3.4.0
 + idna==3.10
 + requests==2.32.3
 + urllib3==2.2.3

Боломжтой бол pip-ийн оронд uv pip-ийг ашиглахыг зөвлөж байна. Энэ нь суулгах хугацааг маш ихээр хэмнэдэг.

Орчин нь хамаарлыг тусгаарлахаас гадна програмчлалын хэлнийхээ ажиллах орчны өөр өөр хувилбаруудыг зэрэг ашиглах боломжийг олгодог.

$ uv venv --python 3.12 venv312
Using CPython 3.12.7
Creating virtual environment at: venv312

$ source venv312/bin/activate && python --version
Python 3.12.7

$ uv venv --python 3.11 venv311
Using CPython 3.11.10
Creating virtual environment at: venv311

$ source venv311/bin/activate && python --version
Python 3.11.10

Энэ нь кодоо Python-ийн олон хувилбар дээр шалгаж үзэх эсвэл төсөл нь тодорхой нэг хувилбарыг шаардаж байгаа үед маш хэрэгтэй байдаг.

Зарим програмчлалын хэлэнд та виртуал орчныг гараар үүсгэх шаардлагагүйгээр төсөл бүр өөрийн гэсэн хамаарлын орчныг автоматаар үүсгэж авдаг ч үндсэн зарчим нь ижил юм. Орчин үеийн ихэнх хэлнүүд нэг систем дээр хэлнийхээ олон хувилбарыг удирдаж, төсөл тус бүрд аль хувилбарыг ашиглахаа зааж өгөх боломжтой механизмыг агуулдаг болсон.

Артефактууд ба багцлалт

Програм хангамжийн хөгжүүлэлтэд бид эх код (source code) болон артефакт (artifact) хоёрыг зааглаж авч үздэг. Хөгжүүлэгчид эх кодыг бичиж, уншдаг бол артефакт нь тухайн эх кодоос гарган авсан, суулгах болон байршуулахад бэлэн болсон, багцалсан бүтээгдэхүүн юм. Артефакт нь бидний ажиллуулдаг энгийн нэг кодын файл байж болохоос гадна аппликейшны ажиллахад шаардлагатай бүх зүйлийг багтаасан бүхэл бүтэн виртуал машин ч байж болно. Жишээ болгон өөрийн хавтсанд байгаа greet.py гэсэн Python файлыг авч үзье:

$ cat greet.py
def greet(name):
    return f"Hello, {name}!"

$ python -c "from greet import greet; print(greet('World'))"
Hello, World!

$ cd /tmp
$ python -c "from greet import greet; print(greet('World'))"
ModuleNotFoundError: No module named 'greet'

Өөр хавтас руу шилжихэд импорт амжилтгүй болж байгаа нь Python зөвхөн тодорхой байршлуудаас (одоо байгаа хавтас, суулгасан багцууд болон PYTHONPATH доторх замууд) модулиудыг хайдагтай холбоотой юм. Багцлалт (packaging) нь кодыг мэдэгдэж буй зөв байршилд суулгаснаар энэ асуудлыг шийддэг.

Python-ийн хувьд санг багцлах гэдэг нь pip эсвэл uv зэрэг багц суулгагчид ашиглаж болохуйц артефактыг бэлтгэх үйл явц юм. Python-ийн артефактуудыг wheel гэж нэрлэдэг бөгөөд энэ нь багцыг суулгахад шаардлагатай бүх мэдээллийг агуулдаг: кодын файлууд, багцын тухай мета-өгөгдөл (нэр, хувилбар, хамаарлууд), болон файлыг хаана байршуулах тухай зааварчилгаа. Артефактыг угсрахад төслийн дэлгэрэнгүй мэдээлэл, шаардлагатай хамаарлууд, багцын хувилбар болон бусад мэдээллийг агуулсан төслийн файл (манифест гэж нэрлэгддэг) бичих шаардлагатай болдог. Python-д бид үүнд зориулж pyproject.toml файлыг ашигладаг.

pyproject.toml нь орчин үеийн бөгөөд зөвлөдөг арга зам юм. Өмнө нь ашиглагдаж байсан requirements.txt болон setup.py зэрэг аргуудыг дэмжсээр байгаа ч боломжтой бол pyproject.toml-ийг сонгох хэрэгтэй.

Командын мөрийн хэрэгсэл бүхий сангийн хамгийн энгийн pyproject.toml жишээг доор харуулав:

[project]
name = "greeting"
version = "0.1.0"
description = "A simple greeting library"
dependencies = ["typer>=0.9"]

[project.scripts]
greet = "greeting:cli"

[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

typer сан нь хамгийн бага нэмэлт кодтойгоор командын мөрийн интерфейс үүсгэхэд өргөн ашиглагддаг Python-ийн багц юм.

Мөн үүнд тохирох greeting.py файл:

import typer


def greet(name: str) -> str:
    return f"Hello, {name}!"


def cli():
    typer.run(greet)


if __name__ == "__main__":
    cli()

Одоо бид wheel-ийг угсарч болно:

$ uv build
Building source distribution...
Building wheel from source distribution...
Successfully built dist/greeting-0.1.0.tar.gz
Successfully built dist/greeting-0.1.0-py3-none-any.whl

$ ls dist/
greeting-0.1.0-py3-none-any.whl
greeting-0.1.0.tar.gz

Уг .whl файл нь wheel өөрөө (тодорхой бүтэцтэй zip архив) бөгөөд .tar.gz нь эх кодноос нь угсрах шаардлагатай системүүдэд зориулсан эх кодын тархац (source distribution) юм.

Багцад юу орсныг харахын тулд wheel-ийн агуулгыг шалгаж болно:

$ unzip -l dist/greeting-0.1.0-py3-none-any.whl
Archive:  dist/greeting-0.1.0-py3-none-any.whl
  Length      Date    Time    Name
---------  ---------- -----   ----
      150  2024-01-15 10:30   greeting.py
      312  2024-01-15 10:30   greeting-0.1.0.dist-info/METADATA
       92  2024-01-15 10:30   greeting-0.1.0.dist-info/WHEEL
        9  2024-01-15 10:30   greeting-0.1.0.dist-info/top_level.txt
      435  2024-01-15 10:30   greeting-0.1.0.dist-info/RECORD
---------                     -------
      998                     5 files

Хэрэв бид энэхүү wheel-ийг өөр хүнд өгвөл тэд дараах тушаалаар суулгах боломжтой:

$ uv pip install ./greeting-0.1.0-py3-none-any.whl
$ greet Alice
Hello, Alice!

Энэ нь бидний өмнө угсарсан санг, түүний доторх greet командын мөрийн хэрэгслийн хамт тэдний орчинд суулгах болно.

Энэ аргад тодорхой хязгаарлалтууд бий. Жишээ нь, манай сан GPU хурдасгуурт зориулсан CUDA гэх мэт тухайн платформд зориулсан сангуудаас хамаардаг бол манай артефакт зөвхөн тэдгээр тусгай сангууд суусан систем дээр л ажиллах бөгөөд бид өөр өөр үйлдлийн систем болон архитектуруудад зориулж тусдаа wheel-үүдийг угсрах шаардлагатай болно.

Програм хангамж суулгах үед эх кодноос суулгах (installing from source) болон бэлэн хоёртын файлыг суулгах (installing a prebuilt binary) хоёрын хооронд маш том ялгаа байдаг. Эх кодноос суулгах гэдэг нь эх кодыг татаж аваад өөрийн компьютер дээр хөрвүүлэхийг хэлнэ — үүнд хөрвүүлэгч (compiler) болон угсрах хэрэгслүүд шаардлагатай бөгөөд том төслүүдийн хувьд маш их хугацаа шаардаж болно.

Харин бэлэн хоёртын файлыг суулгах нь өөр хэн нэгний өмнө нь хөрвүүлсэн артефактыг татаж авна гэсэн үг юм — илүү хурдан бөгөөд хялбар боловч уг файл нь таны үйлдлийн систем болон архитектурт яг таарч байх ёстой. Жишээлбэл, ripgrep-ийн релиз хуудас дээр Linux, macOS, болон Windows-д зориулсан бэлэн хоёртын файлуудыг (prebuilt binaries) харуулсан байдаг.

Релизүүд ба хувилбаржуулалт

Код бичих үйл явц нь тасралтгүй үргэлжилдэг боловч тодорхой хугацааны давтамжтайгаар хувилбар болон гардаг. Програм хангамжийн хөгжүүлэлтэд хөгжүүлэлтийн орчин (dev environment) болон бодит орчны (production environment) хооронд маш тодорхой зааг бий. Кодыг бодит орчин руу хүргэхээс өмнө хөгжүүлэлтийн орчинд зөв ажиллаж байгааг нь нотлох шаардлагатай. Релиз хийх үйл явц нь тест хийх, хамаарлыг удирдах, хувилбаржуулах, тохиргоо хийх, байршуулах, болон нийтлэх зэрэг олон алхмуудыг багтаадаг.

Програм хангамжийн сангууд нь тогтмол биш, цаг хугацааны явцад алдаа засагдаж, шинэ боломжууд нэмэгдэн хувьсаж байдаг. Бид энэхүү хувьслыг тухайн сангийн тодорхой цаг үеийн төлөвт харгалзах хувилбарын дугаараар тэмдэглэж хянадаг. Сангийн өөрчлөлт нь жижиг алдаа зассан засварууд (patches), түүний боломжуудыг нэмэгдүүлсэн шинэ функцууд (features), эсвэл өмнөх хувилбартай нийцэхгүй болгох томоохон өөрчлөлтүүд (breaking changes) ч байж болно. Өөрчлөлтийн бүртгэл (changelog) нь тухайн хувилбарт орсон өөрчлөлтүүдийг баримтжуулдаг — эдгээр нь хөгжүүлэгчид шинэ релизээр ямар өөрчлөлт орж байгааг мэдээлэхэд ашигладаг баримт бичиг юм.

Гэвч хамаарал бүрийн өөрчлөлтийг нэг бүрчлэн хянах нь боломжгүй бөгөөд тэдгээрийн дамжих хамаарлуудыг (transitive dependencies) тооцвол бүр ч хэцүү болно.

Та өөрийн төслийн хамаарлуудын модыг uv tree тушаалаар харах боломжтой бөгөөд энэ нь бүх багцууд болон тэдгээрийн дамжих хамаарлуудыг мод хэлбэрээр харуулдаг.

Энэ асуудлыг хялбарчлахын тулд програм хангамжийг хувилбаржуулах дүрэм журмууд байдаг бөгөөд тэдгээрээс хамгийн өргөн хэрэглэгддэг нь Семантик хувилбаржуулалт буюу SemVer юм. SemVer-ийн дагуу хувилбарын дугаар нь MAJOR.MINOR.PATCH (ГОЛ.ТУСЛАХ.ЗАСВАР) гэсэн хэлбэртэй байна. Товчхондоо:

Энэ бол маш хялбаршуулсан тайлбар бөгөөд жишээ нь яагаад 0.1.3-аас 0.2.0 руу шилжихэд нийцэл эвдэрч болох эсвэл 1.0.0-rc.1 гэж юу гэсэн үг болохыг илүү сайн ойлгохын тулд SemVer-ийн бүрэн баримт бичгийг уншихыг зөвлөж байна.

Python-ийн багцлалт нь семантик хувилбаржуулалтыг шууд дэмждэг тул бид хамаарлын хувилбаруудаа зааж өгөхдөө янз бүрийн нөхцөлүүдийг ашиглаж болно. pyproject.toml файл дотор бид хамаарлынхаа нийцтэй хувилбаруудын хүрээг дараах хэлбэрүүдээр хязгаарлах боломжтой:

[project]
dependencies = [
    "requests==2.32.3",  # Яг таг хувилбар - зөвхөн энэ хувилбар сууна
    "click>=8.0",        # Доод хязгаар - 8.0 эсвэл түүнээс шинэ
    "numpy>=1.24,<2.0",  # Муж - 1.24-өөс их боловч 2.0-оос бага
    "pandas~=2.1.0",     # Нийцтэй хувилбар - >=2.1.0 болон <2.2.0
]

Хувилбар заагчууд нь олон багц менежерүүдэд (npm, cargo г.м.) өөр өөр нарийн утгатайгаар ашиглагддаг. Python-ийн хувьзаар ~= оператор нь “нийцтэй хувилбар” гэдгийг заадаг — ~=2.1.0 гэдэг нь “2.1.0-той нийцтэй ямар ч хувилбар” буюу >=2.1.0 ба <2.2.0 гэсэн үг юм. Энэ нь npm болон cargo дээрх SemVer-ийн нийцтэй байдлыг заадаг карет (^) оператортой бараг ижил утгатай.

Бүх програм хангамж семантик хувилбаржуулалтыг ашигладаггүй. Өргөн хэрэглэгддэг өөр нэг сонголт бол Календарь хувилбаржуулалт (CalVer) бөгөөд хувилбарын дугаар нь семантик утгаас илүүтэйгээр гаргасан огноон дээр тулгуурладаг. Жишээлбэл, Ubuntu нь 24.04 (2024 оны 4-р сар) болон 24.10 (2024 оны 10-р сар) гэсэн хувилбаруудыг ашигладаг. CalVer нь хувилбар хэр хуучин болохыг харахад хялбар болгодог ч нийцтэй байдлын талаар мэдээлэл өгдөггүй. Эцэст нь хэлэхэд, семантик хувилбаржуулалт нь алдаагүй тул хааяа багц хөгжүүлэгчид андуураад жижиг эсвэл засварын релиз дээрээ нийцэл эвдэх өөрчлөлт оруулах тохиолдол гардаг.

Дахин давтагдах байдал

Орчин үеийн програм хангамжийн хөгжүүлэлтэд таны бичсэн код маш олон хийсвэрлэлийн түвшний дээр суурилж ажилладаг. Үүнд програмчлалын хэлний ажиллах орчин, гуравдагч талын сангууд, үйлдлийн систем, эсвэл бүр техник хангамж өөрөө ч багтана. Эдгээр түвшнүүдийн аль нэгэнд гарсан өчүүхэн ялгаа нь таны кодын ажиллагааг өөрчлөх эсвэл ажиллахгүй болоход хүргэж болно. Түүнчлэн техник хангамжийн ялгаа хүртэл код хүргэхэд нөлөөлдөг.

Санг “хадах” (pinning) гэдэг нь хувилбарын муж заахын оронд яг таг хувилбарыг зааж өгөхийг хэлнэ (жишээ нь, requests>=2.0-ийн оронд requests==2.32.3).

Багц менежерийн ажлын нэг хэсэг нь хамаарал болон дамжих хамаарлуудаас заасан бүх хязгаарлалтыг тооцоолж, тэдгээрийг бүрэн хангах хувилбаруудын жагсаалтыг гаргах явдал юм. Уг хувилбаруудын жагсаалтыг дараа нь дахин яг ижилхэн суулгах боломжийг бүрдүүлэх үүднээс файлд хадгалдаг бөгөөд үүнийг лок файл (lock file) гэж нэрлэдэг.

$ uv lock
Resolved 12 packages in 45ms

$ cat uv.lock | head -20
version = 1
requires-python = ">=3.11"

[[package]]
name = "certifi"
version = "2024.8.30"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/...", hash = "sha256:..." }
wheels = [
    { url = "https://files.pythonhosted.org/...", hash = "sha256:..." },
]
...

Хамаарлын хувилбаржуулалт болон дахин давтагдах байдлыг (reproducibility) хангах үед анхаарах нэг чухал зүйл бол сан болон аппликейшн/үйлчилгээнүүдийн ялгаа юм. Сан (library) нь өөр бусад кодонд импортлогдож ашиглагдах зориулалттай бөгөөд тэдгээр нь өөрийн гэсэн хамаарлуудтай байж болдог тул санд хувилбарын хязгаарлалтыг хэт хатуу зааж өгөх нь хэрэглэгчийн бусад хамааралтай зөрчилдөх шалтгаан болдог. Харин аппликейшн болон үйлчилгээнүүд нь програм хангамжийн эцсийн хэрэглэгчид бөгөөд өөрсдийн ажиллагааг програмчлалын интерфэйсээр бус, хэрэглэгчийн интерфэйс (UI) эсвэл API-аар дамжуулан ил гаргадаг. Тиймээс сангуудад хувилбарын мужийг зааж өгөх нь экосистемд нийцтэй ажиллах боломжийг нэмэгдүүлдэг бол аппликейшнуудын хувьд хувилбарыг яг таг хадаж өгөх нь дахин давтагдах байдлыг баталгаажуулдаг — өөрөөр хэлбэл аппликейшныг ажиллуулж буй хүн бүр яг ижил хамаарлуудыг ашиглана.

Хамгийн дээд зэргийн дахин давтагдах байдлыг шаарддаг төслүүдийн хувьд Nix болон Bazel зэрэг хэрэгслүүд нь hermetic builds (битүү угсралт) олгодог — үүнд хөрвүүлэгч, системийн сангууд, тэр байтугай угсралтын орчин хүртэл хадагдсан байдаг. Энэ нь хэзээ, хаана угсарч байгаагаас үл хамааран байт бүрээрээ яг ижил үр дүнг гаргахыг баталгаажуулдаг.

Та NixOS-ийг ашиглан компьютерынхоо бүх тохиргоог удирдаж, хувилбарын хяналтанд байгаа тохиргооны файлуудаар дамжуулан компьютерынхоо шинэ хуулбарыг хялбархан үүсгэн ажиллуулах боломжтой.

Програм хангамжийн хөгжүүлэлтийн мөнхийн зөрчил бол шинэ хувилбарууд нь санаатай болон санамсаргүй байдлаар кодын ажиллагааг эвддэг бол хуучин хувилбарууд нь цаг хугацааны явцад аюулгүй байдлын цоорхойтой болдог явдал юм. Үүнийг бид тасралтгүй интеграцийн сувгуудыг ашиглан манай аппликейшныг шинэ хувилбарууд дээр автоматаар шалгах (энэ талаар Кодын чанар ба CI лекцээс дэлгэрэнгүй үзнэ үү) болон Dependabot зэрэг хэрэгслүүдээр хамаарлуудын шинэ хувилбар гарах үед автоматаар илрүүлэх замаар шийдэж болно.

CI тестүүд хийгддэг байсан ч хувилбар ахиулах үед хөгжүүлэлтийн болон бодит орчны ялгаанаас болоод асуудал үүссээр байдаг. Ийм тохиолдолд хамгийн шилдэг шийдэл бол хувилбарын шинэчлэлийг буцааж, өмнөх тогтвортой хувилбарыг дахин байршуулах rollback plan (буцаах төлөвлөгөөтэй) байх явдал юм.

Виртуал машин ба контейнер

Хэрэв та илүү нарийн төвөгтэй хамаарлуудыг ашиглаж эхэлбэл кодын тань хамаарал багц менежерийн зохицуулж чадах хэмжээнээс давах магадлалтай. Үүний нэг нийтлэг шалтгаан нь системийн тусгай сангууд эсвэл техник хангамжийн драйверуудтай ажиллах шаардлага гардагтай холбоотой. Жишээлбэл, шинжлэх ухааны тооцоолол болон хиймэл оюун ухааны салбарт програмууд GPU техник хангамжийг ашиглахын тулд тусгай сан болон драйверуудыг шаарддаг. Системийн түвшний олон хамаарлууд (GPU драйверууд, хөрвүүлэгчийн тодорхой хувилбарууд, OpenSSL зэрэг дундын сангууд) нь систем даяар суулгалт хийхийг шаарддаг хэвээр байна.

Уламжлал ёсоор энэхүү өргөн хүрээний хамаарлын асуудлыг Виртуал машинуудаар (VM) шийддэг байсан. Виртуал машин нь компьютерийг бүхэлд нь хийсвэрлэж, өөрийн гэсэн үйлдлийн систем бүрэн тусгаарлагдсан орчныг бүрдүүлдэг. Харин орчин үеийн арга бол аппликейшныг өөрийн хамаарал, сангууд, болон файлын системтэй хамт багцалж, компьютерийг бүхэлд нь виртуалчлахын оронд үндсэн (хост) үйлдлийн системийн цөмийг хамтран ашигладаг контейнер (container) юм. Контейнерууд нь үйлдлийн системийн цөмийг хуваалцдаг тул виртуал машинаас хамаагүй хөнгөн бөгөөд ажиллаж эхлэх хугацаа нь хурдан, нөөцийн ашиглалт нь үр ашигтай байдаг.

Хамгийн алдартай контейнер платформ бол Docker юм. Docker нь контейнерыг угсрах, түгээх, болон ажиллуулах нэгдсэн стандартыг бий болгосон. Хөшигний цаана Docker нь контейнерыг ажиллуулах систем болгон Kubernetes зэрэг бусад хэрэгслүүдийн ашигладаг салбарын стандарт болох containerd-ийг ашигладаг.

Контейнерыг ажиллуулах нь маш хялбархан. Жишээлбэл, контейнер дотор Python хөрвүүлэгчийг ажиллуулахын тулд бид docker run тушаалыг ашигладаг (Энд -it туг нь контейнерыг терминалтай интерактив холболтоор ажиллуулна. Терминалаас гарахад контейнер зогсоно).

$ docker run -it python:3.12 python
Python 3.12.7 (main, Nov  5 2026, 02:53:25) [GCC 12.2.0] on linux
>>> print("Hello from inside a container!")
Hello from inside a container!

Амьдрал дээр таны програм систем даяарх файлын бүтцээс хамаарч болно. Үүнийг шийдэхийн тулд бид аппликейшны ажиллах нийт файлын системийг артефакт болгон тээвэрлэдэг контейнерын зургийг (container image) ашигладаг. Эдгээр зургийг код бичих замаар үүсгэдэг. Docker ашиглахдаа бид Dockerfile синтаксийг ашиглан зураг дотор шаардлагатай хамаарлууд, системийн сангууд, болон тохиргоог зааж өгнө:

FROM python:3.12
RUN apt-get update
RUN apt-get install -y gcc
RUN apt-get install -y libpq-dev
RUN pip install numpy
RUN pip install pandas
COPY . /app
WORKDIR /app
RUN pip install .

Чухал ялгаа: Docker-ийн зураг (image) нь багцалсан артефакт (загвар шиг) бөгөөд харин контейнер (container) нь уг зургийн ажиллаж буй бодит хуулбар юм. Та нэг зургаас олон контейнерыг зэрэг ажиллуулах боломжтой. Зургууд нь давхаргуудаар (layers) үүсдэг бөгөөд Dockerfile доторх зааварчилгаа бүр (FROM, RUN, COPY г.м.) шинэ давхарга үүсгэдэг. Docker эдгээр давхаргуудыг кэшлэдэг тул хэрэв та Dockerfile-ийн нэг мөрийг өөрчилбөл зөвхөн тухайн мөр болон түүний дараагийн давхаргууд л дахин угсрагддаг.

Дээрх Dockerfile нь хэд хэдэн сул талтай: энэ нь slim (хөнгөн) хувилбарын оронд бүтэн Python зургийг ашигласан, тусдаа RUN тушаалуудыг ажиллуулж шаардлагагүй давхаргууд үүсгэсэн, хувилбаруудыг хадаагүй, мөн багц менежерийн кэшийг цэвэрлээгүйн улмаас хэрэгцээгүй файлуудыг зурагт багтаасан байна. Бусад нийтлэг алдаануудаас дурдвал контейнерыг root хэрэглэгчээр аюултай ажиллуулах, мөн нууц мэдээллийг санамсаргүйгээр зургийн давхаргад хадгалах зэрэг орно.

Илүү сайжруулсан хувилбар:

FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
RUN apt-get update && \
    apt-get install -y --no-install-recommends gcc libpq-dev && \
    rm -rf /var/lib/apt/lists/*
COPY pyproject.toml uv.lock ./
RUN uv pip install --system -r uv.lock
COPY . /app

Дээрх жишээн дээр бид uv-ийг эх кодноос нь суулгахын оронд ghcr.io/astral-sh/uv:latest зургаас бэлэн хоёртын файлыг хуулж авч байгааг харж болно. Үүнийг builder pattern гэж нэрлэдэг. Энэхүү загварын тусламжтайгаар бид кодоо хөрвүүлэхэд шаардлагатай бүх хэрэгслийг зурагт багтаах шаардлагагүй бөгөөд зөвхөн аппликейшныг ажиллуулахад хэрэгтэй эцсийн хоёртын файлыг л агуулна.

Docker-д анхаарах шаардлагатай зарим хязгаарлалтууд бий. Нэгдүгээрт, контейнерын зургууд нь ихэвчэн платформд зориулагдсан байдаг — linux/amd64-д зориулж угсарсан зураг нь linux/arm64 (Apple Silicon Mac) дээр эмуляцгүйгээр ажиллуулахад маш удаан байдаг. Хоёрдугаарт, Docker контейнерт Linux цөм шаардлагатай тул macOS болон Windows дээр Docker нь цаанаа хөнгөн Linux виртуал машиныг ажиллуулдаг бөгөөд энэ нь нэмэлт ачаалал үүсгэдэг. Гуравдугаарт, Docker-ийн тусгаарлалт нь виртуал машинаас сул байдаг — контейнерууд хост үйлдлийн систем цөмийг хуваалцдаг тул олон түрээслэгчтэй (multi-tenant) орчинд аюулгүй байдлын эрсдэл үүсгэж болно.

Өнөө үед олон төслүүд бүр “систем даяарх” сангууд болон аппликейшнуудыг төсөл бүрээр удирдахын тулд nix flakes-ээр дамжуулан nix-ийг ашиглаж байна.

Тохиргоо

Програм хангамж нь уг чанараараа тохируулах боломжтой байдаг. Командын мөрийн орчин лекцээр бид програмууд нь тугууд (flags), орчны хувьсагчид (environment variables) эсвэл тохиргооны файлуудоор (dotfiles) дамжуулан тохиргоо авдгийг харсан. Энэ нь нарийн төвөгтэй аппликейшнуудад ч хамаатай бөгөөд тохиргоог том хэмжээнд удирдахад ашигладаг тогтсон загварууд байдаг. Програм хангамжийн тохиргоог кодон дотор хатуу бичиж болохгүй бөгөөд харин ажиллах үед нь (runtime) зааж өгөх ёстой. Хамгийн нийтлэг хоёр арга бол орчны хувьсагчид болон тохиргооны файлууд юм.

Орчны хувьсагчдаар тохируулагддаг аппликейшны жишээг доор харуулав:

import os

DATABASE_URL = os.environ.get("DATABASE_URL", "sqlite:///local.db")
DEBUG = os.environ.get("DEBUG", "false").lower() == "true"
API_KEY = os.environ["API_KEY"]  # Шаардлагатай - байхгүй бол алдаа заана

Мөн аппликейшныг тохиргооны файлаар (жишээ нь, yaml.load-оор тохиргоогоо уншдаг Python програм) тохируулж болно, config.yaml:

database:
  url: "postgresql://localhost/myapp"
  pool_size: 5
server:
  host: "0.0.0.0"
  port: 8080
  debug: false

Тохиргооны талаарх хамгийн чухал алтан дүрэм бол: ижил кодын сан нь ямар ч кодын өөрчлөлгүйгээр, зөвхөн тохиргооны өөрчлөлтөөр өөр өөр орнуудад (хөгжүүлэлтийн, туршилтын, бодит) байрших боломжтой байх ёстой.

Тохиргооны сонголтуудын дунд ихэвчлэн API түлхүүрүүд гэх мэт эмзэг мэдээллүүд байдаг. Эдгээр нууц мэдээллийг (secrets) санамсаргүйгээр задлахаас сэргийлж маш болгоомжтой хандах ёстой бөгөөд тэдгээрийг хэзээ ч хувилбарын хяналтын системд оруулж болохгүй.

Үйлчилгээнүүд ба зохицуулалт

Орчин үеийн аппликейшнууд дангаараа орших нь ховор. Жишээ вэб аппликейшнд өгөгдөл хадгалах өгөгдлийн сан (database), гүйцэтгэлийг сайжруулах кэш (cache), ар талын даалгавруудыг ажиллуулах дараалал (message queue) болон бусад туслах үйлчилгээнүүд шаардлагатай болдог. Бүх зүйлийг нэг цул монолит аппликейшнд багтаахын оронд орчин үеийн архитектурууд нь функцуудыг бие даан хөгжүүлж, байршуулж, өргөтгөх боломжтой тусдаа үйлчилгээнүүдэд задлахыг илүүд үздэг.

Жишээлбэл, хэрэв бид аппликейшндээ кэш ашиглах нь зүйтэй гэж үзвэл өөрсдөө шинээр бүтээхийн оронд Redis эсвэл Memcached зэрэг туршигдсан бэлэн шийдлүүдийг ашиглаж болно. Бид Redis-ийг контейнерынхоо нэг хэсэг болгон суулгаж болох ч энэ нь манай аппликейшн болон Redis-ийн хоорондох бүх хамаарлуудыг нэг стандартад оруулах шаардлагатай болох бөгөөд энэ нь хэцүү эсвэл бүр боломжгүй байж болно. Үүний оронд бид бүрэлдэхүүн хэсэг бүрийг өөрийн контейнер дотор тусад нь байршуулж болно. Үүнийг ихэвчлэн микросервис архитектур гэж нэрлэдэг бөгөөд бүрэлдэхүүн хэсэг бүр нь бие даасан үйлчилгээ хэлбэрээр ажиллаж, сүлжээгээр (ихэвчлэн HTTP API-аар) хоорондоо харилцдаг.

Docker Compose нь олон контейнер бүхий аппликейшнуудыг тодорхойлж, ажиллуулахад зориулагдсан хэрэгсэл юм. Контейнеруудыг нэг бүрчлэн удирдахын оронд та нэг YAML файл дотор бүх үйлчилгээнүүдээ зарлаж, тэдгээрийг хамтад нь зохицуулна. Одоо манай аппликейшн нэгээс олон контейнерыг багтаах болно:

# docker-compose.yml
services:
  web:
    build: .
    ports:
      - "8080:8080"
    environment:
      - REDIS_URL=redis://cache:6379
    depends_on:
      - cache

  cache:
    image: redis:7-alpine
    volumes:
      - redis_data:/data

volumes:
  redis_data:

docker compose up тушаалыг ажиллуулахад хоёр үйлчилгээ хамт эхлэх бөгөөд вэб аппликейшн нь cache гэсэн хост нэрээр (Docker-ийн дотоод DNS нь үйлчилгээний нэрийг автоматаар шийдвэрлэдэг) Redis рүү холбогдох боломжтой болно. Docker Compose нь нэг буюу хэд хэдэн үйлчилгээг хэрхэн байршуулахаа тодорхойлох боломжийг олгож, тэдгээрийг хамтад нь ажиллуулах, сүлжээний холболтыг тохируулах, болон өгөгдлийг хадгалах дундын эзлэхүүнүүдийг (volumes) удирдах зэрэг зохицуулалтыг хариуцдаг.

Бодит орчинд (production) байршуулах үед та docker compose үйлчилгээнүүдээ систем ачаалах үед автоматаар эхэлж, алдаа гарвал өөрөө дахин ажиллахыг хүсэх байх. Нийтлэг хэрэглэгддэг нэг арга бол docker compose-ийг удирдахад systemd ашиглах явдал юм:

# /etc/systemd/system/myapp.service
[Unit]
Description=My Application
Requires=docker.service
After=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down

[Install]
WantedBy=multi-user.target

Энэхүү systemd тохиргооны файл нь систем асах үед (Docker бэлэн болсны дараа) таны аппликейшныг автоматаар эхлүүлж, systemctl start myapp, systemctl stop myapp, болон systemctl status myapp гэх мэт стандарт тушаалуудаар удирдах боломжийг олгодог.

Байршуулалтын шаардлага илүү нарийн төвөгтэй болох тусам — олон машин дамжуулан өргөтгөх, үйлчилгээ унах үед автоматаар сэргээх, болон өндөр хүртээмжтэй байдлыг хангах шаардлагатай болоход байгууллагууд олон мянган контейнерыг зэрэг удирдах чадвартай Kubernetes (k8s) зэрэг нарийн төвөгтэй контейнер зохицуулалтын платформуудыг сонгодог. Гэсэн хэдий ч Kubernetes-ийг сурахад хүндрэлтэй бөгөөд ашиглалтын асар их зардал дагуулдаг тул жижиг төслүүдийн хувьд хэтэрхий нүсэрддэг.

Эдгээр олон контейнерт орчин ажиллах боломжтой байгаа нь орчин үеийн үйлчилгээнүүд хоорондоо стандартчилсан API буюу HTTP REST API-аар харилцдагтай холбоотой юм. Жишээлбэл, програм OpenAI эсвэл Anthropic гэх мэт LLM үйлчилгээ үзүүлэгчтэй холбогдох бүртээ цаанаа тэдний сервер рүү HTTP хүсэлт илгээж, хариуг нь задлан уншиж байдаг:

$ curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "content-type: application/json" \
    -H "anthropic-version: 2023-06-01" \
    -d '{"model": "claude-sonnet-4-20250514", "max_tokens": 256,
         "messages": [{"role": "user", "content": "Explain containers vs VMs in one sentence."}]}'

Нийтлэх (Publishing)

Та кодоо зөв ажиллаж байгааг нотолсныхоо дараа түүнийг бусад хүмүүс татаж аваад суулгах боломжтойгоор түгээхийг хүсэж болох юм. Түгээлт нь олон хэлбэртэй бөгөөд таны ашиглаж буй програмчлалын хэл болон орчноос шууд хамаарна.

Түгээлтийн хамгийн энгийн хэлбэр бол хүмүүст татаж аваад өөрсдөө суулгахад зориулж артефактыг вэбэд байршуулах юм. Энэ нь одоо ч хэрэглэгдсээр байгаа бөгөөд жишээ нь Ubuntu-ийн багцын архив-аас үндсэндээ .deb файлуудын жагсаалтыг вэбээр харуулдаг.

Өнөө үед GitHub нь эх код болон артефактуудыг нийтлэх гол платформ болсон. Эх код нь ихэвчлэн нээлттэй байдаг бөгөөд GitHub Releases нь хөгжүүлэгчдэд тодорхой хувилбаруудад бэлэн хоёртын файлууд болон бусад артефактуудыг хавсаргаж нийтлэх боломжийг олгодог.

Багц менежерүүд нь заримдаа эх кодноос эсвэл бэлэн wheel-ээс шууд GitHub-аас суулгахыг дэмждэг:

# Эх кодноос суулгах (clone хийж угсарна)
$ pip install git+https://github.com/psf/requests.git

# Тусгай таг/салаанаас суулгах
$ pip install git+https://github.com/psf/requests.git@v2.32.3

# GitHub релизээс wheel файлыг шууд суулгах
$ pip install https://github.com/user/repo/releases/download/v1.0/package-1.0-py3-none-any.whl

Үнэндээ Go зэрэг зарим хэлнүүд төвлөрөөгүй түгээлтийн загварыг ашигладаг — өөрөөр хэлбэл төвлөрсөн багцын сангийн оронд Go модулиуд нь шууд өөрсдийн эх кодын репозиториос түгээгддэг. github.com/gorilla/mux гэх мэт модулийн замууд нь код хаана байгааг заах бөгөөд go get нь тэндээс шууд татаж авдаг. Гэвч pip, cargo, эсвэл brew зэрэг ихэнх багц менежерүүд түгээлт болон суулгалтыг хялбар болгох үүднээс бэлэн багцалсан төслүүдийн төвлөрсөн индекстэй байдаг. Хэрэв бид дараах тушаалыг ажиллуулбал:

$ uv pip install requests --verbose --no-cache 2>&1 | grep -F '.whl'
DEBUG Selecting: requests==2.32.5 [compatible] (requests-2.32.5-py3-none-any.whl)
DEBUG No cache entry for: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl.metadata
DEBUG No cache entry for: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl

бид requests wheel-ийг хаанаас татаж байгааг харж болно. Файлын нэрэн дэх py3-none-any гэдгийг анхаараарай — энэ нь уг wheel нь ямар ч үйлдлийн систем, ямар ч архитектур дээр ямар ч Python 3 хувилбартай ажиллана гэсэн үг юм. Харин хөрвүүлсэн код агуулсан багцуудын хувьд wheel нь тухайн платформд зориулагдсан байдаг:

$ uv pip install numpy --verbose --no-cache 2>&1 | grep -F '.whl'
DEBUG Selecting: numpy==2.2.1 [compatible] (numpy-2.2.1-cp312-cp312-macosx_14_0_arm64.whl)

Энд байгаа cp312-cp312-macosx_14_0_arm64 нь уг wheel нь зөвхөн macOS 14+ үйлдлийн системтэй, ARM64 архитектур бүхий компьютер дээрх CPython 3.12 хувилбарт зориулагдсаныг заана. Хэрэв та өөр платформ ашиглаж байвал pip өөр өөр wheel татаж авах эсвэл эх кодноос нь угсрах болно.

Эсрэгээрээ, бидний бүтээсэн багцыг хүмүүс олж суулгах боломжтой болгохын тулд бид түүнийг эдгээр регистрүүдийн аль нэг рүү нийтлэх шаардлагатай. Python-ийн хувьд үндсэн бүртгэл нь Python Package Index (PyPI) юм. Суулгахтай адилхан багцыг нийтлэх хэд хэдэн арга бий. uv publish тушаал нь багцуудыг PyPI рүү хуулах орчин үеийн интерфейсийг олгодог:

$ uv publish --publish-url https://test.pypi.org/legacy/
Publishing greeting-0.1.0.tar.gz
Publishing greeting-0.1.0-py3-none-any.whl

Энд бид бодит PyPI-ийг бохирдуулахгүйгээр нийтлэх үйл явцаа турших зориулалттай тусдаа бүртгэл болох TestPyPI-ийг ашиглаж байна. Нэгэнт хуулагдсан бол TestPyPI-аас дараах байдлаар суулгаж болно:

$ uv pip install --index-url https://test.pypi.org/simple/ greeting

Програм хангамж нийтлэх үед анхаарах нэг чухал зүйл бол итгэлцэл юм. Хэрэглэгчид өөрсдийн татаж авсан багцыг үнэхээр танаас ирсэн бөгөөд замдаа өөрчлөгдөөгүй гэдгийг хэрхэн баталгаажуулах вэ? Багцын бүртгэлүүд нь бүрэн бүтэн байдлыг шалгахын тулд хяналтын нийлбэрийг (checksums) ашигладаг бөгөөд зарим экосистем нь зохиогчийг криптографийн аргаар батлахын тулд багц гарын үсэг зурахыг (package signing) дэмждэг.

Хэл бүр өөрийн гэсэн багцын бүртгэлтэй: Rust-ийн хувьд crates.io, JavaScript-ийн хувьд npm, Ruby-ийн хувьд RubyGems, контейнерын зургийн хувьд Docker Hub гэх мэт. Үүний зэрэгцээ, хувийн эсвэл дотоод багцуудын хувьд байгууллагууд өөрсдийн багцын санг (жишээ нь, хувийн PyPI сервер эсвэл хувийн Docker бүртгэл) байршуулж ашиглах эсвэл үүлэн үйлчилгээ үзүүлэгчдийн бэлэн шийдлийг ашигладаг.

Вэб үйлчилгээг интернетэд байршуулахад нэмэлт дэд бүтэц шаардлагатай болно: домэйн нэр бүртгүүлэх, домэйныг сервер рүүгээ чиглүүлэх DNS тохиргоо, болон HTTPS холболтыг зохицуулж, урсгалыг чиглүүлэх nginx зэрэг урвуу прокси (reverse proxy). Баримт бичиг эсвэл статик вэбсайт гэх мэт хялбар тохиолдлуудад GitHub Pages нь репозиториос шууд үнэгүй хостинг олгодог.

Дасгалууд

  1. Өөрийн орчны хувьсагчдыг printenv тушаалаар файлд хадгалаад, venv үүсгэн идэвхжүүлж, дахин орчны хувьсагчдыг өөр файлд хадгалаад diff before.txt after.txt тушаалаар шалгаарай. Орчинд юу өөрчлөгдсөн бэ? Бүрхүүл яагаад venv-ийг илүүд үзэж байна вэ? (Санамж: идэвхжүүлэхийн өмнөх ба дараах $PATH хувьсагчийг хараарай.) which deactivate тушаалыг ажиллуулж, deactivate bash функц юу хийж байгааг тунгаагаарай.
  2. pyproject.toml файл бүхий Python багц үүсгээд, түүнийг виртуал орчинд суулгаарай. Лок файл үүсгэж, түүнийгээ шалгана уу.
  3. Docker-ийг суулгаад, Missing Semester-ийн хичээлийн вэбсайтыг docker compose ашиглан локал орчиндоо угсарч ажиллуулаарай.
  4. Энгийн Python аппликейшнд зориулсан Dockerfile бичээрэй. Дараа нь өөрийн аппликейшныг Redis кэштэй хамт ажиллуулдаг docker-compose.yml файлыг бэлдээрэй.
  5. TestPyPI рүү өөрийн Python багцыг нийтлээрэй (хэрэв бусадтай хуваалцах үнэ цэнэтэй биш бол бодит PyPI рүү бүү нийтлээрэй!). Дараа нь уг багцыг агуулсан Docker зураг үүсгэж, ghcr.io руу хуулаарай.
  6. GitHub Pages ашиглан өөрийн вэбсайтыг үүсгээрэй. Нэмэлтээр: түүндээ өөрийн тусгай домэйныг тохируулаарай.

Энэ хуудсыг засварлах.

Лиценз: CC BY-NC-SA.