Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Laravel docker improvements #1143

Merged
merged 13 commits into from
Aug 11, 2022
Merged
62 changes: 61 additions & 1 deletion scanner/laravel.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@ package scanner

import (
"encoding/base64"

"fmt"
"github.com/superfly/flyctl/helpers"
"os/exec"
"regexp"
"strconv"
)

type ComposerLock struct {
Platform PhpVersion `json:"platform,omitempty"`
}

type PhpVersion struct {
Version string `json:"php"`
}

// setup Laravel with a sqlite database
func configureLaravel(sourceDir string) (*SourceInfo, error) {
// Laravel projects contain the `artisan` command
Expand Down Expand Up @@ -51,5 +62,54 @@ func configureLaravel(sourceDir string) (*SourceInfo, error) {
SkipDatabase: true,
}

phpVersion, err := extractPhpVersion()

if err != nil || phpVersion == "" {
// Fallback to 8.0, which has
// the broadest compatibility
phpVersion = "8.0"
}

s.BuildArgs = map[string]string{
"PHP_VERSION": phpVersion,
"NODE_VERSION": "14",
}

return s, nil
}

func extractPhpVersion() (string, error) {
/* Example Output:
PHP 8.1.8 (cli) (built: Jul 8 2022 10:58:31) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.8, Copyright (c) Zend Technologies
with Zend OPcache v8.1.8, Copyright (c), by Zend Technologies
*/
cmd := exec.Command("php", "-v")
out, err := cmd.CombinedOutput()

if err != nil {
return "", err
}

// Capture major/minor version (leaving out revision version)
re := regexp.MustCompile(`PHP ([0-9]+\.[0-9]+)\.[0-9]`)
match := re.FindStringSubmatch(string(out))

if len(match) > 1 {
// If the PHP version is below 7.4, we won't have a
// container for it, so we'll use PHP 7.4
if match[1][0:1] == "7" {
vers, err := strconv.ParseFloat(match[1], 32)
if err != nil {
return "7.4", nil
}
if vers < 7.4 {
return "7.4", nil
}
}
return match[1], nil
}

return "", fmt.Errorf("could not find php version")
}
1 change: 1 addition & 0 deletions scanner/templates/laravel/common/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ storage/logs/*
*.env*
.rr.yml
rr
vendor

# 2. Ignore common files/directories we don't need
.vscode
Expand Down
113 changes: 49 additions & 64 deletions scanner/templates/laravel/common/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,74 +1,50 @@
# syntax = docker/dockerfile:experimental

# The "base" container installs PHP, etc
FROM alpine:3.16 as base
# Default to PHP 8.1, but we attempt to match
# the PHP version from the user (wherever `flyctl launch` is run)
# Valid version values are PHP 7.4+
ARG PHP_VERSION=8.1
ARG NODE_VERSION=14
FROM serversideup/php:${PHP_VERSION}-fpm-nginx as base

LABEL fly_launch_runtime="laravel"

RUN apk update \
&& apk add curl zip unzip tzdata supervisor nginx htop vim ca-certificates rsync \
php8 php8-cli php8-pecl-mcrypt \
php8-soap php8-openssl php8-gmp \
php8-pdo_odbc php8-json php8-dom \
php8-pdo php8-zip php8-pdo_mysql \
php8-sqlite3 php8-pdo_pgsql php8-bcmath \
php8-gd php8-odbc php8-pdo_sqlite \
php8-gettext php8-xmlreader php8-bz2 \
php8-iconv php8-pdo_dblib php8-curl \
php8-ctype php8-phar php8-xml \
php8-common php8-mbstring php8-tokenizer \
php8-xmlwriter php8-fileinfo php8-opcache \
php8-simplexml php8-pecl-redis php8-sockets \
php8-pcntl php8-posix php8-pecl-swoole \
php8-fpm \
&& ln -sf /usr/bin/php8 /usr/bin/php \
&& cp /etc/nginx/nginx.conf /etc/nginx/nginx.old.conf \
&& rm -rf /etc/nginx/http.d/default.conf \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& adduser -D -u 1000 -g 'app' app \
&& addgroup nginx app \
&& mkdir -p /var/run/php \
&& chown -R app:app /var/run/php \
&& mkdir -p /var/www/html
RUN apt-get update && apt-get install -y \
git curl zip unzip rsync ca-certificates vim htop cron \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \

WORKDIR /var/www/html
# copy application code, skipping files based on .dockerignore
COPY . /var/www/html

# Install dependencies, configure server
# For the time being, we run "composer update" as best effort to get php 8.0 working
RUN composer update \
&& composer install --optimize-autoloader --no-dev \
RUN composer install --optimize-autoloader --no-dev \
&& mkdir -p storage/logs \
&& chown -R app:app /var/www/html \
&& echo "* * * * * /usr/bin/php /var/www/html/artisan schedule:run" > /etc/crontabs/app \
&& mv docker/supervisor.conf /etc/supervisord.conf \
&& mv docker/nginx.conf /etc/nginx/nginx.conf \
&& mv docker/server.conf /etc/nginx/server.conf \
&& mv docker/php.ini /etc/php8/conf.d/php.ini \
&& sed -i 's/protected \$proxies/protected \$proxies = "*"/g' app/Http/Middleware/TrustProxies.php

# If we're not using Octane, configure php-fpm
RUN if ! grep -Fq "laravel/octane" /var/www/html/composer.json; then \
rm -rf /etc/php8/php-fpm.conf; \
rm -rf /etc/php8/php-fpm.d/www.conf; \
mv docker/php-fpm.conf /etc/php8/php-fpm.conf; \
mv docker/app.conf /etc/php8/php-fpm.d/app.conf; \
elif grep -Fq "spiral/roadrunner" /var/www/html/composer.json; then \
if [ -f ./vendor/bin/rr ]; then ./vendor/bin/rr get-binary; fi; \
rm -f .rr.yaml; \
fi

# clear Laravel cache that may be left over
RUN composer dump-autoload \
&& php artisan optimize:clear \
&& chmod -R ug+w /var/www/html/storage \
&& chmod -R 755 /var/www/html

&& chown -R webuser:webgroup /var/www/html \
&& sed -i 's/protected \$proxies/protected \$proxies = "*"/g' app/Http/Middleware/TrustProxies.php \
&& echo "MAILTO=\"\"\n* * * * * webuser /usr/bin/php /var/www/html/artisan schedule:run" > /etc/cron.d/laravel \
&& rm -rf /etc/cont-init.d/* \
&& cp docker/nginx-websockets.conf /etc/nginx/conf.d/websockets.conf \
&& cp docker/nginx-default /etc/nginx/sites-available/default \
&& cp docker/entrypoint.sh /entrypoint \
&& chmod +x /entrypoint

# If we're using Octane...
RUN if grep -Fq "laravel/octane" /var/www/html/composer.json; then \
rm -rf /etc/services.d/php-fpm; \
if grep -Fq "spiral/roadrunner" /var/www/html/composer.json; then \
mv docker/octane-rr /etc/services.d/octane; \
if [ -f ./vendor/bin/rr ]; then ./vendor/bin/rr get-binary; fi; \
rm -f .rr.yaml; \
else \
mv docker/octane-swoole /etc/services.d/octane; \
fi \
fi

# Multi-stage build: Build static assets
# This allows us to not include Node within the final container
FROM node:14 as node_modules_go_brrr
FROM node:${NODE_VERSION} as node_modules_go_brrr

RUN mkdir /app

Expand All @@ -79,27 +55,36 @@ COPY . .
# Use yarn or npm depending on what type of
# lock file we might find. Defaults to
# NPM if no lock file is found.
RUN if [ -f "yarn.lock" ]; then \
yarn install; \
# Note: We run "production" for Mix and "build" for Vite
RUN if [ -f "vite.config.js" ]; then \
ASSET_CMD="build"; \
else \
ASSET_CMD="production"; \
fi; \
if [ -f "yarn.lock" ]; then \
yarn install --frozen-lockfile; \
yarn $ASSET_CMD; \
elif [ -f "package-lock.json" ]; then \
npm ci --no-audit; \
npm run $ASSET_CMD; \
else \
npm install; \
fi
npm run $ASSET_CMD; \
fi;

# From our base container created above, we
# create our image, adding in static assets
# generated above
# create our final image, adding in static
# assets that we generated above
FROM base

# Packages like Laravel Nova may have added assets to the public directory
# or maybe some custom assets were added manually! Either way, we merge
# in the assets we generated above rather than overwrite them
COPY --from=node_modules_go_brrr /app/public /var/www/html/public-npm
RUN rsync -ar /var/www/html/public-npm/ /var/www/html/public/ \
&& rm -rf /var/www/html/public-npm
&& rm -rf /var/www/html/public-npm \
&& chown -R webuser:webgroup /var/www/html/public

# The same port nginx.conf is set to listen on and fly.toml references (standard is 8080)
EXPOSE 8080

ENTRYPOINT ["/var/www/html/docker/run.sh"]
ENTRYPOINT ["/entrypoint"]
16 changes: 16 additions & 0 deletions scanner/templates/laravel/common/docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env sh

if [ $# -gt 0 ];then
# If we passed a command, run it as root
exec "$@"
else
# Otherwise start the web server

## Prepare Laravel caches
/usr/bin/php /var/www/html/artisan config:cache --no-ansi -q
/usr/bin/php /var/www/html/artisan route:cache --no-ansi -q
/usr/bin/php /var/www/html/artisan view:cache --no-ansi -q
chown -R webuser:webgroup /var/www/html

exec /init
fi
5 changes: 5 additions & 0 deletions scanner/templates/laravel/common/docker/nginx-websockets.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# See https://laravel.com/docs/9.x/octane#reloading-the-workers
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
74 changes: 0 additions & 74 deletions scanner/templates/laravel/common/docker/nginx.conf

This file was deleted.

24 changes: 0 additions & 24 deletions scanner/templates/laravel/common/docker/php.ini

This file was deleted.

27 changes: 0 additions & 27 deletions scanner/templates/laravel/common/docker/run.sh

This file was deleted.

Loading