[关闭]
@aloxc 2019-11-19T03:54:27.000000Z 字数 31525 阅读 421

docker基础知识

docker


1、基本概念

镜像(image):就是一个只读的模板,类似于打包好的操作系统iso文件

容器(container):使用镜像启动的系统,类似我们安装好的win7

仓库(Repository):就是保存镜像的地方,官方仓库hub.docker.com

注册器(Registry):就是创建仓库的,分为公有和私有

一个Registry有多个仓库,一个仓库有若干个镜像,可以使用一个镜像创建若干个容器。

2、基本操作

2.1、安装docker

Ubuntu 16.04 安装 Docker

1.选择国内的云服务商,这里选择阿里云为例

  1. curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -

2.安装所需要的包

  1. sudo apt-get install linux-image-extra-$(uname -r) linux-image-extra-virtual

3.添加使用 HTTPS 传输的软件包以及 CA 证书

  1. sudo apt-get update
  2. sudo apt-get install apt-transport-https ca-certificates

4.添加GPG密钥

  1. sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

5.添加软件源

  1. echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" | sudo tee /etc/apt/sources.list.d/docker.list

6.添加成功后更新软件包缓存

  1. sudo apt-get update

7.安装docker

  1. sudo apt-get install docker-engine

8.启动 docker

  1. sudo systemctl enable docker
  2. sudo systemctl start docker

2.2、基本操作

2.2.1、启动和停止docker

  1. #启动docker
  2. sudo systemctl enable docker
  3. sudo systemctl start docker
  4. #停止docker
  5. sudo systemctl stop docker

2.2.2、使用第三方Registry,在国内访问官方镜像仓库可能会很慢,可以使用第三方Register

新建或者编辑配置文件 /etc/docker/daemon.json

添加如下代码

  1. {
  2. "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn/"]
  3. }

保存退出后重启docker

  1. sudo systemctl restart docker

2.2.3、拉取镜像,一个完整的镜像是包含镜像名和tag,如果不写tag就默认的拉取latest的

  1. #示例拉取mysql最后的版本latest
  2. sudo docker pull mysql
  3. #示例拉取mysql的5.6.43,注意5.6.43是tag
  4. sudo docker pull mysql:5.6.43

2.2.4、罗列镜像

  1. sudo docker images

2.2.5、删除镜像

  1. #使用镜像名称
  2. sudo docker rmi mysql
  3. #使用镜像的Image ID(只需要写出部分)
  4. sudo docker rmi bb
  5. #使用镜像名称和tag,中间使用冒号分割
  6. sudo docker rmi mysql:5.6.43

如何确定一个镜像

1.使用tag

2.使用镜像名

3.使用镜像名和tag

2.2.6、镜像的导入和导出

导出镜像,当某的时候可能别的机器需要某个镜像,但是目标机器无法上网或者其他原因,我们可以把我们现有的镜像导出成一个压缩包,

  1. #save命令,后面跟着镜像名称和tag然后 > 到保存镜像的目录和文件名称
  2. sudo docker save nginx:latest >./nginx.dk

导入镜像

  1. sudo docker load < ./nginx.dk

2.2.7、启动容器

  1. #参数说明,
  2. # -it表示是使用交互方式
  3. # --name后面跟着的是给即将启动的容器一个名字,名字和运行中的容器不能一样
  4. # 接下来就是镜像名称和tag
  5. # 最后的/bin/bash就是容器起来后要打开的命令行(容器中的命令行)
  6. sudo docker run -it --name mycentos centos:latest /bin/bash

非交互方式启动容器后,如果想进入的话,可使用如下命令

  1. sudo docker exec -it mynginx /bin/base
  2. #如果报错,可使用如下试试
  3. sudo docker exec -it mynginx /bin/sh

2.2.8、停止容器

  1. #使用容器的名称
  2. sudo docker stop mynginx
  3. #使用容器的容器id(一部分就可以了)
  4. sudo docker stop bb

停止所有容器

  1. sudo docker stop $(sudo docker ps -q)

2.2.9、罗列运行中的容器

  1. sudo docker ps

该命令可选如下参数

2.2.10、删除容器

删除容器

  1. sudo docker rm 容器名称或者容器id

删除所有容器

  1. sudo docker rm 'sudo docker ps -a -q'

2.2.11、不想每次执行命令都输入sudo

  1. sudo groupadd docker
  2. sudo gpasswd -a ubuntu docker
  3. sudo service docker restart
  4. 重新进入shell

2.2.12、容器列表

  1. sudo docker container ls

2.2.13、容器详细信息

  1. sudo docker inspect [container id]

2.2.14、容器运行日志

  1. sudo docker logs [container id]

2.2.15、容器的导入和导出

3、docker应用

3.1、搭建tomcat应用

3.1.1、拉取tomcat镜像

  1. sudo docker pull tomcat

3.1.2、启动tomcat

参数说明:

  1. sudo docker run -d --name mytomcat -p 8888:8080 tomcat:latest

我们使用curl http://localhost:8888看看是否有输出,有就表明mytomcat这个容器已经运行了。

3.1.3、写一个简单的web应用,并打包成war包,名字我们就用docker.war

3.1.4、把war包上传到tomcat的webapps目录

  1. sudo docker cp docker.war mytomcat:/usr/local/tomcat/webapps/

3.1.5、浏览我们开发的页面

  1. curl http://localhost:8888/docker/

4、其他工具

playground 一个练习的虚拟环境,有时间限制,地址:

https://labs.play-with-docker.com,使用docker.com账号登录

5、制作镜像

5.1、制作一个新的镜像

先编写一个程序,本例子使用golang

  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Println("你好,docker!本例使用golang创建的可执行文件")
  5. }

进到该程序的目录执行编译生成hellodocker

  1. go build -o "hellodocker"

编写Dockerfile

vi Dockerfile

  1. FROM scratch
  2. ADD hellodocker /
  3. CMD ["/hellodocker"]

保存

命令说明:

第一行表示新建一个base image

第二行是把我们编译生成的hellodocker放到image的根目录

第三行表示执行hellodocker程序

  1. docker build -t aloxc/hellodocker .

命令说明,docker build 表示执行镜像制作,-t 指定tag,后面的aloxc是对应hub.docker.com里面的账户名称,hellodocker是镜像名称 ,.点好表示从当前目录找Dockerfile

  1. docker images

可以看到我们刚制作好的镜像

  1. #查看我们制作的image的分层,后面的参数是image id
  2. docker history 5b6d6c39bd25

现在我们启动下该容器

  1. docker run 5b6d6c39bd25

注意,有的时候我们使用golang编写的程序,放到镜像中启动容器执行不了,我们重新编译go程序,禁止使用cgo编译,

CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main

