- Created:
- Updated:
Laravel Dockerfile
Dockerfile
# syntax=docker/dockerfile:1
FROM php:8.2-fpm AS fpm-base
RUN echo "Types: deb\n\
# http://snapshot.debian.org/archive/debian/20250203T000000Z\n\
URIs: https://mirrors.cloud.tencent.com/debian\n\
Suites: bookworm bookworm-updates\n\
Components: main\n\
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg\n\
\n\
Types: deb\n\
# http://snapshot.debian.org/archive/debian-security/20250203T000000Z\n\
URIs: https://mirrors.cloud.tencent.com/debian-security\n\
Suites: bookworm-security\n\
Components: main\n\
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg\n" > /etc/apt/sources.list.d/debian.sources
# see https://github.com/mlocati/docker-php-extension-installer
RUN --mount=type=cache,id=/bookworm/var/cache/apt,target=/var/cache/apt,sharing=locked \
--mount=type=bind,from=mlocati/php-extension-installer:latest,source=/usr/bin/install-php-extensions,target=/usr/local/bin/install-php-extensions \
install-php-extensions @composer bcmath gd mongodb pcntl sockets zip pdo_mysql opcache \
&& apt-get update && apt-get install -y cron vim iputils-ping default-mysql-client sqlite3 net-tools && rm -rf /var/lib/apt/lists/*
FROM fpm-base AS fpm-base-dev
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" && \
rm "$PHP_INI_DIR/conf.d/docker-php-ext-opcache.ini"
FROM fpm-base AS fpm-base-prod
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
FROM fpm-base-dev AS deps-dev
# 安装 composer 依赖 dev
WORKDIR /var/www/laravel
RUN --mount=type=bind,source=./composer.json,target=composer.json \
--mount=type=bind,source=./composer.lock,target=composer.lock \
--mount=type=cache,target=/root/.composer/cache,sharing=locked \
composer install --no-scripts --no-interaction
FROM fpm-base-prod AS deps-prod
# 安装 composer 依赖 no-dev
WORKDIR /var/www/laravel
RUN --mount=type=bind,source=./composer.json,target=composer.json \
--mount=type=bind,source=./composer.lock,target=composer.lock \
--mount=type=cache,target=/root/.composer/cache,sharing=locked \
composer install --no-scripts --no-interaction --no-dev
FROM fpm-base-dev AS fpm-dev-plate
# 开发中 plate,不安装 composer 依赖
WORKDIR /var/www/laravel
RUN echo "* * * * * root su www-data -s /bin/bash -c '/usr/local/bin/php /var/www/laravel/artisan schedule:run' > /proc/1/fd/1 2>/proc/1/fd/2" > /etc/cron.d/laravel \
&& chmod 644 /etc/cron.d/laravel
FROM fpm-base-dev AS fpm-dev
# 包含 dev 的镜像
WORKDIR /var/www/laravel
RUN echo "* * * * * root su www-data -s /bin/bash -c '/usr/local/bin/php /var/www/laravel/artisan schedule:run' > /proc/1/fd/1 2>/proc/1/fd/2" > /etc/cron.d/laravel \
&& chmod 644 /etc/cron.d/laravel
COPY --from=deps-dev /var/www/laravel/vendor/ /var/www/laravel/vendor
COPY . /var/www/laravel/
RUN chown www-data:www-data \
bootstrap/cache \
storage \
storage/app \
storage/app/public \
storage/framework \
storage/framework/cache \
storage/framework/cache/data \
storage/framework/sessions \
storage/framework/testing \
storage/framework/views \
storage/logs \
&& chmod 3777 \
bootstrap/cache \
storage \
storage/app \
storage/app/public \
storage/framework \
storage/framework/cache \
storage/framework/cache/data \
storage/framework/sessions \
storage/framework/testing \
storage/framework/views \
storage/logs \
&& chmod +x artisan
RUN composer dumpautoload --no-interaction
FROM fpm-base-prod AS fpm
# 正式部署的镜像
WORKDIR /var/www/laravel
RUN echo "* * * * * root su www-data -s /bin/bash -c '/usr/local/bin/php /var/www/laravel/artisan schedule:run' > /proc/1/fd/1 2>/proc/1/fd/2" > /etc/cron.d/laravel \
&& chmod 644 /etc/cron.d/laravel
COPY --from=deps-prod /var/www/laravel/vendor/ /var/www/laravel/vendor
COPY . /var/www/laravel/
RUN chown www-data:www-data \
bootstrap/cache \
storage \
storage/app \
storage/app/public \
storage/framework \
storage/framework/cache \
storage/framework/cache/data \
storage/framework/sessions \
storage/framework/testing \
storage/framework/views \
storage/logs \
&& chmod 3777 \
bootstrap/cache \
storage \
storage/app \
storage/app/public \
storage/framework \
storage/framework/cache \
storage/framework/cache/data \
storage/framework/sessions \
storage/framework/testing \
storage/framework/views \
storage/logs \
&& chmod +x artisan
RUN composer dumpautoload --no-interaction --no-dev -o --apcu
FROM nginx:latest AS nginx
# 静态资源镜像
WORKDIR /var/www/laravel
COPY ./public/ /var/www/laravel/public
Jenkinsfile
pipeline {
agent any
environment {
docker_registry = 'https://docker-registry.demo.cn/'
docker_credentials_id = 'docker-registry-login'
}
stages {
stage("Initialization") {
steps {
script {
// use name of the patchset as the build name
buildName "#${env.BUILD_NUMBER} ${tag}"
buildDescription "${description}"
}
}
}
stage('Docker Build Base') {
steps {
script {
sh "docker build --target fpm-base ."
sh "docker build --target deps-dev ."
sh "docker build --target deps-prod ."
}
}
}
stage('Docker Build') {
parallel {
stage('Docker Build Fpm Dev') {
steps {
script {
docker.withRegistry(docker_registry, docker_credentials_id) {
// tag 可能是标签也可能是分支名,获取 ‘/’ 后的部分作为版本名称
def version = "${tag}".split("/")[-1]
version = "${version}-dev"
def imageId = "fpm:${version}"
sh "docker build --target fpm-dev -t ${imageId} ."
def image = docker.image(imageId)
image.push(version)
}
}
}
}
stage('Docker Build Fpm') {
steps {
script {
docker.withRegistry(docker_registry, docker_credentials_id) {
// tag 可能是标签也可能是分支名,获取 ‘/’ 后的部分作为版本名称
def version = "${tag}".split("/")[-1]
def imageId = "fpm:${version}"
sh "docker build --target fpm -t ${imageId} ."
def image = docker.image(imageId)
image.push(version)
}
}
}
}
stage('Docker Build Nginx') {
steps {
script {
docker.withRegistry(docker_registry, docker_credentials_id) {
// tag 可能是标签也可能是分支名,获取 ‘/’ 后的部分作为版本名称
def version = "${tag}".split("/")[-1]
def imageId = "my-nginx:${version}"
sh "docker build --target nginx -t ${imageId} ."
def image = docker.image(imageId)
image.push(version)
}
}
}
}
}
}
}
}
Jenkins
参数化构建过程:
- git 参数 tag 默认 ${tag}
- 字符串参数 description
webhook trigger 参数:
- tag 默认 refs/heads/master
- description 默认 ${tag}
流水线:pipeline script from SCM
- Branches to build 指定分支:${tag}
docker compose
services:
fpm:
image: fpm:${MY_VERSION:-latest}
restart: unless-stopped
post_start:
- command: sh -c "php artisan config:cache && php artisan route:cache && php artisan event:cache"
networks:
- fpm
volumes:
- laravel-storage:/var/www/laravel/storage
- laravel-storage-app:/var/www/laravel/storage/app
- laravel-storage-logs:/var/www/laravel/storage/logs
schedule:
extends: fpm
command: [ 'cron', '-f' ]
horizon:
extends: fpm
command: [ 'php', 'artisan', 'horizon' ]
user: www-data:www-data
reverb:
extends: fpm
command: [ 'php', 'artisan', 'reverb:start' ]
user: www-data:www-data
nginx:
image: my-nginx:${MY_VERSION:-latest}
restart: unless-stopped
networks:
- nginx
- fpm
environment:
- NGINX_PORT=80
volumes:
- ./nginx.conf.templates:/etc/nginx/templates
- ./nginx.conf.d:/etc/nginx/conf.d
- ./nginx.log:/var/log/nginx
- laravel-storage-app:/var/www/laravel/storage/app
networks:
fpm:
nginx:
volumes:
laravel-storage:
laravel-storage-app:
laravel-storage-logs:
nginx conf
server {
listen 80;
listen [::]:80;
access_log /var/log/nginx/host.access.log main;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.html index.htm index.php;
charset utf-8;
absolute_redirect off;
root /var/www/laravel/public;
location / {
# 以 / 结尾的路径但不是目录,通过跳转去掉
location ~ ^.*/$ {
if (!-d $request_filename) {
rewrite ^(.*)/$ $1 permanent;
}
}
# 非静态资源,重写到转发 fpm
#try_files $uri $uri/ /index.php?$query_string;
try_files $uri $uri/ @fpm;
}
# storage 路径下全部为静态资源
location /storage/ {
alias /var/www/laravel/storage/app/public/;
expires 12h;
# 静态资源 php 禁止转发 fpm,全部视为文本
location ~ \.php$ {
types {
text/plain php;
}
}
}
# vue 根目录,路径重写到入口
#location /vue/ {
# try_files $uri $uri/ /vue/index.html;
#}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
# php 转发 fpm
location ~ \.php$ {
fastcgi_pass fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
@ php 转发 fpm
location @fpm {
fastcgi_pass fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root/index.php;
include fastcgi_params;
}
}