Google Data Portal을 통해 GiitHub Actions 일정 실행 후 대기 시간 시각화

GiitHub Actions의 스케줄을 사용하여 트리거하는 경우'실행하기 전에 상당한 편차가 있다'Google Data Portal(Data Source)고 생각해 시각화를 시도했다.
▶ 그림1 샘플 화면
Google Data Portal で可視化したグラフを表示しているスクリーンショット

무엇을 시각화하고 있는가


다음 항목을 Google 스프레드시트에 전송하기 위해 시간당 13분 및 43분마다 워크플로우를 시작합니다.
  • 대기 시간 - 워크플로 실행 정보를 작성하는 시간에서 지정된 시작 시간을 뺀 시간
  • 준비 시간 - 작업이 시작된 시점에서 위의 워크플로우 실행 정보가 작성된 시점을 뺀 시간
  • 경과 시간 - 작업이 실행된 후의 현재 시간에서 상기 작업이 시작된 시간
  • 을 뺀 시간
    조금 이해하기 어렵지만 대체로 다음과 같은 관계다.
  • 워크플로우 실행 정보를 만드는 시간
  • 작업 시작 시간
  • 작업 수행 후 현재 시간
  • ワークフローの実行結果にコメントを付けているスクリーンショット
    또한 Google Data Portal을 통해 스프레드시트에 기록된 내용을 시각적으로 볼 수 있습니다.

    시각화 방법


    이용 기술


  • GitHub Actions - 이번 관측 대상, 작업 절차와 작업의 집행 기초

  • zx - 작업 내 다양한 시간을 수집하는 데 사용되는 스크립트

  • GitHub CLI - 작업에서 워크플로우를 얻는 데 사용되는 정보

  • Google Sheets API - GiitHub Actions는 Google 스프레드시트에 레코드 보내기

  • Workload Identity 협력 - 키 없는 인증을 위한 GCP

  • Google Apps Script - GiitHub Actions의 작업 수행 시간을 단축하기 위해 스프레드시트 측면에서도 처리

  • Google Data Portal - 시각 형상 레코드용
  • 워크플로 실행 정보 가져오기


    작업 흐름의 정보 등은 GitHub CLI를 통해 얻는다.실행 과정에서 자신의 정보를 얻을 수 있는지 모르겠지만 ${{ github.run_id }}의 값에서 얻을 수 있습니다.
    ▶ GiitHub CLI를 통해 목록 2-1zx에서 워크플로우 정보 얻기
    const outCreatedAt =
      await $`gh run view ${runId} --json createdAt --jq ".createdAt"`
    
    ▶목록 2-2 작업에 대한 정보 얻기
    const outStartedAt =
      await $`gh api '/repos/{owner}/{repo}/actions/runs/${runId}/jobs' --jq ".jobs[0].started_at"`
    

    워크시트로 보내기


    송신측은 간단히Google Sheets API로 Append를 진행합니다.인증은 위에서 설명한 대로 Workload Identity 협력키 없는 인증 사용 중.
    ▶리스트 2-3 스프레드시트로 보내기
    const request = {
      spreadsheetId,
      range: `${sheetName}!A2:F`,
      valueInputOption: 'USER_ENTERED',
      insertDataOption: 'INSERT_ROWS',
      resource: {
        majorDimension: 'ROWS',
        values
      },
      auth: authClient
    }
    const addRes = await sheets.spreadsheets.values.append(request)
    

    스프레드시트 2개


    GiitHub에서 보낸 스프레드시트와 영구적으로 저장된 스프레드시트는 분리됩니다.
    ▶ 그림2-1 접수 및 저장된 스프레드시트
    flowchart LR
      subgraph GitHub
        workflow[[ Workflow ]]
      end
      subgraph Google
        workflow --> que[( Seet - Que )] ---> data[( Sheet - Data )]
      end

    いくつか理由があるのですが、スプレッドシートで保持しているレコード数が増えたときに Append などの速度がどれくらい低下するか読めなかったというのが大きいです。

    そのため以下のようなスクリプトを作成し、2 時間毎に moveData_ 関数を実行しています。

    図 2-2 受信用のシートは常に数レコードに保たれる

    スプレッドシート上に 2 レコードだけ表示されているスクリーンショット

    (クリックでリストを表示)

    1受信した値の編集と転記

    /**
     * ミリ秒を返す.
     * @param {number} days
     * @param {number} hours
     * @param {number} mins
     * @param {number} secs
     * @param {number} millis
     * @returns {number}
     */
    function mills_(days, hours, mins, secs, mills) {
      return (days * 21600 + hours * 3600 + mins * 60 + secs) * 1000 + mills
    }
    
    /**
     * 指定された sheet へ値を挿入する.
     * 
     * @param {SpreadsheetApp.Sheet} dstSheet- 挿入先 sheet.
     * @param {any[][]} data - 値.
     * @returns {number 挿入した値の行数.
     */
    function insertValues_(dstSheet, data) {
      const dataRows = data.length
      dstSheet.insertRows(2, dataRows)
      dstSheet.getRange(2, 1, dataRows, 7).setValues(data)
      return dataRows
    }
    
    /**
     * 値を移動させる.
     * @param {string} srcSheetName - 移動元 sheet の名前.
     * @param {string} dstSheetName - 移動先 sheet の名前(下記の spread sheet でもこの名前の sheet が移動先になる).
     * @param {string} spreadSheetIdExt - 他の spread sheet の id.
     */
    function moveData_(srcSheetName, dstSheetName, spreadSheetIdExt) {
      const timeout = 30
      // const srcSheetName = "gather"
      // const srcSheetName = "dev"
      // const dstSheetName = "log"
    
      let err
    
      const lock = LockService.getDocumentLock();
      try {
        lock.waitLock(1000 * timeout);
    
        try {
          const ss = SpreadsheetApp.getActiveSpreadsheet()
          const ssExt = SpreadsheetApp.openById(spreadSheetIdExt)
          const srcSheet = ss.getSheetByName(srcSheetName)
          const removed = BlankRows.deleteBlankRows(srcSheet) // 空行ができる可能性があるので除去している.
    
          const srcRange = srcSheet.getDataRange()
          if (srcRange.getValues().length > 1) {
            const dstSheet = ss.getSheetByName(dstSheetName)
            const dstSheetExt = ssExt.getSheetByName(dstSheetName)
            const data = srcSheet.getDataRange().getValues().slice(1).sort(([aRunId], [bRunId]) => bRunId - aRunId).map(row => {
              // 設定されていた開始時刻を求める.
              const createdAt = new Date(row[3])
              const hh = createdAt.getUTCHours()
              // 開始時刻よりも前の時間の場合は日付をまたいでいるときと想定.
              // 24 時間遅延した場合は考えない.
              const across = row[1] > hh
              const date = createdAt.getUTCDate() - (across ? 1 : 0)
              const ts = new Date(0)
              ts.setUTCFullYear(createdAt.getUTCFullYear())
              ts.setUTCMonth(createdAt.getUTCMonth())
              ts.setUTCDate(date)
              ts.setUTCHours(row[1])
              ts.setUTCMinutes(row[2])
              // 計算はログ利用側で行う.
              // const base = mills_(0, row[1], row[2], 0, 0)
              // const delta = mills_(across ? 1 : 0, hh, now.getUTCMinutes(), now.getUTCSeconds(), now.getUTCMilliseconds()) - base
              return [...row, ts.getTime()]
            })
            const dataRows = insertValues_(dstSheet, data)
            insertValues_(dstSheetExt, data)
    
            srcSheet.deleteRows(2, dataRows)
            console.log(`data(${data.length}) has been moved.`)
          } else {
            err = `data is not appended.`
          }
    
          // 空行を削除していたらエラーとするが処理自体は完了している.
          console.log(removed)
          if (removed.length > 0) {
            err = `blank row(s) is detected: ${JSON.stringify(removed)}`
          }
    
        } catch (e) {
          err = e
        }
    
      } catch (e) {
        err = `Could not obtain lock after ${timeout} seconds.`
      } finally {
        lock.releaseLock()
      }
    
      if (err) {
        throw new Error(err)
      }
    }
    

    저장된 레코드 시각화


    처음 쓰는 거라서Google Data Portal 여기가 제일 힘들었어요.
    그래프나 스코어카드 등을 만드는 것 자체를 온라인으로 검색하면 많이 나오는데 이 부분은 조금만 힘들면 대처할 수 있다.
    ▶ 그림 2-3 화면 편집
    Google Data Portal でグラフを編集しているスクリーンショット
    다른 한편, 시시각각의 처리는 시행착오이다.특히 시간대 처리 방법을 잘 모르기 때문에 매개 변수로 가산 연산을 한다.
    따라서 시간대를 나타내는 것이 아니라 시차를 나타내는 것이다.또'30분 동안 엇갈린 처리를 생략했다','선택 범위의 UI에 반영되지 않았다'등 일부 기능이 부족한 경우도 있었다.
    ▶ 그림2-4 계산 공식에서'시차'고려
    Google Data Portal の timestamp フィールドの計算式を編集しているスクリーンショット

    결실


    상술한 과정을 거쳐 아래와 같다.
    ▶ 그림 3-1 일본 시간대
    Google Data Portal の時差パラメーターに 9 を指定しているスクリーンショット
    시차 지정 외에도 객체 기간 변경 등도 지원한다.
    ▶ 그림 3-2 대상 기간 지정
    Google Data Portal の対象期間を編集するダイアログを表示しているスクリーンショット
    ▶ 그림3-3성기 지정(요일별 없음)
    Google Data Portal の曜日を選択するポップアップリストを編表示しているスクリーンショット
    ▶ 그림 3-4 목록 표시
    Google Deta Portal でデータの一覧タブを表示しているスクリーンショット

    기타


    오류 주파수


    지금 한 달가량 운행되고 있지만 파악만 해도 몇 차례 오류가 발생했다.참고로 일람표를 기재하다.
    (클릭하여 목록 보기)
    run id
    위치:
    잘못
    1975007658
    Google
    The API returned an error: Error: The service is currently unavailable.(버그 체크 오류로 인해 작업이 정상적으로 종료됨)
    2000191043
    GitHub
    The hosted runner: Hosted Agent lost communication with the server. Anything in your workflow that terminates the runner process, starves it for CPU/Memory, or blocks its network access can cause this error.
    2000282870
    GitHub
    The hosted runner: Hosted Agent lost communication with the server. Anything in your workflow that terminates the runner process, starves it for CPU/Memory, or blocks its network access can cause this error.
    2000420308
    GitHub
    The hosted runner: Hosted Agent lost communication with the server. Anything in your workflow that terminates the runner process, starves it for CPU/Memory, or blocks its network access can cause this error.
    2000543773
    GitHub
    The hosted runner: Hosted Agent lost communication with the server. Anything in your workflow that terminates the runner process, starves it for CPU/Memory, or blocks its network access can cause this error.
    2021740092
    GitHub
    GitHub Actions has encountered an internal error when running your job.
    2021823739
    GitHub
    GitHub Actions has encountered an internal error when running your job.
    -
    GitHub
    2022-03-2315: 43UTC에서 지정한 트리거가 실행되지 않았습니다.https://www.githubstatus.com/incidents/83lq7ftk19r5
    2057934604
    GitHub
    The API returned an error: failed to get run: HTTP 404: Not Found ( https://api.github.com/repos/hankei6km/gha-now-to-sheet/actions/runs/2057934604 )
    2064168504
    Google
    The API returned an error: Error: The service is currently unavailable.
    2082477256
    Google
    The API returned an error: Error: The service is currently unavailable.
    2106211831
    GitHub
    The API returned an error: failed to get run: HTTP 404: Not Found ( https://api.github.com/repos/hankei6km/gha-now-to-sheet/actions/runs/2106211831 )

    끝말


    GitHub Actions의 시간표 집행과 관련된 각종 시간Google Data Portal이 가시화되었다.
    시각화된 내용과 관련해서는'대기시간이 많은 편','시간대에 따라 1시간 이상 기다려야 한다'등이 파악돼 일정 설정에 참고가 되고 싶었다.
    또한 실제 설치의 경우 GitHub Actions 구글 스프레드시트에 '키 없는 인증을 사용할 수 있어 문턱이 낮아졌다' 는 메시지를 보냅니다.
    먼저 스프레드시트에 저장하면 시각화하는 방법이 많기 때문에 CI가 실행하는 기준 등을 시각화하고자 하는 경우에도 적용할 수 있다.

    좋은 웹페이지 즐겨찾기