Moleculeを使用して、Redis-Clusterを構築するplaybookのテストを行う

# Moleculeを使用して、Redis-Clusterを構築するplaybookのテストを行う

Molecule はAnsible のロールの開発とテストを支援するように設計されているAnsible のテストツールです。

Molecule を使用することで、Docker のコンテナに対してAnsible のロールを実行し、playbook の検証を行うことが可能です。

「なるほど、分からん」という方は、下記をご一読いただければと思います。

- [SlideShare: Molecule入門](https://www.slideshare.net/nikuyoshi/molecule-125233650)

今回は以前に作成したplaybook をカスタマイズして、Molecule を使い方を外部記憶として残そうと思います。

## 前提条件
- Ansible はインストール済みであるとする
- config、inventory の設定は完了済みであるとする
- 鍵生成、鍵交換、疎通確認は完了済みであるとする
- proxy 環境下ではないものとする
- 一つのサーバーにRedis のMaster とSlave を配置する

## 実行環境
```
# cat /etc/redhat-release 
CentOS Linux release 7.7.1908 (Core)

# ansible --version
ansible 2.9.1
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /bin/ansible
  python version = 2.7.5 (default, Aug  7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
```

## 構成図のイメージ

構成図は下記のようになります。docker のコンテナはmolecule コマンドで生成します。

![molecule.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274407/322aad20-dbb0-663b-169d-6fc9ee984778.png)


## Molecule のインストール

下記の手順でMolecule をインストールします。

```
# yum install -y epel-release
# yum install -y gcc python-pip python-devel openssl-devel libselinux-python
# yum install -y docker        ※ 既にdokcer をインストール済みである場合、本手順は飛ばしてください
# pip install -U pip
# pip install -U setuptools
# pip install molecule docker-py pyparsing PyYAML
```

Molecule のインストールが完了したので、Molecule のバージョンを確認しましょう。

```
# molecule --version
molecule, version 2.22
```

### 個人的にハマったポイント

Molecule を使用する際は、docker か docker-py のどちらかを使用したほうがよいです。両方ともインストールされている場合はいったん、アンインストールしてから、どちらかを再インストールしましょう。今回はdocker-py を使用します。

```
# pip uninstall docker
# pip uninstall docker-py
# pip install docker-py
```


## 今回のテスト対象となるplaybook について

今回テスト対象となるplaybook は、以前作成したRedis-Cluster を構築するplaybook となります。

- [Ansibleを使用して、CentOS 7にRedis-Clusterを構築する](https://qiita.com/GENO/items/a87e4fca1e883289ef3a)

下記のようなディレクトリ構成を作成し、上記のリンク先のplaybook の内容をロールとして切り出しします。

```
# tree -r /etc/ansible/roles/
/etc/ansible/roles/
├── redis-cluster
│   ├── start_redis_server
│   │   └── tasks
│   │       └── main.yml
│   ├── setup_redis_conf
│   │   └── tasks
│   │       └── main.yml
│   ├── install_redis
│   │   └── tasks
│   │       └── main.yml
│   ├── create_redis_user
│   │   └── tasks
│   │       └── main.yml
│   ├── create_redis_group
│   │   └── tasks
│   │       └── main.yml
│   ├── create_redis_config_directories
│   │   └── tasks
│   │       └── main.yml
│   └── copy_redis_conf
│       └── tasks
│           └── main.yml
└── common
    └── install_list_of_packages
        └── tasks
            └── main.yml
```

それに合わせて、下記のようなロールを使用したplaybook を作成します。

```yaml
- name: deploy Redis-Cluster
  hosts: localhost
  vars:
    ansible_become: yes
    install_list_of_packages:
      - epel-release
      - http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
    redis_port_number:
      - 7000
      - 7001
      - 7002
      - 7003
      - 7004
      - 7005
  roles:
    - install_list_of_packages
    - install_redis
    - create_redis_group
    - create_redis_user
    - create_redis_config_directories
    - copy_redis_conf
    - setup_redis_conf
    - start_redis_server
```

playbook で呼ばれるロールの確認ついでに、**ansible-lint** を使用して静的解析を行います。

```
# cd /etc/ansible/playbook
# ansible-lint deploy_redis_cluster.yml -v
```

```
Examining deploy_redis_cluster.yml of type playbook
Examining ../roles/common/install_list_of_packages/tasks/main.yml of type tasks
Examining ../roles/redis-cluster/install_redis/tasks/main.yml of type tasks
Examining ../roles/redis-cluster/create_redis_group/tasks/main.yml of type tasks
Examining ../roles/redis-cluster/create_redis_user/tasks/main.yml of type tasks
Examining ../roles/redis-cluster/create_redis_config_directories/tasks/main.yml of type tasks
Examining ../roles/redis-cluster/copy_redis_conf/tasks/main.yml of type tasks
Examining ../roles/redis-cluster/setup_redis_conf/tasks/main.yml of type tasks
Examining ../roles/redis-cluster/start_redis_server/tasks/main.yml of type tasks
[301] Commands should not change things if nothing needs doing
../roles/redis-cluster/start_redis_server/tasks/main.yml:2
Task/Handler: Start Redis-Server

```

## Molecule によるテストの前準備

### molecule init の実行

下記のコマンドを実行し、molecule ディレクトリを生成します。

```
# cd /etc/ansible/roles/redis-cluster
# molecule init scenario -r redis-cluster
--> Initializing new scenario default...
Initialized scenario in /etc/ansible/roles/redis-cluster/molecule/default successfully.

# ll | grep molecule
drwxr-xr-x 3 root root 21  2月 12 22:43 molecule
```

### Molecule のディレクトリ構成の確認

下記のコマンドを実行し、molecule ディレクトリ構成を確認します。

```
# tree -r molecule/
molecule/
└── default
    ├── tests
    │   ├── test_default.py
    │   └── __pycache__
    │       └── test_default.cpython-36.pyc
    ├── playbook.yml
    ├── molecule.yml
    ├── INSTALL.rst
    └── Dockerfile.j2
```

### 各ファイルの設定

Molecule を実行するために、下記3つのファイルを設定します。

```
molecule/
└── default
    ├── playbook.yml         # 検証するplaybook の内容
    ├── molecule.yml         # molecule を実行する際の設定ファイル
    └── Dockerfile.j2        # playbook の検証対象となるコンテナ起動に使用するdocker イメージの設定ファイル
```

- molecule/default/Dockerfile.j2


```dockerfile
# Molecule managed

{% if item.registry is defined %}
FROM {{ item.registry.url }}/{{ item.image }}
{% else %}
FROM {{ item.image }}
{% endif %}

{% if item.env is defined %}
{% for var, value in item.env.items() %}
{% if value %}
ENV {{ var }} {{ value }}
{% endif %}
{% endfor %}
{% endif %}

RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates iproute2 && apt-get clean; \
    elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash iproute && dnf clean all; \
    elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash iproute && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
    elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml iproute2 && zypper clean -a; \
    elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
    elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates iproute2 && xbps-remove -O; fi

CMD ["/sbin/init"]
```

- molecule/default/molecule.yml


```yaml
---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
platforms:
  - name: redis-cluster-instance            # コンテナの名前
    image: centos:7                         # 使用するイメージ
    pull: False
    command: /sbin/init                     # systemd を必要とする場合に必要なオプション
    tmps:                                   # systemd を必要とする場合に必要なオプション
      - /run                                # systemd を必要とする場合に必要なオプション
      - /tmp                                # systemd を必要とする場合に必要なオプション
    volumes:                                # systemd を必要とする場合に必要なオプション
      - /sys/fs/cgroup:/sys/fs/cgroup:ro    # systemd を必要とする場合に必要なオプション
provisioner:
  name: ansible
  log: true
  config_options:
    defaults:
      stdout_callback: yaml
      bin_ansible_callbacks: true
  lint:
    name: ansible-lint
verifier:
  name: testinfra
  lint:
    name: flake8
```

- molecule/default/playbook.yml

```yaml
---
- name: Converge
  hosts: all
  vars:
    ansible_become: yes
    install_list_of_packages:
      - epel-release
      - http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
    redis_port_number:
      - 7000
      - 7001
      - 7002
      - 7003
      - 7004
      - 7005
  roles:
    - ../roles/common/install_list_of_packages
    - ../roles/redis-cluster/install_redis
    - ../roles/redis-cluster/create_redis_group
    - ../roles/redis-cluster/create_redis_user
    - ../roles/redis-cluster/create_redis_config_directories
    - ../roles/redis-cluster/copy_redis_conf
    - ../roles/redis-cluster/setup_redis_conf
    - ../roles/redis-cluster/start_redis_server
```

### Molecule で使用するテスト用の環境を作成

下記のコマンドを実行し、Molecule で使用するテスト用のdocker コンテナを作成します。

```
# molecule create
```

```
--> Validating schema /etc/ansible/roles/redis-cluster/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix
    
└── default
    ├── dependency
    ├── create
    └── prepare
    
--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
--> Scenario: 'default'
--> Action: 'create'
--> Sanity checks: 'docker'
    
    PLAY [Create] ******************************************************************
    
    TASK [Log into a Docker registry] **********************************************
    skipping: [localhost] => (item={'pull': False, 'command': u'/sbin/init', 'name': u'redis-cluster-instance', 'volumes': [u'/sys/fs/cgroup:/sys/fs/cgroup:ro'], 'image': u'centos:7', 'tmps': [u'/run', u'/tmp']}) 
    
    TASK [Create Dockerfiles from image names] *************************************
    changed: [localhost] => (item={'pull': False, 'command': u'/sbin/init', 'name': u'redis-cluster-instance', 'volumes': [u'/sys/fs/cgroup:/sys/fs/cgroup:ro'], 'image': u'centos:7', 'tmps': [u'/run', u'/tmp']})
    
    TASK [Determine which docker image info module to use] *************************
    ok: [localhost]
    
    TASK [Discover local Docker images] ********************************************
    ok: [localhost] => (item={u'changed': True, u'uid': 0, u'dest': u'/root/.cache/molecule/redis-cluster/default/Dockerfile_centos_7', u'owner': u'root', 'diff': [], u'size': 914, u'src': u'/root/.ansible/tmp/ansible-tmp-1581521805.53-277333827862458/source', 'ansible_loop_var': u'item', u'group': u'root', 'item': {'pull': False, 'command': u'/sbin/init', 'name': u'redis-cluster-instance', 'volumes': [u'/sys/fs/cgroup:/sys/fs/cgroup:ro'], 'image': u'centos:7', 'tmps': [u'/run', u'/tmp']}, u'checksum': u'af0854dd479325baac9797c25a580b67bf4faa0e', u'md5sum': u'89ab4f11c078a9f793fa4f94a495193d', 'failed': False, u'state': u'file', u'gid': 0, u'mode': u'0644', u'invocation': {u'module_args': {u'directory_mode': None, u'force': True, u'remote_src': None, u'dest': u'/root/.cache/molecule/redis-cluster/default/Dockerfile_centos_7', u'selevel': None, u'_original_basename': u'Dockerfile.j2', u'delimiter': None, u'regexp': None, u'owner': None, u'follow': False, u'validate': None, u'local_follow': None, u'src': u'/root/.ansible/tmp/ansible-tmp-1581521805.53-277333827862458/source', u'group': None, u'unsafe_writes': None, u'checksum': u'af0854dd479325baac9797c25a580b67bf4faa0e', u'seuser': None, u'serole': None, u'content': None, u'setype': None, u'mode': None, u'attributes': None, u'backup': False}}})
    
    TASK [Build an Ansible compatible image (new)] *********************************
    ok: [localhost] => (item=molecule_local/centos:7)
    
    TASK [Build an Ansible compatible image (old)] *********************************
    skipping: [localhost] => (item=molecule_local/centos:7) 
    
    TASK [Create docker network(s)] ************************************************
    
    TASK [Determine the CMD directives] ********************************************
    ok: [localhost] => (item={'pull': False, 'command': u'/sbin/init', 'name': u'redis-cluster-instance', 'volumes': [u'/sys/fs/cgroup:/sys/fs/cgroup:ro'], 'image': u'centos:7', 'tmps': [u'/run', u'/tmp']})
    
    TASK [Create molecule instance(s)] *********************************************
    changed: [localhost] => (item=redis-cluster-instance)
    
    TASK [Wait for instance(s) creation to complete] *******************************
    FAILED - RETRYING: Wait for instance(s) creation to complete (300 retries left).
    changed: [localhost] => (item={'ansible_loop_var': u'item', u'ansible_job_id': u'429823489509.31439', 'item': {'pull': False, 'command': u'/sbin/init', 'name': u'redis-cluster-instance', 'volumes': [u'/sys/fs/cgroup:/sys/fs/cgroup:ro'], 'image': u'centos:7', 'tmps': [u'/run', u'/tmp']}, u'started': 1, 'changed': True, 'failed': False, u'finished': 0, u'results_file': u'/root/.ansible_async/429823489509.31439'})
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=7    changed=3    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0
    
--> Scenario: 'default'
--> Action: 'prepare'
Skipping, prepare playbook not configured.
```

docker コンテナが生成されているかを確認します。

```
# docker ps -a | grep molecule
CONTAINER ID        IMAGE                     COMMAND       CREATED             STATUS                   PORTS        NAMES
76b96d17d540        molecule_local/centos:7   "/sbin/init"  About a minute ago  Up About a minute                     moleredis-cluster-instance
```

### Molecule による静的解析

下記のコマンドを実行し、Molecule によるロールの静的解析を行います。

```
# molecule lint
```

```log
--> Validating schema /etc/ansible/roles/redis-cluster/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix
    
└── default
    └── lint
    
--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in /etc/ansible/roles/redis-cluster/...
Lint completed successfully.
--> Executing Flake8 on files found in /etc/ansible/roles/redis-cluster/molecule/default/tests/...
Lint completed successfully.
--> Executing Ansible Lint on /etc/ansible/roles/redis-cluster/molecule/default/playbook.yml...
    [301] Commands should not change things if nothing needs doing
    start_redis_server/tasks/main.yml:2
    Task/Handler: Start Redis-Server

```

## molecule によるテストの実行

下記のコマンドを実行し、docker コンテナに対して、playbook の検証を行います。

```
# molecule converge
```

```
--> Validating schema /etc/ansible/roles/redis-cluster/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix
    
└── default
    ├── dependency
    ├── create
    ├── prepare
    └── converge
    
--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
--> Scenario: 'default'
--> Action: 'create'
Skipping, instances already created.
--> Scenario: 'default'
--> Action: 'prepare'
Skipping, prepare playbook not configured.
--> Scenario: 'default'
--> Action: 'converge'
    
    PLAY [Converge] ****************************************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [redis-cluster-instance]
    
    TASK [../roles/common/install_list_of_packages : Install a list of packages] ***
    changed: [redis-cluster-instance]
    
    TASK [../roles/redis-cluster/install_redis : Install redis] ********************
    changed: [redis-cluster-instance]
    
    TASK [../roles/redis-cluster/create_redis_group : Ensure group "redis" exists] ***
    ok: [redis-cluster-instance]
    
    TASK [../roles/redis-cluster/create_redis_user : Create redis user] ************
    changed: [redis-cluster-instance]
    
    TASK [../roles/redis-cluster/create_redis_config_directories : mkdir redis config directory] ***
    changed: [redis-cluster-instance] => (item=7000)
    changed: [redis-cluster-instance] => (item=7001)
    changed: [redis-cluster-instance] => (item=7002)
    changed: [redis-cluster-instance] => (item=7003)
    changed: [redis-cluster-instance] => (item=7004)
    changed: [redis-cluster-instance] => (item=7005)
    
    TASK [../roles/redis-cluster/copy_redis_conf : Copy redis.conf] ****************
    changed: [redis-cluster-instance] => (item=7000)
    changed: [redis-cluster-instance] => (item=7001)
    changed: [redis-cluster-instance] => (item=7002)
    changed: [redis-cluster-instance] => (item=7003)
    changed: [redis-cluster-instance] => (item=7004)
    changed: [redis-cluster-instance] => (item=7005)
    
    TASK [../roles/redis-cluster/setup_redis_conf : Setting bind IPv4Address] ******
    changed: [redis-cluster-instance] => (item=7000)
    changed: [redis-cluster-instance] => (item=7001)
    changed: [redis-cluster-instance] => (item=7002)
    changed: [redis-cluster-instance] => (item=7003)
    changed: [redis-cluster-instance] => (item=7004)
    changed: [redis-cluster-instance] => (item=7005)
    
    TASK [../roles/redis-cluster/setup_redis_conf : Setting port number] ***********
    changed: [redis-cluster-instance] => (item=7000)
    changed: [redis-cluster-instance] => (item=7001)
    changed: [redis-cluster-instance] => (item=7002)
    changed: [redis-cluster-instance] => (item=7003)
    changed: [redis-cluster-instance] => (item=7004)
    changed: [redis-cluster-instance] => (item=7005)
    
    TASK [../roles/redis-cluster/setup_redis_conf : Setting cluster-enabled] *******
    changed: [redis-cluster-instance] => (item=7000)
    changed: [redis-cluster-instance] => (item=7001)
    changed: [redis-cluster-instance] => (item=7002)
    changed: [redis-cluster-instance] => (item=7003)
    changed: [redis-cluster-instance] => (item=7004)
    changed: [redis-cluster-instance] => (item=7005)
    
    TASK [../roles/redis-cluster/setup_redis_conf : Setting cluster-config-file] ***
    changed: [redis-cluster-instance] => (item=7000)
    changed: [redis-cluster-instance] => (item=7001)
    changed: [redis-cluster-instance] => (item=7002)
    changed: [redis-cluster-instance] => (item=7003)
    changed: [redis-cluster-instance] => (item=7004)
    changed: [redis-cluster-instance] => (item=7005)
    
    TASK [../roles/redis-cluster/setup_redis_conf : Setting cluster-node-timeout] ***
    changed: [redis-cluster-instance] => (item=7000)
    changed: [redis-cluster-instance] => (item=7001)
    changed: [redis-cluster-instance] => (item=7002)
    changed: [redis-cluster-instance] => (item=7003)
    changed: [redis-cluster-instance] => (item=7004)
    changed: [redis-cluster-instance] => (item=7005)
    
    TASK [../roles/redis-cluster/setup_redis_conf : Setting appendonly] ************
    changed: [redis-cluster-instance] => (item=7000)
    changed: [redis-cluster-instance] => (item=7001)
    changed: [redis-cluster-instance] => (item=7002)
    changed: [redis-cluster-instance] => (item=7003)
    changed: [redis-cluster-instance] => (item=7004)
    changed: [redis-cluster-instance] => (item=7005)
    
    TASK [../roles/redis-cluster/setup_redis_conf : Setting logfile] ***************
    changed: [redis-cluster-instance] => (item=7000)
    changed: [redis-cluster-instance] => (item=7001)
    changed: [redis-cluster-instance] => (item=7002)
    changed: [redis-cluster-instance] => (item=7003)
    changed: [redis-cluster-instance] => (item=7004)
    changed: [redis-cluster-instance] => (item=7005)
    
    TASK [../roles/redis-cluster/start_redis_server : Start Redis-Server] **********
    changed: [redis-cluster-instance] => (item=7000)
    changed: [redis-cluster-instance] => (item=7001)
    changed: [redis-cluster-instance] => (item=7002)
    changed: [redis-cluster-instance] => (item=7003)
    changed: [redis-cluster-instance] => (item=7004)
    changed: [redis-cluster-instance] => (item=7005)
    
    PLAY RECAP *********************************************************************
    redis-cluster-instance     : ok=15   changed=13   unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

無事、playbook が完了いたしました。下記のコマンドを実行して、docker コンテナにログインしましょう。

```
# molecule login
--> Validating schema /etc/ansible/roles/redis-cluster/molecule/default/molecule.yml.
Validation completed successfully.
```

### Redisのバージョン確認

docker コンテナにインストールされているRedis のバージョンを確認してみましょう。

```
[root@redis-cluster-instance /]# redis-cli --version
redis-cli 5.0.7
[root@redis-cluster-instance /]# redis-server --version
Redis server v=5.0.7 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=91145edf25c40cd7
```

### Redis-Clusterの構築

それでは、docker コンテナで下記のコマンドを叩いて、Redis-Clusterを構築しましょう。

```
[root@redis-cluster-instance /]# redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1
```

```
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7004 to 127.0.0.1:7000
Adding replica 127.0.0.1:7005 to 127.0.0.1:7001
Adding replica 127.0.0.1:7003 to 127.0.0.1:7002
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 7bc5f80dd90213eeb3361571ea3dbbb3291e97d8 127.0.0.1:7000
   slots:[0-5460] (5461 slots) master
M: d59fe4d92511e504ba5cf7f9001cb8ea7def6b7f 127.0.0.1:7001
   slots:[5461-10922] (5462 slots) master
M: d53abfb685e3592a40affcc763a39036f3027103 127.0.0.1:7002
   slots:[10923-16383] (5461 slots) master
S: 906638361f1c40cb349d0a4f507cadb497a7d02a 127.0.0.1:7003
   replicates d53abfb685e3592a40affcc763a39036f3027103
S: ebb4b50135a4ea7c0ad5061cc169d1a14f760fb1 127.0.0.1:7004
   replicates 7bc5f80dd90213eeb3361571ea3dbbb3291e97d8
S: 483c521c5a2b10d23f7335d57d960c274645f2b5 127.0.0.1:7005
   replicates d59fe4d92511e504ba5cf7f9001cb8ea7def6b7f
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
..
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 7bc5f80dd90213eeb3361571ea3dbbb3291e97d8 127.0.0.1:7000
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: d53abfb685e3592a40affcc763a39036f3027103 127.0.0.1:7002
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 483c521c5a2b10d23f7335d57d960c274645f2b5 127.0.0.1:7005
   slots: (0 slots) slave
   replicates d59fe4d92511e504ba5cf7f9001cb8ea7def6b7f
S: ebb4b50135a4ea7c0ad5061cc169d1a14f760fb1 127.0.0.1:7004
   slots: (0 slots) slave
   replicates 7bc5f80dd90213eeb3361571ea3dbbb3291e97d8
M: d59fe4d92511e504ba5cf7f9001cb8ea7def6b7f 127.0.0.1:7001
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 906638361f1c40cb349d0a4f507cadb497a7d02a 127.0.0.1:7003
   slots: (0 slots) slave
   replicates d53abfb685e3592a40affcc763a39036f3027103
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
```

### Redis-Clusterの状態確認

docker コンテナ内で構築したRedis-Cluster の状態を確認してみましょう。

```
[root@redis-cluster-instance /]# redis-cli -p 7000 cluster nodes
```

```
d53abfb685e3592a40affcc763a39036f3027103 127.0.0.1:7002@17002 master - 0 1581523028526 3 connected 10923-16383
483c521c5a2b10d23f7335d57d960c274645f2b5 127.0.0.1:7005@17005 slave d59fe4d92511e504ba5cf7f9001cb8ea7def6b7f 0 1581523028316 6 connected
ebb4b50135a4ea7c0ad5061cc169d1a14f760fb1 127.0.0.1:7004@17004 slave 7bc5f80dd90213eeb3361571ea3dbbb3291e97d8 0 1581523028113 5 connected
d59fe4d92511e504ba5cf7f9001cb8ea7def6b7f 127.0.0.1:7001@17001 master - 0 1581523026273 2 connected 5461-10922
906638361f1c40cb349d0a4f507cadb497a7d02a 127.0.0.1:7003@17003 slave d53abfb685e3592a40affcc763a39036f3027103 0 1581523028000 4 connected
7bc5f80dd90213eeb3361571ea3dbbb3291e97d8 127.0.0.1:7000@17000 myself,master - 0 1581523026000 1 connected 0-5460
```

## Molecule で使用したテスト用の環境を削除

下記のコマンドを実行し、Molecule で使用したテスト用のdocker コンテナを削除します。

```
[root@redis-cluster-instance /]# exit
exit
# molecule destroy
```

```
--> Validating schema /etc/ansible/roles/redis-cluster/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix
    
└── default
    ├── dependency
    ├── cleanup
    └── destroy
    
--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
--> Scenario: 'default'
--> Action: 'cleanup'
Skipping, cleanup playbook not configured.
--> Scenario: 'default'
--> Action: 'destroy'
    
    PLAY [Destroy] *****************************************************************
    
    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=redis-cluster-instance)
    
    TASK [Wait for instance(s) deletion to complete] *******************************
    changed: [localhost] => (item={'ansible_loop_var': u'item', u'ansible_job_id': u'412030201772.27418', 'item': {'pull': False, 'command': u'/sbin/init', 'name': u'redis-cluster-instance', 'volumes': [u'/sys/fs/cgroup:/sys/fs/cgroup:ro'], 'image': u'centos:7', 'tmps': [u'/run', u'/tmp']}, u'started': 1, 'changed': True, 'failed': False, u'finished': 0, u'results_file': u'/root/.ansible_async/412030201772.27418'})
    
    TASK [Delete docker network(s)] ************************************************
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=2    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
    
--> Pruning extra files from scenario ephemeral directory
```

docker コンテナが削除されているかを確認します。

```
# docker ps -a | grep molecule
# 
```

## まとめ
Molecule を使用して、Redis-Cluster を構築するplaybook の静的解析・ロールのテストを行うことができました。

Molecule を使用して思ったことは、新規に作成したインスタンスやVM を汚さずに、playbook の試行錯誤ができるのが嬉しかったです。失敗したら、コンテナを削除・再作成することで、またまっさらな状態で検証できますからね。次は、GitLab Runner とMolecule を組み合わせて、playbook のCI/CD によるテストの自動化を行いたいですね。スケジュール設定をしておけば、毎日夜中にテストできますし、いつテストしたっけ？ってなることがなくなりますし。下記のURLのような状態に持っていけるのがより良いかなと思います。

- [ANSIBLE TOWER + GITLAB + MOLECULEで作るCI/CD環境](https://sky-joker.tech/2019/09/23/ansible-towre-gitlab-molecule%E3%81%A7%E4%BD%9C%E3%82%8Bci-cd%E7%92%B0%E5%A2%83/)

## 参考書籍・参考URL
- [Ansible構築・運用ガイドブック（Compass Booksシリーズ） インフラ自動化のための現場のノウハウ](https://book.mynavi.jp/ec/products/detail/id=112246)
- [SlideShare: Molecule入門](https://www.slideshare.net/nikuyoshi/molecule-125233650)
- [molecule でansible の role と playbook をテストする](https://techblog.szksh.cloud/ansible-molecule/)
- [molecule: Installation](https://molecule.readthedocs.io/en/latest/installation.html)
- [molecule: Getting Started Guide](https://molecule.readthedocs.io/en/latest/getting-started.html)
- [molecule: Command Line Reference](https://molecule.readthedocs.io/en/latest/usage.html)
- [molecule: Common Molecule Use Cases](https://molecule.readthedocs.io/en/latest/examples.html)
- [BACK 2 CODE: Molecule](https://www.back2code.me/2019/08/molecule/)