虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

ChatGPTを使ったインフラ構築

こんにちは、虎の穴ラボの山田です。

本記事は2023 夏のブログ連載企画の13日目の記事になります。

昨日12日目はA.Mさんによる「業務でGitHub Copilotを使ってみて便利と感じた使い方5選」が投稿されています。
明日14日目は古賀さんによる「簡単に作れる!VS CodeでAIとチャットする拡張機能を作ってみた」が投稿される予定です。

こちらもぜひご覧ください。

はじめに

ChatGPTとは、OpenAI社が開発した会話形式でAIと対話ができるチャットサービスです。 特に面倒な設定もなく、AIに対して質問をすると適切な形で回答をもらえます。 今まで書籍やネット上で調査をしたり、社内のメンバーに聞いていたような技術的な問題に対してもすぐにAIから回答を貰えるので、作業が捗ります。

反面、出力される回答は不正確なことがあります。これらは質問の入力フレーズの調整や、間違いの指摘などで改善する場合もありますが、最終的には質問者側での検証が必要です。

今回はこのChatGPTを使ってAnsibleを実行できる環境をDockerで作り、さらにAnsibleを使って対象に対しNginxをインストールするyamlファイルの作成を試したいと思います。

モチベーション

Docker HubのAnsibleの公式イメージ は長らく更新がされておらず、AnsibleをDockerで使う場合は自分でイメージを作成することが多いです。
今回はChatGPTを利用して、なるべく手間をかけずにAnsibleを実行できる環境を作れないか?と思ったのがモチベーションでした。 そのため、最終的には手で修正を加えていますが、なるべくChatGPTからの回答で得られたコードをそのまま利用するようにしています。

Docker

まずはAnsibleを実行するためのDockerfileを作成してみます。

  • まず最初に「Ansibleを実装したDockerイメージを作成するDockerfileを作成してください」と送信して、以下のようなコードが返されました。
FROM python:3.9-slim

# 必要なパッケージのインストール
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        openssh-client \
    && rm -rf /var/lib/apt/lists/*

# Ansibleのインストール
RUN pip install ansible

# Ansibleの実行に必要な設定ファイルの作成
RUN mkdir -p /etc/ansible \
    && echo 'localhost' > /etc/ansible/hosts

# ワーキングディレクトリの設定
WORKDIR /ansible

# Ansibleの実行コマンドのエントリーポイント
ENTRYPOINT ["ansible"]
  • これに対しLinter(今回はmacOS上で hadolint を使用)をかけると以下のような警告を受けます。
Dockerfile:4 DL3008 warning: Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
Dockerfile:10 DL3013 warning: Pin versions in pip. Instead of `pip install <package>` use `pip install <package>==<version>` or `pip install --requirement <requirements file>`
Dockerfile:10 DL3042 warning: Avoid use of cache directory with pip. Use `pip install --no-cache-dir <package>`
  • 1つ目の警告文をChatGPTに続けて送信します。
  • 指摘箇所が修正されたコードが返されますが、他の2箇所が直っていないので続けて2つ目の警告文をChatGPTに送信します。
  • 指摘箇所が修正されたコードが返されるのですが、最初の修正箇所は元に戻ってしまいました。そのため、残り2つの警告文をまとめてChatGPTに送信します。
  • 全ての指摘箇所が修正されLinterにかけても警告を受けなくなったので、ここからは手で修正を加えていきます。
  • apt-getコマンドを使っているところを apt コマンドに変更
  • openssh-clientのバージョンを 1:7.9p1-10+deb10u2 から 1:9.2p1-2 に変更
  • ansibleのバージョンは質問者が置き換えるように回答されたので、8.1.0 を指定
  • ansible-lintも実行できるように、ansible-lintのインストールを追加
  • ansibleコマンド以外のansible系コマンドが利用できるように、ENTRYPOINTの設定を削除

上記のようなやりとりと手修正の結果、今回は以下のようなコードとなりました。

FROM python:3.9-slim

# 必要なパッケージのインストール
RUN apt update \
    && apt install -y --no-install-recommends \
        openssh-client=1:9.2p1-2 \
    && rm -rf /var/lib/apt/lists/*

# Ansibleのインストール
RUN pip install --no-cache-dir ansible==8.1.0
RUN pip install --no-cache-dir ansible-lint==6.17.2

# Ansibleの実行に必要な設定ファイルの作成
RUN mkdir -p /etc/ansible \
    && echo 'localhost' > /etc/ansible/hosts

# ワーキングディレクトリの設定
WORKDIR /ansible

上記のDockerfileを使いコンテナイメージを作成し、実行してバージョンを確認してみます。

$ docker build -t myansibleimage .
$ docker container run --rm -it myansibleimage ansible --version
ansible [core 2.15.1]
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.9/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.9.17 (main, Jul  4 2023, 06:21:22) [GCC 12.2.0] (/usr/local/bin/python)
  jinja version = 3.1.2
  libyaml = True
$ docker container run --rm -it myansibleimage ansible-lint --version
ansible-lint 6.17.2 using ansible-core:2.15.1 ansible-compat:4.1.2 ruamel-yaml:None ruamel-yaml-clib:None

表示されているAnsibleのバージョンが前述のコードで指定しているバージョン(8.1.0)と異なりますが、これはコードで指定しているのが pip の ansible パッケージのバージョンであり、ここで表示されているのはその ansible パッケージに含まれる、 ansible-core パッケージのバージョン(2.15.1)のためです。

Ansible

次にChatGPTでAnsibleを実行するためのyamlファイルを作成してみます。

  • まず「テスト環境のLinuxにNginxをインストールし、自動起動を有効化するAnsibleのプレイブックを作成してください」と送信して、以下のようなコードが返されました。
---
- name: Nginx Installation and Auto-Start
  hosts: your_linux_host
  become: true
  tasks:
    - name: Install Nginx
      yum:
        name: nginx
        state: present

    - name: Enable Nginx service
      systemd:
        name: nginx
        enabled: true

更に質問を重ねていきます。

  • 対象のLinuxはinventoryファイルで指定し、Nginxはロールを使って記述するように変更してください。
  • Nginxの設定ファイルをロールのtemplatesを使って配置できるようにしてください。また、変数はグループ単位で指定できるようにしてください。
  • Nginxが出力するログ形式をLTSV形式としてください。
  • 更に、yamlファイルをAnsibleのバージョン2.10以降の形式に修正してください。

最後の質問ではほぼ変わりが無かったので以下のように質問をし直してみました。

  • モジュールの指定方法を、Ansible バージョン2.10以降の形式に修正してください。

上記のような質問を重ねることで以下のような回答が得られました。

ファイル構成

roles/
└── nginx
    ├── tasks
    │   └── main.yml
    └── templates
        └── nginx.conf.j2
inventory.ini
playbook.yml

roles/nginx/tasks/main.yml

---
- name: Nginx Installation and Auto-Start
  hosts: linux_hosts
  become: true

  tasks:
    - name: Install Nginx
      package:
        name: nginx
        state: present

    - name: Enable Nginx service
      systemd:
        name: nginx
        enabled: true
        masked: no

    - name: Copy Nginx configuration file
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
      notify:
        - restart nginx

  handlers:
    - name: restart nginx
      ansible.builtin.systemd:
        name: nginx
        state: restarted

roles/nginx/templates/nginx.conf.j2

worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main 'time:$time_local\t'
                    'remote_addr:$remote_addr\t'
                    'remote_user:$remote_user\t'
                    'request:"$request"\t'
                    'status:$status\t'
                    'body_bytes_sent:$body_bytes_sent\t'
                    'http_referer:"$http_referer"\t'
                    'http_user_agent:"$http_user_agent"\t'
                    'http_x_forwarded_for:"$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log;

    sendfile on;
    keepalive_timeout 65;

    server {
        listen 80;
        server_name _;

        location / {
            root /var/www/html;
            index index.html;
        }
    }
}

inventory.ini

[linux_hosts]
your_linux_host

[linux_hosts:vars]
nginx_server_name=mydomain.com

playbook.yml

---
- name: Nginx Installation and Auto-Start
  hosts: linux_hosts
  become: true
  roles:
    - nginx

ChatGPTからは、以下のような注意点も回答されていたため追記しておきます。

  1. Nginxの設定ファイルは、LTSV形式のログ出力に関する変更を反映するように修正してください。
  2. この設定はNginxのバージョンやカスタム設定によって異なる場合があります。必要に応じて設定を調整してください。
  3. プレイブックはRed Hat系ディストリビューションでのみテストされています。他のLinuxディストリビューションでは、パッケージ名やサービス名が異なる場合があります。必要に応じてロールを調整してください。

次に回答で示された構成のファイルを一旦そのまま作成し、先ほどのDockerで作成したAnsible-Lintにかけます。

$ docker container run --rm -it -v $(pwd):/ansible myansibleimage ansible-lint playbook.yml

nginxロールのタスクファイル roles/nginx/tasks/main.yml には誤りがあり、警告を受けます。 今回は以下のように手修正を行いました。

roles/nginx/tasks/main.yml

---
- name: Install Nginx
  ansible.builtin.package:
    name: nginx
    state: present
- name: Enable Nginx service
  ansible.builtin.systemd:
    name: nginx
    enabled: true
    masked: false
- name: Copy Nginx configuration file
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
  notify:
    - restart nginx

また、handlersのタスクファイルも追加で作成します。

roles/nginx/handlers/main.yml

---
- name: Restart nginx
  ansible.builtin.systemd:
    name: nginx
    state: restarted

これでAnsible-Lintも通るようになりました。
最後に inventory.ini を修正し、Ansibleを実行してみます。(今回は --checkオプションを追加し、実際には適用しないようにします)
inventory.ini には対象のホストの修正とansible_userの設定を追加しておきます。

$ docker container run --rm -it -v $(pwd):/ansible myansibleimage ansible-playbook -i ./inventory.ini playbook.yml --check

PLAY [Nginx Installation and Auto-Start] ************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [hoge]

TASK [nginx : Install Nginx] ************************************************************************
changed: [hoge]

TASK [nginx : Enable Nginx service] *****************************************************************
changed: [hoge]

TASK [nginx : Copy Nginx configuration file] ********************************************************
changed: [hoge]

RUNNING HANDLER [nginx : restart nginx] *************************************************************
changed: [hoge]

PLAY RECAP ******************************************************************************************
hoge               : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

実行が確認できました。

まとめ

今回はChatGPTを使ってDockerfileを作り、更にAnsibleのyamlファイル作成を試すというのをやってみました。 手を加える部分もありましたが、ある程度の部分のコードは自動生成できていたと思います。 こうしたインフラ系の設定ファイルを久々に作成しようとする時は少し億劫になりがちなのですが、ChatGPTと会話しながら作成すると、思い出しながら、また新しい知識も得られて割と楽に作業ができていたと思います。 次はAPIの方を使ってみたいですね。

採用情報

虎の穴ラボでは一緒に働く仲間を募集中です!
この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧ください。
yumenosora.co.jp