From b34e55679883fcf7c6573818e9b215f60750ab4c Mon Sep 17 00:00:00 2001 From: agung Date: Tue, 1 Aug 2023 20:02:08 +0700 Subject: [PATCH] first commit --- .editorconfig | 18 ++++++ .gitignore | 9 +++ Dockerfile | 70 +++++++++++++++++++++++ Makefile | 95 ++++++++++++++++++++++++++++++++ README.md | 85 ++++++++++++++++++++++++++++ docker-compose.yml | 88 +++++++++++++++++++++++++++++ docker/image/laravel+docker.png | Bin 0 -> 18480 bytes docker/nginx/default.conf | 35 ++++++++++++ docker/postgres/.gitkeep | 0 secret.sh | 23 ++++++++ 10 files changed, 423 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 docker/image/laravel+docker.png create mode 100644 docker/nginx/default.conf create mode 100644 docker/postgres/.gitkeep create mode 100755 secret.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..d7aa8b6b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[Makefile] +indent_style = tab \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..38701aa0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# Editor +.vscode + +# Container file +/docker/postgres/data/* +!/docker/postgres/data/.gitkeep + +# Working directory +src/* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..e615d2cd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,70 @@ +# PHP-FPM is a FastCGI implementation for PHP. +# Read more here: https://hub.docker.com/_/php +FROM php:8.2-fpm + + +RUN apt-get update + +# Install useful tools +RUN apt-get -y install apt-utils nano wget dialog vim + +# Install system dependencies +RUN apt-get -y install --fix-missing \ + apt-utils \ + build-essential \ + git \ + curl \ + libcurl4 \ + libcurl4-openssl-dev \ + zlib1g-dev \ + libzip-dev \ + zip \ + libbz2-dev \ + locales \ + libmcrypt-dev \ + libicu-dev \ + libonig-dev \ + libxml2-dev + +RUN docker-php-ext-install \ + exif \ + pcntl \ + bcmath \ + ctype \ + curl \ + pcntl \ + zip + +# Install Postgre PDO +RUN apt-get install -y libpq-dev \ + && docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \ + && docker-php-ext-install pdo pdo_pgsql pgsql + +# Install NPM +RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - +RUN apt-get install -y nodejs + +# Clear cache +RUN apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Composer +COPY --from=composer:2.3 /usr/bin/composer /usr/bin/composer + +# Set working directory +WORKDIR /var/www/html + +# Add user for laravel application +RUN groupadd -g 1000 www +RUN useradd -u 1000 -ms /bin/bash -g www www + +# Copy existing application directory contents +COPY ./src /var/www/html + +# Copy existing application directory permissions +COPY --chown=www:www ./src /var/www/html + +# Change current user to www +USER www + +# Set port for application +EXPOSE 8000 diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..e13e543f --- /dev/null +++ b/Makefile @@ -0,0 +1,95 @@ +up: + docker compose up -d +build: + docker compose build +laravel-install: + docker compose exec app composer create-project --prefer-dist laravel/laravel . +create-project: + mkdir -p src + @make build + @make up + @make laravel-install + docker compose exec app php artisan key:generate + docker compose exec app php artisan storage:link + docker compose exec app chmod -R 777 storage bootstrap/cache + docker compose exec app npm install +init: + docker compose up -d --build + docker compose exec app composer install + docker compose exec app cp .env.example .env + docker compose exec app php artisan key:generate + docker compose exec app php artisan storage:link + docker compose exec app chmod -R 777 storage bootstrap/cache + docker compose exec app npm install + @make fresh +remake: + @make destroy + @make init +stop: + docker compose stop +down: + docker compose down --remove-orphans +down-v: + docker compose down --remove-orphans --volumes +restart: + @make down + @make up +destroy: + docker compose down --rmi all --volumes --remove-orphans +ps: + docker compose ps +logs: + docker compose logs +logs-watch: + docker compose logs --follow +log-web: + docker compose logs web +log-web-watch: + docker compose logs --follow web +log-app: + docker compose logs app +log-app-watch: + docker compose logs --follow app +log-db: + docker compose logs postgres +log-db-watch: + docker compose logs --follow postgres +web: + docker compose exec web bash +app: + docker compose exec app bash +migrate: + docker compose exec app php artisan migrate +fresh: + docker compose exec app php artisan migrate:fresh --seed +seed: + docker compose exec app php artisan db:seed +dacapo: + docker compose exec app php artisan dacapo +rollback-test: + docker compose exec app php artisan migrate:fresh + docker compose exec app php artisan migrate:refresh +tinker: + docker compose exec app php artisan tinker +test: + docker compose exec app php artisan test +optimize: + docker compose exec app php artisan optimize +optimize-clear: + docker compose exec app php artisan optimize:clear +cache: + docker compose exec app composer dump-autoload -o + @make optimize + docker compose exec app php artisan event:cache + docker compose exec app php artisan view:cache +cache-clear: + docker compose exec app composer clear-cache + @make optimize-clear + docker compose exec app php artisan event:clear +dump-autoload: + docker compose exec app composer dump-autoload +ide-helper: + docker compose exec app php artisan clear-compiled + docker compose exec app php artisan ide-helper:generate + docker compose exec app php artisan ide-helper:meta + docker compose exec app php artisan ide-helper:models --nowrite diff --git a/README.md b/README.md new file mode 100644 index 00000000..b4007e3e --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# Laravel using PostgreSQL in Docker + +

