【问题标题】:How to SSH from Cloud Functions to a GCE Instance如何从 Cloud Functions SSH 到 GCE 实例
【发布时间】:2021-05-31 17:21:26
【问题描述】:

如何通过 SSH 从云函数执行命令到 GCE 实例?

【问题讨论】:

    标签: ssh google-cloud-functions google-compute-engine


    【解决方案1】:

    你需要:

    • GCE 实例。
    • 相同区域和网络中的无服务器 VPC 连接器。
    • 为 GCE 实例或所有项目启用操作系统登录。
    • 创建一个相同区域和网络的 Cloud Functions。

    步骤:

    1. 在区域 europe-west3-c 网络默认值中创建实例
    2. 在同一区域、同一网络中创建 VPC 无服务器连接器。
    3. 使用 Python 创建云函数,并列出代码。
    4. 在运行时、构建和连接设置 > 连接中,添加在第 2 步中创建的 VPC 连接器。
    5. 我明智地启用了 OS Login 项目,但是您可能希望只允许它用于一个实例。
    gcloud compute project-info add-metadata \
        --metadata enable-oslogin=TRUE
    
    1. 运行云功能。

    云函数代码,来自samples:

    import argparse
    import logging
    import subprocess
    import time
    import uuid
    
    import googleapiclient.discovery
    import requests
    
    
    # Global variables
    SERVICE_ACCOUNT_METADATA_URL = (
        'http://metadata.google.internal/computeMetadata/v1/instance/'
        'service-accounts/default/email')
    HEADERS = {'Metadata-Flavor': 'Google'}
    
    # [END imports_and_variables]
    
    
    # [START run_command_local]
    def execute(cmd, cwd=None, capture_output=False, env=None, raise_errors=True):
        """Execute an external command (wrapper for Python subprocess)."""
        logging.info('Executing command: {cmd}'.format(cmd=str(cmd)))
        stdout = subprocess.PIPE if capture_output else None
        process = subprocess.Popen(cmd, cwd=cwd, env=env, stdout=stdout)
        output = process.communicate()[0]
        returncode = process.returncode
        if returncode:
            # Error
            if raise_errors:
                raise subprocess.CalledProcessError(returncode, cmd)
            else:
                logging.info('Command returned error status %s', returncode)
        if output:
            logging.info(output)
        return returncode, output
    # [END run_command_local]
    
    
    # [START create_key]
    def create_ssh_key(oslogin, account, private_key_file=None, expire_time=300):
        """Generate an SSH key pair and apply it to the specified account."""
        private_key_file = private_key_file or '/tmp/key-' + str(uuid.uuid4())
        execute(['ssh-keygen', '-t', 'rsa', '-N', '', '-f', private_key_file])
    
        with open(private_key_file + '.pub', 'r') as original:
            public_key = original.read().strip()
    
        # Expiration time is in microseconds.
        expiration = int((time.time() + expire_time) * 1000000)
    
        body = {
            'key': public_key,
            'expirationTimeUsec': expiration,
        }
        oslogin.users().importSshPublicKey(parent=account, body=body).execute()
        return private_key_file
    # [END create_key]
    
    
    # [START run_command_remote]
    def run_ssh(cmd, private_key_file, username, hostname):
        """Run a command on a remote system."""
        ssh_command = [
            'ssh', '-i', private_key_file, '-o', 'StrictHostKeyChecking=no',
            '{username}@{hostname}'.format(username=username, hostname=hostname),
            cmd,
        ]
        ssh = subprocess.Popen(
            ssh_command, shell=False, stdout=subprocess.PIPE,
            stderr=subprocess.PIPE)
        result = ssh.stdout.readlines()
        return result if result else ssh.stderr.readlines()
    
    # [END run_command_remote]
    
    
    # [START main]
    def main(cmd, project, instance=None, zone=None,
             oslogin=None, account=None, hostname=None, username=None):
        """Run a command on a remote system."""
    
        # Create the OS Login API object.
        oslogin = oslogin or googleapiclient.discovery.build('oslogin', 'v1')
    
        # Identify the service account ID if it is not already provided.
        account = account or requests.get(
            SERVICE_ACCOUNT_METADATA_URL, headers=HEADERS).text
        if not account.startswith('users/'):
            account = 'users/' + account
    
        # Create a new SSH key pair and associate it with the service account.
        private_key_file = create_ssh_key(oslogin, account)
    
        # Using the OS Login API, get the POSIX user name from the login profile
        # for the service account.
        profile = oslogin.users().getLoginProfile(name=account).execute()
        username = username or profile.get('posixAccounts')[0].get('username')
    
        # Create the hostname of the target instance using the instance name,
        # the zone where the instance is located, and the project that owns the
        # instance.
        hostname = hostname or '{instance}.{zone}.c.{project}.internal'.format(
            instance=instance, zone=zone, project=project)
    
        # Run a command on the remote instance over SSH.
        result = run_ssh(cmd, private_key_file, username, hostname)
    
        # Print the command line output from the remote instance.
        # Use .rstrip() rather than end='' for Python 2 compatability.
        for line in result:
            print(line.decode('utf-8').rstrip('\n\r'))
    
        # Shred the private key and delete the pair.
        execute(['shred', private_key_file])
        execute(['rm', private_key_file])
        execute(['rm', private_key_file + '.pub'])
    
    def hello_world(request):
        main("ls -la", YOUR_PROJECT_NAME, hostname=GCE_INSTANCE_PRIVATE_IP)
        return 'done'
    

    Requirements.txt

    google-api-python-client==2.6.0
    google-auth==1.30.1
    google-auth-httplib2==0.1.0
    requests==2.25.1
    

    【讨论】:

    • 您是否真正验证过这段代码在 Cloud Function 上正确执行?
    • 是的,函数 main 打印日志中文件系统的内容,这些内容可以在 Cloud Logging 中找到,也可以在 Cloud Function 中的日志中找到。你有什么错误吗?
    猜你喜欢
    • 2014-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-19
    • 2022-06-18
    • 2016-06-06
    • 2014-03-19
    • 2018-02-20
    相关资源
    最近更新 更多