简单学习Docker。
先理解一个问题:什么是容器?
我以前简单学Docker
的时候,用Docker部署Redis和MySQL的时候,我以为容器仅仅只是一个容器
,在这里我首先要纠正一点就是,不要把容器想成简单的容器,可以把每一个容器想象成一个Linux环境,我认为这一点也是Docker比较牛的地方.
参考:Docker 教程 | 菜鸟教程 (runoob.com)
Docker 是一个开源的应用容器引擎,基于Go实现,遵从Apache2.0协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,这里所说的容器和上面我提到的容器是一个容器,容器是完全使用沙箱机制,可以这么理解就是,一台Linux部署了5个Docker容器,那么这一台电脑可以相当于6台电脑去使用,容器可以比作是Linux中的Linux,这个我也是在学习过程中才理解的.
不幸的是Docker开始收费了,但是幸运的是Docker提供社区版本来供我们学习.
现在,各个大厂都在使用Docker和K8S来进行上云,既然这么多大公司都在用,那么就说明,Docker肯定是有其非常亮眼的优势的,现在我们就来分析一下。
Docker 使您能够将应用程序与基础架构分开,从而可以快速交付软件。借助Docker可以以管理应用程序的方式来管理基础架构。通过利用Docker的方法来快速交付、测试、部署代码。
参考:[Docker] Docker整体架构图 - dhcn - 博客园 (cnblogs.com)
Docker 是基于C/S 模式的架构的,其通过 DocekrClient 和 与 Docker Daemon 建立通信,使用远程API来管理和创建Docker容器. 其可以通过HTTP API 形式通信也可以通过Socket方式进行通信.
Docker Client 通过发送请求到 Docker Server中,Docker Server 根据路由以及请求来调用Engine中的各个不同的Job来完成本次请求操作,而之后各个Job完成自己的任务向Engine返回即可.
因为DEV-CLOUD内部集成了Docker,所以这里省略. 如果想要在自己服务器上安装docker的话,参考上面 Docker 教程
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
重启该容器.参考文档:Dockerfile详解
DockerFile的功能:我们可以利用DockerFIle来构建我们自己的Docker镜像,然后可以进行发布等,但是我们要进行构建镜像的话,前提是我们必须要依赖一个基础镜像. 而Dockerfile的第一行不能是注释,只能是FROM
命令,表示该镜像的基础镜像是什么,docker build
在执行的时候从上乡下执行构建镜像,没执行一行命令就构建一个镜像,整个构建过程就相当于在基础镜像上层层叠加.
Dockerfile 命令形式.
#
以 #
开头的注释信息.instruction args
指令 + 参数.FROM
指令,用来指定当前镜像依据的是那个基础镜像.Docker 命令.
FROM nginx
MAINTAINER
:
MAINTAINER "[email protected]"
COPY
:用于复制宿主机上的文件到镜像上.
COPY "src" "dest"
src 表示的是宿主主机上的文件 dest表示的是镜像上文件的位置.COPY "./index.html" "/root/dockermapping/"
这样的话启动镜像之后,在镜像上位置/root/dockermapping/
就有 ./index.html
文件了./
结尾.LABEL
:让用户镜像指定各种元数据.
LABEL K1=V1 K2=V2
ADD
:类 COPY
tar
文件和URL
路径文件. 当拷贝的源文件是tar文件时,会自动展开为一个目录并拷贝进新的镜像中,而通过URL获取到的tar文件不会自动展开,宿主机可以联网的情况下,docker build 可以将网络上的某文件引用下载并打包到新的镜像中.ADD "src" "dest"
WORKDIR
:指定工作目录. 不是很清楚.VOLUME
:docker run -v 简化版. 也不是很懂.
EXPOSE
:同docker run --expost
-P
的参数才能将待暴露的状态转换为真正暴露的端口.EXPOSE "port/tcp"
ENV
:为镜像定义所需要的环境变量,这里定义的变量应该可以通过go里面的os.GetEnv
获取到.
ENV k v
这种只能一次定义一个,ENV k=v k=v 可以定义多个.文档:Nginx中文文档
Nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器,同时也是一个IMAP、POP3、SMTP代理服务器,Nginx 可以作为一个HTTP服务器进行网站的发布,另外Nginx可以作为反向代理进行负载均衡的实现.
简单来说就是先配置好路由规则以及域名、监听端口,然后启动Nginx,启动之后,根据HTTP请求去访问Nginx监听的端口,Nginx会根据URI去匹配指定的资源文件.
参考文档:Nginx工作原理和优化总结
Nginx采用异步非阻塞的方式来处理网络事件,具体过程:
参考博客:最全Nginx 配置文件详解
由于我们这里是用Docker启动的Nginx,所以我们只需要关注Nginx内部配置文件即可,如果不是通过Docker安装的Nginx的话,我们可以通过去Nginx安装目录下面的sbin文件中去启动Nginx.
... #全局块
events { #events块
...
}
http #http块
{
... #http全局块
server #server块
{
... #server全局块
location [PATTERN] #location块
{
...
}
location [PATTERN]
{
...
}
}
server
{
...
}
... #http全局块
}
#定义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;
}
}
负载均衡算法:
使用.
轮询是upstream默认的分配方式.
权重:在server后面直接配置: server 127.0.0.1:8848 weight=1
, 权重的意思就是指定轮询比例,weight和访问成正比率关系,也就是权重越大,访问的次数也越高 .
ip_hash 方式:每个请求按照访问ip(Nginx的前置服务器或者客户端IP),这样每个用户会固定访问一个后端服务器,可以解决session一致问题. 具体使用如下.
upstream test {
ip_hash:
server....
}
实战:Docker安装Nginx实现静态文件映射.
我这里的动手实战的架构图是这样的:
// 下面将会拉取Nginx的镜像.
docker pull nginx
# 查看所有镜像.
docker images
# -p Docker虚拟Linux端口映射到母机端口上.
# -d 该Docker容器在后台启动.
# nginx 是启动该容器时需要的镜像.
docker run --name 容器名字 -p 18088:18088 -d nginx
// docker ps 查看当前正在运行的容器.
// docker ps -a 查看当前所有的容器.
docker ps || docker ps -a
下面是我的运行成功截图.
既然成功运行了,那么我们首先要做的就是去修改Nginx的配置文件,因为Nginx的主要功能都是通过Nginx.conf配置文件来解决的. 那么怎么去修改配置文件呢? 有两种方式,第一种就是通过启动nginx镜像的时候修改它默认去加载的配置文件,这个可以通过命令find / -name "default.conf"
去修改配置文件中的内容. 不过这种方式我不建议用,这里我建议直接去容器内部修改Nginx的配置文件,最后只需要去重启一下即可,这种方式和正常思维是一样的. 下面就来看看如何修改容器内部Nginx的配置文件.
首先我们先修改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 "[email protected]"
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
Hello Wrod Docker Nginx Mapping to Html.-8089
root@a217d3dd7e75:~/dockermapping#
通过上面可以看到,index.html
是存在的,且页面内容是8089的.
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,大功告成!
欢迎关注下我的公众号(同掘金):考拉小同学