Packer와 Ansible을 이용한 Docker Image 생성 및 배포
- -
Packer는 자동화된 이미지 빌더입니다.
Hashicorp에서 개발된 툴로, 다양한 프로비저너를 지원하여 AWS, Azure, GCP, VMware, Docker 등 다양한 이미지들을 HCL문을 이용하여 생성할 수 있습니다.
이번 가이드는 Docker를 Jenkins가 아닌 Packer를 이용하여 Build 후 Docker Hub로 바로 배포할 수 있도록 하였습니다.
* 준비물
Packer
Ansible
# Python3 pip 설치 및 Ansible 설치
pip3 설치
root@ubuntu:/home/ubuntu# apt-get install python3-pip -y
Ansible 설치
root@ubuntu:/home/ubuntu# pip3 install ansible
# Packer 설치
다운로드
root@ubuntu:/home/ubuntu# wget https://releases.hashicorp.com/packer/1.6.5/packer_1.6.5_linux_amd64.zip
압축 풀기
root@ubuntu:/home/ubuntu# unzip ./packer_1.6.5_linux_amd64.zip
bin으로 이동
root@ubuntu:/home/ubuntu# cp packer /usr/bin/
버전 확인
root@ubuntu:/home/ubuntu# packer version
Packer v1.6.5
스크립트 작성 방식은 두 가지가 있습니다.
json 형식이 있고, hcl2가 있습니다.
본 글은 hcl2를 기준으로 작성하였습니다.
먼저, terraform과 마찬가지로 variable이 있으며, 이를 통해 변수를 지정할 수 있습니다.
variable "dockerhub_username" {
type = string
default = "teichae"
}
variable "dockerhub_password" {
type = string
default = "password"
}
이미지 빌드 후 Docker Hub로 Push를 하기 위해 사용자 인증정보를 입력하였습니다.
이와 별개로 파일 이름이나 다른 형태로도 사용이 가능합니다.
이제 불러올 소스를 지정합니다.
source "docker" "docker" {
changes = ["VOLUME /data", "WORKDIR /data", "EXPOSE 80", "ENTRYPOINT [\"docker-entrypoint.sh\"]"]
commit = true
image = "ubuntu:18.04"
}
changes는 컨테이너에 적용할 옵션을 작성할 수 있습니다.
자세한 옵션은 이곳을 참조해주세요.
이제 불러온 소스를 바탕으로 빌드를 시작합니다.
sources에는 위에서 작성한 source.docker.docker를 불러옵니다.
build {
sources = ["source.docker.docker"]
provisioner "ansible" {
playbook_file = "provision.yml"
user = "root"
}
post-processors {
post-processor "docker-tag" {
repository = "teichae/packer-nginx"
tags = ["latest"]
}
post-processor "docker-push" {
login = "true"
login_username = "${var.dockerhub_username}"
login_password = "${var.dockerhub_password}"
}
}
빌드를 진행하기 위해 provisioner를 Ansible로 입력합니다.
Ansible Playbook을 이용하여 Docker Image안에 필요한 것들을 설치하기 위함입니다.
post-processors에서는 docker-tag를 이용하여 Docker의 tag 정보를 입력합니다.
이후 docker-push를 이용하여 Docker Hub에 Push를 해야 하는데, Push 하기 위해 login과 login_username, login_password 옵션은 필수적으로 들어가야 합니다.
맨 앞서 variable문으로 작성하였고, var.dockerhub_*를 이용하여 변수를 사용할 수 있습니다.
전체 코드입니다.
variable "dockerhub_username" {
type = string
default = "teichae"
}
variable "dockerhub_password" {
type = string
default = "password"
}
source "docker" "docker" {
changes = ["VOLUME /data", "WORKDIR /data", "EXPOSE 80", "ENTRYPOINT [\"docker-entrypoint.sh\"]"]
commit = true
image = "ubuntu:18.04"
}
build {
sources = ["source.docker.docker"]
provisioner "ansible" {
playbook_file = "provision.yml"
user = "root"
}
post-processors {
post-processor "docker-tag" {
repository = "teichae/packer-nginx"
tags = ["latest"]
}
post-processor "docker-push" {
login = "true"
login_username = "${var.dockerhub_username}"
login_password = "${var.dockerhub_password}"
}
}
}
이제 Container 안에서 사용할 ansible playbook의 내용입니다.
내용은 간단하게 Container 안에 Timezone을 KST로 변경하고, nginx를 설치한 후 docker-entrypoint.sh을 통해 nginx를 구동시켜 보았습니다.
- name: Python Install
hosts: all
gather_facts: no
tasks:
- name: Python Install
raw: apt-get -y update && apt-get install -y python3 python3-apt
- name: Build Container Image
hosts: all
tasks:
- name: Creating Workdir
file:
path: /data
state: directory
owner: root
group: root
mode: 0755
- name: Installing Applications
apt:
name: nginx, tzdata
state: present
- name: set timezone
timezone:
name: Asia/Seoul
- name: Add Entrypoints shell
copy:
src: config/{{ item }}
dest: /usr/local/bin/{{ item }}
mode: 0755
owner: root
group: root
with_items:
- docker-entrypoint.sh
- name: Container Cleanup
hosts: all
gather_facts: no
tasks:
- name: Remove Python
raw: apt-get purge -y python3 python3-apt && apt-get autoremove -y
- name: Remove Apt Lists
raw: rm -rf /var/lib/apt/lists/*
Python3은 Ansible을 사용하기 위해 설치하였고, 설치 이후 삭제하도록 설정했습니다.
tzdata는 timezone module을 사용하기 위해 설치하였습니다.
이제 packer build filename으로 하여 빌드를 시작해보겠습니다.
root@ubuntu:~/git/packer-example# packer build nginx.pkr.hcl
docker.docker: output will be in this color.
==> docker.docker: Creating a temporary directory for sharing data...
==> docker.docker: Pulling Docker image: ubuntu:18.04
docker.docker: 18.04: Pulling from library/ubuntu
docker.docker: Digest: sha256:646942475da61b4ce9cc5b3fadb42642ea90e5d0de46111458e100ff2c7031e6
docker.docker: Status: Image is up to date for ubuntu:18.04
docker.docker: docker.io/library/ubuntu:18.04
==> docker.docker: Starting docker container...
docker.docker: Run command: docker run -v /root/.packer.d/tmp326850331:/packer-files -d -i -t --entrypoint=/bin/sh -- ubuntu:18.04
docker.docker: Container ID: eea0f6d6824721face5dad840a766fecefa359a04060457cce22f1ea7dd84b9d
==> docker.docker: Using docker communicator to connect: 172.17.0.2
==> docker.docker: Provisioning with Ansible...
docker.docker: Setting up proxy adapter for Ansible....
==> docker.docker: Executing Ansible: ansible-playbook -e packer_build_name="docker" -e packer_builder_type=docker -e ansible_ssh_private_key_file=/tmp/ansible-key941741365 -i /tmp/packer-provisioner-ansible874125072 /root/git/packer-example/provision.yml
docker.docker:
docker.docker: PLAY [Python Install] **********************************************************
docker.docker:
docker.docker: TASK [Python Install] **********************************************************
docker.docker: changed: [default]
docker.docker:
docker.docker: PLAY [Build Container Image] ***************************************************
docker.docker:
docker.docker: TASK [Gathering Facts] *********************************************************
docker.docker: ok: [default]
docker.docker:
docker.docker: TASK [Creating Workdir] ********************************************************
docker.docker: changed: [default]
docker.docker:
docker.docker: TASK [Installing Applications] *************************************************
docker.docker: changed: [default]
docker.docker:
docker.docker: TASK [set timezone] ************************************************************
docker.docker: changed: [default]
docker.docker:
docker.docker: TASK [Add Entrypoints shell] ***************************************************
docker.docker: changed: [default] => (item=docker-entrypoint.sh)
docker.docker:
docker.docker: PLAY [Container Cleanup] *******************************************************
docker.docker:
docker.docker: TASK [Remove Python] ***********************************************************
docker.docker: changed: [default]
docker.docker:
docker.docker: TASK [Remove Apt Lists] ********************************************************
docker.docker: changed: [default]
docker.docker:
docker.docker: PLAY RECAP *********************************************************************
docker.docker: default : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
docker.docker:
==> docker.docker: Committing the container
docker.docker: Image ID: sha256:4810af171821da5a2dd662b2ba52038964b472eb1491c8c0b9d5c311f5ac9b4f
==> docker.docker: Killing the container: eea0f6d6824721face5dad840a766fecefa359a04060457cce22f1ea7dd84b9d
==> docker.docker: Running post-processor: (type docker-tag)
docker.docker (docker-tag): Tagging image: sha256:4810af171821da5a2dd662b2ba52038964b472eb1491c8c0b9d5c311f5ac9b4f
docker.docker (docker-tag): Repository: teichae/packer-nginx:latest
==> docker.docker: Running post-processor: (type docker-push)
docker.docker (docker-push): Logging in...
docker.docker (docker-push): WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
docker.docker (docker-push): Login Succeeded
docker.docker (docker-push): Configure a credential helper to remove this warning. See
docker.docker (docker-push): https://docs.docker.com/engine/reference/commandline/login/#credentials-store
docker.docker (docker-push): Pushing: teichae/packer-nginx:latest
docker.docker (docker-push): The push refers to repository [docker.io/teichae/packer-nginx]
docker.docker (docker-push): 7fd23a2bd6bd: Preparing
docker.docker (docker-push): 7a694df0ad6c: Preparing
docker.docker (docker-push): 3fd9df553184: Preparing
docker.docker (docker-push): 805802706667: Preparing
docker.docker (docker-push): 7a694df0ad6c: Layer already exists
docker.docker (docker-push): 805802706667: Layer already exists
docker.docker (docker-push): 3fd9df553184: Layer already exists
docker.docker (docker-push): 7fd23a2bd6bd: Pushed
docker.docker (docker-push): latest: digest: sha256:c96e6656210cb1ebc298fe749df5bfc5e41e3717235552f89f75001faefa9673 size: 1155
docker.docker (docker-push): Pushing: teichae/packer-nginx:latest
docker.docker (docker-push): The push refers to repository [docker.io/teichae/packer-nginx]
docker.docker (docker-push): 7fd23a2bd6bd: Preparing
docker.docker (docker-push): 7a694df0ad6c: Preparing
docker.docker (docker-push): 3fd9df553184: Preparing
docker.docker (docker-push): 805802706667: Preparing
docker.docker (docker-push): 805802706667: Layer already exists
docker.docker (docker-push): 3fd9df553184: Layer already exists
docker.docker (docker-push): 7fd23a2bd6bd: Layer already exists
docker.docker (docker-push): 7a694df0ad6c: Layer already exists
docker.docker (docker-push): latest: digest: sha256:c96e6656210cb1ebc298fe749df5bfc5e41e3717235552f89f75001faefa9673 size: 1155
docker.docker (docker-push): Logging out...
docker.docker (docker-push): Removing login credentials for https://index.docker.io/v1/
Build 'docker.docker' finished after 6 minutes 16 seconds.
==> Wait completed after 6 minutes 16 seconds
==> Builds finished. The artifacts of successful builds are:
--> docker.docker: Imported Docker image: sha256:4810af171821da5a2dd662b2ba52038964b472eb1491c8c0b9d5c311f5ac9b4f
--> docker.docker: Imported Docker image: teichae/packer-nginx:latest with tags teichae/packer-nginx:latest
--> docker.docker: Imported Docker image: teichae/packer-nginx:latest with tags teichae/packer-nginx:latest
빌드 후 Docker Hub에 업로드된 내용까지 확인이 됩니다.
이제 Docker Hub 홈페이지에서 정상적으로 업로드되었는지 확인하여 봅니다.
업로드된 것을 확인할 수 있습니다.
이처럼 Packer를 이용하여 Docker 이미지도 생성하고 이미지 생성에 성공하면 Repository로 배포까지 할 수 있고, 여러 프로바이더를 지원하기 때문에 편리하고 유용한 툴인 것을 확인할 수 있었습니다.
긴 글 읽어주셔서 감사합니다.
본 글에서 사용한 소스코드는 아래에서 공개하고 있습니다. :)
'기술 > Application' 카테고리의 다른 글
Github Actions를 이용한 Docker Image Build 및 Push (4) | 2020.12.09 |
---|---|
Terraform Kubernetes Provider 설정하기 (1) (0) | 2020.11.12 |
Argo CD를 이용한 Canary 배포 (4) (0) | 2020.10.22 |
Argo CD를 이용한 Blue/Green 배포 (3) (7) | 2020.10.22 |
[Kubernetes Monitoring] Grafana에서 Panel 추가하기 (0) | 2020.10.16 |
소중한 공감 감사합니다