Skip to main content

Build CI/CD pipeline with Codeflow and Deployflow

Use KakaoCloud Codeflow and Deployflow together to build a basic CI/CD pipeline covering source management, container image builds, and Kubernetes deployment.

Basic information
Related tutorial

This tutorial connects Codeflow and Deployflow to build a basic CI/CD pipeline. After deploying the cicd-app-demo application, continue with Implement Blue/Green deployment with Deployflow to apply a Blue/Green strategy to the same application.

About this scenario​

In this tutorial, you build a basic CI/CD pipeline with KakaoCloud Codeflow and Deployflow. You manage application source and Kubernetes manifests in a Codeflow repository, build a container image with a Codeflow workflow, and configure Deployflow to apply manifest changes to a Kubernetes Engine cluster.

After completing the tutorial, running the Codeflow workflow builds and pushes an image with a new tag, commits the manifest update, and lets Deployflow apply that change to the cluster.

The workflow is as follows.

  1. Source management (Codeflow): Store application source and Kubernetes manifests in a Codeflow repository.
  2. Image build (Codeflow Actions): Run a Codeflow workflow on a self-hosted runner.
  3. Image upload (Container Registry): Push the built image to KakaoCloud Container Registry.
  4. Manifest update (Codeflow Actions): Update the image tag in k8s/deployment.yaml and commit the change.
  5. Deployment automation (Deployflow): Detect the manifest change and synchronize it to the Kubernetes cluster, which pulls the specified image from Container Registry and runs the Pods.

Finally, verify the Deployflow state, access the Service, and confirm that rerunning the workflow updates the cluster.

[Local] git push ──▶ Codeflow repository ──▶ Codeflow workflow (runner)
│ │
│ ├── ① Build image ──▶ Container Registry
│ │
├◀───────────────────────┴── ② Update and commit manifest tag
│
▼
Deployflow ──▶ Deploy to Kubernetes Engine cluster
Related tutorial

Build GitOps in a KakaoCloud Kubernetes environment uses GitHub Actions and ArgoCD. KakaoCloud also provides the same CI/CD flow through the native Codeflow + Deployflow services. This tutorial walks through that native configuration.

Before you start​

Create IAM access key​

Codeflow Git authentication (clone and push) and Container Registry authentication require an IAM access key.

  1. At the top right of the console, go to Account menu > Account settings > Credentials > IAM access keys.
  2. Click [Create IAM access key], select the project, and issue the key.
  3. Store the displayed Access key ID and Secret access key securely.
Caution

The secret access key is displayed only immediately after creation. If you lose it, you must issue a new key. For details, see Create access key.

Create Container Registry​

Create a repository for the built container image.

  1. In KakaoCloud Console, go to Container Pack > Container Registry and click [Create repository].

  2. Create the repository with the following settings.

    ItemValue
    VisibilityPrivate
    Repository nametutorial
    Tag overwriteEnabled
    Image scanAutomatic

Prepare Kubernetes cluster​

Deployflow deploys to an existing Kubernetes cluster. Create a cluster by following Build a Kubernetes cluster with Kubernetes Engine.

In Step 5 and while verifying the deployment, you also inspect the cluster with local kubectl. Follow Configure kubectl access to prepare local kubeconfig access.

At least one worker node required

The node pool must contain at least one worker node. With zero nodes, Pods cannot be scheduled and remain Pending with the event no nodes available to schedule pods.

Getting started​

Step 1. Create Codeflow repository​

  1. In KakaoCloud Console, go to Developer Tools > Codeflow. If this is your first time using the service, click [Get started].

  2. Under Repositories, click [Create repository] and enter the following information.

    ItemValueDescription
    Namecicd-app-demoRepository name
    DescriptionOptionalRepository description
    Default branch namemainInitial default branch
    Approval ruleSingle approvalApproval condition for merging pull requests
  3. Click [Create] to add the repository.

  4. Select the repository and copy its URL from the Details tab. The URL has the form https://codeflow-git.kr-central-2.kakaocloud.com/<PROJECT>/cicd-app-demo.git.

Step 2. Prepare and push sample application​

This tutorial uses argoproj/rollouts-demo. Its COLOR build argument changes the screen color, making deployments easy to verify. Clone the source, point its remote to the Codeflow cicd-app-demo repository, and add Kubernetes manifests and a workflow.

  1. Clone the sample and change its remote to the Codeflow repository. Authenticate to Git by including the IAM access key in the URL.

    git clone https://github.com/argoproj/rollouts-demo cicd-app-demo
    cd cicd-app-demo
    git branch -M main # Rename the rollouts-demo default branch from master to main
    git remote set-url origin https://${ACCESS_KEY_ID}:${SECRET_ACCESS_KEY}@codeflow-git.kr-central-2.kakaocloud.com/${PROJECT}/cicd-app-demo.git
    VariableDescription
    ACCESS_KEY_ID🖌︎IAM access key ID
    SECRET_ACCESS_KEY🖌︎Secret access key
    PROJECT🖌︎KakaoCloud project name
    Information

    rollouts-demo includes Go source and a Dockerfile, so you do not need to write application code. The Dockerfile embeds the COLOR build argument in the image, and the application uses it for the screen color.

  2. Create deployment manifests under k8s/. This becomes the Deployflow Raw Manifest path.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: cicd-app-demo
    namespace: deployflow
    labels:
    app: cicd-app-demo
    spec:
    replicas: 2
    revisionHistoryLimit: 3
    selector:
    matchLabels:
    app: cicd-app-demo
    template:
    metadata:
    labels:
    app: cicd-app-demo
    spec:
    imagePullSecrets:
    - name: cr-pull-secret # Private CR image pull; created in Step 5
    containers:
    - name: cicd-app-demo
    image: ${PROJECT}.kr-central-2.kcr.dev/tutorial/cicd-app-demo:blue
    imagePullPolicy: Always
    ports:
    - name: http
    containerPort: 8080
    readinessProbe:
    httpGet: { path: /, port: http }
    initialDelaySeconds: 3
    periodSeconds: 5
    VariableDescription
    PROJECT🖌︎KakaoCloud project name
    k8s/service.yaml
    apiVersion: v1
    kind: Service
    metadata:
    name: cicd-app-demo
    namespace: deployflow
    labels:
    app: cicd-app-demo
    annotations:
    # Create an NLB with a public IP
    loadbalancer.ke.kakaocloud.com/load-balancer-type: 'NLB'
    service.beta.kubernetes.io/openstack-internal-load-balancer: 'false'
    spec:
    type: LoadBalancer
    selector:
    app: cicd-app-demo
    ports:
    - name: http
    port: 80
    targetPort: http
    Why use a LoadBalancer Service

    The Service uses the LoadBalancer type so you can view the sample application in a browser. To create a load balancer with a public IP in KakaoCloud Kubernetes Engine, use the openstack-internal-load-balancer: 'false' annotation. For details, see Create LoadBalancer Service.

    Charges and exposure

    A public load balancer incurs charges and exposes the application to the internet. Delete the application and Service, including the load balancer, after the lab.

  3. Create the workflow under .codeflow/workflows/. It accepts color and tag, builds the image, updates the image tag in the deployment manifest, and commits the change. The workflow file must be on the default branch to appear in the console.

    .codeflow/workflows/cicd.yml
    name: cicd

    on:
    workflow_dispatch:
    inputs:
    tag:
    description: 'Docker Image Tag'
    required: true
    default: 'blue'
    color:
    description: 'Color (blue/green)'
    required: true
    default: 'blue'

    env:
    DOCKER_IMAGE_TAG: ${{ github.event.inputs.tag }}

    jobs:
    build:
    runs-on: [ codeflow-runner ] # Must match the runner label registered in Step 3
    steps:
    - uses: actions/checkout@v7
    - name: Set up Docker buildx
    uses: docker/setup-buildx-action@v4
    with:
    driver: docker-container
    - name: Log in to Container Registry
    uses: docker/login-action@v4
    with:
    registry: ${{ vars.KCR_REPO_PROJECT }}.kr-central-2.kcr.dev
    username: ${{ vars.KCR_REPO_USER }}
    password: ${{ vars.KCR_REPO_SECRET }}
    - name: Build and push image with COLOR embedded
    uses: docker/build-push-action@v4
    with:
    context: .
    file: ./Dockerfile
    platforms: linux/amd64
    push: true
    tags: ${{ vars.KCR_REPO_PROJECT }}.kr-central-2.kcr.dev/tutorial/cicd-app-demo:${{ env.DOCKER_IMAGE_TAG }}
    build-args: |
    COLOR=${{ github.event.inputs.color }}

    update-manifest:
    runs-on: [ codeflow-runner ]
    needs: build
    steps:
    - uses: actions/checkout@v7
    - name: Update and commit manifest image tag
    run: |
    IMAGE=${{ vars.KCR_REPO_PROJECT }}.kr-central-2.kcr.dev/tutorial/cicd-app-demo:${{ env.DOCKER_IMAGE_TAG }}
    sed -i "s|image: .*/cicd-app-demo:.*|image: ${IMAGE}|" k8s/deployment.yaml
    git config user.email "ci@codeflow"
    git config user.name "codeflow-ci"
    if git diff --quiet k8s/deployment.yaml; then
    echo "No changes; skipping commit"
    else
    git add k8s/deployment.yaml
    git commit -m "ci: update image to ${{ env.DOCKER_IMAGE_TAG }}"
    # Push the commit back using the IAM access key stored in KCR_REPO_*
    git remote set-url origin https://${{ vars.KCR_REPO_USER }}:${{ vars.KCR_REPO_SECRET }}@codeflow-git.kr-central-2.kakaocloud.com/${{ vars.KCR_REPO_PROJECT }}/cicd-app-demo.git
    git push origin HEAD:main
    fi
    Workflow trigger

    This is a manual (workflow_dispatch) workflow. In Step 4, enter color and tag in the console. For the initial deployment, set both to blue.

  4. Commit and push the manifests and workflow.

    commit & push
    git add k8s .codeflow
    git commit -m "init: manifests, workflow"
    git push origin main

    After the push, verify the content under Details > Code / Commits in the console repository.

