[Video Course][TechWorld with Nana] GitLab CI/CD - From Zero To Hero [ENG, 2025]



Инсталляция и подготовка minikube для работы в ubuntu 22.04


Запуск и останов minikube в ubuntu 22.04


Инсталляция локальной версии gitlab в minikube


Starting from chapter 2:

Simple demo Node.js project: https://gitlab.com/nanuchi/mynodeapp-cicd-project.git


Starting from chapter 7 (Microservices):

Microservice mono-repo:
https://gitlab.com/nanuchi/mymicroservice-cicd

Microservice poly-repo:
https://gitlab.com/mymicroservice-cicd

CI-templates (in the poly-repo group):
https://gitlab.com/mymicroservice-cicd/ci-templates


Выполнение примеров из курса:


05 Build Docker Image & Push

5.3 Build Docker Image & Push to Private Registry


Замена docker на kaniko


Создал переменную окружения $DOCKER_PASSWORD


build_and_push:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.9.0-debug
    entrypoint: ['']
  script:
    # Создаем конфиг с логином и паролем
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"https://index.docker.io/v1/\":{\"username\":\"webmakaka\",\"password\":\"$DOCKER_PASSWORD\"}}}" > /kaniko/.docker//config.json

    - |
      /kaniko/executor \
        --context . \
        --destination index.docker.io/webmakaka/mynodeapp-cicd-project:1.0


08. Deploy to K8s


Делаю:
2026.01.11

https://gitlab.com/mymicroservice-cicd/frontend


Create gitlab user and permissions


// create namespace
$ kubectl create namespace my-micro-service


// create Service Account
$ kubectl create serviceaccount cicd-sa --namespace=my-micro-service


$ kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: cicd-sa-token
  namespace: my-micro-service
  annotations:
    kubernetes.io/service-account.name: cicd-sa
type: kubernetes.io/service-account-token
EOF


$ kubectl describe serviceaccount cicd-sa --namespace=my-micro-service
Name:                cicd-sa
Namespace:           my-micro-service
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              cicd-sa-token
Events:              <none>


$ kubectl get secret --namespace=my-micro-service
NAME            TYPE                                  DATA   AGE
cicd-sa-token   kubernetes.io/service-account-token   3      107s


// create Role for GitLab CI/CD
$ kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: my-micro-service
  name: cicd-role
rules:
- apiGroups: [""] # indicates the core API group
  resources: ["pods", "services", "secrets"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["extensions", "apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
EOF


// assign cicd role to SA
$ kubectl create rolebinding cicd-sa-rb \
  --role=cicd-role \
  --serviceaccount=my-micro-service:cicd-sa \
  --namespace=my-micro-service


Create kubeconfig for cicd service account


$ cp ~/.kube/config ~/.kube/config_admin


// 1. Получаем токен из созданного секрета
$ TOKEN=$(kubectl get secret cicd-sa-token -n my-micro-service -o jsonpath='{.data.token}' | base64 --decode)

// 2. Получаем CA сертификат кластера
$ CA=$(kubectl get secret cicd-sa-token -n my-micro-service -o jsonpath='{.data.ca\.crt}')

// 3. Получаем адрес API-сервера
$ SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')


cat <<EOF > kubeconfig-cicd.yaml
apiVersion: v1
kind: Config
clusters:
- name: kubernetes
  cluster:
    certificate-authority-data: $CA
    server: $SERVER
contexts:
- name: cicd-context
  context:
    cluster: kubernetes
    namespace: my-micro-service
    user: cicd-sa
current-context: cicd-context
users:
- name: cicd-sa
  user:
    token: $TOKEN
EOF


$ cp kubeconfig-cicd.yaml config


$ kubectl get namespace
$ kubectl get pod -n kube-system


Gitlab

Создаю группу: microservice-cicd

Settings -> CI/CD

Variables -> Add ->

Type: File

Visible: Visible

Flags: Protect variable

Key: KUBE_CONFIG


Форкаю:

https://gitlab.com/mymicroservice-cicd/frontend.git
https://gitlab.com/mymicroservice-cicd/ci-templates.git
https://gitlab.com/mymicroservice-cicd/products.git
https://gitlab.com/mymicroservice-cicd/shopping-cart.git


Создал переменную окружения $DOCKER_PASSWORD


Ci Templates / build.yml


build:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.9.0-debug
    entrypoint: ['']
  before_script:
    - export IMAGE_NAME=microservice-$MICRO_SERVICE
    - export IMAGE_TAG=$SERVICE_VERSION
  script:
    # Создаем конфиг с логином и паролем
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"https://index.docker.io/v1/\":{\"username\":\"webmakaka\",\"password\":\"$DOCKER_PASSWORD\"}}}" > /kaniko/.docker//config.json

    - |
      /kaniko/executor \
        --context . \
        --destination index.docker.io/webmakaka/$IMAGE_NAME:$IMAGE_TAG


**Ci Templates / deploy-k8s.yml **


deploy:
  stage: deploy
  image:
    name: alpine/kubectl:latest

  before_script:
    - export IMAGE_NAME=webmakaka/microservice-$MICRO_SERVICE
    - export IMAGE_TAG=$SERVICE_VERSION
    - export MICRO_SERVICE=$MICRO_SERVICE
    - export SERVICE_PORT=$SERVICE_PORT
    - export REPLICAS=$REPLICAS
    - export KUBECONFIG=$KUBE_CONFIG
  script:
    # Если вы используете alpine/kubectl, команда envsubst устанавливается через пакет gettext
    - apk add --no-cache gettext
    - envsubst < kubernetes/deployment.yaml | kubectl apply -f -
    - envsubst < kubernetes/service.yaml | kubectl apply -f -


Frontend / .gitlab-ci.yml


include:
  - project: microservice-cicd/ci-templates
    ref: main
    file:
      - build.yml
      - deploy-k8s.yml

variables:
  MICRO_SERVICE: frontend
  SERVICE_VERSION: '1.3'
  SERVICE_PORT: 3000
  REPLICAS: 2

stages:
  - build
  - deploy


$ kubectl expose service frontend \
  --type=NodePort \
  --name=frontend-nodeport \
  --port=3000 \
  --target-port=3000 \
  -n my-micro-service


$ kubectl get svc -n my-micro-service
NAME                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
frontend            ClusterIP   10.99.58.44      <none>        3000/TCP         16m
frontend-nodeport   NodePort    10.104.194.132   <none>        3000:32242/TCP   37s


// OK!
http://192.168.49.2.nip.io:32242/