Build Docker Laravel App with Jenkins

Hi everyone, this article takes you through building a simple react static app using Jenkins. 




Step 1 : I create a folder with name : laravel-app

composer create-project laravel/laravel laravel-app

Step 2 : I create a file Dockfile in folder laravel-app

cd laravel-app
nano Dockerfile


FROM php:8.1-fpm

# Set working directory
WORKDIR /var/www

# Add docker php ext repo
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

# Install php extensions
RUN chmod +x /usr/local/bin/install-php-extensions && sync && \
install-php-extensions mbstring pdo_mysql zip exif pcntl gd memcached

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

# Install supervisor
RUN apt-get install -y supervisor

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

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

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

# Copy code to /var/www
COPY --chown=www:www-data . /var/www

# add root to www group
RUN chmod -R ug+w /var/www/storage

# Copy nginx/php/supervisor configs
RUN cp docker/supervisor.conf /etc/supervisord.conf
RUN cp docker/php.ini /usr/local/etc/php/conf.d/app.ini
RUN cp docker/nginx.conf /etc/nginx/sites-enabled/default

# PHP Error Log Files
RUN mkdir /var/log/php
RUN touch /var/log/php/errors.log && chmod 777 /var/log/php/errors.log

# Deployment steps
RUN composer install --optimize-autoloader --no-dev
RUN chmod +x /var/www/docker/run.sh

EXPOSE 80
ENTRYPOINT ["/var/www/docker/run.sh"]





Step 3 : I create a file Jenkinsfile

nano Jenkinsfile


pipeline {
agent any
environment {
DOCKERHUB_CREDENTIALS = credentials('dockerhub')
}
stages {
stage('Clone') {
steps {
git branch: 'develop', credentialsId: 'github', url: 'https://github.com/khaitk/laravel-docker-jenkins.git'
}
}

stage('Build Docker Image') {
steps{
sh 'docker build -t khraiteka/laravel-jenkins-docker:$BUILD_NUMBER .'
echo 'Build Image Completed'
}
}

stage('Login') {
steps {
sh 'echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin'
}
}
stage('Push') {
steps {
sh 'docker push khraiteka/laravel-jenkins-docker:$BUILD_NUMBER'
echo 'Push Image Completed'
}
}
}
post {
failure {
mail to: 'khaitkdev@gmail.com',
subject: "FAILED: Build ${env.JOB_NAME}",
body: "Build failed ${env.JOB_NAME} build no: ${env.BUILD_NUMBER}.\n\nView the log at:\n ${env.BUILD_URL}\n\nURL:\n${env.RUN_DISPLAY_URL}"
}
success{
mail to: 'khaitkdev@gmail.com',
subject: "SUCCESSFUL: Build ${env.JOB_NAME}",
body: "Build Successful ${env.JOB_NAME} build no: ${env.BUILD_NUMBER}\n\nView the log at:\n ${env.BUILD_URL}\n\nURL:\n${env.RUN_DISPLAY_URL}"
}
aborted{
mail to: 'khaitkdev@gmail.com',
subject: "ABORTED: Build ${env.JOB_NAME}",
body: "Build was aborted ${env.JOB_NAME} build no: ${env.BUILD_NUMBER}\n\nView the log at:\n ${env.BUILD_URL}\n\nURL:\n${env.RUN_DISPLAY_URL}"
}
}
}



Step 4 : Push source code to github

Step 5 : Config account github and dockerhub at Jenkins UI
Dir : Dashboard => Manage Jenkins => Credentials => System => Global Credential => Add Credentials



Link get token:


Step 6 : Create a Pipeline on Jenkins UI

- You select New Item



- Filter name for your pipeline => click Pipeline => click OK



- Fill url your repository and click check box Github hook trigger for GITScm polling



- Fill in the information like the image below


Step 7 : Click button Build Now, and this's result




Good luck!

Share:

Best 2 CI/CD Tools Me Used

 


Hey anh em, hôm nay mình xin chia sẻ 2 công cụ CI/CD mà mình đã học, đã áp dụng vào các project nó giúp ích cho mình như thế.

Thì hôm nay bài viết này, mình sẽ giới thiệu đến đó là Circle CI & Jenkins.


1. CircleCI


CircleCI là một công cụ CI/CD hỗ trợ phát triển và xuất bản phần mềm nhanh chóng.

CircleCI cho phép tự động hóa trên toàn bộ quy trình của người dùng, từ code building, testing to deployment.

