Tải bản đầy đủ (.pdf) (83 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 (12 KB, 83 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ố: 8480103.01

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

NGƯỜI HƯỚNG DẪN KHOA HỌC: TS. VÕ ĐÌNH HIẾU

HÀ NỘI - 2018



LỜI CAM ĐOAN

Tôi xin cam đoan rằng luận văn thạc sĩ Kĩ thuật phần mềm "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ụ" là sản phẩm nghiên cứu khoa
học của riêng cá nhân tôi, dưới sự giúp đỡ rất lớn của Giảng viên hướng dẫn là TS. Võ
Đình Hiếu, không sao chép lại của người khác. Những điều được trình bày trong toàn
bộ nội dung của luận văn này hoặc là của chính cá nhân tôi, hoặc là được tổng hợp từ
nhiều nguồn tài liệu. Tất cả các tài liệu tham khảo đều có nguồn gốc rõ ràng và được
trích dẫn hợp pháp.
Tôi xin hoàn toàn chịu trách nhiệm và chịu mọi hình thức kỷ luật theo quy định
cho lời cam đoan của mình.

Hà nội, tháng 11 năm 2018
Học viên

Nguyễn Trọng Hiếu


LỜI CẢM ƠN

Trước tiên, tôi xin bày tỏ lòng biết ơn chân thành và sâu sắc đến thầy giáo, TS.
Võ Đình Hiếu - người đã dành nhiều tâm huyết, tận tình chỉ bảo và giúp đỡ tôi trong
suốt quá trình bắt đầu thực hiện đề tài cho đến khi hoàn thành.
Tôi xin gửi lời cảm ơn chân thành tới các thầy cô giáo khoa Công nghệ thông tin,
trường Đại học Công nghệ, Đại học Quốc gia Hà nội - nơi tôi đã theo học trong thời
gian qua. Các thầy cô đã cung cấp cho tôi những kiến thức quý báu, tạo điều kiện tốt
nhất cho tôi trong suốt quá trình học tập và nghiên cứu tại trường.
Cuối cùng, tôi xin chân thành cảm ơn những người thân trong gia đình, bạn bè
cùng khóa, đồng nghiệp trong cơ quan đã giúp đỡ và tạo điều kiện cho tôi trong quá
trình học tập và nghiên cứu thực hiện luận văn này.

Tuy rằng tôi đã cố gắng hết sức trong quá trình làm luận văn nhưng không thể
tránh khỏi thiếu sót, tôi rất mong nhận được những góp ý của thầy cô và các bạn.

Hà nội, tháng 11 năm 2018
Học viên

Nguyễn Trọng Hiếu


MỤC LỤC

LỜI CAM ĐOAN -------------------------------------------------------------------------------1
LỜI CẢM ƠN ------------------------------------------------------------------------------------1
BẢNG CÁC KÝ HIỆU VÀ CHỮ VIẾT TẮT ---------------------------------------------1
DANH MỤC HÌNH VẼ ------------------------------------------------------------------------1
DANH MỤC BẢNG BIỂU --------------------------------------------------------------------1
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 ---------------------------------5
1.2 Tích hợp, chuyển giao và triển khai liên tục -------------------------------------6
1.2.1 Tích hợp liên tục ------------------------------------------------------------------6
1.2.2 Chuyển giao liên tục -------------------------------------------------------------7
1.2.3 Triển khai liên tục ----------------------------------------------------------------8
1.3 Kiến trúc hệ thống ---------------------------------------------------------------------8
1.3.1 Kiến trúc một khối----------------------------------------------------------------9
1.3.2 Kiến trúc hướng dịch vụ ------------------------------------------------------- 10
1.3.3 Kiến trúc vi dịch vụ ------------------------------------------------------------ 11
1.4 Các mô hình triển khai ------------------------------------------------------------- 13

1.4.1 Mô hình triển khai tùy biến --------------------------------------------------- 13
1.4.2 Mô hình không tùy biến kết hợp với Reverse Proxy----------------------- 14
1.4.3 Mô hình không tùy biến với ứng dụng vi dịch vụ -------------------------- 15
1.5 Khái niệm Container ---------------------------------------------------------------- 17
CHƯƠNG 2: YÊU CẦU CỦA HỆ THỐNG TRIỂN KHAI LIÊN TỤC ----------- 21
2.1 Yêu cầu về điều phối container --------------------------------------------------- 21
2.1.1 Khám phá dịch vụ -------------------------------------------------------------- 21
2.1.2 Bộ lập lịch ----------------------------------------------------------------------- 22
2.1.3 Điều phối container ------------------------------------------------------------ 23
2.2 Yêu cầu cho các nhà phát triển --------------------------------------------------- 24
2.2.1 Khả năng tự triển khai --------------------------------------------------------- 24
2.2.2 Nhận được các thông tin về kết quả build ----------------------------------- 25
2.3 Yêu cầu về vận hành ---------------------------------------------------------------- 25
2.3.1 Quản lý cấu hình ---------------------------------------------------------------- 25
2.3.2 Thu thập và lưu trữ log -------------------------------------------------------- 25
2.3.3 Giám sát và thông báo lỗi ----------------------------------------------------- 26
2.3.4 Triển khai không bị gián đoạn và có khả năng đảo ngược phiên bản --- 27
2.3.5 Thiết kế mạng triển khai ------------------------------------------------------- 27
CHƯƠNG 3: THIẾT KẾ HỆ THỐNG TRIỂN KHAI LIÊN TỤC ----------------- 28
3.1 Luồng triển khai cơ bản ------------------------------------------------------------ 28
3.2 Phân tích và lựa chọn công nghệ ------------------------------------------------- 29


3.2.1 Hệ thống quản lý mã nguồn --------------------------------------------------- 29
3.2.2 Máy chủ CI và máy chủ triển khai ------------------------------------------- 30
3.2.3 Công nghệ container ----------------------------------------------------------- 32
3.2.4 Nền tảng điều phối container ------------------------------------------------- 32
3.2.5 Kubernetes ----------------------------------------------------------------------- 36
3.2.6 Kênh thông báo ----------------------------------------------------------------- 38
3.2.7 Hệ thống thu thập và quản lý log --------------------------------------------- 39

3.2.8 Công nghệ giám sát hệ thống và cảnh báo ---------------------------------- 41
3.2.9 Giải pháp quản lý cấu hình ---------------------------------------------------- 43
3.3 Luồng triển khai liên tục hoàn thiện--------------------------------------------- 43
CHƯƠNG 4: CÀI ĐẶT VÀ ĐÁNH GIÁ ------------------------------------------------- 47
4.1 Cài đặt --------------------------------------------------------------------------------- 47
4.1.1 Hệ thống vi dịch vụ mẫu------------------------------------------------------- 47
4.1.2 Cách thức tổ chức mã nguồn và quá trình build ảnh docker -------------- 48
4.1.3 Cài đặt môi trường sản phẩm ------------------------------------------------- 53
4.2 Đánh giá kết quả --------------------------------------------------------------------- 58
KẾT LUẬN ------------------------------------------------------------------------------------- 61
TÀI LIỆU THAM KHẢO ------------------------------------------------------------------- 63
PHỤ LỤC --------------------------------------------------------------------------------------- 65


BẢNG CÁC KÝ HIỆU VÀ CHỮ VIẾT TẮT

STT

Thuật ngữ viết tắt

Thuật ngữ đầy đủ

1

EFK

Elasticsearch, Fluentd, Kibana

2


VPC

Virtual Private Cloud

3

VM

Virtual Machine

4

CI

Continuous Integration

5

IaaS

Infrastructure as a Service

6

PaaS

Platform as a Service

7


SaaS

Software as a Service

8

IAC

Infrastructure as code

9

DNS

Domain Name System

10

SOA

Service Oriented Architecture

11

Container

Công nghệ container, ảo hóa mức hệ
điều hành



DANH MỤC HÌNH VẼ
Hình 1.1: Các bước cơ bản trong tích hợp liên tục [5] ........................................ 7
Hình 1.2: Bảng so sánh kiến trúc một khối, kiến trúc hướng dịch vụ và kiến trúc
vi dịch vụ [5] ......................................................................................................... 8
Hình 1.3: Kiến trúc một khối cho ứng dụng đặt hàng ......................................... 9
Hình 1.4: Kiến trúc hướng dịch vụ cho ứng dụng đặt hàng ............................... 11
Hình 1.5: Kiến trúc vi dịch vụ cho ứng dụng đặt hàng ...................................... 12
Hình 1.6: Mô hình triển khai không tùy biến - cập nhật ứng dụng [5] .............. 14
Hình 1.7: Mô hình triển khai không tùy biến - cấu hình reverse proxy khi cập
nhật [5] ................................................................................................................ 15
Hình 1.8: Mô hình triển khai không tùy biến với vi dịch vụ - cập nhật dịch vụ
[5] ........................................................................................................................ 16
Hình 1.9: Mô hình triển khai không tùy biến với vi dịch vụ - cấu hình reverse
proxy [5].............................................................................................................. 16
Hình 1.10: Công nghệ container ........................................................................ 17
Hình 1.11: Khác biệt giữa container và máy ảo................................................. 18
Hình 1.12: Kiến trúc Docker .............................................................................. 19
Hình 1.13: Kiến trúc chi tiết của Docker ........................................................... 20
Hình 2.1: Bộ lập lịch .......................................................................................... 22
Hình 2.2: Điều phối container và các thành phần .............................................. 23
Hình 2.3: Cách thức thu thập log trước đây ....................................................... 26
Hình 3.1: Luồng triển khai liên tục cơ bản ........................................................ 28
Hình 3.2: Mô hình rẽ nhánh ............................................................................... 30
Hình 3.3: Máy chủ CI Jenkins ........................................................................... 31
Hình 3.4: Kiến trúc master-slave của Jenkins [15] ............................................ 32
Hình 3.5: Kiến trúc của Docker Swarm [16] ..................................................... 33
Hình 3.6: Kiến trúc của Apache Mesos ............................................................. 35
Hình 3.7: Kiến trúc Mesosphes Marathon [17] ................................................. 35
Hình 3.8: Kiến trúc của Kubernetes [10] ........................................................... 36
Hình 3.9: Ánh xạ Service và Pod trong Kubernetes .......................................... 37

Hình 3.10: Phần mềm Slack............................................................................... 39
Hình 3.11: Luồng sử dụng cơ bản Fluentd với Elasticsearch và Kibana ........... 41
Hình 3.12: Heapster trong Kubernetes [20] ....................................................... 42
Hình 3.13: Kiến trúc Prometheus [21] ............................................................... 43
Hình 3.14: Hệ thống triển khai liên tục hoàn thiện ............................................ 44
Hình 3.15: Kết nối mạng triển khai ................................................................... 45
Hình 4.1: Mối quan hệ của ccs-service và forex-service ................................... 47
Hình 4.2: Mỗi vi dịch vụ có thể có nhiều bản sao chạy ..................................... 48
Hình 4.3: Cách tổ chức mã nguồn ..................................................................... 48
Hình 4.4: Cấu hình webhook cho build tiền hợp nhất trên Gitlab ..................... 49
Hình 4.5: Cấu hình Gitlab plugin trên Jenkins cho tiền hợp nhất ...................... 50


Hình 4.6 Trạng thái build tiền hợp nhất được cập nhật theo thời gian thực trên
Gitlab................................................................................................................... 51
Hình 4.7: Build tiền hợp nhất chạy thành công ................................................. 51
Hình 4.8: Thông báo từ Jenkins tới Slack .......................................................... 52
Hình 4.9: Groovy DSL cho Jenkins ................................................................... 52
Hình 4.10: Các tác vụ trong Jenkins .................................................................. 53
Hình 4.11: Cách thức kết nối mạng trong kubernetes ....................................... 54
Hình 4.12: Các máy trong Kubernetes cluster ................................................... 55
Hình 4.13: Cách thức cài đặt và quản lý log ...................................................... 55
Hình 4.14: Cách thức cài đặt Prometheus .......................................................... 56


DANH MỤC BẢNG BIỂU
Bảng 3-1: So sánh Docker Swarm, Mesosphere Marathon và Kubernetes ....... 38
Bảng 3-2: So sánh Logstash và Fluentd ............................................................. 40
Bảng 4-1: Kết quả của giải pháp triển khai liên tục ........................................... 58



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 [1] . 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 dịch 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.

1


Lớp trên cùng, chịu trách nhiệm giao tiếp với người dùng, là lớp giao diện. Lớp
giao diện thực hiện việc 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 sử dụng.
Ngay phía dưới lớp giao diện là 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. Lớp logic
nghiệp vụ không tương tác với cơ sở dữ liệu một cách trực tiếp mà dựa vào lớp truy
xuất dữ liệu phía dưới.
Cuối cùng là lớp truy xuất dữ liệu. Đây 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. Đầu tiên, vviệ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ý. Ngoài ra, do tính chất độc
lập, 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). Nhờ có kiến trúc vi dịch vụ, việc 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.

2


Không những thế, việc mmở rộng (scale) ứng dụng trở nên đơn giản và nhanh chóng,
do việc mở rộng ứng dụng sẽ được phân nhỏ thành mở rộng 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.

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 sẽ 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 cũng như công nghệ
container.
Chương 2 tập trung vào việc phân tích yêu cầu để xây dựng mô hình triển khai
liên tục cho cho một hệ thống vi dịch vụ sử dụng container.
Dựa vào y ảnh docker và được triển khai như một dịch vụ ngay trong
Kubernetes cluster.
Các vi dịch vụ forex-service và ccs-service trong luận văn được triển khai vào
các worker node trong kubernetes cluster. Đối với một tổ chức, thông thường các máy
triển khai môi trường sản phẩm được cài đặt trong một VPC với cấu hình mạng cách
ly với môi trường bên ngoài. Do vậy, cần có cách thức để cho phép các yêu cầu gửi từ
bên ngoài được chuyển vào đúng các vi dịch vụ đang chạy trong lòng VPC. Luận văn
đề xuất cách thức cài đặt như Hình 4.12:
• Một vi dịch vụ nginx được cài đặt ngay trong Kubernetes cluster, cùng cấp với
các dịch vụ forex-service và ccs-service. Nginx được cấu hình để chuyển các
yêu cầu với endpoint phù hợp vào các vi dịch vụ phù hợp. Mọi yêu cầu gửi tới
các vi dịch vụ trong Kubernetes đều được thực hiện thông qua nginx.

54



• Nginx sẽ ánh xạ cổng được sử dụng trong ảnh docker ra một cổng thực bên
ngoài worker node. Các load-balancer được thiết lập để trỏ vào tất cả các máy
worker node, tới cổng mà nginx được ánh xạ (Hình 4.12).

Hình 4.12: Các máy trong Kubernetes cluster

4.1.3.2 Cài đặt cách thức thu thập và quản lý log
Các thức cài đặt hệ thống thu thập và quản lý log thể hiện trong Hình 4.13 dưới
đây.

Hình 4.13: Cách thức cài đặt và quản lý log
Khi các vi dịch vụ chạy trong docker container, log của chúng được cấu hình để
ghi ra tệp log tại các vị trí được xác định theo mẫu sau:
/var/log/msa/${TÊN_VI_DỊCH_VỤ}/${TÊN_POD}/

55


Các tệp log trong container sẽ được mount vào thư mục /var/log/msa trên chính
các máy mà container chạy. Kubernetes sẽ tự động sinh ra tên pod ngẫu nhiên dựa vào
tên dịch vụ được cung cấp, do vậy tên pod có thể thay đổi theo thời gian. Việc quy
định như trên đảm bảo các tệp log của pod cũ vẫn còn tồn tại trên các node để phục vụ
mục đích thu thập. Các fluentd agent được cài đặt trên tất cả các máy và được cấu hình
để đọc (tail) các tệp log ở các vị trí xác định, phân giải các trường thông tin trong log
và tạo ra các luồng log để gửi tới một máy tập trung gọi là fluentd-aggregator. Về cơ
bản, fluentd-aggregator cũng là một máy chạy fluentd bình thường; tuy nhiên, fluentd
được cấu hình để lắng nghe sự kiện log gửi tới (tại một port xác định). Trên máy
fluentd-aggregator, các sự kiện log sẽ được lọc, thêm các trường thông tin cần thiết,
gửi tới hệ thống lưu trữ và đánh chỉ số Elasticsearch. Phần mềm Kibana được cấu hình

trên một máy khác, kết nối tới Elastichsearch để truy vấn dữ liệu, từ đó hiển thị thông
tin cho người dùng. Elasticsearch và Kibana được cài đặt như các dịch vụ hệ thống
systemd.

4.1.3.3 Cài đặt hệ thống giám sát

Hình 4.14: Cách thức cài đặt Prometheus

56


Prometheus là thành phần quan trọng nhất trong hệ thống giám sát, có nhiệm vụ
thu thập các thông tin về trạng thái của tất cả các máy và ứng dụng. Các thông tin cần
thu thập bao gồm:
• Tài nguyên sử dụng trên các máy triển khai
• Trạng thái các đối tượng trong Kubernetes cluster
• Các thông tin khác được sinh ra bởi bản thân các ứng dụng
Cơ chế thu thập thông tin của Prometheus là “scrape”. Nghĩa là máy chủ
Prometheus sẽ chủ động tải các thông tin về các đối tượng dựa vào các endpoint mà
đối tượng cung cấp. Prometheus được cài đặt ngay trong Kubernetes cluster, sử dụng
docker container.
Đối với tài nguyên sử dụng trên máy triển khai, do bản thân hệ điều hành đã lưu
những thông tin này tại những vị trí xác định trên máy, người dùng chỉ cần tìm cơ chế
chuyển đổi những thông tin này sang dạng mà Prometheus có thể đọc được. Với yêu
cầu này, luận văn sử dụng dịch vụ node-exporter, cài đặt trên tất cả các máy. Các
thông tin sau khi chuyển đổi sẽ được cung cấp tại port 9100 trên chính các máy đó, với
endpoint là /metrics.
Đối với trạng thái các đối tượng trong Kubernetes cluster (như thông tin về pod,
container, service), luận văn sử dụng kube-state-metrics. Kube-state-metrics sẽ lắng
nghe các thông tin trạng thái đối tượng từ API server trên master node trong

Kubernetes, chuyển đổi định dạng và cung cấp endpoint /metrics để Prometheus có thể
“scrape”. Cũng như máy chủ Prometheus, kube-state-metrics được cài đặt bằng cách
sử dụng docker container trong Kubernetes cluster.
Đối với các trường thông tin khác sinh ra bởi ứng dụng, luận văn sử dụng cơ chế
gắn các chú thích vào các docker container. Bằng cách sử dụng các thư viện
instrumentation, các ứng dụng vi dịch vụ có thể tự định nghĩa các thước đo mới tùy
theo yêu cầu. Máy chủ Prometheus sẽ “scrape” những thông tin này qua endpoint
được cung cấp trong chú thích của docker container.
Ví dụ: thước đo msa_request_count được tích hợp vào trong forex-service và
ccs-service với 2 nhãn success, error để tính số lượng các yêu cầu thành công hay thất
bại gửi tới chúng.
Prometheus được cấu hình để gửi thông tin cảnh báo tới Alert Manager khi trạng
thái của hệ thống vượt qua ngưỡng nhất định. Từ Alert Manager, cảnh báo sẽ được gửi
đi thông qua các kênh thông báo khác nhau (trong bài sử dụng Slack). Alert Manager
trong luận văn được chạy bằng docker, tuy nhiên nằm riêng biệt với Kubernetes
cluster.

57


ALERT high_error_request
IF msa_request_count{msa_request_count="error"} > 3
FOR 1m
LABELS { severity = "slack" }
ANNOTATIONS {
summary = "Number of error request error is too high",
description = "Receives lots of error request%. Reported by
instance {{ $labels.instance }} of job {{ $labels.job }}.",
}
Trong luận văn, tất các các quá trình cài đặt hệ thống phức tạp này đều được viết

bằng Ansible. Việc cài đặt trên một máy hay một nhóm các máy có thể được thực hiện
nhanh chóng thông qua cấu hình trên một tệp duy nhất.

4.2 Đánh giá kết quả
Luận văn được viết dựa trên kết quả làm việc thực tế của tác giả trong quá trình
xây dựng giải pháp triển khai liên tục cho hệ thống phần mềm thương mại điện tử của
công ty Works Applications Singapore. Kết quả của việc ứng dụng giải pháp triển khai
liên tục này được thể hiện qua bảng sau:
Trước khi dùng giải pháp Sau khi dùng giải pháp triển
triển khai

khai liên tục

Thời gian triển khai 15 phút
1 module

5 phút

Thời gian triển khai 5 tiếng
toàn bộ 30 module

1 tiếng

Thời gian từ lúc có 30 phút
mã mới tới khi triển
khai hoàn toàn

5 phút

Số lần triển khai 2-3 lần/module

được thực hiện
trong 1 ngày

8-12 lần/module

Người thực hiện Đội vận hành
việc triển khai

Đội trưởng (leader) của các
đội phát triển và các bên liên
quan được cấp quyền (ví dụ:
đội quản lý chất lượng)

Bảng 4-1: Kết quả của giải pháp triển khai liên tục

58


Qua kết quả đạt được thể hiện trong Bảng 4-1: Kết quả của giải pháp triển khai
liên tục, ta có thể thấy một số lợi ích rõ rệt mà nó mang lại như:
• Giảm chu kỳ phát hành sản phẩm (Cycle Time)
Chu kỳ phát hành được tính từ thời gian có yêu cầu tới khi bản phát hành được
cập nhật trên môi trường sản phẩm. Bằng hệ thống triển khai liên tục, bất cứ một sự
thay đổi mã đều có thể được triển khai ngay lập tức ra môi trường mà không gặp khó
khăn gì. Ngay khi nhận yêu cầu người dùng, nhà phát triển có thể phân tích, sửa đổi
mã nguồn và gửi yêu cầu hợp nhất mã mới vào nhánh chính để kích hoạt quá trình
triển khai.
Việc triển khai ngoài cách kích hoạt tự động còn có thể được thực hiện thông qua
giao diện trên Jenkins (chỉ áp dụng cho người dùng với quyền hạn phù hợp). Với cách
sử dụng đơn giản, hệ thống sau khi được cài đặt không đòi hỏi chuyên gia về triển khai

để quản lý mà trách nhiệm có thể được phân phối về từng đội. Điều này cũng góp phần
khuyến khích việc thực hiện cập nhật với tần suất cao hơn, từ đó cải thiện chu kỳ phát
hành thay đổi của sản phẩm.
• Tăng năng suất lao động
Tăng năng suất là lợi ích rõ nhất của hệ thống triển khai liên tục. Việc triển khai
thường xuyên giúp tổ chức phát hiện ra các vấn đề và nhận được phản hồi sớm từ
người dùng, từ đó đưa ra các giải pháp phù hợp trong thời gian sớm nhất. Bằng việc tự
động hoá quá trình build, kiểm thử cũng như cung cấp các công cụ triển khai dễ dàng,
các nhà phát triển có thể tập trung vào việc phát triển tính năng còn các đội vận hành
cũng giảm thiểu thời gian giám sát hệ thống để tập trung vào nâng cao chất lượng các
dịch vụ khác. Trước đây, việc triển khai, giám sát một cluster gồm hàng trăm máy đòi
hỏi ít nhất một đội vận hành hàng chục người. Với hệ thống triển khai liên tục, tích
hợp với các cơ chế giám sát và thông báo lỗi tự động cho các bên liên quan, chỉ cần
vài người là có thể đảm bảo môi trường sản phẩm hoạt động tốt với độ tin cậy cao.
• Thỏa mãn được các yêu cầu cho đội phát triển
Các nhà phát triển thường quen thuộc với cách thức truy cập trực tiếp vào môi
trường sản phẩm để đọc và phân tích log khi có lỗi xảy ra. Tuy nhiên, kinh nghiệm
thực tế chỉ ra rằng cách làm này có thể gây ra nhiều thiệt hại hơn lợi ích mà nó mang
lại, bởi bất cứ ai cũng có thể tạo ra một sửa đổi khiến máy triển khai chuyển sang trạng
thái hoạt động bất thường.
Với việc sử dụng mô hình triển khai không tùy biến, không có nhiều khác biệt
giữa môi trường sản phẩm và môi trường local (tất cả các thư viện, các yêu cầu để một
dịch vụ chạy đều đã được đóng gói vào trong ảnh container), do vậy các nhà phát triển
hoàn toàn có thể tự tái hiện và kiểm tra lỗi trên chính máy đang sử dụng.

59


Ngoài ra, tất cả cấu hình máy triển khai được khai báo trong các tệp kịch bản
Ansible, được lưu và quản lý như mã nguồn trên kho quản lý mã chung mà các nhà

phát triển đều có thể truy cập. Bằng việc tích hợp EFK stack và sử dụng các công cụ
giám sát với giao diện thể hiện trực quan, những thông tin trước đây cần phải truy cập
trực tiếp vào máy mới xem được đều hiện hữu trên giao diện người dùng.
Thiết kế hệ thống triển khai liên tục trong luận văn thực tế đã đáp ứng gần như
tất cả yêu cầu mà bên phát triển mong muốn, đồng thời đã hạn chế được những rủi ro
mà những phương pháp làm việc trước đó gây ra.

60


KẾT LUẬN

Các ứng dụng dựa vào đám mây là xu hướng mới nhất trong ngành công nghiệp
công nghệ thông tin, giúp phát triển và triển khai phần mềm một cách nhanh chóng và
hiệu quả bằng việc sử dụng các dịch vụ đám mây. Nó giúp cải thiện tốc độ làm việc
cho các đội phát triển bởi giờ đây họ có thể tập trung vào việc phát triển tính năng thay
vì lo lắng về cơ sở hạ tầng. Nó cũng giúp cho các công ty, tổ chức đưa phần mềm ra
thị trường nhanh hơn do không phải dành thời gian cho các thủ tục chuẩn bị máy móc
phức tạp. Các khái niệm tích hợp, chuyển giao liên tục, kiến trúc vi dịch vụ giúp tận
dụng tối đa lợi ích mà đám mây mang lại. Công nghệ container, cùng với sự xuất hiện
của Docker, đã cách mạng hóa cách thức đóng gói và triển khai vi dịch vụ. Bản chất
nhẹ, sử dụng ít tài nguyên của container giúp việc thiết lập môi trường chạy cho ứng
dụng trở nên đơn giản, nhanh chóng (giảm thời gian từ vài chục phút cho việc khởi
động máy ảo gồm toàn bộ hệ điều hành tới chỉ vài phút cho việc bật container). Sự ra
đời của container, tích hợp cùng với những công nghệ trước đó, đã biến việc triển khai
liên tục trở thành hiện thực. Các công cụ mới cũng liên tục xuất hiện để hỗ trợ cho
việc triển khai container như: các công cụ khám phá dịch vụ, các bộ khung điều phối
container, các giải pháp thu thập và quản lý log hay các cách thức giám sát hệ thống.
Tuy nhiên, toàn bộ những công cụ, nền tảng hay công nghệ nêu trên đều xuất hiện và
tồn tại một cách rời rạc, chưa có sự thống nhất. Câu hỏi đặt ra hiện tại là làm thế nào

để phối hợp những công nghệ này để đưa ra một luồng triển khai liên tục hiệu quả,
đảm bảo thỏa mãn yêu cầu của các bên liên quan.
Qua việc thu thập yêu cầu, phân tích công nghệ, luận văn đã đưa ra được một
thiết kế hệ thống triển khai liên tục đáp ứng gần như toàn bộ những yêu cầu cơ bản
nhất cho các ứng dụng dựa vào kiến trúc vi dịch vụ sử dụng container. Tuy nhiên, do
những giới hạn về thời gian và độ dài, một số vấn đề còn chưa được đề cập tới trong
thiết kế.
Đầu tiên là cơ chế tự động scale-out và scale-in của hệ thống. Việc sử dụng giải
pháp quản lý cấu hình cùng với nền tảng điều phối container trên điện toán đám mây
cho phép hệ thống triển khai liên tục có thể cung ứng thêm máy triển khai và tạo ra các
bản sao của container một cách nhanh chóng. Kèm với việc sử dụng các Load
Balancer hoặc các Reverse Proxy, việc scale-out và scale-in mà không có thời gian
ngừng hoạt động hoàn toàn có thể thực hiện được. Tuy nhiên, câu hỏi mà luận văn
chưa giải quyết được là lựa chọn thời điểm để scale-out/in phù hợp nhất. Hệ thống
giám sát cluster cho phép thu thập các thông tin tài nguyên đang sử dụng nhưng những
thông tin này chưa được tận dụng tối đa. Nếu có thể xây dựng một thành phần trí tuệ

61


nhân tạo để tự học những dữ liệu này và đưa ra quyết định thì sẽ tối ưu được quá trình
scale.
Ngoài ra, việc cập nhật và đảo ngược phiên bản triển khai hiện tại chỉ áp dụng
đối với cấp bậc container. Nếu container sử dụng cơ sở dữ liệu ngoài, những hành
động này cũng đòi hỏi sự thay đổi tương ứng trong các bảng cơ sở dữ liệu. Cách thức
làm thế nào để có thể đảo ngược cơ sở dữ liệu chưa được đề cập tới. Hiện nay, có một
số phương án thường được sử dụng như sao lưu gia tăng (incremental backup), áp
dụng các migration script, ... nhưng luận văn cũng chưa đề cập tới và đưa ra những
phân tích, so sánh thỏa đáng để áp dụng.
Trong tương lai, tác giả sẽ tiếp tục nghiên cứu và hoàn thiện giải pháp hiện tại để

giải quyết những vấn đề nêu trên cũng như đáp ứng những yêu cầu mới nảy sinh trong
quá trình phát triển của ngành công nghiệp phần mềm.

62


TÀI LIỆU THAM KHẢO

[1] “Survey Agile,” Techbeacon, 2018. [Online]. Available:
/>[2] Amazon Web Services, Inc, “What is Cloud Computing,” [Online]. Available:
/>[3] IBM, “What is cloud computing?,” [Online]. Available:
/>[4] S. Guckenheimer, “What is Continuous Integration?,” [Online]. Available:
/>[5] V. Farcic, The DevOps 2.0 Toolkit, Leanpub, 2016.
[6] P. Maresca, “From Monolithic Three-tiers Architectures to SOA Vs
Microservices,” [Online]. Available:
/>[7] A. Kharenko, “Monolithic vs. Microservices Architecture,” [Online]. Available:
/>[8] I. Miri, “Microservices vs. SOA,” [Online]. Available:
/>[9] Docker Inc., “What is a container,” [Online]. Available:
/>[10] Docker Inc. All rights reserved., “Docker overview,” [Online]. Available:
/>[11] M. Hausenblas, Docker Networking and Service Discovery, O’Reilly Media,
2016.
[12] J. Sitakange, “Infrastructure as Code: A Reason to Smile,” [Online]. Available:
/>[13] A. Mouat, Using Docker, O’Reilly Media, 2016.
[14] Fluentd Project, “What is Fluentd?,” [Online]. Available:
/>[15] A. s. G. b. model, “A successful Git branching model,” [Online]. Available:
/>[16] S. J. Tirkey, “Setting Up Master Slave Machines Using Jenkins,” [Online].
Available: />[17] A. Parthasarathy, “Kubernetes vs Docker Swarm,” [Online]. Available:
/>[18] A. Parthasarathy, “Kubernetes vs Mesos + Marathon,” [Online]. Available:
/>[19] T. K. Authors, “Services,” [Online]. Available:
/>[20] N. Peri, “Fluentd vs. Logstash: A Comparison of Log Collectors,” [Online].


63


Available: />[21] V. K. a. V. Marmol, “Resource Usage Monitoring in Kubernetes,” [Online].
Available: />[22] Prometheus, “Prometheus,” [Online]. Available:
/>[23] NetApp, “What Is Configuration Management (CM),” [Online]. Available:
/>[24] Apache Mesos, “What is Mesos?,” [Online]. Available: />[25] MongoDB, Inc, “Containers and Orchestration Explained,” [Online]. Available:
/>[26] “ [Online]. Available: />[27] Mesosphere, Inc., “Marathon,” [Online]. Available:
/>[28] Amazon Web Services, Inc, “What are Containers,” [Online]. Available:
/>[29] Fluentd Project, “Why use Fluentd,” [Online]. Available:
/>[30] C. Richardson, “Scale Cube,” [Online]. Available:
/>[31] “kube-state-metrics,” [Online]. Available: />[32] R. Karanam, “Microservices With Spring Boot,” [Online]. Available:
/>[33] “Flannel,” [Online]. Available: />
64


PHỤ LỤC
Phụ lục thể hiện một số phần mã nguồn chính cho Hệ thống triển khai liên tục mẫu
được xây dựng trong luận văn.
I.
Các vi dịch vụ
1. Forex-service
• Controller
@RestController
public class ForexController {
@Autowired
private Environment environment;
@Autowired
private ExchangeValueRepository repository;

private static final Counter requestTotal = Counter.build()
.name("msa_request_count")
.labelNames("msa_request_count")
.help("Number of request received").register();
@GetMapping("/currency-exchange/from/{from}/to/{to}")
public ExchangeValue retrieveExchangeValue
(@PathVariable String from, @PathVariable String to){
ExchangeValue exchangeValue =
repository.findByFromAndTo(from, to);
if(exchangeValue != null) {
requestTotal.labels("success").inc();
exchangeValue.setPort(
Integer.parseInt(environment.getProperty("local.server.port")));
}
else {
requestTotal.labels("error").inc();
}
return exchangeValue;
}
@GetMapping("/health/status")
public boolean healthCheck() {
return true;
}
}

• Dockerfile

FROM release-docker.deanthesis.com:5000/release-test/oracle-jdk:8u121
MAINTAINER Dean Nguyen
RUN mkdir -p /forex-service

COPY script/start.sh /forex-service/
COPY forex-service-0.0.1-SNAPSHOT.jar /forex-service/
RUN chmod +x /forex-service/start.sh

65


EXPOSE 8000
WORKDIR /forex-service

2. Current-conversion-service
• Controller
@RestController
public class CurrencyConversionController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private static final Counter requestTotal = Counter.build()
.name("msa_request_count")
.labelNames("msa_request_count")
.help("Number of request received").register();
@Autowired
private CurrencyExchangeServiceProxy proxy;
// @GetMapping("/currencyconverter/from/{from}/to/{to}/quantity/{quantity}")
@RequestMapping(value = "/currencyconverter/from/{from}/to/{to}/quantity/{quantity}", method =
RequestMethod.GET)
public CurrencyConversionBean convertCurrency(@PathVariable String
from, @PathVariable String to,
@PathVariable BigDecimal quantity) {
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("from", from);
uriVariables.put("to", to);

ResponseEntity<CurrencyConversionBean> responseEntity = new
RestTemplate().getForEntity(
"http://localhost:8000/currency-exchange/from/{from}/to/{to}",
CurrencyConversionBean.class,
uriVariables);
CurrencyConversionBean response = responseEntity.getBody();
return new CurrencyConversionBean(response.getId(), from, to,
response.getConversionMultiple(), quantity,
quantity.multiply(response.getConversionMultiple()),
response.getPort());
}
// @GetMapping("/currency-converterfeign/from/{from}/to/{to}/quantity/{quantity}")
@RequestMapping(value = "/currency-converterfeign/from/{from}/to/{to}/quantity/{quantity}", method =
RequestMethod.GET)
public CurrencyConversionBean convertCurrencyFeign(@PathVariable
String from, @PathVariable String to,
@PathVariable BigDecimal quantity) {
CurrencyConversionBean response = proxy.retrieveExchangeValue(from,
to);
logger.info("{}", response);
if(response != null) {
requestTotal.labels("success").inc();
}
else {
requestTotal.labels("error").inc();
}
return new CurrencyConversionBean(response.getId(), from, to,
response.getConversionMultiple(), quantity,
quantity.multiply(response.getConversionMultiple()),


66


×