Tải bản đầy đủ (.pdf) (31 trang)

XÂY DỰNG KIẾN TRÚC TRIỂN KHAI LIÊN TỤC CHO CÁC HỆ THỐNG DỰA TRÊN VI DỊCH VỤ

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.39 MB, 31 trang )

ĐẠI HỌC QUỐC GIA HÀ NỘI
TRƯỜNG ĐẠI HỌC CÔNG NGHỆ

NGUYỄN TRỌNG HIẾU

XÂY DỰNG KIẾN TRÚC TRIỂN KHAI LIÊN TỤC CHO CÁC
HỆ THỐNG DỰA TRÊN VI DỊCH VỤ

LUẬN VĂN THẠC SĨ KỸ THUẬT PHẦN MỀM

HÀ NỘI - 2018


ĐẠI HỌC QUỐC GIA HÀ NỘI
TRƯỜNG ĐẠI HỌC CÔNG NGHỆ

NGUYỄN TRỌNG HIẾU

XÂY DỰNG KIẾN TRÚC TRIỂN KHAI LIÊN TỤC CHO CÁC
HỆ THỐNG DỰA TRÊN VI DỊCH VỤ
Ngành: Công nghệ thông tin
Chuyên ngành: Kỹ thuật phần mềm
Mã số: 60480103

LUẬN VĂN THẠC SĨ KỸ THUẬT PHẦN MỀM

HÀ NỘI - 2018



MỤC LỤC



MỞ ĐẦU ------------------------------------------------------------------------------------------1
CHƯƠNG 1: KIẾN THỨC CƠ BẢN -------------------------------------------------------4
1.1 Tính toán đám mây--------------------------------------------------------------------4
1.1.1 Giới thiệu về tính toán đám mây -----------------------------------------------4
1.1.2 Sự phổ biến của ứng dụng dựa vào đám mây ---------------------------------4
1.2 Tích hợp, chuyển giao và triển khai liên tục -------------------------------------4
1.2.1 Tích hợp liên tục ------------------------------------------------------------------4
1.2.2 Chuyển giao liên tục -------------------------------------------------------------5
1.2.3 Triển khai liên tục ----------------------------------------------------------------5
1.3 Kiến trúc hệ thống ---------------------------------------------------------------------5
1.3.1 Kiến trúc một khối----------------------------------------------------------------5
1.3.2 Kiến trúc hướng dịch vụ (Service-Oriented Architecture) ------------------6
1.3.3 Kiến trúc vi dịch vụ --------------------------------------------------------------6
1.4 Các mô hình triển khai ---------------------------------------------------------------7
1.4.1 Mô hình triển khai tùy biến -----------------------------------------------------7
1.4.2 Mô hình không tùy biến kết hợp với Reverse Proxy-------------------------8
1.4.3 Mô hình không tùy biến với ứng dụng vi dịch vụ ----------------------------8
1.5 Khái niệm Container ------------------------------------------------------------------8
1.6 Tổng kết ---------------------------------------------------------------------------------9
CHƯƠNG 2: YÊU CẦU CỦA HỆ THỐNG TRIỂN KHAI LIÊN TỤC ----------- 10
2.1 Yêu cầu về điều phối container --------------------------------------------------- 10
2.1.1 Khám phá dịch vụ -------------------------------------------------------------- 10
2.1.2 Bộ lập lịch ----------------------------------------------------------------------- 10
2.1.3 Điều phối container ------------------------------------------------------------ 10
2.2 Yêu cầu cho các nhà phát triển --------------------------------------------------- 11
2.2.1 Khả năng tự triển khai --------------------------------------------------------- 11
2.2.2 Nhận được các thông tin về kết quả build ----------------------------------- 11
2.3 Yêu cầu về vận hành ---------------------------------------------------------------- 11
2.3.1 Quản lý cấu hình ---------------------------------------------------------------- 11

2.3.2 Thu thập và lưu trữ log -------------------------------------------------------- 11
2.3.3 Giám sát và thông báo lỗi ----------------------------------------------------- 12
2.3.4 Có khả năng đảo ngược phiên bản triển khai (rollback) ------------------ 12
2.3.5 Triển khai không có thời gian ngừng hoạt động (no-downtime
deployment) ------------------------------------------------------------------------------- 12
2.3.6 Thiết kế mạng triển khai ------------------------------------------------------- 12
CHƯƠNG 3: THIẾT KẾ -------------------------------------------------------------------- 13
3.1 Luồng triển khai cơ bản ------------------------------------------------------------ 13
3.2 Phân tích và lựa chọn công nghệ ------------------------------------------------- 14
3.2.1 Hệ thống quản lý mã nguồn --------------------------------------------------- 14
3.2.2 Máy chủ CI và máy chủ triển khai ------------------------------------------- 14
3.2.3 Công nghệ container ----------------------------------------------------------- 15
3.2.4 Nền tảng điều phối container ------------------------------------------------- 15


3.2.5 Kênh thông báo ----------------------------------------------------------------- 15
3.2.6 Hệ thống thu thập và quản lý log --------------------------------------------- 15
3.2.7 Công nghệ giám sát hệ thống và cảnh báo ---------------------------------- 16
3.2.8 Giải pháp quản lý cấu hình ---------------------------------------------------- 16
3.3 Luồng triển khai liên tục hoàn thiện--------------------------------------------- 17
CHƯƠNG 4: CÀI ĐẶT VÀ ĐÁNH GIÁ ------------------------------------------------- 20
4.1 Cài đặt --------------------------------------------------------------------------------- 20
4.1.1 Hệ thống vi dịch vụ mẫu------------------------------------------------------- 20
4.1.2 Cách thức tổ chức mã nguồn và quá trình build ảnh docker -------------- 20
4.1.3 Cài đặt môi trường sản phẩm ------------------------------------------------- 21
4.2 Đánh giá kết quả --------------------------------------------------------------------- 24
KẾT LUẬN ------------------------------------------------------------------------------------- 26


MỞ ĐẦU

Trong những thập kỉ qua, chúng ta đã chứng kiến nhiều sự thay đổi trong cách
mà một tổ chức phát triển và chuyển giao phần mềm, đặc biệt là về tốc độ và chất
lượng chuyển giao.
Trước đây, mô hình phát triển phần mềm phổ biến là mô hình thác nước, trong
đó việc phát triển được chia làm nhiều giai đoạn từ việc lập kế hoạch, phân tích yêu
cầu, thiết kế hệ thống, cài đặt, kiểm thử, triển khai và bảo trì. Tuy nhiên, tỉ lệ thành
công của những dự án phát triển phần mềm theo mô hình thác nước là rất thấp. Một
trong những lí do phổ biến đó là yêu cầu hệ thống được khảo sát và được sử dụng để
xây dựng phần mềm từ đầu dự án về cơ bản không chính xác hoặc có thiếu sót: có
những yêu cầu chỉ sinh ra khi người sử dụng thực sự dùng phần mềm.
Những phương pháp xây dựng phần mềm linh hoạt (agile) được phát minh để
khắc phục những điểm yếu của mô hình thác nước đó. Thay vì để người dùng phải chờ
đợi 6 tháng hay 1 năm cho mỗi lần phát hành, các tổ chức sẽ liên tục cập nhật phần
mềm cho người sử dụng (thường là 1 tháng), thu thập phản hồi, sửa đổi hay bổ sung
thêm yêu cầu để cải tiến phần mềm cho những lần cập nhật sau.
Vấn đề phát triển phần mềm đã được cải thiện đáng kể, tuy nhiên vấn đề mới lại
nảy sinh: các công việc liên quan tới vận hành cũ không còn theo kịp tốc độ phát triển
phần mềm: những nhà phát triển muốn phát hành bản cập nhật mới nhanh nhất có thể
để có thể lấy phản hồi từ người sử dụng, nhưng qúa trình từ các bản build tới khi sản
phẩm được triển khai thành công mất nhiều thời gian.
Các khái niệm tích hợp, chuyển giao và triển khai liên tục được giới thiệu với
những kinh nghiệm thực tiễn giúp giải quyết vấn đề nêu trên. Tuy nhiên, do giới hạn
về công nghệ, mục tiêu của triển khai liên tục chưa đạt được vào thời điểm mà nó
được giới thiệu: triển khai đòi hỏi phần cứng, mà việc chuẩn bị phần cứng mất nhiều
thời gian (mua bán, cài đặt, quản lý, ...).
Với sự phát triển của Internet và sự xuất hiện của tính toán đám mây, các ứng
dụng không còn bị bó buộc vào cơ sở hạ tầng vật lý mà đã có thể được triển khai trên
các nền tảng đám mây. Những ứng dụng có thể sử dụng tài nguyên co dãn của đám
mây để mở rộng, thu hẹp tùy vào nhu cầu sử dụng. Những ứng dụng như vậy được gọi
là những ứng dụng dựa vào đám mây.

