Laravel 및 모든 프론트엔드 개발 스택을 만드세요🐱🏍
목차
소개
The purpose of this guide is to make your application as easy as possible to self deploy on the developers side without them bothering with installing mysql, nginx, and a ton of stuff just to start doing what they do best.
요구 사항
Before we start, we're gonna need a few dependencies to get this project underway
Docker
Docker-Compose
Node/npm
To install Docker & Docker-Compose simply use the following commands
# Install Docker
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh
$ sudo groupadd docker
$ sudo usermod -aG docker $USER
$ newgrp docker
$ sudo rm -f get-docker.sh
# Install Docker-Compose
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
npm의 경우 사용 편의성을 위해 nvm을 사용하십시오.
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
$ export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
$ nvm install node
Laravel 백엔드 API 준비
Rather than go through all the hassle of installing php/composer and running the composer command to generate the laravel project you can simply use the following command to create the laravel project
curl -s https://laravel.build/folder-name | bash
Notice: Change folder-name in the command with any folder name you'd like
Lets delete the package.json file in the root directory since we are not gonna be using it
폴더 구조
add the missing folders from the following structure and leave them empty for now
.
├── tools
│ ├── dockerfiles
│ │ ├── ci
│ │ ├── dev-images
│ │ └── prod-images
│ └── scripts
│ │ ├── ci
│ │ ├── local
├── app
├── bootstrap
├── config
├── database
├── public
├── resources
├── routes
├── server
│ └── config
├── storage
├── tests
├── client
└── vendor
좋아하는 프런트 엔드 선택
Or as people say "Pick your poison"
Any of the big 3 will do fine here
lets go with vue.js, inside the root folder run the following command
$ npm install -g @vue/cli
$ vue create client # You can use `vue ui` as an alternative to the cli to generate an interface and guide you through the steps
now you're gonna be greeted with a lot of options to choose from, those are entirely up to you choose with the space bar
and I recommend having vuex router
notice: (At the current time of writing!) avoid choosing a unit/e2e/node-sass preprocessor with typescript as it can have some build problems / errors, simply install those after you finish compiling the project with npm like
$ npm install jest
in case the project is created with a repo simply do a
sudo rm -rf client/.git
도커 이미지 준비
Okay now for the fun part, lets start creating the images the application will use
- Backend image
in tools/dockerfiles/dev-images/
create backend.dockerfile
# ! Image
FROM php:8.0.6-fpm-alpine3.12
# ? Set Working Directory
WORKDIR /var/www/html
# ? Install Mysql Extensions
RUN docker-php-ext-install pdo pdo_mysql
# ? Install and enable PHP Redis extension
# ? Redis extension is not provided with the PHP Source
# ? pecl install will download and compile redis
# ? docker-php-ext-enable will enable it
# ? finally apk del to maintain a small image size
RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS \
&& pecl install redis-5.3.4 \
&& docker-php-ext-enable redis \
&& apk del .build-deps
# ? Install & Configure gd
RUN apk add --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev && \
docker-php-ext-configure gd \
--with-freetype \
--with-jpeg \
NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) && \
docker-php-ext-install -j$(nproc) gd && \
apk del --no-cache freetype-dev libpng-dev libjpeg-turbo-dev
# ? Install git (required by Composer)
RUN apk add git
# *** Install Composer
RUN php -r "readfile('http://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer
- Client image
in tools/dockerfiles/dev-images/
create client.dockerfile
# ! Image
FROM node:14-alpine
# ? Set Working Directory
WORKDIR /var/www/client
# * Install PM2 to serve the app
RUN npm install pm2 -g
# ? Serve the application on start
CMD command pm2 serve ./dist/ 8080 --spa --watch && pm2 log
those are the images we'll need for now
PHP 및 php-fpm 구성
whether or not you want to change something in php.ini
or www.conf
you should at least have them exposed in case you wanna edit them later
we'll do that by creating them and linking them inside the containers
in server/config
create php.ini
& www.conf
files and paste the following gist content accordingly
nginx 구성
We are going to need a self signed certificate, inside the project's root directory run the following command
$ sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./server/key.pem -out ./server/cert.pem
notice: skip all the prompts with enter
once generated, lets open up key.pem
permissions so that we can add it to the repo
sudo chmod 644 server/key.pem
for nginx config, create a default.conf
file inside the server folder and paste the following content
server
{
# Listen To HTTPS port
listen 443 ssl http2;
listen [::]:443 ssl http2;
# Define Domain Name
server_name localhost;
server_tokens off;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src https: wss:" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Index Fallback
index index.html index.htm index.php;
# Default Charset
charset utf-8;
# Redirect Everything to Front-end
location / {
proxy_pass http://client:8080/;
proxy_redirect off;
proxy_set_header Host $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-Host $server_name;
}
# Redirect everything after /api/ to Backend
location /api/ {
try_files $uri $uri/ public/index.php?$query_string;
}
# Handle PHP Files
location ~ \.php$ {
fastcgi_pass backend:9000;
fastcgi_index index.php;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_hide_header X-Powered-By;
include fastcgi_params;
}
###################### Content #########################
# Deny Access to Files
location ~ /\.(?!well-known).* {
deny all;
}
# robots.txt
location = /robots.txt {
log_not_found off;
access_log off;
}
######################## Compression ####################
# Add global gzip compression to all other files
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types
application/atom+xml
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/css
text/plain
text/vcard
text/vnd.rim.location.xloc
text/vtt
text/x-component
text/x-cross-domain-policy
application/octet-stream;
######################## SSL ###########################
# SSL Certificates
ssl_certificate conf.d/cert.pem;
ssl_certificate_key conf.d/key.pem;
# SSL Config
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Mozilla Intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
}
# Redirect HTTP to HTTPs
server
{
listen 80;
listen [::]:80;
server_name localhost;
location / {
return 301 https://localhost$request_uri;
}
}
.env 청사진으로
Before we proceed any further we must adapt the mentality of having our .env file as the true source of configurations, or at least the ones we want to mess with
This will be reflected in the next steps and emphasized upon
모든 것을 연결
first of all, we need to stop our front-end dev server from intercepting anything after /api/ and redirecting the traffic to nginx
we can do that by modifying the vue.config.js
file or the file that houses your webpack.config
, if the file doesn't exist simply create it yourself
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
const plugins = [];
plugins.push(new BundleAnalyzerPlugin({ analyzerMode: "disabled" }));
module.exports = {
devServer: {
proxy: {
"^/api": {
target: "https://webserver/",
ws: true,
secure: false
},
},
port: 8081
},
configureWebpack: {
plugins
},
};
next we are going to need to add some variables to our .env.example
file to use in our docker-compose file and other stuff
# Define Application Specific Keys
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
# Log Channel Variables
LOG_CHANNEL=stack
LOG_LEVEL=debug
# Database Variables
DB_CONNECTION=mysql
DB_HOST=database
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=someuser
DB_PASSWORD="lapassord@100"
DB_ROOT_PASSWORD="password@9000"
# Nginx Variables
HTTP_PORT=80:80
HTTPS_PORT=443:443
# BroadCast, Queue, Session Variables
BROADCAST_DRIVER=log
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
# Cache Variables
CACHE_DRIVER=redis
REDIS_HOST=cache
REDIS_PASSWORD=null
REDIS_PORT=6379
# Mail Variables
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
# Pusher Server Variables
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
create the docker-compose.yml
file in the root directory and paste in the following content
# ! Docker-Compose Specification
version: "3.8"
# ! Define Project Network
networks:
StackName:
# ? App Stack
services:
# ? Bring up Nginx After Backend and Link Volumes
webserver:
image: nginx:1.19.6-alpine
container_name: webserver
restart: unless-stopped
ports:
- "${HTTP_PORT}"
- "${HTTPS_PORT}"
volumes:
- ./server/:/etc/nginx/conf.d
networks:
- StackName
# ? Bring up Mysql and configure the Database
database:
image: mysql:8.0.21
container_name: database
restart: unless-stopped
tty: true
expose:
- "3306"
environment:
MYSQL_DATABASE: "${DB_DATABASE}"
MYSQL_USER: "${DB_USERNAME}"
MYSQL_PASSWORD: "${DB_PASSWORD}"
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
SERVICE_TAG: dev
SERVICE_NAME: database
volumes:
- ./storage/database:/var/lib/mysql
networks:
- StackName
# ? Build, Bring up Backend Container
backend:
build:
context: .
dockerfile: tools/dockerfiles/dev-images/backend.dockerfile
container_name: backend
volumes:
- ./:/var/www/html
- ./server/config/php.ini:/usr/local/etc/php/php.ini
- ./server/config/www.conf:/usr/local/etc/php-fpm.d/www.conf
expose:
- "9000"
depends_on:
- database
- cache
networks:
- StackName
# ? Bring Up Client UI
client:
build:
context: .
dockerfile: tools/dockerfiles/dev-images/client.dockerfile
container_name: client
volumes:
- ./client/:/var/www/client
expose:
- "8080"
networks:
- StackName
# ? Bring Up Caching Layer
cache:
image: redis:6.0.9
container_name: cache
expose:
- "6379"
networks:
- StackName
# * Helper Container for Serving the Front-end
npm:
image: node:14
container_name: npm
volumes:
- ./client:/var/www/client
working_dir: /var/www/client
entrypoint: ["npm"]
networks:
- StackName
Change all instances of StackName
with your application name
초기화 스크립트 만들기
Finally we get to the whole point of this guide, having the user start everything by running a single script which is what we are gonna do here
create init.sh
file in the root directory of the project, run the following command from the terminal to give it execute permissions
$ chmod +x init.sh
paste in the following content
#!/bin/bash
#?###################################################################################################
#? #
#? Output Helper Methods #
#? #
#?###################################################################################################
trap "exit" INT
function blue_text_box()
{
echo " "
local s="$*"
tput setaf 3
echo " -${s//?/-}-
| ${s//?/ } |
| $(tput setaf 4)$s$(tput setaf 3) |
| ${s//?/ } |
-${s//?/-}-"
tput sgr 0
echo " "
}
function red_text_box()
{
echo " "
local s="$*"
tput setaf 3
echo " -${s//?/-}-
| ${s//?/ } |
| $(tput setaf 1)$s$(tput setaf 3) |
| ${s//?/ } |
-${s//?/-}-"
tput sgr 0
echo " "
}
function green_text_box()
{
echo " "
local s="$*"
tput setaf 3
echo " -${s//?/-}-
| ${s//?/ } |
| $(tput setaf 2)$s$(tput setaf 3) |
| ${s//?/ } |
-${s//?/-}-"
tput sgr 0
echo " "
}
#!###################################################################################################
#! #
#! Script Start #
#! #
#!###################################################################################################
# ! Add .env file
cp .env.example .env
# ! bring down any service instance if it exists
red_text_box 'Removing Old Stack if It Exists'
docker-compose down
# ? Change Permissions for Artisan
chmod +x artisan
# ? Remove everything in the storage/database & bootstrap/cache directory
sudo rm -rf storage/database/*
sudo rm -rf bootstrap/cache/*.php
# TODO: Start & Build Container Stack
blue_text_box 'Rebuilding the docker images & Starting them'
docker-compose up -d --build
# * Install Laravel Dependencies
green_text_box 'Installing Laravel Dependencies'
docker exec -i backend composer install
# * Install Front-end Dependencies & Build
green_text_box 'Installing Dependencies & Build for User UI'
docker exec -i client npm i
docker exec -i client npm run build
# ! Generate Key & Caching/Optimizing Config
red_text_box 'Generating Laravel'
docker exec -i backend php artisan key:generate
# ! Migrate and Generate Passport Encryption Key
red_text_box 'Migrating & Seeding'
sleep 10
docker exec -i backend composer dump-autoload
docker exec -i backend php artisan migrate:fresh --seed
docker exec -i backend chmod o+w ./storage/ -R
CLI 생성
Lets make interacting the stack easy for developers by creating a simple psuedo-cli
in our tools/scripts/local
lets create a cli.sh
file and run
$ chmod +x cli.sh
paste in the following content or modify the naming scheme to fit your needs by modifying "stack helper instances" and/or changing the container names
#!/bin/bash
#?###################################################################################################
#? #
#? Output Helper Methods #
#? #
#?###################################################################################################
# ! little helpers for terminal print control and key input
select_option (){
ESC=$(printf '%b' "\033")
cursor_blink_on() {
printf '%s' "$ESC[?25h"
}
cursor_blink_off() {
printf '%s' "$ESC[?25l"
}
cursor_to() {
printf '%s' "$ESC[$1;${2:-1}H"
}
print_option() {
printf ' %s ' "$1"
}
print_selected() {
printf ' %s' "$ESC[7m $1 $ESC[27m"
}
get_cursor_row() {
IFS=';' read -sdR -p $'\E[6n' ROW COL; printf '%s' ${ROW#*[}
}
key_input() {
read -s -n3 key 2>/dev/null >&2
if [[ $key = $ESC[A ]]; then
echo up
fi
if [[ $key = $ESC[B ]]; then
echo down
fi
if [[ $key = "" ]]; then
echo enter
fi
}
# initially print empty new lines (scroll down if at bottom of screen)
for opt; do
printf "\n"
done
# determine current screen position for overwriting the options
local lastrow=$(get_cursor_row)
local startrow=$(($lastrow - $#))
# ensure cursor and input echoing back on upon a ctrl+c during read -s
trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
cursor_blink_off
local selected=0
while true; do
# print options by overwriting the last lines
local idx=0
for opt; do
cursor_to $((startrow + idx))
if [[ $idx == $selected ]]; then
print_selected "$opt"
else
print_option "$opt"
fi
((idx++))
done
# user key control
case $(key_input) in
enter) break;;
up) ((selected--));
if (( $selected < 0 )); then selected=$(($# - 1)); fi;;
down) ((selected++));
if (( selected > $# )); then selected=0; fi;;
esac
done
# cursor position back to normal
cursor_to $lastrow
printf "\n"
cursor_blink_on
return "$selected"
}
#!###################################################################################################
#! #
#! Script Start #
#! #
#!###################################################################################################
# Print Instructions
printf '\n> %s\n\n' "$(tput setaf 3)Please Choose your Shell$(tput sgr 0)":
# Options
options=("Bash" "Zsh" "IDK")
select_option "${options[@]}"
choice=$?
index=$choice
value=${options[$choice]}
case $value in
Bash) ## User selected Bash
shellrc=".bashrc"
;;
Zsh) ## User Selected zsh
shellrc=".zshrc"
;;
IDK) ## User doesn't know
shellrc=".bashrc"
;;
esac
####################################################################################################
# ? Remove Prexisting CLI Script
sed -n -i '1,/# StackHelper CLI START/p;/# StackHelper CLI END/,$p' $HOME/$shellrc
sed -i '/# StackHelper CLI START/d' $HOME/$shellrc
sed -i '/# StackHelper CLI END/d' $HOME/$shellrc
####################################################################################################
# ? Echo the CLI in the user's .shellrc
echo '
# StackHelper CLI START
# This function serves to integrate
# the dependency-less CLI for
# Interactive Events Platform
shelper() {
if [[ $@ == "build" ]]; then
command docker exec -i client npm run build
elif [[ $@ == "serve" ]]; then
command docker-compose run --rm -p "8081:8081" npm run serve
elif [[ $@ == "install client" || $@ == "i client" ]]; then
command docker exec -i client npm install
elif [[ $@ == "install api" || $@ == "i api" ]]; then
command docker exec -i backend composer install &&
command docker exec -i backend composer dump-autoload &&
command docker exec -i backend php artisan key:generate &&
command docker exec -i backend php artisan migrate:fresh
elif [[ $@ == "api migrate" ]]; then
command docker exec -i backend php artisan migrate
elif [[ $@ == "api seed" ]]; then
command docker exec -i backend php artisan db:seed
elif [[ $@ == "api ms" ]]; then
command docker exec -i backend php artisan migrate:fresh --seed
elif [[ $@ == "api refresh" ]]; then
command docker exec -i backend composer dump-autoload &&
command docker exec -i backend php artisan migrate:fresh --seed
elif [[ $@ == "stack refresh" || $@ == "stack r" ]]; then
command docker-compose down && command docker-compose up -d
elif [[ $@ == "-h" || $@ == "--help" ]]; then
echo "
$(tput setaf 3)Stack Helper CLI$(tput sgr 0)
$(tput setaf 3)Usage:$(tput sgr 0)
shelper [options] [arguments]
$(tput setaf 3)Options:$(tput sgr 0)
-h, --help Displays this help page
$(tput setaf 3)Arguments:$(tput sgr 0)
$(tput setaf 2)build$(tput sgr 0) Builds the Front-end
$(tput setaf 1)serve$(tput sgr 0) Serves the front-end through port $(tput setaf 5)8081$(tput sgr 0)
$(tput setaf 3)install client$(tput sgr 0) Installs the npm Dependencies Shorthand -> $(tput setaf 5)i ui$(tput sgr 0)
$(tput setaf 3)install api$(tput sgr 0) Installs Laravel Dependencies, Generate Keys & Migrate DB Shorthand -> $(tput setaf 5)i api$(tput sgr 0)
$(tput setaf 4)api migrate$(tput sgr 0) Pushes Laravel Migrations to DB
$(tput setaf 4)api seed$(tput sgr 0) Seed the Database
$(tput setaf 4)api ms$(tput sgr 0) Migrate & Seed
$(tput setaf 4)api refresh$(tput sgr 0) Composer dump-autoload, Migrate & Seed
$(tput setaf 6)stack refresh$(tput sgr 0) Refresh the docker stack down/up Shorthand -> $(tput setaf 5)stack r$(tput sgr 0)
"
else
echo "
$(tput setaf 1)Please Input a Valid Argument$(tput sgr 0)
OR
Check the Proper Syntax with
$(tput setaf 3)$ shelper -h$(tput sgr 0)
"
fi
}
# For Tab-Completion (Basic/Rudimentary)
_shelper_completions()
{
COMPREPLY+=("build")
COMPREPLY+=("serve")
COMPREPLY+=("install")
COMPREPLY+=("api")
COMPREPLY+=("stack")
}
complete -F _shelper_completions shelper
# StackHelper CLI END
' >> $HOME/$shellrc
Now lets add our cli script execution to the init.sh
script by adding the following lines to the script file
# ! Install CLI
red_text_box 'Installing CLI'
./tools/scripts/local/cli.sh
note: after installing the pseudo cli you'll need to refresh your shell instance with something like
$ bash
$ zsh
리포지토리 만들기
At this point we're technically done lets create the repo and push it to your favorite repo hosting service (hub,lab,bucket...whatever) run the following command in the project's root directory
$ git init
$ git add .
$ git commit -m "First commit or whatever commit message you want"
$ git remote add origin <REMOTE_URL>
$ git push origin <main/master/whatever>
now anyone clones the project, simply runs
$ ./init
and he's up and running
향후 계획
With your request I can add a couple of follow up guides that cover the following
- Creating a CI/CD Pipeline for the Project
- Building Production Images
- Deploying Production Builds
so let me know if you'd like that
TL;DR
We've successfully create an easy to use development stack that starts up by simply cloning and running ./init.sh
, a small pseudo helper cli that helps developers interact with the with the containers
you can find it here
프로세스에서 오타나 문제를 발견하거나 마음에 들지 않는 부분에 대한 의견이 있으면 알려주시면 수정하거나 개선할 수 있습니다.
Reference
이 문제에 관하여(Laravel 및 모든 프론트엔드 개발 스택을 만드세요🐱🏍), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/aeweda/create-a-laravel-any-front-end-development-stack-3i53텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)