Published on

Jenkins trên Kubernetes: Build docker image bằng Kaniko

Giới thiệu

Ở các bài viết trước, mình đã giới thiệu cách Deploy Jenkins trên Kubernetes. Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu cách build docker image trên con Jenkins này bằng công cụ Kaniko.

Run Docker in a Docker Container

Như chúng ta đã biết, thì bản chất của việc deploy Jenkins trên cụm Kubernetes là chúng ta chạy Jenkins master ở dang 1 docker container (pod) và các Agent sẽ được tạo khi các pipeline chay. Agent chính là docker container được đóng gói trong pod.

Như vậy nếu Pipeline của chúng ta chạy task build docker image chẳng hạn, thì sẽ phát sinh ra vấn đề docker container có thể chạy được các lệnh docker để làm nhiệm vụ build docker image khác.

lấy mỡ nó rán nó

Khái niệm này có thể search với từ khóa Run Docker in a Docker Container. Về cơ bản sẽ có 2 giải pháp chính để chúng ta làm được nhiệm vụ đó:

  • DooD (Docker outside of Docker): là một phương pháp triển khai Docker container trong một Docker container khác. Nó được xây dựng dựa trên khái niệm Docker socket (docker.sock), một cơ chế cho phép các container truy cập vào Docker daemon của host. Với DooD, Docker daemon được chạy trên host và các container được triển khai trong một môi trường Docker độc lập.
  • DinD (Docker in Docker): là giải pháp khác cho việc triển khai Docker container trong một Docker container khác. Nó bao gồm việc chạy một Docker daemon trong một container và sử dụng nó để tạo ra các container con. Tuy nhiên, DinD không được khuyến khích sử dụng do các vấn đề về bảo mật, quản lý và hiệu suất.

Cả hai giải pháp trên có nhiều hạn chế và cần được cân nhắc kỹ trước khi triển khai sử dụng.

Kaniko

Kaniko là một công cụ mã nguồn mở dùng để xây dựng các image Docker từ một Dockerfile mà không cần sử dụng daemon Docker. Kaniko có thể chạy trên bất kỳ nền tảng nào hỗ trợ container, như Kubernetes, Google Cloud Build, Tekton, Jenkins, GitLab và nhiều hơn nữa. Kaniko giải quyết vấn đề về bảo mật và hiệu suất khi xây dựng các image Docker trên môi trường của container.

Một số tính năng chính của Kaniko là:

  • Hỗ trợ xây dựng các image Docker từ các nguồn khác nhau, như local directory, Git repository, Google Cloud Storage, Amazon S3, v.v. Hỗ trợ đẩy các image Docker lên các kho lưu trữ khác nhau, như Docker Hub, Google Container Registry, Amazon Elastic Container Registry, v.v.
  • Hỗ trợ sử dụng các biến môi trường, các tham số build-time và các file cấu hình để tùy biến quá trình xây dựng image
  • Hỗ trợ sử dụng cache để tăng tốc độ xây dựng image và giảm dung lượng image
  • Hỗ trợ xây dựng các image đa tầng (multi-stage) và kế thừa từ các image khác
  • Hỗ trợ kiểm tra tính hợp lệ của Dockerfile và thông báo lỗi nếu có

Build docker image

Để build docker image bằng kaniko, bạn cần có một Dockerfile, một build context và một registry để đẩy image sau khi xây dựng. Bạn cũng cần có một container hoặc Kubernetes cluster để chạy kaniko.

Kaniko hỗ trợ build và push image lên một remote docker registry, trong ví dụ này docker image sẽ được đẩy lên Docker Hub. Thường sau khi bạn dùng command docker login nếu thành công sẽ có 1 file $HOME/.docker/config.json trên Linux hoặc %USERPROFILE%/.docker/config.json trên Windows. Kaniko cần sử dụng file này để sau khi build có thể đẩy docker image của bạn lên Docker Hub.

config.json
{
  "auths": {
    "https://index.docker.io/v1/": {
      "auth": "xxxxxxxxxxxxxxx"
    }
  }
}

Nếu bạn sử dụng Container Registry khác thì vui lòng xem thêm trên Google.

Tiếp theo, chúng ta chỉ cần chạy command dưới đây:

docker run -ti --rm -v `pwd`:/workspace -v `pwd`/config.json:/kaniko/.docker/config.json:ro gcr.io/kaniko-project/executor:latest --dockerfile=Dockerfile --destination=yourimagename

Cần chú ý rằng, bạn cần chạy command tại folder chứa file config.jsonDockerfile muốn build.

Tích hợp vào Jenkins

Trong bài viết này, mình sử dụng Jenkins chạy trên Kubernetes. Vui lòng xem thêm bài viết Deploy Jenkins trên Kubernetes để có thêm thông tin.

Kubernetes secret

Tương tự như các bước ở trên, đầu tiên chúng ta phải lưu file config.json vào kubernetes secret

kubectl -n jenkins create secret generic kaniko-secret --from-file=config.json

Tạo pod Agent

Mục đích bước này cho phép chúng khởi tạo Kaniko khi cần sử dụng bằng cú pháp:

agent {
    label 'docker-build'
}

Vào phần Clouds và chọn Cluster mà đã đã có trước đó rồi chọn Add Pod Template

  • Name: kaniko
  • Namespace: jenkins
  • Labels: docker-build
  • Usage: Only build jobs with label expressions matching this node
  • Containers: (Chúng ta sẽ tạo 2 container)
    • Container 1:
      • Name: jnlp
      • Docker image: jenkins/inbound-agent:alpine
      • Working directory: /home/jenkins/agent
      • Command to run: Để trống
      • Arguments to pass to the command: để trống
      • Allocate pseudo-TTY: không chọn
    • Container 2:
      • Name: kaniko
      • Docker image: gcr.io/kaniko-project/executor:debug
      • Working directory: /home/jenkins/agent
      • Command to run: /busybox/cat
      • Allocate pseudo-TTY: Tích chọn
  • Volumnes:
    • Secret name: kaniko-secret
    • Mount path: /kaniko/.docker

Sau đó lưu lại. Nhưng config mình không đề cập thì để mặc định hoặc để trống. Tiếp theo, chúng ta sẽ thử làm một pipeline đơn giản để build docker image bằng Kaniko

Tích hợp vào Jenkin pipeline

Chúng ta cần tạo 1 project , với các thông số cấu hình như dưới đây

Dưới đây mình sẽ giới thiệu ví dụ đơn giản:

  • Checkout source code từ git
  • Chạy pipeline với cấu hình được thiết lập trong Jenkinsfile
Jenkinsfile
pipeline {
    agent any
    options {
        skipDefaultCheckout()
    }
    parameters {
        string(name:'GIT_URL', defaultValue:'https://github.com/nvtienanh/jenkins-agent-docker-kubectl.git', description:'The URL of the source Git repository to use.')
        string(name:'GIT_BRANCH', defaultValue:'main', description:'The branch in the source Git repository to use.')
    }
    stages {
        stage("Checkout") {
            steps {
                checkout(changelog: false, poll: false, scm: [
                    $class: 'GitSCM',
                    branches: [
                        [name: params.GIT_BRANCH],
                    ],
                    doGenerateSubmoduleConfigurations: false,
                    submoduleCfg: [],
                    userRemoteConfigs: [
                        [
                            url: params.GIT_URL,
                        ],
                    ],
                ])
                stash name: 'sources', includes: '**', excludes: '**/.git,**/.git/**'
            }
        }
        stage("Build docker") {
            agent {
                label 'docker-build'
            }
            steps {
                unstash 'sources'
                container(name: 'kaniko') {
                    sh '/kaniko/executor --context=`pwd` --dockerfile=`pwd`/Dockerfile  --destination=nvtienanh/jnlp-from-kaniko:latest'
                }
            }
        }
    }
}

Đây là file dockerimage sẽ build

Dockerfile
FROM jenkins/inbound-agent:alpine
USER root
ENV KUBECTLVERSION=1.28.3
RUN curl -fsSL https://dl.k8s.io/release/v$KUBECTLVERSION/bin/linux/amd64/kubectl > /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl
USER jenkins

Chi tiết có thể xem trên repository: Kubectl in Docker Jenkins

Hy vọng bài viết này sẽ có ích.