ラズパイにAWXとGitLabをインストールするPlaybookを作る
ITソリューション部の水谷です。
先日弊社のアドベントカレンダーに「ラズパイにAWXとGitLabを入れて快適な自宅Playbook環境を作る」というタイトルの記事 を投稿しました。
その記事では、附属品込みで2万円程度で購入した Raspberry Pi 5(メモリ8GB)にAWXとGitLab CEをインストールして、自宅用Playbook実行、ソースコード管理、CI/CD実行環境を手動で構築した模様をお伝えしました。
今回はその記事の続編として、すべての工程をPlaybook化してみたいと思います。
なお、このPlaybookは本記事執筆時点では期待通り動作していますが、今後(依存しているライブラリがバージョンアップした際に互換性がなくなる等の理由で)動作しなくなる可能性がありますので、その点はご了承ください。
※PlaybookはGitHubからCloneすることができます。
Playbook化する理由
本題に入る前に、少し「なぜPlaybook化しようと思ったのか」について書いておきたいと思います。
かなり個人的な考えも含まれますが、Playbook化した理由は以下のようなものです。
今後何回か(設定を少し変えて)構築する可能性があるので、自動化しておきたい
Playbookを見れば、後からどんな設定を行ったのかが分かるので、仕様書代わりに残しておきたい
ちょうどいいサイズと難易度なので、Playbookを書く良い練習になる
自動化を通じてAWXやGitLabを少し勉強できる
最後の点については、特にGitLabのパーソナルアクセストークンを取得(作成?)するところや、RunnerをGitLabに登録する方法などが学べました。
ちょっと時間はかかりますが、何かを構築する際にPlaybookを書くことには、いろいろメリットがあるのではないか、と感じています。
メインPlaybook
さて、作成したPlaybookを見ていきましょう。
このPlaybookでやりたいことを列挙すると以下になります。
AWXのインストール
GitLabのインストール
GitLab Runnerのインストール
AWX/GitLab一括起動スクリプトの作成
連携サンプルの作成
本来ならばそれぞれをRoleとして実行していくのが良いかと思いますが、これは自分用のPlaybookということで、ちょっと楽をして(?)それぞれをタスクファイルとして作り、Playbookはそれらをincludeする形にしたいと思います。
ということでPlaybookはこんな感じです。varsのところでいろいろ設定できるようにしています。
---
- name: Setup Gitlab and AWX on Raspberry Pi
hosts: all
vars:
awx_source_path: ~/awx # AWXリポジトリのクローン先ディレクトリ
awx_version: 24.6.1 # インストールするAWXのバージョン
awx_password_file: ~/awx_admin_password # AWXのadminパスワードの保管先ファイル
gitlab_version: 17.5.1-ce.0 # インストールするGitLabのバージョン
gitlab_password_file: ~/gitlab_root_password # GitLabのrootパスワードの保管先ファイル
gitlab_runner_image: gitlab/gitlab-runner:latest # GitLab Runnerのコンテナイメージ
gitlab_runner_container_image: ghcr.io/ansible/community-ansible-dev-tools:latest # Runner内で実行するコンテナイメージ
gitlab_runner_name: Arm-docker-runner # Runnerの名前
gitlab_access_token: hQT6ms9MKAyEZtfag7yD # GitLabのrootユーザー用パーソナルアクセストークン(任意の20文字)
restart_alyways: true # ラズパイ起動時にすべてのコンテナを起動するか否か
tasks:
- name: Include prepare tasks
ansible.builtin.include_tasks: prepare.yml
- name: Include awx tasks
ansible.builtin.include_tasks: awx.yml
- name: Include gitlab tasks
ansible.builtin.include_tasks: gitlab.yml
- name: Include runner tasks
ansible.builtin.include_tasks: runner.yml
- name: Include launch_script tasks
ansible.builtin.include_tasks: launch_script.yml
事前準備タスク
事前準備タスク(prepare.yml)では以下を行います。
apt updateとapt upgradeでOSを最新化する
必要なソフトウェア(ansible-core、docker.io、docker-compose、docker-buildxそしてmake)をインストールする
sudoなしでdockerコマンドの実行ができるようにする
dockerサービスの起動とenable
docker buildxのインストール
実装は以下の通り。さほど難しくないかと思いますので、コードのみ書いておきます。
---
- name: Upgrade the OS
ansible.builtin.apt:
upgrade: yes
update_cache: yes
become: true
- name: Install required software
ansible.builtin.apt:
name:
- ansible-core
- docker.io
- docker-compose
- docker-buildx
- make
become: true
- name: Add user to docker group
ansible.builtin.user:
name: "{{ ansible_user }}"
groups:
- docker
append: true
become: true
- name: Change owner of docker.sock to the user
ansible.builtin.file:
path: /var/run/docker.sock
owner: "{{ ansible_user }}"
become: true
- name: Enable and start docker service
ansible.builtin.service:
name: docker
enabled: true
state: started
become: true
- name: Install docker buildx
ansible.builtin.command: docker buildx install
AWXのインストールの自動化
続いてAWXをインストールする awx.yml ですが、基本的に前記事で書いた流れをそのままPlaybook化しています。
1つずつ見ていきましょう。まずはAWXリポジトリのクローンです。
- name: Remove old AWX repository if exits
ansible.builtin.file:
path: "{{ awx_source_path }}"
state: absent
- name: Clone AWX repository
ansible.builtin.git:
repo: https://github.com/ansible/awx.git
version: "{{ awx_version }}"
dest: "{{ awx_source_path }}"
クローン先ディレクトリは、awx_source_path変数で、インストールするAWXのバージョンはawx_source_path変数で与えられることを想定しています。
次は make docker-compose-build の実行です。
今回インストールするAWXのバージョン24.6.1では、django-ansible-base と OpenSSLのバージョンを変更する必要があるため、その処理も入れています(今後のバージョンでは不要になるかもしれないので、このバージョンの場合だけ実行するようにしました)。
- name: Apply some changes if AWX version is 24.6.1
block:
- name: Update requirement_git.txt to use stable django-ansible-base
ansible.builtin.lineinfile:
path: "{{ awx_source_path }}/requirements/requirements_git.txt"
regex: '^django'
line: 'django-ansible-base @ git+https://github.com/ansible/django-ansible-base@2024.7.17#egg=django-ansible-base[rest_filters,jwt_consumer,resource_registry,rbac]'
- name: Update openssl version (1/2)
ansible.builtin.lineinfile:
path: "{{ awx_source_path }}/tools/ansible/roles/dockerfile/templates/Dockerfile.j2"
regex: 'openssl-3.0.7'
line: ' openssl-3.2.2 \'
- name: Update openssl version (2/2)
ansible.builtin.lineinfile:
path: "{{ awx_source_path }}/tools/ansible/roles/dockerfile/templates/Dockerfile.j2"
regex: 'openssl-3.0.7'
line: ' openssl-3.2.2 \'
when:
- awx_version == '24.6.1'
- name: Run make docker-compose-build
ansible.builtin.command:
cmd: 'make docker-compose-build'
chdir: "{{ awx_source_path }}/"
次は make docker-composeですが、これを実行するとAWXの起動まで行ってしまうので、Makefile を少し変更して、docker-compose.ymlの生成だけを行います。
- name: Update Makefile not to launch AWX automatically
ansible.builtin.lineinfile:
path: "{{ awx_source_path }}/Makefile"
regex: '^\s*\$\(MAKE\) docker-compose-up'
line: '# $(MAKE) docker-compose-up'
- name: Run make docker-compose
ansible.builtin.command:
cmd: 'make docker-compose'
chdir: "{{ awx_source_path }}/"
これでdocker-compose.ymlができたはずです。ただし、このまま docker-compose で起動するとrsyslog.d関連のエラーがでますので、以下を実行して回避します。
※詳しくはこちらを参照:https://github.com/ansible/awx/issues/14259
- name: Workaround for rsyslogd problem
ansible.builtin.command: "{{ item }}"
loop:
- ln -s /etc/apparmor.d/usr.sbin.rsyslogd /etc/apparmor.d/disable/
- apparmor_parser -R /etc/apparmor.d/usr.sbin.rsyslogd
become: true
生成された docker-compose.yml には restart の設定は書かれていないので、ラズパイ起動時にAWXを起動したい場合は、各サービスに restart: always を追加します。
※Playbook内で定義している変数 restart_always が true の時のみ実行するようにしています。
- name: Update docker-compose.yml to launch automatically
ansible.builtin.lineinfile:
path: "{{ awx_source_path }}/tools/docker-compose/_sources/docker-compose.yml"
insertafter: "{{ item.regexp }}"
line: "{{ item.line }}"
loop:
- regexp: 'awx_1:$'
line: ' restart: always # awx'
- regexp: 'redis_1:$'
line: ' restart: always # redis'
- regexp: 'postgres:$'
line: ' restart: always # postgres'
when:
- restart_always
続いて、docker-compose を実行するコマンドを awx_launch_command 変数に入れて、それを実行します。
- name: Generate AWX launch command
ansible.builtin.set_fact:
awx_launch_command:
"docker-compose -f {{ awx_source_path }}/tools/docker-compose/_sources/docker-compose.yml up -d"
- name: Launch AWX
ansible.builtin.command:
cmd: "{{ awx_launch_command }}"
chdir: "{{ awx_source_path }}/"
AWXの最初の起動にはしばらく時間がかかるので、以下のようにリトライしながら、adminユーザーのパスワードがログに出力されるまで待ちます。
- name: Wait for AWX launched up and retrieve admin password from container log
ansible.builtin.shell:
"docker logs tools_awx_1 | grep 'Admin password:'"
register: admin_password
retries: 60
delay: 10
until: admin_password.rc == 0
- name: Set admin_password param
ansible.builtin.set_fact:
awx_admin_password_from_log: "{{ (admin_password.stdout | split(' '))[2] }}"
- name: Create password file
ansible.builtin.copy:
dest: "{{ awx_password_file }}"
content: "{{ awx_admin_password_from_log }}"
adminユーザーのパスワードがログに表示されたら、それを awx_admin_password_from_log 変数で指定されたファイルに保存しておきます。
続いて、AWXのUIをビルドします。これは、docker exec コマンドをつかって、AWXのコンテナインスタンスで make clean-ui ui-devel を実行すればOKです。
- name: Build AWX UI in awx_1 container
ansible.builtin.command: "docker exec tools_awx_1 make clean-ui ui-devel"
ここまでPlaybookがFailせずに進めば、AWXは起動したはずです。
GitLabのインストール
次はGitLabをインストール(起動)する gitlab.yml です。こちらは、アドベントカレンダーの記事に書いたように、https://hub.docker.com/r/zengxs/gitlab/tags にあるコンテナイメージを使うことにします。
まずは、必要なディレクトリを用意します(モードを0777にしちゃっている点はご愛嬌ということでw)。
- name: Create directories for GitLab
ansible.builtin.file:
path: "{{ item }}"
mode: 0777
owner: "{{ lookup('ansible.builtin.env', 'USER') }}"
state: directory
loop:
- /etc/gitlab
- /etc/gitlab/logs
- /etc/gitlab/config
- /etc/gitlab/data
become: true
次に起動コマンドを作成して実行します。この際、restart_always パラメーターが true ならば、--restart always となるようにしています。
- name: Set fact - launch command
ansible.builtin.set_fact:
gitlab_launch_command: >-
docker run
--detach
--hostname gitlab
--publish 8143:443
--publish 80:80
--publish 22222:22
--name gitlab-container
--restart {{ 'always' if restart_always else 'no' }}
--volume /etc/gitlab/config:/etc/gitlab:Z
--volume /etc/gitlab/logs:/var/log/gitlab:Z
--volume /etc/gitlab/data:/var/opt/gitlab:Z
--net awx
zengxs/gitlab:{{ gitlab_version }}
- name: Run gitlab-ce container
ansible.builtin.command: "{{ gitlab_launch_command }}"
続いて、GitLabのrootユーザーパスワードを取得し、gitlab_password_file パラメーターで指定されたファイルに書き出します。
- name: Retrieve root password
ansible.builtin.shell:
"grep 'Password:' /etc/gitlab/config/initial_root_password"
become: true
register: root_password
retries: 30
delay: 10
until: root_password.rc is defined and root_password.rc == 0
- name: Set gitlab_root_password param
ansible.builtin.set_fact:
gitlab_root_password: "{{ (root_password.stdout.split(' '))[1] }}"
- name: Create password file
ansible.builtin.copy:
dest: "{{ gitlab_password_file }}"
content: "{{ gitlab_root_password }}"
後は、GitLabのUIが使えるようになるまで(実際にはサインイン画面が表示されるようになるまで)待機します。
- name: Wait for GitLab is ready
ansible.builtin.uri:
url: "http://localhost"
register: top_page
retries: 100
delay: 10
until: "'sign_in' in top_page.url"
GitLab Runnerもインストールする
後はGitLab Runnerのインストール(runner.yml)ですが、こちらもコンテナ自体は簡単に立ち上げられます。
まずは、ディレクトリの準備
- name: Create directories for GitLab Runner
ansible.builtin.file:
path: /etc/gitlab/runner
mode: 0777
owner: "{{ lookup('ansible.builtin.env', 'USER') }}"
state: directory
become: true
続いて、起動コマンドを作って実行します。
- name: Create runner run command
ansible.builtin.set_fact:
runner_launch_command: >-
docker run
--detach
--name gitlab-runner
--restart {{ 'always' if restart_always else 'no' }}
--volume /etc/gitlab/runner:/etc/gitlab-runner
--volume /var/run/docker.sock:/var/run/docker.sock
--net awx
{{ gitlab_runner_image }}
- name: Run GitLab Runner
ansible.builtin.command: "{{ runner_launch_command }}"
これでRunner自体は起動したのですが、GitLabへの登録が必要です。
このために、まず registration token を取得するのですが、そのためのRestAPIは無いようなので、Runnerコンテナ内でgitalb railsコマンドを実行します(commandモジュールではなくcommunity.docker.docker_container_execを使うのが良いかと思いますが…)。
- name: Get registration token
ansible.builtin.command:
"docker exec -it gitlab-container gitlab-rails runner
-e production \"puts Gitlab::CurrentSettings.current_application_settings.runners_registration_token\""
register: registration_token
gitlab railsコマンドはrunnerのコンテナインスタンス内でさらにコンテナを起動することもあって、ちょっと時間がかかりますが、仕方のないところです。
では、取得した registartion token を使ってrunnerを登録します。
- name: Register GitLab Runner
ansible.builtin.command: "docker exec -it gitlab-runner
gitlab-runner register --non-interactive
--url http://gitlab/
--registration-token {{ registration_token.stdout_lines[0] }}
--executor docker
--docker-image {{ gitlab_runner_container_image }}
--name {{ gitlab_runner_name }}"
Runnerの種類としては、dockerとし、そこで使うコンテナイメージは、gitlab_runner_container_image パラメーターで指定できるようにしています。
あとは、runnerコンテナの中で動作するコンテナ(ややこしいw)からGitLabにアクセスできるよう、runnerの設定ファイルに extra_hosts の項目を追加します。
- name: Add extra_hosts to config.toml
ansible.builtin.lineinfile:
path: /etc/gitlab/runner/config.toml
line: " extra_hosts = [\"gitlab:{{ ansible_wlan0.ipv4.address }}\"]"
become: true
これは、Runnerコンテナ内で追加するよりも、/etc/gitlab/runner/config.toml を更新する方が lineinfile モジュールが使えて楽なのでそうしました。
テストプロジェクトの追加
動作確認のために、テストプロジェクトを登録するタスク(test_project.yml)も作ってみたいと思います。
やりたい内容は次のようなものです。
GitLabにテストプロジェクト(リポジトリ)を作成する
テスト用PlaybookとCI/CDパイプラインファイル(.gitlab-ci.yml)をリポジトリに追加
AWXからGitLabにアクセスするための認証情報をAWXに作成
AWXにプロジェクトを作成
AWXにジョブテンプレートを作成
まず、GitLabの方からですが、プロジェクトを作成するモジュールとして community.general.gitlab_project があります。
このモジュールを実行すればプロジェクトの作成ができるのですが、このモジュールの実行のために2つやっておくことがあるので、それを先に行います。
1つは community.general コレクションのインストールで、以下のコマンドを実行します。
$ ansible-galaxy collection install community.general
もう1つがパーソナルアクセストークンの取得なのですが、これを一般的にGitLabのWeb UIで行うもので、同等のことを行うAnsibleモジュールは存在しません(たぶん)。
しかし、調べてみると、gitlab-rails コマンドをGitLabのコンテナ内で実行すれば実現できることを見つけました。
APIとレポジトリ、それからRunnerを作成できる権限で、有効期間は1年で作ることにします。
- name: Create personal access token
ansible.builtin.command:
"docker exec -it gitlab-container gitlab-rails runner
\"token = User.find_by_username('root').personal_access_tokens.create(
scopes: ['api', 'write_repository', 'create_runner'],
name: 'Automation token',
expires_at: 365.days.from_now);
token.set_token('{{ gitlab_personal_access_token }}');
token.save!\""
リポジトリ作成の準備ができましたので、作ってみます。なお、GitLabにアクセスするためのパスワードは、gitlab_password_file で指定したファイルに保存されていますので、それを取り出して使っています。
- name: Get gitlab password
ansible.builtin.command: "cat {{ gitlab_password_file }}"
register: gitlab_password
- name: Add test repository to GitLab
community.general.gitlab_project:
api_url: http://localhost/
api_username: 'root'
api_password: "{{ gitlab_password.stdout }}"
validate_certs: false
name: testproject
username: root
issues_enabled: false
merge_method: rebase_merge
wiki_enabled: false
snippets_enabled: true
initialize_with_readme: true
リポジトリができれば、それをCloneして、Playbookを追加し、commit/pushします。
- name: Set user name to Git
ansible.builtin.command:
"git config --global user.name \"{{ ansible_user }}\""
- name: Set email address to Git
ansible.builtin.command:
"git config --global user.email \"{{ ansible_user }}@example.com\""
- name: Remove local repository path if exists
ansible.builtin.file:
path: ~/testproject
state: absent
- name: Clone repository
ansible.builtin.git:
repo: "http://root:{{ gitlab_personal_access_token }}@localhost/root/testproject.git"
dest: ~/testproject
accept_hostkey: true
- name: Copy test playbook and ci pipeline file
ansible.builtin.copy:
src: files/
dest: ~/testproject/
- name: Run git add commit and push
ansible.builtin.command:
cmd: "{{ item }}"
chdir: ~/testproject
loop:
- "git add ."
- "git commit -m 'committed by Ansible'"
- "git push origin"
ちょっと泥臭いコードになってますが、ansible.scm コレクションを使うともう少しきれいになるかもしれませんね。
では、最後にAWX側の設定を行います。
まずはGitLabにアクセスするための認証情報を追加します。名前は GitLab Credential とし、credential_type には Source Control を指定します。また、パスワードには GitLab のパスワードではなく、パーソナルアクセストークンを指定しないとエラーになりますので、気を付ける必要があります。
- name: Get AWX password
ansible.builtin.command: "cat {{ awx_password_file }}"
register: awx_password
- name: Add gitlab credential to AWX
awx.awx.credential:
controller_host: 'https://localhost:8043'
controller_username: 'admin'
controller_password: "{{ awx_password.stdout }}"
validate_certs: false
name: 'GitLab Credential'
organization: 'Default'
state: present
credential_type: 'Source Control'
inputs:
username: root
password: "{{ gitlab_personal_access_token }}"
あとは同様にプロジェクトとジョブテンプレートを作成すれば完成です。
※プロジェクト作成時にEEのPull作業が発生し、時間がかかるため、timeoutを600秒に設定しています。
- name: Add test project to AWX
awx.awx.project:
controller_host: 'https://localhost:8043'
controller_username: 'admin'
controller_password: "{{ awx_password.stdout }}"
validate_certs: false
name: TestProject
description: "This is an Arm AWX test project"
scm_type: 'git'
scm_url: http://gitlab/root/testproject.git
scm_branch: 'main'
scm_update_on_launch: true
scm_clean: true
scm_delete_on_update: true
credential: 'GitLab Credential'
organization: 'Default'
timeout: 600
- name: Add Job Tempalte to AWX
awx.awx.job_template:
controller_host: 'https://localhost:8043'
controller_username: 'admin'
controller_password: "{{ awx_password.stdout }}"
validate_certs: false
name: 'Test Job Template'
job_type: run
organization: Default
inventory: 'Demo Inventory'
project: TestProject
playbook: playbook1.yml
まとめ
今回Playbook化した際に気づいたこととして、GitLabのインストール以外はプロセッサがArmであることや、対象デバイスがラズパイであることはほとんど意識する必要がなかったことがあります。
学習用の小型コンピューターであるラズパイが、ほとんど普通のx86-64アーキテクチャのLinuxマシンと同じように設定でき、期待通り動作するのは、なかなかすごい世界だな、と思ったりしました。
あと、個人的にはGitLabのTokenに関する仕組みや操作方法が(少しかもしれませんが)学べたのは良かったし、Runnerの設定も自動化できることがわかって、得るものはいろいろありました。
出来上がったシステムはしばらく使っているのですが、問題なく使えています。ただ、ストレージに micro SD を使っているため、耐久性に関する懸念はありますね。特にGitLabのリポジトリデータは定期的に外部にバックアップするような仕掛けを作りこんでおく必要はあるかな、と思っています。
ということで、本ブログは以上となります。皆様ももしご興味持たれたら年末年始の休みにでも試してみてはいかがでしょうか?
★公式ブロガー水谷の執筆記事一覧
https://note.com/hashtag/SHIFT_%E6%B0%B4%E8%B0%B7
IaC支援サービスのご紹介
SHIFTではTerraformやCDKを使ったクラウドインフラ構築の自動化や、Ansibleを使ったサーバOSの設定自動化や構成管理のご支援も行っております。ご依頼・ご相談は下記リンクからお願いします。
お問合せはお気軽に
SHIFTについて(コーポレートサイト)
SHIFTのサービスについて(サービスサイト)
SHIFTの導入事例
お役立ち資料はこちら
SHIFTの採用情報はこちら