36 TECH

36 TECH 36 TECH fanpage chia sẻ các kiến thức công nghệ

Một chút "thu hoạch" sau hành trình làm Software EngineerTừ viết code đến lúc tự tay làm hệ thống sập... rồi tự cứu lại....
28/07/2026

Một chút "thu hoạch" sau hành trình làm Software Engineer

Từ viết code đến lúc tự tay làm hệ thống sập... rồi tự cứu lại.

---

Trước đây, mình từng nghĩ công việc của một Software Engineer khá đơn giản:

- Nhận yêu cầu.
- Viết code.
- Chạy được tính năng.
- Push lên GitHub.
- Done.

Nhưng sau một khoảng thời gian trực tiếp xây dựng một hệ thống tương đối đầy đủ, mình mới nhận ra:

«Viết được code chỉ là bước đầu. Làm sao để đoạn code đó chạy ổn định trong một hệ thống thật mới là phần khó nhất.»

---

Hệ thống mình làm gồm khá nhiều thành phần

- Frontend
- Backend API
- Chat Service
- PostgreSQL
- Redis
- DynamoDB
- SQS
- Docker
- Kubernetes
- GitHub Actions
- AWS

Nghe thì khá "hoành tráng".

Nhưng thực tế triển khai lại là chuỗi ngày...

«"Ủa, sao nó không chạy?"»

«"Ủa, nãy còn chạy mà?"»

«"Ủa, trên máy mình chạy được mà?"»

Và câu quen thuộc nhất:

«"Code không đổi, sao hệ thống lại lỗi?"»

---

1. Production khác Local rất nhiều

Ở local, mọi thứ khá dễ.

- Database chạy trong Docker Compose.
- Redis cũng ở đó.
- Service gọi nhau bằng tên container.
- Có lỗi thì restart rồi xem log.

Nhưng khi lên Kubernetes và AWS...

Mọi thứ thay đổi hoàn toàn.

Một service muốn gọi service khác phải đúng:

- DNS
- Namespace
- Port
- Network Policy

Pod muốn kết nối database phải có:

- Connection String đúng
- Security Group đúng
- Route đúng

Secret cũng không thể để thẳng trong file YAML rồi push lên Git.

Lúc đó mình mới hiểu:

«Ứng dụng chạy được không chỉ phụ thuộc vào code, mà còn phụ thuộc vào toàn bộ môi trường xung quanh nó.»

---

2. Docker dạy mình rằng...

«"Máy mình chạy được" không còn là lý do hợp lệ.»

Ban đầu Docker tưởng rất đơn giản.

- Viết Dockerfile
- Build Image
- Run Container

Nhưng rồi đủ loại lỗi xuất hiện:

- Container chạy nhưng Health Check fail
- App khởi động trước Database
- Trùng Port
- Mount thiếu Volume
- Environment Variable không nhận
- Docker Desktop và WSL không kết nối

Có hôm chạy Smoke Test thì nhận ngay:

«The command 'docker' could not be found in this WSL 2 distro.»

Code không sai.

Script cũng không sai.

Sai ở... môi trường.

Sau đó mình bắt đầu có thêm một checklist mới:

- Code đang chạy ở đâu?
- Container nhận biến môi trường nào?
- Service phía dưới đã sẵn sàng chưa?
- Network có hoạt động không?

---

3. CI/CD dạy mình rằng...

Automation chỉ hữu ích khi mình hiểu nó đang tự động hóa điều gì.

Lúc đầu nhìn GitHub Actions tự động:

- Lint
- Test
- Build
- Scan
- Push ECR
- Deploy

Cảm giác rất "xịn".

Đến khi Workflow đỏ...

Mới biết còn nhiều thứ phía sau.

Có lần fail chỉ vì:

- ShellCheck Warning
- Windows Smart App Control chặn Python
- File ".env" có BOM
- Smoke Test fail
- Branch không đúng điều kiện
- Secret thiếu

Lúc đó mới hiểu:

«CI/CD không phải một file YAML thần kỳ.»

Nó là tập hợp của rất nhiều giả định.

Chỉ cần một giả định sai...

Cả Pipeline sẽ dừng.

---

4. IAM dạy mình rằng...

Có chữ Allow chưa chắc đã được phép.

Mình từng gặp:

- AccessDenied
- implicitDeny
- iam:PassRole denied
- ecr:GetAuthorizationToken denied
- AssumeRoleWithWebIdentity failed

