0

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ụ:

Screenshot 2026-09-09 at 14.11.17.png

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ụ:

Screenshot 2026-09-09 at 14.14.39.png Screenshot 2026-09-09 at 14.14.56.png Screenshot 2026-09-09 at 14.15.39.png

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ả:

Screenshot 2026-09-09 at 15.35.12.png

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

Viblo
Let's register a Viblo Account to get more interesting posts.