Theo tôi quan sát, database versioning (DB versioning – phiên bản hoá cơ sở dữ liệu – CSDL) thường là vấn đề khó khăn nhất đối với các nhóm triển khai Agile trong việc thực hành CI và CD; thậm chí nhiều nhóm phát triển không nhận ra vấn đề này.

Mọi lập trình viên ngày nay đều hiểu sự quan trọng của những công cụ quản lý source code và có thể sử dụng thành thạo SVN, Git…, tuy nhiên ít người nhận biết được tầm quan trọng và biết cách quản lý những thay đổi trên CSDL. Tại sao lại như vậy? Bởi theo phương pháp phát triển phần mềm truyền thống, CSDL thường được coi như một phần đặc biệt quan trọng và được thiết kế rất chi tiết trong giai đoạn thiết kế phần mềm; và gần như không được phép thay đổi trong quá trình phát triển. Việc thiết kế CSDL cũng thường được thực hiện bởi những người đặc biệt quan trọng nhằm đảm bảo thiết kế tối ưu và đáp ứng tầm nhìn lâu dài của ứng dụng.

Tuy nhiên, phương pháp phát triển phần mềm mới đã thay đổi hoàn toàn cách suy nghĩ và sử dụng CSDL; phương pháp phát triển hướng CSDL (DB oriented programming) đã không còn được sử dụng. Có nghĩa là, CSDL giờ đây chỉ là một thành phần trong phần mềm đơn thuần, không phải là một phần “bất khả xâm phạm”, càng không phải là thành phần đầu tiên được nghĩ tới khi thiết kế kế hệ thống. Dữ liệu vẫn luôn đặc biệt quan trọng, nhưng cấu trúc của dữ liệu (thiết kế CSDL) thì không hẳn. Trong phương pháp phát triển phần mềm Agile, mọi thành viên trong nhóm phát triển đều có thể thay đổi thiết kế DB bất cứ lúc nào nhằm phục vụ cho chức năng mình đang phát triển. Điều này dẫn đến tần suất thay đổi thiết kế CSDL lớn hơn; và vấn đề quản lý thiết kế CSDL cũng phát sinh.

Thứ nhất, khi một lập trình viên thay đổi CSDL, những thành viên khác trong nhóm phát triển không nhận biết được thay đổi này. Vấn đề này được thực hiện rất tốt với source code version control như SVN hay Git; những thành viên trong nhóm chỉ cần check-out là nhận được những thay đổi trên source code. Thứ hai, khi những thay đổi này gây ra xung đột (conflict), nhóm sẽ giải quyết như thế nào? Vấn đề này phức tạp hơn rất nhiều; vì mỗi thay đổi trên CSDL sau khi được áp dụng sẽ không thể roll-back; không đơn giản như cách chúng ta giải quyết xung đột trên source code.

Theo tôi quan sát, những nhóm phát triển thường áp dụng những phương pháp sau:

  • Sử dụng môi trường chia sẻ. Cách dễ nhất (và đôi khi là cách tốt nhất) là sử dụng môi trường phát triển chia sẻ (shared environment); mỗi lập trình viên làm việc trên máy tính của mình với source code được check-out và phát triển hoàn toàn độc lập, nhưng sử dụng chung một DB server. Phương pháp này có rất nhiều điểm lợi, đặc biệt là đảm bảo một môi trường chung với mọi sự thay đổi được cập nhật kịp thời tới mọi lập trình viên. Tuy nhiên, vẫn có ít nhất 2 vấn đề gặp phải:
    • Hiệu năng. Trong phần nhiều dự án (không xử lý dữ liệu lớn), hiệu năng từ việc sử dụng môi trường chia sẻ thấp hơn nhiều hiệu năng xử lý trên máy tính cá nhân (vấn đề đường truyền, phân chia module…); gây ảnh hưởng tới năng suất làm việc.
    • Quản lý revision. Đây là vấn đề không có giải pháp toàn diện. Chúng ta sẽ xử lý thế nào với trường hợp sau: Sau một vài thay đổi, nhóm phát hiện ra rằng một số thay đổi gần đây không đúng và muốn roll-back? Vấn đề này thường xuyên xảy ra giống như việc chúng ta phải revert những commit lỗi khi sử dụng source code version control. Nhưng chúng ta không thể làm vậy với CSDL.
  • Tạo những script tương ứng với từng thay đổi. Ý tưởng ở đây là, nhóm lưu trữ một CSDL ổn định và tương ứng với mỗi thay đổi, từng script sẽ được tạo ra. Trong trường hợp cần revert commit, nhóm sẽ restore CSDL này và áp dụng từng script tương theo tuần tự tới trước commit cần revert. Vấn đề khác có thể nảy sinh, bằng cách nào những lập trình viên biết cách tạo những script theo thứ tự hợp lý khi việc phát triển, commit của họ được thực hiện song song trong khi sự thay đổi trên CSDL cần thực hiện tuần tự? Bằng cách nào nhóm có thể phát hiện ra những xung đột trên CSDL?

Tuy vậy, ý tưởng về việc scripting DB (kịch bản hoá CSDL) là một ý tưởng hay, và được phát triển thành DB versioning. Cơ bản có thể được mô tả như sau:

  • Mọi thay đổi trên CSDL phải được tạo bằng script. Thay vì thực hiện tạo một bảng mới qua công cụ có giao diện người sử dụng, chúng ta viết thành câu lệnh CREATE TABLE và lưu trong file SQL.
  • CSDL cũng được quản lý bởi source code version control. Khi mọi thay đổi trên CSDL là script, chúng ta hiểu rằng CSDL cũng là source code và những file này được quản lý bởi source code version control là điều hợp lý.
  • Mọi script thay đổi trên CSDL gắn với từng phần phát triển phải được commit đồng thời. Ví dụ, lập trình viên thực hiện user story “quản lý khách hàng” cần check-in source code và script tạo ra bảng KhachHang trong một commit. Điều này giúp cho việc quản lý (đặc biệt là revert) trở nên đơn giản hơn.
  • Mọi thay đổi trên CSDL được kiểm tra trong quá trình build hoặc start-up của ứng dụng. Theo tôi, việc kiểm tra nên được thực hiện sớm nhất có thể (tốt nhất là trong quá trình build) nhằm giảm thiểu thời gian lập trình viên nhận biết những thay đổi trên CSDL. Nếu việc áp dụng những script này không thành công, quá trình build hoặc khởi chạy ứng dụng sẽ thất bại; qua đó lập trình viên nhận biết được sự thay đổi và những xung đột đang xảy ra trên CSDL.
  • Các version của sự thay đổi được lưu trữ tại chính CSDL đó. Một cách tiếp cận đơn giản là tạo ra một bảng lưu trữ lịch sử những script đã được áp dụng trên chính CSDL đó.

Kỹ thuật trên sẽ khiến CSDL và những thay đổi trên CSDL (script) được coi là source code; giúp lập trình viên dễ dàng quản lý hơn rất nhiều, cả về tư tưởng lẫn kỹ thuật.

Hiện nay, thị trường cung cấp rất nhiều công cụ hoặc thư viện hỗ trợ việc versioning DB như dbUp, RoundhousE, các sản phẩm của Red Gate…, hầu hết đều có chung tư tưởng trên, và có thể có thêm một số chức năng khác như:

  • Restore DB. Khởi tạo hoàn toàn một CSDL từ các scripts là điều tuyệt vời khi chúng ta có bộ lịch sử đầy đủ; tuy nhiên, quá trình này thường tốn khá nhiều thời gian thực hiện. Cách làm tốt hơn là nhóm phát triển “chốt” một CSDL ổn định, được coi là điểm bắt đầu và chỉ quản lý những thay đổi bắt đầu từ phiên bản này. Sau một khoảng thời gian, một phiên bản ổn định khác có thể được thay thế. Tính năng này nhằm giảm bớt thời gian khởi tạo CSDL nguyên thuỷ.
  • Có thể roll-back khi gặp lỗi. Khi tạo một script cho sự thay đổi, lập trình viên cũng tạo một “roll-back” script. Khi gặp lỗi, công cụ này sẽ thực hiện những roll-back script này theo thứ tự ngược lại giúp CSDL quay lại trạng thái trước khi bị thay đổi. VD: script ALTER TABLE ADD COLUMN Address NVARCHAR(50); có roll-back script là ALTER TABLE Customer DROP COLUMN Address;
  • Sử dụng chính CSDL hiện có lưu trữ sự thay đổi.
  • Tích hợp với những công cụ CI, CD. Đây là một chức năng tuyệt vời khiến việc tích hợp, triển khai chỉ với một nút nhấn; cũng như đảm bảo không gây sai sót bởi con người (rất dễ xảy ra) ảnh hưởng tới môi trường thật.

DB versioning là vấn đề đặc biệt quan trọng và khá nhức đầu trong việc triển khai CD. Tôi sẽ trở lại vấn đề này với một hướng dẫn cụ thể trong bài viết sau.

1,463 total views, no views today

Đây là chủ đề nhận được khá nhiều sự quan tâm thời gian gần đây, đặc biệt qua hai sự kiện gần nhất của ITLC. Tuy rằng có nhiều người quan tâm và biết đến DevOps nhưng qua hai sự kiện này, theo đánh giá cá nhân, lượng người “thực sự hiểu biết” không nhiều, đa phần mọi người đánh đồng DevOps và CD; bài viết này mong cung cấp một số thông tin giúp hiểu đúng về DevOps và CD.

CD: Continuous Delivery hay Continuous Deployment?

Những nhóm phát triển phần mềm theo phương pháp Agile thường quen thuộc với kỹ thuật / công cụ tích hợp liên tục (Continuos Integration – CI) nhằm liên tục tích hợp phần tăng trưởng được tạo ra vào sản phẩm giúp kiểm soát tình hình thông qua thực hiện kiểm thử (đặc biệt là integration test, regession test) khiến sản phẩm đạt sự ổn định. Đây là một kỹ thuật / công cụ được thực hiện trong giai đoạn phát triển (development).

Bên cạnh CI, mọi người hay nói đến CD, nhưng lại không thực sự hiểu CD từ viết tắt của Continuous Delivery hay Continuous Deployment và cũng thường cho rằng hai khái niệm này là giống nhau. Thực tế là:

Continuous Delivery, giống như CI là một tập hợp những kỹ thuật / công cụ đảm bảo việc triển khai (deployment) được thành công bằng việc liên tục chuyển giao (delivery) những phần tích hợp lên môi trường staging (thường là rất giống với môi trường production). Bằng việc liên tục kiểm thử trong môi trường staging, phần mềm đảm bảo đủ chất lượng để deploy qua production; những deployable artifact này vẫn chỉ tồn tại trên staging và không được deploy tự động qua production.

Continuous Deployment, đúng như tên gọi, là một tập hợp những kỹ thuật / công cụ đảm bảo việc tự động hoá toàn bộ quá trình từ development đến production. Continuous Deployment được coi là bước phát triển của Continuous Delivery, hoàn tất giai đoạn chuyển giao đến người dùng cuối.

Có thể so sánh CI, CD đơn giản thế này:

  • Continuous Integration: Tập trung vào source code, tạo sự “tự tin” về source code qua unit test, integration test, tạo ra staging-ready-artifact.
  • Continuous Delivery: Tập trung vào artifact và môi trường (environment), tạo sự “tự tin” về deployable-artifact qua acception test, tạo ra production-ready-artifact.
  • Continuous Deployment: Tập trung vào người dùng cuối, tạo ra production-deployed.

Continuous_Delivery_Continuous_Deployment

Vậy: Continuous Delivery hay Continuous Deployment?

Nhưng tóm lại là Continuous Delivery và Continuous Deployment khác gì nhau? Rất nhiều người vẫn bị nhập nhằng giữa 2 nguyên tắc này. Lý do là: nếu staging là môi trường giống với production, thì khi đã làm được Continuous Delivery thì đương nhiên chúng ta cũng làm được Continuous Deployment (đều là deploy qua 1 môi trường, vậy thôi). Đúng. Về mặt kỹ thuật là như vậy. Vì vậy, những công cụ như Pupet, Octopus, Ansible… đều được sử dụng cho CD (còn ai hiểu CD là gì thì tuỳ ngữ cảnh, deploy tới staging thì là Delivery; deploy tới production thì là Deployment). Nhưng công cụ không phải thứ quan trọng nhất.

Thứ nhất, staging không phải production. Dù chỉ cần 1 cấu hình đơn giản là artifact được deploy qua bất cứ đâu. Nhưng chúng ta có đủ “tự tin” để việc deploy qua production được thực hiện tự động?

Thứ hai, staging vẫn không phải là production. Production luôn cần ổn định và mong muốn zero-down-time, staging thì không cần thiết.

Với những single-user-app như ứng dụng chạy trên PC, mobile… thì khá đơn giản, tôi không bàn tới. Với web app, các tổ chức thường dè dặt trong việc triển khai Continuous Deployment vì những lý do trên.

Về lý do thứ nhất, tổ chức chỉ có thể thực hiện Continuous Deployment khi hệ thống QA được thực hiện tự động hoặc “gần như tự động” với phần tích hợp để chắc chắn rằng sản phẩm đủ chất lượng và triển khai đúng thời điểm một cách tự động – đây là vấn đề tương đối phức tạp.

Về lý do thứ hai, cách làm phổ biến là sử dụng kỹ thuật “blue-green deployment”, deploy lần lượt từng phần hệ thống với HAproxy đứng trước nhằm đảm bảo traffic được route tới những phần đã được deploy thành công (green, version mới); trước đó traffic được route tới những thành đã chạy ổn định (blue, version cũ). Vấn đề sẽ trở nên phức tạp khi phần green và blue có nhiều sai khác về business logic dẫn đến không toàn vẹn dữ liệu. Đây là một trong những cản trở lớn nhất khiến tổ chức không “tự tin” với việc deploy tự động (tôi sẽ trình bày trong một bài viết khác).

blue_green_deployments

DevOps

Như bạn thấy, CD dù là Continous Delivery hay Continuous Deployment thì vấn đề phức tạp không phải là kỹ thuật hay công nghệ. Và DevOps cũng vậy.

Việc liên tục triển khai dẫn đến nhu cầu cộng tác chặt chẽ giữa nhóm phát triển (development) và nhóm vận hành (operation) và cách phân chia tổ chức theo nhóm chức năng (functional team) trở nên lỗi thời; cách làm việc liên chức năng (cross-functional) sẽ hiệu quả hơn rất nhiều. Và đó là DevOps. DevOps = Development + QA + Operation.

Như vậy DevOps thực sự là sự chuyển đổi trong tổ chức về cách làm, văn hoá (trong cách tổ chức nhóm, cộng tác…) và kỹ thuật, công nghệ nhằm linh hoạt hơn và nhanh chóng phản ứng với sự thay đổi xuyên suốt quá trình từ phát triển tới triển khai đến tay người dùng cuối với chất lượng đảm bảo một cách nhanh nhất.

Tại sao DevOps phức tạp? Vì DevOps thực sự tạo ra một văn hoá cộng tác để chuyển giao sản phẩm theo cách mới. Theo quy trình phát triển phần mềm cổ điển, các nhóm development, QA, operation hoạt động rất riêng biệt với những kỹ năng, văn hoá khác nhau. Giờ đây họ đứng cùng nhau với một mục tiêu chung và một văn hoá chung. Điều này không thể có trong một thời gian ngắn.

Tại sao DevOps thông thường nhanh thành công hơn trong môi trường Linux? Tôi lưu ý là thông thường. Vì thông thường, nhóm opeartion sử dụng Linux quen thuộc hơn với việc sử dụng câu lệnh, lập trình (viết script…); so với Windows, nhóm sử dụng công cụ đồ hoạ để cấu hình. Và CI, CD nói chung làm việc nhiều với dòng lệnh.

Một nhóm toàn developer có thể thực hành DevOps? Hôm trước, anh Trịnh Minh Cường có nói rằng “nhóm của anh toàn dev, làm DevOps tốt”. Đúng, vì bản thân nhóm phát triển đã có thể build, run phần mềm trên môi trường local thì hoàn toàn có thể triển khai đến production và CD là điều nằm trong tầm tay. Nhưng đó mới chỉ là kỹ thuật. Developer thường không có kiến thức về hệ thống, vận hành như operator. Đơn giản như cấu hình quyền thư mục, chmod 777 giúp họ chạy, test hoàn toàn tuyệt vời trên local nhưng sẽ là thảm hoạ trên production. Chính vì vậy, tổ chức cần sự kết hợp kiến thức, kỹ năng của cả developer, QA, operator để triển khai DevOps thành công.

Và xu thế?

Trong bài nói chuyện của CTO Automic tại ITLC, có nói đến heuristic CD khiến tôi liên hệ tới 2 xu thế chính hiện nay.

Một là, tính agility được mở rộng theo bề ngang của tổ chức. Bắt đầu bởi Agile software development; tiếp đến là DevOps khiến Dev và Ops gắn kết hơn; tiếp đến là NetOps khiến Net và Ops (thậm chí là Dev-Ops-Net) gắn kết hơn.

Hai là, tính agility được nâng cao dần. Bắt đầu bởi manual delivery / deployment; tiếp đến là CD khiến việc delivery / deployment tự động; tiếp đến là heuristic CD khiến việc delivery / deployment tự động ở mức độ “thông minh” hơn, tự động nhưng đúng thời điểm và có “trí tuệ” như manual delivery / deployment.

Những vấn đề này hơi phức tạp một chút, hy vọng tôi có thể trở lại trong một bài viết gần. Ngoài ra, bạn có thể tham khảo những bài viết ở dưới:

http://martinfowler.com/bliki/ContinuousDelivery.html

http://martinfowler.com/bliki/BlueGreenDeployment.html

1,844 total views, no views today

Theo tôi, đây là công cụ quan trọng bậc nhất trong những nhóm thực hành Agile với quy mô vừa tới lớn. CI (Continous Integration – tích hợp liên tục) là một quy trình / công cụ giúp nhóm phát triển ngay lập tức nhận diện được những ảnh hưởng của một commit (một đoạn code hay một chức năng được thêm vào) với toàn bộ hệ thống nhằm phản ứng tức thì để đảm bảo toàn hệ thống hoạt động như mong đợi.

Khác với mô hình phát triển phần mềm truyền thống khi việc những module được thiết kế rất tỉ mỉ, phát triển độc lập và được thực hiện việc tích hợp vào giai đoạn cuối của việc phát triển nhằm phục vụ việc kiểm thử tích hợp và kiểm thử hệ thống; Agile coi trọng việc phát triển những chức năng liên tục theo kiểu “bồi đắp”. Cá biệt, trong Scrum, một vài phần tăng trưởng phải được chuyển giao sau mỗi Sprint – là những chức năng phải vận hành được bởi người dùng. Do đó, việc kiểm thử tích hợp và kiểm thử hệ thống diễn ra thường xuyên hơn (tối thiểu là trong mỗi Sprint), khiến việc thực hiện công việc tích hợp thủ công không khả thi. Ý tưởng về một công cụ thực hiện việc tích hợp vì thế đã xuất hiện.

Tuy vậy, chúng ta có thể hiểu CI là một quy trình, yêu cầu những thay đổi trên hệ thống phải được nhanh chóng nhận biết sự ảnh hưởng của chúng thông qua việc tích hợp sớm với hệ thống đang có, và thực hiện việc kiểm thử tích hợp và kiểm thử hệ thống. Lúc này CI liên quan mật thiết đến khái niệm “daily build”: toàn bộ mã nguồn của hệ thống phải được build hàng ngày nhằm nhận biết những lỗi tiềm năng và khắc phục sớm.

Tại sao phải là “daily build”? Hãy nhớ rằng, bất cứ thành viên nào trong nhóm phát triển đều có thể là một nhà thiết kế phần mềm, và một công việc của họ có thể ảnh hưởng rất nhiều tới hệ thống. Ví dụ, nhóm phát triển chạy nước rút với Sprint 2 tuần (10 ngày làm việc), với 2 lập trình viên cùng thực hiện việc thay đổi database nhằm hoàn thành 2 user story song song và mất 5 ngày thực hiện, việc tích hợp vào ngày thứ 6 với hàng tá xung đột về ràng buộc trong database có thể là một cơn ác mộng. Daily build chính là giải pháp để phát hiện xung đột xảy ra ngay từ ngày thứ 2 để nhóm phát triển có cách giải quyết thích hợp.

