AWX を AWX Operator でシングルノード K3s にホストする

やりたかったこと

AWX の 18.0 から、インストールには AWX Operator の利用、すなわち Kubernetes や OpenShift 上へのデプロイが推奨されるようになりました。それ以前のバージョンでは Docker Compose ベースのデプロイも選択肢として用意されていましたが、現在では(開発やテスト目的を除いて)非推奨になっています。

とはいえ、気軽に使いたいだけ、シングルノードの可用性で充分、などの軽めのユースケースに対しては、すでに Kubernetes を使っている環境でない限り、AWX のためだけに Kubernetes 環境を作る(そして運用していく)のも少々大袈裟で大変です。かといって、公式のインストール手順 で気軽に試せる手段として紹介されている minikube は、プロダクション用途が想定されていない開発や学習用のツールのため、AWX をもう一歩踏み込んで日常的に使っていきたい場合には、逆に少し心許ないところがあります。

そんなわけで、ふたつの選択肢の間でほどよく使えるように、

  • シングルノード
  • 外部からもアクセス できて
  • データも永続化 できて
  • それなりに手堅く 使っていける

感じのを、K3s で作ることにしました。

AWX on minikube, K3s, MicroK8s, or k0s

AWX を 公式のインストール手順 に従って minikube で構成した場合、実際に日常で使っていける AWX としての仕上がりを期待してしまうと、現実とは少しギャップが生じます。

minikube はもともとローカル環境での学習や開発を目的にしたツールのため、その目的に沿った利用であれば非常に軽量かつ快適に利用でき、事実、たいへん便利です。一方で、根本的にはプロダクション用途が想定されていないこともあり、外部からのアクセスやデータの永続性に関しては機能が限定的で、学習用途からもう一歩踏み込んだ利用には少し不安感が出てきます(オプションでコントロールは可能ですが、そもそものツールの仕様上、ネットワークが少し複雑になりがちです)。

対して K3s は、いわゆる Kubernetes の軽量版ディストリビューションのひとつで、IoT やエッジコンピューティングなどのユースケースではプロダクション用途も想定されています。元の Kubernetes と比較すると軽量化のために機能が削減されてはいますが、シングルノードでも構成でき、今回のように、

  • プロダクション用途でも使える
  • なるべく手軽(凝った使い方ができなくてもよい)に
  • すぐ使い始められる Kubernetes 環境

としても、とてもよい選択肢です。もちろんマルチノード構成も組めるので、さらに本格的な構成や様々な使い方にも対応できます。

類似のディストリビューションではほかにも MicroK8sk0s などがあります。どちらもシングルノードで動作させられますが、前者はフル機能な Kubernetes になるのでちょっと過剰、後者は日が浅いのでちょっと見送り…… な感じで、今回は K3s に落ち着いています。

目指す AWX の形

手軽とはいいつつまじめに使える状態には仕上げたいので、ほどよいバランスを目指して、今回の AWX は次のような要件で考えます。

  • 1 台の CentOS 8 上で動作する
  • HTTPS で外部からアクセスできる
  • データが永続化される
    • ローカルディスク上(hostPath)でよい
    • その代わり PV を消してもデータがホスト上に残ってほしい
  • パスワード類をあらかじめ指定できる
    • 全損しても元のデータと元のパスワードで作り直せばきれいに復元されてほしい
  • コンポーネントのバージョンを指定できる
    • 全損したときに任意のバージョンで復元できてほしい

まとめると、今回は データやパスワードのオーナシップを人間側に寄せる ことを要件にしています。パスワード類は事前に指定しなければランダム文字列で生成されますが、データをそのままに丸ごと作り直したい場合などに多少面倒になる(古い PostgreSQL の認証情報を別のシークレットで渡す必要がある)ので、決め打ちできるようにしてしまっています。

ただし、理想を言えばこの要件はむしろ悪手 で、データにしてもパスワードにしても、障害時のリカバリを含め、本来はなるべく人間の制御下からは離す方向で考えるほうが望ましいでしょう(後述しますが AWX Operator も組み込みでバックアップとリストアの仕組みが実装されているようで、それらを併用すれば人間の管理負荷も削減できそうです)。

ホストの準備と K3s の導入

今回は CentOS 8 をホストとして利用するため、ミニマム構成でインストールして適当に用意します。ハードウェアの最小要件は Ansible Tower のドキュメント では 2 CPU / 4 GB RAM ですが、今回は大きめに 4 CPU / 8 GB RAM にしています。

K3s のドキュメント に従って、Firewalld を無効化して停止します。

