この記事はアクシス Advent Calendar 2020 4日目です。
はじめに
2020年10月に未経験で入社したムラマツです。
今回の記事は、Ansibleに触れる機会をいただいたこともあり、
復習をかねてAnsibleを使って環境構築することについて重点的に書きます。
Ansibleの概念や詳細の機能などに関しては記事が長くなりすぎるので、
またの機会に投稿していこうと思います。
まだまだ至らぬ点が多々あるかと思いますが、
間違い等ございましたらご指摘いただけると幸いです
行うこと
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ファイル
---
- import_playbook: common.yml
- import_playbook: web.yml
- import_playbook: db.yml
---
- hosts: all
become: yes
roles:
- { role: firewalld, tags: firewalld }
---
- hosts: web
become: yes
roles:
- { role: nginx, tags: nginx }
- { role: php-fpm, tags: php-fpm }
---
- hosts: db
become: yes
roles:
- { role: mysqld, tags: mysqld }
上記設定で進めて参ります。
Nginx
まずはNginxからインストールしていきます。
---
- 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を作成します。
[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の設定ファイルを修正していきます。
#省略
http {
## 省略
include /etc/nginx/conf.d/*.conf;
}
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ファイルを修正していきます。
- name: Restart nginx
service:
name: nginx
state: restarted
Handlerは、Taskの実行時にcahngedが出てた時だけ実行されるジョブの仕組みです。
ここまでで、nginxのインストールから起動まで設定が完了しました。
ですが、このままではfirewallでhttpポートが塞がれているため
firewalldに対しhttpポート開放の設定をしていきます。
そのためfirewalldのroleを作成します。
---
- 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サービスを追加していきます。
DefaultZone=public
CleanupOnExit=yes
Lockdown=no
IPv6_rpfilter=no
IndividualCalls=yes
LogDenied=off
FirewallBackend=iptables
FlushAllOnReload=yes
RFC3964_IPv4=yes
AllowZoneDrifting=yes
- name: reload firewalld
service:
name: firewalld
state: reloaded
以下のコマンドを使用して Playbook を実行します
$ ansible-playbook -i hosts common.yml web.yml
PHP
続いてPHPをインストールしていきます。
---
- 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がインストールされるように設定しています。
---
php_packages:
- '@php:remi-8.0'
- php-mbstring
- php-xml
- php-pdo
- php-mysqlnd
今回はmysqlと接続確認をするため必要なパッケージをインストールしています。
variableに記述することで他に必要なパッケージをインストールする際、
vars
に追記するだけでインストールできるようにしています。
[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ファイルも修正していきます。
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ファイルを作成します。
<?php
echo 'Hello world';
?>
以下のコマンドを使用して Playbook を実行します
$ ansible-playbook -i hosts common.yml web.yml
記載したHello worldが画面上に表示されていればOKです。
MySQL
最後にMySQLをインストールしていきます。
今回は、rootユーザーの初期パスワードを変更し、その後新規ユーザーを作成していきます。
冪同性を保たれるように初期パスワードは1回目の実装で削除するよう設定しています。
・regexp
: 書き換えたい行にマッチする正規表現
---
- 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の項目を増やすだけで作成できるようにしています。
---
mysql_root_password: PASSWORD
mysql_client_password: PASSWORD
セキュアな情報はvaultを使って暗号化した方がいいかと思いますので、
以下のコマンドで暗号化します。
※複数ファイル選択もできます。詳細は以下にて。
Ansible公式ドキュメント
$ ansible-vault encrypt roles/mysqld/vars/main.yml
MySQLと接続確認するためindex.phpファイルを編集していきます。
今回はPDOを使用して簡単な接続確認を試みました。
<?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です。
これでとても殺風景な画面が出来上がりました
group_vars
を使ってコードをよりシンプルにできていないところや
Ansible以前にCentOSやyumリポジトリに関して勉強不足な点が多々出てきました。
まだまだ課題ばかりですが、とても頼もしい先輩についていけるように頑張ります。
参考
調べなきゃ寝れない!と調べたら余計に寝れなくなったソケットの話
nginx と PHP-FPM の仕組みをちゃんと理解しながら PHP の実行環境を構築する
最後に
これからもよろしくお願いします。