前言
此文档是在学习Docker的时候写的,主要目的是为了巩固学习的知识,文档中部分内容来着于技术胖的跟胖哥一起学习Docker的博客,如有侵权,请联系我:fengsh_h@aliyun.com
无论你是开发、测试或是运维,都需要学习Docker技术。因为截至2020年Docker在生产环境的使用已经超过50%,并且还在迅速发展。
安装
如果你是Windows或Mac 设备自己到官网下载对应的软件安装就可以了
那如果你是Linux系统安装其实也很简单,首先使用curl下载安装脚本
curl -fsSL get.docker.com -o get-docker.sh
下载后查看get-docker.sh是否在当前目录下,然后直接执行这个脚本就好了,需要注意的是这里你如果不是root用户是需要使用sudo命令或给用户sudo权限来执行脚本的。也就是sudo sh get-docker.sh执行完等几分钟就好,这个时间长短取决于设备性能。
安装成功后可以使用docker version进行验证,如果同时出现Client和Server就成功了
Client: Docker Engine - Community
Version: 20.10.7
API version: 1.41
Go version: go1.13.15
Git commit: f0df350
Built: Wed Jun 2 11:58:10 2021
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 20.10.7
API version: 1.41 (minimum version 1.12)
Go version: go1.13.15
Git commit: b0f5bc3
Built: Wed Jun 2 11:56:35 2021
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.4.8
GitCommit: 7eba5930496d9bbe375fdf71603e610ad737d2b2
runc:
Version: 1.0.0
GitCommit: v1.0.0-0-g84113ee
docker-init:
Version: 0.19.0
GitCommit: de40ad0
那如果只有Client
Client: Docker Engine - Community
Version: 20.10.7
API version: 1.41
Go version: go1.13.15
Git commit: f0df350
Built: Wed Jun 2 11:58:10 2021
OS/Arch: linux/amd64
Context: default
Experimental: true
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
可以使用sudo systemctl start docker命令来开启Docker服务端
然后你再执行docker version就同时有客户端和服务端了
Docker容器相关操作
创建容器
// 语法:
docker container run < image name >
// 未指定端口
docker container run nginx
// 指定端口 :前面的端口是当前设备的端口,后面的端口是Docker的端口
// 指定端口也被叫做端口映射
docker container run -p 80:80 nginx
// 后台运行 --detached
docker container run -d -p 90:80 nginx
查看容器命令
// 可以用 但不推荐
docker container ps
// 或者是
docker container ls
// 或者是 查看所有容器 包含运行的和停止运行的
docker container ps -a
// 以上命令中的container可以省略不用写,但是docker container ls是较新的命令,
// 暂时还不支持省略container的写法
执行命令后会列出对应信息
停止容器相关的命令
如果你想停止掉一个正在运行的容器,可以使用下面的命令
// 语法:docker container stop <name or ID>
// 需要注意的是使用ID时是可以使用简写的,比如ID是46875653dec2,你可以使用命令
docker container stop 46
删除容器
当我们停止容器之后,容器并没有删除,而只是停止掉了。这时候你可以使用下面的命令删除容器。
// 语法:
docker container rm <name or ID>
相关命令简写方法
容器的创建:docker container run nginx 简写方法 docker run nginx
容器的列出(up): docker container ls 简写方法 docker ps
容器的列出(up和exit):docker container ls -a 简写方法 docker ps -a
容器的停止 : docker container stop 简写方法 docker stop
容器的删除:docker container rm 简写方法 docker rm
停止多个容器
如果现在你有三个容器需要停掉,笨的方法是直接加上ID或名字。
// 语法:
docker container stop <ID1 ID2 ID3 ...>
但如果你有很多个容器需要停掉,不可能是一个个输入ID或name停掉容器的,那么解决办法是什么呢?
可以使用命令查看所有容器的ID,包含没有开启的
docker container ps -aq
然后可以通过命令组合来停掉所有容器
docker container stop $(docker container ps -aq)
删除多个容器
跟停止多个容器一样
// 语法:
docker container rm <ID1 ID2 ID3 ...>
// 删除容器的命令和停止容器的命令
docker container rm $(docker container ps -aq)
强制删除容器
正在运行的容器,是不可以直接删除的,会报错
Error response from daemon: You cannot remove a running container 21d0ec08e126efe73482264a588a3169c9d5b2253e7d53657ab8ddcf0f8302ba. Stop the container before attempting removal or force remove
报错信息说明不能直接删除没有stop的容器。但这个时候就是要删除,你可以使用强制删除命令进行删除。
// 语法:
docker container rm <ID or Image Name> -f
attached 和detached模式
我们在平时使用的时候大部分都是在使用后台运行模式,那么在后台运行模式下如何切换到前台模式呢?后台模式怎么查看日志信息呢?
Docker的端口映射和两种运行模式-attached和detached模式。先来看如何把一个容器的端口映射到主机的80端口上。在开启端口映射之前,你首先要之道Docker对应的容器端口是多少。比如Nginx镜像的端口诗80。知道这个端口后,就可以在启动容器的时候,用-p <port:port>的形式,启用映射了。
使用Nginx举例
docker container run -p 80:80 nginx
第一个端口是映射到服务器本机的端口;第二个端口是Docker容器使用的端口。 比如你想把Docker的80端口,映射到服务器的90端口。
docker container run -p 90:80 nginx
等待项目启动后,打开浏览器窗口,在地址栏输入127.0.0.1,就可以打开nginx的默认网址。
这个时候你会看到你的控制台会打印一些日志,每访问一次都会打印一次日志
attached 模式
两种模式最简单的对比理解就是:attached模式在前台运行,detached模式在后台运行。
上面讲到每访问一次都会出现一条新的日志信息,也就是说Docker容器的日志会实时的展现到窗口并且占用此端口。这种模式叫做attached模式。
在windows系统下并不是一个完整的attached模式,只是帮我们打印出了Log。现在到Linux服务器和Mac OS上,这时候你按Ctrl+C,就会停止掉Docker服务。而现实中我们工作的环境恰恰是这种Linux环境。
由于在Linux系统中的操作命令会传递给Docker容器,就如上面说到的Ctrl+C会让容器的运行停掉,这样可能会带来一些隐患,如你某天忽然忘记了当前是生产环境了,按下了Ctrl+C那么恭喜你了。停掉了生产环境的容器,于是我们需要一个更好,更稳定的模式,也就是detached模式。attached模式更适用于容器和程序的调试阶段。
detached模式
detached模式的开启方法,就是加一个参数-d或者--detach。
docker run -d -p 80:80 nginx
这次你会看到,和attached模式不同的是,这次输入完命令后,只显示出了容器的编号,并且可以再输入任何命令。就算我们关掉窗口,容器依然运行,也就是他是在系统后台进行运行的。
这种就比较适合在生产环境中运行,停掉和删除容器都需要使用Shell脚本的形式。减少了很多误操作。
detached模式转成attached模式
在运行之后,也有需要调试的时候,Docker提供了两个模式间的转换。比如现在要把detached模式的容器改成attached模式。
docker attach <ID or Image Name>
总结:
1.如何映射端口,让Docker可以被访问到
2.attached模式和detached的使用和优缺点介绍
3.如用把detached模式转换为attached模式
detached模式下查看日志
容器被运行起来了,是detached模式,也就是Docker 的后台运行模式。这时候想要查看后台日志,可以使用下面的命令查看。
docker container logs <ID or Image name>
虽然日志在窗口中出现了,但只打印一次logs,如果想动态一直跟踪日志,可以在命令上加入一个-f。
docker container logs -f <ID or Image name>
可以使用Ctrl+C退出跟踪日志
Docker的交互式模式
有时候容器的镜像不是简单的一个服务,而是需要交互的操作系统。例如创建一个Ubuntu系统,然后需要到系统里输入各种Shell命令和系统进行交互。这时候attached模式和detached模式就不能满足要求了。需要使用交互模式。
使用ubuntu镜像并开启交互模式
docker container run -it ubuntu sh
-it 表示交互模式 sh 表示使用shell脚本的交互,进入到交互模式后就可以是使用Linux的命令(shell)进行操作了,如:ls,cd ..等
退出交互模式
# 删除所有容器
docker container rm -f $(docker container ls -aq)
docker container ps -aq表示查询所有容器
如果我们想退出交互模式,但是又不想停止容器,可以先开启 detached模式(后台模式),然后再进入交互模式
docker container run -d -p 80:80 nginx
-d表示使用detached模式 -p表示映射端口
可以通过下面语法进入交互模式
docker exec -it <ID or Image name> sh
exec 表示执行的意思 -it 表示进入交互模式 sh 表示使用shell命令与镜像进行交互
docker exec -it <ID or Image name> sh 表示使用shell脚本的方式执行交互模式
优点是当使用exit 退出后,服务并不会停止,而只是退出了交互模式。这个使用可以使用docker container ls -a 查看容器的status
总结:
两种方式进入交互模式
1.使用
docker container run -it ubuntu sh,直接进入镜像交互模式,exit时直接退出,使用docker container ls -a查看状态,已停止运行2.使用
docker container -d -p 80:80 nginx,先进入detached模式,再使用docker container exec -it <ID or Image name>进入交互模式,exit 退出后,镜像依然再后台执行
获取镜像的3中方式
- 从registry获取,registry是docker官方的镜像插件平台,需要注意的是尽量使用官方认证的镜像,不要乱使用一个没有被官方认证的镜像,以免带来一些经济损失;
- 从Dockerfile构建一个镜像,相当于自己制作一个Image,这个过程稍微麻烦一点,感兴趣的自己查阅这块的知识
- 从本地导入镜像,在一些开发环境比较苛刻的情况下(无网络),可以使用本地已有的镜像
拉取registry中的镜像
我们可以到[hub.docker.com](https://hub.docker.com/)里面找到自己需要的镜像,如nginx镜像,先search然后选择官方认证的镜像,点击进入镜像详情,在右边有一个拉取命令,如docker pull nginx
docker pull nginx 表示拉取nginx镜像,它是docker image pull nginx 的简写
当然一个镜像有很多版本,我们可以拉取一个比较稳定的版本,点击上图中docker pull nginx 下面的View Available Tags选择一个需要的版本进行拉取就好了,如 docker pull nginx:stable-alpine-perl
镜像常用操作命令
# 查看镜像列表
docker image ls
# 查看镜像具体信息
docker image inspect <Image ID>
# 删除镜像
docker image rm <Image ID>
# 导出镜像
docker image save <Image name> -o <export name>
# 删除本机已有镜像
docker image rm busybox
# 导入镜像
docker image load -i <本机镜像文件路径>
-o 表示输出 -i 表示输入 export name 表示导出后的镜像名
注意:如果有容器在使用镜像,是没有办法被删除的,即使容器被停止掉也没有办法删除
初识Dockerfile
上面已经介绍了两种获取镜像的方式,第一种是从registry中获取,第二种是自己导入导出的
而第三种就是通过Dockerfile来制作一个镜像,在使用Dockerfile之前,我们先了解一下什么是Dockerfile
Dockerfile 是一个包含用于组合映像的命令的文档,可以使用在命令行中调用任何命令。docker通过读取Dockerfile中的指令自动生成映像。
总结:
Dockerfile是用于构建docker镜像的文件
Dockerfile里包含了构建镜像所需的“指令”
Dockerfile有其特定的语法规则(非常重要,需要学习)
有这样一个需求,制作一个镜像。镜像的操作系统是Ubuntu最新版,然后在系统上运行test.py程序。Python程序的内容非常简单,只要打印出Hello World,就可以了。
- 第一步:安装Ubuntu系统
FROM ubuntu:lastest
- 第二步:安装Python环境
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y python3.9 python3-pip python3.9-dev
- 第三步:准备一个test.py文件,文件的内容为
print(”Hello World”),记住你的文件目录(如:D:/test.py)后面要用 - 第四步:运行test.py
python3 test.py
控制台打印:Hello World
这是我们拆解后的步骤,有步骤之后,我们看看如何写一个Dockerfile文件(建议把Dockerfile文件和test.py文件放在一起个文件夹下)
FROM ubuntu:latest
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y python3.9 python3-pip python3.9-dev
ADD test.py /
CMD ["python3","test.py"]
/ 是相对于Dockerfile文件的test.py路径
这算是最简单的一个Dockerfile文件的编写,有了这个文件之后,下节就可以动过Dockerfile来构建一个镜像了。这节你只要对镜像的构建有所了解就可以了。
通过Dockerfile构建镜像
上面我们学习到了怎么去写一个简单的Dockerfile,那么写好Dockerfile后我们需要去构建出一个镜像
首先我们要到控制台,并进入到Dockerfile所在的文件夹
docker image build -t <Name:tag> <file path>
比如我们上面已经写好了Dockerfile,我们要去构建一个test的镜像,就可以使用下面的命名去构建
docker image build -t test .
. 是指当前所在目录
命令执行后,可以使用docker image ls查看当前目录下会有一个test的镜像文件,那如果没有test这个镜像,那么就是你的构建命令执行失败了,请仔细检查你的命令或目录是否正确
把镜像分享到Dockerhub
我们上面学习到可以使用Dockerfile构建出一个镜像,那么我们构建出来的镜像想让更多的人去使用,就可以把镜像分享到Dockerhub上
首先我们需要在hub.docker.com上申请一个账号,然后点击打开profile,这时是没有任何镜像的
如果你想上传属于自己的镜像,需要遵守社区规则,就是用户ID/镜像名称 。最简单的方式是重新build一个镜像,镜像的名称符合社区的命名规则就可以了
docker image build -t 你的用户ID/test .
也可以通过修改镜像名的方式解决命名规则的问题,我们可以使用docker image tag命令进行操作,具体语法如下
docker image tag <old image name > <new iamge name>
那么我们可以使用下面的命令达到对应的目的
docker image tag test 你的用户ID/test
接下来的工作就是把我们制作的镜像推到hub.docker.com自己的账号下
推送之前我们需要先登录我们hub.docker.com账号
docker login
命令执行后,会要求你输入username, 也就是你的用户ID,回车后还需要输入密码,如果密码输入正确,会出现login succeeded,这就证明你登录成功了
这个时候就可以使用语法:docker image push Name[:Tag]来推送我们的镜像了
比如我们刚才的镜像,就可以使用下面的命令进行推送
docker image push 用户ID/test
命令执行完成后就可以在Dockerhub上查看你上传的镜像了!
Dockerfile FROM 三大原则
在写Dockerfile时,我们一般情况下第一句都是选择一个基础 镜像,而选择基础镜像需要注意以下三个原则:
- 官方镜像优于非官方镜像;
- 固定版本的Tag,而不是每次都使用lastest;
- 功能满足,选择体积小的镜像;
不建议的Dockerfile写法
如果用RUN命令来编写,新手尝尝写成这样子
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y wget
RUN wget https://github.com/ipinfo/cli/releases/download/ipinfo-2.0.1/ipinfo_2.0.1_linux_amd64.tar.gz
RUN tar zxf ipinfo_2.0.1_linux_amd64.tar.gz
RUN mv ipinfo_2.0.1_linux_amd64 /usr/bin/ipinfo
RUN rm -rf ipinfo_2.0.1_linux_amd64.tar.gz
但这样写是有问题的,镜像的分层会变的很多,每个RUN都是一个分层,打出来的镜像包也会变大。这是很多新手会犯的错误。
可以通过docker image ls 查看镜像信息,可以看到镜像比较大
还可以通过docker image history <Image ID> 可以查看到镜像的具体分层大小,每个分层的体积
正确的Dockerfile写法
把所有执行命令放到一个RUN里,并用&& \进行连接。就可以把很多命令写到一个RUN里边了。
FROM ubuntu:latest
RUN apt-get update && \
apt-get install -y wget && \
wget https://github.com/ipinfo/cli/releases/download/ipinfo-2.0.1/ipinfo_2.0.1_linux_amd64.tar.gz && \
tar zxf ipinfo_2.0.1_linux_amd64.tar.gz && \
mv ipinfo_2.0.1_linux_amd64 /usr/bin/ipinfo && \
rm -rf ipinfo_2.0.1_linux_amd64.tar.gz
这样所有的RUN命令只生成一层image layer。打包出来的镜像也没有那么大了。我们把这个文件写到Dockerfile文件里,然后用命令进行打包。
docker image build -f Dockerfile -t localImage .
这时候再用docker image histroy <Image ID> 查看分层,就会看到分层少了很多。
通过两种方式build后的镜像体积对比,差距是巨大的。
Dockerfile中的文件操作
制作镜像的时候,经常需要向镜像里添加文件。在Dockerfile中有两个命令可以向镜像中添加文件COPY和ADD。这节我们就学习一下这两个命令,并重点了解一下两个命令的不同。
COPY和ADD命令,在复制普通文件的时候,并没有什么太大的不同,两个命令都可以把本地文件,复制到镜像里。如果复制的路径不存在,则会自动创建
现在我们实现一个需求,写一个Dockerfile,里面的内容是用基础Node镜像,然后拷贝一个index.js文件进去
Dockerfile内容如下:
FROM node:alpine3.14
COPY index.js /app/index.js
引用node3.13版本,然后把index.js文件,copy到app目录下面
index.js 文件如下。代码是我们在3000端口上,开启 了一个最简单web服务,然后返回了Hello Nodejs字样
// 导入http模块
const http = require('http');
// 创建一个服务对象
const server = http.createServer();
// 开启服务器
server.listen(3000, () => {
console.log('Server is running....');
});
// 监听浏览器请求并进行处理
server.on('request', (req, res) => {
// end方法能够解析数据返回给浏览器,浏览器会显示改字符串
res.end('Hello Nodejs');
});
两个文件都准备好后,用build命令进行构建
docker image build -f Dockerfile -t hello-copy .
构建完成后,可以使用docker image ls命令进行查询,生产成功后,可以启用交互模式,再加上映射端口形式,运行容器。
docker container run -it -p 3000:3000 hello-copy sh
这里映射了3000端口,这样我们就可以用127.0.0.1:3000进行访问了。访问后页面会显示Hello Nodejs
用ADD构建镜像
ADD构建镜像和COPY最直观的一点不同,是ADD命令可以直接解压压缩文件,这当我们有很多文件要上传操作的时候,就会变的简单很多。
Dockerfile文件内容
FROM node:alpine3.14
ADD index.tar /app/
用ADD命令进行打包镜像
docker image build -f Dockerfile -t hello-gzip .
打包好以后使用交互模式,开启容器。
docker container run -it -p 3000:3000 hello-gzip sh
这个使用我们进入到app目录下,会看到已解压的index.js文件,所以ADD命令拷贝的后是会自动解压压缩文件的。其他区别暂时不知,知道的朋友可以告诉我
切换工作目录WORKDIR
Dockerfile语法
| 语法 | 例子 | 描述 |
|---|---|---|
| FROM Image | FROM ubuntu:latest | 它的意思是选择一个基础镜像,我这里选择的是ubuntu系统的最新版。 |
| docker image build -f <Dockerfile文件名> -t | docker image build -f Dockerfile.bad -t ipinfo-bad . | -f为指定打包的名称 |
| docker image ls | 可以查看image信息 | |
| docker image history | 可以使用命令查看具体的分层情况 |