<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Shahin</title>
    <description>The latest articles on Forem by Shahin (@holyfalcon).</description>
    <link>https://forem.com/holyfalcon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F904193%2F8b6643d8-1595-483b-a0df-cd40f068e202.JPG</url>
      <title>Forem: Shahin</title>
      <link>https://forem.com/holyfalcon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/holyfalcon"/>
    <language>en</language>
    <item>
      <title>Deploy laravel project with docker swarm</title>
      <dc:creator>Shahin</dc:creator>
      <pubDate>Wed, 23 Aug 2023 07:15:52 +0000</pubDate>
      <link>https://forem.com/holyfalcon/deploy-laravel-project-with-docker-swarm-5oi</link>
      <guid>https://forem.com/holyfalcon/deploy-laravel-project-with-docker-swarm-5oi</guid>
      <description>&lt;h2&gt;
  
  
  We check three major step in this guide
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Setup laravel project with docker compose&lt;/li&gt;
&lt;li&gt;Deploy the stack to the swarm&lt;/li&gt;
&lt;li&gt;Create gitlab-ci&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup laravel project with docker compose
&lt;/h2&gt;

&lt;p&gt;we will explore the process of deploying a laravel project using docker swarm and setting up a CI/CD pipline to automate the deployment process.&lt;br&gt;
Now let’s start with containerize a laravel project with docker compose&lt;br&gt;
we need three separate service containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;em&gt;app&lt;/em&gt; service running PHP7.4-FPM;&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;db&lt;/em&gt; service running MySQL 5.7;&lt;/li&gt;
&lt;li&gt;An &lt;em&gt;nginx&lt;/em&gt; service that uses the &lt;em&gt;app&lt;/em&gt; service to parse PHP code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1. Set a env variable in project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In root directory of project we have .env file now we need to update some variable&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=experience
DB_USERNAME=experience_user
DB_PASSWORD=your-password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2. Setting up the application’s Docekrfile&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;we need to build a custom image for the application container. We’ll create a new Dockerfile for that.&lt;/p&gt;

&lt;p&gt;Docker file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM php:7.4-fpm
# Install system dependencies
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip
# Clear cache
RUN apt-get clean &amp;amp;&amp;amp; 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
# Set working directory
WORKDIR /var/www
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3. Setting up Nginx config and Database dump file&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In root directory create a new directory called docker-compose&lt;br&gt;
Now we need two other directories, a nginx directory and mysql directory&lt;br&gt;
So we have this two route in our project&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;laravel-project/docker-compose/nginx/&lt;/li&gt;
&lt;li&gt;laravel-project/docker-compose/mysql/&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In nginx directory create a file called experience.conf we write nginx config in this file like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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;
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In mysql directory create a file called init_db.init we write mysql initialization in this file like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4. Creating a multi container with docker-compose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We need a building three container that should share networks and data volumes.&lt;br&gt;
Ok so create a docker-compose file in root directory of project &lt;br&gt;
For craete a network for connecting services we define network in docker-compose file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;networks:
  experience:
    driver: bridge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;App service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app:
 build:
   context: ./
   dockerfile: Dockerfile
 image: travellist
 container_name: experience-app
 restart: unless-stopped
 working_dir: /var/www/
 volumes:
   - ./:/var/www
 networks:
   - experience
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DB service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db:
 image: mysql:8.0
 container_name: experience-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:
  - experience
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nginx service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx:
 image: nginx:1.17-alpine
 container_name: experience-nginx
 restart: unless-stopped
 ports:
  - 8000:80
 volumes:
  - ./:/var/www
  - ./docker-compose/nginx:/etc/nginx/conf.d
 networks:
  - experience
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;So our docker-compose file be like this:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.7"
services:
 app:
   build:
     context: ./
     dockerfile: Dockerfile
   image: travellist
   container_name: experience-app
   restart: unless-stopped
   working_dir: /var/www/
   volumes:
     - ./:/var/www
   networks:
     - experience

 db:
   image: mysql:8.0
   container_name: experience-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:
     - experience

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

networks:
 experience:
    driver: bridge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5. Running application with docker compose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now we can build the app image with this command:&lt;br&gt;
