Docker 学习

245 阅读17分钟

前言

此文档是在学习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的默认网址。 企业微信截图_3478d8ba-31d6-4445-b924-a8f04e029c47.png 这个时候你会看到你的控制台会打印一些日志,每访问一次都会打印一次日志 Untitled.png

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 Untitled 1.png

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中有两个命令可以向镜像中添加文件COPYADD。这节我们就学习一下这两个命令,并重点了解一下两个命令的不同。

COPYADD命令,在复制普通文件的时候,并没有什么太大的不同,两个命令都可以把本地文件,复制到镜像里。如果复制的路径不存在,则会自动创建

现在我们实现一个需求,写一个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 ImageFROM ubuntu:latest它的意思是选择一个基础镜像,我这里选择的是ubuntu系统的最新版。
docker image build -f <Dockerfile文件名> -t Dockerfile文件路径docker image build -f Dockerfile.bad -t ipinfo-bad .-f为指定打包的名称
docker image ls可以查看image信息
docker image history 可以使用命令查看具体的分层情况