byf123

导航

docker笔记

Docker 入门到精通

一、Docker简介

1.1、Docker是什么

官方地址:https://www.docker.com https://docs.docker.com/
仓库地址:https://hub.docker.com/

Docker 最初是 dotCloud 公司创始人Solomon Hykes在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于2013 年 3 月以 Apache 2.0 授权协议开源 ,主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动 [开放容器联盟(OCI)。

Docker 自开源后受到广泛的关注和讨论,至今其GitHub 项目已经超过 5 万 7 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker

Docker 使用 Google 公司推出的 Go 语言进行开发实现,基于 Linux 内核的cgroup,namespace,以及 OverlayFS类的Union FS等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于LXC,从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 版本开始,则进一步演进为使用runC和containerd。

1.2、我们为什么要用Docker

#更高效的利用系统资源
由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。

#更快速的启动时间
传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。

#一致的运行环境
开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题。

#持续交付和部署
对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration)系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment)系统进行自动部署。而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。

#更轻松的迁移
由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。

#更轻松的维护和扩展
Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像 (opens new window),既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。

1.3、与传统虚拟机对比

传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。

特性 容器 虚拟机
启动 秒级 分钟级
硬盘使用 一般为 MB 一般为 GB
性能 接近原生 弱于
系统支持量 单机支持上千个容器 一般几十个

二、Docker基本概念

2.1、基本概念

Docker三个重要的概念

  • 镜像 (Image)
  • 容器 (Container)
  • 仓库 (Repository)

2.2、Docker镜像

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

2.3、Docker容器

镜像(Image)和容器(Container)的关系,就像是QQ软件跟QQ进程一样,镜像是静态的,容器是镜像运行时的实体。
容器可以被创建、启动、停止、删除、暂停等

宿主机---物理机

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

2.4、Docker仓库

仓库类似于应用商店,存放的是docker镜像,分为公开仓库跟私有仓库

通常,仓库可以包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

以 Ubuntu 镜像为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,16.04, 18.04。我们可以通过 ubuntu:16.04,或者 ubuntu:18.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest。

三、安装Docker

3.1、系统初始化

0.系统版本
[root@docker ~]# cat /etc/redhat-release
CentOS Linux release 7.8.2003 (Core)
[root@docker ~]# uname -r
3.10.0-1127.el7.x86_64

1.更改主机名
[root@docker ~]# hostnamectl set-hostname docker

2.禁用Selinux
[root@docker ~]# sed -i 's/SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config  永久关闭,重启系统后生效
[root@docker ~]# setenfoce 0 临时关闭
setenforce: SELinux is disabled
[root@docker ~]# getenfoce
Disabled

3.关闭防火墙(firewalld)
[root@docker ~]# systemctl stop firewalld 停止
[root@docker ~]# systemctl disbale firewalld 禁止开机自启

4.YUM配置
[root@docker ~]# curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo 下载yum源
[root@docker ~]# yum clean all 清除缓存
[root@docker ~]# yum makecache 生成缓存
[root@docker ~]# yum install epel-release -y 安装额外源
[root@docker ~]# yum install wget  vim gcc gcc-c++ lsof lrzsz bash-completion net-tools tree -y 安装常用命令及工具
[root@docker ~]# systemctl reboot

3.2、安装Docker前置操作

1.卸载Centos7默认的docker
[root@docker ~]# yum remove docker \                
docker-client \
docker-client-latest \                   
docker-common \                   
docker-latest \                 
docker-latest-logrotate \                  
docker-logrotate \                  
docker-engine

