컨테이너 기반의 서비스를 사용할 때 이미지 빌드 서버를 따로 구축하는 경우가 있습니다. 일반적으로 개발자는 도커나,containerd 가 설치되어 있는 환경에서 CLI를 통해 이미지를 빌드하지만 서비스 레이어에서 User가 이미지를 빌드하는 경우가 생깁니다. 예를 들어 Kubeflow에서 사용자가 노트북을 통해 작업을 하고 해당 노트북을 공유하기 위해 이미지를 저장하는 경우가 생깁니다. 서비스와 같은 환경에서 빌드를 진행할 경우 사용자가 적고, 이미지 빌드 작업이 리소스를 별로 사용하지 않으면 문제가 없지만, 많은 리소스를 사용할 경우 빌드하는데 리소스를 많이 사용해 기존 서비스나 서버의 프로세스에 영향을 끼칠 수 있기 때문에 외부 빌드 서버를 따로 구축합니다.
일반적으로 VM이나 서버를 따로 구축해 Container runtime(dockerd,containerd)만 설치하여 빌드 서버로 사용합니다.
도커는 Server-client 구조이기 때문에 daemon에 접근만 가능하면 외부에서 사용할 수 있습니다. 이를 위해 EC2 두 대를 생성해 Server에는 dockerd Client에는 docker cli 만 설치합니다.
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
# add gpg key
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# add apt repo
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# docker install (client 서버에는 cli만 설치)
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
호스트 서버에서 도커가 정상적으로 설치가 완료되었다면 외부에서 접속하기 위해 설정을 변경해야합니다. systemctl을 통해 docker service를 조회하면 다음과 같이 서비스 설정파일에 대한 위치가 나옵니다. 해당 경로로 접속해 -H 옵션을 추가해 외부에서 접속이 가능하도록 설정합니다. 테스트를 위해 0.0.0.0
으로 접근 설정 시 외부에서 Public IP만 알면 접근이 가능하므로 보안상 위험합니다. 실제 사용 환경에서는 내부에서만 사용 가능 하도록 Private IP로 설정합니다.
이후 서비스 재시작 후 docker-cli만 설치한 클라이언트 서버에 접속해 DOCKER_HOST 변수를 추가합니다. docker-cli는 해당 변수를 참조하여 도커 서버와 통신합니다. 변수를 host 서버의 주소로 설정해 통신을 할 수 있도록 설정합니다. 또한 docker -H server주소:port
명령어를 통해 변수 설정 없이 접근 할 수 있습니다.
위와 같은 DOCKER_HOST 변수를 설정하여 접근하는 방법 외에도 19.03 버전에 context 기능이 추가되어 이를 사용할 수 있습니다. 아래와 같이 context를 추가하여 사용하면 변수 설정 없이 remote server에 접근이 가능합니다. 다만 DOCKER_HOST 변수가 설정되어있을 경우 context가 override 되기 때문에 변수를 해제 해야합니다.
위와 같이 따로 VM이나 서버를 구축하여 도커 빌드만을 위한 서버로 사용하면서 Kubernetes 나 도커에 Ubutu이미지를 사용해 docker만 설치해 빌드용 컨테이너를 만들어 리소스를 할당하여 사용하면 안되나? 라는 생각이 들었습니다.
일반적으로 컨테이너에서 도커를 사용하는 방법으로는 컨테이너 내부에 데몬을 설치해 사용하거나(Docker In Docker), Host의 docker.socket 파일을 컨테이너에 마운트하여 사용하는 방식( Docker out of Docker)를 사용합니다.
하지만 두 가지 방식 모두 보안적 이슈가 있어 사용을 권장하고 있지 않습니다. DinD 방식으로 경우 내부 컨테이너를 생성할 때 Security profile이 외부 도커와 confilct 되어 SELinux와 같은 Linux Security Modules이 호환되지 않을 수 있고, filesystem이 정상적으로 동작하지 않아 이미지 관리나 컨테이너 관리에 어려움을 겪을 수 있습니다.(Docker in Docker 의 문제점) DooD 는 그나마 DinD 보다 권장되지만 마찬가지로 안전한다고 볼 수 없고 Permission 과 같은 문제가 발생 할 수 있습니다.
Kaniko는 컨테이너 내부나 쿠버네티스에서 이미지를 빌드할 수 있는 툴로, docker daemon에 상관없이 사용할 수 있습니다.Kaniko는 도커 데몬과 비슷하게 Dockerfile을 기반으로 해당 디렉터리를 참조해 이미지를 빌드합니다. 또한 S3, GCS 버킷 등 에서 빌드 컨텍스트(Dockerfile 이 포함 된 이미지 빌드에 필요한 파일)을 참조해 이미지를 생성할 수 있습니다.
Kaniko의 사용법은 간단합니다.우선 이미지를 push 하기 위해 docker login 정보를 secret으로 생성합니다. 이private repository를 사용하기 위해서는 docker-server option으로 repository 주소를 설정합니다.
kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>
gcr.io/kaniko-project/executor:latest
이미지를 사용하여 파드를 생성하는데(debug 이미지를 사용하면 sh를 사용 할 수 있음). args에 이미지가 push 될 repository를 설정합니다. 현재 테스트를 위해 hostpath로 Dockerfile이 위치하는 폴더를 파드와 마운트 해 excutor가 해당 파일을 이용해 이미지를 빌드합니다.
apiVersion: v1
kind: Pod
metadata:
name: kaniko2
spec:
containers:
- name: kaniko
image: gcr.io/kaniko-project/executor:latest
args: ["--dockerfile=/workspace/dockerfile",
"--context=dir://workspace",
"--destination=seokbin9023/my-repository"]
volumeMounts:
- name: kaniko-secret
mountPath: /kaniko/.docker
- name: dockerfile-storage
mountPath: /workspace
restartPolicy: Never
nodeSelector:
kubernetes.io/hostname: worker1
volumes:
- name: kaniko-secret
secret:
secretName: regcred
items:
- key: .dockerconfigjson
path: config.json
- name: dockerfie-storage
hostPath:
path: /home/ubuntu/kaniko
파드 생성시 이미지 빌드 후 설정한 repository로 푸시한 후 끝나게 됩니다. 이후 repository를 확인하면 docker hub에 이미지가 올라온 것을 확인 할 수 있습니다.
Kaniko 를 이용하면 쿠버네티스에서 또한 이미지 빌드 기능을 실행할 수 있습니다. 이미지 빌드 Cache를 제공해 빌드를 효율적으로 할 수 있으며, docker가 제공하는 대부분의 기능을 제공합니다, 또한 Pod 형태로 생성되기 때문에 리소스의 양을 지정할 수 있습니다. 서비스 레이어에서의 이미지 빌드 기능으로 사용하거나, GitOps를 통해 Kubernetes CI/CD를 사용할 수 있을 거 같습니다.