はじめに
VS Codeは開発者としてなじみ深いものですが、インフラ系の方は意外と使っている人が少ないようです。
今はインフラの構築手順をドキュメントとして残すのでは無く、IaC(Infrastructure as Code)の考えに沿ってコード化しようという動きがawsさんの方で積極的に行われていました。最近ですとaws cdkなんかが便利でいいですね。cloud formationの構成ファイルは正直書くのが辛かった。全然話はそれますが、AWS SAP資格持ちですが、11月に有効期限切れを迎えるので10月ぐらいには再勉強に関する記事も載せていこうかなと思っています。
本題に戻ると、IaCの考えで手順をコード化するのであれば開発者と同じようにIDE(統合開発環境)も使いますし、コードの変更履歴(github)の管理も行います。そしてIDEでコードを書くのであればgithub copilotなどを使う事が出来るのでchatgptに指示をするより簡単です(複数開いているファイルやターミナル情報を元に回答してくれる)
私の場合は、openhpcの構成管理・slurm検証環境の構築のためにVS Code経由でコンテナ化したJupyter notebookに接続し、github Copilotの助けを借りて、slurmのワーカーノード用にansible-playbookを作成します。そしてjupyterからansibleを起動しワーカーノードに反映する所まで実行したら、notebookを履歴として保存します。
ansible経由で配布するslurm用のパッケージはopenhpc x slurm x beeGFS環境の構築と検証:ワーカーノード用のパッケージ化にて作成したものを使用します。
パッケージ作成するためのspecファイル作成や実行もjuypter notebookでやれば良かったと今更気づきました。
IaCのための環境イメージ
Githubの準備
ここら辺は他の方の記事を参考にして作成してください。
githubアカウントの作成
IaCで行うとgithubへのパスワードもコードとして埋め込みます。
しかしパスワードをそのまま埋め込むこと自体がパスワードの意味を無してしまうので、パスワードの代わりとしてアクセストークン(私はこれを入場チケットと新人には教えてます)を発行し、それをコードに埋め込みます。
こちらも他の方を参考に発行してみてください。
githubアクセストークンの作成
最後にコードの履歴を保存するリポジトリを作成します。
インフラをメインにしている人はリポジトリの単位をどうしたらいいのか分かりずらいですが、基本はプロジェクト単位、運用単位で行う事をお勧めします。
タスク単位まで分割してしまうとちょっと切替が面倒かなとは思いますが厳密に分けたい場合はタスク単位でも良いかもしれません。その場合はjupyter notebook x ansibleのコンテナもリポジトリ単位で作成しておかないとミスが発生しやすくなります。
juypter notebook と AnsibleのDockerfile作成
ではgithubの準備も出来たので、次にはjupyter notebookとAnsible そしてgitが使えるコンテナを作成します。
っとそのまえにdockerとdocker-composeもインストールしていない場合は事前にインストールしてください。
手順はこちらを参考にしてください。
dockerのインストール
私は検証環境なのでrootでやってしまいますが、個人アカウントを使う場合はインストール後にdockerグループに追加してください。
dockerのインストール
docker-composeのインストール
sudo usermod -aG docker ユーザ名
ではようやく本題のjupyter notebook , ansible , gitがインストールされたイメージファイルを作成します。
ファイルの作成場所は共有のワークスペースに保存してください。
私の場合はNFSで共有して居る/shareなどに、githubのプロジェクト名と同じ名前のサブフォルダを作成し、さらにdockerfiles/[イメージ名]の下にDockerfileをおきます。
例:/share/PJ_OpenHPC_test/dockerfiles/jupyter_ansible
下記のDockerfileではツールのインストール以外に、作業ディレクトリ(カレントディレクトリ)に/workapceを指定しjupyterをどこからでも接続して良い設定で起動するようにしています
FROM python:3.9-slim
RUN apt-get update && \
apt-get install -y sshpass openssh-client git && \
apt-get clean
RUN pip install ansible
RUN pip install notebook
WORKDIR /workspace
CMD ["jupyter", "notebook", "--ip=0.0.0.0", "--no-browser", "--allow-root"]
以下のケースではDockerfileという名前のファイルに構成を記述しないとイメージファイルを作成出来ないので注意してください。-f オプションを使ってファイル名を指定することで任意のファイル名でbuild出来ます。
このコマンドでコンテナイメージファイルを作成します。
docker build -t jupyter_ansible:latest .
# Dockerfileではなく任意のファイル名にする場合は
# docker build -t jupyter_ansible:latest -f <任意のファイル名>
コンテナを起動する際の物理ボリュームとコンテナボリュームのマッピングや、ポートのマッピングなどはdocker-compose.ymlに記述します。<ユーザ名>部分は分かりやすい名前に置き換えてください。別に名前でなくても問題ないです。
version: '3'
services:
ansible-jupyter:
image: ansible-jupyter-container:latest
ports:
- "8888:8888"
volumes:
- /share/PJ_jupyter_ansible_test:/workspace
- /root/.ssh:/root/.ssh
environment:
- JUPYTER_TOKEN=<ユーザ名>
command: >
bash -c "jupyter notebook --ip=0.0.0.0 --no-browser --allow-root --NotebookApp.token=${JUPYTER_TOKEN}"
docker composeもdocker-compose.yml以外でマッピング情報を記載するときは-fオプションを指定してください。
docker-compose up -d
# docker-compose.yml以外の名前で作成した場合は
# docker-compose up -d -f <任意のファイル名>
まずはこの状態で、起動確認をします。
ブラウザからhttp://:8888/?token=<ユーザ名>
起動出来ると下図のような画面が出ます。右端の「New」をクリックし「Python 3(pykernel)」を選択してください。
そうすると、新しいnotebookファイルが作成されます。
jupyter notebookの使い方は他のブログを参照してください。
下図にあるとおり、イメージファイル作成時に指定したツールとカレントディレクトリが有効かをチェックします。これでエラーがなければ完了です。
vscodeの準備
最終的には、VS CODE側からSSH接続でファイルにアクセスし、jupyter notebookにはWEB:8888ポートから接続する構成になります。
ややこしい構成にしていますが、作業端末側にファイルを分散させずホスト側でファイルを管理してかったからという理由になります。(強いて言えばやってみたかった)
複数人で一つのプロジェクトをメンテナンスする場合はこの構成は良くないので、
シンプルにVS CODEからSSH接続せず作業端末側でgithubからリポジトリのコピー(クローン)を作成した上で、実行のみJupyter経由でさせた方が良いです。
では具体的なインストール方法はまた他のブログを頼って省略します。まずはVS CODE自体のインストール自体はすんなり入ります。
私は普段m1macを使っていますが一般的にはwindows10/11を使っている方が多いと思いますので、下記サイトを参考にインストールをお願いします。
VS Codeから参照するipynbファイルなどはコンテナを動かしているホストに置いておき、
githubと連携したいので、vscodeからリモート接続するためのRemote SSHをインストールします。
下記サイトを参考に追加をお願いします。
今回はややこしい構成の方としてSSH接続を行い、ファイルはホスト側に置きます。
左の赤枠のアイコンをクリックすると下図のようにSSHの画面になりますカーソルを合わせると右側に「+」印がでますのでクリックします。
remoto sssh
普通のSSH接続をします。 私の場合は秘密鍵をed25519を事前に作成し、ホストに配布しているので指定しています。ここらへんは、一般的にはRSAで作る人も多いようです。
ここは省略しますが、後日カギの作成方法は別途掲載します。
設定をドコに保存するか聞かれている画面です。私はm1macなので下図みたいになります。
(保存先は個人にしています)
そうすると左側にホストが追加されます。赤枠で囲まれたアイコンをクリックして別ウィンドウでVS CODEを立ち上げます。
途中でパスワードを聞かれます(上に表示されてますのでお見逃し無く)
接続出来ると下図のように新しいVS CODEが立ち上がります。
次に作業フォルダへ 場所はプロジェクトディレクトリになります。
今回の例で言えば/workspace/PJ_jupyter_ansible_testになります。
下図の通りチェックボックスをONにして、「はい、作成者を信頼します」をクリックしてください
そうすると、フォルダが下図のように表示されます。(現在からっぽ)
一つ忘れてましたマーケットプレイスでjupyterの拡張モジュールを入れてください。
ようやくjupyterに接続する準備が整ったので、下図のように選択してJupyterに接続します。
下図のように Create:New Jupyter Notebookを選択してください。
画面が開いたら右端にある「カーネルを検出する」をクリックしてください。
Jupyter サーバーの選択では、ブラウザで確かめたときと同じURLを入れます。
http://<ホストのIP>:8888/?token=<ユーザ名>
ここでおすすめを選ぶとうまく実行できないので、すでにブラウザから開いていたファイルを指定してください。ここだと真ん中の「Python 3 (ipykernel)(Untitled.ipynb)」になります。
起動後は下図のようにgithubに接続し、フォルダを作成。
そしてDockerfile,docker-compose.ymlをコピーしておきます。
ちなみにこのノートブックをgithubにアップロードは出来ません。アクセストークンが含まれているとポリシーエラーとなってgithubにプッシュ出来なくなりますので気をつけてください。
slurmワーカーノード用のplaybook作成
長かったですが、ようやくここから本題です。
PJ_jupyter_ansible_testの直下に以前buildしたパッケージをコピーします。
slurmのパッケージ作成はこちらを見てください
cd
cp -r rpmbuild /workspace/PJ_jupyter_ansible
また配布先のワーカーノード(slurm01 rockylinux8.8)には、コンテナからSSH接続出来るように既にある秘密鍵と公開鍵をコンテナにコピーして置きます。その上でコンテナ内から一度実行出来るか確認をして置きます。
#今回であればplaybooks/etc/に公開鍵と秘密鍵を入れて置いてください。
ssh-copy-id -i id_ed25519.pub root@<ホストIP>
ssh -i id_ed25519 root@<ホストIP>
github copilotをインストールしているとjupyter notebook上からcopilotを利用出来ます。
私自身の使い方は全部一気に指示を書くのでは無くSTEP BY STEPでちょっとずつ追加しながら作成していきます。
chatgpt/copilotで作成することで、動くplaybookは作成出来ます。しかし変更
には弱いです。
リファクタリング的な考えで、訪れるか分からない将来を見越して余計な設計するよりも、ちょっとずつ変更に強いplaybookを作っていくことをお勧めします。
今回は取り上げないですが、変更がちょこちょこ入るようなサービスがある場合playbookファイルをサービス単位に分割して置くやり方も一つの方法です。
---
- name: Manage slurm nodes
hosts: slurm
become: true # root権限で実行
tasks:
- name: Update all packages
dnf:
name: '*'
state: latest
update_cache: true
- name: Upgrade nfs-utils to the latest version
dnf:
name: nfs-utils
state: latest
- name: Install NIS and related packages
dnf:
name:
- ypbind
- rpcbind
- oddjob-mkhomedir
state: present
- name: Set NIS domain name
command: ypdomainname lmnis
- name: Add NISDOMAIN to /etc/sysconfig/network
lineinfile:
path: /etc/sysconfig/network
line: "NISDOMAIN=lmnis"
create: yes
- name: Add NIS server to /etc/yp.conf
lineinfile:
path: /etc/yp.conf
line: "domain lmnis server beegfs01"
create: yes
- name: Select NIS profile with authselect
command: authselect select nis --force
- name: Enable mkhomedir with authselect
command: authselect enable-feature with-mkhomedir
- name: Set SELinux boolean nis_enabled to on
seboolean:
name: nis_enabled
state: true
persistent: yes
when: ansible_facts['selinux']['status'] == 'enabled'
- name: Enable and start NIS-related services
systemd:
name: "{{ item }}"
enabled: true
state: started
loop:
- rpcbind
- ypbind
- nis-domainname
- oddjobd
- name: Disable and stop firewalld
systemd:
name: firewalld
enabled: false
state: stopped
- name: Disable SELinux
selinux:
state: disabled
- name: Reboot the system to apply SELinux changes
reboot:
msg: "Rebooting to apply SELinux changes"
connect_timeout: 5
reboot_timeout: 300
when: ansible_facts['selinux']['status'] == 'enabled'
- name: Install required packages
dnf:
name:
- ncurses-devel
- openssl-devel
- gcc-c++
- gcc-gfortran
- coreutils
state: present
- name: Create /etc/profile.d/openmpi.sh
copy:
dest: /etc/profile.d/openmpi.sh
content: |
export PATH=$PATH:/opt/openmpi-5.0.3/bin
export MANPATH=$MANPATH:/opt/openmpi-5.0.3/man
owner: root
group: root
mode: '0644'
- name: Add entries to /etc/hosts
blockinfile:
path: /etc/hosts
block: |
<ホストのIP> beegfs01
<ホストのIP> master01
<ホストのIP> slurm01
- name: Add entry to /etc/fstab
blockinfile:
path: /etc/fstab
block: |
beegfs01:/share /share nfs defaults 0 0
- name: Create /share directory
file:
path: /share
state: directory
owner: root
group: root
mode: '0755'
- name: Mount /share
mount:
path: /share
src: beegfs01:/share
fstype: nfs
opts: defaults
state: mounted
- name: Create symbolic link /workspace -> /share
file:
src: /share
dest: /workspace
state: link
- name: Remove tmux temporarily to resolve libevent dependency
dnf:
name: tmux
state: absent
- name: Install libevent RPM packages from local folder
yum:
name:
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/libevent-2.1.12-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/libevent-debuginfo-2.1.12-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/libevent-debugsource-2.1.12-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/hwloc-2.10.0-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/hwloc-debuginfo-2.10.0-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/hwloc-debugsource-2.10.0-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/pmix-4.2.9-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/openmpi-5.0.5-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/openmpi-debuginfo-5.0.5-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/openmpi-debugsource-5.0.5-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/ucx-1.17.0-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/ucx-debuginfo-1.17.0-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/ucx-debugsource-1.17.0-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/munge-0.5.15-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/munge-debuginfo-0.5.15-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/munge-debugsource-0.5.15-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/slurm-23.11.10-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/slurm-debuginfo-23.11.10-1.el8.x86_64.rpm
- /workspace/PJ_jupyter_ansible_test/rpmbuild/RPMS/x86_64/slurm-debugsource-23.11.10-1.el8.x86_64.rpm
state: present
disable_gpg_check: true
- name: Reinstall tmux
dnf:
name: tmux
state: present
VSCODEから実行するとこんな感じです。
〜〜〜〜〜〜 中略 〜〜〜〜〜〜〜
残作業
かなり長くなってしまったので、別のブログにplaybookファイルを分割して、
メンテナンス性を上げつつ、残りのサービス設定を次回行います。