首页  /  云原生  /  正文

Docker 容器基本操作与实战

云原生 2026-10-02📖 26 分钟👁 —
🐳 云原生 · Docker第 3 / 9 页123456789📖 完整导航 →

一、有了镜像才能创建容器

先拉个小的练手,centos 就够用了:

root@VM-0-9-ubuntu:~# docker pull centos
Using default tag: latest
latest: Pulling from library/centos
a1d0c7532777: Pull complete 
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Status: Downloaded newer image for centos:latest
docker.io/library/centos:latest

root@VM-0-9-ubuntu:~# docker images
                                                                                                      i Info →   U  In Use
IMAGE           ID             DISK USAGE   CONTENT SIZE   EXTRA
centos:latest   a27fd8080b51        340MB         83.5MB  

二、run 的参数

docker run --help 里参数一大堆,常用的就那么几个:

docker run [可选参数] image

--name="Name"     # 容器名字(如 tomcat01、tomcat02),用于区分容器
-d                # 后台方式运行
-it               # 使用交互方式运行,进入容器查看内容,后面要跟 /bin/bash 这类 shell
-p                # 指定容器的端口映射

# -p 的几种用法
-p ip:主机端口:容器端口
-p 主机端口:容器端口      		# 常用格式
-p 容器端口               		# 随机映射主机端口
-P                         	# 大写 P,随机指定端口

# 启动并进入容器
root@VM-0-9-ubuntu:~# docker run -it centos /bin/bash
[root@1a67a17a9f1d /]# ll
bash: ll: command not found
# 主机名就是镜像名
[root@1a67a17a9f1d /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
# 退出容器
[root@1a67a17a9f1d /]# exit
exit
# 容器退出的快捷方式:Ctrl + D
# 两种退出方式都会停止容器运行
[root@15c41eb4491f /]# exit
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS          PORTS     NAMES
15c41eb4491f   centos    "/bin/bash"   50 seconds ago   Up 49 seconds             intelligent_hoover

-it 进去之后看到的就是容器里的一个小 linux,命令没几个,ll 都没有。exit 或者 Ctrl + D 退出,容器也跟着停了。


三、ps 看容器

# 正在运行的容器
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
# -a 历史运行过的容器
root@VM-0-9-ubuntu:~# docker ps -a
CONTAINER ID   IMAGE          COMMAND       CREATED         STATUS                     PORTS     NAMES
1a67a17a9f1d   centos         "/bin/bash"   3 minutes ago   Exited (0) 2 minutes ago             boring_leavitt
170f24ecc433   c3cbe1cc1aa5   "/hello"      2 days ago      Exited (0) 2 days ago                nostalgic_ptolemy

# -n=[数字] 显示最近创建的[数字]个容器,比如 -n=1,就只显示一个
# -q 只显示容器编号

# 显示运行过的所有容器的编号
root@VM-0-9-ubuntu:~# docker ps -aq
1a67a17a9f1d
170f24ecc433

容器名和容器 ID 不是一回事,都是启动容器的时候自动生成的,ps 里两个都能看到。


四、启停和删除

停止、重启、杀掉容器:

docker start|restart|stop|kill 容器id
# kill 比 stop 强制,强制停止运行


root@VM-0-9-ubuntu:~# docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED              STATUS                          PORTS     NAMES
f96d3ee8015f   centos    "/bin/bash"   About a minute ago   Exited (0) About a minute ago             jovial_jennings
6dfe6216abfe   centos    "/bin/bash"   About a minute ago   Exited (0) About a minute ago             kind_gates
a6acaefeb7a3   centos    "/bin/bash"   2 minutes ago        Exited (130) 2 minutes ago                nervous_hermann
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
root@VM-0-9-ubuntu:~# docker start a6acaefeb7a3
a6acaefeb7a3
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS         PORTS     NAMES
a6acaefeb7a3   centos    "/bin/bash"   3 minutes ago   Up 4 seconds             nervous_hermann

root@VM-0-9-ubuntu:~# docker stop a6acaefeb7a3
a6acaefeb7a3
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

删除容器不等于删除镜像,删掉之后重新启动命令还能用,但是不挂数据卷的话里面的数据就丢了:

# 删除指定容器,运行中的删除不了,需要加参数 -f 强制删
docker rm 容器id
# 递归删除所有容器
docker rm -f `docker ps -aq`

root@VM-0-9-ubuntu:~# docker rm -f `docker ps -aq`
15c41eb4491f
1a67a17a9f1d
170f24ecc433
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
root@VM-0-9-ubuntu:~# docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
# 这样也可以
docker ps -aq | xargs docker rm -f

最后那行是另一种批量删的写法:docker ps -aq 把所有容器 ID 打出来,交给 xargs 一个个接到 docker rm -f 后面。


五、后台运行为什么会自己退

root@VM-0-9-ubuntu:~# docker run -d centos
f416c306a9d8d944fa728f3cc4c1dc2ee72fc6e4eb0e36db61fbf179167d2fdc
# 这里 8s 前启动,说明启动了就停止了
root@VM-0-9-ubuntu:~# docker ps -a
CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS                     PORTS     NAMES
f416c306a9d8   centos    "/bin/bash"   9 seconds ago   Exited (0) 8 seconds ago             objective_khorana
f96d3ee8015f   centos    "/bin/bash"   5 minutes ago   Exited (0) 5 minutes ago             jovial_jennings
6dfe6216abfe   centos    "/bin/bash"   5 minutes ago   Exited (0) 5 minutes ago             kind_gates
a6acaefeb7a3   centos    "/bin/bash"   6 minutes ago   Exited (0) 2 minutes ago             nervous_hermann
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

为什么 -d 启动完自己就关了?因为后台运行必须有前台进程撑着。-it 交互式启动相当于开了个前台交互进程,所以能一直跑;光 -d 又没东西在前台跑,容器就退出了。比如装 nginx 启动后发现没提供服务,也会自己关掉。tomcat 那种主进程一直在前台跑的,就可以直接 -d。


六、看日志

用法:  docker logs [选项] 容器id

获取容器的日志输出

别名:
  docker container logs, docker logs

选项:
      --details        显示日志提供的额外详细信息
  -f, --follow         实时跟踪日志输出(类似 tail -f)
      --since string   显示指定时间之后的日志(例如 "2013-01-02T13:23:37Z")或相对时间(例如 "42m" 表示 42 分钟前)
  -n, --tail string    显示末尾多少行日志(默认 "all" 表示全部)
  -t, --timestamps     显示时间戳
      --until string   显示指定时间之前的日志(例如 "2013-01-02T13:23:37Z")或相对时间(例如 "42m" 表示 42 分钟前)

# 常用的
# 查看容器所有日志
docker logs 容器id

# 实时跟踪日志
docker logs -f 容器id

# 查看末尾 50 行
docker logs -n 50 容器id

# 查看最近 30 分钟的日志
docker logs --since 30m 容器id

# 查看带时间戳的日志
docker logs -t 容器id

短参数可以连起来写,-f -t -n 10 和 -ftn 10 是一样的:

# 跟踪查看最近带有时间戳的10条日志
docker logs -ftn 10 a6acaefeb7a3
docker logs -f -t -n 10 a6acaefeb7a3
root@VM-0-9-ubuntu:~# docker logs -ftn 10 a6acaefeb7a3
2026-07-29T08:17:06.708230213Z [root@a6acaefeb7a3 /]# ^C
2026-07-29T08:17:10.923350511Z [root@a6acaefeb7a3 /]# ^C
2026-07-29T08:17:17.196434546Z [root@a6acaefeb7a3 /]# ^C
2026-07-29T08:17:18.172594263Z [root@a6acaefeb7a3 /]# exit
2026-07-29T08:21:02.353987473Z [root@a6acaefeb7a3 /]# exit

容器里本来没东西输出,docker logs 自然是空的,那就自己写一段 shell 让它在前台一直跑:

run 最后接 /bin/sh -c 跑一段 while 循环,再用 logs -tf --tail 看输出

写起来就是 docker run -d centos /bin/sh -c "while true;do echo kuangshen;sleep 1;done",注意这里的 -c 是 sh 的参数,不是 docker 的参数。有了这个前台进程,容器也就不会起来就退。


七、看进程和元数据

容器里跑着哪些进程:

root@VM-0-9-ubuntu:~# docker top a6acaefeb7a3
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                1745326             1745301             0                   16:34               pts/0               00:00:00            /bin/bash

容器的一堆底层信息用 docker inspect 看,下面只截了一部分:

docker inspect 容器id
root@VM-0-9-ubuntu:~# docker inspect a6acaefeb7a3
[
    {
    # 容器id只显示前几位
        "Id": "a6acaefeb7a3ad7433a9cf9905a6523a8f25e71afec41c77723d2fee720fe7a1",
        "Created": "2026-07-29T08:17:00.688046697Z",
        "Path": "/bin/bash",
        # -c 没有执行命令,为空
        "Args": [],
        # 状态
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 1745326,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2026-07-29T08:34:32.832822312Z",
            "FinishedAt": "2026-07-29T08:21:02.355498409Z"
        },
# 来源镜像
"Image": "sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177",
        "ResolvConfPath": "/var/lib/docker/containers/a6acaefeb7a3ad7433a9cf9905a6523a8f25e71afec41c77723d2fee720fe7a1/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/a6acaefeb7a3ad7433a9cf9905a6523a8f25e71afec41c77723d2fee720fe7a1/hostname",
        "HostsPath": "/var/lib/docker/containers/a6acaefeb7a3ad7433a9cf9905a6523a8f25e71afec41c77723d2fee720fe7a1/hosts",
        "LogPath": "/var/lib/docker/containers/a6acaefeb7a3ad7433a9cf9905a6523a8f25e71afec41c77723d2fee720fe7a1/a6acaefeb7a3ad7433a9cf9905a6523a8f25e71afec41c77723d2fee720fe7a1-json.log",
        "Name": "/nervous_hermann",
# 挂载
        "Mounts": [],
        # 基本配置
        "Config": {
            "Hostname": "a6acaefeb7a3",
            "Domainname": "",
            "User": "",
            "AttachStdin": true,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": true,
            # 基本环境变量
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/bash"
            ],
            "Image": "centos",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "Labels": {
                "org.label-schema.build-date": "20210915",
                "org.label-schema.license": "GPLv2",
                "org.label-schema.name": "CentOS Base Image",
                "org.label-schema.schema-version": "1.0",
                "org.label-schema.vendor": "CentOS"
            }
        },
        # 网络相关
        "NetworkSettings": {
            "SandboxID": "a7752df2d12367daa30655e47ebb8a77e47515eb45d2e00c80d10a80371eb69d",
            "SandboxKey": "/var/run/docker/netns/a7752df2d123",
            "Ports": {},
            "Networks": {
            # 桥接
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "DriverOpts": null,
                    "GwPriority": 0,
                    "NetworkID": "e41e478988f569a19bdb82901caed5250272087882e92b27d822ae679ac66fb3",
                    "EndpointID": "f6189399d8e4f1cc7a1f8c227dcf66514c2a0e1b2a0a2639ccf5b4eb37d09201",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "MacAddress": "92:ce:35:8f:4f:b5",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "DNSNames": null
                }
            }
        },
        "ImageManifestDescriptor": {
            "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "digest": "sha256:a1801b843b1bfaf77c501e7a6d3f709401a1e0c83863037fa3aab063a7fdb9dc",
            "size": 529,
            "platform": {
                "architecture": "amd64",
                "os": "linux"
            }
        }
    }
]

