Tuesday, 5 September 2023

Laravel CI/CD with Jenkins, Docker, AWS

1. Install docker, docker compose

- add current $USER to docker group

sudo usermod -aG docker $USER

2. Download source code and build in local using docker-compose

https://github.com/thecaringdeveloper/laravel-cicd-tutorial (1)

docker-compose.yml

3. Install, start Jenkins

- install java

- install jenkins

- start jenkins

- access jenkins localhost:8090

- add user jenkins to docker group: sudo usermod -aG docker jenkins

- add plugin for jenkins

+ Pipeline Utility Steps
+ File Operations
+ SSH Agent

4. Create github repository

- push code in (1) to repository

5. Setup pipeline in source local (a job)

- create Makefile

- create Jenkinsfile

- create a job:

 Enter an item name/ Pipeline 

/ General: Testing CICD

/ Pipe script from SCM

/ Repository URL: get from repository in github

/ Credentials select credential create below

/ Branch: select branch in git repository

/ Script path: Jenkinsfile

6. create Credential for jenkins connect to github

Manage Jenkins/ manage credentials/ global /add credentials

    / Kind: SSH username with private key 

    / scope: Global (jenkins, nodes, items, all child items, ect)

    / id: github

    / username: your_github_username

    / create private, public key: private_key for jenkins, public_key  for github

    / Create

* Create a job for CICD

    / name: laravel-test

    / Pipeline: Piple script from SCM

    / SCM: git

    / Repository Url: Your_repo_url

    / Credentials:credential already create above

    / Script path: jenkinsfile 

7. Populate .env by using jenkin

+ copy .env file to jenkin: 

    / create folder in workspace jenkins include .env file: var/lib/jenkins/workspace/envs/laravel-test

    / create .env file in var/lib/jenkins/workspace/envs/laravel-test

    / add script to copy this .env file when deploy 

stage("Populate .env file") {
steps {
dir("/var/lib/jenkins/workspace/envs/LaravelTest02") {
fileOperations([fileCopyOperation(excludes: '', flattenFiles: true, includes: '.env', targetLocation: "${WORKSPACE}")])
}
}
}

- Run pipeline

6. Setup EC2

- get key-ec2.pem to ssh to ec2

- chmode  400 key-ec2.pem

- create credential for ec2 in jenkins

7. Create Artifact 

Create artifact.zip file

8. Copy Artifact artifact.zip file to ec2

update Jenkinsfile to copy Artifact artifact.zip file to ec2

9. Deploy on ec2, install server on ec2

- install server: sudo yum -y install httpd

- start server: sudo service httpd start

- edit inbound, outbound rules

- install php and extension: sudo yum install php-{...}

- set permission: sudo chown ec2-user:ec2-user /var/www/html

- sudo server httpd restart

Jenkinsfile

pipeline {
agent any
stages {
stage("Verify SSH connection to server") {
steps {
sshagent(credentials: ['aws-ec2']) {
sh '''
ssh -o StrictHostKeyChecking=no ec2-user@52.221.199.29 whoami
'''
}
}
}
stage("Verify tooling") {
steps {
sh '''
docker info
docker version
docker compose version
'''
}
}
stage("Clear all running docker containers") {
steps {
script {
try {
sh 'docker rm -f $(docker ps -a -q)'
} catch (Exception e) {
echo 'No running container to clear up...'
}
}
}
}

stage("Start Docker") {
steps {
sh 'make up'
sh 'docker compose ps'
}
}

stage("Run Composer Install") {
steps {
sh 'docker compose run --rm composer install'
}
}

stage("Populate .env file") {
steps {
dir("/var/lib/jenkins/workspace/envs/LaravelTest02") {
fileOperations([fileCopyOperation(excludes: '', flattenFiles: true, includes: '.env', targetLocation: "${WORKSPACE}")])
}
}
}

stage("Run Tests") {
steps {
sh 'docker compose run --rm artisan test'
}
}
}

post {
success {
sh 'cd "/var/lib/jenkins/workspace/LaravelTest02"'
sh 'rm -rf artifact.zip'
sh 'zip -r artifact.zip . -x "*node_modules**"'
withCredentials([sshUserPrivateKey(credentialsId: "aws-ec2", keyFileVariable: 'keyfile')]) {
sh 'scp -v -o StrictHostKeyChecking=no -i ${keyfile} /var/lib/jenkins/workspace/LaravelTest02/artifact.zip ec2-user@52.221.199.29:/home/ec2-user/artifact'
}
sshagent(credentials: ['aws-ec2']) {
sh 'ssh -o StrictHostKeyChecking=no ec2-user@52.221.199.29 unzip -o /home/ec2-user/artifact/artifact.zip -d /var/www/html'
script {
try {
sh 'ssh -o StrictHostKeyChecking=no ec2-user@52.221.199.29 sudo chmod 777 /var/www/html/storage -R'
} catch (Exception e) {
echo 'Some file permissions could not be updated.'
}
}
}
}
always {
sh 'docker compose down --remove-orphans -v'
sh 'docker compose ps'
}
}
}

Makefile

#!/usr/bin/make

SHELL = /bin/sh

UID := $(shell id -u)
GID := $(shell id -g)
USER:= $(shell whoami)

export UID
export GID
export USER

up:
docker compose up -d

Thank you

 

Git merge, reset, commit --amend, rebase, pick cherry, handle conflict

There are two branch
commit on branch A: 123|4.......5
commit on branch B: 123|...678 (checkout from branh A from commit 3, after add commit 678)
---------------------------------------------------
* Git merge: 
add commits from branch B to branch A follow time create commit
- git checkout A
- git merge B
- handle conflict commits: 3 5 8
- git add .
- git commit -m"M"
=> A: 123|4|678|5|M
--------------------------------------------------
* Git rebase: 
add all commits from branch B to branch A at left side of branch A from postion of commit of both branch
- git checkout A
- git rebase B
- handle conflict commit: 8 to 4 -> git add . -> git rebase --continue => A: 123|678|4
- handle conflict commit: 4 to 5 -> git add . -> git rebase --continue => A: 123|678|45
- alert result rebase, handle commits with p,s,.. can change name commit in nano, ex 4 => 4'
- ctrl+x -> y to save
=> A: 123|678|4'5
* git rebase itself: select commit want to keep
- git rebase -i idCommit
- git checkout A (A: 12345)
- git rebsae -i 2
- handle conflict commit: 2 to 3 -> git add . -> git rebase --continue => A: 12|3
- handle conflict commit: 3 to 4 -> git add . -> git rebase --continue => A: 12|34
- handle conflict commit: 4 to 5 -> git add . -> git rebase --continue => A: 12|345
- alert result rebase, handle commits with p,s,.. can change name commit in nano, ex 3 => 3', 4->4'
- ctrl+x -> y to save
=> A: 12|3'4'5
---------------------------------------------------
* Git reset: remove commit from a branch 
+ git reset --soft idCommit  # Removes from HEAD to idCommit, keeps changed staged
+ git reset --hard idCommit # Removes from HEAD to idCommit, DONT keeps changed staged
---------------------------------------------------
* Git commit --amend: to change a last commit message.
+ git commit --amend -m "........"
+ git push origin BranchName -f
* Note: 
 
- Have branh A, PR A to DEV, PM agree merge
- if you commit --amend branch A, after PR A to DEV -> Conflict
---------------------------------------------------

* Git pick cherry: pick commit from a Branch to add for orther branch
- git checkout A
- git cherry-pick -e 7 (git cherry-pick -e idCommit)
-> A: 12345|7
or
- git checkout B
- git cherry-pick -e 5 (git cherry-pick -e idCommit)
-> B: 123678|5
---------------------------------------------------
* Git handle conflict when PR
- git checkout A
- git add .
- git commit -m"..."
- git push origin A
- PR: from A to DEV -> conflict
=> handle
- git checkout A
- git checkout -b A-fix-conflict // create this branch to fix-conflict // because dont want get code from DEV to merge to A
- git checkout DEV
- git pull origin DEV
- git checkout A-fix-conflict
- git merge DEV
- handle fix conflict in local
- git push origin A-fix-conflict
- PR from A-fix-conflict to DEV 
- PM aggree merge
=> now A can PR to DEV


Thank you


 

Sunday, 20 August 2023

Compare React Hooks

* useEffect

Guide url
- The useEffect Hook allows you to perform side effects (tác vụ liên quan đến hiệu suất) in your components, ex: fetching data, directly updating the DOM, and timers.

useEffect(() => {
//Runs on every render, dependencies = null
});
 
useEffect(() => {
//Runs only on the first render, dependencies = []
}, []);
 
useEffect(() => {
//Runs on the first render, dependencies = [prop, state]
//And any time any dependency value changes
}, [prop, state]);
 
useEffect(() => {
// Thực hiện tác vụ sau mỗi lần render
return () => {
// Thực hiện tác vụ khi component bị unmount hoặc dependencies thay đổi
};
}, [prop, state]);
 

* useMemo

Guide Url
- use to optimize performance by returning a memoized value.
- when render, if dependencies:
+ change: will calculate again
+ not change: will not calculate => optimize

const calculation = useMemo(
() => expensiveCalculation(count),
[/* dependencies */]
);
 
<h2>Expensive Calculation {calculation}</h2>

* useCallback

Guide Url
- use to optimize performance by returning a memoized callback function.
- when render, if dependencies:
+ change: will create new version callback function
+ not change: will not create new version callback function
=> do not rerender component use this callback function => optimize

const memoizedCallback = useCallback(() => {
// Logic xử lý khi click
}, [/* dependencies */]);
 
 
<button onClick={memoizedCallback}>Click me</button>;

Thank you


 

Saturday, 8 July 2023

Guide to install Apache2, Php 8.1, MySql 5.6, Mssql driver

1. Install Apache2


Let’s begin by updating the local package index to reflect the latest upstream changes:
sudo apt update
Then, install the apache2 package:
sudo apt install apache2
Check with the systemd init system to make sure the service is running by typing:
sudo systemctl status apache2
When you have your server’s IP address, enter it into your browser’s address bar:
http://your_server_ip/
 


