Docker概览
运维视角
Docker安装:
- 先删除以前安装的docker:
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
- 添加yum相关工具:
yum install -y yum-utils
- 添加阿里yum库:
yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
- 安装:
yum install docker-ce docker-ce-cli containerd.io
- 启动:
systemctl start docker
- 设置开机启动:
systemcetl enable docker
- 添加国内镜像库服务,修改配置文件(没有的话新建)
/etc/docker/daemon.json
,然后重启docker:
{
"registry-mirrors": ["https://xxxxx.mirror.aliyuncs.com"]
}
在安装docker时,会涉及两个主要组件:docker客户端和docker daemon(docker引擎或docker服务端)。默认情况下,客户端与daemon通过本地套接字通信(/var/run/docker.sock),或者网络通信,docker daemon默认会监听2375或2376端口(TLS)。由于docker命令需要root权限,所以可以将用户添加到安装docker时自动创建的docker组里:sudo usermod -aG docker [username]
。
镜像
可以将docker镜像粗略理解为一个包含os文件系统和应用的对象。
查看镜像:docker image ls
拉取镜像:docker pull ubuntu:latest
启动容器:docker run -it ubuntu:latest /bin/bash
,-it
表示开启容器的交互模式,将当前Shell连接到容器终端。/bin/bash
表示用户想在docker里运行bash进程,实际上就是后文要讲的runc组件启动的程序,也是容器里当前唯一的进程(大部分容器只会运行一个进程)。容器随其中运行的应用关闭而终止,在这里就是bash退出后容器就停止了。容器相关命令还有:docker container start|stop|rm [container name|container ID]
。
ctrl-PQ组合键可以在退出容器的同时还保持容器的运行,再执行docker container exec -it [container ID] /bin/bash
会让容器再运行一个bash并连接上,此时再用exit
退出容器里的bash并不会停止容器,因为容器里还有一个bash。
查看运行中的容器:docker container ls
查看所有容器,包括已停止的:docker container ls -a
连接到运行中的容器:docker container exec -it mycontainer /bin/bash
开发视角
从开发者的视角来看,运行的容器就是应用进程,换句话说就是容器化的进程,一个属于特殊命名空间的应用进程。
构建镜像
将应用容器化,应用文件目录如下:
可以看出在项目文件目录里有一个Dockerfile的文件,内容是:
FROM alpine
LABEL maintainer="nigelpoulton@hotmail.com"
RUN apk add --update nodejs nodejs-npm
COPY . /src
WORKDIR /src
RUN npm install
EXPOSE 8080
ENTRYPOINT ["node", "./app.js"]
跳转到项目目录里,然后根据Dockerfile构建名为test:latest的镜像:docker image build -t test:latest .
启动容器:docker container run -d --name web1 --publish 8080:8080 test:latest
Docker引擎
Docker引擎采用的是模块化设计,其组件是可以替换的。
daemon的功能主要有镜像管理、镜像构建、与Docker client的交互、身份验证、安全特性、卷、核心网络以及编排。
runc是一个命令行工具,它只有一个作用:创建容器。
shim是一个实现容器与daemon解耦的工具,containerd每启动一个容器都会fork一个runc,不过一旦容器启动完毕,runc就会退出,一旦runc退出,对应的shim进程就会成为容器的父进程,其主要职责是保持STDIN和STDOUT流是开启状态,避免daemon重启时导致容器关闭,以及将容器的退出状态返回给daemon。
containerd是一个单独的守护进程,它的主要任务是容器的生命周期的管理——start|stop|pause|rm...
containerd还可完成一些诸如push和pull镜像的操作,但这些操作都是模块化可选的。
上面的daemon、containerd、shim、runc在Linux系统中均是以二进制文件形式存在,在宿主机通过ps命令均可以查看到对应的进程。
Docker镜像
镜像其实就是多个只读层文件的叠加。而容器则是在此基础上增加了一个读写层,并且在容器运行时,会映射出一个完整的叠加后的文件目录在对应容器的merged目录下。因为容器的运行依赖它下面的只读层,所以删除镜像前必须先删除以此镜像为基础的容器(哪怕是已经停止的容器),否则无法删除该镜像。删除镜像命令:docker image rm [image ID]|[image tag]
,删除全部镜像:docker image rm $(docker image ls -q) -f
。镜像文件通常位于/var/lib/docker/overlay2/
。
镜像通常都比较小,镜像里并不包含内核——容器都是共用宿主机的内核。所以说容器仅包含必要的操作系统(通常指的是操作系统文件和文件系统对象)
拉取镜像:docker image pull ubuntu:latest
镜像仓库服务:
docker客户端连接的镜像仓库服务是可配置的,默认值Docker Hub。在Docker Hub里还分官方仓库和第三方仓库。
一个镜像可以拥有多个标签。 docker image ls --filter dangling=false
可用于过滤没有标签的镜像(如果新镜像的标签和旧镜像的标签冲突,则会移除旧镜像的标签)。docker image prune
可用于移除全部的悬虚(没有标签)镜像,如果再带上-a
则还会移除所有没有被任何容器使用的镜像。
docker search [NAME]
用于搜索镜像。NAME字段可以是仓库名、你的Docker ID或者非官方仓库的组织名称。docker search [NAME] --filter "is-official=true"
可以只返回官方仓库。默认情况下docker search
只返回25行结果,可以通过--limit
来指定返回行数,最多100行。
查看镜像分层:docker image inspect ubuntu:latest
所有的Docker镜像都始于一个基础镜像,当增加内容时,就会在当前镜像的基础上新增一层镜像。
容器运行时,镜像层会合并在对应容器的merged目录下,容器停止后,merged目录会消失,但新增或修改的内容会存在diff目录下。
不同镜像还会共享可读镜像层。
拉取指定摘要的镜像:
docker image pull nginx@sha256:aeade65e99e5d5e7ce162833636f692354c227ff438556e5f3ed0335b7cc2f1b
显示镜像的摘要:docker image ls --digests nginx
Docker容器
Docker容器重启策略:
除非运行中的容器被明确要求停止(如docker container stop
),否则一直尝试重启:docker container run --name neverdie -it --restart always ubuntu /bin/bash
。不过always
策略会使停止的容器在daemon重启时被启动,而unless-stoped
策略正好相反。on-failure
策略会在容器退出但返回不是0的情况下重启,daemon重启时,这种策略下的停止容器也会重启。
删除所有容器:docker container rm $(docker container ls -aq) -f
docker container inspect [container name|container ID]
可以显示容器的配置细节和运行时信息。
应用容器化
接下来详细说明下应用容器化的流程,项目的目录结构如下,这是一个简单的node.js应用:
对于Docker来说,项目目录通常称为构建上下文,而Dockfile文件一般放在项目的根目录下。
项目目录里有个Dockfile文件,里面的内容如下:
FROM alpine
LABEL maintainer="nigelpoulton@hotmail.com"
RUN apk add --update nodejs nodejs-npm
COPY . /src
WORKDIR /src
RUN npm install
EXPOSE 8080
ENTRYPOINT ["node", "./app.js"]
Dockerfile文件主要有这样几个主要任务:配置应用运行所需的环境、将应用代码拷入镜像中、配置应用启动方式,最后指导Docker将这些信息进行容器化,也就是生成一个完整镜像。接下来逐行分析Dockfile文件里的每行命令:
- FROM alpine:表示以alpine作为基础镜像,后续命令若有新增或修改内容则会新增镜像在基础镜像之上。
- LABEL maintainer="nigelpoulton@hotmail.com":为镜像添加一个键值对元数据(镜像本身是一个配置对象),不会增加新的镜像层。
- RUN apk add --update nodejs nodejs-npm:运行alpine系统里的包管理器来安装nodejs和npm,并在基础镜像层之上新建一个镜像层来存储这些程序。实际上在这里会先将基础镜像层运行起来,然后在运行的容器里使用apk来安装nodejs和npm,最后commit生成一个新镜像层并删除临时容器。
- COPY . /src:生成一个新镜像,将当前目录下的构建上下文拷贝到alpine系统里的/src目录下。
- WORKDIR /src:将alpine里的/src目录作为Dockfile文件里后续还未执行的命令的工作目录,这不会新增镜像层。
- RUN npm install:在当前工作目录运行npm install命令,并新增镜像层。
- EXPOSE 8080:仅表示要暴露的端口,无其它作用,作为元数据存在镜像配置里,不会新增镜像层。
- ENTRYPOINT ["node", "./app.js"]:指定容器启动时默认启动的程序,同样作为元数据,不会新增镜像层。
最后的镜像层组成如下:
最后进行应用容器化(生成镜像):docker image build -t web:latest .
推送镜像到镜像仓库服务
在推送镜像前,Docker需要3个关于镜像的信息,分别是镜像仓库服务、镜像仓库、镜像标签:
镜像仓库服务和镜像标签有默认值,可以不用指定。但由于我们没有官方仓库的访问权限,所以往往我们需要推送到以自己Docker ID命名的二级命名空间下,所以在推送前要给镜像添加一个新仓库名:docker image tag web:latest mydockerid/web:latest
。然后再推送:docker image push mydockerid/web:latest
。
docker image history
命令可以用来查看构建该镜像执行了哪些命令。
多阶段构建
多阶段构建,简单来讲就是在一个Dockfile文件里有多个构建阶段,每个阶段都会构建自己的镜像,然后在构建最后一个阶段时,会直接从前面已构建好的镜像里拷贝所需文件,从而避免在最后的应用镜像里有过多不必要的文件,如包依赖等。
利用构建缓存:
合并镜像:
docker image build --squash
合并镜像层不好的一点是它里面的镜像层不能再分享,并且在push或pull时操作的镜像体积更大。但如果镜像层太多,合并还是一个不错的选择。
Docker Compose
安装Docker Compose:
- 下载二进制文件:
curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
- 添加可执行权限:
chmod +x /usr/local/bin/docker-compose
Docker Compose可以用来管理和部署一个多服务(容器)应用。Docker Compose使用YAML文件来定义多服务应用,也可以使用JSON。Docker Compose默认使用文件名docker-compose.yml。
下面是一个简单的Compose文件示例,它定义了一个包含两个服务(web-fe和redis)的小型Flask应用。这是一个能够对访问者进行计数并将其保存到Redis的简单的Web应用:
#必须以此开头,它指定了该Compose文件的版本格式
version: "3.5"
#若干服务
services:
#定义名叫web-fe的服务
web-fe:
#指定构建镜像的上下文路径,在这里就是指引Docker基于当前目录下的上下文和Dockfile来构建一个镜像
build: .
#指定容器运行时要运行的程序,其实也可以不在这里指定
command: python app.py
#指定端口映射,target是容器内端口,published是主机端口
ports:
- target: 5000
published: 5000
#指定连接的网络
networks:
- counter-net
#将counter-vol卷挂载到容器内的/code目录下
volumes:
- type: volume
source: counter-vol
target: /code
#定义名叫redis的服务
redis:
#让Docker基于镜像redis:alpine启动一个容器
image: "redis:alpine"
#让redis容器连接到counter-net网络
networks:
counter-net:
#指引Docker创建新的网络,默认情况下会创建bridge网络。也可以常见其它网络:
# networks:
# other-net:
# driver: overlay
# attachable: true
networks:
counte-net:
#指引Docker创建新的卷
volumes:
counter-vol:
然后跳转到Compose服务的项目目录下,在这个例子中就是web-fe项目目录下,假设目录名为counter-app,开始部署:docker-compose up -d
,这个命令会先创建网络和卷,然后构建所需镜像,并启动容器。-d
表示后台运行compose。还有这个命令会默认在当前目录查找docker-compose.yml文件,你也可以用-f
命令指定文件。
需要一提的是,在docker compose为services里的每个服务部署容器时,还会使用对应的key作为容器名字的一部分,例如:
实际上,Docker Compose会将项目名(counter-app)作为compose文件里所有资源名的前缀。
Docker Compose管理应用
docker-compose down
命令会先关闭应用和相关容器并删除容器,然后删除网络。但docker-compose up过程中拉取和构建的镜像不会删除,卷也不会删除。
docker-compose ps
可以查看应用相关服务的状态。
docker-compose top
:
docker-compose stop
会停止应用,但不会删除资源。对于已停止的应用,可以用docker-compose rm
来删除容器和网络,也不会删除镜像和卷。
docker-compose restart
用于重启,如果用户在停止应用后对compose文件进行了修改,那么重启不会使这些修改生效,只能重新部署。
docker volume inspect [volume name] | grep Mount
可用于查看卷在Docker主机的什么位置,改变这里的内容会实时反应在运行中的容器里。
Docker Swarm
Docker Swarm主要包含两个方面:一个是企业级Docker安全集群,Swarm可以将一个或多个Docker节点(docker daemon)组织起来,使得用户能够以集群方式管理它们,用户可以自如地添加或删除节点;另一个是微服务应用的编排,Swarm提供了一套丰富的API用来部署和管理微服务应用,通过将应用定义在声明式配置文件中,就可以使用原生的Docker命令完成部署,还支持滚动升级、回滚以及扩容操作。
从集群的交读来说,一个Swarm由一个或多个Docker节点组成,节点会被配置为管理节点和工作节点。Swarm的配置和状态信息保存在etcd里,它作为Swarm的一部分被安装。同时,在集群里Swarm使用TLS进行通信加密、节点认证和角色授权以及自动密钥轮换。
对于应用编排,Swarm中的最小调度单位是服务,是一个基于容器的更高层次的概念,当容器被封装在一个服务中时,我们称之为一个任务或一个副本,一个服务也具有扩缩容、滚动升级以及简单回滚等特性。
Swarm集群示例:
Swarm集群里的节点情况如下:
每个节点都需要安装Docker,并且能够与Swarm的其它节点通信。不包含在任何Swarm中的Docker节点称为运行于单引擎模式,而一旦被加入Swarm集群,则称为Swarm模式。在安装Docker时,需要在路由器和防火墙中开放如下端口:
满足以上前提后,就可以着手开始搭建Swarm集群了:
在单引擎模式下的Docker主机上运行docker swarm init
会将其切换到Swarm模式,并创建一个新的Swarm,将自身设置为Swarm的第一个管理节点。其它节点则可以作为管理节点或工作节点加入进来,这一操作也会将新加入的节点切换为Swarm模式。
- 登录到mgr1并初始化一个新的Swarm。
docker swarm init --advertise-addr 10.0.0.1:2377 --listen-addr 10.0.0.1:2377
--advertise-addr指定其它节点用来连接到当前节点的IP和端口,如果你的机器只有一个 IP 地址,可以省略,docker 会自动选择正确的 IP。当节点上有多个IP时,可以用于指定使用哪个IP。此外,还可以指定一个节点上没有的IP,比如一个负载均衡的IP。
--listen-addr指定用于承载Swarm流量的IP和端口。其设置通常与--advertise-addr相匹配,但是当节点上有多个IP的时候,可用于指定具体某个IP。并且,如果--advertise-addr设置了一个远程IP地址(如负载均衡的IP地址),该属性也是需要设置的。
-
查看Swarm当前节点。
-
在mgr1上执行
docker swarm join-token
命令来获取添加新的工作节点和管理节点到Swarm的命令和Token。
-
登录wrk1,并使用包含工作节点接入Token的
docker swarm join
命令将其接入Swarm。
在wrk2和wrk3上重复上一步骤来将这两个节点作为工作节点加入Swarm。
-
在mgr2作为管理节点接入Swarm。
在mgr3重复上一个操作。
-
在任意一个管理节点上执行
docker node ls
命令列出Swarm节点。
管理节点HA:
内置的Swarm安全机制:
Swarm集群内置有繁多的安全机制,并提供了开箱即用的合理默认配置——如CA设置、接入Token、公用TLS、加密集群存储、加密网络、加密节点ID等。
锁定Swarm:
Swarm服务
例如我们要部署有5个实例的某web应用,先登录到主管理节点,然后使用命令docker service create
。
上图可以看出,其参数与docker container run
有很多相同的。使用该命令后,主管理节点会在Swarm集群中实例化5个副本——请注意管理节点也会作为工作节点运行。相关各工作节点或管理节点会拉取镜像,然后启动相应容器。
docker service ls
用于查看Swarm中所有运行中的服务。docker service ps [service name|service ID]
用于查看服务副本列表及各副本的状态。
docker service inspect --pretty [service name|service ID]
用于查看更详细的信息。
副本模式和全局模式:
副本模式会部署期望数量的服务副本,并尽可能均匀地将各个副本分布在整个集群中。而全局模式下每个节点上仅运行一个副本。默认是副本模式,可以通过给docker service create
命令传--mode global
参数来部署一个全局服务。
服务的扩缩容:
docker service scale
用于对服务进行扩缩容。
删除服务:
docker service rm [service name|service ID]
滚动升级:
登录主管理节点,执行docker network create -d overlay uber-net
,该命令会创建一个名为uber-net的覆盖网络。再执行docker service create --name uber-svc --network uber-net -p 80:80 --replicas 12 nigelpoulton/tu-demo:v1
该命令会创建一个新的服务,使用v1镜像,并将其接入uber-net网络。
对与Swarm的网络,默认是入站模式,该模式下即使节点上没有服务的副本也会开放对应端口;此外还有主机模式,仅在运行有容器副本的节点上开放端口。下面的命令是以主机模式创建服务:docker service create --name uber-svc --network uber-net --publish published=80,target=80,mode=host --replicas 12 nigelpoulton/tu-demo:v1
。在入站模式下,即使某个节点上并没有运行服务的副本,依然可以进行访问——所有节点都配置有映射,因此会将请求转发给运行有服务副本的节点。
docker service update --image nigelpoulton/tu-demo:v2 --update-parallelism 2 --update-delay 20s uber-svc
该命令会对uber-svc进行升级,每次更新两个副本,并且间隔20s,使用新镜像v2。
查看日志:
docker service logs [service name|service ID]
Docker网络
Docker的网络的部分都是由Libnetwork实现,它是一个独立于docker daemon的库,是CNM设计规范的实现。
单机桥接网络:
docker network create -d bridge
会调用Libnetwork内置的网桥驱动去创建一个虚拟网桥(Docker有个默认网桥,映射在主机上的docker0),当Docker运行一个容器时,会调用Libnetwork去创建一对veth虚拟网卡,然后将这对虚拟网卡的一个放在容器里(也就是对应的网络命名空间),一个放在虚拟网桥上。以上这些是我的分析,可能不正确。
Linux上默认的Bridge网络是不支持通过Docker DNS服务进行域名解析的,而自定义桥接网络可以。
macvlan:
查看daemon日志:
查看容器日志:
服务发现:
Ingress网络:
Docker网络命令总结:
Docker覆盖网络
创建一个覆盖网络十分容易:
- 创建一个Swarm集群;
- 登录主管理节点,然后创建一个overlay网络,此时其它节点是看不到这个网络的,并且网络的控制层已是加密,带上-o encrypted还可以对数据层加密;
- 创建一个服务,此时所有节点都会自动连接到这个overlay网络,并可以通过容器名或ip地址相互ping通。
Docker卷
docker container run --mount source=[volume name],target=/vol [image name]
创建一个容器,并挂在一个卷(没有的话自动新建)到容器里的/vol目录下。Swarm模式下创建服务时也可以带上--mount
。
Docker Stack
用Docker Stack来部署总体来说比较容易,简单来说就是用Compose(也称Stack文件)文件定义应用,然后通过docker stack deploy命令完成部署管理。
下面以一个简单的应用举例说明:
最重要的就是项目里的Stack文件,这里先简单写下这个文件:
version: "3.2"
services:
reverse_proxy:
image: dockersamples/atseasampleshopapp_reverse_proxy
ports:
- "80:80"
- "443:443"
secrets:
- source: revprox_cert
target: revprox_cert
- source: revprox_key
target: revprox_key
networks:
- front-tier
database:
image: dockersamples/atsea_db
environment:
POSTGRES_USER: gordonuser
POSTGRES_DB_PASSWORD_FILE: /run/secrets/postgres_password
POSTGRES_DB: atsea
networks:
- back-tier
secrets:
- postgres_password
deploy:
placement:
constraints:
- 'node.role == worker'
appserver:
image: dockersample/atsea_app
networks:
- fron-tier
- back-tier
- payment
deploy:
replicas: 2
update_config:
parallelism: 2
failure_action: rollback
placement:
constraints:
- 'node.role == worker'
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
secrets:
- postgres_password
visualizer:
image: dockersample/visualizer:stable
ports:
- "8001:8080"
stop_grace_period: 1m30s
volumes:
- "var/run/docker.sock:/var/run/docker/sock"
deploy:
update_config:
failure_action: rollback
placement:
constraints:
- 'node.role == manager'
payment_gateway:
image: dockersamples/atseasampleshopapp_payment_gateway
secrets:
- source: staging_token
target: payment_token
networks:
- payment
deploy:
update_config:
failure_action: rollback
placement:
constraints:
- 'node.role == worker'
- 'node.labels.pcidss == yes'
networks:
front-tier:
back-tier:
payment:
driver: overlay
driver_opts:
encrypted: 'yes'
secrets:
postgres_password:
external: true
staging_token:
external: true
revprox_key:
external: true
revprox_cert:
external: true
可以看出上面的Stack文件有4个顶级key,接下来详细分析每个顶级key:
networks
可以看出上面定义了3个网络,默认情况下,这些网络都会采用overlay驱动。payment网络较为特殊,它增加了数据层加密的功能,也可以在docker network create命令中指定-o encrypted参数来实现数据层加密。
secrets
注意到4个密钥都被定义为external,这意味着在Stack部署之前,这些密钥必须存在。
services
image关键字是服务里唯一必选项,Docker Stack和Docker Compose的一个区别是Stack不支持构建,这意味着在部署Stack之前,所有镜像必须是提前构建好的。仓库服务名是可选的,默认会从Docker Hub拉取。
默认情况下,所有端口映射都采用Ingress模式,这意味着Swarm集群中每个节点的对应端口都会映射并且是可访问的,即使是那些没有运行副本的节点。另一种方式是Host模式,端口只映射到运行副本的Starm节点,但是Host模式需要使用完整的配置:
ports:
- target: 80
published: 80
mode: host
secrets关键字表面这个服务用来两个密钥,这两个密钥必须在顶级关键字secrets下定义了,并且必须在系统上已经存在。部署后,密钥以普通文件的形式被挂载到服务副本当中,文件的名称就是Stack文件中定义的target属性的值,其在Linux下的路径为/run/secrets,例如/run/secrets/revprox_cert和/run/secrets/revprox_key。还可以通过docker service create --sercret [secret name]
暴露某个secret给某个服务。
networks关键字确保服务的所有副本都会连接到front-tier网络,网络相关定义必须位于顶级关键字networks之下,如果定义的网络不存在,Docker会以Overlay网络方式新建一个网络。
deploy关键字保证了当前服务只会运行在Swarm集群的worker节点之上。Swarm还支持以下约束:
在appserver里,replicas关键字设置期望服务的副本数量为2,默认值为1。update_config定义了Docker在服务滚动升级的时候具体如何操作,在这个例子里,Docker每次会更新两个副本。failure_action规定了升级失败后自动回滚,failure_action的默认操作是pause,会在服务升级失败后阻止其它副本的升级,还支持continue操作。
restart-policy定义了Swarm针对容器异常退出的重启策略。在这个例子中,如果某个副本以非0返回值退出(conditon: onfailure),会立即重启当前副本,重启最多重试3次,每次都会等待至多120s来检测是否启动成功,每次重启的间隔是5s。
当docker要停止某个容器时,会给容器内部PID为1的进程发送SIGTERM信号,容器内PID为1的进程会有10s的时间来执行一些清理操作,如果进程没有处理该信号,那么10s后就会被SIGKILL信号强制结束。而stop_grace_period关键字可以修改这个10s时间。volumes关键字用于挂载提前创建的卷或者主机目录到某个服务副本当中,在本例中,会挂载Docker主机的/var/run/docker.sock目录到每个服务副本的/var/run/docker.sock路径,这意味着在服务副本中任何对/var/run/docker.sock的读写操作都会指向Docker主机的对应目录中。
/var/run/docker/docker.sock恰巧是Docker提供的IPC套接字,Docker daemon通过该套接字对其它进程暴露其API,这意味着给予了容器查询和管理daemon的能力。
在payment_gateway服务里多了个node.label约束,这是一个标签约束,可以通过docker node update命令来自定义标签,并添加到Swarm集群的指定节点,node.label配置只适用于Swarm集群中的节点,不能用于单独的容器。
部署Stack应用
在进行部署之前,有几个前置处理需要完成:
- Swarm模式:应用将采用Docker Stack部署,而Stack依赖Swarm模式。
- 标签:某个Swarm worker节点需要自定义标签
-
密钥:应用所需的密钥需要在部署前创建完成
上图中给wrk-1节点添加标签可以在管理节点上执行一下命令:
docker node update --label-add key=value wrk-1
应用定义了4个密钥,这些都要在部署前创建好。在管理节点上运行下面的命令:
openssl req -newkey rsa:4096 -nodes -sha256 -keyout domain.key -x509 -days 365 -out domain.crt
这个命令会创建一个证书和对应的私钥。
下面分别创建revprox_cert、revprox_key、postgree_password、stage_token:
docker secret create revprox_cert domain.crt
docker secret create revprox_key domain.key
docker secret create postgres_password domain.key
echo staging | docker secret create staging_token -
开始部署:
docker stack deploy -c docker-stack.yml seastack
如果想查看具体某个服务的详细信息,可以使用docker service logs命令,需要将服务名称/ID或者副本ID作为参数传入。如果传入服务名称或ID,可以看到所有服务副本的日志信息,如果传入的是副本ID,只会看到对应副本的日志信息。
docker stack管理应用
虽然可以通过docker service scale命令来扩充某个服务的副本数,但这不是推荐的方式,最好是将Stack文件作为配置的唯一声明,然后重新执行docker stack deploy -c docker-stack.yml seastack
即可,重新部署Stack并不会影响那些没有改动的服务。