RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

ラクスサービスを管理するAnsibleコードの共通テンプレートを作った話

f:id:tech-rakus:20220127103256p:plain

こんにちは、弊社サービスのインフラを運用している id:keijiu (ijikeman)です。

今回は、「ラクスサービスを管理するAnsibleコードの共通テンプレートを作った話」を記載します。

[対象読者]

  • 対象読者:

    • Ansibleでサーバの管理を行っている人
    • またはこれから行いたいと考えている人
  • 記事を読んでわかること:

    • Ansibleの実装方法(汎用化)
    • パラメータ(vars)の記載箇所
    • Ansibleの学習資料の作成
    • Ansibleコード規約

目次

背景

私が入社した2018年時点ではラクスのインフラはAnsibleの導入が進んでおらず、何度か勉強会を行った状況でした。

またAnsibleはおろかChefや古くはPuppetなどの構成管理ツールの導入は行われておりませんでした。

そこで前職でChefを使ってLinuxサーバの提供サービス設計・開発に携わった経験から、Ansibleの本格導入への白羽の矢が立ったということで、導入までの活動をまとめましたのでその内容を紹介致します。

1. コーディング規約策定

ラクスでは年々社員数が大幅に増えており、インフラ開発部でも毎年のように新卒や中途社員が増えています。

後から入社しAnsibleに携わる方にもできるだけ統一した書き方でAnsibleコードを書いていただく必要がある為、 Ansibleの基本構文となるYAMLのコーディング規約を策定しました。

■ポイント
  • ルールを単に記載するだけではわかりにくい為、記述例を挙げる
    • 記述例によって文章よりも視覚的に理解が深まる狙いです
  • ルールを以下の2種類に分けて記載
    • [MUST] ... 必ず必要
    • [SHOULD] ... より望ましい

コーディング規約一部例

#### 拡張子
- [MUST] YAMLファイルは.ymlとする。

#### タブ/スペース/行間
- [MUST] インデントは2スペースとする。
- [MUST] 変数名の前後に空白を入れる。
- [MUST] コロン:のあとには半角スペース1つを必ず入れる。
- [MUST] ハイフン-のあとには半角スペース1つを必ず入れる。
- [MUST] debugを除く各モジュールには必ず、- name:を付けて処理内容にコメントを記述する。

#good
---
- name: StdOut 'hoge'
  shell: {{ BIN_ECHO }} 'hoge'

#bad
---
-name:test
    shell:{{BIN_ECHO}} 'hoge'
---

### Command/Shellについて
- [SHOULD] 冪等性を保証が困難となる為、できるだけansibleのモジュールで実装し、やむ負えない場合のみ使用する。書き換えはsedを使わずlineinfileモジュールを使うなど
- [MUST] バックグランド実行(&)やパイプ、リダイレクト(>, >>)を利用しない場合はcommandを利用する。
#good
---
- name: Exec command A
  command: {{ BIN_ECHO }} 'hoge'

- name: Exec command B
  shell: {{ BIN_ECHO }} 'hoge' > /etc/fuga

#bad
---
- name: Exec command A
  shell: {{BIN_ECHO}} 'hoge'
---

2. 共通処理の標準化

次に行ったことが、共通処理の標準化になります。

Linuxサーバを構築する場合に必要な処理を、大きく3つのカテゴリにわけて共通化しております。

[カテゴリ]

  • [共通設定]: 無駄なサービス停止、パッケージ削除、パッケージ追加、グループ操作、ユーザ操作、ディレクトリ操作、ファイル操作等
  • [OS標準機能の設定]: selinux設定, sysctl設定、ネットワーク設定、リポジトリ設定等
  • [起動するデーモン毎の処理]: テンプレート化して各デーモン用のrole毎に複製して実装

[共通設定]のAnsible実装例

パッケージの一斉インストール処理を共通化しています。

■ポイント
  • with_itemsを定義することで、リストを使ってループ処理しています
  • パラメータの記載箇所を分けることで、適用する影響範囲を限定することができます(次章 にて説明します)
