All Products
Search
Document Center

CDN:[Konsol Function Compute] Arsipkan offline logs ke OSS

Last Updated:Jul 17, 2026

Gunakan Function Compute untuk mengarsipkan offline logs dari Alibaba Cloud CDN ke Object Storage Service (OSS) secara otomatis dan berkala guna retensi jangka panjang serta analisis.

Latar Belakang

Alibaba Cloud CDN menyediakan access logs terperinci untuk nama domain yang dipercepat Anda. Offline logs ini penting untuk analisis perilaku pengguna, troubleshooting layanan, dan statistik data operasional. Alibaba Cloud CDN hanya menyimpan file offline log di servernya selama 30 hari sebelum menghapusnya secara otomatis.

Untuk memenuhi kebutuhan kepatuhan data, audit jangka panjang, atau analisis data historis, Anda mungkin perlu menyimpan log ini secara permanen. Object Storage Service (OSS) menyediakan solusi penyimpanan yang sangat tersedia, hemat biaya, dan tahan lama, sehingga menjadi pilihan ideal untuk arsip log jangka panjang. Function Compute mendengarkan event yang dihasilkan saat Alibaba Cloud CDN membuat log, lalu memanggil fungsi untuk mengarsipkan offline logs tersebut ke Object Storage Service (OSS). Solusi ini menciptakan alur kerja otomatis yang mengarsipkan log Alibaba Cloud CDN ke bucket OSS Anda secara mulus.

Cara Kerja

Solusi arsip otomatis ini menggunakan Function Compute sebagai "penjadwal" dan "pemindah data" untuk menghubungkan Alibaba Cloud CDN dan Object Storage Service (OSS). Alur kerjanya adalah sebagai berikut:

  1. Pemicu event: Pemicu Function Compute aktif setiap kali Alibaba Cloud CDN menghasilkan file log baru.

  2. Eksekusi fungsi: Saat pemicu aktif, kode fungsi terkait dieksekusi secara otomatis.

  3. Pengambilan log: Kode fungsi menghitung nama file log hari sebelumnya berdasarkan tanggal saat ini dan menghasilkan URL unduh untuk offline log Alibaba Cloud CDN. Kemudian, fungsi tersebut mengirim permintaan ke URL tersebut untuk mengunduh file log ke lingkungan sementara Function Compute.

  4. Transfer ke Object Storage Service (OSS): Setelah file log berhasil diunduh, fungsi tersebut memanggil API OSS untuk mengunggah file ke direktori tertentu dalam bucket OSS yang telah Anda tentukan.

Seluruh proses ini sepenuhnya otomatis, mengintegrasikan Alibaba Cloud CDN, Function Compute, dan Object Storage Service (OSS) secara erat untuk menyederhanakan manajemen layanan cloud Anda.

Penagihan

Solusi ini melibatkan biaya untuk produk-produk berikut:

  • Alibaba Cloud CDN: Menghasilkan dan mengunduh offline logs gratis.

  • Function Compute: Anda dikenai biaya berdasarkan jumlah pemanggilan fungsi, sumber daya yang dikonsumsi (vCPU dan memori), serta durasi eksekusi. Untuk tugas arsip log ringan yang hanya berjalan beberapa kali sehari, biayanya biasanya minimal. Untuk informasi lebih lanjut, lihat Ikhtisar Penagihan Function Compute.

  • Object Storage Service (OSS): Anda dikenai biaya berdasarkan ruang penyimpanan yang digunakan, jumlah permintaan API, dan trafik internet outbound. Untuk informasi lebih lanjut, lihat Ikhtisar Penagihan Object Storage Service (OSS).

Prasyarat

  • Pastikan Anda telah mengaktifkan Alibaba Cloud CDN, Function Compute, dan Object Storage Service (OSS) di bawah Akun Alibaba Cloud yang sama. Hal ini menjamin otorisasi dan akses antar layanan berjalan lancar.

  • Buat bucket Object Storage Service (OSS) untuk menyimpan file log. Untuk informasi lebih lanjut, lihat Buat bucket. Catat nama bucket, endpoint akses publik, dan nama direktori penyimpanan log.

