LoginSignup
1
0

Packer が利用できない状況下で CircleCI と Ansible で GCE マシーンイメージ(Google Cloud Platform)をビルドする

Last updated at Posted at 2023-08-06

前提条件

以前なら GCE マシーンイメージのビルドは Packer を利用して処理が実行できていました。しかし最近のアップデートにより(Packer なのか GCE なのかどちらか分かりませんが)仕様が変わってしまったようで現時点では マシーンイメージのビルドに Packer を採用できません。

今回は代替策として CircleCI と Ansible を利用して GCE マシーンイメージをビルドする方法を紹介します。

CircleCI Executor のビルド

まず初めに CircleCI での処理を実行するための Executor をビルドします。Ansible のバージョンは常に最新のものになります。状況に応じて Ansible のバージョンを固定します。

Dockerfile
FROM python:3-slim

# Install required packages
RUN apt-get update \
  && mkdir -p /usr/share/man/man1 \
  && apt-get install -y \
    apt ca-certificates curl git locales openssh-client sudo unzip

# Add User
RUN groupadd --gid 3434 circleci \
  && useradd --uid 3434 --gid circleci --shell /bin/bash --create-home circleci \
  && echo 'circleci ALL=NOPASSWD: ALL' >> /etc/sudoers.d/50-circleci \
  && echo 'Defaults    env_keep += "DEBIAN_FRONTEND"' >> /etc/sudoers.d/env_keep

# Install Ansible
RUN sudo -u circleci pip3 install --user ansible

USER circleci
ENV PATH /home/circleci/.local/bin:/home/circleci/bin:${PATH}

CMD ["/bin/sh"]

サンプルコード になります。

サービスアカウントの発行

GCP のサービスアカウントを発行して CircleCI へ登録します。必要な権限はPackerのマニュアルを参考にします。

スクリーンショット 2023-08-06 19.12.35.png
スクリーンショット 2023-08-06 19.18.56.png
スクリーンショット 2023-08-06 20.54.56.png

秘匿情報の登録

CircleCI の Context に以下の情報を登録します。

スクリーンショット 2023-08-06 11.05.19.png

これは Orb circleci/gcp-cli で gcloud コマンドの設定をする時に必要な情報になります。

CLOUD_SERVICE_KEY

以下のようなものです。

{
  "type": "service_account",
  "project_id": "sample-project-263612",
  "private_key_id": "552dcba6163bdda85b4f4fac12b09d80d77a3e843a",
  "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCgDYZONQcSd5RV\nJb8TwpZNqjJkNvZWhgixWZkbsFQT5j9bHrCXy32M2gyWiuZdlg/PdufL6PDcotuC\nruJn0Nhp3aWk07n6NiFjNQX+ivxNz/JIhoClucFve8mgSKHVFZYF5SVymuEc7dG/\n3OOHTEogLt3jP0lN+/V+PlhgXcLSYURfphlTmqErIdvovo+rR+gCs0n7WusuHqXv\nUu7zx5WOcZu+MYgtYF7uENrt1Gr81SMKY/lp1WSzt4TTqLZxDEkk/JQ0VLc2ijDh\nxQdUv8fH6EwGaYX65B5BFr+jwawh2T2N69yGEqo3w0rYmCbxq25p/UWaXjFfLHgX\nY5PiVdXhAgMBAAECggEAA34wEWpeWGSUL+SGPkHnolPFzEKXzy0XKPmt2fkX6KRE\nezJZA8MM3yNOFYX7/4xcFXjAl8ZiZUpHlddwdI9R+NbYgJCqcV33cfqNx5L3nF9jpws\nazFGQveiFVMEBDO5lmslbkUBM3ZWVdmJ05BYRdZgIsIrFP/HU4SdZ68oIhhBvXIZ\nmYcbxyhSgNMUsXzwqj6/UVCEER9fUVGDG95JU9WpLAP26gRcqhJTqVRmX+SaQP8z\nJBhh8UHBeWJ3cCFuV3zFv+SmdxnY/pXknOmabyl8X+gOJLB3TSctWSoJSJBQ6O5J\n3R69jLaWqmmSmFGGZtriR7DS+R8GjbwDyFk3rv/uQQKBgQDfNp5Bz2dwkxegrIAm\niVzDWOrd6/lO40vK8UemN2u7hbwxnmti4W9S+N6WMubZShGa97Nb1OURHPujJEhg\nzqmE5b2oHBwccvFSFS1a7z4soxFKTQwnKOxfH1PMJlETX1K6ypwpYPUItBA/1kS8\nbLLm7cU/oQ7HFzT58iSafWqXMQKBgQC3j+mVJzJmfP9axRouv6s7kBU6256RNJ1Z\nvQt5CpRSX36FpadTtixt+Kz5Xiuyb4RHXDSokJAUBbfXYDJiRew3df9WRQdMwlyNq\n5LemiCezr00KoTnunfmgMe+eUViG/uYgyE3sm+U54RbfiLnCPu9JfydNn9DLHNzd\nb+3uTX7dsQKBgGaRr4koKC0nuky/16ddqX2uh0Zw37/rXHGmC7mKb/vciUz3sfrf\nAovLBmVYgJHKompmdkm1e4kwm0UtMAEkFeuWsl7kg9piyxdf2daWZyiVyiUtYG9C\nM9PGDniaBtlzDQ3+emHdRtu3+luLN0yqWk8ZZXFGrga4WESei0leZjORAoGAVSQ9\nVd9uczc7QiD2OgTRKbl0/23qqLNc4Mjcz3HmxiZhCyCA8kUnaoLTyH6zifpLwsWz\n7xPOoFreoNmPxSDARtjmeI+to3YXCXe471dsAt4mv+10b4d6x6Eh4a8dftAwcbg2\n3K6arjQHZfuHHeAPIWoHEuwz7mIe198Y31W2qKECgYEAmQLmyoYFUnF26PusW0+d\nl1mLGblznr9h9mCfh52hgV+D6xTI7vn2gUaonWHM6nnJPc28LZnDNLJRzotHkIBl\nAhXAgDvF357dr8Jp5rSV6Ssf77P93s4XKzjKJmEKnQbDIm53RgFdtisGufkWQq73\njb8Zh+KhFEFiLEnU4igc4K0=\n-----END PRIVATE KEY-----\n",
  "client_email": "mamono210@sample-project-263612.iam.gserviceaccount.com",
  "client_id": "109182338149199700825",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/mamono210%40sample-project-263612.iam.gserviceaccount.com",
  "universe_domain": "googleapis.com"
}

GOOGLE_COMPUTE_REGION

Google Cloud Platform (GCP)の利用可能なリージョンとゾーンは頻繁に更新される可能性があります。以下のような値を選びます。

us-central1
us-west1
us-east1
us-east4
us-west2
us-west3
us-west4
europe-west1
europe-west2
europe-west3
europe-west4
europe-west6
europe-central2
asia-east1
asia-east2
asia-south1
asia-southeast1
asia-southeast2
asia-northeast1
asia-northeast2
asia-northeast3
southamerica-east1
northamerica-northeast1
northamerica-northeast2
australia-southeast1
australia-southeast2

GOOGLE_COMPUTE_ZONE

以下のような値になります。リージョンによってことなります。

a
b
c
d

GOOGLE_PROJECT_ID

Google Cloud Platform のプロジェクトIDです。

CircleCI の設定ファイルの実装

CircleCI の設定ファイルで以下の処理を記述していきます。サンプルコードはこちらになります。

  1. gcloud コマンドのインストール
  2. GCE インスタンスの起動
  3. SSHキーの発行・登録
  4. Ansible playbook の実行
  5. インスタンスをマシーンイメージへ登録
  6. インスタンスの破棄
.circleci/config.yml
version: 2.1

executors:
  packer:
    docker:
      - image: ghcr.io/docker-images-mamono210/circleci-executors/ansible:latest
    resource_class: small

orbs:
  gcp-cli: circleci/gcp-cli@3.1.0

jobs:
  packer:
    executor: packer
    parameters:
      boot-disk-size:
        type: string
      image-family:
        type: string
      image-project:
        type: string
      instance-name:
        type: string
      machine-type:
        default: n1-standard-1
        type: string
      machine-image-name:
        type: string
      project-name:
        type: string
      service-account:
        type: string
      zone:
        type: string
    steps:
      - checkout
      - gcp-cli/setup
      - run:
          name: Set environment variables to create unique resources
          command: |
            # 一意のリソースを作成するためにタイムスタンプを利用して変数を設定する
            TIMESTAMP=$(date --date "9 hours" "+%Y%m%d-%H%M%S")
            echo "export INSTANCE_NAME=<< parameters.instance-name>>-${TIMESTAMP}" >> $BASH_ENV
            echo "export MACHINE_IMAGE_NAME=<< parameters.machine-image-name >>-${TIMESTAMP}" >> $BASH_ENV
            source $BASH_ENV
      - run:
          name: Create instance
          command: |
            # Ansible を実行する対象のインスタンスを Ansible 実行前に起動しておく
            gcloud compute instances create ${INSTANCE_NAME} \
              --project=<< parameters.project-name >> \
              --zone=<< parameters.zone >> \
              --image-project=<< parameters.image-project >> \
              --image-family=<< parameters.image-family >> \
              --machine-type=<< parameters.machine-type >> \
              --boot-disk-size=<< parameters.boot-disk-size >> \
              --network-interface=network-tier=PREMIUM,stack-type=IPV4_ONLY,subnet=default \
              --maintenance-policy=MIGRATE \
              --provisioning-model=STANDARD \
              --service-account=<< parameters.service-account >> \
              --scopes=https://www.googleapis.com/auth/compute,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write,https://www.googleapis.com/auth/trace.append,https://www.googleapis.com/auth/servicecontrol,https://www.googleapis.com/auth/service.management.readonly,https://www.googleapis.com/auth/devstorage.read_only \
              --no-shielded-secure-boot \
              --shielded-vtpm \
              --shielded-integrity-monitoring \
              --reservation-affinity=any
      - run:
          name: Create SSH Key
          command: |
            # Ansible がインスタンスへ接続するためのSSH鍵を発行する
            gcloud compute ssh centos@${INSTANCE_NAME} \
            --command "cat /etc/redhat-release" \
            --zone=<< parameters.zone >> \
            --quiet

            # Set Strict host key checking no
            echo "StrictHostKeyChecking no" > ~/.ssh/config
      - run:
          name: Update OS
          command: |
            # ansible-playbook コマンドを実行して対象のシステムをセットアップする
            # 環境変数に ANSIBLE_FORCE_COLOR、PY_COLORS を設定すると Ansible の実行ログがカラー表示になる
            # Get external Ip addr
            ip_addr=$(
                       gcloud compute instances describe ${INSTANCE_NAME} \
                         --format='get(networkInterfaces[0].accessConfigs[0].natIP)' \
                         --zone=<< parameters.zone >> \
                     )

            # Execute Ansible playbook
            ansible-playbook -i ${ip_addr}, \
            -u centos \
            --key-file="~/.ssh/google_compute_engine" \
            update.yml
          environment:
            ANSIBLE_FORCE_COLOR: '1'
            PY_COLORS: '1'
            TZ: 'Asia/Tokyo'
      - run:
          name: Register machine image
          command: |
            gcloud beta compute machine-images create ${MACHINE_IMAGE_NAME} \
              --project=<< parameters.project-name >> \
              --source-instance=${INSTANCE_NAME} \
              --source-instance-zone=<< parameters.zone >> \
              --storage-location=us
      - run:
          name: Delete instance
          command: |
            gcloud compute instances delete ${INSTANCE_NAME} \
              --project=<< parameters.project-name >> \
              --zone=<< parameters.zone >>

workflows:
  version: 2.1
  packer:
    jobs:
      - packer:
          boot-disk-size: '40'
          context: GCLOUD
          machine-image-name: 'centos-stream-8-golden-image'
          machine-type: 'n1-standard-1'
          image-family: 'centos-stream-8'
          image-project: 'centos-cloud'
          instance-name: 'centos-stream-8-golden-image-builder'
          project-name: 'buoyant-world-263612'
          service-account: 'mamono210@buoyant-world-263612.iam.gserviceaccount.com'
          zone: 'us-central1-a'

まとめ

Packer が利用できない現在の状況下での GCE マシーンイメージのビルド方法の代替案の1つとして参考になれば幸いです。

その他

サンプルコードのように OS のアップデートのみなら Ansible ではなくコマンドで実行した方が簡単です。

ssh user@remote-server-ip "sudo apt-get update; sudo apt upgrade"

本来ならこのサンプルコードでは Ansible を使うべき場面ではないと思いますが説明を分かりやすくするために Ansible playbook を簡単な処理にしてあります。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0