@aloxc
2019-11-19T03:54:27.000000Z
字数 31525
阅读 465
docker
镜像(image):就是一个只读的模板,类似于打包好的操作系统iso文件
容器(container):使用镜像启动的系统,类似我们安装好的win7
仓库(Repository):就是保存镜像的地方,官方仓库hub.docker.com
注册器(Registry):就是创建仓库的,分为公有和私有
一个Registry有多个仓库,一个仓库有若干个镜像,可以使用一个镜像创建若干个容器。
Ubuntu 16.04 安装 Docker
1.选择国内的云服务商,这里选择阿里云为例
curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -
2.安装所需要的包
sudo apt-get install linux-image-extra-$(uname -r) linux-image-extra-virtual
3.添加使用 HTTPS 传输的软件包以及 CA 证书
sudo apt-get updatesudo apt-get install apt-transport-https ca-certificates
4.添加GPG密钥
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
5.添加软件源
echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" | sudo tee /etc/apt/sources.list.d/docker.list
6.添加成功后更新软件包缓存
sudo apt-get update
7.安装docker
sudo apt-get install docker-engine
8.启动 docker
sudo systemctl enable dockersudo systemctl start docker
#启动dockersudo systemctl enable dockersudo systemctl start docker#停止dockersudo systemctl stop docker
新建或者编辑配置文件 /etc/docker/daemon.json
添加如下代码
{"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn/"]}
保存退出后重启docker
sudo systemctl restart docker
#示例拉取mysql最后的版本latestsudo docker pull mysql#示例拉取mysql的5.6.43,注意5.6.43是tagsudo docker pull mysql:5.6.43
sudo docker images
#使用镜像名称sudo docker rmi mysql#使用镜像的Image ID(只需要写出部分)sudo docker rmi bb#使用镜像名称和tag,中间使用冒号分割sudo docker rmi mysql:5.6.43
如何确定一个镜像
1.使用tag
2.使用镜像名
3.使用镜像名和tag
导出镜像,当某的时候可能别的机器需要某个镜像,但是目标机器无法上网或者其他原因,我们可以把我们现有的镜像导出成一个压缩包,
#save命令,后面跟着镜像名称和tag然后 > 到保存镜像的目录和文件名称sudo docker save nginx:latest >./nginx.dk
导入镜像
sudo docker load < ./nginx.dk
#参数说明,# -it表示是使用交互方式# --name后面跟着的是给即将启动的容器一个名字,名字和运行中的容器不能一样# 接下来就是镜像名称和tag# 最后的/bin/bash就是容器起来后要打开的命令行(容器中的命令行)sudo docker run -it --name mycentos centos:latest /bin/bash
非交互方式启动
#参数说明# -d是使用守护进程起来的容器# --name后面跟着的是给即将启动的容器一个名字,名字和运行中的容器不能一样sudo docker run -d --name mynginx nginx:latest
非交互方式启动容器后,如果想进入的话,可使用如下命令
sudo docker exec -it mynginx /bin/base#如果报错,可使用如下试试sudo docker exec -it mynginx /bin/sh
#使用容器的名称sudo docker stop mynginx#使用容器的容器id(一部分就可以了)sudo docker stop bb
停止所有容器
sudo docker stop $(sudo docker ps -q)
sudo docker ps
该命令可选如下参数
删除容器
sudo docker rm 容器名称或者容器id
删除所有容器
sudo docker rm 'sudo docker ps -a -q'
sudo groupadd dockersudo gpasswd -a ubuntu dockersudo service docker restart重新进入shell
sudo docker container ls
sudo docker inspect [container id]
sudo docker logs [container id]
导出容器
$ docker container ls -aCONTAINER ID ……7691a814370e ……$ docker export 7691a814370e > ubuntu.tar
导入容器
将容器导入为镜像
$ cat ubuntu.tar | docker import - test/ubuntu:v1.0$ docker image lsREPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEtest/ubuntu v1.0 9d37a6082e97 20s ago 171.3 MB//也可以通过指定 URL 或者某个目录来导入$ docker import http://ex.com/eximage.tgz ex/imagerepo
sudo docker pull tomcat
参数说明:
sudo docker run -d --name mytomcat -p 8888:8080 tomcat:latest
我们使用curl http://localhost:8888看看是否有输出,有就表明mytomcat这个容器已经运行了。
sudo docker cp docker.war mytomcat:/usr/local/tomcat/webapps/
curl http://localhost:8888/docker/
playground 一个练习的虚拟环境,有时间限制,地址:
https://labs.play-with-docker.com,使用docker.com账号登录
先编写一个程序,本例子使用golang
package mainimport "fmt"func main() {fmt.Println("你好,docker!本例使用golang创建的可执行文件")}
进到该程序的目录执行编译生成hellodocker
go build -o "hellodocker"
编写Dockerfile
vi Dockerfile
FROM scratchADD hellodocker /CMD ["/hellodocker"]
保存
命令说明:
第一行表示新建一个base image
第二行是把我们编译生成的hellodocker放到image的根目录
第三行表示执行hellodocker程序
docker build -t aloxc/hellodocker .
命令说明,docker build 表示执行镜像制作,-t 指定tag,后面的aloxc是对应hub.docker.com里面的账户名称,hellodocker是镜像名称 ,.点好表示从当前目录找Dockerfile
docker images
可以看到我们刚制作好的镜像
#查看我们制作的image的分层,后面的参数是image iddocker history 5b6d6c39bd25
现在我们启动下该容器
docker run 5b6d6c39bd25
注意,有的时候我们使用golang编写的程序,放到镜像中启动容器执行不了,我们重新编译go程序,禁止使用cgo编译,
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main
docker run -it centos
启动一个centos,进入交互界面,在容器中安装vim
yum install -y vimexit
在宿主机执行下面命令,罗列出所有的容器
docker container ls -a
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多出的层
docker history centosdocker history aloxc/centos-vim
不推荐使用原来有的容器创建一个新的镜像
新建Dockerfile,vi Dockerfile
FROM centosRUN yum install -y vim
执行生成镜像的命令
docker build -t aloxc/centos-vim2 .
命令说明:docker build 表示执行镜像制作,-t 指定tag,后面的aloxc是对应hub.docker.com里面的账户名称,centos-vim2是镜像名称 ,.点好表示从当前目录找Dockerfile
docker images
当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。
docker commit 的语法格式为:
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
我们可以用下面的命令将容器保存为镜像:
$ docker commit \--author "aloxc <leerohwa@gmail.com>" \--message "添加了一些文件" \webserver \nginx:v2sha256:07e33465974800ce65751acc279adc6ed2dc5ed4e0838f8b86f0c87aa1795214
其中 --author 是指定修改的作者,而 --message 则是记录本次修改的内容。这点和 git 版本控制相似,不过这里这些信息可以省略留空。
docker commit使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境中并不会这样使用。
首先,如果仔细观察之前的 docker diff webserver 的结果,你会发现除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由于命令的执行,还有很多文件被改动或添加了。这还仅仅是最简单的操作,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿。
此外,使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体在操作的。虽然 docker diff 或许可以告诉得到一些线索,但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的维护工作是非常痛苦的。
FROM scratch #制作一个base image
FROM centos #使用一个base image
FROM ubuntu:18.04 使用其它的image
最佳实践:尽量使用官方的进行作为base image
该关键词定义了一些镜像中的matadata,类似我们写程序中的代码注释,最佳实践:Metadata不可少,LABEL可以添加多个,如下
LABEL maintainer="abc@gmail.com"LABEL version="1.0"LABEL description="this is a description"
就是需要在制作镜像的时候可能需要安装一些软件或者其它命令,最佳实践:每一个RUN就会新生成一层,所有尽量多个命令写到一行,命令太长可使用反斜线换行
RUN yum update && yum install -y vim\python-dev #反斜线换行
RUN apt-get update && apt-get install -y perl\pwgen --no-install-recommends && rm -rf \/var/lib/apt/lists/* #注意清理缓存
RUN /bin/bash -c 'source $HOME/.bashrc;echo $HOME'
设定当前工作目录,类似linux中的cd,
WORKDIR /test #如果没有test目录会自动创建test目录WORKDIR demo #进入到test/demoRUN pwd #输出结果应该是/test/demo
WORKDIR最佳实践:尽量使用WORKDIR,尽量不要使用RUN cd;尽量使用绝对目录
把本地的一些文件添加到镜像中,ADD 不仅能拷贝文件还有解压缩功能,COPY只可以拷贝
ADD hello / #把当前目录的hello文件添加到镜像的根目录ADD test.tar.gz / #添加到根目录并解压缩WORKDIR /rootADD ahello test/ # 此时ahello的目录应该是/root/test/ahelloWORKDIR /rootCOPY bhello test/
最佳实践:大部分COPY优先ADD使用,添加远程的文件或者目录请使用RUN curl或者RUN wget
设置环境常量
ENV MYSQL_VERSION 5.6 #设置常量RUN apt-get install -y mysql-server= "${MYSQL_VERSION}"\&& rm -rf /var/lib/apt/lists/* #引用常量
最佳实践:尽量使用,增加可维护性
存储和网络
EXPOSE:暴露端口,但不映射到宿主机,只被连接的服务访问。
volume:是把宿主机和容器的某个目录做映射,也就是说在容器中访问某个目录,实际上是访问的宿主机的目录,
RUN:执行命令并创建新的镜像层
CMD:设置容器启动后默认执行的命令和参数
ENTRYPOINT:设置容器启动时运行的命令
Shell格式和Exec格式
Shell格式:直接书写命令
RUN apt-get install -y nginx
CMD echo "hello docker"
ENTRYPOINT echo "hello docker 。。。"
Exec格式:使用中括号括起来,命令加参数
RUN ["apt-get","install","-y","vim"]
CMD ["/bin/echo","helo docker"]
ENTRYPOINT ["/bin/bash","-c","/bin/echo helo docker。。。"]
两种格式可以混合,一行用shell格式,一行用Exec格式
CMD命令执行细节:
容器启动时默认执行的命令
如果docker run指定了其他命令,CMD命令会被忽略
如果定义了多个CMD,只有最后一个会执行
假如有这样一个Dockerfile
FROM centosENV name DockeraaaaaaaCMD echo "hello world"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 ,
比如我们启动容器的时候,容器中执行的程序需要参数,我们又不想写死到Dockerfile中,我们可以在启动容器的时候向容器中传递参数,其Dockerfile是这样书写
vi Dockerfile ENTRYPOINT后面的命令必须用中括号,否则传递不了参数
FROM ubuntuRUN apt-get update && apt-get install -y stressENTRYPOINT ["/usr/bin/stress"]CMD []
制作镜像
docker build -t aloxc/stress .
启动容器
docker run -it aloxc/stress --vm 1 --verbose
示例中--vm 1 --verbose就是向容器中传递的参数,这串参数最终就是跟在/usr/bin/stress这个后面,作为其参数。
格式:USER <用户名>[:<用户组>]
USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份。
当然,和 WORKDIR 一样,USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。
RUN groupadd -r redis && useradd -r -g redis redisUSER redisRUN [ "redis-server" ]
如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 gosu。
# 建立 redis 用户,并使用 gosu 换另一个用户执行命令RUN groupadd -r redis && useradd -r -g redis redis# 下载 gosuRUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \&& chmod +x /usr/local/bin/gosu \&& gosu nobody true# 设置 CMD,并以另外的用户执行CMD [ "exec", "gosu", "redis", "redis-server" ]
格式:
HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令。
在没有 HEALTHCHECK 指令前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。在 1.12 以前,Docker 不会检测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。
当在一个镜像指定了 HEALTHCHECK 指令后,用其启动容器,初始状态会为 starting,在 HEALTHCHECK 指令检查成功后变为 healthy,如果连续一定次数失败,则会变为 unhealthy。
HEALTHCHECK 支持下列选项:
--interval=<间隔>:两次健康检查的间隔,默认为 30 秒;--timeout=<时长>:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;--retries=<次数>:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。
FROM nginxRUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*HEALTHCHECK --interval=5s --timeout=3s \CMD curl -fs http://localhost/ || exit 1
这里我们设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应就视为失败,并且使用 curl -fs http://localhost/ || exit 1 作为健康检查命令。
格式:ONBUILD <其它指令>。
ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
Dockerfile 中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD 是为了帮助别人定制自己而准备的。
如有这样的需求,我们写了一个golang应用,我们希望编辑后生成一个镜像,可以有这些方法:
第一种方法build镜像过程太麻烦,第二种方法制作出的镜像太大(因为带有golang环境),有没有一种方法既不麻烦制作出的镜像又小?有,使用多节段构建
FROM golang:1.9-alpine as builderRUN apk --no-cache add gitWORKDIR /go/src/github.com/go/helloworld/RUN go get -d -v github.com/go-sql-driver/mysqlCOPY app.go .RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .FROM alpine:latest as prodRUN apk --no-cache add ca-certificatesWORKDIR /root/COPY --from=0 /go/src/github.com/go/helloworld/app .CMD ["./app"]
我们可以使用 as 来为某一阶段命名,例如
FROM golang:1.9-alpine as builder
例如当我们只想构建 builder 阶段的镜像时,增加 --target=builder 参数即可
$ docker build --target builder -t username/imagename:tag .
上面例子中我们使用 COPY --from=0 /go/src/github.com/go/helloworld/app . 从上一阶段的镜像中复制文件,我们也可以复制任意镜像中的文件。
$ COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
先登录docker.com
docker login
按提示输入用户名和密码
docker image push aloxc/hellodocker:latest#或者docker push aloxc/hellodocker:latest
命令说明
docker image push会饿docker push是push到docker hub的命令
aloxc是docker的用户id
hellodocker是本地制作的镜像,
latest是指tag,
发布完毕后可以在shell中看到结果,也可以在hub.docker.com中看到结果
单机
Bridge Network
Host Network
None Network
多机
Overlay Network
NAT:网络地址转换,当没有公网ip的机器想连接互联网的时候,会经过路由器,路由器再连接互联网。
比如当我们在内网机器192.168.0.12要访问yahoo.com的时候,首先我们内网机器会经过路由器,然后请求dns服务器,找到yahoo.com对应的ip地址,接下来路由器会把请求投递到yahoo对应的服务器上,当yahoo相应并返回的时候,会从请求头中找到请求来源,然后就会向请求来源响应结果,这时候又到我们路由器,路由器上NAT会记录到谁请求的yahoo,再把数据返回给谁。
Volume的类型:
1.受管理的data volume,由docker后台自动创建
2.绑定挂载的volume,具体挂载位置可以由用户指定
使用mysql来做实验,启动一个容器,在启动前先看看有哪些Volume,然后都删除掉
docker volume ls
然后把这些volume都删除掉,删除命令docker volume rm xxxxx
现在启动容器
docker run -d --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
输出:
9fd7ae5ec48c91ca89c724bfd50e46c82bd0bc85b280162c2e6a4e8aee39df9b
容器启动后,查看有哪些volume,
docker volume ls
返回结果9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a
我看下这个volume的详细信息
docker volume inspect 9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a
返回如下结果:
[{"CreatedAt": "2019-04-12T07:29:12Z","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a/_data","Name": "9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a","Options": null,"Scope": "local"}]
从结果看,已经挂载了一个卷到本地的/var/lib/docker/volumes/9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a/_data目录
我们再启动一个容器
docker run -d --name mysql2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
启动后再查看卷列表
docker volume ls
输出如下:
我们现在可以把这两个容器停止并删除
docker stop mysql1 mysql2docker rm mysql1 mysql2
再次查看卷列表,都还在,也就是容器删除并不会删除容器创建的卷
docker volume ls
输出如下:
注意到了没有,这些volume的名字太不友好了,有没办法,有,我们先把刚那两个volume删除掉
docker volume rm 489805b5accc4892f67401740863d20432ad9ed70f529bd108a3a2fe781dd499 9518b293dfe95968f87c788e446ff795b6d553a9f62737a4c858d1ff75a5379a
重新创建mysql1容器,命令的的-v 后面的就mysql1:/var/lib/mysql就是要把容器中/var/lib/mysql映射到本地的mysql1卷,
docker run -d -v mysql1:/var/lib/mysql --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
查看下这个卷的详细信息
docker volume inspect mysql1
[{"CreatedAt": "2019-04-12T07:45:49Z","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/mysql1/_data","Name": "mysql1","Options": null,"Scope": "local"}]
我们现在mysql中创建一个库abc,先进到mysql1容器的shell里面
docker exec -it mysql1 /bin/bash
mysql -urootcreate database abc;show databases;
结果如下所示
的确创建成功了abc这个库。exit退出mysql,再次exit退出容器
现在我们把mysql1停止并删除这个容器,
docker stop mysql1docker rm mysql1
再次启动一个mysql2的容器,指定数据卷,加-v参数并设置映射到刚刚的卷上-v mysql1:/var/lib/mysql
docker run -d -v mysql1:/var/lib/mysql --name mysql2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
启动后进入容器的shell
docker exec -it mysql2 /bin/bash
进入到mysql中,查看刚刚创建的abc库是否在,
mysql -uroot
show databases;
输出如下,结果中是存在我们刚刚创建的abc库的
先编写一个Dockerfile和一个index.html文件
vi Dockerfile
FROM nginx:latestWORKDIR /usr/share/nginx/htmlCOPY index.html index.html
vi index.html
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="Generator" content="EditPlus®"><meta name="Author" content=""><meta name="Keywords" content=""><meta name="Description" content=""><title>hello nginx by aloxc</title></head><body>hello nginx by aloxc,aloxc向nginx打招呼</body></html>
新创建一个镜像
docker build -t aloxc/nginx .
然后创建一个容器
docker run -d -p 8888:80 --name web aloxc/nginx
现在我们的nginx起来了,
curl localhost:8888
返回如下
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="Generator" content="EditPlus®"><meta name="Author" content=""><meta name="Keywords" content=""><meta name="Description" content=""><title>hello nginx by aloxc</title></head><body>hello nginx by aloxc,aloxc向nginx打招呼</body></html>
现在我们把容器停止并删除
docker stop webdocker rm web
重新启动容器,新加参数-v $(pwd):/usr/share/nginx/html
docker run -d -v $(pwd):/usr/share/nginx/html -p 8888:80 --name web aloxc/nginx
接下来我们进入到容器中
docker exec -it web /bin/bash
进到容器中,cd到/usr/share/nginx/html目录
cd /usr/share/nginx/htmlls
结果如下
现在新创建一个文件
touch abc.txt
也就是容器的/usr/share/nginx/html目录下面有三个文件了,我们现在退出容器,看下宿主机的当前目录下有几个文件
ls
结果如下,也是三个文件,
现在我们在宿主机上修改下index.html文件内容,比如修改如下
vi index.html
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="Generator" content="EditPlus®"><meta name="Author" content=""><meta name="Keywords" content=""><meta name="Description" content=""><title>hello nginx by aloxc</title></head><body>hello nginx by aloxc,aloxc向nginx打招呼aaaaaaaaaaaaaaaaaaaaaaaa</body></html>
再次查看下nginx输出,已经变成我们修改的了
结论:也就是容器和宿主机双向修改文件都会同步的
启动mysql容器
docker run -d --name wordpressmysql -v wordpressmysqldata:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=wordpress mysql
启动后,我们看下卷列表应该有一个wordpressmysqldata的。
docker colume ls
启动wordpress容器
docker run -d -e WORDPRESS_DB_HOST=wordpressmysql:3306 --link wordpressmysql -p 80:80 wordpress
Docker Compose 是 Docker 官方编排(Orchestration)项目之一,负责快速的部署分布式应用。分单宿主机和多宿主机情况(swarm),单宿主机可使用-links来连接桥接网络来链接。多宿主机使用overlay来连接。
docker compse是一个工具
这个工具可以通过一个yml文件定义多容器的docker应用
通过一条命令就可以根据yml文件的定义去创建或者管理这多个容器
默认名字docker-compose.yml
docker-compose三个概念
Services
Networks
Volumes
一个示例的docker-compose.yml
version: '3'services:web:image: mysql:5.6.43ports:- 3306networks:- front-tier- back-tiervolumes:- "db-data:/var/lib/mysql" #挂载数据卷redis:build: ./redis #从redis目录的Dockerfile来buildlinks:- webnetworks:- back-tier
命令说明
-f, --file FILE 指定使用的 Compose 模板文件,默认为 docker-compose.yml,可以多次指定。-p, --project-name NAME 指定项目名称,默认将使用所在目录名称作为项目名。--x-networking 使用 Docker 的可拔插网络后端特性--x-network-driver DRIVER 指定网络后端的驱动,默认为 bridge--verbose 输出更多调试信息。-v, --version 打印版本并退出。
命令使用说明
build
格式为 docker-compose build [options] [SERVICE...]。
构建(重新构建)项目中的服务容器。
服务容器一旦构建后,将会带上一个标记名,例如对于 web 项目中的一个 db 容器,可能是 web_db。
可以随时在项目目录下运行 docker-compose build 来重新构建服务。
选项包括:
--force-rm 删除构建过程中的临时容器。
--no-cache 构建镜像过程中不使用 cache(这将加长构建过程)。
--pull 始终尝试通过 pull 来获取更新版本的镜像。
config
验证 Compose 文件格式是否正确,若正确则显示配置,若格式错误显示错误原因。
down
此命令将会停止 up 命令所启动的容器,并移除网络
exec
进入指定的容器
help
获得一个命令的帮助
images
列出 Compose 文件中包含的镜像
kill
格式为 docker-compose kill [options] [SERVICE...]。
通过发送 SIGKILL 信号来强制停止服务容器。
支持通过 -s 参数来指定发送的信号,例如通过如下指令发送 SIGINT 信号。
$ docker-compose kill -s SIGINTlogs
格式为 docker-compose logs [options] [SERVICE...]。
查看服务容器的输出。默认情况下,docker-compose 将对不同的服务输出使用不同的颜色来区分。可以通过 --no-color 来关闭颜色。
该命令在调试问题的时候十分有用。
pause
格式为 docker-compose pause [SERVICE...]。
暂停一个服务容器。
port
格式为 docker-compose port [options] SERVICE PRIVATE_PORT。
打印某个容器端口所映射的公共端口。
选项:
--protocol=proto 指定端口协议,tcp(默认值)或者 udp。
--index=index 如果同一服务存在多个容器,指定命令对象容器的序号(默认为 1)。
ps
格式为 docker-compose ps [options] [SERVICE...]。
列出项目中目前的所有容器。
选项:
-q 只打印容器的 ID 信息。
pull
格式为 docker-compose pull [options] [SERVICE...]。
拉取服务依赖的镜像。
选项:
--ignore-pull-failures 忽略拉取镜像过程中的错误。
push
推送服务依赖的镜像到 Docker 镜像仓库。
restart
格式为 docker-compose restart [options] [SERVICE...]。
重启项目中的服务。
选项:
-t, --timeout TIMEOUT 指定重启前停止容器的超时(默认为 10 秒)。
rm
格式为 docker-compose rm [options] [SERVICE...]。
删除所有(停止状态的)服务容器。推荐先执行 docker-compose stop 命令来停止容器。
选项:
-f, --force 强制直接删除,包括非停止状态的容器。一般尽量不要使用该选项。
-v 删除容器所挂载的数据卷。
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 的指令将无法运行。
scale
格式为 docker-compose scale [options] [SERVICE=NUM...]。
设置指定服务运行的容器个数。
通过 service=num 的参数来设置数量。例如:
$ docker-compose scale web=3 db=2
将启动 3 个容器运行 web 服务,2 个容器运行 db 服务。
一般的,当指定数目多于该服务当前实际运行容器,将新创建并启动容器;反之,将停止容器。
选项:
-t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。
start
格式为 docker-compose start [SERVICE...]。
启动已经存在的服务容器。
stop
格式为 docker-compose stop [options] [SERVICE...]。
停止已经处于运行状态的容器,但不删除它。通过 docker-compose start 可以再次启动这些容器。
选项:
-t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。
top
查看各个服务容器内运行的进程。
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 秒)。
version
格式为 docker-compose version。
打印版本信息。
version: '3'services:wordpress:image: wordpress:latestports:- 80:80environment:WORDPRESS_DB_HOST: mysqlWORDPRESS_DB_PASSWORD: 123456networks:- my-bridgemysql:image: mysql:5.6.43environment:MYSQL_ROOT_PASSWORD: 123456MYSQL_DATABASE: wordpressvolumes:- mysql-data:/var/lib/mysqlnetworks:- my-bridgevolumes:mysql-data:networks:my-bridge:driver: bridge
docker-compose -f docker-compose.yml up
命令说明,-f后面跟的就是文件名,如果文件名是docker-compose.yml,就可以省略 -f 和后面的文件名,up就是说要执行批处理,可以加 -d 在后台执行,
docker-compose的命令列表,docker-compose --help
build Build or rebuild servicesbundle Generate a Docker bundle from the Compose fileconfig Validate and view the Compose filecreate Create servicesdown Stop and remove containers, networks, images, and volumesevents Receive real time events from containersexec Execute a command in a running containerhelp Get help on a commandimages List imageskill Kill containerslogs View output from containerspause Pause servicesport Print the public port for a port bindingps List containerspull Pull service imagespush Push service imagesrestart Restart servicesrm Remove stopped containersrun Run a one-off commandscale Set number of containers for a servicestart Start servicesstop Stop servicestop Display the running processesunpause Unpause servicesup Create and start containersversion Show the Docker-Compose version information
常用命令
docker-compose up -d nginx 构建建启动nignx容器docker-compose exec nginx bash 登录到nginx容器中docker-compose down 删除所有nginx容器,镜像docker-compose ps 显示所有容器docker-compose restart nginx 重新启动nginx容器docker-compose run --no-deps --rm php-fpm php -v 在php-fpm中不启动关联容器,并容器执行php -v 执行完成后删除容器docker-compose build nginx 构建镜像 。docker-compose build --no-cache nginx 不带缓存的构建。docker-compose logs nginx 查看nginx的日志docker-compose logs -f nginx 查看nginx的实时日志docker-compose config -q 验证(docker-compose.yml)文件配置,当配置正确时,不输出任何内容,当文件配置错误,输出错误信息。docker-compose events --json nginx 以json的形式输出nginx的docker日志docker-compose pause nginx 暂停nignx容器docker-compose unpause nginx 恢复ningx容器docker-compose rm nginx 删除容器(删除前必须关闭容器)docker-compose stop nginx 停止nignx容器docker-compose start nginx 启动nignx容器
当我们某个service想创建多个的时候,可以使用此关键词。
比如有这样的场景,我们现在的web流量比较大,我们想多创建一些web服务器,那是不是不要手动创建web呢?这样很麻烦,我们可以在执行docker-compose的时候加上个参数 --scale xxx=n,就会一下创建若干个 xxx 服务,注意,假设我们的xxx服务中指定了端口映射是会出错的,原因就是每台服务器的某个固定端口只有一个。
一般碰到这样的情况,前面还需要加负载均衡设备,比如haproxy等的。
写一个简单的web应用,并打包成war包,名字我们就用docker.war
<%@ page import="java.net.InetAddress" %><%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>李永华的docker tomcat示例页面</title></head><body>李永华的docker tomcat示例页面 <%=System.currentTimeMillis()%><br><%=(InetAddress.getLocalHost()).getHostName()%></body></html>
把war包上传到宿主机的/home/ubuntu/jsp/目录
编写docker-compose.yml文件
version: '3'services:web:image: tomcat:latestvolumes:- /home/ubuntu/jsp:/usr/local/tomcat/webapps/lb:image: dockercloud/haproxylinks:- webports:- 80:80volumes:- /var/run/docker.sock:/var/run/docker.sock
执行启动命令,当前只启动了一个haproxy和一个web
docker-compose -f docker-compose.yml up -d
浏览我们开发的页面
curl http://localhost/docker/
会有以下结果,下面的0971c392da11是主机名称,每次都一样,不会变,
<html><head><title>李永华的docker tomcat示例页面</title></head><body>李永华的docker tomcat示例页面 1555070019650<br>0971c392da11</body></html>
docker-compose -f docker-compose.yml up --scale web=3 -d
我们原来有1台web容器再运行,现在扩展到3台,也就是会再创建2台容器出来,注意蓝色箭头的是Starting的,红色箭头的是Creating的
docker-compose -f docker-compose.yml up --scale web=10 -d
已经有3台,还会创建7台容器出来,注意蓝色箭头的是Starting的,红色箭头的是Creating的
当我们的流量高峰过了后,我们想对web进行减少容器,也用这个命令,会减到2台容器,注意红色箭头是Stoping and removing,蓝色箭头是Starting的
docker-compose -f docker-compose.yml up --scale web=2 -d
links:链接到docker-compose管理的容器,其列表是该docker-compose.yaml中定义的其它服务,注意不可以由交叉链接,注意不建议使用该指令。
external_links:链接到 docker-compose.yml 外部的容器,甚至并非 Compose 管理的外部容器。
external_links:- redis_1- project_db_1:mysql- project_db_1:postgresql
类似 Docker 中的 --add-host 参数,指定额外的 host 名称映射信息。
extra_hosts:- "googledns:8.8.8.8"- "dockerhub:52.1.157.61"
会在启动后的服务容器中 /etc/hosts 文件中添加如下两条条目。
8.8.8.8 googledns52.1.157.61 dockerhub
sysctls
配置容器内核参数。
sysctls:net.core.somaxconn: 1024net.ipv4.tcp_syncookies: 0sysctls:- net.core.somaxconn=1024- net.ipv4.tcp_syncookies=0
ulimits
指定容器的 ulimits 限制值。
例如,指定最大进程数为 65535,指定文件句柄数为 20000(软限制,应用可以随时修改,不能超过硬限制) 和 40000(系统硬限制,只能 root 用户提高)。
ulimits:nproc: 65535nofile:soft: 20000hard: 40000
两种角色: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上执行
docker swarm init --advertise-addr=192.168.0.18
如果你的 Docker 主机有多个网卡,拥有多个 IP,必须使用 --advertise-addr 指定 IP。
执行完毕有如下输出
[node1] (local) root@192.168.0.18 ~$ docker swarm init --advertise-addr=192.168.0.18Swarm initialized: current node (tjum8m7h1dl5tmbf0bud4awme) is now a manager.To add a worker to this swarm, run the following command:docker swarm join --token SWMTKN-1-53xgtit6xqwkp2ig8gbwic3mzeg7qup40f1nn10c1p9hhj62i7-94z5h2jru4tda5wbviaspzs9q 192.168.0.18:2377````在其他两台机器上执行<div class="md-section-divider"></div>```shelldocker swarm join --token SWMTKN-1-53xgtit6xqwkp2ig8gbwic3mzeg7qup40f1nn10c1p9hhj62i7-94z5h2jru4tda5wbviaspzs9q 192.168.0.18:2377
会输出如下内容
This node joined a swarm as a worker.
在192.168.0.18上执行命令
docker node ls
可以看到有三个节点,其中一个是leader节点
该命令类似与dock run命令
docker service create --name demo busybox sh -c "while true;do sleep 3600;done"
查看创建的demo这个service运行在什么地方
docker service ps demos
通过一下命令来扩容
docker service scale demo=5
docker service ps demo ##再看看service的分布情况
现在我们去node2(192.168.0.17)上删除一个容器
docker rm -f 0cca6ab9208b
现在在回到node1(192.168.0.18)上看看还有多少个容器
docker service ps demo
从结果看到没,被我们删除掉了一个,swarm又给我们建立一个起来。
我们再在node1节点执行删除demo操作
docker service rm demo
就删除了,我们过几秒钟去另外两个节点看,也没有了,
先创建overlay network
docker network create -d overlay demo
创建完毕后执行 docker network ls看看结果
接下来创建mysql service
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
创建完毕使用下面命令看看结果
docker service ls
接下来创建wordpress service
docker service create --name wordpress -p 80:80 --env WORDPRESS_DB_PASSWORD=123456 --env WORDPRESS_DB_HOST=mysql --network demo wordpress
创建完毕后使用下面的命令看看结果
docker service ps wordpress
为什么wordpress和mysql不在同一台机器上,wordpress能访问mysql呢?记得开始的时候我们在node1上创建了一个demo的overlay,而wordpress是最终部署到node3上的,我们再node3上docker network ls查看,有了demo的overlay,这样wordpress就能够连接mysql上了,其实是使用的dns服务
重新启动三台机器node1(192.168.0.23)、node2(192.168.0.23)、node3(192.168.0.21)
我们把node1定义为swarm的leader,使用如下命令
docker swarm init --advertise-addr=192.168.0.23
那其他两台就是work节点,在这两个work节点上执行leader节点返回的信息
docker swarm join --token SWMTKN-1-58wwdzjyvuaqamapsw12z54jguqc71jxxnmjyp4eiipjcy5sse-9fg01pnmefaj6pv1lmq3nm9s2 192.168.0.23:2377
现在在看下网络情况,在node1上执行如下命令,可以看到node1是leader节点
docker node ls
现在创建一个overlay网络,名字叫做demo,在node1上执行如下命令
docker network create -d overlay demo
执行完毕后使用docker network ls在node1上看下结果
接下来我们创建一个service(node1上执行),我们使用whoami的service,
docker service create --name whoami -p 8000:8000 --network demo -d jwilder/whoami
执行完毕,我们看下这个服务是在哪台机器上运行
docker service ps whoami
我们再创建 一个busybox,名字叫client
docker service create --name client -d --network demo busybox sh -c "while true;do sleep 3600;done"
过几秒中执行下看看这个client运行在什么地方
docker service ps client
运行在node3,那我们去node3上运行下面的命令看看client的容器id,并进入到容器中
docker ps
docker exec -it dd50 sh
进入到client,我们ping下whoami
ping whoami
通的,实际上是ping的10.0.0.2,这个节点是在node1上的,单实际上这个ip不是node1上的whoami这个容器的ip,我们现在这样做,我们在node1上扩容下这个whoami服务。
docker service scale whoami=2
等待完成。我们看看新扩容的一个容器是部署在什么地方
docker service ps whoami
之前一个是在node1上,这新扩容的就在node2上了,
我们再次回到node3的busybox容器,在容器中执行
ping whoami
从结果来看,还是之前的结果,也就是说,这个whoami不属于任何一个我们创建的whoami容器,而是一个虚拟ip(vip),我们也可以使用nslookup来看看whoami是什么情况。
我们再使用nslookup tasks.whoami看看什么情况
结果中的10.0.0.3和10.0.0.8才是whoami的两个容器中的ip,可以在这两个容器中使用 ip a 看看是否是这样的结果。
我们在client这个容器中可以多次执行如下命令看看结果
wget -q -O - whoami:8000
只会返回两种结果,一个结果应该是node1上的whoami返回的,一个结果是node2上的whoami返回的。实现负载均衡,其实现原理是通过 lvs 来实现的。
Routing mesh的两种体现
我们现在看下whoami的部署情况
docker service ps whoami
结果说明在node1和node2上部署了whoami服务,
我们现在分别在node1、node2和没有部署whoami的node3上执行下
curl localhost:8000
都有结果返回了,现在有个问题,node3是没有部署whoami这个service的,既然也能访问,我们再node3上执行下下面的命令,看看端口转发规则
iptables -nL -t nat
返回的结果中有其中这样的一条
意思是请求本地8000端口就转发到172.19.0.2:8000
再在node3上执行 ifconfig查看下本地网络情况,部分响应结果如下
也就是172.19.0.2 和本机中的172.19.0.1是一个网络。
我们再执行一个命令
brctl show
docker network ls
看看docker_gwbridge这个网络的详细情况
docker network inspect docker_gwbridge
有如下返回(部分)
再执行如下命令看看docker给我们创建了哪些网络
ls /var/run/docker/netns
其中蓝色框框就是我们上上图看到的 ingress_sbox
我们运行下
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中执行命令
iptables -nL -t mangle
可以看到黄框中的,意思是要使用lvs走负载均衡,而后端的服务器就是之前nsloop tasks.whoami出来的结果,
继续在ingress_sbox上执行命令
ipvsadm -l
结果中的10.255.0.8和10.255.0.9就是那两个whoami service的地址之一,我们可以在那两个whoami上使用 ifconfig 验证下,看看是否有这个ip
先编写docker-compose.yml文件
version: '3'services:web:image: wordpressports:- 8080:80environment:WORDPRESS_DB_HOST: mysqlWORDPRESS_DB_PASSWORD: rootnetworks:- my-networkdepends_on:- mysqldeploy:mode: replicatedreplicas: 3restart_policy:condition: on-failuredelay: 5smax_attempts: 3update_config:parallelism: 1delay: 10smysql:image: mysqlenvironment:MYSQL_ROOT_PASSWORD: rootMYSQL_DATABASE: wordpressvolumes:- mysql-data:/var/lib/mysqlnetworks:- my-networkdeploy:mode: globalplacement:constraints:- node.role == managervolumes:mysql-data:networks:my-network:driver: overlay
在node1上执行
docker stack deploy wordpress --compose-file=docker-compose.yml
部署完毕后使用看看情况
docker stack ls
什么是Secret?
用户名和密码
ssh key
tls认证
任何不想被其它人看到的信息
这些信息不想写到docker-compose.yml文件
看看swarm架构图
Secret管理
存在Swarm Manager节点 Raft database里面
秘密可以分配给一个service,这个service就能看到这个秘密
在容器内部秘密看起来像文件,但实际是在内存中
中文件中创建密码
新建一个文件,abc.txt文件,在文件中输入admin123,然后使用下面命令
docker secret create my-pw abc.txt
通过输入创建,
echo "admin123" | docker secret create my-pw2 -
创建完毕后使用下面命令看看有哪些
docker secret ls
service中使用创建的密码,创建service的时候带上--secret参数
docker service create --name client --secret my-pw busybox sh -c "while true;do sleep 3600;done"
进到busybox中,
docker exec -it client sh
ls /run/secrets/
就能看到我们传进去的my-pw,使用cat命令就能看到内容为我们设置的admin123.
可以传若干个secret,只需要多个 --secret参数。
应用:我们新建一个mysql的service,可以指定他的密码是由什么文件提供的,
docker service create --name db --secret my-pw -e MYSQL_ROOT_PASSWORD=/run/secrets/my-pw mysql
进到mysql这个容器,
docker exec -it db sh
cat /run/secrets/my-pw
输出
mysql -u root -p
输入admin123,进到mysql的shell中了,证明通过了。
在node1上执行输入
echo "admin123" | docker secret create my-pw -
新建docker-compose.yml
version: '3'services:web:image: wordpressports:- 8080:80secrets:- my-pwenvironment:WORDPRESS_DB_HOST: mysqlWORDPRESS_DB_PASSWORD_FILE: /run/secrets/my-pwnetworks:- my-networkdepends_on:- mysqldeploy:mode: replicatedreplicas: 3restart_policy:condition: on-failuredelay: 5smax_attempts: 3update_config:parallelism: 1delay: 10smysql:image: mysqlsecrets:- my-pwenvironment:MYSQL_ROOT_PASSWORD_FILE: /run/secrets/my-pwMYSQL_DATABASE: wordpressvolumes:- mysql-data:/var/lib/mysqlnetworks:- my-networkdeploy:mode: globalplacement:constraints:- node.role == managervolumes:mysql-data:networks:my-network:driver: overlay# secrets:# my-pw:# file: ./password
docker stack deploy wordpress --compose-file=docker-compose.yml
在node1上创建overlay网络
docker network create -d overlay demo
创建service
docker service create --name web --publish 8080:5000 --network demo xiaopeng163/python-flask-demo:1.0
扩展下web 的 service 数量
docker service scale web=2
我们再某台机器上执行一下脚本
sh -c "while true;do curl localhost:8080 && sleep 1;done;"
现在在swarm leader节点执行一下代码更新,示例是从image更新
docker service update --image xiaopeng163/python-flask-demo:2.0 web
现在切换到执行脚本的机器上观察,过了一会就出现新的结果了,
看到了没,有新的结果出来了,
更新完毕后,我们再在leader节点node1上执行下面命令
docker service ps web
有两个shutdown了,有两个正在运行,证明我们更新是成功的。
我们再通过端口更新下试试:使用端口更新会中断业务,使用image更新不会中断业务
docker service updae --publish 8080:5000 --publish-add 8088:500 web
架构图