Add Docker setup for production environment.
This commit is contained in:
		
							
								
								
									
										42
									
								
								docker-compose-prod.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								docker-compose-prod.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| version: '3.7' | ||||
| services: | ||||
|   nginx: | ||||
|     build: | ||||
|       context: ./docker/nginx | ||||
|       dockerfile: Dockerfile | ||||
|     depends_on: | ||||
|       - app | ||||
|       - swagger | ||||
|     ports: | ||||
|       - ${DOCKER_CAPTCHA_NGINX_PORT}:80 | ||||
|   app: | ||||
|     depends_on: | ||||
|       - redis | ||||
|     build: | ||||
|       context: ./docker/app | ||||
|       dockerfile: Dockerfile | ||||
|       target: PRODUCTION | ||||
| #    restart: always | ||||
|     cap_drop: | ||||
|       - ALL | ||||
|     cap_add: | ||||
|       - SETGID | ||||
|       - SETUID | ||||
|       - CHOWN | ||||
|       - FOWNER | ||||
|     ports: | ||||
|       - "9000" | ||||
|     env_file: .env | ||||
|     volumes: | ||||
|       - /etc/localtime:/etc/localtime:ro | ||||
|   swagger: | ||||
|     image: swaggerapi/swagger-ui | ||||
|     depends_on: | ||||
|       - app | ||||
|     environment: | ||||
|       URLS: "[ { url: '/swagger.json', name: '/swagger.json' } ]" | ||||
|       BASE_URL: /api-docs | ||||
|     ports: | ||||
|       - "8080" | ||||
|   redis: | ||||
|     image: redis:3.0-alpine | ||||
							
								
								
									
										87
									
								
								docker/app/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								docker/app/Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| FROM docker.io/php:8.2-zts-alpine3.18 AS UNIT_BUILDER | ||||