Kiến trúc các hệ thống phần mềm cũng tiến hóa để sử dụng lợi ích mà nền tảng
đám mây mang lại. Trước đây, những dụng máy chủ (server-side application) truyền
thống thường được xây dựng bằng một kiến trúc gồm nhiều lớp, với mỗi lớp chứa các
loại thành phần khác nhau, ví dụ như:
• Lớp giao diện: chịu trách nhiệm xử lý các yêu cầu HTTP và trả lại kết quả là
giao diện HTML hoặc dữ liệu ở dạng JSON/XML tới người dùng.
• Lớp logic nghiệp vụ: chịu trách nhiệm tính toán và xử lý dữ liệu thô sang dữ
liệu có ý nghĩa theo nghiệp vụ nhất định.

1


• Lớp truy xuất dữ liệu: là cửa ngõ để truy cập vào cơ sở dữ liệu của các lớp
khác. Lớp truy xuất dữ liệu cung cấp giao diện (API) một khối cho các lớp
khác, độc lập với cơ sở dữ liệu đang được sử dụng (MySQL, PostgresSQL, …)
Những ứng dụng sử dụng kiến trúc này thường được đóng gói và triển khai như
một khối thống nhất. Những ứng dụng như vậy gọi là ứng dụng một khối (monolith).
Ứng dụng một khối có ưu điểm là dễ phát triển, kiểm thử và triển khai; tuy nhiên
do tất cả các thành phần (module) đều được đóng vào một gói, ứng dụng ngày càng trở
nên lớn và phức tạp, khiến việc hiểu và sửa đổi ứng dụng khó khăn hơn. Kích cỡ của
ứng dụng sẽ làm chậm quá trình khởi động. Mỗi khi có cập nhật gì thì toàn bộ ứng
dụng cần phải được triển khai lại, dẫn đến việc triển khai liên tục trở nên khó khăn
hơn. Việc scale ứng dụng cũng sẽ đòi hỏi nhiều tài nguyên và phức tạp. Ngoài ra, lỗi
trong một module hoàn toàn có thể khiến cho cả ứng dụng một khối bị sập. Khi muốn
thay đổi công nghệ mới thì có khi cả ứng dụng cần phải được phát triển lại, gây lãng
phí thời gian và tiền bạc.
Kiến trúc vi dịch vụ (microservice) được sinh ra để giải quyết những vấn đề trên.
Ý tưởng của kiến trúc này là chia ứng dụng ra những dịch vụ nhỏ hơn, hoàn toàn riêng
biệt với nhau và giao tiếp với nhau thông qua các API. Ví dụ: những dịch vụ liên quan
tới nghiệp vụ được phát triển riêng, cung cấp các API cho các dịch vụ khác. Những

thành phần giao diện người dùng cũng được tách ra như một dịch vụ, chúng giao tiếp
với những dịch vụ nghiệp vụ để lấy dữ liệu cần thiết và hiển thị lên cho người dùng.
Kiến trúc vi dịch vụ giải quyết được những vấn đề mà những ứng dụng một khối
gặp phải:
• Việc chia ứng dụng ra thành những dịch vụ nhỏ hơn cho phép các dịch vụ được
phát triển và kiểm thử nhanh chóng, độc lập. Mỗi đội sẽ được giao phát triển
một hoặc một vài dịch vụ trong ứng dụng, cách biệt với các dịch vụ khác. Điều
này khiến mã nguồn trở nên nhỏ hơn, dễ hiểu và quản lý.
• Mỗi dịch vụ nhỏ trong ứng dụng có thể được triển khai độc lập bởi mỗi đội.
• Do các dịch vụ trong ứng dụng chỉ giao tiếp với nhau thông qua API, mỗi dịch
vụ có thể sử dụng công nghệ riêng. Điều này giúp việc cập nhật công nghệ cho
toàn bộ ứng dụng trở nên dễ dàng (cập nhật lần lượt).
• Tích hợp liên tục trở nên khả thi do việc triển khai mỗi dịch vụ là độc lập với
các dịch vụ khác.
• Mở rộng (scale) ứng dụng trở nên đơn giản và nhanh chóng, do việc scale ứng
dụng sẽ được phân nhỏ thành scale các dịch vụ.
Kiến trúc vi dịch vụ hiện nay thường được sử dụng cùng với công nghệ
container. Mỗi ảnh container là một đóng gói của ứng dụng kèm những thư viện cần
thiết để ứng dụng có thể chạy được. Container sử dụng nhân chung với nhân hệ điều
hành nên nhẹ hơn nhiều so với công nghệ máy ảo. Ngoài ra, container giúp cho ứng
dụng chạy như nhau trên mọi môi trường, do vậy hỗ trợ tốt trong việc triển khai các
dịch vụ trong kiến trúc vi dịch vụ. Cùng với nền tảng đám mây, kiến trúc vi dịch vụ và
công nghệ container giúp cho ý tưởng về việc triển khai liên tục trở nên ngày một thực
tế hơn.
2


Tuy nhiên, vấn đề gặp phải của kiến trúc vi dịch vụ sử dụng container là khi số
lượng dịch vụ tăng lên, việc quản lý và phối hợp những dịch vụ đó trở nên khó hơn
nhiều lần so với các ứng dụng một khối. Ngoài ra, việc giám sát nhiều dịch vụ cũng

phức tạp hơn so với cách triển khai truyền thống; khi có lỗi xảy ra thì việc xem log và
sửa lỗi cũng gây những khó khăn nhất định cho đội phát triển.
Với thực trạng như vậy, luận văn sẽ đề xuất ra giải pháp để giải quyết những vấn
đề trên, từ đó đảm bảo được việc tích hợp, triển khai liên tục cho quá trình phát triển
phần mềm.
Luận văn bao gồm 04 chương với nội dung của các chương được phân bố như
sau:
• Chương 1: Giới thiệu về các khái niệm cơ bản liên quan tới tính toán đám mây,
tích hợp và triển khai liên tục, các kiến trúc hệ thống phần mềm và công nghệ
container.
• Chương 2: Phân tích yêu cầu để xây dựng mô hình triển khai liên tục cho cho
hệ thống vi dịch vụ sử dụng container
• Chương 3: Trình bày thiết kế
• Chương 4: Cài đặt và đánh giá
• Kết luận: Kết luận và hướng nghiên cứu dự định trong tương lai.

3


CHƯƠNG 1: KIẾN THỨC CƠ BẢN
1.1 Tính toán đám mây
1.1.1 Giới thiệu về tính toán đám mây
Tính toán đám mây là sự cung cấp, phân phối sức mạnh tính toán, hệ thống lưu
trữ, ứng dụng hoặc các nguồn tài nguyên công nghệ thông tin khác thông qua nền tảng
đám mây, sử dụng kênh truyền internet và giá cả được tính toán dựa vào khối lượng sử
dụng. Sử dụng tính toán đám mây, một công ty hay tổ chức không cần đầu tư nhiều chi
phí ban đầu vào việc chuẩn bị các thiết bị phần cứng, hay dành nhiều thời gian để quản
lý chúng. Thay vào đó, công ty/tổ chức chỉ cần cung cấp đúng yêu cầu về chủng loại
và kích thước nguồn tài nguyên tính toán cho nhà cung cấp dịch vụ, mọi thứ sẽ được
cung ứng gần như ngay lập tức. Các dịch vụ đám mây gần như trong suốt với người