Bạn có thể tích hợp CircleCI với GitHub, GitHub Enterprise và Bitbucket.

CircleCI cũng tổ chức tích hợp liên tục.

Các tính năng chính của CircleCI:

  • Tích hợp với Bitbucket, GitHub và GitHub Enterprise
  • Chạy các bản dựng bằng bộ chứa hoặc virtual machine
  • Easy debugging
  • Quick tests
  • Continuous and branch-specific deployment
  • Tự động hợp nhất
  • Thiết lập nhanh và xây dựng không giới hạn.

Trang chủ : https://circleci.com/

2. Jenkins


Jenkins là một máy chủ tự động hóa nguồn mở, trong đó xây dựng và quá trình tích hợp liên tục diễn ra. Đây là một chương trình dựa trên Java độc lập với các gói dành cho Windows, macOS và các hệ điều hành giống Unix khác. Với hàng trăm plugin có sẵn, Jenkins hỗ trợ xây dựng, triển khai và tự động hóa các dự án phát triển phần mềm.

Các tính năng chính của Jenkins:

  • Dễ dàng cài đặt và nâng cấp trên nhiều hệ điều hành khác nhau
  • Giao diện đơn giản và thân thiện với người dùng
  • Có thể mở rộng với tài nguyên plugin khổng lồ do cộng đồng đóng góp
  • Cấu hình môi trường dễ dàng trong giao diện người dùng
  • Hỗ trợ các bản dựng phân tán với kiến trúc master-slave
  • Xây dựng lịch trình dựa trên các biểu thức
  • Hỗ trợ thực thi lệnh shell và Windows trong các bước xây dựng trước
  • Hỗ trợ thông báo về trạng thái build.

- Cảm ơn bạn đã đọc <3 !
- Hãy theo dõi bài viết mình! Sắp tới mình có những chia sẻ về những step integrated with github.

Share:

laravel react websocket - part 2



Xin chào bạn

Bạn đã setup hết cho server trong part 1 ở bài viết Laravel react websocket này chưa, nếu chưa thì quay lại nhé

Hôm nay, mình xin chia sẻ đến bạn phần client, ở đây mình sẽ sử dụng react js để làm ví dụ.

Đầu tiên, mình sẽ tạo một project tên là ' client-app'

npx create-react-app client-app

Cài đặt một số thư viện liên quan:

cd client-app
npm i pusher-js laravel-echo

Tại file App.js

import React from 'react';
import Chat from "./Chat";

function App () {
return (
<div className="App">
<Chat/>
</div>
);
}

export default App;

Tại folder src tạo một component tên là 'Chat.js'

import React, {useEffect} from 'react';
import Echo from 'laravel-echo';

window.Pusher = require('pusher-js');

const Chat = () => {
useEffect(() => {
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'khaitk',
wsHost: window.location.hostname,
wsPort: 6001,
forceTLS: false,
disableStats: true,
cluster : "mt1"
});

window.Echo.channel('chat-listen').subscribed(() => {
console.log('subscribe');
}).listen('ChatEvents', (e) => {
console.log(e);
})
}, []);
async function handleSendMessage(e) {
const data = {
"id": "1",
"channel_id": 1678337641615,
"message" : 'Hey girl'
}
e.preventDefault();
const socket = new WebSocket(`ws://localhost:6001/app/khaitk/chat`)
socket.onopen = (e) => {
socket.send(JSON.stringify({
...data
}))
console.log('open');
}
}

return (
<div>
<button onClick={(e) => handleSendMessage(e)}>Send</button>
</div>
);
}

export default Chat;

Thế là phía client đã setup xong, bắt đầu test kết quả thôi nào.

Let's go.....! Run lệnh : npm start

Khi màn hình đã hiển thị lên ấn phím f12 và click button Send để check kết quả dưới console nhé...

Yeah..... Thế là có chút gì đó realtime rùi phải không nào.

Hãy lên ý tưởng code một ứng dụng realtime sử dụng Laravel vs React để phát triển thôi.

Good luck....!

Lưu ýNếu có lỗi gì hãy cmt ở bài viết này.

Share:

laravel react websocket- part 1

Xin chào bạn
Bạn là một lập trình viên PHP, muốn làm ứng dụng realtime, thì post này nó sẽ dành cho bạn nhé.
Không nói lòng vòng nữa, vào vấn đề chính luôn nhé.
Bạn đã sẵn sàng rồi chứ! Bây giờ mình sẽ hướng dẫn từng bước để config, và test nó nha.


