学习docker为的是部署时能更轻松的解决环境问题,docker启动nginx相比学习了docker的朋友们基本上都会使用了,但要修改配置文件或者是部署dist前端项目就会有点麻烦了,如果直接进入容器,你会发现,容器中的环境基本上是一个阉割版的,Linux上的许多命令在容器中是无法使用的,所以这样是十分麻烦的。好在docker有数据卷挂载技术,但是在启动nginx时又会遇到一堆的问题。
情景再现:
docker run -d -p 80:80 -v /home/nginx/conf:/etc/nginx -v /home/nginx/html:/usr/share/nginx/html --name nginx01 nginx
输入上面命令,回车,再使用docker ps
查看会发现容器并没有正在运行:
[root@localhost home]# docker run -d -p 80:80 -v /home/nginx/conf:/etc/nginx -v /home/nginx/html:/usr/share/nginx/html --name nginx01 nginx
d3dd01432c1f0f2deb588deda51503d88048456c62d66a93f40a811a9c68991f
[root@localhost home]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
使用docker ps -a
查看发现容器确实有了,但不知道为什么自动停止了。
[root@localhost home]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d3dd01432c1f nginx "/docker-entrypoint.…" 2 minutes ago Exited (1) 2 minutes ago nginx01
这时使用docker logs 容器id(或者容器名)
查看容器日志信息
[root@localhost home]# docker logs nginx01
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: /etc/nginx/conf.d/default.conf is not a file or does not exist
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2021/01/14 03:24:08 [emerg] 1#1: open() "/etc/nginx/nginx.conf" failed (2: No such file or directory)
nginx: [emerg] open() "/etc/nginx/nginx.conf" failed (2: No such file or directory)
大概意思好像是/etc/nginx/nginx.conf
没有这个文件,这就有点好奇了,理论上运行一个nginx容器是绝对不会没有配置文件的,那就有可能是这个目录指代的不是容器中的目录。
解决方法:
在docker hub官网上查询了一下发现以下有用的信息:
官网这段话的大概意思就是,要给nginx挂载卷,要先有一个配置文件才能挂载,具体做法就是先以不挂载卷的形式正常启动一个nginx容器,然后从中cp
拷贝一份配置文件,删除这个容器,再重新以挂载卷的形式启动一个新容器。
先把之前的nginx01容器给删除,然后启动一个不挂载卷的nginx。
docker run -d -p 80:80 --name nginx-test nginx
[root@localhost home]# docker run -d -p 80:80 --name nginx-test nginx
278ac2479e5a6054cfdfbbd14171556e1a0065ff8b33f788c4f9a4cb72f4af65
[root@localhost home]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
278ac2479e5a nginx "/docker-entrypoint.…" 2 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp nginx-test
[root@localhost home]#
在Linux中使用curl localhost
查看是否正常启动服务,也可以在浏览器上输入服务器或虚拟机ip地址查看,不过端口一定要查看是否暴露了。
[root@localhost home]# curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@localhost home]#
这样就说明正常启动服务了,这个时候就把容器内的配置文件拷贝一份到本机上,使用docker cp
命令,不熟悉该命令的可以先使用docker cp --help
查看
[root@localhost home]# docker cp --help
Usage: docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
Copy files/folders between a container and the local filesystem
Use '-' as the source to read a tar archive from stdin
and extract it to a directory destination in a container.
Use '-' as the destination to stream a tar archive of a
container source to stdout.
Options:
-a, --archive Archive mode (copy all uid/gid information)
-L, --follow-link Always follow symbol link in SRC_PATH
[root@localhost home]#
我们这里拷贝不需要使用可选参数,先查看一下目录情况:
[root@localhost home]# ls
blog_admin ceshi mysql nginx
[root@localhost home]#
nginx目录是第一次以挂载卷形式启动出现的,虽然容器启动就停止,但目录确确实实生成了,其他目录是我的本地目录。使用rm -rf nginx/
删除nginx目录:
[root@localhost home]# rm -rf nginx/
[root@localhost home]# ls
blog_admin ceshi mysql
[root@localhost home]#
现在开始执行docker的拷贝命令:
docker cp nginx-test:/etc/nginx /home/nginx/conf
[root@localhost home]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
278ac2479e5a nginx "/docker-entrypoint.…" 7 minutes ago Up 6 minutes 0.0.0.0:80->80/tcp nginx-test
[root@localhost home]# docker cp nginx-test:/etc/nginx /home/nginx/conf
invalid output path: directory "/home/nginx" does not exist
[root@localhost home]#
这说明没有/home/nginx这个目录,可以先创建一个目录,mkdir nginx
[root@localhost home]# ls
blog_admin ceshi mysql
[root@localhost home]# mkdir nginx
[root@localhost home]# ls
blog_admin ceshi mysql nginx
[root@localhost home]#
然后再执行上面的docker cp命令:
[root@localhost home]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
278ac2479e5a nginx "/docker-entrypoint.…" 9 minutes ago Up 9 minutes 0.0.0.0:80->80/tcp nginx-test
[root@localhost home]# docker cp nginx-test:/etc/nginx /home/nginx/conf
[root@localhost home]# ls
blog_admin ceshi mysql nginx
[root@localhost home]# cd nginx/
[root@localhost nginx]# ls
conf
[root@localhost nginx]# cd conf/
[root@localhost conf]# ls
conf.d fastcgi_params koi-utf koi-win mime.types modules nginx.conf scgi_params uwsgi_params win-utf
[root@localhost conf]#
这样配置文件就正常拷贝过来了,把这个nginx-test的容器停止并删除。
[root@localhost conf]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
278ac2479e5a nginx "/docker-entrypoint.…" 12 minutes ago Up 3 seconds 0.0.0.0:80->80/tcp nginx-test
[root@localhost conf]# docker stop nginx-test
nginx-test
[root@localhost conf]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@localhost conf]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
278ac2479e5a nginx "/docker-entrypoint.…" 12 minutes ago Exited (0) 5 seconds ago nginx-test
[root@localhost conf]# docker rm nginx-test
nginx-test
[root@localhost conf]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@localhost conf]#
然后再使用第一次的挂载卷形式,启动容器。
[root@localhost conf]# docker run -d -p 80:80 -v /home/nginx/conf:/etc/nginx -v /home/nginx/html:/usr/share/nginx/html --name nginx01 nginx
3ccd5825375603510538fd07b8b39bd9cbb0924881c9b7041e87f702aa379424
[root@localhost conf]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3ccd58253756 nginx "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp nginx01
[root@localhost conf]# curl localhost
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.19.6</center>
</body>
</html>
[root@localhost conf]#
看到403的小伙伴别慌,这其实是正常的启动了,但因为/home/nginx/html
目录下是空的,所以才会出现403,因为此时/home/nginx/html
对应的是容器中的/usr/share/nginx/html
目录,可以进到容器中查看目录,如果为空,自然访问80端口服务不会有任何东西了。
[root@localhost conf]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3ccd58253756 nginx "/docker-entrypoint.…" 3 minutes ago Up 3 minutes 0.0.0.0:80->80/tcp nginx01
[root@localhost conf]# docker exec -it nginx01 /bin/bash
root@3ccd58253756:/# cd /usr/share/nginx/html
root@3ccd58253756:/usr/share/nginx/html# ls
root@3ccd58253756:/usr/share/nginx/html#
这个时候只需要在本地主机目录的/home/nginx/html
目录下放上一个index.html就行了(使用exit命令退出容器):
[root@localhost html]# pwd
/home/nginx/html
[root@localhost html]# ls
[root@localhost html]# touch index.html
[root@localhost html]# ls
index.html
[root@localhost html]# cat index.html
[root@localhost html]# vi index.html
[root@localhost html]# cat index.html
hello world
[root@localhost html]#
这个时候再去访问80端口:
[root@localhost html]# curl localhost
hello world
[root@localhost html]#
以上就完成了docker对nginx的配置文件的挂载。
如果部署dist前端项目,只需要把dist文件夹里的内容全部拷贝到/home/nginx/html
下,此时访问80端口,默认就是你的项目,不需要重启容器。
重点!!!防止再踩坑:
因挂载卷映射出来的nginx配置文件,千万不要配置监听端口,因为配置了也只是对nginx容器里面生效!!!
切记,不要在/home/nignx/conf
的nginx.conf下进行类似如下的监听端口配置:
server {
listen 9528;
server_name localhost;
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ @router;
index index.html index.htm;
}
location @router {
rewrite ^.*$ /index.html last;
}
}
如果你进行了这样的配置,也重启了容器,但你会发现仍然无法通过服务器ip:端口
的形式访问,无法达到你想要的结果,因为这样的配置只在容器中生效:
[root@localhost conf]# pwd
/home/nginx/conf
[root@localhost conf]# ls
conf.d fastcgi_params koi-utf koi-win mime.types modules nginx.conf scgi_params uwsgi_params win-utf
[root@localhost conf]# cat nginx.conf
server {
listen 9528;
server_name localhost;
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ @router;
index index.html index.htm;
}
location @router {
rewrite ^.*$ /index.html last;
}
}
[root@localhost conf]# curl localhost
hello world
[root@localhost conf]# curl localhost:9528
curl: (7) Failed connect to localhost:9528; 拒绝连接
[root@localhost conf]#
ps:在cat nginx.conf命令
为了方便看配置文件,我把其他的删除,只显示增加的配置内容。
而进入容器是可以正常访问9528端口的:
[root@localhost conf]# docker exec -it nginx01 /bin/bash
root@3ccd58253756:/# curl localhost
hello world
root@3ccd58253756:/# curl localhost:9528
hello world
root@3ccd58253756:/#
如果你仍然需要使用9528或者其他端口访问你所部署的项目的话,可以在运行挂载卷的nginx容器中映射端口时不映射80,而是你所想的端口,例如:
docker run -d -p 9528(或者是其他你想达到的端口):80 -v /home/nginx/conf:/etc/nginx -v /home/nginx/html:/usr/share/nginx/html --name nginx01 nginx