dùng, và với giao diện người dùng đơn giản, hầu hết các dịch vụ đám mây đòi hỏi rất
ít kiến thức về quản trị.
Tính toán đám mây được chia thành 3 mô hình cơ bản: cơ sở hạ tầng như một
dịch vụ(Infrastructure as a service – IaaS), nền tảng như một dịch vụ(Platform as a
service – PaaS), phần mềm như một dịch vụ(Software as a service – SaaS).

1.1.2 Sự phổ biến của ứng dụng dựa vào đám mây
Việc chuyển đổi sang đám mây là sự phát triển tự nhiên đối với việc cung ứng
các dịch vụ công nghệ thông tin. Nền tảng đám mây cung cấp một môi trường với các
tài nguyên đa dạng, từ tính toán, lưu trữ cho tới mạng, có khả năng co dãn, linh động
theo nhu cầu sử dụng. Ngày càng nhiều công ty, tổ chức chuyển dần sang phát triển
phần mềm, ứng dụng cho nền tảng đám mây thay vì cách phát triển truyền thống bởi
các đặc điểm nổi bật của đám mây như: tốc độ triển khai nhanh, khả năng cô lập lỗi
tốt, và tính co dãn (scale).

1.2 Tích hợp, chuyển giao và triển khai liên tục
1.2.1 Tích hợp liên tục
Tích hợp liên tục là quá trình tự động hóa việc dịch (build) và kiểm thử mã mỗi
khi các thành viên trong đội cập nhật mã lên hệ thống quản lý phiên bản (tích hợp liên
tục đòi hỏi những nhà phát triển phải tích hợp mã vào một kho chứa mã duy nhất).
Tích hợp liên tục khuyến khích các nhà phát triển chia sẻ mã và các bài kiểm thử đơn
vị (unit test) bằng việc hợp nhất những thay đổi trên máy lên kho quản lý mã chung
ngay khi hoàn thiện một tác vụ nhỏ nào. Từ yêu cầu hợp nhất, các máy chủ sẽ kích
hoạt luồng (pipeline): lấy mã mới về máy chủ CI, chạy quá trình dịch, kiểm thử, …
Nếu có lỗi xảy ra ở bất cứ phần nào trong luồng, sửa lỗi là việc được ưu tiên hàng đầu.
Với mỗi công ty, tổ chức, pipeline tích hợp được xác định tùy thuộc vào công cụ
sử dụng, ngôn ngữ lập trình cũng như rất nhiều yếu tố khác, tuy nhiên luồng thực hiên
cơ bản bao gồm:
4



1.
2.
3.
4.
5.

Đẩy mã lên kho chứa mã chung
Phân tích tĩnh
Kiểm thử trước khi triển khai
Đóng gói và triển khai lên môi trường kiểm thử
Kiểm thử sau khi triển khai

1.2.2 Chuyển giao liên tục
Về cơ bản, một luồng chuyển giao liên tục bao gồm các bước giống như luồng
tích hợp liên tục đã liệt kê ở phần trước đó. Điểm khác biệt lớn nhất đó là: bước "5.
Kiểm thử sau khi triển khai" trong tích hợp liên tục thường đòi hỏi việc can thiệp bằng
tay. Các bản dịch không vượt qua được các bài kiểm tra ở bước này sẽ bị loại. Còn
trong Chuyển giao liên tục, sẽ có các cơ chế để tự động hóa bước này. Kết quả cuối
cùng là một bản dịch mà luôn sẵn sàng để triển khai trên môi trường sản phẩm thực tế.

1.2.3 Triển khai liên tục
Triển khai liên tục là mục tiêu cuối cùng trong chuỗi tự động hóa này. Bản dịch
cuối cùng sau bước chuyển giao liên tục là bản dịch luôn sẵn sàng để triển khai vào
môi trường thực tế, tuy nhiên việc triển khai hay không và chọn phiên bản nào để triển
khai lại phụ thuộc vào quyết định của đội vận hành. Trong triển khai liên tục, bất cứ
bản dịch nào vượt qua được tất cả các bài kiểm thử đều sẽ được triển khai tự động lên
môi trường sản phẩm thực tế. Không có sự can thiệp của con người trong qúa trình
này.


1.3 Kiến trúc hệ thống
Trong những năm gần đây, kiến trúc phần mềm web đã phát triển với một tốc độ
nhanh chóng. Với sự phổ biến của các dịch vụ Internet, chúng ta có thể thấy sự chuyển
dịch từ kiến trúc một khối theo tầng/lớp, sang kiến trúc hướng dịch vụ và như hiện nay
là kiến trúc vi dịch vụ. Việc lựa chọn một kiến trúc phần mềm cụ thể phụ thuộc hoàn
toàn vào những yêu cầu về: khả năng co dãn (scale), độ phức tạp, khả năng triển khai,
... Đối với ứng dụng web, khả năng scale là một trong những yếu tố quan trọng hàng
đầu.

1.3.1 Kiến trúc một khối
Kiến trúc một khối thường sẽ gộp những tính năng thiết yếu lại, từ đó phát triển
cũng như triển khai như một khối duy nhất. Trong kiến trúc một khối, cách thiết kế
phổ biến là phân chia các thành phần theo dạng module hoặc theo lớp. Ví dụ: phân
chia thành lớp giao diện người dùng, tới lớp logic nghiệp vụ, lớp truy cập cơ sở dữ liệu
và cuối cùng là lớp tích hợp để tích hợp với các dịch vụ ngoài.
Ưu điểm của kiến trúc này là:
• Đơn giản cho việc phát triển
• Đơn giản cho việc kiểm thử.
• Đơn giản cho việc triển khai
• Đơn giản trong việc scale theo chiều ngang
5


Kiến trúc này về cơ bản vẫn hoạt động tốt đối với những giai đoạn đầu của dự án.
Nhiều hệ thống phần mềm lớn hiện nay vẫn đang sử dụng kiến trúc này. Tuy nhiên,
kiến trúc một khối có nhiều điểm hạn chế như:
• Cách tiếp cận này có giới hạn về kích thước và độ phức tạp.
• Ứng dụng có thể trở nên quá lớn, phức tạp để có thể hiểu và tiếp tục phát triển.
• Kích cỡ của ứng dụng làm việc khởi động trở nên chậm hơn
• Mỗi khi có cập nhật gì thì đều cần triển khai lại toàn bộ ứng dụng

• Triển khai liên tục trở nên khó khăn
• Nếu việc phân chia dữ liệu khó khăn, ứng dụng khó có thể scale theo chiều
ngang như miêu tả ở trên.
• Ứng dụng một khối có nhiều rào cản trong việc áp dụng các công nghệ mới, bởi
việc thay đổi trong bộ khung hay ngôn ngữ lập trình có thể ảnh hưởng tới toàn
bộ ứng dụng.

1.3.2 Kiến trúc hướng dịch vụ (Service-Oriented Architecture)
Kiến trúc hướng dịch vụ nổi lên như một cách để giải quyết vấn đề các thành
phần phụ thuộc vào nhau quá nhiều trong kiến trúc một khối.
Kiến trúc hướng dịch vụ là mẫu kiến trúc phần mềm trong đó các thành phần ứng
dụng cung cấp dịch vụ cho các thành phần khác thông qua giao thức giao tiếp qua
mạng. Có hai vai trò chính trong kiến trúc hướng dịch vụ: vai trò thành phần cung cấp
dịch vụ và vai trò thành phần tiêu thụ dịch vụ. Một phần mềm có thể đóng cả hai trò,
tùy vào từng hoàn cảnh cụ thể. Lớp tiêu thụ là điểm mà những thành phần tiêu thụ (có
thể là người dùng, dịch vụ khác hoặc các bên thứ ba) tương tác với kiến trúc hướng
dịch vụ, và Lớp cung cấp là lớp chứa tất cả các dịch vụ được định nghĩa trong kiến
trúc hướng dịch vụ.
Enterprise Service Bus (ESB) là một kiểu kiến trúc tích hợp cho phép giao tiếp
được thực hiện thông qua một bus chứa nhiều kết nối điểm-tới-điểm (point-to-point)
giữa thành phần cung cấp và tiêu thụ.