Manage credentials

The IAM access key stored in the workflow variables (vars.KCR_REPO_*) is used for both Container Registry login and Git authentication when pushing the manifest commit. In production, use a dedicated access key with a reviewed permission scope and prevent unnecessary sharing.

Step 3. Create runner VM and register runner​

A self-hosted runner is required to execute the workflow. In this tutorial, you configure the runner on a KakaoCloud Virtual Machine (VM) instead of a local computer. The VM can remain available continuously, and you install Docker on it for image builds.

  1. Create a runner VM: In KakaoCloud Console, go to Compute > Beyond Compute Service > Virtual Machine > Instances, click [Create instance], and use the following settings.

    CategoryItemSetting or valueNotes
    Basic informationNamecodeflow-runner
    Count1
    ImageUbuntu 22.04
    Instance typem2a.largeAdjust for image build load
    VolumeRoot volume30Space for Docker image builds
    Key pair{USER_KEYPAIR}Store the key securely; it cannot be recovered
    NetworkSubnetPublic subnetInternet access required
    Security groupcodeflow-runner-sgAllow inbound SSH (22) from your IP
    Information

    The runner VM requires outbound internet access to the Codeflow Git host (codeflow-git.kr-central-2.kakaocloud.com) and Container Registry (*.kcr.dev). Use a public subnet and public IP, or configure NAT for a private subnet. The security group needs only inbound SSH (22) from your IP.

  2. Connect to the VM and install Docker: Associate a public IP, connect over SSH, and install Docker for the runner.

    chmod 400 ${KEY_FILE}.pem
    ssh -i ${KEY_FILE}.pem ubuntu@${VM_PUBLIC_IP}

    # On the VM
    sudo apt-get update
    sudo apt-get install -y docker.io
    sudo usermod -aG docker ubuntu # Use Docker without sudo
    newgrp docker # Or reconnect over SSH
    VariableDescription
    KEY_FILE🖌︎Key file name
    VM_PUBLIC_IP🖌︎Runner VM public IP
  3. Check the repository token: Copy the Repository token from Details > Action settings > Repository settings. This token registers the runner and differs from the IAM access key used for Git authentication.

Run the runner as an executable. The --labels value must match runs-on (codeflow-runner) in the workflow.

On the Action settings > Runners tab, copy the Linux (amd64) download URL. Download the runner to the VM, grant execute permission, register it, and start the daemon. Use the repository token as the runner token.

# 1) Download and grant execute permission
curl -L -o codeflow_runner "${RUNNER_URL}"
chmod +x ./codeflow_runner

# 2) Register runner
./codeflow_runner register \
--endpoint-url https://codeflow-git.kr-central-2.kakaocloud.com \
--token ${REPO_TOKEN} \
--name vm-runner \
--labels codeflow-runner \
--no-interactive

# 3) Start daemon in the background
nohup ./codeflow_runner daemon > runner.log 2>&1 &
VariableDescription
RUNNER_URL🖌︎Linux amd64 runner download URL
REPO_TOKEN🖌︎Repository token

The VM runs Linux, so you do not need the macOS xattr quarantine removal step.

After starting the runner, verify that its state is Idle under Action settings > Runners. The state is Idle while waiting and Active while processing a job.

Information

The runner download URL and detailed options are available on the Action settings > Runners tab and in Codeflow action settings. The runner processes workflows while the VM and runner are running. For more reliable operation, consider registering a systemd service or deploying the runner to a Kubernetes cluster.

Step 4. Build image and update deployment manifest with workflow​

  1. Under Details > Action settings > Variables in the repository, click [Create] and add the following variables for Container Registry authentication.

    KeyValue
    KCR_REPO_PROJECTKakaoCloud project name
    KCR_REPO_USERIAM access key ID
    KCR_REPO_SECRETSecret access key
  2. Under Details > Workflows > Workflow files, verify that .codeflow/workflows/cicd.yml is recognized and click [Run workflow]. Select the main branch and enter the following parameters to build the blue image.

    ParameterValueDescription
    tagblueImage tag
    colorblueScreen color embedded as a build argument
  3. Check the run under Workflow run history. The state progresses from Queued to Running to Succeeded. Use [⋮] > View logs to inspect each step.

    Workflow log screen Workflow log screen example

    Information

    If the run remains Queued, verify that the runner daemon is running and that the workflow's runs-on label matches the runner's --labels value (codeflow-runner).

  4. A successful workflow produces two results.

    • It pushes the cicd-app-demo:blue image to Container Registry. Check Container Pack > Container Registry > tutorial in the console.
    • It updates the image tag in k8s/deployment.yaml to :blue and creates an automatic codeflow-ci commit. Deployflow synchronizes this commit.

Step 5. Create image pull secret​

The cluster needs a docker-registry secret in the target namespace to pull the private Container Registry image. After configuring local cluster access with kubeconfig, run these commands.

kubectl create namespace deployflow

kubectl -n deployflow create secret docker-registry cr-pull-secret \
--docker-server=${PROJECT}.kr-central-2.kcr.dev \
--docker-username=${ACCESS_KEY_ID} \
--docker-password=${SECRET_ACCESS_KEY}
VariableDescription
PROJECT🖌︎KakaoCloud project name
ACCESS_KEY_ID🖌︎IAM access key ID
SECRET_ACCESS_KEY🖌︎Secret access key
Information

The secret name (cr-pull-secret) and namespace (deployflow) must match the values in k8s/deployment.yaml from Step 2.

Step 6. Create Deployflow application​

Create a Deployflow application that deploys manifests from the Codeflow repository to the target cluster.

  1. In KakaoCloud Console, go to Developer Tools > Deployflow. The left menu contains Applications and Approval management.

  2. Under Applications, click [Create application] and enter a name and description under Basic information.

  3. Enter Cluster information, Source information, and Deployment method as follows.

    ItemValueDescription
    Namecicd-app-demoApplication name
    Deployment clusterThe cluster you createdTarget cluster with at least one node
    Namespace namedeployflowTarget namespace
    Source repositorycicd-app-demoCodeflow repository containing manifests
    Source branchmainBranch to use
    Deployment methodRaw ManifestApply YAML files directly
    Raw Manifest pathk8sPath relative to repository root, without a leading slash
    Three deployment methods
    • Raw Manifest: Apply YAML files from the path directly.
    • Kustomize: Apply an overlay based on kustomization.yaml.
    • Helm: Deploy from a Helm chart.
  4. Optionally configure Approval settings and Advanced options. You can keep their defaults for this tutorial.

    OptionDescription
    Approval setting optionWhen enabled, synchronization, deployment, and rollback create approval requests instead of applying immediately. An approver must approve them under Approval management. Default: disabled.
    Resource deletion optionForeground waits until dependent resources are deleted; Background deletes the target immediately and cleans up dependents in the background; Non-cascading retains dependents.
    ServerSideApplyLets the API server calculate changes and track field ownership. Useful for large manifests, CRDs, or resources managed by multiple controllers; optional for this simple deployment.
  5. Review the values under Summary and click [Create]. In the list, check the new application's State, Sync state, Deployment strategy state, Cluster, and Source repository.

Step 7. Synchronize and verify deployment​

Immediately after creation, the source manifests differ from the cluster, so the sync state is Out of Sync. Synchronize manually to deploy.

  1. Select the application to open its details pane. Under Details, verify that the sync state is Out of Sync and review the source revision and path.

    TabContent
    DetailsName, cluster, namespace, source, revision, path, state, and sync state
    Approval stateApproval requests when approval settings are enabled
    Deployment strategyDeployment settings such as automatic synchronization
    ResourcesTopology and resource summary, manifests, events, and logs
    HistoryDeployment and rollback history
  2. Click [Synchronize] at the top of the details pane. Review the Resource deletion option and Force synchronization setting, then click [Synchronize] in the dialog. A success message appears.

  3. Verify that Sync state is Synced and State is Healthy.

  4. On the Resources tab, inspect the Application → Deployment → ReplicaSet → Pod → Service topology and each resource's summary, manifest, events, and logs.

  5. Open the Service's public EXTERNAL-IP and verify the blue rollouts-demo screen. Load balancer provisioning takes about one to two minutes.

    kubectl -n deployflow get svc cicd-app-demo -w
    # When EXTERNAL-IP changes from <pending> to a public IP, press Ctrl+C.
    # Open http://<EXTERNAL-IP> and verify the blue screen.

    Blue screen Blue screen example

tip

If the state does not change immediately after the success message, click Refresh to retrieve the difference between the manifest and actual cluster state again.

Pod is Pending or ImagePullBackOff
  • If a Pod is Pending and the application remains Missing, the target cluster usually has no nodes.
  • If ImagePullBackOff appears, the target namespace is missing cr-pull-secret or the secret is incorrect.

Check the Events tab in resource details for the cause.

Step 8. Enable automatic synchronization and verify CI/CD flow​

First enable automatic synchronization in Deployflow, then rerun the workflow in Codeflow. Verify that Codeflow builds and pushes the new image and commits the updated manifest, after which Deployflow automatically synchronizes the cluster.

Enable automatic synchronization in Deployflow​

  1. Select the application in Deployflow and click [Configure deployment strategy] in the details pane.
  2. Enable Deployment strategy option, select Automatic synchronization as the Deployment type, and click [Save].
  3. Verify that Deployment strategy state shows Automatic synchronization in the list and details.

Run workflow in Codeflow​

  1. Under Details > Workflows in Codeflow, rerun the workflow from Step 4 with these parameters.

    ParameterValueDescription
    tagv2New image tag
    colorblueScreen color
  2. After the workflow succeeds, verify automatic synchronization in Deployflow.

    • The workflow builds and pushes cicd-app-demo:v2, then updates and commits the tag in k8s/deployment.yaml.
    • Deployflow detects the manifest change, synchronizes it, and updates the Last synchronized at value and resource topology.
    • The application shows a Sync state of Synced and a State of Healthy.
Caution

With automatic synchronization enabled, source changes are applied directly to the target application. Carefully verify the application, cluster, namespace, and path.

Rollback

If a problem occurs after deployment, review earlier deployments on the Deployflow application's History tab and roll back. Check the target history ID and manifest before rollback.

When automatic synchronization is enabled, a later source manifest change can synchronize the application again. To retain the rollback state, also revert the manifest in the Codeflow repository.

Next step​

You have connected a Codeflow workflow to Deployflow Raw Manifest deployment to build a basic CI/CD pipeline. To apply a Blue/Green strategy to the same application and switch traffic after verifying Green, continue with Implement Blue/Green deployment with Deployflow.