[Mackerel] AWS EC2でのhost設定

EC2のMackerel導入手順。取り敢えず確認しておきます。

1. Amazon Linux 2 AMI (HVM), SSD Volume Typeでインスタンスをlaunch

2. mackerelの管理画面から新規ホストの登録を行います。

– Amazon Linux 2 LTS

curl -fsSL https://mackerel.io/file/script/amznlinux/setup-all-yum-v2.sh | MACKEREL_APIKEY='hoge' sh

3. sshログイン
$ ssh ec2-user@${ip} -i ~/.ssh/aws-dev.pem

4. 先ほどのyum setupを実行する

curl -fsSL https://mackerel.io/file/script/amznlinux/setup-all-yum-v2.sh | MACKEREL_APIKEY='hoge' sh

$ sudo /sbin/service mackerel-agent start
$ sudo journalctl -u mackerel-agent.service

mackerelの管理画面のHostsで確認します。

大丈夫そうですね。
テスト用なので、退役にして、ec2はterminateします。

よっしゃああああああああああああああ

[AWS EC2] apache logの見方

ssh ec2-user@${public ip} -i ~/.ssh/***.pem
cat /etc/httpd/conf.d/custom.conf

# アクセスログ
<IfModule log_config_module>
    CustomLog "/var/www/log/access_log" combined
</IfModule>

$ cd /var/www/log/
$ ls
access_log error_log

$ sudo less /var/log/httpd/error_log
$ sudo less /var/log/httpd/access_log

### アクセスログの見方
$ cat /etc/httpd/conf/httpd.conf
LogFormat “%h %l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\” %I %O” combinedio

%h リモートホストのIPアドレス
%l 接続元のユーザー名
%u リモートユーザ
%t アクセスされた日時
\”%r\” アクセスされたファイル
%>s ステータスコード
%b リソースに対する転送量
\”%{Referer}i\” アクセス元のURL
\”%{User-Agent}i\” なんのOSでどのブラウザからアクセスしてきたか

なるほど。

[Laravel8.x] Amazon Linux2のVagrant/EC2開発環境構築

Laravel8.xをAmazon Linux 2 AMI(HVM), SSD Volume Type 64-bit(x86)で動かすことを想定して、Vagrant or EC2の開発環境構築をやっていきたい。
8系はPHP >= 7.3なので、7.4を入れるのがポイント。他は大体いつもの通りです。
EC2の場合はvagrant initは飛ばしてsshログインから始めてください。

### 1.vagrant initからsshログインまで
$ mkdir amazonlinux2
$ cd amazonlinux2
$ vagrant init gbailey/amzn2
$ vi vagrantfile
// 35行目ポートフォワーディング解除

config.vm.network "private_network", ip: "192.168.33.10"

$ vagrant up
$ vagrant ssh-config –host 192.168.33.10
$ vagrant ssh
// sshログイン後
$ cat /etc/*release
$ sudo yum update

### 2.Gitインストール(2.29.2)
$ sudo yum -y install gcc curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker autoconf
// ダウンロード対象を確認(https://mirrors.edge.kernel.org/pub/software/scm/git/)
// 11月3日時点で最新のgit2.29.2を入れる
$ cd /usr/local/src/
$ sudo wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.29.2.tar.gz
$ sudo tar xzvf git-2.29.2.tar.gz
$ sudo rm -rf git-2.29.2.tar.gz
$ cd git-2.29.2
$ sudo make prefix=/usr/local all
$ sudo make prefix=/usr/local install
$ git –version
git version 2.29.2

### 3.Node.jsインストール(v11.15.0)
// 11系を入れる
$ curl –silent –location https://rpm.nodesource.com/setup_11.x | sudo bash –
$ yum install -y gcc-c++ make
$ sudo yum install -y nodejs
$ node –version
$ npm –version

### 4.Apacheインストール(Apache/2.4.46)
$ sudo yum install httpd
$ sudo systemctl start httpd
$ sudo systemctl status httpd
$ sudo systemctl enable httpd
$ sudo systemctl is-enabled httpd
$ httpd -v

### 5.PHP7.4インストール ※laravel8.xはPHP >= 7.3
$ amazon-linux-extras
$ amazon-linux-extras info php7.4
$ sudo amazon-linux-extras install php7.4
$ yum list php* | grep amzn2extra-php7.4
$ sudo yum install php-cli php-pdo php-fpm php-json php-mysqlnd php-mbstring php-xml
$ php -v
PHP 7.4.11 (cli) (built: Oct 21 2020 19:12:26) ( NTS )
おっしゃーーーーーーーーーーーー  ここで一息入れてジンジャーエールを飲みます

### 6.MySQL(8.0.22)
$ sudo yum install https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
$ sudo yum install –enablerepo=mysql80-community mysql-community-server
$ sudo systemctl start mysqld
// パスワード変更
$ sudo cat /var/log/mysqld.log | grep “temporary password”
$ mysql -u root -p
mysql> ALTER USER ‘root’@’localhost’ IDENTIFIED BY ‘${temporary password}’;
mysql> SET GLOBAL validate_password.length=6;
mysql> SET GLOBAL validate_password.policy=LOW;
mysql> ALTER USER ‘root’@’localhost’ IDENTIFIED WITH mysql_native_password BY ‘${new password}’;
$ sudo systemctl enable mysqld
$ mysql -u root -p

### 7.Ansible(2.9.13)
$ sudo amazon-linux-extras install ansible2
$ ansible –version

### 8.Ruby(2.7.2)
$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
$ echo ‘export PATH=”$HOME/.rbenv/bin:$PATH”‘ >> ~/.bash_profile
$ echo ‘eval “$(rbenv init -)”‘ >> ~/.bash_profile
$ source ~/.bash_profile
// rbenvは時間がかかるので注意 痺れを切らさず待ちます
$ rbenv install 2.7.2
$ rbenv rehash
$ sudo yum install rubygems
$ gem update –system 2.7.2

### 9.AmazonLinux2 timezone変更
$ date
$ cat /etc/localtime
$ sudo vi /etc/sysconfig/clock

ZONE="Asia/Tokyo" 
UTC=false

$ sudo cp /etc/sysconfig/clock /etc/sysconfig/clock.org
$ strings /etc/localtime
$ date

お疲れ様でしたあああああああああああああああああああああ。githubに貼り付けときます。
今までPHP7.3で開発してたので、取り敢えず7.4が入ったあたりで満足した。
さて、いよいよcomposerでlaravel8.xをinstallしていきましょう。

[aws ec2] Ubuntu20.04 x Apache2 x Django3.0 x MySQL8系 環境構築

結論から言うと、ec2 x Djangoでapache2 or Nginxを使う場合、仮想環境を作らないと、Djangoのパスが通らずに、「ModuleNotFoundError: No module named ‘django’」とエラーになる。開発環境では、manage.py runserverで確認すれば良かったが、ec2にデプロイしてapache2もしくはNginxを通す場合は、仮想環境の構築が必須。
ec2でapache2もしくはNginxを構築する前に、uWSGIの基礎を学んでから構築した方が、理解が深まるし、トラブルシューティングしやすくなる。
それを省略したので、結局2日かかった orz…

以下が構築手順。Ubuntu20.04のインスタンスが出来ている状態から始める。

### 1.インスタンスログイン
$ ssh ubuntu@${public ip} -i ~/.ssh/*.pem
$ sudo apt update
$ sudo apt upgrade
$ sudo apt install python3-pip

### 2.MySQL8系インストールとdjango用のdb作成
$ sudo apt install mysql-client-core-8.0
$ sudo apt-get update
$ sudo apt install mysql-server
$ sudo service mysql start
$ sudo mysql_secure_installation
$ sudo mysql -u root -p
mysql>set global validate_password.length=6;
mysql>set global validate_password.policy=LOW;
mysql>CREATE USER ‘admin’@’%’ IDENTIFIED BY ‘hogehoge’;
mysql>GRANT ALL PRIVILEGES ON *.* TO ‘admin’@’%’ WITH GRANT OPTION;
mysql>FLUSH PRIVILEGES;
mysql>create database hoge;

### 3.apacheインストール
$ sudo apt update
$ sudo apt install apache2
$ sudo ufw app list
$ sudo ufw allow ‘Apache Full’
$ sudo ufw status
$ sudo systemctl status apache2

### 4.git clone
// 所有権
$ sudo chown ubuntu /home
$ git clone https://github.com/hoge/hoge.git
$ cd hoge

#### 5.django商用設定
settings.py

DEBUG = False
ALLOWED_HOSTS = ['*']  #もしくはEC2のIP
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
migration

// migration
$ python3 manage.py makemigrations hoges
$ python3 manage.py migrate

### 6. 仮想環境構築(!!重要!!)
$ sudo apt install -y python3-wheel python3-venv python3-dev
$ python3 -m venv env
$ source env/bin/activate
$ pip install wheel
// 各種ライブラリインストール 省略
$ apt-get install apache2-dev
$ sudo pip3 install mod_wsgi
$ mod_wsgi-express module-config
LoadModule wsgi_module “/usr/local/lib/python3.8/dist-packages/mod_wsgi/server/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so”
WSGIPythonHome “/usr”
$ python3 manage.py collectstatic
$ deactivate

### 7. Apache設定
$ sudo vi /etc/apache2/sites-available/django.conf

LoadModule wsgi_module /usr/local/lib/python3.8/dist-packages/mod_wsgi/server/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so

WSGIPythonHome /usr
WSGIScriptAlias / /home/ubuntu/hoge/hoge/wsgi.py
WSGIPythonPath /home/ubuntu/hoge:/home/ubuntu/hoge/env/lib/python3.8/site-packages

<Directory /home/ubuntu/hoge/hoge>
  <Files wsgi.py>
    Require all granted
  </Files>
</Directory>

Alias /static/ /home/ubuntu/hoge/hoge/
<Directory /home/ubuntu/hoge/static>
  Require all granted
</Directory>

$ sudo a2dissite 000-default
$ sudo a2ensite django
$ sudo systemctl restart apache2
$ sudo systemctl enable apache2

### 8.挙動確認
EC2のpublic IPを叩く

### 9.AMI作成
– インスタンスのバックアップ

お疲れ様でした。

ちなみにこれ、仮想環境構築をすっ飛ばしてインスタンス作成を5〜6回ぐらいやり直してapacheのエラーログ見て悶絶してた。まあ、ここを乗り切ればハードルは一気に下がりますね。

[aws ec2]ubuntuにsshする際にPermission denied (publickey).

$ ssh -i ~/.ssh/*.pem ec2-user@${public ip} -vvv
ec2-user@${public ip}: Permission denied (publickey).

セキュリティグループのinbound rulesでsshを許可しているのに何故だろうと調べていたところ、sshでログインする際のubuntuのuser nameはec2-userではなく、ubuntuらしい。

[公式]Linux インスタンス用ユーザーガイド

$ ssh ubuntu@${public ip} -i ~/.ssh/*.pem
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-1024-aws x86_64)

なるほど、わかりませんね。

[AWS] bastionサーバからprivate route tableへのSSHログインの方法 [ドハマり]

まず、public route table (public subnet割り当て)と private route table(private subnet割り当て) を作ります。

public route table
– internet gatewayを割り当てています。

private route table
– 後でnat gatewayを割り当て

private subnetに配置しているEC2のセキュリティグループのインバウンドルール
-> sshのルールで、bastion serverの ${private IPv4 ip}/32 を設定する

### ローカルからbastionにssh
$ ssh ec2-user@${basionserverのpublic ip} -i ~/.ssh/*.pem

そして、bastionサーバにログインし、/.sshに 秘密鍵(*.pem)を配置する

### bastionからprivate subnetのec2にログイン
$ ssh -i .ssh/*.pem ec2-user@${bastionserverのprivate ip}

すごいハマった。3時間ぐらい。
ローカルからはpublic ipでsshログインするけど、bationからはprivate IPv4 ipでログインする
これ違い理解してないと、なんでログインできないか永遠にわからなかったわ

EC2のmysql8.0.19でmmap(137363456 bytes) failed; errno 12

EC2でmysqlが頻繁に落ちる。
暫定的に以下を実行すると再起動できるが、1日経つとまた落ちる。

sudo touch /var/lib/mysql/mysql.sock
sudo chmod 777 /var/lib/mysql/mysql.sock
sudo service mysqld restart

cronの実行が影響しているのか? cronを止めて、PDOを手動実行するも問題なし。

ログを確認するとmmap(137363456 bytes) failed;となっている

$ sudo cat /var/log/mysqld.log
2020-03-21T20:45:21.161522Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.19) starting as process 12458
2020-03-21T20:45:21.299847Z 0 [ERROR] [MY-012681] [InnoDB] mmap(137363456 bytes) failed; errno 12
2020-03-21T20:45:21.299951Z 1 [ERROR] [MY-012956] [InnoDB] Cannot allocate memory for the buffer pool
2020-03-21T20:45:21.300002Z 1 [ERROR] [MY-012930] [InnoDB] Plugin initialization aborted with error Generic error.
2020-03-21T20:45:21.301125Z 1 [ERROR] [MY-010334] [Server] Failed to initialize DD Storage Engine
2020-03-21T20:45:21.301283Z 0 [ERROR] [MY-010020] [Server] Data Dictionary initialization failed.
2020-03-21T20:45:21.301362Z 0 [ERROR] [MY-010119] [Server] Aborting
2020-03-21T20:45:21.306253Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.19)  MySQL Community Server - GPL.

innodb_buffer_pool_sizeを超えていることがわかる

mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 134217728 |
+-------------------------+-----------+
1 row in set (0.00 sec)

buffer_pool_sizeを256Mに変更
$ sudo vi /etc/my.cnf

[mysqld]
innodb_buffer_pool_size = 256M

$ sudo service mysqld restart
mysql> SHOW VARIABLES LIKE ‘innodb_buffer_pool_size’;
+————————-+———–+
| Variable_name | Value |
+————————-+———–+
| innodb_buffer_pool_size | 268435456 |
+————————-+———–+
1 row in set (0.02 sec)

これで2~3日様子を見よう。

EC2(amazon linux2)上でDockerを使う

テスト用ですぐにterminateするため、EC2のスペックは最小限にします

### Launch instance
– Amazon Linux 2 AMI (HVM), SSD Volume Type
– type: t2.nano
– subnet: public
– Auto-assign Public IP: enable
– volume: GP2 8Gib
– Security Group: HTTP 0.0.0.0/0, SSH 0.0.0.0/0

### 前準備
$ ssh ec2-user@${public ip} -i ~/.ssh/${秘密鍵}.pem

// git install
$ sudo yum update
$ sudo yum -y install gcc curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker autoconf
// 省略

### EC2にDocker.ceインストール
// Extras Libraryを見てみる
$ amazon-linux-extras
20 docker=latest enabled \
[ =17.12.1 =18.03.1 =18.06.1 =18.09.9 =stable ]
$ amazon-linux-extras info docker
docker recommends docker # yum install docker

ありますね、extrasからインストールします。
$ sudo amazon-linux-extras install docker
$ docker –version
Docker version 18.09.9-ce, build 039a7df

### Docker-compose install
$ sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ docker-compose –version
docker-compose version 1.21.2, build a133471

## docker構築
### docker-compose.yml & Dockerfile
-docker-composeの構成やimageはlocal環境(ubuntu)と同じにする
-> nginx:1.13.5-alpine, node:12.13.0-alpine&php:7.3-fpm-alpine, FROM mysql:5.7.19

$ docker-compose build
ERROR: Couldn’t connect to Docker daemon at http+docker://localhost – is it running?
あれ?
$ sudo docker-compose build
sudo: docker-compose: command not found
ん?

権限をec2-userに付与する
$ sudo usermod -a -G docker ec2-user
$ cat /etc/group |grep docker
docker:x:993:ec2-user

$ docker info
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
あれ?

$ sudo service docker start
$ sudo docker info

### build & up -d
$ docker-compose build
$ docker-compose up -d
$ docker-compose ps
Name Command State Ports
———————————————————————-
app-1 docker-php-entrypoint php-fpm Up 9000/tcp
db-1 docker-entrypoint.sh mysql … Exit 1
nginx-1 nginx -g daemon off; Up 0.0.0.0:80->80/tcp
ん?
$ docker logs db-1

EC2でもswapメモリを増設する
$ sudo /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024
$ sudo /sbin/mkswap /var/swap.1
$ sudo /sbin/swapon /var/swap.1

$ docker-compose ps
Name Command State Ports
————————————————————————-
app-1 docker-php-entrypoint php-fpm Up 9000/tcp
db-1 docker-entrypoint.sh mysql … Up 0.0.0.0:3306->3306/tcp
nginx-1 nginx -g daemon off; Up 0.0.0.0:80->80/tcp

### 動作確認
$ docker exec -it app-1 sh
/var/www/html # ls
index.html index.php
$ docker exec -it nginx-1 sh
/ # cd /var/www/html
/var/www/html
$ docker exec -it db-1 bash
root@3e44157ee4fc:/# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.19 MySQL Community Server (GPL)

publicIPを叩く
http://**.***.***.***/
-> OK

なるほど、ローカルと全く一緒なので割と直ぐに環境構築ができます。
any application, anywhereが実感できる。
EC2の場合は冗長化が前提なので、mysqlのコンテナは必要なしでRDSに繋げるところが異なるか。vagrantが長かったせいか、商用環境にコンテナを使うのは違和感があったが、実際動かしてみると、Dockerでも別に差し支えないように見える。

インフラに変更を加えなければ、デプロイはCodeDeployで対応できそうだが、パッケージ等を入れるにしても、Dockerfileを更新するだけってのは確かに良いかも。
そして、デプロイやロードバランサまわり、オートスケーリングなどをkubernetesで管理するのね。

EC2をprivate subnetに移し、SSH用のbastionサーバの構築

### 既存のWebサーバのAMIを取得
Instances -> Actions -> Image -> Create Image

### purivate subnetのインスタンスの作成
作成したAMIから、再度インスタンスを作る
${appName}-prd-01-private
vpc:dev-vpc
subnet:dev-subnet-private1
Public IP:enable
IAM role: s3readonly
Security group: prd

${appName}-prd-02-private
vpc:dev-vpc
subnet:dev-subnet-private2
Public IP:enable
IAM role: s3readonly
Security group: prd

### public subnetのインスタンス削除
instance -> instance state -> terminate

### ALBのターゲットグループに追加
TargetGroup -> Target -> 追加
->ALBのDNSを叩いて動作確認
->コンソールからinstanceのpublic ipを叩いてssh接続できない事を確認

### bastionのサーバ構築
Instance Type: t2.nano
VPC: dev-vpc
subnet: dev-public-subnet
Auto-assign Public IP: enable
storage size: 8Gib
tag: ${appName}-prd-bastion
security group: create ${appName}-sg-bastion, Custom TCP
key pair: 新規推奨

### bastionのkey pair
bastionサーバにログインし、home/ec2-user/.sshに 秘密鍵(***.pem)を配置する
bastionにSSHログイン

$ ssh ec2-user@*** -i  ~/.ssh/***.pem
$ sudo chmod 600 /home/ec2-user/.ssh/***.pem
$ cd .ssh
$ ls -l

### Webサーバのセキュリティグループ
ssh接続のソースをmyIPに変更

### bastionからprivate subnetのweb serverにログイン
$ ssh ec2-user@${private_ip} -i .ssh/aws-dev.pem

$ curl https://www.google.co.jp/
-> timeout
-> private subnetは、インターネットゲートウェイのルーティングが存在しないことが原因

### NATゲートウェイ作成
VPC -> NATGateways -> create
subnet: dev-subnet-public1
Elastic IP: 割り当て

Private route table
NAT Gateway 割り当て

curl https://www.google.co.jp/
-> レスポンスが返るようになる。

ansible-playbookでvagrantからEC2にデプロイする方法

[EC2側]
まず、EC2を起動し、ターミナルからssh接続
$ ssh ec2-user@**** -i ~/.ssh/***.pem
$ cd /home/release
$ ls
LICENSE appspec.yml index.html scripts

[vagrant側]
.ssh/、inventory/hostsを作成済み
/hosts

[targets]
**.***.**.**

今回デプロイするtest.htmlを作成する

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	ansible deploy test
</body>
</html>

$ ls
LICENSE appspec.yml index.html inventory playbook.yml scripts test.html

playbook.yml

- hosts: targets
  sudo: yes
  tasks:
    - name: put test.html
      copy: src=test.html dest=/home/release/ owner=root group=root mode=640

### デプロイ実行
$ ansible-playbook -i inventory/hosts –private-key=.ssh/***.pem playbook.yml -u ec2-user
PLAY [targets] *****************************************************************

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

TASK [put test.html] ***********************************************************
changed: [**.***.**.**]

PLAY RECAP *********************************************************************
**.***.**.** : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

### EC2側で確認
$ ls
LICENSE appspec.yml index.html scripts test.html
$ sudo cat test.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	ansible deploy test
</body>
</html>

playbook.ymlは、デプロイする場合は、copy: src=${source file} dest=${dest directory} owner=root group=root mode=640 でOK
test.htmlを修正して、再度ansible-playbookを叩いた場合でも、EC2側で反映されていることが確認できます。