1.3.3 Kiến trúc vi dịch vụ
Kiến trúc vi dịch vụ là mẫu kiến trúc phần mềm trong đó các ứng dụng phức tạp
được chia nhỏ ra thành các dịch vụ nhỏ hơn, độc lập và giao tiếp với nhau thông qua
những API độc lập với ngôn ngữ lập trình. Mỗi một vi dịch vụ là một ứng dụng nhỏ có
kiến trúc riêng, có thể chứa logic nghiệp vụ và nhiều loại thành phần khác. Một số vi
dịch vụ cung cấp API ở dạng Rest API, dạng message hoặc RPC và hầu hết các vi dịch
vụ sẽ dùng API được cung cấp bởi các vi dịch vụ khác [6].
Kiến trúc vi dịch vụ có ảnh hưởng lớn trong mối quan hệ giữa ứng dụng và cơ sở

dữ liệu. Thay vì dùng chung một bảng dữ liệu với các dịch vụ khác, mỗi vi dịch vụ sẽ
có bảng dữ liệu riêng. Điều quan trọng trong kiến trúc này đó là mỗi một vi dịch vụ
phải là một thành phần có thể chạy và triển khai độc lập được, việc tắt một vi dịch vụ
không thể gây ra hậu quả lớn tới toàn bộ vi dịch vụ khác.

6


Kiến trúc vi dịch vụ có nhiều ưu điểm, giúp xử lý các vấn đề mà kiến trúc một
khối gặp phải như:
• Giải quyết được vấn đề ứng dụng phức tạp bằng cách chia nhỏ thành các dịch
vụ nhỏ, dễ quản lý hơn (vi dịch vụ). Mỗi một vi dịch vụ sẽ được phát triển
nhanh hơn, dễ hiểu và bảo trì.
• Mỗi một vi dịch vụ có thể được phát triển độc lập bởi một đội, miễn là đảm bảo
API giao tiếp.
• Giúp cập nhật công nghệ dễ dàng do mỗi vi dịch vụ có thể sử dụng công nghệ
hoàn toàn riêng biệt.
• Mỗi một vi dịch vụ có thể được triển khai một cách độc lập, do vậy giúp cho
việc triển khai liên tục trở nên khả thi.
• Việc scale ứng dụng giờ được chuyển thành scale từng vi dịch vụ độc lập, giúp
tiết kiệm tài nguyên, scale nhanh hơn, có thể scale-out do mỗi vi dịch vụ có
bảng cơ sở dữ liệu riêng.
Tuy nhiên, kiến trúc vi dịch vụ có những hạn chế nhất định:
• Kiến trúc vi dịch vụ làm tăng sự phức tạp của dự án
• Việc áp dụng một vài thay đổi liên quan tới nhiều dịch vụ đòi hỏi việc triển
khai cẩn thận, có cơ chế cập nhật từ từ (roll-out) ở từng dịch vụ.
• Triển khai ứng dụng vi dịch vụ là rất phức tạp

1.4 Các mô hình triển khai
Có nhiều mô hình triển khai trong thực tế tùy thuộc vào kiến trúc phần mềm, cơ

sở hạ tầng có sẵn và chiến lược của từng công ty. Tuy nhiên, chương này sẽ chỉ liệt kê
3 mô hình triển khai cơ bản để phân biệt giữa ứng dụng một khối và vi dịch vụ, cách
đóng gói các bản build sử dụng để triển khai và phương án triển khai có tùy biến hay
không.

1.4.1 Mô hình triển khai tùy biến
Ngày nay, cách phổ biến nhất để build và triển khai ứng dụng vẫn là sử dụng mô
hình triển khai tùy biến. Chúng ta dựng lên một máy chủ web với ứng dụng cần chạy
và cập nhật ứng dụng mỗi khi có bản phát hành mới. Sự thay đổi có thể xảy ra trong
cấu hình, bản build (các tệp JAR, WAR, ...) hay cơ sở dữ liệu trên máy chủ đó, miễn là
ứng dụng hoạt động. Với mô hình triển khai tùy biến, ta không thể biết chính xác rằng
cấu hình môi trường phát triển, kiểm thử và môi trường sản phẩm (production) có
giống nhau hay không. Ngoài ra, khó có thể đảm bảo được tất cả các máy đều được
cập nhật đúng một phiên bản. Mô hình triển khai này dù ban đầu khá đơn giản nhưng
nó sẽ dần kết nối mọi thứ vào nhau, tăng độ phức tạp theo thời gian và tạo ra sự không
nhất quán. Khi có bản phát hành mới, việc cập nhật lại máy chủ có thể tạo ra thời gian
ngừng hoạt động. Với mô hình này thì triển khai liên tục hoàn toàn không khả thi.

7


1.4.2 Mô hình không tùy biến kết hợp với Reverse Proxy
Việc chuyển đổi mô hình triển khai sang mô hình không tùy biến sẽ giải quyết
được vấn đề khác nhau nêu trên. Khi triển khai, thay vì sử dụng các bản build đơn
thuần của ứng dụng, ta sử dụng các bản đóng gói máy ảo hoặc container chứa tất cả
môi trường cần thiết để ứng dụng hoạt động. Cách làm này đảm bảo được tính đồng
nhất giữa môi trường build, kiểm thử và môi trường sản phẩm.
Khi triển khai, để đảm bảo không có thời gian ngừng hoạt động, một reverse
proxy có thể được sử dụng: tất cả kết nối tới máy chủ chạy ứng dụng đều thông qua
reverse proxy; khi cần cập nhật ứng dụng, ta triển khai hoàn toàn bản phát hành mới

trên một máy chủ khác, đảm bảo ứng dụng chạy tốt trên đó và cấu hình reverse proxy
để trỏ vào máy chủ vừa triển khai.

1.4.3 Mô hình không tùy biến với ứng dụng vi dịch vụ
Trong mô hình triển khai không tùy biến, mỗi một bản đóng gói bao gồm toàn bộ
ứng dụng, khiến cho việc kiểm thử, triển khai mất nhiều thời gian. Điều này phần nào
hạn chế việc triển khai thường xuyên. Áp dụng mô hình không tùy biến với ứng dụng
sử dụng kiến trúc vi dịch vụ giúp đơn giản hóa được quá trình này: mỗi bản đóng gói
sẽ chỉ gồm một hoặc một vài dịch vụ, các bản đóng gói của các dịch vụ khác nhau có
thể được triển khai trên các máy khác nhau, giao tiếp giữa các dịch vụ được thực hiện
qua mạng.
Điểm khác biệt cơ bản giữa mô hình này với mô hình không tùy biến ở trên là:
do kích cỡ của các dịch vụ khá nhỏ, không phải lúc nào triển khai cũng cần thực hiện
trên một máy chủ mới. Dịch vụ mới hoàn toàn có thể được triển khai trên máy đang
chạy, cấu hình reverse proxy sẽ trỏ trực tiếp vào dịch vụ thay vì vào máy chủ.

1.5 Khái niệm Container
Container là phương pháp ảo hoá ở mức hệ điều hành, cho phép chạy một ứng
dụng cùng các thư viện nó phụ thuộc trong các tiến trình có tài nguyên được cách li.
Container giúp đóng gói mã, công cụ hệ thống, các cấu hình ... của ứng dụng vào trong
một khối duy nhất gọi là ảnh container. Công nghệ container và công nghệ máy ảo có
nhiều điểm tương đồng, ví dụ như về lợi ích liên quan tới phân phối và cách li tài
nguyên. Tuy nhiên, container là ảo hoá ở mức hệ điều hành còn máy ảo là công nghệ
ảo hoá phần cứng. Container có tính di động cao và sử dụng tài nguyên hiệu quả hơn.
Container là một lớp trừu tượng nằm ở tầng ứng dụng, đóng gói mã và các lệ
thuộc vào cùng nhau. Nhiều container có thể được chạy trên cùng một máy và chia sẻ
nhân hệ điều hành với các container khác. Mỗi container sẽ chạy như những tiến trình
được cách li trong user space (Trong Linux, để đảm bảo tính cách li, container thường
sử dụng namespace và control group). Các container có kích thước nhẹ hơn, chiếm ít
không gian bộ nhớ hơn các máy ảo, do vậy có khả năng khởi động gần như ngay tức

thời. Ngược lại, máy ảo là một lớp trừu tượng của phần cứng vật lý, có khả năng biến
một máy chủ thành nhiều máy chủ con. Tầng hypervisor cho phép nhiều máy ảo chạy
trong cùng một máy thật. Mỗi một máy ảo gồm đầy đủ hệ điều hành, các ứng dụng đi
8


kèm, các thư viện cơ bản, do vậy thường chiếm tới hàng chục GB bộ nhớ. Các máy ảo
thường mất nhiều thời gian để khởi động.

1.6 Tổng kết
Như vậy, qua chương này, luận văn đã giới thiệu hầu hết các công nghệ nổi bật
nhất hiện nay: từ sự phổ biến của tính toán đám mây, ý tưởng về một luồng tích hợp,
chuyển giao và triển khai liên tục, tới các mô hình triển khai không tùy biến, sự tiến
hoá về kiến trúc phần mềm hay cuối cùng là sự xuất hiện của công nghệ container.
Đây là những công nghệ làm tiền đề cho việc xây dựng một hệ thống triển khai liên
tục hoàn chỉnh. Tìm hiểu những công nghệ này sẽ giúp chúng ta hiểu rõ hơn về thiết
kế hệ thống triển khai được giới thiệu trong những chương tiếp theo.

9


CHƯƠNG 2: YÊU CẦU CỦA HỆ THỐNG TRIỂN KHAI LIÊN TỤC

2.1 Yêu cầu về điều phối container
2.1.1 Khám phá dịch vụ
Với cách thức xem các máy như nhau khi triển khai, yêu cầu đặt ra là làm cách
nào người vận hành biết được container nào được triển khai trên máy nào bởi bộ lập
lịch. Các công cụ khám phá dịch vụ được giới thiệu để giúp giải quyết vấn đề này.
Đối với các Docker container, vấn đề được thu gọn lại thành làm thế nào để có
thể ánh xạ giữa container đang chạy và vị trí của nó. Vị trí ở đây chính là địa chỉ IP và

cổng trên máy mà container được triển khai. Việc ánh xạ này cần được thực hiện một
cách nhanh chóng và chính xác, bởi các container có thể được tắt hay bật lại trên các
máy khác nhau trong cluster tại bất cứ thời điểm nào tùy thuộc vào môi trường.
Một giải pháp khám phá dịch vụ cần cung cấp được hai chức năng chính:
• Đăng kí: thiết lập ánh xạ giữa container và vị trí chạy khi container được triển
khai. Bởi chỉ có bộ lập lịch biết chính xác vị trí mà container chạy, bộ lập lịch
được xem như là nguồn đáng tin cậy duy nhất về vị trí container.
• Tìm kiếm: Cho phép các container hay các dịch vụ ngoài tìm kiếm các ánh xạ
của các container khác để có thể giao tiếp với chúng. Yêu cầu đối với việc tìm
kiếm là thông tin cần chính xác và thời gian phản hồi nhanh.

2.1.2 Bộ lập lịch
Một bộ lập lịch cho hệ thống phân tán sẽ dựa vào yêu cầu của người dùng để xác
định ứng dụng và triển khai nó trên một hoặc nhiều máy đang khả dụng.
Ví dụ: người dùng có thể gửi yêu cầu chạy nhiều bản sao của một ứng dụng nhất
định, bộ lập lịch sẽ dựa vào thông tin về tài nguyên của các máy để phân bố triển khai
ứng dụng một cách hợp lí. Đối với Docker, việc này đòi hỏi ảnh của ứng dụng phải có
sẵn trên máy chuẩn bị triển khai và Docker phải được cài trên các máy đó.
Một ví dụ của bộ lập lịch: người dùng yêu cầu triển khai 3 bản sao của ứng dụng,
bộ lập lịch sẽ quyết định máy cụ thể để triển khai dựa trên thông tin về trạng thái
cluster, cách tận dụng tài nguyên các máy để đảm bảo bật đủ số lượng bản sao yêu cầu
và làm thế nào để thỏa mãn các ràng buộc khác từ người dùng (Ví dụ: chỉ triển khai
ứng dụng trên máy có sử dụng ổ chứa SSD). Ngoài ra, chất lượng dịch vụ cũng là một
yếu tố để đưa ra quyết định lựa chọn máy triển khai.

2.1.3 Điều phối container
Bộ lập lịch và chức năng khám phá dịch vụ đóng vai trò quan trọng trong việc
quản lý container. Trên thực tế, hai thành phần này gần như luôn được sử dụng cùng
nhau và nằm trong một khái niệm rộng hơn gọi là điều phối container.
Các thành phần cơ bản của một nền tảng điều phối container gồm: thành phần

kiểm tra sức khỏe, tự động scale, thành phần khám phá dịch vụ, bộ lập lịch hay thành
10


phần nâng cấp hệ thống. Nền tảng điều phối container sẽ thực hiện qúa trình khởi tạo,
lập lịch, lựa chọn máy cho tới giám sát, cập nhật và đảm bảo giao tiếp của các
container trong cluster.

2.2 Yêu cầu cho các nhà phát triển
2.2.1 Khả năng tự triển khai
Ngoài việc tự động triển khai khi có sự thay đổi mã nguồn trên kho mã chung, hệ
thống triển khai liên tục cũng cần có cơ chế để cho phép các đội phát triển, kiểm thử tự
triển khai ứng dụng lên các môi trường, phục vụ mục đích kiểm tra tính năng hoặc
kiểm thử, tái hiện lỗi. Hệ thống phân quyền cần được cấu hình để đảm bảo quyền hạn
khác nhau tùy theo vai trò của các đội.

2.2.2 Nhận được các thông tin về kết quả build
Quá trình triển khai liên tục thường gồm 2 giai đoạn: build và triển khai. Quá
trình build thường mất thời gian và các lập trình viên thường bận rộn với việc phát
triển tính năng mới cho sản phẩm, do vậy cần có cơ chế để thông báo trạng thái của
các tác vụ build cho các đội thông qua các kênh giao tiếp của công ty như email, phần
mềm chat, ...

2.3 Yêu cầu về vận hành
2.3.1 Quản lý cấu hình
Cơ sở hạ tầng được quản lý như mã (Infrastructure as code - IAC) là cách tiếp
cận việc tự động hóa quản lý cơ sở hạ tầng dựa trên những kinh nghiệm từ quá trình
phát triển phần mềm. IAC nhấn mạnh vào tính nhất quán, lặp đi lặp lại đối với việc
cung ứng và thay đổi các hệ thống cũng như cấu hình của chúng. Ý tưởng của IAC là
người dùng sẽ viết và thực thi mã để định nghĩa, triển khai và cập nhật cơ sở hạ tầng.

IAC thay đổi nhận thức của mọi người trong việc đối xử các hoạt động vận hành như
là phần mềm, dù một số hoạt động thực tế liên quan tới phần cứng (ví dụ: cài đặt máy
chủ triển khai).
Bằng việc ứng dụng IAC, không chỉ người quản trị, bất cứ ai cũng có thể đọc và
hiểu về toàn bộ cấu hình hệ thống, có khả năng đảo ngược cấu hình về phiên bản chạy
tốt bằng việc sử dụng hệ thống quản lí phiên bản. IAC cũng cho phép đánh giá, kiểm
thử trước khi thực sự triển khai sự thay đổi về cơ sở hạ tầng.

2.3.2 Thu thập và lưu trữ log
Thu thập và lưu trữ log là yêu cầu cơ bản đối với bất cứ hệ thống phần mềm thực
tế nào. Trong một hệ thống vi dịch vụ dựa vào container, các container sẽ được triển
khai trong một cluster gồm nhiều máy. Với mỗi máy, ngoài các container còn có
những dịch vụ khác đang chạy. Các ứng dụng sẽ sản sinh ra các tệp log với định dạng,
vị trí khác nhau, khiến cho việc quản lý và sử dụng log trở nên khó khăn. Cần có cơ
chế để thu thập toàn bộ log cho tất cả các ứng dụng hay container này, đồng thời cho
phép người dùng sử dụng chúng một cách hiệu quả.
11


2.3.3 Giám sát và thông báo lỗi
Các nền tảng điều phối container có cơ chế giám sát để đảm bảo các dịch vụ hoạt
động bình thường và có phương pháp sửa lỗi nếu phát hiện vấn đề. Tuy nhiên, cơ chế
cung cấp bởi nền tảng điều phối container thường chỉ ở mức cơ bản. Trên thực tế, các
dịch vụ thường chứa logic nghiệp vụ phức tạp, mỗi một dịch vụ lại có yêu cầu và các
tham số giám sát riêng. Cần có cơ chế để các nhà phát triển có thể tùy biến các tham
số cần giám sát, có công cụ trực quan để đội vận hành theo dõi và các phương thức tự
động thông báo nếu có lỗi xảy ra.

2.3.4 Có khả năng đảo ngược phiên bản triển khai (rollback)
Hệ thống triển khai liên tục cần có khả năng đảo ngược phiên bản triển khai một

cách dễ dàng. Điều này giúp giảm áp lực trong việc triển khai và khuyến khich mọi
người triển khai thường xuyên hơn để đẩy tính năng mới người dùng một cách nhanh
chóng.

2.3.5 Triển khai không có thời gian ngừng hoạt động (no-downtime deployment)
Hệ thống phải cho phép cập nhật phiên bản mới của ứng dụng mà không có thời
gian ngừng hoạt động để đảm bảo tính liên tục. Một kỹ thuật thường được sử dụng là
triển khai dạng blue-green: chạy hai bản sao giống hệt nhau của ứng dụng. Tại thời
điểm ban đầu, cả hai bản sao được gọi là blue. Khi cần cập nhật, một trong hai bản sao
sẽ được cập nhật trước và được gọi là green. Sau khi đảm bảo phiên bản green hoạt
động tốt, phiên bản blue còn lại sẽ được thay thế bằng phiên bản green. Ngược lại, nếu
có vấn đề xảy ra, phiên bản green sẽ bị loại và phiên bản blue sẽ được triển khai lại. Ở
lần cập nhật tiếp theo, một trong hai phiên bản green sẽ được thay bằng phiên bản mới
được gọi là blue. Phương pháp này là một biến thể của mô hình triển khai không tùy
biến có sử dụng reverse proxy đã giới thiệu trong chương 01.

2.3.6 Thiết kế mạng triển khai
Trong thực tế, các máy triển khai trong đám mây của một công ty/tổ chức thường
nằm trong một VPC (Virtual Private Cloud) với dải mạng ảo riêng. Các máy trong
VPC có thể kết nối ra ngoài Internet thông qua một thiết bị NAT hoặc Internet
Gateway được cung cấp bởi nhà cung cấp dịch vụ đám mây. Tuy nhiên, chúng không
thể được truy cập trực tiếp từ bên ngoài nếu không gắn các địa chỉ Public IP vào
những máy này với những cấu hình định tuyến thích hợp, tạo liên kết VPN hay sử
dụng các dạng cấu hình port-forwarding tại các router.
Máy chủ triển khai thường được cài đặt tại ngay trong mạng nội bộ của công ty,
do vậy ở điều kiện bình thường sẽ không thể kết nối tới tất cả các máy trong VPC, gây
khó khăn cho việc triển khai tự động. Những phương pháp nêu trên đều có thể được áp
dụng để giải quyết vấn đề này. Tuy nhiên, khi số lượng các máy trong VPC tăng lên,
hoặc khi công ty muốn tạo ra nhiều VPC (chẳng hạn tạo ra nhiều môi trường để kiểm
thử, đánh giá, mỗi môi trường được triển khai trên một VPC) thì những cách kết nối

này đều có các hạn chế nhất định. Do vậy, thiết kế hệ thống triển khai liên tục cần tìm
ra giải pháp đề giải quyết được yêu cầu này.
12


CHƯƠNG 3: THIẾT KẾ
3.1 Luồng triển khai cơ bản

Hình 3.1: Luồng triển khai liên tục cơ bản

Hình 3.1 thể hiện thiết kế luồng triển khai liên tục cơ bản cho một hệ thống vi
dịch vụ sử dụng container, trong đó hoạt động của các thành phần cụ thể như sau:
1. Lập trình viên đẩy mã lên kho chứa mã chung (Repository).
2. Kho chứa mã nhận biết có sự thay đổi về mã, kích hoạt máy chủ CI để build mã
vừa được cập nhật. Khi mã được build xong, máy chủ sẽ đóng gói bản build
theo dạng ảnh container. Các cơ chế kiểm thử tự động cũng được dùng trong
quá trình này để đảm bảo mã không có lỗi.
13


3. Nếu build thất bại, toàn bộ quá trình sẽ bị dừng lại. Ngược lại, máy chủ sẽ đẩy
bản build mới lên một kho chứa chung dùng để triển khai. Kết quả build thành
công hay thất bại đều có thông báo gửi tới nhà phát triển thông qua các kênh
giao tiếp chung của công ty.
4. Sau khi bản build được đẩy lên kho chứa, máy chủ CI sẽ kích hoạt máy chủ
triển khai để triển khai bản build lên môi trường sản phẩm. Quá trình triển khai
cần đảm bảo không có thời gian ngừng hoạt động và trong suốt với người sử
dụng.
5. Việc lựa chọn máy để triển khai và giao tiếp giữa các container trong các máy
sẽ được một nền tảng điều phối container quản lý.

6. Các bản build được triển khai trên môi trường sản phẩm sẽ được hệ thống giám
sát kiểm tra thường xuyên. Nếu có vấn đề xảy ra, hệ thống giám sát sẽ gửi
thông báo tới thành phần cảnh báo. Thành phần cảnh báo có trách nhiệm gửi
thông báo lỗi tới nhà phát triển thông qua các kênh giao tiếp.
7. Hệ thống quản lý log sẽ liên tục thu thập log từ các dịch vụ được triển khai, lưu
trữ log vào một hệ thống lưu trữ và cho phép người dùng truy cập, tìm kiếm
thông tin log khi có lỗi xảy ra thông qua giao diện người dùng.

3.2 Phân tích và lựa chọn công nghệ
3.2.1 Hệ thống quản lý mã nguồn
Luận văn sẽ sử dụng Git làm hệ thống quản lý mã nguồn do đặc điểm phân tán,
khả năng hỗ trợ rẽ nhánh, làm việc nhóm hiệu quả cùng sự phổ biến của nó.
Mô hình rẽ nhánh được sử dụng như sau: nhánh develop là nhánh chính được sử
dụng cho việc phát hành. Các nhà phát triển sẽ tạo nhánh mới cho các tính năng mới,
cho việc sửa lỗi hay các bản vá. Sau khi đảm bảo mã hoạt động tốt ở nhánh mới, nhà
phát triển sẽ gửi một yêu cầu hợp nhất mã (merge request) vào nhánh develop. Ngay
khi yêu cầu được tạo ra, Git sẽ sử dụng kỹ thuật Web Hook để kích hoạt quá trình
build với mã mới (pre-merge build). Kết quả quá trình build và yêu cầu sẽ được gửi
cho người quản lý kho mã nguồn. Người quản lý kho mã sẽ đánh giá yêu cầu và chấp
nhận hợp nhất. Ngay sau khi người quản lý chấp nhận, một quá trình build khác sẽ
được kích hoạt (post-merge build). Quá trình này sẽ đóng gói bản build mới vào ảnh
container theo công nghệ container được chọn, thực hiện kiểm thử với ảnh đóng gói và
đẩy ảnh lên kho lưu trữ container nếu không có lỗi xảy ra.

3.2.2 Máy chủ CI và máy chủ triển khai
Máy chủ CI được sử dụng trong bài là Jenkins, một phần mềm tự động hóa, mã
nguồn mở và viết bằng Java. Jenkins hỗ trợ hầu hết các phần mềm quản lý mã nguồn
hiện nay như Git, Subversion, Mercurial ... và hỗ trợ các mã lệnh của nhiều loại ngôn
ngữ script như Bash, Python, Groovy, Gradle ...


14


3.2.3 Công nghệ container
Mã nguồn sau khi được hợp nhất và build hậu hợp nhất sẽ được đóng gói thành
ảnh container và lưu trong kho ảnh. Luận văn sẽ sử dụng công nghệ container phổ biến
nhất hiện nay là Docker.
Docker sử dụng Dockerfile để miêu tả các lớp ảnh, các câu lệnh cần thực hiện
cho việc build ảnh container. Nhằm đảm bảo tính nhất quán trong việc tích hợp build
ảnh container với mã nguồn, Dockerfile sẽ được lưu trữ và được quản lý trong cùng
thư mục trên kho lưu trữ chung như mã nguồn.

3.2.4 Nền tảng điều phối container
Hiện nay, cùng với sự phổ biến của công nghệ container, số lượng các nền tảng
điều phối container ngày càng tăng lên. Trong đó, các nền tảng phổ biến nhất là
Amazon ECS, Azure Container Service, Goole Container Engine, Diego của Cloud
Foundry, Kubernetes, Docker Swarm, Mesosphere Marathon. Tuy nhiên, hầu hết các
nền tảng điều phối đều là bản thương mại được cung cấp bởi các công ty điện toán
đám mây, ngoại trừ Docker swarm, Mesosphere Marathon và Kubernetes. Luận văn
do vậy chỉ tập trung hướng tới phân tích ba nền tảng này.
Ba nền tảng điều phối đều cung cấp các chức năng cơ bản nhất để quản lý
container, tuy nhiên độ phức tạp trong sử dụng, số lượng máy có thể quản lý và khả
năng tùy biến của các nền tảng hoàn toàn khác nhau:
Nền tảng

< 10 máy
++

10 - 100
máy

+

100 – 1000
máy
?

> 1000
máy
?

Khả năng
tùy biến
+

Tính dễ sử
dụng
+++

Docker
swarm
Mesphere
Marathon
Kubernetes

+

++

++


++

++

+

++
++
+
?
++
++
Bảng 3-1: So sánh Docker Swarm, Mesosphere Marathon và Kubernetes

Từ bảng so sánh trên, luận văn đề xuất sử dụng Kubernetes như nền tảng điều
phối container chính cho hệ thống triển khai liên tục.

3.2.5 Kênh thông báo
Kênh thông báo giúp tất cả các bên liên quan luôn cập nhật được trạng thái của
một luồng triển khai liên tục và thường được tích hợp vào chính kênh giao tiếp hiện tại
của công ty như email, các phần mềm chat. Việc lựa chọn kênh thông báo nào tùy
thuộc đặc điểm và nhu cầu của từng công ty. Trong luận văn, phần mềm Slack được
xem như kênh giao tiếp chính và sử dụng cho mục đích thông báo này.

3.2.6 Hệ thống thu thập và quản lý log
Cách thức thu thập log phổ biến hiện nay cho các cluster là sử dụng các log
collector trên các máy triển khai nhằm thực hiện nhiệm vụ phân giải nội dung log
thành các trường thông tin và gửi về hệ thống lưu trữ. Để cung cấp khả năng tìm kiếm
nhanh cho người sử dụng, log thường được lưu và đánh chỉ số trong elasticsearch.
15



Kibana, một công cụ khám phá trực quan và hỗ trợ phân tích dữ liệu thời gian thực
cho elasticsearch, sẽ được sử dụng như giao diện tìm kiếm log cho người dùng.
Như vậy, các giải pháp quản lý log khác biệt cơ bản nhất ở thành phần log
collector. Trong thế giới mã nguồn mở, hai log collector phổ biến nhất là Logstash và
Fluentd, đại diện cho 2 stack công nghệ ELK và EFK.
Điểm khác biệt cơ bản của Logstash và Fluentd được thể hiện trong bảng sau:
Log collector
Log stash
Fluentd

Phương pháp định
tuyến log
Dựa vào các lệnh theo
thuật toán
Dựa vào các thẻ

Tầng giao vận

Hiệu năng

Cần triển khai với
Redis để đảm bảo độ
tin cậy
Độ tin cậy được tích
hợp sẵn nhưng khó cấu
hình

Sử dụng nhiều bộ nhớ

hơn. Sử dụng Elastic
Beats cho các lá.
Sử dụng ít bộ nhớ hơn.
Dùng Fluent Bit và
Fluentd Forwarder cho
các lá.

Bảng 3-2: So sánh Logstash và Fluentd

Với hệ thống vi dịch vụ sử dụng container, số lượng container là rất lớn với khối
lượng và chủng loại log khổng lồ. Do vậy sử dụng Fluentd sẽ giúp người vận hành cấu
hình được cách thức định tuyến log linh hoạt, đảm bảo độ tin cậy mà không cần cài đặt
thêm các dịch vụ ngoài và tối ưu hóa tài nguyên cũng như hiệu năng sử dụng.

3.2.7 Công nghệ giám sát hệ thống và cảnh báo
Việc hiểu một ứng dụng hoạt động ra sao khi được triển khai là yếu tố quan trọng
để scale ứng dụng và cung cấp các dịch vụ đáng tin cậy. Prometheus là công cụ giám
sát hệ thống được phát triển bởi SoundCloud và đã được xuất bản như phần mềm mã
nguồn mở. Prometheus hoạt động bằng cách thu thập (Prometheus sử dụng thuật ngữ
scrape) các mục tiêu, là các endpoints ở dạng key-value. Prometheus sau đó sẽ lưu trữ
thông tin thu thập được như một khung trong cơ sở dữ liệu theo thời gian (time series
database), từ đó cho phép người dùng thực hiện truy vấn đồ thị và các chức năng khác
như cảnh báo.
Prometheus cho phép nhà phát triển thu thập thông tin của từng thư viện, từng
dịch vụ bằng cách tích hợp thư viện Prometheus vào trong mã nguồn để xây dựng các
thông tin (metrics) tùy biến. Đối với các thư viện với cách thức cung cấp thông tin
khác, Prometheus có các exporter để chuyển đổi các thông tin đấy sang dạng thông tin
mà Prometheus có thể sử dụng.
Ngoài ra, bản thân mã nguồn Prometheus cũng cung cấp thành phần cảnh báo lỗi
là AlertManager, giúp gửi thông tin tới các bên liên quan qua nhiều kênh giao tiếp

khác nhau. Thông tin mà Prometheus cung cấp có thể được thể hiện trực quan qua các
phần mềm bên thứ ba như Grafana.
Prometheus giúp đảm bảo mục đích đa dạng trong việc giám sát một hệ thống vi
dịch vụ gồm nhiều container.

3.2.8 Giải pháp quản lý cấu hình
Có nhiều giải pháp giúp quản lý cấu hình như Chef, Puppet hay Ansible. Các giải
pháp này đều hỗ trợ việc tự động hóa quá trình cài đặt, cấu hình cho máy triển khai và
16


