Compare commits

...

19 Commits

Author SHA1 Message Date
b5ed25d542 Доработка docker-окружения
- перенос конфигов для web в его папку
- префикс m3u-su- возвращён к на iptv-
- уточнения по сборке docs и мелочи по остальным
2026-01-03 01:14:19 +08:00
448eada6e9 Перевод checker и docs на контейнеры 2025-11-23 01:40:42 +08:00
bdbd9f9e57 Корректировка скрипта iptv с учётом замены репозиториев на контейнеры 2025-11-23 01:39:58 +08:00
66df86f1aa Выключен appendonly 2025-07-20 10:58:02 +08:00
24a5f988a3 Выключена репликация keydb 2025-07-19 15:58:50 +08:00
28f93bd9a6 Правки конфига keydb
- перечитка старых данных при перезапуске
- попытка вырубить репликацию
2025-07-10 11:15:41 +08:00
70847623e8 Из конфига keydb убран log, ибо не имеет смысла 2025-07-07 08:46:58 +08:00
68d051c36e Конфиг от keydb вместо redis 2025-07-07 08:27:11 +08:00
1456237de2 Merge branch 'master' of git.axenov.dev:IPTV/iptv-docker 2025-07-05 00:41:51 +08:00
fbc5ea4428 Мелочи по keydb 2025-07-05 00:41:33 +08:00
c49b6b979d IPTV_ENV=dev 2025-07-05 00:38:30 +08:00
10bf297543 Вернулась команда iptv exec, исправлена rebuild 2025-07-05 00:34:18 +08:00
4d2cfe8030 Фикс образа web.prod 2025-07-05 00:09:45 +08:00
ea700fc0fe Выключены параметры репликации 2025-06-30 23:33:47 +08:00
13de506761 Удалена директория docs 2025-06-21 22:30:39 +08:00
7e5a0bcebd Внедрение документации 2025-06-21 20:38:22 +08:00
2f3bea76f4 Фикс команд iptv, связанных с контейнерами 2025-06-05 00:04:54 +08:00
f1bd94b35b Фикс команды iptv init 2025-05-13 15:52:48 +08:00
55bc7ce0f8 Доработка entrypoint для чекера: загрузка готового бинаря вместо компиляции 2025-05-13 15:52:37 +08:00
17 changed files with 724 additions and 578 deletions

View File

@@ -1,4 +1,4 @@
IPTV_ENV=prod
IPTV_ENV=dev
KEYDB_UID=1000
KEYDB_GID=1000

3
.gitignore vendored
View File

@@ -1,7 +1,10 @@
/.idea/
/.vscode/
/docker/keydb/data/*
/log/**/*
/iptvc/
/web/
/docs/
/playlists/
/tools/
/.profile/

View File

