てくなべ (tekunabe)

ansible / network automation / 学習メモ

[AWX/Ansible] 開発用に AWX の UI と API の環境を別々につくってみた

はじめに

AWX のユーザーインターフェイスを少し修正したい機会があって、どうやって試す環境を作れるのだろうと調べていました。

このあたりは公式ドキュメントがしっかりしていて、以下のページが非常に参考になりました。

私の環境で、おそらく必要最低限の手順でやってみたので、まとめました。

大きく分けて API 側(バックエンド)と、UI 側(フロントエンド)の環境を作っていきます。

全体

今回いじりたいのは UI 側だけです。現状 React 17 系で作られているようです。

環境

私の環境は以下のとおりです。開発イメージのビルドなどする際に ansible が利用されるので ansible もインストールしておきます。

% docker --version
Docker version 20.10.17, build 100c701

% ansible --version
ansible [core 2.13.2]

% node --version
v17.5.0

% npm --version
8.4.1

要件については、それぞれ以下のドキュメントにに記載があります。

AWX は開発スピードが早いため、本ブログに書いたことが通用しなくなっているかもしれません。最新は公式ドキュメントを参照してください。

準備

まず GitHub の awx リポジトリから git clone します。

今回はあとでプルリクを出す想定なこともあって、先に forks してそのリポジトリ clone しました。

% git clone https://github.com/akira6592/awx.git

(あとで気がついたのですが、AWX 16 の頃に fork したままで、 upstream を fetch 忘れていました。以降、手順は変わらないと思いますが最新版とログなどが微妙に異なるかもしれません。)

awx というディレクトリが作成されるので移動します。

% cd awx

以降、このディレクトリで作業します。

API 側 (バックエンド)

バックエンドとして API の口を docker compose で作成します。

基本的には公式の案内通りにやっていきます。

ビルド

まずはビルド。make コマンド経由でできるようになってます。いきなり Playbook が実行されます。

% make docker-compose-build
ansible-playbook tools/ansible/dockerfile.yml -e build_dev=True -e receptor_image=quay.io/ansible/receptor:devel
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
[WARNING]: An error occurred while calling ansible.utils.display.initialize_locale (unsupported locale setting). This may result in incorrectly calculated text widths that can cause Display to print
incorrect line lengths

PLAY [Render AWX Dockerfile and sources] *******************************************************************************************************************************************************************

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

TASK [dockerfile : Create _build directory] ****************************************************************************************************************************************************************
changed: [localhost]

TASK [dockerfile : Render supervisor configs] **************************************************************************************************************************************************************
changed: [localhost] => (item=supervisor.conf)
changed: [localhost] => (item=supervisor_task.conf)

TASK [dockerfile : Render Dockerfile] **********************************************************************************************************************************************************************
changed: [localhost]

PLAY RECAP *************************************************************************************************************************************************************************************************
localhost                  : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

DOCKER_BUILDKIT=1 docker build -t ghcr.io/ansible/awx_devel:devel \
        --build-arg BUILDKIT_INLINE_CACHE=1 \
        --cache-from=ghcr.io/ansible/awx_devel:devel .
[+] Building 120.1s (51/51) FINISHED
...(略)...
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
%

この時点では、イメージは以下の1つがありました。

% docker images
REPOSITORY                  TAG       IMAGE ID       CREATED        SIZE
ghcr.io/ansible/awx_devel   devel     c7035a7d68e8   15 hours ago   1.83GB

起動

続いて起動です。これも make 経由です。

Playbpook で docker-compose.yml を生成して、docker-compose up する流れのようです。

https://github.com/ansible/awx/blob/devel/tools/docker-compose/README.md#start-the-containers

数分かかります。途中 Admin password: というログの後に、 admin ユーザーの自動生成パスワードが表示されるのでメモっておきます。