phù hợp để tích hợp vào luồng triển khai liên tục. Luận văn sẽ sử dụng Ansible do sự
đơn giản trong việc cài đặt và cú pháp dễ hiểu, dễ viết của nó.

3.3 Luồng triển khai liên tục hoàn thiện
Với các công nghệ đã chọn được nêu trong phần trước, luận văn đề xuất xây
dựng hệ thống triển khai liên tục hoàn thiện như sau (Hình 3.2):
Như vậy, luồng triển khai liên tục hoàn thiện hoạt động chi tiết như sau:
1. Lập trình viên gửi yêu cầu hợp nhất từ nhánh phụ sang nhánh develop trên
Gitlab
2. Dựa vào WebHooks, Gitlab kích hoạt quá trình build tiền hợp nhất trong
Jenkins. Kết quả build được gửi qua Slack cho tất cả các bên liên quan.
3. Người quản lý mã nguồn lựa chọn chấp nhận hợp nhất mã. WebHooks trên
Gitlab kích hoạt quá trình build hậu hợp nhất trên Jenkins. Mã nguồn sau khi
build sẽ được đóng gói vào ảnh Docker và lưu trên máy Jenkins.
4. Người quản lý mã nguồn lựa chọn chấp nhận hợp nhất mã. WebHooks trên
Gitlab kích hoạt quá trình build hậu hợp nhất trên Jenkins. Mã nguồn sau khi
build sẽ được đóng gói vào ảnh Docker và lưu trên máy Jenkins.
5. Các bài kiểm thử cho ảnh Docker sẽ được thực hiện trên máy Jenkins. Nếu tất
cả các bài kiểm thử thành công, ảnh Docker sẽ được đẩy lên Docker Registry

riêng của công ty. Đồng thời, tác vụ triển khai trong Jenkins cũng được kích
hoạt.
6. Jenkins sẽ dựa vào Ansible để triển khai ảnh Docker vào Kubernetes cluster.
Việc lựa chọn máy triển khai phụ thuộc vào bộ lập lịch trong Kubernetes
Master.
7. Mã nguồn được tích hợp với thư viện prometheus để tùy biến các metrics cần
thiết cho việc giám sát. Ngoài ra, các exporter trong prometheus cũng được sử
dụng để chuyển đổi thông tin metrics được cung cấp từ các thư viện khác hoặc
của môi trường.
8. Prometheus Alert Manager được sử dụng làm máy chủ để gửi cảnh báo dựa vào
thông tin nhận được từ máy chủ promethus và các nguyên tắc cảnh báo viết bởi
người vận hành. Tất cả cảnh báo sẽ được gửi tới Slack như các thông báo lỗi
khác.
9. Grafana được sử dụng để cung cấp giao diện trực quan cho người dùng.
10. Fluentd được lựa chọn để làm log collector cho các ảnh container. Cách thức
thu thập log như sau: các tệp log trong container sẽ được ánh xạ ra máy triển
khai. Fluentd sẽ dựa vào plugin tail để đọc nội dung log, phân giải các trường
thông tin, xây dựng luồng sự kiện và gửi tới một máy đóng vai trò Log
Aggregator. Từ máy log aggregator, log sẽ được chuyển vào lưu trữ trong
Elasticsearch. Tại đây, log được lưu trữ và đánh chỉ số để phục vụ cho việc tìm
kiếm nhanh.
11. Một số plugin sẽ được sử dụng để cung cấp giao diện và khả năng truy vấn đối
với dữ liệu trong Elasticsearch. Ngoài ra, Kibana sẽ được cài đặt và liên kết với
Elasticsearch để cung cấp giao diện linh hoạt cho người sử dụng.
17


Hình 3.2: Hệ thống triển khai liên tục hoàn thiện

Đối với phần kết nối mạng để triển khai (trong trường hợp các máy triển khai

được cài đặt trong một VPC riêng trên nền tảng đám mây), luận văn đề xuất phương
án kết nối như sau:

18


Hình 3.3: Kết nối mạng triển khai

Như vậy, một máy trong VPC sẽ đóng vai trò là work-terminal, chứa các kịch
bản triển khai Ansible. Máy work-terminal có thể truy cập tới tất cả các máy khác
trong VPC thông qua kết nối SSH. Máy work-terminal sẽ được gắn với một địa chỉ
Public IP và được định tuyến để có thể truy cập được từ các máy ngoài mạng.

19


CHƯƠNG 4: CÀI ĐẶT VÀ ĐÁNH GIÁ
4.1 Cài đặt
4.1.1 Hệ thống vi dịch vụ mẫu
Để kiểm tra tính hiệu quả của thiết kế luồng triển khai liên tục được đề xuất trong
chương trước, luận văn sẽ xây dựng một ứng dụng gồm hai vi dịch vụ cơ bản như sau:
• Forex-service: vi dịch vụ cung cấp chức năng tra cứu tỷ giá tiền tệ.
• Ccs-service: vi dịch vụ cung cấp chức năng chuyển đổi một lượng tiền nhất
định từ một đơn vị tiền tệ sang đơn vị tiền tệ khác. Vi dịch vụ ccs-service sử
dụng forex-service để lấy thông tin về tỷ giá.
Khi triển khai, mỗi vi dịch vụ nêu trên có thể có nhiều bản sao cùng chạy. Luận
văn sử dụng cơ chế load-blance cho các bản sao này bằng Service, được cung cấp bởi
Kubernetes, để đảm bảo định tuyến các yêu cầu tới đúng các bản sao một cách lần lượt
(round-robin).
Hai vi dịch vụ được lập trình bằng ngôn ngữ Java, sử dụng bộ khung Spring

Boot.

4.1.2 Cách thức tổ chức mã nguồn và quá trình build ảnh docker
• Sử dụng cách thức tổ chức thư mục như quy ước của các dựa án Maven
• Tạo thư mục docker-build ở cùng mức với thư mục mã nguồn src, chứa
Dockerfile dùng để build ảnh docker, các tệp script để sử dụng như tài nguyên
cho ảnh. Ngoài ra, các script để tự động hóa quá trình build và đẩy ảnh lên kho
chứa ảnh cũng được đặt trong thư mục này.
Để có toàn quyền cấu hình, luận văn cài đặt hệ thống quản lý mã Gitlab và máy
chủ CI Jenkins như các ứng dụng nội bộ. Jenkins cần sử dụng các plugin để cung cấp
các chức năng tự động hóa cụ thể. Trong bài, scm-plugin được sử dụng để tải mã từ
gitlab về máy chủ Jenkins và gitlab-plugin sẽ được sử dụng để giúp Jenkins nhận được
thông báo về các sự kiện thay đổi mã trên Gitlab, từ đó kích hoạt quá trình build và
cập nhật kết qủa lên chính commit đã kích hoạt đó.
Có hai quá trình build được sử dụng trong luồng triển khai được cài đặt: build
tiền hợp nhất và build hậu hợp nhất.
Slack được sử dụng trong luận văn như kênh giao tiếp chính. Slack-plugin được
cài đặt trong Jenkins để gửi thông báo tới Slack dựa vào trạng thái của quá trình build.
Tất cả trạng thái của các quá trình build này sẽ được cập nhật trực tiếp lên Gitlab, cung
cấp thông tin trực quan cho nhà phát triển.
Sau khi build tiền hợp nhất thực hiện thành công, luận văn sử dụng API được
cung cấp bởi Jenkins để kích hoạt tiếp quá trình triển khai, từ đó cập nhật ứng dụng
vào môi trường sản phẩm (production environment).
Hầu hết các tác vụ trong Jenkins đều được cấu hình thông qua mã Groovy DSL
một cách tự động. Điều này giúp cho việc cấu hình trở nên đơn giản, nhanh chóng và
quan trọng nhất là có thể kiểm thử được từ trước khi thực sự chạy.
20



×