sudo systemctl disable firewalld --now

K3s のインストールスクリプトをダウンロードして実行します。kubectl 用に作成される設定ファイル(/etc/rancher/k3s/k3s.yaml)を作業用の一般ユーザからも読み取れるようにするため、ここでは --write-kubeconfig-mode 644 オプションを与えています(ドキュメントに記載 があります)。

curl -sfL https://get.k3s.io | sh -s - --write-kubeconfig-mode 644

完了すると、バイナリの導入のほか種々の設定が行われ、K3s が利用できる状態になります。Systemd にもサービス K3s が登録され、自動起動が有効化されます。また、kubectl コマンドも利用できるようになります(この kubectl の実体は、/usr/local/bin/k3s へのシンボリックリンクで、既定で /etc/rancher/k3s/k3s.yaml を読むなど、いくつかアップストリームのそれとは異なる独自の仕様を持っています)。

なお、--write-kubeconfig-mode 644 オプション付きで K3s を導入すると、ホストにログインできる誰でも管理権限で kubectl を叩けるようになります。操作ユーザを限定したいのであればこのオプションは使わず、導入後に /etc/rancher/k3s/k3s.yaml~/.kube/config にコピーして KUBECONFIG 環境変数で指定するなど、何らかの工夫が必要です。

AWX Operator の導入

K3s の導入が終われば、すでにシングルノードの Kubernetes が利用できる状態になっているので、ここからは AWX の導入です。

AWX のインストール手順 に従い、AWX Operator を構成します。ここでは執筆時点の最新の 0.10.0 を利用しています。なお、AWX Operator のバージョンは AWX のバージョンにも紐づく(異なる組み合わせでの利用はサポートしていない)ので、リストア目的で環境を再構築する場合は、この段階からバージョンを気にする必要があります。AWX Operator の 0.10.0 は、AWX の 19.2.0 に対応しているようです(installer ロールの変数ファイル から対応バージョンが読み取れます)。

kubectl apply -f https://raw.githubusercontent.com/ansible/awx-operator/0.10.0/deploy/awx-operator.yaml

これで、デフォルトのネームスペース(通常は default)に awx-operator がデプロイされます。

$ kubectl get deployment
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
awx-operator   1/1     1            1           30s

追記(2021/10/06):

AWX Operator の 0.14.0 から、AWX Operator のデプロイに make を使うようになりました。

# make のインストール
sudo dnf install -y git make

# リポジトリのクローンと特定のバージョンのチェックアウト
git clone https://github.com/ansible/awx-operator.git
cd awx-operator
git checkout 0.14.0

# AWX Operator が動作する名前空間の指定とデプロイ
export NAMESPACE=awx
make deploy

AWX のデプロイ

AWX Operator の導入でカスタムリソース AWX も利用可能になるので、雑に言えば、あとは AWX リソースのマニフェストを書いて kubectl apply すれば完成です。ミニマムでは、マニフェストは AWX Operator のドキュメント に記載のある数行だけです。

が、今回は前述の要件の通り少しカスタマイズを入れたいので、ドキュメント を参照しながらマニフェストに手を入れていきます。いくつかマニフェストファイルを作ってから、Kustomize でまとめて流す作戦で考えます。

詳解しているファイル群はまとめて GitHub 上のリポジトリ でも公開しています。

