Seapy's Blog


Jenkins 에서 docker 이미지를 빌드하기 위해서는 Jenkins 컨테이너 안에 docker가 설치되어야 하는데
이러한 방법을 Docker in Docker 라고 부른다.

Jenkins에서 이미지를 빌드하는 작업은 중간정도 사양의 서버를 사용하는것을 추천하는데
AWS EC2의 경우 최소 t2.micro(메모리 1기가) 이상은 되어야 한다.

t2.micro 기본 옵션으로 실행시 이미지 하나 빌드하고 하드 디스크가 꽉차는것을 보게될것 이다.

Jenkins + Docker In Docker 설치

공식 jenkins 이미지를 사용하되 여기에 덧붙여서 docker 를 설치하는 Dockerfile과 실행 스크립트(jenkins_dind.sh)를 다음과 같이 생성한다.

  • Dockerfile 파일
FROM jenkins:1.554.3
MAINTAINER iamseapy@gmail.com
ENV REFRESHED_AT 2014-07-25

# jenkins 이미지에서 user를 jenkins 로 바꾸고 있어서 다시 root로 변경
USER root

# Docker에 필요한 패키지 설치
RUN apt-get update -qq
RUN apt-get install -qqy iptables ca-certificates lxc

# Docker 저장소의 apt-get을 이용해 docker 설치
RUN apt-get install -qqy apt-transport-https
RUN echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
RUN apt-get update -qq
RUN curl https://get.docker.io/gpg | apt-key add -
RUN apt-get update -qq
RUN apt-get install -qqy lxc-docker

VOLUME /var/lib/docker

# jenkins의 JVM 메모리를 늘린다
ENV JAVA_ARGS -Xms512m -Xmx1024m

ADD jenkins_dind.sh /usr/local/bin/jenkins_dind.sh
RUN chmod +x /usr/local/bin/jenkins_dind.sh

CMD ["/usr/local/bin/jenkins_dind.sh"]
  • jenkins_dind.sh 파일
#!/bin/bash

# First, make sure that cgroups are mounted correctly.
CGROUP=/sys/fs/cgroup

[ -d $CGROUP ] ||
  mkdir $CGROUP

mountpoint -q $CGROUP ||
  mount -n -t tmpfs -o uid=0,gid=0,mode=0755 cgroup $CGROUP || {
    echo "Could not make a tmpfs mount. Did you use -privileged?"
    exit 1
  }

# Mount the cgroup hierarchies exactly as they are in the parent system.
for SUBSYS in $(cut -d: -f2 /proc/1/cgroup)
do
  [ -d $CGROUP/$SUBSYS ] || mkdir $CGROUP/$SUBSYS
  mountpoint -q $CGROUP/$SUBSYS ||
    mount -n -t cgroup -o $SUBSYS cgroup $CGROUP/$SUBSYS
done

# Now, close extraneous file descriptors.
pushd /proc/self/fd
for FD in *
do
  case "$FD" in
  # Keep stdin/stdout/stderr
  [012])
    ;;
  # Nuke everything else
  *)
    eval exec "$FD>&-"
    ;;
  esac
done
popd

docker -d &
exec /usr/bin/java -jar /usr/share/jenkins/jenkins.war

jenkins_dind.sh는 Jenkins와 docker를 동시에 실행시키는 스크립트다.

이제 Jenkins + Docker In Docker 이미지를 빌드한다.

$ docker build -t seapy/jenkins-dind:1.554.3 .

예시의 소스 및 이미지는 githubDocker hub 에서 확인 가능하다.

이대로 사용할것이라면 이번 Dockerfile, jenkins_dind.sh는 무시하고 바로 다음 섹션 'Jenkins + Docker In Docker 실행'으로 넘어가면 된다. 이렇게 하면 내가 미리 만들어둔 https://registry.hub.docker.com/u/seapy/jenkins-dind/ 이미지를 사용하게된다.

Jenkins + Docker In Docker 실행

Jenkins에서 사용할 폴더를 호스트에 만들고 이를 Jenkins 컨테이너에 볼륨으로 마운트 한다. 이렇게 하면 컨테이너 업그레이드시에도 데이터 소실없이 가능하다.

$ mkdir -p /docker/jenkins_dind

다음 명령어를 실행해서 Jenkins 서버를 실행한다. 처음에는 테스트용으로 출력이 실시간으로 보이게 하고 정상작동이 확인되면 -d 옵션을 붙인다.

$ docker run -p 8080:8080 -v /docker/jenkins_dind:/var/jenkins_home --privileged seapy/jenkins-dind:1.554.3

여기서 중요한것은 --privileged 옵션으로 이 옵션을 주지 않으면 Docker In Docker 기능이 제대로 동작하지 않는다.

이 옵션이 있으면 컨테이너가 호스트의 root 권한레벨을 가지게 된다.

보안 옵션, git 설정

보안 옵션 및 git 플러그인에 대해서는 Docker를 이용한 Jenkins 설치 참고

빌드 작업 설정

대쉬 보드 좌측 상단의 새로운 Item > Build a free-style software project를 선택해서 새로운 작업을 추가하자.

git을 사용하는 경우 소스 코드 관리에서 저장소 주소를 입력하고 Credentials에는 서버의 개인키를 입력한다.
이 값은 github, bitbucket의 SSH Deploy Key에 추가한 공개키에 대응하는 개인키여야한다.

이제 마지막으로 실제 빌드 작업을 하는 스크립트를 추가할 차례이다.

Build 영역의 Add build step > Execute shell을 선택하고 텍스트 박스에 다음과 같이 입력한다.

echo "-----BEGIN RSA PRIVATE KEY-----
M+y/Hl3bm5Q/어쩌구저쩌구난리
여기에는비밀키가들어간다
-----END RSA PRIVATE KEY-----" > id_rsa

chmod 600 id_rsa

docker build -t seapy/testjob:test . 

docker login --email="이메일주소" --password="비밀번호" --username="seapy"

docker push seapy/testjob:test

id_rsa 파일의 추가 및 권한설정은 프로젝트 내부에서 git 저장소 접근시 필요한경우에만 추가한다.
ruby에서 개인 저장소에 올려둔 gem을 사용하는경우 설정이 필요하다.

docker loginDocker Hub 사이트에 인증체크하는것으로 개인 저장소를 사용한다면 불필요하다.

오류 상황

Jenkins 실행은 됬는데 docker 데몬 실행 오류가 나는경우

[error] attach_loopback.go:39 There are no more loopback device available.
loopback mounting failed

위의 오류와 함께 docker 데몬이 실행되지 않는경우가 있다.
docker 호스트에서 아래 명령어를 실행하면 된다.

$ kill -15 `ps ax | grep "docker -d" | grep -v grep | awk {'print $1'}`

결론

여기 까지 git 소스를 받아서 docker 이미지를 생성하고 저장소에 푸쉬하는것을 알아보았다.

Jenkins의 다양한 기능을 사용하면 git 소스 변경시마다 이미지를 생성할수도 있고 바로 배포까지 가능하게 할 수도 있다.

처음 Jenkins에서 docker 빌드를 하려고 했던건 편리함을 위해서였는데 당연한것이지만 생각보다 Jenkins 하드디스크 공간이 많이 필요해서 개인프로젝트라면 그냥 개발 컴퓨터에서 빌드하고 푸쉬하는것이 좋겠다.

참고