Integrate cluster operations into the Git pipeline

Service Account, kubeconfig, and a job for cluster actions from Git.

To run cluster operations from a Git CI/CD pipeline against a Shturval cluster, you need to:

  • create a Service Account in the cluster with the permissions required for your operations;
  • obtain a kubeconfig for that Service Account;
  • add the kubeconfig to the project repository;
  • configure a pipeline job that performs actions in the cluster.

The instructions below show how to create a Namespace from a GitLab pipeline.

Creating a namespace from the GitLab pipeline

Creating a Service Account in a cluster

  1. In the namespace default of the cluster, create a Service Account with the name name-service-account:
ServiceAccount creation command
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: name-service-account
  namespace: default
> EOF
  1. Create a cluster role with the necessary rights to perform actions
Command with an example of creating a role
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: special-sa-role
rules:
- apiGroups:
  - ""
  resources:
  - "namespaces"
  verbs:
  - "get"
  - "create"
  - "list"  
> EOF
  1. Bind the created ClusterRole to the Service Account by creating a ClusterRoleBinding in the cluster.
Command — illustration
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: special-sa-rolebinding
subjects:
    kind: ServiceAccount
    name: name-service-account
    namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: special-sa-role
> EOF
  1. Create a secret secret-sa in the cluster for the Service Account. Specify:
  • namespace name default.
  • the name of the Service Account created in the first step is name-service-account;
  • secret type kubernetes.io/service-account-token
Command — illustration
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: secret-sa
  namespace: default
  annotations:
    kubernetes.io/service-account.name: name-service-account
type: kubernetes.io/service-account-token
> EOF
  1. Add the created secret secret-sa to the Service Account.
Command — illustration
kubectl edit sa name-service-account
Service Account Example
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2024-12-11T05:14:47Z"
  name: name-service-account
  namespace: default
  resourceVersion: "586818"
  uid: cd32a856-76d9-472d-9345-1fe8a755be3f
secrets:
- name: secret-sa
  1. Determine the secret index in the Service Account by running:
Command — illustration
kubectl get serviceaccount name-service-account --context username@shturval-clustername --namespace default -o json

Where instead of username@shturval-clustername specify the required cluster context.

Command for obtaining a list of available cluster contexts
kubectl config get-contexts
Example answer
...
"secrets": [
        {
            "name": "secret-sa"
        }
]

In the example, the secret index secret-sa has a value of 0. Use this index when preparing the cluster kubeconfig.

Preparing the cluster kubeconfig

  1. Create a file kubeconfig.sh with a script that builds a kubeconfig for the Service Account name-service-account.
Example kubeconfig.sh
# Set variables
SERVICE_ACCOUNT_NAME=name-service-account
NAMESPACE=default
NEW_CONTEXT=namesa
KUBECONFIG_FILE='kubeconfig-sa'
# Get current context
CONTEXT=$(kubectl config current-context)
# Get secret name from Service Account
SECRET_NAME=$(kubectl get serviceaccount ${SERVICE_ACCOUNT_NAME} --context ${CONTEXT} --namespace ${NAMESPACE} -o jsonpath='{.secrets[0].name}')
# Get access token and decode base64 data
TOKEN_DATA=$(kubectl get secret ${SECRET_NAME} --context ${CONTEXT} --namespace ${NAMESPACE} -o jsonpath='{.data.token}')
TOKEN=$(echo ${TOKEN_DATA} | base64 -d)
# Create kubeconfig file with full copy of current cluster kubeconfig
kubectl config view --raw > ${KUBECONFIG_FILE}.full.tmp
# Switch working context to current
kubectl --kubeconfig ${KUBECONFIG_FILE}.full.tmp config use-context ${CONTEXT}
kubectl --kubeconfig ${KUBECONFIG_FILE}.full.tmp config view --flatten --minify > ${KUBECONFIG_FILE}.tmp
# Rename context to new context
kubectl config --kubeconfig ${KUBECONFIG_FILE}.tmp rename-context ${CONTEXT} ${NEW_CONTEXT}
# Create user token
kubectl config --kubeconfig ${KUBECONFIG_FILE}.tmp set-credentials ${CONTEXT}-token-user --token ${TOKEN}
# Set context to use Service Account token
kubectl config --kubeconfig ${KUBECONFIG_FILE}.tmp set-context ${NEW_CONTEXT} --user ${CONTEXT}-token-user
# Flatten and get kubeconfig for Service Account
kubectl config --kubeconfig ${KUBECONFIG_FILE}.tmp view --flatten --minify > ${KUBECONFIG_FILE}
rm ${KUBECONFIG_FILE}.full.tmp
rm ${KUBECONFIG_FILE}.tmp

Where instead of 0 in .secrets[0].name, specify the index of the secret from the secrets Service Account array obtained in step 5 of the previous section.

  1. Run the script:
source kubeconfig.sh

As a result, a kubeconfig file named kubeconfig-sa is created for Service Account name-service-account in the current directory.

  1. Copy the contents of kubeconfig-sa:
cat kubeconfig-sa

Add a job to the pipeline

  1. In GitLab, add a CI/CD variable to the project repository. Set the key to KUBECONFIG_file and the value to the kubeconfig contents from the previous step.

  2. Open the CI/CD configuration file (by default, .gitlab-ci.yml).

  3. Add a stage to stages, for example create_ns, to create a Namespace in the cluster:

stages:
  - create_ns
  1. Add a job with these parameters:
  • stage — the stage name from the previous step;
  • before_script — write the KUBECONFIG_file variable to kubeconfig-sa.conf and set KUBECONFIG=kubeconfig-sa.conf;
  • script — the command to create the Namespace.
Example job
create_job:
  stage: create_ns
  before_script:
    echo $KUBECONFIG_file> kubeconfig-sa.conf
    export KUBECONFIG=kubeconfig-sa.conf
  script:
    - kubectl create namespace my-namespace

Replace my-namespace with the namespace name you want to create.

  1. Run the pipeline.

Creating a namespace using the API

  1. Sign in to the Shturval platform. See API authorization.
  2. Create a namespace with curl:
curl -k -s -L --request POST $BACKENDPOINT/api/v1/clusters/{cluster_name}/namespaces?is_system=false
--header "Authorization: Bearer $token"
--data @<(cat <<EOF
    {
  "name": "namespacename",
  "labels": [
    {
      "key": "example",
      "value": "1"
    }
  ]
}
EOF
) | jq -r