Files
Avtex/session-2026-04-23.canvas
T

159 lines
7.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"nodes": [
{
"id": "title",
"type": "text",
"x": -200,
"y": -600,
"width": 400,
"height": 80,
"text": "# Сессия 23 апреля 2026\n## CI/CD через Gitea Actions"
},
{
"id": "problem1",
"type": "text",
"x": -800,
"y": -400,
"width": 340,
"height": 200,
"text": "## ❌ Проблема 1\n**node not in PATH**\nRunner выполнял job на хосте без node\n\n**Fix:** настроить runner label\n`ubuntu-latest:docker://node:20-alpine`"
},
{
"id": "problem2",
"type": "text",
"x": -400,
"y": -400,
"width": 340,
"height": 200,
"text": "## ❌ Проблема 2\n**getaddrinfo ENOTFOUND gitea**\nJob контейнер не в сети infra\n\n**Fix:** `container.network: infra` в config.yaml runner'а"
},
{
"id": "problem3",
"type": "text",
"x": 0,
"y": -400,
"width": 340,
"height": 200,
"text": "## ❌ Проблема 3\n**actions/checkout тянет с GitHub**\nНе знает про Gitea\n\n**Fix:** заменить на ручной `git clone` с токеном через env переменную"
},
{
"id": "problem4",
"type": "text",
"x": 400,
"y": -400,
"width": 340,
"height": 200,
"text": "## ❌ Проблема 4\n**bash not found**\nnode:20-alpine не имеет bash\n\n**Fix:** `shell: sh` + `defaults.run.shell: sh`"
},
{
"id": "problem5",
"type": "text",
"x": -800,
"y": -140,
"width": 340,
"height": 200,
"text": "## ❌ Проблема 5\n**Token URL parse error**\nCRLF переносы в токене ломают URL\n\n**Fix:**\n```sh\nCLEAN_TOKEN=$(printf '%s' \"${TOKEN}\" | tr -d '\\r\\n')\n```"
},
{
"id": "problem6",
"type": "text",
"x": -400,
"y": -140,
"width": 340,
"height": 200,
"text": "## ❌ Проблема 6\n**rsync not found**\nНет rsync в Alpine\n\n**Fix:** `apk add --no-cache openssh-client rsync`\n\nПотом поняли что SSH не нужен вообще — runner на том же сервере"
},
{
"id": "problem7",
"type": "text",
"x": 0,
"y": -140,
"width": 340,
"height": 200,
"text": "## ❌ Проблема 7\n**docker compose -f not found**\ndocker-cli в Alpine не поддерживает compose\n\n**Fix:** `apk add docker-cli-compose`\nИспользовать `docker-compose` (v1)"
},
{
"id": "problem8",
"type": "text",
"x": 400,
"y": -140,
"width": 340,
"height": 200,
"text": "## ❌ Проблема 8\n**Duplicate mount: /var/run/docker.sock**\nУказали sock и в options и в volumes\n\n**Fix:** убрать sock из options, оставить только xflow/npb"
},
{
"id": "problem9",
"type": "text",
"x": -800,
"y": 120,
"width": 340,
"height": 180,
"text": "## ❌ Проблема 9\n**Runner label \\\"ubuntu-latest\\\"**\nЭкранированные кавычки в YAML\n\n**Fix:** убрать кавычки вокруг лейбла в config.yaml:\n`- ubuntu-latest:docker://node:20-alpine`"
},
{
"id": "problem10",
"type": "text",
"x": -400,
"y": 120,
"width": 340,
"height": 180,
"text": "## ❌ Проблема 10\n**No such file /opt/xflow/docker-compose.yml**\nVolume не пробрасывался в job контейнер\n\n**Fix:** `container.options` в config.yaml:\n`\"-v /opt/xflow:/opt/xflow\"`\n+ `valid_volumes`"
},
{
"id": "problem11",
"type": "text",
"x": 0,
"y": 120,
"width": 340,
"height": 180,
"text": "## ❌ Проблема 11\n**env file /opt/xflow/.env not found**\n\n**Fix:** создать `/opt/xflow/.env` на сервере с переменными:\nNODE_ENV, DATABASE_URL,\nNUXT_JWT_SECRET, GITEA_URL и т.д."
},
{
"id": "solution",
"type": "text",
"x": -200,
"y": 380,
"width": 500,
"height": 340,
"text": "## ✅ Итоговый стек деплоя\n\n**Runner config.yaml:**\n```yaml\nrunner:\n labels:\n - ubuntu-latest:docker://node:20-alpine\ncontainer:\n network: infra\n options: \"-v /opt/xflow:/opt/xflow -v /opt/npb:/opt/npb\"\n valid_volumes:\n - /opt/xflow\n - /opt/npb\n - /var/run/docker.sock\n```\n\n**Workflow шаги:**\n1. `git clone` через env TOKEN\n2. `npm ci && npm run build`\n3. `cp -r .output/ /opt/xflow/.output/`\n4. `docker-compose up -d --force-recreate`"
},
{
"id": "result",
"type": "text",
"x": -200,
"y": 780,
"width": 500,
"height": 260,
"text": "## 🎉 Результат\n\n- **XFlow** запущен на `http://a-crm.controlsystem.kz` (порт 3002)\n- **NPB** деплоится на порт 3003\n- **Gitea runner** `avtex-runner` обслуживает всю организацию AVTEX\n- Автодеплой при каждом `git push` в master/main\n- Секрет `GIT_TOKEN` на уровне организации\n\n**Следующие шаги:**\n- `prisma migrate deploy`\n- Переписать API с db.ts на Prisma\n- Nginx proxy для NPB"
},
{
"id": "arch",
"type": "text",
"x": 400,
"y": 380,
"width": 340,
"height": 260,
"text": "## 🏗️ Архитектура сервера\n\n```\nDocker network: infra\n├── gitea (3000/3001)\n├── postgres (5432)\n├── gitea-runner\n│ ├── /var/run/docker.sock\n│ ├── /opt/xflow (volume)\n│ └── /opt/npb (volume)\n├── xflow (3002→3000)\n└── npb (3003→3000)\n```\n\nJump host: `alex@178.238.78.98`\nTarget: `root@10.0.0.120`"
}
],
"edges": [
{ "id": "e1", "fromNode": "title", "fromSide": "bottom", "toNode": "problem1", "toSide": "top" },
{ "id": "e2", "fromNode": "title", "fromSide": "bottom", "toNode": "problem2", "toSide": "top" },
{ "id": "e3", "fromNode": "title", "fromSide": "bottom", "toNode": "problem3", "toSide": "top" },
{ "id": "e4", "fromNode": "title", "fromSide": "bottom", "toNode": "problem4", "toSide": "top" },
{ "id": "e5", "fromNode": "problem1", "fromSide": "bottom", "toNode": "problem5", "toSide": "top" },
{ "id": "e6", "fromNode": "problem3", "fromSide": "bottom", "toNode": "problem6", "toSide": "top" },
{ "id": "e7", "fromNode": "problem4", "fromSide": "bottom", "toNode": "problem7", "toSide": "top" },
{ "id": "e8", "fromNode": "problem2", "fromSide": "bottom", "toNode": "problem8", "toSide": "top" },
{ "id": "e9", "fromNode": "problem5", "fromSide": "bottom", "toNode": "problem9", "toSide": "top" },
{ "id": "e10", "fromNode": "problem6", "fromSide": "bottom", "toNode": "problem10", "toSide": "top" },
{ "id": "e11", "fromNode": "problem7", "fromSide": "bottom", "toNode": "problem11", "toSide": "top" },
{ "id": "e12", "fromNode": "problem9", "fromSide": "bottom", "toNode": "solution", "toSide": "top" },
{ "id": "e13", "fromNode": "problem10", "fromSide": "bottom", "toNode": "solution", "toSide": "top" },
{ "id": "e14", "fromNode": "problem11", "fromSide": "bottom", "toNode": "solution", "toSide": "top" },
{ "id": "e15", "fromNode": "solution", "fromSide": "bottom", "toNode": "result", "toSide": "top" },
{ "id": "e16", "fromNode": "solution", "fromSide": "right", "toNode": "arch", "toSide": "left" }
]
}