想知道容器改了什么,Mounts、Config.Env、NetworkSettings 这几块最常看:挂载、环境变量、IP 都在里面。


八、进容器,exec 和 attach

容器一般都在后台跑,有时候得进去改配置。前面 run 是启动的时候就进去,现在说的是跑起来之后再进去。

# 方式1:execute 进入(译为执行)
docker exec -it id [shell]

root@VM-0-9-ubuntu:~# docker exec -it a6acaefeb7a3 /bin/bash
# 测试
[root@a6acaefeb7a3 /]# ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 08:34 pts/0    00:00:00 /bin/bash
root          15       0  0 08:55 pts/1    00:00:00 /bin/bash
root          29      15  0 08:57 pts/1    00:00:00 ps -ef

# 方式2:attach 进入(译为附上)
docker attach id
root@VM-0-9-ubuntu:~# docker attach a6acaefeb7a3
[root@a6acaefeb7a3 /]# 
# 值得一提的是,这个命令进入的是正在运行的命令行
# 如果-c执行了命令,那么就会跟踪看到输出结果
# 这种情况输出的结果会打断用户输入,所以一般使用 exec

从 ps -ef 的输出也能看出区别:exec 是新开了一个进程(PID 15)挂在容器里,attach 是直接贴到原来那个前台命令行上,别人在跑输出就会打断你的输入。所以平时用 exec。


