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.
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: 2Service (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: 5000Configure 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 --tagsAfter 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
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
