K8S 12: GitOps - Using Flux with private Gitlab server and GCR for Continuous Deployment (CD) on GKE Cluster

Lần mò về GitOps thì thấy đây là 1 khái niệm rất hay và thú vị.

Table of Contents

Giới thiệu

Lần mò về GitOps thì thấy đây là 1 khái niệm rất hay và thú vị

Như các bạn thấy trên hình, Flux hoạt động như 1 operator sẽ đi scan 1 Git repository và 1 (hoặc nhiều) Docker registries.
Mỗi khi bạn commit 1 file yaml lên Git repository đó, Flux sẽ tự động deploy file đó vào k8s (Đây chính là GitOps). Nếu bạn thay đổi nội dung file yaml đó và commit tiếp, Flux cũng sẽ update resources tương ứng. Hoặc khi bạn push 1 images mới lên Container Registry, Flux cũng scan Registry và sẽ update app của bạn sử dụng images mới.

Như vậy, việc centralize mọi thứ trong 1 Git repository giúp bạn dễ dàng audit những thay đổi thông qua các commit của Git. Các kubernetes resources cũng dễ dàng đọc hiểu và chỉnh sửa.

Bài này mình sẽ làm 1 demo về việc sử dụng Weave Flux để setup CD (Continuous Deployment) với Gitlab Server riêng và Private Registry riêng của Google (GCR). Mọi thứ làm trên GCP, GKE (cũng của Google thôi)

Các câu lệnh ở đây đều làm trên CloudShell của GCP nhé

Yêu cầu

Đã làm theo bài sau (Nếu bạn đã có Gitlab server riêng thì có thể bỏ qua):
K8S 10

Sau khi làm theo link trên thì chúng ta có 1 Gitlab server riêng ở domain như sau:
https://gitlab.your-subdomain.your-domain.net/

Chuẩn bị biến môi trường, những biến này sẽ sử dụng để tạo 1 số file ở các phần sau:

export PROJECT_ID="your-project-id"

# Ở đây sẽ là subdomain để route traffic của app Hello World vào nhé, 
# nếu ko có cái này cũng ko sao, khi test chúng ta sẽ sử dụng chức năng Web Preview của CloudShell cũng được
export SUBDOMAIN="your-subdomain.your-domain.net"

Cách làm

1. Setting GitOps Repository

Giờ sẽ setting 1 chút trên Gitlab server của bạn:
Tạo user flux với role Admin
Login vào user flux
Tạo personal access token của user flux (https://gitlab.your-subdomain.your-domain.net/profile/personal_access_tokens)

Token có dạng 3KzPQxrkoc6amdQKxAhw, hãy giữ bí mật token này để lát sử dụng sau

Trên CloudShell tạo biến môi trường lưu token vào:

export PERSONAL_ACCESS_TOKEN="3KzPQxrkoc6amdQKxAhw"

Trên Gitlab tạo project để user flux sử dụng làm GitOps repository
Tên project thì tùy ý: đây mình sẽ đặt flux-get-started
Link project sẽ kiểu kiểu như này https://gitlab.your-subdomain.your-domain.net/flux/flux-get-started

Từ CloudShell clone project vừa tạo về:

cd ~
git clone https://gitlab.your-subdomain.your-domain.net/flux/flux-get-started && cd flux-get-started

2. Tạo 1 App sample Hello World App để test

Viết 1 app đơn giản chạy bằng Go (command sau sẽ tạo ra file main.go)

cat > ./main.go <<EOF
package main

import (
 "fmt"
 "log"
 "net/http"
)

var version = 1

func handler(w http.ResponseWriter, r *http.Request) {
 fmt.Fprintln(w, "Hello 世界... from v %s", version)
}
func main() {
 http.HandleFunc("/", handler)
 log.Fatal(http.ListenAndServe(":8888", nil))
}
EOF

Tạo Dockerfile và build images riêng của bạn

cat > ./Dockerfile <<EOF
FROM golang:1.11-alpine AS build
WORKDIR /
COPY main.go go.* /
RUN CGO_ENABLED=0 go build -o /bin/demo
FROM scratch 
COPY --from=build /bin/demo /bin/demo
ENTRYPOINT ["/bin/demo"] 
EOF

Build image từ Dockerfile trên

docker image build -t helloapp:latest .

Confirm rằng images đã được tạo ra

docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
helloapp            latest              c06c85757d4e        About a minute ago   6.51MB
<none>              <none>              9c7ddef38ecb        About a minute ago   325MB
golang              1.11-alpine         e116d2efa2ab        9 months ago         312MB

Đánh tag cho images đó

docker tag helloapp:latest gcr.io/${PROJECT_ID}/helloapp:latest

Push images có tag latest đó lên GCR của bạn

docker push gcr.io/${PROJECT_ID}/helloapp:latest

file hello-app.yaml bao gồm k8s Service và Deployment resources

cat > ./hello-app.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  ports:
  - port: 80
    targetPort: 8888
  selector:
    app: hello

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  selector:
    matchLabels:
      app: hello
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - name: hello
        image: gcr.io/${PROJECT_ID}/helloapp:latest
        ports:
        - containerPort: 8888
EOF

file hello-ingress.yaml chứa Ingress giúp route traffic từ 1 subdomain cụ thể vào service của Hello App
Nếu các bạn ko muốn tạo file này cũng ko sao, khi test app chúng ta sẽ sử dụng chức năng Web Preview của CloudShell

cat > ./hello-ingress.yaml <<EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: ${SUBDOMAIN}
    http:
      paths:
      - path: /
        backend:
          serviceName: hello
          servicePort: 80
EOF

Từ CloudShell bạn hãy push 2 file hello-app.yamlhello-ingress.yaml lên project flux-get-started

cd ~/flux-get-started
git add hello-app.yaml hello-ingress.yaml
git commit "Commit yaml files"
git push origin master

2. Install Flux

# Add the Flux repo:
helm repo add fluxcd https://charts.fluxcd.io
# Trong Cluster tạo namespace `flux`: 
kubectl create namespace flux
# Tạo Secret trong k8s Cluster bằng personal access token đã tạo ở step 1: 
kubectl create secret generic flux-git-auth --namespace flux --from-literal=GIT_AUTHUSER=flux --from-literal=GIT_AUTHKEY=${PERSONAL_ACCESS_TOKEN}

Install flux, chú ý sửa link đến Gitlab Server của bạn:

helm upgrade -i flux fluxcd/flux \
--set git.url='https://$(GIT_AUTHUSER):$(GIT_AUTHKEY)@gitlab.your-subdomain.your-domain.net/flux/flux-get-started.git' \
--set env.secretName=flux-git-auth \
--namespace flux

3. Test

Sau khi install flux thành công, bạn sẽ nhận ra là có 1 pod và 1 svc và 1 ingress được tạo ra, đó chính là các resource để trên GitOps repository của bạn

k get pods,svc -A
NAMESPACE       NAME                                                    READY   STATUS      RESTARTS   AGE
default         hello-54d49c4bb5-hm9z6                                  1/1     Running     0          36s
flux            flux-7b88d998fd-zclth                                   1/1     Running     0          60s
flux            flux-memcached-64f7865494-xq46b                         1/1     Running     0          60s

NAMESPACE       NAME                                      TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                                   AGE                                 140m
default         hello                                     ClusterIP      10.68.71.230    <none>         80/TCP                                    79s
default         kubernetes                                ClusterIP      10.68.0.1       <none>         443/TCP                                   151m
flux            flux                                      ClusterIP      10.68.188.216   <none>         3030/TCP                                  103s
flux            flux-memcached                            ClusterIP      10.68.74.32     <none>         11211/TCP                                 103s

Giờ truy cập vào website your-subdomain.your-domain.net sẽ thấy app của bạn đã được apply tự động

Hoặc cũng có thể bạn dùng chức năng Web Preview của ClouShell để view web app:

kubectl port-forward pod/hello-54d49c4bb5-hm9z6 8888:8888 

3.1. Flux tự động scan GitOps Repository để update application

Giờ hãy thử thay đổi file yaml để xem Flux có tự động phản ánh những thay đổi đó vào Kubernetes cho bạn ko

Trước tiên hãy sửa version = 2 trong file main.go, rồi build lại 1 Docker images với tag :0.2.0 và push lên GCR

nano main.go
# change `version = 2`

# Build lại Docker images và đánh tag 0.2.0
docker image build -t helloapp:0.2.0 .

# tag cho images trên GCR là 0.2.0
docker tag helloapp:0.2.0 gcr.io/${PROJECT_ID}/helloapp:0.2.0

# Push images mới với tag :0.2.0 lên GCR
docker push gcr.io/${PROJECT_ID}/helloapp:0.2.0

Sau đó bạn sẽ sửa image trong file hello-app.yaml thành dùng image tag :0.2.0:

OLD
~~~
image: gcr.io/YOUR_PROJECT_ID/helloapp:latest
~~~
NEW
~~~
image: gcr.io/YOUR_PROJECT_ID/helloapp:0.2.0
~~~

rồi commit và push thay đổi trên lên GitOps repository của bạn:

git add hello-app.yaml
git commit -m "change to using image :0.2.0"
git push origin master

Vài phút sau bạn sẽ thấy 1 pod mới của hello app được tạo lên, pod cũ bị xóa đi

k get pods -A
NAMESPACE       NAME                                                    READY   STATUS      RESTARTS   AGE
default         hello-5856bc89cd-rdrdd                                  1/1     Running     0          37s

Vào check app trên giao diện:

⇒ Như vậy nghĩa là Flux đã tự động apply file yaml mới cho chúng ta rồi!

3.2. Flux tự động scan Registry để update application

Trước hết để làm điều này thì cần phải add annotation vào Deployment đã

Sửa file hello-app.yaml add thêm dòng sau vào

  annotations:
    flux.weave.works/automated: "true"
    flux.weave.works/tag.chart-image: semver:~0.2
cat > ./hello-app.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  ports:
  - port: 80
    targetPort: 8888
  selector:
    app: hello

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
  annotations:
    flux.weave.works/automated: "true"
    flux.weave.works/tag.chart-image: semver:~0.2
spec:
  selector:
    matchLabels:
      app: hello
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - name: hello
        image: gcr.io/${PROJECT_ID}/helloapp:0.2.0
        ports:
        - containerPort: 8888
EOF

rồi commit và push thay đổi trên lên GitOps repository của bạn:

git add hello-app.yaml
git commit -m "add annotation using semver"
git push origin master

Giờ bạn thấy app của bạn chưa có gì thay đổi, vẫn là app version cũ nhé

Giả sử vì 1 lý do gì đó, bạn cần upgrade version của app lên 0.2.2,
sửa version = 2.2 trong file main.go, rồi build lại 1 Docker images với tag :0.2.2 và push lên GCR

nano main.go
# change version to `2.2`

# Build lại Docker images và đánh tag 0.2.2
docker image build -t helloapp:0.2.2 .

# tag cho images trên GCR là 0.2.2
docker tag helloapp:0.2.2 gcr.io/${PROJECT_ID}/helloapp:0.2.2

# Push images mới với tag :0.2.2 lên GCR
docker push gcr.io/${PROJECT_ID}/helloapp:0.2.2

Gần như ngay lập tức, bạn sẽ thấy 1 pod mới của Hello App được tạo lên, pod cũ bị xóa đi, webapp cũng thay đổi giao diện tương ứng

Đấy chính là nhờ các annotation trong file hello-app.yaml, nó giúp Flux tự động apply thay đổi.
Mỗi khi có image mới với tag bắt đầu bằng 0.2.* thì app sẽ được update tự động

Rất thú vị và dễ hiểu phải ko, hãy thử áp dụng Flux xem sao nhé

Done! 🎉🎉

CREDIT

https://medium.com/@m.k.joerg/gitops-weave-flux-in-detail-77ce36945646
https://docs.fluxcd.io/en/1.19.0/tutorials/get-started-helm/
https://github.com/fluxcd/flux/blob/master/chart/flux/values.yaml
https://www.weave.works/blog/managing-helm-releases-the-gitops-way
https://github.com/fluxcd/flux/blob/master/docs/guides/use-private-git-host.md
https://github.com/fluxcd/flux/issues/1358

Thank You!

Your comment has been submitted. It will appear on this page shortly! OK

Yikes, Sorry!

Error occured. Couldn't submit your comment. Please try again. Thank You! OK

Leave a comment