使用Python和Digital Ocean创建和设置服务器
作者:互联网
- 通过Paramiko库与ssh进行编程交互
- 使用Inquirer创建交互式命令行工具
- 通过API在Digital Ocean上创建服务器(或Droplet)
- 在Python中阅读YAML
- 从python启动终端命令
先进行一点设计和设置
我们需要从什么开始?要连接到DigitalOcean API,我们首先需要一个令牌。您可以在这里生成一个,或者:
- 从数字海洋仪表板,单击API链接
- 点击生成新令牌
- 为令牌选择一个名称
- 选择读写功能
- 点击生成令牌
- 立即复制生成的令牌
- 将其粘贴到您的项目文件夹中,并带有自定义名称的新env.yaml文件中,在这里我们使用
doAuthToken
接下来你需要的是sshKey。如果您已经在DigitalOcean上上传了任何指纹,您可以使用它;在列表sshKeys
下复制env.yaml文件中的指纹。
否则,创建一个新的sshKey并将其添加到Digital Ocean中。如果您不知道如何生成sshKey或如何将其上传到您的数字海洋团队,您可以在这里找到所有信息
我们还需要安装一些软件包,因此请运行以下命令:
pip install requests paramiko inquirer yaml
现在我们有了我们需要的东西,让我们开始代码吧!
第一个请求
要开始使用Digital Ocean的API,我们可以调用一些简单的端点,稍后在Droplet创建步骤中需要这些端点:分发和大小端点。
我们必须将之前创建的令牌添加到我们的请求标头中,以便DO的端点正常工作。
由于我们将经常使用此标头,我们最好编写一个函数来处理它:
#utils.py
def buildBasicHeaders():
configsFile = yaml.safe_load(open('./env.yaml'))
token = configsFile['configs']['doAuthToken']
headers = {'Content-Type':'application/json','Authorization':'Bearer '+token}
return headers
标头构建器完成后,我们现在可以请求发行版列表:
#utils.py
def getDistributions(distribution=""):
url = "https://api.digitalocean.com/v2/images?type=distribution"
headers = buildBasicHeaders()
response = requests.get(url,headers=headers)
images = response.json()['images']
images = list(filter(lambda i: i['status'] == 'available', images))
return images
有了这个功能,我们需要从DO端点为我们的服务器提供所有可能的分布。虽然如此,它们并不总是都可用,因此我们过滤结果以仅包含可用发行版的列表。我们现在可以要求液滴的可用尺寸。
#utils.py
def getSizes():
url = "https://api.digitalocean.com/v2/sizes"
headers = buildBasicHeaders()
response = requests.get(url,headers=headers)
return response.json()['sizes']
它与前一个相似,但更直接,所以我们可以继续前进。我们首先创建了这些函数,因为它们是创建droplet所需的参数,但您可以在构建步骤中设置更多配置。您可以在这里查看可能的参数。
创建脚本的第一行
现在让我们创建一个createServer.py
文件,该文件将为我们的程序提供主要进程。
由于我们将向用户提出一堆问题,我们将使用查询库。
让我们轻松开始:
#createServer.py
import utils
import inquirer
questions = [
inquirer.Text('machineName', message="Pick a name for your machine")
]
answers = inquirer.prompt(questions)
machineName = answers['machineName']
我们首先要求用户为新创建的droplet命名。Questions
变量将是我们向用户提出的所有问题的数组。用这条线
answers = inquirer.prompt(questions)
我们告诉Inquirer向用户询问列表中的所有问题,并将结果保存在answers
中,这将是一个列表,作为键,每个提示的第一个参数(在这种情况下,machineName
)提供值。
既然我们已经掌握了这一点,我们就可以得到我们的规模和分布。这有点复杂,但我会一步一步地解释。
#createServer.py
#....
sizes = utils.getSizes()
sizeChoices = []
for i,size in enumerate(sizes, start=1):
choice = f"[{i}] RAM: {size['memory']}MB, CPUs: {size['vcpus']}, disk: {size['disk']}GB"
sizeChoices.append(choice)
images = utils.getDistributions()
imageChoices = []
for i,image in enumerate(images, start=1):
choice = f"[{i}] {image['description']}"
imageChoices.append(choice)
questions = [
inquirer.Text('machineName', message="Pick a name for your machine"),
inquirer.List('dropletSize', message="What size do you need?", choices=sizeChoices ),
inquirer.List('dropletImage', message="What OS do you prefer?", choices=imageChoices)
]
answers = inquirer.prompt(questions)
machineName = answers['machineName']
index = sizeChoices.index(answers['dropletSize'])
dropletSize = sizes[index]['slug']
index = imageChoices.index(answers['dropletImage'])
dropletImage = images[index]['id']
让我们退后一步,了解一下发生了什么,好吗?
第一件事:选项列表创建:
sizes = utils.getSizes()
sizeChoices = []
for i,size in enumerate(sizes, start=1):
choice = f"[{i}] RAM: {size['memory']}MB, CPUs: {size['vcpus']}, disk: {size['disk']}GB"
sizeChoices.append(choice)
images = utils.getDistributions()
imageChoices = []
for i,image in enumerate(images, start=1):
choice = f"[{i}] {image['description']}"
imageChoices.append(choice)
对于尺寸和图像,我们需要先枚举它们,这样我们就可以循环浏览图像,并有一个参考索引供以后参考。
在我们有了一系列选择后,我们可以将这些其他选择添加到用户Questions
中。
questions = [
inquirer.Text('machineName', message="Pick a name for your machine"),
inquirer.List('dropletSize', message="What size do you need?", choices=sizeChoices ),
inquirer.List('dropletImage', message="What OS do you prefer?", choices=imageChoices)
]
answers = inquirer.prompt(questions)
与之前关于机器名称的问题一样,inquirer.List
问题类型需要一个键(如dropletSize
或dropletImage
)和向用户显示的问题。此外,我们必须提供一份选择清单,即我们以前准备的清单。
在这一点上,如果我们执行命令,我们应该有这样的东西:
这是一个好的开始;你觉得呢?
让我们快速解释一下上述代码的最后一部分:
#....
index = sizeChoices.index(answers['dropletSize'])
dropletSize = sizes[index]['slug']
index = imageChoices.index(answers['dropletImage'])
dropletImage = images[index]['id']
在这里,我们有点黑客攻击。由于Inquirer只返回所选答案的文本,我们正在选择列表中找到它的索引,以在原始列表中获取它。之后,我们检索创建液滴所需的部件,因此尺寸的弹头和图像的ID。
现在有趣的部分来了!
创建液滴
终于是时候创建我们的液滴了!
#utils.py
def createDroplet(name, size, image):
headers = buildBasicHeaders()
get_droplets_url = "https://api.digitalocean.com/v2/droplets"
configsFile = yaml.safe_load(open('./env.yaml'))
keys = configsFile['configs']['sshKeys']
keys = getConfig('sshKeys')
data = {
'name':name,
'size':size,
'image':int(image),
'ssh_keys': keys
}
response = requests.post(get_droplets_url, headers=headers,json=data)
return response.json()['droplet']
这一切都非常简单,所以我将介绍关于sshKeys的几点:
- 数据中的sshKeys参数可以包含一个值列表:这些是我们在DigitalOcean上拥有的密钥,我们希望放在新的droplet上,使用它们而不是user:password身份验证通过ssh进行连接。
- 在我们的YAML中,sshKeys参数将是可以从DigitalOcean sshKeys面板中提取的指纹列表
话虽如此,在返回createServer.py
之前,我们知道我们可能想让我们的droplet来检查其状态,所以让我们也为此编写一个函数。
#utils.py
def getDroplet(dropletId):
headers = buildBasicHeaders()
get_droplets_url = f"https://api.digitalocean.com/v2/droplets/{dropletId}"
response = requests.get(get_droplets_url, headers=headers)
return response.json()['droplet']
好的,让我们使用我们的新功能来创建我们的液滴!
#createServer.py
newDroplet = utils.createDroplet(machineName,dropletSize,dropletImage)
newDroplet = utils.getDroplet(newDroplet['id'])
print('[*] Creating the droplet... ', end='', flush=True)
while newDroplet['status'] != 'active' :
newDroplet = utils.getDroplet(newDroplet['id'])
time.sleep(1)
print('OK')
print('[*] Powering the new droplet on...', end='', flush=True)
time.sleep(60)
print('Droplet ready')
那么,这里发生了什么?
- 我们用我们的createDroplet函数创建一个Droplet
- 一旦我们创建了新的droplet并获得了其
id
,我们就会请求检查状态(肯定尚未active
) - 我们告诉用户我们正在创建droplet,然后向服务器迭代请求,直到它显示droplet处于活动状态;我们可以向用户反馈droplet已创建
- 现在,我们必须等到液滴开机后才能操作它。恭喜!现在你有一个全新的液滴要处理!
通过SSH连接并安装软件包
最后,我们可以通过ssh连接,自动安装一些软件包:是时候介绍paramiko了!
#createServer.py
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ip = newDroplet['networks']['v4'][0]['ip_address']
configsFile = yaml.safe_load(open('./env.yaml'))
path = configsFile['configs']['localKeyFile']
ssh.connect(ip, username='root',key_filename=path)
print('CONNECTED')
commands = [
"apt-get update",
"apt-get install -y apache2",
# add all the commands you'd like to exec
]
for command in commands:
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(command)
print(ssh_stdout.read().decode())
ssh_stdin.close()
好吧,再说一遍,这里发生了什么?让我们更深入地看看它!
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
首先,多亏了paramiko,我们正在创建一个sshClient
实例。这是接下来所有行动的中心。
之后,我们将在系统上自动进行ssh密钥搜索。在这两行中,paramiko从我们机器的默认位置加载密钥,如果我们不提供任何精确的密钥,则设置默认回退密钥(我们连接时仍然会这样做)
ip = newDroplet['networks']['v4'][0]['ip_address']
configsFile = yaml.safe_load(open('./env.yaml'))
path = configsFile['configs']['localKeyFile']
ssh.connect(ip, username='root',key_filename=path)
print('CONNECTED')
在这里,我们得到了Droplet的ip以及用于连接的所需私钥的路径;之后,我们可以使用thesshssh.connect()
通过ssh进行连接。现在我们可以执行我们想要的操作:
commands = [
"apt-get update",
"apt-get install -y apache2",
# add all the commands you'd like to exec
]
for command in commands:
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(command)
print(ssh_stdout.read().decode())
ssh_stdin.close()
我们列出了要执行的命令:之后,我们通过它们循环;ssh.exec_command(command)
方法允许我们执行命令并在ssh_stdout变量中接收输出,我们将在屏幕上打印该变量以遵循该过程。
当所有命令执行后,我们可以关闭连接。
最终获得控制权
现在滴已经准备就绪,软件包已经安装完毕,我们想登录shell并检查apache是否已正确安装。因此,让我们通过添加最后几行来结束它:
#createServer.py
print(f"New machine is at IP: {ip}")
webbrowser.open(f'http://{ip}')
os.system(f"ssh -o StrictHostKeyChecking=no root@{ip}")
我们写下液滴的IP,以便我们知道它,并且我们可以注意,以防我们需要(您始终可以在您的数字海洋仪表板上找到此信息);然后我们打开一个新的浏览器窗口到该IP,然后直接在ssh上登录,在用于创建droplet的终端上!
标签:Python,Digital Ocean,服务器 来源: