One place for hosting & domains

      Laravel

      How To Deploy Laravel 7 and MySQL on Kubernetes using Helm


      The author selected the Diversity in Tech Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Laravel is one of the most popular open-source PHP application frameworks today. It is commonly deployed with a MySQL database but can be configured to use a variety of backend data storage options. Laravel prides itself on taking advantage of many of PHP’s modern features and extensive package ecosystem.

      Kubernetes is a container orchestration platform that can be hosted on DigitalOcean Kubernetes clusters to take much of the administration work out of setting up and running containers in production. Helm is a Kubernetes package manager that makes configuring and installing services and pods on Kubernetes easier.

      In this guide, you will create a Laravel PHP application, build your app into a Docker image, and deploy that image to a DigitalOcean Kubernetes cluster using the LAMP Helm chart. Next, you’ll set up an Ingress Controller to add SSL and a custom domain name to your app. When completed, you will have a working Laravel application connected to a MySQL database that is running on a Kubernetes cluster.

      Prerequisites

      • Docker installed on the machine that you’ll access your cluster from. You can find detailed instructions on installing Docker for most Linux distributions here or on Docker’s website for other operating systems.
      • An account at Docker Hub for storing Docker images you’ll create during this tutorial.
      • A DigitalOcean Kubernetes 1.17+ cluster with your connection configuration set as the kubectl default. To learn how to create a Kubernetes cluster on DigitalOcean, see Kubernetes Quickstart. To learn how to connect to the cluster, see How to Connect to a DigitalOcean Kubernetes Cluster.
      • The Helm 3 package manager installed on your local machine. Complete the first step and add the stable repo from the second step of the How To Install Software on Kubernetes Clusters with the Helm 3 Package Manager tutorial.
      • A fully registered domain name with an available A record. This tutorial will use your_domain throughout. You can purchase a domain name on Namecheap, get one for free on Freenom, or use the domain registrar of your choice. Don’t worry about associating your domain’s A record with an IP at this time. Once you reach Step 5 and your Ingress controller is in place, you will connect your_domain to the proper IP.

      Step 1 — Creating a New Laravel Application

      In this step, you’ll use Docker to create a new Laravel 7 application, but you should be able to go through the same process with an existing Laravel application that uses MySQL as the backing database. The new application you build will verify that Laravel is connected to the database and display the name of the database.

      First, move to your home directory and then create a new Laravel application using a composer Docker container:

      • cd ~
      • docker run --rm -v $(pwd):/app composer create-project --prefer-dist laravel/laravel laravel-kubernetes

      Once the container is done and all the Composer packages have been installed, you should see a fresh installation of Laravel in your current directory called laravel-kubernetes/. Navigate to that folder:

      You’ll execute the rest of this tutorial’s commands from here.

      The purpose of this application is to test your database connection and display its name in your browser. In order to test the database connection, open up the ./resources/views/welcome.blade.php file in a text editor:

      • nano ./resources/views/welcome.blade.php

      Find the section <div class="links">...</div> and replace its contents with the following:

      ./resources/views/welcome.blade.php

      ...
      <div class="links">
         <strong>Database Connected: </strong>
          @php
              try {
                  DB::connection()->getPDO();
                  echo DB::connection()->getDatabaseName();
                  } catch (Exception $e) {
                  echo 'None';
              }
          @endphp
      </div>
      ...
      

      Save and close the file.

      That’s all the customization you’ll need to make to the default Laravel app for this tutorial. Once completed, this brief snippet of PHP will test your database connection and display the database’s name on the Laravel splash screen in your web browser.

      In the next step, you’ll use Docker to build an image containing this Laravel application and Docker Compose to test that it runs locally and connects to a MySQL database.

      Step 2 — Containerizing Your Laravel Application

      Now that you have created a new Laravel application, you’ll need to build your code into a Docker image and then test the image with Docker Compose. While the goal of this tutorial is to deploy your application to a Kubernetes cluster, Docker Compose is a convenient way to test your Docker image and configuration locally before running it in the cloud. This fast feedback loop can be useful for making and testing small changes.

      First, using nano or your preferred text editor, create a file in the root of your Laravel application called Dockerfile:

      Add the following content. Docker will use this file to build your code into an image:

      ./Dockerfile

      FROM php:7.4-apache
      
      # Install packages
      RUN apt-get update && apt-get install -y 
          git 
          zip 
          curl 
          sudo 
          unzip 
          libicu-dev 
          libbz2-dev 
          libpng-dev 
          libjpeg-dev 
          libmcrypt-dev 
          libreadline-dev 
          libfreetype6-dev 
          g++
      
      # Apache configuration
      ENV APACHE_DOCUMENT_ROOT=/var/www/html/public
      RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
      RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
      RUN a2enmod rewrite headers
      
      # Common PHP Extensions
      RUN docker-php-ext-install 
          bz2 
          intl 
          iconv 
          bcmath 
          opcache 
          calendar 
          pdo_mysql
      
      # Ensure PHP logs are captured by the container
      ENV LOG_CHANNEL=stderr
      
      # Set a volume mount point for your code
      VOLUME /var/www/html
      
      # Copy code and run composer
      COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
      COPY . /var/www/tmp
      RUN cd /var/www/tmp && composer install --no-dev
      
      # Ensure the entrypoint file can be run
      RUN chmod +x /var/www/tmp/docker-entrypoint.sh
      ENTRYPOINT ["/var/www/tmp/docker-entrypoint.sh"]
      
      # The default apache run command
      CMD ["apache2-foreground"]
      

      Save and close the file.

      This Dockerfile starts with the PHP 7.4 Apache Docker Image found on Docker Hub, then installs several Linux packages that are commonly required by Laravel applications. Next, it creates Apache configuration files and enables header rewrites. The Dockerfile installs several common PHP extensions and adds an environment variable to ensure that Laravel’s logs are streamed to the container via stderr. This will allow you to see Laravel logs by tailing your Docker Compose or Kubernetes logs.

      Finally, the Dockerfile copies all the code in your Laravel application to /var/www/tmp and installs the Composer dependencies. It then sets an ENTRYPOINT, but you’ll need to create that file, which we will do next.

      At the root directory of your project, create a new file called docker-entrypoint.sh. This file will run when your container is run locally or in the Kubernetes cluster, and it will copy your Laravel application code from the /var/www/tmp directory to /var/www/html where Apache will be able to serve it.

      • nano ./docker-entrypoint.sh

      Now add the following script:

      ./docker-entrypoint.sh

      #!/bin/bash
      
      cp -R /var/www/tmp/. /var/www/html/
      chown -R www-data:www-data /var/www/html
      
      exec "$@"
      

      The final line, exec "$@" instructs the shell to run whatever command was passed in as an input argument next. This is important because you want Docker to continue running the Apache run command (apache2-foreground) after this script executes. Save and close the file.

      Next, create a .dockerignore file in your app’s root directory. This file will ensure that when you build your Docker image it won’t become polluted with packages or environment files that shouldn’t be copied into it:

      ./.dockerignore

      .env
      /vendor
      

      Save and close the file.

      The last file that you need to create before you can run your app locally using Docker Compose is a docker-compose.yml file. But during the configuration of this YAML file, you will need to enter the APP_KEY that Laravel generated during installation. Find this by opening and searching the ./.env file, or by running the following cat and grep commands:

      You will see an output like this:

      Output

      APP_KEY=base64:0EHhVpgg ... UjGE=

      Copy your key to your clipboard. Be sure to include the base64: prefix. Now create the docker-compose.yml file in your app’s root directory:

      • nano ./docker-compose.yml

      Here we will include your Laravel application’s PHP image as well as a MySQL container to run your database. Add the following content:

      ./docker-compose.yml

      version: '3.5'
      services:
        php:
          image: your_docker_hub_username/laravel-kubernetes:latest
          restart: always
          ports:
            - 8000:80
          environment:
            - APP_KEY="your_laravel_app_key"
            - APP_ENV=local
            - APP_DEBUG=true
            - DB_PORT=3306
            - DB_HOST=mysql
            - DB_DATABASE
            - DB_USERNAME
            - DB_PASSWORD
        mysql:
          image: mysql:5.7
          restart: always
          environment:
            - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
            - MYSQL_DATABASE=${DB_DATABASE}
            - MYSQL_USER=${DB_USERNAME}
            - MYSQL_PASSWORD=${DB_PASSWORD}
      

      Use the APP_KEY variable that you copied to your clipboard for the your_laravel_app_key variable, and use your Docker Hub username for the your_docker_hub_username variable. Save and close the file.

      You’ll create the first image locally using docker build. The second image is the official MySQL Docker image available on Docker Hub. Both require several environment variables, which you’ll include when you run the containers.

      In order to build the Docker image containing your Laravel application, run the following command. Make sure to replace your_docker_hub_username with your username or your team’s username at Docker Hub where this image will be stored:

      • docker build -t your_docker_hub_username/laravel-kubernetes:latest .

      Next, you can run the two containers with Docker Compose with the required database credentials:

      • DB_ROOT_PASSWORD=rootpassword DB_DATABASE=local_db DB_USERNAME=admin DB_PASSWORD=password docker-compose up -d

      The four environment variables used here (DB_ROOT_PASSWORD, DB_DATABASE, DB_USERNAME, DB_PASSWORD) can be modified if you’d like, but since you are only testing your application locally, you don’t have to worry about securing them yet.

      It may take up to 30 seconds for your MySQL database to initialize and your containers to be ready. Once they are, you can view your Laravel application on your machine at localhost:8000.

      The Laravel application running locally using Docker Compose

      Your PHP application will connect to your MySQL database. After a successful connection, the text “Database Connected: local_db” will appear beneath the Laravel logo.

      Now that you’ve tested your Docker image locally using Docker Compose, you can bring the containers down by running docker-compose down:

      In the next section, you’ll push your Docker image to Docker Hub so that your Helm chart can use it to deploy your application to your Kubernetes cluster.

      Step 3 — Pushing Your Docker Image to Docker Hub

      The LAMP Helm Chart that you’ll use to deploy your code to Kubernetes requires that your code be available in a container registry. While you can push your image to a private or self-hosted registry, for the purposes of this tutorial you’ll use a publicly available and free Docker registry on Docker Hub.

      Access your account on Docker Hub using your web browser and then create a new repository called laravel-kubernetes.

      Creating a new repository on Docker Hub

      Next, if you haven’t connected to Docker Hub from your local machine, you’ll need to log into Docker Hub. You can do this through the command line:

      • docker login -u your_docker_hub_username

      Enter your login credentials when prompted. This typically only needs to be done once per machine as Docker will save your credentials to the ~/.docker/config.json in your home directory.

      Finally, push your image to Docker Hub:

      • docker push your_docker_hub_username/laravel-kubernetes:latest

      It may take a few minutes to upload your app depending on your connecti
      on speed, but once Docker is done, you’ll see a final digest hash and the size of your image in the terminal. It will look something like this:

      Output

      latest: digest: sha256:df4bdeda91484c8c26a989b13b8f27ab14d93ab2e676e3c396714cb3811c4086 size: 4918

      Now that your Laravel application is containerized and you’ve pushed an image to Docker Hub, you can use the image in a Helm Chart or Kubernetes deployment. In the next step, you’ll set custom values based on the LAMP Helm Chart and deploy it to your DigitalOcean Kubernetes cluster.

      Step 4 — Configuring and Deploying the Application with the LAMP Helm Chart

      Helm provides a number of Charts to help you set up Kubernetes applications using preset combinations of tools. While you could write your own Kubernetes service files to accomplish a similar deployment, you’ll see in this section that using a Helm Chart will require much less configuration.

      First, you’ll need a directory to store all your Helm configuration files. Create a new directory in the root of your Laravel project called helm/:

      Inside the helm/ directory, you will create two new files: values.yml and secrets.yml. First create and open values.yml:

      The values.yml file will include non-secret configuration options that will override the default values in the LAMP Helm chart. Add the following configurations, making sure to replace your_docker_hub_username with your own username:

      ./helm/values.yml

      php:
        repository: "your_docker_hub_username/laravel-kubernetes"
        tag: "latest"
        fpmEnabled: false
        envVars:
          - name: APP_ENV
            value: production
          - name: APP_DEBUG
            value: false
          - name: DB_PORT
            value: 3306
          - name: DB_HOST
            value: localhost
      

      Save and close the file.

      Now create a secrets.yml file:

      secrets.yml will not be checked into version control. It will contain sensitive configuration information like your database password and Laravel app key. Add the following configurations, adjusting as needed to fit your credentials:

      ./helm/secrets.yml

      mysql:
        rootPassword: "your_database_root_password"
        user: your_database_user
        password: "your_database_password"
        database: your_database_name
      
      php:
        envVars:
          - name: APP_KEY
            value: "your_laravel_app_key"
          - name: DB_DATABASE
            value: your_database_name
          - name: DB_USERNAME
            value: your_database_user
          - name: DB_PASSWORD
            value: "your_database_password"
      

      Be sure to use strong username and password combinations for your production database, and use the same your_laravel_app_key as above, or open a new terminal window and generate a new one by running the following command. You can then copy the new value Laravel sets in your .env file:

      • docker run --rm -v $(pwd):/app php:cli php /app/artisan key:generate

      Save and close secrets.yml.

      Next, in order to prevent your secrets.yml file from being built into the Docker image or saved to version control, make sure to add the following line to both your .dockerignore and .gitignore files. Open and append /helm/secrets.yml to each file, or run the following command to add both:

      • echo '/helm/secrets.yml' >> ./.dockerignore && echo '/helm/secrets.yml' >> ./.gitignore

      Now that you’ve created Helm configuration files for your application and the Docker image, you can install this Helm chart as a new release on your Kubernetes cluster. Install your chart from your application’s root directory:

      • helm install laravel-kubernetes -f helm/values.yml -f helm/secrets.yml stable/lamp

      You will see an output like this:

      Output

      NAME: laravel-kubernetes LAST DEPLOYED: Mon May 18 13:21:20 2020 NAMESPACE: default STATUS: deployed REVISION: 1

      Your application will take a minute or two to become available, but you can run this command to monitor the Kubernetes services in your cluster:

      Look for the name of your application:

      Output

      NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) laravel-kubernetes-lamp LoadBalancer your_cluster_ip your_external_ip 80:32175/TCP,3306:32243/TCP

      When your new laravel-kubernetes-lamp service displays an IP address under EXTERNAL-IP, you can visit your_external_ip to see the application running on your Kubernetes cluster. Your app will connect to your database and you will see the name of the database below the Laravel logo, like you did when running your app locally on Docker Compose.

      The Laravel application running on Kubernetes using the LAMP Helm chart

      Running a web application on an unsecured IP address might be okay for a proof of concept, but your website isn’t production-ready without an SSL certificate and a custom domain name. Before you set that up in the next step, uninstall your release via the command line:

      • helm delete laravel-kubernetes

      In the next step you’ll expand on this first Helm configuration to add an Ingress controller, SSL certificate, and custom domain to your Laravel application.

      Step 5 — Adding an Ingress Controller and SSL to Your Kubernetes Cluster

      In Kubernetes, an Ingress Controller is responsible for exposing your application’s services to the internet. In the previous step, the LAMP Helm chart created a DigitalOcean Load Balancer and exposed your application directly via the load balancer’s IP address.

      You could terminate SSL and your domain name directly on the load balancer, but because you’re working in Kubernetes, it might be more convenient to manage it all in the same place. For much more about Ingress Controllers and details about the following steps, read How To Set Up an Nginx Ingress on DigitalOcean Kubernetes Using Helm.

      The LAMP Helm chart includes a configuration option for supporting Ingress. Open up your helm/values.yml file:

      Now add the following lines:

      ./helm/values.yml

      ...
      # Use Ingress Controller
      service:
        type: ClusterIP
        HTTPPort: 80
      ingress:
        enabled: true
        domain: your_domain
      

      This instructs your deployment not to install a load balancer and instead to expose the application to the Kubernetes cluster’s port 80 where the Ingress Controller will expose it to the internet. Save and close values.yml.

      Now run the helm install command you ran previously to get your Laravel application running again. Make sure to run the command from your app’s root directory:

      • helm install laravel-kubernetes -f helm/values.yml -f helm/secrets.yml stable/lamp

      Next, install the nginx-ingress controller on your Kubernetes cluster using the Kubernetes-maintained Nginx Ingress Controller:

      • helm install nginx-ingress stable/nginx-ingress --set controller.publishService.enabled=true

      After installation, you will see an output like this:

      Output

      NAME: nginx-ingress LAST DEPLOYED: Mon May 18 13:28:34 2020 NAMESPACE: default STATUS: deployed REVISION: 1

      You also need an Ingress Resource to expose your Laravel app’s deployment. Create a new file in your app’s root directory called ingress.yml:

      This file defines your application’s host, SSL certificate manager, and backend service and port name. Add the following configurations, replaceing your_domain with the domain of your choice:

      ./ingress.yml

      apiVersion: networking.k8s.io/v1beta1
      kind: Ingress
      metadata:
        name: laravel-kubernetes-ingress
        annotations:
          kubernetes.io/ingress.class: nginx
          cert-manager.io/cluster-issuer: letsencrypt-prod
      spec:
        tls:
          - hosts:
              - your_domain
            secretName: laravel-kubernetes-tls
        rules:
          - host: your_domain
            http:
              paths:
                - backend:
                    serviceName: laravel-kubernetes-lamp
                    servicePort: 80
      

      Save and close the file.

      Next, you should install Cert-Manager and create an issuer that will allow you to create production SSL certificates using Let’s Encrypt. Cert-Manager requires Custom Resource Definitions that you can apply from the Cert-Manager repository using the command line:

      • kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.15.0/cert-manager.crds.yaml

      This will create a number of Kubernetes resources that will be displayed in the command line:

      Output

      customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/issuers.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/orders.acme.cert-manager.io create

      Cert-Manager also requires a namespace to isolate it in your Kubernetes cluster:

      • kubectl create namespace cert-manager

      You will see this output:

      Output

      namespace/cert-manager created

      Because Jetstack’s Cert-Manager is not one of the Kubernetes-maintained charts, you will need to add the Jetstack Helm repository as well. Run the following command to make it available in Helm:

      • helm repo add jetstack https://charts.jetstack.io

      A successful addition will output the following:

      Output

      "jetstack" has been added to your repositories

      Now you’re ready to install Cert-Manager into the cert-manager namespace on your Kubernetes cluster:

      • helm install cert-manager --version v0.15.0 --namespace cert-manager jetstack/cert-manager

      When complete, you’ll see a summary of the deployment like this:

      Output

      NAME: cert-manager LAST DEPLOYED: Mon May 18 13:32:08 2020 NAMESPACE: cert-manager STATUS: deployed REVISION: 1

      The last file you’ll need to add to your Laravel application’s root directory is a production_issuer.yml Kubernetes configuration file. Create the file:

      • nano ./production_issuer.yml

      Now add the following:

      apiVersion: cert-manager.io/v1alpha2
      kind: ClusterIssuer
      metadata:
        name: letsencrypt-prod
      spec:
        acme:
          # Email address used for ACME registration
          email: your_email_address
          server: https://acme-v02.api.letsencrypt.org/directory
          privateKeySecretRef:
            # Name of a secret used to store the ACME account private key
            name: letsencrypt-prod-private-key
          # Add a single challenge solver, HTTP01 using nginx
          solvers:
            - http01:
                ingress:
                  class: nginx
      

      Save and close the file.

      Let’s Encrypt will send your_email_address any important notices and expiration warnings, so be sure to add an address that you’ll check regularly. Save this file and create a new resource for both your Ingress resource and production issuer in your Kubernetes cluster:

      • kubectl create -f ingress.yml
      • kubectl create -f production_issuer.yml

      Finally, update your domain name’s DNS records to point an A record to your load balancer’s IP address. To find the IP address for your Ingress Controller enter:

      • kubectl get service nginx-ingress-controller

      Output

      NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-ingress-controller LoadBalancer your_cluster_ip your_external_ip 80:30187/TCP,443:31468/TCP 6m10s

      Use the your_external_ip address as the IP address for your DNS A Record. The process for updating your DNS records varies depending on where you manage your domain names and DNS hosting, but if you’re using DigitalOcean you can reference our guide on How to Manage DNS Records.

      Once your DNS records update and your SSL certificate is generated, your application will be available at your_domain and SSL will be enabled.

      The Laravel application with SSL termination and a custom domain name

      While your PHP application and database are already connected, you will still need to run database migrations. In the last step, you’ll see how to run Artisan commands on your Kubernetes pod to perform database migrations and other common maintenance tasks.

      Step 6 — Running Remote Commands

      While your Laravel application is running and connected to the MySQL database in Kubernetes, there are several common operations that you should run on a new Laravel installation. One common task that you should perform is database migrations.

      Before you can run an Artisan command on your Laravel application, you need to know the name of the pod that is running your Laravel application container. Using the command line, you can view all the pods in your Kubernetes cluster:

      You will see an output like this:

      Output

      NAME READY STATUS RESTARTS AGE laravel-kubernetes-lamp-77fb989b46-wczgb 2/2 Running 0 16m

      Select the pod for your laravel-kubernetes-lamp-... deployment. Make sure to use the name in your output and not the one listed above. Now you can run kubectl exec on it. For example, run a database migration using the artisan migrate command. You will add the --force flag because you’re running the pod in production:

      • kubectl exec laravel-kubernetes-lamp-77fb989b46-wczgb -- php artisan migrate --force

      This command will produce an output:

      Output

      Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (0.16 seconds) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (0.05 seconds)

      You have now successfully deployed Laravel 7 and MySQL to Kubernetes and performed an essential database maintenance task.

      Conclusion

      In this tutorial, you learned how to containerize a Laravel PHP application, connect it to a MySQL database, push a Docker image containing your code to Docker Hub, and then use a Helm chart to deploy that image to a DigitalOcean Kubernetes cluster. Finally, you added SSL and a custom domain name and learned how to run command line tools on your running pods.

      Kubernetes and Helm offer you a number of advantages over traditional LAMP stack hosting: scalability, the ability to swap out services without logging into your server directly, tools to perform rolling upgrades, and control over your hosting environment. That said, the complexity of initially containerizing and configuring your application makes the barrier to getting started quite high. With this guide as a starting point, deploying Laravel to Kubernetes becomes more attainable. From here you might consider learning more about the power of Laravel or adding monitoring tools to Kubernetes like Linkerd, which you can install manually with our guide or with a DigitalOcean 1-Click.



      Source link

      How To Install and Set Up Laravel with Docker Compose on Ubuntu 20.04


      Not using Ubuntu 20.04?


      Choose a different version or distribution.

      Introduction

      To containerize an application refers to the process of adapting an application and its components in order to be able to run it in lightweight environments known as containers. Such environments are isolated and disposable, and can be leveraged for developing, testing, and deploying applications to production.

      In this guide, we’ll use Docker Compose to containerize a Laravel application for development. When you’re finished, you’ll have a demo Laravel application running on three separate service containers:

      • An app service running PHP7.4-FPM;
      • A db service running MySQL 5.7;
      • An nginx service that uses the app service to parse PHP code before serving the Laravel application to the final user.

      To allow for a streamlined development process and facilitate application debugging, we’ll keep application files in sync by using shared volumes. We’ll also see how to use docker-compose exec commands to run Composer and Artisan on the app container.

      Prerequisites

      Step 1 — Obtaining the Demo Application

      To get started, we’ll fetch the demo Laravel application from its Github repository. We’re interested in the tutorial-01 branch, which contains the basic Laravel application we’ve created in the first guide of this series.

      To obtain the application code that is compatible with this tutorial, download release tutorial-1.0.1 to your home directory with:

      • cd ~
      • curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-1.0.1.zip -o travellist.zip

      We’ll need the unzip command to unpack the application code. In case you haven’t installed this package before, do so now with:

      • sudo apt update
      • sudo apt install unzip

      Now, unzip the contents of the application and rename the unpacked directory for easier access:

      • unzip travellist.zip
      • mv travellist-laravel-demo-tutorial-1.0.1 travellist-demo

      Navigate to the travellist-demo directory:

      In the next step, we’ll create a .env configuration file to set up the application.

      Step 2 — Setting Up the Application’s .env File

      The Laravel configuration files are located in a directory called config, inside the application’s root directory. Additionally, a .env file is used to set up environment-dependent configuration, such as credentials and any information that might vary between deploys. This file is not included in revision control.

      Warning: The environment configuration file contains sensitive information about your server, including database credentials and security keys. For that reason, you should never share this file publicly.

      The values contained in the .env file will take precedence over the values set in regular configuration files located at the config directory. Each installation on a new environment requires a tailored environment file to define things such as database connection settings, debug options, application URL, among other items that may vary depending on which environment the application is running.

      We’ll now create a new .env file to customize the configuration options for the development environment we’re setting up. Laravel comes with an example.env file that we can copy to create our own:

      Open this file using nano or your text editor of choice:

      The current .env file from the travellist demo application contains settings to use a local MySQL database, with 127.0.0.1 as database host. We need to update the DB_HOST variable so that it points to the database service we will create in our Docker environment. In this guide, we’ll call our database service db. Go ahead and replace the listed value of DB_HOST with the database service name:

      .env

      APP_NAME=Travellist
      APP_ENV=dev
      APP_KEY=
      APP_DEBUG=true
      APP_URL=http://localhost:8000
      
      LOG_CHANNEL=stack
      
      DB_CONNECTION=mysql
      DB_HOST=db
      DB_PORT=3306
      DB_DATABASE=travellist
      DB_USERNAME=travellist_user
      DB_PASSWORD=password
      ...
      

      Feel free to also change the database name, username, and password, if you wish. These variables will be leveraged in a later step where we’ll set up the docker-compose.yml file to configure our services.

      Save the file when you’re done editing. If you used nano, you can do that by pressing Ctrl+x, then Y and Enter to confirm.

      Step 3 — Setting Up the Application’s Dockerfile

      Although both our MySQL and Nginx services will be based on default images obtained from the Docker Hub, we still need to build a custom image for the application container. We’ll create a new Dockerfile for that.

      Our travellist image will be based on the php:7.4-fpm official PHP image from Docker Hub. On top of that basic PHP-FPM environment, we’ll install a few extra PHP modules and the Composer dependency management tool.

      We’ll also create a new system user; this is necessary to execute artisan and composer commands while developing the application. The uid setting ensures that the user inside the container has the same uid as your system user on your host machine, where you’re running Docker. This way, any files created by these commands are replicated in the host with the correct permissions. This also means that you’ll be able to use your code editor of choice in the host machine to develop the application that is running inside containers.

      Create a new Dockerfile with:

      Copy the following contents to your Dockerfile:

      Dockerfile

      FROM php:7.4-fpm
      
      # Arguments defined in docker-compose.yml
      ARG user
      ARG uid
      
      # Install system dependencies
      RUN apt-get update && apt-get install -y 
          git 
          curl 
          libpng-dev 
          libonig-dev 
          libxml2-dev 
          zip 
          unzip
      
      # Clear cache
      RUN apt-get clean && rm -rf /var/lib/apt/lists/*
      
      # Install PHP extensions
      RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
      
      # Get latest Composer
      COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
      
      # Create system user to run Composer and Artisan Commands
      RUN useradd -G www-data,root -u $uid -d /home/$user $user
      RUN mkdir -p /home/$user/.composer && 
          chown -R $user:$user /home/$user
      
      # Set working directory
      WORKDIR /var/www
      
      USER $user
      
      

      Don’t forget to save the file when you’re done.

      Our Dockerfile starts by defining the base image we’re using: php:7.4-fpm.

      After installing system packages and PHP extensions, we install Composer by copying the composer executable from its latest official image to our own application image.

      A new system user is then created and set up using the user and uid arguments that were declared at the beginning of the Dockerfile. These values will be injected by Docker Compose at build time.

      Finally, we set the default working dir as /var/www and change to the newly created user. This will make sure you’re connecting as a regular user, and that you’re on the right directory, when running composer and artisan commands on the application container.

      Step 4 — Setting Up Nginx Configuration and Database Dump Files

      When creating development environments with Docker Compose, it is often necessary to share configuration or initialization files with service containers, in order to set up or bootstrap those services. This practice facilitates making changes to configuration files to fine-tune your environment while you’re developing the application.

      We’ll now set up a folder with files that will be used to configure and initialize our service containers.

      To set up Nginx, we’ll share a travellist.conf file that will configure how the application is served. Create the docker-compose/nginx folder with:

      • mkdir -p docker-compose/nginx

      Open a new file named travellist.conf within that directory:

      • nano docker-compose/nginx/travellist.conf

      Copy the following Nginx configuration to that file:

      docker-compose/nginx/travellist.conf

      
      server {
          listen 80;
          index index.php index.html;
          error_log  /var/log/nginx/error.log;
          access_log /var/log/nginx/access.log;
          root /var/www/public;
          location ~ .php$ {
              try_files $uri =404;
              fastcgi_split_path_info ^(.+.php)(/.+)$;
              fastcgi_pass app:9000;
              fastcgi_index index.php;
              include fastcgi_params;
              fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
              fastcgi_param PATH_INFO $fastcgi_path_info;
          }
          location / {
              try_files $uri $uri/ /index.php?$query_string;
              gzip_static on;
          }
      }
      

      This file will configure Nginx to listen on port 80 and use index.php as default index page. It will set the document root to /var/www/public, and then configure Nginx to use the app service on port 9000 to process *.php files.

      Save and close the file when you’re done editing.

      To set up the MySQL database, we’ll share a database dump that will be imported when the container is initialized. This is a feature provided by the MySQL 5.7 image we’ll be using on that container.

      Create a new folder for your MySQL initialization files inside the docker-compose folder:

      • mkdir docker-compose/mysql

      Open a new .sql file:

      • nano docker-compose/mysql/init_db.sql

      The following MySQL dump is based on the database we’ve set up in our Laravel on LEMP guide. It will create a new table named places. Then, it will populate the table with a set of sample places.

      Add the following code to the file:

      docker-compose/mysql/db_init.sql

      DROP TABLE IF EXISTS `places`;
      
      CREATE TABLE `places` (
        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
        `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
        `visited` tinyint(1) NOT NULL DEFAULT '0',
        PRIMARY KEY (`id`)
      ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
      
      INSERT INTO `places` (name, visited) VALUES ('Berlin',0),('Budapest',0),('Cincinnati',1),('Denver',0),('Helsinki',0),('Lisbon',0),('Moscow',1),('Nairobi',0),('Oslo',1),('Rio',0),('Tokyo',0);
      

      The places table contains three fields: id, name, and visited. The visited field is a flag used to identify the places that are still to go. Feel free to change the sample places or include new ones. Save and close the file when you’re done.

      We’ve finished setting up the application’s Dockerfile and the service configuration files. Next, we’ll set up Docker Compose to use these files when creating our services.

      Step 5 — Creating a Multi-Container Environment with Docker Compose

      Docker Compose enables you to create multi-container environments for applications running on Docker. It uses service definitions to build fully customizable environments with multiple containers that can share networks and data volumes. This allows for a seamless integration between application components.

      To set up our service definitions, we’ll create a new file called docker-compose.yml. Typically, this file is located at the root of the application folder, and it defines your containerized environment, including the base images you will use to build your containers, and how your services will interact.

      We’ll define three different services in our docker-compose.yml file: app, db, and nginx.

      The app service will build an image called travellist, based on the Dockerfile we’ve previously created. The container defined by this service will run a php-fpm server to parse PHP code and send the results back to the nginx service, which will be running on a separate container. The mysql service defines a container running a MySQL 5.7 server. Our services will share a bridge network named travellist.

      The application files will be synchronized on both the app and the nginx services via bind mounts. Bind mounts are useful in development environments because they allow for a performant two-way sync between host machine and containers.

      Create a new docker-compose.yml file at the root of the application folder:

      A typical docker-compose.yml file starts with a version definition, followed by a services node, under which all services are defined. Shared networks are usually defined at the bottom of that file.

      To get started, copy this boilerplate code into your docker-compose.yml file:

      docker-compose.yml

      version: "3.7"
      services:
      
      
      networks:
        travellist:
          driver: bridge
      

      We’ll now edit the services node to include the app, db and nginx services.

      The app Service

      The app service will set up a container named travellist-app. It builds a new Docker image based on a Dockerfile located in the same path as the docker-compose.yml file. The new image will be saved locally under the name travellist.

      Even though the document root being served as the application is located in the nginx container, we need the application files somewhere inside the app container as well, so we’re able to execute command line tasks with the Laravel Artisan tool.

      Copy the following service definition under your services node, inside the docker-compose.yml file:

      docker-compose.yml

        app:
          build:
            args:
              user: sammy
              uid: 1000
            context: ./
            dockerfile: Dockerfile
          image: travellist
          container_name: travellist-app
          restart: unless-stopped
          working_dir: /var/www/
          volumes:
            - ./:/var/www
          networks:
            - travellist
      

      These settings do the following:

      • build: This configuration tells Docker Compose to build a local image for the app service, using the specified path (context) and Dockerfile for instructions. The arguments user and uid are injected into the Dockerfile to customize user creation commands at build time.
      • image: The name that will be used for the image being built.
      • container_name: Sets up the container name for this service.
      • restart: Always restart, unless the service is stopped.
      • working_dir: Sets the default directory for this service as /var/www.
      • volumes: Creates a shared volume that will synchronize contents from the current directory to /var/www inside the container. Notice that this is not your document root, since that will live in the nginx container.
      • networks: Sets up this service to use a network named travellist.

      The db Service

      The db service uses a pre-built MySQL 5.7 image from Docker Hub. Because Docker Compose automatically loads .env variable files located in the same directory as the docker-compose.yml file, we can obtain our database settings from the Laravel .env file we created in a previous step.

      Include the following service definition in your services node, right after the app service:

      docker-compose.yml

        db:
          image: mysql:5.7
          container_name: travellist-db
          restart: unless-stopped
          environment:
            MYSQL_DATABASE: ${DB_DATABASE}
            MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
            MYSQL_PASSWORD: ${DB_PASSWORD}
            MYSQL_USER: ${DB_USERNAME}
            SERVICE_TAGS: dev
            SERVICE_NAME: mysql
          volumes:
            - ./docker-compose/mysql:/docker-entrypoint-initdb.d
          networks:
            - travellist
      

      These settings do the following:

      • image: Defines the Docker image that should be used for this container. In this case, we’re using a MySQL 5.7 image from Docker Hub.
      • container_name: Sets up the container name for this service: travellist-db.
      • restart: Always restart this service, unless it is explicitly stopped.
      • environment: Defines environment variables in the new container. We’re using values obtained from the Laravel .env file to set up our MySQL service, which will automatically create a new database and user based on the provided environment variables.
      • volumes: Creates a volume to share a .sql database dump that will be used to initialize the application database. The MySQL image will automatically import .sql files placed in the /docker-entrypoint-initdb.d directory inside the container.
      • networks: Sets up this service to use a network named travellist.

      The nginx Service

      The nginx service uses a pre-built Nginx image on top of Alpine, a lightweight Linux distribution. It creates a container named travellist-nginx, and it uses the ports definition to create a redirection from port 8000 on the host system to port 80 inside the container.

      Include the following service definition in your services node, right after the db service:

      docker-compose.yml

        nginx:
          image: nginx:1.17-alpine
          container_name: travellist-nginx
          restart: unless-stopped
          ports:
            - 8000:80
          volumes:
            - ./:/var/www
            - ./docker-compose/nginx:/etc/nginx/conf.d
          networks:
            - travellist
      

      These settings do the following:

      • image: Defines the Docker image that should be used for this container. In this case, we’re using the Alpine Nginx 1.17 image.
      • container_name: Sets up the container name for this service: travellist-nginx.
      • restart: Always restart this service, unless it is explicitly stopped.
      • ports: Sets up a port redirection that will allow external access via port 8000 to the web server running on port 80 inside the container.
      • volumes: Creates two shared volumes. The first one will synchronize contents from the current directory to /var/www inside the container. This way, when you make local changes to the application files, they will be quickly reflected in the application being served by Nginx inside the container. The second volume will make sure our Nginx configuration file, located at docker-compose/nginx/travellist.conf, is copied to the container’s Nginx configuration folder.
      • networks: Sets up this service to use a network named travellist.

      Finished docker-compose.yml File

      This is how our finished docker-compose.yml file looks like:

      docker-compose.yml

      version: "3.7"
      services:
        app:
          build:
            args:
              user: sammy
              uid: 1000
            context: ./
            dockerfile: Dockerfile
          image: travellist
          container_name: travellist-app
          restart: unless-stopped
          working_dir: /var/www/
          volumes:
            - ./:/var/www
          networks:
            - travellist
      
        db:
          image: mysql:5.7
          container_name: travellist-db
          restart: unless-stopped
          environment:
            MYSQL_DATABASE: ${DB_DATABASE}
            MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
            MYSQL_PASSWORD: ${DB_PASSWORD}
            MYSQL_USER: ${DB_USERNAME}
            SERVICE_TAGS: dev
            SERVICE_NAME: mysql
          volumes:
            - ./docker-compose/mysql:/docker-entrypoint-initdb.d
          networks:
            - travellist
      
        nginx:
          image: nginx:alpine
          container_name: travellist-nginx
          restart: unless-stopped
          ports:
            - 8000:80
          volumes:
            - ./:/var/www
            - ./docker-compose/nginx:/etc/nginx/conf.d/
          networks:
            - travellist
      
      networks:
        travellist:
          driver: bridge
      

      Make sure you save the file when you’re done.

      Step 6 — Running the Application with Docker Compose

      We’ll now use docker-compose commands to build the application image and run the services we specified in our setup.

      Build the app image with the following command:

      This command might take a few minutes to complete. You’ll see output similar to this:

      Output

      Building app Step 1/11 : FROM php:7.4-fpm ---> fa37bd6db22a Step 2/11 : ARG user ---> Running in f71eb33b7459 Removing intermediate container f71eb33b7459 ---> 533c30216f34 Step 3/11 : ARG uid ---> Running in 60d2d2a84cda Removing intermediate container 60d2d2a84cda ---> 497fbf904605 Step 4/11 : RUN apt-get update && apt-get install -y git curl libpng-dev libonig-dev ... Step 7/11 : COPY --from=composer:latest /usr/bin/composer /usr/bin/composer ---> e499f74896e3 Step 8/11 : RUN useradd -G www-data,root -u $uid -d /home/$user $user ---> Running in 232ef9c7dbd1 Removing intermediate container 232ef9c7dbd1 ---> 870fa3220ffa Step 9/11 : RUN mkdir -p /home/$user/.composer && chown -R $user:$user /home/$user ---> Running in 7ca8c0cb7f09 Removing intermediate container 7ca8c0cb7f09 ---> 3d2ef9519a8e Step 10/11 : WORKDIR /var/www ---> Running in 4a964f91edfa Removing intermediate container 4a964f91edfa ---> 00ada639da21 Step 11/11 : USER $user ---> Running in 9f8e874fede9 Removing intermediate container 9f8e874fede9 ---> fe176ff4702b Successfully built fe176ff4702b Successfully tagged travellist:latest

      When the build is finished, you can run the environment in background mode with:

      Output

      Creating travellist-db ... done Creating travellist-app ... done Creating travellist-nginx ... done

      This will run your containers in the background. To show information about the state of your active services, run:

      You’ll see output like this:

      Output

      Name Command State Ports -------------------------------------------------------------------------------- travellist-app docker-php-entrypoint php-fpm Up 9000/tcp travellist-db docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp travellist-nginx /docker-entrypoint.sh ngin ... Up 0.0.0.0:8000->80/tcp

      Your environment is now up and running, but we still need to execute a couple commands to finish setting up the application. You can use the docker-compose exec command to execute commands in the service containers, such as an ls -l to show detailed information about files in the application directory:

      • docker-compose exec app ls -l

      Output

      total 260 -rw-rw-r-- 1 sammy sammy 737 Jun 9 11:19 Dockerfile -rw-rw-r-- 1 sammy sammy 101 Jan 7 08:05 README.md drwxrwxr-x 6 sammy sammy 4096 Jan 7 08:05 app -rwxr-xr-x 1 sammy sammy 1686 Jan 7 08:05 artisan drwxrwxr-x 3 sammy sammy 4096 Jan 7 08:05 bootstrap -rw-rw-r-- 1 sammy sammy 1501 Jan 7 08:05 composer.json -rw-rw-r-- 1 sammy sammy 179071 Jan 7 08:05 composer.lock drwxrwxr-x 2 sammy sammy 4096 Jan 7 08:05 config drwxrwxr-x 5 sammy sammy 4096 Jan 7 08:05 database drwxrwxr-x 4 sammy sammy 4096 Jun 9 11:19 docker-compose -rw-rw-r-- 1 sammy sammy 965 Jun 9 11:27 docker-compose.yml -rw-rw-r-- 1 sammy sammy 1013 Jan 7 08:05 package.json -rw-rw-r-- 1 sammy sammy 1405 Jan 7 08:05 phpunit.xml drwxrwxr-x 2 sammy sammy 4096 Jan 7 08:05 public -rw-rw-r-- 1 sammy sammy 273 Jan 7 08:05 readme.md drwxrwxr-x 6 sammy sammy 4096 Jan 7 08:05 resources drwxrwxr-x 2 sammy sammy 4096 Jan 7 08:05 routes -rw-rw-r-- 1 sammy sammy 563 Jan 7 08:05 server.php drwxrwxr-x 5 sammy sammy 4096 Jan 7 08:05 storage drwxrwxr-x 4 sammy sammy 4096 Jan 7 08:05 tests drwxrwxr-x 41 sammy sammy 4096 Jun 9 11:32 vendor -rw-rw-r-- 1 sammy sammy 538 Jan 7 08:05 webpack.mix.js

      We’ll now run composer install to install the application dependencies:

      • docker-compose exec app composer install

      You’ll see output like this:

      Output

      Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 85 installs, 0 updates, 0 removals - Installing doctrine/inflector (1.3.1): Downloading (100%) - Installing doctrine/lexer (1.2.0): Downloading (100%) - Installing dragonmantank/cron-expression (v2.3.0): Downloading (100%) - Installing erusev/parsedown (1.7.4): Downloading (100%) - Installing symfony/polyfill-ctype (v1.13.1): Downloading (100%) - Installing phpoption/phpoption (1.7.2): Downloading (100%) - Installing vlucas/phpdotenv (v3.6.0): Downloading (100%) - Installing symfony/css-selector (v5.0.2): Downloading (100%) … Generating optimized autoload files > IlluminateFoundationComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: facade/ignition Discovered Package: fideloper/proxy Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully.

      The last thing we need to do before testing the application is to generate a unique application key with the artisan Laravel command-line tool. This key is used to encrypt user sessions and other sensitive data:

      • docker-compose exec app php artisan key:generate

      Output

      Application key set successfully.

      Now go to your browser and access your server’s domain name or IP address on port 8000:

      http://server_domain_or_IP:8000
      

      Note: In case you are running this demo on your local machine, use http://localhost:8000 to access the application from your browser.

      You’ll see a page like this:

      Demo Laravel Application

      You can use the logs command to check the logs generated by your services:

      • docker-compose logs nginx
      Attaching to travellist-nginx
      …
      travellist-nginx | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
      travellist-nginx | /docker-entrypoint.sh: Configuration complete; ready for start up
      travellist-nginx | 192.168.0.1 - - [09/Jun/2020:11:46:34 +0000] "GET / HTTP/1.1" 200 627 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36"
      travellist-nginx | 192.168.0.1 - - [09/Jun/2020:11:46:35 +0000] "GET / HTTP/1.1" 200 627 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36"
      

      If you want to pause your Docker Compose environment while keeping the state of all its services, run:

      Output

      Pausing travellist-db ... done Pausing travellist-nginx ... done Pausing travellist-app ... done

      You can then resume your services with:

      Output

      Unpausing travellist-app ... done Unpausing travellist-nginx ... done Unpausing travellist-db ... done

      To shut down your Docker Compose environment and remove all of its containers, networks, and volumes, run:

      Output

      Stopping travellist-nginx ... done Stopping travellist-db ... done Stopping travellist-app ... done Removing travellist-nginx ... done Removing travellist-db ... done Removing travellist-app ... done Removing network travellist-laravel-demo_travellist

      For an overview of all Docker Compose commands, please check the Docker Compose command-line reference.

      Conclusion

      In this guide, we’ve set up a Docker environment with three containers using Docker Compose to define our infrastructure in a YAML file.

      From this point on, you can work on your Laravel application without needing to install and set up a local web server for development and testing. Moreover, you’ll be working with a disposable environment that can be easily replicated and distributed, which can be helpful while developing your application and also when moving towards a production environment.



      Source link

      How To Install and Configure Laravel with Nginx on Ubuntu 20.04


      Not using Ubuntu 20.04?


      Choose a different version or distribution.

      Introduction

      Laravel is an open-source PHP framework that provides a set of tools and resources to build modern PHP applications. With a complete ecosystem leveraging its built-in features, Laravel’s popularity has grown rapidly in the past few years, with many developers adopting it as their framework of choice for a streamlined development process.

      In this guide, you’ll install and configure a new Laravel application on an Ubuntu 20.04 server, using Composer to download and manage the framework dependencies and Nginx to serve the application. When you’re finished, you’ll have a functional Laravel demo application pulling content from a MySQL 8 database.

      Prerequisites

      In order to complete this guide, you will first need to perform the following tasks on your Ubuntu 20.04 server:

      Step 1 — Installing Required PHP modules

      Before you can install Laravel, you need to install a few PHP modules that are required by the framework. We’ll use apt to install the php-mbstring, php-xml and php-bcmath PHP modules. These PHP extensions provide extra support for dealing with character encoding, XML and precision mathematics.

      If this is the first time using apt in this session, you should first run the update command to update the package manager cache:

      Now you can install the required packages with:

      • sudo apt install php-mbstring php-xml php-bcmath

      Your system is now ready to execute Laravel’s installation via Composer, but before doing so, you’ll need a database for your application.

      Step 2 — Creating a Database for the Application

      To demonstrate Laravel’s basic installation and usage, we’ll create a travel list application to show a list of places a user would like to travel to, and a list of places that they already visited. This can be stored in a places table with a field for locations that we’ll call name and another field to mark them as visited or not visited, which we’ll call visited. Additionally, we’ll include an id field to uniquely identify each entry.

      To connect to the database from the Laravel application, we’ll create a dedicated MySQL user, and grant this user full privileges over the travellist database.

      At the time of this writing, the native MySQL PHP library mysqlnd doesn’t support caching_sha2_authentication, the default authentication method for MySQL 8. We’ll need to set up our database user with the mysql_native_password authentication method in order to be able to connect to the MySQL database from PHP.

      To get started, log in to the MySQL console as the root database user with:

      To create a new database, run the following command from your MySQL console:

      • CREATE DATABASE travellist;

      Now you can create a new user and grant them full privileges on the custom database you’ve just created. In this example, we’re creating a user named travellist_user with the password password, though you should change this to a secure password of your choosing:

      • CREATE USER 'travellist_user'@'%' IDENTIFIED WITH mysql_native_password BY 'password';

      Now we need to give this user permission over the travellist database:

      • GRANT ALL ON travellist.* TO 'travellist_user'@'%';

      This will give the travellist_user user full privileges over the travellist database, while preventing this user from creating or modifying other databases on your server.

      Following this, exit the MySQL shell:

      You can now test if the new user has the proper permissions by logging in to the MySQL console again, this time using the custom user credentials:

      • mysql -u travellist_user -p

      Note the -p flag in this command, which will prompt you for the password used when creating the travellist_user user. After logging in to the MySQL console, confirm that you have access to the travellist database:

      This will give you the following output:

      Output

      +--------------------+ | Database | +--------------------+ | information_schema | | travellist | +--------------------+ 2 rows in set (0.01 sec)

      Next, create a table named places in the travellist database. From the MySQL console, run the following statement:

      • CREATE TABLE travellist.places (
      • id INT AUTO_INCREMENT,
      • name VARCHAR(255),
      • visited BOOLEAN,
      • PRIMARY KEY(id)
      • );

      Now, populate the places table with some sample data:

      • INSERT INTO travellist.places (name, visited)
      • VALUES ("Tokyo", false),
      • ("Budapest", true),
      • ("Nairobi", false),
      • ("Berlin", true),
      • ("Lisbon", true),
      • ("Denver", false),
      • ("Moscow", false),
      • ("Olso", false),
      • ("Rio", true),
      • ("Cincinnati", false),
      • ("Helsinki", false);

      To confirm that the data was successfully saved to your table, run:

      • SELECT * FROM travellist.places;

      You will see output similar to this:

      Output

      +----+-----------+---------+ | id | name | visited | +----+-----------+---------+ | 1 | Tokyo | 0 | | 2 | Budapest | 1 | | 3 | Nairobi | 0 | | 4 | Berlin | 1 | | 5 | Lisbon | 1 | | 6 | Denver | 0 | | 7 | Moscow | 0 | | 8 | Oslo | 0 | | 9 | Rio | 1 | | 10 | Cincinnati| 0 | | 11 | Helsinki | 0 | +----+-----------+---------+ 11 rows in set (0.00 sec)

      After confirming that you have valid data in your test table, you can exit the MySQL console:

      You’re now ready to create the application and configure it to connect to the new database.

      Step 3 — Creating a New Laravel Application

      You will now create a new Laravel application using the composer create-project command. This Composer command is typically used to bootstrap new applications based on existing frameworks and content management systems.

      Throughout this guide, we’ll use travellist as an example application, but you are free to change this to something else. The travellist application will display a list of locations pulled from a local MySQL server, intended to demonstrate Laravel’s basic configuration and confirm that you’re able to connect to the database.

      First, go to your user’s home directory:

      The following command will create a new travellist directory containing a barebones Laravel application based on default settings:

      • composer create-project --prefer-dist laravel/laravel travellist

      You will see output similar to this:

      Output

      Installing laravel/laravel (v5.8.17) - Installing laravel/laravel (v5.8.17): Downloading (100%) Created project in travellist > @php -r "file_exists('.env') || copy('.env.example', '.env');" Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 80 installs, 0 updates, 0 removals - Installing symfony/polyfill-ctype (v1.11.0): Downloading (100%) - Installing phpoption/phpoption (1.5.0): Downloading (100%) - Installing vlucas/phpdotenv (v3.4.0): Downloading (100%) - Installing symfony/css-selector (v4.3.2): Downloading (100%) ...

      When the installation is finished, access the application’s directory and run Laravel’s artisan command to verify that all components were successfully installed:

      • cd travellist
      • php artisan

      You’ll see output similar to this:

      Output

      Laravel Framework 7.11.0 Usage: command [options] [arguments] Options: -h, --help Display this help message -q, --quiet Do not output any message -V, --version Display this application version --ansi Force ANSI output --no-ansi Disable ANSI output -n, --no-interaction Do not ask any interactive question --env[=ENV] The environment the command should run under -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug ...

      This output confirms that the application files are in place, and the Laravel command-line tools are working as expected. However, we still need to configure the application to set up the database and a few other details.

      Step 4 — Configuring Laravel

      The Laravel configuration files are located in a directory called config, inside the application’s root directory. Additionally, when you install Laravel with Composer, it creates an environment file. This file contains settings that are specific to the current environment the application is running, and will take precedence over the values set in regular configuration files located at the config directory. Each installation on a new environment requires a tailored environment file to define things such as database connection settings, debug options, application URL, among other items that may vary depending on which environment the application is running.

      Warning: The environment configuration file contains sensitive information about your server, including database credentials and security keys. For that reason, you should never share this file publicly.

      We’ll now edit the .env file to customize the configuration options for the current application environment.

      Open the .env file using your command line editor of choice. Here we’ll use nano:

      Even though there are many configuration variables in this file, you don’t need to set up all of them now. The following list contains an overview of the variables that require immediate attention:

      • APP_NAME: Application name, used for notifications and messages.
      • APP_ENV: Current application environment.
      • APP_KEY: Used for generating salts and hashes, this unique key is automatically created when installing Laravel via Composer, so you don’t need to change it.
      • APP_DEBUG: Whether or not to show debug information at client side.
      • APP_URL: Base URL for the application, used for generating application links.
      • DB_DATABASE: Database name.
      • DB_USERNAME: Username to connect to the database.
      • DB_PASSWORD: Password to connect to the database.

      By default, these values are configured for a local development environment that uses Homestead, a prepackaged Vagrant box provided by Laravel. We’ll change these values to reflect the current environment settings of our example application.

      In case you are installing Laravel in a development or testing environment, you can leave the APP_DEBUG option enabled, as this will give you important debug information while testing the application from a browser. The APP_ENV variable should be set to development or testing in this case.

      In case you are installing Laravel in a production environment, you should disable the APP_DEBUG option, because it shows to the final user sensitive information about your application. The APP_ENV in this case should be set to production.

      The following .env file sets up our example application for development:

      Note: The APP_KEY variable contains a unique key that was auto generated when you installed Laravel via Composer. You don’t need to change this value. If you want to generate a new secure key, you can use the php artisan key:generate command.

      /var/www/travellist/.env

      APP_NAME=TravelList
      APP_ENV=development
      APP_KEY=APPLICATION_UNIQUE_KEY_DONT_COPY
      APP_DEBUG=true
      APP_URL=http://domain_or_IP
      
      LOG_CHANNEL=stack
      
      DB_CONNECTION=mysql
      DB_HOST=127.0.0.1
      DB_PORT=3306
      DB_DATABASE=travellist
      DB_USERNAME=travellist_user
      DB_PASSWORD=password
      
      ...
      

      Adjust your variables accordingly. When you are done editing, save and close the file to keep your changes. If you’re using nano, you can do that with CTRL+X, then Y and Enter to confirm.

      Your Laravel application is now set up, but we still need to configure the web server in order to be able to access it from a browser. In the next step, we’ll configure Nginx to serve your Laravel application.

      Step 5 — Setting Up Nginx

      We have installed Laravel on a local folder of your remote user’s home directory, and while this works well for local development environments, it’s not a recommended practice for web servers that are open to the public internet. We’ll move the application folder to /var/www, which is the usual location for web applications running on Nginx.

      First, use the mv command to move the application folder with all its contents to /var/www/travellist:

      • sudo mv ~/travellist /var/www/travellist

      Now we need to give the web server user write access to the storage and cache folders, where Laravel stores application-generated files:

      • sudo chown -R www-data.www-data /var/www/travellist/storage
      • sudo chown -R www-data.www-data /var/www/travellist/bootstrap/cache

      The application files are now in order, but we still need to configure Nginx to serve the content. To do this, we’ll create a new virtual host configuration file at /etc/nginx/sites-available:

      • sudo nano /etc/nginx/sites-available/travellist

      The following configuration file contains the recommended settings for Laravel applications on Nginx:

      /etc/nginx/sites-available/travellist

      server {
          listen 80;
          server_name server_domain_or_IP;
          root /var/www/travellist/public;
      
          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;
      
          location / {
              try_files $uri $uri/ /index.php?$query_string;
          }
      
          location = /favicon.ico { access_log off; log_not_found off; }
          location = /robots.txt  { access_log off; log_not_found off; }
      
          error_page 404 /index.php;
      
          location ~ .php$ {
              fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
              fastcgi_index index.php;
              fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
              include fastcgi_params;
          }
      
          location ~ /.(?!well-known).* {
              deny all;
          }
      }
      

      Copy this content to your /etc/nginx/sites-available/travellist file and, if necessary, adjust the highlighted values to align with your own configuration. Save and close the file when you’re done editing.

      To activate the new virtual host configuration file, create a symbolic link to travellist in sites-enabled:

      • sudo ln -s /etc/nginx/sites-available/travellist /etc/nginx/sites-enabled/

      Note: If you have another virtual host file that was previously configured for the same server_name used in the travellist virtual host, you might need to deactivate the old configuration by removing the corresponding symbolic link inside /etc/nginx/sites-enabled/.

      To confirm that the configuration doesn’t contain any syntax errors, you can use:

      You should see output like this:

      Output

      • nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
      • nginx: configuration file /etc/nginx/nginx.conf test is successful

      To apply the changes, reload Nginx with:

      • sudo systemctl reload nginx

      Now go to your browser and access the application using the server’s domain name or IP address, as defined by the server_name directive in your configuration file:

      http://server_domain_or_IP
      

      You will see a page like this:

      Laravel splash page

      That confirms your Nginx server is properly configured to serve Laravel. From this point, you can start building up your application on top of the skeleton provided by the default installation.

      In the next step, we’ll modify the application’s main route to query for data in the database using Laravel’s DB facade.

      Step 6 — Customizing the Main Page

      Assuming you’ve followed all the steps in this guide so far, you should have a working Laravel application and a database table named places containing some sample data.

      We’ll now edit the main application route to query for the database and return the contents to the application’s view.

      Open the main route file, routes/web.php:

      This file comes by default with the following content:

      routes/web.php

      <?php
      
      /*
      |--------------------------------------------------------------------------
      | Web Routes
      |--------------------------------------------------------------------------
      |
      | Here is where you can register web routes for your application. These
      | routes are loaded by the RouteServiceProvider within a group which
      | contains the "web" middleware group. Now create something great!
      |
      */
      
      Route::get('/', function () {
          return view('welcome');
      });
      
      

      Routes are defined within this file using the static method Route::get, which receives a path and a callback function as arguments.

      The following code replaces the main route callback function. It makes 2 queries to the database using the visited flag to filter results. It returns the results to a view named travellist, which we’re going to create next. Copy this content to your routes/web.php file, replacing the code that is already there:

      routes/web.php

      <?php
      
      use IlluminateSupportFacadesDB;
      
      Route::get('/', function () {
        $visited = DB::select('select * from places where visited = ?', [1]); 
        $togo = DB::select('select * from places where visited = ?', [0]);
      
        return view('travellist', ['visited' => $visited, 'togo' => $togo ] );
      });
      

      Save and close the file when you’re done editing. We’ll now create the view that will render the database results to the user. Create a new view file inside resources/views:

      • nano resources/views/travellist.blade.php

      The following template creates two lists of places based on the variables visited and togo. Copy this content to your new view file:

      resources/views/travellist/blade.php

      <html>
      <head>
          <title>Travel List</title>
      </head>
      
      <body>
          <h1>My Travel Bucket List</h1>
          <h2>Places I'd Like to Visit</h2>
          <ul>
            @foreach ($togo as $newplace)
              <li>{{ $newplace->name }}</li>
            @endforeach
          </ul>
      
          <h2>Places I've Already Been To</h2>
          <ul>
                @foreach ($visited as $place)
                      <li>{{ $place->name }}</li>
                @endforeach
          </ul>
      </body>
      </html>
      

      Save and close the file when you’re done. Now go to your browser and reload the application. You’ll see a page like this:

      Demo Laravel Application

      You have now a functional Laravel application pulling contents from a MySQL database.

      Conclusion

      In this tutorial, you’ve set up a new Laravel application on top of a LEMP stack (Linux, Nginx, MySQL and PHP), running on an Ubuntu 20.04 server. You’ve also customized your default route to query for database content and exhibit the results in a custom view.

      From here, you can create new routes and views for any additional pages your application needs. Check the official Laravel documentation for more information on routes, views, and database support. If you’re deploying to production, you should also check the optimization section for a few different ways in which you can improve your application’s performance.



      Source link