Why pay for server management when you can use this…
Ansible playbooks for setting up a LEMP stack with Redis for Laravel.
- Local development environment with Vagrant
- High-performance production servers
- One-command provisioning
- One-command deployments
Anstead is a fork of Trellis but for Laravel instead of WordPress.
I've used it in production for over a year however it's only ever had one person use it.
I'm uploading it to show how I changed Trellis to work with Laravel.
I would love to have it merged back into Trellis 💚
Major changes from Trellis:
- Laravel apps instead of WordPress sites
- separate server for the database
- adds Redis
- splits out the web user to a new role as it's only used on the app server
- splits out the MariaDB and app setup roles into client/code and server/database roles
- adds bash aliases to the Vagrant box
- re-adds the
provision.sh
script - handlers are now in their own role that other roles have as a dependency - this is because handlers are shared between roles but not shared between plays and this gets around that
- other than the MariaDB PPA it hasn't been synced with Trellis for a while
TODO:
- rollbacks
- xdebug
- laravel.log file still stored in storage and not managed by logrotate
An example project is available at https://github.com/ptibbetts/anstead-example-project
This playbook configures the following and more:
- Ubuntu 16.04 Xenial LTS
- Nginx
- PHP 7.1
- MariaDB
- Redis
- Supervisor
- SSL support (scores an A+ on the Qualys SSL Labs Test)
- Let's Encrypt integration for free SSL certificates
- HTTP/2 support (requires SSL)
- Composer
- sSMTP (mail delivery)
- MailHog
- Fail2ban
- ferm
Ensure all dependencies have been installed before moving on:
- Ansible 2.0.2
- Virtualbox >= 4.3.10
- Vagrant >= 1.5.4
- vagrant-bindfs >= 0.3.1
- vagrant-hostmanager
# → Root folder
├── anstead/ # → Ansible / Vagrant folder
└── laravel/ # → Laravel app
All Ansible commands should be ran from the /anstead
directory
cd anstead
This playbook has been designed to have three servers in production:
- a web server consisting of application code, PHP & Nginx
- a database server with MariaDB installed
- a separate server for Redis
however it is possible to use only the one server, which is how the staging and development environments are setup.
Vagrant calls the Ansible script to provision a virtual machine for local development.
vagrant up # start Vagrant
cd /srv/www/example.com/current # all Artisan & Composer commands to be ran here
The Vagrant box comes with the following bash aliases:
art
is an alias ofphp artisan
tinker
is an alias ofphp artisan tinker
phpspec
is an alias of/vendor/bin/phpspec
phpunit
is an alias of/vendor/bin/phpunit
jsonprettify
is an alias ofpython -mjson.tool
You can customise these aliases and add your own by editing the /aliases
file.
At least one base Ubuntu 16.04
server is required for setting up remote servers.
This is how the staging environment is setup by default.
To change the production environment you'll need to add the IP address of the server to the /hosts/production
file under [production]
and each of the services.
This is the how the production environment is setup by default but you will still need to configure it to get it to work.
You must make sure each server has private networking (internal IP addresses) enabled so that communication between the servers is both faster and cheaper - you don't usually have to pay for bandwidth on the internal network.
You need to add the domain of the web server and the external IP addresses of the database and Redis servers to the /hosts/production
file. Then you need to add the private/internal IP address of each server under the env
section in the /group_vars/production/laravel_apps.yml
file.
This is so that you can connect to the servers with Ansible using the external IP but each server can communicate with each other using the internal network.
The database and Redis servers, by default, will only open the SSH port. If you separate the services to their own server then they will also open the port for their respective service.
You also need to SSH into each server once as Ansible seems to struggle accepting multiple new servers at the same time - this would be a good time to run apt update && apt upgrade -y
before provisioning them.
Ensure hosts/{staging,production}
contains the IP address(es) of the server(s) you wish to provision and that
group_vars/{staging,production}/{laravel_apps, vault}.yml
have been configured, then you can use the provision command to call Ansible:
bin/provision.sh <environment>
Could not be easier:
bin/deploy.sh <environment> <app name>
Add the package to the php_extensions_custom
array in roles/php/defaults/main.yml
and re-provision the app server by running ansible-playbook server.yml -e env=production --tags=php
.
Add the package to the apt_packages_custom
array in roles/common/defaults/main.yml
and run the provisioning script again - htop
is a an example of something I'd add here.
If you'd like to use Laravel Horizon to manage the queue workers then you need to make the following changes to Anstead:
anstead/roles/deploy/hooks/finalize-after.yml
- - name: Restart all queue workers
- shell: /usr/bin/php artisan queue:restart chdir={{ deploy_helper.new_release_path }}
+ - name: Restart Horizon
+ shell: /usr/bin/php artisan horizon:terminate chdir={{ deploy_helper.new_release_path }}
anstead/roles/supervisor/templates/laravel-workers.j2
- [program:{{ item.key }}-worker]
+ [program:{{ item.key }}-horizon-worker]
- process_name=%(process_num)02d
+ process_name=%(program_name)s
- command=php {{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}/artisan queue:work --queue=default --tries=2
+ command=php {{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}/artisan horizon
autostart=true
autorestart=true
user={{ web_user }}
- numprocs=2
+ numprocs=1
redirect_stderr=true
- stdout_logfile={{ www_root }}/{{ item.key }}/logs/worker.log
+ stdout_logfile={{ www_root }}/{{ item.key }}/logs/horizon.log
config/horizon.php
Change the local
environment to development
.