Docker入门梳理

最近终于基本看完了《Docker — 从入门到实践》 一书,学了后面忘了前面的就掌握了基本的使用哈哈哈(这玩意真的太需要实践了!!!) 顺便写个总结 都是些最简单的东西 方便快速上手 后面深入的东西再继续学习吧 像 Docker compose之类


Docker是什么

Docker是操作系统层面的虚拟化技术, 类似的还有以前用的虚拟机技术 他们都是提供虚拟化服务

区别在于 Docker 是作为进程 直接运行在宿主机的内核中 不需要进行硬件虚拟
而虚拟机是先虚拟出一套硬件后在上面运行一个完成的操作系统 在系统上在运行所需要的进程

这样比起来 Docker 就显得轻量级 更轻便

镜像 Image

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

容器 Container

容器是镜像运行时的实体。 它可以被创建、启动、停止、删除、暂停等

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全

仓库 Registry

Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。
一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像

镜像操作

当我们想要运行某个服务的时候 肯定需要一个镜像作为基础 然后才可以生成容器跑服务,所以需要先把镜像下载到本地

获取镜像

1
docker pull image_name:tag

这个是常用的一个命令,因为一个镜像可以有很多的版本所以可以通过指定tag来声明你需要具体的哪个版本的镜像。后面的tag也可以省略 如果省略的话默认会找 latest 也就是最新的镜像下载 更多请自己查看 docker pull --help

列出本地镜像

如果想要列出本地的镜像 常用的命令

1
2
3
4
5
6
7
8
9
10
11
12
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 5f515359c7f8 5 days ago 183 MB
nginx latest 05a60462f8ba 5 days ago 181 MB
# 如果只想列出ID 可以加 -q参数
$ docker image ls -q
5f515359c7f8
# 还可以使用格式化
$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
IMAGE ID REPOSITORY TAG
5f515359c7f8 redis latest
05a60462f8ba nginx latest

删除本地镜像

如果想要删除本地镜像 常用的命令

1
2
3
4
# 其中xx可以是镜像的名字、镜像的完整ID、镜像的ID前三位等
$ docker image rm xx
# 批量删除镜像 可以配合查询命令来删除
$ docker image rm $(docker image ls -q)

更多骚操作请自己查看 --help

Dockerfile定制镜像

镜像的定制实际上就是定制每一层所添加的配置、文件。
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建

拿Nginx基础镜像来做个测试

下面新建个目录来创建 Dockerfile

1
2
3
4
5
6
$ mkdir mynginx
$ cd mynginx
$ touch Dockerfile
# 编辑Dockerfile文件输入如下内容
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

这是一个很简单的配置 就2个命令 FROMRUN

FROM 是指定基础镜像 表示已哪个镜像做为基础 他必须是第一条指令

RUN 是用来执行命令行命令的 它有两个格式用法
shell 格式:RUN <命令>,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。
RUN echo ‘Hello, Docker!’ > /usr/share/nginx/html/index.html

exec 格式:RUN [“可执行文件”, “参数1”, “参数2”],这更像是函数调用中的格式。

现在有了Dockerfile就可以构建镜像了

1
2
3
4
5
6
7
8
9
10
[root@localhost mynginx]# docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048kB
Step 1/2 : from nginx
---> 568c4670fa80
Step 2/2 : run echo '<h1>Hello,Docker</h1>' > /usr/share/nginx/html/index.html
---> Running in 95bfd552874e
Removing intermediate container 95bfd552874e
---> 3d4e1fb1e8ea
Successfully built 3d4e1fb1e8ea
Successfully tagged nginx:v3

我们通过 -t 参数 给新的镜像打了个 tag 所以最终生成的镜像是 nginx:v3

然后看下他的执行过程

1.先是把上下文路径资源上传 Docker 引擎
2.从基础容器启动一个容器 568c4670fa80
3.然后 run 命令 启动一个容器 95bfd552874e 然后删除它 并用新内容生成了最后的容器 3d4e1fb1e8ea

注意:docker build -t nginx:v3 . 最后是有个 . 这个点是 上下文路径

上下文路径

Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具.Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举

理解了上面的东西 在来看 上下文路径 的概念

我当前的路径是:

1
2
[root@localhost mynginx]# pwd
/data/docker_test/mynginx

注意在生成镜像的时候 Docker 会把 上下文路径 内的资源全部上传到Docker引擎中,所以不要傻的把 根目录 当作Docker的上下文路径 这样它会把所有东西都上传上去。

在本次示例中 是把我的目录 /data/docker_test/mynginx 当作了上下文路径 也就是这个目录下的资源会上传Docker引擎然后生成镜像。

如果你在 Dockerfile 中有指定 路径的命令 比如 COPY ../index.php /usr/share/nginx/html/ 这个其实是执行不成功的 即使你在 /data/docker_test 目录下 有 index.php 文件,因为它超出了你所指定的 上下文路径 也就是 mynginx 目录

1
2
3
4
5
6
7
8
9
[root@localhost mynginx]# docker build -t nginx:v4 .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM nginx
---> 568c4670fa80
Step 2/3 : RUN echo '<h1>Hello,Docker</h1>' > /usr/share/nginx/html/index.html
---> Using cache
---> 3d4e1fb1e8ea
Step 3/3 : COPY ../index.php /usr/share/nginx/html/
COPY failed: Forbidden path outside the build context: ../index.php ()

可以看到是失败的

然后我们可以把 COPY ../index.php /usr/share/nginx/html/ 换成 COPY ./index.php /usr/share/nginx/html/

然后指定上下文路径(ps:-f是指定当前使用的Dockerfile文件路径)

1
2
3
4
5
6
7
8
9
10
11
[root@localhost mynginx]# docker build -t nginx:v4 -f /data/docker_test/mynginx/Dockerfile /data/docker_test/
Sending build context to Docker daemon 3.584kB
Step 1/3 : FROM nginx
---> 568c4670fa80
Step 2/3 : RUN echo '<h1>Hello,Docker</h1>' > /usr/share/nginx/html/index.html
---> Using cache
---> 3d4e1fb1e8ea
Step 3/3 : COPY ./index.php /usr/share/nginx/html/
---> 173c978fc188
Successfully built 173c978fc188
Successfully tagged nginx:v4

这样就OK了

容器操作

创建容器

以Nginx容器和Mysql容器为例

docker run --name webserver -d -p 80:80 -v /data/website/blog:/usr/share/nginx/html nginx

docker run --name mysql8 -d -p 3306:3306 -v /data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root123456 mysql

docker run 是一个启动容器的命令 参数 --name xxx 是给容器起个名字 这个不是必须也可以默认镜像的名字。 -d 表示容器做后台运行,-p port:port 参数表示 指定端口号映射 冒号前面的时候本机的端口号,冒号后面的是容器里的端口号 可以保持不一致, 参数 -v path:path 表示目录映射,冒号前面的是主机的目录,冒号后面的容器里的目录 一般是用来存储数据的 这样容器销毁了数据还在本机(建议写绝对路径) 最后的就是镜像的名字了 表示使用该镜像创建容器

另外在Mysql创建的容器中还有个 -e 参数 这个是运行一个命令 后面的表示给mysql创建默认的root用户密码

查看容器

1
2
3
4
5
6
[root@localhost ~]# docker ps
....
[root@localhost ~]# docker container ls
....
[root@localhost ~]# docker container ls -a
....

都可以列出运行的容器 参数 -a 的话可以列出所有状态的容器 包括停止的,有时候我们只列出了运行中的容器时 在创建容器会提示你容器已经存在 其实可能是容器只是 不是运行状态 而已

终止容器

可以是用命令 docker container stop name/container_id 最后的名字可以是启动时的名字 也可以是启动后的容器ID

删除容器

可以使用命令 docker container rm name 最后是容器的名字 同时可以使用 docker container prune 来删除无主的容器

