[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).
- Application repository sẽ chứa source code của ứng dụng
- 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:
- Gitlab (Self-managed) và Gitlab CI
- Docker (Standalone, version 23.0)
- Portainer CE (version 2.18.4)
- Harbor (Private image registry)
Application Repository
- Để 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>
- 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;"]
- 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
- 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
- Đă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
- 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ặcrefs/tags/tag_name
. Trường hợp để trống hệ thống sẽ tự động deploy từ nhánhrefs/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
- 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
All Rights Reserved