|  | ||||
| ARG UNIT_VERSION=1.31.1 | ||||
|  | ||||
| RUN apk --no-cache add pcre2-dev gcc git musl-dev make && \ | ||||
|     mkdir -p /usr/lib/unit/modules && \ | ||||
|     git clone https://github.com/nginx/unit.git && \ | ||||
|     cd unit && \ | ||||
|     git checkout $UNIT_VERSION && \ | ||||
|     ./configure --prefix=/var --statedir=/var/lib/unit --runstatedir=/var/run --control=unix:/run/unit/control.unit.sock --log=/var/log/unit.log --user=www-data --group=www-data --tmpdir=/tmp --modulesdir=/var/lib/unit/modules && \ | ||||
|     ./configure php && \ | ||||
|     make && \ | ||||
|     make install | ||||
|  | ||||
| FROM docker.io/php:8.2-zts-alpine3.18 as BUILD | ||||
|  | ||||
| COPY --from=UNIT_BUILDER /var/sbin/unitd /usr/sbin/unitd | ||||
| COPY --from=UNIT_BUILDER /var/lib/unit/ /var/lib/unit/ | ||||
|  | ||||
| COPY docker-entrypoint.sh /home/unit/docker-entrypoint.sh | ||||
| COPY unit-config.json /docker-entrypoint.d/config.json | ||||
|  | ||||
| RUN apk --no-cache add pcre2 libbz2 libpng libwebp libjpeg-turbo icu-libs freetype oniguruma libzip \ | ||||
|     && apk add --no-cache --virtual .phpize-deps icu-dev libpng-dev bzip2-dev libwebp-dev libjpeg-turbo-dev freetype-dev oniguruma-dev libzip-dev pcre2-dev ${PHPIZE_DEPS} \ | ||||
|     && docker-php-ext-configure intl --enable-intl && \ | ||||
|     docker-php-ext-configure bcmath --enable-bcmath && \ | ||||
|     docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp && \ | ||||
|     docker-php-ext-install -j$(nproc) gd && \ | ||||
|     docker-php-ext-install bcmath &&\ | ||||
|     docker-php-ext-install pdo \ | ||||
|         mysqli pdo_mysql \ | ||||
|         intl mbstring \ | ||||
|         zip pcntl \ | ||||
|         exif opcache bz2 \ | ||||
|         calendar \ | ||||
|     && pear update-channels && pecl update-channels \ | ||||
|     && pecl install redis && docker-php-ext-enable redis \ | ||||
|     && rm -rf /tmp/pear \ | ||||
|     && docker-php-source delete \ | ||||
|     && apk del .phpize-deps \ | ||||
|     && rm -rf /var/cache/apk/* && rm -rf /etc/apk/cache \ | ||||
|     && rm -rf /usr/share/php && rm -rf /tmp/* \ | ||||
|     && rm "$PHP_INI_DIR/php.ini-development" \ | ||||
|     && mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \ | ||||
|     && mkdir -p /tmp/php/upload \ | ||||
|     && mkdir -p /tmp/php/sys \ | ||||
|     && mkdir -p /tmp/php/session \ | ||||
|     && chown -R www-data:www-data /tmp/php \ | ||||
|     && ln -sf /dev/stdout /var/log/unit.log \ | ||||
|     && addgroup -S unit && adduser -S unit -G unit \ | ||||
|     && chmod 755 /home/unit/docker-entrypoint.sh | ||||
|  | ||||
| FROM BUILD as APP_BUILD_FOR_PRODUCTION | ||||
| WORKDIR /home/app | ||||
| RUN apk --no-cache add git nodejs npm \ | ||||
|     && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ | ||||
|     && git clone https://git.kor-elf.net/kor-elf/service-captcha.git . \ | ||||
|     && composer install --optimize-autoloader --no-dev \ | ||||
|     && npm install && npm run build \ | ||||
|     && rm -rf /home/app/node_modules /home/app/.git /home/app/docker | ||||
|  | ||||
| # | ||||
| FROM BUILD AS PRODUCTION | ||||
|  | ||||
| COPY --from=APP_BUILD_FOR_PRODUCTION /home/app /var/www/html | ||||
|  | ||||
| WORKDIR /var/www/html | ||||
|  | ||||
| STOPSIGNAL SIGTERM | ||||
|  | ||||
| ENTRYPOINT ["/home/unit/docker-entrypoint.sh"] | ||||
| EXPOSE 9000 | ||||
| CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock", "--user", "unit", "--group", "unit"] | ||||
|  | ||||
|  | ||||
| # | ||||
| FROM BUILD AS DEVELOP | ||||
|  | ||||
| WORKDIR /var/www/html | ||||
|  | ||||
| STOPSIGNAL SIGTERM | ||||
|  | ||||
| RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer | ||||
|  | ||||
| ENTRYPOINT ["/home/unit/docker-entrypoint.sh"] | ||||
| EXPOSE 9000 | ||||
| CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock", "--user", "unit", "--group", "unit"] | ||||
							
								
								
									
										111
									
								
								docker/app/docker-entrypoint.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								docker/app/docker-entrypoint.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| #!/bin/sh | ||||
|  | ||||
| set -euo pipefail | ||||
|  | ||||
| WAITLOOPS=5 | ||||
| SLEEPSEC=1 | ||||
| unitd="unitd" | ||||
|  | ||||
| curl_put() | ||||
| { | ||||
|     RET=$(/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2) | ||||
|     RET_BODY=$(echo $RET | /bin/sed '$ s/...$//') | ||||
|     RET_STATUS=$(echo $RET | /usr/bin/tail -c 4) | ||||
|     if [ "$RET_STATUS" -ne "200" ]; then | ||||
|         echo "$0: Error: HTTP response status code is '$RET_STATUS'" | ||||
|         echo "$RET_BODY" | ||||
|         return 1 | ||||
|     else | ||||
|         echo "$0: OK: HTTP response status code is '$RET_STATUS'" | ||||
|         echo "$RET_BODY" | ||||
|     fi | ||||
|     return 0 | ||||
| } | ||||
|  | ||||
| if [ "$unitd" = "unitd" ] || [ "$unitd" = "unitd-debug" ]; then | ||||
|       echo "$0: Launching Unit daemon to perform initial configuration..." | ||||
|       /usr/sbin/$unitd --control unix:/var/run/control.unit.sock | ||||
|       for i in $(/usr/bin/seq $WAITLOOPS); do | ||||
|           if [ ! -S /var/run/control.unit.sock ]; then | ||||
|               echo "$0: Waiting for control socket to be created..." | ||||
|               /bin/sleep $SLEEPSEC | ||||
|           else | ||||
|               break | ||||
|           fi | ||||
|       done | ||||
|  | ||||
|       # even when the control socket exists, it does not mean unit has finished initialisation | ||||
|       # this curl call will get a reply once unit is fully launched | ||||
|       /usr/bin/curl -s -X GET --unix-socket /var/run/control.unit.sock http://localhost/ | ||||
|  | ||||
|       if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -print -quit 2>/dev/null | /bin/grep -q .; then | ||||
|           echo "$0: /docker-entrypoint.d/ is not empty, applying initial configuration..." | ||||
|  | ||||
|           echo "$0: Looking for certificate bundles in /docker-entrypoint.d/..." | ||||
|           for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.pem"); do | ||||
|               echo "$0: Uploading certificates bundle: $f" | ||||
|               curl_put $f "certificates/$(basename $f .pem)" | ||||
|           done | ||||
|  | ||||
|           echo "$0: Looking for JavaScript modules in /docker-entrypoint.d/..." | ||||
|           for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.js"); do | ||||
|               echo "$0: Uploading JavaScript module: $f" | ||||
|               curl_put $f "js_modules/$(basename $f .js)" | ||||
|           done | ||||
|  | ||||
|           echo "$0: Looking for configuration snippets in /docker-entrypoint.d/..." | ||||
|           for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.json"); do | ||||
|               echo "$0: Applying configuration $f"; | ||||
|               curl_put $f "config" | ||||
|           done | ||||
|  | ||||
|           if [ ! -z ${UNIT_SOURCE+x} ] | ||||
|           then | ||||
|               echo "[${UNIT_SOURCE}]" > /docker-entrypoint.d/unit_source.json | ||||
|               curl_put "/docker-entrypoint.d/unit_source.json" "config/listeners/*:9000/forwarded/source" | ||||
|           fi | ||||
|  | ||||
|           echo "$0: Looking for shell scripts in /docker-entrypoint.d/..." | ||||
|           for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -name "*.sh"); do | ||||
|               echo "$0: Launching $f"; | ||||
|               "$f" | ||||
|           done | ||||
|  | ||||
|           # warn on filetypes we don't know what to do with | ||||
|           for f in $(/usr/bin/find /docker-entrypoint.d/ -type f -not -name "*.sh" -not -name "*.json" -not -name "*.pem" -not -name "*.js"); do | ||||
|               echo "$0: Ignoring $f"; | ||||
|           done | ||||
|       fi | ||||
|  | ||||
|       echo "$0: Stopping Unit daemon after initial configuration..." | ||||
|       kill -TERM $(/bin/cat /var/run/unit.pid) | ||||
|  | ||||
|       for i in $(/usr/bin/seq $WAITLOOPS); do | ||||
|           if [ -S /var/run/control.unit.sock ]; then | ||||
|               echo "$0: Waiting for control socket to be removed..." | ||||
|               /bin/sleep $SLEEPSEC | ||||
|           else | ||||
|               break | ||||
|           fi | ||||
|       done | ||||
|       if [ -S /var/run/control.unit.sock ]; then | ||||
|           kill -KILL $(/bin/cat /var/run/unit.pid) | ||||
|           rm -f /var/run/control.unit.sock | ||||
|       fi | ||||
|  | ||||
|       echo | ||||
|       echo "$0: Unit initial configuration complete; ready for start up..." | ||||
|       echo | ||||
| fi | ||||
|  | ||||
| php artisan config:cache | ||||
| php artisan event:cache | ||||
| php artisan route:cache | ||||
| php artisan view:cache | ||||
| php artisan migrate --force | ||||
|  | ||||
| chown -R unit:unit /var/www/html | ||||
| chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache | ||||
| chmod -R 777 /var/www/html/storage /var/www/html/bootstrap/cache | ||||
|  | ||||
| exec "$@" | ||||
							
								
								
									
										69
									
								
								docker/app/unit-config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								docker/app/unit-config.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| { | ||||
|     "listeners": { | ||||
|         "*:9000": { | ||||
|             "pass": "routes", | ||||
|             "forwarded": { | ||||
|                 "client_ip": "X-Forwarded-For", | ||||
|                 "recursive": false, | ||||
|                 "source": [ | ||||
|  | ||||
|                 ] | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     "routes": [ | ||||
|         { | ||||
|             "match": { | ||||
|                 "uri": [ | ||||
|                     "/index.php/", | ||||
|                     "~^/index\\.php/.*", | ||||
|                     "~\\.php$" | ||||
|                 ] | ||||
|             }, | ||||
|             "action": { | ||||
|                 "return": 404 | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             "action": { | ||||
|                 "share": "/var/www/html/public$uri", | ||||
|                 "fallback": { | ||||
|                     "pass": "applications/laravel" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     ], | ||||
|  | ||||
|     "applications": { | ||||
|         "laravel": { | ||||
|             "type": "php", | ||||
|             "root": "/var/www/html/public", | ||||
|             "working_directory": "/var/www/html", | ||||
|             "user": "www-data", | ||||
|             "group": "www-data", | ||||
|             "script": "index.php", | ||||
|             "processes": { | ||||
|                 "max": 10, | ||||
|                 "spare": 5, | ||||
|                 "idle_timeout": 20 | ||||
|             }, | ||||
|             "options": { | ||||
|                 "file": "/usr/local/etc/php/php.ini", | ||||
|                 "admin": { | ||||
|                     "upload_tmp_dir": "/tmp/php/upload", | ||||
|                     "sys_temp_dir": "/tmp/php/sys", | ||||
|                     "session.save_path": "/tmp/php/session", | ||||
|                     "open_basedir": "/var/www/html:/tmp/php:.", | ||||
|                     "memory_limit": "256M", | ||||
|                     "upload_max_filesize": "20M", | ||||
|                     "post_max_size": "20M", | ||||
|                     "expose_php": "0" | ||||
|                 }, | ||||
|                 "user": { | ||||
|                     "display_errors": "0" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										3
									
								
								docker/nginx/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docker/nginx/Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| FROM nginx:alpine3.18-slim | ||||
|  | ||||
| COPY nginx.conf /etc/nginx/conf.d/default.conf | ||||
							
								
								
									
										36
									
								
								docker/nginx/nginx.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								docker/nginx/nginx.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| server { | ||||
|     listen 80 default_server; | ||||
|     listen [::]:80 default_server; | ||||
|     server_name captcha; | ||||
|  | ||||
|     client_max_body_size 1024M; | ||||
|  | ||||
|     root /var/www/html/public; | ||||
|  | ||||
|     location / { | ||||
|         location /api-docs { | ||||
|             proxy_pass http://swagger:8080; | ||||
|             proxy_set_header Host $http_host; | ||||
|             proxy_set_header X-Forwarded-Host $http_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_redirect off; | ||||
|             proxy_http_version 1.1; | ||||
|             proxy_set_header Upgrade $http_upgrade; | ||||
|         } | ||||
|         location / { | ||||
|             proxy_pass http://app:9000; | ||||
|             proxy_set_header Host $http_host; | ||||
|             proxy_set_header X-Forwarded-Host $http_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_redirect off; | ||||
|             proxy_http_version 1.1; | ||||
|             proxy_set_header Upgrade $http_upgrade; | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user