+ docker+laravel +

+ +## Introduction + +Build a simple laravel application development environment with docker compose. + + +## Requirement + +- Docker ^19.* + + +## Installation + +1. Git clone & move to working directory +2. Settings your credentials docker compose using [Docker Secrets](https://docs.docker.com/engine/swarm/secrets/) + +- Create credentials for DB Name + + ```bash + $ echo "" | docker secret create app_db_name - + ``` +- Create credentials for DB User + + ```bash + $ echo "" | docker secret create app_db_user - + ``` +- Create credentials for DB Password + + ```bash + $ echo "" | docker secret create app_db_password - + ``` +### *Optional credentials* +If you want to use pgAdmin management add this credentials: + +- Create credentials for pgAdmin Password + + ```bash + $ echo "" | docker secret create app_pgadmin_password - + ``` + +**Uncomment** in top level secret for pgAdmin in file docker-compose.yml to: +```bash + app_pgadmin_password: + external: true +``` + +1. Execute the following command for create application + +```bash +$ make create-project +``` + +4. set src/.env variable : +``` +DB_CONNECTION=pgsql +DB_HOST=postgres +DB_PORT=5432 +DB_DATABASE= +DB_USERNAME= +DB_PASSWORD= +``` + +5. show application in [http://localhost:85](http://localhost:85) +6. show adminer in [http://localhost:8080](http://localhost:8080) +7. list execute command in [Makefile](Makefile). + +## Container details : +- ``app`` use image: + - [php](https://hub.docker.com/_/php):8.2-fpm + - [composer](https://hub.docker.com/_/composer):2.3 + - [npm](https://deb.nodesource.com/setup_lts.x):latest +- ``web`` use image: + - [nginx](https://hub.docker.com/_/nginx):stable-alpine +- ``db`` use image: + - [postgres](https://hub.docker.com/_/postgres):15 +- ``adminer`` use image: + - [adminer](https://hub.docker.com/_/adminer):latest +*Optional* +- ``pgadmin`` use image: + - [pgadmin](https://hub.docker.com/_/pgadmin):latest diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..a88c2498 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,88 @@ +version: "3.9" + +networks: + aselole: + name: aselole + +services: + app: + container_name: aselole-app + build: + context: . + dockerfile: Dockerfile + volumes: + - ./src:/var/www/html + depends_on: + - postgres + networks: + - aselole + + postgres: + container_name: aselole-db + image: postgres:15 + restart: always + volumes: + - ./docker/postgres/data:/var/lib/postgres/data + environment: + - POSTGRES_DB_FILE=/run/secrets/app_db_name + - POSTGRES_USER_FILE=/run/secrets/app_db_user + - POSTGRES_PASSWORD_FILE=/run/secrets/app_db_password + secrets: + - app_db_name + - app_db_user + - app_db_password + ports: + - "5432:5432" + networks: + - aselole + + web: + container_name: aselole-web + image: nginx:stable-alpine + restart: always + ports: + - "81:80" + - "5173:80" + volumes: + - ./src:/var/www/html + - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf + networks: + - aselole + + # Database management with pgAdmin + pgadmin: + image: dpage/pgadmin4 + container_name: aselole-pgAdmin + environment: + - PGADMIN_DEFAULT_EMAIL=agung@gmail.com + - PGADMIN_DEFAULT_PASSWORD_FILE=/run/secrets/app_pgadmin_password + secrets: + - app_pgadmin_password + ports: + - "5050:80" + depends_on: + - postgres + networks: + - aselole + + # Database management with Adminer + adminer: + container_name: aselole-adminer + image: adminer + restart: always + ports: + - "8080:8080" + depends_on: + - postgres + networks: + - aselole + +secrets: + app_db_name: + external: true + app_db_user: + external: true + app_db_password: + external: true + app_pgadmin_password: + external: true diff --git a/docker/image/laravel+docker.png b/docker/image/laravel+docker.png new file mode 100644 index 0000000000000000000000000000000000000000..398d48f0e3a762a6a1f75e208b22c83120a97511 GIT binary patch literal 18480 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