nakayumcの技術ブログ

エンジニア2年目のブログです。

serialディレクティブと、run_once + delegate_to を付けたタスクを実行したらハマった

はじめに

Ansibleでは、特に何も指定しなければ、1度に全てのノードに対し処理を実行しますが、
全てのノードに対して処理を実行すると、コントロールノードの負荷が高くなり、処理速度が遅くなる可能性があります。

そのため、serial ディレクティブを使って、指定した台数づつ処理を実行させたところ、見事にハマったので備忘録として残しておきます。

環境

  • Ansible 2.16.0
  • Python 3.10.2
  • 適当に作成したLinuxサーバ4台

serial ディレクティブとは

Ansible では、serial ディレクティブを使うことで、指定した台数づつ処理を実行することができます。
Delegation, Rolling Updates, and Local Actions — Ansible DocumentationTitle

下記のようなplaybookを実行すると、PLAYレベルで分割され、sv というグループに対して1度に2台づつ、ディレクトリ作成とファイルをコピーできます。

Playbook

- name: copy file
  hosts: sv
  serial: 2
  gather_facts: false
  tasks:
    - name: Create directory
      ansible.builtin.file:
        path: ./dest
        state: directory

    - name: Copy file
      ansible.builtin.copy:
        src: ./src/file
        dest: ./dest/file

実行結果

$ anisble-playbook test.yml -i inventory.ini

### 1回目のプレイ
PLAY [sv] *****************************************************************

TASK [Create directory] ***************************************************
changed: [test1]
changed: [test2]

TASK [Copy file] **********************************************************
changed: [test1]
changed: [test2]

### 2回目のプレイ
PLAY [sv] *****************************************************************

TASK [Create directory] ***************************************************
changed: [test3]
changed: [test4]

TASK [Copy file] **********************************************************
changed: [test3]
changed: [test4]

PLAY RECAP ****************************************************************
test1 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test2 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test3 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test4 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

ただ、serial を指定した状態で、他のタスクで delegate_torun_once を指定している場合は注意が必要です。

serial と run_once + delegate_to の併用の注意点

例えば、上記の Playbook に対して、コントロールノード側で git clone を実行させたい場合などです。

この場合、git clone は1度だけ実行すれば良く、かつコントロールノード側で処理させたいため、run_once + delegate_to を指定します。

すると、以下のように、serialrun_once + delegate_to を併用すると、serial で指定した台数づつ処理を実行するときに、1度だけ実行させたいタスクが、serial で分割された回数分実行されてしまいます。

Playbook

- name: delegate
  hosts: sv
  serial: 2
  gather_facts: false
  tasks:
    - name: git clone
      ansible.builtin.command:
        cmd: git clone http://nakayumc:<api>@gitlab.kensho.nakayumc.com/ansible/ansible_repo.git
      delegate_to: localhost
      run_once: true

    - name: Create directory
      ansible.builtin.file:
        path: ./dest
        state: directory

    - name: Copy file
      ansible.builtin.copy:
        src: ./src/file
        dest: ./dest/file

実行結果

$ anisble-playbook test.yml -i inventory.ini

### 1回目のプレイ
PLAY [sv] *****************************************************************

TASK [git clone] **********************************************************
changed: [test1 -> localhost]

TASK [Create directory] ***************************************************
changed: [test1]
changed: [test2]

TASK [Copy file] **********************************************************
changed: [test1]
changed: [test2]

### 2回目のプレイ
PLAY [sv] *****************************************************************