&lt;code&gt;$ docker-compose build app&lt;/code&gt;&lt;br&gt;
When the build is finished, we can run the environment in background mode with:&lt;br&gt;
&lt;code&gt;$ docker-compose up -d&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
Creating exprience-db   ... done
Creating exprience-app   ... done
Creating exprience-nginx ... done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to show information about the state of your active services, run:&lt;br&gt;
&lt;code&gt;$ docker-compose ps&lt;/code&gt;&lt;br&gt;
Well in these 5 simple steps, we have successfully ran our application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffmud4rp3akjet6vuvrii.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffmud4rp3akjet6vuvrii.gif" alt="Image description" width="500" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have a docker-compose file for our application that needs for using in docker swarm.&lt;/p&gt;
&lt;h3&gt;
  
  
  Let’s start Initialize docker swarm.
&lt;/h3&gt;

&lt;p&gt;After installing docker in your server&lt;br&gt;
*attention: To install Docker, be sure to use the official documentation &lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;install docker&lt;/a&gt;&lt;br&gt;
check docker information with this command:&lt;br&gt;
&lt;code&gt;$ docker info&lt;/code&gt;&lt;br&gt;
You should see “swarm : inactive” in output&lt;br&gt;
 For activate swarm in docker use this command:&lt;br&gt;
&lt;code&gt;$ docker swarm init&lt;/code&gt;&lt;br&gt;
The docker engine targeted by this command becomes a manager in the newly created single-node swarm.&lt;br&gt;
What we want to use is the services of this docker swarm.&lt;br&gt;
We want to update our service like app with docker swarm, The advantage of updating our service in Docker Swarm is that there is no need to down the app service first, update the service, and then bring the service up.&lt;br&gt;
In this method, with one command, we can give the image related to the service to Docker and give the update command. Docker raises the new service without down the old service and slowly transfers the load from the old service to the new service.&lt;br&gt;
When running Docker Engine in swarm mode, we can use docker stack deploy to deploy a complete application stack to the swarm. The deploy command accepts a stack description in the form of a Compose file.&lt;br&gt;
So we down our docker compose with this command:&lt;br&gt;
&lt;code&gt;$ docker-compose down&lt;/code&gt;&lt;br&gt;
And create our stack.&lt;/p&gt;

&lt;p&gt;ok if everything is ok until now take a rest&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4af026jux3mfyglvgig4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4af026jux3mfyglvgig4.gif" alt="Image description" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploy the stack to the swarm
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;$ docker stack deploy --compose-file docker-compose.yml &amp;lt;your-stack-name&amp;gt;&lt;/code&gt;&lt;br&gt;
For example :&lt;br&gt;
&lt;code&gt;$ docker stack deploy --compose-file docker-compose.yml staging&lt;/code&gt;&lt;br&gt;
Probably you see this in output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Creating network staging_exprience
Creating service staging_nginx
failed to create service staging_nginx: Error response from daemon: The network staging_exprience cannot be used with services. Only networks scoped to the swarm can be used, such as those created with the overlay driver.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because of “&lt;em&gt;driver: bridge&lt;/em&gt;” for deploying your service in swarm mode you must use overlay driver for network if you remove this line in your docker compose file When the stack is being deployed this network will be create on overlay driver automatically. So our docker-compose file in network section be like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;networks:
 experience:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And run upper command:&lt;br&gt;