Prosedur

1. Dapatkan detail bucket

Fungsi Function Compute memerlukan detail bucket OSS Anda. Ikuti langkah-langkah berikut untuk mendapatkan nama bucket, endpoint akses publik, dan nama direktori penyimpanan log.

Dapatkan detail bucket

  1. Buka halaman Buckets di konsol Object Storage Service (OSS) dan pilih bucket untuk penyimpanan log.

  2. Klik nama bucket untuk membuka halaman detail bucket.

  3. Di tab Overview bucket, temukan Bucket Name di bawah Informasi Dasar. Lalu, di bawah Access Port, temukan Endpoint untuk Access Over Internet.

  4. Klik New Directory di daftar file bagian File Management dan masukkan nama direktori (nama direktori yang direkomendasikan adalah cdn_log).

2. Buat fungsi dan pemicu Function Compute

Di Function Compute, Anda akan membuat fungsi untuk melakukan arsip dan pemicu untuk menjalankannya.

  1. Masuk ke Konsol Function Compute. Di panel navigasi sebelah kiri, pilih Functions.

  2. Di halaman Functions, klik Create Function, pilih Event Function, lalu klik Create Event Function.

  3. Saat membuat fungsi event, konfigurasikan parameter utama berikut:

    • Basic Configuration-Function Name: Nama fungsi ini diperlukan untuk operasi selanjutnya. Kami merekomendasikan Anda menggunakan cdn-log-dump.

    • Function Code > Runtime: Fungsi ini ditulis dalam Python. Pilih Official Runtimes, Python, lalu Python 3.10.

    • Advanced Settings > Environment Variable: Untuk meneruskan detail bucket OSS ke fungsi, buat variabel lingkungan berikut:

      • target_oss_bucket: Nama bucket

      • target_oss_endpoint: Endpoint untuk akses publik

      • target_oss_prefix: Nama direktori untuk menyimpan file log

      Sebagai contoh, atur target_oss_bucket ke demo-bucket, target_oss_endpoint ke oss-cn-hangzhou.aliyuncs.com, dan target_oss_prefix ke cdn_log.

  4. Setelah mengonfigurasi parameter, klik Create.

  5. Di Function Details, klik tab Triggers, lalu klik Create Trigger.

  6. Konfigurasikan parameter utama pemicu sebagai berikut, lalu klik OK.

    • Trigger Type: Pilih Alibaba Cloud CDN (Sync invocation).

    • Name: Masukkan nama untuk pemicu (Direkomendasikan: cdn-logs-triggers).

    • Trigger Event: Pilih LogFileCreated.

    • Domain Name: Masukkan nama domain yang dipercepat aktif yang berada di bawah Akun Alibaba Cloud yang sama.

    • Description: Masukkan deskripsi untuk pemicu. Kami merekomendasikan Anda menggunakan "CDN offline log file generation trigger".

    • Role: Pilih AliyunCDNEventNotificationRole.

  7. Setelah mengonfigurasi parameter pemicu, klik OK. Jika muncul pesan No Default Role Is Created for CDN Trigger, klik Authorize Now dan ikuti instruksi di layar untuk membuat role default. Jika tidak, pemicu akan dibuat segera.

  8. Di Function Details, klik tab Code, lalu di editor online, masukkan kode berikut, yang mengambil offline logs dari Alibaba Cloud CDN dan menyimpannya di Object Storage Service (OSS).

    Kode tugas arsip

    # -*- coding: utf-8 -*-
    import os, time, json, requests, traceback, oss2, fc2
    from requests.exceptions import *
    from fc2.fc_exceptions import *
    from oss2.models import PartInfo
    from oss2.exceptions import *
    from multiprocessing import Pool
    from contextlib import closing
    MAX_PROCCESSES = 20 # Jumlah proses pekerja di setiap subtugas
    BLOCK_SIZE = 6 * 1024 * 1024 # Ukuran setiap bagian
    BLOCK_NUM_INTERNAL = 18 # Jumlah blok default di setiap subtugas untuk URL internal
    BLOCK_NUM = 10 # Jumlah blok default di setiap subtugas
    MAX_SUBTASKS = 49 # Jumlah proses pekerja untuk menjalankan subtugas
    CHUNK_SIZE = 8 * 1024 # Ukuran setiap chunk
    SLEEP_TIME = 0.1 # Detik awal menunggu sebelum mencoba ulang
    MAX_RETRY_TIME = 10 # Jumlah maksimum percobaan ulang
    def retry(func):
        """
        Eksekutor fungsi dengan mekanisme percobaan ulang.
        :param func: (wajib, lambda) Fungsi yang akan dieksekusi.
        :return: Hasil eksekusi func.
        """
        wait_time = SLEEP_TIME
        retry_cnt = 1
        while True:
            if retry_cnt > MAX_RETRY_TIME:
                return func()
            try:
                return func()
            except (ConnectionError, SSLError, ConnectTimeout, Timeout) as e:
                print(traceback.format_exc())
            except (OssError) as e:
                if 500 <= e.status < 600:
                    print(traceback.format_exc())
                else:
                    raise Exception(e)
            except (FcError) as e:
                if (500 <= e.status_code < 600) or (e.status_code == 429):
                    print(traceback.format_exc())
                else:
                    raise Exception(e)
            print('Mencoba ulang (percobaan ke-%d)...' % retry_cnt)
            time.sleep(wait_time)
            wait_time *= 2
            retry_cnt += 1
    def get_info(url):
        """
        Mendapatkan CRC64 dan panjang total file.
        :param url: (wajib, string) URL file.
        :return: CRC64, panjang
        """
        with retry(lambda : requests.get(url, {}, stream = True)) as r:
            return r.headers['x-oss-hash-crc64ecma'], int(r.headers['content-length'])
    class Response(object):
        """
        Kelas respons yang mendukung pembacaan chunked.
        """
        def __init__(self, response):
            self.response = response
            self.status = response.status_code
            self.headers = response.headers
        def read(self, amt = None):
            if amt is None:
                content = b''
                for chunk in self.response.iter_content(CHUNK_SIZE):
                    content += chunk
                return content
            else:
                try:
                    return next(self.response.iter_content(amt))
                except StopIteration:
                    return b''
        def __iter__(self):
            return self.response.iter_content(CHUNK_SIZE)
    def migrate_part(args):
        """
        Mengunduh bagian dari URL dan mengunggahnya ke OSS.
        :param args: (bucket, object_name, upload_id, part_number, url, st, en)
        :bucket: (wajib, Bucket) Bucket OSS tujuan.
        :object_name: (wajib, string) Nama objek tujuan.
        :upload_id: (wajib, integer) ID unggah untuk tugas unggah ini.
        :part_number: (integer) Nomor bagian untuk bagian ini.
        :url: (wajib, string) URL file.
        :st, en: (wajib, integer) Rentang byte bagian ini, menunjukkan [st, en].
        :return: (part_number, etag)
        :part_number: (integer) Nomor bagian untuk bagian ini.
        :etag: (string) Etag dari hasil upload_part.
        """
        bucket = args[0]
        object_name = args[1]
        upload_id = args[2]
        part_number = args[3]
        url = args[4]
        st = args[5]
        en = args[6]
        try:
            headers = {'Range' : 'bytes=%d-%d' % (st, en)}
            resp = Response(retry(lambda : requests.get(url, headers = headers, stream = True)))
            result = retry(lambda : bucket.upload_part(object_name, upload_id, part_number, resp))
            return (part_number, result.etag)
        except Exception as e:
            print(traceback.format_exc())
            raise Exception(e)
    def do_subtask(event, context):
        """
        Mengunduh rentang file dari URL dan mengunggahnya ke OSS.
        :param event: (wajib, json) Event dalam format JSON.
        :param context: (wajib, FCContext) Konteks handler.
        :return: parts
        :parts: ([(integer, string)]) Nomor bagian dan etag setiap proses.
        """
        oss_endpoint = os.environ.get('target_oss_endpoint')
        oss_bucket_name = os.environ.get('target_oss_bucket')
        access_key_id = context.credentials.access_key_id
        access_key_secret = context.credentials.access_key_secret
        security_token = context.credentials.security_token
        auth = oss2.StsAuth(access_key_id, access_key_secret, security_token)
        bucket = oss2.Bucket(auth, oss_endpoint, oss_bucket_name)
        object_name = event['object_name']
        upload_id = event['upload_id']
        part_number = event['part_number']
        url = event['url']
        st = event['st']
        en = event['en']
        if part_number == 1:
            return [migrate_part((bucket, object_name, upload_id, part_number, url, st, en))]
        pool = Pool(MAX_PROCCESSES)
        tasks = []
        while st <= en:
            nxt = min(en, st + BLOCK_SIZE - 1)
            tasks.append((bucket, object_name, upload_id, part_number, url, st, nxt))
            part_number += 1
            st = nxt + 1
        parts = pool.map(migrate_part, tasks)
        pool.close()
        pool.join()
        return parts
    def invoke_subtask(args):
        """
        Memanggil fungsi yang sama secara sinkron untuk memulai subtugas.
        :param args: (object_name, upload_id, part_number, url, st, en, context)
        :object_name: (wajib, string) Nama objek tujuan.
        :upload_id: (wajib, integer) ID unggah untuk tugas unggah ini.
        :part_number: (integer) Nomor bagian pertama dalam subtugas ini.
        :url: (wajib, string) URL file.
        :st, en: (wajib, integer) Rentang byte subtugas ini, menunjukkan [st, en].
        :context: (wajib, FCContext) Konteks handler.
        :return: Nilai kembali dari fungsi yang dipanggil.
        """
        object_name = args[0]
        upload_id = args[1]
        part_number = args[2]
        url = args[3]
        st = args[4]
        en = args[5]
        context = args[6]
        account_id = context.account_id
        access_key_id = context.credentials.access_key_id
        access_key_secret = context.credentials.access_key_secret
        security_token = context.credentials.security_token
        region = context.region
        service_name = context.service.name
        function_name = context.function.name
        endpoint = 'http://%s.%s-internal.fc.aliyuncs.com' % (account_id, region)
        client = fc2.Client(
            endpoint = endpoint,
            accessKeyID = access_key_id,
            accessKeySecret = access_key_secret,
            securityToken = security_token
        )
        payload = {
            'object_name' : object_name,
            'upload_id' : upload_id,
            'part_number' : part_number,
            'url' : url,
            'st' : st,
            'en' : en,
            'is_children' : True
        }
        if part_number == 1:
            return json.dumps(do_subtask(payload, context))
        ret = retry(lambda : client.invoke_function(service_name, function_name, payload = json.dumps(payload)))
        return ret.data
    def divide(n, m):
        """
        Menghitung ceil(n / m) tanpa aritmetika floating point.
        :param n, m: (integer)
        :return: (integer) ceil(n / m).
        """
        ret = n // m
        if n % m > 0:
            ret += 1
        return ret
    def migrate_file(url, oss_object_name, context):
        """
        Mengunduh file dari URL dan mengunggahnya ke OSS.
        :param url: (wajib, string) URL file.
        :param oss_object_name: (wajib, string) Nama objek tujuan.
        :param context: (wajib, FCContext) Konteks handler.
        :return: actual_crc64, expect_crc64
        :actual_crc64: (string) CRC64 file yang diunggah.
        :expect_crc64: (string) CRC64 file sumber.
        """
        crc64, total_size = get_info(url)
        oss_endpoint = os.environ.get('target_oss_endpoint')
        oss_bucket_name = os.environ.get('target_oss_bucket')
        access_key_id = context.credentials.access_key_id
        access_key_secret = context.credentials.access_key_secret
        security_token = context.credentials.security_token
        auth = oss2.StsAuth(access_key_id, access_key_secret, security_token)
        bucket = oss2.Bucket(auth, oss_endpoint, oss_bucket_name)
        upload_id = retry(lambda : bucket.init_multipart_upload(oss_object_name)).upload_id
        pool = Pool(MAX_SUBTASKS)
        st = 0
        part_number = 1
        tasks = []
        block_num = BLOCK_NUM_INTERNAL if '-internal.aliyuncs.com' in oss_endpoint else BLOCK_NUM
        block_num = min(block_num, divide(divide(total_size, BLOCK_SIZE), MAX_SUBTASKS + 1))
        while st < total_size:
            en = min(total_size - 1, st + block_num * BLOCK_SIZE - 1)
            tasks.append((oss_object_name, upload_id, part_number, url, st, en, context))
            size = en - st + 1
            cnt = divide(size, BLOCK_SIZE)
            part_number += cnt
            st = en + 1
        subtasks = pool.map(invoke_subtask, tasks)
        pool.close()
        pool.join()
        parts = []
        for it in subtasks:
            for part in json.loads(it):
                parts.append(PartInfo(part[0], part[1]))
        res = retry(lambda : bucket.complete_multipart_upload(oss_object_name, upload_id, parts))
        return str(res.crc), str(crc64)
    def get_oss_object_name(url):
        """
        Mendapatkan nama objek OSS.
        :param url: (wajib, string) URL file.
        :return: (string) Nama objek OSS.
        """
        prefix = os.environ.get('target_oss_prefix')
        tmps = url.split('?')
        if len(tmps) != 2:
            raise Exception('URL tidak valid: %s' % url)
        urlObject = tmps[0]
        if urlObject.count('/') < 3:
            raise Exception('URL tidak valid: %s' % url)
        objectParts = urlObject.split('/')
        objectParts = [prefix] + objectParts[len(objectParts) - 3 : len(objectParts)]
        return '/'.join(objectParts)
    def handler(event, context):
        evt = json.loads(event)
        if list(evt.keys()).count('is_children'):
            return json.dumps(do_subtask(evt, context))
        url = evt['events'][0]['eventParameter']['filePath']
        if not (url.startswith('http://') or url.startswith('https://')):
            url = 'https://' + url
        oss_object_name = get_oss_object_name(url)
        st_time = int(time.time())
        wait_time = SLEEP_TIME
        retry_cnt = 1
        while True:
            actual_crc64, expect_crc64 = migrate_file(url, oss_object_name, context)
            if actual_crc64 == expect_crc64:
                break
            print('CRC64 objek yang dimigrasi tidak cocok. Yang diharapkan: %s, Aktual: %s' % (expect_crc64, actual_crc64))
            if retry_cnt > MAX_RETRY_TIME:
                raise Exception('Jumlah maksimum percobaan ulang terlampaui.')
            print('Mencoba ulang (percobaan ke-%d)...' % retry_cnt)
            time.sleep(wait_time)
            wait_time *= 2
            retry_cnt += 1
        print('Berhasil! Total waktu: %d dtk.' % (int(time.time()) - st_time))
    
  9. Klik Deploy Code.

3. Buat role dan kebijakan kustom

Fungsi Function Compute memerlukan izin untuk mengakses Object Storage Service (OSS). Anda dapat memberikan izin ini dengan menyambungkan role kustom ke fungsi tersebut. Ikuti langkah-langkah berikut untuk membuat role dan kebijakan yang diperlukan.

  1. Masuk ke konsol Resource Access Management (RAM). Di panel navigasi kiri, pilih Permissions > Policies.

  2. Klik Create Policy dan pilih tab JSON.

  3. Pada kebijakan berikut, ganti BucketName dengan nama bucket Anda, dan ganti ketiga kemunculan FC-NAME dengan nama fungsi dari Langkah 2 (kami merekomendasikan Anda menggunakan cdn-log-dump).

    {
      "Version": "1",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "oss:PutObject",
          "Resource": "acs:oss:*:*:BucketName/*"
        },
        {
          "Effect": "Allow",
          "Action": "fc:InvokeFunction",
          "Resource": [
            "acs:fc:*:*:services/FC-NAME/functions/FC-NAME",
            "acs:fc:*:*:services/FC-NAME.*/functions/*"
          ]
        }
      ]
    }
  4. Klik OK dan masukkan Policy Name serta Remarks. Lalu, klik OK lagi untuk membuat kebijakan (Kami merekomendasikan Anda mengatur Policy Name menjadi AliyunCDNLogDumpAccess dan Description menjadi Izin untuk mengelola dump log offline CDN).

  5. Di panel navigasi kiri, pilih Identities > Roles dan klik Create Role.

  6. Untuk Select Trusted Entity, pilih Alibaba Cloud Service. Untuk Trusted Service, pilih Function Compute, lalu klik OK.

  7. Di kotak dialog Create Role, masukkan Role Name (kami merekomendasikan Anda menggunakan AliyunCDNLogDumpRole) dan klik OK untuk membuat role.

  8. Di tab Permissions, klik Precise Authorization. Untuk Policy Type, pilih Custom Policies, dan untuk Policy Name, masukkan nama kebijakan yang Anda buat di Langkah 4 (kami merekomendasikan Anda menggunakan AliyunCDNLogDumpAccess). Lalu, klik OK.

  9. Di tab Trust Policy, klik Edit Trust Policy. Di editor JSON, masukkan kebijakan kepercayaan berikut, lalu klik OK.

    {
      "Statement": [
        {
          "Action": "sts:AssumeRole",
          "Effect": "Allow",
          "Principal": {
            "Service": [
              "fc.aliyuncs.com"
            ]
          }
        }
      ],
      "Version": "1"
    }

