The Startup
Published in

The Startup

DevOps with Kong, Deck and Azure Pipelines

Kong

Deck

Workflow

  • We will manage our kong configurations using deck and CI pipelines to avoid manual intervention/running manual scripts
  • We will do this for each of the different environments we might be having (dev, test, master etc). For this demo, I have assumed test and master environment
  1. Pipelines
Pipelines
pr:
- master
resources:
- repo: self
stages:
- stage: build_and_push_docker_image
displayName: Build and push deck docker image
jobs:
- job: build_docker_image
displayName: Build and push docker
steps:
- task: Docker@2
displayName: Build and Push docker image
inputs:
containerRegistry: 'MyDockerHub'
repository: 'pjanicked/test-deck'
command: 'buildAndPush'
Dockerfile: '**/Dockerfile'
tags: latest
- stage: deck_test
displayName: Deck Test Environment
condition: and(or(eq(variables['Build.Reason'], 'IndividualCI'), eq(variables['Build.Reason'], 'BatchedCI'), eq(variables['Build.Reason'], 'Manual')), succeeded())
dependsOn: build_and_push_docker_image
jobs:
- template: /deck_commands.yaml
parameters:
kong_url: https://pjanicked-kong-test.herokuapp.com/kong-admin
kong_file: test.yaml

- stage: deck_master
displayName: Deck Master Environment
condition: and(or(eq(variables['Build.Reason'], 'IndividualCI'), eq(variables['Build.Reason'], 'BatchedCI'), eq(variables['Build.Reason'], 'Manual')), succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
dependsOn: deck_test
jobs:
- template: /deck_commands.yaml
parameters:
kong_url: https://pjanicked-kong-master.herokuapp.com/kong-admin
kong_file: master.yaml
trigger:
- master
resources:
- repo: self
stages:
- stage: build_and_push_docker_image
displayName: Build and push deck docker image
jobs:
- job: build_docker_image
displayName: Build and push docker
steps:
- task: Docker@2
displayName: Build and Push docker image
inputs:
containerRegistry: 'MyDockerHub'
repository: 'pjanicked/test-deck'
command: 'buildAndPush'
Dockerfile: '**/Dockerfile'
tags: latest
- stage: deck_test
displayName: Deploy Test Environment
condition: and(or(eq(variables['Build.Reason'], 'IndividualCI'), eq(variables['Build.Reason'], 'BatchedCI'), eq(variables['Build.Reason'], 'Manual')), succeeded())
dependsOn: build_and_push_docker_image
jobs:
- template: /deck_commands.yaml
parameters:
kong_url: https://pjanicked-kong-test.herokuapp.com/kong-admin
to_sync: true
kong_file: test.yaml

- stage: deck_master
displayName: Deck Master Environment
condition: and(or(eq(variables['Build.Reason'], 'IndividualCI'), eq(variables['Build.Reason'], 'BatchedCI'), eq(variables['Build.Reason'], 'Manual')), succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
dependsOn: deck_test
jobs:
- template: /deck_commands.yaml
parameters:
kong_url: https://pjanicked-kong-master.herokuapp.com/kong-admin
to_sync: true
kong_file: master.yaml
  • Building deck docker image: this stage builds a custom docker image of deck which essentially runs a shell script file (more on that later) that contains execution of deck commands
  • Running deck commands for each environment: After the image has been built, we run the deck commands for each of the available environment(test, master) one by one
  • We will have a separate configuration yaml file for each of the environment (test.yaml, master.yaml) in kong folder where one can specify the services, routes, plugins (configurations)
Pipeline stages
parameters:
- name: kong_url
type: string
default: https://pjanicked-kong-test.herokuapp.com/kong-admin
- name: to_sync
type: boolean
default: false
- name: kong_file
type: string
default: test.yaml
jobs:
- job: Deck_Ping
displayName: Deck Ping for Kong
steps:
- template: /action.yaml
parameters:
command: ping
options: kong-addr ${{ parameters.kong_url }}

- job: Deck_Validate
displayName: Deck Validate for Kong
dependsOn: Deck_Ping
steps:
- template: /action.yaml
parameters:
command: validate
options: kong-addr ${{ parameters.kong_url }}
kong_workspaces: kong
kong_file: ${{ parameters.kong_file }}
- ${{ if eq(parameters.to_sync, false) }}:
- job: Deck_Diff
displayName: Deck Diff for Kong
dependsOn:
- Deck_Ping
- Deck_Validate
steps:
- template: /action.yaml
parameters:
command: diff
options: kong-addr ${{ parameters.kong_url }}
kong_workspaces: kong
kong_file: ${{ parameters.kong_file }}
- ${{ if eq(parameters.to_sync, true) }}:
- job: Deck_Sync
displayName: Deck Sync for Kong
dependsOn:
- Deck_Ping
- Deck_Validate
steps:
- template: /action.yaml
parameters:
command: sync
options: kong-addr ${{ parameters.kong_url }}
kong_workspaces: kong
kong_file: ${{ parameters.kong_file }}
  • command: the deck command to execute.
  • options: options/flags passed to the deck command. Here, we pass the kong address as kong-addr flag.
  • kong_workspaces: we mention directory of our config files here.
  • kong_file: the actual kong config file name.
parameters:
- name: command
type: string
default: ping
- name: kong_workspaces
type: string
default: kong
- name: options
type: string
- name: kong_file
type: string
default: test.yaml

steps:
- task: Docker@2
displayName: Docker Run deck
- script: |
docker run pjanicked/test-deck ${{ parameters.command }} ${{ parameters.kong_workspaces }} ${{ parameters.options }} ${{ parameters.kong_file }}
FROM hbagdi/deck

COPY entrypoint.sh /entrypoint.sh
COPY kong /kongUSER rootRUN ["chmod", "+x", "/entrypoint.sh"]ENTRYPOINT [ "/entrypoint.sh" ]
#!/bin/sh -l
set -e -o pipefail
main (){
cmd=$1
dir=$2
ops=$3
opsvalue=$4
kongfile=$5
if [ ! -e ${dir} ]; then
echo "${dir}: No such file or directoy exists";
exit 1;
fi
echo "Executing: deck $cmd --$ops $opsvalue -s $dir/$kongfile"
deck $cmd --$ops $opsvalue -s $dir/$kongfile
}
case $1 in
"ping") deck $1 --$3 $4;;
"validate"|"diff"|"sync") main $1 $2 "$3" "$4" "$5";;
* ) echo "deck $1 is not supported." && exit 1 ;;
esac
  • I have used kong heroku (currently, archived) to deploy a kong instance. (Note: This instance comes preconfigured with certain services and plugins. Since I couldnt have a test machine, I decided to use heroku. The docker-compose file provided below works fine though when locally tested)
  • Test Env Kong Url: https://pjanicked-kong-test.herokuapp.com/
  • Master Env Kong Url: https://pjanicked-kong-master.herokuapp.com/
  • We add a new service (mock_service) and a new route (/mock) in our test.yaml and master.yaml
  • The other service (kong-admin) and consumer (heroku-admin) added is to adhere to the preconfigured setup used by kong heroku
