Do not speak Portuguese? Translate this site with Google or Bing Translator
Running Dockerized Laravel Applications On Top Of Kubernetes

Posted on: April 25, 2024 11:33 PM

Posted by: Renato

Views: 511

Running Dockerized Laravel Applications On Top Of Kubernetes

.

This article includes the steps needed to build a to-do app in Laravel, deploying the app to a server, database migration, and Laravel's Eloquent models.

Related posts

Cloud-Native Now Webinar: Containers Kubernetes Management

Read more

Kubernetes Security - A Complete Guide to Securing Your Containers

Read more

KubeCon EU 2023 Recap – GitOps Sessions on Flux with OCI, Liquid Metal CI/CD Platforms & Telco Cloud Platforms

Read more

Laravel is a free, open-source PHP framework, created by Taylor Otwell and intended for the development of web applications following the model–view–controller (MVC) architectural pattern.

In this article, we’ll be building a Laravel app, dockerizing it, and deploying it on top of Kubernetes. But first, you may be wondering why we’re using Kubernetes and Docker for the deployment of our Laravel application. Kubernetes allows us to derive maximum utility from containers and build cloud-native applications that can run anywhere, independent of cloud-specific requirements. With Kubernetes, we can automatically deploy, scale, and manage our application so that we can pay more attention to building efficient applications. We will go through the steps required in building a To-do app in Laravel and automating the deployment of the app to a server. We'll learn how Laravel handles database migration as well as the Laravel's Eloquent models. Of course, we'll see the Routing and View Layouts. All basic stuff, but fundamental elements of Laravel.

Step 1: Install Laravel

The first thing you have to do is to install composer from https://getcomposer.org/ and then install Laravel, check Laravel’s documentation for that https://laravel.com/docs. At this point, we’ll need to pay close attention to the codeblocks that we are going to use for the building of our Laravel application because, without the correct codeblocks, we will have no dockerizing nor deployment of our app.

Step 2: Create Your Laravel App

To create our new laravel app, run this:

$ laravel new todo

Set Up Database Configuration

Create a database in your preferred database app, then set-up your project-dir/.env file like:

DB_HOST=localhost
DB_CONNECTION=mysql
DB_DATABASE=tasks
DB_USERNAME=root
DB_PASSWORD=""

Create Database Migration

Create a database migration for ‘tasks’ :

$ php artisan make:migration create_tasks_table --create=tasks

The migration will be placed in the database/migrations directory of our project. Add a string column to hold the name of the tasks by adding $table->string('name'); to create_task_table.php file like this :

public function up()
{
    Schema::create('tasks', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });

Then run:

$ php artisan migrate

Create A Model

Next, define a Task model that corresponds to our tasks database table. Run :

$ php artisan make:model Task

Routing

All Laravel routes are defined in the app/Http/routes.php.

Our app needs three routes:

  • GET: to display the view
  • POST: for updating tasks
  • DELETE: to delete tasks