5.2、用一个base image重新build出一个新的image

  1. docker run -it centos

启动一个centos,进入交互界面,在容器中安装vim

  1. yum install -y vim
  2. exit

在宿主机执行下面命令,罗列出所有的容器

  1. docker container ls -a

  1. docker commit stupefied_mendeleev aloxc/centos-vim

命令说明:

docker commit 是从现有容器制作一个新的镜像

stupefied_mendeleev 是我们运行centos的names,如上图中

aloxc对应hub.docker.com中的用户id

centos-vim是新的镜像名字

执行完毕会生成一个新的image,docker images

从结果中我们可以看到,的确有个新的镜像aloxc/centos-vim

我们可以通过命令查看之前的centos和现在的centos-vim分层结构,有部分一样的分层。红色框是一样的,蓝色是新的centos-vim多出的层

  1. docker history centos
  2. docker history aloxc/centos-vim

不推荐使用原来有的容器创建一个新的镜像

5.3、使用已有的镜像制作新的镜像

新建Dockerfile,vi Dockerfile

  1. FROM centos
  2. RUN yum install -y vim

执行生成镜像的命令

  1. docker build -t aloxc/centos-vim2 .

命令说明:docker build 表示执行镜像制作,-t 指定tag,后面的aloxc是对应hub.docker.com里面的账户名称,centos-vim2是镜像名称 ,.点好表示从当前目录找Dockerfile

  1. docker images

5.4、使用现有容器制作镜像

当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。

docker commit 的语法格式为:

  1. docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]

我们可以用下面的命令将容器保存为镜像:

  1. $ docker commit \
  2. --author "aloxc <leerohwa@gmail.com>" \
  3. --message "添加了一些文件" \
  4. webserver \
  5. nginx:v2
  6. sha256:07e33465974800ce65751acc279adc6ed2dc5ed4e0838f8b86f0c87aa1795214

其中 --author 是指定修改的作者,而 --message 则是记录本次修改的内容。这点和 git 版本控制相似,不过这里这些信息可以省略留空。

慎用 docker commit

使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境中并不会这样使用。

首先,如果仔细观察之前的 docker diff webserver 的结果,你会发现除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由于命令的执行,还有很多文件被改动或添加了。这还仅仅是最简单的操作,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿。

此外,使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体在操作的。虽然 docker diff 或许可以告诉得到一些线索,但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的维护工作是非常痛苦的。

6、Dockerfile最佳实现及语法

6.1、FROM 关键词

FROM scratch #制作一个base image

FROM centos #使用一个base image

FROM ubuntu:18.04 使用其它的image

最佳实践:尽量使用官方的进行作为base image

6.2、LABEL关键词

该关键词定义了一些镜像中的matadata,类似我们写程序中的代码注释,最佳实践:Metadata不可少,LABEL可以添加多个,如下

  1. LABEL maintainer="abc@gmail.com"
  2. LABEL version="1.0"
  3. LABEL description="this is a description"

6.3、RUN关键词

就是需要在制作镜像的时候可能需要安装一些软件或者其它命令,最佳实践:每一个RUN就会新生成一层,所有尽量多个命令写到一行,命令太长可使用反斜线换行

  1. RUN yum update && yum install -y vim\
  2. python-dev #反斜线换行
  1. RUN apt-get update && apt-get install -y perl\
  2. pwgen --no-install-recommends && rm -rf \
  3. /var/lib/apt/lists/* #注意清理缓存
  1. RUN /bin/bash -c 'source $HOME/.bashrc;echo $HOME'

6.4、WORKDIR关键词

设定当前工作目录,类似linux中的cd,

  1. WORKDIR /test #如果没有test目录会自动创建test目录
  2. WORKDIR demo #进入到test/demo
  3. RUN pwd #输出结果应该是/test/demo

WORKDIR最佳实践:尽量使用WORKDIR,尽量不要使用RUN cd;尽量使用绝对目录

6.5、ADD 和 COPY关键词

把本地的一些文件添加到镜像中,ADD 不仅能拷贝文件还有解压缩功能,COPY只可以拷贝

  1. ADD hello / #把当前目录的hello文件添加到镜像的根目录
  2. ADD test.tar.gz / #添加到根目录并解压缩
  3. WORKDIR /root
  4. ADD ahello test/ # 此时ahello的目录应该是/root/test/ahello
  5. WORKDIR /root
  6. COPY bhello test/

最佳实践:大部分COPY优先ADD使用,添加远程的文件或者目录请使用RUN curl或者RUN wget

6.6、 ENV关键词

设置环境常量

  1. ENV MYSQL_VERSION 5.6 #设置常量
  2. RUN apt-get install -y mysql-server= "${MYSQL_VERSION}"\
  3. && rm -rf /var/lib/apt/lists/* #引用常量

最佳实践:尽量使用,增加可维护性

6.7 、VOLUME 和 EXPOSE关键词

存储和网络

EXPOSE:暴露端口,但不映射到宿主机,只被连接的服务访问。

volume:是把宿主机和容器的某个目录做映射,也就是说在容器中访问某个目录,实际上是访问的宿主机的目录,

6.8、CMD 和 ENTRYPOINT还有RUN

RUN:执行命令并创建新的镜像层

CMD:设置容器启动后默认执行的命令和参数

ENTRYPOINT:设置容器启动时运行的命令

两种格式可以混合,一行用shell格式,一行用Exec格式

CMD命令执行细节:

容器启动时默认执行的命令

如果docker run指定了其他命令,CMD命令会被忽略

如果定义了多个CMD,只有最后一个会执行

假如有这样一个Dockerfile

  1. FROM centos
  2. ENV name Dockeraaaaaaa
  3. CMD echo "hello world"
  4. CMD echo "hello $name"

现在两种方式启动容器:

docker run [image id] #输出 hello Dockeraaaaaaa

docker run -it [image id] /bin/bash #不会输出 hello Dockeraaaaaaa,原因是docker run命令指定了启动后的命令,CMD就会被忽略

ENTRYPOINT 命令执行细节

让容器以应用程序或者服务器的形式运行

不会被忽略,一定会执行

最佳实践:写一个shell脚本作为entrypoint,让容器启动后执行该shell脚本

使用ENTRYPOINT还有一个好处,就是我们可以在启动命名中添加的参数会传给ENTRYPOINT ,

6.9、 向容器传递参数

比如我们启动容器的时候,容器中执行的程序需要参数,我们又不想写死到Dockerfile中,我们可以在启动容器的时候向容器中传递参数,其Dockerfile是这样书写

vi Dockerfile ENTRYPOINT后面的命令必须用中括号,否则传递不了参数

  1. FROM ubuntu
  2. RUN apt-get update && apt-get install -y stress
  3. ENTRYPOINT ["/usr/bin/stress"]
  4. CMD []

制作镜像

  1. docker build -t aloxc/stress .

启动容器

  1. docker run -it aloxc/stress --vm 1 --verbose

示例中--vm 1 --verbose就是向容器中传递的参数,这串参数最终就是跟在/usr/bin/stress这个后面,作为其参数。

6.10、USER指令

格式:USER <用户名>[:<用户组>]

USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份。

当然,和 WORKDIR 一样,USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。

  1. RUN groupadd -r redis && useradd -r -g redis redis
  2. USER redis
  3. RUN [ "redis-server" ]

如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 gosu

  1. # 建立 redis 用户,并使用 gosu 换另一个用户执行命令
  2. RUN groupadd -r redis && useradd -r -g redis redis
  3. # 下载 gosu
  4. RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \
  5. && chmod +x /usr/local/bin/gosu \
  6. && gosu nobody true
  7. # 设置 CMD,并以另外的用户执行
  8. CMD [ "exec", "gosu", "redis", "redis-server" ]

6.11、HEALTHCHECK 健康检测

格式:

HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令。

在没有 HEALTHCHECK 指令前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。在 1.12 以前,Docker 不会检测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。

当在一个镜像指定了 HEALTHCHECK 指令后,用其启动容器,初始状态会为 starting,在 HEALTHCHECK 指令检查成功后变为 healthy,如果连续一定次数失败,则会变为 unhealthy

HEALTHCHECK 支持下列选项:

  1. FROM nginx
  2. RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
  3. HEALTHCHECK --interval=5s --timeout=3s \
  4. CMD curl -fs http://localhost/ || exit 1

这里我们设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应就视为失败,并且使用 curl -fs http://localhost/ || exit 1 作为健康检查命令。

6.12、ONBUILD 为他人做嫁衣裳

格式:ONBUILD <其它指令>

ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。

Dockerfile 中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD 是为了帮助别人定制自己而准备的。

6.13、多阶段构建

如有这样的需求,我们写了一个golang应用,我们希望编辑后生成一个镜像,可以有这些方法:

第一种方法build镜像过程太麻烦,第二种方法制作出的镜像太大(因为带有golang环境),有没有一种方法既不麻烦制作出的镜像又小?有,使用多节段构建

  1. FROM golang:1.9-alpine as builder
  2. RUN apk --no-cache add git
  3. WORKDIR /go/src/github.com/go/helloworld/
  4. RUN go get -d -v github.com/go-sql-driver/mysql
  5. COPY app.go .
  6. RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
  7. FROM alpine:latest as prod
  8. RUN apk --no-cache add ca-certificates
  9. WORKDIR /root/
  10. COPY --from=0 /go/src/github.com/go/helloworld/app .
  11. CMD ["./app"]

只构建某一阶段的镜像

我们可以使用 as 来为某一阶段命名,例如

  1. FROM golang:1.9-alpine as builder

例如当我们只想构建 builder 阶段的镜像时,增加 --target=builder 参数即可

  1. $ docker build --target builder -t username/imagename:tag .

构建时从其他镜像复制文件

上面例子中我们使用 COPY --from=0 /go/src/github.com/go/helloworld/app . 从上一阶段的镜像中复制文件,我们也可以复制任意镜像中的文件。

  1. $ COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf

7、镜像的发布

7.1、直接使用docker发布命令

先登录docker.com

  1. docker login

按提示输入用户名和密码

  1. docker image push aloxc/hellodocker:latest
  2. #或者
  3. docker push aloxc/hellodocker:latest

命令说明

docker image push会饿docker push是push到docker hub的命令

aloxc是docker的用户id

hellodocker是本地制作的镜像,

latest是指tag,

发布完毕后可以在shell中看到结果,也可以在hub.docker.com中看到结果

7.2、使用github.com或者bitbucket和hub.docker.com关联起来(推荐)

8、网络

单机

Bridge Network

Host Network

None Network

多机

Overlay Network

NAT:网络地址转换,当没有公网ip的机器想连接互联网的时候,会经过路由器,路由器再连接互联网。

比如当我们在内网机器192.168.0.12要访问yahoo.com的时候,首先我们内网机器会经过路由器,然后请求dns服务器,找到yahoo.com对应的ip地址,接下来路由器会把请求投递到yahoo对应的服务器上,当yahoo相应并返回的时候,会从请求头中找到请求来源,然后就会向请求来源响应结果,这时候又到我们路由器,路由器上NAT会记录到谁请求的yahoo,再把数据返回给谁。

9、持久化数据方案

9.1、基于本地文件系统的Volume。可以在运行容器的的时候加个参数 -v 将主机的目录作为容器的数据卷。这部分功能便是基于本地文件系统的valume管理

Volume的类型:

1.受管理的data volume,由docker后台自动创建

2.绑定挂载的volume,具体挂载位置可以由用户指定

9.1.1、数据持久化Data Volume,受管理的data volume,由docker后台自动创建

使用mysql来做实验,启动一个容器,在启动前先看看有哪些Volume,然后都删除掉

  1. docker volume ls

然后把这些volume都删除掉,删除命令docker volume rm xxxxx

现在启动容器

  1. docker run -d --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql

输出:

  1. 9fd7ae5ec48c91ca89c724bfd50e46c82bd0bc85b280162c2e6a4e8aee39df9b

容器启动后,查看有哪些volume,

  1. docker volume ls

返回结果9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a

我看下这个volume的详细信息

  1. docker volume inspect 9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a

返回如下结果:

  1. [
  2. {
  3. "CreatedAt": "2019-04-12T07:29:12Z",
  4. "Driver": "local",
  5. "Labels": null,
  6. "Mountpoint": "/var/lib/docker/volumes/9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a/_data",
  7. "Name": "9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a",
  8. "Options": null,
  9. "Scope": "local"
  10. }
  11. ]

从结果看,已经挂载了一个卷到本地的/var/lib/docker/volumes/9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a/_data目录

我们再启动一个容器

  1. docker run -d --name mysql2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql

启动后再查看卷列表

  1. docker volume ls

输出如下:

我们现在可以把这两个容器停止并删除

  1. docker stop mysql1 mysql2
  2. docker rm mysql1 mysql2

再次查看卷列表,都还在,也就是容器删除并不会删除容器创建的卷

  1. docker volume ls

输出如下:

注意到了没有,这些volume的名字太不友好了,有没办法,有,我们先把刚那两个volume删除掉

  1. docker volume rm 489805b5accc4892f67401740863d20432ad9ed70f529bd108a3a2fe781dd499 9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a

重新创建mysql1容器,命令的的-v 后面的就mysql1:/var/lib/mysql就是要把容器中/var/lib/mysql映射到本地的mysql1卷,

  1. docker run -d -v mysql1:/var/lib/mysql --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql

查看下这个卷的详细信息

  1. docker volume inspect mysql1
  1. [
  2. {
  3. "CreatedAt": "2019-04-12T07:45:49Z",
  4. "Driver": "local",
  5. "Labels": null,
  6. "Mountpoint": "/var/lib/docker/volumes/mysql1/_data",
  7. "Name": "mysql1",
  8. "Options": null,
  9. "Scope": "local"
  10. }
  11. ]

我们现在mysql中创建一个库abc,先进到mysql1容器的shell里面

  1. docker exec -it mysql1 /bin/bash
  1. mysql -uroot
  2. create database abc;
  3. show databases;

结果如下所示

的确创建成功了abc这个库。exit退出mysql,再次exit退出容器

现在我们把mysql1停止并删除这个容器,

  1. docker stop mysql1
  2. docker rm mysql1

再次启动一个mysql2的容器,指定数据卷,加-v参数并设置映射到刚刚的卷上-v mysql1:/var/lib/mysql

  1. docker run -d -v mysql1:/var/lib/mysql --name mysql2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql

启动后进入容器的shell

  1. docker exec -it mysql2 /bin/bash

进入到mysql中,查看刚刚创建的abc库是否在,

  1. mysql -uroot
  1. show databases;

输出如下,结果中是存在我们刚刚创建的abc库的

9.1.2、数据持久化Bind Mouting,绑定挂载的volume,具体挂载位置可以由用户指定

先编写一个Dockerfile和一个index.html文件

vi Dockerfile

  1. FROM nginx:latest
  2. WORKDIR /usr/share/nginx/html
  3. COPY index.html index.html

vi index.html

  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="Generator" content="EditPlus®">
  6. <meta name="Author" content="">
  7. <meta name="Keywords" content="">
  8. <meta name="Description" content="">
  9. <title>hello nginx by aloxc</title>
  10. </head>
  11. <body>
  12. hello nginx by aloxc,aloxc向nginx打招呼
  13. </body>
  14. </html>

新创建一个镜像

  1. docker build -t aloxc/nginx .

然后创建一个容器

  1. docker run -d -p 8888:80 --name web aloxc/nginx

现在我们的nginx起来了,

  1. curl localhost:8888

返回如下

  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="Generator" content="EditPlus®">
  6. <meta name="Author" content="">
  7. <meta name="Keywords" content="">
  8. <meta name="Description" content="">
  9. <title>hello nginx by aloxc</title>
  10. </head>
  11. <body>
  12. hello nginx by aloxc,aloxc向nginx打招呼
  13. </body>
  14. </html>

现在我们把容器停止并删除

  1. docker stop web
  2. docker rm web

重新启动容器,新加参数-v $(pwd):/usr/share/nginx/html

  1. docker run -d -v $(pwd):/usr/share/nginx/html -p 8888:80 --name web aloxc/nginx

接下来我们进入到容器中

  1. docker exec -it web /bin/bash

进到容器中,cd到/usr/share/nginx/html目录

  1. cd /usr/share/nginx/html
  2. ls

结果如下

现在新创建一个文件

  1. touch abc.txt

也就是容器的/usr/share/nginx/html目录下面有三个文件了,我们现在退出容器,看下宿主机的当前目录下有几个文件

  1. ls

结果如下,也是三个文件,

现在我们在宿主机上修改下index.html文件内容,比如修改如下

vi index.html

  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="Generator" content="EditPlus®">
  6. <meta name="Author" content="">
  7. <meta name="Keywords" content="">
  8. <meta name="Description" content="">
  9. <title>hello nginx by aloxc</title>
  10. </head>
  11. <body>
  12. hello nginx by aloxc,aloxc向nginx打招呼aaaaaaaaaaaaaaaaaaaaaaaa
  13. </body>
  14. </html>

再次查看下nginx输出,已经变成我们修改的了

结论:也就是容器和宿主机双向修改文件都会同步的

9.2、基于插件的Volume,支持第三方的存储方案,比如NAS,aws等

9.3、使用docker run部署一个wordpress

9.3.1、部署mysql

启动mysql容器

  1. docker run -d --name wordpressmysql -v wordpressmysqldata:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=wordpress mysql

启动后,我们看下卷列表应该有一个wordpressmysqldata的。

  1. docker colume ls

9.3.2、部署wordpress

启动wordpress容器

  1. docker run -d -e WORDPRESS_DB_HOST=wordpressmysql:3306 --link wordpressmysql -p 80:80 wordpress

10、docker compose批处理(单机)

Docker Compose 是 Docker 官方编排(Orchestration)项目之一,负责快速的部署分布式应用。分单宿主机和多宿主机情况(swarm),单宿主机可使用-links来连接桥接网络来链接。多宿主机使用overlay来连接。

10.1、docker-compose批处理

docker compse是一个工具

这个工具可以通过一个yml文件定义多容器的docker应用

通过一条命令就可以根据yml文件的定义去创建或者管理这多个容器

默认名字docker-compose.yml

docker-compose三个概念

Services

Networks

Volumes

一个示例的docker-compose.yml

  1. version: '3'
  2. services:
  3. web:
  4. image: mysql:5.6.43
  5. ports:
  6. - 3306
  7. networks:
  8. - front-tier
  9. - back-tier
  10. volumes:
  11. - "db-data:/var/lib/mysql" #挂载数据卷
  12. redis:
  13. build: ./redis #从redis目录的Dockerfile来build
  14. links:
  15. - web
  16. networks:
  17. - back-tier

使用

命令说明

  1. -f, --file FILE 指定使用的 Compose 模板文件,默认为 docker-compose.yml,可以多次指定。
  2. -p, --project-name NAME 指定项目名称,默认将使用所在目录名称作为项目名。
  3. --x-networking 使用 Docker 的可拔插网络后端特性
  4. --x-network-driver DRIVER 指定网络后端的驱动,默认为 bridge
  5. --verbose 输出更多调试信息。
  6. -v, --version 打印版本并退出。

命令使用说明

  1. build

格式为 docker-compose build [options] [SERVICE...]。

构建(重新构建)项目中的服务容器。

服务容器一旦构建后,将会带上一个标记名,例如对于 web 项目中的一个 db 容器,可能是 web_db。

可以随时在项目目录下运行 docker-compose build 来重新构建服务。

选项包括:

--force-rm 删除构建过程中的临时容器。

--no-cache 构建镜像过程中不使用 cache(这将加长构建过程)。

--pull 始终尝试通过 pull 来获取更新版本的镜像。

  1. config

验证 Compose 文件格式是否正确,若正确则显示配置,若格式错误显示错误原因。

  1. down

此命令将会停止 up 命令所启动的容器,并移除网络

  1. exec

进入指定的容器

  1. help

获得一个命令的帮助

  1. images

列出 Compose 文件中包含的镜像

  1. kill

格式为 docker-compose kill [options] [SERVICE...]。

通过发送 SIGKILL 信号来强制停止服务容器。

支持通过 -s 参数来指定发送的信号,例如通过如下指令发送 SIGINT 信号。

  1. $ docker-compose kill -s SIGINT
  2. logs

格式为 docker-compose logs [options] [SERVICE...]。

查看服务容器的输出。默认情况下,docker-compose 将对不同的服务输出使用不同的颜色来区分。可以通过 --no-color 来关闭颜色。

该命令在调试问题的时候十分有用。

  1. pause

格式为 docker-compose pause [SERVICE...]。

暂停一个服务容器。

  1. port

格式为 docker-compose port [options] SERVICE PRIVATE_PORT。

打印某个容器端口所映射的公共端口。

选项:

--protocol=proto 指定端口协议,tcp(默认值)或者 udp。

--index=index 如果同一服务存在多个容器,指定命令对象容器的序号(默认为 1)。

  1. ps

格式为 docker-compose ps [options] [SERVICE...]。

列出项目中目前的所有容器。

选项:

-q 只打印容器的 ID 信息。

  1. pull

格式为 docker-compose pull [options] [SERVICE...]。

拉取服务依赖的镜像。

选项:

--ignore-pull-failures 忽略拉取镜像过程中的错误。

  1. push

推送服务依赖的镜像到 Docker 镜像仓库。

  1. restart

格式为 docker-compose restart [options] [SERVICE...]。

重启项目中的服务。

选项:

-t, --timeout TIMEOUT 指定重启前停止容器的超时(默认为 10 秒)。

  1. rm

格式为 docker-compose rm [options] [SERVICE...]。

删除所有(停止状态的)服务容器。推荐先执行 docker-compose stop 命令来停止容器。

选项:

-f, --force 强制直接删除,包括非停止状态的容器。一般尽量不要使用该选项。

-v 删除容器所挂载的数据卷。

  1. run

格式为 docker-compose run [options] [-p PORT...] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]。

在指定服务上执行一个命令。

例如:

$ docker-compose run ubuntu ping docker.com
将会启动一个 ubuntu 服务容器,并执行 ping docker.com 命令。

默认情况下,如果存在关联,则所有关联的服务将会自动被启动,除非这些服务已经在运行中。

该命令类似启动容器后运行指定的命令,相关卷、链接等等都将会按照配置自动创建。

两个不同点:

给定命令将会覆盖原有的自动运行命令;

不会自动创建端口,以避免冲突。

如果不希望自动启动关联的容器,可以使用 --no-deps 选项,例如

$ docker-compose run --no-deps web python manage.py shell
将不会启动 web 容器所关联的其它容器。

选项:

-d 后台运行容器。

--name NAME 为容器指定一个名字。

--entrypoint CMD 覆盖默认的容器启动指令。

-e KEY=VAL 设置环境变量值,可多次使用选项来设置多个环境变量。

-u, --user="" 指定运行容器的用户名或者 uid。

--no-deps 不自动启动关联的服务容器。

--rm 运行命令后自动删除容器,d 模式下将忽略。

-p, --publish=[] 映射容器端口到本地主机。

--service-ports 配置服务端口并映射到本地主机。

-T 不分配伪 tty,意味着依赖 tty 的指令将无法运行。

  1. scale

格式为 docker-compose scale [options] [SERVICE=NUM...]。

设置指定服务运行的容器个数。

通过 service=num 的参数来设置数量。例如:

$ docker-compose scale web=3 db=2
将启动 3 个容器运行 web 服务,2 个容器运行 db 服务。

一般的,当指定数目多于该服务当前实际运行容器,将新创建并启动容器;反之,将停止容器。

选项:

-t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

  1. start

格式为 docker-compose start [SERVICE...]。

启动已经存在的服务容器。

  1. stop

格式为 docker-compose stop [options] [SERVICE...]。

停止已经处于运行状态的容器,但不删除它。通过 docker-compose start 可以再次启动这些容器。

选项:

-t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

  1. top

查看各个服务容器内运行的进程。

  1. unpause

格式为 docker-compose unpause [SERVICE...]。

恢复处于暂停状态中的服务。

up
格式为 docker-compose up [options] [SERVICE...]。

该命令十分强大,它将尝试自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。

链接的服务都将会被自动启动,除非已经处于运行状态。

可以说,大部分时候都可以直接通过该命令来启动一个项目。

默认情况,docker-compose up 启动的容器都在前台,控制台将会同时打印所有容器的输出信息,可以很方便进行调试。

当通过 Ctrl-C 停止命令时,所有容器将会停止。

如果使用 docker-compose up -d,将会在后台启动并运行所有的容器。一般推荐生产环境下使用该选项。

默认情况,如果服务容器已经存在,docker-compose up 将会尝试停止容器,然后重新创建(保持使用 volumes-from 挂载的卷),以保证新启动的服务匹配 docker-compose.yml 文件的最新内容。如果用户不希望容器被停止并重新创建,可以使用 docker-compose up --no-recreate。这样将只会启动处于停止状态的容器,而忽略已经运行的服务。如果用户只想重新部署某个服务,可以使用 docker-compose up --no-deps -d 来重新创建服务并后台停止旧服务,启动新服务,并不会影响到其所依赖的服务。

选项:

-d 在后台运行服务容器。

--no-color 不使用颜色来区分不同的服务的控制台输出。

--no-deps 不启动服务所链接的容器。

--force-recreate 强制重新创建容器,不能与 --no-recreate 同时使用。

--no-recreate 如果容器已经存在了,则不重新创建,不能与 --force-recreate 同时使用。

--no-build 不自动构建缺失的服务镜像。

-t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

  1. version

格式为 docker-compose version。

打印版本信息。

10.1.1、使用docker-compose部署一个完整的wordpress和mysql的实例。

  1. version: '3'
  2. services:
  3. wordpress:
  4. image: wordpress:latest
  5. ports:
  6. - 80:80
  7. environment:
  8. WORDPRESS_DB_HOST: mysql
  9. WORDPRESS_DB_PASSWORD: 123456
  10. networks:
  11. - my-bridge
  12. mysql:
  13. image: mysql:5.6.43
  14. environment:
  15. MYSQL_ROOT_PASSWORD: 123456
  16. MYSQL_DATABASE: wordpress
  17. volumes:
  18. - mysql-data:/var/lib/mysql
  19. networks:
  20. - my-bridge
  21. volumes:
  22. mysql-data:
  23. networks:
  24. my-bridge:
  25. driver: bridge
  1. docker-compose -f docker-compose.yml up

命令说明,-f后面跟的就是文件名,如果文件名是docker-compose.yml,就可以省略 -f 和后面的文件名,up就是说要执行批处理,可以加 -d 在后台执行,

docker-compose的命令列表,docker-compose --help

  1. build Build or rebuild services
  2. bundle Generate a Docker bundle from the Compose file
  3. config Validate and view the Compose file
  4. create Create services
  5. down Stop and remove containers, networks, images, and volumes
  6. events Receive real time events from containers
  7. exec Execute a command in a running container
  8. help Get help on a command
  9. images List images
  10. kill Kill containers
  11. logs View output from containers
  12. pause Pause services
  13. port Print the public port for a port binding
  14. ps List containers
  15. pull Pull service images
  16. push Push service images
  17. restart Restart services
  18. rm Remove stopped containers
  19. run Run a one-off command
  20. scale Set number of containers for a service
  21. start Start services
  22. stop Stop services
  23. top Display the running processes
  24. unpause Unpause services
  25. up Create and start containers
  26. version Show the Docker-Compose version information

常用命令

  1. docker-compose up -d nginx 构建建启动nignx容器
  2. docker-compose exec nginx bash 登录到nginx容器中
  3. docker-compose down 删除所有nginx容器,镜像
  4. docker-compose ps 显示所有容器
  5. docker-compose restart nginx 重新启动nginx容器
  6. docker-compose run --no-deps --rm php-fpm php -v php-fpm中不启动关联容器,并容器执行php -v 执行完成后删除容器
  7. docker-compose build nginx 构建镜像
  8. docker-compose build --no-cache nginx 不带缓存的构建。
  9. docker-compose logs nginx 查看nginx的日志
  10. docker-compose logs -f nginx 查看nginx的实时日志
  11. docker-compose config -q 验证(docker-compose.yml)文件配置,当配置正确时,不输出任何内容,当文件配置错误,输出错误信息。
  12. docker-compose events --json nginx json的形式输出nginxdocker日志
  13. docker-compose pause nginx 暂停nignx容器
  14. docker-compose unpause nginx 恢复ningx容器
  15. docker-compose rm nginx 删除容器(删除前必须关闭容器)
  16. docker-compose stop nginx 停止nignx容器
  17. docker-compose start nginx 启动nignx容器

10.2、scale关键词(动态扩容容器数量),haproxy负载均衡

当我们某个service想创建多个的时候,可以使用此关键词。

比如有这样的场景,我们现在的web流量比较大,我们想多创建一些web服务器,那是不是不要手动创建web呢?这样很麻烦,我们可以在执行docker-compose的时候加上个参数 --scale xxx=n,就会一下创建若干个 xxx 服务,注意,假设我们的xxx服务中指定了端口映射是会出错的,原因就是每台服务器的某个固定端口只有一个。

一般碰到这样的情况,前面还需要加负载均衡设备,比如haproxy等的。

写一个简单的web应用,并打包成war包,名字我们就用docker.war

  1. <%@ page import="java.net.InetAddress" %>
  2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  3. <html>
  4. <head>
  5. <title>李永华的docker tomcat示例页面</title>
  6. </head>
  7. <body>
  8. 李永华的docker tomcat示例页面 <%=System.currentTimeMillis()%>
  9. <br><%=(InetAddress.getLocalHost()).getHostName()%>
  10. </body>
  11. </html>

把war包上传到宿主机的/home/ubuntu/jsp/目录

编写docker-compose.yml文件

  1. version: '3'
  2. services:
  3. web:
  4. image: tomcat:latest
  5. volumes:
  6. - /home/ubuntu/jsp:/usr/local/tomcat/webapps/
  7. lb:
  8. image: dockercloud/haproxy
  9. links:
  10. - web
  11. ports:
  12. - 80:80
  13. volumes:
  14. - /var/run/docker.sock:/var/run/docker.sock

执行启动命令,当前只启动了一个haproxy和一个web

  1. docker-compose -f docker-compose.yml up -d

浏览我们开发的页面

  1. curl http://localhost/docker/

会有以下结果,下面的0971c392da11是主机名称,每次都一样,不会变,

  1. <html>
  2. <head>
  3. <title>李永华的docker tomcat示例页面</title>
  4. </head>
  5. <body>
  6. 李永华的docker tomcat示例页面 1555070019650
  7. <br>0971c392da11
  8. </body>
  9. </html>
  1. docker-compose -f docker-compose.yml up --scale web=3 -d

我们原来有1台web容器再运行,现在扩展到3台,也就是会再创建2台容器出来,注意蓝色箭头的是Starting的,红色箭头的是Creating的

  1. docker-compose -f docker-compose.yml up --scale web=10 -d

已经有3台,还会创建7台容器出来,注意蓝色箭头的是Starting的,红色箭头的是Creating的

当我们的流量高峰过了后,我们想对web进行减少容器,也用这个命令,会减到2台容器,注意红色箭头是Stoping and removing,蓝色箭头是Starting的

  1. docker-compose -f docker-compose.yml up --scale web=2 -d

links:链接到docker-compose管理的容器,其列表是该docker-compose.yaml中定义的其它服务,注意不可以由交叉链接,注意不建议使用该指令。

external_links:链接到 docker-compose.yml 外部的容器,甚至并非 Compose 管理的外部容器。

  1. external_links:
  2. - redis_1
  3. - project_db_1:mysql
  4. - project_db_1:postgresql

10.4、extra_hosts 类似 Docker 中的 --add-host 参数,指定额外的 host 名称映射信息

类似 Docker 中的 --add-host 参数,指定额外的 host 名称映射信息。

  1. extra_hosts:
  2. - "googledns:8.8.8.8"
  3. - "dockerhub:52.1.157.61"

会在启动后的服务容器中 /etc/hosts 文件中添加如下两条条目。

  1. 8.8.8.8 googledns
  2. 52.1.157.61 dockerhub

10.5、设置容器的内核参数sysctls和ulimits限制值

sysctls
配置容器内核参数。

  1. sysctls:
  2. net.core.somaxconn: 1024
  3. net.ipv4.tcp_syncookies: 0
  4. sysctls:
  5. - net.core.somaxconn=1024
  6. - net.ipv4.tcp_syncookies=0

ulimits
指定容器的 ulimits 限制值。

例如,指定最大进程数为 65535,指定文件句柄数为 20000(软限制,应用可以随时修改,不能超过硬限制) 和 40000(系统硬限制,只能 root 用户提高)。

  1. ulimits:
  2. nproc: 65535
  3. nofile:
  4. soft: 20000
  5. hard: 40000

11、swarm容器编排技术

11.1、基本概念

两种角色:manager、worker。

两个概念:service、replicas

Swarm mode 内置 kv 存储功能,提供了众多的新特性,比如:具有容错能力的去中心化设计、内置服务发现、负载均衡、路由网格、动态伸缩、滚动更新、安全传输等。使得 Docker 原生的 Swarm 集群具备与 Mesos、Kubernetes 竞争的实力。

创建三台机器,ip分别是192.168.0.18 、192.168.0.17、192.168.0.16,

在192.168.0.18上执行

  1. docker swarm init --advertise-addr=192.168.0.18

如果你的 Docker 主机有多个网卡,拥有多个 IP,必须使用 --advertise-addr 指定 IP。

执行完毕有如下输出

  1. [node1] (local) root@192.168.0.18 ~
  2. $ docker swarm init --advertise-addr=192.168.0.18
  3. Swarm initialized: current node (tjum8m7h1dl5tmbf0bud4awme) is now a manager.
  4. To add a worker to this swarm, run the following command:
  5. docker swarm join --token SWMTKN-1-53xgtit6xqwkp2ig8gbwic3mzeg7qup40f1nn10c1p9hhj62i7-94z5h2jru4tda5wbviaspzs9q 192.168.0.18:2377
  6. ````
  7. 在其他两台机器上执行
  8. <div class="md-section-divider"></div>
  9. ```shell
  10. docker swarm join --token SWMTKN-1-53xgtit6xqwkp2ig8gbwic3mzeg7qup40f1nn10c1p9hhj62i7-94z5h2jru4tda5wbviaspzs9q 192.168.0.18:2377

会输出如下内容

  1. This node joined a swarm as a worker.

在192.168.0.18上执行命令

  1. docker node ls

可以看到有三个节点,其中一个是leader节点

11.2、service

该命令类似与dock run命令

  1. docker service create --name demo busybox sh -c "while true;do sleep 3600;done"

查看创建的demo这个service运行在什么地方

  1. docker service ps demos

通过一下命令来扩容

  1. docker service scale demo=5

  1. docker service ps demo ##再看看service的分布情况

现在我们去node2(192.168.0.17)上删除一个容器

  1. docker rm -f 0cca6ab9208b

现在在回到node1(192.168.0.18)上看看还有多少个容器

  1. docker service ps demo

从结果看到没,被我们删除掉了一个,swarm又给我们建立一个起来。

我们再在node1节点执行删除demo操作

  1. docker service rm demo

就删除了,我们过几秒钟去另外两个节点看,也没有了,

11.2.1、使用swarm service部署wordpress

先创建overlay network

  1. docker network create -d overlay demo

创建完毕后执行 docker network ls看看结果

接下来创建mysql service

  1. docker service create --name mysql --env MYSQL_ROOT_PASSWORD=123456 --env MYSQL_DATABASE=wordpress --network demo --mount type=volume,source=mysql-data,destination=/var/lib/mysql mysql

创建完毕使用下面命令看看结果

  1. docker service ls

接下来创建wordpress service

  1. docker service create --name wordpress -p 80:80 --env WORDPRESS_DB_PASSWORD=123456 --env WORDPRESS_DB_HOST=mysql --network demo wordpress

创建完毕后使用下面的命令看看结果

  1. docker service ps wordpress

为什么wordpress和mysql不在同一台机器上,wordpress能访问mysql呢?记得开始的时候我们在node1上创建了一个demo的overlay,而wordpress是最终部署到node3上的,我们再node3上docker network ls查看,有了demo的overlay,这样wordpress就能够连接mysql上了,其实是使用的dns服务

11.3、集群服务间通信之RoutingMesh

重新启动三台机器node1(192.168.0.23)、node2(192.168.0.23)、node3(192.168.0.21)

我们把node1定义为swarm的leader,使用如下命令

  1. docker swarm init --advertise-addr=192.168.0.23

那其他两台就是work节点,在这两个work节点上执行leader节点返回的信息

  1. docker swarm join --token SWMTKN-1-58wwdzjyvuaqamapsw12z54jguqc71jxxnmjyp4eiipjcy5sse-9fg01pnmefaj6pv1lmq3nm9s2 192.168.0.23:2377

现在在看下网络情况,在node1上执行如下命令,可以看到node1是leader节点

  1. docker node ls

现在创建一个overlay网络,名字叫做demo,在node1上执行如下命令

  1. docker network create -d overlay demo

执行完毕后使用docker network ls在node1上看下结果

接下来我们创建一个service(node1上执行),我们使用whoami的service,

  1. docker service create --name whoami -p 8000:8000 --network demo -d jwilder/whoami

执行完毕,我们看下这个服务是在哪台机器上运行

  1. docker service ps whoami

我们再创建 一个busybox,名字叫client

  1. docker service create --name client -d --network demo busybox sh -c "while true;do sleep 3600;done"

过几秒中执行下看看这个client运行在什么地方

  1. docker service ps client

运行在node3,那我们去node3上运行下面的命令看看client的容器id,并进入到容器中

  1. docker ps

  1. docker exec -it dd50 sh

进入到client,我们ping下whoami

  1. ping whoami

通的,实际上是ping的10.0.0.2,这个节点是在node1上的,单实际上这个ip不是node1上的whoami这个容器的ip,我们现在这样做,我们在node1上扩容下这个whoami服务。

  1. docker service scale whoami=2

等待完成。我们看看新扩容的一个容器是部署在什么地方

  1. docker service ps whoami

之前一个是在node1上,这新扩容的就在node2上了,

我们再次回到node3的busybox容器,在容器中执行

  1. ping whoami

从结果来看,还是之前的结果,也就是说,这个whoami不属于任何一个我们创建的whoami容器,而是一个虚拟ip(vip),我们也可以使用nslookup来看看whoami是什么情况。

我们再使用nslookup tasks.whoami看看什么情况

结果中的10.0.0.3和10.0.0.8才是whoami的两个容器中的ip,可以在这两个容器中使用 ip a 看看是否是这样的结果。

我们在client这个容器中可以多次执行如下命令看看结果

  1. wget -q -O - whoami:8000

只会返回两种结果,一个结果应该是node1上的whoami返回的,一个结果是node2上的whoami返回的。实现负载均衡,其实现原理是通过 lvs 来实现的。

Routing mesh的两种体现

11.4、RoutingMesh之Ingress负载均衡

我们现在看下whoami的部署情况

  1. docker service ps whoami

结果说明在node1和node2上部署了whoami服务,

我们现在分别在node1、node2和没有部署whoami的node3上执行下

  1. curl localhost:8000

都有结果返回了,现在有个问题,node3是没有部署whoami这个service的,既然也能访问,我们再node3上执行下下面的命令,看看端口转发规则

  1. iptables -nL -t nat

返回的结果中有其中这样的一条

意思是请求本地8000端口就转发到172.19.0.2:8000

再在node3上执行 ifconfig查看下本地网络情况,部分响应结果如下

也就是172.19.0.2 和本机中的172.19.0.1是一个网络。

我们再执行一个命令

  1. brctl show

  1. docker network ls

看看docker_gwbridge这个网络的详细情况

  1. docker network inspect docker_gwbridge

有如下返回(部分)

再执行如下命令看看docker给我们创建了哪些网络

  1. ls /var/run/docker/netns

其中蓝色框框就是我们上上图看到的 ingress_sbox

我们运行下

  1. nsenter --net=/var/run/docker/netns/ingress_sbox

这是由于我们使用labs.play-with-docker.com限制,其实如果我们本地搭建docker环境的话应该是能进到ingress 环境的,我找了张图,别人进去的,看两个用户不一致,这个别人的图,在ingress里面运行ip a,就可以看到我们之前的的ip 172.18.0.2(这个ip不是和我们环境一样,原因就是环境不一致导致的,如果我们搭建环境肯定一样) 了

总结如下图,

再在这个ingress_sbox中执行命令

  1. iptables -nL -t mangle

可以看到黄框中的,意思是要使用lvs走负载均衡,而后端的服务器就是之前nsloop tasks.whoami出来的结果,

继续在ingress_sbox上执行命令

  1. ipvsadm -l

结果中的10.255.0.8和10.255.0.9就是那两个whoami service的地址之一,我们可以在那两个whoami上使用 ifconfig 验证下,看看是否有这个ip

11.5、使用docker stack部署wordpress,

先编写docker-compose.yml文件

  1. version: '3'
  2. services:
  3. web:
  4. image: wordpress
  5. ports:
  6. - 8080:80
  7. environment:
  8. WORDPRESS_DB_HOST: mysql
  9. WORDPRESS_DB_PASSWORD: root
  10. networks:
  11. - my-network
  12. depends_on:
  13. - mysql
  14. deploy:
  15. mode: replicated
  16. replicas: 3
  17. restart_policy:
  18. condition: on-failure
  19. delay: 5s
  20. max_attempts: 3
  21. update_config:
  22. parallelism: 1
  23. delay: 10s
  24. mysql:
  25. image: mysql
  26. environment:
  27. MYSQL_ROOT_PASSWORD: root
  28. MYSQL_DATABASE: wordpress
  29. volumes:
  30. - mysql-data:/var/lib/mysql
  31. networks:
  32. - my-network
  33. deploy:
  34. mode: global
  35. placement:
  36. constraints:
  37. - node.role == manager
  38. volumes:
  39. mysql-data:
  40. networks:
  41. my-network:
  42. driver: overlay

在node1上执行

  1. docker stack deploy wordpress --compose-file=docker-compose.yml

部署完毕后使用看看情况

  1. docker stack ls

11.6、DockerSecret管理和使用,

什么是Secret?

用户名和密码

ssh key

tls认证

任何不想被其它人看到的信息

这些信息不想写到docker-compose.yml文件

看看swarm架构图

Secret管理

存在Swarm Manager节点 Raft database里面

秘密可以分配给一个service,这个service就能看到这个秘密

在容器内部秘密看起来像文件,但实际是在内存中

创建完毕后使用下面命令看看有哪些

  1. docker secret ls

service中使用创建的密码,创建service的时候带上--secret参数

  1. docker service create --name client --secret my-pw busybox sh -c "while true;do sleep 3600;done"

进到busybox中,

  1. docker exec -it client sh
  1. ls /run/secrets/

就能看到我们传进去的my-pw,使用cat命令就能看到内容为我们设置的admin123.

可以传若干个secret,只需要多个 --secret参数。

应用:我们新建一个mysql的service,可以指定他的密码是由什么文件提供的,

  1. docker service create --name db --secret my-pw -e MYSQL_ROOT_PASSWORD=/run/secrets/my-pw mysql

进到mysql这个容器,

  1. docker exec -it db sh
  1. cat /run/secrets/my-pw

输出

  1. mysql -u root -p

输入admin123,进到mysql的shell中了,证明通过了。

11.7、在docker stack中使用secret

在node1上执行输入

  1. echo "admin123" | docker secret create my-pw -

新建docker-compose.yml

  1. version: '3'
  2. services:
  3. web:
  4. image: wordpress
  5. ports:
  6. - 8080:80
  7. secrets:
  8. - my-pw
  9. environment:
  10. WORDPRESS_DB_HOST: mysql
  11. WORDPRESS_DB_PASSWORD_FILE: /run/secrets/my-pw
  12. networks:
  13. - my-network
  14. depends_on:
  15. - mysql
  16. deploy:
  17. mode: replicated
  18. replicas: 3
  19. restart_policy:
  20. condition: on-failure
  21. delay: 5s
  22. max_attempts: 3
  23. update_config:
  24. parallelism: 1
  25. delay: 10s
  26. mysql:
  27. image: mysql
  28. secrets:
  29. - my-pw
  30. environment:
  31. MYSQL_ROOT_PASSWORD_FILE: /run/secrets/my-pw
  32. MYSQL_DATABASE: wordpress
  33. volumes:
  34. - mysql-data:/var/lib/mysql
  35. networks:
  36. - my-network
  37. deploy:
  38. mode: global
  39. placement:
  40. constraints:
  41. - node.role == manager
  42. volumes:
  43. mysql-data:
  44. networks:
  45. my-network:
  46. driver: overlay
  47. # secrets:
  48. # my-pw:
  49. # file: ./password
  1. docker stack deploy wordpress --compose-file=docker-compose.yml

11.8、service更新

在node1上创建overlay网络

  1. docker network create -d overlay demo

创建service

  1. docker service create --name web --publish 8080:5000 --network demo xiaopeng163/python-flask-demo:1.0

扩展下web 的 service 数量

  1. docker service scale web=2

我们再某台机器上执行一下脚本

  1. sh -c "while true;do curl localhost:8080 && sleep 1;done;"

现在在swarm leader节点执行一下代码更新,示例是从image更新

  1. docker service update --image xiaopeng163/python-flask-demo:2.0 web

现在切换到执行脚本的机器上观察,过了一会就出现新的结果了,

看到了没,有新的结果出来了,

更新完毕后,我们再在leader节点node1上执行下面命令

  1. docker service ps web

有两个shutdown了,有两个正在运行,证明我们更新是成功的。

我们再通过端口更新下试试:使用端口更新会中断业务,使用image更新不会中断业务

  1. docker service updae --publish 8080:5000 --publish-add 8088:500 web

11.9、通过docker stack更新

12、Docker cloud

13、k8s

架构图

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注