Cloud Native 14 min read

Master GitLab CI/CD with Kubernetes: Deploy a Flask App End‑to‑End

This article walks through deploying a Flask web service with GitLab CI/CD, covering Docker image creation, a comprehensive .gitlab-ci.yml pipeline, Kubernetes resource definitions, runner variable configuration, and how branch, tag, and manual review jobs orchestrate continuous integration, delivery, and deployment.

Ops Development Stories
Ops Development Stories
Ops Development Stories
Master GitLab CI/CD with Kubernetes: Deploy a Flask App End‑to‑End

Service Background

Deploy a Flask web service using GitLab CI to perform code‑style checks, unit tests, packaging, and release into a Kubernetes environment. The .gitlab-ci.yml file configures branch‑ and tag‑based pipelines for automated builds.

Dockerfile for the Flask Service

FROM python:3.4

COPY . /skeleton
WORKDIR /skeleton
RUN pip install -r requirements.txt -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com  # configure pip source
EXPOSE 5000
ENTRYPOINT ["sh","scripts/dev.sh"]

Define .gitlab-ci.yml

stages:  # five stages executed sequentially
  - style
  - test
  - release
  - review
  - deploy

pep8:  # code‑style job
  image: python:2.7
  stage: style
  script:
    - pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com tox
    - tox -e pep8

unittest-py2.7:
  image: python:2.7
  stage: test
  script:
    - pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com tox
    - tox -e py27

unittest-py3.4:
  image: python:3.4
  stage: test
  script:
    - pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com tox
    - tox -e py34

buildimage:
  image: docker:latest
  variables:
    DOCKER_DRIVER: overlay
    DOCKER_HOST: tcp://localhost:2375
  services:
    - name: docker:17.03-dind
      command:
        - "--registry-mirror=https://*****.mirror.aliyuncs.com"
  stage: release
  script:
    - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" ${CI_REGISTRY_REPO_URL}
    - docker build -t "${CI_REGISTRY_IMAGE}:latest" -f ./Dockerfile .
    - docker tag "${CI_REGISTRY_IMAGE}:latest" "${CI_REGISTRY_REPO_URL}/${CI_REGISTRY_NAMESPACE}/${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}"
    - test ! -z "${CI_COMMIT_TAG}" && docker push "${CI_REGISTRY_REPO_URL}/${CI_REGISTRY_NAMESPACE}/${CI_REGISTRY_IMAGE}:latest"
    - docker push "${CI_REGISTRY_REPO_URL}/${CI_REGISTRY_NAMESPACE}/${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}"

deploy_review:
  image: bitnami/kubectl
  stage: review
  only:
    - branches
  except:
    - tags
  environment:
    name: dev
    url: https://dev-gitlab-k8s-demo.****.cn-beijing.alicontainer.com
    on_stop: stop_review
  script:
    - kubectl version
    - cd manifests/
    - sed -i "s/__CI_ENVIRONMENT_SLUG__/${CI_ENVIRONMENT_SLUG}/" deployment.yaml ingress.yaml service.yaml
    - sed -i "s/__VERSION__/${CI_COMMIT_REF_NAME}/" deployment.yaml ingress.yaml service.yaml
    - |
      if kubectl apply -f deployment.yaml | grep -q unchanged; then
        echo "=> Patching deployment to force image update."
        kubectl patch -f deployment.yaml -p '{"spec":{"template":{"metadata":{"annotations":{"ci-last-updated":"$(date +'%s')"}}}}}'
      else
        echo "=> Deployment apply has changed the object, no need to force image update."
      fi
    - kubectl apply -f service.yaml || true
    - kubectl apply -f ingress.yaml
    - kubectl rollout status -f deployment.yaml
    - kubectl get all,ing -n devops
  when: manual

stop_review:
  image: bitnami/kubectl
  stage: review
  variables:
    GIT_STRATEGY: none
  when: manual
  only:
    - branches
  except:
    - master
    - tags
  environment:
    name: dev
    action: stop
  script:
    - kubectl version
    - kubectl delete ing -l ref=${CI_ENVIRONMENT_SLUG}
    - kubectl delete all -l ref=${CI_ENVIRONMENT_SLUG}

