Manning.Learn.Docker.in.a.Month.of.Lunches.2020.6-笔记,关联 docker-compose
例子
https://github.com/microservices-demo
It’s a great sample application if you want to see how microservices are actually implemented. Each component owns its own data and exposes it through an API.
serverless framework
If you’re running in the datacenter, you can host your own platform in Docker using Nuclio
, OpenFaaS
, or Fn Project
, which are all popular open source serverless frameworks.
安装
- Docker Desktop
- Docker Desktop 下载地址: https://www.docker.com/products/docker-desktop/
- macOS Sierra 10.12 及以上版本;
- Windows 10 及以上
- Docker Toolbox
- 不能安装 Docker Desktop 的旧版 Windows 和 MacOS。
Linux 安装
Docker 安装脚本 : https://get.docker.com/
Docker Engine : https://docs.docker.com/engine/install/
Docker Compose : https://docs.docker.com/compose/install/
- arch linux / manjaro
1 | sudo pacman -S docker docker-compose |
- ubuntu
https://docs.docker.com/engine/install/ubuntu/
图形化管理后台
- Portainer : Open source
- UCP(Universal Control Plane) : commercial
配置
查看 docker 版本
docker version
可以查看 docker Engine Client & Server 的版本信息。
如果看不到 Docker Engine Server,看下service 状态并启动
1 2 3 | systemctl status docker.service sudo systemctl start docker.service sudo docker version |
docker-compose version
可以看到 docker compose 版本信息。
container 管理
- 容器中的app process 运行,容器运行; app process 退出,容器也退出。
- 停止的容器,不会自动被删除,只能用户自己手动删除。
启动容器
1 2 | # 启动执行目标命令,执行完退出
docker container run diamol/ch02-hello-diamol
|
1 2 3 4 5 | # 通过命令行访问 container # 执行 `exit` 退出交互命令行,同时 container 也停止。 docker container run --interactive --tty diamol/base # 或 docker container run -it diamol/base |
1 2 3 4 5 | # --detach : 命令执行完,container 以 service 方式运行,不退出。 # --publish : 端口映射,宿主机的8088 映射 container 的 80 端口 docker container run --detach --publish 8088:80 diamol/ch02-hello-diamol-web # 或 docker container run -d -p 8088:80 diamol/ch02-hello-diamol-web |
带参数启动
1 2 3 | docker container run --env TARGET=google.com diamol/ch03-web-ping # 或 docker container run -e TARGET=docker.com -e INTERVAL=5000 web-ping |
在 container 中执行命令
1 2 3 4 | docker container exec <container-id> ls /usr/local/apache2/htdocs # 或 windows 下用 dir 代替 ls docker container exec <container-id> cmd /s /c dir C:\usr\local\apache2\htdocs index.html |
向 container 中拷贝本地文件
1 | docker container cp index.html <container-id>:/usr/local/apache2/htdocs/index.html |
停止的容器
docker container stop <container-id>
删除容器
docker container rm <container-id>
1 2 | # 删除所有容器 docker container rm --force $(docker container ls --all --quiet) |
状态查看 docker ps 或 docker container ls
docker ps
等价于 docker container ls
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # 当前正在运行的container docker ps # or docker container ls # 所有的container ,无论是否正在运行 docker ps -a docker container ls -a docker container ls --all # -q 参数,表示只打印id docker ps -q # 所有停止运行的container docker ps --filter "status=exited" |
- the container ID is the same as the hostname inside the container.
容器的详细情况 docker container inspect
1 | docker container inspect <container-id> |
容器运行的进程 docker container top
1 | docker container top <container-id> |
容器执行的日志 docker container logs
1 | docker container logs <container-id> |
docker container stats
显示 container 当前占用的 CPU、内存、网络等统计信息。
1 | docker container stats <container-id> |
image 镜像管理
镜像库服务器
镜像库服务器,称为,registries
Docker Hub 是公用镜像库, 地址是 https://hub.docker.com/
从 Docker Hub 下载 image
1 | docker image pull diamol/ch03-web-ping |
image 大小
1 2 3 4 5 6 | docker image ls -f reference=diamol/golang -f reference=image-gallery # output REPOSITORY TAG IMAGE ID CREATED SIZE image-gallery latest b41869f5d153 20 minutes ago 25.3MB diamol/golang latest ad57f5c226fc 2 hours ago 774MB |
使用 Dockerfile 创建 image
简单的例子
- Dockerfile
1 2 3 4 5 6 7 | FROM diamol/node ENV TARGET="blog.sixeyed.com" ENV METHOD="HEAD" ENV INTERVAL="3000" WORKDIR /web-ping COPY app.js . CMD ["node", "/web-ping/app.js"] |
WORKDIR
,创建这个目录,并作为工作目录。
COPY
, 将本地文件或文件夹,拷贝到image文件系统中。
- 目录结构
1 2 3 4 | web-ping ├── app.js ├── Dockerfile └── README.md |
- build image
1 2 3 4 5 | cd /some-path/web-ping # --tag 参数指定 image 的 name # 最后一个参数,说明build 的 local 目录,点表示当前目录 docker image build --tag web-ping . |
build好的image,被存放在 local image cache
1 2 | # 在本地 image缓存中,查看w字母开头的image docker image ls 'w*' |
注意 docker 使用 image layer 来复用底层 image,Every Dockerfile instruction results in an image layer,所以 Dockerfile 中语句的顺序,应该注意,将经常更新改动的部分,放在最后。
例如:
1 2 3 4 5 6 7 8 9 10 | FROM diamol/node CMD ["node", "/web-ping/app.js"] ENV TARGET="blog.sixeyed.com" \ METHOD="HEAD" \ INTERVAL="3000" WORKDIR /web-ping COPY app.js . |
1 | docker image build -t web-ping:v2 . |
Multi-stage Dockerfile
例子:
1 2 3 4 5 6 7 8 | FROM diamol/base AS build-stage RUN echo 'Building...' > /build.txt FROM diamol/base AS test-stage COPY --from=build-stage /build.txt /build.txt RUN echo 'Testing...' >> /build.txt FROM diamol/base COPY --from=test-stage /build.txt /build.txt CMD cat /build.txt |
上例有3个stage: build-stage, test-stage, and the final unnamed stage
实际最后的image,还是 final stage 生成。
每个stage 独立运行,但是可以从前一个stage 拷贝文件。
任何一个 stage fails, build fails。
编译 java app 的 Dockerfile 例子
1 2 3 4 5 6 7 8 9 10 11 12 13 | FROM diamol/maven AS builder WORKDIR /usr/src/iotd COPY pom.xml . RUN mvn -B dependency:go-offline COPY . . RUN mvn package # app FROM diamol/openjdk WORKDIR /app COPY --from=builder /usr/src/iotd/target/iotd-service-0.1.0.jar . EXPOSE 80 ENTRYPOINT ["java", "-jar", "/app/iotd-service-0.1.0.jar"] |
不需要编译的app Dockerfile 例子,以 Nodejs 为例
1 2 3 4 5 6 7 8 9 10 11 | FROM diamol/node AS builder WORKDIR /src COPY src/package.json . RUN npm install # app FROM diamol/node EXPOSE 80 CMD ["node", "server.js"] WORKDIR /app COPY --from=builder /src/node_modules/ /app/node_modules/ COPY src/ . |
Golang 的 Dockerfile 例子
1 2 3 4 5 6 7 8 9 10 11 12 | FROM diamol/golang AS builder COPY main.go . RUN go build -o /server # app FROM diamol/base ENV IMAGE_API_URL="http://iotd/image" \ ACCESS_API_URL="http://accesslog/access-log" CMD ["/web/server"] WORKDIR web COPY index.html . COPY --from=builder /server . RUN chmod +x server |
diamol/golang
有700多M,只是在编译的时候需要,不需要放在最终的image中。满足最小运行条件的 diamol/base
其实很小,只有20+M。
执行命令的指令: RUN、CMD、ENTRYPOINT
- refer
- RUN 指令
- RUN 执行命令并创建新的镜像层,RUN 经常用于安装软件包。
- CMD命令
- CMD 指令允许用户指定容器的 默认执行的命令 。
- 此命令会在容器启动且 docker run 没有指定其他命令时运行。
- 如果 docker run 指定了其他命令,CMD 指定的默认命令将被忽略。
- 如果 Dockerfile 中有多个 CMD 指令,只有最后一个 CMD 有效
-
CMD 有三种格式:
- Exec 格式:
CMD ["executable","param1","param2"]
这是 CMD 的推荐格式。 CMD ["param1","param2"]
为 ENTRYPOINT 提供额外的参数,此时 ENTRYPOINT 必须使用 * * Exec 格式。- Shell 格式:
CMD command param1 param2
- Exec 格式:
- ENTRYPOINT
- 与CMD类似,但 ENTRYPOINT 不会被忽略,一定会被执行,即使运行 docker run 时指定了其他命令。
-
ENTRYPOINT 有两种格式:
- Exec 格式:
ENTRYPOINT ["executable", "param1", "param2"]
这是 ENTRYPOINT 的推荐格式。
Exec 格式用于设置要执行的命令及其参数,同时可通过 CMD 提供额外的参数。
ENTRYPOINT 中的参数始终会被使用,而 CMD 的额外参数可以在容器启动时动态替换掉。 - Shell 格式:
ENTRYPOINT command param1 param2
Shell 格式会忽略任何 CMD 或 docker run 提供的参数。
- Exec 格式:
镜像库 / sharing images / Docker Hub and other registries
镜像名称,aka. image reference
例子: docker.io/diamol/golang:latest
docker.io
镜像库的domain,默认是 docker hub的域名docker.io
diamol
镜像作者或组织golang
镜像名称latest
镜像的tag,用来作为版本或变体标识,默认是 latest
push image
先要设置 docker id ,即docker.com 上的用户名,而非email。
1 2 3 4 5 | # using PowerShell on Windows $dockerId="<your-docker-id-goes-here>" # using Bash on Linux or Mac export dockerId="<your-docker-id-goes-here>" |
login docker.com
1 | docker login --username $dockerId
|
为已存在的 image 创建reference,并tag
1 2 3 4 5 | docker image tag image-gallery $dockerId/image-gallery:v1 # List the image-gallery image references: docker image ls --filter reference=image-gallery --filter reference='*/image-gallery' |
自此,(1)使用了docker login 完成登陆;(2)有一个在 docker id 名下的 image
push 到 docker hub 上去:
1 | docker image push $dockerId/image-gallery:v1
|
给镜像打 tag
一种用法是以3位版本号,如 [major].[minor].[patch]
,给镜像打 tag 。
例如:
1 2 3 4 | docker image tag image-gallery registry.local:5000/gallery/ui:latest docker image tag image-gallery registry.local:5000/gallery/ui:2 docker image tag image-gallery registry.local:5000/gallery/ui:2.1 docker image tag image-gallery registry.local:5000/gallery/ui:2.1.106 |
其他开源 docker registries
- Docker 轻量级的 registry server
https://github.com/distribution/distribution
没有web ui 管理界面,支持 pull 和 push - VMWare 的 Harbor
- CNCF Harbor Project
- Sonatype Nexus Repository OSS
https://www.sonatype.com/products/repository-oss-vs-pro-features
搭建自己的镜像库
docker image 默认之允许 https 连接,自己的registry一般都是http,所以要设置docker运行http。
方法(1) Docker Desktop 上的设置方法:
- 任务栏上的鲸鱼icon,右键菜单 > Settings 或 Preferences
- Daemon 标签页,在 insecure registries 列表中添加允许http的地址,例如,registry.local:5000
- 重启 Docker Engine
方法(2)直接修改 daemon.json
daemon.json 位置:
- Windows : C:\ProgramData\docker\config
- Linux : /etc/docker
1 2 3 4 5 | { "insecure-registries": [ "registry.local:5000" ] } |
然后重启Docker Engine。
重启后,可以用 docker info
查看配置效果。
网络管理
DNS
Docker has its own DNS service built in.
container 先使用 Docker 自建的DNS 查询域名,查不到,再开始外部的 DNS lookup。
可以在container 中执行 nslookup container-name
查看对应的docker内网IP。
对于有个多个IP关联的 container, Docker DNS 会每次变动下IP列表的顺序,做一个简单的负载均衡。
创建 NAT 网络
1 2 | # network-name ,默认创建的叫 nat
docker network create <network-name>
|
启动容器时,--network
指定接入的网络。
1 2 | docker container run --name iotd -d -p 800:80 --network nat image-of- the-day |
存储 / docker volumes & mount
container 默认文件系统
从docker 容器和本地电脑,相互拷贝文件:
1 2 3 4 5 | # docker to local docker container cp docker-name:/some/path/somefile somefile # local to docker docker container cp somefile docker-name:/some/path/somefile |
每个容器都有自己的文件系统,在容器中以单独磁盘形式出现,如,/dev/sda1
或 C:\
The writable layer is unique to each container.
The container writable layer is created by Docker when the container is started, and it’s deleted by Docker when the container is removed.
Stopping a container doesn’t automatically remove the writable layer.
Docker volumes
A docker volume is a unit of storage, exists independently of containers, and has it’s own life cycle.
Docker Volumnes can be attached to containers.
2种创建volume的方法:
- 使用
docker volume create
命令 - 在 dockerfile 中使用语法
VOLUME <target-directory>
例子,dockerfile 创建volume
运行容器的时候,自动生成一个volume,挂载到容器的 /data
目录
1 2 3 4 5 | FROM diamol/dotnet-aspnet WORKDIR /app ENTRYPOINT ["dotnet", "ToDoList.dll"] VOLUME /data COPY --from=builder /out/ . |
1 2 3 4 5 6 7 8 9 10 11 | # 运行容器todo1,同时自动创建volume挂载到 /data docker container run --name todo1 -d -p 8010:80 diamol/ch06-todo-list # 列出 todo1 容器的volume docker container inspect --format '\{\{.Mounts\}\}' todo1 # 列出所有 volume docker volume ls # 其他容器共用volume docker container run --name todo3 -d --volumes-from todo1 diamol/ch06-todo-list |
docker volume create 创建一个volume,在容器之间使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # 容器中 volume 的挂载点 target = "data" # for linux containers $target = "c:\data" # for Windows containers # create a named volume docker volune create todo-list # v1 app ,使用 todo-list volume docker container run -d -p 8011:80 -v todo-list:$target --name todo-v1 diamol/ch06-todo-list # 删除 v1 app container docker container rm -f todo-v1 # v2 app ,接着使用 todo-list volume docker container run -d -p 8011:80 -v todo-list:$target --name todo-v2 diamol/ch06-todo-list:v2 |
-v
--volume
参数指定的volume 目标目录会覆盖镜像原有的目录。 作为镜像作者,写dockerfile时候可以使用 VOLUME 命令指定下默认的volume,以防用户运行容器时,没有使用volume参数。
bind mounts
A bind mount makes a directory on the host available as a path on a container. 通过这个方法,host 和 container 可以直接相互访问文件。
示例:
1 2 3 4 5 6 | $source="$(pwd)\database".ToLower(); $target="c:\data" # Windows source="$(pwd)/database" && target='/data' # Linux mkdir ./database docker container run --mount type=bind,source=$source,target=$target -d -p 8012:80 diamol/ch06-todo-list |
只读方式mount,加readonly
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | cd ./ch06/exercises/todo-list # save the source path as a variable: $source="$(pwd)\config".ToLower(); $target="c:\app\config" # Windows source="$(pwd)/config" && target='/app/config' # Linux # run the container using the mount: docker container run --name todo-configured -d -p 8013:80 --mount type=bind,source=$source,target=$target,readonly diamol/ch06-todo-list # check the application: curl http://localhost:8013 # and the container logs: docker container logs todo-configured |
局限性:
- Linux可以针对单个文件mount,Windows不可以。
- mount的如果是分布式文件系统,而这个文件系统不支持某些文件操作,app可能崩溃。
例如,Postgres数据库使用了 Azure Files。Azure Files支持正常读写,但是不支持创建hard link,Postgres数据尝试创建file link,结果就崩溃了。
Docker Compose
Docker Compose 用来将隶属于一个app的多个容器,协同部署。
Docker Compose file docker-compose.yml
使用 YAML语法,用来指导 compose 工具部署容器。
docker-compose up 的简单例子
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 | version: '3.7' services: todo-web: image: diamol/ch06-todo-list ports: - "8020:80" networks: - app-net networks: app-net: external: name: nat |
version
表明 compose file format 的版本
external
表明使用外部已经创建好的网络,而不用compose重新创建。
运行:
1 2 | docker network create nat docker-compose up --detach |
docker-compose
命令会在当前目录下读取 docker-compose.yml
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 | accesslog: image: diamol/ch04-access-log iotd: image: diamol/ch04-image-of-the-day ports: - "80" image-gallery: image: diamol/ch04-image-gallery ports: - "8010:80" depends_on: - accesslog - iotd |
docker-compose-viz
docker-compose-viz 工具可以根据当前目录的 docker-compose.yml 生成一个组件依赖关系图。
https://github.com/pmsipilot/docker-compose-viz
docker-compose 常用命令
docker-compose
不带任何参数,会显示帮助,列出可用子命令列表。
1 2 3 4 5 | # 重复启动一个镜像的多个容器,下例启动3个 docker-compose up -d --scale service-name=3 # 查看最后一个service的log docker-compose logs --tail=1 iotd |
1 2 3 4 5 6 7 8 | # 停止container,但是不从文件系统删除container docker-compose stop # 将停止的container重新启动 docker-compose start # 停止,并且删除之前创建的container docker-compose down |
override files 继承和覆盖配置
- docker-compose.yml
The core Compose file, specifies services and settings that apply in every environment. - docker-compose-dev.yml
The dev override file adds some specific settings for development. - docker-compose-test.yml
The test override file adds some specific settings for testing.
简单的例子,
1 2 3 4 5 6 7 8 9 10 | # from docker-compose.yml - the core app specification: services: todo-web: image: diamol/ch06-todo-list ports: - 80 environment: - Database:Provider=Sqlite networks: - app-net |
1 2 3 4 | # and from docker-compose-v2.yml - the version override file:
services:
todo-web:
image: diamol/ch06-todo-list:v2
|
config
命令不实际部署app,只是校验下配置。
1 | docker-compose -f ./todo-list/docker-compose.yml -f ./todo-list/docker-compose-v2.yml config |
一个展示override策略的例子
1 2 3 4 5 6 7 8 9 10 11 | # remove any existing containers docker container rm -f $(docker container ls -aq) # run the app in dev configuration: docker-compose -f ./numbers/docker-compose.yml -f ./numbers/docker-compose-dev.yml -p numbers-dev up -d # and the test setup: docker-compose -f ./numbers/docker-compose.yml -f ./numbers/docker-compose-test.yml -p numbers-test up -d # and UAT(User Acceptance test): docker-compose -f ./numbers/docker-compose.yml -f ./numbers/docker-compose-uat.yml -p numbers-uat up -d |
关闭app
1 2 3 4 5 6 7 8 | # this would work if we'd used the default docker-compose.yml file: docker-compose down # this would work if we'd used override files without a project name: docker-compose -f ./numbers/docker-compose.yml -f ./numbers/docker-compose-test.yml down # but we specified a project name, so we need to include that too: docker-compose -f ./numbers/docker-compose.yml -f ./numbers/docker-compose-test.yml -p numbers-test down |
注入自定义参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | services: todo-web: image: diamol/ch06-todo-list ports: - "${TODO_WEB_PORT}:80" environment: - Database:Provider=Sqlite env_file: - ./config/logging.debug.env secrets: - source: todo-db-connection target: /app/config/secrets.json secrets: todo-db-connection: file: ./config/empty.json |
-
secrets 参数注入
docker-compose 将会将
source
即./config/empty.json
拷贝到容器的target
即/app/config/secrets.json
-
环境变量注入方式,可以用
environment
直接在 yml 中配置,也可以env_file
指定参数配置文件,文件中每一行就是一个KEY=VALUE
的参数配置。 -
host 环境变量
例如,上例中
${TODO_WEB_PORT}
, docker-compose 可以从同目录的.env
文件中读取,当然也可以从系统变量中读取。.env
文件的例子:1 2 3 4 5 6 7 8
# container configuration - ports to publish: TODO_WEB_PORT=8877 TODO_DB_PORT=5432 # compose configuration - files and project name: COMPOSE_PATH_SEPARATOR=; COMPOSE_FILE=docker-compose.yml;docker-compose-test.yml COMPOSE_PROJECT_NAME=todo_ch10
yml配置文件,预定义属性减少重复和冗余
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | -labels: &logging logging: options: max-size: '100m' max-file: '10' x-labels: &labels app-name: image-gallery services: accesslog: <<: *logging labels: <<: *labels iotd: ports: - 8080:80 <<: *logging labels: <<: *labels public: api |
性能监测
Prometheus
An open source project to do containerized monitoring.
It runs in a Docker container, and collects metrics data from the applications and the Docker Engines.
It stores all data with the timestamp when it was collected.
需要手动在Docker Engine 的设置中开启 Prometheus metrics
:
1 2 | "metrics-addr" : "0.0.0.0:9323", "experimental": true |
以上设置,开启 监控功能,设置metrics数据的获取端口在 9323。
访问 http:/ /localhost:9323/metrics
查看当前监控信息,都是key-value列表,不是图表,看起来挺乱,这就是 Prometheus 的原始数据格式。
启动 Prometheus 的时候,告知 host 的 IP。
1 2 3 4 5 6 7 8 9 10 | # load your machine's IP address into a variable - on Windows: $hostIP = $(Get-NetIPConfiguration | Where-Object {$_.IPv4DefaultGateway -ne $null }).IPv4Address.IPAddress # on Linux: hostIP=$(ip route get 1 | awk '{print $NF;exit}') # and on Mac: hostIP=$(ifconfig en0 | grep -e 'inet\s' | awk '{print $2}') # pass your IP address as an environment variable for the container: docker container run -e DOCKER_HOST=$hostIP -d -p 9090:9090 diamol/prometheus:2.13.1 |
访问 http://localhost:9090
就可以看到 Prometheus 的 Web UI 界面了。
集群
- Docker Swarm
- Kubernetes
技巧
docker 清除容器和镜像
1 2 3 | docker container rm -f $(docker container ls -aq) docker image rm -f $(docker image ls -f reference='diamol/*' -q) |
docker 使用的磁盘空间
1 | docker system df |