Docker 配置与实践清单
本文节选自 Awesome CheatSheet/Docker CheatSheet,是对来自官方文档及 Docker Links 中链接内容的归档整理,包含了日常工作中常用的 Docker 概念与命令,如果对于 Linux 常用操作尚不熟悉的可以参考 Linux Commands CheatSheet。
# 更改 Ubuntu 默认源地址
$ sudo sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
# 安装必备的系统命令
$ sudo apt-get install -y python-software-properties
$ curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
$ sudo apt-get update
$ apt-cache policy docker-ce # 列举 docker-ce 版本
$ apt-get install docker-ce=17.03.2-ce....
# 配置开机自启动
$ sudo systemctl enable docker
# 取消开机自启动
$ sudo systemctl disable docker
# 使用 systemctl 命令行修改
$ sudo systemctl edit docker.service
# 或者查找配置地址并使用 Vim 修改
$ systemctl status docker
# 修改文件内容
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock --insecure-registry 10.196.108.176:5000 --dns 114.114.114.114 --dns 8.8.8.8 --dns 8.8.4.4 -g /mnt
# 重新载入服务配置
$ sudo systemctl daemon-reload
# 重启 Docker
$ sudo systemctl restart docker.service
# 判断是否配置成功
$ sudo netstat -lntp | grep dockerd
# 在主节点启动 Swarm
$ docker swarm init
# 查看 Swarm 密钥
$ docker swarm join-token -q worker
# 在主节点启动 Procontainer
$ docker run -it -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer
# 在主节点启动 Registry
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
# 将子节点加入到 Swarm
$ docker swarm join \
--token ${TOKEN} \
10.196.108.176:2377
$ docker run -i -t -e SERVER_ADDR=ss.server.ip -e SERVER_PORT=port -e PASSWORD=123456 bluebu/shadowsocks-privoxy
$ apt install python3-pip
$ pip3 install https://github.com/shadowsocks/shadowsocks/archive/master.zip -U
{
"server": "...",
"server_port": ...,
"local_port": 1080,
"password": "..."
"method": "chacha20-ietf-poly1305",
"timeout": 600
}
$ sslocal -c config.json
listen-address 0.0.0.0:8118 # 所有 interface 上监听流量
forward-socks5 / 127.0.0.1:1080 . # 流量导向本机上的 ss 代理
$ HTTP_PROXY=127.0.0.1:8118
$ HTTPS_PROXY=127.0.0.1:8118
$ curl https://www.google.com
[Environment]
Environment="HTTP_PROXY=127.0.0.1:8118" "HTTPS_PROXY=127.0.0.1:8118" "NO_PROXY=localhost,127.0.0.1,1.1.1.2,1.1.1.3,1.1.1.4"
...
$ sudo docker build -t myrepo/myapp /tmp/test1/
$ docker build -t username/image_name:tag_name .
$ docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
# 拉取镜像
$ docker pull image_name
# 将某个容器保存为镜像
$ docker commit -m “commit message” -a “author” container_name username/image_name:tag
Docker 支持将镜像保存为文件,以方便镜像的导出与加载:
# 保存镜像
$ docker save --output saved-image.tar my-image:1.0.0
$ docker save my-image:1.0.0 > saved-image.tar
$ docker save my_image:my_tag | gzip > my_image.tar.gz
# 导入镜像
$ docker load --input saved-image.tar
$ docker load < saved-image.tar
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
mynewimage latest 4d2eab1c0b9a 5 minutes ago 278.1 MB
ubuntu 14.04 ad892dd21d60 11 days ago 275.5 MB
<none> <none> 6b0a59aa7c48 11 days ago 169.4 MB
<none> <none> 6cfa4d1f33fb 7 weeks ago 0 B
已使用镜像(used image): 指所有已被容器(包括已停止的)关联的镜像。即 docker ps -a 看到的所有容器使用的镜像。
未引用镜像(unreferenced image):没有被分配或使用在容器中的镜像,但它有 Tag 信息。
悬空镜像(dangling image):未配置任何 Tag (也就无法被引用)的镜像,所以悬空。这通常是由于镜像 build 的时候没有指定 -t 参数配置 Tag 导致的。
# 列举未使用的
$ docker images --filter "dangling=true"
# 删除所有无用的镜像
$ docker rmi $(docker images -q -f dangling=true)
#
# MongoDB Dockerfile
#
# https://github.com/dockerfile/mongodb
#
# Pull base image.
FROM dockerfile/ubuntu
ENV SOURCE http://downloads-distro.mongodb.org/repo/ubuntu-upstart
# Install MongoDB.
RUN \
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 && \
echo 'deb $SOURCE dist 10gen' > /etc/apt/sources.list.d/mongodb.list && \
apt-get update && \
apt-get install -y mongodb-org && \
rm -rf /var/lib/apt/lists/*
ENV PATH /usr/local/mongo/bin:$PATH
# Define mountable directories.
VOLUME ["/data/db"]
# Define working directory.
WORKDIR /data
# Define default command.
CMD ["mongod"]
# Expose ports.
# - 27017: process
# - 28017: http
EXPOSE 27017
EXPOSE 28017
指令名 | 格式 | 描述 | 备注 |
---|---|---|---|
FROM | 格式为 FROM <image> 或FROM <image>:<tag> | 第一条指令必须为 FROM 指令。 | 如果在同一个 Dockerfile 中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次) |
MAINTAINER | 格式为 MAINTAINER <name> | 指定维护者信息。 | |
RUN | RUN <command> 或 RUN ["executable", "param1", "param2"] | 前者将在 shell 终端中运行命令,即 /bin/sh -c ;后者则使用 exec 执行。指定使用其它终端可以通过第二种方式实现,例如 RUN ["/bin/bash", "-c", "echo hello"] | 每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行。 |
CMD | 支持三种格式,CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数; | 指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。 | |
EXPOSE | EXPOSE <port> [<port>...] | 告诉 Docker 服务端容器暴露的端口号,供互联系统使用 | 在启动容器时需要通过 -p 来指定端口映射,Docker 主机会自动分配一个端口转发到指定的端口 |
ENV | ENV<key><value> 。指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持 | ||
ADD | ADD<src><dest> | 该命令将复制指定的 <src> 到容器中的 <dest> 。 | <src> 可以是 Dockerfile 所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录) |
COPY | COPY <src><dest> | 复制本地主机的 <src> (为 Dockerfile 所在目录的相对路径)到容器中的 dest | 当使用本地目录为源目录时,推荐使用 COPY |
ENTRYPOINT | ENTRYPOINT ["executable", "param1", "param2"] ,使用指定可执行文件执行;ENTRYPOINT command param1 param2 ,会在 Shell 中执行 | 配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。每个 Dockerfile 中只能有一个 ENTRYPOINT ,当指定多个时,只有最后一个起效。 | |
VOLUME | VOLUME ["/data"] | 创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等 | |
USER | USER daemon | 指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户 | |
WORKDIR | WORKDIR /path/to/workdir | 为后续的 RUN 、CMD 、ENTRYPOINT 指令配置工作目录 | 可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径 |
当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres;要临时获取管理员权限可以使用 gosu,而不推荐 sudo。
ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]
# docker run -it <image>
# Hello world
# docker run -it <image> John
# Hello John
FROM ubuntu:latest
RUN apt-get update && apt-get install -y supervisor
RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY my_first_process my_first_process
COPY my_second_process my_second_process
CMD ["/usr/bin/supervisord"]
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
# 拉取公共镜像
$ docker pull ubuntu:16.04
# 为镜像添加 Registry 信息
$ docker tag ubuntu:16.04 custom-domain:5000/my-ubuntu
# 将其推送到私有镜像库
$ docker push custom-domain:5000/my-ubuntu
# 从私有镜像库中拉取镜像
$ docker pull custom-domain:5000/my-ubuntu
-v /mnt/registry:/var/lib/registry
{ "insecure-registries": ["myregistry.example.com:5000"] }
$ mkdir auth
$ docker run \
--entrypoint htpasswd \
registry:2 -Bbn cscan cscancscan > ~/auth/htpasswd
$ openssl req -new -newkey rsa:4096 -days 365 \
-subj "/CN=localhost" \
-nodes -x509 \
-keyout ~/certs/domain.key \
-out ~/certs/domain.crt
registry-srv:
restart: always
image: registry:2
ports:
- 5000:5000
environment:
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
REGISTRY_HTTP_TLS_KEY: /certs/domain.key
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
volumes:
- /opt/registry:/var/lib/registry
- ~/certs:/certs
- ~/auth:/auth
$ docker-compose up -d
# 登录到镜像服务器
$ docker login myregistrydomain.com:5000
多阶段构建
随着 17.05 版本的发布,Docker 对于镜像构建这块也作了一项重要更新,那就是 multi-stage build(多阶段构建),这有助于方便源代码控制,减小镜像体积。
# First stage: complete build environment
FROM maven:3.5.0-jdk-8-alpine AS builder
# add pom.xml and source code
ADD ./pom.xml pom.xml
ADD ./src src/
# package jar
RUN mvn clean package
# Second stage: minimal runtime environment
From openjdk:8-jre-alpine
# copy jar from the first stage
COPY --from=builder target/msb-1.0.jar msb.jar
# run jar
CMD ["java", "-jar", "msb.jar"]
对于 multi-stage build,其关键点主要有两点:
在前面阶段的 FROM 指令后面增加了一个 AS 参数,可为该构建阶段命名,便于后续构建阶段引用,格式如下:
FROM image[:tag | @digest] AS stage
在后续阶段的 COPY 指令后面增加了--from 参数,指明引用前面哪一个构建阶段的成果,格式如下:
COPY --from=stage ...
同理,多阶段构建同样可以很方便地将多个彼此依赖的项目通过一个 Dockerfile 就可轻松构建出期望的容器镜像,而不用担心镜像太大、源码泄露等风险。
docker create:创建一个容器但是不启动。
docker rename:允许重命名容器。
docker run:在同一个操作中创建并启动一个容器。
docker rm:删除容器。
docker update:更新容器的资源限制。
-i 即使在没有附着的情况下依然保持 STDIN 处于开启。单纯使用 -i 命令是不会出现 root@689d580b6416:/ 这种前缀
-t 分配一个伪 TTY 控制台
# 创建,并且启动某个容器以执行某个命令
$ docker run -ti --name container_name image_name command
# 创建,启动容器执行某个命令然后删除该容器
$ docker run --rm -ti image_name command
# 创建,启动容器,并且映射卷与端口,同时设置环境变量
$ docker run -it --rm -p 8080:8080 -v /path/to/agent.jar:/agent.jar -e JAVA_OPTS=”-javaagent:/agent.jar” tomcat:8.0.29-jre8
# 创建容器,指定网络
$ docker run --network=<NETWORK>
标志值 | 描述 |
---|---|
-p 8080:80 | 将容器的 80 端口映射到 Docker 主机的 8080 端口(TCP) |
-p 8080:80/udp | 将容器的 80 端口映射到 Docker 主机的 8080 端口(UDP) |
-p 8080:80/tcp -p 8080:80/udp | 将容器的 80 端口映射到 Docker 主机的 8080 端口(TCP 和 UDP) |
# 启动/停止某个容器
$ docker [start|stop] container_name
# 在某个容器内执行某条命令
$ docker exec -ti container_name command.sh
# 查看某个容器的输出日志
$ docker logs -ft container_name
docker ps 查看运行中的所有容器。
docker logs 从容器中获取日志。(你也可以使用自定义日志驱动,不过在 1.10 中,它只支持 json-file 和 journald)
docker inspect 查看某个容器的所有信息(包括 IP 地址)。
docker events 从容器中获取事件(events)。
docker port 查看容器的公开端口。
docker top 查看容器中活动进程。
docker stats 查看容器的资源使用情况统计信息。
docker diff 查看容器的 FS 中有变化文件信息。
# 根据条件过滤查询
$ docker ps --filter "name=nostalgic"
# 显示正在运行的容器列表
$ docker stats --all
no:不进行重启
on-failure:当容器以非零状态码退出时重启容器
unless-stopped:当某个容器被显性关闭或者 Docker 本身关闭或重启时重启
always:无论出现任何情况都重启容器
# 设置重启策略
# Off, On-failure, Unless-stopped, Always
$ docker run -dit — restart unless-stopped [CONTAINER]
# 关闭所有正在运行的容器
$ docker kill $(docker ps -q)
# 根据 ID 或 Name 移除
$ docker rm ID
$ docker rm Name
# 移除所有停止的容器
$ docker rm $(docker ps -a -q)
# 根据状态移除
$ docker rm $(docker ps -q -f 'status=exited')
# 根据标签移除
$ docker rm $(docker ps -a | grep rabbitmq | awk '{print $1}')
$ docker rm $(docker ps -a | grep "46 hours ago")
docker cp 在容器和本地文件系统之间复制文件或文件夹。
docker export 将容器的文件系统切换为压缩包(tarball archive stream)输出到 STDOUT。
$ docker stats redis1 redis2
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
redis1 0.07% 796 KB / 64 MB 1.21% 788 B / 648 B 3.568 MB / 512 KB
$ docker run -it -m 300M ubuntu:14.04 /bin/bash
# 指定容器可占用的 CPU 核编号,0-3 表示占用四个核,1、3 表示占用两个核
$ docker run -it --cpuset-cpus="1,3" ubuntu /bin/bash
# 最多允许占用单 CPU 50% 的计算资源,如果双核 CPU,则可以设置为 1.5 等
$ docker run -it --cpus=".5" ubuntu /bin/bash
# 不同的值能够指定不同的容器权重,用于动态分配 CPU 资源
$ docker run -it --cpu-shares="512" ubuntu /bin/bash
CPU Set 保障了容器的 CPU 核数,安全性较强,但是整体资源利用率低。如果容器实际并不需要如此多核的 CPU 资源来处理任务则会造成资源浪费,并且导致其他容器上的任务无法利用该容器上的 CPU 空闲时间片,人为阻断了 CPU 闲时复用的能力。
CPU Share 允许通过共享的方式获得 CPU 资源,不同的容器共享一定总量的 CPU 计算能力,每个容器都绑定全量核,而每个容器获取一定份额的 CPU 计算力。该模式下,每个容器的 CPU 资源分配不再以整核分配,而是可精细到 CPU 时间片份额的粒度,并且是连续的 CPU 核能力值。当整机闲时,可以让较为繁忙的业务获得整机空闲。采用 CPU 资源共享的机制,其资源隔离性没有 set 模式强,对于极个别 CPU 资源敏感型业务,有可能出现偶尔等待 CPU 时间片的情况,而影响业务稳定性。对于极少数的这类业务,我们容许继续使用 CPU set 模式。
# 限制单个容器最多占用 20G 空间,将应用于任何新建容器。
$ --storage-opt dm.basesize=20G
$ btrfs qgroup limit -e 50G /var/lib/docker/btrfs/subvolumes/<CONTAINER_ID>
docker run -it --device=/dev/ttyUSB0 debian bash
docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb debian bash
数据卷可以在容器之间共享和重用
对数据卷的修改会立马生效
对数据卷的更新,不会影响镜像
卷会一直存在,直到没有容器使用
数据卷的使用,类似于 Linux 下对目录或文件进行 mount
# the following creates a tmpfs volume called foo with a size of 100 megabyte and uid of 1000.
$ docker volume create --driver local \
--opt type=tmpfs \
--opt device=tmpfs \
--opt o=size=100m,uid=1000 \
foo
$ docker volume create --driver local \
--opt type=nfs \
--opt o=addr=192.168.1.1,rw \
--opt device=:/path/to/dir \
foo
$ docker run -d \
-it \
--name devtest \
-v myvol2:/app \
nginx:latest
"Mounts": [
{
"Type": "volume",
"Name": "myvol2",
"Source": "/var/lib/docker/volumes/myvol2/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
# 挂载目录
$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py
# 挂载文件
$ sudo docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash
# Docker 挂载数据卷的默认权限是读写,用户也可以通过 `:ro` 指定为只读。
$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp:ro
training/webapp python app.py
VOLUME /data
bridge:默认的网络驱动,常用于多个应用运行与独立容器中并且需要相互通讯的时候。
host:移除容器与 Docker 主机之间的网络隔离,直接使用宿主机所在的网络。底层与宿主机共用一个 Network Namespace,容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。
Overlay:Overlay 网络用语连接多个 Docker Daemon,保证 Docker Swarm 服务的正常运行;独立的容器与 Swarm 服务,或者不同宿主机上的容器同样能够通过 Overlay 进行通信。
none:对于指定容器禁止所有的网络通信。
Macvlan:Macvlan 网络会允许直接为容器分配 MAC 地址,使其作为真正的物理设备接入到宿主机所在的网络中。
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
f707aa0ef50d bridge bridge local
97dd7a032d96 host host local
d5a1bed0b12d none null local
# 创建新的网络
$ docker network create --driver bridge isolated
# 指定网段,宿主机会作为默认网关
$ docker network create --driver=bridge --subnet=192.168.2.0/24 --gateway=192.168.2.10 new_subnet
# 创建时将某个容器连接到网络
$ docker run --network=isolated -itd --name=docker-nginx nginx
# 将某个运行中容器连接到某个网络
$ docker network connect multi-host-network container1
-h HOSTNAME or --hostname=HOSTNAME 设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts 。但它在容器外部看不到,既不会在 docker ps 中显示,也不会在其他的容器的 /etc/hosts 看到。
--link=CONTAINER_NAME:ALIAS 选项会在创建容器的时候,添加一个其他容器的主机名到 /etc/hosts 文件中,让新容器的进程可以使用主机名 ALIAS 就可以连接它。
--dns=IP_ADDRESS 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。
--dns-search=DOMAIN 设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的 主机时,DNS 不仅搜索 host,还会搜索 host.example.com 。 注意:如果没有上述最后 2 个选项, Docker 会默认用主机上的 /etc/resolv.conf 来配置容器。
# 查看当前目录下的文件空间占用
$ du -h --max-depth=1 | sort
# 空间占用总体分析
$ docker system df
# 输出空间占用细节
$ docker system df -v
# 输出容器的空间占用
$ docker ps -s
# 一并清除所有未使用的镜像和悬空镜像
$ docker system prune --all
# 列举悬空镜像
$ docker images -f dangling=true
# 删除全部悬空镜像
$ docker image prune
# 删除所有未被使用的镜像
$ docker image prune -a
# 删除指定模式的镜像
$ docker images -a | grep "pattern" | awk '{print $3}' | xargs docker rmi
# 删除全部镜像
$ docker rmi $(docker images -a -q)
# 删除全部停止的容器
$ docker rm $(docker ps -a -f status=exited -q)
# 根据指定模式删除容器
$ docker rm $(docker ps -a -f status=exited -f status=created -q)
$ docker rm $(docker ps -a | grep rabbitmq | awk '{print $1}')
# 删除全部容器
$ docker stop $(docker ps -a -q)
$ docker rm $(docker ps -a -q)
# 列举并删除未被使用的卷
$ docker volume ls -f dangling=true
$ docker volume prune
# 根据指定的模式删除卷
$ docker volume prune --filter "label!=keep"
# 删除未被关联的网络
$ docker network prune
$ docker network prune --filter "until=24h"
我们也可以手动指定日志文件的尺寸或者清空日志文件:
# 设置日志文件最大尺寸
$ dockerd ... --log-opt max-size=10m --log-opt max-file=3
# 清空当前日志文件
truncate -s 0 /var/lib/docker/containers/*/*-json.log
# 指定 Docker Compose 文件版本
version: '3'
services:
web:
# 指定从本地目录进行编译
build: .
# 指定导出端口
ports:
- '5000:5000'
# 替换默认的 CMD 命令
command: python app.py
# 将本地目录绑定到容器内目录
volumes:
- .:/code
redis:
# 镜像的 ID
image: 'redis:alpine'
FROM python:3.4-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
cache = redis.Redis(host='redis', port=6379)
# 交互式启动
$ docker-compose up
# 守护进程式启动
$ docker-compose up -d
# 查看运行情况
$ docker-compose ps
# 关闭
$ docker-compose stop
# 移除内部卷
$ docker-compose down --volumes
version: "3.2"
services:
web:
image: nginx:alpine
volumes:
- type: volume
source: mydata
target: /data
volume:
nocopy: true
- type: bind
source: ./static
target: /opt/app/static
db:
image: postgres:latest
volumes:
- "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
- "dbdata:/var/lib/postgresql/data"
volumes:
mydata:
dbdata:
# 创建一个新的服务
$ docker service create \
--image nginx \
--replicas 2 \
nginx
# 更新服务
$ docker service update \
--image nginx:alpine \
nginx
# 删除服务
$ docker service rm nginx
# 缩容,而不是直接删除服务
$ docker service scale nginx=0
# 扩容
$ docker service scale nginx=5
# 列出所有的服务
$ docker service ls
# 列出一个服务的所有实例(包括服务的健康状况)
$ docker service ps nginx
# 服务的详细信息
$ docker service inspect nginx
$ docker stack deploy application
version: '3'
services:
web:
image: registry.gitlab.com/example/example # you need to use external image
command: npm run prod
ports:
- 80:80
deploy:
replicas: 6
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
https://github.com/wxyyxc1992/Awesome-Links/blob/master/Infrastructure/Virtualization/Container/Docker/Docker-Links.md
https://github.com/wxyyxc1992/Awesome-CheatSheet/blob/master/Infrastructure/OS/Linux/Linux-Commands-CheatSheet.md
https://github.com/wxyyxc1992/Backend-Boilerplate/tree/master/infra/docker
https://mirrors.ustc.edu.cn/help/docker-ce.html
https://docs.docker.com/config/containers/multi-service_container/
来源:Awesome-CheatSheet
原文:http://t.cn/EwsdKMZ
题图:来自谷歌图片搜索
版权:本文版权归原作者所有
今日思想
人一生最大的幸运,就是在年富力强时发现了自己的人生使命。
——茨威格「人类群星闪耀时」
推荐阅读