&lt;code&gt;$ docker stack deploy --compose-file docker-compose.yml staging&lt;/code&gt;&lt;br&gt;
For now you probably you see this error :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;failed to create service staging_nginx: Error response from daemon: The network staging_experience cannot be used with services. Only networks scoped to the swarm can be used, such as those created with the overlay driver.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get network list in your docker:&lt;br&gt;
&lt;code&gt;$ docker network ls&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
NETWORK ID      NAME                   DRIVER     SCOPE
30f94ae1c94d    staging_experience     bridge     local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So your network has local scope yet because in first time deploy stack this network save in local scope and we must remove that by:&lt;br&gt;
&lt;code&gt;$ docker network rm staging_experience&lt;/code&gt;&lt;br&gt;
After all this run command:&lt;br&gt;
&lt;code&gt;$ docker stack deploy --compose-file docker-compose.yml staging&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
Creating network staging_experience
Creating service staging_app
Creating service staging_db
Creating service staging_nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now get check stack by:&lt;br&gt;
&lt;code&gt;$ docker stack ls&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:
NAME           SERVICES
staging         3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And get service list by:&lt;br&gt;
&lt;code&gt;$ docker service ls&lt;/code&gt;&lt;br&gt;
Output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0j432cgt3k9vy4nj7gw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0j432cgt3k9vy4nj7gw.png" alt="Image description" width="800" height="60"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your REPLICAS is 0/1 something wrong is your service &lt;br&gt;
For checking service status run this command:&lt;br&gt;
&lt;code&gt;$ docker service ps staging_app&lt;/code&gt;  for example&lt;br&gt;
And for check detail of service run this command:&lt;br&gt;
&lt;code&gt;$ docker service logs staging_app&lt;/code&gt;  for example&lt;br&gt;
Output of this command show you what is problem of your service.&lt;br&gt;
And for updating your a service with an image the command you need is this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ docker service update --image "&amp;lt;your-image&amp;gt;" "&amp;lt;name-of-your-service&amp;gt;" --force&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's it your docker swarm is ready for zero down time deployment :)))&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F25gkxrj4807c2fi6638r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F25gkxrj4807c2fi6638r.gif" alt="Image description" width="430" height="242"&gt;&lt;/a&gt;&lt;br&gt;
Last step for have a complete process zero down time deployment is create pipeline in gitlab.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create gitlab-ci
&lt;/h2&gt;

&lt;p&gt;In this step we want create a pipeline in gitlab for build, test and deploy a project&lt;br&gt;
So we have three stage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stages:
 - Build
 - Test
 - Deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok let’s clear what we need and what is going on in this step .&lt;br&gt;
We want update laravel project and push our change in gitlab create a new image of this changes and test that and after that log in to host server pull that updated image in server, and update service of project.&lt;br&gt;
For login to server we need define some variable in gitlab in your repository goto setting-&amp;gt;CI/CD-&amp;gt;VARIABLES   Add variable &lt;br&gt;
Add this variables:&lt;/p&gt;