AWX リソースの準備(awx.yaml

AWX リソースのマニフェストを用意します。中で参照させているシークレットや PV、PVC については後述します。

---
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
  name: awx
spec:
  admin_user: admin
  admin_password_secret: awx-admin-password

  ingress_type: ingress
  ingress_tls_secret: awx-secret-tls
  hostname: awx.example.com

  postgres_configuration_secret: awx-postgres-configuration

  postgres_storage_class: awx-postgres-volume
  postgres_storage_requirements:
    requests:
      storage: 2Gi

  projects_persistence: true
  projects_existing_claim: awx-projects-claim

なお、このパラメータ名は AWX Operator の 0.10.0 準拠ですが、以前の 0.9.0 までは、パラメータ名にすべて tower_ が接頭辞としてついていました。古いバージョンを利用する場合は置き換えが必要です。

Namespace リソースの準備(namespace.yaml

作ったり消したりしやすくなるので、ネームスペースを用意します。

---
apiVersion: v1
kind: Namespace
metadata:
  name: awx

PersistentVolume リソースとディレクトリの準備(pv.yamlpvc.yaml

前述の awx.yaml で参照しているふたつの PV(PostgreSQL 用、プロジェクト用)と、そのうちプロジェクト用の PV のための PVC のマニフェストです。PostgreSQL 用の PVC は事前に定義できないようなので、クラス名で紐づけさせています。

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: awx-postgres-volume
spec:
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  capacity:
    storage: 2Gi
  storageClassName: awx-postgres-volume
  hostPath:
    path: /data/postgres

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: awx-projects-volume
spec:
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  capacity:
    storage: 2Gi
  storageClassName: awx-projects-volume
  hostPath:
    path: /data/projects
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: awx-projects-claim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 2Gi
  storageClassName: awx-projects-volume

併せて、PV 中で hostPath で指定しているディレクトリも作成します。プロジェクトディレクトリは UID が 1000awx ユーザが触ることになるので、その UID で作成します。

sudo mkdir -p /data/postgres
sudo mkdir -p /data/projects
sudo chown 1000:0 /data/projects

なお、K3s では Local Path Provisioner がデフォルトで動作しており、PV を手で用意しなくても PVC に対して動的に PV をプロビジョニングできますが、

  • persistentVolumeReclaimPolicyDelete になる(PV を消すと実データも消える)
  • accessModesReadWriteMany の PVC に対応できない
  • PV 名が予測できない(/var/lib/rancher/k3s/storage 下の GUID ベースのディレクトリとして作成される)

ので、今回は使っていません。

サーバ証明書の準備(tls.crttls.key

AWX のインタフェイスを Ingress で HTTPS 化するために、サーバ証明書を作ります。ここでは自己署名証明書を作っていますが、正規のものでももちろんよいです。

openssl コマンドで作成します。

AWX_HOST="awx.example.com"
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -out tls.crt -keyout tls.key -subj "/CN=${AWX_HOST}/O=${AWX_HOST}" -addext "subjectAltName = DNS:${AWX_HOST}"

Kustomize の準備(kustomization.yaml

ここまでで作った色々をまとめて放り込むために、kustomization.yaml を作成します。冒頭で namespace を指定して、そのネームスペース内に閉じ込めています。

また、awx.yaml 中で指定していたシークレットリソースもこの中で作成させます。PostgreSQL の認証情報と AWX の管理者の初期パスワードもここであらかじめ作成させます(ランダムでよければ当該ブロックは丸ごと削除できます)。

---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: awx

generatorOptions:
  disableNameSuffixHash: true

secretGenerator:
  - name: awx-secret-tls
    type: kubernetes.io/tls
    files:
      - tls.crt
      - tls.key

  - name: awx-postgres-configuration
    type: Opaque
    literals:
      - host=awx-postgres
      - port=5432
      - database=awx
      - username=awx
      - password=Ansible123!
      - type=managed

  - name: awx-admin-password
    type: Opaque
    literals:
      - password=Ansible123!

resources:
  - namespace.yaml
  - pv.yaml
  - pvc.yaml
  - awx.yaml

デプロイ

Kustomize を実行します。

$ kubectl apply -k .
namespace/awx created
secret/awx-admin-password created
secret/awx-postgres-configuration created
secret/awx-secret-tls created
awx.awx.ansible.com/awx created
persistentvolume/awx-postgres-volume created
persistentvolume/awx-projects-volume created
persistentvolumeclaim/awx-projects-claim created

うまくいけば AWX Operator の Pod のログに Ansible っぽいアレが出て完了します。

$ kubectl logs -f deployment/awx-operator
...
--------------------------- Ansible Task Status Event StdOut  -----------------
PLAY RECAP *********************************************************************
localhost                  : ok=51   changed=2    unreachable=0    failed=0    skipped=32   rescued=0    ignored=0
-------------------------------------------------------------------------------

追記(2021/10/06):

AWX Operator 0.14.0 以降では、ログは次のコマンドで確認できます。

kubectl -n awx logs -f deployments/awx-operator-controller-manager -c manager

awx ネームスペースにいろいろできあがります。

$ kubectl get all -n awx
NAME                      READY   STATUS    RESTARTS   AGE
pod/awx-postgres-0        1/1     Running   0          4m30s
pod/awx-b47fd55cd-d8dqj   4/4     Running   0          4m22s

NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/awx-postgres   ClusterIP   None            <none>        5432/TCP   4m30s
service/awx-service    ClusterIP   10.43.159.187   <none>        80/TCP     4m24s

NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/awx   1/1     1            1           4m22s

NAME                            DESIRED   CURRENT   READY   AGE
replicaset.apps/awx-b47fd55cd   1         1         1       4m22s

NAME                            READY   AGE
statefulset.apps/awx-postgres   1/1     4m30s

DNS か hosts ファイルでホスト名の名前解決をできるようにして、ブラウザで HTTPS でアクセスすると、ログイン画面が表示され、ユーザ名 admin と指定したパスワードでログインできます。

楽しいですね。

管理用パスワードを事前に指定しなかった場合は、シークレットの値をデコードして取得できます。

$ kubectl get secret awx-admin-password -o jsonpath="{.data.password}" -n awx | base64 --decode
Mp1UtHgMqxRnDGzcz0fy3e4RBePgL96S

簡易バックアップと簡易リストア

AWX Operator の 0.10.0 からは、組み込みで バックアップリストア の機能が追加されています。必要なファイル一式を少しの操作でひとつのフォルダにまとめてくれるうえ、リストアも全自動なので、バックアップやリストアを行いたいときは基本的にはこれの利用がおすすめです。使い方は別エントリで紹介しています。

何らかの理由で AWX Operator の機能を使わずにすべてを手作業で行いたい場合は、

  • デプロイに利用したファイル一式
    • 証明書と秘密鍵を含む
  • 二つの PV のまるごとの中身
    • /data/postgres/data/projects
  • 自動生成されたシークレットキー
    • <インスタンス名>-secret-key で自動作成されるシークレット
    • kubectl -n awx get secret awx-secret-key -o yaml > secret_key.yaml などで保存しておく
    • AWX 内で /etc/tower/SECRET_KEY として利用され、DB 内の機微情報(Credential など)の暗号化や複合化に使われる

の保持を考えます。これらが維持できれば、極論、K3s をアンインストールしても、元のファイルを元のパスに置いて作り直せば同じ環境に戻せます。

# シークレットキーの保存
kubectl -n awx get secret awx-secret-key -o yaml > secret_key.yaml

# 環境全体のアンインストール
/usr/local/bin/k3s-uninstall.sh

# 環境全体の再インストール・再デプロイ
curl -sfL https://get.k3s.io | sh -s - --write-kubeconfig-mode 644
kubectl apply -f https://raw.githubusercontent.com/ansible/awx-operator/0.10.0/deploy/awx-operator.yaml
kubectl create namespace awx
kubectl apply -f secret_key.yaml
kubectl apply -k .

PostgreSQL の PV は、静止点が取りにくければ、素直に pg_dump でもよいですね。

kubectl exec awx-postgres-0 -n awx -- sh -c 'pg_dump -c --if-exists -U $POSTGRES_USER' > dump.sql

リストアするには、awx リソースの replicas0 にして、PostgreSQL だけを残して AWX を止めてからがよさそうです。

kubectl patch awx awx -n awx -p '{"spec": {"replicas": 0}}' --type=merge
kubectl exec -i awx-postgres-0 -n awx -- sh -c 'psql -U $POSTGRES_USER' < dump.sql
kubectl patch awx awx -n awx -p '{"spec": {"replicas": 1}}' --type=merge

まとめ

軽量 Kubernetes ディストリビューションの K3s を使って AWX をホストする方法を紹介しました。パスワードを固定してしまうのは少し乱暴ではありますが、その分少しだけ取り回しはしやすくなっています。

前述の通り、0.10.0 から追加された AWX Operator 組み込みのバックアップとリストアは、別のエントリ で取り扱っています。手作業よりは圧倒的にラクなので、併せて使うと便利です。

@kurokobo

くろいです。ギターアンサンブルやら音響やらがフィールドの IT やさんなアルトギター弾き。たまこう 48 期ぎたさん、SFC '07 おんぞう、新日本ギターアンサンブル、Rubinetto。今は野良気味。

「AWX を AWX Operator でシングルノード K3s にホストする」への2件のフィードバック

  1. Hi,

    Thanks for this tutorial, I’d managed to have AWX ingress run perfectly – I have some clarifications on the volumes. I can see that the PV are both status “bound” using “k3s kubectl get pv” – however, I can’t see any PVCs using the “k3s kubectl get pvc” it only says “No resources found in default namespace”. Tried to run sample AWX playbook, whenever I tried to save files onto the “hosts: localhost” – it gives me a denied permission error on the file path: /data/projects.

    Appreciate your response on this.

    thanks.

  2. Wow sry for the late reply. The PVC is the namespace-dependent resource, therefore you should specify correct namespace by “-n awx” or “–namespace=awx” for “kubectl” command, e.g. “k3s kubectl -n awx get pvc”.

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です