+10

[DevOps] Triển khai GitOps CI/CD trên Docker với Portainer

GitOps là một phương pháp sử dụng Git (Github, Gitlab...) như một nguồn cấu hình chính cho hạ tầng và ứng dụng (Single Source of Truth). Điều này giúp tập trung các cấu hình triển khai ứng dụng tại một nơi và mọi thay đổi chỉ được thực hiện thông qua git. Tất cả các thay đổi đối với mã code đều được theo dõi, giúp cập nhật dễ dàng đồng thời cung cấp khả năng kiểm soát phiên bản nếu cần khôi phục (rollback) trong trường hợp cần thiết. Khi triển khai một ứng dụng mới hoặc cập nhật ứng dụng hiện có, ta chỉ cần cập nhật cấu hình đã được lưu trữ - các quy trình triển khai liên tục (CD) sẽ xử lý mọi việc khác.

Tiến hành tìm kiếm từ khóa Gitops, đa phần các kết quả ta nhận được đều liên quan tới triển khai Gitops trên Kubernetes Cluster (K8s) với một số công cụ phổ biến như ArgoCD, FluxCD. Tuy nhiên, trong trường hợp ứng dụng chưa sẵn sàng triển khai trên K8s, chúng ta vẫn có thể cài đặt triển khai trên Docker Standalone hoặc Docker Swarm với công cụ Portainer.

GitOps hoạt động như thế nào?

GitOps tổ chức các quy trình triển khai thông qua mã code. Như vậy ta cần ít nhất 2 kho lưu trữ: kho lưu trữ ứng dụng (application repository) và kho lưu trữ cấu hình (configuration repository).

  1. Application repository sẽ chứa source code của ứng dụng
  2. Configuration repository bao gồm tất cả các cấu hình của ứng dụng (docker-compose, K8s manifest, Helm Chart,...). Mô tả ứng dụng hay dịch vụ (nessage broker, monitor tool,..) cần chạy với cấu hình và version nào.

Luồng thực hiện (GitOps flow)

  • Developer commit code lên application repository
  • Công cụ CI được kích hoạt tự động, tiến hành build và push docker image lên kho lưu trữ (DockerHub hoặc private registry)
  • Công cụ CI cập nhật thông tin phiên bản ứng dụng vào các file cấu hình triển khai ứng dụng lưu trên configuration repository
  • Công cụ CD phát hiện thay đổi (Qua polling, webhook,..) trên configuration repository thì cập nhật thay đổi về
  • Công cụ CD so sánh thông tin cấu hình mới với cấu hình hiện tại trên hệ thống, nếu phát hiện sai khác sẽ cảnh báo và đồng bộ lại theo cấu hình được thay đổi

Chuẩn bị

Công cụ

Dưới đây là các công cụ được trong phạm vi bài viết:

  1. Gitlab (Self-managed) và Gitlab CI
  2. Docker (Standalone, version 23.0)
  3. Portainer CE (version 2.18.4)
  4. Harbor (Private image registry)

Application Repository

  1. Để dễ dàng, ta sử dụng 1 file index.html với nội dung đơn giản như sau:
    <!DOCTYPE html>
    <html>
    <body style="background-color:DodgerBlue; text-align: center">
    <h1 style="color: white">GitOps Example</h1>
    </body>
    </html>
    
  2. Dockerfile để tạo Docker image, sử dụng Nginx làm web server
    FROM harbor.congtrinhviettel.com.vn/public/library/nginx:1.25.1-alpine
    COPY index.html /usr/share/nginx/html
    EXPOSE 80
    CMD ["nginx", "-g", "daemon off;"]
    
  3. gitlab-ci.yml define các job trong Gitlab CI. Ví dụ:
    Release Docker Image:
      image:
        name: $HARBOR_HOST/$HARBOR_PUBLIC_PROJECT/library/docker:23.0.6
        pull_policy: if-not-present
      needs: []
      services:
        - name: $HARBOR_HOST/$HARBOR_PUBLIC_PROJECT/library/docker:23.0.6-dind
          alias: docker
          pull_policy: if-not-present
      variables:
        DOCKER_TLS_CERTDIR: ""
        DOCKER_DRIVER: overlay2
      before_script:
        - echo $HARBOR_PASSWORD | docker login $HARBOR_HOST -u $HARBOR_USERNAME --password-stdin
      script:
        - docker image build -t $CI_PROJECT_NAME:$CI_COMMIT_SHA .
        #push image to harbor
        - docker image tag $CI_PROJECT_NAME:$CI_COMMIT_SHA $HARBOR_HOST/$HARBOR_PROJECT/$CI_PROJECT_NAME:$CI_COMMIT_SHA
        - docker image tag $CI_PROJECT_NAME:$CI_COMMIT_SHA $HARBOR_HOST/$HARBOR_PROJECT/$CI_PROJECT_NAME:latest
        - docker image push --all-tags $HARBOR_HOST/$HARBOR_PROJECT/$CI_PROJECT_NAME
      after_script:
        - docker logout $HARBOR_HOST
    
    Update Manifest:
      image:
        name: $HARBOR_HOST/chaunm16/git-with-ssh-client:1.0.0
        pull_policy: if-not-present
      variables:
        GIT_STRATEGY: none
      needs:
        - job: Release Docker Image
      before_script:
        - eval $(ssh-agent -s)
        - chmod og= $DEPLOY_SSH_KEY
        - ssh-add $DEPLOY_SSH_KEY
        - mkdir -p ~/.ssh
        - chmod 700 ~/.ssh
        - ssh-keyscan -H $CI_SERVER_HOST >> ~/.ssh/known_hosts
        - git config --global user.email "gitlab-bot@gitlab-vcc.com"
        - git config --global user.name "Gitlab-CI"
        - git clone git@$CI_SERVER_HOST:chaunm16/$DEPLOYMENT_REPOSITORY.git
        - cd $CI_PROJECT_DIR/$DEPLOYMENT_REPOSITORY
      script:
        - sed -i "s/chaunm16\/demo-web:.*/chaunm16\/demo-web:${CI_COMMIT_SHA}/g" docker-compose.yaml
        - git add .
        - git commit -m "Modifying image tag $CI_COMMIT_SHORT_SHA"
        - git push origin main
    
  4. Tạo 1 git repository và commit các file đã tạo trên. Gitlab CI sẽ tiến hành run các job được define từ gitlab-ci.yml. Sau khi thực hiện xong truy cập Harbor kiểm tra Image đã được push thành công hay chưa.

Configuration Repository

Khai báo cấu hình docker-compose.yaml cho ứng dụng và commit lên 1 repository khác:

version: "3.8"
services:
  demo-web:
    container_name: demo-web
    image: harbor.congtrinhviettel.com.vn/chaunm16/demo-web:latest
    deploy:
      replicas: 1
    ports:
      - "8865:80"

Cài đặt GitOps sử dụng Portainer

Tạo stack ứng dụng

  1. Đăng nhập vào Portainer, từ màn hình trang chủ chọn môi trường bạn muốn sử dụng để deploy, ví dụ chọn môi trường local
  2. Chọn Stacks -> Add Stack và điền các thông tin vào form

  • Name: Tên stack ứng dụng
  • Build Method: Chọn Use a git repository
  • Repository URL: Link Git tới Configuration Repository
  • Repository reference: Chọn nhánh hoặc tag cần deploy, cần điền theo format refs/heads/branch_name hoặc refs/tags/tag_name. Trường hợp để trống hệ thống sẽ tự động deploy từ nhánh refs/heads/main. Giá trị này có thể sẽ hữu ích trong trường hợp có nhiều môi trường muốn deploy từ 1 config repository, ví dụ môi trường production sẽ reference tới nhánh production,...
  • Compose path: Đường dẫn tới file docker-compose. Mặc định sẽ đọc từ file docker-compose.yml
  • Authentication: Trường hợp git cần xác thực, bật option và điền user/token. Bạn nên tạo và sử dụng access token
  • Automatic Update: Bật tự động cập nhật. cho phép tự động re-deploy ứng dụng. Đây là điều kiện đảm bảo GitOps được cài đặt. Portainer cung cấp 2 cơ chế Polling và Webhook
    • Polling: Portainer sẽ liên tục kiểm tra trạng thái Git config repository định kỳ (mặc định là 5 phút hoặc có thể cấu hình qua giá trị Fetch Interval)
    • Webhook: Tạo ra 1 webhook URL để kích hoạt re-deploy khi cần thiết
  1. Chọn Deploy the stack và đợi 1 vài phút cho tới khi ứng dụng được deploy lên docker

Cập nhật ứng dụng

Để có thể tự động re-deploy sau mỗi lần commit code, ta cần thực hiện bật automatic update trong quá trình tạo stack. Portainer sẽ thực hiện kiểm tra theo các bước sau:

  • Portainer kết nối tới Git Repository và truy xuất tới mã SHA của commit gần nhất
  • Nếu mã SHA của commit gần nhất trùng với mã SHA của portainer stack (Portainer sẽ lưu commit SHA gần nhất trong quá trình khởi tạo stack), Stack được coi là up-to-date và không thực hiện hành động nào tiếp
  • Nếu mã SHA commit gần nhất không trùng với mã SHA của stack, portainer sẽ kích hoạt re-deploy lại ứng dụng qua compose path: docker-compose up -f my-compose-file -f additional-compose-file -up d

Trường hợp sử dụng cơ chế Webhook để thực hiện automatic update, Git config repository cần khai báo webhook stack đã tạo và kích hoạt sau mỗi lần được push code. Ta truy cập vào config repository, chọn Settings -> Webhooks, khai báo URL và trigger Push Event

Để kiểm tra, ta tiến hành thay đổi source code trên application repository, ví dụ ta thay đổi style:

<body style="background-color:Red; text-align: center">
<h1 style="color: white">GitOps Example</h1>
</body>

Commit code và kiểm tra kết quả

Kết luận

Portainer là một công cụ tương đối tốt trong việc quản lý Docker. Cập nhật stack/application tự động cho phép chúng ta xây dựng một luồng GitOps đơn giản.

Hi vọng bài viết có hữu ích cho các bạn!!!

Tài liệu tham khảo

  1. https://www.portainer.io/gitops-automation
  2. https://www.portainer.io/blog/gitops-with-portainer-using-github-actions
  3. https://medium.com/linux-shots/gitops-on-docker-using-portainer-8712ba7d38c9
  4. https://portal.portainer.io/knowledge/how-do-automatic-updates-for-stacks-applications-work

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.