Skip to content

chore: настройка GitHub Actions для сборки Docker-образа и оптимизация Dockerfile#186

Open
GaMeRaM wants to merge 7 commits into
rekryt:masterfrom
GaMeRaM:chore-ci
Open

chore: настройка GitHub Actions для сборки Docker-образа и оптимизация Dockerfile#186
GaMeRaM wants to merge 7 commits into
rekryt:masterfrom
GaMeRaM:chore-ci

Conversation

@GaMeRaM

@GaMeRaM GaMeRaM commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Что сделано

  • Добавлен GitHub Actions workflow для сборки Docker-образа.
  • Настроена публикация образа в GHCR.
  • Публикация выполняется только для default branch, git-тегов и ручного запуска workflow.
  • Для pull_request выполняется только проверочная сборка без публикации.
  • Для ручного запуска workflow_dispatch добавлен явный image_tag, чтобы публиковать тестовые образы с предсказуемым тегом.
  • Фронтенд-сборка перенесена внутрь Docker image, чтобы образ сам собирал актуальный public/.
  • Корневой public/ переведен в статус generated-артефакта и больше не используется как источник правды.

Что изменено в Dockerfile

  • Сборка geoip вынесена в отдельный stage.
  • Для установки geoip используется go install с зафиксированной версией.
  • В runtime-образ больше не попадают golang и лишние build-зависимости.
  • Установка Composer упрощена через копирование из composer image.
  • Добавлен отдельный stage для сборки фронтенда через bun.
  • Nuxt static build теперь выполняется внутри Docker image.
  • В финальный образ копируется уже сгенерированный public/ из frontend builder stage.
  • Слои с apt и PHP-расширениями объединены и упрощены.
  • Улучшено кэширование зависимостей за счет порядка COPY и composer install.
  • ADD заменен на COPY.
  • Обновлены комментарии по выбору версии geoip.
  • Убрана повторная ручная загрузка OPcache, из-за которой в контейнере появлялся warning Cannot load Zend OPcache - it was already loaded.

Что изменено в окружении запуска

  • docker-compose.yml и docker-compose.prod.yml больше не монтируют host public/ внутрь контейнеров.
  • Nginx-конфиг обновлен так, чтобы при отсутствии локального static-файла запрос уходил в app, а не завершался 404.
  • Это выравнивает compose-конфигурацию с новым подходом, где frontend собирается внутри образа.

Что изменено в структуре фронтенда

  • Источником правды для статических frontend-файлов теперь считается frontend/public.
  • Сгенерированный корневой public/ исключен из git и удален из индекса как build-артефакт.
  • В .gitignore добавлено исключение для корневого public/.
  • В .dockerignore исключены frontend/node_modules, .nuxt и другие локальные frontend-артефакты, чтобы не раздувать Docker context.

Результат

  • CI теперь автоматически проверяет сборку контейнера.
  • Образ можно публиковать в ghcr.io.
  • Ручной publish из GitHub Actions теперь пригоден для тестирования образов из feature-веток и PR.
  • Docker image самодостаточно собирает backend и frontend.
  • Dockerfile, compose-конфигурация и runtime-путь для статики стали согласованнее и проще в сопровождении.
  • Финальный runtime-образ стал чище и не содержит лишних инструментов сборки.
  • Исключен класс проблем, когда закоммиченный public/ расходится с реальным frontend source.

GaMeRaM added 6 commits April 13, 2026 05:59
Move the static frontend build into the Docker image instead of relying on a checked-in public snapshot.

Why:
- the committed public output had drifted from frontend source and referenced missing _nuxt assets
- the image should produce a consistent public directory from source at build time
- this removes a class of stale artifact bugs where HTML, payloads and hashed assets no longer match

What changed:
- add a Bun-based frontend builder stage that runs Nuxt generate
- copy the generated public output from the builder stage into the runtime image
- keep geoip and PHP dependency setup as separate build concerns
- add frontend bun.lock for reproducible frontend dependency installs
- tighten .dockerignore so local frontend build artifacts are not sent into Docker context

Result:
- Docker builds no longer depend on the repository shipping a prebuilt public tree
- image builds are reproducible from frontend source
- Docker context size is drastically smaller after excluding frontend/node_modules and Nuxt artifacts
- the generated public output is now aligned with the actual frontend build
Treat the root public directory as generated frontend output instead of repository source.

Why:
- frontend source now lives under frontend/ and the Docker image builds the static site during image build
- keeping generated public artifacts in git creates drift between source and built output
- compose mounts and nginx host-file assumptions were overriding or bypassing the image-built frontend

What changed:
- ignore the generated root public directory in git
- remove tracked public artifacts from the repository index while keeping them local for development
- stop bind-mounting host public into app and nginx containers in compose files
- let nginx fall back to the app for static asset misses instead of hard failing on missing host files

