
개요
흔히 컨테이너 이미지를 빌드하는 방법은 docker가 설치된 환경에서 docker build 명령어를 사용하는 것이다. 하지만 kubernetes cluster 안에서 컨테이너 이미지를 빌드하는 방법으로 사용하기에는 제약사항이 많다. 컨테이너 안에서 docker daemon을 띄우는 방법이나 host의 docker.sock을 마운트 하는 방법은 보안상 문제로 배척되곤 한다.
이러한 문제를 해결하고자 다양한 방법과 도구들이 등장했는데 그 중에 Kaniko를 통해 컨테이너 안에서 컨테이너 이미지를 빌드하는 방법과 이를 통해 Jenkins pipeline으로 만드는 방법에 대해 정리한다.
Kaniko
소개
Kaniko는 Docker daemon에 의존하지 않고 Dockerfile 안에 있는 각각의 명령들을 userspace 안에서 실행한다. 이 것은 kuberentes cluster와 같이 Docker daemon을 쉽게 혹은 안전하게 실행할 수 없는 환경에서 컨테이너 이미지를 빌드하는 것을 가능하게 해 준다.
준비
빌드한 이미지를 레지스트리에 올리기 위해서는 해당 레지스트리에 이미지를 올릴 수 있는 권한이 있는 자격증명이 필요하다.
docker hub에 이미지를 올린다고 가정하고 여기를 참고해서 username과 password를 가지고 secret을 생성하거나 docker login 후 생성된 ~/.docker/config.json 파일을 이용해 secret을 생성할 수 있다.
Dockerfile
컨테이너 이미지를 생성할 때 필요한 파일이다. 이 전 포스팅에서 만든 dockerfile을 사용.
사용
Docker에서 사용
# 현재 폴더 내 파일 확인
$ ls
Dockerfile package.json
$ docker run -it --rm \
-v `pwd`:/workspace \
-v `echo $HOME`/.docker/config.json:/kaniko/.docker/config.json:ro \
gcr.io/kaniko-project/executor:latest \
--dockerfile=Dockerfile \
--destination=xodwkx2/show-me-host:4.0.0
#결과
INFO[0000] Retrieving image manifest node:20-alpine
INFO[0000] Retrieving image node:20-alpine from registry index.docker.io
INFO[0002] Built cross stage deps: map[]
INFO[0002] Retrieving image manifest node:20-alpine
INFO[0002] Returning cached image manifest
INFO[0002] Executing 0 build triggers
INFO[0002] Building stage 'node:20-alpine' [idx: '0', base-idx: '-1']
INFO[0002] Unpacking rootfs as cmd COPY package*.json ./ requires it.
INFO[0007] WORKDIR /usr/src/app
INFO[0007] Cmd: workdir
INFO[0007] Changed working directory to /usr/src/app
INFO[0007] Creating directory /usr/src/app with uid -1 and gid -1
INFO[0007] Taking snapshot of files...
INFO[0007] Resolving srcs [package*.json]...
INFO[0007] COPY package*.json ./
INFO[0007] Resolving srcs [package*.json]...
INFO[0007] Taking snapshot of files...
INFO[0007] COPY . .
INFO[0007] Taking snapshot of files...
INFO[0007] EXPOSE 8080
INFO[0007] Cmd: EXPOSE
INFO[0007] Adding exposed port: 8080/tcp
INFO[0007] CMD [ "node", "index.js" ]
INFO[0008] Pushing image to xodwkx2/show-me-host:4.0.0
INFO[0013] Pushed index.docker.io/xodwkx2/show-me-host@sha256:066953d147c1cac5030d7505061bae0e5b9761df20331ec1b7b2883c55827208
이미지 업로드 확인

privileged container도 아니었고 docker.sock을 마운트 하지 않고 컨테이너 이미지를 빌드하고 레지스트리에 업로드까지 완료.
Kubernetes에서 사용하는 것도 동일할 것으로 판단하고 바로 Jenkins pipeline에 적용.
Jenkins Pipeline에 적용
jenkins pipeline에 적용 시
- kaniko 컨테이너가 jnlp 컨테이너가 준비되는 것을 기다려주지 않는다
- pipeline의 stage, step에서 동작을 관리하지 않는다
위와 같은 이유로 :latest 이미지를 사용할 수 없었고 대신 busybox의 sh가 추가된 :debug 이미지를 사용해 아래와 같이 pipeline을 설정.

Jenkinsfile
// Uses Declarative syntax to run commands inside a container.
pipeline {
agent {
kubernetes {
yamlFile 'jenkins-pod.yaml'
defaultContainer 'kaniko'
}
}
stages {
stage('build') {
steps {
container('kaniko') {
echo "Hello Kaniko."
sh 'pwd'
sh 'ls -alF'
sh '/kaniko/executor --context=./ --destination=xodwkx2/show-me-host:4.0.1 --dockerfile=./Dockerfile'
}
}
}
}
}
Jenkinsfile에서는 docker에서 kaniko를 실행했을 때와 동일하게 flag 옵션을 사용해서 kaniko 실행.
jenkins-pod.yaml
apiVersion: v1
kind: Pod
spec:
containers:
- name: kaniko
image: gcr.io/kaniko-project/executor:debug
command: ["sleep"]
args: ["infinity"]
volumeMounts:
- name: configjson
readOnly: true
mountPath: "/kaniko/.docker/"
volumes:
- name: configjson
secret:
secretName: xodwkx2-docker-registry-credential
items:
- key: .dockerconfigjson
path: config.json
pod 설정은 :latest가 아닌 :debug 이미지를 사용, busybox/sleep 사용을 위해.
이렇게 pipeline을 만들고 빌드를 하면 아래와 같이 kubernetes agent 안에서 컨테이너 이미지 빌드가 가능하다.
+ /kaniko/executor '--context=./' '--destination=xodwkx2/show-me-host:4.0.2' '--dockerfile=./Dockerfile'
[36mINFO[0m[0001] Retrieving image manifest node:20-alpine
...
[36mINFO[0m[0013] Pushed index.docker.io/xodwkx2/show-me-host@sha256:201e9ca50a32b17444aeaabed4b03fbe7fec9b2b1f819631c201f6cf3ba102c4
[Pipeline] }
...
[Pipeline] End of Pipeline
Finished: SUCCESS
