Otomasi Deployment Laravel dengan Ansible pada Ubuntu Server

Halo semuanya! Kali ini saya akan menunjukkan cara setup Ansible Playbook untuk instalasi Laravel dan beberapa aplikasi pendukungnya.

Sandy Maulana Akbar
DOT Intern
9 min readDec 11, 2023

--

Dalam dunia pengembangan perangkat lunak, automation telah menjadi kunci untuk meningkatkan efisiensi dalam proses manajemen server. Saat mendapatkan tugas untuk manajemen suatu server yang berjumlah tidak sedikit dan memiiliki konfigurasi yang sama pada setiap perangkatnya, tentunya hal tersebut cukup melelahkan jika dilakukan secara satu-persatu.

Apa itu Ansible?

“Ansible is a software tool that provides simple but powerful automation for cross-platform computer support. It is primarily intended for IT professionals, who use it for application deployment, updates on workstations and servers, cloud provisioning, configuration management, intra-service orchestration, and nearly anything a systems administrator does on a weekly or daily basis. Ansible doesn’t depend on agent software and has no additional security infrastructure, so it’s easy to deploy.” source: https://opensource.com/resources/what-ansible

Jadi, ansible adalah suatu tools automation untuk manajemen suatu server. Dengan ansible, kita bisa melakukan deployment aplikasi, instalasi & konfigurasi service, dan juga melakukan konfigurasi pada server. Cukup dengan koneksi SSH, kita bisa melakukan manajemen pada puluhan hingga ratusan server sekaligus menggunakan ansible.

Melalui tutorial ini, saya akan mempraktikkan Ansible untuk melakukan automation dalam deployment Laravel dan aplikasi pendukungnya ke Ubuntu server.

Implementation

Dalam Artikel ini, saya mengimplementasikan ansible dimana webserver akan dikonfigurasikan dengan:

  • PHP 8.2
  • MySQL Database
  • Nginx Webserver

Prerequisites

Sebelum lebih lanjut, kita harus memenuhi beberapa prasyarat berikut:

  • Server Ubuntu 22.04
  • Ansible versi terbatu
  • Koneksi SSH ke Ubuntu server

Steps

Ansible Connection

  • Pastikan pada perangkat anda sudah terinstall Ansible, bisa dicek menggunakan perintah berikut
ansible --version
Pada perangkat saya, sudah terinstall ansible versi 2.14.6
  • Siapkan server Ubuntu 22.04 yang akan dikonfigurasi, disini saya menggunakan Virtual Machine yang dijalankan pada jaringan local
  • Register SSH Key menggunakan format perintah sebagai berikut
ssh-copy-id <server-user>@<server-ip>
  • Buat sebuah file inventory bernama hosts yang berisikan detail perangkat server yang akan dikonfigurasi seperti contoh berikut
[webservers]
Ubuntu ansible_host=192.168.1.5 ansible_user=ubuntuuser
  • Uji koneksi ansible ke perangkat server dengan perintah berikut:
ansible -i hosts all -m ping
Jika response dari server success berarti ansible sudah terkoneksi dengan perangkat server

Ansible Playbook

Ansible Playbook berfungsi sebagai tempat untuk menuliskan task-task yang digunakan untuk manajemen server.

  • Buat sebuah playbook yang bernama playbook.yml. Tambahkan beberapa parameter untuk mengawali playbook seperti berikut
---
- hosts: all # menentukan host target dimana playbook akan dijalankan
become: true # menentukan jika task akan di eksekusi dengan privilege sudo
  • Tambahkan block vars untuk mengatur variabel yang akan digunakan dalam konfigurasi server
  vars:
php_vers: 8.2
Fresh_install: true # isikan true untuk deployment pertama, false untuk deployment selanjutnya
new_user: sunday # user yang akan digunakan untuk konfigurasi & deploy laravel
nginx_app_url: urllaravel.xyz # domain yang digunakan untuk mengakses aplikasi
DB_DATABASE: laravel_db # untuk mengatur nama database yang digunakan laravel
DB_USERNAME: laravel # untuk mengatur user MySQL yang digunakan laravel
DB_PASSWORD: laravelpw # untuk mengatur password yang digunakan user MySQL

Keterangan:
Variabel dapat digunakan dengan menggunakan 2 buah tanda kurung kurawal setiap awal dan akhir variabel, contoh: {{ variabel1 }}.

  • Tambahkan block tasks untuk mengelompokkan task, dan tambahkan juga task untuk membuat User baru sebagai service PIC.
    Dengan ini, ownership dari direktori app, user & group dari pool PHP, akan menggunakan user baru ini.

NOTE:
Perhatikan juga indentation dari playbook, pastikan task berjarak 2 spasi dari parameter tasks.

  tasks:
- name: Create new user with custom home directory
user:
name: "{{ new_user }}"
home: "/var/www/{{ new_user }}"
shell: /bin/bash
createhome: yes
when: Fresh_install == true

- name: Set ownership and permissions for the user's home directory
file:
path: "/var/www/{{ new_user }}"
owner: "{{ new_user }}"
group: "{{ new_user }}"
mode: "0775"
recurse: yes
when: Fresh_install == true
  • Tambahkan task untuk instalasi beberapa package yang akan dibutuhkan untuk konfigurasi.
    - name: Install required packages
block:
- name: Refresh package lists # update package list (sama dengan sudo apt update)
apt:
update_cache: yes

- name: Install essential packages # install beberapa package yang dibutuhkan saat deployment
apt:
name:
- composer
- acl
- software-properties-common
- ca-certificates
- python3-mysqldb
- lsb-release
- apt-transport-https
state: present
  • Buat sebuah template dari file konfigurasi nginx yang memiliki nama laravel.conf.j2.
    Di dalam file konfigurasi ini, saya akan menggunakan variable untuk mengatur server_name dan direktori dari laravel.
server {
listen 80;
listen [::]:80;

root /var/www/{{ new_user }}/laravel-starter/public;

index index.php index.html index.htm index.nginx-debian.html;

server_name {{ nginx_app_url }};

location / {
try_files $uri $uri/ /index.php?$query_string;
}

location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php-fpm.sock;
}

location ~ /\.ht {
deny all;
}

location ~ /.well-known {
allow all;
}
}
  • Tambahkan task untuk install & konfigurasi Nginx
    - name: Deploy and Configure Nginx
block:
- name: Install Nginx # untuk install Nginx webserver
apt:
name: nginx
state: present

- name: Copy Nginx default site configuration # untuk copy template konfigurasi ke direktori yang ditentukan
template:
src: laravel.conf.j2
dest: /etc/nginx/sites-available/laravel
owner: root
group: root
mode: "0644"
notify: # untuk trigger handler
- Restart Nginx

- name: Enable Nginx site # untuk menghubungkan (link) file dari direktori "sites-available" ke "sites-enabled" di host target
file:
src: /etc/nginx/sites-available/laravel
dest: /etc/nginx/sites-enabled/laravel.conf
state: link
  • Tambahkan task untuk menginstall PHP dan beberapa ekstensi yang diperlukan
    - name: Install PHP and required extensions
block:
- name: Install the ppa:ondrej/php repo # menambahkan repository ondrej untuk install php dengan versi spesifik
apt_repository:
repo: ppa:ondrej/php
update_cache: yes
when: "'Ubuntu' in ansible_distribution"

- name: Install PHP with a certain version # install php dengan versi yang ditentukan
ansible.builtin.apt:
name: "php{{ php_vers }}"
state: present
install_recommends: no

- name: Install PHP packages # install extensi PHP yang dibutuhkan saat deployment aplikasi
apt:
name:
- php{{ php_vers }}-cli
- php{{ php_vers }}-curl
- php{{ php_vers }}-intl
- php{{ php_vers }}-zip
- php{{ php_vers }}-fpm
- php{{ php_vers }}-gd
- php{{ php_vers }}-mbstring
- php{{ php_vers }}-mcrypt
- php{{ php_vers }}-mysqlnd
- php{{ php_vers }}-odbc
- php{{ php_vers }}-pdo
- php{{ php_vers }}-soap
- php{{ php_vers }}-xml
- php{{ php_vers }}-xmlrpc
- php{{ php_vers }}-cli
- php{{ php_vers }}-redis
- php{{ php_vers }}-dev
- php{{ php_vers }}-imap
- php{{ php_vers }}-imagick
- php{{ php_vers }}-bcmath
- php{{ php_vers }}-gmp
update_cache: yes
state: present
  • Buat sebuah template file konfigurasi yang berformat jinja2, yang bernama laravel-starter.conf.
    File ini berisikan konfigurasi dari php-fpm, dan disini digunakan untuk mengatur user & group dari php-fpm tersebut.
[laravel-starter]

user = {{ new_user }} ; User yang akan digunakan sebagai owner pool PHP
group = {{ new_user }} ; Grup yang akan digunakan sebagai owner pool PHP
listen = /run/php/php-fpm.sock ; Lokasi socket untuk komunikasi dengan Nginx

listen.owner = www-data ; Owner socket
listen.group = www-data ; Grpup socket
listen.mode = 0660 ; Privilege untuk socket

pm = dynamic ; Menggunakan process manager mode dynamic untuk PHP-FPM
pm.max_children = 5 ; Jumlah maksimal child processes yang diizinkan
pm.start_servers = 2 ; Jumlah child processes yang akan dibuat saat PHP-FPM pertama kali startup
pm.min_spare_servers = 1 ; Jumlah minimal spare child processes
pm.max_spare_servers = 3 ; Jumlah maksimal spare child processes
  • Tambahkan task copy konfigurasi dari PHP-FPM pool
    - name: Configure PHP-FPM pool
block:
- name: Delete Default PHP-FPM pool # menghapus konfigurasi default dari php-fpm pool
file:
state: absent
path: "/etc/php/{{ php_vers }}/fpm/pool.d/www.conf"

- name: Configure PHP-FPM pool # menyalin file template konfigurasi ke direktori yang ditentukan.
template:
src: laravel-pool.conf.j2
dest: "/etc/php/{{ php_vers }}/fpm/pool.d/laravel-starter.conf"
notify: Restart PHP-FPM
  • Tambahkan task untuk menginstall & konfigurasi Database MySQL
    - name: Deploy and configure MySQL
block:
- name: Install MySQL # untuk install MySQL sebagai Database server
apt:
name: mysql-server
state: present

- name: start MySQL service # Start service MySQL setiap booting
service:
name: mysql
state: started
enabled: yes

- name: Create Application DB User # menambahkan user dari MySQL yang akan digunakan oleh aplikasi.
mysql_user:
name: "{{ DB_USERNAME }}"
password: "{{ DB_PASSWORD }}"
priv: "*.*:ALL"
host: '%'
state: present

- name: Create Application Database # menambahkan database untuk aplikasi.
mysql_db:
name: "{{ DB_DATABASE }}"
state: present
notify: Restart MySQL service
  • Buat sebuah file template untuk .env (.env.j2), dan sesuaikan key value nya
# Application Settings
APP_NAME='Laravel Starter'
APP_ENV=local
APP_KEY={{ app_key }}
APP_DEBUG=true
APP_URL=http://laravel-starter.local

# User Settings
INITIAL_USERNAME=100000
DEMO_MDOE=false
USER_REGISTRATION=true

# Database Settings
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE={{ DB_DATABASE }}
DB_USERNAME={{ DB_USERNAME }}
DB_PASSWORD={{ DB_PASSWORD }}

# Cache and Session Settings
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120

# Email Setting untuk user verification (Optional)
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

# AWS Settings (Optional)
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

# Pusher Settings (Optional)
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1

# Scout (Search) Settings
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

# Scout (Search) Settings
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://meilisearch:7700

# Backup and Notification Settings (Optional)
BACKUP_NOTIFICATION_EMAIL=you@example.com

# Slack Notification Settings (Optional)
SLACK_NOTIFICATION_WEBHOOK=

# Facebook Login Settings (Optional)
FACEBOOK_ACTIVE=true
FACEBOOK_CLIENT_ID=
FACEBOOK_CLIENT_SECRET=
FACEBOOK_REDIRECT=http://laravel-starter.local/login/facebook/callback

# GitHub Login Settings (Optional)
GITHUB_ACTIVE=true
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_REDIRECT=http://laravel-starter.local/login/github/callback