@@ -1,13 +1,13 @@
# Инфраструктурный слой проекта iptv.axenov.dev
# Инфраструктурный слой проекта m3u.su
Docker-окружение для работы проекта iptv.axenov.dev.
Docker-окружение для работы проекта m3u.su.
> **Веб-сайт:** [iptv.axenov.dev](https://iptv.axenov.dev)
> **Зеркало:** [m3u.su](https://m3u.su)
> **Веб-сайт:** [m3u.su](https://m3u.su)
> **Документация:** [m3u.su/docs](https://m3u.su/docs)
> Исходный код: [git.axenov.dev/IPTV](https://git.axenov.dev/IPTV)
> Telegram-канал: [@iptv_aggregator](https://t.me/iptv_aggregator)
> Обсуждение: [@iptv_aggregator_chat](https://t.me/iptv_aggregator_chat)
> Дополнительные сведения: [git.axenov.dev/IPTV/.profile](https://git.axenov.dev/IPTV/.profile)
> Бот: [@iptv_aggregator_bot](https://t.me/iptv_aggregator_bot)
## Использованный стек
@@ -38,15 +38,15 @@ wget -O - https://git.axenov.dev/IPTV/iptv-docker/raw/branch/master/iptv | bash
## Описание переменных окружения
* `IPTV_ENV` -- окружение для развёртывания: это имена директорий и/или префиксы имён конфигов, которые будут проброшены в контейнеры;
* `KEYDB_UID`, `KEYDB_GID` -- ID пользователя/группы для разрешения владельца файлов и директорий keydb;
* `KEYDB_PORT` -- порт keydb, который будет проброшен на хост.
* `KEYDB_USERNAME`, `KEYDB_PASSWORD` -- реквизиты доступа к keydb;
* `CHECKER_DB` -- БД keydb для хранения кеша проверенных плейлистов;
* `CHECKER_TTL` -- время жизни кеша проверенных плейлистов;
* `CHECKER_WAIT` -- кол-во секунд между запусками iptvc;
* `CHECKER_INIFILE` -- путь к файлу списка плейлистов внутри контейнера;
* `CHECKER_TAGFILE` -- путь к файлу списка тегов внутри контейнера.
* `IPTV_ENV` окружение для развёртывания: это имена директорий и/или префиксы имён конфигов, которые будут проброшены в контейнеры;
* `KEYDB_UID`, `KEYDB_GID` ID пользователя/группы для разрешения владельца файлов и директорий keydb;
* `KEYDB_PORT` порт keydb, который будет проброшен на хост.
* `KEYDB_USERNAME`, `KEYDB_PASSWORD` реквизиты доступа к keydb;
* `CHECKER_DB` БД keydb для хранения кеша проверенных плейлистов;
* `CHECKER_TTL` время жизни кеша проверенных плейлистов;
* `CHECKER_WAIT` кол-во секунд между запусками iptvc;
* `CHECKER_INIFILE` путь к файлу списка плейлистов внутри контейнера;
* `CHECKER_TAGFILE` путь к файлу списка тегов внутри контейнера.
## Reverse-proxy

View File

@@ -1,5 +1,7 @@
name: iptv
networks:
iptv:
iptv-network:
driver: bridge
x-common-attributes: &common-attributes
@@ -12,34 +14,50 @@ x-common-attributes: &common-attributes
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
networks:
- iptv
- iptv-network
services:
nginx:
<<: *common-attributes
container_name: iptv-nginx
image: nginx:latest
pull_policy: always
volumes:
- ./docker/nginx/vhost.conf:/etc/nginx/conf.d/default.conf:ro
- ./log/nginx:/var/log/nginx:rw
- ./web:/var/www:ro
ports:
- 3000:80
depends_on:
- web
keydb:
<<: *common-attributes
container_name: iptv-keydb
image: eqalpha/keydb:latest
user: "${KEYDB_UID}:${KEYDB_GID}"
pull_policy: always
user: ${KEYDB_UID}:${KEYDB_GID}
entrypoint: ["sh", "/entrypoint.sh"]
volumes:
- ./docker/keydb/entrypoint.sh:/entrypoint.sh
- ./docker/keydb/keydb.conf:/etc/keydb/keydb.conf
- ./docker/keydb/data/:/data:rw
- ./log/keydb:/var/log/keydb/:rw
ports:
- "${KEYDB_PORT:-6379}:6379"
- ${KEYDB_PORT:-6379}:6379
web:
<<: *common-attributes
container_name: iptv-web
build:
dockerfile: dockerfile.web.${IPTV_ENV}
context: ./web
dockerfile: Dockerfile
target: iptv-web-${IPTV_ENV}
environment:
- PHP_IDE_CONFIG=serverName=iptv.local
volumes:
- ./docker/php/${IPTV_ENV}/www.conf:/usr/local/etc/php-fpm.d/www.conf:ro
- ./docker/php/${IPTV_ENV}/php.ini:/usr/local/etc/php/conf.d/php.ini:ro
- ./web/docker/${IPTV_ENV}/www.conf:/usr/local/etc/php-fpm.d/www.conf:ro
- ./web/docker/${IPTV_ENV}/php.ini:/usr/local/etc/php/conf.d/php.ini:ro
- ./playlists/playlists.ini:/var/www/config/playlists.ini
# - ./playlists/channels.json:/var/www/config/channels.json
- ./log/php:/var/log/php:rw
- ./web:/var/www:rw
depends_on:
@@ -48,39 +66,29 @@ services:
checker:
<<: *common-attributes
container_name: iptv-checker
image: git.axenov.dev/iptv/iptvc:latest
build:
dockerfile: ./dockerfile.checker
context: ./iptvc
dockerfile: Dockerfile
command: ["check", "--repeat", "0", "--every", "${CHECKER_WAIT:-60}"]
environment:
- CACHE_ENABLED=true
# - CACHE_HOST=localhost
- CACHE_HOST=iptv-keydb
- CACHE_PORT=${KEYDB_PORT:-6379}
- CACHE_USERNAME=${KEYDB_USERNAME}
- CACHE_PASSWORD=${KEYDB_PASSWORD}
- CACHE_DB=${CHECKER_DB:-0}
- CACHE_TTL=${CHECKER_TTL:-1800}
- CHECKER_WAIT=${CHECKER_WAIT:-60}
- CHECKER_INIFILE=${CHECKER_INIFILE:-/app/playlists.ini}
- CHECKER_TAGFILE=${CHECKER_TAGFILE:-/app/channels.json}
volumes:
- ./docker/checker/entrypoint.sh:/entrypoint.sh
- ./iptvc/:/app/
- ./playlists/playlists.ini:${CHECKER_INIFILE:-/app/playlists.ini}
- ./playlists/channels.json:${CHECKER_TAGFILE:-/app/channels.json}
depends_on:
- keydb
- ./playlists/playlists.ini:/app/playlists.ini
- ./playlists/channels.json:/app/channels.json
nginx:
docs:
<<: *common-attributes
container_name: iptv-nginx
image: nginx:latest
volumes:
- ./docker/nginx/vhost.conf:/etc/nginx/conf.d/default.conf:ro
- ./log/nginx:/var/log/nginx:rw
- ./web:/var/www:ro
container_name: iptv-docs
image: git.axenov.dev/iptv/iptv-docs:latest
build:
context: ./docs
dockerfile: Dockerfile
ports:
- "8080:80"
links:
- web
depends_on:
- web
- 3001:80

View File

@@ -1,24 +0,0 @@
#!/bin/bash
echo "CHECKER_WAIT=$CHECKER_WAIT"
echo "CHECKER_INIFILE=$CHECKER_INIFILE"
echo "CHECKER_TAGFILE=$CHECKER_TAGFILE"
binary="/app/bin/linux_amd64/iptvc"
args="check -i $CHECKER_INIFILE -t $CHECKER_TAGFILE"
go get
make linux
if [ ! -f "$binary" ]; then
echo "Not found: $binary"
exit 1
fi
while true; do
echo
echo "Running: $binary $args"
$binary $args
echo "Waiting $CHECKER_WAIT seconds"
sleep $CHECKER_WAIT
done

View File

@@ -0,0 +1,4 @@
#!/bin/sh
trap 'echo "Received SIGTERM, saving KeyDB data..."; keydb-cli SAVE; echo "Data saved. Exiting."; exit 0' TERM
echo "[entrypoint] Starting KeyDB..."
exec keydb-server /etc/keydb/keydb.conf "$@"

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,14 @@
server {
server_name iptv.local;
listen 80;
root /var/www/public;
index index.php;
index index.html index.php;
# access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
add_header Access-Control-Allow-Origin '*';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers "*";
add_header Access-Control-Allow-Credentials "true";
gzip on;
gzip_vary on;
gzip_proxied any;
@@ -10,15 +16,29 @@ server {
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
location ~* \.(jpg|jpeg|gif|css|png|js|ico|html)$ {
access_log off;
expires max;
log_not_found off;
location = /docs {
return 301 /docs/;
}
location ^~ /docs/ {
proxy_pass http://docs:80/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
}
location / {
root /var/www/public;
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
root /var/www/public;
try_files $uri /index.php =404;
fastcgi_pass web:9000;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
@@ -28,6 +48,20 @@ server {
fastcgi_hide_header X-Powered-By;
fastcgi_read_timeout 300;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
include fastcgi_params;
location ~* \.(jpg|jpeg|gif|css|png|ttf|woff|svg|js|ico)$ {
access_log off;
expires max;
log_not_found off;
}
}
location ~* \.(jpg|jpeg|gif|css|png|ttf|woff|svg|js|ico)$ {
root /var/www/public;
access_log off;
expires max;
log_not_found off;
}
}

View File

@@ -1,26 +0,0 @@
[PHP]
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED
expose_php = Off
file_uploads = Off
max_execution_time=-1
memory_limit = 512M
[opcache]
opcache.enable = 1
opcache.enable_cli = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 30000
opcache.revalidate_freq = 0
opcache.jit_buffer_size = 64M
opcache.jit = tracing
[xdebug]
; https://xdebug.org/docs/all_settings
zend_extension = xdebug.so
xdebug.mode = debug
xdebug.start_with_request = yes
xdebug.trigger_value = go
xdebug.client_host = host.docker.internal
xdebug.REQUEST = *
xdebug.SESSION = *
xdebug.SERVER = *

View File

@@ -1,22 +0,0 @@
[www]
user = www-data
group = www-data
listen = 127.0.0.1:9000
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 50
pm.status_path = /status
ping.path = /ping
ping.response = pong
access.log = /var/log/php/$pool.access.log
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{milli}d %{kilo}M %C%%"
; chroot = /var/www
; chdir = /var/www
php_flag[display_errors] = on
php_admin_value[error_log] = /var/log/php/$pool.error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 512M
php_admin_value[error_reporting] = E_ALL & ~E_NOTICE & ~E_DEPRECATED

View File

@@ -1,16 +0,0 @@
[PHP]
error_reporting = E_ALL
expose_php = Off
file_uploads = Off
memory_limit = 512M
; upload_max_filesize=10M
; post_max_size=10M
[opcache]
opcache.enable = 1
opcache.enable_cli = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 30000
opcache.revalidate_freq = 0
opcache.jit_buffer_size = 64M
opcache.jit = tracing

View File

@@ -1,22 +0,0 @@
[www]
user = www-data
group = www-data
listen = 127.0.0.1:9000
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 50
pm.status_path = /status
ping.path = /ping
ping.response = pong
access.log = /var/log/php/$pool.access.log
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{milli}d %{kilo}M %C%%"
; chroot = /var/www
; chdir = /var/www
php_flag[display_errors] = on
php_admin_value[error_log] = /var/log/php/$pool.error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 512M
php_admin_value[error_reporting] = E_ALL & ~E_NOTICE & ~E_DEPRECATED

View File

@@ -1,14 +0,0 @@
FROM alpine:3.21 AS iptvc-compiler
RUN apk --no-cache add \
bash \
tzdata \
go \
make
RUN mkdir /app && \
chmod 777 /app
WORKDIR /app
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -1,37 +0,0 @@
FROM php:8.4-fpm AS iptv-php-dev
RUN apt update && \
apt upgrade -y && \
apt install -y \
git \
unzip \
7zip \
cron \
zlib1g-dev \
imagemagick \
libpng-dev \
libjpeg-dev
# https://pecl.php.net/package/xdebug
# https://pecl.php.net/package/redis
RUN pecl channel-update pecl.php.net && \
pecl install \
xdebug-3.4.1 \
redis-6.1.0
RUN docker-php-ext-enable redis && \
docker-php-ext-configure gd --with-jpeg && \
docker-php-ext-install gd
RUN mkdir -p /var/run/php && \
mkdir -p /var/log/php && \
chmod -R 777 /var/log/php
COPY --from=composer /usr/bin/composer /usr/local/bin/composer
RUN git config --global --add safe.directory /var/www
EXPOSE 9000
WORKDIR /var/www
CMD composer install && \
php-fpm --nodaemonize

View File

@@ -1,34 +0,0 @@
FROM php:8.4-fpm AS iptv-php-prod
RUN apt update && \
apt upgrade -y && \
apt install -y \
git \
unzip \
7zip \
cron \
zlib1g-dev \
imagemagick \
libpng-dev \
libjpeg-dev
# https://pecl.php.net/package/redis
RUN pecl channel-update pecl.php.net && \
pecl install redis-6.1.0
RUN docker-php-ext-enable xdebug redis && \
docker-php-ext-configure gd --with-jpeg && \
docker-php-ext-install gd
RUN mkdir -p /var/run/php && \
mkdir -p /var/log/php && \
chmod -R 777 /var/log/php
COPY --from=composer /usr/bin/composer /usr/local/bin/composer
RUN git config --global --add safe.directory /var/www
EXPOSE 9000
WORKDIR /var/www
RUN composer install
ENTRYPOINT php-fpm --nodaemonize

53
iptv
View File

@@ -21,7 +21,7 @@ else
ROOT_PATH="$(pwd)"
fi
IPTV_PROJECTS=("iptvc" "web" "playlists")
IPTV_PROJECTS=("web" "playlists")
IPTV_GITEA_URL_SSH="git@git.axenov.dev:IPTV"
IPTV_GITEA_URL_HTTPS="https://git.axenov.dev/IPTV"
IPTV_DOCKER_URL_SSH="$IPTV_GITEA_URL_SSH/iptv-docker.git"
@@ -338,6 +338,7 @@ docker.build_base_images() {
# done
subtitle "Построение образов"
docker.compose build
[ ! -d web/cache/views ] && mkdir -p web/cache/views
success "Базовые образы построены"
}
@@ -362,12 +363,12 @@ docker.exec_www() {
# Возвращает ssh-адрес к репозиторию проекта
project_url_ssh() {
echo "$IPTV_GITEA_URL_SSH/${IPTV_PROJECTS[$1]}.git"
echo "$IPTV_GITEA_URL_SSH/$1.git"
}
# Возвращает https-адрес к репозиторию проекта
project_url_https() {
echo "$IPTV_GITEA_URL_HTTPS/${IPTV_PROJECTS[$1]}"
echo "$IPTV_GITEA_URL_HTTPS/$1"
}
# Копирует .env.example в .env, если возможно
@@ -416,8 +417,8 @@ find_service_compose() {
for known in $(docker.compose config --services); do
if [ "$known" = "$svc" ]; then
debug "Сервис '$svc' найден в композе"
echo "iptv-$known"
debug "Сервис '$known' найден в композе"
echo "$known"
exit
fi
done
@@ -432,9 +433,8 @@ find_services_compose() {
[ "$*" ] && for svc in "$@"; do
grep_match "$svc" "^--?.*" && continue
var_dump svc
svc="$(find_service_compose "$svc")"
services="$services iptv-$svc"
svc="$(find_service_compose "${svc/iptv-/}")"
services="$services $svc"
done
trim "$services"
@@ -469,7 +469,7 @@ init() {
local project_repo_path="$docker_repo_path/$repo_name"
subtitle "[$counter/$repo_count] Подготовка репозитория ${FBOLD}$repo_name${FRESET}..."
local project_repo_url=$(project_url_ssh "$repo_name")
local project_repo_url=$(project_url_https "$repo_name")
debug "Известная ссылка на репозиторий: $project_repo_url"
project_clone "$project_repo_url" "$project_repo_path"
@@ -495,12 +495,11 @@ up() {
process_help_arg
subtitle "Создание и запуск контейнеров"
argl profiles 0 profiles
local services=''
[ "$*" ] && services="$(find_services_compose "$@")"
COMPOSE_PROFILES="$profiles" docker.compose up "$services" --build --detach --remove-orphans && \
[ ! -d web/cache/views ] && mkdir -p web/cache/views
docker.compose up "$services" --build --detach --remove-orphans && \
success 'Среда запущена успешно'
}
@@ -523,13 +522,10 @@ down() {
process_help_arg
subtitle "Остановка и удаление контейнеров"
argl profiles 0 profiles
[[ -z "$profiles" ]] && profiles="full"
local services=''
[ "$*" ] && services="$(find_services_compose "$@")"
COMPOSE_PROFILES="$profiles" docker.compose down "$services" --remove-orphans && \
docker.compose down "$services" --remove-orphans && \
success 'Среда остановлена успешно'
}
@@ -555,7 +551,7 @@ rebuild() {
is_full=$(arg full 1)
[ "$is_full" = 0 ] && is_full=$(argl full 1)
[ -n "$*" ] && down "$@"
[ -n "$*" ] && down "$@" || down
[ "$is_full" = 1 ] && docker.build_base_images
up "$@"
@@ -580,6 +576,27 @@ purge() {
success 'Образы удалены успешно'
}
# Выполняет команду в контейнере
exec() {
process_help_arg
as_root=0
svc="$1"
regex_match "$svc" "--?r(oot)?" && { as_root=1; shift; svc="$1"; }
svc_correct="iptv-$(find_service_compose "$svc")"
command=("${@:2}")
regex_match "${command[0]}" "--?r(oot)?" && { as_root=1; unset "command[0]"; }
[[ -z "${command[*]}" ]] && die "не указана команда для выполнения в контейнере"
#TODO многострочные команды прокидываются корректно, но выполняется только первая строка?
if [[ "$as_root" == 1 ]]; then
docker.exec "$svc_correct" "${command[*]}"
else
docker.exec_www "$svc_correct" "${command[*]}"
fi
}
# Выводит логи сервиса
logs() {
process_help_arg
@@ -603,7 +620,6 @@ stats() {
docker.compose stats "$@"
}
########################################################
# Команды справки
########################################################
@@ -828,6 +844,7 @@ case "$COMMAND" in
stop ) stop "$@" ;;
r|rebuild ) rebuild "$@" ;;
restart ) restart "$@" ;;
exec ) exec "$@" ;;
purge ) purge ;;
logs ) logs "$@" ;;
stats ) stats "$@" ;;

View File