Streamlining CI/CD Pipelines with Hasura GraphQL Engine
Tags: hasura, cicd, gitops, kubernetes
Published: 2024-02-20 12:42:31


In the dynamic landscape of API management, the perennial debate surrounding architectural choices - whether it's gRPC, traditional HTTP, or the revolutionary GraphQL - remains ever-present. Drawing from a wealth of experience amassed over years in the tech industry, one conclusion stands out: GraphQL, with its unparalleled performance and precision, emerges as the optimal solution for real-world scenarios.
Unleashing the Power of GraphQL.

What sets GraphQL apart is its innate ability to deliver exactly what is needed, precisely when it's needed. Unlike traditional HTTP endpoints, where retrieving data often entails fetching entire objects and sifting through unwanted information, GraphQL empowers developers to fetch only the necessary details, significantly streamlining the process. Imagine a scenario where you're dealing with massive datasets but require only specific fields - GraphQL's fine-grained control ensures you retrieve precisely what's required, enhancing efficiency and performance manifold.

Embracing Hasura: The Game-Changer in GraphQL Implementation
Enter Hasura - a transformative force in the realm of GraphQL. With its comprehensive feature set and intuitive interface, Hasura simplifies schema creation and management, making it the go-to choice for developers seeking simplicity without compromising on power. The Hasura CLI facilitates seamless migration management, empowering teams to deploy with confidence and ease.

Automating CI/CD Pipelines: A Custom Solution with GitLab, Docker, and Kubernetes

Now, let's dive into the heart of the matter: automating CI/CD pipelines for Hasura. I'll guide you through a custom solution tailored to seamlessly integrate with your preferred pipeline tools, leveraging the robust capabilities of GitLab, Docker, and Kubernetes to ensure high availability and scalability.

Setting the Stage
Firstly, let's establish a dedicated repository to manage Hasura metadata and migrations. Initiate your project with the Hasura CLI and utilize the console to generate database schemas and migrations effortlessly. Ensure you have the necessary prerequisites in place:

**Prerequisites**:

- Hasura CLI
- Docker installed
- Docker Compose
- Git repository
- Git Helm chart repository

`https://git.opshive.io/myproject/hasura.git`
Run hasura init to create the project directory, guiding you through the initial setup process and allowing you to configure basic settings locally.
Crafting the Environment
Next, let's configure the Dockerfile at the root of your project to facilitate container creation and automated migration processing:


```
FROM hasura/graphql-engine:v2.27.0.cli-migrations-v3

RUN mkdir /app
WORKDIR /appd

COPY ./myproject /app
```

Additionally, prepare a Docker Compose file to orchestrate multiple containers and the database, ensuring seamless integration:

```
version: "3.6"
services:
  graphql-engine:
    build: 
      context: ./
      dockerfile: Dockerfile 
    # image: hasura/graphql-engine:v2.27.0.cli-migrations-v3
    ports:
      - "8080:8080"
    restart: always
    environment:
      HASURA_GRAPHQL_MIGRATIONS_DIR: /app/migrations
      HASURA_GRAPHQL_METADATA_DIR: /app/metadata
      ## postgres database to store Hasura metadata
      ## this env var can be used to add the above postgres database to Hasura as a data source. this can be removed/updated based on your needs
      HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgres@postgres:5432/myproject
      ## enable the console served by server
      HASURA_GRAPHQL_ENABLE_CONSOLE: "true" # set to "false" to disable console
      ## enable debugging mode. It is recommended to disable this in production
      HASURA_GRAPHQL_DEV_MODE: "true"
      HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
      ## uncomment next line to run console offline (i.e load console assets from server instead of CDN)
      # HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: /srv/console-assets
      ## uncomment next line to set an admin secret
      HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey
  postgres:
    image: postgres:14.1-alpine
    container_name: pg_db
    restart: always
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - '5432:5432'
    volumes:
      - postgresql:/var/lib/postgresql/data
  pgadmin:
    container_name: pgadmin_container-new
    image: dpage/pgadmin4
    environment:
      PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org}
      PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin}
      PGADMIN_CONFIG_SERVER_MODE: 'False'
    volumes:
       - pgadmin:/var/lib/pgadmin
      #  - /home/opshive/workspace/qasim/storage/projects/fabmedic/hasura/pg_backups:/tmp
    ports:
      - "${PGADMIN_PORT:-5050}:80"
volumes:
  postgresql:
    driver: local
  pgadmin:
```
This setup enables you to launch all containers and access the Hasura console for schema creation and management locally:

```
docker-compose up -d
cd myproject 
hasura console --endpoint=localhost:8080 --admin-secret=myadminsecretkey
```
Automating Deployment: A GitLab CI/CD Pipeline
With the foundation laid, it's time to automate deployment to your development server. Create a .gitlab-ci.yml file at the root of your project with the following content

```
image: docker:latest
stages:
  - docker-build-dev
  - patch-dev

docker-build-dev:
  stage: docker-build-dev

  services: 
    - name: docker:dind    
  before_script:
    - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" "docker.io"
  script:
    - docker build . -t "$DEV_IMAGE_NAME":"$CI_COMMIT_SHA" 
    - docker push "$DEV_IMAGE_NAME":"$CI_COMMIT_SHA"
  
  tags:
    - my-runner
  only:
    - dev

patch-dev:
  stage: patch-dev
  script:
    - |
      apk add git
      git clone -b main $HELM_REPO
      cd helm-charts/myproject-hasura
      git config credential.helper store
      rm -f dev-values.yaml   
      cat <<EOF >> dev-values.yaml
      replicaCount: 1

      image:
        repository: $DEV_IMAGE_NAME
        pullPolicy: IfNotPresent
        # Overrides the image tag whose default is the chart appVersion.
        tag: "$CI_COMMIT_SHA"

      imagePullSecrets:
      - name: mypullsecret
      nameOverride: ""
      fullnameOverride: "hasura-dev"

      serviceAccount:
        # Specifies whether a service account should be created
        create: true
        # Annotations to add to the service account
        annotations: {}
        # The name of the service account to use.
        # If not set and create is true, a name is generated using the fullname template
        name: ""

      podAnnotations: {}

      podSecurityContext: {}
        # fsGroup: 2000

      securityContext: {}
        # capabilities:
        #   drop:
        #   - ALL
        # readOnlyRootFilesystem: true
        # runAsNonRoot: true
        # runAsUser: 1000

      service:
        type: ClusterIP
        port: 8080

      ingress:
        enabled: true
        className: "nginx"
        annotations:
          kubernetes.io/ingress.class: nginx
          kubernetes.io/tls-acme: "true"
          nginx.ingress.kubernetes.io/proxy-body-size: 100m
          cert-manager.io/cluster-issuer: letsencrypt-mydomain-issuer-dns
        hosts:
          - host: dev-hasura.mydomain.io
            paths:
              - path: /
                pathType: ImplementationSpecific
        tls:
          - secretName: domain-hasura-tls
            hosts:
            - dev-hasura.mydomain.com
        # livenessProbe:
        #   httpGet:
        #     path: /
        #     port: http
        # readinessProbe:
        #   httpGet:
        #     path: /
        #     port: http
      resources: 
        # We usually recommend not to specify default resources and to leave this as a conscious
        # choice for the user. This also increases chances charts run on environments with little
        # resources, such as Minikube. If you do want to specify resources, uncomment the following
        # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
        limits:
          cpu: 1000m
          memory: 800Mi
        requests:
          cpu: 250m
          memory: 128Mi

      autoscaling:
        enabled: false
        minReplicas: 1
        maxReplicas: 3
        targetCPUUtilizationPercentage: 80
        # targetMemoryUtilizationPercentage: 80

      nodeSelector: {}

      tolerations: []

      affinity: {}
      # configmaps
      HASURA_GRAPHQL_DATABASE_URL: "postgresql://user:securepassword@postgresql-server:5432/devmyproject"
      HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
      HASURA_GRAPHQL_DEV_MODE: "false"
      HASURA_GRAPHQL_ADMIN_SECRET: "ASD78F5ASD95FGAS8765GSD6F9S7F69ASD78"
      HASURA_GRAPHQL_ENABLE_ALLOWLIST: "false"
      HASURA_GRAPHQL_ENABLED_LOG_TYPES: "startup,http-log,query-log,websocket-log,webhook-log"
      HASURA_GRAPHQL_UNAUTHORIZED_ROLE: "anonymous"

      HASURA_GRAPHQL_CORS_DOMAIN: "*"

      EOF
      git config --global user.email "bot@mycompany.com"
      git config --global user.name "bot"
      git add .
      git commit -m "$CI_COMMIT_SHA"
      git push origin main
  tags:
    - my-runner
  only:
    - dev
```
By following these meticulously crafted steps, you'll seamlessly integrate Hasura GraphQL Engine into your CI/CD workflows, fostering efficiency, reliability, and scalability throughout the development lifecycle.

---

This comprehensive guide empowers developers to harness the full potential of GraphQL with Hasura while streamlining CI/CD processes for unparalleled efficiency and agility.