티스토리 뷰

스터디/CICD

1주차 - Image Build

Cand0 2025. 10. 18. 21:21

Jib으로 컨테이너 빌드해보기

Jib 이란?

 -> Java App을 빌드하기 위한 컨테이너 이미지 빌더

 

Jib은 Docker에 비해 Java라는 언어에 대한 이해를 바탕으로 동작한다고 이해했다. 기존 Docker는 코드가 바뀌면 전체를 re build 해야하지만, Jib은 이미 만들어둔 부분은 다시 안건드리고, 바뀐 부분만 새로 만들 수 있는 이미지 빌더다.

 

기존에는 Dockerfile을 만들어서 Project와 함께 docker로 Build 해야했지만,

https://cloud.google.com/java/getting-started/jib

 

Jib은 Project와 함께 코드로 관리할 수 있다. 다시말하면, 프로젝트에서 빌드하면서 이미지가 만들어지고 그 이미지를 Docker로 설정을 다시 구성하는게아니라 그냥 바로 활용할 수 있는 것이다.

https://cloud.google.com/java/getting-started/jib

이걸 테스트하기 위해 Maven을 사용하는데, Maven은 라비르러리 관리 뭐 어쩌구 저쩌구 해준다는데, Maven은 플랫폼?레벨이고 Jib은 플러그인 레벨이라고 한다. -> 그냥 Docker가 Container Engine을 사용하는 것처럼 Maven이 Jib을 이용하는 건듯?

그래서 실제로 Maven, Gradle과 같은 곳에서 Jib을 사용할 수 있다.

 

실제로 기본 세팅 {HOME}/.m2 밑에 이것저것 파일이 생기고 여기서 빌드들이 이루어지는 것 같다.

root@myk8s-worker:~/.m2# pwd
/root/.m2
root@myk8s-worker:~/.m2# tree -L 2
.
`-- repository
    |-- aopalliance
    |-- asm
    |-- backport-util-concurrent
    |-- ch
    |-- com
    |-- commons-codec
    |-- commons-io
    |-- commons-logging
    |-- io
    |-- jakarta
    |-- javax
    |-- net
    `-- org

15 directories, 0 files

 

그래서 실제로 mvn으로 complie해보고 결과를 확인해보면, pom.xml을 확인해서 빌드를 하고 지금 명령어 기준 docker.io/cand0/jib-example 이라는 이름으로 push 까지 해준다.

mvn compile com.google.cloud.tools:jib-maven-plugin:3.4.6:build \
  -Dimage=docker.io/cand0/jib-example:latest \
  -Djib.to.auth.username=cand0 \
  -Djib.to.auth.password={password} \
  -Djib.from.platforms=linux/amd64

 

Jar파일은 Java 프로그램 관련 파일을 하나로 묶어 놓은 파일인데, 아래와 같은 방식으로 결국에 Jar파일이 생기는 것도 확인할 수 있다.

root@myk8s-worker:~/.m2/repository/com/google/cloud/tools/jib-maven-plugin/3.4.6# ls -l
total 576
-rw-r--r-- 1 root root    212 Oct 13 12:10 _remote.repositories
-rw-r--r-- 1 root root 566936 Oct 13 12:10 jib-maven-plugin-3.4.6.jar
-rw-r--r-- 1 root root     41 Oct 13 12:10 jib-maven-plugin-3.4.6.jar.sha1
-rw-r--r-- 1 root root   4909 Oct 13 12:10 jib-maven-plugin-3.4.6.pom
-rw-r--r-- 1 root root     41 Oct 13 12:10 jib-maven-plugin-3.4.6.pom.sha1

 

base 이미지는 eclipse-temurin:11-jre인 것을 확인할 수 있고, eclipse-temurin:11-jre는 maven에서 image를 지정하지 않았을 때 기본적으로 사용하는 Image이다. Jib는 명령어를 실행하는 그 디렉토리의 pom.xml을 참고하여 하위 디렉토리들을 build해준다.

 

Buildah(+Podman)으로 컨테이너 빌드해보기

Builda란?

 - Jib은 Java언어만을 지원하는데, Builda는 다양한 언어를 지원한다.

Podman란?

 - Docker 명령어 치는 것 처럼, 컨테이너를 유지 관리하기 위한 도구. docker ps, docker inspect 같은 명령어를 podman ps, podman inspect 해서 사용할 수 있다.

 

먼저 Control Plane에 들어가서 Podman과 Crictl로 확인하면 이렇게 뜬다. crictl은 kubernetes의 저장소를 확인하는 거고, podman은 내가 추가로 설치했기 때문에, 다른 곳을 바라보고있다. (뭔가 OCI 구조에 의해서? 같은 곳을 바라보고 있을 줄 알았는데 그렇지는 않나보다.)

root@myk8s-control-plane:/# crictl images
IMAGE                                           TAG                  IMAGE ID            SIZE
docker.io/kindest/kindnetd                      v20250512-df8de77b   409467f978b4a       44.4MB
docker.io/kindest/local-path-helper             v20241212-8ac705d0   baa0d31514ee5       3.08MB
docker.io/kindest/local-path-provisioner        v20250214-acbabc1a   bbb6209cc873b       22.5MB
registry.k8s.io/coredns/coredns                 v1.11.3              c69fa2e9cbf5f       18.6MB
registry.k8s.io/etcd                            3.5.16-0             a9e7e6b294baf       57.7MB
registry.k8s.io/kube-apiserver-amd64            v1.32.8              0d4edaa48e2f9       98.1MB
registry.k8s.io/kube-apiserver                  v1.32.8              0d4edaa48e2f9       98.1MB
registry.k8s.io/kube-controller-manager-amd64   v1.32.8              b248d0b0c74ad       90.8MB
registry.k8s.io/kube-controller-manager         v1.32.8              b248d0b0c74ad       90.8MB
registry.k8s.io/kube-proxy-amd64                v1.32.8              d7b94972d43c5       95.3MB
registry.k8s.io/kube-proxy                      v1.32.8              d7b94972d43c5       95.3MB
registry.k8s.io/kube-scheduler-amd64            v1.32.8              2ac266f06c9a5       70.7MB
registry.k8s.io/kube-scheduler                  v1.32.8              2ac266f06c9a5       70.7MB
registry.k8s.io/pause                           3.10                 873ed75102791       320kB
root@myk8s-control-plane:/# podman images
REPOSITORY  TAG         IMAGE ID    CREATED     SIZE
### podman Image Path ###

root@myk8s-control-plane:/var/lib/containers/storage/overlay-images# podman info | grep -A3 store
store:
  configFile: /usr/share/containers/storage.conf
  containerStore:
    number: 0
root@myk8s-control-plane:/var/lib/containers/storage/overlay-images# cat /usr/share/containers/storage.conf
cat: /usr/share/containers/storage.conf: No such file or directory

---

없으면 default 경로는 아래라고한다.

---
root@myk8s-control-plane:/var/lib/containers/storage/overlay-images# ls -l
total 0
-rw-r--r-- 1 root root 0 Oct 13 12:49 images.lock

 

실제로 조금 해맨건, 그냥 kind로 구성해서 알아서 잘 해줬기때문에 안보인다는 거다.

runtime-endpoint: unix:///run/containerd/containerd.sockroot@myk8s-control-plane:/var/lib/containers/storage/overlay-images#
root@myk8s-control-plane:/var/lib/containers/storage/overlay-images#
root@myk8s-control-plane:/var/lib/containers/storage/overlay-images# cd /var/lib/containers/storage/overlay-images

---

실제로 podman과 같은 경로를 바라보고 있고, 그냥 kind에서 쿵짝쿵짝해서 아래에다가 만들었다고....

---

root@myk8s-control-plane:/var/lib/containerd# ls -l
total 44
drwxr-xr-x 4 root root 4096 Oct 13 11:47 io.containerd.content.v1.content
drwxr-xr-x 4 root root 4096 Oct 12 11:20 io.containerd.grpc.v1.cri
drwx------ 2 root root 4096 Oct 12 11:20 io.containerd.grpc.v1.introspection
drwx--x--x 2 root root 4096 Oct 12 11:20 io.containerd.metadata.v1.bolt
drwx--x--x 3 root root 4096 Oct 12 11:20 io.containerd.runtime.v2.task
drwx--x--x 2 root root 4096 Aug 27 16:53 io.containerd.sandbox.controller.v1.shim
drwx------ 2 root root 4096 Aug 27 16:53 io.containerd.snapshotter.v1.blockfile
drwx------ 2 root root 4096 Oct 13 11:47 io.containerd.snapshotter.v1.erofs
drwx------ 3 root root 4096 Oct 12 11:20 io.containerd.snapshotter.v1.native
drwx------ 3 root root 4096 Oct 12 11:20 io.containerd.snapshotter.v1.overlayfs
drwx------ 2 root root 4096 Oct 13 11:47 tmpmounts

 

실제로 Builda에서도 같은 경로를 바라보고 있다.

root@myk8s-control-plane:/var/lib/containers/storage# buildah info | grep GraphRoot
        "GraphRoot": "/var/lib/containers/storage",

 

그래서 결론적으로는 buildah랑, podman하고 같은 경로를 바라보고 있는 것을 확인했고, 아쉽지만 crictl은 kind에서 쿵짝쿵짝해서 다른 경로를 바라보게 한 것 같다.

root@myk8s-control-plane:~# podman images
REPOSITORY             TAG         IMAGE ID      CREATED     SIZE
quay.io/centos/centos  latest      8cc50b67966b  6 days ago  316 MB
root@myk8s-control-plane:~# buildah images
REPOSITORY              TAG      IMAGE ID       CREATED      SIZE
quay.io/centos/centos   latest   8cc50b67966b   6 days ago   316 MB
root@myk8s-control-plane:~# crictl images
IMAGE                                           TAG                  IMAGE ID            SIZE
docker.io/kindest/kindnetd                      v20250512-df8de77b   409467f978b4a       44.4MB
docker.io/kindest/local-path-helper             v20241212-8ac705d0   baa0d31514ee5       3.08MB
docker.io/kindest/local-path-provisioner        v20250214-acbabc1a   bbb6209cc873b       22.5MB
registry.k8s.io/coredns/coredns                 v1.11.3              c69fa2e9cbf5f       18.6MB
registry.k8s.io/etcd                            3.5.16-0             a9e7e6b294baf       57.7MB
registry.k8s.io/kube-apiserver-amd64            v1.32.8              0d4edaa48e2f9       98.1MB
registry.k8s.io/kube-apiserver                  v1.32.8              0d4edaa48e2f9       98.1MB
registry.k8s.io/kube-controller-manager-amd64   v1.32.8              b248d0b0c74ad       90.8MB
registry.k8s.io/kube-controller-manager         v1.32.8              b248d0b0c74ad       90.8MB
registry.k8s.io/kube-proxy-amd64                v1.32.8              d7b94972d43c5       95.3MB
registry.k8s.io/kube-proxy                      v1.32.8              d7b94972d43c5       95.3MB
registry.k8s.io/kube-scheduler-amd64            v1.32.8              2ac266f06c9a5       70.7MB
registry.k8s.io/kube-scheduler                  v1.32.8              2ac266f06c9a5       70.7MB
registry.k8s.io/pause                           3.10                 873ed75102791       320kB

 

이렇게 Layer를 추가할 수도있다. (전체를 재빌드하는게 아니라 특정 파일만 Update)

buildah copy centos-working-container index.html /var/www/html/index.html

 

뭔가 좀 더 찾아보니 두개가 다른 DB 형식을 써서 안된다는 것 같다.

containerd와 Podman/Buildah는 안되고, CRI-O와 Podman/Buildah는 가능하다고 한다.

 

Buildpacks을 통한 컨테이너 빌드

Buildpack을 사용하면 해당 dir 밑에 코드를 확인하고 어떤 builder image를 사용할지 확인할 수 있다.

nodejs-app $ ls -l
total 0
-rwxrwxrwx 1 cand0 cand0  73 Oct 12 20:29 package-lock.json
-rwxrwxrwx 1 cand0 cand0 386 Oct 12 20:29 package.json
-rwxrwxrwx 1 cand0 cand0 335 Oct 12 20:29 server.js
nodejs-app $ cat ./package.json | jq
{
  "name": "nodejs-app",
  "version": "1.0.0",
  "description": "Example app with Node.JS",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/gitops-cookbook"
  },
  "keywords": [
    "gitops",
    "cookbook"
  ],
  "author": "Natale Vinto",
  "license": "GPL-3.0"
}
nodejs-app $ pack builder suggest
Suggested builders:
        Google:                gcr.io/buildpacks/builder:google-22                     Ubuntu 22.04 base image with buildpacks for .NET, Dart, Go, Java, Node.js, PHP, Python, and Ruby                   
        Heroku:                heroku/builder:24                                       Ubuntu 24.04 AMD64+ARM64 base image with buildpacks for .NET, Go, Java, Node.js, PHP, Python, Ruby & Scala.        
        Paketo Buildpacks:     paketobuildpacks/builder-jammy-base                     Ubuntu 22.04 Jammy Jellyfish base image with buildpacks for Java, Go, .NET Core, Node.js, Python, Apache HTTPD, NGINX and Procfile
        Paketo Buildpacks:     paketobuildpacks/builder-jammy-buildpackless-static     Static base image (Ubuntu Jammy Jellyfish build image, distroless-like run image) with no buildpacks included. To use, specify buildpacks at build time.
        Paketo Buildpacks:     paketobuildpacks/builder-jammy-full                     Ubuntu 22.04 Jammy Jellyfish full image with buildpacks for Apache HTTPD, Go, Java, Java Native Image, .NET, NGINX, Node.js, PHP, Procfile, Python, and Ruby
        Paketo Buildpacks:     paketobuildpacks/builder-jammy-tiny                     Tiny base image (Ubuntu Jammy Jellyfish build image, distroless-like run image) with buildpacks for Java, Java Native Image and Go
        Paketo Buildpacks:     paketobuildpacks/builder-ubi8-base                      Ubi 8 base builder with buildpacks for Node.js, Java, Quarkus and Procfile                                         

Tip: Learn more about a specific builder with:
        pack builder inspect <builder-image>

이 builder를 통해 app을 build할 수 있고 왜 인지는 모르겠지만.... 45년전에 만들어진 docker image가 내 images에 들어와있는 것을 확인할 수 있다.

nodejs-app $ docker images | grep nodejs-app
nodejs-app                                  latest      9569abb5a686   45 years ago    374MB

 

GPT피셜 Reproducible Builds라는 개념이 있다고 한다.

이는 같은 코드와 같은 설정으로 빌드하면 오나전히 동일한 결과가 나와야한다는 개념이다. 그러다 보니 이미지를 생성할 때 그 때의 시점을 넣어서 build를 하면 시간이 매번 바껴서 코드는 그대로인데 시간이 바껴서 Hash가 바뀌게 된다. 그래서 그 시간을 그냥 고정 때려버려서 문제를 해결한다고 한다.

이렇게하면 이미지에 대한 Hash검증이 가능해지고, 매번 같은 Hash가 나오니 이미지에 대한 트러블 슈팅을 하 떄 좀 쉬울 지도...?

 

Shipwrite와 kanko or buildah를 사용한 쿠버네티스 기반 컨테이너 빌드

Shipwrite에서도 크게 다르지 않게, secrets으로 docker ID, PW넣고 build하면  자동으로 넘어가게 할 수 있다.

nodejs-app $ kubectl create -f buildrun-go.yaml
buildrun.shipwright.io/kaniko-golang-buildrun-6hnxd created
nodejs-app $ kubectl get pod -n default -w
NAME                                     READY   STATUS     RESTARTS   AGE
kaniko-golang-buildrun-6hnxd-h7rsd-pod   0/3     Init:0/2   0          3s
kaniko-golang-buildrun-6hnxd-h7rsd-pod   0/3     Init:1/2   0          6s
kaniko-golang-buildrun-6hnxd-h7rsd-pod   0/3     PodInitializing   0          9s
kaniko-golang-buildrun-6hnxd-h7rsd-pod   3/3     Running           0          36s
kaniko-golang-buildrun-6hnxd-h7rsd-pod   3/3     Running           0          36s
kaniko-golang-buildrun-6hnxd-h7rsd-pod   2/3     NotReady          0          39s
kaniko-golang-buildrun-6hnxd-h7rsd-pod   0/3     Completed         0          111s
kaniko-golang-buildrun-6hnxd-h7rsd-pod   0/3     Completed         0          112s

 

Customize

base image를 두고, 거기에 내가 인자값을 주는 형태다. helm을 사용하지 않을때? 이걸 사용 했던 것 같다.

사용법은 쉽다. 그냥 원하는 필드에다가 namePrefix: cand1- 이런식으로 넣어주면 되고, 실제 Name을 그대로 변경하려면 patches 로 해서 변경해주면 된다.

custom $ k get all
NAME                                         READY   STATUS      RESTARTS   AGE
pod/cand1-646754b7dd-m2dx8                   1/1     Running     0          5s
pod/cand1-646754b7dd-t64ht                   1/1     Running     0          5s
pod/cand1-my-nginx-646754b7dd-4wrnf          1/1     Running     0          5m27s
pod/cand1-my-nginx-646754b7dd-k7jbb          1/1     Running     0          5m27s
pod/dev-my-nginx-646754b7dd-2vc98            1/1     Running     0          7m47s
pod/dev-my-nginx-646754b7dd-7hd92            1/1     Running     0          7m47s
pod/kaniko-golang-buildrun-6hnxd-h7rsd-pod   0/3     Completed   0          14m
pod/my-nginx-646754b7dd-hxrzt                1/1     Running     0          74s
pod/my-nginx-646754b7dd-j6p7w                1/1     Running     0          74s
pod/newname-646754b7dd-5grpg                 1/1     Running     0          29s
pod/newname-646754b7dd-q7zxw                 1/1     Running     0          29s
pod/prod-my-nginx-646754b7dd-dk55d           1/1     Running     0          5m51s
pod/prod-my-nginx-646754b7dd-tgxcw           1/1     Running     0          5m51s

NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/cand1-my-nginx   ClusterIP   10.96.207.45    <none>        80/TCP    5m27s
service/dev-my-nginx     ClusterIP   10.96.52.57     <none>        80/TCP    7m47s
service/kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP   6d
service/my-nginx         ClusterIP   10.96.42.135    <none>        80/TCP    74s
service/prod-my-nginx    ClusterIP   10.96.158.135   <none>        80/TCP    5m51s

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/cand1            2/2     2            2           5s
deployment.apps/cand1-my-nginx   2/2     2            2           5m27s
deployment.apps/dev-my-nginx     2/2     2            2           7m47s
deployment.apps/my-nginx         2/2     2            2           74s
deployment.apps/newname          2/2     2            2           29s
deployment.apps/prod-my-nginx    2/2     2            2           5m51s

NAME                                        DESIRED   CURRENT   READY   AGE
replicaset.apps/cand1-646754b7dd            2         2         2       5s
replicaset.apps/cand1-my-nginx-646754b7dd   2         2         2       5m27s
replicaset.apps/dev-my-nginx-646754b7dd     2         2         2       7m47s
replicaset.apps/my-nginx-646754b7dd         2         2         2       74s
replicaset.apps/newname-646754b7dd          2         2         2       29s
replicaset.apps/prod-my-nginx-646754b7dd    2         2         2       5m51s
custom $ tree
.
├── base
│   ├── deployment.yaml
│   ├── kustomization.yaml
│   └── service.yaml
├── cand1
│   └── kustomization.yaml
├── dev
│   └── kustomization.yaml
└── prod
    └── kustomization.yaml

4 directories, 6 files
custom $ cat ./cand1/kustomization.yaml
resources:
- ../base

patches:
  - target:
      kind: Deployment
      name: my-nginx     # 원래 이름
    patch: |-
      - op: replace
        path: /metadata/name
        value: cand1

 

 

'스터디 > CICD' 카테고리의 다른 글

5주차 - Argo CD 2/3  (0) 2025.11.14
4주차 - Argo CD 1/3  (1) 2025.11.08
3주차 - Jenkins + ArgoCD  (0) 2025.11.02
2주차 - Helm, Tekton  (0) 2025.10.25
helm이 만든 secret에는 어떤 정보가 있을까?  (0) 2025.10.25
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/09   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함