cloud-init:新机器开机即就绪
每次开新机器都要重复一遍「装软件、建用户、加 SSH key、配防火墙」。cloud-init 让这些在开机时就自动完成。
一、它是什么
云厂商在创建实例时,可以传一段「user-data」,系统首次启动时会读取并执行。这就是 cloud-init。
# 在已有机器上确认是否安装
cloud-init --version
systemctl status cloud-init
# 看上次执行的日志
sudo cat /var/log/cloud-init-output.log
cloud-init status主流云平台(阿里云、腾讯云、AWS、Vultr、DigitalOcean)的镜像都预装了。
二、最小可用的 user-data
#cloud-config
hostname: baize-node-1
timezone: Asia/Shanghai
users:
- name: baize
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-ed25519 AAAAC3... your-key-comment
package_update: true
packages:
- curl
- git
- vim
- tmux
- htop
- nginx
ssh_pwauth: false
disable_root: true第一行 #cloud-config 必须写,这是格式标识。
三、常用模块
跑命令
runcmd:
- apt update
- apt install -y fail2ban
- ufw allow 22/tcp
- ufw allow 80/tcp
- ufw allow 443/tcp
- ufw --force enable
- systemctl enable --now nginx
- [ sh, -c, "echo done > /root/init-ok" ]
runcmd每次开机都会跑(不只是首次)。只跑一次用bootcmd(早期)或自己加判断文件。
写文件
write_files:
- path: /etc/nginx/conf.d/baize.conf
permissions: "0644"
owner: root:root
content: |
server {
listen 80;
server_name www.bzii.cn;
root /var/www/baize;
index index.html;
}
- path: /opt/scripts/hello.sh
permissions: "0755"
content: |
#!/usr/bin/env bash
echo "hello from cloud-init"
- path: /root/.bashrc-extra
append: true
content: |
alias ll="ls -alhF"内核参数与 apt 源
write_files:
- path: /etc/sysctl.d/99-tuning.conf
content: |
net.core.somaxconn = 65535
vm.swappiness = 10
apt:
sources:
docker.list:
source: "deb [arch=amd64] https://download.docker.com/linux/debian bookworm stable"
keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88挂载磁盘
disk_setup:
/dev/vdb:
table_type: gpt
layout: true
fs_setup:
- device: /dev/vdb
partition: 1
filesystem: ext4
mounts:
- [ /dev/vdb1, /data, ext4, "defaults,noatime", "0", "2" ]四、一份完整的初始化模板
#cloud-config
hostname: baize-node
timezone: Asia/Shanghai
disable_root: true
ssh_pwauth: false
users:
- name: baize
groups: [sudo, docker]
shell: /bin/bash
sudo: "ALL=(ALL) NOPASSWD:ALL"
lock_passwd: true
ssh_authorized_keys:
- ssh-ed25519 AAAAC3...
package_update: true
package_upgrade: true
packages:
- curl wget git vim tmux htop btop ncdu mtr jq rsync
- ufw fail2ban nginx certbot python3-certbot-nginx
write_files:
- path: /etc/sysctl.d/99-baize.conf
content: |
net.core.somaxconn = 65535
vm.swappiness = 10
runcmd:
- sed -i "s/^#\?Port 22/Port 2222/" /etc/ssh/sshd_config
- sed -i "s/^#\?PermitRootLogin.*/PermitRootLogin no/" /etc/ssh/sshd_config
- sed -i "s/^#\?PasswordAuthentication.*/PasswordAuthentication no/" /etc/ssh/sshd_config
- systemctl reload ssh
- ufw allow 2222/tcp
- ufw allow 80/tcp
- ufw allow 443/tcp
- ufw --force enable
- sysctl --system
- echo "init done" > /var/log/baize-init.ok
final_message: "主机 $hostname 初始化完成,用时 $UPTIME 秒"注意:改 SSH 端口后要记得放行新端口,否则开机后你就连不上了。这是最危险的一步,务必先测。
五、怎么传 user-data
| 平台 | 位置 |
|---|---|
| 阿里云 | 创建实例 → 高级选项 → 用户数据 |
| 腾讯云 | 创建实例 → 高级设置 → 自定义数据 |
| AWS | User data 字段 |
| Vultr / DO | Startup Script / User Data |
部分平台要求 base64 编码:
base64 -w0 user-data.yaml六、调试
这是最容易卡住的地方。看日志:
sudo cat /var/log/cloud-init-output.log # 脚本输出
sudo cat /var/log/cloud-init.log # 详细过程
cloud-init status --long
cloud-init status --wait # 等待完成
# 看配置有没有被正确解析
sudo cloud-init query --all
sudo cat /var/lib/cloud/instance/user-data.txt
# 检查语法(YAML 错误是最常见问题)
cloud-init devel schema --config-file user-data.yaml
# 重新跑一次(调试时很有用)
sudo cloud-init clean --logs
sudo cloud-init init
cloud-init devel schema一定要跑:90% 的失败是 YAML 缩进错误,这个命令能直接指出来。
七、常见坑
| 问题 | 原因 |
|---|---|
| 完全没执行 | 缺 #cloud-config 头 / 平台没传成功 |
| 包没装上 | 网络未就绪就跑了,或包名错 |
| SSH key 没生效 | 用户名写错,或 ssh_pwauth 冲突 |
| 改了 SSH 端口连不上 | 新端口没放行 |
| 命令没跑 | runcmd 缩进错,或用了 shell 特性(它不经过 bash) |
runcmd里的命令不经过 shell,所以>重定向、&&管道都无效。要用的话写成- [ sh, -c, "command > file" ]。
八、cloud-init vs 手动 vs 配置管理工具
| 方案 | 适用 |
|---|---|
| 手动 | 一两台机器,不常开新机 |
| cloud-init | 每次开新机都要同样的初始化 |
| Ansible | 机器多、需要持续管理配置 |
| 打包自定义镜像 | 初始化内容多且耗时 |
我的用法:
- cloud-init 做基础初始化(用户、SSH、防火墙、基础软件);
- 剩下的用一份 shell 脚本或 Ansible 补;
- 如果初始化很长(比如要编译),就做好一次打快照,以后从快照开。
cloud-init 的价值是把「新机器」这件事变得可重复。写一次,以后每台都一样。