数据卷

数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

数据卷 可以在容器之间共享和重用

对 数据卷 的修改会立马生效

对 数据卷 的更新,不会影响镜像

数据卷 默认会一直存在,即使容器被删除

注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的 数据卷。

查看和创建 数据卷

可以使用命令 docker volume create vol_name 来创建数据库。 使用 docker volume ls 或者 docker volume inspect vol_name 来查看数据卷

删除数据卷

可是使用命令 docker volume rm vol_name 来删除数据卷 同时可以使用 docker volume prune 来删除无主数据卷

使用数据卷

已启动Nginx容器为例 增加数据卷使用

docker run --name webserver -d -p 80:80 -v vol_name:/usr/share/nginx/html nginx 或者把其中的 -v vol_name:path 换成 --mount source=vol_name,target=path 注意path是容器里的路径

挂载主机目录

使用

这个同挂载数据卷差不多,我还是比较喜欢挂载主机目录这种形式 使用形式 还是用Nginx容器为例

docker run --name webserver -d -p 80:80 -v /data/website/blog:/usr/share/nginx/html nginx 其中 -v path1:path2 就是把主机的目录path1挂载到了容器里的目录path2中了 还可以使用 --mount type=bind,source=path1,target=path2,readonly 来指定,最后的 readonly 不是必填参数 因为默认挂载的目录 在Docker容器中是 读写 权限 加了 readonly 后就变为了只读权限了。

查看容器挂载信息

docker inspect container_name 最后是容器的名字 挂载主机目录 的配置信息在 “Mounts” Key 下面

1
2
3
4
5
6
7
8
9
10
"Mounts": [
{
"Type": "bind",
"Source": "/data/website/blog",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],

容器网络

端口设置

一般在容器启动的时候我们通常会使用 -p (注意这是 小写p) 来指定端口号,我们还可以使用 -P(注意这是 大写P) 使用这个的时候 Docker 会随机映射一个 49000~49900 的 本机端口 到内部容器开放的网络端口(也就是到容器默认开放的端口,比如Mysql的默认端口是3306 他就会自动映射到容器的3306端口上)。

在使用 -p port:container_port 的时候 还可以使用多个 -p 参数一次指定多个端口映射 比如

1
docker run -d -p 5000:5000 -p 3000:80 nginx

还可以映射指定IP地址的到容器

1
docker run -d -p 127.0.0.1:80:80 -p 3000:80 nginx

如果不限制IP上的端口到容器里端口的映射可以

1
docker run -d -p 127.0.0.1::80 -p 3000:80 nginx

容器互联

每个容器都有自己的网络环境 要让容器之间的互相连接可以通过创建一个Docker网络来实现

docker network create -d bridge my-net -d 参数指定 Docker 网络类型,有 bridge overlay。其中 overlay 网络类型用于 Swarm mode.

在运行容器的时候指定使用的网络 还是以Nginx容器启动为例

docker run --name webserver -d -p 80:80 -v /data/website/blog:/usr/share/nginx/html --network my-net nginx 其中 参数 --network my-net 就是指定使用网络 网络名字是 my-net

打开新的终端,再运行一个容器并加入到 my-net 网络

docker run --name webserver2 -d -p 80:80 -v /data/website/blog2:/usr/share/nginx/html --network my-net nginx

然后可以分别进入到容器里使用 ping 对方来查看是否 ping 的通

比如进入 webserver 容器中 docker exec -it webserver sh 进入容器后然后执行

1
2
3
4
$ ping webserver2
PING busybox1 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.064 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.143 ms

同理进入第二个容器来ping第一个容器 也可以成功连接,这样2个容器就建立了互联

如果需要多个容器之间的互联 推荐使用 Docker Compose

原书

上面的总结全部自来 《Docker — 从入门到实践》 一书 自己总结了一些适合快速上手并使用的内容 更深的内容需要自己去看去发掘

原文地址:https://yeasy.gitbooks.io/docker_practice/content/


-------------The End-------------