Chapter 1 Lab — Startup
Kiến trúc cuối cùng
Internet
│
│ HTTP :80
▼
┌───────────────┐
│ Azure VM │
│ Ubuntu │
│ │
│ Nginx │
│ │ │
│ ▼ │
│ Frontend │
│ :3000 │
│ │
│ Backend │
│ :8080 │
│ │
│ PostgreSQL │
│ :5432 │
└───────────────┘
STEP 0 — Chuẩn bị
Bạn cần:
- Azure account
- GitHub account
- Mac của bạn
- SSH
- Git
Kiểm tra local:
git --version
ssh -V
Nếu cả hai chạy được → OK.
STEP 1 — Tạo Resource Group
Vào:
Azure Portal → Resource groups → Create
Chọn:
Resource group name:
devops-chapter1-rg
Region:
Japan East
Ví dụ:

STEP 2 — Tạo Azure VM
Vào:
Azure Portal → Virtual machines → Create → Azure virtual machine
Chọn:
Subscription:
Your subscription
Resource group:
devops-chapter1-rg
Virtual machine name:
chapter1-vm
Region:
Japan East
Image:
Ubuntu Server 24.04 LTS
Size:
nhỏ và rẻ nhất
Authentication type:
SSH public key
Username:
azureuser
Nếu chưa có SSH key:
Generate new key pair
Key pair name:
chapter1-key
Inbound port rules:
HTTP (80), SSH (22)
Ví dụ:
![]() |
![]() |
![]() |
Bạn không cần VM mạnh cho lab này.
Quan trọng: tải private key về máy.
Ví dụ:
chapter1-key.pem
STEP 3 — Lấy Public IP
Sau khi VM chạy:
Azure Portal
→ Virtual Machine
→ chapter1-vm
Tìm:
Public IP address
Ví dụ:
20.XXX.XXX.XXX
Gọi nó là:
VM_IP=20.XXX.XXX.XXX
STEP 4 — SSH vào VM
Trên Mac:
chmod 400 ./chapter1-key.pem
Sau đó:
ssh -i ./chapter1-key.pem azureuser@VM_IP
Ví dụ:
ssh -i ./chapter1-key.pem azureuser@20.XXX.XXX.XXX
Nếu thành công:
Welcome to Ubuntu...
azureuser@chapter1-vm:~$
🎯 Checkpoint 1
Bạn đã vào được Production server.
STEP 5 — Kiểm tra server
Chạy:
uname -a
lsb_release -a
free -h
df -h
Kiểm tra IP:
ip addr
STEP 6 — Update Ubuntu
sudo apt update
sudo apt upgrade -y
STEP 7 — Cài Git
sudo apt install git -y
Kiểm tra:
git --version
Ví dụ:
git version 2.x.x
STEP 8 — Cài Docker
Cách đơn giản cho lab:
sudo apt install docker.io -y
Khởi động:
sudo systemctl enable docker
sudo systemctl start docker
Kiểm tra:
sudo systemctl status docker
STEP 9 — Cho user chạy Docker không cần sudo
sudo usermod -aG docker $USER
Sau đó:
exit
SSH lại:
ssh -i ./chapter1-key.pem azureuser@VM_IP
Kiểm tra:
docker ps
Nếu không còn lỗi permission:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
STEP 10 — Kiểm tra Docker Compose
docker compose version
Nếu ra version:
Docker Compose version v2.x.x
→ OK.
Nếu chưa có, hãy cài đặt theo các bước sau:
1. Thêm Docker repository
sudo apt update
sudo apt install ca-certificates curl -y
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
2. Cài Compose V2
sudo apt update
sudo apt install docker-compose-plugin -y
3. Kiểm tra
docker compose version
STEP 11 — Test Docker
docker run hello-world
Nếu thấy:
Hello from Docker!
→ Docker hoạt động.
STEP 12 — Clone application lên VM
Trên VM:
git clone https://github.com/dockersamples/todo-list-app
cd todo-list-app
Kiểm tra:
ls
STEP 13 — Production Deployment lần đầu
Chạy:
docker compose up -d --build
Docker sẽ build/pull các thành phần cần thiết và start:
Azure VM
│
▼
Docker Compose
│
├── app
│
└── mysql
Kiểm tra:
docker compose ps
STEP 14 — Test từ bên trong VM
Trên VM:
curl http://localhost:3000
Nếu nhận được response:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1.0, user-scalable=0" />
<link rel="stylesheet" href="css/bootstrap.min.css" crossorigin="anonymous" />
<link rel="stylesheet" href="css/font-awesome/all.min.css" crossorigin="anonymous" />
<link href="https://fonts.googleapis.com/css?family=Lato&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css" />
<title>Todo App</title>
</head>
<body>
<div id="root"></div>
<script src="js/react.production.min.js"></script>
<script src="js/react-dom.production.min.js"></script>
<script src="js/react-bootstrap.js"></script>
<script src="js/babel.min.js"></script>
<script type="text/babel" src="js/app.js"></script>
</body>
</html>
thì deployment đã thành công.
Nhưng vẫn còn một vấn đề.
Internet không truy cập được VM:
Internet
│
X
│
Azure VM
│
:3000
STEP 15 — Tại sao không mở port 3000?
Một cách đơn giản là mở:
VM Public IP:3000
Nhưng chúng ta không muốn expose trực tiếp Node.js application.
Thay vào đó:
Internet
│
:80
│
▼
Nginx
│
:3000
│
▼
Node.js
Lý do:
- 🔒 Bảo mật: Không expose trực tiếp port 3000 ra Internet.
- 🚦 Reverse proxy: Nginx nhận request rồi chuyển vào Node.js.
- 🔐 HTTPS: Nginx dễ xử lý SSL/TLS.
- ⚡ Performance: Nginx xử lý static files, connection tốt hơn Node.js.
- 🔄 Scale: Sau này có thể proxy đến nhiều Node.js instances.
STEP 15 — Cài Nginx
Trên VM:
sudo apt install nginx -y
Kiểm tra:
sudo systemctl status nginx
STEP 16 — Cấu hình Reverse Proxy
Tạo configuration:
sudo nano /etc/nginx/sites-available/todo-app
Nội dung:
server {
listen 80;
server_name _;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Enable:
sudo ln -s \
/etc/nginx/sites-available/todo-app \
/etc/nginx/sites-enabled/todo-app
Test:
sudo nginx -t
Nếu:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
restart:
sudo systemctl restart nginx
STEP 17 — Mở HTTP trên Azure
Trong Azure NSG chỉ cần cho phép:
22 SSH
80 HTTP
Không cần expose:
3000
3306
Architecture:
Internet
│
│ :80
▼
┌─────────────┐
│ Nginx │
└──────┬──────┘
│
│ localhost:3000
▼
┌─────────────┐
│ Node.js App │
└──────┬──────┘
│
│ Docker Network
▼
┌─────────────┐
│ MySQL │
└─────────────┘
STEP 18 — Deployment đầu tiên hoàn thành
Mở:
http://<VM_PUBLIC_IP>
Kết quả:

Bạn đã đưa Todo application lên Internet.
🎉 First Deployment!
STEP 19 — Production lúc này trông như thế nào?
Internet
│
│ HTTP :80
▼
┌───────────────┐
│ Azure VM │
│ Ubuntu │
│ │
│ Nginx │
│ │ │
│ ▼ │
│ Node.js │
│ :3000 │
│ │ │
│ ▼ │
│ MySQL │
│ :3306 │
│ │
│ Docker Compose│
└───────────────┘
Đây là một architecture hoàn toàn hợp lý cho một startup rất nhỏ.
STEP 20 — Nhưng deployment vẫn đang rất thủ công
Giả sử developer sửa code.
Developer
│
▼
GitHub
Code mới đã được push.
Nhưng production vẫn đang chạy version cũ.
DevOps Engineer phải:
ssh azureuser@<VM_IP>
Sau đó:
cd todo-list-app
git pull
docker compose build
docker compose up -d
STEP 21 — Thử deploy version mới
Bây giờ chúng ta mô phỏng một thay đổi của developer.
Sửa một file trong project.
Ví dụ thay đổi giao diện Todo App:
Todo App
thành:
Todo App - Production
Commit:
git add .
git commit -m "Update application title"
Push:
git push
STEP 22 — Production chưa thay đổi
Refresh:
http://<VM_PUBLIC_IP>
Bạn vẫn thấy version cũ.
Bởi vì:
GitHub
│
│ NEW CODE
▼
Production
│
└── OLD CODE
STEP 23 — Manual Deploy version mới
SSH vào VM:
ssh -i ./chapter1-key.pem azureuser@VM_IP
Đi vào project:
cd ~/todo-list-app
Pull:
git pull
Build:
docker compose build
Deploy:
docker compose up -d
Kiểm tra:
docker compose ps
Refresh browser.
Version mới xuất hiện.
Và đây chính là vấn đề của Startup
Ban đầu:
1 developer
1 deployment
Manual deployment hoàn toàn ổn.
Nhưng startup phát triển.
3 developers
↓
10 developers
↓
20 developers
Số lần deployment cũng tăng:
1 deploy/day
↓
5 deploy/day
↓
20 deploy/day
Quy trình:
git pull
docker build
docker compose up
bắt đầu trở thành bottleneck.
Những vấn đề bắt đầu xuất hiện
Human Error
Developer có thể quên:
git pull
hoặc:
docker compose build
Deployment không nhất quán
Developer A:
docker compose up -d
Developer B:
docker compose down
docker compose up -d --build
Developer C:
docker compose build
docker compose restart
Không có một quy trình chuẩn.
Không có automated test
Developer có thể:
Code
↓
Push
↓
Deploy
mà không chạy:
Unit Test
Integration Test
Rollback khó
Version:
v1
↓
v2
↓
v3
v3 lỗi.
Muốn quay lại v2 phải tự xử lý:
git checkout
docker build
docker compose
Startup bắt đầu có một câu hỏi mới
Ban đầu chúng ta hỏi:
"Làm thế nào chạy application?"
Docker Compose đã giải quyết vấn đề đó.
Bây giờ problem đã thay đổi:
"Làm thế nào deploy application một cách nhanh, nhất quán và tự động?"
Đây là một problem hoàn toàn khác.
Architecture bắt đầu tiến hóa
Phase 1:
Developer
│
▼
GitHub
│
│ SSH
▼
Azure VM
│
▼
Docker Compose
Phase tiếp theo:
Developer
│
▼
GitHub
│
▼
CI/CD
│
├── Build
├── Test
└── Deploy
│
▼
Azure VM
│
▼
Docker Compose
Đó chính là lý do Chapter 2 xuất hiện.
Điều chúng ta vừa xây dựng
Sau Chapter 1:
┌──────────────┐
│ GitHub │
└──────┬───────┘
│
git pull
│
▼
┌─────────────────┐
│ Azure VM │
│ │
│ Nginx │
│ │ │
│ ▼ │
│ Node.js App │
│ │ │
│ ▼ │
│ MySQL │
│ │
│ Docker Compose │
└─────────────────┘
Các thành phần:
Cloud
└── Azure VM
OS
└── Ubuntu
Container Runtime
└── Docker
Container Orchestration
└── Docker Compose
Application
└── Node.js Todo App
Database
└── MySQL
Reverse Proxy
└── Nginx
Source Code
└── GitHub
Những gì bạn cần nhớ sau Chapter 1
Không cần nhớ hàng chục Docker command.
Chỉ cần nhớ flow:
Code
↓
GitHub
↓
Cloud VM
↓
Docker
↓
Docker Compose
↓
Nginx
↓
Internet
Và quan trọng hơn:
Problem
│
▼
Solution phù hợp
Startup nhỏ:
Problem:
Chạy application
Solution:
Cloud VM + Docker Compose
Startup lớn hơn:
Problem:
Manual deployment
Solution:
CI/CD
Sau này lớn hơn nữa:
Problem:
Scale + reliability + many services
Solution:
Kubernetes
Đừng bắt đầu bằng Kubernetes chỉ vì Kubernetes "xịn".
Hãy bắt đầu bằng problem.
Chapter 1 Checklist
Kết thúc lab, bạn cần tự làm được:
□ Tạo Azure VM
□ SSH vào VM
□ Cài Docker
□ Cài Docker Compose
□ Clone repository trên VM
□ Deploy application
□ Cài Nginx
□ Reverse proxy :80 → :3000
□ Truy cập application qua Public IP
□ Thay đổi code
□ git push
□ SSH vào VM
□ git pull
□ docker compose build
□ docker compose up
□ Deploy version mới
Nếu hoàn thành checklist này, bạn đã thực sự hoàn thành Chapter 1 — Startup.
Và quan trọng nhất
Bạn chưa cần học:
❌ Kubernetes
❌ Helm
❌ ArgoCD
❌ Harbor
❌ Prometheus
❌ GitOps
❌ Terraform
Chúng ta cố tình chưa dùng chúng.
Bởi vì ở thời điểm này, chưa có problem nào cần chúng.
Đó chính là tư duy DevOps mà Chapter 1 muốn xây dựng.
All Rights Reserved