九、容器和主机之间拷文件

docker cp 管的是容器和 linux 之间的拷贝,linux 和 windows 之间可以用 Xftp。

docker cp 容器id:容器内路径 目的主机路径

root@VM-0-9-ubuntu:~# docker attach a6acaefeb7a3
[root@a6acaefeb7a3 /]# pwd   
/
[root@a6acaefeb7a3 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@a6acaefeb7a3 /]# touch test.sh
[root@a6acaefeb7a3 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  test.sh	tmp  usr  var
[root@a6acaefeb7a3 /]# exit
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
root@VM-0-9-ubuntu:~# docker cp a6acaefeb7a3:/test.sh /root
Successfully copied 0B (transferred 1.54kB) to /root

应用场景就是把容器里的数据备份到 linux 上。等后面学了数据卷,就可以做到自动同步,不用手动 cp 了。


十、常用命令小结

镜像、容器、Tar 包、Dockerfile、Registry 之间的命令,以及容器的状态流转
attach     Attach to a running container                            # 连接到正在运行的容器
build      Build an image from a Dockerfile                         # 通过 Dockerfile 定制镜像
commit     Create a new image from a container changes              # 提交当前容器为新的镜像
cp         Copy files/folders from the containers filesystem to the host path   # 从容器中拷贝文件或目录到宿主机
create     Create a new container                                   # 创建一个新容器(同 run,但不启动)
diff       Inspect changes on a container's filesystem              # 查看容器文件系统变化
events     Get real time events from the server                     # 从 docker 服务获取容器实时事件
exec       Run a command in an existing container                   # 在已存在的容器上运行命令
export     Stream the contents of a container as a tar archive      # 导出容器内容流为 tar 归档文件(对应 import)
history    Show the history of an image                             # 展示镜像构建历史
images     List images                                              # 列出系统当前镜像
import     Create a new filesystem image from the contents of a tarball  # 从 tar 包内容创建新文件系统(对应 export)
info       Display system-wide information                          # 显示系统相关信息
inspect    Return low-level information on a container              # 查看容器详细信息
kill       Kill a running container                                 # 终止运行中的容器
load       Load an image from a tar archive                         # 从 tar 包加载镜像(对应 save)
login      Register or Login to the docker registry server          # 注册或登录 docker 服务器
logout     Log out from a Docker registry server                    # 从 docker 服务器登出
logs       Fetch the logs of a container                            # 输出容器日志
port       Lookup the public-facing port which is NAT-ed to PRIVATE_PORT  # 查看映射端口对应的容器内部端口
pause      Pause all processes within a container                   # 暂停容器
ps         List containers                                          # 列出容器(默认只显示运行中)
pull       Pull an image or a repository from the docker registry server  # 从镜像仓库拉取镜像
push       Push an image or a repository to the docker registry server    # 推送镜像到仓库
restart    Restart a running container                              # 重启容器
rm         Remove one or more containers                            # 删除容器
rmi        Remove one or more images                                # 删除镜像(需无容器使用,或加 -f 强制)
run        Run a command in a new container                         # 创建并启动一个新容器
save       Save an image to a tar archive                           # 保存镜像为 tar 包(对应 load)
search     Search for an image on the Docker Hub                    # 在 Docker Hub 搜索镜像
start      Start a stopped containers                               # 启动已停止的容器
stop       Stop a running containers                                # 停止运行中的容器
tag        Tag an image into a repository                           # 给镜像打标签
top        Lookup the running processes of a container              # 查看容器内运行的进程
unpause    Unpause a paused container                               # 恢复暂停的容器
version    Show the docker version information                      # 查看 docker 版本信息
wait       Block until a container stops, then print its exit code  # 等待容器停止并打印退出状态码

docker 的命令很多,上面这些是常用的容器和镜像命令,后面还会碰到不少。


十一、跑几个真实的服务

1. nginx

装镜像之前先去 hub 上确认版本,pull 需要的那一版:

root@VM-0-9-ubuntu:~# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
d26f27cc8c41: Pull complete 
062e450697fa: Pull complete 
82454cdbf456: Pull complete 
b6698f04e005: Pull complete 
cacfcdd01f30: Pull complete 
2bedaf25031a: Pull complete 
3c7ab7949321: Pull complete 
6c496f5b5050: Download complete 
ea1d76ccc2c6: Download complete 
Digest: sha256:5a88c9c45479443d7be2eadc894b4ed0a9801bae03d97a5760ae13b5c2005942
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest


root@VM-0-9-ubuntu:~# docker images
                                                                                                      i Info →   U  In Use
IMAGE           ID             DISK USAGE   CONTENT SIZE   EXTRA
centos:latest   a27fd8080b51        340MB         83.5MB    U   
nginx:latest    5a88c9c45479        241MB           66MB  

启动,容器名字叫 nginx01,外部端口 3344 映射到容器里的 80:

# 容器名字指定为 nginx01,外部端口3344,内部docker为80
# 云服务器安全组需要开放3344的t/u协议
root@VM-0-9-ubuntu:~# docker run -d --name nginx01 -p 3344:80 nginx
023e8fbeaa9d1270cc3595e574944e9a9b400a94a922abf7c1a0803be0127113
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                                     NAMES
023e8fbeaa9d   nginx     "/docker-entrypoint.…"   2 seconds ago   Up 2 seconds   0.0.0.0:3344->80/tcp, [::]:3344->80/tcp   nginx01

本地测一下:

# 1.测试本地
root@VM-0-9-ubuntu:~# curl localhost:3344
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, nginx is successfully installed and working.
Further configuration is required for the web server, reverse proxy, 
API gateway, load balancer, content cache, or other features.</p>

<p>For online documentation and support please refer to
<a href="https://nginx.org/">nginx.org</a>.<br/>
To engage with the community please visit
<a href="https://community.nginx.org/">community.nginx.org</a>.<br/>
For enterprise grade support, professional services, additional 
security features and capabilities please refer to
<a href="https://f5.com/nginx">f5.com/nginx</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>


# 2.运行测试
root@VM-0-9-ubuntu:~# docker images
                                                                                                      i Info →   U  In Use
IMAGE           ID             DISK USAGE   CONTENT SIZE   EXTRA
centos:latest   a27fd8080b51        340MB         83.5MB    U   
nginx:latest    5a88c9c45479        241MB           66MB    U   

# 3.网页测试
无问题

这里把前面说的"容器是隔离的"落实一下:容器端口是独立的,nginx01 和 nginx02 各有各的 80 端口;外部流量访问的是宿主机的端口,外网能访问到 linux,但访问不到 docker 里面,所以要用 -p 做端口映射,把宿主机和 docker 的端口打通。

nginx01、nginx02 各自有容器内的 80,靠 -p 3344:80 映射到宿主机,外面还有一层安全组

映射好之后,外网访问 3344 就能测到容器里的 80 通不通:

浏览器用公网 IP 加 3344 端口,打开的是 nginx 默认页

进容器里看看 nginx 装在哪:

# 这里写容器id和名字都可以
root@VM-0-9-ubuntu:~# docker exec -it nginx01 /bin/bash
root@023e8fbeaa9d:/# whereis nginx
nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx
root@023e8fbeaa9d:/# cd /etc/nginx
root@023e8fbeaa9d:/etc/nginx# ls
conf.d	fastcgi_params	mime.types  modules  nginx.conf  scgi_params  uwsgi_params

# 停掉之后就无法访问了
root@VM-0-9-ubuntu:~# docker stop nginx01
nginx01

顺带把一个坑说清楚:exit 退出容器会让容器退出运行,是因为你退的是 shell 环境。前面用 bash 环境操作 centos,退出 shell 自然 centos 也没了;而 nginx 的主进程不是那个 bash 进程,所以你就算退出去了,nginx 照样正常跑。

2. tomcat

用 run 顺便下载,--rm 表示用完就删:

# 用run下载
docker run -it --rm tomcat
# 之前启动都是后台,停掉容器之后容器可以查到
# -it --rm 用完之后就会删除,一般用来测试用的,用完就删

root@VM-0-9-ubuntu:~# docker run -it --rm tomcat
Unable to find image 'tomcat:latest' locally
latest: Pulling from library/tomcat
4f4fb700ef54: Pull complete 
ca2678b20700: Pull complete 
5a66f521929b: Pull complete 
93eeac639cb8: Pull complete 
fe89cbdf1404: Pull complete 
6f002909f8b6: Pull complete 
72b04d9a8a7b: Pull complete 
0fcab57e3521: Download complete 
76edba644816: Download complete 
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
root@VM-0-9-ubuntu:~# docker images
                                                                                                      i Info →   U  In Use
IMAGE           ID             DISK USAGE   CONTENT SIZE   EXTRA
centos:latest   a27fd8080b51        340MB         83.5MB    U   
nginx:latest    5a88c9c45479        241MB           66MB    U   
tomcat:latest   3747125ecc78        601MB          160MB  
root@VM-0-9-ubuntu:~# docker ps -a
# 这里没有启动tomcat
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS                     PORTS     NAMES
023e8fbeaa9d   nginx     "/docker-entrypoint.…"   24 minutes ago   Exited (0) 5 minutes ago             nginx01
f416c306a9d8   centos    "/bin/bash"              4 hours ago      Exited (0) 4 hours ago               objective_khorana
f96d3ee8015f   centos    "/bin/bash"              4 hours ago      Exited (0) 4 hours ago               jovial_jennings
6dfe6216abfe   centos    "/bin/bash"              4 hours ago      Exited (0) 4 hours ago               kind_gates
a6acaefeb7a3   centos    "/bin/bash"              4 hours ago      Exited (0) 3 hours ago               nervous_hermann

-it --rm 这种方式不适合在练习环境里用,一退出东西就没了。正常下载启动:

root@VM-0-9-ubuntu:~# docker pull tomcat
Using default tag: latest
latest: Pulling from library/tomcat
Digest: sha256:3747125ecc78357b5eb6e2e59352713c14a428514753fdf67db7af97999a18c4
Status: Image is up to date for tomcat:latest
docker.io/library/tomcat:latest

root@VM-0-9-ubuntu:~# docker images
                                                                                                      i Info →   U  In Use
IMAGE           ID             DISK USAGE   CONTENT SIZE   EXTRA
centos:latest   a27fd8080b51        340MB         83.5MB    U   
nginx:latest    5a88c9c45479        241MB           66MB    U   
tomcat:latest   3747125ecc78        601MB          160MB 

root@VM-0-9-ubuntu:~# docker run -d -p 3355:8080 --name tomcat01 tomcat
769ff3c26bab5be4420ccd8a7827efef1f890e554b7e1faaf8fda2cdd7c142fe
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE     COMMAND             CREATED         STATUS         PORTS                                         NAMES
769ff3c26bab   tomcat    "catalina.sh run"   3 seconds ago   Up 3 seconds   0.0.0.0:3355->8080/tcp, [::]:3355->8080/tcp   tomcat01

安全组开放 3355 之后浏览器能访问,但是页面不对:

3355 端口打开是 Tomcat 的 404 页面,因为 webapps 目录是空的

外网能访问说明通了,就是没显示 tomcat。进容器翻一下就明白了:

root@VM-0-9-ubuntu:~# docker exec -it tomcat01 /bin/bash
root@769ff3c26bab:/usr/local/tomcat# ls
bin           CONTRIBUTING.md  LICENSE         NOTICE         RUNNING.txt    webapps
BUILDING.txt  filtered-KEYS    logs            README.md      temp           webapps.dist
conf          lib              native-jni-lib  RELEASE-NOTES  upstream-KEYS  work
root@769ff3c26bab:/usr/local/tomcat# ls -al
total 248
drwxr-xr-x 1 root root  4096 Jul  8 16:53 .
drwxr-xr-x 1 root root  4096 Jul  8 16:53 ..
drwxr-xr-x 2 root root  4096 Jul  8 16:54 bin
-rw-r--r-- 1 root root 26826 Jul  3 07:01 BUILDING.txt
drwxr-xr-x 1 root root  4096 Jul 29 12:36 conf
-rw-r--r-- 1 root root  8626 Jul  3 07:01 CONTRIBUTING.md
-rw-r--r-- 1 root root 30936 Jul  8 16:53 filtered-KEYS
drwxr-xr-x 2 root root  4096 Jul  8 16:53 lib
-rw-r--r-- 1 root root 60517 Jul  3 07:01 LICENSE
drwxrwxrwt 1 root root  4096 Jul 29 12:36 logs
drwxr-xr-x 2 root root  4096 Jul  8 16:54 native-jni-lib
-rw-r--r-- 1 root root  2333 Jul  3 07:01 NOTICE
-rw-r--r-- 1 root root  3224 Jul  3 07:01 README.md
-rw-r--r-- 1 root root  6470 Jul  3 07:01 RELEASE-NOTES
-rw-r--r-- 1 root root 16114 Jul  3 07:01 RUNNING.txt
drwxrwxrwt 2 root root  4096 Jul  8 16:53 temp
-rw-r--r-- 1 root root 32010 Jul  8 16:53 upstream-KEYS
drwxr-xr-x 2 root root  4096 Jul  8 16:53 webapps
drwxr-xr-x 7 root root  4096 Jul  3 07:01 webapps.dist
drwxrwxrwt 2 root root  4096 Jul  3 07:01 work
root@769ff3c26bab:/usr/local/tomcat# cd webapps
root@769ff3c26bab:/usr/local/tomcat/webapps# ls
root@769ff3c26bab:/usr/local/tomcat/webapps#
# 这里什么都没有,所以tomcat是一个阉割版显示不全很正常
# 只有最小运行环境

webapps 是空的,页面自然出不来。把 webapps.dist 里的东西拷过去就行:

root@769ff3c26bab:/usr/local/tomcat# cd webapps.dist
root@769ff3c26bab:/usr/local/tomcat/webapps.dist# ls
docs  examples  host-manager  manager  ROOT
# 两个目录互换名字也是可以的
root@769ff3c26bab:/usr/local/tomcat# cp -r webapps.dist/* webapps
root@769ff3c26bab:/usr/local/tomcat# ls webapps
docs  examples  host-manager  manager  ROOT

再访问就正常了:

把 webapps.dist 里的目录拷进 webapps 后,看到 Tomcat 默认首页

以后部署项目如果每次都要进容器改文件会很麻烦,所以容器外会给一个映射路径,项目放在外面自动同步到里面,这就是 docker 的数据卷挂载,后面单独讲。

3. Elasticsearch

ELK 的架构是这样的:应用程序日志 → Filebeat(采集) → Logstash(过滤/解析) → Elasticsearch(存储/索引) → Kibana(展示)。

镜像直接去 Docker Hub 上找,官方页面右边会给一条现成的 docker pull 命令:

Docker Hub 上的 elasticsearch 官方镜像页,右边可以直接复制 docker pull 命令

docker 官方给了运行方式:

# docker 官方给出了运行方式
$ docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:tag
# --net 是网络配置暂时可以去掉,默认最新镜像拉不到,换了一个7.6.2
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2

# ES 很耗内存,可能导致交互式界面卡住
root@VM-0-9-ubuntu:~# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED              STATUS              PORTS                                                                                      NAMES
5ac3a8bbfe16   elasticsearch:7.6.2   "/usr/local/bin/dock…"   About a minute ago   Up About a minute   0.0.0.0:9200->9200/tcp, [::]:9200->9200/tcp, 0.0.0.0:9300->9300/tcp, [::]:9300->9300/tcp   elasticsearch
769ff3c26bab   tomcat                "catalina.sh run"        26 minutes ago       Up 26 minutes       0.0.0.0:3355->8080/tcp, [::]:3355->8080/tcp                                                tomcat01

root@VM-0-9-ubuntu:~# curl localhost:9200
{
  "name" : "5ac3a8bbfe16",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "r5Gd1ziVSXOF8QI5v3ioHw",
  "version" : {
    "number" : "7.6.2",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
    "build_date" : "2020-03-26T06:34:37.794943Z",
    "build_snapshot" : false,
    "lucene_version" : "8.4.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

ES 太吃内存了,得限制一下。先看资源占用:

docker stats			查看CPU状态(statistics)
CONTAINER ID   NAME            CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
5ac3a8bbfe16   elasticsearch   0.36%     1.228GiB / 7.445GiB   16.49%    866B / 126B      0B / 1.06MB   45
769ff3c26bab   tomcat01        0.05%     140.9MiB / 7.445GiB   1.85%     24.8kB / 448kB   0B / 7.76MB   37

从 docker hub 跳到 ES 的官方文档,里面写了怎么限制内存,改配置文件:

www.elastic.co/docs/release-notes/elasticsearch
# 通过参数 -e 修改配置,限制最大512M
# 可读性高的格式,容器名和端口映射需要换
docker run -d \
  --name elasticsearch02 \
  -p 9201:9200 \
  -p 9301:9300 \
  -e "discovery.type=single-node" \
  -e "ES_JAVA_OPTS=-Xms64m -Xmx512m" \
  elasticsearch:7.6.2

# 删掉旧的也行
# 删除未启动的冲突容器
docker rm -f elasticsearch02

# 查看内存占用 02 很明显被限制了很多
  CONTAINER ID   NAME              CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O     PIDS
e9430a760e95   elasticsearch02   0.44%     339.6MiB / 7.445GiB   4.45%     656B / 126B       0B / 1.03MB   46
5ac3a8bbfe16   elasticsearch     0.10%     1.233GiB / 7.445GiB   16.56%    1.92kB / 1.11kB   0B / 1.86MB   45
769ff3c26bab   tomcat01          0.12%     141.6MiB / 7.445GiB   1.86%     27.1kB / 472kB    0B / 8.09MB   37

# 测试
root@VM-0-9-ubuntu:~# curl localhost:9201
{
  "name" : "e9430a760e95",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "h5HEQ4XPTEafmYpFCdOHuw",
  "version" : {
    "number" : "7.6.2",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
    "build_date" : "2020-03-26T06:34:37.794943Z",
    "build_snapshot" : false,
    "lucene_version" : "8.4.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

用 Kibana 连 ES 的时候要注意:容器之间互相隔离,没法直接用 localhost 连过去。linux 有内网 IP,用内网 IP 可以访问到 ES 容器和 Kibana 容器,再配合别的技术才能真正连上,这块要有点计算机网络的基础,也是 docker 的网络原理。

4. Portainer 可视化面板

Portainer 不是最佳选择,后面 CI/CD 那块最好用 rancher,但它是个现成的图形化管理工具,可以拿来先感受一下后台操作:

docker run -d -p 8088:9000 \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  --privileged=true \
  portainer/portainer
# --restart 先不管
# -v 是挂载,里面的数据挂载到本机
# --privileged=true 授权
docker run -d -p 8088:9000 portainer/portainer

root@VM-0-9-ubuntu:~# docker run -d -p 8088:9000 \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  --privileged=true \
  portainer/portainer
Unable to find image 'portainer/portainer:latest' locally
latest: Pulling from portainer/portainer
96fd13befc87: Pull complete 
772227786281: Pull complete 
b5d1b01b1d39: Pull complete 
0bad1d247b5b: Pull complete 
Digest: sha256:47b064434edf437badf7337e516e07f64477485c8ecc663ddabbe824b20c672d
Status: Downloaded newer image for portainer/portainer:latest
1f7fde25c5319446758f38cda5f0791440833f5e08cc5805fe6497be15696650

访问测试,第一次加载要连 docker、灌数据,会很慢:

Portainer 第一次访问要先建管理员用户,密码至少 12 位

设置完进去,容器情况一目了然:

Portainer 的容器列表,running 和 exited 状态一眼可见

容器、镜像、网络、卷这几块都能在面板里点:

左边菜单里红框圈出来的 Containers、Images、Networks、Volumes
原笔记收尾的表情包,基础部分到此收工

以上就是 docker 的基础体系了。光有基础肯定不够用,解决不了实际需求,还得接着往下学。