### ディレクトリが既にあるため、git clone は実行できなかった
TASK [git clone] **********************************************************
fatal: [test3 -> localhost]: FAILED! => {"changed": true, "cmd": 
["git", "clone", "http://nakayumc:<api>@gitlab.kensho.nakayumc.com/ansible_repo.git"], 
"delta": "0:00:00.002001", "end": "2023-12-23 10:32:12.488677", "msg": "non-zero 
return code", "rc": 128, "start": "2023-12-23 10:32:12.486676", "stderr": "fatal: 
destination path 'ansible_repo' already exists and is not an empty directory.", 
"stderr_lines": ["fatal: destination path 'ansible_repo' already exists and is not 
an empty directory."], "stdout": "", "stdout_lines": []}

NO MORE HOSTS LEFT ********************************************************

PLAY RECAP ****************************************************************

解決方法

解決方法としては、PLAYレベルで localhost に対して処理を実行させるもの、sv に対して処理を実行させるものに分けることで解消できました。 localhost に対して処理を実行させるものは、serial を指定せず、run_once + delegate_to を指定します。

Playbook

- name: Localhost
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Git clone
      ansible.builtin.command:
        cmd: git clone http://nakayumc:<api>@gitlab.kensho.nakayumc.com/ansible/test.git
      delegate_to: localhost
      run_once: true
      ignore_errors: true

- name: Linux Hosts
  hosts: sv
  serial: 2
  gather_facts: false
  tasks:
    - name: Create directory
      ansible.builtin.file:
        path: ./dest
        state: directory

    - name: Copy file
      ansible.builtin.copy:
        src: ./src/file
        dest: ./dest/file

実行結果

$ play test.yml -i inventory.ini 

PLAY [Localhost] **********************************************************

TASK [Git clone] **********************************************************
changed: [localhost]

PLAY [Linux Hosts] ********************************************************

TASK [Create directory] ***************************************************
ok: [test1]
ok: [test2]

TASK [Copy file] **********************************************************
ok: [test2]
ok: [test1]

PLAY [Linux Hosts] ********************************************************

TASK [Create directory] ***************************************************
ok: [test4]
ok: [test3]

TASK [Copy file] **********************************************************
ok: [test4]
ok: [test3]

PLAY RECAP ****************************************************************
localhost : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0
test1     : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test2     : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test3     : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test4     : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

まとめ

今回は、serial ディレクティブがある場合で、run_once + delegate_to のタスクがある場合について、
serialrun_once + delegate_to を併用すると、serial で指定した台数づつ処理を実行するときに、1度だけ実行させたいタスクが、serial で分割された回数分実行されてしまうことについて紹介しました。

serialrun_once + delegate_to を併用する場合は、PLAYレベルで localhost に対して処理を実行させるもの、sv に対して処理を実行させるものに分けることで解消できました。

ansible-builder 3系 で /usr/bin/dnf: No such file or directory で失敗する

はじめに

Ansible の ExecutionEnvironment をビルドするツールである ansible-builder がメジャーアップデートされ、 3.0.0 がリリースされました。

いままで ansible-builder の 1系を使っていたので、お試しで ansible-builder 3 を触ってビルドしてみたところ、/usr/bin/dnf: No such file or directory というエラーで失敗したのでメモします。

環境

  • ansible-builder 3.0.0
  • docker 24.0.2
  • EEベースイメージ: quay.io/centos/centos:stream9

事象

execution-environment.yml を以下のように記述してビルドを実行したところ、エラーになってしまいました。

---
version: 3

images:
  base_image:
    name: quay.io/centos/centos:stream9
dependencies:
  galaxy:
    collections:
      - name: community.general
        version: 6.4.0
  python:
    - ansible-lint==5.4.0
  system:
    - git
additional_build_files:
  - src: ./ansible.cfg
    dest: configs
additional_build_steps:
  prepend_base:
    - ADD _build/configs/ansible.cfg ~/.ansible.cfg
  prepend_final:
    - RUN whoami
    - RUN cat /etc/os-release
  append_final:
    - RUN echo This is a post-install command!
    - RUN ls -la /etc
ansible-builder build -t nakayumc0278/custom-ee:001

エラーメッセージ

#26 3.830 + /usr/bin/dnf install -y dnf gcc git krb5-devel ・・・
#26 3.830 /output/scripts/assemble: line 75: /usr/bin/dnf: No such file or directory
#26 ERROR: process "/bin/sh -c /output/scripts/assemble" did not complete successfully: exit code: 127
ERROR: failed to solve: process "/bin/sh -c /output/scripts/assemble" did not complete successfully: exit code: 127

結論から書くと、ansible-builder の 3系では、パッケージマネージャーがデフォルトで /usr/bin/dnf になっていて、ベースイメージに dnf が入っていないためでした。

ベースイメージのパッケージ一覧を確認してみると、確かに dnf は入っていませんが、microdnf というものが入っています。なにやら dnf よりも軽量なパッケージマネージャーで docker イメージのサイズを小さくするために使われているようでした。

また、RHEL のアップストリーム版である Fedora では、dnfmicrodnf に置き換わる計画をしているようです。 近いうちに microdnf に置き換わるのかもしれません。

解決策

上の画像にもありますが、 ベースイメージには、dnf の軽量版である microdnf が入っています。

execution-environment.yml に optionspackage_manager_pathmicrodnf を指定して追記することで解決できました。

追記後の execution-envoronment.yml

---
version: 3

images:
  base_image:
    name: quay.io/centos/centos:stream9
dependencies:
  galaxy:
    collections:
      - name: community.general
        version: 6.4.0
  python:
    - ansible-lint==5.4.0
  system:
    - git
options:
  package_manager_path: /usr/bin/microdnf # 追記
additional_build_files:
  - src: ./ansible.cfg
    dest: configs
additional_build_steps:
  prepend_base:
    - ADD _build/configs/ansible.cfg ~/.ansible.cfg
  prepend_final:
    - RUN whoami
    - RUN cat /etc/os-release
  append_final:
    - RUN echo This is a post-install command!
    - RUN ls -la /etc

実行結果

#34 [final 13/13] RUN rm -rf /output
#34 DONE 0.5s

#35 exporting to image
#35 exporting layers
#35 exporting layers 2.1s done
#35 writing image sha256:126e70fa982cc99b488f740f828282efe390249f1ee97e3592d35e29c0fa3b5c done
#35 naming to docker.io/nakayumc0278/ee001 done
#35 DONE 2.1s

Complete! The build context can be found at: /home/nakayumc/container/context

AlmaLinux 環境に GitLab CE をインストールしてコンテナレジストリを有効化

2023/05/07 GItLab再構成の部分に一部追記しました。


こんにちは、nakayumc です。
最近、仕事でよく GitLab を使用する機会が多くなったので検証環境を作りました。その備忘録として残しておきます。

また、今回はコンテナレジストリ機能を使用するので、有効化する方法も記載しています。

環境

環境は以下の通りです。
今回は AlmaLinux 8.7 に GitLab CE をインストールします。

  • OS: AlmaLinux Relese 8.7
  • GitLab: GitLab-CE 15.11.0

IPアドレスなどは以下の表です。

役割 IPアドレス ホスト名
GitLab 10.32.64.1 gitlab.int.nakayumc.com

GitLab を gitlab.int.nakayumc.com というドメインで動かす想定で、WEBアクセスは HTTPS を使い、コンテナレジストリのポートは 5050/TCP で動かします。

前提条件

以下の条件を満たしていることを前提としています。

  • SELinuxPermissive か、Disabled の状態であること。
  • firewalld は、http, https, 5050/tcp を開けておくか、firewalld が無効化されていること。

構築時は SELinux や firewalld などで引っかかる可能性があるので、適宜設定してください。

インストール

リポジトリの追加

まず、AlmaLinux の デフォルトリポジトリには GitLab が含まれていないため、リポジトリを追加します。

curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | bash

以下のようなメッセージが表示されれば成功です。

Generating yum cache for gitlab_gitlab-ce...
Importing GPG key 0x51312F3F:
 Userid     : "GitLab B.V. (package repository signing key) <packages@gitlab.com>"
 Fingerprint: F640 3F65 44A3 8863 DAA0 B6E0 3F01 618A 5131 2F3F
 From       : https://packages.gitlab.com/gitlab/gitlab-ce/gpgkey
Importing GPG key 0xF27EAB47:
 Userid     : "GitLab, Inc. <support@gitlab.com>"
 Fingerprint: DBEF 8977 4DDB 9EB3 7D9F C3A0 3CFC F9BA F27E AB47
 From       : https://packages.gitlab.com/gitlab/gitlab-ce/gpgkey/gitlab-gitlab-ce-3D645A26AB9FBD22.pub.gpg
Generating yum cache for gitlab_gitlab-ce-source...

The repository is setup! You can now install packages.

次に、以下のコマンドを実行して、リポジトリが追加されていることを確認します。

yum repolist

以下のような出力が得られればOKです。

repo id                                           repo name
appstream                                         Rocky Linux 8 - AppStream
baseos                                            Rocky Linux 8 - BaseOS
extras                                            Rocky Linux 8 - Extras
gitlab_gitlab-ce                                  gitlab_gitlab-ce
gitlab_gitlab-ce-source                           gitlab_gitlab-ce-source

GitLab のインストール

次に、下記のコマンドを実行して、GitLab をインストールします。
時間がかかる場合があるので、気長に待ちましょう。

dnf install -y gitlab-ce

GitLab がインストールされると、以下のようなメッセージが表示されます。
タヌキのアイコン (実はキツネではないことを知ってましたか?) とロゴがかっこいいですね。

It looks like GitLab has not been configured yet; skipping the upgrade script.

       *.                  *.
      ***                 ***
     *****               *****
    .******             *******
    ********            ********
   ,,,,,,,,,***********,,,,,,,,,
  ,,,,,,,,,,,*********,,,,,,,,,,,
  .,,,,,,,,,,,*******,,,,,,,,,,,,
      ,,,,,,,,,*****,,,,,,,,,.
         ,,,,,,,****,,,,,,
            .,,,***,,,,
                ,*,.

     _______ __  __          __
    / ____(_) /_/ /   ____ _/ /_
   / / __/ / __/ /   / __ `/ __ \
  / /_/ / / /_/ /___/ /_/ / /_/ /
  \____/_/\__/_____/\__,_/_.___/
  
Thank you for installing GitLab!
GitLab was unable to detect a valid hostname for your instance.
Please configure a URL for your GitLab instance by setting `external_url`
configuration in /etc/gitlab/gitlab.rb file.
Then, you can start your GitLab instance by running the following command:
  sudo gitlab-ctl reconfigure

For a comprehensive list of configuration options please see the Omnibus GitLab readme
https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md

Help us improve the installation experience, let us know how we did with a 1 minute survey:
https://gitlab.fra1.qualtrics.com/jfe/form/SV_6kVqZANThUQ1bZb?installation=omnibus&release=15-11

    Verifying  : gitlab-ce-15.11.0-ce.0.el8.x86_64                        1/1

Installed:
  gitlab-ce-15.11.0-ce.0.el8.x86_64

Complete!

GitLab の設定

次に、GitLab の設定を行います。
ここで変更するのは、「ドメイン名の変更」と、「コンテナレジストリ機能を有効化」を行います。

設定ファイルは /etc/gitlab/gitlab.rb にあります。

vi /etc/gitlab/gitlab.rb

ドメイン名の変更

以下のように、external_urlドメイン名を設定します。
ここで注意が必要なのは、今回はHTTPSでアクセスするので https:// から始まるドメインを設定してください。

IPアドレスで構築したい場合は、ドメインの部分をIPアドレスに読み変えてください。
(32行目あたり)

# external_url 'http://gitlab.example.com'
external_url 'https://gitlab.int.nakayumc.com'

コンテナレジストリ機能を有効化

次に、コンテナレジストリ機能を有効化します。
今回は、コンテナレジストリのポート番号を 5050 に設定します。
(861行目あたり)

# registry_external_url 'https://registry.example.com'
registry_external_url 'https://gitlab.int.nakayumc.com:5050'

以上で、GitLab の設定は完了です。

GitLab の再構成

続いて、GitLab の再構成を行います。以下のコマンドを実行してください。
この操作は数分かかるので、気長に待ちましょう。

gitlab-ctl reconfigure

以下のような出力が得られればOKです。

Running handlers:
[2023-04-28T12:30:09-04:00] INFO: Running report handlers
Running handlers complete
[2023-04-28T12:30:09-04:00] INFO: Report handlers complete
Infra Phase complete, 13/852 resources updated in 03 minutes 57 seconds
gitlab Reconfigured!

再構成時のエラー

2023/05/07 追記

設定ファイルを変更して、再構成すると以下のエラーが起きました。

[2023-05-07T14:06:14+09:00] ERROR: Running exception handlers
There was an error running gitlab-ctl reconfigure:

letsencrypt_certificate[gitlab.int.nakayumc.com] (letsencrypt::http_authorization line 6) had an error: RuntimeError: acme_certificate[staging] (letsencrypt::http_authorization line 43) had an error: RuntimeError: ruby_block[create certificate for gitlab.int.nakayumc.com] (letsencrypt::http_authorization line 110) had an error: RuntimeError: [gitlab.int.nakayumc.com] Validation failed, unable to request certificate, Errors: [{url: https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/6386713374/-CEe9A, status: invalid, error: {"type"=>"urn:ietf:params:acme:error:dns", "detail"=>"no valid A records found for gitlab.int.nakayumc.com; no valid AAAA records found for gitlab.int.nakayumc.com", "status"=>400}} ]

今回は、GitLabサーバを外に公開してないので「LetsEncrypt側から見えないよ」というエラーが起きています。 私の場合は、もう一度 gitlab-ctl reconfigure コマンドを実行することでエラー解消されました。

それでも解消されない方は証明書を発行する必要があるので、以下のサイトが参考になると思われます。 blogs.networld.co.jp

GitLab にログインと動作確認

GitLab にログインするために、ブラウザからアクセスします。
ユーザ名は root、パスワードは /etc/gitlab/initial_root_password で確認できます。

cat /etc/gitlab/initial_root_password

リポジトリの作成

ログインができたら、グループとリポジトリを作成します。
今回はこのような構成にします。

作成が出来ると、以下のような画面が表示されます。

コンテナレジストリの確認

コンテナレジストリは左のサイドバーから、Packages & Registries を選択して、
Container Registry」という項目があれば、正常にコンテナレジストリが有効化されています。

まとめ

今回は、GitLab をインストールして、コンテナレジストリを有効化するまでを行いました。
GitLabのコンテナレジストリで遊んでみたい方は、ぜひ試してみてください。

AAPのカスタム認証情報タイプの使い方

はじめに

こちらは、エーピーコミュニケーションズ Advent Calendar 2022 の23日目のエントリとなります。

こんにちは、nakayumcです。

今回は、AAPの機能の一つである「カスタム認証情報タイプ」を使って、ユーザ名やパスワードをAnsibleの変数ではなくAAP上で管理出来るようにしてみます。

また、カスタム認証タイプの定義をextra_varsenv でログの出力で違いがあるのでこちらも合わせて比べてみます。

環境

  • Ansible Automation Platform 2.2

Playbook

今回はGithubからHTTPS認証を用いてGitHubからCloneするPlaybookで試してみます。
{{ GITHUB_USER }}{{ GITHUB_PASSWORD }} という変数は、AAPの認証情報で定義をしています。

---
  - name: git clone
    ansible.builtin.git:
      repo: "https://{{GITHUB_USER}}:{{GITHUB_PASSWORD}}@github.com/nakayumc0278/til.git"  #1行です
      dest: "./repository"

カスタム認証情報タイプを作成

まず、カスタム認証情報タイプ(変数を入れる箱のようなもの)を作成します。
AAP の左メニュー、管理 -> 認証情報タイプ を開き、追加ボタンをクリックし登録します。

  • 入力の設定
---
fields:
  - id: github_user
    type: string
    label: github_user
  - id: github_password
    type: string
    label: github_password
    secret: true
required:
  - github_user
  - github_password

ここでは、extra_vars変数に設定してみます。

---
extra_vars:
  GITHUB_USER: '{{ github_user }}'
  GITHUB_PASSWORD: '{{ github_password }}'

以上でカスタム認証情報タイプの設定が完了しました。

認証情報の入力

次に、AAP の左メニュー、リソース -> 認証情報 を開き、追加ボタンをクリックします。
認証情報タイプをクリックすると、先程「認証情報タイプ」で作成したものが出てくるのでクリックします。
ユーザ名とパスワード(APIキーでも可)を入力して保存します。

ジョブテンプレートに認証情報の追加

次に、ジョブテンプレートに先程設定した「認証情報」を追加します。

まず、AAP の左メニュー、リソース -> テンプレート -> 編集マーク でジョブテンプレートを編集します。

認証情報の虫眼鏡マーク -> カテゴリーを 「GitHub Credential Type」 を選択して「認証情報」を選択して保存します。

これで設定完了です。

実行してみよう

実際にPlaybookを起動してみた結果です。

このように正常に完了しましたが、詳細を見てみると認証情報がモロ出しでした。
これは、Playbookに no_log: true を追加すれば避けられますが、デバッグすると出力されてしまいます。

回避方法

ジョブの結果で出力を避けるには、カスタム認証情報タイプを環境変数にすることで回避できます。
実際に設定してみます。

カスタム認証情報を変更

AAP の左メニュー、管理 -> 認証情報タイプ を開き、編集します。
インジェクターの設定で、extra_varsenv に変更し、保存します。

env:
  GITHUB_USER: '{{ github_user }}'
  GITHUB_PASSWORD: '{{ github_password }}'

Playbookの変数を環境変数に設定

Playbookも環境変数にしたことで、変更が必要です。
環境変数を用いるために、{{ GITHUB_USER }}{{ GITHUB_PASSWORD }} を下記に変更します。

---
  - name: git clone
    ansible.builtin.git:
      repo: "https://${GITHUB_USER}:${GITHUB_PASSWORD}@github.com/nakayumc0278/til.git"
      dest: "./repository"

これで再度実行してみます。

このように環境変数を用いることで、認証情報を秘匿することが出来ました。

新卒エンジニア1年目の振り返ってみる

こちらは、エーピーコミュニケーションズ Advent Calendar 2022 の12日目のエントリとなります。

はじめに

こんにちは、nakayumc です。

最近まで少しだけ寒いな~と思っていたら、先日からどっと寒くなってしまい、ついに石油ファンヒーターを押し入れから出しました。
ヒーターを付けたときの匂いがなにか懐かしい気持ちが蘇ってくる感じがしました。 香りと記憶は結びついているんだなと思いつつ、こういう匂いって冬でしか感じられない季節の香りなのだなと感じています。

さて今回は、会社に新卒で入社してから今までやってきたこと、得られたことを振り返ってみたいと思います。

自己紹介

あまり自己紹介をしていないので、ここで簡単に自己紹介をしたいと思います。

新宿にある専門学校の専門学校を卒業後、APCに新卒で入社しました。
専門学校では主にCiscoの機器を使って実際にコマンドを叩いてネットワークを組んでみたり、Linuxを使って設定を投入するなど、実践的なことをしていました。
就活をしていくにつれて、学科内の会社説明会APCを知り入社しました。

新卒エンジニア入社1年目でやってきたこと

入社(4月~6月)

入社して3ヶ月間の間は入社時研修で新卒研修の期間が設けられています。

4月~6月の研修や当時の思っていたことをこの記事でも書いてあるので見ていただけると嬉しいです。

配属(7月~9月)

レーニン

3ヶ月の入社時研修を終えると、いよいよ配属になります。
どの部署に配属になるのかすごくドキドキしていましたが、6月中旬あたりで事前に内示を受けていて、個人的には入社前から希望していた部署だったので、安心しました。

配属後、部署の中でAnsibleについてのトレーニングを受けます。Ansibleは学生時代でも多少触っていたため知っている内容も一部ありましたが、ほぼ新しいことばかりで学びが多くて食らいつくのに必死という感じでした。

レーニングで記入していくテスト仕様書や作業手順書を今まで書いたことがなかったので、どの粒度でそれらを書いていけば良いのか全く見当つかずでしたが、自分なりにまとめて何とか発表することができたかなと感じています。

レーニングを受けて教訓があって、「Playbookの作成代行者」にはならないこと です。
今まで手作業でやっていた手順の中で、「どこの部分で自動化できるのか」、「自動化しなくても良くなる手順は何なのか」と言う点で業務効率化を図るということは今でも印象に残っています。

Backlog のツール改善

その後は、少しAnsibleから離れてBacklogの自動応答の実装をしていました。

弊社サービスで、運用の支援を目的としたチケット制のサポート「チケットサポート」というものを提供していて、Backlogを使ってお客様とやりとりをしています。
お客様からチケットを受けたときにまず受付一次回答をするのですが、チケットサポート担当者が不在で受付一次回答が遅れてしまう場合がありました。そこで、Pythonを使って自動応答を作成することになりました。

作成していて難しかった点は、

  • 設計をいまいち考えておらず、出戻りがあったと感じています。
  • 初めてLambdaやAPI Gateway を触れたので仕組みがいまいち理解できてなかったので時間がかかった。
  • ユーザ情報が入ったCSVファイルを使ってごにょごにょする実装が結構難しくて時間がかかってしまった。

というところでした。
なんとか1ヶ月弱ほどで作成することができ、9月からリリースすることができました。

CCNA の合格

本当は入社時研修中に合格したかったのですが、手が回らずなかなか受けることが出来ませんでした。
なんとか白本を読了しつつ、分からない問題をPing-tでひたすらやって、受けに行きました。

結果はこんな感じで、けっこう良い結果だったのかなと感じています。

受けた感想としては、めちゃくちゃCisco語が出るんじゃないかって不安があったんですが、きちんと翻訳されていてそんなことなかったです。
あと、ルーティングの問題がたくさん出てきた印象です。一通り読んで理解して答えるとなるとどうしても時間がかかりました。

案件を受け持ちはじめる(10月~12月)

BIG-IPの自動化

10月に入るといよいよ初めての案件を持ち始め、参入する日につれてとても緊張していました。
何をやっているのかというとBIG-IPでいままで手動で行っていたさまざまな作業を自動化する案件なのですが、今までBIG-IPという名前の機器を知らなかったのと、そもそもロードバランサを触れたことがなかったのでその概念というか、ロードバランサの立ち位置が自分の中で曖昧だったのでインプットをするのに時間がかかりました。

あと、会議の内容を記録する議事録を取ったりしていますが、いまいちきちんとうまく取ることができなくて自分の中で試行錯誤して書いていたのですが、先輩方に「きちんと書けてる」と行っていただいたのはとても嬉しかったです。

思ったことと反省すべき点

時が過ぎるのがめちゃくちゃ早い

この1年で感じたことは、今までで一番最速で時間が過ぎていたなということです。
もう1年が終わってしまうんだな・・と正直びっくりしています。
ただ年齢を重ねていくと、いま体感しているよりももっと速く進んでいってしまうんだろうな・・と感じています。

今年はいろいろタスクがたくさんあって、それに食らいつくのがあまり技術に触れられなかったな、と感じています。 来年はもっと様々な技術に触れるようにしていきたいです。

いろいろな面で当事者意識を持つ

今年は、ただ与えられたタスクをひたすらやるだけになってしまっていたなと感じています。
サボっていた訳ではないのですが、ある課題に関して議論しても自分からは意見がなにも思いつかず、主体性を持って行動できていないなと感じました。

なのでこれはいち速く主体性を持って行動して組織、プロジェクトに貢献しつつ、自分を成長にもしていきたいです。

最後に

最後まで読んでいただき、ありがとうございました!。 社会人になってから景色ががらりと変わりました。 仕事量も責任も比例して多さが増し「本当にやっていけるのだろうか」と常に心配です。

まだまだ分からないことだらけですが、一生懸命業務に取り組んでいきたいと思います!

ansible-playbookコマンドでcowsayさせる

はじめに

coysayコマンドはジョークコマンドとして有名です。

$ echo Hello. I'm a cow. mooooo. | cowsay
 _______________________
< Hello. I'm a cow. mooooo. >
 ---------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |

そのcowsayがインストールされている環境ではansible実行時に、デフォルトで牛が表示されます。
今回は、そのやりかたです。

epelリポジトリが入ってない方は下記コマンドで入れてください。

$ sudo yum install epel-release

cowsay という牛が表示できるパッケージをインストールします。

$ sudo yum install cowsay

基本的にこれで準備完了です。

Playbookを実行すると・・・・

実際に実行させてみます

モ~~~~~~~。
$ ansible-playbook -i inventory/inventory.ini test.yml 
 _______________
< PLAY [node01] >
 ---------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
 ____________________
< TASK [output message.] >
 ------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
・・・

牛じゃなくて猫にしたい

生粋のにゃんしぶるユーザの方は、牛じゃなくて猫がいいですよね、、

1.cowsayに猫を追加する

/usr/share/cowsay/cat.cow に下記を貼り付けます。
UTF-8では文字化けするので、UTF-16 LEで保存しました。

##
## A cow wadvertising the World Wide Web, from lim@csua.berkeley.edu
##
$the_cow = <<EOC;
   ∧,,∧
  (,,・∀・)
 ~(_u,uノ
EOC

2.ansible.cfgの記述

デフォルトでは牛が出力されるようになっています。
ねこに変更するには、ansible.cfgに以下を記述します。

[defaults]
cow_selection = cat

3.出力結果

にゃ~ん。

< PLAY [node01] >
 ---------------
   ∧,,∧
  (,,・∀・)
 ~(_u,uノ
 ________________________
< TASK [output message.] >
 ------------------------
   ∧,,∧
  (,,・∀・)
 ~(_u,uノ

ok: [node01] => {
    "changed": false,
    "msg": "Hello. I'm a cat. nyaaaan."
}
・・・

かわいいですね。

anisbleの assertモジュールの基本的な使い方をまとめてみた

はじめに

お疲れ様です。
今回は、assert モジュールについてまとめていきます。

assertモジュールですが個人的に機能がボリューミーだと思ってます。
あまり理解できていないところもありますが、分かる範囲で備忘録で残しておきます。

間違っている箇所もあるかと思うので、なにか気になることがあったら@nakayumc0278 まで直接連絡いただければと思います。

TL;DR

  • assert モジュールを使うことで、ある値が指定した条件に合致しているか判定できる。
  • 複数の条件式がある場合、AND と OR で判定させることができる。
  • ignore_errors ディレクティブを使うことで、エラーとなっても Playbook の処理を継続させることができる

assertモジュールとは何ぞや??

assertモジュールとは、ある値が期待した条件を合致しているか判定できるモジュールです。
基本的に、assert の結果がfaildになると、それ以降の Playbook の処理が止まります。

環境

環境は下記の通りです。前回と同じ環境です。

  • サーバとか
名前 IPアドレス OS
control 192.168.1.110 RHEL 8.6
node01 192.168.1.111 CentOS 7
node02 192.168.1.112 CentOS 7
  • ユーザ周り
ユーザ名 パスワード
ansible ansible
  • ansible バージョン
    (venv) ansible [core 2.12.1]

検証した Inventory ファイル

今回検証に使った inventory ファイルはこんな感じです。
最低限のことしか書いていません。

[nodes]
node01 ansible_host=192.168.1.111

[nodes:vars]
ansible_user=ansible
ansible_password=ansible

基本的な使い方と実行結果

条件式が1つだけの場合

成功するパターン

下記に記載のthatにある条件式が、左の111と右の111が等しいので、実行結果がAll assertions passedとなり、成功します。

---
- name: assert check
  ansible.builtin.assert
    that:
      - 111 == 111

実行結果

ok: [node01] => {
    "changed": false,
    "msg": "All assertions passed"
}

失敗するパターン

thatにある条件式が、左の111と 右の999が等しくないので、この場合は実行結果がAssertion failedとなり、エラーが吐かれます。

---
- name:  assert check
  ansible.builtin.assert:
    that:
      - 111 == 999

実行結果

fatal: [node01]: FAILED! => {
    "assertion": "111 == 999",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}

複数の条件式がある場合

assertには複数の条件式を指定して判断させることができます。
2つの条件判定があり、ANDとORがあります。

AND条件とき

AND条件の場合は、条件式がすべて等しい場合に成功し、1つでも等しくない条件式があると失敗します。

成功するパターン

これは条件式がすべて等しいので、All assertions passedとなり、成功します。

---
  - name:  assert check
    ansible.builtin.assert:
      that:
        - 111 == 111
        - 222 == 222

実行結果

ok: [node01] => {
    "changed": false,
    "msg": "All assertions passed"
}

失敗するパターン

この場合は、2つ目の条件式が222 == 999 となっています。 この場合だとすべて等しくないので、Assertion failedが出力され、失敗します。

---
  - name:  assert check
    ansible.builtin.assert:
      that:
        - 111 == 111
        - 222 == 999

実行結果

fatal: [node01]: FAILED! => {
    "assertion": "222 == 999",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}

OR条件とき

OR条件の場合は、1つでも成功すればAll assertions passedとなります。

成功するパターン

これは、111 == 444では等しくありませんが、後ろの222 == 222では等しいため、All assertions passedが出力され、成功します。

---
- name:  assert check
    ansible.builtin.assert:
    that:
      111 == 444 or 222 == 222

実行結果

ok: [node01] => {
    "changed": false,
    "msg": "All assertions passed"
}

失敗するパターン

この場合は、2つの条件式がどちらも等しくない場合はAssertion failedとなり、失敗します。

---
- name:  assert check
  ansible.builtin.assert:
    that:
        111 == 444 or 222 == 999

実行結果

fatal: [node01]: FAILED! => {
    "assertion": "111 == 444 or 222 == 999",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}

assert で failed になると、、、

ここからは、 assert モジュールの 結果が failed になると、基本的にそれ以降のPlaybookの処理が止まることについて解説していきます。

失敗したとき

まず、失敗パターンです。条件式を111 == 999としているので、等しくないです。

---
- name:  assert check
  ansible.builtin.assert:
    that:
      111 == 999
- name: output message
  ansible.builtin.debug:
    msg: "Hello. I'm a cat. I'm not cat command."

実行した結果

PLAY [node01] ******************************************************************

TASK [assert check] ************************************************************
fatal: [node01]: FAILED! => {
    "assertion": "111 == 999",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}

このように、assert が実行され、111 == 999となっており等しくないので、Assertion failedとなりました。このとき、2つ目のdebugモジュールが実行されていません。

成功したとき

次に、成功したパターンです。

---
- name:  assert check
  ansible.builtin.assert:
    that:
      111 == 111
- name: output message
  ansible.builtin.debug:
    msg: "Hello. I'm a cat. I'm not cat command."

実行した結果

PLAY [node01] ******************************************************************

TASK [assert check] ************************************************************
ok: [node01] => {
    "changed": false,
    "msg": "All assertions passed"
}

TASK [output message.] *********************************************************
ok: [node01] => {
    "changed": false,
    "msg": "Hello. I'm a cat. I'm not cat command."
}

はい、実行結果のように assert が実行されて、111 == 111となっており等しいためAll assertions passedとなりました。
その後、処理が継続されて、2つ目のdebugモジュールが実行されました。

ignore_errors ディレクティブを使う

ignore_errorsディレクティブとは、万一、エラーが発生しても Playbook の処理を継続させることができる機能です。
111 == 222 の下に、ignore_errors: trueを追加しました。

---
- name: assert check
  ansible.builtin.assert:
    that:
      111 == 222
  ignore_errors: true

- name: output message.
  ansible.builtin.debug:
    msg: "Hello. I'm a cat. I'm not cat command."

実行結果

PLAY [node01] ******************************************************************

TASK [assert check] ************************************************************
fatal: [node01]: FAILED! => {
    "assertion": "111 == 222",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}
...ignoring

TASK [output message.] *********************************************************
ok: [node01] => {
    "changed": false,
    "msg": "Hello. I'm a cat. I'm not cat command."
}

本来であれば、assert がエラーになっているため、Playbook の処理が止まりますが、ignore_errors: trueを追加したことにより、その後の処理が続行されました。

さいごに

今回は、assertモジュールについて触れました。
指定した値が正しく設定されているかどうかを確認するために使用されるため、よく使われるんじゃないかなと感じました。

ということが確認できました。

yumモジュールのpresentとinstalledの違いを比べてみた

はじめに

お疲れ様です。 最近暑い日が続いて大変ですね。

さて、私は7月から配属となりました。
配属となった部署ではAnsibleを主に使っていて、現在、Ansibleについての研修を私と先輩、2人で受けています。

今回は、yum モジュールで git をインストールする Playbook の課題に取り組んでいました。

それぞれ完成したPlaybook を 2人で確認したら state の指定が presentinstalled で分かれました。

そこで私は、指定方法によってどのような違いがあるのか気になり、出力結果を比べてみたり、debugとかしてみました。(最終的にソースコードを覗きました)

間違っている箇所もあるかと思うので、なにか気になることがあったら@nakayumc0278 まで直接連絡いただければと思います。

TL;DR

  • 結果的には同じ動作をする
  • debug で確認しても同様の結果
  • ソースコード上で確認すると if state in ['installed', 'present']: となっており、 同じ動作をするようになっていた。
  • installed は present の Alias である。

環境

環境は下記の通りです。

  • サーバとか
名前 IPアドレス OS
control 192.168.1.110 RHEL 8.6
node01 192.168.1.111 CentOS 7
node02 192.168.1.112 CentOS 7
  • ansible バージョン
    (venv) ansible [core 2.12.1]

検証した Playbook と Inventory ファイル

今回検証に使った Playbook です。

---
- hosts: node01
  gather_facts: false
  become: true

  tasks:
    - name: install git
      ansible.builtin.yum:
        name: git
        state: <present or installed>

inventory ファイルはこんな感じです。

[sv]
node01 ansible_host=192.168.1.111
node02 ansible_host=192.168.1.112

[sv:vars]
ansible_user=ansible
ansible_password=ansible
ansible_become=yes
ansible_become_password=ansible

実際に実行してみる

最初、state: present で実行します。

$ ansible-playbook -i inventory.ini install_git.yml
PLAY [node01] ***********************************************************************************
TASK [install git] ******************************************************************************
changed: [node01]
PLAY RECAP **************************************************************************************
node01                     : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

次に、state: installed に変更し、実行します。

$ ansible-playbook -i inventory.ini install_git.yml
PLAY [node01] ***********************************************************************************
TASK [install git] ******************************************************************************
changed: [node01]
PLAY RECAP **************************************************************************************
node01                     : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

どちらもgitパッケージをインストールできました。

ansible-playbook コマンドの -vvv オプションで見てみる

Playbook上で、state: presentに変更してデバッグオプションを入れて(-vvv)実行すると、が"state": "present"になっていることが確認できました。

$ ansible-playbook -i inventory.ini install_git.yml -vvv
changed: [node01] => {
    ・・・
    },
    "invocation": {
        "module_args": {
            "allow_downgrade": false,
             ・・・
            "skip_broken": false,
            "state": "present",
            "update_cache": false,
        }

今度はPlaybook上をstate: installedに変更して実行した結果、"state": "installed"になっていることが確認できました。

$ ansible-playbook -i inventory.ini install_git.yml -vvv
changed: [node01] => {
    ・・・
    },
    "invocation": {
        "module_args": {
            "allow_downgrade": false,
             ・・・
            "skip_broken": false,
            "state": "installed",
            "update_cache": false,
        }

結果としては、stateの部分が変わっただけで、これといって特に大きな変更は見当たりませんでした。

yumモジュールのソースコードを見てみた

ソースコード上ではどのように動作しているのか、githubで公開されているyumモジュールのソースコードを確認してみました。

github.com

すると、1645行目あたりに記述がありました。

        if self.state == 'latest' or self.update_only:
            if self.disable_gpg_check:
                self.yum_basecmd.append('--nogpgcheck')
            if self.security:
                self.yum_basecmd.append('--security')
            if self.bugfix:
                self.yum_basecmd.append('--bugfix')
            res = self.latest(pkgs, repoq)
        elif self.state in ('installed', 'present'):
            if self.disable_gpg_check:
                self.yum_basecmd.append('--nogpgcheck')
            res = self.install(pkgs, repoq)
        elif self.state in ('removed', 'absent'):
            res = self.remove(pkgs, repoq)

上記のコードの9行目あたりにある、elif self.state in ('installed', 'present'): ですが、if文で書かれていて、ここにstateinstalled と present は同様の動作をするように書かれています。
他にも、stateremoved と absentどちらを指定しても、パッケージ削除の動作をしています。

まとめ

まとめると以下のようになります。

  • yum モジュール の state を present と installed は同じ動作をする
  • debug で確認しても同様の結果だった。
  • ソースコード上で確認すると if state in ['installed', 'present']: となっており、
    同じ動作をするようになっていた。
  • installed は present の Alias である。

ということが確認できました。