なかなか、そのままのタイトルですが

なんで、そんなことしたいかと言うと、実行しているPodの中でバックアップ処理を外からさせたいからです。

kubectlが実行出来る環境を構築すれば良いんですが、面倒なので、Pythonだけでなんとかしたかったのですよ

 


func.py

import sys
import io
import json
import logging
import yaml
import oci

from kubernetes import client, config
from kubernetes.client.rest import ApiException
from kubernetes.stream import stream

from fdk import response

# Execute command to Pod.
def exec_commands(api_instance : client.CoreV1Api, podname, commands, loopwait=10, namespace = 'default' ):
    result=""
    name = podname

    resp = api_instance.read_namespaced_pod(name=name,
                                            namespace=namespace)

    if not resp:
        raise Exception('not', 'resp')

    # Calling exec interactively
    exec_command = ['/bin/sh']
    resp = stream(api_instance.connect_get_namespaced_pod_exec,
                  name,
                  namespace,
                  command=exec_command,
                  stderr=True, stdin=True,
                  stdout=True, tty=False,
                  _preload_content=False)

    max_loop=loopwait
    loop=0
    while resp.is_open():
        sout = resp.readline_stdout(timeout=1)
        if sout is not None :
            result += (sout + '\n')
            loop=0
        if resp.peek_stderr():
            print( resp.read_stderr(), file=sys.stderr)

        if commands:
            c = commands.pop(0)
            resp.write_stdin(c + "\n")
            loop=0
        else:
            if loop > max_loop :
                break
            loop+=1

    resp.close()

    return result

def handler(ctx, data: io.BytesIO = None):
    result = ""

    # OKE Cluster OCID
    cid = ""
    apikey = ''
    podname = ''
    commands = []
    try:
        # Jsonロード
        body = json.loads(data.getvalue())
        bdata = body.get("data")
        cid = bdata['clusterid']
        apikey = bdata['apikey']
        podname = bdata['podname']

        for cmd in bdata['commands']:
            commands.append( cmd )

        # Function auth information
        signer = oci.auth.signers.get_resource_principals_signer()

        # Build OKE Client
        ce_client : oci.container_engine.ContainerEngineClient = oci.container_engine.ContainerEngineClient({}, signer=signer)
        # Build Cluster config
        res : oci.response.Response = ce_client.create_kubeconfig(cluster_id=cid)
        kconfig = res.data.text

        # Cluster config load
        kubeconfig2 = yaml.load(kconfig,Loader=yaml.FullLoader)
        config.load_kube_config_from_dict(kubeconfig2)

        # Set ServiceAccount's token
        apiclient = config.new_client_from_config_dict(kubeconfig2)
        apiclient.configuration.api_key['authorization'] = apikey
        apiclient.configuration.api_key_prefix['authorization'] = 'Bearer'

        # Build Kubernetes client
        v1 = client.CoreV1Api(apiclient)

        result = exec_commands(v1, podname, commands, loopwait=10, namespace = 'default' )

    except (Exception, ValueError) as ex:
        logging.getLogger().info('error: ' + str(ex))
        return response.Response(
            ctx, response_data= "Error:"+str(ex),
            headers={"Content-Type": "application/text"}
        )

    return response.Response(
        ctx, response_data= result,
        headers={"Content-Type": "application/text"}
    )

requirements.txt

fdk>=0.1.66
kubernetes
oci
pyyaml

これを fn やら、oci fn なんかで呼びだせば Ok です。

APIGatewayから呼び出せるようにすれば、何かと便利ですね。

 

oci fn function invoke --function-id ファンクションOCID --file "-" --body '
{
    "data": {
        "clusterid" : "クラスタOCID",
        "apikey" : "サービスアカウントのトークン",
        "podname" : "ポッド名",
        "commands" : [コマンド]
    }
}'

 

サービスアカウントのトークンは、以下を参考にして下さい。

OCI の Functionで、OKE(kubernetes) を制御したい

 

Joomla templates by a4joomla