Nói chung, daily build giống như Daily Scrum về mặt mặt mã nguồn hệ thống. Trong Daily Scrum, nhóm thực hiện việc tích hợp, đồng bộ công việc với nhau cho các task hay user story. Trong daily build, nhóm thực hiện việc tích hợp, đồng bộ công việc với nhau cho mã nguồn và những thành phần liên quan như database, service… Xét cho cùng, kết quả của những task hay user story trên cũng là mã nguồn và những thành phần liên quan; bởi vậy, việc thực hiện Daily Scrum mà không có daily build giống như chúng ta đang đồng bộ những vấn đề “trên trời” mà không giải quyết những xung đột cụ thể.

Với nhiều nhóm thực hành Agile, daily build thậm chí là không đủ, nhóm cần tới commit build – thực hiện việc tích hợp, kiểm thử cho mỗi commit vào source code repository.

Một hệ thống CI thông thường thực hiện những tác vụ sau:

  1. Phát hiện thay đổi trong source code repository (xuất hiện commit mới)
  2. Phân tích chất lượng source code
  3. Thực hiện build
  4. Chạy toàn bộ unit test
  5. Chạy toàn bộ integration test
  6. Sinh ra những tạo tác có thể triển khai được, gọi là deployable artifact
  7. Có thể, deploy những artifact này và thực hiện những kiểm thử khác nếu cần

Nếu một trong những bước trên không thành công:

  • Tuỳ thuộc vào mức độ nghiêm trọng, việc tích hợp có thể dừng lại hoặc đi tiếp
  • Kết quả tích hợp được thông báo tới nhóm phát triển qua email, hệ thống chat. Thông qua sour code repository, CI có thể nhận biết cá nhân đã thực hiện việc commit gây ra lỗi trong việc tích hợp.
  • Nhóm phát triển hoặc cá nhân thực hiện commit thực hiện sửa lỗi và commit
  • CI phát hiện thay đổi trong source code repository và thực hiện lại những bước trên

Trong những tác vụ trên, tác vụ 1, 3, 6 thực sự rất dễ thực hiện và được hỗ trợ bởi hầu hết những công cụ CI hiện có trên thị trường. Những tác vụ 2, 4, 5 không đơn giản chỉ là sự hỗ trợ của công cụ, tư tưởng và cách thực hiện phía sau quan trọng hơn rất nhiều; chúng ta sẽ bàn về những vấn đề này ở những bài viết sau. Tác vụ 7 lại liên quan tới một hệ thống CD (Continuos Deployment – triển khai liên tục) với tư tưởng giống với CI nhưng dưới góc độ “triển khai” và lại phụ thuộc vào tần suất release của sản phẩm và độ phức tạp của môi trường nên không hẳn là một công cụ tiên quyết trong việc thực hành Agile.

Tuy vậy, nhiều nhóm thực hành Agile vẫn lựa chọn việc triển khai CI và CD đồng thời bởi một trong những best practice của CI là đảm bảo môi trường đồng nhất (hoặc gần giống nhất) giữa môi trường kiểm thử (tích hợp) và môi trường production.

Chúng ta dễ dàng nhận thấy ưu điểm của commit build so với daily build vì cô lập được thay đổi theo từng commit khiến việc xử lý xung đột đơn giản hơn. Tuy vậy, thời gian để CI thực hiện toàn bộ 7 tác vụ trên có thể rất nhiều với một số môi trường của dự án khiến việc này không khả thi; nên nhiều nhóm thực hành Agile vẫn lựa chọn daily build. Tôi là một người theo trường phái commit build và luôn cố gắng duy trì CI theo cách này. Với những dự án phức tạp, tôi thường sử dụng nhiều build agent để thực hiện việc tích hợp song song với những commit cùng thời điểm. Một giải pháp khác là, lược bỏ những tác vụ không cần thiết (ví dụ tác vụ 7) để vẫn thực hiện việc tích hợp với từng commit và thực hiện một “long build” vào cuối ngày.

Hiện nay có rất nhiều công cụ CI cho phép nhóm thực hành Agile lựa chọn với những tác vụ cơ bản như trên, phổ biến nhất có lẽ là Jenkins – hệ thống mã nguồn mở với rất nhiều plugin phù hợp với nhiều điều kiện hệ thống khác nhau. Tôi cũng là một kẻ hâm mộ Jenkins nhưng lại thường sử dụng những công cụ khác khi có thể, chỉ bởi giao diện của Jenkins thì quá tệ và cũng vì cónhiều người sử dụng quá.

821 total views, no views today