/var/log/pabotesu.log

日々の記録

minecraftサーバの実装を自動化しちゃいたい♡

Introduction - 自動化をしちゃいたい

お疲れ様です。皆様、最近のバズワード「自動化」をご存知でしょうか?

まぁ、自動化と言っても自分は特にIaC(Infrastructure as Code)というものに興味津々でございます。これでもインフラエンジニアの端くれらしいので...

で、話は少し逸れて先週ふと...「マイクラしてぇぇぇ(ゲスボイス)」と思い立ったのです。

イクラを普通にインストールして遊ぶのもすごく楽しいですが、せっかくだしAWS上にインスタンスを用意して、マイクラサーバをデプロイして、みんなで遊んじゃいたいということです。

今回は、それをインフラ周りの構築からマイクラサーバのデプロイ、起動までを自動化してみたら勉強にもなるかしらと思い、キーボードを叩くことにしました。

まぁ、自動化ツールは自分の野望にも必要になってくるものではあるので、少しずつ慣れていければと思います。(まぁ、自動化ツールはあくまでツールであって、「構築・運用における銀の弾丸になりうる」とまでは思っていないというのが私の考えではありますが...)

環境のご説明

前置きが長くなりましたね...

環境のご説明をいたします。

  • 環境
    • ホスト環境
      • OS:Linux
      • 利用したツール:Ansible、Terraform
    • サーバ側の環境
      • 利用クラウドプロバイダ:AWS
      • 利用したサービス:EC2、VPC
      • OS:AmazonLinux 2
      • 公開しているポート番号:80、443、22、25565
      • インストールパッケージ:java、screen (最新のものとなります)

コードのご紹介

書いたものについて

書いたコードはここに置いておきます。

利用方法とかは、ちゃんと README書くので許して...

で、今回は主に初めて利用するAnsibleのコードについてご紹介していきたいと思います。

Ansibleについては、Google先生に聞くか、某大佐の本を購入するか、モクモクの会に出てみるか、調べる方法はいくらでもあるので、割愛。

少し言うなら、エンジニアの皆さんがここ数年、生暖かい目線を送って盛り上げてる自動化ツールかなというのが自分の中での印象です。

んで、最初にファイルのツリーをご開帳。

  • ファイルのツリー
.
├── build_infra.yml
├── build_minecraft.yml
├── inventory
│   ├── aws_ec2.yml
│   └── inventory_file
├── key
│   └── mykey.pem
└── roles
    ├── make_games
    │   ├── files
    │   │   └── eula.txt
    │   └── tasks
    │       └── main.yml
    └── make_infra
        ├── infra_destroy.yml
        ├── tasks
        │   └── main.yml
        └── terraform
            ├── environment
            │   ├── instance
            │   │   ├── aws_ec2.tf
            │   │   ├── output.tf
            │   │   └── variables.tf
            │   ├── main.tf
            │   ├── network
            │   │   ├── aws_securitygroup.tf
            │   │   ├── aws_vpc.tf
            │   │   ├── output.tf
            │   │   └── variables.tf
            │   └── variables.tf
            ├── main.tf
            ├── terraform.tfstate
            ├── terraform.tfstate.backup
            ├── terraform.tfvars
            └── variables.tf

リソースの展開について

Ansibleについて文章書きたいって言ってるのに、半分以上がTerrforomなのは... ?

とまぁ、Terraformのご説明を簡単にすると、インフラストラクチャプロバイダなどのリソース設定をコード化できてしまうというものです。

今回は、わざと混合状態で挑むこととしました。(AWSのリソース設定をAnsibleでイチから書くのがめんどくさかったというのは内緒♡)

Terraformは主に、AWSの各リソースのデプロイに利用しています。

ここで、デプロイの準備を解説させていただくと、EC2とVPCの設定を各ディレクトリに配置しております。

実行する上で、./roles/make_infra/terraform/terraform.tfvarsAWSで取得したIAMユーザのアクセスキーとシークレットキーを入力してください。記入後、下記コマンドを実行。

#編集してください。"./roles/make_infra/terraform/terraform.tfvars"
/*provider-settings*/
aws_access_key = "XXXXXXXXXXXXXXXXXXXX"
aws_secret_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#Terraformの初期化
terraform init

これで、Terraform側が必要な情報を取得してくれます。

./roles/make_infra/terraform/下にterraform.tfstateterraform.tfstate.backup.terraform/が生成されるかと思います。

Terraform側はこれでOK

一応、以下のようなリソースを展開する準備ができています。

f:id:pabotesu:20200912043439j:plain

ネットワークのアドレスやインスタンスのタイプに関しては、./roles/make_infra/terraform/environment/variables.tfで設定できます。(※プロジェクトネームはいろいろ連動しているので、変えないのが良きかも)

下に、プライベートサブネットができるのは過去に作ったものの流用なので、見逃してください...

#./roles/make_infra/terraform/environment/variables.tf

/*aws-name-settings*/
variable "project-name" {
  default     = "minecraft"
}

/*aws-network-settings*/
variable "vpc-cidr" {
  default     = "192.168.32.0/24"
}

variable "az" {
  default     = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
}


variable "public-subnets" {
  default     = ["192.168.32.0/25"]
}

variable "private-subnets" {
  default     = ["192.168.32.128/25"]
}

/*aws-instance-settings*/
variable "ec2-config" {
      type = map(string)
      default = {
          ami = "ami-0ff21806645c5e492"
          instance_type = "t2.small"
          instance_key = "mykey"
          root_ebs_type = "gp2"
          root_ebs_size = "8"
      }
  }

Ansibleの利用

今回、Ansibleはrolesという機能を利用していきたいと思います。

rolesとは、

ロールは、既知のファイル構造に基づいて特定の vars_files、タスク、およびハンドラーを自動的に読み込む方法です。 ロールでコンテンツをグループ化すると、他のユーザーとのロールの共有が容易になります。

参考:ロール - Ansible Documentation

と、言う感じで、つらつらPlaybookを書くよりも、より可読性や共有性をアップできるということですね。

上から、./build_infra.yml./build_minecraft.ymlは各ロール内のtasksのymlファイルを実行しています。

./build_infra.ymlは上のTerraformタスクを実行していて、./build_minecraft.ymlは展開されたインスタンスに対してminecraftの実装をしています。また、ここのversion変数でminecraftのバージョンを指定できます。

#./build_infra.yml
---
- name: build infra   
  hosts: localhost
  become: false
  roles:
    - make_infra
#./build_minecraft.yml
---
- name: build minecraft 
  hosts: target
  become: yes
  vars:
    version: "1.12.2"
    password: passw0rd
  roles:
    - make_games

次にinventoryディレクトリにある、./inventory/aws_ec2.ymlは展開されたEC2インスタンスの情報を引っ張ってきてくれています。

ここで、./inventory/aws_ec2.ymlaws_access_key_idaws_secret_access_keyは、先程Terraformにも記入したアクセスキー、シークレットキーと同じテナントに紐付いているものとなります。(同じもので良いですよ)

./inventory/inventory_fileインスタンスと22番接続する際に必要な情報が入ってます。

./inventory/inventory_file内のansible_ssh_private_key_fileは接続に必要なキーペアの名前を記入の上、ファイルを./key/ディレクトリ下に保存してください。

#./inventory/aws_ec2.yml
plugin: aws_ec2

aws_access_key_id: XXXXXXXXXXXXXXXXXXXX
aws_secret_access_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
aws_security_token:

regions:
 - ap-northeast-1
strict: no
keyed_groups:
 - key: tags.Name
   prefix: tag_Name_
   separator: ""

groups:
 target: "tags.Name is match('^minecraft-')"

compose:
  ansible_host: global_ip_address
  
#./inventory/inventory_file
[target:vars]
ansible_port=22
ansible_user=ec2-user
ansible_ssh_private_key_file=key/mykey.pem

一応ここまでで実行はできちゃいますが、もう少しだけ続くんじゃ...

ここでは、各タスクについてご説明します。

./roles/make_infra/tasks/main.yml は単純にTerraformを実行しています。正直、$ terraform applyとなんら変わりません。ただ、今回はAnsibleからTerraformを実行してみたいという試みなのです。

実際、TerraformからAnsibleを動かすか、AnsibleからTerraformを動かすかは状況に応じて異なるかと思います。

仮に、今回の件が業務に関わるなら、まずAnsibleで要件のOS、必要なパッケージを携えたイメージをローカル環境で作成して、それをAWS 上にAMIとしてアップロードする。(ここまで、自動化)

その後そのAMIを利用してTerraformを実行する。

という手法を取るかと思います。(これも臨機応変

で、その下に120秒の待機があるのは、単純にインスタンスの起動を「ちょっと待ってー」という意味です。

  # ./roles/make_infra/tasks/main.yml
  - name: make infra terraform
    terraform:
      project_path: 'roles/make_infra/terraform'
      state: present
  - name: pause
    pause:
      seconds: 120
    register: result
  - name: show result
    debug:
      var: result

次に、minecraftサーバの実装です。

ファイルは、./roles/make_games/tasks/main.ymlになります。

しかし...まぁ...

ここが一番書くことがないですね...

ちなみに、ローカルからコピーしてきている、eula.txtというファイルとは、マイクラサーバを起動する際に、「承諾する」という意味のファイルになります。(ファイルは承諾済み)

なので、これを実行したとき、すでに「承諾している」ということですね♡(ゲス顔)

ついでに、マイクラサーバのjarファイルをダウロードしてきているS3には最新版はなかったのであしからず...

# ./roles/make_games/tasks/main.yml
- name: install java
  yum: name=java state=installed

- name: install screen
  yum: name=screen state=installed

- name: add a minecraft user
  user: name=minecraft password={{password}} state=present

- name: settings sudo user minecraft
  lineinfile: "dest=/etc/sudoers backup=yes state=present regexp='^minecraft' line='minecraft ALL=(ALL) NOPASSWD: ALL'"

- name: make minecraft directory
  file: path=/opt/minecraft state=directory owner=minecraft group=minecraft mode=0755

- name: get minecraft
  get_url: url="https://s3.amazonaws.com/Minecraft.Download/versions/{{version}}/minecraft_server.{{version}}.jar" dest=/opt/minecraft

- name: create symbolic link
  file: src=/opt/minecraft/minecraft_server.{{version}}.jar dest=/opt/minecraft/server.jar state=link

- name: copy eula.txt
  copy: src=eula.txt dest=/opt/minecraft/eula.txt

- name: start minecraft
  shell: /usr/bin/java -Xmx1024M -Xms1024M -jar /opt/minecraft/server.jar nogui &
  args:
    chdir: /opt/minecraft/
  async: 5
  poll: 0

参考というか、ほぼほぼパクリ

実行

実行しちゃう

以上のように実装したコードを実行していきます。

基本的には、プロジェクトのルートディレクトリで実行。

# ./ で実行
./ $ ansible-playbook build_infra.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [build infra] *******************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************************************************************************************************************************************
ok: [localhost]

TASK [make_infra : make infra terraform] *********************************************************************************************************************************************************************************************************************************************
changed: [localhost]

TASK [make_infra : pause] ************************************************************************************************************************************************************************************************************************************************************
Pausing for 120 seconds
(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)
ok: [localhost]

TASK [make_infra : show result] ******************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "result": {
        "changed": false,
        "delta": 120,
        "echo": true,
        "failed": false,
        "rc": 0,
        "start": "2020-09-12 03:54:11.657614",
        "stderr": "",
        "stdout": "Paused for 120.0 seconds",
        "stop": "2020-09-12 03:56:11.657858",
        "user_input": ""
    }
}

PLAY RECAP ***************************************************************************************************************************************************************************************************************************************************************************
localhost                  : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

一応、インスタンスの確認。

./ $ ansible-inventory -i inventory --graph
@all:
  |--@aws_ec2:
  |  |--ec2-xx-xx-xx-xx9.ap-northeast-1.compute.amazonaws.com
  |--@tag_Name_minecraft_public_ap_northeast_1a:
  |  |--ec2-xx-xx-xx-xx9.ap-northeast-1.compute.amazonaws.com
  |--@target:
  |  |--ec2-xx-xx-xx-xx9.ap-northeast-1.compute.amazonaws.com
  |--@ungrouped:

インスタンスいますね。

最後に、マイクラサーバのサービス起動まで!

./ $ ansible-playbook -i inventory build_minecraft.yml

PLAY [build minecraft] ***************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************************************************************************************************************************************
The authenticity of host 'ec2-xx-xx-xx-xx9.ap-northeast-1.compute.amazonaws.com (xx.xx.xx.xx9)' can't be established.
ECDSA key fingerprint is SHA256:4zFNS8SaLRQtm08Hs87FOBqN1XPh2vEi3OPk9AWQDt0.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
[WARNING]: Platform linux on host ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com]

TASK [make_games : install java] *****************************************************************************************************************************************************************************************************************************************************
changed: [ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com]

TASK [make_games : install screen] ***************************************************************************************************************************************************************************************************************************************************
ok: [ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com]

TASK [make_games : add a minecraft user] *********************************************************************************************************************************************************************************************************************************************
[WARNING]: The input password appears not to have been hashed. The 'password' argument must be encrypted for this module to work properly.
changed: [ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com]

TASK [make_games : settings sudo user minecraft] *************************************************************************************************************************************************************************************************************************************
changed: [ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com]

TASK [make_games : make minecraft directory] *****************************************************************************************************************************************************************************************************************************************
changed: [ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com]

TASK [make_games : get minecraft] ****************************************************************************************************************************************************************************************************************************************************
changed: [ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com]

TASK [make_games : create symbolic link] *********************************************************************************************************************************************************************************************************************************************
changed: [ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com]

TASK [make_games : copy eula.txt] ****************************************************************************************************************************************************************************************************************************************************
changed: [ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com]

TASK [make_games : start minecraft] **************************************************************************************************************************************************************************************************************************************************
changed: [ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com]

PLAY RECAP ***************************************************************************************************************************************************************************************************************************************************************************
ec2-18-183-143-139.ap-northeast-1.compute.amazonaws.com : ok=10   changed=8    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

はい、できました。

実行結果

awsインスタンスを確認して、

f:id:pabotesu:20200912043312p:plain

イクラ接続♡

f:id:pabotesu:20200912043329p:plain

f:id:pabotesu:20200912043353p:plain

できたわね!

ちなみに、インスタンスごと葬り去りたいときは...

# /roles/make_infra/
$ ansible-playbook infra_destroy.yml

で、お願いします。

あくまでAnsibleは運用の技術なので、全消しはあまり良くないかなーってことでひっそりと...(思想に支配されすぎてもだめだけどね)

Afterword

はい、お疲れ様です。

無事実装できましたね♪

しかし、まだまだ爪のあまい部分があるかと思います。(warningめっちゃ出てるし...)

これを読んで、おかしいと思うことやよくないわねって思うことは,

がしがし、Twitterにでもご指摘ください。(コメントでも良いですが) ※突然のTwitter宣伝乙