Skip to content

Instantly share code, notes, and snippets.

@unixzilla
Created November 23, 2021 03:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save unixzilla/c498f54f2bb82b2db835eae83f70ecbd to your computer and use it in GitHub Desktop.
Save unixzilla/c498f54f2bb82b2db835eae83f70ecbd to your computer and use it in GitHub Desktop.

Introduction

A software development that plans development how developer coding and testing that process is a continuous integration and then continuous delivery is a process that auto testing on staging[docker] that will marge other code committed by other developers and then deploy to production server manually OR continuous deployment is a process that auto testing and auto-deploy to a production server, aim to automatic DevOps.

How CI/CD works

every time you do git push will trigger CI operation that will follow the user script instruction (gitlab-ci.yml) to do DevOps operation.

create a git project

We use Gitlab for our repository and CI/CD pipeline controls. So we need to create a project on Gitlab first. Then open Terminal:

docker run --rm -v $(pwd):/app composer create-project --prefer-dist laravel/laravel laravel7
cd laravel7
git add .
git commit -m "initial"
git push -u origin master

docker-compose

we will use docker-compose to build on our production. we will build Laravel 7 and Nginx with Certbot that provide HTTPS on production but we will also keep HTTPS(Mkcert self sign) in the development environment. First, install docker-compose then we write a docker-compose.yml to instruct docker to build Laravel environment.

create a docker-compose.yml

vim docker-compose.yml

place following script to docker-compose.yml

version: '3'
services:

  #PHP Service
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: php
    container_name: app
    restart: unless-stopped
    tty: true
    environment:
      SERVICE_NAME: app
      SERVICE_TAGS: dev
    working_dir: /var/www
    volumes:
      - ./:/var/www
      - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
    networks:
      - app-network

  #Nginx Service
  webserver:
    image: nginx:alpine
    container_name: webserver
    restart: unless-stopped
    tty: true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./:/var/www
      - ./nginx/conf.d/:/etc/nginx/conf.d/
      - ./mkcert:/etc/ssl/certs
    depends_on:
      - mkcert
    networks:
      - app-network
  #MySQL Service
  # image: mysql:5.7.22
  db:
    image: mysql:5.7.22
    container_name: db
    restart: unless-stopped
    tty: true
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: root@password
      MYSQL_USER: root
      MYSQL_PASSWORD: mysql@password
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./dbdata/:/var/lib/mysql/
      - ./mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - app-network
  mkcert:
    build:
      context: ./mkcert
      dockerfile: Dockerfile
    container_name: mkcert
    volumes:
      - ./mkcert:/home/certuser/cert
    command: local.test

#Docker Networks
networks:
  app-network:
    driver: bridge
#Volumes
volumes:
  web-root:

create some folders for your environment

mkdir dbdata
mkdir mkcert
mkdir php
mkdir mysql
mkdir -p nginx/conf.d

create a Dockerfile for app service:

vim Dockerfile
FROM php:7.2-fpm

# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/

# Set working directory
WORKDIR /var/www

# Install dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    mariadb-client \
    libpng-dev \
    libjpeg62-turbo-dev \
    libfreetype6-dev \
    locales \
    zip \
    jpegoptim optipng pngquant gifsicle \
    vim \
    unzip \
    git \
    curl

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install extensions
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd

#for Development XDebug
RUN pecl install xdebug && docker-php-ext-enable xdebug
RUN echo '[XDebug]' >> /usr/local/etc/php/php.ini
#follow path come from pecl install xdebug messages.
RUN echo 'zend_extension="/usr/local/lib/php/extensions/no-debug-non-zts-20170718/xdebug.so"' >> /usr/local/etc/php/php.ini
RUN echo 'xdebug.default_enable=1' >> /usr/local/etc/php/php.ini
RUN echo 'xdebug.remote_host=host.docker.internal' >> /usr/local/etc/php/php.ini
RUN echo 'xdebug.remote_enable=1' >> /usr/local/etc/php/php.ini
RUN echo 'xdebug.remote_autostart=1' >> /usr/local/etc/php/php.ini
RUN echo 'xdebug.remote_handler="dbgp"' >> /usr/local/etc/php/php.ini
#/var/www/public that it is laravel public path
RUN echo 'xdebug.remote_log=/var/www/public/xdebug.log' >> /usr/local/etc/php/php.ini
RUN echo 'xdebug.remote_connect_back=0' >> /usr/local/etc/php/php.ini


# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www

# Copy existing application directory contents
COPY . /var/www

# Copy existing application directory permissions
COPY --chown=www:www . /var/www

# Change current user to www
USER www

# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]

Create the php local.ini:

vim php/local.ini

To demonstrate how to configure PHP, we’ll add the following code to set size limitations for uploaded files:

upload_max_filesize=40M
post_max_size=40M

Configuring Nginx

create the app.conf configuration file:

vim nginx/conf.d/app.conf
server {

    listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name local.test;

        server_tokens off;

        ssl_certificate /etc/ssl/certs/local.test.pem;
        ssl_certificate_key /etc/ssl/certs/local.test-key.pem;

        ssl_buffer_size 8k;

        ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
        ssl_prefer_server_ciphers on;

        ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;

        ssl_ecdh_curve secp384r1;
        ssl_session_tickets off;

        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 8.8.8.8;

    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;
    }
}


server {
        listen 80;
        listen [::]:80;
        server_name local.test;
	root /var/www/public;
	index index.html index.htm index.nginx-debian.html;
        location ~ /.well-known/acme-challenge {
          allow all;
          root /var/www/public;
        }

        location / {
                rewrite ^ https://$host$request_uri? permanent;
        }
}

Configuring MySQL

make the my.cnf file:

vim mysql/my.cnf

In the file, add the following code to enable the query log and set the log file location:

[mysqld]
general_log = 1
general_log_file = /var/lib/mysql/general.log

#self sign certificate Modify your /etc/hosts file to have the following entry

127.0.0.1 local.test

create a ./mkcert/Dockerfile for mkcert

vim mkcert/Dockerfile
FROM ubuntu:latest
MAINTAINER Unix Chan (unix@id55.com)

RUN useradd -ms /bin/bash certuser

RUN apt-get update && apt-get install -y \
    build-essential \
    libnss3-tools \
    curl \
    file \
    git

USER certuser
WORKDIR /home/certuser

RUN git clone https://github.com/Homebrew/brew ~/.linuxbrew/Homebrew \
&& mkdir ~/.linuxbrew/bin \
&& ln -s ../Homebrew/bin/brew ~/.linuxbrew/bin \
&& eval $(~/.linuxbrew/bin/brew shellenv) \
&& brew install mkcert
RUN mkdir ~/cert
WORKDIR /home/certuser/cert

ENTRYPOINT ["/home/certuser/.linuxbrew/bin/mkcert"]                                                                                                          

Creating a User for MySQL

The default MySQL installation only creates the root administrative account, which has unlimited privileges on the database server. In general, it’s better to avoid using the root administrative account when interacting with the database. Instead, let’s create a dedicated database user for our application’s Laravel database.

To create a new user, execute an interactive bash shell on the db container with docker-compose exec:

docker-compose exec db bash

Inside the container, log into the MySQL root administrative account:

mysql -u root -p

You will be prompted for the password you set for the MySQL root account during installation in your docker-compose file.

Start by checking for the database called laravel, which you defined in your docker-compose file. Run the show databases command to check for existing databases:

show databases;

Next, create the user account that will be allowed to access this database. Our username will be laraveluser, though you can replace this with another name if you’d prefer. Just be sure that your username and password here match the details you set in your .env file in the previous step:

GRANT ALL ON laravel.* TO 'laraveluser'@'%' IDENTIFIED BY 'mysql@password';

Flush the privileges to notify the MySQL server of the changes:

FLUSH PRIVILEGES;
exit;