2.安装Docker依赖并且配置阿里云docker源
[root@docker ~]# yum install lvm2 device-mapper-persistent-data yum-utils -y  安装依赖相关
[root@docker ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 配置docker下载源
[root@docker ~]# yum makecache fast

3.3、安装Docker并启动

docker-ce docker-ce-cli containerd.io -y ce是社区版,cli是客户端, containerd是容器

1.安装Docker 
[root@docker ~]# yum install docker-ce docker-ce-cli containerd.io -y

2.配置AliYun加速
[root@docker ~]# mkdir -p /etc/docker
[root@docker ~]# cat >/etc/docker/daemon.json<<EOF
{
  "registry-mirrors": ["https://ncw0pf4i.mirror.aliyuncs.com"]
}
EOF
[root@docker ~]# systemctl daemon-reload

3.设置开机自启并启动
[root@docker ~]# systemctl enable --now docker

3.4、启动第一个Docker容器

[root@docker ~]# docker run hello-world

1.查找本地镜像仓库
2.拉取docker hub中的hello-world镜像
3.下载完成
4.启动镜像为容器
Hello from Docker!

四、Docker常见操作

4.1、查看版本信息

Docker版本信息

[root@docker ~]# docker version
Client: Docker Engine - Community 客户端版本信息
 Version:           20.10.6
 API version:       1.41
 Go version:        go1.13.15
......
Server: Docker Engine - Community 服务端版本信息
 Engine:
  Version:          20.10.6
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
.....

解析:
Docker客户端与服务端版本默认为一致,Docker底层用Golang语言开发的
Docker是C/S架构,分为客户端与服务端
客户端敲命令---->服务端运行解析命令并且展示结果


Docker详细信息(包含全面,建议使用次命令)
[root@docker ~]# docker info
Client:
......

Server:
 Containers: 1 容器数量
  Running: 0 有几个正在运行
  Paused: 0 有几个暂停啦
  Stopped: 1 有几个停止了
 Images: 2 有几个镜像
 Kernel Version: 3.10.0-1127.el7.x86_64 系统内核版本
 Operating System: CentOS Linux 7 (Core) 系统版本
 OSType: linux 
 Architecture: x86_64
 CPUs: 1
 Total Memory: 972.3MiB
 Name: docker
 Registry Mirrors:
  https://ncw0pf4i.mirror.aliyuncs.com/ 阿里云docker镜像地址,可以加速docker下载

4.2、镜像相关命令

0.获取帮助
[root@docker ~]# docker image --help

Usage:  docker image COMMAND
Commands:
  build       Build an image from a Dockerfile
  history     Show the history of an image
  import      Import the contents from a tarball to create a filesystem image
  inspect     Display detailed information on one or more images
  load        Load an image from a tar archive or STDIN
  ls          List images
  prune       Remove unused images
  pull        Pull an image or a repository from a registry
  push        Push an image or a repository to a registry
  rm          Remove one or more images
  save        Save one or more images to a tar archive (streamed to STDOUT by default)
  tag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE


1.查看镜像
docker image ls 显示本地镜像
docker images 显示本地镜像

docker image ls 简写为 docker images

-q 只显示镜像id

[root@docker ~]# docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
redis         latest    bc8d70f9ef6c   2 weeks ago    105MB
hello-world   latest    d1165f221234   2 months ago   13.3kB
镜像名    版本(latest最新版本) 镜像ID       创建时间(官方)  镜像大小


2.下载镜像
docker pull 镜像名:版本号 ---> 下载指定镜像及版本
docker pill 镜像名 ---> 下载指定镜像(最新版本-latest)
在 https://hub.docker.com/网站中
[root@docker ~]# docker pull redis:6.0   下载redis6.0镜像
6.0: Pulling from library/redis  拉取镜像信息
69692152171a: Pull complete  下载相关镜像内容
......
d040d72a588c: Pull complete 
Digest: sha256:71909943bb7ca4b34b6b151e7b29c608ddae654cdc9a1d2d15f30df666b3a805 镜像签名
Status: Downloaded newer image for redis:6.0  告诉你下载一个新的镜像
docker.io/library/redis:6.0

3.删除镜像
docker image rm 镜像名:版本
docker image rm 镜像ID
docker rmi 镜像名:版本
docker rmi 镜像ID
docker image rm 简写 docker rmi

删除hello-world镜像
[root@docker ~]# docker image rm hello-world:latest 
Untagged: hello-world:latest
......
Deleted: sha256:f22b99068db93900abe17f7f5e09ec775c2826ecfe9db961fea68293744144bd

删除redis镜像
[root@docker ~]# docker rmi 5f6517031937
Untagged: redis:6.0
......
Deleted: sha256:02c055ef67f5904019f43a41ea5f099996d8e7633749b6e606c400526b2c4b33

如何强制删除镜像,不管有没有容器在运行

无法删除镜像,因为有容器在依赖于这个镜像
[root@docker ~]# docker rmi -f d1a364dc548d
Error response from daemon: conflict: unable to delete d1a364dc548d (cannot be forced) - image is being used by running container ff53779c6e49

只是删了镜像的名称跟id跟版本号,但是镜像本身并没有被删除
[root@docker ~]# docker image rm -f nginx:latest 
Untagged: nginx:latest
Untagged: nginx@sha256:6d75c99af15565a301e48297fa2d121e15d80ad526f8369c526324f0f7ccb750

总结:容器在运行中的状态无法强制删除镜像


4.搜索镜像
docker search 镜像名称

[root@docker ~]# docker search redis
NAME                             DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
redis                            Redis is an open source key-value store that…   9491      [OK]  
不推荐使用docker search,无法显示详细版本信息,推荐浏览器访问 docker hub(hub.docker.com)去搜索 

4.3、容器相关操作

1.运行镜像为容器
docker run 镜像名:版本
docker run 镜像id
-d 把容器放入后台执行(默认是一直在终端显示的)
-p 宿主机端口映射至容器内(默认容器不能跟外部连接的) -p 宿主机端口:容器内部端口
--name 自定义容器名称(默认是docker创建的)

[root@docker ~]# docker run nginx:latest 
[root@docker ~]# docker run -d -p 1192:80 nginx:latest  运行nginx:latest镜像为容器80端口映射至宿主机为1192,并且放到后台运行
[root@docker ~]# docker run -d -p 1721:80 --name nginx01 nginx:latest 运行nginx:latest镜像为容器80端口映射至宿主机为1721,容器名称为nginx01并且放到后台运行

2.查看容器信息
docker ps 查看容器(默认只能显示已启动的容器)
-a 显示全部容器
-q 只显示容器id

[root@docker ~]# docker ps 
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS     NAMES
ff53779c6e49   nginx:latest   "/docker-entrypoint.…"   23 seconds ago   Up 21 seconds   80/tcp    relaxed_goldwasser
容器id          次容器使用的镜像    容器内使用的命令            容器创建时间      容器状态         容器内端口    容器名

[root@docker ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                      PORTS     NAMES
2df897bd6df2   nginx:latest   "/docker-entrypoint.…"   29 seconds ago   Exited (0) 13 seconds ago             blissful_kepler
7be3ab768824   nginx:latest   "/docker-entrypoint.…"   2 minutes ago    Up About a minute           80/tcp    kind_shannon

[root@docker ~]# docker ps -aq
2df897bd6df2
7be3ab768824

3.启动停止重启容器
启动
docker start 容器名称/容器id

停止
docker stop 容器名称/容器id

重启
docker restart 容器名称/容器id

杀死
docker kill 容器名称/容器id

停止容器
[root@docker ~]# docker stop nginx01
nginx01

重启容器
[root@docker ~]# docker restart 88a13d4d8563
88a13d4d8563
[root@docker ~]# docker ps
88a13d4d8563   nginx:latest   "/docker-entrypoint.…"   15 minutes ago   Up 2 minutes    0.0.0.0:1192->80/tcp, :::1192->80/tcp                                          adoring_kapitsa
启动容器
[root@docker ~]# docker start kind_shannon
kind_shannon

杀死容器
[root@docker ~]# docker kill 5e571d75a8a1
5e571d75a8a1


4.删除容器
docker rm 容器名/容器id
-f 强制删除(不管容器是不是在运行)
[root@docker ~]# docker rm 5e571d75a8a1
[root@docker ~]# docker rm nervous_chaplygin 提示容器在运行中,无法删除
Error response from daemon: You cannot remove a running container 8b8a79ae102b78ca80d5a1c118c94a943dcdd1a22b890d0f4799cbfc7ad9a261. Stop the container before attempting removal or force remove
[root@docker ~]# docker rm -f nervous_chaplygin
nervous_chaplygin

删除所有容器,不管是不是在运行中
[root@docker ~]# docker rm -f $(docker ps -qa)
d0f474399e44
e5d9a0b71fd8
88a13d4d8563
80f537b47ffe
2df897bd6df2
7be3ab768824
[root@docker ~]# docker ps -qa

五、Docker高级操作

5.1、容器命令高级操作

1.宿主机与容器进行交互
docker exec 容器名称/容器的ID CMD
-i 交互模式(始终保持标准输入STDIN打开)
-t 分配一个虚拟终端

[root@docker ~]# docker exec -it nginxtest bash
root@574d9d59f4c8:/# ls
bin   dev		   docker-entrypoint.sh  home  lib64  mnt  proc  run   srv  tmp  var
boot  docker-entrypoint.d  etc			 lib   media  opt  root  sbin  sys  usr

exit 容器停止退出
cril+p+q   退出但不停止


2.容器中的进程
docker top 容器id/容器名称
[root@docker ~]# docker top 574d9d59f4c8
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                1686                1667                0                   10:04               ?                   00:00:00            nginx: master process nginx -g daemon off;
101                 1740                1686                0                   10:04               ?                   00:00:00            nginx: worker process


3.容器启动命令高阶
docker run 选项 镜像名称/镜像id
—P 随机分配端口
-h 指定容器内主机名    格式: -h "ngx"
-i 以交互模式运行容器
-t 为容器分配一个虚拟终端

3.1随机分配一个端口
[root@docker ~]# docker run -d -P --name test01 nginx:latest
09808471f276ab4dcb52554a1019c521a1f88f11be408c37fdc7cc1a348ff8c7
[root@docker ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                     NAMES
09808471f276   nginx:latest   "/docker-entrypoint.…"   8 seconds ago    Up 6 seconds    0.0.0.0:49153->80/tcp, :::49153->80/tcp   test01

3.2随机分配一个端口并且指定容器主机名为ngx
[root@docker ~]# docker run -d -P -h "ngx" --name test02 nginx:latest
614b6f37cde9afcd98ea48711f9250d2ee168995754a9edf7f6a5bf4c45c278e
[root@docker ~]# docker exec -it test02 bash
root@ngx:/# exit
exit

3.3以交互模式运行一个容器
[root@docker ~]# docker run -it nginx:latest bash
root@e645897cd8f3:/# ls
bin   dev		   docker-entrypoint.sh  home  lib64  mnt  proc  run   srv  tmp  var
boot  docker-entrypoint.d  etc			 lib   media  opt  root  sbin  sys  usr
[root@docker ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED              STATUS                     PORTS                                     NAMES
e645897cd8f3   nginx:latest   "/docker-entrypoint.…"   17 seconds ago       Exited (0) 6 seconds ago                                             mystifying_rosalind
如果退出,对应的容器也就结束运行


4.宿主机与容器复制
docker cp
4.1容器文件复制到宿主机
docker cp 容器id/容器名:容器内的文件路径 宿主机存放位置  

[root@docker ~]# docker cp nginxtest:/usr/share/nginx/html/index.html /root/
[root@docker ~]# ls
index.html  init.sh

4.2宿主机复制到容器内
docker cp 宿主机文件 容器id/容器名:容器存放目录 
[root@docker ~]# docker cp /root/test.html nginxtest:/usr/share/nginx/html
root@574d9d59f4c8:/usr/share/nginx/html# pwd
/usr/share/nginx/html
root@574d9d59f4c8:/usr/share/nginx/html# ls
50x.html  index.html  test.html


5.查看运行容器的日志
docker logs
docker logs 容器id/容器名
-f 实时刷新
-t 日志显示系统时间戳
--tail N 查看后几行日志,N表示数字

5.1实时刷新显示容器日志内容
[root@docker ~]# docker logs -f nginxtest
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf


5.2显示容器的后5行内容
[root@docker ~]# docker logs --tail 2 nginxtest
2021/05/28 03:36:25 [error] 31#31: *3 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.20.100.2, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "172.20.100.4", referrer: "http://172.20.100.4/"
172.20.100.2 - - [28/May/2021:03:36:25 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://172.20.100.4/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36" "-"

6.查看容器详细信息
docker inspect 容器名/容器ID

[root@docker ~]# docker inspect nginxtest | less

5.2、Docker数据卷基本操作

1.数据卷的作用 volume
可以实现宿主机跟容器之间文件共享,可供一个或多个容器使用,与cp命令不一样

场景
希望宿主机内的/test/app 跟容器内的/html 目录进行实时同步,分享,有项目上新 直接更改宿主机的目录即可,无需使用cp命令复制
Docker官方要求数据卷的使用需要在启动时指定

特性:
数据卷可以再宿主机跟容器之间共享文件,多个容器使用一个数据卷
数据卷修改可以直接生效,无需重启容器
数据卷的修改不会干扰镜像本身
数据卷的生命周期,不会被容器启停删除干扰


数据卷命令选项及格式
docker run 
-v 容器内路径            #匿名挂载
-v 宿主机目录:容器内目录   #指定路径挂载
-v 数据卷名称:容器内目录   #具名挂载

将宿主机目录直接映射到容器
自定义数据卷目录(会清空容器数据卷目录下面的内容)
[root@docker ~]# docker run -d -P -v /html:/usr/share/nginx/html nginx:latest 
7693cfc076994602a522b199dc84cd6a18c49fc7530a0ae86c4e84d5c968028e

在宿主机内进行操作,查看容器映射的目录下有没有改变
[root@docker html]# cat index.html 
<h1>hello,nginx,centos</h1>
<h2>12121</h2>

宿主机进行查看
[root@docker ~]# docker exec -it 7693c bash
root@7693cfc07699:/usr/share/nginx/html# pwd
/usr/share/nginx/html
root@7693cfc07699:/usr/share/nginx/html# cat index.html 
<h1>hello,nginx,centos</h1>
<h2>12121</h2>


自动数据卷目录(不会清空容器数据卷目录下的原始内容,自动拷贝到宿主机数据卷目录下)
宿主机进行查看
[root@docker ~]# docker run -d -P -v wb:/usr/share/nginx/html nginx:latest 
cd7d68ae095e119b9d616bad63b56b4f926533faa8b589519ee5e56bfb59f9fa
[root@docker ~]# cd `find / -name wb -type d`
[root@docker wb]# cd _data/
[root@docker _data]# ls
50x.html  index.html
[root@docker ~]# docker inspect nginx03 | grep html_date
                "html_date:/usr/share/nginx/html"
                "Name": "html_date",
                "Source": "/var/lib/docker/volumes/html_date/_data",


容器内数据卷映射目录查看
[root@docker _data]# docker exec -it cd7d68ae095 bash
root@cd7d68ae095e:/# cd /usr/share/nginx/html/
root@cd7d68ae095e:/usr/share/nginx/html# ls
50x.html  index.html

对文件数据进行修改
[root@docker _data]# echo '<h1>12111111</h1>' > index.html 

容器内查看
root@cd7d68ae095e:/usr/share/nginx/html# cat index.html 
<h1>12111111</h1>

5.2.1、删除容器查看数据卷

1.启动容器并且挂载
[root@docker ~]# docker run -d -P --name ngx01 -v ngx:/usr/share/nginx/html nginx:latest 
6348b2dc90fdaa2827f39bb1eda5f75ea27fd13fb19b6c61d1d420857c7e8375

2.宿主机新增文件
[root@docker _data]# echo  '<h1>abc</h1>' > abc.html
[root@docker _data]# ls
50x.html  abc.html  index.html

3.容器内查看
[root@docker _data]# docker exec -it ngx01 bash
root@6348b2dc90fd:/# cd /usr/share/nginx/html/ && ls
50x.html  abc.html  index.html

4.删除容器,查看数据卷有没有被删除
[root@docker _data]# docker rm -f ngx01
ngx01
[root@docker _data]# pwd
/var/lib/docker/volumes/ngx/_data
[root@docker _data]# ls
50x.html  abc.html  index.html

5.3、打包、导出、导入

docker commit 把容器打包为镜像(运行中的容器会暂停)
-m 描述选项
-a 作者信息

docker commit -m "描述" -a "作者信息" 容器id/容器名称 镜像名称

打包
[root@docker ~]# docker commit -m "ngx-test" -a "xwx" 152c041dc3ea ngx-01
sha256:5e8145edd814197961acb03ef80b62685f0273321d346724f551879a86b5436a

查看本地镜像仓库
[root@docker ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
ngx-01       latest    5e8145edd814   8 seconds ago   133MB

尝试运行
[root@docker ~]# docker run -d -P --name ngx0000  ngx-01
51419a9ff948c77371d4ad7f2593bdf425daf494e27c1f0afd52c4a129b9d324
[root@docker html]# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                     NAMES
51419a9ff948   ngx-01         "/docker-entrypoint.…"   8 seconds ago    Up 7 seconds    0.0.0.0:49164->80/tcp, :::49164->80/tcp   ngx0000


把镜像保存成tar包
docker save
-o 保存成为文件
docker save 镜像名称/镜像id -o 文件名

[root@docker html]# docker save ngx-01 -o ngx-01.tar
[root@docker html]# ls
123.ht  index.html  jjjj.html  ngx-01.tar


导入使用save命令导出的镜像
docker load [选项]
-i 指定导入的文件

[root@docker html]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
ngx-01       latest    5e8145edd814   5 minutes ago   133MB
nginx        latest    d1a364dc548d   2 days ago      133MB
tomcat       latest    c43a65faae57   2 weeks ago     667MB
[root@docker html]# docker rmi -f 5e8145edd814
Untagged: ngx-01:latest
Deleted: sha256:5e8145edd814197961acb03ef80b62685f0273321d346724f551879a86b5436a
[root@docker html]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
nginx        latest    d1a364dc548d   2 days ago    133MB
tomcat       latest    c43a65faae57   2 weeks ago   667MB
[root@docker html]# docker load -i ngx-01.tar 
Loaded image: ngx-01:latest
[root@docker html]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
ngx-01       latest    5e8145edd814   7 minutes ago   133MB

5.4、镜像分层原理

UNiOSFS -- 联合文件系统

0.什么是docker镜像
镜像就是软件,可以独立运行的软件包(已经把软件运行时候需要依赖的各种库,配置文件都已经打包进行)

1.docker镜像为什么这么大
镜像包含了软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时所需的库、环境变量和配置文件。

2.Docker镜像分层原理
如果不使用分层原理的话,相同的软件不同版本,下载的话会非常占用系统空间,因为每个软件都需要自己的依赖跟操作系统支撑
镜像构建的时候,尽量划分的层级细腻一点确保,公共层可以被更好的复用,同类型的软件,可以复用,比方用上个镜像的操作系统依赖,既可以完成本身的镜像构建,也可以被镜像复用

3.公共层
一般都被我们成为基础镜像,base镜像,把具有公共性的层抽取出来做成公共镜像,所有的镜像都是基于公共镜像去构建字节的,拆分越细,复用率越高,在宿主机上占用的空间越小,镜像运行必须依赖操作系统,因为docker镜像采用是系统级别的隔离,最小操作系统 bootfs 主引导操作系统文件,主要包含bootloader(引导操作系统内核)、kernel(操作系统的内核),rootfs典型的linux系统,包含常用目录及文件,精简版的os系统 

六、容器网络

6.1、Docker网络简介

6.1.1、网络详解

当 Docker 启动时,会自动在主机上创建一个 docker0 虚拟网桥,实际上是 Linux 的一个 bridge,可以理解为一个路由。它会在挂载到它的网口之间进行转发。

同时,Docker 随机分配一个本地未占用的私有网段(在 RFC1918 中定义)中的一个地址给 docker0 接口。比如典型的 172.17.42.1,掩码为 255.255.0.0。此后启动的容器内的网口也会自动分配一个同一网段(172.17.0.0/16)的地址。

当创建一个 Docker 容器的时候,同时会创建了一对 veth pair 接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即 eth0;另一端在本地并被挂载到 docker0 网桥,名称以 veth 开头(例如 vethAA)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络。

image-20210729112910173

#我们发现这个容器的网卡,都是一对对的
#veth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
#正是因为这个特性,veth-pair充当一个桥梁,连接各种虚拟设备的。


3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:92:14:4f:79 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:92ff:fe14:4f79/64 scope link 
       valid_lft forever preferred_lft forever
5: vetha8fa520@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 5e:e4:79:1c:d1:5b brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::5ce4:79ff:fe1c:d15b/64 scope link 
       valid_lft forever preferred_lft forever
7: veth4445fe6@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether f6:17:36:b0:c2:b6 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::f417:36ff:feb0:c2b6/64 scope link 
       valid_lft forever preferred_lft forever

查看docker容器启动后的地址

[root@dagongren ~]# docker inspect ng01 
.
.
.
   "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "fcec18903afc7930a8035ea731c485cc04d58a13cbdbed9708586fd0ebc75996",
                    "EndpointID": "333e8954de90e7460ac1f8e43250ebb518e10cc9f3e478cbb92ebd63bde5372a",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,

[root@dagongren ~]#docker exec -it tomcat01 ping 172.16.0.2

容器和容器之间是可以ping通的。

image-20210729114645727

结论:tomcat01和tomcat02是共用一个路由器,docker0

所有 的容器不指定网络的情况下,都是docker0路由的,docker会给我们的容器分配一个默认的可用ip

  • 小结:docker使用时linux桥接,宿主机中是一个docker容器的网桥docker0
  • image-20210729115327074

docker中的所有网络接口都是虚拟的,虚拟的网络转发效率高

只要容器删除,对应的一对网桥就没了

默认的docker0网桥通过ping docker容器的名字 不能ping通,可以使用--link 连通

[root@dagongren ~]# docker run  -d -P  --name ng03 --link ng02 nginx:latest

此时就就可以通过[root@dagongren ~]#docker exec -it ng01 ping ng02  可以通
但是反过来 ng02 ping不通  ng01



例如 ng02 和 ng03
[root@dagongren ~]# docker exec -it ng03 cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3      ng02 362d97f91ad8
172.17.0.5      282c8f3ad3b9

6.1.2、为什么docker有网络

容器与容器之间是互相隔离的,如果想通信,第一种方式是公用一个数据卷,第二种方式是网络去进行访问
1.容器之间互联通信
2.外部宿主机可以访问容器数据

6.2、Docker网络自定义

[root@dagongren ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
fcec18903afc   bridge    bridge    local
870442fabd84   host      host      local
d6c91f88ccec   none      null      local
bridge   桥接  docker默认
host     宿主机共享
none  不配置

docker network 网络相关操作
ls 查看所有网桥
rm 删除某一个网桥
create 创建网桥
prune 删除未使用的网桥

docker启动容器模式使用的是网桥

1.查看docker所有网桥
[root@docker ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
f108dc540f3a   test      bridge    local

2.创建网桥
[root@docker ~]# docker network create test
f108dc540f3ab9d241b0211e2410021b3c6afc6b69e139e6587f817958f5b9d2
[root@docker ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
f108dc540f3a   test      bridge    local
[root@dagongren ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
422aa5d80473e7af47ba7824d8ef0c304110567d3f8f124cdfc092aebd7bdaad
[root@dagongren ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
fcec18903afc   bridge    bridge    local
870442fabd84   host      host      local
422aa5d80473   mynet     bridge    local
d6c91f88ccec   none      null      local
34313acf3ca5   test      bridge    local
[root@dagongren ~]# docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "422aa5d80473e7af47ba7824d8ef0c304110567d3f8f124cdfc092aebd7bdaad",
        "Created": "2021-07-29T12:24:08.391332249+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.0.0/16",
                    "Gateway": "192.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }


3.如何使用网桥
--network 指定网桥,不能自动创建网桥
[root@docker ~]# docker run -d -P --name nginx02 --network test nginx:latest

4.删除网桥
[root@docker ~]# docker network rm test
test

5.查看网桥详细信息
[root@docker ~]# docker network inspect test
[
    {
        "Name": "test", 网桥名称
        "Id": "c38d9239253e5470ae2748f82e38d370dde714546de74ea1cd4fbbabd93cf7e9",
        ......
                    "Subnet": "172.25.0.0/16" IP网段/掩码
                    "Gateway": "172.25.0.1" 网关

]

6.host模式
容器不会虚拟的网卡,而是直接使用宿主机的ip跟端口号
[root@docker ~]# docker run -d --network host --name nginx04 -P nginx:latest

7.空模式
[root@docker ~]# docker run -d --network none --name nginx05 -P nginx:latest

8.container模式
不对网络进行任何配置,而是和指定的容器共享ip跟端口
[root@docker ~]# docker run -d -p 88:80 -p 6377:6379 --network test --name redis redis:latest
[root@docker ~]# docker run -d --network container:redis --name nginx nginx:latest

6.3、容器连通

#不同网段之间的容器连通使用connect连通
[root@dagongren ~]# docker network connect mynet ng03
[root@dagongren ~]# docker inspect ng03
  "Networks": {
                "bridge": {
                              ...

                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.5",
                    "IPPrefixLen": 16,
                     .......
                },
                "mynet": {
                         ...
                    "Gateway": "192.168.0.1",
                    "IPAddress": "192.168.0.3",
                    "IPPrefixLen": 16,
                .......
#一个容器两个ip地址     将容器加入自定义的mynet虚拟网卡中。

七、数据卷管理

7.1、数据卷特点

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

  • 数据卷 可以在容器之间共享和重用 --name 容器2 --volumes-from (容器1) (镜像)
  • image-20210728155132783
  • 数据卷 的修改会立马生效
  • 数据卷 的更新,不会影响镜像
  • 数据卷 默认会一直存在,即使容器被删除

注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会复制到数据卷中(仅数据卷为空时会复制)。

7.2、数据卷操作

0.数据卷管理命令
docker volume COMMAND
create      创建数据卷
inspect     查看数据卷详细信息
ls          显示所有数据卷
prune       删除所有未使用的数据卷
rm          删除数据卷

1.创建数据卷
[root@docker ~]# docker volume create web001
web001
[root@docker ~]# docker volume ls | grep web001
local     web001

2.查看数据卷
[root@docker ~]# docker volume inspect web001
[
    {
        "CreatedAt": "2021-06-01T14:18:17+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/web001/_data",
        "Name": "web001",
        "Options": {},
        "Scope": "local"
    }
]

3.挂载数据卷
[root@docker ~]# docker run -d -P -v web001:/usr/share/nginx/html --name nginx10 nginx:latest 
90a87cf3afd67d9565b58cba7ea2814eb650128e9951d8ed30295b0ae353dd37
[root@docker ~]# docker inspect nginx10 | grep volumes
                "Source": "/var/lib/docker/volumes/web001/_data",

4.删除数据卷
[root@docker ~]# docker volume rm web001 
web001
[root@docker ~]# docker volume ls | grep web001

八、Docker部署常见服务

8.1、部署MySQL

0.Docker Hub 搜索Mysql镜像

1.拉取mysql镜像到本地
[root@docker ~]# docker pull mysql::5.6.51
	
2.运行mysql服务
[root@docker ~]# docker run -d -P --name mysql01 mysql:5.6.51 
7c8760d433ce37156cb897cc56fc47fd4bfa7bdc4aedcba880f58065ac0e4ed1
[root@docker ~]# docker ps 
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
[root@docker ~]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS                     PORTS     NAMES
7c8760d433ce   mysql:5.6.51   "docker-entrypoint.s…"   5 seconds ago   Exited (1) 3 seconds ago             mysql01

# mysql容器无法正常运行已经退出啦,查看日志排查问题
[root@docker ~]# docker logs -f mysql01
2021-06-01 05:35:10+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified
    You need to specify one of the following:
    - MYSQL_ROOT_PASSWORD
    - MYSQL_ALLOW_EMPTY_PASSWORD
    - MYSQL_RANDOM_ROOT_PASSWORD
    
# 提示没有初始化,没有指定密码,我们添加设置密码变量

5.使用环境配置参数
[root@docker ~]# docker run -d -P --name mysql02 -e MYSQL_ROOT_PASSWORD=123 mysql:5.6.51 
b909416e6eb0a2de21579cd6ec81c1f1e2d3240a0aef382d013aea4e8b8611dd
d[root@docker ~]# docker ps 
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                                         NAMES
b909416e6eb0   mysql:5.6.51   "docker-entrypoint.s…"   6 seconds ago   Up 5 seconds   0.0.0.0:49154->3306/tcp, :::49154->3306/tcp   mysql02

MYSQL_ROOT_PASSWORD 给root账号设置密码

6.将容器数据位置与宿主机位置挂载持久化数据
[root@docker ~]# docker run -d -P --name mysql03 -v mysqldata:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.6.51
[root@docker ~]# docker inspect mysql03 | grep volumes
                "Source": "/var/lib/docker/volumes/mysqldata/_data",

7.将mysql数据库备份为sql文件
docker exec mysql03 sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > /root/all-databases.sql  --导出全部数据
docker exec mysql03 sh -c 'exec mysqldump --databases 库表 -uroot -p"$MYSQL_ROOT_PASSWORD"' > /root/all-databases.sql  --导出指定库数据
docker exec mysql03 sh -c 'exec mysqldump --no-data --databases 库表 -uroot -p"$MYSQL_ROOT_PASSWORD"' > /root/all-databases.sql  --导出指定库数据不要数据

9.执行sql文件到mysql中
docker exec -i mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"' < /root/xxx.sql

8.2、部署Tomcat

0.Docker Hub 搜索Tomcat镜像

1.下载tomcat镜像
[root@docker ~]# docker pull tomcat:8.0-jre8

2.运行tomcat镜像
[root@docker ~]# docker run -d -P --name tomcat01 tomcat:8.0-jre8

3.将webapps目录挂载在外部
[root@docker ~]# docker run -d -P -v webapps:/usr/local/tomcat/webapps --name tomcat02 tomcat:8.0-jre8
[root@docker ~]# docker inspect tomcat02 | grep volumes
                "Source": "/var/lib/docker/volumes/webapps/_data",

8.3、部署Redis

0.Docker Hub 搜索Redis镜像

1.拉取redis镜像到本地
[root@docker ~]# docker pull redis:5.0.12

2.启动redis服务运行容器
[root@docker ~]# docker run -d -P --name redis01 redis:5.0.12 (暴露外部宿主机端口为6379进行连接) 

3.将数据目录挂在到本地保证数据安全
[root@docker ~]# docker run -d -P --name redis02 redis:5.0.12 --appendonly yes 开启aof持久化
[root@docker ~]# docker inspect redis02 | grep volumes
                "Source": "/var/lib/docker/volumes/3892899dbe4d0ed85499084f0df6e2a9a50fe748d8a4d34dc99bd988d0788d47/_data",
[root@docker _data]# pwd
/var/lib/docker/volumes/3892899dbe4d0ed85499084f0df6e2a9a50fe748d8a4d34dc99bd988d0788d47/_data
[root@docker _data]# ls
appendonly.aof

8.4、部署Nginx

0.Docker Hub 搜索Nginx镜像

1.拉取nginx镜像到本地
[root@docker ~]# docker pull nginx

2.启动nginx容器
[root@docker ~]# docker run -d -P --name nginx01 nginx:latest

3.进入容器
[root@docker ~]# docker exec -it nginx01 /bin/bash
find / -name html -type d
html文件:  /usr/share/nginx/html

4.挂载nginxhtml到宿主机外部
[root@docker ~]# docker run -d -P --name nginx02 -v html:/usr/share/nginx/html nginx:latest	
[root@docker ~]# docker inspect nginx02 | grep volumes
                "Source": "/var/lib/docker/volumes/html/_data",

九、Dockerfile

9.1、什么是Dockerfile

Docker镜像的描述文件,是由一系列命令和参数构成的脚本。主要作用是用来构建docker镜像的构建文件,Dockerfile 为文件名,指令必须大写

9.2、执行过程

Dockerfile 是一个文本文件,其内包含了一条条的指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

我们使用了 docker build 命令进行镜像构建。其格式为:

docker build [选项] <上下文路径->

Dockerfile 就在当前目录,因此不少初学者以为这个路径是在指定 Dockerfile 所在路径,这么理解其实是不准确的。如果对应上面的命令格式,你可能会发现,这是在指定 上下文路径。那么什么是上下文呢?当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

Docker 在运行时分为 Docker引擎(服务端守护进程) 以及 客户端工具,

我们日常使用各种 docker 命令,其实就是在使用客户端工具与 Docker 引擎 进行交互。
那么当我们使用 docker build 命令来构建镜像时,这个构建过程其实是在 Docker引擎 中完成的,而不是在本机环境。
那么如果在 Dockerfile 中使用了一些 COPY 等指令来操作文件,如何让 Docker引擎 获取到这些文件呢?
这里就有了一个 镜像构建上下文 的概念,当构建的时候,由用户指定构建镜像的上下文路径,而 docker build 会将这个路径下所有的文件都打包上传给 Docker 引擎,引擎内将这些内容展开后,就能获取到所有指定上下文中的文件了。
比如说 dockerfile 中的 COPY ./package.json /project,其实拷贝的并不是本机目录下的 package.json 文件,而是 docker引擎 中展开的构建上下文中的文件,所以如果拷贝的文件超出了构建上下文的范围,Docker引擎 是找不到那些文件的。
所以 docker build 最后的 . 号,其实是在指定镜像构建过程中的上下文环境的目录。




注意事项

有人可能会觉得上下文路径就是Dockerfile所在的路径,并非如此。
默认情况下建议将Dockerfile和所需的各种文件以及包放在同一个目录下,理所当然此时所构建的上下文路径就是指Dockerfile所在的路径。但是并不是一定要这样做,-f 参数可以指定Dockerfile的所在路径以及名字,但是大多时候都是使用默认的名字即可。

使用时注意,不要将Dockerfile置于根目录下进行编译,你会发现整个镜像会将你的磁盘进行打包!!!
最佳实践是建一个空目录,创建Dockerfile,并将你要用的文件和所有包置于该目录下。



如果dockerfile文件在/usr/local/test目录下,有个1.txt文件在/usr/local/test/123/abc目录下。

9.3、Dockerfile相关命令

指令 作用
FROM 指定基础镜像
RUN 执行命令,构建镜像时
EXPOSE 声明端口
WORKDIR 指定工作目录
ENV 设置环境变量
ADD 更高级的复制文件
COPY 复制文件
VOLUME 定义匿名卷
CMD 容器启动命令
ENTRYPOINT 入口点

9.3.1、FROM命令

  • 镜像的基础,基础镜像是必须指定的。而 FROM 就是指定 基础镜像,因此一个 DockerfileFROM 是必备的指令,并且必须是第一条指令。

  • 语法

    FROM <image> 模式拉取latest
    FROM <image>:<tag>
    FROM centos:7
    

9.3.2、RUN命令

  • RUN指令将在当前层级之上的新层中执行RUN后面的命令。生成的提交映像将用于Dockerfile中的下一步

  • 语法

    #shell 格式:
    RUN <命令>
    RUN yum install -y vim
    
    #exec 格式:
    RUN ["可执行文件", "参数1", "参数2"]
    RUN ["yum","install","-y","vim"]
    

9.3.3、EXPOSE命令

  • EXPOSE 指令是声明容器运行时提供服务的端口,这只是一个声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

  • 语法

    EXPOSE <端口1> [<端口2>...]
    EXPOSE 80 8080 22
    

9.3.4、WORKDIR命令

  • 使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录

  • 语法

    WORKDIR <工作目录路径>
    WORKDIR /app
    
    #如果你的 WORKDIR 指令使用的相对路径,那么所切换的路径与之前的 WORKDIR 有关:
    WORKDIR /a
    WORKDIR b
    WORKDIR c
    RUN pwd
    #/a/b/c
    

9.3.5、ENV命令

  • 设置环境变量而已,无论是后面的其它指令,如 RUN,还是运行时的应用,都可以直接使用这里定义的环境变量
  • 语法
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

ENV NGX_VER 1.20.0
RUN echo $NGX_VER
#1.20.0

在这里先定义了环境变量 NGX_VER,其后的 RUN 这层里,使用 $NGX_VER 来进行查看

9.3.6、ADD命令

  • <源路径> 可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去。不推荐使用。如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径>

  • 语法

    ADD [--chown=<user>:<group>] <src>... <dest>
    ADD Redhat ./
    ADD http://nginx.org/download/nginx-1.20.1.tar.gz /usr/local
    

9.3.7、COPY命令

  • COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置

  • 语法

    COPY [--chown=<user>:<group>] <源路径>... <目标路径>
    COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
    COPY index.html /usr/share/nginx/html
    

9.3.8、VOLUME命令

  • 容器运行时自动挂载为匿名卷,为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。

  • 语法

    VOLUME ["<路径1>", "<路径2>"...]
    VOLUME <路径>
    VOLUME /data
    
    这里的 /data 目录就会在容器运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行容器时可以覆盖这个挂载设置。比如:
    docker run -d -v mydata:/data xxxx
    在这行命令中,就使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置。
    

9.3.9、CMD命令

  • Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器主进程的启动命令的。

  • 语法

    shell格式:CMD <命令>
    exec格式:CMD ["可执行文件", "参数1", "参数2"...]
    参数列表格式:CMD ["参数1", "参数2"...] 在指定了ENTRYPOINT`指令后,用CMD指定具体的参数。
    
    CMD echo $HOME
    CMD ["nginx", "-g", "daemon off;"]
    

9.3.10、ENTRYPOINT命令

  • ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。
  • 语法
shell格式:ENTRYPOINT <命令>
exec格式:ENTRYPOINT ["可执行文件", "参数1", "参数2"...]

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为:
<ENTRYPOINT> "<CMD>"

说明

假设我们需要下载Nginx镜像,那么可以先用 CMD来实现:

FROM centos:7
RUN yum install -y wget
CMD ["wget","-nv","http://nginx.org/download/nginx-1.20.1.tar.gz"]

docker build -t wget . 来构建镜像的话,如果我们需要下载,只需要执行:

$ docker run wget
2021-06-01 11:57:09 URL:http://nginx.org/download/nginx-1.20.1.tar.gz [1061461/1061461] -> "nginx-1.20.1.tar.gz" [1]

命令总有参数,如果我们希望加参数呢?比如从上面的 CMD 中可以看到实质的命令是 wget,那么如果我们希望保存文件不叫nginx-1.20.1.tar.gz,就需要加上 -O 参数。那么我们可以直接加 -O nginx.tar.gz 参数给 docker run wget

[root@docker wget]# docker run wget -O nginx.tar.gz
docker: Error response from daemon: OCI runtime create failed: container_linux.go:367: starting container process caused: exec: "-O": executable file not found in $PATH: unknown.

我们可以看到可执行文件找不到的报错,executable file not found。之前我们说过,跟在镜像名后面的是 command,运行时会替换 CMD 的默认值。因此这里的 -O nginx.tar.gz 替换了原来的 CMD,而不是添加在原来命令的后面

我们就必须重新完整的输入这个命令:

[root@docker wget]# docker run wget wget -nv http://nginx.org/download/nginx-1.20.1.tar.gz -O nginx.tar.gz
2021-06-01 12:03:29 URL:http://nginx.org/download/nginx-1.20.1.tar.gz [1061461/1061461] -> "nginx.tar.gz" [1]

使用 ENTRYPOINT 就可以解决这个问题

FROM centos:7
RUN yum install -y wget
CMD [""]
ENTRYPOINT ["wget","-nv","http://nginx.org/download/nginx-1.20.1.tar.gz"]

[root@docker wget]# docker run wget -O nginx.tar.gz
2021-06-01 12:06:36 URL:http://nginx.org/download/nginx-1.20.1.tar.gz [1061461/1061461] -> "nginx.tar.gz" [1]

因为当存在 ENTRYPOINT 后,CMD 的内容将会作为参数传给 ENTRYPOINT,这里 -O nginx.tar.gz 就是新的 CMD,因此会作为参数传给 wget

9.4、Dockerfile构建Nginx

1.编写Dockerfile
FROM centos:7
WORKDIR /nginx
ADD http://nginx.org/download/nginx-1.20.1.tar.gz ./
RUN tar xf nginx-1.20.1.tar.gz
RUN yum install make gcc pcre pcre-devel zlib zlib-devel vim -y \
    && cd nginx-1.20.1 \
    && ./configure \
    && make && make install
EXPOSE 80
VOLUME /usr/share/nginx/html
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]

2.构建镜像
docker build -t nginx:01 .

3.启动镜像为容器
docker run -d -P --name nginx01 nginx:01

十、Docker-Compose

10.1、介绍

docker-compose是官方的产品,对容器的快速编排,定义多容器启动,使用的是yaml语言去定义的
https://docs.docker.com/compose/install/
例:
项目前端依赖于nginx,后端依赖于tomcat跟mysql,如果使用dockerfile需要一个一个run镜像,dockercompose可以一次性启动所有容器,根据我们的依赖去一个一个启动

compose核心概念
服务 service 一个应用容器,各种中间件 mysql,redis,nginx
项目 project 一组相关联的容器组成的一个完整的业务单元(这组容器可以运行支撑我们的项目)
多个服务组成项目,一个服务就是一个容器

10.2、安装

1.下载
[root@test ~]# curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose 官网太慢

国内下载地址
sudo curl -L  https://get.daocloud.io/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose

2.授权执行权限
[root@test ~]# chmod +x /usr/local/bin/docker-compose

3.启动
[root@test ~]# ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
[root@test ~]# docker-compose --version
docker-compose version 1.29.2, build 5becea4c

10.3、入门案例

1.创建docker-compose文件
[root@test ~]# mkdir -v hello
mkdir: created directory ‘hello’
[root@test ~]# cd hello

2.配置文件编写
[root@test hello]# vim docker-compose.yml
version: "3.8"  指定下docker-compose的版本

services:  服务关键字
  java:  服务名称
    image: tomcat:8.0-jre8  这个服务基于那个镜像的
    ports:      端口
      - 8080:8080

3.运行docker-compose
[root@test hello]# docker-compose up

4.测试
[root@test ~]# >/dev/tcp/127.0.0.1/8080
[root@test ~]# echo $?
0
[root@test ~]# eli
elif    elinks
[root@test ~]# elinks http://127.0.0.1:8080 --dump
Apache Tomcat/8.0.53
If you're seeing this, you've successfully installed Tomcat. Congratulations!

5.启动个服务
[root@test hello]# cat docker-compose.yml
version: "3.8"

services:
  java:
    image: tomcat:8.0-jre8
    ports:
      - 8080:8080
  web:
    image: nginx:1.20.1
    ports:
      - 80:80
[root@test hello]# docker-compose up
[root@test ~]# docker ps -a
CONTAINER ID   IMAGE             COMMAND                  CREATED         STATUS         PORTS                                       NAMES
018c46335116   nginx:1.20.1      "/docker-entrypoint.…"   6 minutes ago   Up 6 minutes   0.0.0.0:80->80/tcp, :::80->80/tcp           hello_web_1
c226e10545a0   tomcat:8.0-jre8   "catalina.sh run"        6 minutes ago   Up 6 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   hello_java_1
[root@test ~]# >/dev/tcp/127.0.0.1/8080
[root@test ~]# >/dev/tcp/127.0.0.1/80

10.4、Docker-compose命令

10.4.1、image

指定服务所需要的镜像,如果镜像本地不存在会拉取
image: nginx:1.20.1

10.4.2、ports

暴露的端口,只写端口,表示分配随主机随机端口到容器,端口号小于60应该放到引号中,因为yaml会自动解析为60进制
host:container
ports:
  - "3306"
  - "80:80"

10.4.3、volumes

数据卷挂载的路径

volumes:
  - /root/apps:/usr/local/tomcat/webapps #自定义映射
  - tomcatdir:/usr/local/tomcat/webapps #自动创建,必须声明
  
声明指定的卷名
volumes: 
  tomcatdir:
    external:
      true

十一、docker swarm

一、什么是Docker Swarm

img

  Swarm是Docker公司推出的用来管理docker集群的平台,几乎全部用GO语言来完成的开发的,代码开源在https://github.com/docker/swarm, 它是将一群Docker宿主机变成一个单一的虚拟主机,Swarm使用标准的Docker API接口作为其前端的访问入口,换言之,各种形式的Docker

Client(compose,docker-py等)均可以直接与Swarm通信,甚至Docker本身都可以很容易的与Swarm集成,这大大方便了用户将原本基于单节点的系统移植到Swarm上,同时Swarm内置了对Docker网络插件的支持,用户也很容易的部署跨主机的容器集群服务。

  Docker Swarm 和 Docker Compose 一样,都是 Docker 官方容器编排项目,但不同的是,Docker Compose 是一个在单个服务器或主机上创建多个容器的工具,而 Docker Swarm 则可以在多个服务器或主机上创建容器集群服务,对于微服务的部署,显然 Docker Swarm 会更加适合。

从 Docker 1.12.0 版本开始,Docker Swarm 已经包含在 Docker 引擎中(docker swarm),并且已经内置了服务发现工具,我们就不需要像之前一样,再配置 Etcd 或者 Consul 来进行服务发现配置了。

  Swarm deamon只是一个调度器(Scheduler)加路由器(router),Swarm自己不运行容器,它只是接受Docker客户端发来的请求,调度适合的节点来运行容器,这就意味着,即使Swarm由于某些原因挂掉了,集群中的节点也会照常运行,放Swarm重新恢复运行之后,他会收集重建集群信息。

二、Docker Swarm 基本结构图

img

在结构图可以看出 Docker Client使用Swarm对 集群(Cluster)进行调度使用。

上图可以看出,Swarm是典型的master-slave结构,通过发现服务来选举manager。manager是中心管理节点,各个node上运行agent接受manager的统一管理,集群会自动通过Raft协议分布式选举出manager节点,无需额外的发现服务支持,避免了单点的瓶颈问题,同时也内置了DNS的负载均衡和对外部负载均衡机制的集成支持

回到顶部

三.Swarm的几个关键概念

1.Swarm``集群的管理和编排是使用嵌入docker引擎的SwarmKit,可以在docker初始化时启动swarm模式或者加入已存在的swarm` `2.Node``一个节点是docker引擎集群的一个实例。您还可以将其视为Docker节点。您可以在单个物理计算机或云服务器上运行一个或多个节点,但生产群集部署通常包括分布在多个物理和云计算机上的Docker节点。``要将应用程序部署到swarm,请将服务定义提交给 管理器节点。管理器节点将称为任务的工作单元分派 给工作节点。``Manager节点还执行维护所需群集状态所需的编排和集群管理功能。Manager节点选择单个领导者来执行编排任务。``工作节点接收并执行从管理器节点分派的任务。默认情况下,管理器节点还将服务作为工作节点运行,但您可以将它们配置为仅运行管理器任务并且是仅管理器节点。代理程序在每个工作程序节点上运行,并报告分配给它的任务。工作节点向管理器节点通知其分配的任务的当前状态,以便管理器可以维持每个工作者的期望状态。` `3.Service``一个服务是任务的定义,管理机或工作节点上执行。它是群体系统的中心结构,是用户与群体交互的主要根源。创建服务时,你需要指定要使用的容器镜像。` `4.Task``任务是在docekr容器中执行的命令,Manager节点根据指定数量的任务副本分配任务给worker节点` `------------------------------------------使用方法-------------------------------------``docker swarm:集群管理,子命令有init, ``join``, leave, update。(docker swarm --help查看帮助)``docker service:服务创建,子命令有create, inspect, update, remove, tasks。(docker service--help查看帮助)``docker node:节点管理,子命令有accept, promote, demote, inspect, update, tasks, ``ls``, ``rm``。(docker node --help查看帮助)`` ` `node是加入到swarm集群中的一个docker引擎实体,可以在一台物理机上运行多个node,node分为:``manager nodes,也就是管理节点``worker nodes,也就是工作节点` `1)manager node管理节点:执行集群的管理功能,维护集群的状态,选举一个leader节点去执行调度任务。``2)worker node工作节点:接收和执行任务。参与容器集群负载调度,仅用于承载task。``3)service服务:一个服务是工作节点上执行任务的定义。创建一个服务,指定了容器所使用的镜像和容器运行的命令。``  ``service是运行在worker nodes上的task的描述,service的描述包括使用哪个docker 镜像,以及在使用该镜像的容器中执行什么命令。``4)task任务:一个任务包含了一个容器及其运行的命令。task是service的执行实体,task启动docker容器并在容器中执行任务。

回到顶部

四、Swarm的工作模式

\1. Node

img

\2. Service

img

\3. 任务与调度

img

\4. 服务副本与全局服务

img

回到顶部

五、Swarm的调度策略

Swarm在调度(scheduler)节点(leader节点)运行容器的时候,会根据指定的策略来计算最适合运行容器的节点,目前支持的策略有:spread, binpack, random.``1)Random``顾名思义,就是随机选择一个Node来运行容器,一般用作调试用,spread和binpack策略会根据各个节点的可用的CPU, RAM以及正在运``行的容器的数量来计算应该运行容器的节点。`` ` `2)Spread``在同等条件下,Spread策略会选择运行容器最少的那台节点来运行新的容器,binpack策略会选择运行容器最集中的那台机器来运行新的节点。``使用Spread策略会使得容器会均衡的分布在集群中的各个节点上运行,一旦一个节点挂掉了只会损失少部分的容器。`` ` `3)Binpack``Binpack策略最大化的避免容器碎片化,就是说binpack策略尽可能的把还未使用的节点留给需要更大空间的容器运行,尽可能的把容器运行在``一个节点上面。

回到顶部

六、Swarm Cluster模式特性

1)批量创建服务``建立容器之前先创建一个overlay的网络,用来保证在不同主机上的容器网络互通的网络模式`` ` `2)强大的集群的容错性``当容器副本中的其中某一个或某几个节点宕机后,cluster会根据自己的服务注册发现机制,以及之前设定的值--replicas n,``在集群中剩余的空闲节点上,重新拉起容器副本。整个副本迁移的过程无需人工干预,迁移后原本的集群的load balance依旧好使!``不难看出,docker service其实不仅仅是批量启动服务这么简单,而是在集群中定义了一种状态。Cluster会持续检测服务的健康状态``并维护集群的高可用性。`` ` `3)服务节点的可扩展性``Swarm Cluster不光只是提供了优秀的高可用性,同时也提供了节点弹性扩展或缩减的功能。当容器组想动态扩展时,只需通过scale``参数即可复制出新的副本出来。`` ` `仔细观察的话,可以发现所有扩展出来的容器副本都run在原先的节点下面,如果有需求想在每台节点上都run一个相同的副本,方法``其实很简单,只需要在命令中将``"--replicas n"``更换成``"--mode=global"``即可!`` ` `复制服务(--replicas n)``将一系列复制任务分发至各节点当中,具体取决于您所需要的设置状态,例如“--replicas 3”。`` ` `全局服务(--mode=global)``适用于集群内全部可用节点上的服务任务,例如“--mode global”。如果大家在 Swarm 集群中设有 7 台 Docker 节点,则全部节点之上都将存在对应容器。`` ` `4. 调度机制``所谓的调度其主要功能是cluster的server端去选择在哪个服务器节点上创建并启动一个容器实例的动作。它是由一个装箱算法和过滤器``组合而成。每次通过过滤器(constraint)启动容器的时候,swarm cluster 都会调用调度机制筛选出匹配约束条件的服务器,并在这上面运行容器。`` ` `------------------Swarm cluster的创建过程包含以下三个步骤----------------------``1)发现Docker集群中的各个节点,收集节点状态、角色信息,并监视节点状态的变化``2)初始化内部调度(scheduler)模块``3)创建并启动API监听服务模块`` ` `一旦创建好这个cluster,就可以用命令docker service批量对集群内的容器进行操作,非常方便!`` ` `在启动容器后,docker 会根据当前每个swarm节点的负载判断,在负载最优的节点运行这个task任务,用``"docker service ls"` `和``"docker service ps + taskID"``可以看到任务运行在哪个节点上。容器启动后,有时需要等待一段时间才能完成容器创建。

回到顶部

七、Dcoker Swarm 集群部署

温馨提示:

机器环境(三台机器,centos系统)

IP:192.168.31.43 主机名:manager43 担任角色:swarm manager

IP:192.168.31.188 主机名:node188 担任角色:swarm node

IP:192.168.31.139 主机名:node139 担任角色:swarm node

1、准备工作

1) 修改主机名
# 192.168.31.43  主机上执行
[root@manager43 ~]# hostnamectl set-hostname manager43
 
# 192.168.31.188 主机上执行
[root@node188 ~]# hostnamectl set-hostname node188
 
# 192.168.31.139 主机上执行
[root@node139 ~]# hostnamectl set-hostname node139
 
2)配置hosts文件(可配置可不配置)
[root@manager43 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
 
192.168.31.43 manager43
192.168.31.188 node188
192.168.31.139 node139
 
# 使用scp复制到node主机
[root@manager43 ~]# scp /etc/hosts root@192.168.31.188:/etc/hosts
[root@manager43 ~]# scp /etc/hosts root@192.168.31.139:/etc/hosts
 
3) 设置防火墙
关闭三台机器上的防火墙。如果开启防火墙,则需要在所有节点的防火墙上依次放行2377/tcp(管理端口)、7946/udp(节点间通信端口)、4789/udp(overlay 网络端口)端口。
[root@manager43 ~]# systemctl disable firewalld.service
[root@manager43 ~]# systemctl stop firewalld.service
 
4) 安装docker并配置加速器(在三台主机都要安装哟...)
[root@manager43 ~]# yum -y install docker
[root@node188 ~]# yum -y install docker
[root@node139 ~]# yum -y install docker

也可以安装最新版docker,可查考:docker安装教程

加速器配置,可查考:docker加速器配置教程

2、创建Swarm并添加节点

1) 创建Swarm集群
[root@manager43 ~]# docker swarm init --advertise-addr 192.168.31.43
Swarm initialized: current node (z2n633mty5py7u9wyl423qnq0) is now a manager.
 
To add a worker to this swarm, run the following command:
 
    # 这就是添加节点的方式(要保存初始化后token,因为在节点加入时要使用token作为通讯的密钥)
    docker swarm join --token SWMTKN-1-2lefzq18zohy9yr1vskutf1sfb2a590xz9d0mjj2m15zu9eprw-2938j5f50t35ycut0vbj2sx0s 192.168.31.43:2377  
 
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
 #获取令牌
[root@manager43 ~]#docker swarm join --token worker    worker节点的令牌
[root@manager43 ~]#docker swarm join-token manager      manager节点的令牌
 
上面命令执行后,该机器自动加入到swarm集群。这个会创建一个集群token,获取全球唯一的 token,作为集群唯一标识。后续将其他节点加入集群都会用到这个token值。
其中,--advertise-addr参数表示其它swarm中的worker节点使用此ip地址与manager联系。命令的输出包含了其它节点如何加入集群的命令。
 
这里无意中遇到了一个小小的问题:
# 在次执行上面的命令,回报下面的错误
[root@manager43 ~]# docker swarm init --advertise-addr 192.168.31.43
Error response from daemon: This node is already part of a swarm. Use "docker swarm leave" to leave this swarm and join another one.
# 解决方法
[root@manager43 ~]# docker swarm leave -f
这里的leave就是在集群中删除节点,-f参数强制删除,执行完在重新执行OK
 
2) 查看集群的相关信息
[root@manager43 ~]# docker info
上面的命令执行后 找到Swarm的关键字,就可以看到相关信息了
 
[root@manager43 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
3jcmnzjh0e99ipgshk1ykuovd *   manager43           Ready               Active              Leader              18.06.0-ce
上面的命令是查看集群中的机器(注意上面node ID旁边那个*号表示现在连接到这个节点上)
 
3) 添加节点主机到Swarm集群
上面我们在创建Swarm集群的时候就已经给出了添加节点的方法
 
# 192.168.31.188 主机上执行
[root@node188 ~]# docker swarm join --token SWMTKN-1-2lefzq18zohy9yr1vskutf1sfb2a590xz9d0mjj2m15zu9eprw-2938j5f50t35ycut0vbj2sx0s 192.168.31.43:2377
This node joined a swarm as a worker.
 
# 192.168.31.139 主机上执行
[root@node139 ~]# docker swarm join --token SWMTKN-1-2lefzq18zohy9yr1vskutf1sfb2a590xz9d0mjj2m15zu9eprw-2938j5f50t35ycut0vbj2sx0s 192.168.31.43:2377
This node joined a swarm as a worker.
 
如果想要将其他更多的节点添加到这个swarm集群中,添加方法如上一致
 
# 集群:至少要三个管理节点,并且保证至少两个管理节点(主节点)存活,否则只有一个管理节点存活的话,不再工作。
 
在manager43主机上我们可以看一下集群中的机器及状态
[root@manager43 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
3jcmnzjh0e99ipgshk1ykuovd *   manager43           Ready               Active              Leader              18.06.0-ce
vww7ue2xprzg46bjx7afo4h04     node139             Ready               Active                                  18.06.1-ce
c5klw5ns4adcvumzgiv66xpyj     node188             Ready               Active                                  18.06.1-ce
 
--------------------------------------------------------------------------------------------------------------------
温馨提示:更改节点的availablity状态
swarm集群中node的availability状态可以为 active或者drain,其中:
active状态下,node可以接受来自manager节点的任务分派;
drain状态下,node节点会结束task,且不再接受来自manager节点的任务分派(也就是下线节点)
[root@manager43 ~]# docker node update --availability drain node139               # 将node139节点下线。如果要删除node139节点,命令是"docker node rm --force node139"
node139
[root@manager43 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
3jcmnzjh0e99ipgshk1ykuovd *   manager43           Ready               Active              Leader              18.06.0-ce
vww7ue2xprzg46bjx7afo4h04     node139             Ready               Drain                                   18.06.1-ce
c5klw5ns4adcvumzgiv66xpyj     node188             Ready               Active                                  18.06.1-ce
 
如上,当node1的状态改为drain后,那么该节点就不会接受task任务分发,就算之前已经接受的任务也会转移到别的节点上。
再次修改为active状态(及将下线的节点再次上线)
[root@manager43 ~]# docker node update --availability active node139
node139
[root@manager43 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
3jcmnzjh0e99ipgshk1ykuovd *   manager43           Ready               Active              Leader              18.06.0-ce
vww7ue2xprzg46bjx7afo4h04     node139             Ready               Active                                  18.06.1-ce
c5klw5ns4adcvumzgiv66xpyj     node188             Ready               Active                                  18.06.1-ce

3、在Swarm中部署服务(nginx为例)

Docker 1.12版本提供服务的Scaling、health check、滚动升级等功能,并提供了内置的dns、vip机制,实现service的服务发现和负载均衡能力
1) 创建网络在部署服务
# 创建网络
[root@manager43 ~]# docker network create -d overlay nginx_net
a52jy33asc5o0ts0rq823bf0m
[root@manager43 ~]# docker network ls | grep nginx_net
a52jy33asc5o        nginx_net           overlay             swarm
 
# 部署服务
[root@manager43 ~]# docker service create --replicas 1 --network nginx_net --name my_nginx -p 80:80 nginx    # 就创建了一个具有一个副本(--replicas 1 )的nginx服务,使用镜像nginx
olexfmtdf94sxyeetkchwhehg
overall progress: 1 out of 1 tasks
1/1: running   [==================================================>]
verify: Service converged
在manager-node节点上使用上面这个覆盖网络创建nginx服务:
其中,--replicas 参数指定服务由几个实例组成。
注意:不需要提前在节点上下载nginx镜像,这个命令执行后会自动下载这个容器镜像(比如此处创建tomcat容器,就将下面命令中的镜像改为tomcat镜像)。
 
# 使用 docker service ls 查看正在运行服务的列表
[root@manager43 ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
olexfmtdf94s        my_nginx            replicated          1/1                 nginx:latest        *:80->80/tcp
 
2) 查询Swarm中服务的信息
-pretty 使命令输出格式化为可读的格式,不加 --pretty 可以输出更详细的信息:
[root@manager43 ~]# docker service inspect --pretty my_nginx
ID:             zs7fw4ereo5w7ohd4n9ii06nt
Name:           my_nginx
Service Mode:   Replicated
 Replicas:      1
Placement:
UpdateConfig:
 Parallelism:   1
 On failure:    pause
 Monitoring Period: 5s
 Max failure ratio: 0
 Update order:      stop-first
RollbackConfig:
 Parallelism:   1
 On failure:    pause
 Monitoring Period: 5s
 Max failure ratio: 0
 Rollback order:    stop-first
ContainerSpec:
 Image:         nginx:latest@sha256:b73f527d86e3461fd652f62cf47e7b375196063bbbd503e853af5be16597cb2e
 Init:          false
Resources:
Networks: nginx_net
Endpoint Mode:  vip
Ports:
 PublishedPort = 80
  Protocol = tcp
  TargetPort = 80
  PublishMode = ingress
 
# 查询到哪个节点正在运行该服务。如下该容器被调度到manager-node节点上启动了,然后访问http://192.168.31.43即可访问这个容器应用(如果调度到其他节点,访问也是如此)
[root@manager43 ~]# docker service ps my_nginx
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE               ERROR               PORTS
yzonph0zu7km        my_nginx.1          nginx:latest        manager43           Running             Running about an hour ago                      
温馨提示:如果上面命令执行后,上面的 STATE 字段中刚开始的服务状态为 Preparing,需要等一会才能变为 Running 状态,其中最费时间的应该是下载镜像的过程
 
有上面命令可知,该服务在manager-node节点上运行。登陆该节点,可以查看到nginx容器在运行中
[root@manager43 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
0dc7103f8030        nginx:latest        "nginx -g 'daemon of…"   About an hour ago   Up About an hour    80/tcp              my_nginx.1.yzonph0zu7km0211uj0ro5brj
 
3) 在Swarm中动态扩展服务(scale)
当然,如果只是通过service启动容器,swarm也算不上什么新鲜东西了。Service还提供了复制(类似kubernetes里的副本)功能。可以通过 docker service scale 命令来设置服务中容器的副本数
比如将上面的my_nginx容器动态扩展到4个
[root@manager43 ~]# docker service scale my_nginx=4
#相当于:[root@manager43 ~]# docker service update --replicas 4 my_nginx

my_nginx scaled to 4
overall progress: 4 out of 4 tasks
1/4: running   [==================================================>]
2/4: running   [==================================================>]
3/4: running   [==================================================>]
4/4: running   [==================================================>]
verify: Service converged
 
和创建服务一样,增加scale数之后,将会创建新的容器,这些新启动的容器也会经历从准备到运行的过程,过一分钟左右,服务应该就会启动完成,这时候可以再来看一下 nginx 服务中的容器
[root@manager43 ~]# docker service ps my_nginx
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE               ERROR               PORTS
yzonph0zu7km        my_nginx.1          nginx:latest        manager43           Running             Running about an hour ago                      
mlprstt9ds5x        my_nginx.2          nginx:latest        node139             Running             Running 52 seconds ago                         
y09lk90tdzdp        my_nginx.3          nginx:latest        node139             Running             Running 52 seconds ago                         
clolfl3zlvj0        my_nginx.4          nginx:latest        node188             Running             Running 2 minutes ago  
 
可以看到,之前my_nginx容器只在manager-node节点上有一个实例,而现在又增加了3个实例。
这4个副本的my_nginx容器分别运行在这三个节点上,登陆这三个节点,就会发现已经存在运行着的my_nginx容器
 
 
4) 模拟宕机node节点
特别需要清楚的一点:
如果一个节点宕机了(即该节点就会从swarm集群中被踢出),则Docker应该会将在该节点运行的容器,调度到其他节点,以满足指定数量的副本保持运行状态。
    
比如:
将node139宕机后或将node139的docker服务关闭,那么它上面的task实例就会转移到别的节点上。当node139节点恢复后,它转移出去的task实例不会主动转移回来,
只能等别的节点出现故障后转移task实例到它的上面。使用命令"docker node ls",发现node139节点已不在swarm集群中了(状态为:Down)。
[root@node139 ~]# systemctl stop docker
[root@manager43 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ppk7q0bjond8a58xja7in1qid *   manager43           Ready               Active              Leader              18.06.0-ce
mums8azgbrffnecp3q8fz70pl     node139             Down                Active                                  18.06.1-ce
z3n36maf03yjg7odghikuv574     node188             Ready               Active                                  18.06.1-ce
    
然后过一会查询服务的状态列表
[root@manager43 ~]# docker service ps my_nginx
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
yzonph0zu7km        my_nginx.1          nginx:latest        manager43           Running             Running about an hour ago                       
wb1cpk9k22rl        my_nginx.2          nginx:latest        node188             Running             Running about a minute ago                      
mlprstt9ds5x         \_ my_nginx.2      nginx:latest        node139             Shutdown            Running 4 minutes ago                           
rhbj4bcr4t2c        my_nginx.3          nginx:latest        manager43           Running             Running about a minute ago                      
y09lk90tdzdp         \_ my_nginx.3      nginx:latest        node139             Shutdown            Running 4 minutes ago                           
clolfl3zlvj0        my_nginx.4          nginx:latest        node188             Running             Running 6 minutes ago
 
上面我们可以发现node139故障后,它上面之前的两个task任务已经转移到node188和manager43节点上了
 
登陆到node188和manager43节点上,可以看到这两个运行的task任务。当访问192.168.31.188和192.168.31.43节点的80端口,swarm的负载均衡会把请求路由到一个任意节点的可用的容器上
[root@manager43 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
ae4c5c2e6f3f        nginx:latest        "nginx -g 'daemon of…"   4 minutes ago       Up 4 minutes        80/tcp              my_nginx.3.rhbj4bcr4t2c3y2f8vyfmbi21
0dc7103f8030        nginx:latest        "nginx -g 'daemon of…"   About an hour ago   Up About an hour    80/tcp              my_nginx.1.yzonph0zu7km0211uj0ro5brj
 
[root@node188 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
a63ef253f7dd        nginx:latest        "nginx -g 'daemon of…"   3 minutes ago       Up 3 minutes        80/tcp              my_nginx.2.wb1cpk9k22rl1ydab7aozl2b5
74a1a1db81d4        nginx:latest        "nginx -g 'daemon of…"   8 minutes ago       Up 8 minutes        80/tcp              my_nginx.4.clolfl3zlvj0ewmh85c2ljnza
 
再次在node188和manager43节点上将从node139上转移过来的两个task关闭
[root@manager43 ~]# docker stop my_nginx.3.rhbj4bcr4t2c3y2f8vyfmbi21
my_nginx.3.rhbj4bcr4t2c3y2f8vyfmbi21
 
[root@node188 ~]# docker stop my_nginx.2.wb1cpk9k22rl1ydab7aozl2b5
my_nginx.2.wb1cpk9k22rl1ydab7aozl2b5
 
再次查询服务的状态列表,发现这两个task又转移到node139上了
[root@manager43 ~]# docker service ps my_nginx
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE             ERROR               PORTS
yzonph0zu7km        my_nginx.1          nginx:latest        manager43           Running             Running 2 hours ago                          
j2q61f8jtzba        my_nginx.2          nginx:latest        node188             Running             Running 24 seconds ago                       
wb1cpk9k22rl         \_ my_nginx.2      nginx:latest        node188             Shutdown            Complete 29 seconds ago                      
mlprstt9ds5x         \_ my_nginx.2      nginx:latest        node139             Shutdown            Running 11 minutes ago                       
oz9wyjuldw1t        my_nginx.3          nginx:latest        manager43           Running             Running 40 seconds ago                       
rhbj4bcr4t2c         \_ my_nginx.3      nginx:latest        manager43           Shutdown            Complete 45 seconds ago                      
y09lk90tdzdp         \_ my_nginx.3      nginx:latest        node139             Shutdown            Running 11 minutes ago                       
clolfl3zlvj0        my_nginx.4          nginx:latest        node188             Running             Running 12 minutes ago    
结论:即在swarm cluster集群中启动的容器,在worker node节点上删除或停用后,该容器会自动转移到其他的worker node节点上
 
 
5) Swarm 动态缩容服务(scale)
同理,swarm还可以缩容,同样是使用scale命令
如下,将my_nginx容器变为1个
[root@manager43 ~]# docker service scale my_nginx=1
my_nginx scaled to 1
overall progress: 1 out of 1 tasks
1/1:  
verify: Service converged
 
[root@manager43 ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
zs7fw4ereo5w        my_nginx            replicated          1/1                 nginx:latest        *:80->80/tcp
 
[root@manager43 ~]# docker service ps my_nginx
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE             ERROR               PORTS
yzonph0zu7km        my_nginx.1          nginx:latest        manager43           Running             Running 11 hours ago                         
wb1cpk9k22rl        my_nginx.2          nginx:latest        node188             Shutdown            Complete 9 hours ago                         
mlprstt9ds5x         \_ my_nginx.2      nginx:latest        node139             Shutdown            Shutdown 29 seconds ago                      
rhbj4bcr4t2c        my_nginx.3          nginx:latest        manager43           Shutdown            Complete 9 hours ago                         
y09lk90tdzdp         \_ my_nginx.3      nginx:latest        node139             Shutdown            Shutdown 29 seconds ago      
 
通过docker service ps my_nginx 可以看到node节点上已经为Shutdown状态了
 
在登录到node节点主机上查看
[root@node188 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
f93c0a27374a        nginx:latest        "nginx -g 'daemon of…"   9 hours ago         Exited (0) 44 seconds ago                       my_nginx.2.j2q61f8jtzba9kb3unupkhl25
a63ef253f7dd        nginx:latest        "nginx -g 'daemon of…"   9 hours ago         Exited (0) 9 hours ago                          my_nginx.2.wb1cpk9k22rl1ydab7aozl2b5
[root@node139 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                   PORTS               NAMES
e8ac2e44f5c4        nginx:latest        "nginx -g 'daemon of…"   9 hours ago         Exited (0) 9 hours ago                       my_nginx.2.mlprstt9ds5xi48u1rzscgfdk
5b031aa5a2cc        nginx:latest        "nginx -g 'daemon of…"   9 hours ago         Exited (0) 9 hours ago                       my_nginx.3.y09lk90tdzdp8cwj6mm5oyr3f
登录node节点,使用docker ps -a 查看,会发现容器被stop而非rm
 
6) 除了上面使用scale进行容器的扩容或缩容之外,还可以使用docker service update 命令。 可对 服务的启动 参数 进行 更新/修改。
[root@manager43 ~]# docker service update --replicas 3 my_nginx
my_nginx
overall progress: 3 out of 3 tasks
1/3: running   [==================================================>]
2/3: running   [==================================================>]
3/3: running   [==================================================>]
verify: Service converged
 
[root@manager43 ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
zs7fw4ereo5w        my_nginx            replicated          3/3                 nginx:latest        *:80->80/tcp
 
[root@manager43 ~]# docker service ps my_nginx
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
yzonph0zu7km        my_nginx.1          nginx:latest        manager43           Running             Running 11 hours ago                        
j3hduzd9pret        my_nginx.2          nginx:latest        node188             Running             Running 18 seconds ago                      
wb1cpk9k22rl         \_ my_nginx.2      nginx:latest        node188             Shutdown            Complete 9 hours ago                        
mlprstt9ds5x         \_ my_nginx.2      nginx:latest        node139             Shutdown            Shutdown 4 minutes ago                      
gng96vc5vqpv        my_nginx.3          nginx:latest        node139             Running             Running 18 seconds ago                      
rhbj4bcr4t2c         \_ my_nginx.3      nginx:latest        manager43           Shutdown            Complete 9 hours ago                        
y09lk90tdzdp         \_ my_nginx.3      nginx:latest        node139             Shutdown            Shutdown 4 minutes ago    
 
docker service update 命令,也可用于直接 升级 镜像等
[root@manager43 ~]# docker service update --image nginx:new my_nginx
 
[root@manager43 ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
zs7fw4ereo5w        my_nginx            replicated          3/3                 nginx:new           *:80->80/tcp
注意IMAGE列 变成了nginx:new
 
 
7) 为了下面的直观显示,我这里把my_nginx服务直接删除了
[root@manager43 ~]# docker service rm my_nginx
 
这样就会把所有节点上的所有容器(task任务实例)全部删除了

4、Swarm中使用Volume(挂在目录,mount命令)

1) 查看volume的帮助信息
[root@manager43 ~]# docker volume --help
 
Usage:  docker volume COMMAND
 
Manage volumes
 
Commands:
  create      Create a volume
  inspect     Display detailed information on one or more volumes
  ls          List volumes
  prune       Remove all unused local volumes
  rm          Remove one or more volumes
 
Run 'docker volume COMMAND --help' for more information on a command.
 
2) 创建一个volume
[root@manager43 ~]# docker volume create --name testvolume
testvolume
 
# 查看创建的volume
[root@manager43 ~]# docker volume ls
DRIVER              VOLUME NAME
local               testvolume
 
# 查看volume详情
[root@manager43 ~]# docker volume inspect testvolume
[
    {
        "CreatedAt": "2018-10-21T10:50:02+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/testvolume/_data",
        "Name": "testvolume",
        "Options": {},
        "Scope": "local"
    }
]
 
 
3) 创建新的服务并挂载testvolume(nginx为例)
[root@manager43 ~]# docker service create --replicas 3 --mount type=volume,src=testvolume,dst=/zjz --name test_nginx nginx
sh7wc8yzcvr0xaedo4tnraj7l
overall progress: 3 out of 3 tasks
1/3: running   [==================================================>]
2/3: running   [==================================================>]
3/3: running   [==================================================>]
verify: Service converged
 
温馨提示:
参数src写成source也可以;dst表示容器内的路径,也可以写成target
 
# 查看创建服务
[root@manager43 ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
sh7wc8yzcvr0        test_nginx          replicated          3/3                 nginx:latest       
[root@manager43 ~]# docker service ps test_nginx
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
m7m41kwt4q6w        test_nginx.1        nginx:latest        node188             Running             Running 56 seconds ago                      
kayh81q1o1kx        test_nginx.2        nginx:latest        node139             Running             Running 56 seconds ago                      
eq11v0rcwy38        test_nginx.3        nginx:latest        manager43           Running             Running 56 seconds ago           
 
# 查看有没有挂载成功(登录各个节点的容器看看有没有指定的目录并创建文件测试)
# 容器中操作
[root@manager43 ~]# docker exec -it 63451219cb4e /bin/bash
root@63451219cb4e:/# cd /zjz/
root@63451219cb4e:/zjz# ls
root@63451219cb4e:/zjz# echo "gen wo xue docker" > docker.txt
root@63451219cb4e:/zjz# ls
docker.txt
 
执行docker volume inspect testvolume 可以看到本地的路径(上面已经执行过了)
本地路径:/var/lib/docker/volumes/testvolume/_data
[root@manager43 ~]# cd /var/lib/docker/volumes/testvolume/_data
[root@manager43 _data]# ls
docker.txt
[root@manager43 _data]# cat docker.txt
gen wo xue docker
 
还可以将node节点机上的volume数据目录做成软链接
[root@manager43 _data]# ln -s /var/lib/docker/volumes/testvolume/_data /zjz
[root@manager43 _data]# cd /zjz/
[root@manager43 zjz]# ls
docker.txt
[root@manager43 zjz]# echo "123" > 1.txt  
[root@manager43 zjz]# ll
总用量 8
-rw-r--r-- 1 root root  4 10月 21 11:04 1.txt
-rw-r--r-- 1 root root 18 10月 21 11:00 docker.txt
 
# 容器中查看
[root@manager43 zjz]# docker exec -it 63451219cb4e /bin/bash
root@63451219cb4e:/# cd /zjz/
root@63451219cb4e:/zjz# ls
1.txt  docker.txt
root@63451219cb4e:/zjz# cat 1.txt
123
root@63451219cb4e:/zjz# cat docker.txt
gen wo xue docker
 
# 还有一种挂载方式简单说一下吧,上面的会了下面的肯定简单
命令格式:
docker service create --mount type=bind,target=/container_data/,source=/host_data/
其中,参数target表示容器里面的路径,source表示本地硬盘路径
 
# 示例创建并挂载并使用网络
[root@manager43 ~]# docker service create --replicas 1 --mount type=bind,target=/usr/share/nginx/html/,source=/opt/web/ --network nginx_net --name zjz_nginx -p 8880:80 nginx

5、多服务Swarm集群部署

问:上面我们只是对单独的一个nginx服务进行的集群部署,那如果要统一编排多个服务呢?
答:docker 三剑客中有个compose 这个就是对单机进行统一编排的,它的实现是通过docker-compose.yml的文件,这里我们就可以结合compose和swarm进行多服务的编排(docker compose教程)

温馨提示:
我们这里要部署的服务有三个(nginx服务,visualizer服务,portainer服务) 都是集群 GUI 管理服务
docker service部署的是单个服务,我们可以使用docker stack进行多服务编排部署
 
1) 编写docker-compose.yml文件
[root@manager43 ~]# mkdir testswarm
[root@manager43 ~]# cd testswarm/
[root@manager43 testswarm]# cat docker-compose.yml
version: "3"
services:
  nginx:
    image: nginx
    ports:
      - 8888:80
    deploy:
      mode: replicated
      replocas: 3
 
  visualizer:
    image: dockersamples/visualizer
    ports:
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]
 
  portainer:
    image: portainer/portainer
    ports:
      - "9000:9000"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]
 
2) 通过这个yml文件部署服务
[root@manager43 testswarm]# docker stack deploy -c docker-compose.yml deploy_deamon
Creating network deploy_deamon_default
Creating service deploy_deamon_portainer
Creating service deploy_deamon_nginx
Creating service deploy_deamon_visualizer
 
通过上面的执行过程可以看出这样创建会默认创建一个网络并使用它,名字都是我们给的名字的前缀加上服务名
 
# 查看创建服务
[root@manager43 testswarm]# docker service ls
ID                  NAME                       MODE                REPLICAS            IMAGE                             PORTS
xj2f1t5ax3nm        deploy_deamon_nginx        replicated          3/3                 nginx:latest                      *:8888->80/tcp
ky9qpldr5abb        deploy_deamon_portainer    replicated          1/1                 portainer/portainer:latest        *:9000->9000/tcp
r47ff177x1ir        deploy_deamon_visualizer   replicated          1/1                 dockersamples/visualizer:latest   *:8080->8080/tcp
 
[root@manager43 testswarm]# docker service ps deploy_deamon_nginx
ID                  NAME                    IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
z3v4uc1ujsnq        deploy_deamon_nginx.1   nginx:latest        node139             Running             Running about a minute ago                      
jhg3ups0cko5        deploy_deamon_nginx.2   nginx:latest        manager43           Running             Running about a minute ago                      
3e6guv791x21        deploy_deamon_nginx.3   nginx:latest        node188             Running             Running about a minute ago        
 
[root@manager43 testswarm]# docker service ps deploy_deamon_portainer
ID                  NAME                        IMAGE                        NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
whyuvy82cvvw        deploy_deamon_portainer.1   portainer/portainer:latest   manager43           Running             Running about a minute ago                      
 
[root@manager43 testswarm]# docker service ps deploy_deamon_visualizer
ID                  NAME                         IMAGE                             NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
wge5w1eqykg3        deploy_deamon_visualizer.1   dockersamples/visualizer:latest   manager43           Running             Starting 7 seconds ago                       

测试

img

img

img

img

img

回到顶部

八、Docker Swarm 容器网络

在Docker版本1.12之后swarm模式原生支持覆盖网络(overlay networks),可以先创建一个覆盖网络,然后启动容器的时候启用这个覆盖网络,
这样只要是这个覆盖网络内的容器,不管在不在同一个宿主机上都能相互通信,即跨主机通信!不同覆盖网络内的容器组之间是相互隔离的(相互ping不通)。
   
swarm模式的覆盖网络包括以下功能:
1)可以附加多个服务到同一个网络。
2)默认情况下,service discovery为每个swarm服务分配一个虚拟IP地址(vip)和DNS名称,使得在同一个网络中容器之间可以使用服务名称为互相连接。
3)可以配置使用DNS轮循而不使用VIP
4)为了可以使用swarm的覆盖网络,在启用swarm模式之间你需要在swarm节点之间开放以下端口:
5)TCP/UDP端口7946 – 用于容器网络发现
6)UDP端口4789 – 用于容器覆盖网络
   
实例如下:
-----------在Swarm集群中创建overlay网络------------
[root@manager-node ~]# docker network create --driver overlay --opt encrypted --subnet 10.10.19.0/24 ngx_net
   
参数解释:
–opt encrypted  默认情况下swarm中的节点通信是加密的。在不同节点的容器之间,可选的–opt encrypted参数能在它们的vxlan流量启用附加的加密层。
--subnet 命令行参数指定overlay网络使用的子网网段。当不指定一个子网时,swarm管理器自动选择一个子网并分配给网络。
   
[root@manager-node ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
d7aa48d3e485        bridge              bridge              local            
9e637a97a3b9        docker_gwbridge     bridge              local            
b5a41c8c71e7        host                host                local            
7f4fx3jf4dbr        ingress             overlay             swarm            
3x2wgugr6zmn        ngx_net             overlay             swarm            
0808a5c72a0a        none                null                local
   
由上可知,Swarm当中拥有2套覆盖网络。其中"ngx_net"网络正是我们在部署容器时所创建的成果。而"ingress"覆盖网络则为默认提供。
#Swarm 管理节点会利用 ingress 负载均衡以将服务公布至集群之外。
在将服务连接到这个创建的网络之前,网络覆盖到manager节点。上面输出的SCOPE为 swarm 表示将服务部署到Swarm时可以使用此网络。
在将服务连接到这个网络后,Swarm只将该网络扩展到特定的worker节点,这个worker节点被swarm调度器分配了运行服务的任务。
在那些没有运行该服务任务的worker节点上,网络并不扩展到该节点。
   
------------------将服务连接到overlay网络-------------------
[root@manager-node ~]# docker service create --replicas 5 --network ngx_net --name my-test -p 80:80 nginx
   
上面名为"my-test"的服务启动了3个task,用于运行每个任务的容器都可以彼此通过overlay网络进行通信。Swarm集群将网络扩展到所有任务处于Running状态的节点上。
[root@manager-node ~]# docker service ls
ID            NAME     REPLICAS  IMAGE  COMMAND
dsaxs6v463g9  my-test  5/5       nginx
   
在manager-node节点上,通过下面的命令查看哪些节点有处于running状态的任务:
[root@manager-node ~]# docker service ps my-test
ID                         NAME       IMAGE  NODE          DESIRED STATE  CURRENT STATE          ERROR
8433fuiy7vpu0p80arl7vggfe  my-test.1  nginx  node2         Running        Running 2 minutes ago
f1h7a0vtojv18zrsiw8j0rzaw  my-test.2  nginx  node1         Running        Running 2 minutes ago
ex73ifk3jvzw8ukurl8yu7fyq  my-test.3  nginx  node1         Running        Running 2 minutes ago
cyu73jd8psupfhken23vvmpud  my-test.4  nginx  manager-node  Running        Running 2 minutes ago
btorxekfix4hcqh4v83dr0tzw  my-test.5  nginx  manager-node  Running        Running 2 minutes ago
   
可见三个节点都有处于running状态的任务,所以my-network网络扩展到三个节点上。
   
可以查询某个节点上关于my-network的详细信息:
[root@manager-node ~]# docker network inspect ngx_net
[
    {
        "Name": "ngx_net",
        "Id": "3x2wgugr6zmn1mcyf9k1du27p",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.10.19.0/24",
                    "Gateway": "10.10.19.1"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "00f47e38deea76269eb03ba13695ec0b0c740601c85019546d6a9a17fd434663": {
                "Name": "my-test.5.btorxekfix4hcqh4v83dr0tzw",
                "EndpointID": "ea962d07eee150b263ae631b8a7f8c1950337c11ef2c3d488a7c3717defd8601",
                "MacAddress": "02:42:0a:0a:13:03",
                "IPv4Address": "10.10.19.3/24",
                "IPv6Address": ""
            },
            "957620c6f7abb44ad8dd2d842d333f5e5c1655034dc43e49abbbd680de3a5341": {
                "Name": "my-test.4.cyu73jd8psupfhken23vvmpud",
                "EndpointID": "f33a6e9ddf1dd01bcfc43ffefd19e19514658f001cdf9b2fbe23bc3fdf56a42a",
                "MacAddress": "02:42:0a:0a:13:07",
                "IPv4Address": "10.10.19.7/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "257"
        },
        "Labels": {}
    }
]
   
从上面的信息可以看出在manager-node节点上,名为my-test的服务有一个名为my-test.5.btorxekfix4hcqh4v83dr0tzw和
my-test.4.cyu73jd8psupfhken23vvmpud的task连接到名为ngx_net的网络上(另外两个节点node1和node2同样可以用上面命令查看)
[root@node1 ~]# docker network inspect ngx_net
.......
        "Containers": {
            "7d9986fad5a7d834676ba76ae75aff2258f840953f1dc633c3ef3c0efd2b2501": {
                "Name": "my-test.3.ex73ifk3jvzw8ukurl8yu7fyq",
                "EndpointID": "957ca19f3d5480762dbd14fd9a6a1cd01a8deac3e8e35b23d1350f480a7b2f37",
                "MacAddress": "02:42:0a:0a:13:06",
                "IPv4Address": "10.10.19.6/24",
                "IPv6Address": ""
            },
            "9e50fceada1d7c653a886ca29d2bf2606debafe8c8a97f2d79104faf3ecf8a46": {
                "Name": "my-test.2.f1h7a0vtojv18zrsiw8j0rzaw",
                "EndpointID": "b1c209c7b68634e88e0bf5e100fe03435b3096054da6555c61e6c207ac651ac2",
                "MacAddress": "02:42:0a:0a:13:05",
                "IPv4Address": "10.10.19.5/24",
                "IPv6Address": ""
            }
        },
.........
  
[root@node2 web]# docker network inspect ngx_net
........
        "Containers": {
            "4bdcce0ee63edc08d943cf4a049eac027719ff2dc14b7c3aa85fdddc5d1da968": {
                "Name": "my-test.1.8433fuiy7vpu0p80arl7vggfe",
                "EndpointID": "df58de85b0a0e4d128bf332fc783f6528d1f179b0f9f3b7aa70ebc832640d3bc",
                "MacAddress": "02:42:0a:0a:13:04",
                "IPv4Address": "10.10.19.4/24",
                "IPv6Address": ""
            }
        },
   
可以通过查询服务来获得服务的虚拟IP地址,如下:
[root@manager-node ~]# docker service inspect --format='{{json .Endpoint.VirtualIPs}}' my-test
[{"NetworkID":"7f4fx3jf4dbrp97aioc05pul4","Addr":"10.255.0.6/16"},{"NetworkID":"3x2wgugr6zmn1mcyf9k1du27p","Addr":"10.10.19.2/24"}]

 img

加入ngx_net网络的容器彼此之间可以通过IP地址通信,也可以通过名称通信。

[root@node2 ~]# docker ps
CONTAINER ID    IMAGE           COMMAND                  CREATED         STATUS             PORTS    NAMES
4bdcce0ee63e    nginx:latest    "nginx -g 'daemon off"   22 minutes ago  Up 22 minutes      80/tcp   my-test.1.8433fuiy7vpu0p80arl7vggfe
  
[root@node2 ~]# docker exec -ti 4bdcce0ee63e /bin/bash
root@4bdcce0ee63e:/# ip addr                                                                                          
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
1786: eth0@if1787: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
    link/ether 02:42:0a:ff:00:08 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.255.0.8/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet 10.255.0.6/32 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:feff:8/64 scope link
       valid_lft forever preferred_lft forever
1788: eth1@if1789: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet 172.18.0.3/16 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe12:3/64 scope link
       valid_lft forever preferred_lft forever
1791: eth2@if1792: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
    link/ether 02:42:0a:0a:13:04 brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet 10.10.19.4/24 scope global eth2
       valid_lft forever preferred_lft forever
    inet 10.10.19.2/32 scope global eth2
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:fe0a:1304/64 scope link
       valid_lft forever preferred_lft forever
  
root@4bdcce0ee63e:/# ping 10.10.19.3
PING 10.10.19.3 (10.10.19.3): 56 data bytes
64 bytes from 10.10.19.3: icmp_seq=0 ttl=64 time=0.890 ms
64 bytes from 10.10.19.3: icmp_seq=1 ttl=64 time=0.622 ms
.....-
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.622/0.756/0.890/0.134 ms
  
root@4bdcce0ee63e:/# ping 10.10.19.6
PING 10.10.19.6 (10.10.19.6): 56 data bytes
64 bytes from 10.10.19.6: icmp_seq=0 ttl=64 time=0.939 ms
64 bytes from 10.10.19.6: icmp_seq=1 ttl=64 time=0.590 ms
  
----------------------------使用swarm模式的服务发现--------------------------
默认情况下,当创建了一个服务并连接到某个网络后,swarm会为该服务分配一个VIP。此VIP根据服务名映射到DNS。在网络上的容器共享该服务的DNS映射,
所以网络上的任意容器可以通过服务名访问服务。
  
在同一overlay网络中,不用通过端口映射来使某个服务可以被其它服务访问。Swarm内部的负载均衡器自动将请求发送到服务的VIP上,然后分发到所有的
active的task上。
  
如下示例:
在同一个网络中添加了一个centos服务,此服务可以通过名称my-test访问前面创建的nginx服务:
[root@manager-node ~]# docker service create --name my-centos --network ngx_net centos       
  
查询centos运行在哪个节点上(上面创建命令执行后,需要一段时间才能完成这个centos服务的创建)
[root@manager-node ~]# docker service ps my-centos
ID                         NAME             IMAGE   NODE   DESIRED STATE  CURRENT STATE            ERROR
e03pqgkjs3l1qizc6v4aqaune  my-centos.1      centos  node2  Running        Preparing 4 seconds ago
  
登录centos运行的节点(由上可知是node2节点),打开centos的交互shell:
[root@node2 ~]# docker ps
CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS            NAMES
e4554490d891        centos:latest            "/bin/bash"             About an hour ago   Up About an hour   my-centos.1.9yk5ie28gwk9mw1h1jovb68ki
  
[root@node2 ~]# docker exec -ti my-centos.1.9yk5ie28gwk9mw1h1jovb68ki /bin/bash
root@4bdcce0ee63e:/# nslookup my-test
Server: 127.0.0.11
Address 1: 127.0.0.11
  
Name: my-test
Address 1: 10.10.19.2 10.10.19.2
  
  
从centos容器内部,使用特殊查询 查询DNS,来找到my-test服务的所有容器的IP地址:
root@4bdcce0ee63e:/# nslookup tasks.my-test
Server: 127.0.0.11
Address 1: 127.0.0.11
  
Name: tasks.my-test
Address 1: 10.10.19.4 my-test.1.8433fuiy7vpu0p80arl7vggfe
Address 2: 10.10.19.5 my-test.2.f1h7a0vtojv18zrsiw8j0rzaw
Address 3: 10.10.19.6 my-test.3.ex73ifk3jvzw8ukurl8yu7fyq
Address 2: 10.10.19.7 my-test.4.cyu73jd8psupfhken23vvmpud
Address 3: 10.10.19.3 my-test.5.btorxekfix4hcqh4v83dr0tzw
  
从centos容器内部,通过wget来访问my-test服务中运行的nginx网页服务器
root@4bdcce0ee63e:/# wget -O- my-test     
Connecting to my-test (10.10.19.2:80)
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
  
Swarm的负载均衡器自动将HTTP请求路由到VIP上,然后到一个active的task容器上。它根据round-robin选择算法将后续的请求分发到另一个active的task上。
  
-----------------------------------为服务使用DNS round-robin-----------------------------
在创建服务时,可以配置服务直接使用DNS round-robin而无需使用VIP。这是通过在创建服务时指定 --endpoint-mode dnsrr 命令行参数实现的。
当你想要使用自己的负载均衡器时可以使用这种方式。
  
如下示例(注意:使用DNS round-robin方式创建服务,不能直接在命令里使用-p指定端口)
[root@manager-node ~]# docker service create --replicas 3 --name my-dnsrr-nginx --network ngx_net --endpoint-mode dnsrr nginx
  
[root@manager-node ~]# docker service ps my-dnsrr-nginx
ID                         NAME              IMAGE  NODE          DESIRED STATE  CURRENT STATE          ERROR
65li2zbhxvvoaesndmwjokouj  my-dnsrr-nginx.1  nginx  node1         Running        Running 2 minutes ago
5hjw7wm4xr877879m0ewjciuj  my-dnsrr-nginx.2  nginx  manager-node  Running        Running 2 minutes ago
afo7acduge2qfy60e87liz557  my-dnsrr-nginx.3  nginx  manager-node  Running        Running 2 minutes ago
  
  
当通过服务名称查询DNS时,DNS服务返回所有任务容器的IP地址:
root@4bdcce0ee63e:/# nslookup my-dnsrr-nginx
Server:    127.0.0.11
Address 1: 127.0.0.11
  
Name:      my-dnsrr-nginx
Address 1: 10.10.19.10 my-dnsrr-nginx.3.0sm1n9o8hygzarv5t5eq46okn.my-network
Address 2: 10.10.19.9  my-dnsrr-nginx.2.b3o1uoa8m003b2kk0ytl9lawh.my-network
Address 3: 10.10.19.8  my-dnsrr-nginx.1.55za4c83jq9846rle6eigiq15.my-network
  
需要注意的是:一定要确认VIP的连通性
通常Docker官方推荐使用dig,nslookup或其它DNS查询工具来查询通过DNS对服务名的访问。因为VIP是逻辑IP,ping并不是确认VIP连通性的正确的工具。

posted on 2022-02-16 16:52  byf123  阅读(529)  评论(0编辑  收藏  举报