_format_version: "1.1"
services:
- connect_timeout: 60000
host: mockbin.org
name: mock_service
port: 80
protocol: http
read_timeout: 60000
retries: 8
write_timeout: 60000
routes:
- name: mock_test
paths:
- /mock
preserve_host: false
protocols:
- http
- https
regex_priority: 0
strip_path: true
- connect_timeout: 60000
host: localhost
name: kong-admin
port: 8001
protocol: http
read_timeout: 60000
retries: 5
write_timeout: 60000
routes:
- name: admin_route
paths:
- /kong-admin
preserve_host: false
protocols:
- http
- https
regex_priority: 0
strip_path: true
consumers:
- username: heroku-admin
  • Deck Ping
  • Deck Validate
  • Deck Diff
  • Deck Sync
  1. When working with Azure pipelines and Azure Repos Git, make sure you give the template path right.
  2. If you are using a specific build agent, then make sure you add
pool:
Linux Build (whatever your build agent is)
version: "3.7"volumes:
kong_data: {}

networks:
kong-net:
services:
kong-database:
image: postgres:9.6
container_name: kong-postgres
restart: on-failure
networks:
- kong-net
volumes:
- kong_data:/var/lib/postgresql/data
environment:
POSTGRES_USER: kong
POSTGRES_PASSWORD: ${KONG_PG_PASSWORD:-kong}
POSTGRES_DB: kong
ports:
- "5432:5432"
healthcheck:
test: ["CMD", "pg_isready", "-U", "kong"]
interval: 30s
timeout: 30s
retries: 3
kong-migration:
image: ${KONG_DOCKER_TAG:-kong:latest}
command: kong migrations bootstrap
networks:
- kong-net
restart: on-failure
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-database
KONG_PG_DATABASE: kong
KONG_PG_USER: kong
KONG_PG_PASSWORD: ${KONG_PG_PASSWORD:-kong}
depends_on:
- kong-database
kong:
image: ${KONG_DOCKER_TAG:-kong:latest}
restart: on-failure
networks:
- kong-net
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-database
KONG_PG_DATABASE: kong
KONG_PG_USER: kong
KONG_PG_PASSWORD: ${KONG_PG_PASSWORD:-kong}
KONG_PROXY_LISTEN: 0.0.0.0:8000
KONG_PROXY_LISTEN_SSL: 0.0.0.0:8443
KONG_ADMIN_LISTEN: 0.0.0.0:8001
depends_on:
- kong-database
healthcheck:
test: ["CMD", "kong", "health"]
interval: 10s
timeout: 10s
retries: 10
ports:
- "8000:8000"
- "8001:8001"
- "8443:8443"
- "8444:8444"

--

--

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +768K followers.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store