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.
- Estimated time: 60 minutes
- Prerequisites
- IAM access key
- A local environment that can run
git,kubectl, andssh; Docker for image builds is installed on the runner VM in Step 3 - Build a Kubernetes cluster with Kubernetes Engine, with at least one worker node
- Configure kubectl access to access the target cluster locally using kubeconfig
- A Container Registry repository
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.
- Source management (Codeflow): Store application source and Kubernetes manifests in a Codeflow repository.
- Image build (Codeflow Actions): Run a Codeflow workflow on a self-hosted runner.
- Image upload (Container Registry): Push the built image to KakaoCloud Container Registry.
- Manifest update (Codeflow Actions): Update the image tag in
k8s/deployment.yamland commit the change. - 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
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.
- At the top right of the console, go to Account menu > Account settings > Credentials > IAM access keys.
- Click [Create IAM access key], select the project, and issue the key.
- Store the displayed Access key ID and Secret access key securely.
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.
-
In KakaoCloud Console, go to Container Pack > Container Registry and click [Create repository].
-
Create the repository with the following settings.
Item Value Visibility Private Repository name tutorialTag overwrite Enabled Image scan Automatic
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.
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
-
In KakaoCloud Console, go to Developer Tools > Codeflow. If this is your first time using the service, click [Get started].
-
Under Repositories, click [Create repository] and enter the following information.
Item Value Description Name cicd-app-demoRepository name Description Optional Repository description Default branch name mainInitial default branch Approval rule Single approval Approval condition for merging pull requests -
Click [Create] to add the repository.
-
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.
-
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.gitVariable Description ACCESS_KEY_ID🖌︎ IAM access key ID SECRET_ACCESS_KEY🖌︎ Secret access key PROJECT🖌︎ KakaoCloud project name Informationrollouts-demoincludes Go source and aDockerfile, so you do not need to write application code. TheDockerfileembeds theCOLORbuild argument in the image, and the application uses it for the screen color. -
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: 5Variable Description PROJECT🖌︎ KakaoCloud project name k8s/service.yamlapiVersion: 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: httpWhy use a LoadBalancer ServiceThe Service uses the
LoadBalancertype so you can view the sample application in a browser. To create a load balancer with a public IP in KakaoCloud Kubernetes Engine, use theopenstack-internal-load-balancer: 'false'annotation. For details, see Create LoadBalancer Service.Charges and exposureA public load balancer incurs charges and exposes the application to the internet. Delete the application and Service, including the load balancer, after the lab.
-
Create the workflow under
.codeflow/workflows/. It acceptscolorandtag, 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.ymlname: 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
fiWorkflow triggerThis is a manual (
workflow_dispatch) workflow. In Step 4, entercolorandtagin the console. For the initial deployment, set both toblue. -
Commit and push the manifests and workflow.
commit & pushgit add k8s .codeflow
git commit -m "init: manifests, workflow"
git push origin mainAfter the push, verify the content under Details > Code / Commits in the console repository.
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.
-
Create a runner VM: In KakaoCloud Console, go to Compute > Beyond Compute Service > Virtual Machine > Instances, click [Create instance], and use the following settings.
Category Item Setting or value Notes Basic information Name codeflow-runnerCount 1 Image Ubuntu 22.04 Instance type m2a.large Adjust for image build load Volume Root volume 30 Space for Docker image builds Key pair {USER_KEYPAIR}Store the key securely; it cannot be recovered Network Subnet Public subnet Internet access required Security group codeflow-runner-sgAllow inbound SSH (22) from your IP InformationThe 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. -
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 SSHVariable Description KEY_FILE🖌︎ Key file name VM_PUBLIC_IP🖌︎ Runner VM public IP -
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.
- Executable
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 &
| Variable | Description |
|---|---|
| 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.
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
-
Under Details > Action settings > Variables in the repository, click [Create] and add the following variables for Container Registry authentication.
Key Value KCR_REPO_PROJECTKakaoCloud project name KCR_REPO_USERIAM access key ID KCR_REPO_SECRETSecret access key -
Under Details > Workflows > Workflow files, verify that
.codeflow/workflows/cicd.ymlis recognized and click [Run workflow]. Select themainbranch and enter the following parameters to build the blue image.Parameter Value Description tagblueImage tag colorblueScreen color embedded as a build argument -
Check the run under Workflow run history. The state progresses from
QueuedtoRunningtoSucceeded. Use [⋮] > View logs to inspect each step.
Workflow log screen exampleInformationIf the run remains
Queued, verify that the runner daemon is running and that the workflow'sruns-onlabel matches the runner's--labelsvalue (codeflow-runner). -
A successful workflow produces two results.
- It pushes the
cicd-app-demo:blueimage to Container Registry. Check Container Pack > Container Registry > tutorial in the console. - It updates the
imagetag ink8s/deployment.yamlto:blueand creates an automaticcodeflow-cicommit. Deployflow synchronizes this commit.
- It pushes the
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}
| Variable | Description |
|---|---|
| PROJECT🖌︎ | KakaoCloud project name |
| ACCESS_KEY_ID🖌︎ | IAM access key ID |
| SECRET_ACCESS_KEY🖌︎ | Secret access key |
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.
-
In KakaoCloud Console, go to Developer Tools > Deployflow. The left menu contains Applications and Approval management.
-
Under Applications, click [Create application] and enter a name and description under Basic information.
-
Enter Cluster information, Source information, and Deployment method as follows.
Item Value Description Name cicd-app-demoApplication name Deployment cluster The cluster you created Target cluster with at least one node Namespace name deployflowTarget namespace Source repository cicd-app-demoCodeflow repository containing manifests Source branch mainBranch to use Deployment method Raw Manifest Apply YAML files directly Raw Manifest path k8sPath 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.
-
Optionally configure Approval settings and Advanced options. You can keep their defaults for this tutorial.
Option Description Approval setting option When enabled, synchronization, deployment, and rollback create approval requests instead of applying immediately. An approver must approve them under Approval management. Default: disabled. Resource deletion option Foreground waits until dependent resources are deleted; Background deletes the target immediately and cleans up dependents in the background; Non-cascading retains dependents. ServerSideApply Lets 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. -
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.
-
Select the application to open its details pane. Under Details, verify that the sync state is
Out of Syncand review the source revision and path.Tab Content Details Name, cluster, namespace, source, revision, path, state, and sync state Approval state Approval requests when approval settings are enabled Deployment strategy Deployment settings such as automatic synchronization Resources Topology and resource summary, manifests, events, and logs History Deployment and rollback history -
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.
-
Verify that Sync state is
Syncedand State isHealthy. -
On the Resources tab, inspect the Application → Deployment → ReplicaSet → Pod → Service topology and each resource's summary, manifest, events, and logs.
-
Open the Service's public EXTERNAL-IP and verify the blue
rollouts-demoscreen. 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 example
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.
- If a Pod is
Pendingand the application remainsMissing, the target cluster usually has no nodes. - If
ImagePullBackOffappears, the target namespace is missingcr-pull-secretor 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
- Select the application in Deployflow and click [Configure deployment strategy] in the details pane.
- Enable Deployment strategy option, select
Automatic synchronizationas the Deployment type, and click [Save]. - Verify that Deployment strategy state shows
Automatic synchronizationin the list and details.
Run workflow in Codeflow
-
Under Details > Workflows in Codeflow, rerun the workflow from Step 4 with these parameters.
Parameter Value Description tagv2New image tag colorblueScreen color -
After the workflow succeeds, verify automatic synchronization in Deployflow.
- The workflow builds and pushes
cicd-app-demo:v2, then updates and commits the tag ink8s/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
Syncedand a State ofHealthy.
- The workflow builds and pushes
With automatic synchronization enabled, source changes are applied directly to the target application. Carefully verify the application, cluster, namespace, and path.
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.