Role có.

Policy có.

ARN nhìn cũng đúng.

Nhưng AWS vẫn từ chối.

Sau đó mới biết còn có:

- Trust Policy
- Principal
- Condition
- GitHub Repository
- Branch
- Resource "*"

Từ đó mình bỏ thói quen đoán quyền.

Thay vào đó dùng:

aws iam simulate-principal-policy

Để kiểm tra từng Action.

Một bài học rất đáng giá:

«Đừng đoán quyền. Hãy kiểm chứng quyền.»

---

5. Kubernetes dạy mình rằng...

Pod Running chưa chắc ứng dụng đang khỏe.

Mình từng gặp:

- Pod Running nhưng API chết
- Pod Running nhưng Redis không kết nối
- Pod Running nhưng Migration fail
- Pod Running nhưng không ra Internet
- Pod Running nhưng Health Check đỏ

Kubernetes chỉ nói:

«Container còn tồn tại.»

Còn ứng dụng có hoạt động đúng không...

Mình vẫn phải kiểm tra:

- Log
- Readiness Probe
- Liveness Probe
- Service
- Ingress
- Dependency

---

6. NAT Gateway

Đây là lần mình "tiết kiệm" đáng nhớ nhất.

Nghĩ rằng:

«"Ít người dùng, tắt NAT Gateway chắc không sao."»

Sau đó...

- Pod không ra Internet
- API lỗi
- Deploy fail
- GitHub Actions lỗi
- Private Subnet gần như bị cô lập

Kiểm tra Code.

Không lỗi.

Kiểm tra IAM.

Không lỗi.

Cuối cùng...

Mới nhớ chính mình vừa tắt NAT.

Bật lại.

Mọi thứ hoạt động bình thường.

Sau lần đó mình hiểu thật sự:

- Public Subnet
- Private Subnet
- NAT Gateway
- Internet Gateway
- Route Table
- VPC Endpoint

---

7. Database

Database dạy mình rằng:

«Dữ liệu không thể xử lý bằng niềm tin.»

Ví dụ:

- Check tồn tại
- Chưa có thì Insert

Ở Local rất ổn.

Nhưng khi có hai Request cùng lúc...

Cả hai đều thấy dữ liệu chưa tồn tại.

Kết quả:

Insert trùng.

Từ đó mình bắt đầu chú ý hơn tới:

- Unique Constraint
- Transaction
- Rollback
- HTTP 409 Conflict
- Optimistic Locking
- Idempotency Key

---

8. Message Queue

Một bài học mình rất thích.

Nếu:

- Commit Database xong
- Gửi SQS thất bại

=> Event mất.

Nếu:

- Gửi SQS trước
- Database Rollback

=> Event sai.

Sau đó mình tìm hiểu Outbox Pattern.

Event được ghi cùng Transaction.

Dispatcher gửi sau.

Nếu fail thì Retry.

Consumer Deduplicate.

Người dùng không nhìn thấy.

Nhưng hệ thống đáng tin cậy hơn rất nhiều.

---

9. AWS

AWS dạy mình rằng:

Tạo được Resource...

Không có nghĩa là xong.

Ví dụ:

RDS vừa tạo chỉ mới ở trạng thái:

«creating»

Sau đó còn phải:

- available
- Endpoint
- Security Group
- Secret
- Connection

EKS, ECR, SageMaker...

Đều như vậy.

Mình bắt đầu phân biệt rõ:

- Resource được khai báo
- Resource đang tạo
- Resource sẵn sàng
- Ứng dụng kết nối được
- Health Check thành công

Đó là 5 trạng thái hoàn toàn khác nhau.

---

Điều mình học được nhiều nhất

Không phải Docker.

Không phải Kubernetes.

Không phải AWS.

Cũng không phải Terraform.

Mà là...

Cách xử lý vấn đề có hệ thống.

Khi hệ thống lỗi, mình không còn sửa ngẫu nhiên nữa.

Mình chia nhỏ:

- Lỗi ở Application hay Infrastructure?
- Trước hay sau Deploy?
- Container có chạy không?
- DNS có Resolve không?
- Network có Route không?
- IAM có quyền không?
- Dependency đã sẵn sàng chưa?
- Log cuối cùng thành công là gì?

Mình tìm điểm gãy đầu tiên.

Không phải triệu chứng cuối cùng.

---

Nhìn lại

Trước đây...

Hệ thống lỗi.

Mình nghĩ:

«"Chắc code sai."»

Bây giờ mình biết...

Lỗi có thể nằm ở:

- Code
- Config
- Environment Variable
- Container
- Network
- Database
- Permission
- Infrastructure
- CI/CD

Hoặc...

Chỉ đơn giản là một Service chưa sẵn sàng.

---

Ngày trước mình chỉ quan tâm:

«Feature chạy được.»

Bây giờ mình quan tâm thêm:

- Nếu request gửi hai lần thì sao?
- Nếu database commit nhưng queue lỗi thì sao?
- Nếu Pod Restart thì sao?
- Nếu Deploy thất bại thì Rollback thế nào?
- Nếu Service ngoài chết thì hệ thống phản ứng ra sao?

Có lẽ...

Đó là lúc mình bắt đầu hiểu hơn về công việc của một Software Engineer.

Không chỉ là người viết code.

Mà là người chịu trách nhiệm cho cách một hệ thống vận hành, thất bại và phục hồi.

---

Tổng kết

Viết code giúp mình tạo ra tính năng.

Debug giúp mình hiểu hệ thống.

Triển khai giúp mình hiểu hạ tầng.

Sự cố giúp mình hiểu những phần trước đây chỉ đọc trên tài liệu.

Và những lần tự tay làm hệ thống lỗi...

Lại là những lần giúp mình nhớ kiến thức lâu nhất.

Đến hiện tại, hệ thống vẫn chưa hoàn hảo.

Vẫn còn nhiều thứ phải tối ưu.

Nhưng sau mỗi lần:

- Workflow đỏ
- Pod Crash
- IAM từ chối
- Migration Fail
- Network mất kết nối

Mình không còn quá hoang mang.

Mình bắt đầu biết nên kiểm tra từ đâu.

Và có lẽ...

Đó là "thu hoạch" lớn nhất của mình sau hành trình này.

«Một Software Engineer không phải là người không bao giờ làm hệ thống lỗi.»

«Mà là người sau mỗi lần hệ thống lỗi đều hiểu nó hơn, sửa nó tốt hơn và thiết kế để lỗi đó khó lặp lại hơn.»

---

Còn hiện tại...

Pipeline đã xanh.

Pod đã Running.

API trả về 200 OK.

Database kết nối bình thường.

Thì...

Mình xin phép chưa đụng vào Production thêm một lúc. 😄

Vì đôi khi...

Cách tốt nhất để giữ Production ổn định là đừng "tối ưu thêm một chút" vào cuối ngày.

"No space left on device" nhưng ổ cứng vẫn còn 40% dung lượng? Đây là một trong những lỗi Linux dễ gây hiểu lầm nhấtMột ...
09/06/2026

"No space left on device" nhưng ổ cứng vẫn còn 40% dung lượng? Đây là một trong những lỗi Linux dễ gây hiểu lầm nhất
Một trong những tình huống khiến nhiều DevOps, SysAdmin và SRE đau đầu là nhìn thấy ứng dụng báo lỗi:
No space left on device
Phản ứng đầu tiên của hầu hết mọi người thường là:
df -h
Kết quả:
Filesystem Size Used Avail Use%
/dev/xvda1 100G 60G 40G 60%
Ổ đĩa vẫn còn hàng chục GB trống.
Nhưng ứng dụng vẫn không ghi được dữ liệu.
Log vẫn lỗi.
Container vẫn crash.
Database vẫn báo "disk full".
Vậy chuyện gì đang xảy ra?

Không phải lúc nào "No space left on device" cũng có nghĩa là hết dung lượng
Đây là một trong những hiểu lầm phổ biến nhất khi vận hành Linux.
Thông báo này thực chất có thể xuất phát từ nhiều nguyên nhân khác nhau.
Trong một số trường hợp:

Disk vẫn còn trống nhưng hệ điều hành không thể tạo thêm file mới.
Nghe có vẻ vô lý nhưng hoàn toàn có thể xảy ra.

Thủ phạm phổ biến: Hết inode
Linux quản lý file thông qua inode.
Mỗi file hoặc thư mục trên hệ thống đều cần một inode.
Khi tạo file mới:
Cần dung lượng lưu dữ liệu
Cần một inode để lưu metadata
Nếu inode bị sử dụng hết:
No space left on device
sẽ xuất hiện mặc dù ổ cứng vẫn còn rất nhiều GB trống.
Kiểm tra bằng:
df -i
Ví dụ:
Filesystem Inodes IUsed IFree IUse%
/dev/xvda1 6.5M 6.5M 0 100%
Lúc này vấn đề không nằm ở disk space.
Mà nằm ở inode.

