From 05811e76efa78c43dfb03b8d5055d0bfa36b10c6 Mon Sep 17 00:00:00 2001 From: Ahmad Saufi Date: Mon, 25 May 2026 10:20:58 +0800 Subject: [PATCH] buat docker --- .dockerignore | 15 ++++ .env.docker.example | 56 +++++++++++++++ Dockerfile | 67 ++++++++++++++++++ compose.debug.yaml | 12 ++++ compose.yaml | 10 +++ docker-compose.yml | 97 ++++++++++++++++++++++++++ docker/apache/000-default.conf | 20 ++++++ docker/entrypoint.sh | 30 ++++++++ docker/nginx/chatbotadmin.conf.example | 17 +++++ docker/php/production.ini | 14 ++++ 10 files changed, 338 insertions(+) create mode 100644 .dockerignore create mode 100644 .env.docker.example create mode 100644 Dockerfile create mode 100644 compose.debug.yaml create mode 100644 compose.yaml create mode 100644 docker-compose.yml create mode 100644 docker/apache/000-default.conf create mode 100644 docker/entrypoint.sh create mode 100644 docker/nginx/chatbotadmin.conf.example create mode 100644 docker/php/production.ini diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1b8ab61 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ +.git +.github +node_modules +vendor +storage/logs/*.log +storage/framework/cache/data/* +storage/framework/sessions/* +storage/framework/testing/* +storage/framework/views/* +bootstrap/cache/*.php +.env +.env.* +npm-debug.log +Dockerfile +docker-compose*.yml \ No newline at end of file diff --git a/.env.docker.example b/.env.docker.example new file mode 100644 index 0000000..800679e --- /dev/null +++ b/.env.docker.example @@ -0,0 +1,56 @@ +APP_NAME="Pangkalan Pengetahuan" +APP_ENV=production +APP_KEY= +APP_DEBUG=false +APP_URL=http://localhost +APP_PORT=8080 +APP_LOCALE=ms +APP_FALLBACK_LOCALE=ms + +LOG_CHANNEL=stack +LOG_LEVEL=debug + +DB_CONNECTION=mysql + +# Production MySQL: +# DB_HOST=172.17.200.16 + +# Local MySQL on the Docker host: +# Use host.docker.internal inside the container. Do not use localhost here +# unless MySQL is running in the same container. +DB_HOST=host.docker.internal +DB_PORT=3306 +DB_DATABASE=knowledge_base +DB_USERNAME=root +DB_PASSWORD= + +SESSION_DRIVER=database +CACHE_STORE=database +QUEUE_CONNECTION=database +FILESYSTEM_DISK=local + +# If Ollama/Qdrant run on the Docker host, use host.docker.internal. +OLLAMA_BASE_URL=http://host.docker.internal:11434 +OLLAMA_CHAT_MODEL=llama3 +OLLAMA_EMBEDDING_MODEL=nomic-embed-text + +QDRANT_BASE_URL=http://host.docker.internal:6333 +QDRANT_API_KEY= +QDRANT_COLLECTION=knowledge_base +QDRANT_VECTOR_SIZE=768 +QDRANT_VECTOR_DISTANCE=Cosine + +KB_MAX_FILE_SIZE=20480 +KB_STORAGE_DISK=local +KB_CHUNK_MAX_WORDS=500 +KB_CHUNK_OVERLAP_WORDS=75 +KB_CHUNK_MIN_WORDS=30 +KB_RAG_MAX_CHUNKS=5 +KB_RAG_MAX_CONTEXT_WORDS=2000 +KB_CHAT_RATE_LIMIT=20 +KB_QUEUE_INGESTION=default +KB_QUEUE_EMBEDDING=default +KB_QUEUE_CHAT_LOG=default + +RUN_LARAVEL_MIGRATIONS=false +RUN_LARAVEL_STORAGE_LINK=true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..285a169 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,67 @@ +FROM node:24-bookworm-slim AS assets +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY resources ./resources +COPY vite.config.js ./ +RUN npm run build +FROM composer:2 AS composer +FROM php:8.4-apache AS app +WORKDIR /var/www/html +ENV APACHE_DOCUMENT_ROOT=/var/www/html/public \ + COMPOSER_ALLOW_SUPERUSER=1 +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + curl \ + default-mysql-client \ + git \ + libfreetype6-dev \ + libicu-dev \ + libjpeg62-turbo-dev \ + libmagickwand-dev \ + libpng-dev \ + libzip-dev \ + unzip \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j"$(nproc)" \ + bcmath \ + exif \ + gd \ + intl \ + opcache \ + pcntl \ + pdo_mysql \ + zip \ + && pecl install imagick \ + && docker-php-ext-enable imagick \ + && a2enmod rewrite headers remoteip \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/pear +COPY --from=composer /usr/bin/composer /usr/bin/composer +COPY composer.json composer.lock ./ +RUN composer install \ + --no-dev \ + --no-interaction \ + --no-progress \ + --prefer-dist \ + --no-scripts \ + --optimize-autoloader +COPY . . +COPY --from=assets /app/public/build ./public/build +COPY docker/apache/000-default.conf /etc/apache2/sites-available/000-default.conf +COPY docker/php/production.ini /usr/local/etc/php/conf.d/99-production.ini +COPY --chmod=755 docker/entrypoint.sh /usr/local/bin/docker-entrypoint +RUN mkdir -p \ + storage/app/public \ + storage/framework/cache/data \ + storage/framework/sessions \ + storage/framework/testing \ + storage/framework/views \ + storage/logs \ + bootstrap/cache \ + && chown -R www-data:www-data storage bootstrap/cache \ + && composer dump-autoload --optimize \ + && php artisan package:discover --ansi +EXPOSE 80 +ENTRYPOINT ["docker-entrypoint"] +CMD ["apache2-foreground"] \ No newline at end of file diff --git a/compose.debug.yaml b/compose.debug.yaml new file mode 100644 index 0000000..8f54182 --- /dev/null +++ b/compose.debug.yaml @@ -0,0 +1,12 @@ +services: + chatbotadmin: + image: chatbotadmin + build: + context: . + dockerfile: ./Dockerfile + environment: + NODE_ENV: development + ports: + - 3000:3000 + - 9229:9229 + command: ["node", "--inspect=0.0.0.0:9229", "index.js"] diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..ae4b1eb --- /dev/null +++ b/compose.yaml @@ -0,0 +1,10 @@ +services: + chatbotadmin: + image: chatbotadmin + build: + context: . + dockerfile: ./Dockerfile + environment: + NODE_ENV: production + ports: + - 3000:3000 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..def9145 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,97 @@ +services: + app: + build: + context: . + dockerfile: Dockerfile + image: chatbotadmin:latest + container_name: chatbotadmin_app + restart: unless-stopped + ports: + - "${APP_PORT:-8080}:80" + env_file: + - path: .env + required: false + environment: + APP_NAME: "${APP_NAME:-Pangkalan Pengetahuan}" + APP_ENV: "${APP_ENV:-production}" + APP_KEY: "${APP_KEY:-}" + APP_DEBUG: "${APP_DEBUG:-false}" + APP_URL: "${APP_URL:-http://localhost}" + APP_LOCALE: "${APP_LOCALE:-ms}" + APP_FALLBACK_LOCALE: "${APP_FALLBACK_LOCALE:-ms}" + DB_CONNECTION: "${DB_CONNECTION:-mysql}" + DB_HOST: "${DB_HOST:-host.docker.internal}" + DB_PORT: "${DB_PORT:-3306}" + DB_DATABASE: "${DB_DATABASE:-knowledge_base}" + DB_USERNAME: "${DB_USERNAME:-root}" + DB_PASSWORD: "${DB_PASSWORD:-}" + CACHE_STORE: "${CACHE_STORE:-database}" + QUEUE_CONNECTION: "${QUEUE_CONNECTION:-database}" + SESSION_DRIVER: "${SESSION_DRIVER:-database}" + FILESYSTEM_DISK: "${FILESYSTEM_DISK:-local}" + OLLAMA_BASE_URL: "${OLLAMA_BASE_URL:-http://host.docker.internal:11434}" + OLLAMA_CHAT_MODEL: "${OLLAMA_CHAT_MODEL:-llama3}" + OLLAMA_EMBEDDING_MODEL: "${OLLAMA_EMBEDDING_MODEL:-nomic-embed-text}" + QDRANT_BASE_URL: "${QDRANT_BASE_URL:-http://host.docker.internal:6333}" + QDRANT_API_KEY: "${QDRANT_API_KEY:-}" + QDRANT_COLLECTION: "${QDRANT_COLLECTION:-knowledge_base}" + QDRANT_VECTOR_SIZE: "${QDRANT_VECTOR_SIZE:-768}" + RUN_LARAVEL_MIGRATIONS: "${RUN_LARAVEL_MIGRATIONS:-false}" + RUN_LARAVEL_STORAGE_LINK: "${RUN_LARAVEL_STORAGE_LINK:-true}" + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - chatbotadmin_storage:/var/www/html/storage + - chatbotadmin_cache:/var/www/html/bootstrap/cache + networks: + - chatbotadmin + + queue: + image: chatbotadmin:latest + container_name: chatbotadmin_queue + restart: unless-stopped + depends_on: + - app + env_file: + - path: .env + required: false + environment: + APP_NAME: "${APP_NAME:-Pangkalan Pengetahuan}" + APP_ENV: "${APP_ENV:-production}" + APP_KEY: "${APP_KEY:-}" + APP_DEBUG: "${APP_DEBUG:-false}" + APP_LOCALE: "${APP_LOCALE:-ms}" + APP_FALLBACK_LOCALE: "${APP_FALLBACK_LOCALE:-ms}" + DB_CONNECTION: "${DB_CONNECTION:-mysql}" + DB_HOST: "${DB_HOST:-host.docker.internal}" + DB_PORT: "${DB_PORT:-3306}" + DB_DATABASE: "${DB_DATABASE:-knowledge_base}" + DB_USERNAME: "${DB_USERNAME:-root}" + DB_PASSWORD: "${DB_PASSWORD:-}" + CACHE_STORE: "${CACHE_STORE:-database}" + QUEUE_CONNECTION: "${QUEUE_CONNECTION:-database}" + SESSION_DRIVER: "${SESSION_DRIVER:-database}" + FILESYSTEM_DISK: "${FILESYSTEM_DISK:-local}" + OLLAMA_BASE_URL: "${OLLAMA_BASE_URL:-http://host.docker.internal:11434}" + OLLAMA_CHAT_MODEL: "${OLLAMA_CHAT_MODEL:-llama3}" + OLLAMA_EMBEDDING_MODEL: "${OLLAMA_EMBEDDING_MODEL:-nomic-embed-text}" + QDRANT_BASE_URL: "${QDRANT_BASE_URL:-http://host.docker.internal:6333}" + QDRANT_API_KEY: "${QDRANT_API_KEY:-}" + QDRANT_COLLECTION: "${QDRANT_COLLECTION:-knowledge_base}" + QDRANT_VECTOR_SIZE: "${QDRANT_VECTOR_SIZE:-768}" + extra_hosts: + - "host.docker.internal:host-gateway" + command: ["php", "artisan", "queue:work", "--queue=default", "--sleep=3", "--tries=2", "--timeout=600"] + volumes: + - chatbotadmin_storage:/var/www/html/storage + - chatbotadmin_cache:/var/www/html/bootstrap/cache + networks: + - chatbotadmin + +networks: + chatbotadmin: + driver: bridge + +volumes: + chatbotadmin_storage: + chatbotadmin_cache: diff --git a/docker/apache/000-default.conf b/docker/apache/000-default.conf new file mode 100644 index 0000000..7edd71f --- /dev/null +++ b/docker/apache/000-default.conf @@ -0,0 +1,20 @@ + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html/public + + + AllowOverride All + Require all granted + Options -Indexes +FollowSymLinks + + + RemoteIPHeader X-Forwarded-For + RemoteIPTrustedProxy 10.0.0.0/8 + RemoteIPTrustedProxy 172.16.0.0/12 + RemoteIPTrustedProxy 192.168.0.0/16 + + SetEnvIf X-Forwarded-Proto "https" HTTPS=on + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..e0ba128 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env sh +set -e + +cd /var/www/html + +mkdir -p \ + storage/app/public \ + storage/framework/cache/data \ + storage/framework/sessions \ + storage/framework/testing \ + storage/framework/views \ + storage/logs \ + bootstrap/cache + +chown -R www-data:www-data storage bootstrap/cache + +if [ "${RUN_LARAVEL_STORAGE_LINK:-true}" = "true" ]; then + php artisan storage:link --force >/dev/null 2>&1 || true +fi + +if [ "${RUN_LARAVEL_MIGRATIONS:-false}" = "true" ]; then + php artisan migrate --force +fi + +if [ "${APP_ENV:-production}" = "production" ]; then + php artisan config:cache + php artisan view:cache +fi + +exec "$@" diff --git a/docker/nginx/chatbotadmin.conf.example b/docker/nginx/chatbotadmin.conf.example new file mode 100644 index 0000000..ba953f5 --- /dev/null +++ b/docker/nginx/chatbotadmin.conf.example @@ -0,0 +1,17 @@ +server { + listen 80; + server_name chatbotadmin.example.com; + + client_max_body_size 55M; + + location / { + proxy_pass http://127.0.0.1:8080; + proxy_http_version 1.1; + 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_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + } +} diff --git a/docker/php/production.ini b/docker/php/production.ini new file mode 100644 index 0000000..cab7215 --- /dev/null +++ b/docker/php/production.ini @@ -0,0 +1,14 @@ +memory_limit=512M +max_execution_time=300 +max_input_time=300 +upload_max_filesize=50M +post_max_size=55M +date.timezone=Asia/Kuala_Lumpur + +opcache.enable=1 +opcache.enable_cli=1 +opcache.memory_consumption=256 +opcache.interned_strings_buffer=16 +opcache.max_accelerated_files=20000 +opcache.validate_timestamps=0 +opcache.revalidate_freq=0