deploy:
  image: bitnami/kubectl
  stage: deploy
  environment:
    name: live
    url: https://${CI_ENVIRONMENT_SLUG}.****.cn-beijing.alicontainer.com
  only:
    - tags
  when: manual
  script:
    - kubectl version
    - cd manifests/
    - sed -i "s/__CI_ENVIRONMENT_SLUG__/${CI_ENVIRONMENT_SLUG}/" deployment.yaml ingress.yaml service.yaml
    - sed -i "s/__VERSION__/${CI_COMMIT_REF_NAME}/" deployment.yaml ingress.yaml service.yaml
    - kubectl apply -f deployment.yaml service.yaml ingress.yaml
    - kubectl rollout status -f deployment.yaml
    - kubectl get all,ing -l ref=${CI_ENVIRONMENT_SLUG}

Kubernetes Resource Declarations

Deployment (deployment.yaml)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__
  namespace: devops
  labels:
    app: gitlab-k8s-demo
    ref: __CI_ENVIRONMENT_SLUG__
    track: stable
spec:
  replicas: 2
  selector:
    matchLabels:
      app: gitlab-k8s-demo
      ref: __CI_ENVIRONMENT_SLUG__
  template:
    metadata:
      labels:
        app: gitlab-k8s-demo
        ref: __CI_ENVIRONMENT_SLUG__
        track: stable
    spec:
      imagePullSecrets:
        - name: myregistry
      containers:
        - name: app
          image: registry.cn-beijing.aliyuncs.com/*****/gitlab-ci-flaskapp-test:__VERSION__
          imagePullPolicy: Always
          ports:
            - name: web
              protocol: TCP
              containerPort: 5000
          livenessProbe:
            httpGet:
              path: /
              port: 5000
            initialDelaySeconds: 3
            timeoutSeconds: 2
          readinessProbe:
            httpGet:
              path: /
              port: 5000
            initialDelaySeconds: 3
            timeoutSeconds: 2

Service (service.yaml)

apiVersion: v1
kind: Service
metadata:
  name: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__
  namespace: devops
  labels:
    app: gitlab-k8s-demo
    ref: __CI_ENVIRONMENT_SLUG__
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "5000"
    prometheus.io/scheme: "http"
    prometheus.io/path: "/"
spec:
  type: ClusterIP
  ports:
    - name: http-metrics
      port: 5000
      protocol: TCP
  selector:
    app: gitlab-k8s-demo
    ref: __CI_ENVIRONMENT_SLUG__

Ingress (ingress.yaml)

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__
  namespace: devops
  labels:
    app: gitlab-k8s-demo
    ref: __CI_ENVIRONMENT_SLUG__
  annotations:
    nginx.ingress.kubernetes.io/service-weight: ''
spec:
  rules:
    - host: __CI_ENVIRONMENT_SLUG__-gitlab-k8s-demo.****.cn-beijing.alicontainer.com
      http:
        paths:
          - path: /
            backend:
              serviceName: gitlab-k8s-demo-__CI_ENVIRONMENT_SLUG__
              servicePort: 5000

Configure Runner Environment Variables

Runner variables can be defined at the project level or group level in the GitLab UI. After setting them, they are referenced in the .gitlab-ci.yml pipeline.

Pipeline Execution Overview

When code is pushed to the master branch, GitLab automatically creates a pipeline that runs the defined jobs. The pipeline view shows stages such as style , test , release , etc.

For feature branches (e.g., feature-01), only the jobs allowed by only/except run. Review jobs require manual triggering, and stop_review can be used to delete the temporary environment.

Tag‑Based Deployments

Pushing a new tag (e.g., v2.0) triggers a pipeline that runs the deploy job, indicating a stable version ready for production.

git tag v2.0
🐳 👉 git push origin --tags

After deployment, the service can be accessed via the generated URL and the Flask login page should appear, confirming a successful CI/CD workflow.

References

GitLab CI YAML reference: https://docs.gitlab.com/ee/ci/yaml/README.html

Runner variables documentation: https://docs.gitlab.com/ee/ci/variables/README.html

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerKubernetesDevOpsGitLab CIFlaskCI/CD
Ops Development Stories
Written by

Ops Development Stories

Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.