GCPでElasticsearchのGCS Repository Pluginがインストールされたカスタムイメージを作ってみる
Elasticsearch Serviceがある今、自前でインスタンスを立ててElasticsearchの環境を作るという機会はなかなかないように思えますが、やったのでメモです。
ElasticsearchのSnapshotをGCSに上げるための設定をGCPのサービス群とPacker,Ansibleなどを使って実現する方法を書いていきます。
はじめに
まずElasticsearchのGCS Repository Pluginの設定の仕方について確認します。
- Pluginをインストールする
- GCSのBucketを作成する
- Service Accountを作成し、GCSに読み書きできる権限をつける
- Service AccountをElasticsearch keystoreに登録する
Google Cloud Storage Repository Plugin | Elasticsearch Plugins and Integrations [6.6] | Elastic
Getting started | Elasticsearch Plugins and Integrations [6.6] | Elastic
Client Settings | Elasticsearch Plugins and Integrations [6.6] | Elastic
Repository Settings | Elasticsearch Plugins and Integrations [6.6] | Elastic
PluginのインストールとService AccountのJSONキーをElasticsearch Keystoreに登録するところをAnsibleで行います。
そして、その構成のカスタムイメージをPackerで作ってみようと思います。
ディレクトリ構成
elasticsearch ├─group_vars │ └─all │ ├─vars.yml │ └─vault.yml ├─roles │ ├─elastic.elasticsearch //←省略 │ └─elasticsearch_others │ ├─defaults │ │ └─main.yml │ ├─tasks │ │ └─main.yml │ └─templates │ └─credentials_file.j2 ├─cloudbuild.yaml ├─packer.json ├─requirements.yml ├─site.yml └─vault-password-file.enc
Ansibleの作成
Elasticsearchの基本的な設定
公式がAnsibleのRoleを用意してくれているのでこれを使います。
GitHub - elastic/ansible-elasticsearch: Ansible playbook for Elasticsearch
適当にAnsibleを書いていきます。
- requirements.yml
--- - src: elastic.elasticsearch version: 6.6.0
- site.yml
--- - hosts: all become: yes connection: local roles: - elastic.elasticsearch #←ElasticのRole - elasticsearch_others #←GCS Repository Plugin関連
- group_vars/all/var.yml
--- es_instance_name: "node" es_config: cluster.name: "sample" node.name: "${HOSTNAME}" node.data: true node.master: true network.host: "0.0.0.0" discovery.zen.hosts_provider: "gce" cloud.gce.zone: ["asia-northeast1-a", "asia-northeast1-b", "asia-northeast1-c"] bootstrap.memory_lock: true es_plugins: - plugin: discovery-gce - plugin: repository-gcs
設定は割と適当です。
あと、ついでにGCE Discovery Pluginもインストールするようにします。
そうすることで、MasterノードのIPアドレスを指定することなく、クラスタリングが組めるようになります。
作成するGCEのインスタンスには「Cloud API アクセス スコープ」の「Compute Engine」を読み書き権限を付ける必要があります。
GCE Discovery Plugin | Elasticsearch Plugins and Integrations [6.6] | Elastic
Setting up GCE Discovery | Elasticsearch Plugins and Integrations [6.6] | Elastic
Elasticsearch Keystore周辺
Ansible Valut
KeystoreにService AccountのJSONキーを登録していくのですが、JSONキーをGitに含めるわけにはいかないので、ここでAnsible Vaultを使って暗号化します。
以下のようにAnsibleのVariableファイルを用意します。
- group_vars/all/vault.yml
--- service_account_json: | { "type": "service_account", "project_id": "[PROJECT-ID]", "private_key_id": "[KEY-ID]", "private_key": "-----BEGIN PRIVATE KEY-----\n[PRIVATE-KEY]\n-----END PRIVATE KEY-----\n", #省略 }
以下コマンドで暗号化します。この時に復号用のパスワードを設定します。
$ cd group_vars/all
$ ansible-vault encrypt vault.yml
Vault password:
Confirm Vault password:
Encryption successful
Ansible Task
上で定義した変数を使ってService AccountのJSONキーを登録していきます。
- roles/elasticsearch_others/templates/credentials_file.j2
{{ service_account_json }}
- roles/elasticsearch_others/tasks/main.yml
--- - name: create file template: src: credentials_file.j2 dest: /tmp/credentials_file - name: check service account set keystore shell: "{{ es_home }}/bin/elasticsearch-keystore list | grep gcs.client.default.credentials_file" environment: ES_PATH_CONF: "{{ conf_dir }}" register: service_account_keystore ignore_errors: yes - name: add service account shell: "{{ es_home }}/bin/elasticsearch-keystore add-file gcs.client.default.credentials_file /tmp/credentials_file" environment: ES_PATH_CONF: "{{ conf_dir }}" when: service_account_keystore | failed - name: delete file file: path: /tmp/credentials_file state: absent
elasticsearch-keystore add-file
でしかJSONキーをうまく登録できなかったので、いったんJSONキーをファイルに書き出して、登録した後に消しています。
Cloud KMS
Ansible VaultにてJSONキーは暗号化しましたが、Ansible Vaultの複合するためのパスワードがあり、このパスワードもGitに含めるわけにはいきません。
そこでAnsible Vaultの復号用パスワードをCloud KMSを使って管理します。
以下、クイックスタートどおりに行っていきます。
クイックスタート | Cloud KMS ドキュメント | Google Cloud
$ gcloud kms keyrings create ansible-keyring --location global $ gcloud kms keys create ansible-key --location global --keyring ansible-keyring --purpose encryption $ echo -n "Some text to be encrypted" > vault-password-file #先ほど設定したAnsible Vaultのパスワード $ gcloud kms encrypt --location global --keyring ansible-keyring --key ansible-key --plaintext-file vault-password-file --ciphertext-file vault-password-file.enc
これでAnsible Vaultの復号用パスワードの暗号化もできました。
Packerの作成
packer.jsonを作成していきます。
- packer.json
{ "builders": [ { "type": "googlecompute", "project_id": "{{user `project_id`}}", "source_image_family": "debian-9", "zone": "asia-northeast1-a", "disk_size": 20, "image_name" : "elasticsearch-{{timestamp}}", "image_description": "elasticsearch", "ssh_username": "packer" } ], "provisioners": [ { "type": "shell", "inline": [ "sudo apt update", "sudo apt install -y python python-pip", "sudo pip install ansible==2.7.0", "sudo pip install jmespath==0.9.3" ] }, { "type": "ansible-local", "playbook_dir": ".", "playbook_file": "site.yml", "galaxy_file": "requirements.yml", "extra_arguments": [ "--vault-password-file","vault-password-file", "-vvvv" ] }, { "type": "shell", "inline": [ "rm -rf /tmp/packer-provisioner-ansible-local" ] } ] }
provisioners
のtype
でansible
ではなくansible-local
を使ったのはgalaxyが使えなかったからです。
"playbook_dir": "."
でカレントディレクトリを対象のインスタンスに持って行ってくれます。
--vault-password-file
に復号後のファイルを指定します。
Cloud Build
次にPackerをCloud Buildで実行してカスタムイメージを作ってみます。
- cloudbuild.yaml
--- steps: - name: 'gcr.io/cloud-builders/gcloud' args: - kms - decrypt - --ciphertext-file=vault-password-file.enc - --plaintext-file=vault-password-file - --location=global - --keyring=ansible-keyring - --key=ansible-key - name: 'hashicorp/packer:1.3.3' args: - build - -var - project_id=$PROJECT_ID - packer.json
暗号化されたファイルを復号して、その後にPackerを実行しています。
あとはCloud Buildを実行すればカスタムイメージが作成されると思います。
$ gcloud builds submit --config cloudbuild.yaml ./
おわりに
Cloud Build, Cloud KMS, Ansible, Packerを使ってGitにセンシティブな情報を含めずにElasticsearchのGCS Repository Pluginがインストールされたカスタムイメージを作ってみました。
バケツリレー感が否めないですね。。。
その他、運用面とか考えるとElasticsearch Service(Elastic Cloud)使うのが楽そうでいいなーと思いました。