Đầu tiên, bạn hãy tạo một source laravel nhé.
Mình sẽ đặt tên của nó là back-end :

composer create-project laravel/laravel back-end

Bật VS Code mở file .env config một số cái ở đây

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3006
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=123456
BROADCAST_DRIVER=pusher
QUEUE_CONNECTION=database
PUSHER_APP_ID=khaitk
PUSHER_APP_KEY=khaitk
PUSHER_APP_SECRET=khaitk
PUSHER_HOST=khaitk
PUSHER_PORT=443
PUSHER_SCHEME=http
PUSHER_APP_CLUSTER=mt1
và đi vào folder back-end, ở đây mình sẽ cài đặt một số thư viện cho project :

cd back-end

composer require beyondcode/laravel-websockets

php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider"
--tag="migrations"

php artisan migrate

php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider"
--tag="config"

composer require pusher/pusher-php-server

php artisan queue:table

php artisan migrate

php artisan make:event ChatEvents

- Tiếp theo, bạn vào đường dẫn này ' back-end/config/app.php ' bỏ comment ở dòng này
App\Providers\BroadcastServiceProvider::class,
Tại file này ' back-end/config/broadcasting.php 
'pusher' => [
'driver' => 'pusher',
'key' => env( 'PUSHER_APP_KEY' ),
'secret' => env( 'PUSHER_APP_SECRET' ),
'app_id' => env( 'PUSHER_APP_ID' ),
'options' => [
'cluster' => env( 'PUSHER_APP_CLUSTER' ),
'useTLS' => env( 'PUSHER_APP_USETLS' ),
'encryted' => true,
'host' => '127.0.0.1',
'port' => 6001,
'scheme' => 'http',
],
'client_options' => [],
],

Tại file này 'back-end/config/websockets.php
'apps'                   => [
[
'id' => env( 'PUSHER_APP_ID' ),
'name' => env( 'APP_NAME' ),
'key' => env( 'PUSHER_APP_KEY' ),
'secret' => env( 'PUSHER_APP_SECRET' ),
'capacity' => null,
'enable_client_messages' => true,
'enable_statistics' => true,
],
],
Tại file này 'back-end/app/Events/ChatEvents.php

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class ChatEvents implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;

public $data;

public function __construct($data)
{
$this->data = $data;
}

public function broadcastWith(): array
{
return [
'data' => $this->data
];
}

public function broadcastOn(): Channel
{
return new Channel('chat-listen');
}
}


- Ở đây bạn tạo một file trong folder cùng cấp với app/ => app/Websocket
- Và tạo một file trong app/Websocket/ChatWebSocketHandler.php

<?php

namespace App\Websocket;

use App\Events\ChatEvents;
use BeyondCode\LaravelWebSockets\Apps\App;
use BeyondCode\LaravelWebSockets\Dashboard\DashboardLogger;
use BeyondCode\LaravelWebSockets\Facades\StatisticsLogger;
use BeyondCode\LaravelWebSockets\QueryParameters;
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\UnknownAppKey;
use BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler;
use Illuminate\Http\Request;
use Ratchet\ConnectionInterface;
use Ratchet\RFC6455\Messaging\MessageInterface;

class ChatWebSocketHandler extends WebSocketHandler{
public function onOpen( ConnectionInterface $connection ) {
$connection->socketId = $socketId = sprintf( '%d.%d',
random_int( 1, 1000000000 ), random_int( 1, 1000000000 ) );
$connection->app = new \stdClass();
$connection->app->id = env( 'APP_NAME' );

$connection->send( json_encode( [
'status' => 200,
'message' => 'Opened',
] ) );
}

public function onClose( ConnectionInterface $connection ) {
// TODO: Implement onClose() method.
}

public function onError( ConnectionInterface $connection,
\Exception $e ) {
// TODO: Implement onError() method.
}

public function onMessage( ConnectionInterface $connection,
MessageInterface $msg ) {
$request = new Request();
$body = json_decode( $msg->getPayload(), true );
$request->replace( $body );

if ( $this->chatSocket( $request ) ) {
$connection->send( json_encode( [
'status' => 200,
'message' => 'Successful',
] ) );
} else {
$connection->send( json_encode( [
'status' => 500,
'message' => 'Fail',
] ) );
}
}

protected function verifyAppKey( ConnectionInterface $connection ) {
$appKey = QueryParameters::create( $connection->httpRequest )
->get( 'appKey' );
dump( $appKey );
if ( ! $app = App::findByKey( $appKey ) ) {
throw new UnknownAppKey( $appKey );
}

$connection->app = $app;

return $this;
}

protected function generateSocketId( ConnectionInterface $connection ) {
$socketId = sprintf( '%d.%d', random_int( 1, 1000000000 ),
random_int( 1, 1000000000 ) );

$connection->socketId = $socketId;

return $connection;
}

protected function establishConnection( ConnectionInterface $connection ) {
$connection->send( json_encode( [
'event' => 'pusher:connection_established',
'data' => json_encode( [
'socket_id' => $connection->socketId,
'activity_timeout' => 30,
] ),
] ) );

DashboardLogger::connection( $connection );

StatisticsLogger::connection( $connection );

return $this;
}

private function chatSocket( $request ) {
try {
event( new ChatEvents( $request->all() ) );

return true;
} catch ( \Exception $e ) {
return false;
}
}
}


- Trong file routes/web.php
<?php

use App\Websocket\ChatWebSocketHandler;
use BeyondCode\LaravelWebSockets\Facades\WebSocketsRouter;


WebSocketsRouter::webSocket( 'app/khaitk/chat', ChatWebSocketHandler::class );

- Thế là phía server đã hoàn tất các bước, bạn đã sẵn sàng run source code của mình chưa nào.....!
- Run song song 3 lệnh này :

php artisan serve

php artían websockets:serve

php artisan queue:work

-- Bùm, có lỗi không nào, hãy cmt cho mình biết nhé.....! Good luck.....!
-- Server đã xong rồi, thì qua config Client nha, post tiếp theo
Share:

Lần đầu sử dụng Node JS - MySQL - Docker viết API



Hi mọi người

Bài viết này là kết quả học tập với Node JS và Docker sau một thời gian tìm kiếm, tham khảo nhiều nơi.

Mình khá hứng thú mỗi khi đi học hỏi, tìm tòi một công nghệ mới.

Sau đây là những chia sẻ của mình về project này.

Đầu tiên đó là cấu trúc :

Và các thư viện được cài đặt

- npm install body-parser

- npm install dotenv

- npm install express

- npm install mysql

- Bước 1:  Chúng ta tạo và cấu hình file .env

MYSQLDB_USER=root
MYSQLDB_ROOT_PASSWORD=123456
MYSQLDB_DATABASE=nodejs_api
MYSQLDB_LOCAL_PORT=3306
MYSQLDB_DOCKER_PORT=3306

NODE_LOCAL_PORT=6868
NODE_DOCKER_PORT=8080

CLIENT_ORIGIN=http://127.0.0.1:8888
CLIENT_API_BASE_URL=http://127.0.0.1:6868/api

REACT_LOCAL_PORT=8888
REACT_DOCKER_PORT=80

- Bước 2 : Tạo và cấu hình file docker-compose.yml

version: '3.8'

services:
mysqldb:
image: mysql:5.7
restart: unless-stopped
env_file: ./.env
environment:
- MYSQL_ROOT_PASSWORD=$MYSQLDB_ROOT_PASSWORD
- MYSQL_DATABASE=$MYSQLDB_DATABASE
- DB_HOST=mysqldb
- DB_USER=$MYSQLDB_USER
- DB_PASSWORD=$MYSQLDB_ROOT_PASSWORD
ports:
- ${MYSQLDB_LOCAL_PORT}:$MYSQLDB_DOCKER_PORT
volumes:
- db:/var/lib/mysql
networks:
- backend

volumes:
db:

networks:
backend:

- Bước 3 : Tạo và code của file server.js

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const port = process.env.PORT || 3000;

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

let routes = require('./api/routes'); //importing route
routes(app);

app.use(function(req, res) {
res.status(404).send({ url: req.originalUrl + ' not found' });
});

app.listen(port);

console.log('RESTful API server đã bắt đầu: ' + port);

- Bước 4 : Trong folder api ta có những file sau :

     routes.js

    module.exports = function(app) {
    let user = require('./controllers/users.controller');
    app.route('/get-users/:userID').get(user.findOne)
    app.route('/create-users').post(user.createUser)
    app.route('/get-all-users').get(user.findAll)
    app.route('/update-user/:userId').put(user.update)
    app.route('/delete-user/:userId').delete(user.delete)
    };

       db.js

    const mysql = require('mysql');

    const db = mysql.createConnection({
    host: process.env.DB_HOST || 'localhost',
    user: process.env.DB_USER || 'root',
    password: process.env.DB_PASS || '123456',
    database: process.env.DB_NAME || 'nodejs_api',
    });

    module.exports = db;

     - Tiếp đến, folder controllers chứa file sau :

     app/controllers/users.controller.js

    const Users = require('../models/users.model');

    exports.findOne = (req, res) => {
    Users.findById(req.params.userID, (err, data) => {
    if (err) {
    if (err.kind === 'not_found') {
    res.status(400).send({
    message: `Không tìm thấy id : ${req.params.userID}`
    })
    } else {
    res.status(500).send({
    message: "Kết quả trả về lỗi ở id : " + req.params.userID
    })
    }
    } else {
    res.send(data);
    }
    })
    }

    exports.createUser = (req, res) => {
    if (!req.body) {
    res.status(400).send({
    message: "Nội dung gửi lên không được rỗng"
    })
    }

    const user = new Users({
    name : req.body.name,
    gmail: req.body.gmail,
    phone: req.body.phone
    })

    Users.create(user, (err, data) => {
    if (err) {
    res.status(500).send({
    message: err.message
    })
    } else {
    res.send(data)
    }
    })
    }

    exports.findAll = (req, res) => {
    Users.getAll((err, data) => {
    if (err) {
    res.status(500).send({
    message: err.message
    })
    } else {
    res.send(data)
    }
    })
    }

    exports.update = (req, res) => {
    if (!req.body) {
    res.status(400).send({
    message: "Nội dung không được rỗng"
    })
    }
    
    Users.updateUser(req.params.userId, new Users(req.body), (err, data) => {
    if (err) {
    if (err.kind === "not_found") {
    return res.status(400).send({
    message: `Không tìm thấy User id : ${req.params.userId}`
    })
    } else {
    res.status(500).send({
    message: err.message
    })
    }
    } else {
    res.send(data)
    }
    })
    }

    exports.delete = (req, res) => {
    Users.deleteUser(req.params.userId, (err, data) => {
    if (err) {
    if (err.kind === "not_found") {
    return res.status(400).send({
    message: `Không tìm thấy User id : ${req.params.userId}`
    })
    } else {
    res.status(500).send({
    message: err.message
    })
    }
    }

    res.send(data)
    })

    }

 - Và folder models chứa file sau :

   app/models/users.model.js

    const db = require('./../db');

    const Users = function (user) {
    this.name = user.name;
    this.gmail = user.gmail;
    this.phone = user.phone;
    }
    Users.findById = (userID, result) => {
    db.query(`SELECT *
    FROM users
    WHERE id = ${userID}`, (err, res) => {
    if (err) {
    return result(err, null);
    }

    if (res.length) {
    return result(null, res[0])
    }

    result({kind: "not_found"}, null)
    })
    }

    Users.create = (newUsers, result) => {
    db.query("INSERT INTO users SET ? ", newUsers, (err, res) => {
    if (err) {
    return result(err, null)
    }

    result(null, newUsers)
    })
    }

    Users.getAll = (result) => {
    db.query("SELECT * FROM users", (err, res) => {
    if (err) {
    return result(err, null)
    }

    result(null, res)
    })
    }

    Users.updateUser = (id, users, result) => {
    db.query("UPDATE users SET name = ?, gmail = ?, phone = ? WHERE id = ?", [
    users.name, users.gmail, users.phone, id
    ], (err, res) => {
    if (err) {
    return result(null, err)
    }

    if (res.affectedRows == 0) {
    return result({kind : "not_found"}, null);
    }

    result(null, {id : id, ...users})
    })
    }

    Users.deleteUser = (id, result) => {
    db.query(`DELETE FROM users WHERE id = ?`, [id], (err, res) => {
    if (err) {
    return result(null, err)
    }

    if (res.affectedRows == 0) {
    return result({kind : "not_found"}, null);
    }
    result(res, null)
    })
    }


    module.exports = Users

Bước 5 : Bạn chạy lệnh này để build docker

docker-compose up

Hãy download phần mềm Docker về nhé, để dễ quản lý.

Sau khi build xong, bạn mở MySQL Workbench lên sau khi bạn kết nối thì popup sẽ hiển thị để kết nối tới database

Bạn phải nhập đúng thông tin như trong file .env mà bạn đã cấu hình

Và tạo table users ( id, name, gmail, phone )

Cuối cùng, bạn chạy lệnh này npm start  để xem kết quả nhé.

Chúc các bạn thành công.

Share: