一、文本目的
- 前面学完了Docker的一些常用语法,现在来开始将Docker用于构建和测试
- 将Docker作为本地Web开发环境是Docker的一个最简单的应用场景。这样的环境可以完全复制生产环境,并确保用户开发的东西在生产环境中也能运行。下面从将Nginx Web服务器安装到容器来架构一个简单的网站开始,这个网站的名字暂时命名为sample
本文的代码、文件链接
二、创建相关文件
- 首先在根目录下创建一个sample目录,用来存放本次镜像构建的上下文
mkdir ~/sample
创建Dockerfile文件
- 进入sample目录,在该目录内建立一个Dockerfile文件,用于后面构建镜像
cd ~/sample
touch Dockerfile
- Dockerfile文件的内容如下:
- FROM指令:表示要构建的镜像蓝本,此处以ubuntu:16.04为例开始构建新的镜像
- MAINTAINER指令:指定该镜像的作者与邮箱
- ENV指令:为了重新构建缓存,详情请参阅前文的“基于构建缓存的Dockerfile模板”专题:https://blog.csdn.net/qq_41453285/article/details/107389320
- 第1条RUN指令:更新软件包,并安装Nginx
- 第2条RUN指令:创建一个目录,作为Nginx服务器的根目录
- 第1条ADD指令:将宿主机的当前路径下的nginx/global.conf文件(这个文件我们在下面创建,见下面)复制到镜像的/etc/nginx/conf.d/目录
- 第2条ADD指令:将宿主机的当前路径下的nginx.conf文件复制到镜像的/etc/nginx/nginx.d/目录下,名为nginx.conf
- EXPOSE指令:公开镜像的80端口
FROM ubuntu:16.04
MAINTAINER dongyusheng "1286550014@qq.com"
ENV PEFRESHED_AT 2020-07-17
RUN apt-get -yqq update && apt-get -yqq install nginx
RUN mkdir -p /var/www/html/website
# 一个指定目录, 一个指定存放目录和文件名, 没什么区别
ADD nginx/global.conf /etc/nginx/conf.d/
ADD nginx/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
创建Nginx配置文件
- 在sample目录内创建一个nginx目录,然后在nginx目录内创建两个配置文件,分别名为global.conf和nginx.conf
mkdir nginx
touch nginx/global.conf
touch nginx/nginx.conf
ls nginx
server {
# nginx监听在80端口上
listen 0.0.0.0:80;
server_name _;
# nginx网站的根路径
root /var/www/html/website;
index index.html index.htm;
access_log /var/log/nginx/deafulr_access.log;
error_log /var/log/nginx/deafulr_error.log;
}
user www-data;
worker_processes 4;
pid /run/nginx.pid;
daemon off;
events { }
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_disable "msie6";
include /etc/nginx/conf.d/*.conf;
}
- 为什么此处要让nginx在容器中以非守护进程模式运行,原因如下:
- 默认的情况下,Nginx会以守护进程的方式启动
- 在下面“docker run”运行容器的时候,我们让容器去运行Nginx,如果Nginx是以守护进程运行的,那么当容器将Nginx以守护进程运行之后,容器就认为自己的任务完成了,于是就关闭自己这个进程,此时容器就停止运行了,Nginx也跟着关闭
- 因此,我们将Nginx运行在容器的前台,这使得容器一直保持有任务可执行的状态,从而容器不会关闭
创建网页文件
- 在sample目录下创建一个website目录用来存放网页,网页名为index.html
mkdir website
touch website/index.html
ls website
<head>
<title>Test website</title>
</head>
<body>
<h1>This is a test website</h1>
</body>
三、构建镜像
- 上面的Dockerfile创建好之后,在sample目录内,输入“docker build”来构建镜像了。命令如下:
# 构建镜像, 镜像名为dongshao/nginx
sudo docker build -t dongshao/nginx .
- 创建完成之后,可以使用下面的命令查看该镜像的构建历史。命令如下:
sudo docker history dongshao/nginx
sudo docker images dongshao/nginx
四、创建/运行容器
- 上面的镜像构建好之后,可以利用该镜像来创建一个容器。命令如下:
- -d:让容器不在前台运行,在宿主机中以守护进程的模式运行
- -p:将容器的80端口映射到宿主机的任意端口上
- --name:将容器的名字取名为website
- -v:将宿主机的目录作为卷,挂在到容器中。此处我们把宿主机的“$PWD/website”目录挂在到容器的“/var/www/html/website”目录,在上面的global.conf配置文件中,我们将“/var/www/html/website”作为Nginx网页的根目录(关于“卷”,详情见下面介绍)
- dongshao/nginx:指定镜像,为我们上面创建的镜像
- nginx:这是一条命令,让容器执行该命令来运行Nginx,Nginx会默认以上面的nginx.conf为配置文件启动
sudo docker run -d -p 80 --name website -v $PWD/website:/var/www/html/website dongshao/nginx nginx
- 现在我们可以通过下面的命令查看运行的容器,命令如下,可以看到容器的80端口映射到宿主机的32769端口上:
sudo docker ps -l
运行的原理
- 在上面docker run运行容器时,我们把宿主机的"$PWD/website"目录挂在到容器的“/var/www/html/website”目录
- 在镜像中,Nginx的global.conf配置文件中使用“/var/www/html/website”作为Nginx网页的根目录
- 当我们通过网页去访问容器中的Nginx时,其会去访问容器中/var/www/html/website目录下的index.html文件,而我们把宿主机的"$PWD/website"目录挂在到容器的“/var/www/html/website”目录,所以访问的就是宿主机"$PWD/website"目录下的index.html文件
- 所以结果最终如下所示
五、附加知识:卷
- 卷在Docker里非常重要,也很有用
- 卷是在一个或多个容器内被选定的目录,可以绕过分层的联合文件系统(Union File System),为Docker提供持久数据或者共享数据提供几个有用的特性:
- 卷可以在容器间共享和重用
- 共享卷时不一定要运行相应的容器
- 对卷的修改会直接在卷上反映出来
- 更新镜像时不会包含对卷的修改
- 这意味着对卷的修改会直接生效,并绕过镜像。当提交或者创建镜像时,卷不会被包含在镜像里
- 利用卷,可以在不用提交镜像修改的情况下,向镜像里加入数据(如源代码、数据或者其他内容),并且可以在容器间共享这些数据
查看卷
- 卷在Docker宿主机的/var/lib/docker/volumes目录下,每个容器的卷目录都会对应一个目录
sudo ls -l /var/lib/docker/volumes
- 还可以通过docker inspect命令查看某个容器使用的卷的目录。例如,下面的sample_app有一个卷,我们查看其卷所在的位置
sudo docker inspect -f "{{ range.Mounts }} {{ . }} {{ end }}" sample_app
- 可以看到返回的目录名为“0f15606bad9162f64e392bb20b3302f2c2ad890617777e2d9ae2ba9aa0c807bd”,查看该目录会有一个_data目录,该目录下就有该容器卷中存在的文件的内容
sudo ls -l /var/lib/docker/volumes/0f15606bad9162f64e392bb20b3302f2c2ad890617777e2d9ae2ba9aa0c807bd
提示
提示
- 卷可以在容器间共享
- 即便容器被删除了,卷的内容依旧在宿主机汇总存在,因为一般卷的内容都存放在宿主机中
- 在上面的例子中,我们因为某些原因不想把应用或者代码构建到镜像中,就可以利用卷。例如:
- 希望同时对代码做开发和测试
- 代码改动很频繁,不想在开发过程中重构镜像
- 希望在多个容器间共享代码
"docker run"的-v选项
- 上面演示案例用到了"-v"选项
- 通过指定一个目录或者登上与容器上与该目录分离的本地宿主机来工作
- 两个目录用“:”隔离,前面的目录表示宿主机的目录,后面的目录为容器的目录。如果容器目录不存在,Docker会自动创建一个
rw、ro标志
- 如果想要指定容器内目录的读写状态,可以用这两个标志来指示。例如:
# 我们指定了ro, 此时容器的/var/www/html/website目录变为只读状态
sudo docker run -d -p 80 --name website -v $PWD/website:/var/www/html/website:ro dongshao/nginx nginx
"docker run"的--volumes-from选项
容器卷的备份
Dockerfile的VOLUME指令
六、修改测试
- 有了“卷”的好处之后,我们现在可以在不修改镜像的情况下,修改宿主机中的网页文件来改变网页的内容
- 例如,下面将smaple/wensite/目录下的index.html文件修改为下面的内容:
vim website/index.html
总结
- 现在sample网页已经更新了,可以看到这个操作是非简单,也十分符合生产环境里的真实状态
- 你可以模仿本文做法,给每个用于生产的网页服务环境(如Apache、Nginx)配置一个容器,给不同开发框架的运行环境(如PHP或者Ruby on Rails)配置一个容器,或者给后端数据库配置一个容器,等等。
- 我是小董,V公众点击"笔记白嫖"解锁更多【Docker】资料内容。