$ roles/common/tasks/main.yml
---
- name: Install Packages
  yum:
    name: "{{ item.NAME }}"
    state: 'installed'
    enablerepo: "{{ item.ENABLEREPO | default() }}"
  with_items:
    - "{{ COMMON_INSTALL_PACKAGES | default([]) }}"
    - "{{ COMMON_HOSTGROUP_INSTALL_PACKAGES | default([]) }}"
    - "{{ INVENTORY_INSTALL_PACKAGES | default([]) }}"
    - "{{ INVENTORY_HOSTGROUP_INSTALL_PACKAGES | default([]) }}"
    - "{{ INVENTORY_HOSTVARS_INSTALL_PACKAGES | default([]) }}"
---

[OS標準機能の設定]

■ポイント
  • OSの設定は設定種別が多い為、同じカテゴリの処理をまとめてsetup_*.ymlとしています
  • main.ymlはimport_tasks:を使用し各処理をまとめたsetup_*.ymlを読み込んでいます。こうすることでosロール全体の見通しがよくなります。
$ roles/os/tasks/main.yml
---
- name: Setup Network
  import_tasks: setup_network.yml

- name: Setup Sysctl
  import_tasks: setup_sysctl.yml

- name: Setup Selinux
  import_tasks: setup_selinux.yml

[OS標準機能の設定]: ネットワーク設定の一部 ホスト名の設定

■ポイント
  • NETWORK_SETTINGS.HOSTNAMEを定義することでホスト名の構成情報を管理
  • ホスト単体の設定なので、host_vars/[HOSTNAME].ymlに記載するとよいでしょう
    • もし適用ステージを分けたい場合は、inventories/[STAGE_NAME]/host_vars/[HOSTNAME].ymlに書くとよいでしょう
$ host_vars/[HOSTNAME].yml
---
NETWORK_SETTINGS:
  HOSTNAME: 'hoge.fuga.rakus.co.jp'
$ roles/os/tasks/setup_network.yml
---
- name: Setup Hostname
  hostname:
    name: "{{ NETWORK_SETTINGS.HOSTNAME }}"
  when:
    - NETWORK_SETTINGS.HOSTNAME is defined

上記のように、どの種類のサーバでも同じような構築ルールで実装を行うことが多いと思います。
(例えばIPv6は基本的に無効とか)

そういった処理を共通処理化することでAnsibleコードの共有が行えます。
同じ実装でも記載する人によって微妙に変数名等の命名規則が違ったりすることで、Ansibleコードの流用ができないなど、保守性が下がることを避けることが出来ます。

3. 各サーバの構成管理情報(パラメータ)の記載場所の固定化

Ansibleでは、構成管理情報を多くの箇所に記載することができます。
これは便利な反面、複数名によるコーディングを行う場合は管理面で煩雑となります。

例えば以下のような場面が考えられます。

  • 複数種別のサーバを1つのAnsibleコードで管理している場合、同じパラメータ名が別々のファイルに記載されることで値が上書きされ、設定が変更されてしまう。

具体的には?

もともとのAnsibleコードに以下内容が記載されていたとします。

$ group_vars/all.yml
---
GATEWAY: 192.168.0.254

上記設定があることに気が付かず、誰かが以下のような設定を追記したとすると。。。

$ group_vars/webserver.yml
---
GATEWAY: 10.0.0.254

Ansibleの仕様では、ファイル同名のパラメータは優先度によって上書きされます。
この場合group_vars/webserver.ymlの方が優先度が高い為、webserverグループに属しているサーバ群のGATEWAYは書き換えられてしまいます。

この様に1人でAnsibleコードを記載している場合には問題ないかもしれませんが、
複数名ともなると共通認識を合わせコーディングする必要がある為、ルールの策定が必要です。

よって、構成管理情報の記載箇所にも共通ルールを作成しています。

各パラメータの記載場所

group_vars/
  └ all.yml ... COMMON_INSTALL_PACKAGES (全ての環境)
  └ HOSTGROUP名.yml ... COMMON_HOSTGROUP_INSTALL_PACKAGES (特定のグループにのみ)
inventories/
  └ [STAGE_NAME]/
    └ group_vars/
      └ all.yml ... INVENTORY_INSTALL_PACKAGES (特定のステージのみ)
      └ HOSTGROUP名.yml ... INVENTORY_HOSTGROUP_INSTALL_PACKAGES (特定のステージのみ 且つ 特定のグループにのみ)
    └ host_vars/
      └ HOSTNAME.yml ... INVENTORY_HOSTVARS_INSTALL_PACKAGES (特定のステージのみ 且つ 特定のホストにのみ)

