P
Cloud Blog ProAWS Blog · Cộng đồng VN
Xây Dựng Hệ Thống CICD và infrastructure cho hệ thống web application nhiều tiers chuẩn thực tế (Phần 2)

Xây Dựng Hệ Thống CICD và infrastructure cho hệ thống web application nhiều tiers chuẩn thực tế (Phần 2)

NSNgô Sỹ Long··16 phút đọc·6 lượt xem

Lab Introduction

  • AWS experience: Intermediate
  • Time to complete: 180 minutes
  • AWS Region: US East (N. Virginia) us-east-1
  • Cost to complete: (Optional) Free Tier eligible, bellow 10$
  • Services used: Github Action (Github), EC2, Auto Scaling Group, S3, Cloudwatch Log, AMI, RDS, KMS, Terraform (Hashi Corp), Packer (HashiCorp)

Giới thiệu về Lab

Xin chào các bạn, Mình là Long Mentor hôm nay mình sẽ mang tới cho các bạn một bài lab khá thú vị, về việc xây dựng một hệ thống CICD hoàn chỉnh với độ phức tạp tiệm cận với một dự án thực tế. Mục đích mình xây dựng bài lab này là để cho các bạn có thể hình dung về một hệ thống CICD hoàn chỉnh thực tế sẽ trông như thế nào. Dù bài viết sẽ không cover được hoàn toàn tất cả các biến thể của hệ thống thực tế nhưng mà có sao đâu, học hành là con đường dài, nếu các bạn đi với mình thì chúng ta cùng nhau khám phá dần dần nhé. 😎 Vì bài viết khá dài nên mình xin phép được chia ra làm nhiều phần.

Bài viết không được generate bởi AI, nên có thể sẽ bao gồm :

  1. Lỗi chính tả. ✍
  2. Ý văn lủng củng. ☹
  3. Một số nội dung không phù hợp do mình cao hứng viết. 😝
  4. Câu chuyện do mình bịa ra nhưng gây đồng cảm sâu sắc. 😊

Tuy nhiên sẽ không bao gồm:

  1. Câu từ hoa mỹ nhưng không có ý nghĩa. 🤦‍♂️
  2. Code không được verify.🤬
  3. Cảm giác fake fake khi đọc. 🤢

Câu chuyện, tiếp nối phần trước ...

<div style={{ backgroundColor: '#fff3cd', border: '2px solid #dc3545', borderRadius: '8px', padding: '20px', marginTop: '30px', color: '#721c24', boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)' }}>
    <h3 style={{ color: '#dc3545', fontSize: '1.5em', marginBottom: '15px', display: 'flex', alignItems: 'center' }}>
        ⚠️ Tuyên BMin TrTrách Nhim
    </h3>
    <div style={{ width: '50px', height: '4px', backgroundColor: '#dc3545', marginBottom: '15px' }}></div>
    <p style={{ fontSize: '1em', lineHeight: 1.6, margin: '10px 0', textAlign: 'justify' }}>
        <strong style={{ color: '#dc3545' }}>Lưu ý:</strong> Tt ccác nhân vt, công ty, skin, và tình hung được đề cp trong câu chuyn này đều là sn phm ca shư cu, được to ra vi mc đích gii trí hoc minh ha.
    </p>
    <p style={{ fontSize: '1em', lineHeight: 1.6, margin: '10px 0', textAlign: 'justify' }}>
        Câu chuyn <strong style={{ color: '#dc3545' }}>không có mc đích công kích, bôi nhọ</strong> bt kcá nhân, tchc, hoc ám chỉ đến bt kstht thc tế nào. Mi strùng hp vtên gi, skin, hoc chi tiết đều chlà <strong style={{ color: '#dc3545' }}>ngu nhiên</strong>.
    </p>
    <p style={{ fontSize: '1em', lineHeight: 1.6, margin: '10px 0', textAlign: 'justify' }}>
        <strong style={{ color: '#dc3545' }}>Tác gikhông chu bt ktrách nhim nào</strong> đối vi vic ni dung này bsdng sai mc đích, chng hn như tuyên truyn hoc xuyên tc bi bt kcá nhân hoc tchc nào khác.
    </p>
</div>
<p><em>"Mnó chứ!"</em></p>
<p>Đôi môi Trung mím cht, hàm răng nghiến ken két, tng tnhư lướt qua krăng. Chi xong, anh cm ly bia tươi mi rót, bt còn si, nc mt hơi cn sch.</p>
<p><em>"Không biết my lão sếp nghĩ cái quái gì trong đầu! Start dự án ngay k30/4 - 1/5. Em đã có plan đi Phú Quc, vé máy bay đặt trước ba tháng ri. Gilnào bvic?"</em></p>

<p>Trung là thành viên đội dev, tng làm cùng team DevOps vi Long. Hai người gn bó lâu năm, ăn ý như hình vi bóng. Trung gii kthut nhưng tính nóng như la, sn sàng cãi tay đôi vi qun lý hay team lead. Báo cáo task thì uoi, nhưng hai đụng đến mình, anh không bao gibqua, thm chí sn sàng "xlý" bng nm đấm nếu cn. Qun lý nào hp vi Trung vài ln cũng ngán ngm, xin chuyn team. Câu ca ming ca Trung là:</p>
<p><em>"Con ln nào chlàm qun lý được. Gii hơn dev thì vào mà code!"</em></p>

<p>Nhưng vi Long, Trung li khác. Long xut thân tdev, tng hướng dn Trung tngày đầu vào công ty. Long ít nói, dtính, hay cnể, thường hùa theo ý kiến người khác. Vì thế, hai người hp cạ, lâu dn thành thân thiết. Ông bà xưa có câu: <em>"Cây khô có cành, người khó có đôi"</em> – chính là để chnhng mi quan hnhư thế này.</p>

<p>Vvstart dự án trúng knghlễ, Long lúc nhn task tsếp chng nghĩ ngi gì. Dù là team lead, anh thiếu kinh nghim qun lý và tcht lãnh đạo. Trước giờ, Long chlàm qua loa, giao vic cho team ri tự ôm task khó vmình, cgng hoàn thành đúng hn. Ngoài cách đó, Long chng có chiêu gì khác. My tun nay, anh không tchc daily meeting, nên chng biết team đã lên lch nghỉ. Đến giờ, chng biết trách ai, đành chơi li bài cũ.</p>
<p>Long chc lưỡi: <em>"Thôi chú cnghthong thả, task cũng không ln, team scover được."</em></p>

<p>Vì ngi giao tiếp và sxung đột, Long thường ôm vic vmình, lng lOT gii quyết. Trước kia thì ổn, nhưng giLong cm thy sc khe không còn như xưa. Gn 38 tui, anh chng ththc đêm code hay hc công nghmi như bn trẻ. Trong làn sóng "vibe coding", nhng dev ln tui như Long, vi mc lương cao, thường là mc tiêu bngm cho layoff. Làm sao vô tư như Trung, kiu công ty làm pht ý thì nghvic, chuyn chkhác?</p>

<pang mi suy nghĩ, mt ging nhnhvang lên bên cnh: <em>"Chc công ty cũng có khó khăn. Mi người trong team cùng cgng, em nghĩ svượt qua được."</em></p>
<p>Đó là Phương Linh, cô bé Gen Z mi vào công ty đầu năm dưới dng intern. Dù chưa có nhiu kinh nghim code, Linh biết ba thtiếng, được bmẹ đầu tư tnhỏ, shu lot chng chtghế ging đường. Nhờ đó, cô ddàng giành vtrí intern trong team ca Long. Công vic ca Linh không nng, chyếu làm tester, dch tài liu cho user nước ngoài, và tham gia qung bá sn phmhi chcông nghnhngoi hình xinh xn. Quý nào cô cũng nhn gii "Tài năng trtiêu biu" ca công ty.</p>

<p>Nhìn Linh, Long luôn hoài nim tui trca mình. Giá như ngày xưa chăm hc ngoi ngữ, có lgianh đã có nhiu cơ hi tt hơn, thm chí đổi ngành. Càng nghĩ càng bun, Long cn chén, nc biangc, say lúc nào không hay.</p>

<p>Long vnhà trong tình trng say bí tỉ. Lâu ri anh không ung quá chén thế này. Mca, bước đến thm nhà, Long thy nôn nao, đầu óc quay cung như sp ngt. Trong cơn màng màng, anh cht nhli các cụ ở quê: ung rượu say, đi đêm dbtrúng gió. Nhthì méo ming, nng thì "cưỡi hc" vtri. Sáng ra, người ta chthy cái xác cng đờ.</p>
<p>Nghĩ đến đó, Long lnh sng lưng, mhôi túa ra. <em>Chng lẽ đây là khonh khc các cnói?</em> Anh cmmt, gượng dy, nhưng tay chân vô lc. Nhng hìnhnh tui thơ cht lướt qua như cun phim quay chm. Ý thc dn mờ đi...</p>

<p>Cngỡ đã chm hết, thì ánh đèn trong nhà sáng lên. Mt dáng người mnh mai chy ra, quxung, đỡ Long dy. Ging nóim áp, ngt ngào xen chút trách móc: <em>"Sao li ra bdng này? Em đã bo ung ít thôi mà. Mau vào nhà, em thay qun áo cho."</em></p>
<p>Long thy cơ thnhhn, lng ngcm lên, tay chân dn có cm giác. Vdìu anh đi tm, nu bát canh nóng gii rượu, thay đồ pyjama. Hai vchng lên giường. Long khết chuyn công vic, nhng bun phin gn đây, mt cay cay.</p>
<p>Vdu dàng: <em>"Cuc sng có lúc khó khăn. Nếu anh mt vic, em sẽ đi dy li. Con mình ln ri, ssan svic nhà. Tn tin mt chút, mình svượt qua."</em></p>
<p>Nghe nhng li ani, Long chìm vào gic ngủ, lòng nhnhàng, đầy động lc như chưa tng có.</p>

<p>Chuông báo thc tiPhone vang lên. Long mmt, nhn ra mình vn nm trước ca nhà, mc nguyên bộ đồ đi nhu hôm qua. Trước mt là bãi nôn ln dch mt xanh vàng. Chng khô khc, đầu đau như búa bổ. Làm gì có vnào? 37 tui, anh vn độc thân.</p>
<p>Long uoi tt chuông, cnhm mt tìm li gic mơ, nhưng cht nhhôm nay phi đi làm. Anh vi chm dy, vsinh qua loa, vác gương mt mt mi đến công ty.</p>

<p>Lên công ty, xung quanh vng tanh. Mi người đã nghlễ Độc lp, chteam ca Long phi làm. Nhli gic mơ đêm qua, Long bng thy cô đơncm giác chưa tng xut hin bao năm nay.</p>
<pin thoi rung. Tin nhn tPhương Linh: <em>"Sáng nay em không khe, đau bng, bun nôn. Bác sĩ bo ngộ độc thc phm, phi nhp vin vài ngày. Em xin nghỉ đến ngày XX."</em></p>
<p>Long lo lng, nghĩ chc do quán nhu hôm qua. Cm thy có phn trách nhim, anh định chiu xong vic sẽ đến thăm cô intern nhỏ.</p>
<p>Nhưng chng bao lâu, Long phát hin ra stht. Pha tách cà phê, lướt feed như thường lệ, anh thy bài post ca Phương Linh: <em>"Nng vàng, bin xanh, Phú Quc và anh."</em> Phía dưới là ảnh tay trong tay, trước bãi bin trng tinh và hàng da xanh.</p>
<p>Hóa ra cp đôi hôm qua vchung đã bay đi Phú Quc sáng nay. <em>"Chc Linh quên mình có trong friend list."</em> Long cười nht, lòng như mếu. Mbacklog, nhìn đống task phi xong trong ba ngày, anh thdài ngao ngán...</p>
<p style={{ textAlign: 'center', fontStyle: 'italic' }}>(To be continued...)</p>

OK văn vở đủ rồi ! vô món chính thôi các bạn 🤓

Về sự thay đổi so với phần 1

Sau khi được góp ý và xem xét mình có thay đổi kiến trúc như bên dưới:

Trước khi thay đổi:

Sau khi thay đổi:

Mình đã thay đổi những điểm sau:

  • Tầng app run bằng ec2 mình đưa vào private subnet
  • Dù không thể hiện trên sơ đồ architect nhưng cách kết nối cũng thay đổi như sau :
    • Kết nối vào EC2 thay vì sử dụng SSH -> chuyển qua sử dụng SSM
    • Packer cũng phải build trong private subnet
    • Thêm bastion host để kết nối rds

Để cho đơn giản dễ hiểu mình sẽ trình bày cách tiếp cận và build của mình như sau :

Thành phần hệ thống :

  1. Application (Frontend, Backend) : Là ứng dụng chúng cần deploy.
  2. Infrastructure (EC2, RDS, S3 ...): Là config của cơ sở hạ tầng để triển khai ứng dụng, cái này hiểu nôm na là cái server để ở đây sẽ được quản lý dưới dạng IAC (Terraform) code.
  3. CICD : Là các workflow tự động hóa các quy trình build, kiểm thử , deploy ứng dụng của chúng ta lên cơ sở hạ tầng . Không có cái này thì hệ thống của chúng ta vẫn hoạt động đc các bạn nhé chỉ là sẽ phải làm bằng tay hết thôi.

Hướng tiếp cận của mình không chỉ riêng cho bài lab này mà là bất kỳ công việc thực tế được giao luôn luôn là manual first. Có nghĩa là mình luôn tạo ra một workflow hoàn chỉnh , làm thủ công bằng tay , sau đó mới tìm cách tự động hóa . Trong bài này mình vẫn giữ nguyên hướng tiếp cận như vậy.

Giới thiệu infra code

infra
├── README.md
├── backend.tf
├── main.tf
├── modules
│   ├── cloudfront
│   │   ├── main.tf
│   │   ├── output.tf
│   │   └── variables.tf
│   ├── compute
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── efs
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── load_balancer
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── monitoring
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── rds
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── rds_bastion
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── s3
│   │   ├── main.tf
│   │   ├── ouput.tf
│   │   └── variables.tf
│   ├── security
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   └── vpc
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
├── outputs.tf
├── packer
│   └── packer.pkr.hcl
├── plan.out
├── scripts
└── variables.tf

Dưới đây là mô tả ngắn gọn chức năng của từng module Terraform trong mã :

  1. cloudfront:

    • Tạo CloudFront distribution để phân phối nội dung tĩnh từ S3 (path /) và chuyển tiếp yêu cầu API (/api/*) tới ALB.
    • Cấu hình cache behavior, HTTPS redirect, và chứng chỉ mặc định.
  2. compute:

    • Triển khai EC2 trong Auto Scaling Group (ASG) với launch template.
    • Tạo IAM role và security group cho EC2, cho phép truy cập S3 và CloudWatch.
  3. load_balancer:

    • Tạo Application Load Balancer (ALB) với target group cho backend EC2.
    • Cấu hình listener HTTP (port 80) và security group cho traffic công khai.
  4. monitoring:

    • Thiết lập CloudWatch logs cho EC2, RDS, CloudFront.
    • Tạo alarms cho CPU EC2 (>75%) và kết nối RDS (>100), gửi thông báo qua SNS.
  5. rds:

    • Triển khai PostgreSQL instance (RDS) với multi-AZ.
    • Tạo subnet group và security group, cho phép EC2 truy cập port 5432.
  6. rds_bastion:

    • Tạo EC2 bastion host với SSM và PostgreSQL client.
    • Cấu hình IAM role và security group để truy cập RDS qua SSM.
  7. s3:

    • Tạo S3 bucket cho nội dung tĩnh với tên duy nhất (prefix + UUID).
    • Cấu hình website hosting, chính sách cho CloudFront OAI, chặn truy cập công khai.
  8. security:

    • Tạo WAF để chặn IP độc hại, gắn với ALB.
    • Tạo KMS key để mã hóa tài nguyên.
  9. vpc:

    • Tạo VPC với public/private subnets, NAT Gateway, Internet Gateway.
    • Cấu hình route table và S3 VPC endpoint để truy cập S3 an toàn.

Code từng phần các bạn hãy xem ở đây nhé :

https://github.com/longngo192/cmp-cicd/tree/main/infra

Chạy thử infra code

Để chạy được yêu cầu các bạn phải cài đặt terraform, vì tránh cho bài viết quá dài mình xin rút ngắn khoản cài đặt terraform nhé các bạn. Lưu ý : Ở file variable các bạn hãy thay đổi tên s3 bucket và email cho hợp lý nhé , hoặc các bạn có thể truyền giá trị các biến vào khi chạy lệnh terraform apply.

  • Di chuyển vào thư mục infra
cd infra
  • Chạy lệnh terraform apply
terraform apply --auto-approve

Sau khi deploy xong chúng ta sẽ kiểm tra một vài đâu mục bên dưới để đảm bảo code terraform provision chính xác như mong đợi :

  1. Check connection vào ec2 (backend)
  2. Kiểm tra config cloud front

Check connection vào ec2 (backend)

trước khi access nào ec2 thông qua ssm các bạn cần phải config (manual) SSM service một tẹo. Config này chỉ cần làm một lần.

Nguyên do là vì khi connect vào ec2 thông qua ssm , chúng ta sẽ sử dụng ssm-user . User này được service ssm tạo thông qua ssm agent trong instance, tuy nhiên khi deploy và run application của chúng ta , AWS khuyến khích sử dụng một user riêng nên chúng ta config như bên dưới để chuyển sang ec2-user nhé

  • Tìm service SSM
  • Ở menu bên trái tìm Session Manager
  • Setting như hình gif

Okey tiến hành access thôi nào.

  • Để access vào ec2 instance thông qua ssm chúng ta có thể chạy thẳng câu lệnh từ local cli hoặc kết nối thông qua aws connect or aws cloud shell Connect thông qua AWS connect
aws ssm start-session \
    --target <instance-id>
  • Trường hợp của mình sẽ là
aws ssm start-session \
    --target i-062b8408428f21739
  • Volla !!!

  • Chạy những câu lệnh bên dưới để Cài đặt backend

Trường hợp các bạn fork repo của mình hãy chú ý đổi link repo cho hợp lý nhé

# Sau khi access vào ec2 instance chạy những lệnh bên dưới để setup server.
sudo yum clean all
sudo yum makecache
sudo yum install -y git gcc postgresql-devel python3 python3-pip python3-devel
sudo mkdir -p ~/app
sudo chown ec2-user:ec2-user ~/app
cd ~/app

git clone https://github.com/longngo192/cmp-cicd.git .

cd app/backend/

cat requirements.txt

pip3 install --no-cache-dir -r requirements.txt

RDS_ENDPOINT=$(aws rds describe-db-instances \
  --db-instance-identifier webapp-rds-primary \
  --region us-east-1 \
  --query 'DBInstances[0].Endpoint.Address' \
  --output text)


sed -i "s/YOUR_RDS_ENDPOINT/$RDS_ENDPOINT/g" .env

cat .env

ls app.py

sudo mkdir -p migrations

sudo chown ec2-user:ec2-user migrations

export FLASK_APP=app.py

export FLASK_ENV=production
python3 db_init.py
flask db init
flask db migrate

# chạy app

gunicorn -w 4 -b 0.0.0.0:5000 app:app

Test backend

  • Lấy alb endpoint

  • Chúng ta hãy vào trình duyệt và test api
http://webapp-alb-376268860.us-east-1.elb.amazonaws.com/api
  • Data trả về thành công

Kiểm tra config cloudfront

Chúng ta cần kiểm tra xem cloudfront đã trỏ tới ALB chưa

  • Lấy domain của cloudfront

  • Chúng ta hãy vào trình duyệt và test api (với domain của cloudfront)
https://d2z4bbmw3ua6rp.cloudfront.net/api
  • Data trả về thành công

Triển khai frontend manual

Build Frontend

  • Vào thư mục frontend
cd app/frontend

# Set api backend & build

 REACT_APP_API_URL=https://d2z4bbmw3ua6rp.cloudfront.net/api npm run build 
  • Updload toàn bộ nội dung trong thư mục build lên s3

  • Test thôi

Hôm nay chúng ta đã hoàn thành phần 2 của seri phần cuối sẽ hướng dẫn các bạn tự động hóa tất cả step . Các bạn hãy đón xem nhé

Tất cả code hoàn chỉnh bao gồm workfow đã được upload trên repo bên dưới các bạn có thể tham khảo trước or đợi phần 3 của mình nhé

Nhá hàng một chút về workflow

Quay lại trang chủ

Bình luận