&lt;p&gt;CI_REGISTRY : &lt;a href="https://registry.gitlab.com" rel="noopener noreferrer"&gt;https://registry.gitlab.com&lt;/a&gt;&lt;br&gt;
DOCKER_AUTH_CONFIG:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "auths": {
    "registry.gitlab.com": {
        "auth": "&amp;lt;auth-key&amp;gt;"
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;auth-key is base64 hash of “gitlab-username:gitlab-password”&lt;/p&gt;

&lt;p&gt;SSH_KNOWN_HOSTS:  &lt;br&gt;
Like 192.168.1.1 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCGUCqCK3hNl+4TIbh3+Af3np+v91AyW4+BxXRtHBC2Y/uPJXF2jdR6IHlSS/0RFR3hOY+8+5a/r8O1O9qTPgxG8BSIm9omb8YxF2c4Sz/USPDK3ld2oQxbBg5qdhRN28EvRbtN66W3vgYIRlYlpNyJA+b3HQ/uJ+t3UxP1VjAsKbrBRFBth845RskSr1V7IirMiOh7oKGdEfXwlOENxOI7cDytxVR7h3/bVdJdxmjFqagrJqBuYm30&lt;br&gt;
 You can see how generate ssh key in this post: &lt;a href="https://dev.to/azibom/login-to-the-server-without-password-4l55"&gt;generate sshkey&lt;/a&gt;&lt;br&gt;
SSH_PRIVATE_KEY: &lt;br&gt;
SSH_REMOTE_HOST: root@&lt;/p&gt;

&lt;p&gt;This is your variables in gitlab.&lt;br&gt;
So let’s back to gitlab-ci&lt;br&gt;
In root directory of project create a new file .gitlab-ci.yml and &lt;br&gt;
set build stage&lt;br&gt;
set test stage&lt;br&gt;
And in the last set deploy stage like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stages:
 - Build
 - Test
 - Deploy

variables:
    IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA

build:
  stage: Build
  image: docker:20.10.16
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build --pull -f Dockerfile -t $IMAGE_TAG .
    - docker push $IMAGE_TAG

preparation:
 stage: Test
 image: $IMAGE_TAG
 needs:
   - build
 script:
   - composer install
 artifacts:
   expire_in: 1 day
   paths:
     - ./vendor
 cache:
   key: ${CI_COMMIT_REF_SLUG}-composer
   paths:
     - ./vendor

unit-test:
 stage: Test
 image: $IMAGE_TAG
 services:
   - name: mysql:8
     alias: mysql-test
 needs:
   - preparation
 variables:
   APP_KEY: ${APP_KEY}
   MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
   MYSQL_DATABASE: ${MYSQL_DATABASE}
   DB_HOST: ${DB_HOST}
   DB_USERNAME: ${DB_USERNAME}
   DB_PASSWORD: ${DB_PASSWORD}
 script:
   - php vendor/bin/phpunit

staging-deploy:
 stage: Deploy
 extends:
   - .deploy-script
 variables:
   APP: "stackdemo_app"
   STACK: "travellist-staging"
 only:
   - develop
 needs:
   - unit-test
 environment:
   name: stage

.remote-docker:
 variables:
   DOCKER_HOST: ssh://${SSH_REMOTE_HOST}
 image: docker:20.10.16
 before_script:
   - eval $(ssh-agent -s)
   - echo $IMAGE_TAG
   - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
   - mkdir -p ~/.ssh
   - chmod 700 ~/.ssh
   - echo "HOST *" &amp;gt; ~/.ssh/config
   - echo "StrictHostKeyChecking no" &amp;gt;&amp;gt; ~/.ssh/config
   - echo -n $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY

.deploy-script:
 extends:
   - .remote-docker
 script:
   - cp $develop_config /root/project/core
   - docker pull $IMAGE_TAG
   - docker service update --image "$IMAGE_TAG" "$APP" --force
 dependencies: []
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change something in your project and push to gitlab and wait for it&lt;br&gt;
To see all pipeline pass like this : &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9q396h0fm11d7a1oyf3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9q396h0fm11d7a1oyf3.png" alt="Image description" width="800" height="60"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is beautiful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Finosn6s0wrqvddy6ri6r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Finosn6s0wrqvddy6ri6r.gif" alt="Image description" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>cicd</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Create Facebook Messaenger Bot</title>
      <dc:creator>Shahin</dc:creator>
      <pubDate>Wed, 24 May 2023 10:36:03 +0000</pubDate>
      <link>https://forem.com/holyfalcon/create-facebook-messaenger-bot-529n</link>
      <guid>https://forem.com/holyfalcon/create-facebook-messaenger-bot-529n</guid>
      <description>&lt;p&gt;We want to create a bot to answer users for us.&lt;br&gt;
Imagine there are many people in facebook that they want something from you, they have so many question or they want buy something from you, they need help and you should answer their question, or help for to complete their order.&lt;br&gt;
But you can't answer all question . in this time we need a Responsive bot for answering users question.&lt;br&gt;
Let's go to see how we can create a messaenger bot in facebook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hUDBbjNq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://www.insegment.com/blog/wp-content/uploads/2020/11/chatbot-marketing.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hUDBbjNq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://www.insegment.com/blog/wp-content/uploads/2020/11/chatbot-marketing.gif" alt="Image description" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In First step we need a Facebook app, so we must visit to &lt;a href="https://developers.facebook.com/"&gt;facebook developer site&lt;/a&gt; after login you can find My app In header in this page you should create your first app like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A2-hQI2z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7l5n23axilwa2rc7126o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A2-hQI2z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7l5n23axilwa2rc7126o.png" alt="Image description" width="800" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;after select a name for app like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ipT_NGjx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5m8nbngssrsk2197tx62.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ipT_NGjx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5m8nbngssrsk2197tx62.png" alt="Image description" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should be redirected to the dashboard that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gp404U6C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7z59nmvds2zooju7q9v3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gp404U6C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7z59nmvds2zooju7q9v3.png" alt="Image description" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ok now i must mention that if you just need to send a message to any user you need a set up "messenger" in this dashboard, but if you want create a conversation to user and get message process on it and send a response you need set up a "webhook" too. &lt;br&gt;
 i know, a bot that can conversation with user is more attractive so we need two setup,&lt;br&gt;
if you do not have a page you must crate page firstly&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j3LP_N4v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/loehdcjbg7sc38rgy4us.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j3LP_N4v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/loehdcjbg7sc38rgy4us.png" alt="Image description" width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and then generate token for this page.&lt;br&gt;
this token is a authorization for use facebook api and send message to users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Send a Basic Text&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To send a basic text message to a person who sent your Page a message, send a POST request to the /PAGE-ID/messages endpoint.&lt;/p&gt;

&lt;p&gt;sample request to send a message be like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST -H "Content-Type: application/json" -d '{
  "recipient":{
    "id":"PSID"
  },
  "messaging_type": "RESPONSE",
  "message":{
    "text":"Hello, world!"
  }
}' "https://graph.facebook.com/LATEST-API-VERSION/PAGE-ID/messages?access_token=PAGE-ACCESS-TOKEN"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now we don't have 'PSID' where can we find this Id, you probably know, yes from webhook that we must setup.&lt;/p&gt;

&lt;p&gt;if you want submit a webhook for the first time facebook send a post request to your webhook with some parameter, and and you should return one of those parameters that call "hub_challenge".&lt;br&gt;
 and done your webhook is set.&lt;br&gt;
ok now in messenger setting we have a webhook configuration and for get message from user we should edit that like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uU5EHwf3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/por1nftmytfqhc8zr9xo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uU5EHwf3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/por1nftmytfqhc8zr9xo.png" alt="Image description" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and set messages&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ThGgjAMG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zo51eeb1w0z5xivovl6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ThGgjAMG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zo51eeb1w0z5xivovl6z.png" alt="Image description" width="701" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;now send a message from another account to this page in facebook you get a json body like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "object": "page",
  "entry": [
    {
      "time": 1682502617664,
      "id": "0",
      "messaging": [
        {
          "sender": {
            "id": "12334"
          },
          "recipient": {
            "id": "23245"
          },
          "timestamp": "1527459824",
          "message": {
            "mid": "test_message_id",
            "text": "Hello world"
          }
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and this send id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"sender": {
            "id": "12334"
          },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;be that PSID you looking for it.&lt;/p&gt;

</description>
      <category>php</category>
      <category>facebook</category>
      <category>bot</category>
    </item>
    <item>
      <title>How many requests can we handle in Laravel?</title>
      <dc:creator>Shahin</dc:creator>
      <pubDate>Wed, 07 Sep 2022 10:42:21 +0000</pubDate>
      <link>https://forem.com/holyfalcon/how-many-requests-can-we-handle-in-laravel-3m8</link>
      <guid>https://forem.com/holyfalcon/how-many-requests-can-we-handle-in-laravel-3m8</guid>
      <description>&lt;p&gt;In this section, I will review how many requests we can handle in Laravel based on my experience.&lt;/p&gt;

&lt;p&gt;I worked on a Laravel project for a campaign. In this campaign, we wanted users come to our website and sign up for it.&lt;/p&gt;

&lt;p&gt;On the first day of the campaign, numerous users unexpectedly came to sign up, and the server came down.&lt;/p&gt;

&lt;p&gt;After this problem occurred, I researched and came across a concept called Throttle.&lt;/p&gt;

&lt;p&gt;With Throttle, also known as Rate Limiter, we can specify how many requests are allowed to come to the program in a certain period.&lt;/p&gt;

&lt;p&gt;For example, we have a form, and the user can only register this form ten times in 1 minute, and if it is more than ten times, he must face HTTP error 429, "Too Many Requests." This means more than the number of requests allowed in a period. For this, Laravel has an internal middleware called Throttle, which we can be used by setting it on the routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Route::post('login', '...')-&amp;gt;middleware('throttle:10,1');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The part "throttle:10,1" means that the user can send ten requests to the specified route in 1 minute. The request for more than the specified value will not be checked and will be blocked with status code 429.&lt;/p&gt;

&lt;p&gt;There is another way;&lt;br&gt;
Every Laravel project inside the &lt;code&gt;app/Http/Kernel.php&lt;/code&gt; file has a default throttle limit for all API routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected $middlewareGroups = [
    ...
    'api' =&amp;gt; [
        'throttle:60,1',
    ],
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;burst inserts&lt;/strong&gt;&lt;br&gt;
Another reason that may cause problems, as I mentioned in the previous article, is the connection with the database.&lt;br&gt;
Well, it is clear that when you create a panel for registration or user login, you must have a connection with the database, the issue you must pay attention to is controlling this connection.&lt;br&gt;
For example, if you want to insert the information of 10,000 users in the table, you will probably face a problem and get the Too Many Request error.&lt;br&gt;
The proposed solution for large and sudden insertions in a short period of time is to use the queue.&lt;/p&gt;

&lt;p&gt;By changing the configuration of this file, we can control how many requests must be handled per minute.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>webserver</category>
      <category>throttle</category>
    </item>
    <item>
      <title>Limits of sending requests for getting the correct response</title>
      <dc:creator>Shahin</dc:creator>
      <pubDate>Wed, 24 Aug 2022 07:34:00 +0000</pubDate>
      <link>https://forem.com/holyfalcon/limits-of-sending-a-request-for-getting-the-correct-response-5fij</link>
      <guid>https://forem.com/holyfalcon/limits-of-sending-a-request-for-getting-the-correct-response-5fij</guid>
      <description>&lt;p&gt;In this article, I will check the limits of sending a request in a project. We will also have a general review of different layers to get more familiar with the nature of the request and response.&lt;/p&gt;

&lt;p&gt;So let us first see what route a request from the user takes to reach our project:&lt;/p&gt;

&lt;p&gt;When a user wants to view a website in a browser or register on a specific website, he must send a request to the server through his browser and wait for a response.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V68Na_I7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cs4oqbv70olm0dn4o2oz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V68Na_I7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cs4oqbv70olm0dn4o2oz.gif" alt="Image description" width="640" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The request that the user sends determines how the server should respond; if the user wants to read or see information from the server, he should use the "GET" method, but if he wants to send information to the server, he should use the "POST" method. In most cases, these requests proceed from the user to the server on the "HTTP" protocol.&lt;/p&gt;

&lt;p&gt;Imagine you have a site with many active users containing various functions. These functions can be storing and maintaining user information, buying a product, making a transaction, creating and receiving an Excel file on your site, and many other activities you may have seen so far. &lt;br&gt;
Suppose we want to look at these users' activities from a developer's vision. In that case, we know that most of these events must be handled with the database. We also know that our code must be connected to the database, put specific data in it, and show it to the user.&lt;/p&gt;

&lt;p&gt;So, I will divide my project into two parts for further review:&lt;/p&gt;

&lt;p&gt;1- Logic of the project&lt;/p&gt;

&lt;p&gt;2- Connecting to the database&lt;/p&gt;

&lt;p&gt;The project's logic includes conditions, loops, arrays, etc., related to the developer's thinking. The performance may decrease due to the complexity of implementing a project. Alternatively, it may fail in a part of the project in general.&lt;/p&gt;

&lt;p&gt;Connecting to the database and reading and writing to it is essential to our codes. Each connection to the database is an important weight on our project that we must be aware of and ensure that the number of these weights does not increase.&lt;/p&gt;

&lt;p&gt;Meaning, that if we connect to the database through our project, it is better to close the connection when we are done.&lt;/p&gt;

&lt;p&gt;Suppose we want to read a large number of records from the database. In that case, it is better to divide this information and read it with several different queries at different times. It is clear that if, for instance, we want to read a million records from the database with a query, we put a heavy overhead on the project, and most likely, the project will crash in the middle of the work.&lt;/p&gt;

&lt;p&gt;Alternatively, if we want to insert one hundred thousand records into the database in one move with a loop, we will most likely encounter the status code 429, which indicates the "Too Many Request" error.&lt;/p&gt;

&lt;p&gt;In this situation, it is better to use a queue. The queue performs the inserting process one by one, regularly and gradually. After inserting record number one, it goes to record number two.&lt;/p&gt;

&lt;p&gt;Different languages and frameworks have provided methods for handling requests to prevent the application from crashing.&lt;/p&gt;

&lt;p&gt;In the next chapter of this title, I want to check the limits of requests in the laravel project.&lt;/p&gt;

</description>
      <category>throttle</category>
      <category>php</category>
      <category>web</category>
    </item>
  </channel>
</rss>