# Google Login Settings (Optional)
GOOGLE_ACTIVE=true
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT=http://laravel-starter.local/login/google/callback
    - name: Deploy Laravel app
become: true
become_user: "{{ new_user }}" # untuk menjalankan task yang ada dalam block menggunakan user baru
block:
- name: Clone laravel-starter from GitHub # clone template aplikasi laravel dari repository github
ansible.builtin.git:
repo: https://github.com/nasirkhan/laravel-starter
dest: "/var/www/{{ new_user }}/laravel-starter/"
version: main
force: yes
when: Fresh_install == true

- name: Install Composer dependencies # install dependensi aplikasi menggunakan Composer
command: composer install --prefer-dist --no-interaction
args:
chdir: "/var/www/{{ new_user }}/laravel-starter/"

- name: Generate new Laravel application key # generate laravel key
command: "php /var/www/{{ new_user }}/laravel-starter/artisan key:generate --show"
args:
chdir: "/var/www/{{ new_user }}/laravel-starter/"
register: key_output
when: Fresh_install == true
ignore_errors: true

- name: Save Laravel application key to file # menyimpan laravel key kedalam file supaya key tidak hilang ketika run berikutnya
copy:
content: "{{ key_output.stdout }}"
dest: "/var/www/{{ new_user }}/laravel-starter/laravel_key.txt"
when: Fresh_install == true
ignore_errors: true

- name: Read Laravel application key from the file using echo # membaca key yang ada pada file
shell: cat /var/www/{{ new_user }}/laravel-starter/laravel_key.txt
register: key_output
changed_when: false
failed_when: false

- name: Set app_key variable from the output of echo # membuat variable untuk key aplikasi
set_fact:
app_key: "{{ key_output.stdout }}"

- name: Create .env file # copy template file untuk .env
template:
src: .env.j2
dest: "/var/www/{{ new_user }}/laravel-starter/.env"

- name: Run migrations # migrasi database
command: "php /var/www/{{ new_user }}/laravel-starter/artisan migrate"

- name: seed the database # seeding database
command: "php /var/www/{{ new_user }}/laravel-starter/artisan db:seed"
when: Fresh_install == true

- name: Run storage:link command # link storage directory
command: "php /var/www/{{ new_user }}/laravel-starter/artisan storage:link"
when: Fresh_install == true

- name: Insert demo data # menambahkan data dummy (demo)
shell: echo "yes" | php artisan starter:insert-demo-data --fresh
args:
chdir: "/var/www/{{ new_user }}/laravel-starter"
when: Fresh_install == true
notify:
- Restart Nginx
  • Terakhir, tambahkan handler sesuai dengan list yang pada parameter notify
  handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted

- name: Restart PHP-FPM
ansible.builtin.service:
name: php{{ php_vers }}-fpm
state: restarted

- name: Restart MySQL service
service:
name: mysql
state: restarted

KETERANGAN:
Handler akan di trigger oleh parameter notify, handler akan berjalan jika task sudah dijalankan dan menghasilkan status changed.

Pengujian

  • Jalankan playbook dengan perintah berikut
ansible-playbook playbook.yml -i hosts -K
  • Jika sukses, maka akan terlihat seperti berikut
  • Tambahkan IP dan domain yang digunakan untuk akses website pada file /etc/hosts jika perangkat anda menggunakan OS berbasis Linux, C:\Windows\System32\drivers\etc\hosts untuk OS Windows, dan /private/etc/hosts untuk MacOS
  • Jika akses website menggunakan domain, akan diarahkan ke website Laravel yang telah di deploy
  • jika akses menggunakan, IP server, akan ditampilkan website default Nginx

Kesimpulan

Ansible memungkinkan para developer dan administrator sistem untuk dengan mudah dan efisien menyusun playbook yang berisi langkah-langkah konfigurasi yang konsisten untuk manajemen server dengan konfigurasi yang dibutuhkan oleh aplikasi Laravel. Dari instalasi dan konfigurasi package hingga mendeploy aplikasi Laravel, semua dapat dilakukan dengan konsisten dan efisien. Sekian untuk artikel kali ini, Terima kasih.

--

--