4. Sambungkan role ke fungsi

  1. Di tab Functions di konsol Function Compute, pilih fungsi yang Anda buat di Langkah 2, lalu klik Configure.

  2. Di halaman detail fungsi, klik tab Configure. Di bagian Advanced Settings, klik Configure.

  3. Di Advanced Settings, temukan opsi Permissions - Function Role dan pilih role yang Anda buat di Langkah 3. Kami merekomendasikan Anda menggunakan AliyunCDNLogDumpRole. Lalu, klik Deploy untuk menyambungkan role ke tugas Function Compute.

5. Uji fungsi (Opsional)

Konfigurasi sekarang telah lengkap. Karena Alibaba Cloud CDN menghasilkan offline logs dengan penundaan sekitar 24 jam, Anda tidak dapat langsung memverifikasi apakah pengaturan berfungsi. Untuk menguji fungsi segera, ikuti langkah-langkah opsional berikut.

  1. Di tab Functions di konsol Function Compute, pilih fungsi yang dibuat di Langkah 2, lalu klik Configure.

  2. Di halaman detail fungsi, klik tab Test. Untuk Test Event, pilih Create New Test Event. Untuk Event Template, pilih CDN (LogFileCreated). Untuk Event Name, masukkan Test_cdn_log_dump.

    Editor JSON secara otomatis diisi dengan template event CDN LogFileCreated, yang mencakup bidang seperti eventName, eventSource, region, eventTime, traceId, userIdentity (aliUid), resource (misalnya, domain adalah example.com), dan eventParameter.

  3. Gunakan parameter yang diperoleh di bawah ini untuk mengubah parameter filePath dalam template event.

    Cara mendapatkan parameter filePath untuk pengujian

    1. Buka halaman Offline Log Download di konsol Alibaba Cloud CDN.

    2. Pilih nama domain yang dipercepat yang telah Anda konfigurasikan untuk pemicu, pilih tanggal kemarin, lalu klik Query.

    3. Di hasilnya, temukan file log. Arahkan kursor ke tombol Download, klik kanan, lalu pilih Copy Link.

  4. Klik Test Function. Setelah eksekusi selesai, hasil yang dikembalikan adalah null dan status eksekusi berhasil.

  5. Di Konsol Object Storage Service (OSS), pilih bucket yang digunakan untuk menyimpan log Alibaba Cloud CDN.

  6. Klik Files dan buka direktori yang Anda konfigurasikan untuk menyimpan log Alibaba Cloud CDN. Anda akan melihat folder yang dinamai sesuai nama domain yang dipercepat. Di dalamnya, subfolder yang dinamai sesuai tanggal berisi file log yang diarsipkan. Hal ini mengonfirmasi bahwa tugas Function Compute berjalan berhasil.