1096 字
5 分钟
Cloud-init ESXi 尝试
我这里使用ESXI 7.0.3 和 jammy-server-cloudimg-amd64.ova 进行尝试cloud-init
使用ova创建虚拟机 ,先不要开机,在编辑->高级 中添加cloud-init的base64参数,保存后开机
- guestinfo.userdata
- guestinfo.userdata.encoding=“base64”
- guestinfo.metadata
- guestinfo.metadata.encoding=“base64”
python生成配置脚本
import uuidimport base64import shutilimport subprocessfrom pathlib import Pathfrom textwrap import dedentfrom pyfiglet import Figlet
# --- 配置区 ---OUTPUT_BASE_DIR = Path("./cloudinit_output")HOSTNAMES = [ "ubuntu-master", "ubuntu-worker01", "ubuntu-worker02"]# --- 配置区结束 ---
def generate_ssh_keypair(key_path: Path): key_path.parent.mkdir(parents=True, exist_ok=True) if key_path.exists(): print(f"密钥文件已存在,跳过生成:{key_path}") return print(f"生成新的 SSH 密钥对: {key_path}") try: subprocess.run( ["ssh-keygen", "-t", "rsa", "-b", "4096", "-f", str(key_path), "-N", "", "-q"], check=True ) print(f"密钥对已生成: {key_path} 和 {key_path.with_suffix('.pub')}") except (FileNotFoundError, subprocess.CalledProcessError) as e: print(f"[X] ssh-keygen 命令执行失败: {e}") print("[i] 请确保您的系统中安装了 OpenSSH 客户端,并且 'ssh-keygen' 在系统 PATH 中。") raise
def get_ssh_pubkey(pubkey_path: Path) -> str: if not pubkey_path.exists(): raise FileNotFoundError(f"公钥文件不存在: {pubkey_path}") return pubkey_path.read_text(encoding="utf-8").strip()
def manual_input_pubkey() -> str: while True: print("\n请输入 SSH 公钥(单行格式,如 ssh-rsa AAAA...),输入完成后按回车:") key = input("公钥: ").strip() if key.startswith("ssh-rsa") or key.startswith("ssh-ed25519"): return key print("输入无效。公钥通常以 'ssh-rsa' 或 'ssh-ed25519' 开头。请重试。")
""" #cloud-config hostname: {hostname} fqdn: {hostname}.local manage_etc_hosts: true
# 用户和认证设置 disable_root: false ssh_pwauth: false
users: - name: root # 关键修正:'ssh_authorized_keys' 必须与 'name' 对齐 ssh_authorized_keys: - {ssh_key}
# (可选) 设置 root 密码 # chpasswd: # list: | # root:password123 # expire: false
# 启动后执行的命令 runcmd: # 1. 替换 APT 软件源为清华大学镜像 (推荐保留) - sed -i 's@https?://[^/]+/ubuntu/@https://mirrors.tuna.tsinghua.edu.cn/ubuntu/@g' /etc/apt/sources.list
# 2. 增强 SSH 安全性,禁止 root 密码登录 - sed -i 's/^#?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
# 3. 重启 SSH 服务使配置生效 - systemctl restart sshd
# 4. 更新系统 - apt-get update - apt-get upgrade -y """def generate_user_data(hostname: str, ssh_key: str) -> str: return dedent(f"""\ #cloud-config hostname: {hostname} fqdn: {hostname}.local manage_etc_hosts: true disable_root: false ssh_pwauth: false users: - name: root ssh_authorized_keys: - {ssh_key} runcmd: - sed -i 's@https?://[^/]+/ubuntu/@https://mirrors.tuna.tsinghua.edu.cn/ubuntu/@g' /etc/apt/sources.list - sed -i 's/^#?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config - systemctl restart sshd - apt-get update - apt-get upgrade -y """)
def generate_meta_data(hostname: str) -> str: instance_id = f"{hostname}-{uuid.uuid4().hex[:8]}" return dedent(f"""\ instance-id: {instance_id} local-hostname: {hostname} """)
def create_cloud_init_iso(hostname: str, source_dir: Path): command_name = "genisoimage" command_path = shutil.which(command_name) if not command_path: command_name = "mkisofs" command_path = shutil.which(command_name)
if not command_path: print(f"[!] 警告: 未找到 'genisoimage' 或 'mkisofs' 命令,无法为 {hostname} 创建 seed.iso。") print(" - 在 Debian/Ubuntu 上, 请运行: sudo apt-get install -y cloud-image-utils") print(" - 在 CentOS/RHEL/Fedora 上, 请运行: sudo yum install -y genisoimage") print(" - 在 Windows 上, 您可以安装 cdrtools 或手动创建 ISO。") return
iso_path = source_dir / "seed.iso" print(f"正在为 {hostname} 创建 cloud-init ISO: {iso_path} (使用 {command_name})")
try: subprocess.run( [ command_path, "-output", str(iso_path), "-volid", "cidata", "-joliet", "-rock", str(source_dir) ], check=True, capture_output=True ) print(f"[√] 成功创建 {iso_path}") except subprocess.CalledProcessError as e: print(f"[X] 使用 {command_name} 创建 ISO 失败:") print(f" 错误码: {e.returncode}") print(f" 错误输出: {e.stderr.decode(errors='ignore')}")
def main(): print("--- Cloud-Init 配置生成器 ---") ssh_pub_key = "" while True: choice = input( "请选择 SSH 公key获取方式:\n1) 手动输入公钥\n2) 自动生成/使用现有密钥对\n请输入 1 或 2: ").strip() if choice in ["1", "2"]: break print("无效选择,请重试。") try: if choice == "1": ssh_pub_key = manual_input_pubkey() else: priv_key_path = OUTPUT_BASE_DIR / "auth" / "id_rsa" generate_ssh_keypair(priv_key_path) ssh_pub_key = get_ssh_pubkey(priv_key_path.with_suffix(".pub")) print(f"\n[i] 将使用以下公钥(位于 {priv_key_path.with_suffix('.pub')}):") print(ssh_pub_key) except Exception as e: print(f"\n[X] 获取SSH密钥时发生错误: {e}") return
while True: out_choice = input( "\n请选择输出格式:\n1) 生成独立文件和 seed.iso (用于 NoCloud 数据源)\n2) 生成 Base64 键值对 (同时保存原始YAML文件)\n请输入 1 或 2: ").strip() if out_choice in ["1", "2"]: break print("无效选择,请重试。")
print("\n--- 开始生成配置文件 ---") if out_choice == "1": for hostname in HOSTNAMES: host_dir = OUTPUT_BASE_DIR / hostname host_dir.mkdir(parents=True, exist_ok=True) user_data = generate_user_data(hostname, ssh_pub_key) meta_data = generate_meta_data(hostname) (host_dir / "user-data").write_text(user_data, encoding="utf-8") (host_dir / "meta-data").write_text(meta_data, encoding="utf-8") print(f"[√] 已为 {hostname} 生成配置文件于: {host_dir}/") create_cloud_init_iso(hostname, host_dir) else: # out_choice == "2" all_configs_path = OUTPUT_BASE_DIR / "all_hosts_guestinfo.txt" all_configs_path.parent.mkdir(parents=True, exist_ok=True) with all_configs_path.open("w", encoding="utf-8") as f_all: for i, hostname in enumerate(HOSTNAMES): print(f"\n--- 正在处理: {hostname} ---") # 关键修复:使用 ssh_pub_key 而不是 ssh_key user_data_str = generate_user_data(hostname, ssh_pub_key) meta_data_str = generate_meta_data(hostname) host_dir = OUTPUT_BASE_DIR / hostname host_dir.mkdir(parents=True, exist_ok=True) (host_dir / "user-data.yaml").write_text(user_data_str, encoding="utf-8") (host_dir / "meta-data.yaml").write_text(meta_data_str, encoding="utf-8") print(f"[i] 原始YAML文件已保存至: {host_dir}/") user_data_b64 = base64.b64encode(user_data_str.encode("utf-8")).decode("utf-8") meta_data_b64 = base64.b64encode(meta_data_str.encode("utf-8")).decode("utf-8") output_content = dedent(f"""\ # Hostname: {hostname} guestinfo.userdata="{user_data_b64}" guestinfo.userdata.encoding="base64" guestinfo.metadata="{meta_data_b64}" guestinfo.metadata.encoding="base64" """) print(f"[√] 正在生成 Base64 配置...") if i > 0: f_all.write("\n") f_all.write(output_content) print(f"\n[√] 所有主机的 Base64 配置已合并保存到: {all_configs_path}")
print("\n--- 所有任务完成 ---")
if __name__ == "__main__": try: f = Figlet(font='slant') print(f.renderText('cloud-init creat config by inuyume')) except NameError: print("--- cloud-init creat config by inuyume ---")
main()
print( "\n[Hint] : 如果想使用SSH密码,请在generate_user_data的dedent中修改prohibit-password为yes并取消chpasswd的注释并修改默认的password123") print( "\n[Hint] : 请注意保存生成的公钥和私钥和生成的配置文件,相同目录下运行脚本会覆盖旧的生成文件,密钥默认使用已有的密钥!") Cloud-init ESXi 尝试
https://blog.fiveqm.com/archives/509