(2022/08/27 後日のPR #12753#12755 あたりで、admin のパスワードを自分で指定できるようになったかもしれません。試せてはいません。)

% make docker-compose
ansible-playbook -i tools/docker-compose/inventory tools/docker-compose/ansible/sources.yml \
        -e awx_image=ghcr.io/ansible/awx_devel \
        -e awx_image_tag=devel \
        -e receptor_image=quay.io/ansible/receptor:devel \
        -e control_plane_node_count=1 \
        -e execution_node_count=2 \
        -e minikube_container_group=false \
        -e enable_keycloak=false \
        -e enable_ldap=false \
        -e enable_splunk=false \
        -e enable_prometheus=false \
        -e enable_grafana=false
[WARNING]: An error occurred while calling ansible.utils.display.initialize_locale (unsupported locale setting). This may result in incorrectly calculated text widths that can cause Display to print
incorrect line lengths

PLAY [Render AWX Dockerfile and sources] *******************************************************************************************************************************************************************

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

TASK [sources : Create _sources directories] ***************************************************************************************************************************************************************
changed: [localhost] => (item=secrets)
changed: [localhost] => (item=receptor)

TASK [sources : Detect secrets] ****************************************************************************************************************************************************************************
ok: [localhost] => (item=pg_password)
ok: [localhost] => (item=secret_key)
ok: [localhost] => (item=broadcast_websocket_secret)

...(略)...

tools_awx_1     | Superuser created successfully.
tools_awx_1     | Admin password: (ここにadminのパスワードが表示される)
tools_receptor_hop | WARNING 2022/08/25 11:37:01 Backend connection failed (will retry): dial tcp 172.18.0.4:2222: connect: connection refused
tools_awx_1     | System check identified some issues:
tools_awx_1     |

...(略)...

数分立つと、以下のログくらいしか出力されない、落ち着いた状態になりました。

tools_redis_1   | 1:M 25 Aug 2022 12:30:20.082 * 100 changes in 300 seconds. Saving...
tools_redis_1   | 1:M 25 Aug 2022 12:30:20.093 * Background saving started by pid 16
tools_redis_1   | 16:C 25 Aug 2022 12:30:20.117 * DB saved on disk
tools_redis_1   | 16:C 25 Aug 2022 12:30:20.118 * Fork CoW for RDB: current 0 MB, peak 0 MB, average 0 MB
tools_redis_1   | 1:M 25 Aug 2022 12:30:20.199 * Background saving terminated with success

フォアグラウンドで実行されてるので、このターミナルはそのままにしておきます。

なお、デフォルトでは git としてのブランチ名と同じ名前の ghcr.io/ansible/awx_devel:ブランチ名 を利用するようです。

もし、

Pulling awx_1 (ghcr.io/ansible/awx_devel:fix-awsome)...
ERROR: manifest unknown
make: *** [docker-compose] Error 1

のような、エラーになったら、COMPOSE_TAG=devel make docker-compose のように、存在するイメージのタグ名を明示指定します(参考)。

別ターミナルを立ち上げて・・・イメージ一覧を確認すると以下のようになりました。

% docker images
REPOSITORY                  TAG       IMAGE ID       CREATED        SIZE
ghcr.io/ansible/awx_devel   devel     c7035a7d68e8   15 hours ago   1.83GB
quay.io/ansible/receptor    devel     981db0fc0f90   40 hours ago   233MB
postgres                    12        f2f1f275f1a1   47 hours ago   373MB
redis                       latest    dc7b40a0b05d   2 days ago     117MB

この時点で、docker-compose ps の表示は以下のようになりました。docker-compose.yml が特定のディレクトリに生成されるので、-f で指定するか、そこまで移動します。

% docker-compose -f tools/docker-compose/_sources/docker-compose.yml ps
       Name                     Command               State                                                                       Ports
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
tools_awx_1          /entrypoint.sh launch_awx.sh     Up      22/tcp, 0.0.0.0:2222->2222/tcp, 0.0.0.0:3000->3001/tcp, 0.0.0.0:6899->6899/tcp, 0.0.0.0:7899->7899/tcp, 0.0.0.0:7900->7900/tcp,
                                                              0.0.0.0:7901->7901/tcp, 0.0.0.0:7902->7902/tcp, 0.0.0.0:7903->7903/tcp, 0.0.0.0:7904->7904/tcp, 0.0.0.0:7905->7905/tcp,
                                                              0.0.0.0:7906->7906/tcp, 0.0.0.0:7907->7907/tcp, 0.0.0.0:7908->7908/tcp, 0.0.0.0:7909->7909/tcp, 0.0.0.0:7910->7910/tcp,
                                                              0.0.0.0:7911->7911/tcp, 0.0.0.0:7912->7912/tcp, 0.0.0.0:7913->7913/tcp, 0.0.0.0:7914->7914/tcp, 0.0.0.0:7915->7915/tcp,
                                                              0.0.0.0:7916->7916/tcp, 0.0.0.0:7917->7917/tcp, 0.0.0.0:7918->7918/tcp, 0.0.0.0:7919->7919/tcp, 0.0.0.0:7920->7920/tcp,
                                                              0.0.0.0:7921->7921/tcp, 0.0.0.0:7922->7922/tcp, 0.0.0.0:7923->7923/tcp, 0.0.0.0:7924->7924/tcp, 0.0.0.0:7925->7925/tcp,
                                                              0.0.0.0:7926->7926/tcp, 0.0.0.0:7927->7927/tcp, 0.0.0.0:7928->7928/tcp, 0.0.0.0:7929->7929/tcp, 0.0.0.0:7930->7930/tcp,
                                                              0.0.0.0:7931->7931/tcp, 0.0.0.0:7932->7932/tcp, 0.0.0.0:7933->7933/tcp, 0.0.0.0:7934->7934/tcp, 0.0.0.0:7935->7935/tcp,
                                                              0.0.0.0:7936->7936/tcp, 0.0.0.0:7937->7937/tcp, 0.0.0.0:7938->7938/tcp, 0.0.0.0:7939->7939/tcp, 0.0.0.0:7940->7940/tcp,
                                                              0.0.0.0:7941->7941/tcp, 0.0.0.0:7942->7942/tcp, 0.0.0.0:7943->7943/tcp, 0.0.0.0:7944->7944/tcp, 0.0.0.0:7945->7945/tcp,
                                                              0.0.0.0:7946->7946/tcp, 0.0.0.0:7947->7947/tcp, 0.0.0.0:7948->7948/tcp, 0.0.0.0:7949->7949/tcp, 0.0.0.0:7950->7950/tcp,
                                                              0.0.0.0:7951->7951/tcp, 0.0.0.0:7952->7952/tcp, 0.0.0.0:7953->7953/tcp, 0.0.0.0:7954->7954/tcp, 0.0.0.0:7955->7955/tcp,
                                                              0.0.0.0:7956->7956/tcp, 0.0.0.0:7957->7957/tcp, 0.0.0.0:7958->7958/tcp, 0.0.0.0:7959->7959/tcp, 0.0.0.0:7960->7960/tcp,
                                                              0.0.0.0:7961->7961/tcp, 0.0.0.0:7962->7962/tcp, 0.0.0.0:7963->7963/tcp, 0.0.0.0:7964->7964/tcp, 0.0.0.0:7965->7965/tcp,
                                                              0.0.0.0:7966->7966/tcp, 0.0.0.0:7967->7967/tcp, 0.0.0.0:7968->7968/tcp, 0.0.0.0:7969->7969/tcp, 0.0.0.0:7970->7970/tcp,
                                                              0.0.0.0:7971->7971/tcp, 0.0.0.0:7972->7972/tcp, 0.0.0.0:7973->7973/tcp, 0.0.0.0:7974->7974/tcp, 0.0.0.0:7975->7975/tcp,
                                                              0.0.0.0:7976->7976/tcp, 0.0.0.0:7977->7977/tcp, 0.0.0.0:7978->7978/tcp, 0.0.0.0:7979->7979/tcp, 0.0.0.0:7980->7980/tcp,
                                                              0.0.0.0:7981->7981/tcp, 0.0.0.0:7982->7982/tcp, 0.0.0.0:7983->7983/tcp, 0.0.0.0:7984->7984/tcp, 0.0.0.0:7985->7985/tcp,
                                                              0.0.0.0:7986->7986/tcp, 0.0.0.0:7987->7987/tcp, 0.0.0.0:7988->7988/tcp, 0.0.0.0:7989->7989/tcp, 0.0.0.0:7990->7990/tcp,
                                                              0.0.0.0:7991->7991/tcp, 0.0.0.0:7992->7992/tcp, 0.0.0.0:7993->7993/tcp, 0.0.0.0:7994->7994/tcp, 0.0.0.0:7995->7995/tcp,
                                                              0.0.0.0:7996->7996/tcp, 0.0.0.0:7997->7997/tcp, 0.0.0.0:7998->7998/tcp, 0.0.0.0:7999->7999/tcp, 0.0.0.0:8013->8013/tcp,
                                                              0.0.0.0:8043->8043/tcp, 0.0.0.0:8080->8080/tcp, 0.0.0.0:8888->8888/tcp
tools_postgres_1     docker-entrypoint.sh postg ...   Up      5432/tcp
tools_receptor_1     /entrypoint.sh receptor -- ...   Up      22/tcp, 8013/tcp, 8043/tcp, 8080/tcp
tools_receptor_2     /entrypoint.sh receptor -- ...   Up      22/tcp, 8013/tcp, 8043/tcp, 8080/tcp
tools_receptor_hop   /usr/local/bin/dumb-init - ...   Up      0.0.0.0:5555->5555/tcp, 7323/tcp
tools_redis_1        redis-server /usr/local/et ...   Up      6379/tcp
%

パスワードについて

もし、パスワードが分からなくなってしまった場合でも、以下のコマンドで新しい管理者ユーザーを作成できます。

% docker exec -ti tools_awx_1 awx-manage createsuperuser

API の確認

できたかなー、ということで、さっそく API を叩きます。ポートは 8043 です。

試すエンドポイントは、認証いらずで便利な ping を指定します。

% curl -ks https://localhost:8043/api/v2/ping/ | jq .
{
  "ha": false,
  "version": "16.0.1.dev4198+g4fbf5e9e2f",
  "active_node": "awx_1",
  "install_uuid": "6d9c484a-47f9-45c5-ac97-71c53b889792",
  "instances": [
    {
      "node": "awx_1",
      "node_type": "hybrid",
      "uuid": "00000000-0000-0000-0000-000000000000",
      "heartbeat": "2022-08-25T12:15:32.388568Z",
      "capacity": 59,
      "version": "16.0.1.dev4198+g4fbf5e9e2f"
    },
    {
      "node": "receptor-1",
      "node_type": "execution",
      "uuid": "e4b8a674-eaf5-48e6-804c-32d448d67b6e",
      "heartbeat": "2022-08-25T12:15:27.896150Z",
      "capacity": 59,
      "version": "ansible-runner-2.1.1.dev58"
    },
    ...(略)...
}
%

無事に返ってきました。これで API 側はできたようです。

(あとで色々試していたら "version": "21.5.1.dev3+g4fbf5e9e2f" と最新になりました。ずっと ghcr.io/ansible/awx_devel:devel を利用してたつもりではあるのですが・・)

コンテナ側の UI は特になにもせず

この時点で、localhost:8043 の通常の UI を見ても、以下のような文字列しか返ってきません。

<% if (process.env.NODE_ENV === 'production') { %> <% } %> <% if (process.env.NODE_ENV === 'production') { %> <% } else { %> <% } %> <% if (process.env.NODE_ENV === 'production') { %>

docker exec tools_awx_1 make clean-ui ui-devel を実行すれば、コンテナ側の UI も構築されるようです(うまくいくまで試せていません)。ですが、今回は UI はホストマシンに構築していじりたいので、コンテナ側はこのままにしておきます。

■ UI 側 (フロントエンド)

続いて、UI 側です。API 側を起動したターミナルはそのままにして、別のターミナルで作業します。

パッケージのインストール

npm で必要なパッケージをインストールします。コマンドはこちらに記載されています。

% npm --prefix=awx/ui install

UI 側の起動

次に起動します。

% npm --prefix=awx/ui start

> prestart
> lingui compile

Compiling message catalogs…
Done!

> start
> GENERATE_SOURCEMAP=false ESLINT_NO_DEV_ERRORS=true PORT=3001 HTTPS=true DANGEROUSLY_DISABLE_HOST_CHECK=true react-scripts start

Browserslist: caniuse-lite is outdated. Please run:
  npx browserslist@latest --update-db
  Why you should do it regularly: https://github.com/browserslist/browserslist#browsers-data-updating
(node:56871) [DEP_WEBPACK_DEV_SERVER_HTTPS] DeprecationWarning: 'https' option is deprecated. Please use the 'server' option.
(Use `node --trace-deprecation ...` to show where the warning was created)
[HPM] Proxy created: /api,/websocket,/sso  -> https://localhost:8043
(node:56871) [DEP_WEBPACK_DEV_SERVER_ON_AFTER_SETUP_MIDDLEWARE] DeprecationWarning: 'onAfterSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
(node:56871) [DEP_WEBPACK_DEV_SERVER_ON_BEFORE_SETUP_MIDDLEWARE] DeprecationWarning: 'onBeforeSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
Starting the development server...        (ここで数分かかる)

You can now view ui in the browser.

  Local:            https://localhost:3001
  On Your Network:  https://192.168.1.135:3001

Note that the development build is not optimized.
To create a production build, use npm run build.

assets by path static/ 23.7 MiB
  assets by path static/js/*.js 19.9 MiB
    assets by chunk 2.83 MiB (id hint: vendors) 50 assets
    25 assets
  assets by path static/media/ 3.85 MiB
    assets by chunk 2.32 MiB (auxiliary name: main)
      assets by path static/media/*.woff2 1.22 MiB 38 assets
      assets by path static/media/*.woff 1.1 MiB 26 assets
    assets by path static/media/*.jpg 1.53 MiB 6 assets
    asset static/media/status-icon-sprite.4fee9fefc3971799d2dd.svg 3.44 KiB [emitted] [immutable] [from: node_modules/@patternfly/react-styles/css/assets/images/status-icon-sprite.svg] (auxiliary id hint: vendors)
asset asset-manifest.json 36 KiB [emitted]
asset index.html 1.27 KiB [emitted]
orphan modules 3.02 MiB [orphan] 2074 modules
runtime modules 59.8 KiB 30 modules
javascript modules 14.2 MiB
  modules by path ./node_modules/ 8.17 MiB 1821 modules
  modules by path ./src/ 6.02 MiB 971 modules
asset modules 8.32 KiB (javascript) 3.85 MiB (asset)
  modules by path ./node_modules/@patternfly/react-core/dist/styles/assets/fonts/ 2.54 KiB (javascript) 2.28 MiB (asset) 62 modules
  modules by path data:image/svg+xml;charset=utf8,%3Csvg xmlns=%27http://www.w3.org/2000/ 5.41 KiB 8 modules
  modules by path ./node_modules/@patternfly/react-styles/ 294 bytes (javascript) 1.54 MiB (asset) 7 modules
  modules by path ./node_modules/@patternfly/react-core/dist/styles/assets/pficon/ 84 bytes (javascript) 36.9 KiB (asset) 2 modules
json modules 30 KiB
  ./node_modules/entities/lib/maps/entities.json 28.4 KiB [built] [code generated]
  ./node_modules/entities/lib/maps/legacy.json 1.24 KiB [built] [code generated]
  ./node_modules/entities/lib/maps/xml.json 62 bytes [built] [code generated]
  ./node_modules/entities/lib/maps/decode.json 308 bytes [built] [code generated]
webpack 5.66.0 compiled successfully in 211217 ms
...(フォアグラウンド実行中)...

正常にアクセスできまで数分間かかりました。

https://localhost:3001/ を開いて、ログイン画面が表示され、admin と先程メモったパスワードでログインできれば一区切りです。

ジャガイモがお出迎え

ログインできた

いつものでデモデータもはいっていました。

admin のパスワードは、覚えやすく安全なものに変えちゃったほうがいいと思います。

なお、途中のログにあるように、デフォルトでは https://localhost:8043API を叩きに行くようです。今回は、docker compose で構築した API の口が https://localhost:8043 で同じなので、デフォルトのままでOKです。

もし、別のポートや別のサーバーを相手にしたい場合は、ドキュメントに記載があるように環境変数 TARGET をセットして起動します。(おためし

# 別のサーバーのAPIを使う例
TARGET='https://awx.local:8043' npm --prefix awx/ui start

UI側と機能の整合性があるのであれば、今回用にAPI側をわざわざ作るのではなく、既存のものを使ってもよいかと思います。

UI のコードがあるパス

ui/src ディレクトリ配下に、コンポーネントや hook のコードがあります。このあたりを触っていけばいいわけですね。

ということで、ほんとにやりたかったちょっとした修正にとりかかりました。

おわりに

API 側をコンテナで用意し、ホスト側にUIを用意しました。

AWX の画面表示が、どのような仕組みで成り立っているのか少し理解するきっかけになりました。