一、Docker 部署以及镜像和容器的基本操作

序言

Docker 入门系列文章是根据视频教程全程实操的记录,基本上按照本系列文章操作,就能掌握 Docker 基本入门技巧。由于纯属个人笔记,难免存在错误和不详尽之处。欢迎交流批评指正。

1. 准备环境

准备 Linux 服务器,推荐 CentOS 7,64位、系统内核版本为 3.10 以上

[root@k8s-master ~]# uname -r
3.10.0-1160.53.1.el7.x86_64

2. 配置 docker 镜像源

最大的公开的镜像仓库是 Docker Hub:
https://hub.docker.com 国外的镜像源会被墙,推荐使用阿里云:

sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

3. 安装docker

Docker 版本分为 CE(社区版,免费)和EE(企业版,安全CE)。我们安装社区版。
执行如下命令安装:

yum install docker-ce -y

如果之前安装过其他源的 docker,可能会冲突,安装失败,先卸载旧版本,执行命令:

sudo yum remove docker  docker-common docker-selinux docker-engine

启动 docker 守护进程

systemctl start docker

设置 docker 守护进程开机自动启动

systemctl enable docker

查看 docker 版本

docker --version
或者
docker -v

查看 docker 详细信息

docker info

4. docker 镜像的操作

在线搜索 docker 镜像

docker search [镜像名]

注意:
判断镜像是否是官方的:official 栏显示 OK 则是官方镜像。

关于 docker 的内核问题:
我们下载的 centos、或者 ubuntu 的 docker 镜像,没有系统内核,只有运行需要的 lib 库等软件和文件。镜像使用的内核是宿主机的内核。所以同一台宿主机上的 docker 使用的内核都是一样的,都是宿主机的内核。 所以,如果宿主机系统为 centos,想在上面使用 docker 为 ubuntu 的特性,是无法实现的。

docker 下载镜像

docker pull [镜像名]

示例:

docker pull centos

关于 docker 镜像标签:如果不手动设置标签,镜像的标签默认为 latest :

[root@k8s-master ~]# docker images | grep centos
centos                                                            latest     5d0da3dc9764   5 months ago   231MB

查看本地宿主机上的所有镜像:docker images。每个镜像都有唯一的ID

[root@k8s-slave1 ~]# docker images
REPOSITORY                                           TAG       IMAGE ID       CREATED        SIZE
registry.aliyuncs.com/google_containers/kube-proxy   v1.23.4   2114245ec4d6   13 days ago    112MB
rancher/mirrored-flannelcni-flannel                  v0.16.3   8cb5de74f107   4 weeks ago    59.7MB
rancher/mirrored-flannelcni-flannel-cni-plugin       v1.0.1    ac40ce625740   5 weeks ago    8.1MB
centos                                               latest    5d0da3dc9764   5 months ago   231MB
registry.aliyuncs.com/google_containers/pause        3.6       6270bb605e12   6 months ago   683kB
kubernetesui/metrics-scraper                         v1.0.7    7801cfc6d5c0   8 months ago   34.4MB

镜像导出

docker save -o [镜像压缩包的名称] centos:latest

说明:导出的镜像默认使用 tar 包的形式。
例如:

docker save -o centos.tar centos:latest

镜像导入

docker load --input centos.tar
或者
docker load < centos.tar

删除镜像(image)和删除容器(containers)

docker rmi [镜像名或者镜像ID]

强制删除:

docker rmi -f [镜像名或者镜像ID]

注意: 如果该镜像已经启动了容器,则该命令是无法删除镜像的。系统会提示该镜像启动过那个容器,并且给出该容器的ID,删除该容器后才能删除镜像。生产环境千万不要轻易执行强制删除命令!!!

5. docker 容器的操作

启动容器并输出 Hello world

docker run centos /bin/echo "Hello world"

注意:
该命令输出的 Hello world 是容器输出的,而不是宿主机输出的。该命令方式下,容器执行完 echo 这个任务后就终止运行了。

启动容器并命名

docker run --name [自定义的容器名称] -t -i [镜像名] /bin/bash

说明:
-t 是 tty,是指分配一个伪终端。-i 是 标准输入,因为要登陆进容器内部。该命令执行时会检查指定的[镜像名]在本地是否存在,如果不存在,就会到公共仓库中去下载。然后再启动容器,接着给该容器分配一个文件系统,挂在镜像的上面。最后还要给容器分配一个 IP 地址。
示例:

docker run --name mycentos -t -i centos /bin/bash

容器退出
执行 exit 命令退出容器,容器也终止了。

再次启动容器
先执行

docker ps -a

查看之前容器启动时的名称或者 ID ,再执行

docker start [容器名]

或者

docker start [容器ID]

就能启动刚才的容器了。

关闭容器

docker stop [容器名]

或者

docker stop [容器ID]

查看容器详细配置

docker inspect [容器名]

让一个容器结束运行后自动删除

docker run --rm [镜像名] /bin/echo "hehe"

执行完该命令后,使用命令 docker ps -a 看不到刚刚运行过的用于输出 hehe 的容器

删除容器:

docker rm [容器名或者容器ID]

二、种进入 Docker 容器的方法

1. 使用 attach 命令连接容器

docker attach [容器名] 或者 [容器ID]

attach 命令有如下缺点:
1.1 当你从多个终端同时使用 attach 命令连接到同一个容器后,多个终端显示的操作是同步的。也就是说你从A终端操作什么命令,在B终端也显示一样的命令和结果。同理,你在某个终端针对该容器的某个操作阻塞了或者异常了,那么其他的终端也同样不可操作了。
1.2 在 attach 的连接方式下使用 exit 退出容器后,该容器也会终止运行。

2. 使用 nsenter 命令连接容器

使用该命令连接容器,用 exit 命令退出容器后,容器不会终止运行。
2.1 nsenter 命令包含在 util-linux 里面,所以使用之前要先安装。

yum install -y util-linux

2.2 使用 nsenter 命令连接容器,需要获取容器的 PID。
可以使用 inspect 命令获取容器的详情:

docker inspect [容器名]或者[容器ID]

获取 docker 的第一个进程的 PID:

docker inspect --format "{{.State.Pid}}" mycentos

通过该 PID 连接容器:

nsenter --target [容器第一个进程的PID] --mount --uts --ipc --net --pid

写一个连接容器的脚本,命名为 docker_login.sh,脚本内容如下:

#!/bin/bash
#此脚本用来登录任意容器。使用方法:./docker_login [容器名]或者[容器ID]

docker_in(){
  NAME_ID=$1
  PID=$(docker inspect --format "{{.State.Pid}}" $NAME_ID)
  #nsenter --target $PID --mount --uts --ipc --net --pid
  nsenter --target $PID --mount --uts --ipc --net --pid /bin/bash
  #nsenter --target $PID --mount --uts --ipc --net --pid /bin/bash 2>/dev/null
  #nsenter --target $PID --mount --uts --ipc --net --pid /bin/bash >/dev/null 2>&1
  #nsenter --target $PID --mount --uts --ipc --net --pid /bin/bash >/dev/null 2>&1 &
  if (test $? -ne 0)
  then
    nsenter --target $PID --mount --uts --ipc --net --pid /bin/sh
  fi
}

docker_in $1

3. 使用 exec 命令连接容器

docker exec -it [容器名] /bin/bash

使用该命令连接容器,exit 退出后容器仍然继续运行。

说明:
为什么使用 exec 和 nsenter 命令连接容器再退出就不会导致容器终止,而使用 attach 连接容器再退出,容器就终止运行了呢?
因为前两个命令都是另外启动一个新的 bash 来连接容器,当退出容器时,终止的只是它们建立的 bash,而容器原来的 bash 进程还在。相应的,attach 使用的是容器启动后的 bash,所以退出该 bash ,容器也就终止了。

总结:
脚本的方式适合单节点上的容器维护,缺点是每台主机上都放个脚本,增加维护成本。最简单最常用的方式其实是 exec ,一个命令,语法也很简单。三种 docker 的连接方式,选择哪个还是要看使用场景。

三、Docker 网络映射和数据卷管理

先复习一下 docker 的基本操作:

1. 使用镜像启动一个容器,并且容器在后台运行

命令:

docker run -d -P [镜像名]

示例:

docker run -d -P nginx

参数说明:
-d 是让容器在后台运行。
-P 是随机的端口映射。命令的执行结果,会返回启动的容器的完整 ID(64位数字和字母组成)。示例中,会自动将宿主机的一个随机端口映射到生成的容器中的80端口(nginx 的默认端口是 80)。

思源笔记部署docker docker 思源笔记_nginx

2. 查看容器日志

命令:

docker logs [容器名或者ID]

思源笔记部署docker docker 思源笔记_思源笔记部署docker_02

3. 启动容器并设置指定的映射端口

命令:

docker run -d -p [宿主机的端口]:[容器的端口] --name [给新容器指定名称] [镜像名或者ID]

示例:

docker run -d -p 3333:80 --name nginx-demo nginx

参数说明:
-p 用来设置指定的映射端口。
做了端口映射之后,通过访问宿主机IP+映射到宿主机的端口,就能访问到docker中的服务。

思源笔记部署docker docker 思源笔记_linux_03

4. 查看容器映射了哪些端口

命令:

docker port nginx-demo

思源笔记部署docker docker 思源笔记_nginx_04

5. 启动容器并设置指定的IP和映射端口

命令:

docker run -d -p [宿主机IP]:[宿主机端口]:[容器端口]

说明:
这个命令适用于宿主机存在多个 IP 地址的情况。

6. 启动容器并指定容器的端口,但不指定宿主机的端口

命令:

docker run -d -p [宿主机IP]::[容器端口]

说明:
这个命令适用于宿主机存在多个 IP 地址的情况。

7. 启动容器并指定容器使用的协议

命令:

docker run -d -p [宿主机端口]:[容器端口]:udp

说明:
-p 默认使用的是 tcp 协议。可以指定使用 udp 协议。

接下来本篇文章要讲的核心内容来了

8. 容器的数据存储

必须知道的常识:容器的数据默认都存储在容器的卷里面,如果容器删除,数据也就没有了。

容器技术提供了一种方式,可以将宿主机的卷挂载到容器里面,这样就可以将容器的数据保存到宿主机上。

挂载参数是 -v,有两种使用方式:

【方式一】
-v /data
意思是:将宿主机的某目录挂载到容器的 /data 目录下。如果镜像(或者容器)中默认没有 /data 目录,挂载完成后会自动在容器中创建该目录。
登录容器,查看 /data 的磁盘挂载情况:

思源笔记部署docker docker 思源笔记_docker_05

问题: 宿主机的哪个卷挂载到容器上了呢?

验证: 先在容器的 /data 目录创建一个文件 hehe。回到宿主机,使用 inspect 命令查看容器的详细信息,在 Mount 这段信息中找到 Source 这个部分。

思源笔记部署docker docker 思源笔记_docker_06

进入 Source 后面的路径,可以查看到刚才创建的文件 hehe 。

思源笔记部署docker docker 思源笔记_思源笔记部署docker_07

进入宿主机的目录 /var/lib/docker/volumes/,通常可以看到该目录下有很多卷。每一个与宿主机进行了卷挂载的容器,它挂载的卷都在这个目录下。即 使用命令 docker inspect [容器名]或[容器ID] | grep Source 查到的路径名称与这里的目录名称是可以匹配上的。如下图所示:

思源笔记部署docker docker 思源笔记_docker_08

结论:挂载宿主机的卷到容器但不指定具体是哪个卷,这个时候系统会随机在宿主机的目录 /var/lib/docker/volumes/ 里面找一个卷挂载到容器上。

【方式二】
-v src:dst
意思是:将宿主机的目录 src 挂载到容器的 dst 目录下。这是更好的使用方式。

示例:
-> 在宿主机本地创建一个目录用于挂载给容器:

mkdir /data/docker-volume-nginx

-> 启动容器并挂载刚才创建的目录到容器的目录 /data 下:

docker run -d --name nginx-test2 -v /data/docker-volume-nginx/:/data nginx

-> 进入刚才创建的目录,在里面创建一个文件 mmm:

touch mmm

-> 登录容器,在 /data 目录中,看到刚才在宿主机中创建的目录 mmm:

sh /data/shell/docker_in.sh nginx-test2

具体操作过程如下截图所示:

思源笔记部署docker docker 思源笔记_思源笔记部署docker_09

四、Docker 数据卷容器实践

数据卷挂载应用场景:
开发测试环境和生产环境

开发测试环境主要是用来挂载代码目录。生产环境主要是用来实现容器数据持久化,防止容器关闭时数据不可访问或者容器被删除时数据丢失。

1. 开发测试环境挂载数据卷

开发人员通常喜欢用办公电脑 Windows 系统来编写代码,那么怎样将本地的代码快速同步到 docker 环境中进行测试和打包?

以 VMware 为例,VMware 虚拟机的菜单中包含将办公电脑跟 Linux 虚拟机进行磁盘共享的功能,设置完该共享,然后再按照《Docker 入门私人笔记(三)》中将宿主机跟容器共享数据卷的方法设置共享即可。然后就实现了将办公机的本地磁盘跟 docker 数据卷共享。

2. 生产环境挂载数据卷

思路: 并不需要将每个容器都跟宿主机的磁盘配置共享(挂载数据卷)。而是在宿主机上启动一个容器,专门用来实现宿主机和容器的磁盘共享,可以称之为数据卷容器

如果其他任何容器想实现跟宿主机做磁盘共享,只需在启动时使用 --volumes-from 参数调用数据卷容器即可。

说简单些,数据卷容器所做的事情只有一个:执行宿主机跟容器挂载数据卷的命令。其他容器来调用数据卷容器时,就等于在调用挂载数据卷的命令而已。

优势:

  • 简化容器配置(不必在每个容器内进行挂载数据卷);
  • 降低维护成本(当数据卷不再需要时也不用卸载)。

步骤:2.1 启动一个专门用来挂载数据卷的容器:数据卷容器
命令:

docker run -d --name [自定义容器名] -v [宿主机的路径]:[容器的路径] [镜像名]

示例:

docker run -d --name volume-demo -v /data:/data nginx

验证:
登录数据卷容器 volume-demo 的目录 /data,确认能看到宿主机的目录 /data 中的文件即可。

2.2 启动一个新容器,调用数据卷容器的配置
命令:

docker run -d --name [自定义容器名] --volumes-from [专门挂载数据卷的容器名] [镜像名]

示例:

docker run -d --name nginx-volume-demo --volumes-from volume-demo nginx

验证:
登录新容器 nginx-volume-demo,确认跟数据卷容器 volume-demo 一样共享了宿主机的目录 /data 并且能看到里面的文件即可。

说明:
数据卷容器的运行状态,不影响引用它的容器的状态。以上面的示例来说明,即 数据卷容器 volume-demo 如果关闭,容器 nginx-volume-demo 的数据卷挂载完全不受影响。
另外,如果容器 nginx-volume-demo 重启,数据卷会自动挂载,无需重新挂载。

缺点:
调用数据卷容器只能按照数据卷容器的的挂载配置去挂载数据卷,即挂载目录固定。

五、手动构建 Docker 镜像

本文核心内容:基于现有容器手动创建一个镜像

以创建一个 nginx 的镜像为例。步骤如下。

1. 使用镜像 centos 启动一个容器

docker run --name mynginx -it centos /bin/bash

2. 登录容器内安装 nginx

在阿里云的源中找到 epel 的地址

https://mirrors.aliyun.com/epel/

在容器内执行安装:

rpm -ivh https://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm

安装nginx:

yum install -y nginx

注意:

  • 在镜像中执行完在线安装之后,要执行 yum clean all,把没用的软件包和数据都清理掉,可以降低容器的大小(已验证)。
  • 需要配置 nginx 在前台运行。因为 nginx 默认是在后台运行,容器启动之后马上又会退出(未验证)。具体操作方法是:
    编辑 nginx 配置文件,vi /etc/nginx/nginx.conf,在里面增加一行配置:
    daemon off;
    这样 nginx 就能在前台运行了。

3. 将容器提交为一个本地镜像

命令:

docker commit -m "[备注]" [使用的容器的ID或者容器名] [自定义的镜像名]:[标签]

示例:

docker commit -m "My nginx" 5382f6e3d987 mynginx:v1

思源笔记部署docker docker 思源笔记_思源笔记部署docker_10

4. 验证:使用新创建的 nginx 镜像启动一个容器

命令:

docker run -d -p [宿主机的端口]:[容器的端口] [刚创建的镜像名]:[标签] [容器内执行的命令]

示例:

docker run -d -p 91:80 mynginx:v1 nginx

注意:
该命令的最后一个字段不是容器名或者标签等等,而是要在容器中执行的命令。

思源笔记部署docker docker 思源笔记_思源笔记部署docker_11