{
    "componentChunkName": "component---src-templates-blog-post-tsx",
    "path": "/build-ubuntu-image-for-proxmox-with-packer/",
    "result": {"data":{"site":{"siteMetadata":{"title":"vulcan Blog"}},"markdownRemark":{"id":"1fedb6aa-64a5-5805-bae0-aad311932b1d","tableOfContents":"<ul>\n<li>\n<p><a href=\"#%EC%8B%9C%EC%9E%91\">시작</a></p>\n</li>\n<li>\n<p><a href=\"#1-packer%EB%9E%80\">1. Packer란?</a></p>\n</li>\n<li>\n<p><a href=\"#2-install-packer\">2. install Packer</a></p>\n</li>\n<li>\n<p><a href=\"#3-directory-configuration\">3. directory configuration</a></p>\n</li>\n<li>\n<p><a href=\"#4-file-configuration\">4. file configuration</a></p>\n<ul>\n<li>\n<p><a href=\"#41-credentialspkrhcl\">4.1. credentials.pkr.hcl</a></p>\n</li>\n<li>\n<p><a href=\"#42-ssh-user-credentialspkrhcl\">4.2. ssh-user-credentials.pkr.hcl</a></p>\n</li>\n<li>\n<p><a href=\"#43-user-data\">4.3. user-data</a></p>\n</li>\n<li>\n<p><a href=\"#44-ubuntu-server-noblepkrhcl\">4.4. ubuntu-server-noble.pkr.hcl</a></p>\n<ul>\n<li><a href=\"#441-variable-%EC%A0%95%EC%9D%98\">4.4.1. Variable 정의</a></li>\n<li><a href=\"#442-resource-%EC%A0%95%EC%9D%98\">4.4.2. Resource 정의</a></li>\n<li><a href=\"#443-build-%EC%A0%95%EC%9D%98\">4.4.3. Build 정의</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"#45-%EB%82%98%EB%A8%B8%EC%A7%80\">4.5. 나머지</a></p>\n</li>\n<li>\n<p><a href=\"#46-run-packersh\">4.6. run-packer.sh</a></p>\n</li>\n</ul>\n</li>\n<li>\n<p><a href=\"#5-validate-and-build\">5. validate and build</a></p>\n</li>\n<li>\n<p><a href=\"#6-clone\">6. clone</a></p>\n</li>\n<li>\n<p><a href=\"#%EA%B2%B0%EB%A1%A0\">결론</a></p>\n</li>\n<li>\n<p><a href=\"#%EC%B0%B8%EA%B3%A0\">참고</a></p>\n</li>\n</ul>","excerpt":"시작 proxmox를 벌써 3년 정도 사용하면서 많은 vm을 생성하고 삭제했다. 하지만 매번 vm을 생성할 때마다 같은 설정들을 반복했다는 걸 느꼈다. ssh key를 넣어주고, apt 저장소를 kakao mirror로 바꾸고, 필요할 경우 고정 ip…","html":"<h1 id=\"시작\" style=\"position:relative;\">시작<a href=\"#%EC%8B%9C%EC%9E%91\" aria-label=\"시작 permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h1>\n<p>proxmox를 벌써 3년 정도 사용하면서 많은 vm을 생성하고 삭제했다. 하지만 매번 vm을 생성할 때마다 같은 설정들을 반복했다는 걸 느꼈다. ssh key를 넣어주고, apt 저장소를 kakao mirror로 바꾸고, 필요할 경우 고정 ip를 할당하는 등의 것들이다. 하지만, 이 세팅들을 매번 하는 것도 질렸고, 무엇보다 이번에 k8s 클러스터를 구축하면서 동일한 설정과 동일한 패키지를 가지는 여러 vm을 생성해야 했다. 1n 개 이상의 vm을 이런 식으로, 수동으로 설정하는 것은 바보 같은 짓이라는 생각이 들었다.</p>\n<p>물론 <a href=\"https://www.proxmox.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">proxmox</a>에도 <a href=\"https://pve.proxmox.com/wiki/VM_Templates_and_Clones\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Template</a>라는 기능이 존재한다. Packer도 결과적으로 Template를 만드는 것이지만, proxmox만으로 이것을 하려면 직접 VM을 설치하고 세팅하여 이후에 이것을 Template으로 전환해야 하고, 만약 실수한다면 이 고정을 다시 해야 한다. 그 때문에 IaC도구의 힘을 빌리기로 했다. <a href=\"https://www.hashicorp.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">HashiCorp</a>의 <a href=\"https://www.hashicorp.com/products/packer\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Packer</a>와 <a href=\"https://www.hashicorp.com/products/terraform\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Terraform</a>, 그리고 <a href=\"https://github.com/ansible/ansible\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Ansible</a>을 사용하여 이 과정을 자동화하고자 한다. 이 글을 그 과정 중 첫 번째인 Packer에 대해서 간단하게 설명하고, proxmox에서 Packer로 기본적인 설정이 완료된 ubuntu 24.04, 22.04이미지를 만들어보고자 한다.</p>\n<h1 id=\"1-packer란\" style=\"position:relative;\">1. Packer란?<a href=\"#1-packer%EB%9E%80\" aria-label=\"1 packer란 permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h1>\n<p>Packer는 이미지 빌드를 자동화해 주는 오픈소스 소프트웨어이다. 서버나 가상머신, 클라우드 인스턴스와 같은 환경에서 일관되고, 반복적인 배포를 할 때 유용하다. Template를 이용해서 항상 같은 이미지를 빌드하기 때문에 개발, 테스트, 프로덕션 환경을 일관적으로 유지할 수 있다. 또한 반복적인 작업을 줄일 수 있다. 만약 각 vm에 k8s 관련 패키지를 Ansible로 설치한다고 생각하면, 각 VM에 SSH로 접속해서 설치 스크립트를 실행할 것이고, 더 높은 네트워크 사용과 미리 설치해 둔 패키지를 설치하는 것보다는 당연히 클러스터의 배포가 느려질 것이다.</p>\n<p>이처럼 Packer는 DevOps 파이프라인의 가장 앞에서 가장 필수적이고 공통적인 패키지를 포함한 이미지를 미리 만들어둠으로써 배포 시간을 줄이고, 일관성을 유지할 수 있게 해준다. 이는 홈서버를 운영하는 입장에서도 매우 편리하다. 앞서 말한 ssh key, kakao mirror etc…들을 매번 세팅하는 건 매우 귀찮은 일이다. 특히 지인들과 서버를 공유하고 있다면 더욱 귀찮은 일이다. 이제 이 귀찮음을 해결해 보자.</p>\n<h1 id=\"2-install-packer\" style=\"position:relative;\">2. install Packer<a href=\"#2-install-packer\" aria-label=\"2 install packer permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h1>\n<p>기본적으로 Packer의 설정에 관한 문서는 <a href=\"https://developer.hashicorp.com/packer\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">여기</a>에 아주 잘 정리돼 있다. ubuntu server noble에 설치할 거기 때문에 이 <a href=\"https://developer.hashicorp.com/packer/install#linux\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">튜토리얼</a>을 따라 하자.</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg\necho &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main&quot; | sudo tee /etc/apt/sources.list.d/hashicorp.list\nsudo apt update &amp;&amp; sudo apt install packer</code></pre></div>\n<p>설치가 완료되고 <code class=\"language-text\">packer --version</code>을 입력했을 때</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">~$ packer --version\nPacker v1.12.0</code></pre></div>\n<p>이런 식으로 나온다면 설치가 완료된 것이다.</p>\n<h1 id=\"3-directory-configuration\" style=\"position:relative;\">3. directory configuration<a href=\"#3-directory-configuration\" aria-label=\"3 directory configuration permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h1>\n<p>Packer를 이용해서 proxmox를 빌드하기 위해서는 packer Template에 사용될 폴더와 proxmox에 접속하기 위한 credentials를 저장할 파일이 필요하다. 그리고 이미지를 생성할 템플릿, ssh user의 credentials 파일, 설치 시 전달한 user data가 필요하다. 때문에 아래와 같이 폴더를 구성해서 jammy와 noble을 위한 Template를 작성할 것이다.</p>\n<div class=\"gatsby-highlight\" data-language=\"css\"><pre class=\"language-css\"><code class=\"language-css\">packer/\n├── credentials.pkr.hcl\n├── ubuntu-server-jammy\n│   ├── files\n│   │   └── 99-pve.cfg\n│   ├── http\n│   │   ├── meta-data\n│   │   └── user-data\n│   ├── run-packer.sh\n│   ├── ssh-user-credentials.pkr.hcl\n│   └── ubuntu-server-jammy.pkr.hcl\n└── ubuntu-server-noble\n    ├── files\n    │   └── 99-pve.cfg\n    ├── http\n    │   ├── meta-data\n    │   └── user-data\n    ├── run-packer.sh\n    ├── ssh-user-credentials.pkr.hcl\n    └── ubuntu-server-noble.pkr.hcl</code></pre></div>\n<p><code class=\"language-text\">run-packer.sh</code>는 validate와 build를 편하게 하기 위해서 작성했다.</p>\n<h1 id=\"4-file-configuration\" style=\"position:relative;\">4. file configuration<a href=\"#4-file-configuration\" aria-label=\"4 file configuration permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h1>\n<p>ubuntu server noble(24.04.x)를 기준으로 진행하겠다.</p>\n<h2 id=\"41-credentialspkrhcl\" style=\"position:relative;\">4.1. credentials.pkr.hcl<a href=\"#41-credentialspkrhcl\" aria-label=\"41 credentialspkrhcl permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h2>\n<p>Packer가 proxmox에서 VM을 생성하고, 설정값을 입력하고, 이것을 template로 바꾸기 위해서는 어떤 방법으로든 proxmox의 쉘에 접속할 수밖에 없다. 이를 위해서 proxmox의 api_url, username과 password 혹은 token이 필요하다.(passward나 token 중 하나만 있어도 된다.) 이는 모든 template 파일에 포함돼야 하는데, 편의를 위해서 root 권한을 그대로 사용한다. 그 때문에 credentials 파일에서 username, password, token, api_url을 변수로 저장하고, 일관적으로 관리하려고 한다.(Packer를 위한 proxmox의 권한에 관련된 문서는 찾지 못했지만, 권한과 관련된 보안 이슈를 해결할 만한 솔루션을 발견했다. 해당 <a href=\"https://github.com/hashicorp/packer-plugin-proxmox/issues/184\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">이슈</a>에 따라 pool, group, user를 생성하고 적절히 권한을 주고 Packer가 proxmox Template를 완전히 생성한 뒤 root 권한으로 원하는 pool로 생성된 Template를 이동시키면 될 듯하다.) 아래와같이 <code class=\"language-text\">credentials</code> 파일을 작성해 준다.</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\"># proxmox API credentials\nproxmox_api_url             = &quot;https://YOUR_PROXMOX_IP:8006/api2/json&quot;\nproxmox_api_token_id        = &quot;YOUR_USERNAME@REALM_NAME!tokenid&quot;\n#token을 사용할거면 !tokenid를 뒤에 붙여줘야함 e.g. root@pam!packer\nproxmox_api_password        = &quot;YOUR_PASSWORD&quot;   #token을 사용할거면 비워도도 됨\nproxmox_api_token_secret    = &quot;TOKEN_SECRET&quot;    #password를 사용할거면 비워도도 됨</code></pre></div>\n<p>되도록 <code class=\"language-text\">token</code>을 사용하도록 하자.</p>\n<h2 id=\"42-ssh-user-credentialspkrhcl\" style=\"position:relative;\">4.2. ssh-user-credentials.pkr.hcl<a href=\"#42-ssh-user-credentialspkrhcl\" aria-label=\"42 ssh user credentialspkrhcl permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h2>\n<p>Packer가 VM을 생성하고, 이후에 VM에 접속해서 provisioner로 필요한 패키지를 설치하기 위해서는 해당 VM에 ssh로 접속해야 한다. 이를 위해서 <code class=\"language-text\">user-data</code>에 입력한 user와 동일한 데이터가 필요하다. 이 값도 예제 아래에 작성한 이유로 Template에서 유일하게 자주 변경해야 할 수도 있기 때문에 변수로 분리했다. 아래와같이 <code class=\"language-text\">ssh-user-credentials</code> 파일을 작성해 준다.</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\"># ssh credentials\nssh_username                = &quot;YOUR_USERNAME&quot;   #e.g. packer\nssh_password                = &quot;YOUR_PASSWORD&quot;   #ssh key를 사용한다면 비워도도 됨\nssh_private_key_file_path   = &quot;YOUR_PRIVATE_SSH_KEY_PATH&quot;   #Packer가 설치돼있는 host의 path임 e.g. ~/.ssh/packer</code></pre></div>\n<p>이때 빌드를 위한 packer user와 ssh key를 입력해 두고, 나중에 Terraform 등을 이용해서 지워도 된다. 하지만 많은 VM을 생성하지 않을 때는 수동으로 clone 하므로, 이 user가 거슬릴 수 있다. 예제에서는 packer라는 user로 진행했지만, 이를 자주 사용하는 user와 ssh key로 변경해도 된다.</p>\n<p><a href=\"https://developer.hashicorp.com/packer/integrations/hashicorp/proxmox/latest/components/builder/iso\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">문서</a>대로라면 <code class=\"language-text\">ssh_password</code>를 이용해서도 접속할 수 있어야 하지만 나의 경우 오직 ssh key로만 접속할 수 있었다.</p>\n<h2 id=\"43-user-data\" style=\"position:relative;\">4.3. user-data<a href=\"#43-user-data\" aria-label=\"43 user data permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h2>\n<p>이 <code class=\"language-text\">user-data</code>에 관해서 설명하려면 먼저 cloud-init에 대해서 알아야 한다. cloud-init은 VM 혹은 클라우드 인스턴스의 초부팅 시 사용자 지정 스크립트/환경 설정을 자동 적용해 주는 툴이다. proxmox에도 cloud-init을 설정해서 부팅 시 적용할 수 있는 옵션이 있다. 하지만 Packer는 이 방식을 사용하지 않고, 직접 임시로 HTTP 서버를 구동한다. 그리고 아래에서 작성할 template 코드에서 부팅시 옵션을 입력하는데, 이것이 Packer가 임시로 구동한 HTTP 서버에 접속해 <code class=\"language-text\">user-data</code>와 <code class=\"language-text\">meta-data</code>를 가져온다. 여기서는 http 폴더 아래에 있는 두 파일이 된다. 이 과정을 거치면서 VM은 설치 시에 <code class=\"language-text\">user-data</code>에 기술된 user를 생성하게 되고, 이후에 부팅했을 때 ssh로 접속하는 것이다. 위에서 작성한 <code class=\"language-text\">ssh-user-credentials</code>로 ssh 접속을 시도하기 때문에 대응하는 값으로 <code class=\"language-text\">user-data</code>파일을 작성해 준다.</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\">#cloud-config\nautoinstall<span class=\"token operator\">:</span>\n  version<span class=\"token operator\">:</span> <span class=\"token number\">1</span>\n  locale<span class=\"token operator\">:</span> en_US\n  keyboard<span class=\"token operator\">:</span>\n    layout<span class=\"token operator\">:</span> us\n  ssh<span class=\"token operator\">:</span>\n    install-server<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    allow-pw<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    disable_root<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    ssh_quiet_keygen<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    allow_public_ssh_keys<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n  packages<span class=\"token operator\">:</span>\n    - qemu-guest-agent\n    - sudo\n  storage<span class=\"token operator\">:</span>\n    layout<span class=\"token operator\">:</span>\n      name<span class=\"token operator\">:</span> direct\n    swap<span class=\"token operator\">:</span>\n      size<span class=\"token operator\">:</span> <span class=\"token number\">0</span>\n  user-data<span class=\"token operator\">:</span>\n    package_upgrade<span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n    timezone<span class=\"token operator\">:</span> Asia/Seoul\n    users<span class=\"token operator\">:</span>\n      - name<span class=\"token operator\">:</span> YOUR_USERNAME\n        groups<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>adm<span class=\"token punctuation\">,</span> sudo<span class=\"token punctuation\">]</span>\n        lock-passwd<span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n        sudo<span class=\"token operator\">:</span> ALL=(ALL) NOPASSWD<span class=\"token operator\">:</span>ALL\n        shell<span class=\"token operator\">:</span> /bin/bash\n        #passwd<span class=\"token operator\">:</span> YOUR_PASSWORD\n        # - or -\n        ssh_authorized_keys<span class=\"token operator\">:</span>\n          - YOUR_SSH_PUBLIC_KEY</code></pre></div>\n<p>위에서 작성한 <code class=\"language-text\">ssh-user-credentials</code>의 <code class=\"language-text\">YOUR_USERNAME</code>과 같은 username을 적어주고, <code class=\"language-text\">YOUR_PRIVATE_SSH_KEY_PATH</code>에 대응하는 public key를 적어주면 된다. 부팅하면서 최신 패키지를 적용하고 싶다면 <code class=\"language-text\">package_upgrade</code>를 true로 바꿔주면 된다.</p>\n<h2 id=\"44-ubuntu-server-noblepkrhcl\" style=\"position:relative;\">4.4. ubuntu-server-noble.pkr.hcl<a href=\"#44-ubuntu-server-noblepkrhcl\" aria-label=\"44 ubuntu server noblepkrhcl permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h2>\n<p>가장 핵심이 되는 template 파일이다. proxmox에서 VM을 실행하고 빌드하고 템플릿으로 바꿔주는 파일이다. 파일에서 사용한 값에 대한 상세나, 추가 값들은 <a href=\"https://developer.hashicorp.com/packer/integrations/hashicorp/proxmox/latest/components/builder/iso\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">여기</a>에서 확인하면된다. 이 파일은 상당히 길기 때문에 나눠서 진행하겠다.</p>\n<h3 id=\"441-variable-정의\" style=\"position:relative;\">4.4.1. Variable 정의<a href=\"#441-variable-%EC%A0%95%EC%9D%98\" aria-label=\"441 variable 정의 permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h3>\n<p>앞서 작성한 <code class=\"language-text\">credentials</code>, <code class=\"language-text\">ssh-user-credentials</code> 두 파일에서 작성한 variable들에 대한 정의를 해줘야 한다. 각 type은 <a href=\"https://developer.hashicorp.com/packer/integrations/hashicorp/proxmox/latest/components/builder/iso\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">여기</a>에 적혀있는 type에 따라서 작성하면 된다. password는 사용하지 않는다면 이 부분과 아래에서 이 변수를 사용하는 부분 모두를 지워줘도 된다.</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"># Variable Definitions\nvariable <span class=\"token string\">\"proxmox_api_url\"</span> <span class=\"token punctuation\">{</span>\n    type = string\n<span class=\"token punctuation\">}</span>\nvariable <span class=\"token string\">\"proxmox_api_token_id\"</span> <span class=\"token punctuation\">{</span>\n    type = string\n<span class=\"token punctuation\">}</span>\nvariable <span class=\"token string\">\"proxmox_api_password\"</span> <span class=\"token punctuation\">{</span>\n    type = string\n    sensitive = <span class=\"token boolean\">true</span>\n<span class=\"token punctuation\">}</span>\nvariable <span class=\"token string\">\"proxmox_api_token_secret\"</span> <span class=\"token punctuation\">{</span>\n    type = string\n    sensitive = <span class=\"token boolean\">true</span>\n<span class=\"token punctuation\">}</span>\nvariable <span class=\"token string\">\"ssh_username\"</span> <span class=\"token punctuation\">{</span>\n    type = string\n<span class=\"token punctuation\">}</span>\nvariable <span class=\"token string\">\"ssh_password\"</span> <span class=\"token punctuation\">{</span>\n    type = string\n    sensitive = <span class=\"token boolean\">true</span>\n<span class=\"token punctuation\">}</span>\nvariable <span class=\"token string\">\"ssh_private_key_file_path\"</span> <span class=\"token punctuation\">{</span>\n    type = string\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"442-resource-정의\" style=\"position:relative;\">4.4.2. Resource 정의<a href=\"#442-resource-%EC%A0%95%EC%9D%98\" aria-label=\"442 resource 정의 permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h3>\n<p>여기서 사용되는 값들에 대한 상세는 다시 말하지만 <a href=\"https://developer.hashicorp.com/packer/integrations/hashicorp/proxmox/latest/components/builder/iso\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">여기</a>를 참고하자.</p>\n<ul>\n<li><code class=\"language-text\">source</code>의 <code class=\"language-text\">proxmox-iso</code>는 type이기 때문에 그대로 작성해야 한다.</li>\n<li><code class=\"language-text\">Proxmox Connection Settings</code>는 앞에 서술했듯이 proxmox에 접속해서 VM을 생성하고 설치 과정을 진행하기 위해서 선언해야 하는 값이다.\n<ul>\n<li><code class=\"language-text\">insecure_skip_tls_verify</code>의 경우 valid한 TLS인증서가 달려있는 <code class=\"language-text\">proxmox_url</code>를 사용한다면 생략해도 된다.</li>\n</ul>\n</li>\n<li><code class=\"language-text\">ssh Settings</code>는 생성된 VM에 접속해서 빌드 과정을 진행하기 위해서 ssh 접속을 위해서 선언해야 하는 값이다.</li>\n<li><code class=\"language-text\">VM General Settings</code>는 VM을 생성할 때 사용되는 기본적인 값이다.\n<ul>\n<li><code class=\"language-text\">node</code>의 경우 클러스터 구성이 아니라면 지정해 줄 필요는 없다.</li>\n<li><code class=\"language-text\">bios</code>값의 경우 default는 seabios이다. 그런데 이 값을 ovmf로 할 경우 efi_config로 efi disk를 지정해 줘야 하는데, 이 경우 <a href=\"https://bugzilla.proxmox.com/show_bug.cgi?id=3227\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">proxmox에서 live migration을 진행할 수 없게 되니</a> 주의해야한다.(이 <a href=\"https://forum.proxmox.com/threads/why-cant-proxmox-live-migrate-an-efi-disk.152941/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">스레드</a>를 보면 해당 이슈가 아직도 존재하는것으로 보이고, 실제로 나도 실패했다.)</li>\n</ul>\n</li>\n<li><code class=\"language-text\">VM OS Settings</code>는 proxmox에서 OS가 저장된 저장소의 위치를 정의한다. 추가적인 디스크를 추가하지 않았다면 아마도 <code class=\"language-text\">local</code>일 것이다. 이 경우라면 <code class=\"language-text\">YOUR_ISO_STORAGE_POOL</code>를 local로 하면 된다. 그리고 <a href=\"https://releases.ubuntu.com/24.04.1/ubuntu-24.04.1-live-server-amd64.iso?_ga=2.174163618.1627445440.1739895538-2098516798.1731756258&#x26;_gl=1*1ijd3p4*_gcl_au*NDYzMTY4OTQ3LjE3Mzk4OTU1Mzg.\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">ubuntu-24.04.1-live-server-amd64.iso</a>를 해당 저장소에 받아둬야 한다.</li>\n<li><code class=\"language-text\">VM Hard Disk Settings</code>는 VM의 디스크 설정이다.</li>\n<li><code class=\"language-text\">VM Network Settings</code>는 네트워크 설정이다. <code class=\"language-text\">vlan_tag</code>의 경우 해당 VLAN과 Packer host 사이에 방화벽이 있어서 ssh 접속이 불가능하진 않은지 확인해야한다.</li>\n<li><code class=\"language-text\">VM Cloud-Init Settings</code>는 다음에 Terraform이나 직접 cloud-init 값을 입력하기 위해서 필요한 proxmox용 cloud-init disk를 생성하는 과정이다.</li>\n<li><code class=\"language-text\">PACKER Boot Commands</code>는 앞서 설명했듯이 부팅 시에 Packer host의 임시 http 서버에 접속해서 cloud-init 정보를 가져오기 위한 커맨드이다.</li>\n<li><code class=\"language-text\">PACKER Autoinstall Settings</code>는 방금 설명한 임시 http 서버에 대한 설정이다. 여기서 만약 <code class=\"language-text\">http_port_min</code>, <code class=\"language-text\">http_port_max</code>가 똑같이 설정된 여러 Packer template를 동시에 build 한다면 문제가 발생할 수 있다.</li>\n</ul>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\"># Resource Definiation for the VM Template\nsource &quot;proxmox-iso&quot; &quot;ubuntu-server-noble&quot; {\n\n    # Proxmox Connection Settings\n    proxmox_url = &quot;${var.proxmox_api_url}&quot;\n    username    = &quot;${var.proxmox_api_token_id}&quot;\n    password    = &quot;${var.proxmox_api_password}&quot;\n    token       = &quot;${var.proxmox_api_token_secret}&quot;\n    insecure_skip_tls_verify = true # (Optional) Skip TLS Verification\n\n    # ssh Settings\n    ssh_username                = &quot;${var.ssh_username}&quot;\n    # (Option 1) Add your Password here\n    #ssh_password                = &quot;${var.ssh_password}&quot;\n    # - or -\n    # (Option 2) Add your Private SSH KEY file here\n    ssh_private_key_file        = &quot;${var.ssh_private_key_file_path}&quot;\n    ssh_clear_authorized_keys   = true\n    ssh_timeout                 = &quot;30m&quot;\n    ssh_handshake_attempts      = 50\n    ssh_pty                     = true\n\n    # VM General Settings\n    template_description = &quot;Ubuntu Server Noble Image&quot;\n    node        = &quot;pve&quot;\n    vm_id       = &quot;9000&quot;\n    vm_name     = &quot;ubuntu-server-noble&quot;\n    #pool        = &quot;&quot;\n    tags        = &quot;ubuntu_noble&quot;\n    os          = &quot;l26&quot;\n    machine     = &quot;q35&quot;\n    qemu_agent  = true\n    cores       = &quot;2&quot;\n    memory      = &quot;4096&quot;\n    #bios        = &quot;ovmf&quot;\n    #efi_config {\n    #    efi_storage_pool    = &quot;YOUR_STORAGE_POOL&quot; #e.g. local-lvm\n    #    efi_type            = &quot;4m&quot;\n    #    pre_enrolled_keys   = true\n    #}\n\n    # VM OS Settings\n    boot_iso {\n      type              = &quot;scsi&quot;\n      iso_file          = &quot;YOUR_ISO_STORAGE_POOL:iso/ubuntu-24.04.1-live-server-amd64.iso&quot;  #iso file name\n      iso_storage_pool  = &quot;YOUR_ISO_STORAGE_POOL&quot; #local\n      unmount           = true\n    }\n\n    # VM Hard Disk Settings\n    scsi_controller     = &quot;virtio-scsi-pci&quot;\n    disks {\n        disk_size       = &quot;20G&quot;\n        format          = &quot;raw&quot;\n        storage_pool    = &quot;YOUR_STORAG_POOL&quot;  #e.g. local-lvm\n        type            = &quot;virtio&quot;\n    }\n\n    # VM Network Settings\n    network_adapters {\n        model       = &quot;virtio&quot;\n        bridge      = &quot;vmbr1&quot;\n        #vlan_tag    = &quot;100&quot;\n        firewall    = &quot;false&quot;\n    }\n\n    # VM Cloud-Init Settings\n    cloud_init              = true\n    cloud_init_storage_pool = &quot;YOUR_STORAG_POOL&quot;  #e.g. local-lvm\n\n    # PACKER Boot Commands\n    boot_command = [\n        &quot;&lt;esc&gt;&lt;wait&gt;&quot;,\n        &quot;e&lt;wait&gt;&quot;,\n        &quot;&lt;down&gt;&lt;down&gt;&lt;down&gt;&lt;end&gt;&quot;,\n        &quot;&lt;bs&gt;&lt;bs&gt;&lt;bs&gt;&lt;bs&gt;&lt;wait&gt;&quot;,\n        &quot;autoinstall ds=nocloud-net\\\\;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ ---&lt;wait&gt;&quot;,\n        &quot;&lt;f10&gt;&lt;wait&gt;&quot;\n    ]\n    boot                    = &quot;c&quot;\n    boot_wait               = &quot;10s&quot;\n    communicator            = &quot;ssh&quot;\n\n    # PACKER Autoinstall Settings\n    http_directory          = &quot;http&quot;\n    # (Optional) Bind IP Address and Port\n    http_bind_address       = &quot;YOUR_PACKER_HOST_IP&quot; #packer host IP\n    http_port_min           = 8802\n    http_port_max           = 8902\n}</code></pre></div>\n<h3 id=\"443-build-정의\" style=\"position:relative;\">4.4.3. Build 정의<a href=\"#443-build-%EC%A0%95%EC%9D%98\" aria-label=\"443 build 정의 permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h3>\n<p>VM생성 이후 ssh로 접속해서 실행할 커맨드이다. 여기서 <code class=\"language-text\">sources</code>는 위의 template에서 <code class=\"language-text\">source</code>와 대응하게 작성해야 한다. 여기서는 이후에 proxmox의 cloud-init 저장소를 활용하기 위한 준비와, 나의 경우 apt 저장소를 kakao로 변경하는 작업을 추가했다.</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\"># Build Definition to create the VM Template\nbuild {\n\n    name    = &quot;ubuntu-server-noble&quot;\n    sources = [&quot;source.proxmox-iso.ubuntu-server-noble&quot;]\n\n    # Provisioning the VM Template for Cloud-Init Integration in Proxmox #1\n    provisioner &quot;shell&quot; {\n        inline = [\n            &quot;while [ ! -f /var/lib/cloud/instance/boot-finished ]; do echo &#39;Waiting for cloud-init...&#39;; sleep 1; done&quot;,\n            &quot;sudo rm /etc/ssh/ssh_host_*&quot;,\n            &quot;sudo truncate -s 0 /etc/machine-id&quot;,\n            &quot;sudo apt -y autoremove --purge&quot;,\n            &quot;sudo apt -y clean&quot;,\n            &quot;sudo apt -y autoclean&quot;,\n            &quot;sudo cloud-init clean&quot;,\n            &quot;sudo rm -f /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg&quot;,\n            &quot;sudo rm -f /etc/netplan/00-installer-config.yaml&quot;,\n            &quot;sudo sync&quot;\n        ]\n    }\n\n    # Provisioning the VM Template for Cloud-Init Integration in Proxmox #2\n    provisioner &quot;file&quot; {\n        source      = &quot;files/99-pve.cfg&quot;\n        destination = &quot;/tmp/99-pve.cfg&quot;\n    }\n\n    # Provisioning the VM Template for Cloud-Init Integration in Proxmox #3\n    provisioner &quot;shell&quot; {\n        inline = [ &quot;sudo cp /tmp/99-pve.cfg /etc/cloud/cloud.cfg.d/99-pve.cfg&quot; ]\n    }\n\n    # Change apt repo to kakao mirror\n    provisioner &quot;shell&quot; {\n        inline = [\n            &quot;sudo sed -i &#39;s|http://kr.archive.ubuntu.com/ubuntu/|http://mirror.kakao.com/ubuntu/|g&#39; /etc/apt/sources.list.d/ubuntu.sources&quot;\n        ]\n    }\n\n    # Add additional provisioning scripts here\n    # ...\n}</code></pre></div>\n<p>ubuntu server noble과 jammy 모두 지금까지의 값들은 당연한 이름 같은 것을 제외하면 모두 동일하다. 하지만 ubuntu 24.04버전으로 오면서 apt 저장소 리스트를 저장하는 위치가 약간 바뀌었다. <code class=\"language-text\">/etc/apt/sources.list -> /etc/apt/sources.list.d/ubuntu.sources</code> 때문에 jammy의 경우 아래와같이 apt 저장소를 바꾸는 코드만 수정해 주면 된다.</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">    # for ubuntu server jammy\n    # Change apt repo to kakao mirror\n    provisioner &quot;shell&quot; {\n        inline = [\n            &quot;sudo sed -i &#39;s|http://kr.archive.ubuntu.com/ubuntu/|http://mirror.kakao.com/ubuntu/|g&#39; /etc/apt/sources.list&quot;\n        ]\n    }</code></pre></div>\n<h2 id=\"45-나머지\" style=\"position:relative;\">4.5. 나머지<a href=\"#45-%EB%82%98%EB%A8%B8%EC%A7%80\" aria-label=\"45 나머지 permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h2>\n<p><code class=\"language-text\">meta-data</code>의 경우 필요하다면 사용하면 된다. 여기서는 아무것도 작성하지 않아도 된다.\n<code class=\"language-text\">99-pve.cfg</code>에는 <code class=\"language-text\">datasource_list: [ConfigDrive, NoCloud]</code> 한 줄만 있으면 되는데, 이것은 부팅 시에 cloud-init이 ConfigDrive를 먼저 진행할지, NoCloud로 먼저 진행할지를 정의하는 부분이다. proxmox의 경우 NoCloud방식을 사용한다. ConfigDrive를 먼저 확인하고 없다면 proxmox의 설정을 따르게 된다.</p>\n<h2 id=\"46-run-packersh\" style=\"position:relative;\">4.6. run-packer.sh<a href=\"#46-run-packersh\" aria-label=\"46 run packersh permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h2>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">#!/bin/bash\n\nACTION=$1\nTEMPLATE_FILE=$2\n\nif [ -z &quot;$TEMPLATE_FILE&quot; ]; then\n    echo &quot;Usage: $0 {validate|build|-v|-b} &lt;template-file&gt;&quot;\n    exit 1\nfi\n\nif [ &quot;$ACTION&quot; == &quot;validate&quot; ] || [ &quot;$ACTION&quot; == &quot;-v&quot; ]; then\n    packer validate -var-file ../credentials.pkr.hcl -var-file ./ssh-user-credentials.pkr.hcl &quot;$TEMPLATE_FILE&quot;\nelif [ &quot;$ACTION&quot; == &quot;build&quot; ] || [ &quot;$ACTION&quot; == &quot;-b&quot; ]; then\n    packer build -var-file ../credentials.pkr.hcl -var-file ./ssh-user-credentials.pkr.hcl &quot;$TEMPLATE_FILE&quot;\nelse\n    echo &quot;Usage: $0 {validate|build|-v|-b} &lt;template-file&gt;&quot;\n    exit 1\nfi</code></pre></div>\n<p>빌드의 편의를 위해 작성한 파일이다. 앞서 작성된 Packer 파일들을 빌드하기 위해서는</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">packer build -var-file ../credentials.pkr.hcl -var-file ./ssh-user-credentials.pkr.hcl ubuntu-server-noble.pkr.hcl</code></pre></div>\n<p>를 입력해야 한다고 했다. 이 스크립트는 반복되는 variable 파일을 이미 넣어둬서</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">./run-packer.sh -b ubuntu-server-noble.pkr.hcl</code></pre></div>\n<p>로 진행할 수 있다. validate의 경우</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">./run-packer.sh -v ubuntu-server-noble.pkr.hcl</code></pre></div>\n<h1 id=\"5-validate-and-build\" style=\"position:relative;\">5. validate and build<a href=\"#5-validate-and-build\" aria-label=\"5 validate and build permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h1>\n<p>위의 <code class=\"language-text\">run-packer</code> 스크립트를 이용해서 빌드해도 되지만, 일단은 정석적인 방법으로 빌드하려고 한다. 일단 Packer의 proxmox plugin을 설치해 준다.</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">~$ packer plugins install github.com/hashicorp/proxmox\nInstalled plugin github.com/hashicorp/proxmox v1.2.2 in &quot;/home/user/.config/packer/plugins/github.com/hashicorp/proxmox/packer-plugin-proxmox_v1.2.2_x5.0_linux_amd64&quot;</code></pre></div>\n<p>위와 같이 뜬다면 성공적으로 설치된 것이다.</p>\n<p><code class=\"language-text\">packer build</code>를 하기 이전에 validate 과정을 거쳐서 template 코드가 정상적으로 작성됐는지 확인해야 한다.</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">~$ packer validate -var-file ../credentials.pkr.hcl -var-file ./ssh-user-credentials.pkr.hcl ubuntu-server-noble.pkr.hcl\nThe configuration is valid.</code></pre></div>\n<p>위와 같이 뜬다면 성공적으로 validate 된 것이다.</p>\n<p>validate가 성공했다면 비슷한 명령으로 build를 해준다.</p>\n<div class=\"gatsby-highlight\" data-language=\"sh\"><pre class=\"language-sh\"><code class=\"language-sh\">~$ packer build -var-file ../credentials.pkr.hcl -var-file ./ssh-user-credentials.pkr.hcl ubuntu-server-noble.pkr.hcl</code></pre></div>\n<p>빌드를 하면 proxmox웹에서 지정한 vmid로 VM이 생성되고 부팅되는 걸 확인할 수 있다. 이때 아래와 같은 화면에서 절대 다른 조작을 해서는 안 된다. 이 화면에서 Packer가 proxmox에 키보드 입력을 통해 Packer가 띄운 http서버에서 cloud-init 정보를 가져오기 위한 커맨드를 입력하는데, 오조작으로 바로 설치 환경으로 들어가게 되면 Packer가 이후 프로세스를 진행할 수 없게 된다.\n<span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/b18ed4d83b8ddc3211b9aeaeffbed5ae/34abc/before_boot.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 55.06329113924051%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAABGElEQVQoz62Ta6uCQBCGN81LGZYmZiEkXiAvCFFaX/z/P+s9vMPZIM4tDn14nNmd4d2dcVYppfATtm3DcRzMZjOYpgnDMPBbvpBlGS6Xi9D3Pdq2Fbquw/l8xv1+x+12e9hxHDEMA67Xq1jtM5bnOVTTNCI2TZOI8AAGSFEUOJ1OX6jr+gm9V5Yl1GKx+LuMF1kul1BRFMmC/WGv/gs11us1VBAEb7uh7/tQ8nmnIK/JkZjP5y/DfI1lWTJetJ+XU/A8Txa0q9VKekHobzYb8RnXOdvtVmC7KMQe6n+g6Oz3exkTjszxeESaptjtdjgcDrJXVZXkJEmCMAwfgnEcw3Xd59IpyNfA8SH0CUvj6TqmLfd06d/18gNVv/uqnvtc9AAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"before_boot\"\n        title=\"before_boot\"\n        src=\"/static/b18ed4d83b8ddc3211b9aeaeffbed5ae/f058b/before_boot.png\"\n        srcset=\"/static/b18ed4d83b8ddc3211b9aeaeffbed5ae/c26ae/before_boot.png 158w,\n/static/b18ed4d83b8ddc3211b9aeaeffbed5ae/6bdcf/before_boot.png 315w,\n/static/b18ed4d83b8ddc3211b9aeaeffbed5ae/f058b/before_boot.png 630w,\n/static/b18ed4d83b8ddc3211b9aeaeffbed5ae/40601/before_boot.png 945w,\n/static/b18ed4d83b8ddc3211b9aeaeffbed5ae/78612/before_boot.png 1260w,\n/static/b18ed4d83b8ddc3211b9aeaeffbed5ae/34abc/before_boot.png 3164w\"\n        sizes=\"(max-width: 630px) 100vw, 630px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span>\n성공적으로 빌드가 완료되면 아래와 같이 proxmox Template가 생성된 것을 확인할 수 있다.\n<span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 418px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/5a57189967a04d3a9eebddbac2426e2c/d7398/build_complete.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 21.51898734177215%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAw0lEQVQY0zVPuw6DMBDja7pUAvFIgARIQAUqFkDqwECn9gO69Pvd+lqGyLHvzr4L6rrGuq4gTtMkr21bFEWBrutQVRWcc1BKiU7O3rIs4b1H3/dw1qJ+vXE2BkGe59i2DcuyQGsNY4wMU7fWisZ/lmVSO3gcx79+a1FqDXV/4BRlCJg2z7OYMJFDaZoiSRLRiDQLw1AMDs5HToyjBM2oMD7/G+77LlvyRJ7HRuIwDBLCUxl0cNaokTvv4JsW19sFw9fwA62UiZRBVcX+AAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"build_complete\"\n        title=\"build_complete\"\n        src=\"/static/5a57189967a04d3a9eebddbac2426e2c/d7398/build_complete.png\"\n        srcset=\"/static/5a57189967a04d3a9eebddbac2426e2c/c26ae/build_complete.png 158w,\n/static/5a57189967a04d3a9eebddbac2426e2c/6bdcf/build_complete.png 315w,\n/static/5a57189967a04d3a9eebddbac2426e2c/d7398/build_complete.png 418w\"\n        sizes=\"(max-width: 418px) 100vw, 418px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<h1 id=\"6-clone\" style=\"position:relative;\">6. clone<a href=\"#6-clone\" aria-label=\"6 clone permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h1>\n<p>이제 proxmox에 생성된 이미지를 clone 해보자. clone 하기에 앞서 proxmox에서 cloud-init 설정을 해줘야 한다.\n<span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/0b8318f1c09f53862bd41a04669ad26e/d9199/proxmox_cloud-init.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 62.65822784810127%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAACCklEQVQ4y11TXYvTQBSdd/FVkDZJ2yRtkzZfM20ySbPrShG7VVZWEBcEX/YX+BMEQYUVfNDfe+RcnRB8ONzmzr3nnDt3qvb7Peq6hjFGovuttcZut5PIGsaqqqCNwTZNsSpLXH7/iauHX3j24zeef/4KozWU1kYKi6JElmXI8xxlWQ4ERVHIb+Yp1jQNdsbA1A3suzuUN7co39yiOr/GdrOB6g8HaSCiKEYURULcNH+dkpxOmTudTjifz3jJeH2Ny9Yi9D1EgY8oCBDHMZTRFYwuocsCq2iBZbTAxUWPtm2xWq0E6/V6gPve17WMv04SJGmKJEkkr5L7B3gfvgkevf2Cx6dPaNoOTV3LuFSdTqfwfV/geZ5EEhD8nkwmQ16lr+4RvvgI/+oOT/r3eGpvYNsOrbUyMkHSMAwH8Fp4pxR0d+5q1KHWaE0BvV0jDX0s5x6stTJynmWyGBYGQYDZbCZxPp8LiVse6ynCM9V2B1TaoG4ssqJEGMWyBG6UDSTk3TgikjJy3DRNsdlsxCVFeabcG2MTVWi973tRdU+HY7N5sVgMYB1FCN4dyZhXXddJM0FVJo/HI5h3D5tuGcfNFFoul4MJOpSl0CFH4ztzT4OOSDb+97DZPSMK84yPnLD/FsgzxYQsIM9lLIKqY1CdhA505PLjzfNMHNI+XdAli6k0JnAkY/x/7mr+AK1CkOR8PRTGAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"proxmox_cloud-init\"\n        title=\"proxmox_cloud-init\"\n        src=\"/static/0b8318f1c09f53862bd41a04669ad26e/f058b/proxmox_cloud-init.png\"\n        srcset=\"/static/0b8318f1c09f53862bd41a04669ad26e/c26ae/proxmox_cloud-init.png 158w,\n/static/0b8318f1c09f53862bd41a04669ad26e/6bdcf/proxmox_cloud-init.png 315w,\n/static/0b8318f1c09f53862bd41a04669ad26e/f058b/proxmox_cloud-init.png 630w,\n/static/0b8318f1c09f53862bd41a04669ad26e/40601/proxmox_cloud-init.png 945w,\n/static/0b8318f1c09f53862bd41a04669ad26e/d9199/proxmox_cloud-init.png 960w\"\n        sizes=\"(max-width: 630px) 100vw, 630px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span>\n위와 같이 User, Password, SSH public key를 넣어주고, IP Config는 기본으로 dhcp로 설정해 주면 된다. 이후에 다룰 Terraform으로 VM 여러 개 배포하기에서 고정 IP로 생성하는 방법에 대해서 자세히 다루겠다. 값을 다 넣었다면 VM을 우클릭하거나 우측 상단의 more에서 clone을 찾아서 clone을 진행한다.\n<span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/f20e55b68b3f9d633646237b8a822fea/e72de/proxmox_clone.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 41.77215189873418%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABB0lEQVQoz3WRPU7FMBCEfQUK6JPYzp8dO78dT6LkCHQ06blAem49aDZshPR4xWg2491vrdj4PsEPi6jOG3xaxa11sNZeqqpK3LkzL8tSdOaV9Ls+w4QQkXJGCAFN02KcZsQ4IMZ4qes68b7vBVbXNZZlwbZtmKZJzoZhQB8CDIN1XdG1LZqmwe32inmekXPGOI4ywGYdYg+BzAllDxfxQlxsGPCjKAppJIwZgQoljCAOKJAQFefpPDO6KaUkA3RCVHpD772A+A9Zt217iSCKuWHwd8N/UpDCtL6Xh+Er6Ws9bjxBCtP6krPwthI3+77jOA7Q+YJ3zQ9A7lcEPb994uXjG0/vX/gBLjvwzghKEYMAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"proxmox_clone\"\n        title=\"proxmox_clone\"\n        src=\"/static/f20e55b68b3f9d633646237b8a822fea/f058b/proxmox_clone.png\"\n        srcset=\"/static/f20e55b68b3f9d633646237b8a822fea/c26ae/proxmox_clone.png 158w,\n/static/f20e55b68b3f9d633646237b8a822fea/6bdcf/proxmox_clone.png 315w,\n/static/f20e55b68b3f9d633646237b8a822fea/f058b/proxmox_clone.png 630w,\n/static/f20e55b68b3f9d633646237b8a822fea/40601/proxmox_clone.png 945w,\n/static/f20e55b68b3f9d633646237b8a822fea/e72de/proxmox_clone.png 1198w\"\n        sizes=\"(max-width: 630px) 100vw, 630px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span>\n위와 같은 창이 뜰 것이다. 여기서 VM ID, Name 등을 넣어주고, Mode를 지정해 줘야 한다. 여기서 Linked Clone과 Full Clone이 있는데, Linked Clone은 원본 이미지에 의존해서 VM을 생성하기 때문에 디스크 공간을 절약하고 더 빠른 Clone을 할 수 있지만, 원본 이미지의 안정성이 보장돼야 한다. 또한 Linked Clone을 하면 link 돼 있는 이미지(예제에선 VM ID 9000)와 같은 디스크에 clone 된 VM이 존재해야 한다. replication을 적용한 상태의 서로다른 proxmox 노드로의 migration은 시도해 보진 않았지만, replication 없이 migration을 진행했을 때는 실패했으니 다른 노드로 migration 할 계획이 있다면 Full Clone을 권장한다. 보통의 경우라면 테스트할 때는 linked로 장기간 사용할 계획이라면 full을 추천한다.</p>\n<h1 id=\"결론\" style=\"position:relative;\">결론<a href=\"#%EA%B2%B0%EB%A1%A0\" aria-label=\"결론 permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h1>\n<p>앞으로는 생성된 이미지를 가지고 편하게 초기 세팅이 완료된 VM을 clone으로 생성해서 바로 사용할 수 있을 것이다. Packer를 이용해서 이미지를 만들고 이 이미지로 VM을 생성하는 방식은 그냥 OS 이미지로 부팅하고 생성하는 것보다 훨씬 편하고 일관적이다. proxmox를 사용을 굉장히 편리하게 해줘서 이 경험을 최대한 자세히 공유하고자 노력했고, 많은 사람들에게 도움이 됐으면 한다.</p>\n<p>다음 글에서 Terraform으로 같은 이미지를 사용해서 k8s cluster를 위한 VM 여러 개를 배포하는 과정을 설명하려고 한다.</p>\n<p>잘못된 설명이나 추가로 설명했으면 좋겠는 부분, 오타, 맞춤법에 대한 지적은 언제나 환영합니다.</p>\n<ul>\n<li>이 글을 홈서버 사용자를 위한 것입니다. 실제 환경에서 사용할 때는 credential을 Vault나 환경 변수 등으로 좀 더 엄격하게 관리하고, dedicate 한 proxmox user, pool, token 사용을 고려하기를 바랍니다. Packer가 설치된 host의 보안에 충분한 주의를 기울이기를 바랍니다.</li>\n<li>이 글을 포함해 인터넷에 존재하는 코드, 스크립트를 복사해 사용할 때는 코드, 스크립트를 충분히 읽고, 분석하여 문제가 없는지 확인하시기를 바랍니다. 최소한 ChatGPT 같은 AI에 해당 코드, 스크립트가 정말 안전하고 사용해도 되는지 확인받으시길 권장합니다.</li>\n</ul>\n<h1 id=\"참고\" style=\"position:relative;\">참고<a href=\"#%EC%B0%B8%EA%B3%A0\" aria-label=\"참고 permalink\" class=\"heading-anchor after\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a></h1>\n<p><a href=\"https://github.com/ChristianLempa\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">ChristianLempa</a> github/boilerplates</p>\n<p><a href=\"https://github.com/ChristianLempa/boilerplates/tree/main/packer/proxmox\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://github.com/ChristianLempa/boilerplates/tree/main/packer/proxmox</a></p>\n<p><a href=\"https://www.hashicorp.com/ko/products/packer\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">HashiCorp Packer</a> developer docs</p>\n<p><a href=\"https://developer.hashicorp.com/packer/integrations/hashicorp/proxmox\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://developer.hashicorp.com/packer/integrations/hashicorp/proxmox</a></p>","fields":{"slug":"/build-ubuntu-image-for-proxmox-with-packer/"},"frontmatter":{"title":"Packer로 proxmox에 VM 이미지 생성하기","date":"2025-02-18","description":"Packer에 대해서","tags":["proxmox","packer","ubuntu"]}},"previous":{"fields":{"slug":"/proxmox/admin-guide/01/"},"frontmatter":{"title":"Proxmox Admin Guide 살펴보기"}},"next":{"fields":{"slug":"/deploy-ubuntu-on-proxmox-with-terraform/"},"frontmatter":{"title":"Terraform으로 proxmox에 VM 생성하기"}}},"pageContext":{"id":"1fedb6aa-64a5-5805-bae0-aad311932b1d","previousPostId":"a35e39dd-3fb9-5e2b-9728-6de51e5fcdc1","nextPostId":"c9ec09c1-fb37-5d17-ba6b-98f4f77acf98"}},
    "staticQueryHashes": ["2260143708","2538111481","658623446","984448874"]}