Mỗi lần tạo VPS mới rồi SSH vào cài package, tạo user, chép SSH key và chỉnh cấu hình thủ công rất dễ dẫn tới sai lệch giữa các máy. cloud-init cho phép bạn khai báo user-data ngay từ lúc khởi tạo máy để hệ thống tự thực hiện các bước bootstrap ở lần boot đầu tiên. Bài viết này hướng dẫn cấu trúc `#cloud-config`, tạo user quản trị, thêm SSH key, cài package, dùng `write_files` và `runcmd`, kiểm tra log và validate schema để biến quá trình dựng VPS thành quy trình có thể lặp lại.
Nếu bạn thường xuyên tạo VPS mới, quy trình thủ công rất dễ lặp lại:
- đăng nhập bằng user mặc định;
- tạo user quản trị;
- thêm SSH key;
- cập nhật package;
- cài
curl,git,htop, Docker hoặc agent; - chỉnh timezone;
- ghi file cấu hình;
- restart service.
Làm một máy thì đơn giản. Làm 20 máy thì rất dễ có máy thiếu package, sai permission hoặc quên một bước.
cloud-init giúp tự động hóa giai đoạn bootstrap. Bạn cung cấp một file user-data, thường bắt đầu bằng:
#cloud-config
Ở lần khởi động đầu tiên, cloud-init đọc metadata/user-data từ nền tảng cloud và thực hiện các module tương ứng.
Nếu bạn đã quản lý nhiều VPS sau khi máy được tạo, xem thêm bài Tự động quản lý nhiều VPS bằng Ansible. Cloud-init phù hợp với bootstrap ban đầu, Ansible phù hợp hơn với configuration management lâu dài.
cloud-init phù hợp khi nào?
Các use case điển hình:
- tạo nhiều VPS giống baseline;
- tự cài SSH key ngay từ lần boot đầu;
- tạo user
admin/ops; - cài package cơ bản;
- cấu hình hostname/timezone;
- ghi file cấu hình;
- bật service;
- bootstrap agent monitoring/logging;
- chuẩn bị máy để Ansible tiếp quản.
cloud-init có chạy mỗi lần reboot không?
Phần lớn module cloud-init có frequency riêng.
Ví dụ runcmd là once-per-instance: nó được cấu hình để thực thi trong giai đoạn final của lần khởi tạo instance, không phải mỗi lần reboot thông thường.
Đây là điểm quan trọng: cloud-init không phải cron hay systemd service để chạy lặp lại mọi boot.
Nếu bạn cần command chạy mỗi boot, hãy dùng systemd unit hoặc cơ chế phù hợp thay vì nhồi vào cloud-init.
File cloud-config tối thiểu
Ví dụ:
#cloud-config
hostname: web-01
package_update: true
packages:
- curl
- git
- htop
Khi nền tảng cloud hỗ trợ user-data đúng cách, cloud-init có thể:
- đặt hostname;
- update package index;
- cài package được liệt kê.
Cloud-init Docs lưu ý nếu packages được khai báo thì package update cũng được thực hiện theo module package tương ứng.
Tạo user quản trị
Ví dụ:
#cloud-config
users:
- default
- name: ops
gecos: Operations User
groups: [sudo]
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
default giữ user mặc định của distro/cloud image.
Nếu bỏ default, bạn có thể vô tình không tạo user mặc định mà nền tảng đang kỳ vọng.
Không nên copy cấu hình sudo không password vào mọi môi trường nếu policy của bạn yêu cầu xác thực nâng quyền.
Thêm SSH public key
users:
- default
- name: ops
groups: [sudo]
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... admin-laptop
Chỉ đặt public key vào user-data.
Không đưa private key vào cloud-init.
Nếu bạn chưa có SSH key, xem bài Dùng SSH key trên VPS Ubuntu.
Có nên tắt password SSH ngay trong cloud-init?
Bạn có thể cấu hình SSH password policy, nhưng với lần đầu triển khai nên cẩn thận.
Quy trình an toàn:
- thêm SSH key;
- tạo user;
- boot instance;
- xác nhận SSH key hoạt động;
- sau đó mới áp baseline cứng hơn.
Nếu bạn tự động tắt password/root SSH nhưng user-data có lỗi key, bạn có thể tự khóa khỏi VPS ngay lần boot đầu.
Luôn bảo đảm nền tảng có console/recovery path.
Cài package
package_update: true
package_upgrade: false
packages:
- curl
- git
- htop
- jq
- ca-certificates
Có nên package_upgrade: true không phụ thuộc use case.
Ưu điểm:
- máy mới nhận security/update mới hơn.
Nhược điểm:
- provisioning chậm hơn;
- package update có thể thay đổi behavior so với image đã test;
- có thể cần reboot.
Với production nghiêm ngặt, nhiều team chọn image/version đã kiểm thử và kiểm soát update bằng pipeline riêng.
Dùng write_files
write_files cho phép cloud-init ghi file với owner/permission.
Ví dụ tạo file baseline:
write_files:
- path: /etc/motd
owner: root:root
permissions: '0644'
content: |
Managed VPS
Configuration is automated.
Ví dụ tạo systemd drop-in/config:
write_files:
- path: /etc/sysctl.d/99-custom.conf
owner: root:root
permissions: '0644'
content: |
vm.swappiness=10
Chỉ thêm kernel/sysctl mà bạn hiểu rõ tác động.
Không ghi file tạm vào /tmp trong cloud-init
Cloud-init Docs cảnh báo tránh dùng /tmp trong các giai đoạn boot vì có thể race với systemd-tmpfiles-clean.
Nếu cần thư mục tạm, ưu tiên:
/run/my-bootstrap
Ví dụ:
runcmd:
- [mkdir, -p, /run/my-bootstrap]
Dùng runcmd
runcmd chạy command theo thứ tự ở final boot stage.
Ví dụ:
runcmd:
- [systemctl, enable, --now, chrony]
- [mkdir, -p, /opt/app]
- [sh, -c, 'echo ready > /var/lib/bootstrap-status']
Cloud-init hỗ trợ item dạng list hoặc string.
Dạng list an toàn hơn khi argument phức tạp vì ít phụ thuộc shell parsing.
Ví dụ:
runcmd:
- [apt-get, install, -y, nginx]
thường dễ kiểm soát hơn:
runcmd:
- "apt-get install -y nginx"
runcmd không nên trở thành script 500 dòng
Nếu logic bootstrap dài:
- lưu script version-controlled ở repository nội bộ;
- tải artifact đã pin checksum/version;
- hoặc tạo image/base image;
- sau đó cloud-init chỉ gọi bước nhỏ để bootstrap.
Một user-data quá lớn khó review, test và rollback.
Ví dụ cloud-config hoàn chỉnh
#cloud-config
hostname: app-01
users:
- default
- name: ops
gecos: Operations
groups: [sudo]
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... admin-laptop
package_update: true
packages:
- curl
- git
- htop
- jq
- ca-certificates
write_files:
- path: /etc/motd
owner: root:root
permissions: '0644'
content: |
Provisioned automatically with cloud-init.
- path: /etc/systemd/system/bootstrap-marker.service
owner: root:root
permissions: '0644'
content: |
[Unit]
Description=Bootstrap marker
[Service]
Type=oneshot
ExecStart=/usr/bin/touch /var/lib/bootstrap-complete
[Install]
WantedBy=multi-user.target
runcmd:
- [systemctl, daemon-reload]
- [systemctl, enable, --now, bootstrap-marker.service]
final_message: "cloud-init finished after $UPTIME seconds"
Địa chỉ SSH key chỉ là placeholder. Hãy thay bằng public key thật của bạn.
Validate cloud-config trước khi dùng
Cloud-init có schema validation.
Trên máy có cloud-init:
cloud-init schema --config-file user-data.yaml
Tùy phiên bản, command/option có thể khác nhẹ; kiểm tra:
cloud-init schema --help
Validation giúp bắt lỗi key/schema trước khi bạn boot instance production.
Nhưng schema pass không có nghĩa command trong runcmd chắc chắn thành công.
Kiểm tra cloud-init sau khi VPS boot
Xem trạng thái:
cloud-init status
Chi tiết:
cloud-init status --long
Chờ hoàn tất trong automation:
cloud-init status --wait
Xem log:
sudo less /var/log/cloud-init.log
sudo less /var/log/cloud-init-output.log
cloud-init-output.log thường chứa stdout/stderr của script/module và rất hữu ích khi runcmd fail.
Kiểm tra dữ liệu cloud-init đã nhận
Một lỗi phổ biến là user-data đúng nhưng cloud provider không truyền vào instance như bạn nghĩ.
Có thể xem metadata/cache dưới:
/var/lib/cloud/
Ví dụ:
sudo find /var/lib/cloud/instance -maxdepth 2 -type f -ls
Cẩn thận: user-data có thể chứa secret nếu bạn đã đưa vào, vì vậy không upload toàn bộ thư mục này công khai khi debug.
Không nên đưa secret dài hạn vào user-data
Tùy cloud platform, user-data có thể được lưu trong metadata, log, API hoặc disk cache.
Không nên nhúng trực tiếp:
- database password lâu dài;
- private key;
- API token quyền cao;
- cloud credential;
- root password plaintext.
Nếu cần bootstrap secret, dùng cơ chế secret manager/short-lived token phù hợp với nền tảng.
Idempotency và lần chạy đầu tiên
Nhiều cloud-init module chạy once-per-instance, nhưng command trong runcmd vẫn nên được viết theo cách an toàn nếu chạy lại ngoài ý muốn.
Ví dụ tốt:
mkdir -p /opt/app
thay vì command fail nếu directory đã tồn tại.
Hoặc:
systemctl enable --now nginx
thường có thể chạy lại an toàn hơn script tự chỉnh file phức tạp.
cloud-init và Ansible khác nhau thế nào?
cloud-init
Phù hợp:
- first boot;
- tạo user;
- SSH key;
- package/bootstrap agent;
- cấu hình nền ban đầu.
Ansible
Phù hợp:
- cập nhật cấu hình về sau;
- rollout nhiều máy;
- roles/templates;
- drift correction;
- playbook có versioning rõ ràng.
Một workflow tốt:
Create VPS
↓
cloud-init bootstrap
↓
VPS online + SSH key
↓
Ansible configure
↓
Application deployment
cloud-init và Terraform
Terraform/IaC có thể tạo VM/network/firewall, còn cloud-init cấu hình guest OS ở first boot.
Ví dụ conceptual:
Terraform tạo VPS
↓
user_data = cloud-config
↓
cloud-init bootstrap Linux
Đây là cách tách rõ infrastructure provisioning và OS initialization.
Bootstrap monitoring agent
Ví dụ bạn muốn mọi VPS mới đều có Node Exporter/Alloy.
Có thể cloud-init:
- cài package;
- ghi config;
- enable service.
Sau đó monitoring server tự discover hoặc được Ansible cập nhật.
Nếu bạn đang dùng observability stack, xem:
Bootstrap firewall có nên làm ngay không?
Có thể, nhưng đây là bước rủi ro cao.
Nếu cloud-init enable UFW và rule SSH sai, máy mới có thể mất remote access ngay lần boot đầu.
Quy trình tốt hơn:
- xác định interface/network;
- add SSH allow rule trước;
- test trên staging image;
- có console/recovery;
- chỉ sau đó đưa firewall vào baseline tự động.
Xem bài Cài UFW Firewall trên VPS Ubuntu.
Bootstrap Docker bằng cloud-init
Bạn có thể dùng cloud-init để cài Docker, nhưng nên lấy package từ repository chính thức hoặc workflow đã test.
Không nên viết:
curl random-url | sudo sh
trong provisioning production mà không pin nguồn/version.
Nếu Docker là thành phần chuẩn của fleet, tốt hơn là:
- repository chính thức;
- package version được kiểm soát;
- hoặc golden image.
Golden image hay cloud-init?
cloud-init
Ưu điểm:
- linh hoạt;
- cùng một image có thể bootstrap theo role khác nhau;
- dễ thay SSH key/config.
Nhược điểm:
- provisioning lâu hơn;
- phụ thuộc package repository/network lúc boot.
Golden image
Ưu điểm:
- boot nhanh;
- package đã preinstalled/tested;
- ít phụ thuộc Internet.
Nhược điểm:
- cần pipeline build image;
- image dễ cũ nếu không rebuild thường xuyên.
Hạ tầng trưởng thành thường kết hợp cả hai: image ổn định + cloud-init nhẹ.
Xử lý lỗi thường gặp
User không được tạo
Kiểm tra:
cloud-init status --long
sudo grep -i user /var/log/cloud-init.log | tail -50
Nhớ rằng nhiều option user không được áp nếu user đã tồn tại từ trước.
SSH key không hoạt động
Kiểm tra:
sudo cat /home/ops/.ssh/authorized_keys
sudo stat /home/ops/.ssh
Và log SSH:
sudo journalctl -u ssh -n 100 --no-pager
Package cài thất bại
Có thể DNS/network chưa sẵn sàng hoặc repository lỗi.
Xem:
sudo grep -i -E 'apt|package|error' /var/log/cloud-init-output.log | tail -100
runcmd không chạy
Kiểm tra YAML và log. runcmd chạy ở final stage, không phải ngay lúc module được parse.
Sai lầm thường gặp
Đưa private key vào user-data
Rủi ro lộ credential nghiêm trọng.
Script quá dài
Khó debug và khó version control.
Dùng /tmp cho file boot tạm
Cloud-init Docs cảnh báo race với cleanup service.
Tắt SSH password/root quá sớm
Nếu key sai, bạn tự khóa khỏi máy.
Không validate schema
Một lỗi indent YAML có thể làm bootstrap fail từ đầu.
Quy trình triển khai an toàn
- viết
user-data.yaml; - validate schema;
- test trên VPS thử nghiệm;
- kiểm tra
cloud-init status --wait; - kiểm tra SSH user/key;
- kiểm tra package/file/service;
- review log;
- version-control cloud-config template;
- mới dùng cho production fleet.
Checklist
- File bắt đầu bằng
#cloud-config. - Có user quản trị riêng.
- Chỉ dùng SSH public key.
- Không nhúng private key/token dài hạn.
- Package list tối thiểu.
-
write_filescó owner/permission đúng. -
runcmdngắn và idempotent. - Không dùng
/tmpcho boot artifact. - Validate schema trước khi deploy.
- Test
cloud-init status --wait. - Có console recovery.
- Lưu template trong Git.
FAQ
cloud-init có chạy lại sau mỗi reboot không?
Phần lớn module bootstrap như runcmd chạy once-per-instance, không phải mọi lần reboot.
cloud-init có thay Ansible không?
Không hoàn toàn. cloud-init phù hợp first boot; Ansible phù hợp quản lý cấu hình liên tục sau đó.
Có thể cài Docker bằng cloud-init không?
Có, nhưng nên dùng repository/package chính thức, pin version khi cần và test trước production.
Có nên đặt password trong cloud-init không?
Không nên dùng plaintext password dài hạn. SSH key hoặc secret bootstrap ngắn hạn an toàn hơn.
Làm sao biết cloud-init đã chạy xong?
Dùng cloud-init status --wait và kiểm tra /var/log/cloud-init.log, /var/log/cloud-init-output.log.
Kết luận
cloud-init giúp biến việc cấu hình VPS mới từ checklist thủ công thành một quá trình có thể lặp lại. Chỉ với một file #cloud-config, bạn có thể tạo user, cài SSH key, package, ghi file và chạy bootstrap command ngay từ first boot.
Hãy giữ user-data nhỏ, không nhúng secret dài hạn, validate schema và luôn test trên một VPS staging trước khi áp cho cả fleet.
Nếu bạn thường xuyên tạo thêm VPS cho staging, worker hoặc ứng dụng production, có thể tham khảo các dịch vụ VPS tại HUONG CLOUD và chuẩn hóa bootstrap bằng cloud-init ngay từ máy đầu tiên.
Nguồn tham khảo
- cloud-init 26.2 — All cloud config examples: https://docs.cloud-init.io/en/latest/reference/examples.html
- cloud-init — Configure users and groups: https://docs.cloud-init.io/en/latest/reference/yaml_examples/user_groups.html
- cloud-init — Writing files: https://docs.cloud-init.io/en/latest/reference/yaml_examples/write_files.html
- cloud-init — Module reference / runcmd: https://docs.cloud-init.io/en/latest/reference/modules.html
Kết luận
Nếu chưa chắc cấu hình hoặc loại dịch vụ nào phù hợp, hãy gửi nhu cầu thực tế để đội ngũ hỗ trợ tư vấn trước khi mua.
Chưa biết nên chọn gói nào?
Xem bảng giá VPS hoặc liên hệ đội ngũ hỗ trợ để chọn cấu hình phù hợp.
Xem bảng giá VPS →