Docker

我给自己的要求是,大概会用就好,当沙箱环境来用。

当今任何工具使用,你都可以借助ChatGPT来问答解决,包括基础的内容。

Basic

Docker基本的东西要直到,Containers、Images、Registries、Networks、Volumes、Docker Contexts

在VSCode中有插件 “Container Tools” By Microsoft 可以用,可以管理上面6类内容。

Docker是什么

Docker是一个以一致的方式构建运行和交付应用程序的平台。 A platform for building, running and shipping applications.

虚拟机:

Windows | Linux
----------------
   Hypervisor
----------------
       Mac

Hypervisors 有 VirtualBox、VMware、Hyper-v(Windows only)。

Containers优点:

Docker架构,Docker使用客户端服务器架构,因此它有一个客户端组件。

Client--REST API-->Docker Engine
Containers: Process1 Process2 Process3
-----------
  Windows

安装Docker

root@ser745692301841:/dev_dir/avant/bin# docker version
Client:
 Version:           27.5.1
 API version:       1.47
 Go version:        go1.22.2
 Git commit:        27.5.1-0ubuntu3~24.04.2
 Built:             Mon Jun  2 11:51:53 2025
 OS/Arch:           linux/amd64
 Context:           default

Server:
 Engine:
  Version:          27.5.1
  API version:      1.47 (minimum version 1.24)
  Go version:       go1.22.2
  Git commit:       27.5.1-0ubuntu3~24.04.2
  Built:            Mon Jun  2 11:51:53 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.7.27
  GitCommit:        
 runc:
  Version:          1.2.5-0ubuntu1~24.04.1
  GitCommit:        
 docker-init:
  Version:          0.19.0
  GitCommit:     

https://docs.docker.com/get-started/get-docker/

根据是哪种系统 Windows、Mac、Linux 安装 Docker。

Development Workflow

Dockerlize,为项目写 Dockerfile。 Application+Dockerfile 打包成镜像 Image。

root@ser745692301841:/dev_dir# mkdir hello-docker
root@ser745692301841:/dev_dir# cd hello-docker
root@ser745692301841:/dev_dir/hello-docker# touch app.js
root@ser745692301841:/dev_dir/hello-docker# ls
app.js
// app.js
console.log("Hello Docker!");
root@ser745692301841:/dev_dir/hello-docker# node app.js
Hello Docker!

流程是 * Start with an OS * Install Node * Copy app files * Run node app.js

https://hub.docker.com/_/node 使用基础镜像node,基础镜像是什么,这就有点像面向对象编程中的继承。

例如 node:alpine 在:有不同的发行版,可以向dockerhub中查看。

FROM node:alpine
# 要将当前目录中的所有文件复制到该映像的app目录中
COPY . /app
# 指定当前工作目录workdir
WORKDIR /app
# 执行命令
CMD node /app/app.js
# -t 指定tag ./指定在那个文件路径下找到Dockerfile
root@ser745692301841:/dev_dir/hello-docker# ls
Dockerfile  app.js
root@ser745692301841:/dev_dir/hello-docker# docker build -t hello-docker ./

Digest: sha256:77f3c4d1f33c17dfa4af4b0add57d86957187873e313c2c37f52831d117645c8
Status: Downloaded newer image for node:alpine
 ---> 0d3f5d817db1
Step 2/4 : COPY . /app
 ---> c231dc670222
Step 3/4 : WORKDIR /app
 ---> Running in 348cdfb75ea5
 ---> Removed intermediate container 348cdfb75ea5
 ---> 0edce7865a3b
Step 4/4 : CMD node /app/app.js
 ---> Running in 2c5ae4933702
 ---> Removed intermediate container 2c5ae4933702
 ---> f7f9d58fbf17
Successfully built f7f9d58fbf17
Successfully tagged hello-docker:latest
root@ser745692301841:/dev_dir/hello-docker# ls
Dockerfile  app.js 

还是只有Dockerfile 和 app.js, 因为这里不存储镜像,事实上,镜像不是单个文件,Docker如何存储这个镜像很很复杂,我们不必担心。

查看该计算机上的所有映像

root@ser745692301841:/dev_dir/hello-docker# docker images
REPOSITORY     TAG          IMAGE ID       CREATED         SIZE
hello-docker   latest       f7f9d58fbf17   2 minutes ago   168MB
node           alpine       0d3f5d817db1   5 days ago      168MB
redis          latest       f2cd22713a18   2 months ago    128MB
mongo          latest       ccf68b64aa3e   3 months ago    901MB
postgres       latest       445ed93b882f   3 months ago    438MB
rabbitmq       management   928da815d9a3   5 months ago    275MB
mysql          latest       4c2531d6bf10   5 months ago    859MB

root@ser745692301841:/dev_dir/hello-docker# docker image ls
REPOSITORY     TAG          IMAGE ID       CREATED         SIZE
hello-docker   latest       f7f9d58fbf17   3 minutes ago   168MB
node           alpine       0d3f5d817db1   5 days ago      168MB
redis          latest       f2cd22713a18   2 months ago    128MB
mongo          latest       ccf68b64aa3e   3 months ago    901MB
postgres       latest       445ed93b882f   3 months ago    438MB
rabbitmq       management   928da815d9a3   5 months ago    275MB
mysql          latest       4c2531d6bf10   5 months ago    859MB

运行我们刚才build出的hello-docker镜像

root@ser745692301841:/dev_dir/hello-docker# docker run hello-docker
Hello Docker!

查看主机上的容器

root@ser745692301841:/dev_dir/hello-docker# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
root@ser745692301841:/dev_dir/hello-docker# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                      PORTS     NAMES
0ab0ab52a140   hello-docker   "docker-entrypoint.s…"   19 seconds ago   Exited (0) 18 seconds ago             reverent_franklin

root@ser745692301841:/dev_dir/hello-docker# docker rm 0ab0ab52a140
0ab0ab52a140
root@ser745692301841:/dev_dir/hello-docker# docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

如何从dockerhub拉取镜像,例如

docker pull gaowanlu/avant

就会从dockerhub拉去 镜像 https://hub.docker.com/r/gaowanlu/avant

Docker使用Ubuntu

https://hub.docker.com/_/ubuntu

root@ser745692301841:/dev_dir/note# docker run ubuntu
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
953cdd413371: Pull complete 
Digest: sha256:353675e2a41babd526e2b837d7ec780c2a05bca0164f7ea5dbbd433d21d166fc
Status: Downloaded newer image for ubuntu:latest
root@ser745692301841:/dev_dir/note# 

root@ser745692301841:/dev_dir/note# docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS                      PORTS     NAMES
b922ca594b6e   ubuntu    "/bin/bash"   11 seconds ago   Exited (0) 10 seconds ago             vigorous_herschel

root@ser745692301841:/dev_dir/note# docker rm b922ca594b6e
b922ca594b6e

执行docker run ubuntu 会直接 Exit。容器会启动就停止了。

交互模式下启动容器

root@ser745692301841:/dev_dir/note# docker run -it ubuntu
root@b704966c4897:/# 
root@b704966c4897:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@b704966c4897:/# exit
exit
root@ser745692301841:/dev_dir/note# docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS                     PORTS     NAMES
b704966c4897   ubuntu    "/bin/bash"   15 seconds ago   Exited (0) 5 seconds ago             angry_jones

会Linux命令行,学习Linux基本知识,是你学习使用docker的前提。

启动容器

可以用 docker start

root@ser745692301841:/dev_dir/note# docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

root@ser745692301841:/dev_dir/note# docker run hello-docker
Hello Docker!

root@ser745692301841:/dev_dir/note# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS                     PORTS     NAMES
1d01b9630803   hello-docker   "docker-entrypoint.s…"   5 seconds ago   Exited (0) 3 seconds ago             charming_williams

root@ser745692301841:/dev_dir/note# docker start -i 1d01b9630803
Hello Docker!

root@ser745692301841:/dev_dir/note# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                     PORTS     NAMES

1d01b9630803   hello-docker   "docker-entrypoint.s…"   18 seconds ago   Exited (0) 4 seconds ago             charming_williams
root@ser745692301841:/dev_dir/note# 

进入正在运行的容器的bash

docker exec -it 容器ID bash
root@ser745692301841:/dev_dir/note# docker images
REPOSITORY     TAG          IMAGE ID       CREATED        SIZE
hello-docker   latest       f7f9d58fbf17   4 hours ago    168MB
node           alpine       0d3f5d817db1   5 days ago     168MB
ubuntu         latest       6d79abd4c962   3 weeks ago    78.1MB
redis          latest       f2cd22713a18   2 months ago   128MB
mongo          latest       ccf68b64aa3e   3 months ago   901MB
postgres       latest       445ed93b882f   3 months ago   438MB
rabbitmq       management   928da815d9a3   5 months ago   275MB
mysql          latest       4c2531d6bf10   5 months ago   859MB
root@ser745692301841:/dev_dir/note# docker run -it ubuntu
root@7eb976bab217:/# 

root@ser745692301841:/dev_dir/note# docker ps 
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS          PORTS     NAMES
7eb976bab217   ubuntu    "/bin/bash"   20 seconds ago   Up 19 seconds             jolly_mclaren
root@ser745692301841:/dev_dir/note# docker exec 7eb976bab217 bash
root@ser745692301841:/dev_dir/note# docker exec -it  7eb976bab217 bash
root@7eb976bab217:/# ls
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr
root@7eb976bab217:/# 

要分清楚Container和Image

它们的关系就像 面向对象编程中的 Class 与 Class的对象实例,Image是Class,Container是Object。

Dockerfile详情

从一个C++项目开始实践

https://github.com/mfavant/avant

其是一个C++ CMake Linux 项目。

在项目下建立一个Dockerfile

生产环境项目中,永远不要使用latest,因为ubuntu官方发布了新的image,我们每次构建时可能都与以前的镜像不同。

root@ser745692301841:/dev_dir/dockerlab# ls
root@ser745692301841:/dev_dir/dockerlab# git clone https://github.com/mfavant/avant.git
Cloning into 'avant'...
remote: Enumerating objects: 1956, done.
remote: Counting objects: 100% (149/149), done.
remote: Compressing objects: 100% (23/23), done.
remote: Total 1956 (delta 136), reused 132 (delta 126), pack-reused 1807 (from 1)
Receiving objects: 100% (1956/1956), 1.37 MiB | 2.44 MiB/s, done.
Resolving deltas: 100% (1175/1175), done.
root@ser745692301841:/dev_dir/dockerlab# ls
avant
root@ser745692301841:/dev_dir/dockerlab# cd avant
root@ser745692301841:/dev_dir/dockerlab/avant# rm Dockerfile
root@ser745692301841:/dev_dir/dockerlab/avant# touch Dockerfile
# 使用最新版本的ubuntu
FROM ubuntu:25.04

构建镜像 docker-avant

root@ser745692301841:/dev_dir/dockerlab/avant# docker build -t docker-avant ./
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

Sending build context to Docker daemon  5.009MB
Step 1/1 : FROM ubuntu:25.04
25.04: Pulling from library/ubuntu
bf2a53a9f660: Pull complete 
Digest: sha256:103c7471649a4fd9996fe73dff20f46082e4e0f0f6240c91954b8b09c38b6faf
Status: Downloaded newer image for ubuntu:25.04
 ---> 482b635a13cd
Successfully built 482b635a13cd
Successfully tagged docker-avant:latest
root@ser745692301841:/dev_dir/dockerlab/avant# docker images
REPOSITORY     TAG          IMAGE ID       CREATED        SIZE
node           alpine       0d3f5d817db1   7 days ago     168MB
docker-avant   latest       482b635a13cd   7 days ago     77MB
ubuntu         25.04        482b635a13cd   7 days ago     77MB
ubuntu         latest       6d79abd4c962   3 weeks ago    78.1MB
redis          latest       f2cd22713a18   2 months ago   128MB
mongo          latest       ccf68b64aa3e   3 months ago   901MB
postgres       latest       445ed93b882f   3 months ago   438MB
rabbitmq       management   928da815d9a3   5 months ago   275MB
mysql          latest       4c2531d6bf10   5 months ago   859MB

交互模式启动镜像 docker-avant

root@ser745692301841:/dev_dir/dockerlab/avant# docker run -it docker-avant  
root@a8516f0e9781:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@a8516f0e9781:/# 

只是进入了一个 ubuntu:25.04环境,并不能找到avant相关项目信息,因为我们dockerfile中还没有涉及到呢。

Copying Files And Directories

FROM ubuntu:25.04
WORKDIR /avant
COPY ./ ./

build镜像

root@ser745692301841:/dev_dir/dockerlab/avant# docker build -t docker-avant ./
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

Sending build context to Docker daemon  5.009MB
Step 1/3 : FROM ubuntu:25.04
 ---> 482b635a13cd
Step 2/3 : WORKDIR /avant
 ---> Running in a6e24d55fc73
 ---> Removed intermediate container a6e24d55fc73
 ---> e26cffc594c9
Step 3/3 : COPY ./ ./
 ---> 65090793a7f1
Successfully built 65090793a7f1
Successfully tagged docker-avant:latest
root@ser745692301841:/dev_dir/dockerlab/avant# docker images
REPOSITORY     TAG          IMAGE ID       CREATED         SIZE
docker-avant   latest       65090793a7f1   9 seconds ago   81.7MB
node           alpine       0d3f5d817db1   7 days ago      168MB
ubuntu         25.04        482b635a13cd   7 days ago      77MB
redis          latest       f2cd22713a18   2 months ago    128MB
mongo          latest       ccf68b64aa3e   3 months ago    901MB
postgres       latest       445ed93b882f   3 months ago    438MB
rabbitmq       management   928da815d9a3   5 months ago    275MB
mysql          latest       4c2531d6bf10   5 months ago    859MB
root@ser745692301841:/dev_dir/dockerlab/avant# 

启动镜像docker-avant容器

root@ser745692301841:/dev_dir/dockerlab/avant# docker run -it docker-avant 
root@be893c402dbf:/avant# ls
CMakeLists.txt  Dockerfile  LICENSE  README.md  bin  centos8.md  client  external  protobuf.md  protocol  src  test
root@be893c402dbf:/avant# cd ..
root@be893c402dbf:/# ls
avant  bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@be893c402dbf:/# cd avant
root@be893c402dbf:/avant# ls
CMakeLists.txt  Dockerfile  LICENSE  README.md  bin  centos8.md  client  external  protobuf.md  protocol  src  test
root@be893c402dbf:/avant# 

可见我们的项目内容已经被COPY到镜像中的 /avant目录下了。

# COPY 多个文件
COPY 文件1 文件2 /avant/
# ADD可以创建文件夹
ADD /新文件夹
# ADD可以网络下载文件到目标文件夹
ADD http://file /目标文件夹/
# ADD可以解压到目标文件夹
ADD 压缩文件.zip /目标文件夹/

.dockerignore

类似于git中的 .gitignore, 我们不希望项目中的某些内容被COPY到镜像中。例如nodejs项目中的 node_modules文件夹。

bin/avant
build/
.git/
root@ser745692301841:/dev_dir/dockerlab/avant# docker build -t docker-avant ./
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

Sending build context to Docker daemon  3.417MB
Step 1/3 : FROM ubuntu:25.04
 ---> 482b635a13cd
Step 2/3 : WORKDIR /avant
 ---> Running in 731ce162db64
 ---> Removed intermediate container 731ce162db64
 ---> df59f482154d
Step 3/3 : COPY ./ ./
 ---> 8439d5ece311
Successfully built 8439d5ece311
Successfully tagged docker-avant:latest
root@ser745692301841:/dev_dir/dockerlab/avant# docker images
REPOSITORY     TAG          IMAGE ID       CREATED         SIZE
docker-avant   latest       8439d5ece311   7 seconds ago   80.2MB
node           alpine       0d3f5d817db1   7 days ago      168MB
ubuntu         25.04        482b635a13cd   7 days ago      77MB
redis          latest       f2cd22713a18   2 months ago    128MB
mongo          latest       ccf68b64aa3e   3 months ago    901MB
postgres       latest       445ed93b882f   3 months ago    438MB
rabbitmq       management   928da815d9a3   5 months ago    275MB
mysql          latest       4c2531d6bf10   5 months ago    859MB
root@ser745692301841:/dev_dir/dockerlab/avant# 

Running Commands

下一步是安装我们的项目依赖项

FROM ubuntu:25.04
WORKDIR /avant
COPY ./ ./
RUN apt update
RUN apt install cmake g++ make git -y
RUN apt install protobuf-compiler libprotobuf-dev  -y
RUN apt install libssl-dev -y
docker build -t docker-avant ./

RUN提供的命令行将会一条条执行,然后镜像中就会有我们配置的项目环境。

我们运行一个容器。

root@ser745692301841:/dev_dir/dockerlab/avant# docker images
REPOSITORY     TAG          IMAGE ID       CREATED         SIZE
docker-avant   latest       5a78ee94b4ce   3 seconds ago   579MB

可见镜像挺大的。

root@ser745692301841:/dev_dir/dockerlab/avant# docker run -it docker-avant
root@d31b5733a6ae:/avant# ls
CMakeLists.txt  Dockerfile  LICENSE  README.md  bin  centos8.md  client  external  protobuf.md  protocol  src  test
root@d31b5733a6ae:/avant# mkdir build
root@d31b5733a6ae:/avant# cd ./protocol
root@d31b5733a6ae:/avant/protocol# make
root@d31b5733a6ae:/avant/protocol# cd ../
root@d31b5733a6ae:/avant# mkdir build
root@d31b5733a6ae:/avant# cd build 
root@d31b5733a6ae:/avant/build# cmake ../   
-- The C compiler identification is GNU 14.2.0
-- The CXX compiler identification is GNU 14.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
/avant/src
external start
external end
-- Configuring done (0.8s)
-- Generating done (0.1s)
-- Build files have been written to: /avant/build
root@d31b5733a6ae:/avant/build# make -j2
[  1%] Building CXX object external/CMakeFiles/avant-buffer.dir/avant-buffer/buffer.cpp.o
[  2%] Building CXX object external/CMakeFiles/avant-xml.dir/avant-xml/document.cpp.o
[  3%] Linking CXX shared library libavant-buffer.so
[  3%] Built target avant-buffer
[  4%] Building C object external/CMakeFiles/avant-lua.dir/lua/lapi.c.o
[  5%] Building C object external/CMakeFiles/avant-lua.dir/lua/lauxlib.c.o
...
[ 99%] Building CXX object CMakeFiles/avant.dir/protocol/proto_res/proto_tunnel.pb.cc.o
[100%] Linking CXX executable /avant/bin/avant
[100%] Built target avant

root@d31b5733a6ae:/avant/build# cd ../bin
root@d31b5733a6ae:/avant/bin# ls
avant  config  lua

我们成功构建出了项目的可执行文件。

Setting Environment Variables

FROM ubuntu:25.04
WORKDIR /avant
COPY ./ ./
RUN apt update
RUN apt install cmake g++ make git -y
RUN apt install protobuf-compiler libprotobuf-dev  -y
RUN apt install libssl-dev -y
ENV AVANT_LISTEN_PORT=20023

启动一个容器

root@ser745692301841:/dev_dir/dockerlab/avant# docker run -it docker-avant
root@adb1cdb66bed:/avant# printenv 
HOSTNAME=adb1cdb66bed
PWD=/avant
AVANT_LISTEN_PORT=20023
HOME=/root
...
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/printenv
root@adb1cdb66bed:/avant# printenv AVANT_LISTEN_PORT
20023
root@adb1cdb66bed:/avant# echo $AVANT_LISTEN_PORT
20023

项目build过程写进Dockerfile

这么一来我们可以将项目构建命令写进Dockerfile

