一篇文章带你入门Docker + Nginx.
Posted 小马教你写Bug
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一篇文章带你入门Docker + Nginx.相关的知识,希望对你有一定的参考价值。
👨🏫 Docker And nginx.
简单学习Docker。
先理解一个问题:什么是容器?
我以前简单学Docker
的时候,用Docker部署Redis和mysql的时候,我以为容器仅仅只是一个容器
,在这里我首先要纠正一点就是,不要把容器想成简单的容器,可以把每一个容器想象成一个Linux环境,我认为这一点也是Docker比较牛的地方.
🔖 一、Docker学习。
💡 1.1 什么是Docker.
Docker 是一个开源的应用容器引擎,基于Go实现,遵从Apache2.0协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,这里所说的容器和上面我提到的容器是一个容器,容器是完全使用沙箱机制,可以这么理解就是,一台Linux部署了5个Docker容器,那么这一台电脑可以相当于6台电脑去使用,容器可以比作是Linux中的Linux,这个我也是在学习过程中才理解的.
不幸的是Docker开始收费了,但是幸运的是Docker提供社区版本来供我们学习.
💡 1.2 Docker 有什么优点.
现在,各个大厂都在使用Docker和K8S来进行上云,既然这么多大公司都在用,那么就说明,Docker肯定是有其非常亮眼的优势的,现在我们就来分析一下。
Docker 使您能够将应用程序与基础架构分开,从而可以快速交付软件。借助Docker可以以管理应用程序的方式来管理基础架构。通过利用Docker的方法来快速交付、测试、部署代码。
💡 1.3 Docker 整体将架构.
Docker 是基于C/S 模式的架构的,其通过 DocekrClient 和 与 Docker Daemon 建立通信,使用远程API来管理和创建Docker容器. 其可以通过HTTP API 形式通信也可以通过Socket方式进行通信.
Docker Client 通过发送请求到 Docker Server中,Docker Server 根据路由以及请求来调用Engine中的各个不同的Job来完成本次请求操作,而之后各个Job完成自己的任务向Engine返回即可.
💡 1.4 Docker安装.
因为DEV-CLOUD内部集成了Docker,所以这里省略. 如果想要在自己服务器上安装docker的话,参考上面 Docker 教程
💡 1.5 Docker 中容器和镜像的关系.
Docker中容器和镜像之间的关系就相当于操作系统中的进程和程序的关系。
💡 1.5 Docker命令.
下面是一些基本的Docker命令,一些高级的Docker命令没有列出来.
docker search nginx
搜索某个镜像.docker pull nginx
下载nginx@latest.docker images
: 查看当前Docker中存在的镜像.docker ps
:查看当前未关闭的容器.docker ps -a
:查看所有容器.docekr rm imagname
删除指定容器.docker rmi imgid
删除指定镜像.docker run --name 容器名字 -p 18088:18088 -d 镜像名字
, 关于docker run 的命令参数可以通过命令:docker run --help
去查看该命令参数的使用详解.- 使用
docker run
命令启动容器之后,可以通过docker ps
查看当前正在运行的容器,如果发现当前没有正在运行的容器,那么可以使用docker ps -a
去查看所有的容器,包含已经关闭的容器. docker exec -it 容器名字 bash
:进入该容器内部,每一个容器都相当于是一个Linux环境。docker strop 容器名字|容器ID
可以停掉该容器.docker start 容器名字|容器ID
启动该容器,重新启动这个容器,可能该容器内部的配置文件已经改变.docker restart 容器名字|容器ID
重启该容器.
💡 1.6 DockerFile 非常重要.
参考文档:Dockerfile详解
-
DockerFile的功能:我们可以利用DockerFIle来构建我们自己的Docker镜像,然后可以进行发布等,但是我们要进行构建镜像的话,前提是我们必须要依赖一个基础镜像. 而Dockerfile的第一行不能是注释,只能是
FROM
命令,表示该镜像的基础镜像是什么,docker build
在执行的时候从上乡下执行构建镜像,没执行一行命令就构建一个镜像,整个构建过程就相当于在基础镜像上层层叠加. -
Dockerfile 命令形式.
- Dockerfile 整体就两类语句组成:
#
以#
开头的注释信息.instruction args
指令 + 参数.- Dockerfile 文件名字必须首字母大写.
- Dockerfile 指令不区分大小写,但是为了方便和参数做区分,通常指令使用大写.
- Dockerfile 指令按顺序从上至下依次执行.
- Dockerfile 中第一个非注释行必须是
FROM
指令,用来指定当前镜像依据的是那个基础镜像. - Dockerfile 中需要调用的文件必须跟Dockerfile文件在同一目录下,或者在其子目录下,父目录或者其他路径将会失效.
-
Docker 命令.
- FROM指令:
- FROM 指令用于指定构建镜像所使用的基础镜像,后续的指令运行都要依靠此基础镜像上所提供的环境,也就是Dockerfile中是没有提供mkdir命令的。实际使用中,如果没有指定仓库,会先在本机查看是否有该基础镜像,没有则会去Docker Hub 上拉取.
- 实例:
FROM nginx
MAINTAINER
:- 用于让Dockerfile的作者提供个人信息,Dockerfile 并不限制MAINTAINER指令的位置,但是建议放在FROM指令之后,在比较新的版本中已经被LABEL指令替代.
- 实例:
MAINTAINER "zhenxinma@tencent.com"
COPY
:用于复制宿主机上的文件到镜像上.- Demo :
COPY "src" "dest"
src 表示的是宿主主机上的文件 dest表示的是镜像上文件的位置. - 实例:
COPY "./index.html" "/root/dockermapping/"
这样的话启动镜像之后,在镜像上位置/root/dockermapping/
就有./index.html
文件了. - 规则:
- src 必须是build上下文中的目录,不能是其父目录中的文件.
- 如果src是文件,则内部的文件或者子目录会被递归复制,DNAsrc目录本身不会被复制.
- 如果指定了多个src,或者src中使用了通配符,则dest必须是一个目录,且必须以
/
结尾. - 如果dest不存在,那么会自动创建.
- Demo :
LABEL
:让用户镜像指定各种元数据.- 类似 docker run -l 等,让用户镜像指定各种元数据,键值对的形式.
- 实例:
LABEL K1=V1 K2=V2
ADD
:类COPY
- 作用:ADD和COPY指令类似,不过ADD支持使用
tar
文件和URL
路径文件. 当拷贝的源文件是tar文件时,会自动展开为一个目录并拷贝进新的镜像中,而通过URL获取到的tar文件不会自动展开,宿主机可以联网的情况下,docker build 可以将网络上的某文件引用下载并打包到新的镜像中. - 实例:
ADD "src" "dest"
- 作用:ADD和COPY指令类似,不过ADD支持使用
WORKDIR
:指定工作目录. 不是很清楚.VOLUME
:docker run -v 简化版. 也不是很懂.- 用于在镜像中创建一个挂载的目录, 在dockerfile中支持docker管理的卷,也就是说只能指定容器内部的路径,不能指定宿主机的路径.
- 实例: VOLUME “moutpoint”
EXPOSE
:同docker run --expost- 指定容器中待暴露的端口。比如容器提供的是一个https服务并且需要对外提供访问,那就需要指定暴露443端口,然后在使用此镜像启动容器时通过
-P
的参数才能将待暴露的状态转换为真正暴露的端口. - 实例:
EXPOSE "port/tcp"
- 指定容器中待暴露的端口。比如容器提供的是一个https服务并且需要对外提供访问,那就需要指定暴露443端口,然后在使用此镜像启动容器时通过
ENV
:为镜像定义所需要的环境变量,这里定义的变量应该可以通过go里面的os.GetEnv
获取到.ENV k v
这种只能一次定义一个,ENV k=v k=v 可以定义多个.
- FROM指令:
🔖 二、Nginx.
文档:Nginx中文文档
💡 2.1 什么是Nginx?
Nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器,同时也是一个IMAP、POP3、SMTP代理服务器,Nginx 可以作为一个HTTP服务器进行网站的发布,另外Nginx可以作为反向代理进行负载均衡的实现.
- 正向代理:服务端不知道客户端.
- 反向代理:客户端不知道服务端.
💡 2.2 Nginx 能用来做什么.
- 静态 HTTP 服务器:Nginx 是一个 HTTP 服务器,可以将服务器上的静态文件(HTML、图片)通过HTTP协议展现给客户端.
- 反向代理服务器:反向代理是Nginx做的最多的一件事情,客户端不知道服务端的地址,客户端去请求该Nginx,Nginx通过配置的的路由规则将路由分发到不同的机器上,这个过程客户端是不知道服务端的地址的,只知道Nginx的地址.
- 负载均衡:当网站访问量非常大的时候,可以通过Nginx + 反向代理实现负载均衡 .
💡 2.3 应该怎么用呢?
简单来说就是先配置好路由规则以及域名、监听端口,然后启动Nginx,启动之后,根据HTTP请求去访问Nginx监听的端口,Nginx会根据URI去匹配指定的资源文件.
💡 2.4 Nginx工作原理.
参考文档:Nginx工作原理和优化总结
- Nginx 进程模型:
Nginx采用异步非阻塞的方式来处理网络事件,具体过程:
- 接收请求:每个Worker都是从master进程fork过来的,在master进程建立好需要listen的socket之后,然后带上这个socketfork出多个进程,所有worker进程的socket会在新链接到来时变得可读,每个work进程都可以去accept这个socket,当一个请求到来时,所有可accept的work进程都会感受到通知,为了保证只有一个accept成功,Nginx提供了一个共享锁来保证同一时刻只有一个work进程accept链接。所有worker进程在注册socket读事件前抢accept_mutex,抢到互斥锁的那个进程可以注册socket读事件,在读事件里面调用accept接收该链接.
- 处理请求:当一个worker进程在accept这个链接之后,就开始读取请求、解析请求、处理请求、产生数据之后,再返回给客户端之后才断开链接。
💡 2.5 Nginx 配置详解.
参考博客:最全Nginx 配置文件详解
由于我们这里是用Docker启动的Nginx,所以我们只需要关注Nginx内部配置文件即可,如果不是通过Docker安装的Nginx的话,我们可以通过去Nginx安装目录下面的sbin文件中去启动Nginx.
- Nginx 整体配置.
... #全局块
events { #events块
...
}
http #http块
{
... #http全局块
server #server块
{
... #server全局块
location [PATTERN] #location块
{
...
}
location [PATTERN]
{
...
}
}
server
{
...
}
... #http全局块
}
- 全局快:配置影响Nginx全局的指令,一般有运行Nginx服务器的用户组,Nginx进程PID存放路径、日志存放路基、配置文件引入、允许生成Worker的个数等.
- events快:配置影响Nginx服务器或者与用户的网络连接,有每个进程的最大连接数,选取那种事件驱动模型处理链接请求,是否允许同时接收多个网络连接,开启多个网络序列化等。
- http快:可以嵌套多个server,配置代理、缓存,日志定义等绝大多数功能和第三方模块的配置、链接超时时间、单链接请求数等.
- server快:配置虚拟主机的相关参数,比如域名、编码规则等.
- location块:配置请求路由、以及各种页面处理情况等,比较核心.
- Nginx 详细配置.
#定义Nginx运行的用户和用户组
# 如果出现403可以将user改为root,那是因为本次请求没有权限访问该路径表示的资源.
user root;
#
#nginx进程数,建议设置为等于CPU总核心数.也就是上面的worker的数量.
worker_processes 8;
#
#全局错误日志定义类型,[ debug | info | notice | warn | error | crit ]
error_log /var/log/nginx/error.log info;
#
#进程文件
pid /var/run/nginx.pid;
#
#一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀,所以建议与ulimit -n的值保持一致.
# 不是很懂这个 .
worker_rlimit_nofile 65535;
#
#工作模式与连接数上限
events
{
#参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型.
use epoll;
#单个进程最大连接数(最大连接数=连接数*进程数)
worker_connections 1024; #最大连接数,默认为512
}
#
#设定http服务器
http
{
#charset utf-8; #默认编码
client_header_buffer_size 32k; #上传文件大小限制
keepalive_timeout 65; #连接超时时间,默认为75s,可以在http,server,location块。
# 开启目录列表访问,合适下载服务器,默认关闭.
autoindex on; # 显示目录
autoindex_exact_size on; # 显示文件大小 默认为on,显示出文件的确切大小,单位是bytes 改为off后,显示出文件的大概大小,单位是kB或者MB或者GB
autoindex_localtime on; # 显示文件时间 默认为off,显示的文件时间为GMT时间 改为on后,显示的文件时间为文件的服务器时间
sendfile on; # 开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载.注意:如果图片显示不正常把这个改成off.
tcp_nopush on; # 防止网络阻塞
tcp_nodelay on; # 防止网络阻塞
# 性能优化的模块.
# gzip模块设置
gzip on; #开启gzip压缩输出
gzip_min_length 1k; #允许压缩的页面的最小字节数,页面字节数从header偷得content-length中获取.默认是0,不管页面多大都进行压缩.建议设置成大于1k的字节数,小于1k可能会越压越大
gzip_buffers 4 16k; #表示申请4个单位为16k的内存作为压缩结果流缓存,默认值是申请与原始数据大小相同的内存空间来存储gzip压缩结果
gzip_http_version 1.1; #压缩版本(默认1.1,目前大部分浏览器已经支持gzip解压.前端如果是squid2.5请使用1.0)
gzip_comp_level 2; #压缩等级.1压缩比最小,处理速度快.9压缩比最大,比较消耗cpu资源,处理速度最慢,但是因为压缩比最大,所以包最小,传输速度快
gzip_types text/plain application/x-javascript text/css application/xml;
#压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn.
gzip_vary on;#选项可以让前端的缓存服务器缓存经过gzip压缩的页面.例如:用squid缓存经过nginx压缩的数据
#开启限制IP连接数的时候需要使用
#limit_zone crawler $binary_remote_addr 10m;
##upstream的负载均衡,四种调度算法(下例主讲)##
#虚拟主机的配置
server
{
# 监听端口,如果有请求打到80端口上时,Nginx会处理该请求.
listen 80;
# 域名可以有多个,用空格隔开
server_name 127.0.0.1;
# HTTP 自动跳转 HTTPS
rewrite ^(.*) https://www.baidu.com;
deny 127.0.0.1; #拒绝的ip,表示拒绝该IP进行访问.
allow 172.18.5.54; #允许的ip
# 对需要指定的路径的请求解决跨越问题.
location /指定路径或者正则/{
proxy_pass https://localhost:13580/指定路径或者正则/;
proxy_set_header HOST $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
在了解了Nginx配置的全局概念之后,我么主要分析下Http模块以及该模块的Server模块.
我们看下面的配置文件,我来主要说一说其主要功能,如果按照下面这样的配置的话,那么当有请求:http://mazhenxin.xyz:18088/index.html
首先,Nginx 匹配到mazhenxin.xyz
之后,将会根据后面的URI进行匹配,Nginx发现 /index.html
和 location /index.html
匹配,那么就会走这个映射规则,然后Nginx就会去服务器找 root+/location-uri
这个路径上的资源文件,至于多不多/
,Nginx会自动优化,因为我在root
后面加不加 /
都可以匹配到。 因为我们的服务器上在路径root + location-uri
上存在着对应的文件,Nginx 在获取到改文件之后将会解析,解析之后,返回给客户端.
server {
listen 18088;
listen [::]:18088;
server_name mazhenxin.xyz;
# 下面的index不知道是啥意思.
location /index.html {
root /root/nginxmapping/docker;
# index /index.html;
}
#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;
}
}
💡 2.6 upstream 负载均衡详解.
-
负载均衡算法:
- 轮询、权重、IP散列是Nginx原生支持的分配方式
- fail和url_hash为第三方支持的分配方式.
-
使用.
-
轮询是upstream默认的分配方式.
-
权重:在server后面直接配置:
server 127.0.0.1:8848 weight=1
, 权重的意思就是指定轮询比例,weight和访问成正比率关系,也就是权重越大,访问的次数也越高 . -
ip_hash 方式:每个请求按照访问ip(Nginx的前置服务器或者客户端IP),这样每个用户会固定访问一个后端服务器,可以解决session一致问题. 具体使用如下.
upstream test { ip_hash: server.... }
-
🔖 三、动手实战.
实战:Docker安装Nginx实现静态文件映射.
我这里的动手实战的架构图是这样的:
💡 3.1 安装、启动、进入容器内部.
- 安装.
// 下面将会拉取Nginx的镜像.
docker pull nginx
- 查看是否拉取成功.
# 查看所有镜像.
docker images
- 启动镜像.
# -p Docker虚拟Linux端口映射到母机端口上.
# -d 该Docker容器在后台启动.
# nginx 是启动该容器时需要的镜像.
docker run --name 容器名字 -p 18088:18088 -d nginx
- 然后查看当前是否有容器正在运行Nginx镜像.
// docker ps 查看当前正在运行的容器.
// docker ps -a 查看当前所有的容器.
docker ps || docker ps -a
下面是我的运行成功截图.
-
既然成功运行了,那么我们首先要做的就是去修改Nginx的配置文件,因为Nginx的主要功能都是通过Nginx.conf配置文件来解决的. 那么怎么去修改配置文件呢? 有两种方式,第一种就是通过启动nginx镜像的时候修改它默认去加载的配置文件,这个可以通过命令
find / -name "default.conf"
去修改配置文件中的内容. 不过这种方式我不建议用,这里我建议直接去容器内部修改Nginx的配置文件,最后只需要去重启一下即可,这种方式和正常思维是一样的. 下面就来看看如何修改容器内部Nginx的配置文件.-
首先进入容器内部.
docker exec -it 容器名字 bash
-
Nginx 默认的配置文件在容器内部
/etc/nginx/
下面的。 -
但是容器内部不支持
VIM
命令格式,所以我们需要使用下面的命令去增加VIM插件来使用VIM编辑器apt-get update apt-get install vim
-
💡 3.2 修改配置文件.
-
首先我们先修改Nginx的配置文件,因为Nginx容器的启动和安装VIM插件已经学习的差不多了,这里就直接开始修改配置文件.
申明一点:默认的以Docker方式启动的Ngixn容器,其配置文件Nginx.conf是通过include来指向Server模块的配置文件,其在conf.d目录下. 这里我就以Nginx.conf为配置文件来举例说明.
// 1. 首先先进入Nginx容器. docker exec -it nginx bash // 进入/etc/nginx/下修改文件. cd /etc/nginx // 2. 修改Nginx的配置文件如下: vim nginx.conf ------------Nginx.conf-------------- user root; # 这里需要改成root,否则会出现403错误. worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; // 默认负载均衡是轮询方式. upstream proxy_html { server mazhenxin.xyz:8088 max_fails=3 fail_timeout=10s; server mazhenxin.xyz:8089 max_fails=3 fail_timeout=10s; } #gzip on; server { listen 18088; # 通过域名访问. server_name mazhenxin.xyz; #charset koi8-r; #access_log logs/host.access.log main; # 只要URI是index.html的请求,则全部转到proxy_html上. # 转发之后的路径是 http://proxy_html/index.html location /index.html { proxy_pass http://proxy_html; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } ------------Nginx.conf--------------
其实这样Nginx网关类似就做好了,然后只需要重新启动以下这个Nginx容器.
# 首先退出当前容器. exit. # 重启容器。 docker restart 容器名称|容器ID.
现在最上面的Nginx网关层已经做好了,现在来配置HTML服务.
-
修改Server HTML-8088
这里我是通过以Nginx为基础镜像外加上一个HTML文件的形式打包成一个镜像,名字称为Linux。具体的Dockerfile如下.
FROM nginx MAINTAINER "zhenxinma@tencent.com" COPY "./index.html" "/root/dockermapping/"
然后我们可以使用
docker build .
来通过当前目录下的Dockerfile来构造一个基于FROM
镜像的镜像.然后就和上面步骤一样了,启动就OK了.
还是同样的道理,先启动容器、进入容器、安装
vim
插件,修改配置. 上面已经有过步骤,这里就不在赘述.-
首先先检查下
/root/dockermapping/
目录下是否有index.html
要进行映射的文件. 因为最终请求是要来获取该资源的,所以说这里的资源一定要有. -
然后就是修改配置文件.
修改如下.
# 这里是通过包含配置文件的形式来启动配置文件的. # 先来看下nginx.conf配置文件. # 这里的user一定要改为root,否则请求可能没有权限去访问目标资源. user root; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; # 这里说明当前配置文件包含了哪些配置文件. include /etc/nginx/conf.d/*.conf; } # 全局配置文件。 server { listen 8088; listen [::]:8088; server_name mazhenxin.xyz; location /index.html { root /root/dockermapping; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
当有请求从18088Nginx服务过来之后,其会根据URI来取访问目标资源.
-
-
修改Server HTML-8089 配置文件.
同理,依然利用Nginx为基础镜像,将HTML打包成一个镜像. 具体和上面类似,这里就不在啰嗦了直接上修改好的配置文件即可.
- 我们先看下
index.html
文件是否存在.
# 下面的命令是在进入容器之后执行的. root@a217d3dd7e75:/# cd /root/dockermapping/ root@a217d3dd7e75:~/dockermapping# ls index.html root@a217d3dd7e75:~/dockermapping# cat index.html <b> Hello Wrod Docker Nginx Mapping to Html.-8089 </b> root@a217d3dd7e75:~/dockermapping#
通过上面可以看到,
index.html
是存在的,且页面内容是8089的.- 修改Nginx的配置文件.
root@a217d3dd7e75:/etc/nginx/conf.d# cat default.conf server { listen 8089; listen [::]:8089; # 配置以域名的形式访问. server_name mazhenxin.xyz; location / { root /root/dockermapping; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
重新启动加载配置文件,这里一定要将修改过配置文件的容器重新启动。
- 我们先看下
来看最后结果:
第一次:
第二次:
OK,大功告成!
欢迎关注下我的公众号(同掘金):小马教你写Bug 一起学习 互相成长!
以上是关于一篇文章带你入门Docker + Nginx.的主要内容,如果未能解决你的问题,请参考以下文章