Skip to main content

CI/CD Pipeline Deployment

If you've been manually deploying your app onto CloudFlow (e.g. via the Console UI or kubectl), there are many benefits to having an automated pipeline deployment that you should consider.

Reasons to have an automated pipeline:

  • There's no need to share Auth tokens to access your Kubernetes cluster amongst your dev team
  • You can run automated tests prior, mitigating risks of a bad deployment
  • Builds always commence from a consistent starting condition, avoiding scenarios of "it worked on my machine"

Examples

This tutorial provides you with examples for GitHub Actions workflow, and Bitbucket pipelines. If you require assistance with other CI/CD tools, reach out to us via the support channel and we will endeavour to assist.

Prerequisites

You will need to obtain the following values before proceeding:

Optional:

  • A container image repository

Create your Kubernetes deployment YAMLs

For simplicity, we will store our deployment YAMLs within a folder named k8s at the root of the repository. You may add additional files to be deployed if desired.

mkdir -p k8s
touch k8s/deploy.yaml
./k8s/deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cicd-demo
labels:
app: cicd-demo
spec:
replicas: 1
selector:
matchLabels:
app: cicd-demo
template:
metadata:
labels:
app: cicd-demo
spec:
containers:
- name: cicd-demo
image: ${IMAGE_NAME}:main
imagePullPolicy: Always
resources:
requests:
cpu: ".1"
memory: ".1Gi"
limits:
cpu: ".1"
memory: ".1Gi"
ports:
- containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
name: ingress-upstream
labels:
app: ingress-upstream
spec:
selector:
app: cicd-demo
ports:
- name: 80-to-80
protocol: TCP
port: 80
targetPort: 80


GitHub Actions

GitHub Actions (docs) are defined by creating workflow .yaml files within the .github/workflows directory in the root of your repository. The following is an example folder structure, with a deploy-to-cloudflow.yaml action defined:

.
├── .github
│ └── workflows
│ ├── < WORKFLOW_NAME >.yaml
│ └── deploy-to-cloudflow.yaml
├── k8s
│ └── deploy.yaml
├── Dockerfile
├── README.md
└── ...

Add repository secrets

Before you add and commit the workflows YAML files, you will first need to insert the two values from earlier (CLOUDFLOW_K8S_API_URL & CLOUDFLOW_API_TOKEN) as repository secrets in the GitHub repo.

This can be added via Settings > Secrets > Actions, use New repository secret to add the two secrets.

GitHub Secrets

Create the workflow YAML

Create the GitHub Actions workflows directory and YAML if you haven't done so already

mkdir -p .github/workflows
touch .github/workflows/deploy-to-cloudflow.yaml

Within the deploy-to-cloudflow.yaml file, insert the following contents:

deploy-to-cloudflow.yaml
name: Deploy to CloudFlow

on:
push:
branches: [main]

env:
REGISTRY: ghcr.io
REPO_NAME: ${{ github.repository }} # Uses the GitHub repository name as image name by default. Override if desired.
APP_NAME: cicd-demo # k8s deployment's app name (refer to k8s/deploy.yaml)

jobs:
build-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: downcase REPO
run: |
echo "LOWERCASE_REPO=${REPO_NAME,,}" >>${GITHUB_ENV}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.LOWERCASE_REPO }}

- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Deploy on CloudFlow
env:
CLOUDFLOW_K8S_API_URL: "${{ secrets.CLOUDFLOW_K8S_API_URL }}"
CLOUDFLOW_API_TOKEN: "${{ secrets.CLOUDFLOW_API_TOKEN }}"
CERT_PATH: "/etc/ssl/certs/ca-certificates.crt"
IMAGE_NAME: ${{ env.REGISTRY }}/${{ env.LOWERCASE_REPO }}
run: |
#######################################
# Configure kubectl to talk to CloudFlow
#######################################

kubectl config set-cluster cloudflow-cluster --server=$CLOUDFLOW_K8S_API_URL --certificate-authority=$CERT_PATH
kubectl config set-credentials cloudflow-user --token=$CLOUDFLOW_API_TOKEN
kubectl config set-context cloudflow --cluster=cloudflow-cluster --user=cloudflow-user

kubectl config use-context cloudflow

########################
# Deploy k8s YAML file
########################

envsubst '$IMAGE_NAME' < ./k8s/deploy.yaml > ./k8s/deploy.yaml.temp
mv ./k8s/deploy.yaml.temp ./k8s/deploy.yaml

kubectl apply -f ./k8s/

kubectl rollout restart deployment $APP_NAME

Once all files have been committed and pushed, you should see the GitHub Action run immediately.


Bitbucket Pipelines

Bitbucket Pipelines (docs) are configured by creating a bitbucket-pipelines.yml file in the root of your repository. The following is an example folder structure:

.
├── k8s
│ └── deploy.yaml
├── bitbucket-pipelines.yml
├── Dockerfile
├── README.md
└── ...

Add Repository Variables

You may refer to the Bitbucket documentation on steps to add Secrets and Variables to be used by this pipeline.

For the purposes of this tutorial, we assume that your image will be deployed on Docker Hub, and will require inserting user credentials.

Secrets/Variables needed:

  • DOCKERHUB_USERNAME: Login credentials for Docker Hub.
  • DOCKERHUB_PASSWORD: Login credentials for Docker Hub.
  • DOCKERHUB_NAMESPACE: This may be the same value as the Username. If not, ensure that the user has write permissions to this namespace.
  • CLOUDFLOW_K8S_API_URL: This is the value of your project's Kubernetes API endpoint
  • CLOUDFLOW_API_TOKEN: Create or use an existing CloudFlow API token
  • IMAGE_NAME: The name for the built container image.

Create the pipeline YAML

Create the bitbucket-pipelines.yml file if you haven't done so already

touch bitbucket-pipelines.yml

Within that file, insert the following contents:

bitbucket-pipelines.yml
image: node:16.16.0
definitions:
services:
docker:
memory: 2048

pipelines:
branches:
main:
- step:
name: "Build and push to Docker Hub"
services:
- docker
caches:
- docker
script:
- docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
- docker build -t $DOCKERHUB_NAMESPACE/$IMAGE_NAME:$BITBUCKET_COMMIT .
- docker push $DOCKERHUB_NAMESPACE/$IMAGE_NAME:$BITBUCKET_COMMIT

- step:
name: Deploy to CloudFlow.io
deployment: production
script:
# Install Kubectl
- echo "Installing Kubectl..."
- curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
- curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
- echo "$(cat kubectl.sha256) kubectl" | sha256sum --check
- install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

# Configure kubectl to connect to CloudFlow's cluster
- echo "Configuring Kubectl..."
- PATH_TO_CERT_AUTHORITY="/etc/ssl/certs/ca-certificates.crt"
- kubectl config set-cluster cloudflow-cluster --certificate-authority=$PATH_TO_CERT_AUTHORITY --server=$CLOUDFLOW_K8S_API_URL
- kubectl config set-credentials cloudflow-user --token=$CLOUDFLOW_API_TOKEN
- kubectl config set-context cloudflow --cluster=cloudflow-cluster --user=cloudflow-user --namespace=default
- kubectl config use-context cloudflow

# Deploying to CloudFlow
- echo "Deploying to CloudFlow..."
- IMAGE_NAME=$DOCKERHUB_NAMESPACE/$IMAGE_NAME:$BITBUCKET_COMMIT
- envsubst '$IMAGE_NAME' < ./k8s/deploy.yaml > ./k8s/deploy.yaml.temp
- mv ./k8s/deploy.yaml.temp ./k8s/deploy.yaml
- kubectl apply -f ./k8s/
- APP_NAME="cicd-demo" # k8s deployment's app name (refer to k8s/deploy.yaml above)
- kubectl rollout restart deployment $APP_NAME

Once all files have been committed and pushed, you should see the Bitbucket pipeline run on any commits on the main branch.


Need assistance?

If you require assistance with other CI/CD tools, reach out to us via the support channel and we will endeavour to assist.