FROM ubuntu:25.04
WORKDIR /avant
COPY ./ ./
RUN apt update
RUN apt install cmake g++ make git -y
RUN apt install protobuf-compiler libprotobuf-dev  -y
RUN apt install libssl-dev -y
WORKDIR /avant
RUN rm -rf CMakeCache.txt \
    && cd protocol \
    && make \
    && cd .. \
    && mkdir build \
    && rm -rf ./build/* \
    && cd build \
    && cmake .. \
    && make -j3 \
    && cd .. \
    && cd bin \
    && ls
WORKDIR /avant/bin

Exposing Ports

在 Dockerfile中 使用EXPOSE 声明端口是个好习惯,这仅仅是像文档一样告诉别人 启动的容器需要暴露哪些端口。

FROM ubuntu:25.04
WORKDIR /avant
COPY ./ ./
RUN apt update
RUN apt install cmake g++ make git -y
RUN apt install protobuf-compiler libprotobuf-dev  -y
RUN apt install libssl-dev -y
WORKDIR /avant
RUN rm -rf CMakeCache.txt \
    && cd protocol \
    && make \
    && cd .. \
    && mkdir build \
    && rm -rf ./build/* \
    && cd build \
    && cmake .. \
    && make -j3 \
    && cd .. \
    && cd bin \
    && ls
WORKDIR /avant/bin
EXPOSE 20023 20024

main.ini 为avant配置文件,公网监听端口为20023、IPC通信端口为20024。

root@adb1cdb66bed:/avant/bin# ls
config  lua
root@adb1cdb66bed:/avant/bin# cd config
root@adb1cdb66bed:/avant/bin/config# ls
ipc.json  main.ini  ssl_cfg.sh  workflow.xml
root@adb1cdb66bed:/avant/bin/config# cat main.ini
[server]
app_id = 0.0.0.1
ip = 0.0.0.0
port = 20023
# worker_cnt range [1, 511]
worker_cnt = 4
# max_client_cnt range [1, 8388607]
max_client_cnt = 20000
# using wait_time setting tick time, 10ms
epoll_wait_time = 100
accept_per_tick = 100
task_type = HTTP_TASK
#task_type = STREAM_TASK
#task_type = WEBSOCKET_TASK
http_static_dir = /avant_static
lua_dir = ./lua
# SSL_CTX_use_certificate_chain_file
crt.pem = ./config/certificate.crt
# SSL_CTX_use_PrivateKey_file
key.pem = ./config/private_key.pem
use_ssl = 0
daemon = 1
# DEBUG 0, INFO 1, WARN 2, ERROR 3, FATAL 4,
log_level = 0

[ipc]
max_ipc_conn_num = 100
ipc_json_path = ./config/ipc.json

[client]
threads = 10
root@adb1cdb66bed:/avant/bin/config# cat ipc.json
[
    {
        "app_id": "0.0.0.1",
        "ip": "127.0.0.1",
        "port": 20024
    }
]
root@adb1cdb66bed:/avant/bin/config# 

Setting the User

默认情况下 docker使用具有最高权限的root用户运行我们的应用程序。

# 建立组
root@767e336bc8e9:/# groupadd app
# 建立用户并加入组
root@767e336bc8e9:/# useradd -r -g app -s /usr/sbin/nologin app
# 查看用户属于哪些组
root@767e336bc8e9:/# groups app
app : app

# 另一种更简洁的 Ubuntu 写法
adduser --system --ingroup app app
FROM ubuntu:25.04
WORKDIR /avant
COPY ./ ./
RUN apt update
RUN apt install cmake g++ make git -y
RUN apt install protobuf-compiler libprotobuf-dev  -y
RUN apt install libssl-dev -y
EXPOSE 20023 20024
RUN groupadd app && useradd -r -g app -s /usr/sbin/nologin app
USER app
WORKDIR /avant
RUN rm -rf CMakeCache.txt \
    && cd protocol \
    && make \
    && cd .. \
    && mkdir build \
    && rm -rf ./build/* \
    && cd build \
    && cmake .. \
    && make -j3 \
    && cd .. \
    && cd bin \
    && ls
WORKDIR /avant/bin
root@ser745692301841:/dev_dir/dockerlab/avant# docker build -t docker-avant ./

会构建失败,因为 COPY项目时是用 root用户执行的,然后 构架项目用的app用户,app用户没有某些项目文件权限。

改一下Dockerfile

FROM ubuntu:25.04
RUN apt update
RUN apt install cmake g++ make git -y
RUN apt install protobuf-compiler libprotobuf-dev  -y
RUN apt install libssl-dev -y
RUN groupadd app && useradd -r -g app -s /usr/sbin/nologin app
WORKDIR /avant
COPY ./ ./
RUN rm -rf CMakeCache.txt \
    && cd protocol \
    && make \
    && cd .. \
    && mkdir build \
    && rm -rf ./build/* \
    && cd build \
    && cmake .. \
    && make -j3 \
    && cd .. \
    && cd bin \
    && ls
WORKDIR /avant/bin
EXPOSE 20023 20024
USER app

Defining Entrypoints

为了方便我们还是直接使用 root, 因为COPY 的动作是用 root执行的,再此我们不关心用户问题了。

FROM ubuntu:25.04
RUN apt update
RUN apt install cmake g++ make git -y
RUN apt install protobuf-compiler libprotobuf-dev  -y
RUN apt install libssl-dev -y
WORKDIR /avant
COPY ./ ./
RUN rm -rf CMakeCache.txt \
    && cd protocol \
    && make \
    && cd .. \
    && mkdir build \
    && rm -rf ./build/* \
    && cd build \
    && cmake .. \
    && make -j3 \
    && cd .. \
    && cd bin \
    && ls
WORKDIR /avant/bin
EXPOSE 20023 20024
docker build -t docker-avant ./
# 启动容器
root@ser745692301841:/dev_dir/dockerlab/avant# docker run docker-avant ./avant
loading file sucess: /avant/bin/config/main.ini

执行了 /avant/bin 下的 avant可执行文件 avant配置文件 main.ini daemon = 1 制定了 avant为守护进程运行。

root@ser745692301841:/dev_dir/dockerlab/avant/bin# ls
config  lua
root@ser745692301841:/dev_dir/dockerlab/avant/bin# cat config/main.ini
[server]
app_id = 0.0.0.1
ip = 0.0.0.0
port = 20023
# worker_cnt range [1, 511]
worker_cnt = 4
# max_client_cnt range [1, 8388607]
max_client_cnt = 20000
# using wait_time setting tick time, 10ms
epoll_wait_time = 100
accept_per_tick = 100
task_type = HTTP_TASK
#task_type = STREAM_TASK
#task_type = WEBSOCKET_TASK
http_static_dir = /avant_static
lua_dir = ./lua
# SSL_CTX_use_certificate_chain_file
crt.pem = ./config/certificate.crt
# SSL_CTX_use_PrivateKey_file
key.pem = ./config/private_key.pem
use_ssl = 0
daemon = 1
# DEBUG 0, INFO 1, WARN 2, ERROR 3, FATAL 4,
log_level = 0

[ipc]
max_ipc_conn_num = 100
ipc_json_path = ./config/ipc.json

[client]

关于 RUN 与 CMD,RUN是构建时指令,构建镜像时的运行命令。

CMD 是在得到镜像后,启动容器执行的命令。

# Shell form
# CMD ./avant
# Exec form
CMD ["avant"]
ENTRYPOINT [ "avant" ]

例如nodejs项目

CMD ["npm", "start"]
ENTRYPOINT ["npm", "start"]

CMD 与 ENTRYPOINT 有何不同,CMD可以轻易的被覆盖

docker run react-app sh
# 此处的sh 会把 CMD ["npm", "start"] 覆盖掉
# 而使用ENTRYPOINT 就不能 必须指定 --entrypoint才行
docker run react-app --entrypoint 你的命令

Speeding Up Builds

我们发现每次build都会花费很长时间,这是因为 我们配置环境花费了大量时间。在 apt install 时。

这是可以优化的,Image中是有层级的,你可以理解为层级就是只有配置文件的操作系统。

root@ser745692301841:/dev_dir/dockerlab/avant# docker history docker-avant
IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
ae7b5f04f085   45 seconds ago       /bin/sh -c #(nop)  EXPOSE 20023 20024           0B        
d4c5825849a5   45 seconds ago       /bin/sh -c #(nop) WORKDIR /avant/bin            0B        
5545133ce33e   46 seconds ago       /bin/sh -c rm -rf CMakeCache.txt     && cd p…   70.6MB    
59094d39a6c5   About a minute ago   /bin/sh -c #(nop) COPY dir:27c02cd54de056900…   3.14MB    
1c44fb52b068   About a minute ago   /bin/sh -c #(nop) WORKDIR /avant                0B        
89e1cc658d0d   About a minute ago   /bin/sh -c apt install libssl-dev -y            16.7MB    
c8365f8a1bb3   About a minute ago   /bin/sh -c apt install protobuf-compiler lib…   20.7MB    
f4f7a3fa69d7   2 minutes ago        /bin/sh -c apt install cmake g++ make git -y    421MB     
2e2b66762d70   3 minutes ago        /bin/sh -c apt update                           40.2MB    
482b635a13cd   8 days ago           /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B        
<missing>      8 days ago           /bin/sh -c #(nop) ADD file:286c37c491e2efd33…   77MB      
<missing>      8 days ago           /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B        
<missing>      8 days ago           /bin/sh -c #(nop)  LABEL org.opencontainers.…   0B        
<missing>      8 days ago           /bin/sh -c #(nop)  ARG LAUNCHPAD_BUILD_ARCH     0B        
<missing>      8 days ago           /bin/sh -c #(nop)  ARG RELEASE                  0B    

docker有一种自我优化的内在机制,当我们每次让docker去创建映像,它会观察第一个命令,看看命令改变了没有,如果没有 变化它就不会重构映像,它会从缓存中复用,然后看第二条重复检查变化,而COPY 比较特殊,docker看不出是否发生了变化, 这样它就需要去看具体文件内容,也就是说,当你在应用中进行微笑改动,docker就无法使用它的缓存,COPY之后的所有层级都要进行重构。

例如nodejs项目

FROM node:14.16.0-alpine3.13
WORKDIR /app
COPY . .
RUN npm install
ENV API_URL=http://api.myapp.com/
EXPOSE 3000
CMD ["npm", "start"]

这样每次build,都会重新 npm install。

可以优化为

FROM node:14.16.0-alpine3.13
WORKDIR /app
COPY package*.json .
RUN npm install
COPY . .
ENV API_URL=http://api.myapp.com/
EXPOSE 3000
CMD ["npm", "start"]

这样就不会每次都要从网络上拉取npm包。

如何清空缓存呢。

清空全部镜像缓存

# 不管在哪个目录下都能执行
docker builder prune -af

如果你只是想重建某个项目,不想全局清理,可以用:

docker build --no-cache -t 镜像名 .

所以那些不经常改变的部分应该放在dockerfile内容的前面,那些频繁改动的部分应该放在后面。

Removing Images

移除没有使用中的镜像

docker image prune

移除停止状态的容器

docker container prune
root@ser745692301841:/dev_dir/dockerlab/avant# docker image

Usage:  docker image COMMAND

Manage images

Commands:
  build       Build an image from a Dockerfile
  history     Show the history of an image
  import      Import the contents from a tarball to create a filesystem image
  inspect     Display detailed information on one or more images
  load        Load an image from a tar archive or STDIN
  ls          List images
  prune       Remove unused images
  pull        Download an image from a registry
  push        Upload an image to a registry
  rm          Remove one or more images
  save        Save one or more images to a tar archive (streamed to STDOUT by default)
  tag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE

Run 'docker image COMMAND --help' for more information on a command.

可以看见有 rm Remove one or more images

docker image rm 镜像名字
docker image rm IMAGEID

Tagging Images

docker build -t docker-avant:0.0.1 ./
root@ser745692301841:/dev_dir/dockerlab/avant# docker images
REPOSITORY     TAG          IMAGE ID       CREATED          SIZE
docker-avant   latest       ae7b5f04f085   45 minutes ago   650MB
node           alpine       0d3f5d817db1   8 days ago       168MB
ubuntu         25.04        482b635a13cd   8 days ago       77MB
redis          latest       f2cd22713a18   2 months ago     128MB
mongo          latest       ccf68b64aa3e   3 months ago     901MB
postgres       latest       445ed93b882f   3 months ago     438MB
rabbitmq       management   928da815d9a3   5 months ago     275MB
mysql          latest       4c2531d6bf10   5 months ago     859MB
root@ser745692301841:/dev_dir/dockerlab/avant# docker build -t docker-avant:0.0.1 ./
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

Sending build context to Docker daemon  3.417MB
Step 1/10 : FROM ubuntu:25.04
 ---> 482b635a13cd
Step 2/10 : RUN apt update
 ---> Using cache
 ---> 2e2b66762d70
Step 3/10 : RUN apt install cmake g++ make git -y
 ---> Using cache
 ---> f4f7a3fa69d7
Step 4/10 : RUN apt install protobuf-compiler libprotobuf-dev  -y
 ---> Using cache
 ---> c8365f8a1bb3
Step 5/10 : RUN apt install libssl-dev -y
 ---> Using cache
 ---> 89e1cc658d0d
Step 6/10 : WORKDIR /avant
 ---> Using cache
 ---> 1c44fb52b068
Step 7/10 : COPY ./ ./
 ---> Using cache
 ---> 59094d39a6c5
Step 8/10 : RUN rm -rf CMakeCache.txt     && cd protocol     && make     && cd ..     && mkdir build     && rm -rf ./build/*     && cd build     && cmake ..     && make -j3     && cd ..     && cd bin     && ls
 ---> Using cache
 ---> 5545133ce33e
Step 9/10 : WORKDIR /avant/bin
 ---> Using cache
 ---> d4c5825849a5
Step 10/10 : EXPOSE 20023 20024
 ---> Using cache
 ---> ae7b5f04f085
Successfully built ae7b5f04f085
Successfully tagged docker-avant:0.0.1
root@ser745692301841:/dev_dir/dockerlab/avant# docker images
REPOSITORY     TAG          IMAGE ID       CREATED          SIZE
docker-avant   0.0.1        ae7b5f04f085   46 minutes ago   650MB
docker-avant   latest       ae7b5f04f085   46 minutes ago   650MB
node           alpine       0d3f5d817db1   8 days ago       168MB
ubuntu         25.04        482b635a13cd   8 days ago       77MB
redis          latest       f2cd22713a18   2 months ago     128MB
mongo          latest       ccf68b64aa3e   3 months ago     901MB
postgres       latest       445ed93b882f   3 months ago     438MB
rabbitmq       management   928da815d9a3   5 months ago     275MB
mysql          latest       4c2531d6bf10   5 months ago     859MB

打出了两个 docker-avant 镜像但是 TAG不同,一个是 docker-avant:0.0.1 一个是 docker-avant:latest, 但是两者的 IMAGE ID是相同。

删除指定TAG的镜像:

root@ser745692301841:/dev_dir/dockerlab/avant# docker images
REPOSITORY     TAG          IMAGE ID       CREATED          SIZE
docker-avant   0.0.1        ae7b5f04f085   48 minutes ago   650MB
docker-avant   latest       ae7b5f04f085   48 minutes ago   650MB
node           alpine       0d3f5d817db1   8 days ago       168MB
ubuntu         25.04        482b635a13cd   8 days ago       77MB
redis          latest       f2cd22713a18   2 months ago     128MB
mongo          latest       ccf68b64aa3e   3 months ago     901MB
postgres       latest       445ed93b882f   3 months ago     438MB
rabbitmq       management   928da815d9a3   5 months ago     275MB
mysql          latest       4c2531d6bf10   5 months ago     859MB
root@ser745692301841:/dev_dir/dockerlab/avant# docker image remove docker-avant:0.0.1
Untagged: docker-avant:0.0.1

在build后再打TAG

root@ser745692301841:/dev_dir/dockerlab/avant# docker image tag docker-avant:latest docker-avant:1
root@ser745692301841:/dev_dir/dockerlab/avant# docker image tag  ae7b5f04f085   docker-avant:2
root@ser745692301841:/dev_dir/dockerlab/avant# docker images
REPOSITORY     TAG          IMAGE ID       CREATED          SIZE
docker-avant   1            ae7b5f04f085   50 minutes ago   650MB
docker-avant   2            ae7b5f04f085   50 minutes ago   650MB
docker-avant   latest       ae7b5f04f085   50 minutes ago   650MB
node           alpine       0d3f5d817db1   8 days ago       168MB
ubuntu         25.04        482b635a13cd   8 days ago       77MB
redis          latest       f2cd22713a18   2 months ago     128MB
mongo          latest       ccf68b64aa3e   3 months ago     901MB
postgres       latest       445ed93b882f   3 months ago     438MB
rabbitmq       management   928da815d9a3   5 months ago     275MB
mysql          latest       4c2531d6bf10   5 months ago     859MB

Sharing Images

https://hub.docker.com/

注册自己的账号。

先创建一个 repository https://hub.docker.com/repository/create , 免费计划中 一个账号只能由一个私有 repositories, 多了要付费才行。

docker tag local-image:tagname new-repo:tagname
docker push new-repo:tagname

登录 docker后才能 push

root@ser745692301841:/dev_dir/dockerlab/avant# docker login -u gaowanlu
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credential-stores

Login Succeeded

Saving and Loading Images

假设你在这台机器上有一个镜像,你想把它放在另一台机器上,但不经过docker hub。

在这种情况下,您可以将该映像保存为压缩文件并在另一台机器上加载。

root@ser745692301841:/dev_dir/dockerlab/avant# docker image save --help

Usage:  docker image save [OPTIONS] IMAGE [IMAGE...]

Save one or more images to a tar archive (streamed to STDOUT by default)

Aliases:
  docker image save, docker save

Options:
  -o, --output string   Write to a file, instead of STDOUT

save docker-avant镜像。

root@ser745692301841:/dev_dir/dockerlab/avant# docker images
REPOSITORY     TAG          IMAGE ID       CREATED             SIZE
docker-avant   latest       ae7b5f04f085   About an hour ago   650MB
node           alpine       0d3f5d817db1   8 days ago          168MB
ubuntu         25.04        482b635a13cd   8 days ago          77MB
redis          latest       f2cd22713a18   2 months ago        128MB
mongo          latest       ccf68b64aa3e   3 months ago        901MB
postgres       latest       445ed93b882f   3 months ago        438MB
rabbitmq       management   928da815d9a3   5 months ago        275MB
mysql          latest       4c2531d6bf10   5 months ago        859MB
root@ser745692301841:/dev_dir/dockerlab/avant# docker image save -o docker-avant.tar ae7b5f04f085
root@ser745692301841:/dev_dir/dockerlab/avant# ls
CMakeLists.txt  Dockerfile  LICENSE  README.md  bin  build  centos8.md  client  docker-avant.tar  external  protobuf.md  protocol  src  test
root@ser745692301841:/dev_dir/dockerlab/avant# ll -lrth docker-avant.tar
-rw------- 1 root root 631M Oct  4 04:11 docker-avant.tar

使用 image load

root@ser745692301841:/dev_dir/dockerlab/avant# docker image load --help

Usage:  docker image load [OPTIONS]

Load an image from a tar archive or STDIN

Aliases:
  docker image load, docker load

Options:
  -i, --input string   Read from tar archive file, instead of STDIN
  -q, --quiet          Suppress the load output
root@ser745692301841:/dev_dir/dockerlab/avant# docker image load -i docker-avant.tar

Starting Containers

查看正在运行的容器

root@ser745692301841:/dev_dir/note# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

为一个镜像启动一个新的容器实例

docker run 镜像:tag或者镜像ID

守护进程运行

docker run -d 镜像

为docker容器进程起名字

docker run -d --name blue-sky react-app

Viewing the Logs

对于 docker run -d 运行的进程,我们不知道终端输出了什么。这可以通过Log来查看

docker logs 容器ID
root@ser745692301841:/dev_dir/note# docker logs --help

Usage:  docker logs [OPTIONS] CONTAINER

Fetch the logs of a container

Aliases:
  docker container logs, docker logs

Options:
      --details        Show extra details provided to logs
  -f, --follow         Follow log output
      --since string   Show logs since timestamp (e.g. "2013-01-02T13:23:37Z") or relative (e.g. "42m" for 42 minutes)
  -n, --tail string    Number of lines to show from the end of the logs (default "all")
  -t, --timestamps     Show timestamps
      --until string   Show logs before a timestamp (e.g. "2013-01-02T13:23:37Z") or relative (e.g. "42m" for 42 minutes)

例如,查看最新的5行,并查看日志内容的时间戳

docker logs -n 5 -t 容器ID

Publishing Ports

可以看到 docker ps,可以看到进程的PORTS

root@ser745692301841:/dev_dir/note# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

将主机的端口和容器的端口进行映射:

docker run -d -p 主机的端口:容器的端口 --name 进程名 镜像名

Executing Commands in Running Containers

当你启动一个容器时,它会执行我们在Dockerfile文件中指定的默认命令。

如果你想在稍后运行的容器中执行一个命令呢,使用 docker exec

docker exec 容器Name或者容器ID 命令行

使用终端

docker exec -it 容器Name或容器ID bash
# 或者
docker exec -it 容器Name或容器ID sh

Stopping and Starting Containers

停止容器

docker stop 容器名或ID

重启容器

docker start c1

docker start与docker run,有什么不同,使用docker run我们启动一个新的容器,而使用docker start我们启动一个已停止的容器。

Removing Containers

有两种方法可以移除容器

docker container rm 容器名字或ID

删除一个容器,这个容器不能为运行中。要么先停止然后rm,要么直接强制删除。

docker container rm -f 容器名字或ID

查看全部容器,docker ps列出的只是运行中的,常看全部容器要加-a

docker ps -a

一次性清除所有停止的容器

docker container prune

Container File System

每个容器都有自己的文件系统,对其他容器是不可见的。

如果我们删除容器,容器的文件系统也会随之消失,我们将会丢失我们的数据。

基本上我们永远不应该将数据存储在容器的文件系统中。

Persisting Data using Volumes

卷Volume是容器外部的存储。

root@ser745692301841:/dev_dir/note# docker volume

Usage:  docker volume COMMAND

Manage volumes

Commands:
  create      Create a volume
  inspect     Display detailed information on one or more volumes
  ls          List volumes
  prune       Remove unused local volumes
  rm          Remove one or more volumes

Run 'docker volume COMMAND --help' for more information on a command.

查看卷

root@ser745692301841:/dev_dir/note# docker volume ls
DRIVER    VOLUME NAME
local     docker_mysql-data

root@ser745692301841:/dev_dir/note# docker volume inspect docker_mysql-data
[
    {
        "CreatedAt": "2025-08-22T01:16:19Z",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "docker",
            "com.docker.compose.version": "1.29.2",
            "com.docker.compose.volume": "mysql-data"
        },
        "Mountpoint": "/var/lib/docker/volumes/docker_mysql-data/_data",
        "Name": "docker_mysql-data",
        "Options": null,
        "Scope": "local"
    }
]

其中的 Driver为local说明,卷在本地主机上,在 /var/lib/docker/volumes/docker_mysql-data/_data。也可以在云中创建卷的驱动程序, 如果您使用云平台,需要自己研究找到该云平台中创建卷的驱动程序。

创建卷 app-data

docker volume create app-data

容器如何使用卷

docker run -d -p 4000:3000 -v app-data:/app/data react-app
# 如果使用一个未创建的卷,docker会自动创建
docker run -d -p 4000:3000 -v app-data2:/app/data react-app
# 如果镜像中没有目录 /app/data docker会自动在容器中创建 但是使用root用户创建的
# 需要注意用户权限问题

同一个卷可以被挂载到多个容器上,可以在多个容器之间共享一个卷。

Copying Files between the Host and Containers

有时候我们需要在主机和容器之间复制文件,例如,我们假设在我们的容器中有这个日志文件,我们想要把它带到主机上进行分析。

root@ser745692301841:/dev_dir/note# docker cp --help

Usage:  docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
        docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

Copy files/folders between a container and the local filesystem

Use '-' as the source to read a tar archive from stdin
and extract it to a directory destination in a container.
Use '-' as the destination to stream a tar archive of a
container source to stdout.

Aliases:
  docker container cp, docker cp

Options:
  -a, --archive       Archive mode (copy all uid/gid information)
  -L, --follow-link   Always follow symbol link in SRC_PATH
  -q, --quiet         Suppress progress output during copy. Progress output is automatically suppressed if no terminal is attached
# 将容器ID下的/app/log.txt 复制到主机的当前目录下
docker cp 容器ID:/app/log.txt ./

同样可以从主机复制到文件到容器中

docker cp 主机文件 容器ID:/app/

Sharing the Source Code with a Container

例如你是一个前端工程师,如改了一下html,如何 Publishing Changes

对于生产机器,你应该始终构建一个新镜像,正确标记它,然后部署。

对于开发环境,你不想在每次代码中做一个微小的修改时重启build新镜像,你可能说复制文件怎么样,但那也是一件麻烦事。

现实是,我们可以在主机上的一个目录和容器内部的一个目录之间创建映射或绑定。

Host                  Container
/project ------------ >/app
docker run -d -p 5001:3000 -v /project:/app/data 镜像

root@ser745692301841:/dev_dir/note# docker run -d -p 5001:3000 -v $(pwd):/app/data 镜像
# 等价于
docker run -d -p 5001:3000 -v /dev_dir/note:/app/data 镜像

Docker Compose

例如我们平时项目中,一般有前端项目、后端服务、DB、消息队列、Redis等,这些通常都是好多镜像,很多容器同意构成了整个大系统。

Installing Docker Compose

https://docs.docker.com/compose/install/

更方便的教程是直接去问 ChatGPT。

root@ser745692301841:/dev_dir/note# docker-compose --version
docker-compose version 1.29.2, build unknown

Cleaning Up our Workspace

删除所有容器

docker container rm -f $(docker container ls -aq)

删除所有镜像

docker image rm -f $(docker image ls -q)

The Sample Web Application

例如我们有一个 前后端项目

backend(后端项目里有Dockerfile)
frontend(前端项目里有Dockerfile)
docker-compose.yml

而且你还要准备 mongodb,但是有了 docker compose 这将使得部署环境变得简单。

docker-compose的文件,它是用来组合多容器应用程序的。

root@ser745692301841:/dev_dir/dockerlab# ls
backend  docker-compose.yml  frontend
root@ser745692301841:/dev_dir/dockerlab# docker-compose up

可以一次性把 backend、frontend、database环境准备好。

JSON and YAML Formats

JSON不在阐述,大家基本都知道,如

{
  "name" : "hello",
  "price": 148,
  "is_published": true,
  "tags": ["software", "devops"],
  "author": {
    "first_name": "wanlu",
    "last_name": "gao"
  }
}

将上面json转为yaml或yml表述

---
name: hello
price: 148
is_published: true
tags:
  - software
  - devops
author:
  first_name: wanlu
  last_name: gao

Creating a Compose File

https://docs.docker.com/reference/compose-file/version-and-name/

在此我们不可能学习到全部内容,还是要多了解官方文档内容,像端口、卷、环境变量 都可以在 docker-compose中配置

docker-compose.yml

name: myapp
version: "3.8"

services:
  web:
    depends_on:
      - api
    build: ./frontend
    ports:
      - 3000:3000
    environment:
      DB_URL: mongodb://db/vidly
  api:
    depends_on:
      - db
    build: ./backend
    ports:
      - 3001:3001
  db:
    image: mongo:4.0-xenial
    ports:
      - 27017:27017
    volumes:
      - vidly:/data/db

volumes:
  vidly: 

https://docs.docker.com/reference/compose-file/services/#depends_on

With the depends_on attribute, you can control the order of service startup and shutdown. It is useful if services are closely coupled, and the startup sequence impacts the application’s functionality.

Building Images

root@ser745692301841:/dev_dir/dockerlab# docker-compose
Define and run multi-container applications with Docker.

Usage:
  docker-compose [-f <arg>...] [--profile <name>...] [options] [--] [COMMAND] [ARGS...]
  docker-compose -h|--help

Options:
  -f, --file FILE             Specify an alternate compose file
                              (default: docker-compose.yml)
  -p, --project-name NAME     Specify an alternate project name
                              (default: directory name)
  --profile NAME              Specify a profile to enable
  -c, --context NAME          Specify a context name
  --verbose                   Show more output
  --log-level LEVEL           Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
  --ansi (never|always|auto)  Control when to print ANSI control characters
  --no-ansi                   Do not print ANSI control characters (DEPRECATED)
  -v, --version               Print version and exit
  -H, --host HOST             Daemon socket to connect to

  --tls                       Use TLS; implied by --tlsverify
  --tlscacert CA_PATH         Trust certs signed only by this CA
  --tlscert CLIENT_CERT_PATH  Path to TLS certificate file
  --tlskey TLS_KEY_PATH       Path to TLS key file
  --tlsverify                 Use TLS and verify the remote
  --skip-hostname-check       Don't check the daemon's hostname against the
                              name specified in the client certificate
  --project-directory PATH    Specify an alternate working directory
                              (default: the path of the Compose file)
  --compatibility             If set, Compose will attempt to convert keys
                              in v3 files to their non-Swarm equivalent (DEPRECATED)
  --env-file PATH             Specify an alternate environment file

Commands:
  build              Build or rebuild services
  config             Validate and view the Compose file
  create             Create services
  down               Stop and remove resources
  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 version information and quit

上面的命令都将应用于我们的整个docker-compose应用程序,因此,这些命令中的大多数将映像我们应用程序中的多个服务或多个容器。

root@ser745692301841:/dev_dir/dockerlab# docker build --help
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/


Usage:  docker build [OPTIONS] PATH | URL | -

Build an image from a Dockerfile

Aliases:
  docker image build, docker build, docker builder build

Options:
      --add-host list           Add a custom host-to-IP mapping ("host:ip")
      --build-arg list          Set build-time variables
      --cache-from strings      Images to consider as cache sources
      --cgroup-parent string    Set the parent cgroup for the "RUN" instructions during build
      --compress                Compress the build context using gzip
      --cpu-period int          Limit the CPU CFS (Completely Fair Scheduler) period
      --cpu-quota int           Limit the CPU CFS (Completely Fair Scheduler) quota
  -c, --cpu-shares int          CPU shares (relative weight)
      --cpuset-cpus string      CPUs in which to allow execution (0-3, 0,1)
      --cpuset-mems string      MEMs in which to allow execution (0-3, 0,1)
      --disable-content-trust   Skip image verification (default true)
  -f, --file string             Name of the Dockerfile (Default is "PATH/Dockerfile")
      --force-rm                Always remove intermediate containers
      --iidfile string          Write the image ID to the file
      --isolation string        Container isolation technology
      --label list              Set metadata for an image
  -m, --memory bytes            Memory limit
      --memory-swap bytes       Swap limit equal to memory plus swap: -1 to enable unlimited swap
      --network string          Set the networking mode for the RUN instructions during build (default "default")
      --no-cache                Do not use cache when building the image
      --platform string         Set platform if server is multi-platform capable
      --pull                    Always attempt to pull a newer version of the image
  -q, --quiet                   Suppress the build output and print image ID on success
      --rm                      Remove intermediate containers after a successful build (default true)
      --security-opt strings    Security options
      --shm-size bytes          Size of "/dev/shm"
  -t, --tag list                Name and optionally a tag in the "name:tag" format
      --target string           Set the target build stage to build.
      --ulimit ulimit           Ulimit options (default [])

在 docker-compose.yml 文件的目录下执行, 将会build整个docker-compose 应用

docker-compose build
# 完全不用缓存rebuild
docker-compose build --no-cache

docker images 可以得到5个镜像,其中 vidly_frontend和vidly_backend 是build过程中产生的原始镜像, vidly_web、vidly_api 其实是从原始镜像打了TAG,它们的IMAGE_ID可以看出。

vidly_frontend
vidly_web
vidly_api
vidly_backend
mongo

Starting and Stopping the Application

root@ser745692301841:/dev_dir/myapp# docker-compose up

如果compose镜像都准备好了,那么compose将在容器内运行它们,否则将自动构建镜像。

root@ser745692301841:/dev_dir/myapp# docker-compose up --help
Builds, (re)creates, starts, and attaches to containers for a service.

Unless they are already running, this command also starts any linked services.

The `docker-compose up` command aggregates the output of each container. When
the command exits, all containers are stopped. Running `docker-compose up -d`
starts the containers in the background and leaves them running.

If there are existing containers for a service, and the service's configuration
or image was changed after the container's creation, `docker-compose up` picks
up the changes by stopping and recreating the containers (preserving mounted
volumes). To prevent Compose from picking up changes, use the `--no-recreate`
flag.

If you want to force Compose to stop and recreate all containers, use the
`--force-recreate` flag.

Usage: up [options] [--scale SERVICE=NUM...] [--] [SERVICE...]

Options:
    -d, --detach               Detached mode: Run containers in the background,
                               print new container names. Incompatible with
                               --abort-on-container-exit.
    --no-color                 Produce monochrome output.
    --quiet-pull               Pull without printing progress information
    --no-deps                  Don't start linked services.
    --force-recreate           Recreate containers even if their configuration
                               and image haven't changed.
    --always-recreate-deps     Recreate dependent containers.
                               Incompatible with --no-recreate.
    --no-recreate              If containers already exist, don't recreate
                               them. Incompatible with --force-recreate and -V.
    --no-build                 Don't build an image, even if it's missing.
    --no-start                 Don't start the services after creating them.
    --build                    Build images before starting containers.
    --abort-on-container-exit  Stops all containers if any container was
                               stopped. Incompatible with -d.
    --attach-dependencies      Attach to dependent containers.
    -t, --timeout TIMEOUT      Use this timeout in seconds for container
                               shutdown when attached or when containers are
                               already running. (default: 10)
    -V, --renew-anon-volumes   Recreate anonymous volumes instead of retrieving
                               data from the previous containers.
    --remove-orphans           Remove containers for services not defined
                               in the Compose file.
    --exit-code-from SERVICE   Return the exit code of the selected service
                               container. Implies --abort-on-container-exit.
    --scale SERVICE=NUM        Scale SERVICE to NUM instances. Overrides the
                               `scale` setting in the Compose file if present.
    --no-log-prefix            Don't print prefix in logs.

查看 docker-compose 进程

root@ser745692301841:/dev_dir/myapp# docker-compose ps
  Name        Command   State   ...
vidly_api_1   .....     up      ...
vidly_db_1    .....     up      ...
vidly_web_1   .....     up      ...

关闭整个compose应用

root@ser745692301841:/dev_dir/myapp# docker-compose down

Docker Networking

DockerCompose 将自动创建一个网络,并将我们的容器添加到该网络中,这样不同容器就可以相互通信了

root@ser745692301841:/dev_dir/myapp# docker-compose up -d
Creating network "vidly_default" with the default driver
...

docker network

root@ser745692301841:/dev_dir/dockerlab# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
54d8cd91a4b0   bridge    bridge    local
8852c867c445   host      host      local
c33ae1c821b7   none      null      local
9fc9a8b8f73c   vidly_default    bridge    local

vidly_default 这个网络包含三个主机或三个容器 web、api、db

所以这些主机或这些容器可以使用它们的名称相互通信 例如 api的 DB_URL 写的是 mongodb://db/vidly

mongodb://IP地址/数据库名

你可以进到 db 容器中

docker exec -it -u root db容器 sh
# 在db容器内执行 ping api
ping api
# 是可以ping通的

背后原因是,docker附带一个嵌入式DNS服务器 DNS SERVER,其中包含这些容器的名称和IP

每个容器内部,有一个称为DNS解析器 DNS RESOLVER 的组件,这个DNS解析器与DNS服务器通信,以找到目标容器的IP地址。

当我们ping API容器时,这个DNS服务器会询问服务器API机器或API容器的IP是什么

Viewing Logs

docker-compose logs

可以在一个地方查看这个应用程序所有容器的日志

root@ser745692301841:/dev_dir/dockerlab# docker-compose logs --help
View output from containers.

Usage: logs [options] [--] [SERVICE...]

Options:
    --no-color              Produce monochrome output.
    -f, --follow            Follow log output.
    -t, --timestamps        Show timestamps.
    --tail="all"            Number of lines to show from the end of the logs
                            for each container.
    --no-log-prefix         Don't print prefix in logs.

当然可以仍用 docker logs 容器,来看指定容器的。

Publishing Changes

像原来单个容器一样使用卷

name: myapp
version: "3.8"

services:
  web:
    depends_on:
      - api
    build: ./frontend
    ports:
      - 3000:3000
    environment:
      DB_URL: mongodb://db/vidly
  api:
    depends_on:
      - db
    build: ./backend
    ports:
      - 3001:3001
    volumes:
      - ./backend:/app
  db:
    image: mongo:4.0-xenial
    ports:
      - 27017:27017
    volumes:
      - vidly:/data/db

volumes:
  vidly: 

其中的 api 映射了本地目录到容器,./backend 表示我们使用 docker-compose up 的所在目录下的backend目录

    volumes:
      - ./backend:/app

Migrating the Database

大多数时候,当我们发布我们的应用程序时,我们希望我们的数据库具有特定的结构和一些数据,这被成为数据库迁移。

mongodb有个工具是 migratemongo,nodejs可以使用 migrate-mongo 依赖。

docker-compose.yml 覆盖 api 容器Dockerfile的CMD, migrate-mongo up 会执行项目里预备的db改动脚本(用js写的)

services:
  api:
    ...
    command: ./wait-for db:27017 && migrate-mongo up && npm start

但是有个问题,在我们的api容器执行 migrate-mongo up 时,我们的db服务器根本尚未准备好 即使我们的db容器可能正在运行,因为启动数据库引擎通常需要几秒钟。

wait-for 就是为了等待db启动,才继续 && migrate-mongo up && npm start 剩余命令,wait-for https://github.com/vishnubob/wait-for-it

https://docs.docker.com/compose/how-tos/startup-order/

官方有一个样例,尽量用官方的相关特性

services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy
        restart: true
      redis:
        condition: service_started
  redis:
    image: redis
  db:
    image: postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      retries: 5
      start_period: 30s
      timeout: 10s

我们更该做的是,将DB的管理与docker管理进行分离,不要扯到一起,自动升级迁移数据库本身就是非常危险的事情。

当我们执行过 docker-compose down后,网络将会删除,但是卷不会

docker volume ls
DRIVER    VOLUME NAME
local     myapp_vidly
......

myapp_vidly 前面 myapp 是 项目名称 后面vidly是卷名称。

Running Tests

前端开发在docker容器中进行npm test进行单元测试

name: myapp
version: "3.8"

services:
  web:
    depends_on:
      - api
    build: ./frontend
    image: myapp_web:0.0.1
    ports:
      - 3000:3000
    environment:
      DB_URL: mongodb://db/vidly
  web-tests:
    image: myapp_web
    volumes:
      - ./frontend:/app
    commands: npm test
  api:
    depends_on:
      - db
    build: ./backend
    image: myapp_api:0.0.1
    ports:
      - 3001:3001
    volumes:
      - ./backend:/app
  db:
    image: mongo:4.0-xenial
    ports:
      - 27017:27017
    volumes:
      - vidly:/data/db

volumes:
  vidly: 

改了代码,直接进到 myapp_tests 容器终端中,去测试就好了

image: myapp_web:0.0.1 与 image: myapp_api:0.0.1 制定了 compose build出的镜像的TAG

Deployment

将应用上 Cloud中。

Deployment Options

Cluster Solutions有自己的编排工具,Docker Swarm 它并不是特别受欢迎。如今大多数人使用另一种工具, 成为 Kubernetes,它是一个Google的产品。

Kubernetes非常复杂,下面的我们将学习单主机的,一但你开始设计到集群你就去学Kubernetes。

Getting a Virtual Private Server

Installing Docker Machine

Dev                                    PROD

Docker Machine ----Docker Images----->

安装Docker Machine 我们就可以在终端中执行docker命令,我们的命令将被发送到服务器上的docker引擎。

https://github.com/docker-archive-public/docker.machine

On Linux

$ curl -L https://github.com/docker/machine/releases/download/v0.16.2/docker-machine-`uname -s`-`uname -m` >/tmp/docker-machine &&
    chmod +x /tmp/docker-machine &&
    sudo cp /tmp/docker-machine /usr/local/bin/docker-machine

后,执行

$ docker-machine --version
docker-machine version 0.16.2, build ....

关于 docker-machine 在 2025/10/5 看的时候已经停止维护了,所以我们在此 Give Up It。

所以 去实践、去Kubernetes继续进步。

Reducing the Image Size

在前端中的生产环境的Dockerfile通常将 打包和部署分开如

Dockerfile.prod

# Step1
FROM node:14.16.0-alpine3.13 AS build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Step2
FROM nginx:1.12-alpine
COPY --from=build-stage /app/build /usr/share/nginx/html
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
docker build -t myapp_web_opt -f Dockerfile.prod ./

在docker-compose.prod.yml 更新

services:
  web:
    build:
      context: ./frontend
      dockerfile: Dockerfile.prod
    ports:
      - 80:80
    restart: unless-stopped
...
docker-compose -f docker-compose.prod.yml build

Docker Network

同主机不同compose之间

在多个Docker Compose应用之间实现容器网络通信,可以通过Docker网络来完成。

理解 Docker 网络

Docker Compose 默认会为每个 Compose 文件创建一个独立的网络(通常命名为 <项目名>_default),因此不同 Compose 应用的容器默认是隔离的,无法直接通信。要让它们通信,需要让这些容器加入同一个 Docker 网络。

创建共享的 Docker 网络

为了让不同 Compose 应用的容器能够通信,可以创建一个共享的外部 Docker 网络,然后在每个 Compose 文件中引用这个网络。

创建外部网络:

docker network create shared-network

修改Docker Compose文件:

在每个Docker Compose文件种,配置服务使用这个共享网络,假设你有两个Compose应用 app1 和 app2

app1/docker-compose.yml

version: '3.8'
services:
  app1-service:
    image: nginx
    networks:
      - shared-network
    container_name: app1-container

networks:
  shared-network:
    external: true

app2/docker-compose.yml

version: '3.8'
services:
  app2-service:
    image: redis
    networks:
      - shared-network
    container_name: app2-container

networks:
  shared-network:
    external: true

启动Compose应用

docker-compose -f app1/docker-compose.yml up -d
docker-compose -f app2/docker-compose.yml up -d

容器间通信

在同一个Docker网络中,容器可以通过容器名称或服务名称直接通信(Docker提供内置的DNS解析)

示例:

假设 app1-container(Nginx服务) 想访问 app2-container(Redis服务)

curl http://app2-container:6379

或者使用 Redis 客户端连接:

redis-cli -h app2-container -p 6379

注意:

services:
  app1-service:
    networks:
      shared-network:
        aliases:
          - app1-alias

这样其他容器可以通过 app1-alias 访问 app1-service。

验证网络连通性

检查网络

docker network inspect shared-network

确认 app1-container 和 app2-container 都在 shared-network 中。

测试连通性:

进入 app1-container:

docker exec -it app1-container bash

然后 ping 或访问 app2-container:

ping app2-container

注意事项

docker network rm shared-network

确保没有容器在使用该网络。

动态管理网络

如果多个 Compose 应用动态创建,可以编写脚本自动化创建和分配网络,或者在 Compose 文件中通过 network_mode: bridge 使用默认桥接网络(但不推荐,因为默认桥接网络的 DNS 解析不如自定义网络方便)。

不同主机不同compose之间

不同主机上的Docker Compose应用之间可以通过配置适当的Docker网络实现通信,但由于容器运行在不同的物理或虚拟主机上,无法直接使用默认的bridge网络(如单主机场景)。需要使用支持跨主机通信的网络模式,例如 overlay 网络(通常与Docker Swarm或其他容器编排工具配合使用)。

1. 使用Docker Swarm的Overlay网络

基本没人用,大家都在用 kubernetes。

初始化Docker Swarm: 在其中一台主机(主节点)上初始化Swarm

docker swarm init

这回生成一个加入Swarm的令牌 token,在其他主机(工作节点)上运行一下命令加入Swarm:

docker swarm join --token <token> <主节点IP>:<端口>

例如:

docker swarm join --token SWMTKN-1-xxx 192.168.1.100:2377

创建Overlay网络:在主节点上创建一个overlay网络:

docker network create --driver overlay --attachable shared-overlay-network

修改 Docker Compose 文件:在每个主机的 Docker Compose 文件中,配置服务使用这个 overlay 网络。例如:

主机1的docker-compose.yml

version: '3.8'
services:
  app1-service:
    image: nginx
    container_name: app1-container
    networks:
      - shared-overlay-network

networks:
  shared-overlay-network:
    external: truea

主机2的docker-compose.yml

version: '3.8'
services:
  app2-service:
    image: redis
    container_name: app2-container
    networks:
      - shared-overlay-network

networks:
  shared-overlay-network:
    external: true

部署Compose应用:在每台主机上运行 Docker Compose:

docker-compose up -d

验证通信:

docker exec -it app1-container bash
ping app2-container

或访问 Redis 服务:

redis-cli -h app2-container -p 6379

注意事项

2. 使用外部编排工具 Kubernetes

如果不适用Docker Swarm,可以考虑Kubernetes,它通过自己的网络模型(CNI 插件,如Flannel或Calico)支持跨主机容器通信。

Kubernetes的学习曲线陡峭,适合复杂或大规模应用,需要额外的Kubernetes集群管理。

3. 直接通过主机IP和端口映射

如果不使用编排工具,可以通过主机间的 IP 和端口映射实现通信,但这不是推荐方式,因为缺乏动态性和 DNS 解析。

在Docker Compose文件中映射容器端口到主机;

version: '3.8'
services:
  app2-service:
    image: redis
    ports:
      - "6379:6379"

从另一主机的容器通过主机IP和映射端口访问:

redis-cli -h <主机2的IP> -p 6379

缺点:

Docker Contexts

查看当前的Context

运行以下命令查看当前使用的 Context 和所有可用 Context:

docker context ls

输出示例:

NAME                DESCRIPTION                               DOCKER ENDPOINT
default *           Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
remote              Remote server                             tcp://192.168.1.100:2376

创建新的 Context

创建一个指向远程 Docker 主机的 Context,例如:

docker context create remote --docker "host=tcp://192.168.1.100:2376,ca=/path/to/ca.pem,cert=/path/to/cert.pem,key=/path/to/key.pem"

切换 Context

切换到另一个 Context:

docker context use remote

切换后,Docker CLI 的所有命令(如 docker ps、docker run)都会针对 remote 主机执行。这边与自动化脚本管理不同主机上的容器,只要切换context 然后执行docker命令。

查看Context详情

检查某个Context的配置

docker context inspect remote

删除 Context

删除不再需要的 Context:

docker context rm remote

使用 Context 运行命令

即使未切换 Context,也可以在命令中指定 Context:

docker --context remote ps

这会在 remote 主机上运行 docker ps,而不改变当前默认 Context。

支持的Docker Endpoint类型

Docker Contexts 支持多种类型的端点(endpoint):

与Docker Compose的关系

Docker Compose 支持 Context,可以指定某个 Context 来运行 Compose 命令。例如:

docker-compose --context remote up -d

这会将 Compose 应用的部署目标设置为 remote 主机的 Docker 守护进程。

实际应用场景

注意事项

示例:跨主机 Docker Compose 通信

为每台主机创建一个 Context:

docker context create host1 --docker "host=tcp://192.168.1.100:2376"
docker context create host2 --docker "host=tcp://192.168.1.101:2376"

部署Compose应用到不同主机

docker-compose --context host1 -f app1/docker-compose.yml up -d
docker-compose --context host2 -f app2/docker-compose.yml up -d

使用 overlay 网络(如前文所述)实现跨主机通信。

Docker Contexts 是一个强大的工具,用于管理多个 Docker 环境(本地、远程、Swarm、Kubernetes),通过简单的命令切换控制目标。它特别适合需要操作多个 Docker 主机的场景,与 Docker Compose 结合可以简化跨主机部署和通信。