Thursday 6 October 2022

CI/CD with GitHub Actions to deploy Application to Azure Kubernetes Cluster

 This article here is going to be bit lengthy but if you stay with me, I’m sure it would be one stop learning for your application to automate the deployment on Azure Kubernete Service.

  1. Azure Kubernetes Cluster with Azure Container Registry.
    Reference: https://learn.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-portal?tabs=azure-cli
  2. kubernete Service Namespaces where all our services and ingress will be created. You can use the default namespace too.
  3. Azure Application Gateway with a public IP. If your Azure Application Gateway and AKS Cluster both are in different VNet then don’t forget the pair them both for seamless connectivity between pods and app gateway.
    Reference: https://learn.microsoft.com/en-us/azure/application-gateway/tutorial-ingress-controller-add-on-existing
FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS base
WORKDIR /app
EXPOSE 8080
ENV ASPNETCORE_URLS=http://+:8080FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build
COPY . .
WORKDIR "./MyApp"
RUN dotnet build "MyApp.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]
{
"clientId": "*********",
"clientSecret": "*********",
"subscriptionId": "**********",
"tenantId": "*********",
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
"resourceManagerEndpointUrl": "https://management.azure.com/",
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
"galleryEndpointUrl": "https://gallery.azure.com/",
"managementEndpointUrl": "https://management.core.windows.net/"
}
src
k8s
MyApp
.
.
MyApp.csproj
apiVersion: apps/v1
kind: Deployment
metadata:
name: myappservice
spec:
selector:
matchLabels:
app: myappservice

template:
metadata:
labels:
app: myappservice
spec:
containers:
- name: myappservice
image: #{CONTAINER_IMAGE}#
imagePullPolicy: Always
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
- name: ASPNETCORE_URLS
value: http://+:8080
livenessProbe:
httpGet:
path: /health/ping
port: 8080
initialDelaySeconds: 5
periodSeconds: 60
timeoutSeconds: 15
failureThreshold: 3
successThreshold: 1
readinessProbe:
httpGet:
path: /health/ping
port: 8080
initialDelaySeconds: 5
periodSeconds: 60
timeoutSeconds: 15
failureThreshold: 3
successThreshold: 1
ports:
- containerPort: 8080
imagePullSecrets:
- name: #{CONTAINER_REGISTRY_SECRET}#
apiVersion: v1
kind: Service
metadata:
name: myappservice-service
spec:
selector:
app: myappservice
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myappservice-ingress-appgateway
labels:
website: myapp
annotations:
kubernetes.io/ingress.class: azure/application-gateway
spec:
rules:
- http:
paths:
- path: /(.*)
pathType: Prefix
backend:
service:
name: myappservice-service
port:
number: 80
pathType: Exact
name: service-deployon:
workflow_dispatch:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
- name: Run unit and integration tests
shell: bash
working-directory: ./src
run: dotnet test -c Release
publish:
runs-on: ubuntu-latest
needs: [build]
environment: test
steps:
- uses: actions/checkout@v2
- name: Replace token for appsettings.Production.json
uses: cschleiden/replace-tokens@v1.1
with:
files: '["src/MyApp/appsettings.Production.json"]'
env:
ConnectionString: ${{ secrets.CONNECTIONSTRING }}
      - name: Container Registry login
uses: docker/login-action@v1.10.0
with:
registry: ${{ secrets.containerRegistryUrl }}
username: ${{ secrets.containerUser }}
password: ${{ secrets.containerToken }}
      - name: Build and push container
uses: docker/build-push-action@v2
with:
push: true
tags: ${{ secrets.containerRegistryUrl }}/test/MyApp
context: ./src
file: src/MyApp/Dockerfile
    - name: Write container image tag to file
shell: bash
working-directory: src/MyApp
run: echo ${{ secrets.containerRegistry }}/test/MyApp > containerImageTag
    - name: Publish as an artifact
uses: actions/upload-artifact@v3
with:
name: myapp
path: |
k8s/deployment.yml
k8s/service.yml
k8s/ingress-appgateway.yml
src/MyApp/containerImageTag
if-no-files-found: error
deploy:
runs-on: ubuntu-latest
needs: [publish]
environment: test
steps:
- uses: actions/checkout@v2
    - name: Downloading artifacts
uses: actions/download-artifact@v2
with:
name: myapp
path: myapp
    - name: Extracting the image tag
id: image-tag
shell: bash
working-directory: myapp
run: |
imageTag=$(cat src/test/containerImageTag)
echo "::set-output name=imageTag::$imageTag"
echo $imageTag
    - name: kubectl setup
uses: azure/setup-kubectl@v1
id: install
    - name: Setting aks context
uses: azure/aks-set-context@v1
with:
creds: '${{ secrets.azureCredentials }}'
resource-group: ${{ secrets.resourceGroupName }}
cluster-name: ${{ secrets.clusterName }}
    - name: replace tokens for kubernete manifest file
uses: cschleiden/replace-tokens@v1.1
with:
files: 'myapp/k8s/deployment.yml'
env:
CONTAINER_IMAGE: ${{ steps.image-tag.outputs.imageTag }}
ASPNETCORE_ENVIRONMENT: Production
CONTAINER_REGISTRY_SECRET: myapp-image-pull-secret
    - name: Set Image Pull Secret
uses: azure/k8s-create-secret@v1
id: create-secret
with:
namespace: ${{ secrets.kubernetesNamespace }}
container-registry-url: ${{ secrets.containerRegistryUrl }}
container-registry-username: ${{ secrets.containerUser }}
container-registry-password: ${{ secrets.containerPassword }}
secret-name: myapp-image-pull-secret
    - name: Deploy to AKS
id: deploy-aks
uses: Azure/k8s-deploy@v4
with:
namespace: ${{ secrets.kubernetesNamespace }}
manifests: myapp/k8s/
imagepullsecrets: myapp-image-pull-secret
    - name: Display structure of downloaded files-Debug
shell: bash
run: ls -R
working-directory: ${{ inputs.artifactName }}

No comments:

Post a Comment