LoginSignup
13
3

More than 3 years have passed since last update.

AnsibleでNginx + PHP-FPM + MySQL 環境構築(CentOS8)

Last updated at Posted at 2020-12-03

この記事はアクシス Advent Calendar 2020 4日目です。

はじめに

2020年10月に未経験で入社したムラマツです。
今回の記事は、Ansibleに触れる機会をいただいたこともあり、
復習をかねてAnsibleを使って環境構築することについて重点的に書きます。
Ansibleの概念や詳細の機能などに関しては記事が長くなりすぎるので、
またの機会に投稿していこうと思います。

まだまだ至らぬ点が多々あるかと思いますが、
間違い等ございましたらご指摘いただけると幸いです:bow_tone1:

行うこと

AnsibleでNginx + PHP-FPM + MySQLの環境構築

前提

【環境】
・CentOS 8.2.2004
・Ansible 2.9.13
・DockerやVagrantなどでweb/dbコンテナ(サーバー)を構築済み

【以下をインストールしていきます】
・Nginx 1.18.0
・PHP 8.0.0
・MySQL 8.0.22

本題

webサーバーとdbサーバーに分け、それぞれに接続を行なっていきます。

・inventoryファイル
ファイル名 hosts

[web]
webserver

[db]
dbserver

[all:vars]
ansible_ssh_user=deploy

・playbookファイル

site.yml
---
- import_playbook: common.yml
- import_playbook: web.yml
- import_playbook: db.yml
common.yml
---
- hosts: all
  become: yes
  roles:
    - { role: firewalld, tags: firewalld }
web.yml
---
- hosts: web
  become: yes
  roles:
    - { role: nginx,     tags: nginx     }
    - { role: php-fpm,   tags: php-fpm   }
db.yml
---
- hosts: db
  become: yes
  roles:
    - { role: mysqld, tags: mysqld }

上記設定で進めて参ります。

Nginx

まずはNginxからインストールしていきます。

roles/nginx/tasks/main.yml
---
- name: Repo nginx
  template:
    src: nginx.repo.j2
    dest: /etc/yum.repos.d/nginx.repo

- name: Install nginx
  dnf:
    name: nginx
    disablerepo: AppStream
    state: latest

- name: Transfer nginx.conf
  template:
    src: nginx.conf
    dest: /etc/nginx/nginx.conf
  notify: restart nginx

- name: Transfer default.conf.j2
  template:
    src: default.conf.j2
    dest: /etc/nginx/conf.d/default.conf
  notify: restart nginx

- name: Automatic load when boot
  service:
    name: nginx
    enabled: yes

- name: Start nginx
  service:
    name: nginx
    state: started

## php-fpmとの連携確認のため作成
- name: Make hello world in nginx/html
  template:
    src: index.php.j2
    dest: /usr/share/nginx/html/index.php
  notify: restart nginx

CentOS8では「AppStream」というRHEL8で発表された新しい概念が導入され、
nginxのリポジトリ設定をしなくてもAppStreamリポジトリからインストールが可能です。

ですが今回はNginx公式のstableリポジトリからインストールを行うため以下のnginx.repoを作成します。

roles/nginx/templates/nginx.repo.j2
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

Ansibleには、Jinja2というテンプレートエンジンによるテンプレートファイルを利用し
ファイルを生成してリモートに送れる template モジュール があります。
nginx.conf などの設定ファイルを、変数とテンプレートから生成するといった用途に利用することができます。
srcオプションなど細かい詳細についてはどこかの機会で投稿していきます。

続いて、nginxの設定ファイルを修正していきます。

roles/nginx/templates/nginx.conf
#省略

http {
    ## 省略
    include             /etc/nginx/conf.d/*.conf;
}
roles/nginx/templates/default.conf.j2
server {
    listen       80;

    location / {
        root   /usr/share/nginx/html;
        index  index.php index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

php-fpmとの連携の際に、default.confファイルを修正していきます。

roles/nginx/handlers/main.yml
- name: Restart nginx
  service:
    name: nginx
    state: restarted

Handlerは、Taskの実行時にcahngedが出てた時だけ実行されるジョブの仕組みです。

ここまでで、nginxのインストールから起動まで設定が完了しました。

ですが、このままではfirewallでhttpポートが塞がれているため
firewalldに対しhttpポート開放の設定をしていきます。
そのためfirewalldのroleを作成します。

roles/firewalld/tasks/main.yml
---
- name: Install firewalld
  dnf:
    name: firewalld
    state: latest

- name: Setting firewalld
  template:
    src: firewalld.conf.j2
    dest: /etc/firewalld/firewalld.conf
  notify: reload firewalld

- name: Port release firewall
  firewalld:
    service: http
    permanent: true
    state: enabled
  notify: reload firewalld

- name: Allow mysql
  firewalld:
    service: mysql
    permanent: true
    state: enabled
  notify: reload firewalld

今回はpublicゾーンに対してhttpサービスを追加していきます。

roles/firewalld/templates/firewalld.conf.j2
DefaultZone=public
CleanupOnExit=yes
Lockdown=no
IPv6_rpfilter=no
IndividualCalls=yes
LogDenied=off
FirewallBackend=iptables
FlushAllOnReload=yes
RFC3964_IPv4=yes
AllowZoneDrifting=yes
roles/firewalld/handlers/main.yml
- name: reload firewalld
  service:
    name: firewalld
    state: reloaded

以下のコマンドを使用して Playbook を実行します

$ ansible-playbook -i hosts common.yml web.yml

localhostに接続して問題ないか確認します。
image.png

PHP

続いてPHPをインストールしていきます。

roles/php-fpm/tasks/main.yml
---
- name: Install Remi
  rpm_key:
    state: present
    key: https://rpms.remirepo.net/RPM-GPG-KEY-remi2018

- name: Install EPEL
  rpm_key:
    state: present
    key: https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-8

- name: Add EPEL Repository
  dnf:
    name: https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
    state: present

- name: Add Remi Repository
  dnf:
    name: https://rpms.remirepo.net/enterprise/remi-release-8.rpm
    state: present

- name: Install php
  dnf:
    name: "{{ php_packages }}"
    state: present

- name: Configuration file
  template:
    src: php-fpm.conf.j2
    dest: /etc/php-fpm.d/www.conf

- name: Start php-fpm
  systemd:
    name: 'php-fpm'
    state: started
    enabled: yes

デフォルトの状態では、CentOS8の標準リポジトリからPHP7.2がインストールされるようになっていたため、RemiリポジトリからPHP8.0がインストールされるように設定しています。

roles/php-fpm/vars/main.yml
---
php_packages:
  - '@php:remi-8.0'
  - php-mbstring
  - php-xml
  - php-pdo
  - php-mysqlnd

今回はmysqlと接続確認をするため必要なパッケージをインストールしています。
variableに記述することで他に必要なパッケージをインストールする際、
varsに追記するだけでインストールできるようにしています。

roles/php-fpm/vars/main.yml
[www]
# nginxに変更
user =  nginx
group = nginx

listen = /var/run/php-fpm/php-fpm.sock

listen.allowed_clients = 127.0.0.1

# nginxに変更
listen.owner = nginx
listen.group = nginx


## 省略

Nginxとphp-fpmの通信は、TCPではなくUNIXドメインソケット通信で行いました。
UNIXドメインソケット通信はTCPよりもスループットが優れているとのことです。
ソケット通信に関しては、以下の記事がとても参考になりました。
調べなきゃ寝れない!と調べたら余計に寝れなくなったソケットの話

Nginxのconfファイルも修正していきます。

roles/nginx/templates/default.conf.j2
server {
    listen       80;

    location / {
        root   /usr/share/nginx/html;
        index  index.php index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    location ~ \.php$ {
        root           /usr/share/nginx/html;
        fastcgi_pass   unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

fastcgi_passをunixドメインソケット通信に変更しています。

fastcgi_passについては下記の記事を参考にさせていただきました。
nginx と PHP-FPM の仕組みをちゃんと理解しながら PHP の実行環境を構築する

加えてNginxとphp-fpmが連携できているかを確認するためindex.phpファイルを作成します。

roles/nginx/templates/index.php.j2
<?php
  echo 'Hello world';
?>

以下のコマンドを使用して Playbook を実行します

$ ansible-playbook -i hosts common.yml web.yml

記載したHello worldが画面上に表示されていればOKです。

image.png

MySQL

最後にMySQLをインストールしていきます。
今回は、rootユーザーの初期パスワードを変更し、その後新規ユーザーを作成していきます。
冪同性を保たれるように初期パスワードは1回目の実装で削除するよう設定しています。
 ・regexp: 書き換えたい行にマッチする正規表現

roles/mysqld/tasks/main.yml
---
- name: Install RPM-GPG-KEY-mysql
  rpm_key:
    state: present
    key: https://repo.mysql.com/RPM-GPG-KEY-mysql

- name: Download epel-release
  dnf:
    name: https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm
    state: present

- name: Install mysql-packages
  dnf:
    name: ["mysql-community-server", "mysql-community-devel"]
    disablerepo: AppStream
    state: present

- name: Install mysql plugin for python
  dnf:
    name: python3-PyMySQL
    state: present

- name: Configuration my.cnf
  template:
    src: my.cnf.j2
    dest: /etc/my.cnf
    owner: root
    group: root
    mode: 0644

- name: Start mysqld
  systemd:
    name: mysqld
    state: started
    enabled: yes

- name: Get root password
  shell: >
    grep "password" /var/log/mysqld.log | awk '{print $(NF)}'
  register: root_password

- name: Update expried root user password
  command: >
    mysql --user root --password={{ root_password.stdout }} --connect-expired-password \
    --execute="ALTER USER 'root'@'localhost' IDENTIFIED BY '{{ mysql_root_password }}';"
  when: root_password.stdout != ""

- name: Delete init pass
  lineinfile:
    path: /var/log/mysqld.log
    regexp: 'A temporary password is generated for root@localhost'
    state: absent

- name: Create mysql client user
  mysql_user:
    update_password: on_create
    name: "{{ item.user }}"
    password: "{{ item.password }}"
    host: "{{ item.host }}"
    priv: "{{ item.privileges }}"
    state: present
    login_unix_socket: /var/lib/mysql/mysql.sock
    login_password: "{{ mysql_root_password }}"
    login_user: root
  with_items:
    - { user: "sample", password: "{{ mysql_client_password }}", host: "%", privileges: "*.*:ALL,GRANT" }

itemを使用して今後新しくユーザーを作成する場合でも
with_itemsの項目を増やすだけで作成できるようにしています。

roles/mysqld/vars/main.yml
---
mysql_root_password: PASSWORD
mysql_client_password: PASSWORD

セキュアな情報はvaultを使って暗号化した方がいいかと思いますので、
以下のコマンドで暗号化します。
※複数ファイル選択もできます。詳細は以下にて。
Ansible公式ドキュメント

$ ansible-vault encrypt roles/mysqld/vars/main.yml

MySQLと接続確認するためindex.phpファイルを編集していきます。
今回はPDOを使用して簡単な接続確認を試みました。

roles/nginx/templates/index.php.j2
<?php

const DB_HOST = 'mysql:dbname=;host=db;charset=utf8';
const DB_USER = 'sample';
const DB_PASSWORD = 'PASSWORD';

try {
  $pdo = new PDO(DB_HOST, DB_USER, DB_PASSWORD);
  echo '接続成功';
} catch (PDOException $e) {
  echo '接続失敗' . $e->getMessage() . "\n";
  exit();
}

$sql = 'select host, user from mysql.user';

$stmt = $pdo->query($sql);

$result = $stmt->fetchall();

echo '<pre>';
var_dump($result);
echo '</pre>';

それでは、Playbookを実行します

$ ansible-playbook -i hosts site.yml

localhostに接続して下記のようなmysqlとの接続確認やwith_itemsに追加したユーザー(今回であればsample)が確認できればOKです。

image.png

これでとても殺風景な画面が出来上がりました:sunny:

group_varsを使ってコードをよりシンプルにできていないところや
Ansible以前にCentOSやyumリポジトリに関して勉強不足な点が多々出てきました。
まだまだ課題ばかりですが、とても頼もしい先輩についていけるように頑張ります。

参考

Ansibleベストプラクティス

調べなきゃ寝れない!と調べたら余計に寝れなくなったソケットの話

nginx と PHP-FPM の仕組みをちゃんと理解しながら PHP の実行環境を構築する

最後に

これからもよろしくお願いします。

13
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
3