顯示具有 nginx 標籤的文章。 顯示所有文章
顯示具有 nginx 標籤的文章。 顯示所有文章

2022/10/18

Reload Nginx And Apache Config In Docker

如果在本機安裝 apache2 或 nginx 時想要 reload config 的話我們通常會使用下列的指令:

# nginx
nginx -s reload

# apache2
service httpd reload

但在 docker 的環境下就沒有那麼簡單,像 apache 的 reload 是把 script 寫在 service 的 config 裡面,不一定每個 image 裡面都有包含這個指令,CentOS 的我記得好像就沒有,為了要兼容所有的環境,我們可以利用 kill -USR1 這個方式套用到每個我們想要 reload without restart 的專案上面。

apache2

首先在根目錄建立兩個網頁檔:

  • ./www/1/index.html
  • ./www/2/index.html

各別會在網頁印出 1 以及 2 的內容識別。

docker-compose.yml

version: "3.9"

services:
  http:
    ports:
      - "80:80"
    container_name: httpd
    image: httpd
    restart: always
    volumes:
      - "./www:/www"
      - "./config/httpd:/usr/local/apache2/conf:ro"

我們建立了一個名為 http 的 container,使用 httpd 的 image,並且把 www 目錄掛進去,我事先把 httpd 的 config 複製出來以便在外面修改內容,接下來跑 docker compose up -d,打上 IP 後可以看到 httpd 傳統的首頁內容 It works!。

接著我們修改 httpd.conf:

DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">

# 改為

DocumentRoot "/www/1"
<Directory "/www/1">

我們先使用 docker compose restart 測試,執行後 refresh 網頁可以看到我們要的 1,接著再把 config 改成:

DocumentRoot "/www/2"
<Directory "/www/2">

這次我們使用 docker compose kill -s USR1 http ,此時我們可以看到 container 活得好好的,但 refresh 頁面後得到我們想要的 2 了,他的原理就是呼叫 container 內的 httpd daemon 去重新整理他的 service。

nginx

docker-compose.yml

version: "3.9"

services:
  http:
    ports:
      - "80:80"
    container_name: nginx
    image: nginx
    restart: always
    volumes:
      - "./www:/www"
      - "./config/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf:ro"

一樣,我們事前複製了 nginx 的 config 出來重新掛載回去,啟動 container 以後看見 nginx 的預設字樣 Welcome to nginx!。

接著我們修改 defalut.conf

root   /usr/share/nginx/html;

# 改為

root   /www/1;

跟 apache2 一樣我們先使用 docker compose restart,可以順利看到 1 的結果,接著我們將 config 修改為:

root   /www/2;

使用 docker compose exec http nginx -s reload,我們就可以順利看到 2 了,nginx 跟 apache2 的 container 啟動方式不太一樣,所以不能用 kill 的方法,但事實上在 nginx 的 logrotate config 裡面他使用的方式也是:

kill -USR1 `cat /var/run/nginx.pid`

上面介紹了兩個主流網頁伺服器的重取 config 的目的是在當我們把 server 的 log 掛到我們本機目錄時,我們可能會需要依照情況在本機執行 logrotate 的動作,這樣就可以把 reload 腳本寫在本機的 logrotate config 的 postrotate/endscript 裡面了。

這邊附上 logrotate for docker 基本的配置:

nginx-docker

/YOUR_PATH_TO_LOG/*.log {
        daily
        missingok
        rotate 52
        compress
        delaycompress
        notifempty
        create 640 nginx adm
        sharedscripts
        postrotate
            sudo docker exec YOUR_CONTAINER_NAME nginx -s reload
        endscript
}

httpd-docker

/YOUR_PATH_TO_LOG/*.log {
        daily
        missingok
        rotate 14
        compress
        delaycompress
        notifempty
        sharedscripts
        postrotate
            sudo docker kill -s USR1 YOUR_CONTAINER_NAME 
        endscript
}

2020/12/01

php-fpm config

apache 跟 nginx 都有接觸,nginx 上本就是用 php-fpm 跟 php 溝通,現在 apache 上我也捨棄內建的 php module,改用 fcgi 去溝通了,有幾個好處:

  1. apache 內建 php module 必須綁死 php 版本
  2. 因為綁死 php 版本如果有使用 virtual host 的話要做到每個網站跑自己的 php 版本很麻煩
  3. php-fpm 效能以及穩定性綜合值應該是優於 apache php module

在 Ubuntu 上我選擇了 ondrej/php 當作 repo

sudo add-apt-repository ppa:ondrej/php
sudo apt-get update
apt install php7.3 php7.3-fpm

CentOS 上使用 remi

dnf install dnf-utils http://rpms.remirepo.net/enterprise/remi-release-8.rpm
dnf module reset php
dnf module enable php:remi-7.4
dnf install php

Config

Apache

Through socket file
<VirtualHost>
    # someting config
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/var/run/php/php7.2-fpm.sock|fcgi://localhost/"
    </FilesMatch>    
</VirtualHost>
Through port
<VirtualHost>
    # someting config
    <FilesMatch \.php$>
        setHandler "proxy:fcgi://127.0.0.1:9000"
    </FilesMatch>  
</VirtualHost>

Apache 使用上必須確定 proxy_module 以及 proxy_fcgi_module 這兩個 module 有開啟,前者必須在後者上方先載入。

Nginx

location ~* \.php$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_split_path_info ^(.+\.php)(.*)$;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Reference

2020/11/26

Temporary SSL config for Apache and Nginx

開發時需要測試 ssl 的功能或配置結果但不需要真的 ssl 證書時,我們可以生成自己的 ssl 配置在本機使用 https 連入。

生成證書

openssl req -nodes -newkey rsa:2048 -sha256 -keyout server.key -out server.csr

# 生成過程其他問題都可以亂填,但 FQDN 必須填對
# 這邊使用 ssl.local 當作測試 domain
# Common Name (e.g. server FQDN or YOUR name) []:ssl.local

openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

Nginx config

server {
    listen      443;
    listen  [::]:443;
    server_name  ssl.local;

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

    ssl on;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_certificate /etc/nginx/ssl/server.crt;

    location ~ /\.ht {
        deny  all;
    }
}

Apache Config

<VirtualHost *:443>
    ServerName ssl.local
    DocumentRoot /var/www/ssl/
 
    SSLEngine on
    SSLCertificateKeyFile /usr/local/apache2/conf/server.key
    SSLCertificateFile /usr/local/apache2/conf/server.crt
    SSLCertificateChainFile /usr/local/apache2/conf/server.crt
 
    <Directory "/var/www/ssl/">
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>
</VirtualHost>

2017/02/27

Reverse Proxy For Docker

前言

現在很流行用 docker 處理一些服務,以我這行來講用來處理 LAMP 或者是 LNMP 最常見,也可能是產生個 gitlab 之類的,docker 最大的優點是服務都跑在 container,安裝快啟動也快,要幾個有幾個,寫好 config 像是 Dockerfile 之類的設定還可以一個指令搞定,讓你的電腦可以保持很乾淨,網站使用 80 port 當作預設的入口,但每個 server port 都是唯一的,所以假設你有很多網站服務,可能會指派很多不同的 port,像是 localhost:66666 或者是 localhost:66667,但有些測試你可能想要盡可能逼真,希望是用網址來跑服務的話,我們可以在 80 port 假一個反向的 proxy server,讓你可以將 domain 指定到帶 port 的 IP,下面會示範 nginx 以及 apache 的設定方式

Docker

我測試的環境是 Windows,我先使用 Vagrant 安裝一個 ubuntu 16 的 VM,IP 為 10.10.10.22,之後將會在這個 VM 裡面跑 nginx 以及 apache servie,還有 docker

我 container 使用的 image 是 novice/lnmp,我先啟動兩個 web service 服務

$ sudo docker run -itd --name web1 -p 33333:80 novice/lnmp
$ sudo docker run -itd --name web2 -p 33334:80 novice/lnmp

我將 33333 port 給 nginx 這個 web service 使用,33334 給 apache 使用,然後我們將 Windows 的 hosts 設定一下

10.10.10.22 docker.web1
10.10.10.22 docker.web2

接著我進到 apache container 的 /var/www 目錄,加上一個 hello.php

<?php

echo 'hello I am web1';

同樣在 nginx 的 container /var/www 加入 hello.php。

<?php

echo 'hello I am web2';

在網址輸入 http://docker.web1:33333/hello.php 以及 http://docker.web2:33334/hello.php 各別會看到 hello I am web1 以及 hello I am web2,不過其實目前兩個網址互換得到的結果也會一樣,因為現在主要是因為不同的 port 帶出不同的 web service,而我們要做的事情就是把 port 拿掉以後還會成立

nginx

先在這個 VM 安裝 nginx。

$ sudo apt-get install nginx

安裝好之後,我們在 /etc/nginx/sites-available的目錄建立一個 proxy.conf內容如下

server {
    listen 80;
    sendfile off;
    server_name docker.web1;

    location / {
        proxy_pass http://127.0.0.1:33333;
        proxy_redirect     off;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Host $server_name;
    }
}

server {
    listen 80;
    sendfile off;
    server_name docker.web2;

    location / {
        proxy_pass http://127.0.0.1:33334;
        proxy_redirect     off;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Host $server_name;
    }
}

接著設定 config,然後將服務重啟

$ sudo ln -s /etc/nginx/sites-available/proxy.conf /etc/nginx/sites-enabled/proxy.conf
$ sudo service nginx restart

此時如果你將 port 拿掉還是可以看到正確結果的話表示成功了,說明一下 sendfile off 主要是因為我使用 VM,這個不關的話會一直看到 cache,如果你是直接在 Linux 環境的話應該是不用特別設定

Apache

首先來安裝 apache,記得先將 nginx 關掉,並且確認兩個 proxy 必要的 module 有開

$ sudo service nginx stop
$ sudo apt-get install apache2
$ sudo a2enmod proxy
$ sudo a2enmod proxy_http

接著在 /etc/apache2/sites-available加入 proxy.conf,內容如下

<VirtualHost *:80>
    ServerName docker.web1
    ProxyPass / http://127.0.0.1:33334/
    ProxyPassReverse  / http://127.0.0.1:33334/
</VirtualHost>

<VirtualHost *:80>
    ServerName docker.web2
    ProxyPass / http://127.0.0.1:33333/
    ProxyPassReverse  / http://127.0.0.1:33333/
</VirtualHost>

接著我們製作 link,以及重起 apache

$ sudo a2ensite proxy.conf
$ sudo service apache2 restart

如果那兩個網址重整以後一樣可以看到對應的文字,表示 apache reserse proxy 也成功了