You have configured the user account for your Laravel application database and are ready to migrate your data and work with the Tinker console.

Migrating Data and Working with the Tinker Console

With your application running, you can migrate your data and experiment with the tinker command, which will initiate a PsySH console with Laravel preloaded. PsySH is a runtime developer console and interactive debugger for PHP, and Tinker is a REPL specifically for Laravel. Using the tinker command will allow you to interact with your Laravel application from the command line in an interactive shell.

First, test the connection to MySQL by running the Laravel artisan migrate command, which creates a migrations table in the database from inside the container:

docker-compose exec app php artisan migrate

This command will migrate the default Laravel tables. The output confirming the migration will look like this:

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

Once the migration is complete, you can run a query to check if you are properly connected to the database using the tinker command:

docker-compose exec app php artisan tinker

Test the MySQL connection by getting the data you just migrated:

\DB::table('migrations')->get();

You will see output that looks like this:

=> Illuminate\Support\Collection {#2856
     all: [
       {#2862
         +"id": 1,
         +"migration": "2014_10_12_000000_create_users_table",
         +"batch": 1,
       },
       {#2865
         +"id": 2,
         +"migration": "2014_10_12_100000_create_password_resets_table",
         +"batch": 1,
       },
     ],
   }

You can use tinker to interact with your databases and to experiment with services and models.

With your Laravel application in place, you are ready for further development and experimentation.

npm install

Why npm?

We don't want to install Node.js and npm on the local machine and already run your application in a Docker container. we don't want to build a new image all the time...

Install laravel UI

docker run --rm -v $(pwd):/app composer require laravel/ui

then you can use artisan command create ui component:

docker-compose exec app php artisan ui vue --auth
docker run -it --rm -v $(pwd):/app -w /app node:latest npm install

Once the dependencies have been installed using npm install, you can compile your SASS files to plain CSS using Laravel Mix. The npm run dev command will process the instructions in your webpack.mix.js file. Typically, your compiled CSS will be placed in the public/css directory:

docker run -it --rm -v $(pwd):/app -w /app node:latest npm run dev

The webpack.mix.js file included with Laravel's frontend scaffolding will compile the resources/sass/app.scss SASS file. This app.scss file imports a file of SASS variables and loads Bootstrap, which provides a good starting point for most applications. Feel free to customize the app.scss file however you wish or even use an entirely different pre-processor by configuring Laravel Mix.

docker-compose exec app php artisan storage:link

Unit test

docker-compose exec app php artisan test

#write a gitlab-ci.yml

Some Frequencies Laravel artisan commands:

create data model

docker-compose exec app php artisan make:migration 

create controller

docker-compose exec app php artisan make:controller NewController --invokable

create middleware

docker-compose exec app php artisan make:middleware CheckAge

precompile all of the views

docker-compose exec app php artisan view:cache

You may use the session:table Artisan command to generate this migration

docker-compose exec app php artisan session:table
docker-compose exec app php artisan migrate

To create a form request class, use the make:request Artisan CLI command:

docker-compose exec app php artisan make:request StoreBlogPost

You may publish Laravel's error page templates using the vendor:publish Artisan command

docker-compose exec app php artisan vendor:publish --tag=laravel-errors

#Setup SSH private and public key for Gitlab deploy to server

#Remove docker images and containers list out all images

docker images -a

Remove images

docker rmi -f $(docker images -a -q)

list out all containers

docker ps -a -f status=exited

Remove all containers

docker rm $(docker ps -a -f status=exited -q)

#For XDebug Ref: https://www.youtube.com/watch?v=mahIIF0c8Zo

VS Code Launch.json

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for XDebug",
            "type": "php",
            "request": "launch",
            "pathMappings": {"/var/www/":"${workspaceFolder}"},
            "port": 9000
        },
        {
            "name": "Launch currently open script",
            "type": "php",
            "request": "launch",
            "program": "${file}",
            "cwd": "${fileDirname}",
            "port": 9000
        }
    ]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment