Categories
Miscellaneous

New Year’s Resolution of 2020

Năm 2019 đã đến những giờ phút cuối, đến hẹn lại lên thì thời điểm này lại ngồi nhìn lại một năm đã trải qua và tự đánh giá để rút tỉa kinh nghiệm cũng như sau này nếu có giảm trí nhớ thì cũng biết mình đã từng trải qua những gì.

2019 – Tiếp tục tiến tới

Về kinh doanh thì 2019 tạm gọi là “đủ ăn” chứ không dư dả gì vì đúng là năm bản lề tăng trưởng. Ít ra, sau 6 năm thành lập thì công ty vẫn tồn tại tốt và biểu đồ có chiều hướng đi lên dần đều chứ không đi xuống tịt ngòi. Công ty vẫn kiên định với 2 mảng kinh doanh chính là Software-as-a-Service (SaaS) và Outsourcing, cả 2 mảng đều tập trung vào tầm nhìn từ ban đầu của công ty là “Số hóa vận hành của doanh nghiệp”.

Trong nhánh SaaS thì Teamcrop, Basecrop thì vẫn tăng trưởng đều. Đặc biệt, hệ thống tăng trưởng tốt nhất đó là Movecrop. Chatcrop thì đã chấm dứt sớm sau khoảng 3 tháng giới thiệu vì mình cũng không đặt nặng vào mảng chatbot và messaging, bu bám các social platform. Bạn Helpcrop cũng sớm ra đi sau đó ít lâu sau khi không có traction. Với phương châm “Try very fast, Fail very fast” thì không có thiệt hại gì đáng quan tâm bởi các dự án đã fail.

Trong nhánh Outsourcing thì tập trung chính vào mảng xây dựng các hệ thống thương mại điện tử trên nền tảng web & mobile app. Bắt đầu nhận các dự án liên quan đến AI/Machine Learning.

Về gia đình thì con cái khỏe mạnh, vợ chồng hòa thuận, ai cũng được làm được cái mình thích là vui rồi. Vẫn phương châm 9am-5pm nên có nhiều thời gian ở nhà hơn. Đặc biệt là năm nay là kỷ niệm tròn 10 năm quen được bà xã. Đáng gọi là thành công vang dội rồi.

Về cộng đồng thì cuối năm cũng tham gia được sự kiện nho nhỏ để chia sẻ đến bà con công việc của mình. Năm nay hoạt động cộng đồng khá ít vì tuổi cũng lớn và tập trung xây dựng nền tảng hơn. Để dành nhiều thời gian cho công ty và bản thân hơn.

Về bạn bè thì vẫn những người bạn cũ đã quen biết, không biết thêm nhiều bạn bè mới, nhưng quả thực “Quý hồ tinh bất quý hồ đa”, có được những người bạn hiện tại cũng là một phần may mắn của Tuấn vì với tính khí khùng điên như mình mà mấy chế vẫn giữ được bình tĩnh thì các bạn thật vĩ đại.

Về cá nhân thì vẫn vui vẻ, được làm việc trong một môi trường thân thiện, hòa đồng và đọc được khá nhiều sách. Năm nay đọc nhiều sách tiếng Anh và chuyên ngành hơn, dù không đạt chỉ tiêu 100 cuốn sách (chỉ khoảng 70 cuốn) và sẽ cố gắng đọc nhiều hơn trong 2020. Cũng đi du lịch nước quài được 2 lần để đạt yêu cầu tối thiểu của việc đi chơi.

Tóm lại thì 2019 là một năm đạt một số thành tựu nho nhỏ, từ hoạt động kinh doanh đến phát triển bản thân, nên 12 tháng vừa qua khá ưng ý và không có gì hối tiếc.

Hứa hẹn vào 2020

Nối tiếp sự tăng trưởng của 2019 thì hy vọng 2020 sẽ mang lại nhiều thành tựu hơn, từ kinh tế đến trình độ phát triển bản thân. Dù biết rằng các chỉ tiêu đặt ra có thể khó đạt được, nhưng vẫn mạnh dạn đặt ra một số chỉ tiêu để đeo đuổi như:

  • Triển khai 10 dự án Outsourcing
  • Đọc 200 cuốn sách
  • Công ty tăng 50% nhân sự.
  • Viết blog mỗi tuần 1 bài.
  • Tham gia meetup chia sẻ 1 lần / tháng
  • Đi du lịch quốc tế 4 lần, và cố gắng có 1 lần đi Mỹ
  • Ra mắt 3 dự án mới trong mảng công nghệ.
  • Phấn đấu đạt một chứng chỉ quốc tế nào đó.

Và cuối cùng, chúc mọi người một năm 2020 tràn đầy sức khỏe, may mắn và thành công. Happy New Year!

Categories
DevOps

Speed up Microservices 4: Asynchronous Big Task

Nối tiếp nội dung về tối ưu cho kiến trúc Microservices, lần này mình sẽ đề cập đến một bài toán cũng gặp rất nhiều và có thể coi là giải pháp khác của bài trước là Export dữ liệu. Nếu như ở bài trước, việc export dữ liệu được diễn ra ở Frontend (JS) bao gồm tải dữ liệu có phân trang và đóng gói excel thì có những hạn chế sau:

  • Nếu dữ liệu nhiều sẽ ảnh hưởng đến frontend vì memory có giới hạn
  • Quá trình export này là đồng bộ, tức là nếu đóng tab hoặc reload trang là tải lại từ đầu
  • Tính năng cần xuất dữ liệu này chưa có giao diện frontend, kéo theo thêm việc làm (làm trang danh sách) nếu chỉ muốn export.

Do những hạn chế trên nên cách đây ít lâu vẫn còn 1 vài tính năng Export dữ liệu phải được thực hiện ở phía server và rất khó đưa xuống Frontend vì yếu tố phức tạp của lấy dữ liệu và số lượng dữ liệu lớn. Những tính năng export kiểu cũ này kéo theo hạn chế rõ ràng là script sẽ chạy lâu và luôn kích hoạt cơ chế slow log vì có những script chạy cả tiếng đồng hồ chưa xong file excel để export và Memory chiếm vài GB cho 1 process. Nếu chỉ cần vài khách hàng vào thực hiện tính năng Export thì sẽ dẫn đến hệ thống treo, và trong môi trường không kiểm soát được thì làm sao có những yêu cầu dạng “cậu chỉ được export vào lúc X giờ”…

Do những hạn chế của cả 2 hình thức công việc như trên nên Teamcrop đã hoàn thiện giải pháp khá tối ưu, hiệu quả và an toàn cho người và của, và đó chính là nội dung của bài chia sẻ này.

Big Task là gì?

Trước tiên, cần phải định nghĩa Big Task là gì để dễ dàng thảo luận. Big Task ám chỉ những task khi thực hiện sẽ tốn khá nhiều thời gian và tài nguyên (Memory, CPU..). Ví dụ như những tính năng export tồn kho hàng chục ngàn sản phẩm, SKU, import hàng ngàn sản phẩm, đơn hàng, hoặc đi spam email, sms…

Một số đặc điểm của hệ thống BigTask mà Teamcrop đã xây dựng là:

  • Có thể chia nhỏ
  • Kiểm soát được tiến độ công việc (Bao nhiêu % hoàn thành..)
  • Bất đồng bộ: người dùng có tắt trình duyệt hay tắt máy thì hệ thống vẫn cứ làm việc
  • Có khả năng retry

Big Task được vận hành thế nào?

Tất nhiên, mỗi task bây giờ được định nghĩa là một row trong Database, mỗi task sẽ có 3 giai đoạn:

  • Bước 1: Chuẩn bị
  • Bước 2: Xử lý dữ liệu
  • Bước 3: Kết thúc

Bước chuẩn bị thường được trigger bởi người dùng và từ giao diện frontend, như submit form chứa thông tin. Ở bước chuẩn bị này chính là giai đoạn quan trọng nhất, bao gồm khởi tạo tổng số record cần xử lý (để kiểm soát progress), ghi nhận các cấu hình cần thiết cho task như số lượng record cần xử lý 1 trang, các dữ liệu cài đặt khi người dùng submit form và một bước quan trọng là khởi tạo 1 file excel chỉ có header (không có dữ liệu) ở môi trường local.

Bước xử lý dữ liệu là công việc chính liên quan đến nhiệm vụ, như lấy dữ liệu để insert vào file excel theo từng batch, ví dụ nếu task được giao mỗi trang lấy 100 record thì chỉ lấy 100 record và đẩy nhiệm vụ trang tiếp theo vào Messsage Queue (ví dụ RabbitMQ), hệ thống Queue worker sẽ chịu trách nhiệm thực thi trang tiếp theo dựa vào nội dung trong message đã được đẩy vào queue.

Bước kết thúc chính là đảm bảo dữ liệu đã đầy đủ và đưa các file cần thiết vào hệ thống, đối với Teamcrop là gọi File Service để lưu file tạm ở trên, thông báo và kết thúc task để đánh dấu task hoàn tất.

Như vậy, với sự giúp đỡ của kiến trúc Message Queue thì những task lớn có thể chia nhỏ và tiến trình được kiểm soát, đặc biệt là giúp cho hệ thống không có những script chạy quá lâu và tốn quá nhiều tài nguyên. Hiện tại bên Tuấn đã bắt đầu migrate dần các task dạng này vào mô hình Big Task của mình để tăng performance của hệ thống.

Hy vọng bài viết này sẽ giúp các bạn đối phó với một trong những bài toán đau đầu khi scale.

 

Categories
DevOps

Speed up Microservices 3: Export dữ liệu ra Excel

Chào mọi người, nối tiếp bài trước là tận dụng caching ở trình duyệt để tăng tốc cho microservices. Ở bài này mình sẽ chia sẻ một case study khá thông dụng trong các dự án “vừa vừa”, đó là nhu cầu export dữ liệu ra file excel. Nếu như các bạn thường xuyên làm việc liên quan đến các hệ thống quản lý dữ liệu thì data table / grid sẽ xuất hiện khá nhiều, kéo theo tính năng thường xuyên được yêu cầu là export dữ liệu (ra file Excel).

Categories
Web Programming

MySQL ngoại truyện

Cuối tuần vừa rồi mới vừa clear gần 50% table trong database của Teamcrop, đây là những table của những tính năng không còn sử dụng và đã trải qua thời gian deprecated (chờ xử trảm), thấy có lẽ nên viết một bài về database nhân dịp đầu năm mới cũng như khai blog 2019.

Cũng giống như nhiều đàn anh, đàn chị thời nay, mình đã sớm tiếp xúc với PHP từ 2003 khi xu hướng diễn đàn lớp, diễn đàn trường, diễn đàn vớ vẫn nở rộ. Những đoạn code đầu tiên là những mod diễn đàn viết bằng PHP, cũng như những câu truy vấn database MySQL đầu tiên cũng xuất hiện từ thời điểm này. Bài viết mang tính chất cổ súy MySQL nói riêng (RDBMS nói chung) nên nếu ai không biết nó là gì thì nên tìm hiểu hoặc làm với nó ít ít rồi hẳn đọc tiếp và đối tượng bài viết có thể là DB Administrator hoặc Developer có nhiệm vụ làm việc với MySQL, hầu hết chúng ta – web developer cũng là những người maintain database schema.

Làm việc với MySQL hơn 15 năm (và hiện nay vẫn chỉ tin và dùng MySQL), nên ít nhiều có chút kinh nghiệm khi sử dụng cũng như hình thành những quy trình, đường lối cho cá nhân và team khi tham gia xây dựng dự án. Mỗi khi có Web developer mới tham gia vào công ty thì những kiến thức này lại được truyền đạt, tuy nhiên đó giờ vẫn chưa có một tài liệu cụ thể nên bài blog này cũng là một phần phục vụ việc training này.

Tại sao lại gọi là ngoại truyện?

Hầu hết những gì mình chia sẻ ở đây có thể các bạn sẽ rất ít thấy đề cập ở các sách hay giáo trình về MySQL. Thậm chí có những thứ có thể được coi là đi ngược với những gì bạn biết về MySQL.

Để dễ chia sẻ thông tin, mình sẽ tách thành 3 giai đoạn khi các bạn làm việc với MySQL, đó là trước khi có dữ liệu (data), trong khi có dữ liệu và khi không còn dữ liệu. Ở mỗi giai đoạn sẽ có những kinh nghiệm khác nhau và phù hợp với ngữ cảnh để mọi người tiện theo dõi.

1. Trước khi có dữ liệu

Theo quan sát và kinh nghiệm, rất nhiều bạn khá hời hợt và xem thường giai đoạn này, tức giai đoạn phân tích và thiết kế database, hay nói 1 cách dân dã là tạo bảng (CREATE TABLE..). Rất nhiều vấn đề phát sinh bởi việc thiết kế bảng hời hợt, thậm chí là thiết kế sai, kéo theo technical debt sẽ ngày càng lớn và thậm chí là phải bỏ cả tính năng / bảng đang sử dụng để refactoring lại thành tính năng mới, tạo table mới và migrate dữ liệu từ bảng cũ sang bảng mới.

Ngoài việc thiết kế bảng đúng, duy trì tính dễ hiểu (understandable), dễ bảo trì (maintainable) và đồng nhất (consistent) cũng là một số yếu tố luôn được cân nhắc khi thiết kế một table. Bên dưới là một số “guideline” mà ai muốn tham gia vào team của Tuấn sẽ được training và bắt buộc phải theo:

Về database:

  • Trong một Database, tất cả tên bảng phải có tiền tố giống nhau (prefix). Ví dụ: crp_, abc_…
  • Để tăng khả năng chuyển đổi DBMS và tương thích với các hệ SQL, không sử dụng các khái niệm “phức tạp” của database như Store procedure, View, Constraint
  • Không sử dụng khái niệm ràng buộc khóa chính, khóa ngoại cài đặt trong DBMS. Chỉ sử dụng khái niệm này trong quá trình trao đổi thông tin giữa các developer và thiết kế bảng.
  • Áp dụng kiến trúc Microservices để chia nhỏ database theo từng domain/context riêng để giúp giảm tải chung cho toàn Database. Ví dụ: Mỗi service sẽ cần 1 kiến trúc / server DB riêng để vận hành riêng biệt.
  • Những phiên bản MySQL khác nhau sẽ có một số config khác nhau kéo theo những lỗi không ngờ tới khi nâng cấp phiên bản.
  • Không sử dụng docker / VM cho MySQL Server.

Về bảng (table):

  • Khi đặt tên bảng, tên cột thì không sử dụng chữ HOA hoặc ký tự đặc biệt. Cho phép a-z, 0-9 và underscore (gạch dưới “_”).
  • Tên bảng luôn là tiếng anh và sử dụng từ số ít. Ví dụ: crp_product, crp_news
  • Nếu tên bảng là tổ hợp nhiều từ, thì mỗi từ cần cách nhau gạch dưới (underscore). Ví dụ: abc_product_category, abc_order_detail
  • Trừ các cột khóa ngoại, các cột dữ liệu riêng của một bảng luôn có tiền tố giống nhau, và tổng hợp từ chữ cái đầu tiên của tên bảng (không bao gồm tiền tố database). Ví dụ: bảng abc_product_category thì các cột của bảng này phải có tiền tố là “pc_”, như “pc_id”, “pc_name”..
  • Tên cột ngoài tiền tố bảng thì là các từ tiếng anh, số ít và không có khoảng cách, các từ phải viết liền nhau. Ví dụ: p_countview, p_datecreated..
  • Khóa chính của một cột trong bảng luôn là tiền tố của bảng + “id”. Ví dụ: “pc_id”, “p_id”, “n_id”..
  • Khóa ngoại của một bảng sẽ được nằm đầu tiên trong danh sách cột của bảng này, trước cả cột khóa chính. Tên cột khóa ngoại phải giữ nguyên tên cột mà nó làm khóa chính trong bảng của nó. Ví dụ: u_id là User ID và là khóa chính trong bảng “abc_user”, thì khi làm khóa ngoại trong bảng “abc_product” thì nó vẫn là cột “u_id”.
  • Collation của database / table / text column là “utf8_general_ci”
  • Table luôn có engine mặc định là MyISAM (tối ưu cho SELECT dữ liệu)
  • Có thể tận dụng engine MEMORY để tối ưu truy vấn vì toàn bộ dữ liệu của table loại MEMORY sẽ nằm trong RAM thay vì ổ cứng.

Về kiểu dữ liệu của cột trong bảng:

  • Kiểu dữ liệu của khóa ngoại và khóa chính phải trùng khớp nhau (data type & data length)
  • Không sử dụng kiểu dữ liệu DATETIME trừ trường hợp lưu ngày sinh.
  • Không sử dụng kiểu dữ liệu ENUM trong trường hợp cần lưu theo logic này, chỉ cần lưu INT (11) hoặc SMALLINT(3) và trong Model của code sẽ khai báo constant.
  • Tất cả cột thời gian đều lưu Timestamp và kiểu dữ liệu INT(10).
  • Các cột dữ liệu thời gian luôn là những cột nằm cuối cùng của bảng. Hầu hết các bảng luôn có cột: datecreated (ngày tạo) và datemodified (ngày cập nhật).
  • Trường IP Address luôn là BIGINT (20), và sử dụng hàm ip2long() và long2ip() của PHP để encode/decode.
  • Giá trị tiền nên là DECIMAL (18), tốt nhất là DECIMAL (18,4)
  • Cẩn thận khi thiết kế dữ liệu cho việc lưu ký tự emoji.

Việc tuân thủ các guideline này sẽ giúp rất nhiều trong việc debug cũng như đoán được nguồn gốc, xuất xứ của 1 cột khi làm việc, và tạo sự nhất quán trong toàn team để dễ trao đổi, debug. Ví dụ bên dưới là thiết kế cho table user, product.

CREATE TABLE `crp_user` (
  `u_id` int(11) NOT NULL,
  `u_email` varchar(128) NOT NULL,
  `u_password` text NOT NULL,
  `u_ipaddress` bigint(20) NOT NULL,
  `u_status` smallint(3) NOT NULL,
  `u_datecreated` int(10) NOT NULL,
  `u_datemodified` int(10) NOT NULL,
  `u_datelastloggedin` int(10) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


CREATE TABLE `crp_product` (
  `u_id` int(11) NOT NULL,
  `p_id` int(11) NOT NULL,
  `p_name` varchar(255) NOT NULL,
  `p_description` text NOT NULL,
  `p_price` decimal(18,4) NOT NULL,
  `p_status` smallint(3) NOT NULL,
  `p_ishot` tinyint(1) NOT NULL,
  `p_datecreated` int(10) NOT NULL,
  `p_datemodified` int(10) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Với những tiêu chí trên thì đảm bảo team bạn sẽ dễ dàng làm việc trên các bảng dữ liệu của các dự án.

2. Trong khi có dữ liệu

Sau quá trình thiết kế bảng hoàn tất và đã được đưa lên hệ thống, tính năng cũng đã được chạy và bạn bắt đầu có dữ liệu, không gì tuyệt hơn là có dữ liệu. Đến giai đoạn này, việc duy trì cho hệ thống đang chạy là ưu tiên hàng đầu, và một số chia sẻ ở giai đoạn này cũng nhằm giúp việc đảm bảo hệ thống DB không bị gián đoạn.

  • Không thay đổi tên cột trong bất kì tình huống nào.
  • Không xóa cột trong bất kì tình huống nào.
  • Chỉ đổi kiểu dữ liệu cột sang cùng loại và sang dung lượng lớn hơn. Ví dụ cho phép đổi từ VARCHAR(50) -> VARCHAR (100), SMALLINT(3) -> INT (11).
  • Cần áp dụng replicate database (Master / Slave) dữ liệu để giảm tải. Để tránh rắc rối không cần thiết, chỉ nên sử dụng 1 Master và có thể cho phép nhiều Slave.
  • Trong bất kì tình huống nào liên quan đến vấn đề đồng bộ giữa Master & Slave và liên quan đến lỗi đồng bộ binlog, tốt nhất là disable replicate, chỉ sử dụng Master, tìm ra nguyên nhân cụ thể để khắc phục và phần lớn vấn đề liên quan đến networking, ổ cứng mạng, tắt bất tử.
  • Lưu ý “Delay Gap” (thời gian bất đồng bộ dữ liệu) giữa Master và Slave(s).
  • Đồng bộ dữ liệu vào thời gian ít truy cập nhất
  • Backup dữ liệu định kì và upload lên một hệ thống / server khác hệ thống đang chạy. Bên mình kết hợp S3cmd để đẩy file backup sql lên Amazon S3 để backup mỗi ngày.
  • Tuyệt đối lưu ý đến max connection mặc định của MySQL là 150 để tránh lỗi MySQL connection.
  • Nếu cài đặt max connection lớn thì lưu ý việc MySQL server sẽ block IP do cơ chế security.
  • Hạn chế đến mức tối đa việc sử dụng JOIN TABLE để tăng khả năng refactoring, migration cũng như giảm thiểu vấn đề về performance / SQL execution. Chỉ JOIN khi đây là hoạt động bất khả kháng và không còn phương án nào tối ưu hơn cho việc truy vấn (tìm kiếm).
  • Không sử dụng JOIN cho các entity không liên quan đến nhau hoặc cùng 1 domain/service. VD: không join user & comment, product & comment..Có thể JOIN order & order detail.
  • Luôn có Cache layer ở tầng Application (Redis, memcached) để hạn chế truy vấn vào Database cũng như hỗ trợ việc hạn chế JOIN.

3. Khi dữ liệu không cần thiết

Hầu hết dự án nào cũng có những tính năng “không còn sử dụng” và có nhu cầu xóa bỏ luôn dữ liệu thì bạn cũng cần có những quy định cho việc này. Vì sao phải xóa dữ liệu? Theo thời gian, giữ liệu sẽ ngày một phình to, nếu vẫn cố giữ những dữ liệu không còn sử dụng thì sẽ kéo theo thời gian backup / migration / recovery lớn hơn, dẫn đến nhiều rủi ro tiềm ẩn không cần thiết, do đó, tốt nhất là xóa những gì không dùng. Hoặc nếu muốn giữ lại thì cũng nên tách data này sang 1 nơi mà không ảnh hưởng đến performance / rủi ro chung của toàn hệ thống.

Bên dưới là một số bước mà bên mình dùng để xóa dữ liệu, các bạn có thể tham khảo và áp dụng.

  • Bởi vì không có cài đặt ràng buộc tự động (Constraint) nên tầng Application cần có các tính năng (cronjob) tự động để xóa các dòng dữ liệu không cần thiết hoặc update các cột dữ liệu bị lỗi.
  • Đảm bảo các bảng cần xóa không được tham chiếu bởi bất kì tính năng nào. Nếu có tham chiếu thì cần đảm bảo tính năng này hiện đã không còn sử dụng.
  • Kiểm tra ngày cuối cùng mà bảng này có dữ liệu mới, hay modified date của bảng.
  • Không có bảng ngay mà chỉ đổi tên bảng và thêm tiền tố. Ví dụ: thêm OLD_, DEPRECATED_
  • Chờ tối thiểu 1 tháng -> 3 tháng và chỉ xóa các bảng có tiền tố định trước là sẽ xóa.
  • Hạn chế tối đa việc gõ tên bảng, nên copy tên bảng ra trước rồi copy vào CMD.
  • Khi xóa bảng thì không nên dùng MySQL Client / GUI để thao tác mà thông qua MySQL CLI và thực thi SQL mà thôi.
  • Theo dõi hệ thống support / CS tối thiểu 1 ngày để phát sinh những support ticket liên quan đến mất dữ liệu, lỗi tính năng do khách hàng report.

Trên đây là tất cả những quy trình, kinh nghiệm mà team mình áp dụng khi làm việc với MySQL. Hy vọng mọi người cũng có những quy trình để giúp bảo vệ dữ liệu tốt hơn và cải thiện quá trình lưu trữ và làm việc nhóm liên quan đến cấu trúc dữ liệu.

Categories
Miscellaneous

New Year’s Resolution of 2019

Cũng như các năm trước, 31/12 là dịp ngồi review lại những gì đã diễn ra trong năm và 2018 là một năm được đánh giá là “có thu hoạch” về một số mặt quan trọng trong sự nghiệp và đời sống. 

Categories
DevOps

Speed up Microservices 2: Tận dụng trình duyệt và cache

Cũng gần 9 tháng kể từ bài 1 ra mắt, trong bài này mình sẽ chia sẻ cách hệ thống Teamcrop đã tối ưu thế nào để tăng tốc cho hệ thống Microservices của mình. Cũng như bài 1 có đề cập, dữ liệu trong hệ thống microservices khá phân tán và khi cần lấy dữ liệu nếu không thiết kế tốt thì sẽ tốn rất nhiều thời gian cho các request lấy dữ liệu. Ví dụ lấy danh sách đơn hàng là một ví dụ rõ ràng nhất có nêu ở bài trước.

Teamcrop cũng lưu dữ liệu dạng này, trong một đơn hàng được lưu trong database thì chỉ lưu ID (Khóa ngoại) của các bảng như nhân viên, cửa hàng, nguồn đơn hàng…mà các thông tin này chỉ được cung cấp bởi các service liên quan như service Nhân viên, Store và Metadata. Để hiển thị thông tin như giao diện thì cần phải gọi thêm nhiều request (Restful) để fetch data từ các nguồn này và đây là cách hoàn toàn không hiệu quả bởi ảnh hưởng đến thời gian chờ của người dùng.

Trong kiến trúc của Teamcrop, các thông tin như nhân viên, cửa hàng, nhà kho, sản phẩm, … này được gọi là Tài nguyên công ty (Company Resource – CR) và hiện Teamcrop có khoảng gần 20 company resource.

Cách hoạt động của các Company Resource:

Các company resource cũng là dữ liệu và tất nhiên vẫn có các API như các resource khác, hầu hết đều được quản lý (CRUD) và có giao diện tương ứng để quản lý. Chỉ có một khác biệt là các company resource sẽ có thêm 1 web service cho phép lấy toàn bộ dữ liệu, gọi là api /fulldata. API này cho phép lấy toàn bộ dữ liệu resource, không phân trang.

Khi có một user truy cập vào website thì sẽ load toàn bộ các company resource và lưu trữ trong trình duyệt. Mình có thể setup thêm giao diện để thể hiện progress bar, khi nào toàn bộ company resource được load hết thì cơ chế router của web sẽ bắt đầu và tiến hành load giao diện tính năng đang truy cập. Hình dưới là ví dụ một action viết bằng PHP của việc lấy một company resource là Vendor (thương hiệu):

Company Resource sẽ ảnh hưởng thời gian tải trang

Nếu đọc đến đây thì sẽ có nhiều bạn thắc mắc là nếu mỗi truy cập đều phải load toàn bộ Company Resource thì sẽ chờ khá lâu bởi vì mỗi CR sẽ cần 1 request đến /fulldata và API này cũng phải lấy toàn bộ data của resource và response về.

Đối với phía server thì sẽ không có vấn đề nhiều vì mình sẽ tận dụng cache (Redis) để lưu kết quả vào memory và khi có request thì mình sẽ lấy từ cache ra mà thôi.

Ở phía client (trình duyệt) thì mới là vấn đề lớn, bởi dù server có nhanh thì có bao nhiêu company resource thì cũng sẽ tốn ngần ấy số request lên server để lấy /fulldata. Tuy nhiên, Teamcrop hầu như không tốn 1 request nào cho các company resource khi sử dụng…Service Worker của trình duyệt.

Đối với các API /fulldata thì script trong service worker của mình sẽ lấy từ cache trên trình duyệt ra mà không phải tốn 1 request nào lên server. Như vậy là tiết kiệm được mấy chục request và có được một số lượng data để hỗ trợ lấy dữ liệu.

Company Resource sẽ được lưu trữ thế nào?

Sau khi lấy được các dữ liệu company resource thì việc lưu trữ và sử dụng các dữ liệu này cũng là một vấn đề lớn. Bởi các company resource này sẽ được dùng cho việc tìm kiếm theo 1 ID (getById), tìm theo một số tiêu chí (search) và lấy toàn bộ danh sách (getList).

Để đảm bảo quá trình truy xuất, tìm kiếm thì nó phải nhanh cũng như gần gũi, dễ sử dụng nên mình đã chọn TaffyDB, được coi là một thư viện nhỏ gọn, In-memory Database, hỗ trợ tìm kiếm theo ID, điều kiện..và khá dễ dùng. Mỗi Company Resource sau khi lấy về sẽ trở thành 1 database và có thể truy xuất data thoải mái. Khá nhanh bởi vì nó là in-memory.

Như vậy thì từ giờ, mỗi khi có 1 dữ liệu có ID là ID của một company resource thì có thể tìm kiếm và lấy thông tin một cách nhanh nhất từ bộ nhớ của trình duyệt mà không phải tốn công JOIN bảng, request giữa các service hoặc client gửi request để lấy detail.

Tuy nhiên, một vấn đề lớn hơn đã xuất hiện đó là nếu dữ liệu company resource đã cache hoàn toàn ở trình duyệt thì khi có dữ liệu mới thì làm sao cập nhật (hay xóa cache). Đối với vấn đề này phát sinh 2 tình huống từ dễ đến khó, tình huống đầu tiên là xóa cache đối với người refresh lại trang web (reload) và tình huống thứ 2, đau đầu hơn là bởi vì Teamcrop là dạng Single Page App (SPA), nếu người dùng không reload trang thì làm sao cập nhật dữ liệu mới nhất. Mình sẽ xử lý lần lượt từng tình huống.

Xóa cache của Company Resource khi reload trang

Đối với trường hợp người dùng reload lại trang web thì chỉ cần thêm cơ chế versioning vào các company resource là giải quyết được.

Những thao tác thay đổi dữ liệu liên quan đến Company Resource (thêm, xóa, sửa) thì đều được gọi 1 API để nâng version của resource tương ứng.

Trước khi request các /fulldata API, hệ thống sẽ gửi request lấy thông tin version của các company resource, từ đó mới request để lấy full data. Do version được nối vào URL của fulldata nên Service Worker sẽ kích hoạt cơ chế cache theo URL, nếu version không đổi thì sẽ không request lên server, ngược lại thì sẽ request lên server.

Thậm chí còn làm tính năng nho nhỏ trên giao diện để debug version của các resource trong trường hợp không thể inspect được.

Xóa cache của Company Resource khi đang sử dụng và không reload

Đối với trường hợp một user vẫn “miệt mài” làm việc và không reload trang, bởi đặc thù của Single Page App là không reload mỗi khi chuyển tính năng nên xóa cache của company resource cho đối tượng này sẽ không theo cách thông thường. Bài toán đặt ra là làm sao trigger (thông báo) cho trình duyệt biết là có sự thay đổi là được bởi vì nếu được trigger thì chỉ cần gọi ajax lên Company Resource API sẽ lấy được bảng version của các resource và đối chiếu cái nào tăng thì lấy bản mới của /fulldata.

Suy nghĩ một hồi và nghĩ ngay đến giải pháp sử dụng Socket. Và Teamcrop sử dụng Pusher để cung cấp giải pháp socket bởi vì đây là một trong những dịch vụ tốt nhất hiện nay đối với dịch vụ socket.

Nhằm tận dụng cơ chế web socket sẵn có của Teamcrop (dùng cho cơ chế web notification) nên trình duyệt tiến hành “subscribe” để nghe trên một channel và mỗi khi version tăng thì server (PHP) của Teamcrop gửi một message cho trình duyệt đang lắng nghe ở một channel chung của công ty, tất nhiên thông qua Pusher API.

Sample code ở client (Javascript):

Sample code ở server (PHP):

Như vậy, mỗi khi company resource service nhận được yêu cầu nâng version thì sẽ trigger và client sẽ tự động cập nhật dữ liệu đang cache trên trình duyệt. Và giải quyết ổn thỏa bài toán cache data cho Company Resource.

Tiêu chí nào để trở thành Company Resource?

Như từ đầu đã chia sẻ, Company Resource sẽ lưu ở máy giúp  việc “phân giải” các ID khóa ngoại thành dữ liệu để hiển thị nhằm cải tiến performance chung của toàn hệ thống. Tuy nhiên, không phải tất cả tài nguyên đều có thể trở thành Company Resource.

Trước tiên, cần xác định rõ là Company Resource không phải là Transaction Data, ví dụ Đơn hàng, Khách hàng…không thể nào là ứng cử viên trở thành Company Resource bởi tính thay đổi cũng như số lượng của nó. Chỉ nên chọn những dữ liệu thường xuyên được tham chiếu, số lượng dữ liệu ít, tránh làm trình duyệt lưu quá nhiều dữ liệu vào bộ nhớ.

Lời kết, như vậy cùng với việc thay đổi mô hình microservice thì cũng phát sinh nhiều vấn đề mới liên quan đến kiến trúc này. Trên đây chỉ là một giải pháp cho một bài toán cụ thể mà Teamcrop gặp phải trong quá trình vận hành hệ thống của mình. Kỳ tiếp theo (kỳ 3) mình sẽ đề cập đến một bài toán cụ thể hơn là Export dữ liệu ra file (vd Excel). Bởi vì đặc thù là dữ liệu phân tán, nên export dữ liệu trong hệ thống cũng là một bài toán khá thú vị.

Tuấn có nhận tư vấn giải pháp liên quan đến triển khai, chuyển đổi kiến trúc sang Microservices. Ai cần thì đừng ngần ngại liên hệ nhé.

Categories
DevOps

Triển khai CI/CD với Gitlab 9

Cũng hơn một tháng kể từ bài viết gần nhất, nay mới có thời gian ngồi viết lách tiếp. Dạo gần đây thường release các dự án outsource nên cũng hay làm documentation cũng như  mở các dự án mới nên việc setup CI/CD thường xuyên hơn và chân tay hơn. Thấy các kiến thức này hay nên hôm nay mình sẽ chia sẻ mọi người quy trình CI/CD bên mình áp dụng cho “đại dự án” Teamcrop cũng như các dự án outsourcing mà Moout thực hiện.

CI/CD là gì?

Bạn sẽ thấy có nhiều định nghĩa từ hai lúa cho đến hàn lâm cho khái niệm CI/CD. Mình sẽ dùng cách định nghĩa của mình để mọi người dễ hiểu theo cách thông thường nhất. CI/CD là một bộ đôi công việc, bao gồm CI (Continuous Integration) và CD (Continuous Delivery), ý nói là quá trình tích hợp (integration) thường xuyên, nhanh chóng hơn khi code cũng như thường xuyên cập nhật phiên bản mới (delivery).

Tại sao phải quan tâm đến CI/CD?

Ngày nay, với xu hướng agile/lean dẫn đến việc phát triển tính năng là điều bình thường, quan trọng phải là thần thái, ý lộn, quan trọng là phải nhanh. Nếu một tính năng mà mất 2, 3 tháng mới release thì dẫn đến nhiều hệ lụy như làm không phù hợp nhu cầu khách hàng, hoặc đối thủ đã ra mắt trước đó, mất đi cái lợi thế dẫn đầu. Do đó, việc làm ra một sản phẩm, tính năng đòi hỏi thần tốc là ưu tiên số một hiện nay.

Bên cạnh đó, để nhanh chóng ra mắt một tính năng, phiên bản mới nếu theo cách cổ điển sẽ mất nhiều thời gian bởi công việc chân tay khá nhiều và mỗi lần release cũng huy động một cơ số người không nhỏ để cập nhật một thay đổi dù là nhỏ nhất. Bởi vậy, xu hướng CI/CD giúp cung cấp các framework, workflow giúp tiết kiệm thời gian, nguồn lực của quá trình release (delivery).

Quy trình CI/CD tham khảo

Có rất nhiều quy trình, công cụ khác nhau để ứng dụng CI/CD vào dự án. Nội dung của bài viết này dựa trên kinh nghiệm cho các dự án của mình triển khai cũng như đặc thù là các hệ thống web, và viết bằng PHP (PHP hoặc Python hoặc abc…không quá quan trọng trong ngữ cảnh chia sẻ về CI/CD).

Dưới đây là các bước thông thường của quá trình release tính năng trong một dự án thuộc hệ thống Teamcrop.
Bước 1: [Manual] Khởi tạo repository và có branch default là master và dev. Cài đặt trên Gitlab 9.
Bước 2: [Manual] Trừ owner ra, thì các coder sẽ push code tính năng lên branch dev
Bước 3: [Auto] Hệ thống tự động thực hiện test source code, nếu PASS thì sẽ deploy tự động (rsync) code lên server beta.
Bước 4: [Manual] Tester/QA sẽ vào hệ thống beta để làm UAT (User Acceptance Testing) và confirm là mọi thứ OK.
Bước 5: [Manual] Coder hoặc owner sẽ vào tạo Merge Request, và merge từ branch dev sang branch master.
Bước 6: [Manual] Owner sẽ accept merge request.
Bước 7: [Auto] Hệ thống sẽ tự động thực hiện test source code, nếu PASS sẽ enable tính năng cho phép deploy lên production server.
Bước 8: [Manual] Owner review là merge request OK, test OK. Tiến hành nhấn nút để deploy các thay đổi lên môi trường production.
Bước 9: [Manual] Tester/QA sẽ vào hệ thống production để làm UAT và confirm mọi thứ OK. Nếu không OK, Owner có thể nhấn nút Deploy phiên bản master trước đó để rollback hệ thống về trạng thái stable trước đó.
Bước 10: [Manual] Thắp nhang và hy vọng khách hàng không chửi rủa hoặc email complain.

Áp dụng CI/CD với Gitlab 9

Trong khuôn khổ bài viết này, mình sẽ hướng dẫn mọi người cài đặt Gitlab 9 để quản lý source code, và trên công nghệ Git. Đòi hỏi của tất cả setup này là trên server đã cài Docker, nếu các bạn chưa có docker trên server thì có thể tham khảo các bài viết về docker trên bloghoctap cũng như tìm kiếm thêm trên google.

Tại sao phải là Gitlab 9?

Đây là câu hỏi hay, bởi vì trước đó bên mình sử dụng Gitlab 8, tuy nhiên, do các hạn chế về UI/UX cũng như không tích hợp ngon lành vụ CI/CD nên khi bản 9 ra mắt, mình đã thử sử dụng và thấy bản 9 phù hợp hơn cho workflow nên quyết định dọn dẹp sang Gitlab 9.

Cài đặt Gitlab 9 với docker

Bạn chạy câu lệnh sau để tạo một container chứa Gitlab 9.

docker run --detach \
    --hostname code.teamcrop.com \
    --publish 8080:80 --publish 2222:22 \
    --name gitlab9 \
    --restart=always \
    --volume /gitlab9/config:/etc/gitlab \
    --volume /gitlab9/logs:/var/log/gitlab \
    --volume /gitlab9/data:/var/opt/gitlab \
    gitlab/gitlab-ce:9.0.3-ce.0

Nếu ai từng dùng docker sẽ hiểu ý nghĩa câu lệnh trên. Đơn giản là mình sử dụng image gitlab/gitlab-ce:9.0.3-ce.0. Có mount ra 3 thư mục bên ngoài máy ở thục mục /gitlab9 để lỡ có chuyện gì chỉ cần stop, remove container, khi chạy docker run lại thì không bị mất dữ liệu, source code. Câu lệnh trên có map 2 port là 8080 và 2222 tương ứng tới 2 port 80 và 22 trong container. Mình mapping port vậy bởi vì trên server dev này có rất nhiều service khác và đã chiếm port 80 và 22 (ssh ^^!).

Sau khi bạn start container thì có thể truy cập vào từ ip hoặc domain (mà bạn đã trỏ DNS), ví dụ: http://code.teamcrop.com:8080 là có thể vào gitlab 9, tài khoản mặc định là `root`.

Không có gì cao siêu ở cài đặt này, có thể tham khảo thêm ở trang chủ của Gitlab.com nhé.

Quản lý sourcecode bằng Gitlab

Về phần này thì mình không cần nói dài dòng, cũng như một hệ thống git thông thường (như github..), bạn có thể tìm hiểu thêm về git và Gitlab để team có thể cùng làm việc và quản lý sourcecode trên Gitlab.

CI/CD với Gitlab CI

Thông thường, các hệ thống quản lý sourcecode không kèm theo cơ chế CI/CD. Nếu bạn muốn triển khai thì buộc phải liên kết đến repository, phân quyền đủ kiểu để hệ thống đó có thể lấy source code từ respository. Trước đây bên mình sử dụng Jenkins cho việc này. Tuy nhiên, từ khi Gitlab ra mắt tính năng Gitlab CI, kèm theo sự chậm chạp, rắc rối và rề rề của Jenkins thì mình quyết định chia tay với Jenkins và đến với Gitlab CI luôn, và quả là một bộ đôi hoàn hảo. Code để ở Gitlab, rồi trong đó có cho cài đặt CI/CD để test và deploy code tự động.

Cũng như một số bạn mới lần đầu tiếp xúc với Gitlab CI, mình đã từng thấy nó khó hiểu và cao siêu vì setup tùm lum. Rồi setup xong lại không biết nó chạy thế nào, cơ chế deploy source code ra sao. Tuy nhiên, sau một vài “va chạm” đầy mồ hôi và nước mắt thì cũng nắm và hiểu được cách Gitlab CI vận hành, và nay chia sẻ cho mọi người để vận dụng cho workflow của mình.

Để dùng được Gitlab CI thì bạn cần có 2 thành phần sau: file `.gitlab-ci.yml` nằm ở thư mục gốc của dự án và Gitlab Runner.

File .gitlab-ci.yml là gì?

Mặc định Gitlab không có cơ chế nào về CI cho dự án của bạn, chỉ khi nào dự án của bạn có file .gitlab-ci.yml nằm ở thư mục gốc thì Gitlab mới nhận dạng được dự án của bạn muốn áp dụng Gitlab CI. File này có định dạng và cần hợp lệ thì mới có thể hoạt động được, không thì khi bạn push code lên thì Gitlab sẽ báo lỗi file định dạng nội dung của file cấu hình không hợp lệ. Tham khảo cú pháp của cấu hình này tại https://docs.gitlab.com/ce/ci/yaml/

Trong file này có gì? File này có một số section để khai báo như trước khi chạy test thì làm gì, khi test thì thực hiện lệnh gì (vd chạy linter check cú pháp, chạy PHPUnit test…), test xong rồi thì thực hiện deploy đi đâu (beta, production..) với câu lệnh gì (vd: rsync..). Tùy đặc thù ngôn ngữ lập trình, cách đóng gói của dự án mà sẽ có các lệnh tương ứng thực hiện.

Tới đây các bạn sẽ có câu hỏi là vậy cái gì sẽ chạy, thực thi các câu lệnh, chỉ dẫn trong file config trên? Hay là Gitlab Server sẽ chạy. Nếu là Gitlab server chạy thì nếu dự án mình thực hiện những lệnh không có thì sao, vì gitlab server thì cũng chỉ chứa gitlab và các program cho nó chứ đâu thể cài sẵn các program? Bên cạnh đó, mỗi lần chạy thì các thông tin liên quan đến file tạm có bị reset lại hay không?

Nếu bạn đi đến đây thì bạn đã đoán được là thực ra “cái thứ” thực thi các chỉ dẫn, câu lệnh trong file .gitlab-ci.yml không phải là Gitlab Server (là cái container đang chạy gitlab 9 mình start ở trên), mà đó chính là Gitlab Runner. Wow! Welcome to matrix!

Gitlab Runner là gì?

Gitlab Runner là thành phần cực kỳ quan trọng trong workflow Gitlab CI. Nếu không có Runner thì sẽ không có lệnh test, deploy nào được thực thi. Runner có nhiều loại, phân biệt dựa vào cái gọi là executor. Khi khởi tạo runner, bạn sẽ phải chọn nó là loại executor nào, và nó sẽ quyết định môi trường thực thi các câu lệnh trong file config ở trên. Bạn có thể tham khảo link https://docs.gitlab.com/runner/executors/ để biết sự khác nhau của các executor cũng như cách cài đặt, cấu hình chúng.

Do đặc thù hệ thống đã có docker, nên bên mình chỉ sử dụng executor loại Docker mà thôi. Và bên dưới là câu lệnh docker để start một Gitlab Runner.

docker run -d --name gitlab-runner --restart always \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gitlab/gitlab-runner:latest

Ở đây bạn sẽ thấy container này mount thư mục config ra ngoài, bởi vì mình muốn các cấu hình của runner không bị mất khi stop/remove container. Chỉ cần start lại là giữ được cấu hình. Ngoài ra, nó còn mount docker.sock vào bên trong container, đây là cách để executor loại docker có thể tận dụng lệnh docker bên ngoài host để thực hiện lệnh tạo container phụ trong quá trình runner chạy (test, deploy).

Start container lên chỉ là bước đầu, bởi vì lưu ý là tới thời điểm này, Runner này không có liên quan gì đến Gitlab server của chúng ta. Cần một bước link lại (gọi là register) runner này vào trong Gitlab server để mình có thể cho phép các dự án dùng runner trong quá trình CI/CD.

Xem link này https://docs.gitlab.com/runner/register/index.html để biết cách register runner này vào Gitlab Server.

Dưới đây là hình ảnh tham khảo bạn có thể dùng trong quá trình register 1 runner. Có 2 thông tin quan trọng là 1 cái URL và một random token. Và cái URL đặc biệt lưu ý là thường thêm /ci sau domain. Ví dụ ở trường hợp của mình setup là http://code.teamcrop.com/ci

Sau khi Runner đã được gán vào Gitlab Server, bạn có thể enable runner này cho một hoặc nhiều dự án trong Gitlab. Hình bên dưới minh họa việc gán Runner vào dự án trong phần cài đặt Pipeline của Gitlab 9.

Đến đây hầu như đã cấu hình xong. Dự án đã kích hoạt 1 runner, và dự án đã có file .gitlab-ci.yml. Từ bây giờ, mỗi lần code được đưa lên thì runner sẽ thực thi test cũng như deploy dựa trên các câu lệnh được khai báo trong file cấu hình.

Khai báo biến để dùng trong các câu lệnh

Trong một số trường hợp, bạn có thể khai báo biến để có thể dùng trong các lệnh của runner. Có 3 nơi có thể cấu hình biến:
1. Cấu hình ngay bên trong file .gitlab-ci.yml
2. Cấu hình trong dự án. Vào Settings // CI/CD Pipelines, phần Secret variables (xem hình)

3. Cấu hình bên trong file config của runner. Bạn có nhớ lúc mình khởi tạo runner, có chỉ định một thư mục chứa config không, đây chính là nơi cấu hình chung cho runner này. Trong thư mục này sẽ có file là config.toml. Và bạn có thể gán biến trong cấu hình của từng runner. Cấu hình ở đây có một lợi thế là cứ runner này chạy sẽ nhận được biến đã cấu hình. Bạn không cần phải cấu hình nhiều lần ở từng dự án.

Ví dụ về một file .gitlab-ci.yml

Bên dưới là file cấu hình của một dự án trong hệ thống Microservices thuộc Teamcrop:

before_script:
- export "PATH=$PATH:/vendor/bin"
# Install ssh-agent if not already installed, it is required by Docker.
# (change apt-get to yum if you use a CentOS-based image)
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'

# Run ssh-agent (inside the build environment)
- eval $(ssh-agent -s)

# For Docker builds disable host key checking. Be aware that by adding that
# you are suspectible to man-in-the-middle attacks.
# WARNING: Use this only with the Docker executor, if you use it with shell
# you will overwrite your user's SSH config.
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'

variables:
  # Change this base on project name
  DEPLOYMENT_FOLDER_NAME: "tc-file"

test:
  image: voduytuan/gitlab-php-ci
  script:
  - bash ./ci/phplint.sh ./src/
  - phpcs --config-set ignore_errors_on_exit 1
  - phpcs --config-set ignore_warnings_on_exit 1
  - phpcs --standard=PSR2 --ignore=./src/index.php --error-severity=1 --warning-severity=8 -w --colors ./src/
  - phpunit --configuration ci/phpunit.xml

dev:
  image: voduytuan/gitlab-php-ci
  stage: deploy
  script:
  - ssh-add <(echo "$DEPLOYER_BETA_KEY")
  - echo "Deploy to $DEPLOYMENT_FOLDER_NAME"
  - rsync -avuz -e "ssh -p 22" --exclude-from="ci/deploy_exclude.txt" $CI_PROJECT_DIR/src/ $DEPLOYER_BETA_USER@$DEPLOYER_BETA_IP:/teamcrop/services/$DEPLOYMENT_FOLDER_NAME/src
  only:
  - dev

production:
  image: voduytuan/gitlab-php-ci
  stage: deploy
  script:
  - ssh-add <(echo "$DEPLOYER_PRODUCTION_KEY")
  - echo "Deploy to $DEPLOYMENT_FOLDER_NAME"
  - rsync -avuz -e "ssh -p 22" --exclude-from="ci/deploy_exclude.txt" $CI_PROJECT_DIR/src/ $DEPLOYER_PRODUCTION_USER@$DEPLOYER_PRODUCTION_IP:/teamcrop/services/$DEPLOYMENT_FOLDER_NAME/src
  only:
  - master
  when: manual

Trong ví dụ trên, phần test bên mình làm 3 việc:
– Chạy linter để đảm bảo sourcecode không bị lỗi cú pháp (phplint)
– Kiểm tra source code có theo chuẩn PSR2 hay không.
– Chạy PHPUnit

Còn về phần deploy thì có cấu hình 2 task là deploy dev và production. Ở task dev thì auto và lấy code từ branch dev. CÒn task production deploy từ branch master, tuy nhiên, có chế độ deploy manual, tức là nhấn thì mới deploy.

Về phần deploy source code thì sử dụng rsync để đẩy code từ repo sang server. Bạn sẽ thấy cú pháp giống nhau, chỉ khác là cấu hình đẩy đi đâu, với user nào và private key nào.

Do đặc thù của commandline nên sử dụng privatekey để đồng bộ code thông qua rsync. Do đó, trong project mình có cấu hình privatekey của user. Và bên server nhận (beta, production) mình đã đưa public key vào file authorized_keys. Bạn có thể tìm hiểu thêm về setup và generate cặp public/private key cho user deploy để hỗ trợ quá trình này tại link https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys–2. Hay ngắn gọn là thực hiện câu lệnh “ssh-keygen -t rsa -C “[email protected]” -b 4096″, nhập vài thông tin là bạn đã có public key (id_rsa.pub) để đem bỏ lên server (beta, production) và private key (id_rsa) đem bỏ vào setting biến môi trường.

—-
Dựa trên những kinh nghiệm CI/CD cho hệ thống Teamcrop.com theo mô hình microservice với hơn 40 repository lớn nhỏ, hy vọng bài viết này sẽ giúp được cho quá trình setup CI/CD cho hệ thống của bạn, cũng như tăng tốc quá trình phát triển dự án. Nếu thấy bài viết hay và hữu ích, hãy chia sẻ cho các anh em khác để cùng trao đổi và giao lưu.

Categories
DevOps

Speed up Microservices 1: Tác dụng phụ và một số chiến lược cơ bản


Chào mọi người, nếu như các bạn cũng biết thì dự án Teamcrop của mình xây dựng và chạy hoàn toàn trên kiến trúc Microservices và sau hơn 2 năm triển khai thì có một số vấn đề liên quan đến kiến trúc này, thiết nghĩ cần chia sẻ thêm với mọi người để mọi người thấy được rằng Microservices không phải là chìa khóa vạn năng như vẫn hay nghe quảng cáo, dụ dỗ. Mọi kiến trúc đều có đánh đổi và Microservices cũng vậy.

Categories
Technology

Lấy email từ bình luận của bài viết trong Facebook

Chào các bạn, số là hôm nay bữa giờ trong cái group Launch trên facebook có thấy 1 bài có 1 membẻ tặng phần mềm email marketing (miễn phí) cho các bạn bình luận. Có đến gần 500 comment chứa nội dung email nên thiết nghĩ đây là 1 kho báu cho những ai đang làm các tool email marketing, còn gì tuyệt vời hơn khi có gần 500 email khách hàng cực kỳ tiềm năng và sẵn sàng sử dụng. Mình nghĩ cũng có nhiều người như mình nhưng phải ngồi copy từng email ra thì thiệt là khổ, nay chia sẻ mọi người cách nhanh nhất (3 phút) để “copy” toàn bộ các email này.

Categories
Miscellaneous

New Year’s Resolution of 2018

Đến hẹn lại lên, hôm nay đã là ngày cuối cùng của 2017, mình lại ngồi viết vài dòng để tự đánh giá xem năm nay đã làm được những gì và có chỉ tiêu nào đã hoàn thành khi đặt ra cho năm nay hay không.