ことさら−古都プログラマーの更級日記

京都でお寺を回りながら御朱印集めをしていたエンジニアのブログ。おもに技術的なはなしとか日常的なはなし。たまにカメラの話や競馬の話も書きます。

Ansibleを使ってPHP7環境をVagrant上に構築してLaravelに入門

この記事は Laravel Advent Calender 2017 7日目の記事です。

昨日の記事は LaravelのControllerクラスって何やってるの? - Qiita でした

Laravel DI からはじまり何もかもが揃っていてグッドなフルスタックフレームワークLaravelを使っていきましょう。Ansibleで環境構築してLaravelが動くまでをやっていきたいと思います。

なんでHomestead使わないの?

Laravel Homestead っていう、Laravelがいい感じに動くVagrant Boxみたいなのがあるんですが

  • Homestead ブラックボックスすぎてよく分かってないからちゃんと理解した
  • Ansible 勉強したい
  • 実際動かす際には仮想環境じゃないときもある

とまあれこれあるので、AnsibleでPHP7 + Laravelが動く環境を頑張って作ってみたいなとおもいます。

Vagrant使ってますが、Ansible流れればいいのでVagrantである必要は)ないです。

とりあえずVagrantつくる

まずは最小構成で。 vagrant up できて vagrant ssh できる程度のもの適当に作ります。Cent OS 7.1 で。

$ cat Vagrantfile
# coding: utf-8
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(2) do |config|
  config.vm.box = "centos/7"
  config.vm.hostname = "vagrant"

  # ipアドレス ipは適当に変更しておk
  config.vm.network :private_network, ip: "192.168.33.11"
  config.ssh.insert_key = false
  config.ssh.forward_agent = true
end

$ vagrant up
# なんやかんや

$ vagrant ssh
[vagrant@vagrant ~]$

できた。

Vagrant に Ansible を流せるようにする

ディレクトリ構成こんな感じで

Vagrantfile
ansible/
 └playbook.yml

VagrantfileにAnsibleの設定を追記

$ cat Vagrantfile
# coding: utf-8
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(2) do |config|
  config.vm.box = "centos/7"
  config.vm.hostname = "vagrant"

  # ipアドレス ipは適当に変更しておk
  config.vm.network :private_network, ip: "192.168.33.11"
  config.ssh.insert_key = false
  config.ssh.forward_agent = true

  # ansibleの設定
  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "ansible/playbook.yml"
  end
end

vagrantが起動している状態で

$ ls
Vagrantfile   ansible/

$ cat ansible/playbook.yml
- hosts: all
  sudo: true
  user: vagrant

$ vagrant provision
==> default: [vagrant-hostsupdater] Checking for host entries
==> default: [vagrant-hostsupdater]   found entry for: 192.168.33.11 vagrant
==> default: Running provisioner: ansible...
    default: Running ansible-playbook...
[DEPRECATION WARNING]: Instead of sudo/sudo_user, use become/become_user and
make sure become_method is 'sudo' (default).
This feature will be removed in a
future release. Deprecation warnings can be disabled by setting
deprecation_warnings=False in ansible.cfg.

PLAY [all] *********************************************************************

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

PLAY RECAP *********************************************************************
default                    : ok=1    changed=0    unreachable=0    failed=0

まあ何も書いてないので changed=0 ですがとりあえずOKになるのを確認。

このタイミングで初めて知ったのですが、 sudo: true はAnsible 1.9 からはDeprecatedらしいので become: true に書き換えます。

$ cat ansible/playbook.yml
- hosts: all
  become: true

Ansible で gitを入れてみる

Ansibleでgitを入れます。ほんとはソースをダウンロードしてビルドすると、バージョンが固定されて良い気がするのですが、めんどいのでyumで入れてしまいます。Ansibleの動作確認も兼ねてます。

Ansibleはだいたいディレクトリ構成が決められています。基本的には「gitのインストール」「Apacheのインストール」といった単位でモジュール化して、roles というディレクトリ以下に置きます。以下のリポジトリに実際に出来たAnsibleを置いておいたので、これをみてもらうと分かりやすいかと思います。

github.com

ansible/
+playbook.yml
+roles/
 +tasks/
  +git/
   +main.yml

roles/git/tasks/main.yml *1 にgitのインストール用のAnsibleを書きます。

$ cat ansible/roles/git/tasks/main.yml
- name: install git
  yum: name=git state=installed

yumを使ってgitをinstalledな状態にするということです。

続いてplaybook.ymlに以下を追記します。

$ cat ansible/playbook.yml
- hosts: all
  become: true
  user: vagrant
  roles:
  - git

roles に git を追加することで。roles/git/tasks/main.yml が実行されます。

$ vagrant provision
==> default: [vagrant-hostsupdater] Checking for host entries
==> default: [vagrant-hostsupdater]   found entry for: 192.168.33.11 vagrant
==> default: Running provisioner: ansible...
    default: Running ansible-playbook...

PLAY [all] *********************************************************************

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

TASK [git : install git] *******************************************************
changed: [default]

PLAY RECAP *********************************************************************
default                    : ok=2    changed=1    unreachable=0    failed=0

changed=1 となりました。gitが入っているか確かめてみます。

$ vagrant ssh
Last login: Mon Dec  4 22:57:06 2017 from 10.0.2.2
[vagrant@vagrant ~]$ git

AnsibleでPHP7

PHPはgitと違って、デプロイしたコードの動作に差異が出る可能性があるので、これこそソースからビルドしたほうがいいのですが、やはり面倒なのでyumで入れてしまいます。

roles以下にphp7を作成します。

$ cat ansible/roles/php7/tasks/main.yml
---
- name: yum install epel-repease
  yum: name=epel-release state=installed

- name: add remi-repo repository
  command: rpm -ih http://rpms.famillecollet.com/enterprise/remi-release-7.rpm creates=/etc/yum.repos.d/remi.repo

- name: install php 7.1
  yum: name={{ item }} enablerepo=remi,remi-php71 state=installed
  with_items:
    - php
    - php-devel
    - php-fpm
    - php-gd
    - php-mbstring
    - php-mcrypt
    - php-mysqlnd
    - php-pdo
    - php-xml
    - php-zip

$ cat ansible/playbook.yml
- hosts: all
  become: true
  user: vagrant
  roles:
  - git
  - php7

$ vagrant provision

パッケージはなんとなく使いそうなのを適当にチョイスしてインストールしています。 vagrant ssh して php -v すると php がインストールされているはずです。phpの設定ファイル php.ini もAnsibleで管理されているのが望ましいので、ansible管理するようにします。

roles/php7/templates/php.ini.j2 に php.ini のテンプレートを配置して、roles/php7/tasks/main.yml に、このテンプレートをデプロイするコマンドを書きます。

基本的にはデフォルトのテンプレートでよいのですが、timezoneだけちゃんと設定されていることを確認してください https://github.com/yoshikyoto/php7-ansible/blob/41c103ef61ee6488c2293ff2327eb7c2ebb372aa/roles/php7/templates/php.ini.j2#L903

- name: deploy php.ini
  template:
    src: php.ini.j2
    dest: /etc/php.ini

詳しくは先程紹介したリポジトリを見て下さい。 php.ini をデプロイして php が正しく動くことを確認します。

$ vagrant provision

$ vagrant ssh
[vagrant@vagrant ~]$ ls -la /etc/php.ini
-rw-r--r-- 1 root root 62433 Dec  4 23:21 /etc/php.ini # きっとタイムスタンプが更新されている

[vagrant@vagrant ~]$ php -i
# ちゃんと設定ファイルが読み込まれているか

AnsibleでApache

AnsibleでApacheをソースからビルドして入れようと思ったのですが、Vagrantcentos/7 は最初からApacheが入っているらしく、ソースからビルドしようとするとちょっと複雑なことに...ということで、ソースのビルドのあたりはなんとなく適当に書きました。。。まあconfigさえちゃんとデプロイできれば問題ないので...

ソースのビルド&インストールは https://github.com/yoshikyoto/php7-ansible/blob/1ebf06244a94ee02cbe7a66996c4fecb4a6b807e/roles/apache2_4/tasks/install.yml みたいな感じですね。

roles/apache2_4/tasls/main.yml には以下を書いておきます。

---
- name: check if exist apache
  command: service httpd status
  ignore_errors: True
  register: check_apache

# Apacheのインストール
- include: install.yml
  when: check_apache|failed

- name: enable apache
service: name=httpd enabled=yes

service httpd status 叩いてみて Apache がインストールされて無さそうだったらインストールします。そして、OS起動時にApacheも起動するように設定しておきます。

ApacheのconfigをAnsibleで管理する

roles/apache2_4/templates 以下に Apache の config のテンプレートを置いておきます。

https://github.com/yoshikyoto/php7-ansible/blob/1ebf06244a94ee02cbe7a66996c4fecb4a6b807e/roles/apache2_4/templates/httpd.conf.j2

基本的にはデフォルトのままですが、Apacheの起動ユーザーだけvagrantに変更しています。*2

https://github.com/yoshikyoto/php7-ansible/blob/1ebf06244a94ee02cbe7a66996c4fecb4a6b807e/roles/apache2_4/templates/httpd.conf.j2#L66-L67

User vagrant
Group vagrant

さて、このconfigをデプロイします。

ここで問題があります。ソースからビルドした場合、 ApacheのConfigファイルは /usr/local/apache2/conf 以下にあったりするのですが、もとから入っているやつとかyumで入れたやつは /etc/httpd/conf 以下にあったりします。一応両方に対応しておきます。

#####
# httpd.confのデプロイ
#####
- name: check existance /etc/httpd/conf
  stat: path=/etc/httpd/conf
  register: etc_httpd_conf

- name: deploy /etc/httpd/conf/httpd.conf
  template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
  when: etc_httpd_conf.stat.exists

- name: check existance /usr/local/apache2/conf
  stat: path=/usr/local/apache2/conf
  register: usr_local_apache_conf

- name: deploy /usr/local/apache2/conf/httpd.conf
  template: src=httpd.conf.j2 dest=/usr/local/apache2/conf/httpd.conf
  when: usr_local_apache_conf.stat.exists

Apache動作確認

Apacheがインストールされていることを確認します。

[vagrant@vagrant ~]$ service httpd status
Redirecting to /bin/systemctl status httpd.service
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2017-12-05 02:52:01 UTC; 1min 32s ago
     Docs: man:httpd(8)
           man:apachectl(8)
 Main PID: 19685 (httpd)
   Status: "Total requests: 0; Current requests/sec: 0; Current traffic:   0 B/sec"
   CGroup: /system.slice/httpd.service
           ├─19685 /usr/sbin/httpd -DFOREGROUND
           ├─19686 /usr/sbin/httpd -DFOREGROUND
           ├─19687 /usr/sbin/httpd -DFOREGROUND
           ├─19688 /usr/sbin/httpd -DFOREGROUND
           ├─19689 /usr/sbin/httpd -DFOREGROUND
           └─19690 /usr/sbin/httpd -DFOREGROUND

さてここで、Apacheを入れたので、Webページにアクセスしてみたいところです。Vagrantfileに以下を追記したうえで vagrant reload します。

config.vm.network "forwarded_port", guest: 80, host: 58080

これを行うと、hostの58080ポートがvagrantの80ポートにフォワーディングされるようになります。 localhost:58080 にアクセスしてみまよう。

f:id:yoshiki_utakata:20171207134409p:plain

Apacheが動いていることがわかりました。

Apache で php7 が動いていることを確認する

vagrantのbox centos/7の設定がそうなっているのか、すでにPHP7がApache上で動いているみたいです。 vagrant ssh してみて、Apache上でphpが動いているか軽く確認します。

vagrant@vagrant ~]$ sudo su -
Last login: Thu Dec  7 01:45:48 UTC 2017 on pts/1
[root@vagrant ~]# vi /var/www/html/test.php
[root@vagrant ~]# cat /var/www/html/test.php
<?php

phpinfo();

これで http://localhost:58080/test.php にアクセスしてみます。

f:id:yoshiki_utakata:20171207134620p:plain

PHPのバージョンなどが確認できます。

composer のインストール

PHP開発するならcomposerもインストールしておきたいです。PHP7をインストールする時に composer も同時にインストールされるようにしました。以下をAnsibleのrole/php7以下のタスクに追加します。((本当はshell: curlじゃなくてちゃんとインストールしたい...)

- name: install composer
  shell: curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin && mv /usr/bin/composer.phar /usr/bin/composer
         creates=/usr/bin/composer

Laravelのプロジェクト作成

composerを入れたことにより、Laravelプロジェクトを作成できるようになりました。vagrantのホームディレクトリ以下に適当なLaravelプロジェクトを作成してみます。 vagrant ssh して以下のコマンドで作成できます。

[vagrant@vagrant ~]$ composer create-project laravel/laravel myapp 5.5.*

これで Laravel 5.5 でプロジェクトが作成されます。初回はパッケージのインストールなどで時間がかかります。

ApacheでLaravelを動かすための設定を入れる

ApacheでLaravelを動かすための設定を入れます。

今回は、 roles/apache2_4/templates/laravel.conf.j2 というものを作成しました。

<VirtualHost *:80>
        DocumentRoot /home/vagrant/myapp/public

        ErrorLog /etc/httpd/logs/laravel_error.log
        CustomLog /etc/httpd/logs/laravel_access.log combined

        <Directory "/home/vagrant/myapp/public">
                AllowOverride All
                Require all granted
        </Directory>
</VirtualHost>

ApacheでLaravelのプロジェクトを動かすためには、 AllowOverride All の設定を入れる必要があるので注意です。

この config を apache の conf.d ディレクトリにデプロイするようにします。

https://github.com/yoshikyoto/php7-ansible/blob/1ebf06244a94ee02cbe7a66996c4fecb4a6b807e/roles/apache2_4/tasks/config.yml#L22-L39

#####
# laravel.conf のデプロイ
#####
- name: check existance /etc/httpd/conf.d
  stat: path=/etc/httpd/conf.d
  register: etc_httpd_conf_d

- name: deploy /etc/httpd/conf.d/laravel.conf
  template: src=laravel.conf.j2 dest=/etc/httpd/conf.d/laravel.conf
  when: etc_httpd_conf_d.stat.exists

- name: check existance /usr/local/apache2/conf.d
  stat: path=/usr/local/apache2/conf.d
  register: usr_local_httpd_conf_d

- name: deploy config
  template: src=laravel.conf.j2 dest=/usr/local/apache2/conf.d/laravel.conf
when: usr_local_httpd_conf_d.stat.exists

これで http://localhost:58080/ にアクセスすると... Forbidden になってしまいます

SELinuxを無効化する

SELinuxというのがジャマをしているらしいです。これを無効化するのが良いのかドウか微妙ですが…とりあえず無効化します。

適当にlaravelというroleを作成して(ドンドン雑になってきましたが気にしない...)、SELinuxを無効化する設定を Ansible にいれます。

https://github.com/yoshikyoto/php7-ansible/blob/1ebf06244a94ee02cbe7a66996c4fecb4a6b807e/roles/laravel/tasks/main.yml

####
# Laravel動かすためにSELinuxを無効化したほうがよさげ
####
- name: install libselinux-python
  yum: name=libselinux-python state=latest

- name: disable SELinux
selinux: state=disabled

最終的に playbook.yml はこうなりました。

https://github.com/yoshikyoto/php7-ansible/blob/1ebf06244a94ee02cbe7a66996c4fecb4a6b807e/playbook.yml

これを vagrant provision で流して、 http://localhost:58080/ にアクセスすると...

f:id:yoshiki_utakata:20171207135701p:plain

Laravelが動くようになりました!

ここから開発とかしていくにはどうしたらいいのか

とりあえずAnsibleでLaravel環境を構築するまでを書きましたが、実際に開発する際には、Vagrantのsynced_folderとかを使って開発していくのが良いと思います。長くなってしまいましたのでここまで。synced folder については別記事でかくかもしれないです!これでLaravelの開発環境が整ったぞ!

*1:ディレクトリは必ず複数形に!

*2:本当はvagrantじゃなくてちゃんとapache用のユーザーを作ったほうがいいのですが、今回は簡単のためにvagrantユーザーで起動します。