* Errors can happen:

-  Server not open port (manual with ufw)

-  Server not open port (on control pannel)  

2. Setup Virtual Hosts for project


Create the Directory Structure
sudo mkdir -p /var/www/example.com/public
Grant Permissions
Now we have the directory structure for our files, but they are owned by our root user. If we want our regular user to be able to modify files in our web directories, we can change the ownership by doing this:
sudo chown -R $USER:$USER /var/www/example.com/public
We should also modify our permissions to ensure that read access is permitted to the general web directory and all of the files and folders it contains so that pages can be served correctly:
sudo chmod -R 755 /var/www
Create file index.html
nano /var/www/example.com/public/index.html
<html>
<head>
<title>Welcome to example.com!</title>
</head>
<body>
<h1>Success! The example.com virtual host is working!</h1>
</body>
</html>
Create New Virtual Host Files
Start by copying the file for the first domain:
sudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/example.com.conf
Open the new file in your editor with root privileges:
sudo nano /etc/apache2/sites-available/example.com.conf
When complete, our virtual host file should look like this:
/etc/apache2/sites-available/example.com.conf
<VirtualHost *:80>
ServerAdmin admin@example.com
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com/public
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Enable the New Virtual Host Files
sudo a2ensite example.com.conf
Next, disable the default site defined in 000-default.conf:
sudo a2dissite 000-default.conf
sudo a2ensite ec-cube-dev.cdb-lab1.com.conf
enter ip into your browser’s address bar:
http://your_server_ip/
 

3. Install php 8.1

Run the following commands to update your list of available packages, then then install PHP 8.1:
sudo apt update
sudo apt install --no-install-recommends php8.1
Check your PHP version information with the following command:
php -v
Install package needed for php 8.1
sudo apt install php8.1-{ctype,pdo,tokenizer,cli,gd,curl,mysql,ldap,zip,fileinfo,fpm,xml,mbstring,exif,pspell,imagick,bcmath}
install the Apache module for PHP 8.1 for apache2 using php8.1
sudo apt install libapache2-mod-php8.1
Install sqlsrv driver for php 8.1 to connect mssql
sudo apt install php8.1-dev
sudo update-alternatives --set php /usr/bin/php8.1
sudo update-alternatives --set php-config /usr/bin/php-config8.1
sudo update-alternatives --set phpize /usr/bin/phpize8.1
sudo pecl install -f sqlsrv
sudo pecl install -f pdo_sqlsrv
sudo phpenmod -v 8.1 sqlsrv pdo_sqlsrv
sudo service apache2 restart
 

4. Install Mysql Server 5.6

Server: <IP server>

User: root

Password: <password> 

 
Download version 5.6.46 from MySQL site
wget https://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.46-linux-glibc2.12-x86_64.tar.gz
Add mysql user group
sudo groupadd mysql
Add mysql (not the current user) to mysql user group
sudo useradd -g mysql mysql
Extract it
sudo tar -xvf mysql-5.6.46-linux-glibc2.12-x86_64.tar.gz
Move it to /usr/local
sudo mv mysql-5.6.46-linux-glibc2.12-x86_64 /usr/local/
Create mysql folder in /usr/local by moving the untarred folder
cd /usr/local
sudo mv mysql-5.6.46-linux-glibc2.12-x86_64 mysql
set MySql directory owner and user group
cd mysql
sudo chown -R mysql:mysql *
Install the required lib package
sudo apt-get install libaio1 libncurses5
Execute mysql installation script
sudo scripts/mysql_install_db --user=mysql
Set mysql directory owner from outside the mysql directory
sudo chown -R root .
Set data directory owner from inside mysql directory
sudo chown -R mysql data
Copy the mysql configuration file
sudo cp support-files/my-default.cnf /etc/my.cnf
Start mysql
sudo bin/mysqld_safe --user=mysql & sudo cp support-files/mysql.server /etc/init.d/mysql.server
Set root user password
sudo bin/mysqladmin -u root password '[your new password]'
Add mysql path to the system
sudo ln -s /usr/local/mysql/bin/mysql /usr/local/bin/mysql
Start mysql server
sudo /etc/init.d/mysql.server start
Stop mysql server
sudo /etc/init.d/mysql.server stop
Check status of mysql
sudo /etc/init.d/mysql.server status
Now login using below command, start mysql server if it's not running already
mysql -u root -p

Create new User, DB for Mysql 

// handle with root user 
mysql -u root -p
mysql root {$password}
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY {$password};
// add new user, db
CREATE USER '{$user_name}'@'localhost' IDENTIFIED BY {$password};  
ALTER USER '{$user_name}'@'localhost' IDENTIFIED BY {$new_password};
CREATE DATABASE IF NOT EXISTS '{$db_name}';
GRANT ALL ON {$db_name}.* TO '{$user_name}'@'localhost';
FLUSH PRIVILEGES  

Thank you


Publish npm package

  Để publish   pav-kit  lên NPM, bạn hãy làm theo các bước dưới đây. Tôi đã tạo thêm file  index.js  để đảm bảo gói tin hợp lệ. Bước 1: Tạo ...