構成管理のためにTerraformでAnsibleを使用する方法

提供:Dev Guides
移動先:案内検索

著者は、 Write for DOnations プログラムの一環として、 Free and Open SourceFundを選択して寄付を受け取りました。

序章

Ansible は、 playbooks を実行する構成管理ツールです。これは、指定されたターゲットサーバー上でYAMLで記述されたカスタマイズ可能なアクションのリストです。 ソフトウェアのインストールと更新、ユーザーの作成と削除、システムサービスの構成など、すべてのブートストラップ操作を実行できます。 そのため、デフォルトで空白で作成されるTerraformを使用してデプロイするサーバーを起動するのに適しています。

AnsibleとTerraformは、インフラストラクチャとソフトウェアの展開のさまざまなフェーズを解決するため、競合するソリューションではありません。 Terraformを使用すると、アプリケーションを実行するハードウェアを含む、システムのインフラストラクチャを定義および作成できます。 逆に、Ansibleは、提供されたサーバーインスタンスでプレイブックを実行することにより、ソフトウェアを構成およびデプロイします。 作成直後にプロビジョニングされたリソースTerraformでAnsibleを実行すると、リソースをユースケースで使用できるようにすることができます。 また、デプロイされたすべてのサーバーに同じアクションが適用されるため、メンテナンスとトラブルシューティングが容易になります。

このチュートリアルでは、Terraformを使用してドロップレットをデプロイし、作成直後にAnsibleを使用してドロップレットをブートストラップします。 リソースがデプロイされるときに、Terraformから直接Ansibleを呼び出します。 また、構成でTerraformのremote-execおよびlocal-execプロビジョナーを使用して競合状態を導入することは避けます。これにより、セットアップを開始する前にDropletの展開が完全に完了します。

前提条件

  • DigitalOceanパーソナルアクセストークン。DigitalOceanコントロールパネルから作成できます。 手順については、DigitalOcean製品ドキュメントパーソナルアクセストークンの作成方法を参照してください。
  • ローカルマシンにインストールされたTerraformと、DigitalOceanプロバイダーでセットアップされたプロジェクト。 DigitalOcean チュートリアルでTerraformを使用する方法のステップ1およびステップ2を完了し、プロジェクトフォルダーにterraform-ansibleではなく名前を付けてください。 loadbalance
  • Ansibleがマシンにインストールされています。 Ubuntu 20.04の場合は、 Ubuntu20.04チュートリアルにAnsibleをインストールして構成する方法の最初のステップを完了します。 Ansibleの詳細については、このAnsibleを使用した構成管理の概要の記事をお読みください。

注:このチュートリアルは、Terraform1.0.2で特別にテストされています。


ステップ1—液滴を定義する

このステップでは、後でAnsibleプレイブックを実行するドロップレットを定義します。これにより、ApacheWebサーバーがセットアップされます。

前提条件の一部として作成したterraform-ansibleディレクトリにいると仮定して、Dropletリソースを定義し、countを指定してそのコピーを3つ作成し、それらのIPアドレスを出力します。 droplets.tfという名前のファイルに定義を保存します。 次のコマンドを実行して、編集用に作成して開きます。

nano droplets.tf

次の行を追加します。

〜/ terraform-ansible / droplets.tf

resource "digitalocean_droplet" "web" {
  count  = 3
  image  = "ubuntu-18-04-x64"
  name   = "web-${count.index}"
  region = "fra1"
  size   = "s-1vcpu-1gb"

  ssh_keys = [
      data.digitalocean_ssh_key.terraform.id
  ]
}

output "droplet_ip_addresses" {
  value = {
    for droplet in digitalocean_droplet.web:
    droplet.name => droplet.ipv4_address
  }
}

ここでは、リージョンfra1のCPUコアに1GBのRAMを搭載したUbuntu18.04を実行するDropletリソースを定義します。 Terraformは、前提条件で定義したSSHキーをアカウントからプルし、ssh_keysに渡された指定された一意のIDリスト要素でプロビジョニングされたドロップレットに追加します。 countパラメータが設定されているため、Terraformはドロップレットを3回デプロイします。 その後の出力ブロックには、3つのドロップレットのIPアドレスが表示されます。 loop はドロップレットのリストをトラバースし、インスタンスごとに、その名前とIPアドレスをペアにして、結果のマップに追加します。

完了したら、ファイルを保存して閉じます。

これで、Terraformが展開するドロップレットが定義されました。 次のステップでは、デプロイされた3つのドロップレットのそれぞれで実行され、ApacheWebサーバーをデプロイするAnsibleプレイブックを作成します。 後でTerraformコードに戻り、Ansibleとの統合を追加します。

ステップ2—AnsiblePlaybookを作成する

次に、新しいユーザーの作成やインストールされたパッケージのアップグレードなど、サーバーの初期セットアップタスクを実行するAnsibleプレイブックを作成します。 ターゲットホストで実行されるアクションの単位であるtasksを記述して、Ansibleに何をすべきかを指示します。 タスクは、組み込み関数を使用することも、実行するカスタムコマンドを指定することもできます。 初期設定のタスクに加えて、Apache Webサーバーをインストールし、そのmod_rewriteモジュールを有効にします。

プレイブックを作成する前に、DigitalOceanアカウントのSSHキーに対応する公開SSHキーと秘密SSHキーが、TerraformとAnsibleを実行しているマシンで使用可能でアクセス可能であることを確認してください。 Linuxでそれらを保存するための一般的な場所は、~/.sshです(ただし、他の場所に保存することもできます)。

注: Linuxでは、秘密鍵ファイルに適切な権限があることを確認する必要があります。 次のコマンドを実行して設定できます。

chmod 600 your_private_key_location

秘密鍵の変数はすでに定義されているため、公開鍵の場所に1つ追加するだけで済みます。

provider.tfを開いて、次のコマンドを実行して編集します。

nano provider.tf

次の行を追加します。

〜/ terraform-ansible / Provider.tf

terraform {
  required_providers {
    digitalocean = {
      source = "digitalocean/digitalocean"
      version = "~> 2.0"
    }
  }
}

variable "do_token" {}
variable "pvt_key" {}
variable "pub_key" {}

provider "digitalocean" {
  token = var.do_token
}

data "digitalocean_ssh_key" "terraform" {
  name = "terraform"
}

完了したら、ファイルを保存して閉じます。

pub_key変数が定義されたら、Ansibleプレイブックの作成を開始します。 apache-install.ymlというファイルに保存します。 編集のために作成して開きます。

nano apache-install.yml

徐々にプレイブックを作成していきます。 まず、プレイブックを実行するホスト、その名前、およびタスクをrootとして実行するかどうかを定義する必要があります。 次の行を追加します。

〜/ terraform-ansible / apache-install.yml

- become: yes
  hosts: all
  name: apache-install

becomeyesに設定すると、スーパーユーザーとしてコマンドを実行するようにAnsibleに指示し、hostsallを指定すると、Ansibleにタスクの実行を許可します。 Terraformのように、任意のサーバーで、コマンドラインから渡されたサーバーでも。

追加する最初のタスクは、root以外の新しいユーザーを作成します。 次のタスク定義をプレイブックに追加します。

〜/ terraform-ansible / apache-install.yml

  tasks:
    - name: Add the user 'sammy' and add it to 'sudo'
      user:
        name: sammy
        group: sudo

最初にタスクのリストを定義してから、それにタスクを追加します。 sammy という名前のユーザーを作成し、適切なグループに追加することで、sudoを使用してスーパーユーザーアクセスを許可します。

次のタスクでは、公開SSHキーをユーザーに追加するため、後で接続できるようになります。

〜/ terraform-ansible / apache-install.yml

    - name: Add SSH key to 'sammy'
      authorized_key:
        user: sammy
        state: present
        key: "{{ lookup('file', pub_key) }}"

このタスクは、ローカルファイルから検索される公開SSHキーがターゲット上でpresentであることを確認します。 次のステップで、Terraformからpub_key変数の値を指定します。

次のタスクを追加することで、Apacheとmod_rewriteモジュールのインストールを注文できるようになりました。

〜/ terraform-ansible / apache-install.yml

    - name: Wait for apt to unlock
      become: yes
      shell:  while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 5; done;
      
    - name: Install apache2
      apt:        
        name: apache2
        update_cache: yes
        state: latest
        
    - name: Enable mod_rewrite
      apache2_module:
        name: rewrite
        state: present
      notify:
        - Restart apache2

  handlers:
    - name: Restart apache2
      service:
      name: apache2
      state: restarted

最初のタスクは、aptパッケージマネージャーを使用した以前のパッケージのインストールが完了するまで待機します。 2番目のタスクはaptを実行してApacheをインストールします。 次に、3つ目は、mod_rewriteモジュールがpresentであることを確認します。 有効にした後、Apacheを再起動する必要があります。これは、タスク自体から構成することはできません。 これを解決するには、ハンドラーを呼び出して再起動を発行します。

この時点で、プレイブックは次のようになります。

〜/ terraform-ansible / apache-install.yml

- become: yes
  hosts: all
  name: apache-install
  tasks:
    - name: Add the user 'sammy' and add it to 'sudo'
      user:
        name: sammy
        group: sudo
        
    - name: Add SSH key to 'sammy'
      authorized_key:
        user: sammy
        state: present
        key: "{{ lookup('file', pub_key) }}"
        
    - name: Wait for apt to unlock
      become: yes
      shell:  while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 5; done;
      
    - name: Install apache2
      apt:
        name: apache2
        update_cache: yes
        state: latest
      
    - name: Enable mod_rewrite
      apache2_module:
        name: rewrite 
        state: present
      notify:
        - Restart apache2

  handlers:
    - name: Restart apache2
      service:
        name: apache2
        state: restarted

完了したら、すべてのYAML要素のインデントが正しく、上記のインデントと一致していることを確認します。 Ansible側で定義する必要があるのはこれだけなので、プレイブックを保存して閉じます。 ドロップレットのプロビジョニングが完了したときにこのプレイブックを実行するように、ドロップレット展開コードを変更します。

ステップ3—デプロイされたドロップレットでAnsibleを実行する

Ansibleがターゲットサーバーで実行するアクションを定義したので、Dropletの作成時に実行するようにTerraform構成を変更します。

Terraformは、コマンドを実行する2つのプロビジョナーを提供します。local-execremote-execで、それぞれローカルまたはリモート(ターゲット上)でコマンドを実行します。 remote-execはタイプやアクセスキーなどの接続データを必要としますが、local-execはTerraformが実行されているマシンですべてを実行するため、接続情報は必要ありません。 local-execは、定義したリソースのプロビジョニングが完了した直後に実行されることに注意してください。 したがって、リソースが実際に起動するのを待つことはありません。 クラウドプラットフォームがシステム内での存在を確認した後に実行されます。

ドロップレットにプロビジョナー定義を追加して、デプロイ後にAnsibleを実行します。 droplets.tfを開いて編集します。

nano droplets.tf

強調表示された行を追加します。

〜/ terraform-ansible / droplets.tf

resource "digitalocean_droplet" "web" {
  count  = 3
  image  = "ubuntu-18-04-x64"
  name   = "web-${count.index}"
  region = "fra1"
  size   = "s-1vcpu-1gb"

  ssh_keys = [
      data.digitalocean_ssh_key.terraform.id
  ]

  provisioner "remote-exec" {
    inline = ["sudo apt update", "sudo apt install python3 -y", "echo Done!"]

    connection {
      host        = self.ipv4_address
      type        = "ssh"
      user        = "root"
      private_key = file(var.pvt_key)
    }
  }

  provisioner "local-exec" {
    command = "ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u root -i '${self.ipv4_address},' --private-key ${var.pvt_key} -e 'pub_key=${var.pub_key}' apache-install.yml"
  }
}

output "droplet_ip_addresses" {
  value = {
    for droplet in digitalocean_droplet.web:
    droplet.name => droplet.ipv4_address
  }
}

Terraformと同様に、Ansibleはローカルで実行され、SSH経由でターゲットサーバーに接続します。 これを実行するには、ansible-playbookコマンドを実行するDroplet定義でlocal-execプロビジョナーを定義します。 これにより、ユーザー名( root )、現在のドロップレットのIP(${self.ipv4_address}で取得)、SSH公開鍵と秘密鍵が渡され、実行するプレイブックファイルが指定されます(apache-install.yml)。 ANSIBLE_HOST_KEY_CHECKING環境変数をFalseに設定すると、サーバーが事前に接続されているかどうかの確認をスキップできます。

前述のように、local-execプロビジョナーは、ドロップレットが利用可能になるのを待たずに実行されるため、プレイブックの実行は、ドロップレットが実際に利用可能になる前に実行される場合があります。 これを修正するには、remote-execプロビジョナーを定義して、ターゲットサーバーで実行するコマンドを含めます。 remote-execを実行するには、ターゲットサーバーが使用可能である必要があります。 remote-execlocal-execの前に実行されるため、サーバーはAnsibleが呼び出されるまでに完全に初期化されます。 python3はUbuntu18.04にプリインストールされているため、必要に応じてコマンドをコメントアウトまたは削除できます。

変更が完了したら、ファイルを保存して閉じます。

次に、次のコマンドを実行してドロップレットを展開します。 private_key_locationpublic_key_locationを、それぞれ秘密鍵と公開鍵の場所に置き換えることを忘れないでください。

terraform apply -var "do_token=${DO_PAT}" -var "pvt_key=private_key_location" -var "pub_key=public_key_location"

出力が長くなります。 ドロップレットがプロビジョニングされ、それぞれとの接続が確立されます。 次に、remote-execプロビジョナーがpython3を実行してインストールします。

Output...
digitalocean_droplet.web[1] (remote-exec): Connecting to remote host via SSH...
digitalocean_droplet.web[1] (remote-exec):   Host: ...
digitalocean_droplet.web[1] (remote-exec):   User: root
digitalocean_droplet.web[1] (remote-exec):   Password: false
digitalocean_droplet.web[1] (remote-exec):   Private key: true
digitalocean_droplet.web[1] (remote-exec):   Certificate: false
digitalocean_droplet.web[1] (remote-exec):   SSH Agent: false
digitalocean_droplet.web[1] (remote-exec):   Checking Host Key: false
digitalocean_droplet.web[1] (remote-exec): Connected!
...

その後、Terraformは各ドロップレットに対してlocal-execプロビジョナーを実行し、Ansibleを実行します。 次の出力は、ドロップレットの1つについてこれを示しています。

Output...
digitalocean_droplet.web[2] (local-exec): Executing: ["/bin/sh" "-c" "ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u root -i 'ip_address,' --private-key private_key_location -e 'pub_key=public_key_location' apache-install.yml"]

digitalocean_droplet.web[2] (local-exec): PLAY [apache-install] **********************************************************

digitalocean_droplet.web[2] (local-exec): TASK [Gathering Facts] *********************************************************
digitalocean_droplet.web[2] (local-exec): ok: [ip_address]

digitalocean_droplet.web[2] (local-exec): TASK [Add the user 'sammy' and add it to 'sudo'] *******************************
digitalocean_droplet.web[2] (local-exec): changed: [ip_address]

digitalocean_droplet.web[2] (local-exec): TASK [Add SSH key to 'sammy''] *******************************
digitalocean_droplet.web[2] (local-exec): changed: [ip_address]

digitalocean_droplet.web[2] (local-exec): TASK [Update all packages] *****************************************************
digitalocean_droplet.web[2] (local-exec): changed: [ip_address]

digitalocean_droplet.web[2] (local-exec): TASK [Install apache2] *********************************************************
digitalocean_droplet.web[2] (local-exec): changed: [ip_address]

digitalocean_droplet.web[2] (local-exec): TASK [Enable mod_rewrite] ******************************************************
digitalocean_droplet.web[2] (local-exec): changed: [ip_address]

digitalocean_droplet.web[2] (local-exec): RUNNING HANDLER [Restart apache2] **********************************************
digitalocean_droplet.web[2] (local-exec): changed: [ip_address]

digitalocean_droplet.web[2] (local-exec): PLAY RECAP *********************************************************************
digitalocean_droplet.web[2] (local-exec): [ip_address]             : ok=7    changed=6    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

...

出力の最後に、3つのドロップレットとそのIPアドレスのリストが表示されます。

Outputdroplet_ip_addresses = {
  "web-0" = "..."
  "web-1" = "..."
  "web-2" = "..."
}

これで、ブラウザのIPアドレスの1つに移動できます。 デフォルトのApacheウェルカムページが表示され、Webサーバーが正常にインストールされたことを示します。

これは、Terraformがサーバーをプロビジョニングし、Ansibleプレイブックがサーバー上で正常に実行されたことを意味します。

SSHキーがプロビジョニングされたドロップレットのsammyに正しく追加されたことを確認するには、次のコマンドを使用してそれらの1つに接続します。

ssh -i private_key_location sammy@droplet_ip_address

Terraform出力にあるプロビジョニングされたドロップレットの1つの秘密鍵の場所とIPアドレスを忘れずに入力してください。

出力は次のようになります。

OutputWelcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-121-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of ...

  System load:  0.0               Processes:           88
  Usage of /:   6.4% of 24.06GB   Users logged in:     0
  Memory usage: 20%               IP address for eth0: ip_address
  Swap usage:   0%                IP address for eth1: ip_address

0 packages can be updated.
0 updates are security updates.

New release '20.04.1 LTS' available.
Run 'do-release-upgrade' to upgrade to it.


*** System restart required ***
Last login: ...
...

ターゲットに正常に接続し、 sammy ユーザーのシェルアクセスを取得しました。これにより、SSHキーがそのユーザーに対して正しく構成されていることが確認されます。

次のコマンドを実行し、プロンプトが表示されたらyesと入力すると、デプロイされたドロップレットを破棄できます。

terraform destroy -var "do_token=${DO_PAT}" -var "pvt_key=private_key_location"  -var "pub_key=public_key_location"

このステップでは、Ansibleプレイブックの実行をlocal-execプロビジョナーとしてドロップレット定義に追加しました。 サーバーが接続に使用できるようにするために、remote-execプロビジョナーを含めました。これは、python3前提条件をインストールするのに役立ち、その後Ansibleが実行されます。

結論

TerraformとAnsibleは一緒になって、必要なソフトウェアとハードウェアの構成でサーバーを起動するための柔軟なワークフローを形成します。 Terraformデプロイメントプロセスの一部としてAnsibleを直接実行すると、サーバーを起動して、開発作業とアプリケーションの依存関係をはるかに高速にブートストラップできます。

このチュートリアルは、Terraformシリーズでインフラストラクチャを管理する方法の一部です。 このシリーズでは、Terraformの初めてのインストールから複雑なプロジェクトの管理まで、Terraformの多くのトピックを取り上げています。

また、Ansibleトピックページで追加のAnsibleコンテンツリソースを見つけることができます。