技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

AmazonLinux2 に Java11 + Tomcat9 を Ansible2.7 で突っ込む

やろうと思ったきっかけは、そんなわりかし新しい構成でサーバ作ってと頼まれたので。

そして、やってみるとわかるのだけど、2019/03/21 時点でこれをリポジトリサポートしてるLinuxがそうそう無いと言うことに気づく。
強いて言えば Ubuntu18 が JDK11 だけサポートしてた。

でも Tomcat の Java11 対応って、Tomcat9 からなので、それなりに最新を入れざるを得なかったという問題があった。

前回もそんな流れで、JDK11 だけ先行して入ってりゃどうにかなんだろーと思ったためだ。

white-azalea.hatenablog.jp

しかし甘かった…Tomcat9 の問題が待ち構えていたのだ。

なので、今回は前回をパワーアップチャレンジした。

インストールの段階を分離

Playbook は本来やりたいことの単位で区切っておいた方が、可読性の問題から言ってもいいので、全体として 4 つの playbook を作った。

  • main.yml
    メインの playbook。他の platbook を呼び出すハブ
    • common.yml
      yum update だけしておく。
    • java.yml
      内部で Java バージョンを指定し、既存のインストールバージョン指定
    • tomcat.yml
      Tomcat のインストールとサービス登録までします。

common.yml : yum update

といってもやることは yum 操作だけなので、悩むことはない。

---
- hosts:  develop-server
  user:   ec2-user
  become: yes
  tasks:
    - name: Update all package
      yum:
        name: '*'
        state: latest
    - name: Install wget.
      yum:
        name: wget
        state: latest

全部更新する場合に、name に対して '*' を指定しているくらい。

java.yml : java11 インストール

ここはだいぶがっつり変えた。
というのも、JDK を DL する実行コストがバカにならない(平たく言えば遅い)。なので、事前に java コマンドがあるか?その java は指定のバージョンか?を確認する。

- hosts:  develop-server
  user:   ec2-user
  become: yes
  vars:
    corretto: https://d3pxv6yz143wms.cloudfront.net/11.0.2.9.3/java-11-amazon-corretto-devel-11.0.2.9-3.x86_64.rpm
    java_home: /usr/lib/jvm/java-11-amazon-corretto
    java_version: '11.0.2'
  tasks:
    - name: Check Java install.
      find:
        paths: /usr/bin
        patterns: 'java'
        file_type: any
      register: java_cmd

    - name: Check java version.
      command: 'java -version'
      register: java_vsn
      when: "java_cmd.files|length > 0"

    - debug:
        msg: "java_vsn = {{ java_vsn }}"

    - name: Install corret.
      yum:
        name: '{{ corretto }}'
        state: present
      when: "(java_cmd.files|length == 0) or (java_vsn is defined and '{{ java_version }}' not in java_vsn.stderr)"

    - name: Alternatives.
      alternatives:
        name: '{{ item }}'
        link: "/usr/bin/{{ item }}"
        path: "{{ java_home }}/bin/{{ item }}"
      with_items:
        - java
        - javac
      when: "(java_cmd.files|length == 0) or (java_vsn is defined and '{{ java_version }}' not in java_vsn.stderr)"

尚この処理の参考にしたのは公式。
こういうドキュメントの充実性はさすが Python さすが RedHat という印象。

docs.ansible.com

一つだけ問題があるとすれば、なぜか java -version コマンドの応答が stdout ではなくて stderr だと言う点。
解せぬ…

Tomcat9 のインストール

参考にしたのはこちら

weblabo.oscasierra.net

ただし見ればわかるのは、Tomcat をバイナリダウンロードで入れて言うと言う点。
コレは要するに完全に同じ手順を2回やろうとするとコケる事を意味してる。

冪等性(何回やっても指定した状況になること)の確保のため、ここでは Tomcat 指定バージョンのインストールディレクトリ有無でインストールプロセスを実行するかどうか決定するようにした。

- hosts:  develop-server
  user:   ec2-user
  become: yes
  vars:
    tomcat: http://ftp.jaist.ac.jp/pub/apache/tomcat/tomcat-9/v9.0.17/bin/apache-tomcat-9.0.17.tar.gz
    tomcat_home: /opt/tomcat
    tomcat_version: '9.0.17'
  tasks:
    - name: Create tomcat user.
      user:
        append: yes
        create_home: no
        name: tomcat
        shell: /sbin/nologin

    - name: Create tomcat dir.
      file:
        mode: '700'
        path: /opt/tomcat
        owner: tomcat
        group: tomcat
        state: directory

    - name: Check current version tomcat {{ tomcat_version }}.
      find:
        paths: "/opt/tomcat/apache-tomcat-{{ tomcat_version }}"
        patterns: "*"
        file_type: any
      register: tomcat_files

    - name: Stop tomcat service.
      command: systemctl stop tomcat.service
      when: tomcat_files.matched != 0

    - name: Download tomcat.
      get_url:
        dest: /opt/tomcat
        owner: tomcat
        url: "{{tomcat}}"
      when: tomcat_files.matched == 0

    - name: Extract tomcat.
      unarchive:
        dest: /opt/tomcat
        owner: tomcat
        group: tomcat
        remote_src: yes
        src: "/opt/tomcat/apache-tomcat-{{ tomcat_version }}.tar.gz"
      when: tomcat_files.matched == 0

    - name: Create sybolic link /opt/tomcat/latest
      file:
        src: "/opt/tomcat/apache-tomcat-{{ tomcat_version }}"
        dest: /opt/tomcat/latest
        state: link
        owner: tomcat
        group: tomcat
      when: tomcat_files.matched == 0

    - name: Register service.
      template:
        src: templates/tomcat.service.j2
        dest: /etc/systemd/system/tomcat.service
        owner: root
        group: root
        mode: '755'
        backup: yes
      when: tomcat_files.matched == 0

    - name: Register startup.
      command: systemctl enable tomcat.service
      when: tomcat_files.matched == 0

    - name: Remove archives.
      file:
        path: "/opt/tomcat/apache-tomcat-{{ tomcat_version }}.tar.gz"
        state: absent
      when: tomcat_files.matched == 0

Tomcat のサービス起動スクリプトはこちら。
latestシンボリックリンクがあるなら、別にバージョンごときのために template 使う意味なくね?という気はする。

そのうち何かに使うかも知れないので、取り敢えずおいてある雰囲気で。

[Unit]
Description=Apache Tomcat {{ tomcat_version }}
After=network.target

[Service]
User=tomcat
Group=tomcat
Type=oneshot
PIDFile=/opt/tomcat/latest/tomcat.pid
RemainAfterExit=yes

ExecStart=/opt/tomcat/latest/bin/startup.sh
ExecStop=/opt/tomcat/latest/bin/shutdown.sh
ExecReStart=/opt/tomcat/latest/bin/shutdown.sh;/opt/tomcat/latest/bin/startup.sh

[Install]
WantedBy=multi-user.target

やっぱローカル上で検証できるっていいわー。

main.yml

コレは上記3つを呼ぶだけなので特筆すべきものはないはず

---
- import_playbook: common.yml
- import_playbook: java.yml
- import_playbook: tomcat.yml

ということで、AmazonLinux2 に Tomcat9 + Java11 構成ができた。
web アプリケーションを置くなら、「/opt/tomcat/latest/webapps」以下に突っ込めばいいのではなかろうか。

実行

$ ansible-playbook main.yml 

PLAY [develop-server] *****************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************
Enter passphrase for key '/Users/xxxx/workspace/VirtualBox/virtualbox': 
ok: [192.168.56.2]

TASK [Update all package] *************************************************************************************************************************************************
ok: [192.168.56.2]

TASK [Install wget.] ******************************************************************************************************************************************************
ok: [192.168.56.2]

PLAY [develop-server] *****************************************************************************************************************************************************

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

TASK [Check Java install.] ************************************************************************************************************************************************
ok: [192.168.56.2]

TASK [Check java version.] ************************************************************************************************************************************************
skipping: [192.168.56.2]

TASK [debug] **************************************************************************************************************************************************************
ok: [192.168.56.2] => {
    "msg": "java_vsn = {'changed': False, 'skipped': True, 'skip_reason': 'Conditional result was False'}"
}

TASK [Install corret.] ****************************************************************************************************************************************************
 [WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: (java_cmd.files|length == 0) or (java_vsn is defined and '{{
java_version }}' not in java_vsn.stderr)

changed: [192.168.56.2]

TASK [Alternatives.] ******************************************************************************************************************************************************
 [WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: (java_cmd.files|length == 0) or (java_vsn is defined and '{{
java_version }}' not in java_vsn.stderr)

ok: [192.168.56.2] => (item=java)
ok: [192.168.56.2] => (item=javac)

PLAY [develop-server] *****************************************************************************************************************************************************

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

TASK [Create tomcat user.] ************************************************************************************************************************************************
changed: [192.168.56.2]

TASK [Create tomcat dir.] *************************************************************************************************************************************************
changed: [192.168.56.2]

TASK [Check current version tomcat 9.0.17.] *******************************************************************************************************************************
ok: [192.168.56.2]

TASK [Stop tomcat service.] ***********************************************************************************************************************************************
skipping: [192.168.56.2]

TASK [Download tomcat.] ***************************************************************************************************************************************************
changed: [192.168.56.2]

TASK [Extract tomcat.] ****************************************************************************************************************************************************
changed: [192.168.56.2]

TASK [Create sybolic link /opt/tomcat/latest] *****************************************************************************************************************************
changed: [192.168.56.2]

TASK [Register service.] **************************************************************************************************************************************************
changed: [192.168.56.2]

TASK [Register startup.] **************************************************************************************************************************************************
changed: [192.168.56.2]

TASK [Remove archives.] ***************************************************************************************************************************************************
changed: [192.168.56.2]

PLAY RECAP ****************************************************************************************************************************************************************
192.168.56.2               : ok=18   changed=9    unreachable=0    failed=0   

しれっと中にインストールされ、サービスも無事起動するようになっている。
あとはサービス起動前に war を所定の位置にコピーして、ansible の handlers あたりでサービス起動もしてやれば OK だ。

[root@amazonlinux ~]# systemctl start tomcat.service
[root@amazonlinux ~]# wget http://127.0.0.1:8080/
--2019-03-21 13:56:33--  http://127.0.0.1:8080/
127.0.0.1:8080 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 
長さ: 特定できません [text/html]
`index.html' に保存中

    [ <=>                                                                                                                              ] 11,266      --.-K/s 時間 0.001s  

2019-03-21 13:56:34 (11.5 MB/s) - `index.html' へ保存終了 [11266]

あと、あくまで検証環境なので、Tomcat 内のデフォルトアプリとかセキュリティ設定とかはやってない。
その辺はこれを元に作り込んで欲しい GoodLuck!