構成管理のためにTerraformでAnsibleを使用する方法
著者は、 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
become
をyes
に設定すると、スーパーユーザーとしてコマンドを実行するようにAnsibleに指示し、hosts
にall
を指定すると、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-exec
とremote-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-exec
はlocal-exec
の前に実行されるため、サーバーはAnsibleが呼び出されるまでに完全に初期化されます。 python3
はUbuntu18.04にプリインストールされているため、必要に応じてコマンドをコメントアウトまたは削除できます。
変更が完了したら、ファイルを保存して閉じます。
次に、次のコマンドを実行してドロップレットを展開します。 private_key_location
とpublic_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コンテンツリソースを見つけることができます。