Tsung-Jung Tsai (TJ_Tsai)
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    [debug][v1.0.0-rc1] slinky x KEDA === ###### tags: `Slurm / SlinkyProject / debug` ###### tags: `Slurm`, `SlinkyProject`, `slurm-operator`, `slurmctld`, `slurm-exporter`, `Kubernetes`, `k8s`, `KEDA`, `Prometheus`, `Grafana`, `ServiceMonitor`, `Helm`, `kubectl`, `curl`, `jq`, `autoscaling`, `ScaledObject`, `NodeSet`, `CRD`, `Custom Resource`, `scale subresource`, `/scale`, `metrics endpoint`, `Prometheus metrics`, `slurm metrics`, `slurmctld metrics`, `slurm_partition_jobs_pending`, `slurm_partition_jobs_pending_total`, `apiVersion`, `v1alpha1`, `v1beta1`, `slinky.slurm.net`, `keda.sh/v1alpha1`, `controller.metrics.enabled`, `controller.metrics.serviceMonitor.enabled`, `discovery client`, `discovery cache`, `KEDA cache`, `API discovery`, `GVR`, `GVK`, `GroupResource`, `GroupVersionResource`, `GroupVersionKind`, `ScaleClient`, `ScaleForResource`, `discoveryScaleResolver`, `ServerResourcesForGroupVersion`, `meta.k8s.io`, `error reporting`, `cluster-scoped CRD`, `custom CRD scaling`, `Prometheus trigger`, `metricType Value`, `rollout restart`, `troubleshooting`, `debug`, `v1.0.0-rc1`, `v0.4.1`, `KEDA v2.17.2`, `KEDA v2.18.1` <br> [TOC] <br> ## Notes ### 1. slurm-exporter 已於 v1.0.0-rc1 phase out - 根據 [CHANGELOG-1.0.md:95](https://github.com/SlinkyProject/slurm-operator/blob/main/CHANGELOG/CHANGELOG-1.0.md) 明確指出: > Replaced slurm-exporter with a serviceMonitor that scrapes slurmctld directly. - slurm-exporter 已被移除,改為直接從 slurmctld 抓取 metrics。 <br> ### 2. v1.0.0-rc1 如何匯出指標? - 新版作法是透過 **slurmctld 內建的 metrics endpoints**: - ### 啟用 metrics 安裝時需設定: ```sh helm install slurm oci://ghcr.io/slinkyproject/charts/slurm \ --set 'controller.metrics.enabled=true' \ --set 'controller.metrics.serviceMonitor.enabled=true' \ --namespace slurm --create-namespace ``` - ### 可用的 metrics endpoints 根據 [helm/slurm/values.yaml:275-278](https://github.com/SlinkyProject/slurm-operator/blob/main/helm/slurm/values.yaml#L275-L278) 及 [internal/builder/controller_servicemonitor.go:55-60](https://github.com/SlinkyProject/slurm-operator/blob/main/internal/builder/controller_servicemonitor.go#L55-L60),預設提供四個 endpoints: - `/metrics/jobs` - `/metrics/nodes` - `/metrics/partitions` - `/metrics/scheduler` - ### 如果忘了 metric URL ,如何找出 metrics endpoints? - **STEP1**:查詢 controller IP & port ``` $ kubectl -n slurm get pod,svc -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/slurm-controller-0 3/3 Running 0 3d15h 192.168.0.218 stage-kube01 <none> <none> NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR service/slurm-controller ClusterIP 10.107.212.234 <none> 6817/TCP 3d15h app.kubernetes.io/instance=slurm,app.kubernetes.io/name=slurmctld ``` - 192.168.0.218:6817 - 10.107.212.234:6817 - **STEP2**: 透過 `curl <ip>:<port>` 獲取資訊 ``` $ curl 192.168.0.218:6817 slurmctld index of endpoints: '/readyz': check slurmctld is servicing RPCs '/livez': check slurmctld is running '/healthz': check slurmctld is running '/metrics': print available metric endpoints ``` ``` $ curl 10.107.212.234:6817 slurmctld index of endpoints: '/readyz': check slurmctld is servicing RPCs '/livez': check slurmctld is running '/healthz': check slurmctld is running '/metrics': print available metric endpoints ``` - **STEP3**: 透過 `curl <ip>:<port>/metrics` 獲取資訊 ``` $ curl 192.168.0.218:6817/metrics slurmctld index of metrics endpoints: '/metrics/jobs': get job metrics '/metrics/nodes': get node metrics '/metrics/partitions': get partition metrics '/metrics/jobs-users-accts': get user and account jobs metrics '/metrics/scheduler': get scheduler metrics ``` - **STEP4**: 選擇 metrics 來源:jobs, nodes, partitions, jobs-users-accts, scheduler ``` $ curl -s 192.168.0.218:6817/metrics/partitions | head # HELP slurm_partitions Total number of partitions # TYPE slurm_partitions gauge slurm_partitions 5 # HELP slurm_partition_jobs Number of jobs in this partition # TYPE slurm_partition_jobs gauge slurm_partition_jobs{partition="slinky"} 0 slurm_partition_jobs{partition="all"} 0 slurm_partition_jobs{partition="book"} 0 slurm_partition_jobs{partition="tainan"} 0 slurm_partition_jobs{partition="tp"} 7 ``` - ### 新版 ScaledObject 範例 > 參考文件:https://github.com/SlinkyProject/slurm-operator/blob/main/docs/usage/autoscaling.md#keda-scaledobject ```yaml= # config-slurm-worker-241-gpu1080-scaler.yaml apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: slurm-worker-gpu1080-scaler namespace: slurm spec: scaleTargetRef: apiVersion: slinky.slurm.net/v1beta1 # (1) 改為 v1beta1 kind: NodeSet name: slurm-worker-gpu1080 idleReplicaCount: 0 minReplicaCount: 1 maxReplicaCount: 3 triggers: - type: prometheus metricType: Value metadata: # $> kubectl run curl-test -it --rm --image=curlimages/curl -- sh # $> nslookup kube-prometheus-stack-prometheus.monitoring.svc.cluster.local # $> curl http://kube-prometheus-stack-prometheus.monitoring:9090/api/v1/query?query=up serverAddress: http://kube-prometheus-stack-prometheus.monitoring:9090 # depend on environments query: slurm_partition_jobs_pending{partition="tp"} # (2) 拿掉 API suffix: _total threshold: '5' ``` - ### 主要變更 1. **apiVersion**: `v1alpha1` → `v1beta1` 2. **指標來源**: slurm-exporter → slurmctld 直接提供 3. **指標名稱**: `slurm_partition_jobs_pending_total` → `slurm_partition_jobs_pending` - ### 透過 Grafana 觀看 ![image](https://hackmd.io/_uploads/S1ZuIs--be.png) - ### 透過 endpoints 查詢 ``` $ curl -s http://192.168.0.218:6817/metrics/partitions | grep slurm_partition_jobs_pending # HELP slurm_partition_jobs_pending Number of jobs in Pending state # TYPE slurm_partition_jobs_pending gauge slurm_partition_jobs_pending{partition="slinky"} 0 slurm_partition_jobs_pending{partition="all"} 0 slurm_partition_jobs_pending{partition="book"} 0 slurm_partition_jobs_pending{partition="tainan"} 0 slurm_partition_jobs_pending{partition="tp"} 0 ``` - 已經沒有 `slurm_partition_jobs_pending_total` <br> --- <br> ## 診斷問題:`"error": "the server could not find the requested resource (get nodesets.meta.k8s.io slurm-worker-gpu1080)"` ### keda/vendor/k8s.io/client-go/discovery/fake/discovery.go https://github.com/kedacore/keda/blob/release/v2.17/vendor/k8s.io/client-go/discovery/fake/discovery.go#L63 ### 診斷指令 - ### Step1:ScaledObject 設定與狀態,對應的 KEDA 錯誤訊息 ```bash # 1. 檢查 NodeSet 是否存在 $ kubectl -n slurm get nodeset slurm-worker-gpu1080 NAME REPLICAS UPDATED READY AGE slurm-worker-gpu1080 # 2. 查看 ScaledObject 詳細狀態 kubectl -n slurm describe scaledobject slurm-worker-gpu1080-scaler # 3. 查看 ScaledObject 完整 YAML kubectl -n slurm get scaledobject slurm-worker-gpu1080-scaler -o yaml # 4. 查看 KEDA operator logs kubectl -n keda logs -l app=keda-operator --tail=50 ``` - ### Step2:NodeSets 的 CRD 版本,以及是否支援 scale subresource ```bash # 1. 檢查 CRD 版本 $ kubectl get crd nodesets.slinky.slurm.net -o jsonpath='{.spec.versions[*].name}' v1beta1 # 2. 檢查是否有 scale subresource $ kubectl get crd nodesets.slinky.slurm.net -o jsonpath='{.spec.versions[?(@.name=="v1beta1")].subresources}' | jq { "scale": { "labelSelectorPath": ".status.selector", "specReplicasPath": ".spec.replicas", "statusReplicasPath": ".status.replicas" }, "status": {} } # or # $ kubectl get crd nodesets.slinky.slurm.net -o yaml | grep -A 10 subresources # 3. 如果 CRD 缺少 scale subresource,需要重新安裝或更新 CRD #helm upgrade slurm oci://ghcr.io/slinkyproject/charts/slurm \ # --namespace slurm \ # --reuse-values ``` - ### Step3:確認 scale subresource 是否可存取 ```bash # 1. 直接測試 scale API $ kubectl get --raw "/apis/slinky.slurm.net/v1beta1/namespaces/slurm/nodesets/slurm-worker-gpu1080/scale" | jq { "kind": "Scale", "apiVersion": "autoscaling/v1", "metadata": { "name": "slurm-worker-gpu1080", "namespace": "slurm", "uid": "83e0e85e-8758-4157-b0c5-de928faead60", "resourceVersion": "25964227", "creationTimestamp": "2025-11-21T10:31:39Z" }, "spec": {}, "status": { "replicas": 0, "selector": "app.kubernetes.io/instance=slurm-worker-gpu1080,app.kubernetes.io/name=slurmd" } } # 2. 檢查 KEDA 版本(需 v2.10+ 以完整支援自訂 CRD scaling) $ kubectl -n keda get deploy keda-operator -o jsonpath='{.spec.template.spec.containers[0].image}' ghcr.io/kedacore/keda:2.17.2 ``` <br> ### 診斷結果 - ### 如果 CRD 已經有 scale subresource,問題可能是**: - CRD 的 `.spec.replicas` 或 `.status.replicas` 欄位路徑設定錯誤 - NodeSet CR 實例沒有這些欄位 - KEDA 的 discovery client 快取問題(需要重啟 KEDA operator) - ### 深度分析:問題根本原因 根據程式碼追蹤,我找到了問題的關鍵: ### **錯誤發生的完整流程** 1. **KEDA 在 `scaledobject_controller.go:409` 呼叫**: ```go scale, errScale = (r.ScaleClient).Scales(scaledObject.Namespace).Get(ctx, gr, scaledObject.Spec.ScaleTargetRef.Name, metav1.GetOptions{}) ``` 其中 `gr` 是 `schema.GroupResource{Group: "slinky.slurm.net", Resource: "nodesets"}` 2. **Scale client 在 `client.go:187` 呼叫 ScaleForResource**: ```go desiredGVK, err := c.client.scaleKindResolver.ScaleForResource(gvr) ``` - 這裡 `gvr` 是 `slinky.slurm.net/v1beta1/nodesets` - GVK 就是 group、verison、kind - GVR 就是group、version、resource 3. **discoveryScaleResolver 在 `util.go:63-88` 查詢 scale subresource**: ```go func (r *discoveryScaleResolver) ScaleForResource(inputRes schema.GroupVersionResource) (scaleVersion schema.GroupVersionKind, err error) { groupVerResources, err := r.discoveryClient.ServerResourcesForGroupVersion(inputRes.GroupVersion().String()) for _, resource := range groupVerResources.APIResources { resourceParts := strings.SplitN(resource.Name, "/", 2) if len(resourceParts) != 2 || resourceParts[0] != inputRes.Resource || resourceParts[1] != "scale" { continue } scaleGV := inputRes.GroupVersion() if resource.Group != "" && resource.Version != "" { scaleGV = schema.GroupVersion{ Group: resource.Group, Version: resource.Version, } } return scaleGV.WithKind(resource.Kind), nil } return schema.GroupVersionKind{}, fmt.Errorf("could not find scale subresource for %s in discovery information", inputRes.String()) } ``` 4. **當找不到 scale subresource 時返回錯誤**,錯誤訊息顯示 `meta.k8s.io` 是因為**錯誤訊息中的 resource path 被錯誤格式化** ### **問題的真正原因** `meta.k8s.io` 出現在錯誤訊息中,是因為: 1. **KEDA 的 discovery client 沒有正確發現 NodeSet 的 scale subresource** 2. **可能的原因**: - KEDA operator 啟動時快取了舊的 API discovery 資訊 - CRD 在 KEDA operator 啟動後才更新了 scale subresource - **Discovery cache 沒有正確更新** <-- ### **解決方案** 請依照以下步驟操作: ```bash # 1. 重啟 KEDA operator 以清除 discovery cache kubectl rollout restart deployment keda-operator -n keda # 2. 等待重啟完成 kubectl rollout status deployment keda-operator -n keda # 3. 刪除並重新建立 ScaledObject(可選,但建議執行) kubectl delete -f config-slurm-worker-241-gpu1080-scaler.yaml kubectl apply -f config-slurm-worker-241-gpu1080-scaler.yaml # 4. 監控 logs 確認問題解決 kubectl -n keda logs -l app=keda-operator -f ``` ### **為什麼會出現 `meta.k8s.io`?** 這是 Kubernetes error reporting 的特性:當 discovery client 無法找到特定 GVR 的 scale subresource 時,錯誤訊息會使用 `meta.k8s.io` 作為預設的 API group 來格式化錯誤訊息,這只是錯誤格式化的副作用,**並非 KEDA 真的去查詢了 `meta.k8s.io` API group**。 <br> ### KEDA cache 問題的重製步驟: **關鍵**:apiVersion 變化:v1alpha1 <-> v1beta1 Step1: 清空 KEDA cache (rollout restart) Step2: 部署 v1alpha1 版本的 slinky (v0.4.1) Step3: 部署 v1alpha1 版本的 worker scaler -> READY: true Step4: 移除 v1alpha1 版本的 slinky & worker scaler Step5: 部署 v1beta1 版本的 sliny (v1.0.0-rc1) Step6: 部署 v1beta1 版本的 worker scaler -> READY: false (delete scaler 後,重新再 apply ,READY 狀態依舊是 false ) Step7: 清空 KEDA cache (rollout restart) -> worker scaler : READY: true <br> ### 2025/11/26 - 是否跟 Commit 6a7301a 有關? > https://github.com/alvidofaisal/keda/commit/6a7301a5f71c9a136592621a6172734e350d40b9 > Fix(scaling): Correct API group resolution for cluster-scoped CRDs This commit addresses an issue where KEDA would incorrectly attempt to query the `meta.k8s.io` API group when scaling cluster-scoped Custom Resources (CRs) that have a /scale subresource. > > The primary change involves updating `pkg/k8s/scaleclient.go` to use `scale.NewFixedScaleKindResolver`. This allows KEDA to directly use the `apiVersion` and `kind` specified in the `ScaledObject`'s `spec.scaleTargetRef` to resolve the API group, ensuring correct interaction with cluster-scoped CRDs. > > A new test case, `testClusterScopedCRDScale`, has been added to `tests/internals/subresource_scale/subresource_scale_test.go` to specifically verify the scaling of cluster-scoped CRDs. This test includes: > - Definition of a cluster-scoped CRD (`ClusterScaler`) with a `/scale` subresource. > - Creation of a `ScaledObject` targeting an instance of `ClusterScaler`. > - Verification of scale-out and scale-in operations. > - Checks to ensure no errors related to incorrect API group querying appear in KEDA operator logs. > > **Note:** Due to environmental limitations (out of disk space) I encountered during the development process, I couldn't complete the full build and end-to-end testing (including the new test case). I recommend you manually verify and further test in a stable environment. > > Fixes kedacore#6798 <br> ### 2025/11/26 - 使用 keda v2.18.1 (latest) 版本測試,問題依舊 ![image](https://hackmd.io/_uploads/HkxtuX4--e.png) <br> {%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully