Docker/DockerSwarm部署LNMP并平滑升級(jí)PHP過程
一.基礎(chǔ)環(huán)境
1.配置系統(tǒng)內(nèi)核轉(zhuǎn)發(fā)功能
docker網(wǎng)絡(luò)依賴于系統(tǒng)的內(nèi)核轉(zhuǎn)發(fā)功能,如果不配置內(nèi)核轉(zhuǎn)發(fā),docker功能可能會(huì)出錯(cuò)
[root@webapi4-app-22-151 ~]# vi /etc/sysctl.conf net.ipv4.ip_forward = 1 net.ipv4.conf.default.rp_filter = 0 net.ipv4.conf.all.rp_filter = 0 [root@webapi4-app-22-151 ~]# sysctl -p
2.安裝docker-ce
移除舊版本,如果之前沒有安裝過docker則跳過
[root@webapi4-app-22-151 ~]# yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine
安裝工具
[root@webapi4-app-22-151 ~]# yum install -y yum-utils device-mapper-persistent-data lvm2
添加docker-ce的yum源
[root@webapi4-app-22-151 ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
安裝docker-ce
[root@webapi4-app-22-151 ~]# yum install -y docker-ce
開啟docker
[root@webapi4-app-22-151 ~]# systemctl daemon-reload [root@webapi4-app-22-151 ~]# systemctl start docker [root@webapi4-app-22-151 ~]# systemctl enable docker
3.配置鏡像倉庫
docker默認(rèn)從國(guó)外的DockerHub公共倉庫拉取鏡像,拉取鏡像的速度可能會(huì)很慢,所以要配置國(guó)內(nèi)的鏡像倉庫。
[root@webapi4-app-22-151 ~]#mkdir /etc/docker #如果文件夾已存在則跳過
新建daemon.json文件
[root@webapi4-app-22-151 ~]# cat /etc/docker/daemon.json
{
“registry-mirrors”: [“https://5twf62k1.mirror.aliyuncs.com”]
}
[root@webapi4-app-22-151 ~]# systemctl restart docker
[root@webapi4-app-22-151 ~]#docker info查看,配置鏡像倉庫成功

4.拉取鏡像
[root@webapi4-app-22-151 ~]# docker pull php:7.3.27-fpm [root@webapi4-app-22-151 ~]# docker pull nginx:latest [root@webapi4-app-22-151 ~]# docker pull mysql:8.0
查看當(dāng)前主機(jī)的鏡像

二、部署LNMP
新建nginx、php、mysql目錄,這三個(gè)目錄用于保存容器內(nèi)的配置文件和數(shù)據(jù)文件,防止刪除容器之后導(dǎo)致數(shù)據(jù)丟失。
[root@webapi4-app-22-151 ~]# mkdir -p /lnmp/{nginx,php,mysql}
[root@webapi4-app-22-151 ~]# mkdir /lnmp/mysql/data1.搭建mysql
啟動(dòng)一個(gè)臨時(shí)的mysql容器
[root@webapi4-app-22-151 ~]# docker run -itd --name tmp mysql:8.0 /bin/bash
將mysql容器內(nèi)的配置文件拷貝出來,拷貝到主機(jī)的目錄
[root@webapi4-app-22-151 ~]# docker cp tmp:/etc/mysql /lnmp/mysql/

刪除臨時(shí)容器
[root@webapi4-app-22-151 mysql]# docker rm -f tmp
配置防火墻,放通端口
[root@webapi4-app-22-151 mysql]# firewall-cmd --add-port=3306/tcp
啟動(dòng)mysql容器
參數(shù)解釋
- -d:后臺(tái)運(yùn)行容器
- –name:指定容器名
- -p:端口映射,將主機(jī)的3306端口與容器的3306端口映射起來。外部訪問數(shù)據(jù)庫時(shí)可以通過主機(jī)的IP:Port來訪問,不需要訪問容器的IP:Port。
- –restart:指定容器的重啟策略,always表示總是重啟容器,可以實(shí)現(xiàn)容器異常退出時(shí)自動(dòng)重啟容器
- -e:指定容器的環(huán)境變量,其中MYSQL_ROOT_PASSWORD=000000表示設(shè)置數(shù)據(jù)庫root用戶的密碼為000000。mysql容器內(nèi)有腳本可以接收這個(gè)參數(shù),如果啟動(dòng)mysql容器時(shí)不指定這個(gè)環(huán)境變量,則容器可能會(huì)啟動(dòng)失敗。

- -v:映射數(shù)據(jù)卷,其中/lnmp/mysql/mysql/:/etc/mysql表示將主機(jī)的/lnmp/mysql/mysql/目錄映射為容器的/etc/mysql目錄。
查看容器

測(cè)試Mysql連接,使用主機(jī)的IP:Port訪問


連接mysql成功
2.搭建php
啟動(dòng)一個(gè)臨時(shí)的php容器
[root@webapi4-app-22-151 ~]# docker run -itd --name tmp php:7.3.27-fpm
將容器內(nèi)的配置文件復(fù)制到主機(jī)目錄
[root@webapi4-app-22-151 ~]# docker cp tmp:/usr/local/etc/ /lnmp/php
刪除臨時(shí)容器
[root@webapi4-app-22-151 ~]# docker rm -f tmp [root@webapi4-app-22-151 ~]# firewall-cmd --add-port=9000/tcp
啟動(dòng)php容器
[root@webapi4-app-22-151 ~]# docker run -itd -p 9000:9000 --name php --restart always -v /lnmp/php/etc/:/usr/local/etc -v /lnmp/nginx/html:/usr/share/nginx/html php:7.3.27-fpm
參數(shù)解釋
- -i:打開容器的標(biāo)準(zhǔn)輸入
- -t:給容器分配一個(gè)終端
3.搭建nginx
啟動(dòng)臨時(shí)的nginx容器
[root@webapi4-app-22-151 ~]# docker run -d --name tmp nginx:latest
將容器內(nèi)的網(wǎng)頁文件和配置文件拷貝出來,拷貝到主機(jī)目錄
[root@webapi4-app-22-151 ~]# docker cp tmp:/usr/share/nginx/html/ /lnmp/nginx/ [root@webapi4-app-22-151 ~]# docker cp tmp:/etc/nginx /lnmp/nginx/

刪除臨時(shí)容器
[root@webapi4-app-22-151 ~]# docker rm -f tmp [root@webapi4-app-22-151 ~]# firewall-cmd --add-port=80/tcp
啟動(dòng)nginx容器
[root@webapi4-app-22-151 ~]# docker run -d --name nginx -p 80:80 --restart always -v /lnmp/nginx/html:/usr/share/nginx/html -v /lnmp/nginx/nginx/:/etc/nginx --link php nginx:latest
參數(shù)解釋
–link:容器互聯(lián),–link php表示nginx容器連接到php容器。設(shè)置這個(gè)參數(shù)之后,nginx容器內(nèi)的hosts文件會(huì)添加一條php容器的IP映射。
網(wǎng)頁訪問

4.將nginx和php連接起來
添加php測(cè)試頁面index.php

編輯nginx的配置文件default.conf。(之前nginx容器配置過-v參數(shù)的數(shù)據(jù)卷映射。修改主機(jī)目錄中的配置文件,那么文件的修改會(huì)同步到nginx容器,這樣子就省去了進(jìn)去容器修改文件的麻煩,而且容器內(nèi)一般是沒有vi命令的)
[root@webapi4-app-22-151 ~]# cat /lnmp/nginx/nginx/conf.d/default.conf
location / {
root /usr/share/nginx/html;
index index.php index.html index.htm;
}
location ~ \.php$ {
root /usr/share/nginx/html;
fastcgi_pass php:9000;
#之前nginx容器配置過--link php參數(shù),nginx容器可以使用php這個(gè)名字來連接
php容器。使用容器的IP來標(biāo)識(shí)php容器不太方便,因?yàn)閜hp容器異常重啟時(shí)容器
的IP可能會(huì)變化。
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi_params;
}
完整配置文件:
[root@webapi4-app-22-151 ~]# cat /lnmp/nginx/nginx/conf.d/default.conf
server {
listen 80;
listen [::]:80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.php index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
root /usr/share/nginx/html;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi_params;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
編輯好文件之后,重啟nginx容器
[root@webapi4-app-22-151 ~]# docker restart nginx
打開瀏覽器,訪問nginx

三、問題
1.php容器狀態(tài)異常
php容器一啟動(dòng)就退出,狀態(tài)異常,一直重啟

解決:在啟動(dòng)容器時(shí)添加兩個(gè)參數(shù)-it

容器就不會(huì)異常退出了
2.nginx連接php失敗
添加了php的測(cè)試主頁index.php,修改了nginx的配置文件來連接php之后,訪問nginx主頁失?。?/p>

用命令“docker logs nginx”查看nginx容器的日志:
2021/04/28 09:55:32 [error] 24#24: *4 FastCGI sent in stderr: “Primary script unknown” while reading response header from upstream, client: 125.218.208.62, server: localhost, request: “GET / HTTP/1.1”, upstream: “fastcgi://172.17.0.3:9000”, host: “192.168.22.151”

解決:編輯nginx的default.conf文件,在location ~ .php$模塊中將
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
修改成
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

然后使用命令“docker exec nginx nginx -s reload”重載文件
訪問成功

四、DockerSwarm平滑升級(jí)PHP版本
一些優(yōu)秀的容器平臺(tái)/產(chǎn)品(例如Docker Swarm、Kubernetes、Rancher),集成了“平滑升級(jí)應(yīng)用”的功能,借助這些平臺(tái),能夠比較順利、方便地完成應(yīng)用升級(jí)。
上面部署的LNMP是通過docker部署三個(gè)容器(mysql容器、php容器、nginx容器)完成的;現(xiàn)在部署的LNMP是通過docker swarm集群(單節(jié)點(diǎn))部署三種服務(wù)(mysql服務(wù)、php服務(wù)、nginx服務(wù))來完成的,每種服務(wù)可以包含多個(gè)容器,這樣的話即使其中一個(gè)容器異常那么其他容器也能夠正常提供服務(wù)。
這里演示PHP從7.3.27版本升級(jí)到7.4.16 版本。

刪除之前部署的mysql、php、nginx容器
[root@webapi4-app-22-151 ~]# docker rm -f mysql php nginx
查看容器,沒有容器在運(yùn)行
[root@webapi4-app-22-151 ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@webapi4-app-22-151 ~]#
之前部署LNMP時(shí)用數(shù)據(jù)卷實(shí)現(xiàn)了數(shù)據(jù)持久化,即使把nginx、mysql、php容器刪除,容器的配置文件、數(shù)據(jù)文件依然存留在主機(jī)中。接下來直接引用這些現(xiàn)成的配置文件和數(shù)據(jù)文件。

1.初始化集群
[root@webapi4-app-22-151 ~]# docker swarm init --advertise-addr 192.168.22.151 #192.168.22.151為當(dāng)前主機(jī)的IP地址
查看集群里的節(jié)點(diǎn)

2.創(chuàng)建overlay網(wǎng)絡(luò)
創(chuàng)建一個(gè)名字為lnmp-demo的overlay類型網(wǎng)絡(luò)。
[root@webapi4-app-22-151 ~]# docker network create --driver overlay lnmp-demo
3.部署LNMP服務(wù)
創(chuàng)建PHP服務(wù)(7.3.27版本)
[root@webapi4-app-22-151 ~]# docker service create --name php --network lnmp-demo --publish 9000:9000 --mount type=bind,src=/lnmp/php/etc,dst=/usr/local/etc --mount type=bind,src=/lnmp/nginx/html,dst=/usr/share/nginx/html --replicas=4 php:7.3.27-fpm
參數(shù)解釋
- –name:設(shè)置服務(wù)名稱
- –network:設(shè)置服務(wù)的網(wǎng)絡(luò)
- –publish:暴露端口,9000:9000表示把主機(jī)的9000端口與容器的9000端口映射起來
- –mount:掛載文件系統(tǒng),bind指主機(jī)目錄掛載到容器目錄
- –replicas:設(shè)置服務(wù)的副本數(shù)量,這里值為4,也就是說PHP服務(wù)內(nèi)有4個(gè)PHP容器
創(chuàng)建mysql服務(wù)
[root@webapi4-app-22-151 ~]# docker service create --name mysql --network lnmp-demo --publish 3306:3306 --replicas=4 --env MYSQL_ROOT_PASSWORD=000000 --mount type=bind,src=/lnmp/mysql/mysql,dst=/etc/mysql mysql:8.0
創(chuàng)建nginx服務(wù)
[root@webapi4-app-22-151 ~]# docker service create --name nginx --network lnmp-demo --publish 80:80 --mount type=bind,src=/lnmp/nginx/nginx,dst=/etc/nginx --mount type=bind,src=/lnmp/nginx/html,dst=/usr/share/nginx/html --replicas 4 nginx:latest
查看服務(wù)狀態(tài)

4.連接nginx與php
編輯nginx的default.conf配置文件
location ~ \.php$ {
root /usr/share/nginx/html;
fastcgi_pass php:9000;
#三種容器運(yùn)行在同一個(gè)overlay網(wǎng)絡(luò),服務(wù)之間可以用服務(wù)名或服務(wù)的虛擬IP來通信。
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi_params;
}
完整配置文件:
server {
listen 80;
listen [::]:80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.php index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
root /usr/share/nginx/html;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi_params;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
重啟nginx服務(wù)內(nèi)的所有容器
[root@webapi4-app-22-151 ~]# docker restart $(docker ps -a -f name=nginx --format ‘{{.Names}}')訪問nginx主頁,可以看到當(dāng)前PHP版本是7.3.27

升級(jí)PHP
Docker Swarm更新版本時(shí),先停止一個(gè)舊版本容器,然后啟動(dòng)一個(gè)新版本容器,隔一段時(shí)間之后再停止一個(gè)舊版本容器、啟動(dòng)一個(gè)新版本容器,一直到所有舊版本容器停止、所有新版本容器啟動(dòng)完成,更新完成。
執(zhí)行docker service update命令,對(duì)PHP服務(wù)進(jìn)行升級(jí)
[root@webapi4-app-22-151 ~]# docker service update --image php:7.4.16-fpm --update-parallelism 1 --update-delay 10s --update-max-failure-ratio 0.5 --update-failure-action pause php
參數(shù)解釋
- –image: 設(shè)置更新的鏡像,這里用php:7.4.16-fpm,也就是把PHP服務(wù)的鏡像換成php:7.4.16-fpm鏡像,即升級(jí)為7.4.16版本
- –update-parallelism :設(shè)置并行更新的副本數(shù),這里設(shè)置為1,也就是停止1個(gè)舊版本容器,啟動(dòng)1個(gè)新版本容器。設(shè)置為2的話則表示每停止2個(gè)舊版本容器,啟動(dòng)2個(gè)新版本容器。
- –date-delay:更新間隔,這里設(shè)置為10秒。也就是更新一次容器之后,過10秒再進(jìn)行下一次更新。
- –update-max-failure-ratio:更新期間可以容忍的故障率,這里設(shè)置為0.5即50%,也就是說如果更新過程中有50%容器更新失敗那么就認(rèn)為此次更新是失敗的,與–update-failure-action參數(shù)搭配使用。
- –update-failure-action:設(shè)置更新失敗之后的策略,pause表示更新失敗之后停止更新,與上面的–update-max-failure-ratio參數(shù)連用,兩個(gè)參數(shù)結(jié)合起來的作用是:當(dāng)50%容器更新失敗之后,就停止更新。另外,–update-failure-action還可以設(shè)置成rollback,即更新失敗后自動(dòng)回滾上一個(gè)版本。
更新過程:



更新完成,可以看到PHP服務(wù)已經(jīng)升級(jí)到7.4.16版本。

訪問更新PHP之后的Nginx

至此,PHP版本升級(jí)完成
更新之后,舊版本容器沒有被刪除,占用空間

刪除舊版本容器,docker rm $(docker ps -qa -f status=exited)

6.回滾測(cè)試
現(xiàn)在PHP已經(jīng)升級(jí)到7.4.16版本,如果想要PHP回滾成舊的7.3.27版本,可以使用docker swarm的rollback功能來實(shí)現(xiàn):


五、DockerSwarm升級(jí)應(yīng)用遇到的問題
1 . File not found
執(zhí)行以下三條命令部署三種服務(wù)

并且連接了nginx和php之后,訪問nginx主頁顯示File not found:

原因是三種服務(wù)運(yùn)行沒有運(yùn)行在overlay網(wǎng)絡(luò)中,導(dǎo)致服務(wù)之間無法通過服務(wù)名來通信。
解決:刪除三種服務(wù),然后新建overlay網(wǎng)絡(luò),讓三種服務(wù)運(yùn)行在同一個(gè)overlay網(wǎng)絡(luò)中

就可以正常顯示網(wǎng)頁了

總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用Docker部署todo任務(wù)管理器的實(shí)現(xiàn)
本文介紹使用Docker部署Todo任務(wù)管理器的全過程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-07-07
Docker安裝配置RabbitMQ的實(shí)現(xiàn)步驟
RabbitMQ是基于AMQP的一款消息管理系統(tǒng)。AMQP(Advanced?Message?Queuing?Protocol),是一個(gè)提供消息服務(wù)的應(yīng)用層標(biāo)準(zhǔn)高級(jí)消息隊(duì)列協(xié)議,其中RabbitMQ就是基于這種協(xié)議的一種實(shí)現(xiàn)2021-11-11
Docker容器生命周期 | kill和 stop的區(qū)別與聯(lián)系 | d
這篇文章主要介紹了Docker容器生命周期 | kill和 stop的區(qū)別與聯(lián)系 | docker pause/ unpause,本講內(nèi)容是從?Docker入門到進(jìn)階里面抽離出來的內(nèi)容,從而使原文更加有序、重點(diǎn)突出,需要的朋友可以參考下2023-08-08
一次Docker中Redis連接暴增的問題排查實(shí)戰(zhàn)記錄
這篇文章主要給大家介紹了一次Docker中Redis連接暴增的問題排查的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06

