Last active
October 8, 2023 13:36
-
-
Save chris-cmsoft/d398cb87907db6f39341df53345d6925 to your computer and use it in GitHub Desktop.
TLDR - Laravel In Kubernetes Part 2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/vendor | |
/node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
version: '3' | |
services: | |
# We need to run the FPM container for our application | |
laravel.fpm: | |
build: | |
context: . | |
target: fpm_server | |
image: laravel-in-kubernetes/fpm_server | |
# We can override any env values here. | |
# By default the .env in the project root will be loaded as the environment for all containers | |
environment: | |
APP_DEBUG: "true" | |
# Mount the codebase, so any code changes we make will be propagated to the running application | |
volumes: | |
# Here we mount in our codebase so any changes are immediately reflected into the container | |
- '.:/opt/apps/laravel-in-kubernetes' | |
networks: | |
- laravel-in-kubernetes | |
# Run the web server container for static content, and proxying to our FPM container | |
laravel.web: | |
build: | |
context: . | |
target: web_server | |
image: laravel-in-kubernetes/web_server | |
# Expose our application port (80) through a port on our local machine (8080) | |
ports: | |
- '8080:80' | |
environment: | |
# We need to pass in the new FPM hst as the name of the fpm container on port 9000 | |
FPM_HOST: "laravel.fpm:9000" | |
# Mount the public directory into the container so we can serve any static files directly when they change | |
volumes: | |
# Here we mount in our codebase so any changes are immediately reflected into the container | |
- './public:/opt/apps/laravel-in-kubernetes/public' | |
networks: | |
- laravel-in-kubernetes | |
# Run the Laravel Scheduler | |
laravel.cron: | |
build: | |
context: . | |
target: cron | |
image: laravel-in-kubernetes/cron | |
# Here we mount in our codebase so any changes are immediately reflected into the container | |
volumes: | |
# Here we mount in our codebase so any changes are immediately reflected into the container | |
- '.:/opt/apps/laravel-in-kubernetes' | |
networks: | |
- laravel-in-kubernetes | |
# Run the frontend, and file watcher in a container, so any changes are immediately compiled and servable | |
laravel.frontend: | |
build: | |
context: . | |
target: frontend | |
# Override the default CMD, so we can watch changes to frontend files, and re-transpile them. | |
command: ["npm", "run", "watch"] | |
image: laravel-in-kubernetes/frontend | |
volumes: | |
# Here we mount in our codebase so any changes are immediately reflected into the container | |
- '.:/opt/apps/laravel-in-kubernetes' | |
# Add node_modeules as singular volume. | |
# This prevents our local node_modules from being propagated into the container, | |
# So the node_modules can be compiled for each of the different architectures (Local, Image) | |
- '/opt/app/node_modules/' | |
networks: | |
- laravel-in-kubernetes | |
mysql: | |
image: 'mysql:8.0' | |
ports: | |
- '${FORWARD_DB_PORT:-3306}:3306' | |
environment: | |
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' | |
MYSQL_DATABASE: '${DB_DATABASE}' | |
MYSQL_USER: '${DB_USERNAME}' | |
MYSQL_PASSWORD: '${DB_PASSWORD}' | |
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' | |
volumes: | |
- 'laravel-in-kubernetes-mysql:/var/lib/mysql' | |
networks: | |
- laravel-in-kubernetes | |
healthcheck: | |
test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"] | |
retries: 3 | |
timeout: 5s | |
networks: | |
laravel-in-kubernetes: | |
volumes: | |
laravel-in-kubernetes-mysql: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Create args for PHP extensions and PECL packages we need to install. | |
# This makes it easier if we want to install packages, | |
# as we have to install them in multiple places. | |
# This helps keep ou Dockerfiles DRY -> https://bit.ly/dry-code | |
# You can see a list of required extensions for Laravel here: https://laravel.com/docs/8.x/deployment#server-requirements | |
ARG PHP_EXTS="bcmath ctype fileinfo mbstring pdo pdo_mysql tokenizer dom pcntl" | |
ARG PHP_PECL_EXTS="redis" | |
# We need to build the Composer base to reuse packages we've installed | |
FROM composer:2.1 as composer_base | |
# We need to declare that we want to use the args in this build step | |
ARG PHP_EXTS | |
ARG PHP_PECL_EXTS | |
# First, create the application directory, and some auxilary directories for scripts and such | |
RUN mkdir -p /opt/apps/laravel-in-kubernetes /opt/apps/laravel-in-kubernetes/bin | |
# Next, set our working directory | |
WORKDIR /opt/apps/laravel-in-kubernetes | |
# We need to create a composer group and user, and create a home directory for it, so we keep the rest of our image safe, | |
# And not accidentally run malicious scripts | |
RUN addgroup -S composer \ | |
&& adduser -S composer -G composer \ | |
&& chown -R composer /opt/apps/laravel-in-kubernetes \ | |
&& apk add --virtual build-dependencies --no-cache ${PHPIZE_DEPS} openssl ca-certificates libxml2-dev oniguruma-dev \ | |
&& docker-php-ext-install -j$(nproc) ${PHP_EXTS} \ | |
&& pecl install ${PHP_PECL_EXTS} \ | |
&& docker-php-ext-enable ${PHP_PECL_EXTS} \ | |
&& apk del build-dependencies | |
# Next we want to switch over to the composer user before running installs. | |
# This is very important, so any extra scripts that composer wants to run, | |
# don't have access to the root filesystem. | |
# This especially important when installing packages from unverified sources. | |
USER composer | |
# Copy in our dependency files. | |
# We want to leave the rest of the code base out for now, | |
# so Docker can build a cache of this layer, | |
# and only rebuild when the dependencies of our application changes. | |
COPY --chown=composer composer.json composer.lock ./ | |
# Install all the dependencies without running any installation scripts. | |
# We skip scripts as the code base hasn't been copied in yet and script will likely fail, | |
# as `php artisan` available yet. | |
# This also helps us to cache previous runs and layers. | |
# As long as comoser.json and composer.lock doesn't change the install will be cached. | |
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist | |
# Copy in our actual source code so we can run the installation scripts we need | |
# At this point all the PHP packages have been installed, | |
# and all that is left to do, is to run any installation scripts which depends on the code base | |
COPY --chown=composer . . | |
# Now that the code base and packages are all available, | |
# we can run the install again, and let it run any install scripts. | |
RUN composer install --no-dev --prefer-dist | |
# For the frontend, we want to get all the Laravel files, | |
# and run a production compile | |
FROM node:14 as frontend | |
# We need to copy in the Laravel files to make everything is available to our frontend compilation | |
COPY --from=composer_base /opt/apps/laravel-in-kubernetes /opt/apps/laravel-in-kubernetes | |
WORKDIR /opt/apps/laravel-in-kubernetes | |
# We want to install all the NPM packages, | |
# and compile the MIX bundle for production | |
RUN npm install && \ | |
npm run prod | |
# For running things like migrations, and queue jobs, | |
# we need a CLI container. | |
# It contains all the Composer packages, | |
# and just the basic CLI "stuff" in order for us to run commands, | |
# be that queues, migrations, tinker etc. | |
FROM php:8.0-alpine as cli | |
# We need to declare that we want to use the args in this build step | |
ARG PHP_EXTS | |
ARG PHP_PECL_EXTS | |
WORKDIR /opt/apps/laravel-in-kubernetes | |
# We need to install some requirements into our image, | |
# used to compile our PHP extensions, as well as install all the extensions themselves. | |
# You can see a list of required extensions for Laravel here: https://laravel.com/docs/8.x/deployment#server-requirements | |
RUN apk add --virtual build-dependencies --no-cache ${PHPIZE_DEPS} openssl ca-certificates libxml2-dev oniguruma-dev && \ | |
docker-php-ext-install -j$(nproc) ${PHP_EXTS} && \ | |
pecl install ${PHP_PECL_EXTS} && \ | |
docker-php-ext-enable ${PHP_PECL_EXTS} && \ | |
apk del build-dependencies | |
# Next we have to copy in our code base from our initial build which we installed in the previous stage | |
COPY --from=composer_base /opt/apps/laravel-in-kubernetes /opt/apps/laravel-in-kubernetes | |
COPY --from=frontend /opt/apps/laravel-in-kubernetes/public /opt/apps/laravel-in-kubernetes/public | |
# We need a stage which contains FPM to actually run and process requests to our PHP application. | |
FROM php:8.0-fpm-alpine as fpm_server | |
# We need to declare that we want to use the args in this build step | |
ARG PHP_EXTS | |
ARG PHP_PECL_EXTS | |
WORKDIR /opt/apps/laravel-in-kubernetes | |
RUN apk add --virtual build-dependencies --no-cache ${PHPIZE_DEPS} openssl ca-certificates libxml2-dev oniguruma-dev && \ | |
docker-php-ext-install -j$(nproc) ${PHP_EXTS} && \ | |
pecl install ${PHP_PECL_EXTS} && \ | |
docker-php-ext-enable ${PHP_PECL_EXTS} && \ | |
apk del build-dependencies | |
# We have to copy in our code base from our initial build which we installed in the previous stage | |
COPY --from=composer_base --chown=www-data /opt/apps/laravel-in-kubernetes /opt/apps/laravel-in-kubernetes | |
COPY --from=frontend --chown=www-data /opt/apps/laravel-in-kubernetes/public /opt/apps/laravel-in-kubernetes/public | |
# We want to cache the event, routes, and views so we don't try to write them when we are in Kubernetes. | |
# Docker builds should be as immutable as possible, and this removes a lot of the writing of the live application. | |
RUN php artisan event:cache && \ | |
php artisan route:cache && \ | |
php artisan view:cache | |
# As FPM uses the www-data user when running our application, | |
# we need to make sure that we also use that user when starting up, | |
# so our user "owns" the application when running | |
USER www-data | |
# We need an nginx container which can pass requests to our FPM container, | |
# as well as serve any static content. | |
FROM nginx:1.20-alpine as web_server | |
WORKDIR /opt/apps/laravel-in-kubernetes | |
# We need to add our NGINX template to the container for startup, | |
# and configuration. | |
COPY nginx.conf.template /etc/nginx/templates/default.conf.template | |
# Copy in ONLY the public directory of our project. | |
# This is where all the static assets will live, which nginx will serve for us. | |
COPY --from=frontend /opt/apps/laravel-in-kubernetes/public /opt/apps/laravel-in-kubernetes/public | |
# We need a CRON container to the Laravel Scheduler. | |
# We'll start with the CLI container as our base, | |
# as we only need to override the CMD which the container starts with to point at cron | |
FROM cli as cron | |
WORKDIR /opt/apps/laravel-in-kubernetes | |
# We want to create a laravel.cron file with Laravel cron settings, which we can import into crontab, | |
# and run crond as the primary command in the forground | |
RUN touch laravel.cron && \ | |
echo "* * * * * cd /opt/apps/laravel-in-kubernetes && php artisan schedule:run" >> laravel.cron && \ | |
crontab laravel.cron | |
CMD ["crond", "-l", "2", "-f"] | |
FROM cli |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
server { | |
listen 80 default_server; | |
listen [::]:80 default_server; | |
# We need to set the root for our sevrer, | |
# so any static file requests gets loaded from the correct path | |
root /opt/apps/laravel-in-kubernetes/public; | |
index index.php index.html index.htm index.nginx-debian.html; | |
# _ makes sure that nginx does not try to map requests to a specific hostname | |
# This allows us to specify the urls to our application as infrastructure changes, | |
# without needing to change the application | |
server_name _; | |
# At the root location, | |
# we first check if there are any static files at the location, and serve those, | |
# If not, we check whether there is an indexable folder which can be served, | |
# Otherwise we forward the request to the PHP server | |
location / { | |
# Using try_files here is quite important as a security concideration | |
# to prevent injecting PHP code as static assets, | |
# and then executing them via a URL. | |
# See https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#passing-uncontrolled-requests-to-php | |
try_files $uri $uri/ /index.php?$query_string; | |
} | |
# Some static assets are loaded on every page load, | |
# and logging these turns into a lot of useless logs. | |
# If you would prefer to see these requests for catching 404's etc. | |
# Feel free to remove them | |
location = /favicon.ico { access_log off; log_not_found off; } | |
location = /robots.txt { access_log off; log_not_found off; } | |
# When a 404 is returned, we want to display our applications 404 page, | |
# so we redirect it to index.php to load the correct page | |
error_page 404 /index.php; | |
# Whenever we receive a PHP url, or our root location block gets to serving through fpm, | |
# we want to pass the request to FPM for processing | |
location ~ \.php$ { | |
#NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini | |
include fastcgi_params; | |
fastcgi_intercept_errors on; | |
fastcgi_pass ${FPM_HOST}; | |
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; | |
} | |
location ~ /\.ht { | |
deny all; | |
} | |
location ~ /\.(?!well-known).* { | |
deny all; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello!
Great tutorial first of all. It's really helping me solve a client's migration to Kubernetes.
I found that on the Dockerfile the PHP version is outdated and I was getting errors on the build. Can you change it from
php:8.0-fpm-alpine
tophp:8.1-fpm-alpine
. I also changed thephp:8.0-alpine
in the cli step locally to keep it the same version, although it wasn't returning any errors on that part.Also, on the web_server step, on the blog page, you indicate that the nginx template file should be created in a new folder named
docker
. But in the Dockerfile you attempt to copy from the root. So please changeCOPY nginx.conf.template /etc/nginx/templates/default.conf.template
toCOPY docker/nginx.conf.template /etc/nginx/templates/default.conf.template
.Cheers!