@aloxc
2019-11-19T03:54:27.000000Z
字数 31525
阅读 421
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 update
sudo 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 docker
sudo systemctl start docker
#启动docker
sudo systemctl enable docker
sudo systemctl start docker
#停止docker
sudo systemctl stop docker
新建或者编辑配置文件 /etc/docker/daemon.json
添加如下代码
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn/"]
}
保存退出后重启docker
sudo systemctl restart docker
#示例拉取mysql最后的版本latest
sudo docker pull mysql
#示例拉取mysql的5.6.43,注意5.6.43是tag
sudo 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 docker
sudo gpasswd -a ubuntu docker
sudo service docker restart
重新进入shell
sudo docker container ls
sudo docker inspect [container id]
sudo docker logs [container id]
导出容器
$ docker container ls -a
CONTAINER ID ……
7691a814370e ……
$ docker export 7691a814370e > ubuntu.tar
导入容器
将容器导入为镜像
$ cat ubuntu.tar | docker import - test/ubuntu:v1.0
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
test/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 main
import "fmt"
func main() {
fmt.Println("你好,docker!本例使用golang创建的可执行文件")
}
进到该程序的目录执行编译生成hellodocker
go build -o "hellodocker"
编写Dockerfile
vi Dockerfile
FROM scratch
ADD 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 id
docker 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 vim
exit
在宿主机执行下面命令,罗列出所有的容器
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 centos
docker history aloxc/centos-vim
不推荐使用原来有的容器创建一个新的镜像
新建Dockerfile,vi Dockerfile
FROM centos
RUN 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:v2
sha256: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/demo
RUN pwd #输出结果应该是/test/demo
WORKDIR最佳实践:尽量使用WORKDIR,尽量不要使用RUN cd;尽量使用绝对目录
把本地的一些文件添加到镜像中,ADD 不仅能拷贝文件还有解压缩功能,COPY只可以拷贝
ADD hello / #把当前目录的hello文件添加到镜像的根目录
ADD test.tar.gz / #添加到根目录并解压缩
WORKDIR /root
ADD ahello test/ # 此时ahello的目录应该是/root/test/ahello
WORKDIR /root
COPY 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 centos
ENV name Dockeraaaaaaa
CMD 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 ubuntu
RUN apt-get update && apt-get install -y stress
ENTRYPOINT ["/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 redis
USER redis
RUN [ "redis-server" ]
如果以 root
执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 su
或者 sudo
,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 gosu
。
# 建立 redis 用户,并使用 gosu 换另一个用户执行命令
RUN groupadd -r redis && useradd -r -g redis redis
# 下载 gosu
RUN 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 nginx
RUN 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 builder
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest as prod
RUN apk --no-cache add ca-certificates
WORKDIR /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 mysql2
docker 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 -uroot
create database abc;
show databases;
结果如下所示
的确创建成功了abc这个库。exit退出mysql,再次exit退出容器
现在我们把mysql1停止并删除这个容器,
docker stop mysql1
docker 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:latest
WORKDIR /usr/share/nginx/html
COPY 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 web
docker 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/html
ls
结果如下
现在新创建一个文件
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.43
ports:
- 3306
networks:
- front-tier
- back-tier
volumes:
- "db-data:/var/lib/mysql" #挂载数据卷
redis:
build: ./redis #从redis目录的Dockerfile来build
links:
- web
networks:
- 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 SIGINT
logs
格式为 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:latest
ports:
- 80:80
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_PASSWORD: 123456
networks:
- my-bridge
mysql:
image: mysql:5.6.43
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: wordpress
volumes:
- mysql-data:/var/lib/mysql
networks:
- my-bridge
volumes:
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 services
bundle Generate a Docker bundle from the Compose file
config Validate and view the Compose file
create Create services
down Stop and remove containers, networks, images, and volumes
events Receive real time events from containers
exec Execute a command in a running container
help Get help on a command
images List images
kill Kill containers
logs View output from containers
pause Pause services
port Print the public port for a port binding
ps List containers
pull Pull service images
push Push service images
restart Restart services
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
top Display the running processes
unpause Unpause services
up Create and start containers
version 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:latest
volumes:
- /home/ubuntu/jsp:/usr/local/tomcat/webapps/
lb:
image: dockercloud/haproxy
links:
- web
ports:
- 80:80
volumes:
- /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 googledns
52.1.157.61 dockerhub
sysctls
配置容器内核参数。
sysctls:
net.core.somaxconn: 1024
net.ipv4.tcp_syncookies: 0
sysctls:
- net.core.somaxconn=1024
- net.ipv4.tcp_syncookies=0
ulimits
指定容器的 ulimits 限制值。
例如,指定最大进程数为 65535,指定文件句柄数为 20000(软限制,应用可以随时修改,不能超过硬限制) 和 40000(系统硬限制,只能 root 用户提高)。
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 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.18
Swarm 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>
```shell
docker 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: wordpress
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_PASSWORD: root
networks:
- my-network
depends_on:
- mysql
deploy:
mode: replicated
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
update_config:
parallelism: 1
delay: 10s
mysql:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress
volumes:
- mysql-data:/var/lib/mysql
networks:
- my-network
deploy:
mode: global
placement:
constraints:
- node.role == manager
volumes:
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: wordpress
ports:
- 8080:80
secrets:
- my-pw
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/my-pw
networks:
- my-network
depends_on:
- mysql
deploy:
mode: replicated
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
update_config:
parallelism: 1
delay: 10s
mysql:
image: mysql
secrets:
- my-pw
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/my-pw
MYSQL_DATABASE: wordpress
volumes:
- mysql-data:/var/lib/mysql
networks:
- my-network
deploy:
mode: global
placement:
constraints:
- node.role == manager
volumes:
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
架构图