Cloud Native 30 min read

How VPGAME Migrated Its Services to Kubernetes Using GitLab CI/CD and Docker

This article details VPGAME's end‑to‑end migration of its esports platform services to a Kubernetes‑based architecture, covering CI/CD tool selection, Docker and GitLab Runner setup, automated Docker image builds, and Kubernetes Deployment and Service templating with health checks, resource limits, logging and monitoring configurations.

Alibaba Cloud Native
Alibaba Cloud Native
Alibaba Cloud Native
How VPGAME Migrated Its Services to Kubernetes Using GitLab CI/CD and Docker

Background

VPGAME, an integrated esports service platform, decided to move its services to a container environment using Kubernetes to improve deployment speed, standardise CI/CD pipelines and reduce operational overhead.

CI/CD Tool Selection

The team chose GitLab‑CI because it integrates tightly with GitLab, requires only a .gitlab-ci.yml file for pipeline definition, and offers a simpler configuration than Jenkins.

Docker Installation (CentOS)

yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce
systemctl start docker
systemctl enable docker

GitLab Runner Installation and Registration

curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash
sudo yum install -y gitlab-runner
sudo gitlab-runner start
# Non‑interactive registration example
gitlab-runner register \
  --non-interactive \
  --url "http://git.example.cn" \
  --registration-token "YOUR_TOKEN" \
  --executor "docker" \
  --docker-image "alpine:latest" \
  --description "base-runner-docker" \
  --tag-list "base-runner" \
  --run-untagged="true" \
  --docker-pull-policy "if-not-present" \
  --docker-privileged="true" \
  --docker-volumes "/etc/docker/daemon.json:/etc/docker/daemon.json" \
  --docker-volumes "/etc/gitlab-runner/config.toml:/etc/gitlab-runner/config.toml"

Docker Image Build Process

All Docker images are defined by Dockerfile files stored in the repository. When a merge to master occurs, a script scans for changed Dockerfiles and builds the corresponding images.

for FILE in $(bash ./find_diff_files | grep Dockerfile | sort); do
  DIR=$(dirname "$FILE")
  IMAGE_NAME=$(echo $DIR | sed -e 's/\//-/g')
  docker build -t $CI_REGISTRY/$HARBOR_DIR/$IMAGE_NAME -f $FILE $DIR
  docker push $CI_REGISTRY/$HARBOR_DIR/$IMAGE_NAME
done

.gitlab-ci.yml Overview

The pipeline defines three stages: build, test and deploy. Jobs in the same stage run in parallel; subsequent stages start only after all jobs in the previous stage succeed.

stages:
  - build
  - test
  - deploy

unittest:
  stage: test
  image: docker.vpgame.cn/infra/php-1.0-ci-1.1
  services:
    - name: docker.vpgame.cn/infra/mysql-5.6-multi
      alias: mysql
    - name: docker.vpgame.cn/infra/redis-4.0
      alias: redis_default
  script:
    - mv .env .env.tp
    - composer install --no-dev
    - phpunit -v --coverage-text --coverage-html=coverage --stderr
  artifacts:
    when: on_success
    paths:
      - vendor/
      - coverage/
    expire_in: 1 hour
  only:
    - branches
    - tags
  tags:
    - base-runner

.build-op:
  stage: build
  image: docker.vpgame.cn/infra/docker-kubectl-1.0
  script:
    - echo "Image name: $DOCKER_IMAGE_NAME"
    - docker build -t $DOCKER_IMAGE_NAME .
    - docker push $DOCKER_IMAGE_NAME
  tags:
    - base-runner

build-test:
  extends: .build-op
  variables:
    DOCKER_IMAGE_NAME: "$CI_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA"
  only:
    - /^testing/
    - master

build-prod:
  extends: .build-op
  variables:
    DOCKER_IMAGE_NAME: "$CI_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_TAG"
  only:
    - tags
  tags:
    - pro-deploy

.deploy-op:
  stage: deploy
  image: docker.vpgame.cn/infra/docker-kubectl-1.0
  script:
    - echo "Deploying $DOCKER_IMAGE_NAME"
    - sed -i "s/__NAMESPACE__/${NAMESPACE}/g" deployment.yml
    - sed -i "s/__APP_NAME__/${APP_NAME}/g" deployment.yml
    - kubectl apply -f deployment.yml
    - kubectl apply -f service.yml
    - kubectl rollout status deployment/${APP_NAME}
    - kubectl get all -l app=${APP_NAME} -n ${NAMESPACE}
  only:
    - /^testing/
    - master
  tags:
    - base-runner

deploy-test:
  extends: .deploy-op
  variables:
    REPLICAS: 2
    VERSION: "$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA"
  environment:
    name: test
    url: http://example.com
  only:
    - /^testing/
    - master
  tags:
    - base-runner

deploy-prod:
  extends: .deploy-op
  variables:
    REPLICAS: 3
    VERSION: "$CI_COMMIT_TAG"
  environment:
    name: prod
    url: http://example.com
  only:
    - tags
  tags:
    - pro-deploy

Kubernetes Deployment Template (deployment.yml)

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  labels:
    app: __APP_NAME__
    group: __GROUP_NAME__
  name: __APP_NAME__
  namespace: __NAMESPACE__
spec:
  replicas: 2
  selector:
    matchLabels:
      app: __APP_NAME__
  template:
    metadata:
      labels:
        app: __APP_NAME__
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: group
                operator: In
                values:
                - __GROUP_NAME__
      containers:
      - name: fpm
        image: $DOCKER_IMAGE_NAME
        ports:
        - containerPort: 80
        env:
        - name: aliyun_logs_vpgame
          value: stdout
        - name: aliyun_logs_vpgame_tags
          value: topic=__APP_NAME__
        livenessProbe:
          httpGet:
            path: /__PROJECT_NAME__
            port: 80
          initialDelaySeconds: 3
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /__PROJECT_NAME__
            port: 80
          initialDelaySeconds: 3
          periodSeconds: 5
        resources:
          limits:
            cpu: "500m"
            memory: "512Mi"
          requests:
            cpu: "250m"
            memory: "256Mi"
      restartPolicy: Always
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%

Kubernetes Service Template (service.yml)

apiVersion: v1
kind: Service
metadata:
  annotations:
    service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet
  labels:
    app: __APP_NAME__
  name: __APP_NAME__
  namespace: __NAMESPACE__
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: __APP_NAME__
  type: LoadBalancer

Additional Configuration

Health Checks : Liveness and readiness probes ensure containers are alive and ready before traffic is sent.

Resource Allocation : Pods use node affinity based on the project group to schedule on specific nodes.

Logging : Environment variables aliyun_logs_vpgame and aliyun_logs_vpgame_tags direct container logs to an Alibaba Cloud Logstore.

Monitoring : Annotations prometheus.io/scrape: "true", prometheus.io/port: "80", and prometheus.io/path: /{{project_name}}/metrics enable Prometheus to scrape metrics.

Upgrade Strategy : RollingUpdate with maxSurge and maxUnavailable set to 25% to minimise downtime.

Conclusion

By standardising Docker image creation, GitLab‑CI pipelines, and Kubernetes manifests, VPGAME achieved a fast, repeatable migration process for its services. The approach demonstrates how to combine containerisation, CI/CD automation and Kubernetes best practices to improve deployment efficiency, scalability and observability.

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.

Dockerci/cdKubernetesGitLab CI
Alibaba Cloud Native
Written by

Alibaba Cloud Native

We publish cloud-native tech news, curate in-depth content, host regular events and live streams, and share Alibaba product and user case studies. Join us to explore and share the cloud-native insights you need.

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.