Put all of these routes in app/Http/routes.php file:

 'web'], function () {
/**
* Show Task Dashboard
*/
Route::get('/', function () {
$tasks = Task::orderBy('created_at', 'asc')->get();
return view('tasks', [
'tasks' => $tasks
]);
});
/**
* Add New Task
*/
Route::post('/task', function (Request $request) {
$validator = Validator::make($request->all(), [
'name' => 'required|max:255',
]);
if ($validator->fails()) {
return redirect('/')
->withInput()
->withErrors($validator);

Blades Layout For Our View

Define a new layout view in resources/views/layouts/app.blade.php. The .blade.php extension instructs the framework to use the Blade templating engine to render the view.

The new app.blade.php view would look like this:

<!DOCTYPE html>
<html lang="en”>
    <head>
        <title>Laravel Quickstart - Basic</title>
        <!-- CSS And JavaScript -->
    </head>
    <body>
        <div class="container">
            <nav class="navbar navbar-default">
                <!-- Navbar Contents -->
            </nav>
        </div>
        @yield('content')
    </body>
</html>

Create A Form And A Table Listing For The Task

Define a view (resources/views/tasks.blade.php) that contains a form to create a new task as well as a table that lists all existing tasks.

Define this view in resources/views/tasks.blade.php like this:

@extends('layouts.app')
@section('content')
    <!-- Bootstrap Boilerplate... -->
    <div class="panel-body">
        <!-- Display Validation Errors -->
        @include('common.errors')
        <!-- New Task Form -->
        <form action="{{ url('task') }}" method="POST" class="form-horizontal">
            {!! csrf_field() !!}
            <!-- Task Name -->
            <div class="form-group">
                <label for="task" class="col-sm-3 control-label">Task</label>
                <div class="col-sm-6">
                    <input type="text" name="name" id="task-name" class="form-control">
                </div>
            </div>
            <!-- Add Task Button -->
            <div class="form-group">
                <div class="col-sm-offset-3 col-sm-6">
                    <button type="submit" class="btn btn-default">
                        <i class="fa fa-plus"></i> Add Task
                    </button>
                </div>
            </div>
        </form>
    </div>
    <!-- TODO: Current Tasks -->
@endsection

Error Validation View

Define the contents of the view resources/views/common/errors.blade.php. It should look like this:

@if (count($errors) > 0)
    <!-- Form Error List -->
    <div class="alert alert-danger">
        <strong>Whoops! Something went wrong!</strong>

        



        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

Complete The View

@extends('layouts.app')
@section('content')
    <!-- Bootstrap Boilerplate... -->
    <div class="panel-body">
        <!-- Display Validation Errors -->
        @include('common.errors')
        <!-- New Task Form -->
        <form action="{{ url('task') }}" method="POST" class="form-horizontal">
            {!! csrf_field() !!}
            <!-- Task Name -->
            <div class="form-group">
                <label for="task" class="col-sm-3 control-label">Task</label>
                <div class="col-sm-6">
                    <input type="text" name="name" id="task-name" class="form-control">
                </div>
            </div>
            <!-- Add Task Button -->
            <div class="form-group">
                <div class="col-sm-offset-3 col-sm-6">
                    <button type="submit" class="btn btn-default">
                        <i class="fa fa-plus"></i> Add Task
                    </button>
                </div>
            </div>
        </form>
    </div>
    <!-- Current Tasks -->
    @if (count($tasks) > 0)
        <div class="panel panel-default">
            <div class="panel-heading">
                Current Tasks
            </div>
            <div class="panel-body">
                <table class="table table-striped task-table">
                    <!-- Table Headings -->
                    <thead>
                        <th>Task</th>
                        <th> </th>
                    </thead>
                    <!-- Table Body -->
                    <tbody>
                        @foreach ($tasks as $task)
                            <tr>
                                <!-- Task Name -->
                                <td class="table-text">
                                    <div>{{ $task->name }}</div>
                                </td>
                                <!-- Delete Button -->
                                <td>
                                    <form action="{{ url('task/'.$task->id) }}" method="POST">
                                        {!! csrf_field() !!}
                                        {!! method_field('DELETE') !!}
                                        <button>Delete Task</button>
                                    </form>
                                </td>
                            </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>
        </div>
    @endif
@endsection

Run App On Local

After successfully building our app, we’ll be running the app locally using the following commands:

$ php artisan serve

Step 3: Dockerize Your Laravel Project

There’s a number of ways to dockerize your Laravel project. You may use the official Nginx and PHP images from Dockerhub, but we found it’s a bit troublesome to set them up.

So instead of messing around with all the different kinds of docker images, we discovered docker-images-php, a set of production-ready docker images.

In building your project, your Dockerfile should look like this:

ARG PHP_EXTENSIONS="apcu bcmath opcache pcntl pdo_mysql redis zip sockets imagick gd exif"
FROM thecodingmachine/php:7.3-v2-slim-apache as php_base
ENV TEMPLATE_PHP_INI=production
COPY --chown=docker:docker . /var/www/html
RUN composer install --quiet --optimize-autoloader --no-dev
FROM node:10 as node_dependencies
WORKDIR /var/www/html
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=false
COPY --from=php_base /var/www/html /var/www/html
RUN npm set progress=false && \
    npm config set depth 0 && \
    npm install && \
    npm run prod && \
    rm -rf node_modules
FROM php_base
COPY --from=node_dependencies --chown=docker:docker /var/www/html /var/www/html

Step 4: Create Your Kubernetes Deployment Files

There are a couple of yaml files required to set up the deployment of the Laravel project on Kubernetes:

configmap.yaml

The ConfigMap is the section of your code that stores the configuration settings for your application to work, or for a Kubernetes pod to work. Now while writing your configmap, you have to consider your App_DEBUG input on line 6 “APP_DEBUG: "false"” making sure your application is not debugged and inputting your APP_NAME on line 10 APP_NAME: "Laravel-Todo" correctly so as not to send your deployment on a wild goose chase.

apiVersion: v1
kind: ConfigMap
metadata:
  name: laravel-todo
data:
  APP_DEBUG: "false"
  APP_ENV: production
  APP_KEY: changeme
  APP_LOG_LEVEL: debug
  APP_NAME: "Laravel-Todo"
  APP_URL: https://www.laravel.com
  BROADCAST_DRIVER: pusher
  CACHE_DRIVER: redis
  QUEUE_DRIVER: redis
  SESSION_DRIVER: redis
  DB_CONNECTION: mysql
  DB_DATABASE: task
  DB_HOST: host
  DB_PASSWORD: password
  DB_PORT: "3306"
  DB_USERNAME: username
  REDIS_HOST: redis
  REDIS_PORT: "6379"

deployment.yaml

A Deployment resource uses a ReplicaSet to manage the pods. However, it handles updating them in a controlled way.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: laravel-todo
  name: laravel-todo
spec:
  minReadySeconds: 5
  replicas: 3
  revisionHistoryLimit: 1
  selector:
    matchLabels:
      app: laravel-todo
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 50%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: laravel-todo
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - backend
                topologyKey: kubernetes.io/hostname
              weight: 100
      initContainers:
        - args:
            - /bin/bash
            - -c
            - (php artisan migrate || true) && (php artisan config:cache || true) && (php
              artisan route:cache || true) && (cp -rp /var/www/html /codebase)
          envFrom:
            - configMapRef:
                name: laravel-todo-config
          image: changeme
          imagePullPolicy: Always
          name: artisan
          volumeMounts:
            - mountPath: /codebase
              name: codebase
      containers:
        - name: app
          envFrom:
            - configMapRef:
                name: laravel-todo-config
          image: changeme
          imagePullPolicy: Always
          livenessProbe:
            initialDelaySeconds: 10
            periodSeconds: 15
            tcpSocket:
              port: 80
            timeoutSeconds: 30
          ports:
            - containerPort: 80
          readinessProbe:
            initialDelaySeconds: 10
            periodSeconds: 10
            tcpSocket:
              port: 80
          resources:
            limits:
              cpu: 200m
              memory: 400M
            requests:
              cpu: 100m
              memory: 200M
          volumeMounts:
            - mountPath: /var/www
              name: codebase

      volumes:
        - emptyDir: {}
          name: codebase

ingress.yaml

Ingress brings in, and exposes, your HTTP and HTTPS routes that are outside the cluster into services that are inside the cluster.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: 100m
  labels:
    app: laravel-todo
  name: laravel-todo
spec:
  rules:
    - host: yourdomain.com
      http:
        paths:
          - laravel-todo:
              serviceName: laravel-todo
              servicePort: 80
            path: /
  # tls:
  #   - hosts:
  #       - yourdomain.com
  #     secretName: yourdomain-com-tls
# ---
# apiVersion: certmanager.k8s.io/v1alpha1
# kind: Certificate
# metadata:
#   name: yourdomain.com
# spec:
#   acme:
#     config:
#       - dns01:
#           provider: cf-dns
#         domains:
#           - yourdomain.com
#   commonName: yourdomain.com
#   dnsNames:
#     - yourdomain.com
#   issuerRef:
#     kind: ClusterIssuer
#     name: letsencrypt
#   secretName: yourdomain-com-tls

service.yaml

Service does only one thing, and that’s exposing your running applications on a specified set of pods as a network.

apiVersion: v1
kind: Service
metadata:
  labels:
    app: laravel-todo
  name: laravel-todo
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
  selector:
    app: laravel-todo
  type: ClusterIP

Step 5: Configure GitLab CI/CD

Now that we have our Laravel To-do app, Dockerfile, and Kubernetes configurations ready, go ahead and set up Gitlab Continuous Integration to automate our deployment making use of Kubernetes token authentication.

Set Up CI/CD Environment Variables

Within Repo goto Settings then CI / CD, we need to store our Kubernetes Cluster credentials into Gitlab CI’s environment variables.

Move the Dockerfile to your project’s root directory, then create a folder called k8 and store all the Kubernetes yaml files inside. Create a file called .gitlab-ci.yml that contains the following:

variables:
  DOCKER_DRIVER: "overlay2"
  REPOSITORY_URL: "url_name:latest"
stages:
  - build
  - deploy
services:
  - docker:dind
build-app:
  stage: build
  only:
    - master    
  script:
    - mkdir -p $HOME/.docker
    - apk add --no-cache curl jq python py-pip
    - pip install awscli
    - $(aws ecr get-login --no-include-email --region ap-southeast-1)
    - docker info
    - docker build -t ${REPOSITORY_URL} .
    - docker push ${REPOSITORY_URL}
deploy-app:
  stage: deploy
  image: alpine
  only:
    - master
  script:
    - apk add --no-cache curl
    - curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s 
https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
    - chmod +x ./kubectl
    - mv ./kubectl /usr/local/bin/kubectl
    - kubectl config set-cluster k8s --server="$KUBE_URL" --insecure-skip-tls-verify=true
    - kubectl config set-credentials admin --token="$KUBE_TOKEN"
    - kubectl config set-context default --cluster=k8s --user=admin
    - kubectl config use-context default
    - cd k8s && kubectl apply -f -
    - kubectl patch deployment laravel-todo -p 
"{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"date\":\"`date +'%s'`\"}}}}}"

The final step is to push your code to the repo and wait for it to be deployed automatically and without any downtime.

Step 6: Deploy And Access Your Application

After pushing your Laravel code to the repository, it’s automatically deployed. Navigate to your URL on a browser to check your application. It should look like this:

 

You can then begin to add and delete tasks (as needed) on your deployed application.

TL;DR

  • Laravel is a free, open-source PHP framework, created by Taylor Otwell and intended for the development of web applications following the model–view–controller (MVC) architectural pattern.
  • Docker is a tool designed to make it easy to create, deploy and run applications by using containers. A container packages all the code and all its dependencies as one unit for quick running from one computing environment to another.
  • There are only a couple of yaml files required to set up the deployment of the Laravel project on Kubernetes.
  • Gitlab Continuous Integration is used to automate our deployment, making use of Kubernetes token authentication.

Fonte:

- https://www.weave.works/blog/running-dockerized-laravel-applications-on-top-of-kubernetes

- https://web.archive.org/web/20231201073026/https://www.weave.works/blog/running-dockerized-laravel-applications-on-top-of-kubernetes


1

Share

Donate to Site


About Author

Renato

Developer

Add a Comment
Comments 0 Comments

No comments yet! Be the first to comment

Blog Search


Categories

OUTROS (16) Variados (109) PHP (133) Laravel (171) Black Hat (3) front-end (29) linux (114) postgresql (39) Docker (28) rest (5) soap (1) webservice (6) October (1) CMS (2) node (7) backend (13) ubuntu (56) devops (25) nodejs (5) npm (3) nvm (1) git (8) firefox (1) react (7) reactnative (5) collections (1) javascript (7) reactjs (8) yarn (0) adb (1) Solid (2) blade (3) models (1) controllers (0) log (1) html (2) hardware (3) aws (14) Transcribe (2) transcription (1) google (4) ibm (1) nuance (1) PHP Swoole (5) mysql (31) macox (4) flutter (1) symfony (1) cor (1) colors (2) homeOffice (2) jobs (3) imagick (2) ec2 (1) sw (1) websocket (2) markdown (1) ckeditor (1) tecnologia (14) faceapp (1) eloquent (14) query (4) sql (40) ddd (3) nginx (9) apache (4) certbot (1) lets-encrypt (3) debian (12) liquid (1) magento (2) ruby (1) LETSENCRYPT (1) Fibonacci (1) wine (1) transaction (1) pendrive (1) boot (1) usb (1) prf (1) policia (2) federal (1) lucena (1) mongodb (4) paypal (1) payment (1) zend (1) vim (4) ciencia (6) js (1) nosql (1) java (1) JasperReports (1) phpjasper (1) covid19 (1) saude (1) athena (1) cinnamon (1) phpunit (2) binaural (1) mysqli (3) database (42) windows (6) vala (1) json (2) oracle (1) mariadb (4) dev (12) webdev (24) s3 (4) storage (1) kitematic (1) gnome (2) web (2) intel (3) piada (1) cron (2) dba (18) lumen (1) ffmpeg (2) android (2) aplicativo (1) fedora (2) shell (4) bash (3) script (3) lider (1) htm (1) csv (1) dropbox (1) db (3) combustivel (2) haru (1) presenter (1) gasolina (1) MeioAmbiente (1) Grunt (1) biologia (1) programming (22) performance (3) brain (1) smartphones (1) telefonia (1) privacidade (1) opensource (3) microg (1) iode (1) ssh (3) zsh (2) terminal (3) dracula (1) spaceship (1) mac (2) idiomas (1) laptop (2) developer (37) api (5) data (1) matematica (1) seguranca (2) 100DaysOfCode (9) hotfix (1) documentation (1) laravelphp (10) RabbitMQ (3) Elasticsearch (1) redis (2) Raspberry (4) Padrao de design (4) JQuery (1) angularjs (4) Dicas (43) Kubernetes (3) vscode (2) backup (1) angular (3) servers (2) pipelines (1) AppSec (1) DevSecOps (4) rust (1) RustLang (1) Mozilla (1) algoritimo (1) sqlite (1) Passport (2) jwt (5) security (2) translate (1) kube (2) iot (1) politica (2) bolsonaro (1) flow (1) podcast (1) Brasil (1) containers (3) traefik (1) networking (1) host (1) POO (2) microservices (2) bug (1) cqrs (1) arquitetura (3) Architecture (4) sail (3) militar (1) artigo (1) economia (1) forcas armadas (1) ffaa (1) autenticacao (2) autorizacao (2) authentication (4) authorization (3) NoCookies (1) wsl (4) memcached (1) macos (2) unix (2) kali-linux (1) linux-tools (5) apple (1) noticias (2) composer (1) rancher (1) k8s (1) escopos (1) orm (1) jenkins (4) github (5) gitlab (3) queue (1) Passwordless (1) sonarqube (1) phpswoole (1) laraveloctane (1) Swoole (1) Swoole (1) octane (1) Structurizr (1) Diagramas (1) c4 (1) c4-models (1) compactar (1) compression (1) messaging (1) restfull (1) eventdrive (1) services (1) http (1) Monolith (1) microservice (1) historia (1) educacao (1) cavalotroia (1) OOD (0) odd (1) chatgpt (1) openai (3) vicuna (1) llama (1) gpt (1) transformers (1) pytorch (1) tensorflow (1) akitando (1) ia (1) nvidia (1) agi (1) guard (1) multiple_authen (2) rpi (1) auth (1) auth (1) livros (2) ElonMusk (2) Oh My Zsh (1) Manjaro (1) BigLinux (2) ArchLinux (1) Migration (1) Error (1) Monitor (1) Filament (1) LaravelFilament (1) replication (1) phpfpm (1) cache (1) vpn (1) l2tp (1) zorin-os (1) optimization (1) scheduling (1) monitoring (2) linkedin (1) community (1) inteligencia-artificial (2) wsl2 (1) maps (1) API_KEY_GOOGLE_MAPS (1) repmgr (1) altadisponibilidade (1) banco (1) modelagemdedados (1) inteligenciadedados (4) governancadedados (1) bancodedados (2) Observability (1) picpay (1) ecommerce (1) Curisidades (1) Samurai (1) KubeCon (1) GitOps (1) Axios (1) Fetch (1) Deepin (1) vue (4) nuxt (1) PKCE (1) Oauth2 (2) webhook (1) TypeScript (1) tailwind (1) gource (2)

New Articles



Get Latest Updates by Email