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:
CLOUDFLOW_K8S_API_URL
: This is the value of your project's Kubernetes API endpointCLOUDFLOW_API_TOKEN
: Create or use an existing CloudFlow API token
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
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.
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:
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 endpointCLOUDFLOW_API_TOKEN
: Create or use an existing CloudFlow API tokenIMAGE_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:
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.