この様に、記載場所と命名規則を予め決めておくことで、互いに影響しあったり値が上書きされることを防いでいます。
以下は、 [共通設定]のAnsible実装例 で紹介したパッケージインストール共通処理でも記載されている部分です。

with_items:
  - "{{ COMMON_INSTALL_PACKAGES | default([]) }}"
  - "{{ COMMON_HOSTGROUP_INSTALL_PACKAGES | default([]) }}"
  - "{{ INVENTORY_INSTALL_PACKAGES | default([]) }}"
  - "{{ INVENTORY_HOSTGROUP_INSTALL_PACKAGES | default([]) }}"
  - "{{ INVENTORY_HOSTVARS_INSTALL_PACKAGES | default([]) }}"

ですので例えば、

  • 管理しているサーバ全部に対して適用したい -> group_vars/all.ymlにCOMMON_INSTALL_PACKAGES:を定義する
  • 管理しているWebサーバ全部に対して適用したい -> group_vars/webserver.ymlにCOMMON_HOSTGROUP_INSTALL_PACKAGESを定義
  • 本番環境のWEBサーバにだけ適用したい -> inventories/production/group_vars/webserver.ymlにINVENTORY_HOSTGROUP_INSTALL_PACKAGESを定義

といった様に、パラメータを定義していきます。

4. マニュアル、Howto用意

前述した多くの仕様を、新しく参加してきたメンバーに理解してもらう為に勉強会を毎回開くにわけにもいきません。

とはいえ、一定のレベルになるまで学習してもらう必要がありますので、品質のばらつきが出ることを防ぐ為のHowto形式で学べるマニュアルを用意しています。

なお、テキストのみの教育プログラムではなく、理解度向上を狙い、
サンプルとAnsibleの実行画面のキャプチャ画像のアニメーションGifを使ったマニュアルを用意しました。

一部Howtoサンプル

# 3. 全てのサーバに対してパッケージの一括インストール
## 3-1. コードの解説
- 必要なパラメータは"NAME"と"ENABLEREPO"が設定できる
- ENABLEREPOは省略可能
- NAMEとVALUEを設定する必要がある
---
設定例:
$ group_vars/all.yml
---
COMMON_INSTALL_PACKAGES:
  -
    NAME: 'curl'
  -
    NAME: 'libc-client'
    ENABLEREPO: 'epel'

f:id:keijiu:20220124173550g:plain
HOWTO01

5. 自動Lintチェック

Ansibleのコードはgitlabにて管理しています。
そのため、gitlabにコードをPushする度に、Jenkinsが連動しYAML Lint, Ansible Lintのチェックを自動で行っております。

f:id:keijiu:20220125171500g:plain

細かな実装に関しては長くなる為、今回は割愛させていただきます。
機会がありましたら別途記事にしたいと思います。

6. 取り組みの効果について

がんばったおかげ?かわかりませんが

ラクスが展開するサービスのサーバを管理するAnsibleコードでは私が作ったAnsibleベースコードが使われるようになりました。

どのサービスのAnsibleコードを見ても基本的な構成が同じである為、他のサービスに関わっていた人が別のAnsibleコードを見てもパラメータの記載場所や処理の記載箇所処理内容が同じであったり、追加機能や修正箇所の移植が簡単に行えるなど運用管理が行いやすい状態となりました。

この様にコードの統一性の効果は高く、またそれによって品質の安定化にもつながる為、メリットは非常に高いです。
組織の拡大及び、管理対象範囲が増えていく毎にさらに効果が高まっていくと考えられます。

以上ありがとうございました。


  • エンジニア中途採用サイト
    ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
    ご興味ありましたら是非ご確認をお願いします。
    20210916153018
    https://career-recruit.rakus.co.jp/career_engineer/

  • カジュアル面談お申込みフォーム
    どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
    以下フォームよりお申込みください。
    rakus.hubspotpagebuilder.com

  • イベント情報
    会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください!
    rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.