Hệ thống log sinh file quá nhiều
Đây là nguyên nhân rất thường gặp.
Ví dụ:
logs/
├── app-1.log
├── app-2.log
├── app-3.log..
├── app-500000.log
Tổng dung lượng có thể chỉ vài GB.
Nhưng số lượng file quá lớn.
Kết quả:
Inode bị sử dụng hết
Không tạo được file mới
Application bắt đầu lỗi

Docker cũng là "nghi phạm quen thuộc"
Nhiều môi trường Docker hoặc Kubernetes gặp tình trạng:
Container logs
Container layers
Image cache
Temporary files
tăng dần theo thời gian.
Đặc biệt trên những host chạy lâu năm.
Một số trường hợp:
docker system df
cho thấy dung lượng vẫn ổn.
Nhưng số lượng file bên trong overlay filesystem lại cực lớn.

File đã bị xóa nhưng dung lượng vẫn không được giải phóng
Đây là lỗi kinh điển trong production.
Ví dụ:
rm huge.log
Bạn nghĩ đã giải phóng được 50 GB.
Nhưng:
df -h
vẫn không thay đổi.
Nguyên nhân là process nào đó vẫn đang giữ file descriptor.
Linux chỉ thực sự giải phóng dung lượng khi:
File bị xóa
Và process không còn sử dụng file đó
Kiểm tra:
lsof | grep deleted
Nhiều trường hợp chỉ cần restart service là lấy lại hàng chục GB dung lượng.

Tmpfs hoặc bộ nhớ tạm bị đầy
Một số ứng dụng ghi dữ liệu vào:
/tmp
/dev/shm
run
tmpfs
Khi các vùng này đầy, ứng dụng cũng có thể báo:
No space left on device
dù ổ cứng vật lý còn rất nhiều dung lượng.
Kiểm tra:
df -h
và chú ý các filesystem dạng:
tmpfs
shm

Kubernetes cũng có thể gặp lỗi tương tự
Trong Kubernetes, lỗi này thường xuất hiện khi:
EmptyDir đầy
Ephemeral Storage đầy
Container logs tăng quá nhanh
Node Disk Pressure
Kết quả:
Pod restart liên tục
Eviction xảy ra
Scheduling thất bại
Trong khi người vận hành vẫn thấy node còn nhiều dung lượng.

Cách tiếp cận khi gặp lỗi
Thay vì chỉ chạy:
df -h
hãy kiểm tra theo checklist:
Bước 1
df -h
Xem dung lượng thực tế.
Bước 2
df -i
Kiểm tra inode.
Bước 3
lsof | grep deleted
Tìm file đã bị xóa nhưng vẫn đang mở.
Bước 4
du -sh /*
Xác định thư mục chiếm nhiều dung lượng.
Bước 5
Kiểm tra:
Docker
Kubernetes
tmpfs
application logs

Bài học rút ra
Một trong những sai lầm phổ biến của người mới làm Linux là nghĩ rằng:

"Disk còn trống thì chắc chắn không thể bị lỗi hết chỗ."
Thực tế hệ điều hành phức tạp hơn nhiều.
Dung lượng lưu trữ chỉ là một phần của câu chuyện.
Inode, file descriptor, tmpfs, container storage hay log management đều có thể trở thành nguyên nhân khiến hệ thống báo:
No space left on device
trong khi ổ cứng vẫn còn hàng chục GB trống.

Kết luận
Khi gặp lỗi:
No space left on device
đừng vội kết luận rằng server đã hết dung lượng.
Hãy nhớ rằng:
Disk Space ≠ Khả năng tạo file mới.
Nhiều sự cố production kéo dài hàng giờ chỉ vì mọi người chăm chăm nhìn vào df -h mà quên kiểm tra inode, file descriptor hoặc container storage.
Đôi khi nguyên nhân không nằm ở việc hết dung lượng.
Mà nằm ở cách Linux quản lý tài nguyên bên dưới.

Nguồn:https://devops.vn/

Address

Thanh Hóa

Alerts

Be the first to know and let us send you an email when 36 TECH posts news and promotions. Your email address will not be used for any other purpose, and you can unsubscribe at any time.

Shortcuts

Share