Result:
- frontend source of truth is consolidated under frontend/
- Docker and compose use the image-built frontend output consistently
- generated Nuxt assets no longer churn in git history
Address two follow-up issues discovered during review of the Docker and CI changes.

Why:
- workflow_dispatch could trigger a publish without providing a predictable tag, which made manual test publishes unsuitable for branch and PR validation
- the custom PHP JIT ini file was loading OPcache explicitly even though the base php:8.2-cli image already enables it, causing repeated "Cannot load Zend OPcache - it was already loaded" warnings during image build and runtime PHP commands

What changed:
- add a required workflow_dispatch input named image_tag for manual image publishing
- split Docker metadata generation so manual runs always publish with the explicit tag provided by image_tag
- keep the existing default-branch and git-tag publish behavior unchanged for normal push-based automation
- remove the redundant zend_extension=opcache.so line from the custom JIT ini file

Result:
- manual GHCR publishes are now deterministic and usable for testing images from non-default branches
- PHP no longer emits duplicate OPcache load warnings in the Docker image
- JIT remains enabled and the existing image behavior is preserved aside from the warning removal
The Dockerfile refactor switched to a combined COPY for src, config and storage, but that form flattened directory contents into /app instead of creating /app/src, /app/config and /app/storage.

Why this matters:
- Composer autoload expects /app/src/functions.php based on composer.json autoload configuration
- the plain image could fail at startup because the application files no longer matched the expected runtime layout
- this also made runtime verification misleading because bind mounts in compose could mask the broken image structure

What changed:
- replace the combined COPY with explicit directory copies for src, config and storage

Result:
- the built image now preserves the expected application layout
- the plain Docker image can be started without relying on host bind mounts to restore missing paths
Comment thread nginx/conf.d/app.conf Outdated
location / {
location ~* ^.+\.(jpg|jpeg|gif|png|svg|js|css|mp3|ogg|mpe?g|avi|zip|gz|bz2?|rar|swf|woff|woff2|eot|txt)$ {
try_files $uri $uri/ =404;
try_files $uri $uri/ @fallback;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for DDoS protection this thing may cause problems
nginx static cache not work for 404 but will make request to api

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted

Comment thread .dockerignore
frontend/.output
tmp
package-lock.json No newline at end of file
package-lock.json

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

предлагаю за одно удалить все лишние игноры из папки frontend, там копипаста (и gitignore тоже)

Ну и если тут проект ориентирован на bun, package-lock/yarn.lock наверное можно удалить из игноров так как их тут быть не должно.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Из гита удалить стоит, а из игноров наоборот. Надо ведь оставить возможность собирать тем чем хочется локально без docker для тех кто хочет запустить без docker-а.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Из гита удалить стоит, а из игноров наоборот. Надо ведь оставить возможность собирать тем чем хочется локально без docker для тех кто хочет запустить без docker-а.

Тогда надо лок файлы собрать и под гит их сохранить, так как я собрал на nodejs и словил пару ошибок сначала при резолве пакетов, потом при билде (ворнинги + ошибки резолва путей)

Comment thread Dockerfile
# geoip
RUN git clone https://github.com/v2fly/geoip.git /app/geoip/ \
&& cd /app/geoip/ && go build .
COPY ./frontend /app/frontend

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а не легче было бы просто всё скопировать, потом заинсталлить всё что нужно, а public/* собственно скопировать как это сделано дальше?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Суть в том чтобы не переустанавливать зависимости каждый раз когда меняется код

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я понимаю суть этого докерфайла, я говорю о том, что нет смысла копировать что-то дважды, скопировать сразу все просто логичней

Comment thread Dockerfile

WORKDIR /app

COPY ./composer.json /app/

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

я бы предложил под гит закинуть composer.lock чтобы зафиксировать дерево версий и тут его просто копировать, чтобы лишнего не закачивать в образ

Comment thread Dockerfile
@@ -1,50 +1,64 @@
FROM php:8.2-cli
FROM golang:1.24-bookworm AS geoip-builder

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

по хорошему бы доку дописать, чтобы запускали docker compose up -d --build, чтобы перебилд делали, раз вся загрузка ассетов и прочего находится тут, чтобы контейнер перебилдился

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для разработки согласен. Для обычного деплоя достаточно образа из реестра

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Дока существует как раз для того, чтобы можно было делать пулл реквесты и раз тут переделывается способ разработки, то и доку дополнять было бы хорошо

@rekryt

rekryt commented Apr 19, 2026

Copy link
Copy Markdown
Owner

@copilot resolve the merge conflicts in this pull request - you can leave the files in the /public/ folder, those that are newer, do not apply these from pull request

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants