Docker Container 基础命令

1 Docker 容器的操作

启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(exited)的容器重新启动。

因为 Docker 的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。

容器的生命周期

容器的生命周期是容器可能处于的状态,容器的生命周期分为 5 种。

  1. created:初建状态
  2. running:运行状态
  3. stopped:停止状态
  4. paused: 暂停状态
  5. deleted:删除状态

1.1 新建并启动

所需要的命令主要为 docker run

例如,下面的命令输出一个 "Hello World",之后终止容器。

arduino 复制代码
xuyatao@evan171206  ~  docker run ubuntu /bin/echo 'Hello world'
Hello world

这跟在本地直接执行 /bin/echo 'hello world' 几乎感觉不出任何区别。

下面的命令则启动一个 bash 终端,允许用户进行交互。

ruby 复制代码
xuyatao@evan171206  ~  docker run -t -i ubuntu /bin/bash
root@42a89d8706fd:/#

其中,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。 参数说明:

  • -i: 交互式操作。
  • -t: 终端。
  • -d: 容器的运行模式

在交互模式下,用户可以通过所创建的终端来输入命令,例如

ruby 复制代码
root@42a89d8706fd:/# pwd
/
root@42a89d8706fd:/# ls
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@42a89d8706fd:/#

root@42a89d8706fd:/# pwd
/
root@42a89d8706fd:/# ls
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@42a89d8706fd:/#exit

要退出终端,直接输入 exit:

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从registry下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

1.2 终止容器

java 复制代码
 ✘ xuyatao@evan171206  ~  docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS          PORTS      NAMES
f6b7f560a7c2   redis                  "docker-entrypoint.s..."   31 minutes ago   Up 31 minutes   6379/tcp   competent_sutherland

 ✘ xuyatao@evan171206  ~  docker stop f6b

可以使用 docker container stop 或 docker stop 来终止一个运行中的容器。

此外,当 Docker 容器中指定的应用终结时,容器也自动终止。

对于只启动了一个终端的容器,用户通过 exit 命令或 Ctrl+d 来退出终端时,所创建的容器立刻终止。

终止状态的容器可以用 docker container ls -a 命令看到。例如

bash 复制代码
$ docker container ls -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

ba267838cc1b ubuntu:18.04 "/bin/bash" 30 minutes ago Exited (0) About a minute ago trusting_newton

处于终止状态的容器,可以通过 docker container start 命令来重新启动。

此外,docker container restart 命令会将一个运行态的容器终止,然后再重新启动它。

css 复制代码
$ docker ps -a
CONTAINERID       IMAGE      COMMAND            CREATED             STATUS     PORTS         NAMES
28d477d3737a        busybox             "sh"                26 minutes ago      Exited (137) About a minute ago                       busybox

处于终止状态的容器也可以通过docker start命令来重新启动。

ruby 复制代码
$ docker start busybox
busybox
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
28d477d3737a        busybox             "sh"                30 minutes ago      Up 25 seconds                           busybox

此外,docker restart命令会将一个运行中的容器终止,并且重新启动它。

ruby 复制代码
$ docker restart busybox
busybox
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
28d477d3737a        busybox             "sh"                32 minutes ago      Up 3 seconds                            busybox

1.3 进入容器

在使用 -d 参数时,容器启动后会进入后台。

处于运行状态的容器可以通过docker attach、docker exec、nsenter等多种方式进入容器。

1.3.1 docker attach 命令

使用 docker attach ,进入我们上一步创建好的容器,如下所示。

ruby 复制代码
$ docker attach busybox
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 sh
    7 root      0:00 ps aux
/ #

注意:当我们同时使用docker attach命令同时在多个终端运行时,所有的终端窗口将同步显示相同内容,当某个命令行窗口的命令阻塞时,其他命令行窗口同样也无法操作。 由于docker attach命令不够灵活,因此我们一般不会使用docker attach进入容器。下面我介绍一个更加灵活的进入容器的方式docker exec

1.3.2 使用 docker exec 命令进入容器

Docker 从 1.3 版本开始,提供了一个更加方便地进入容器的命令docker exec,我们可以通过docker exec -it CONTAINER的方式进入到一个已经运行中的容器,如下所示。

ruby 复制代码
 xuyatao@evan171206  ~  docker run -dit ubuntu
98b282ab50c3904037875f2af16a88260dafdc25e810767dee726c46653b51fd
xuyatao@evan171206  ~  docker exec -it 98b bash
root@98b282ab50c3:/# ls
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@98b282ab50c3:/# pwd
/
root@98b282ab50c3:/#

我们进入容器后,可以看到容器内有两个sh进程,这是因为以exec的方式进入容器,会单独启动一个 sh 进程,每个窗口都是独立且互不干扰的,也是使用最多的一种方式。

  • docker exec 后边可以跟多个参数,主要 -i -t 参数。
  • 只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。
  • -i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。

如果从这个容器中 exit,不会导致容器的停止。

  • docker start :启动一个或多个已经被停止的容器
  • docker stop :停止一个运行中的容器
  • docker restart :重启容器

1.4 删除容器

我们已经掌握了用 Docker 命令创建、启动和终止容器。那如何删除处于终止状态或者运行中的容器呢?删除容器命令的使用方式如下:docker rm [OPTIONS] CONTAINER [CONTAINER...]。

如果要删除一个停止状态的容器,可以使用docker rm命令删除。

bash 复制代码
docker rm busybox

如果要删除正在运行中的容器,必须添加 -f (或 --force) 参数, Docker 会发送 SIGKILL 信号强制终止正在运行的容器。

bash 复制代码
docker rm -f busybox

2 容器该如何与外界互联互通

如Nginx、Redis、MySQL这样的后台服务应用,因为它们运行在容器的"沙盒"里,完全与外界隔离,无法对外提供服务,也就失去了价值。这个时候,容器的隔离环境反而成为了一种负面特性。

所以,容器不应该是一个完全密闭的铁屋子,而是应该给它开几扇门窗,让应用在"足不出户"的情况下,也能够与外界交换数据、互通有无,这样"有限的隔离"才是我们真正所需要的运行环境。

2.1 如何拷贝容器内的数据

sql 复制代码
 xuyatao@evan171206  ~  docker run -d --rm redis
f6b7f560a7c29fb700a4e4a5e59edca822a9ae132a647699e47fae712f96ed18
 xuyatao@evan171206  ~  docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED              STATUS              PORTS      NAMES
f6b7f560a7c2   redis                  "docker-entrypoint.s..."   7 seconds ago        Up 6 seconds        6379/tcp   competent_sutherland

-d--rm 两个参数,表示运行在后台,容器结束后自动删除,然后使用 docker ps 命令可以看到Redis容器正在运行,容器ID是"f6b7f560a7c2"。

docker cp 的用法很简单,很类似Linux的"cp""scp",指定源路径(src path)和目标路径(dest path)就可以了。如果源路径是宿主机那么就是把文件拷贝进容器,如果源路径是容器那么就是把文件拷贝出容器,注意需要用容器名或者容器ID来指明是哪个容器的路径。

假设当前目录下有一个"log/a.txt"的文件,现在我们要把它拷贝进Redis容器的"/data"目录,如果使用容器ID,命令就会是这样:

bash 复制代码
 xuyatao@evan171206  ~  docker cp logs/a.txt f6b:/data/

接下来我们可以使用 docker exec 命令,进入容器看看文件是否已经正确拷贝了:

bash 复制代码
xuyatao@evan171206  ~  docker exec -it f6b sh
# ls
a.txt
# pwd
/data

可以看到,在"/data"目录下,确实已经有了一个"a.txt"。

现在让我们再来试验一下从容器拷贝出文件,只需要把 docker cp 后面的两个路径调换一下位置:

typescript 复制代码
 ✘ xuyatao@evan171206  ~  docker cp f6b:/data/a.txt ./b.txt
 xuyatao@evan171206  ~  ll
 -rw-r--r--    1 xuyatao  staff   9.3M  7 19  2021 b.txt

这样,在宿主机的当前目录里,就会多出一个新的"b.txt",也就是从容器里拿到的文件。

2.2如何共享主机上的文件

docker cp 的用法模仿了操作系统的拷贝命令,偶尔一两次的文件共享还可以应付,如果容器运行时经常有文件来往互通,这样反复地拷来拷去就显得很麻烦,也很容易出错。

你也许会联想到虚拟机有一种"共享目录"的功能。它可以在宿主机上开一个目录,然后把这个目录"挂载"进虚拟机,这样就实现了两者共享同一个目录,一边对目录里文件的操作另一边立刻就能看到,没有了数据拷贝,效率自然也会高很多。

沿用这个思路,容器也提供了这样的共享宿主机目录的功能,效果也和虚拟机几乎一样,用起来很方便,只需要在 docker run 命令启动容器的时候使用 -v 参数就行,具体的格式是"宿主机路径:容器内路径"。

以Redis为例,启动容器,使用 -v 参数把本机的"/tmp"目录挂载到容器里的"/tmp"目录,也就是说让容器共享宿主机的"/tmp"目录:

bash 复制代码
docker run -d --rm -v /tmp:/tmp redis

然后我们再用 docker exec 进入容器,查看一下容器内的"/tmp"目录,应该就可以看到文件与宿主机是完全一致的。

sql 复制代码
 xuyatao@evan171206  /  cd tmp
 xuyatao@evan171206  /tmp  ll
total 64
srwxr-xr-x  1 xuyatao  wheel     0B  2 22 10:08 Sublime Text.4cff18d2bab96a93366319a9e0fa060d.a18bce1dbfdc29215c8c91bb0c099887.sock
-rw-r--r--  1 root     wheel    10B  2 22 08:52 WillFinish.txt
drwx------  3 xuyatao  wheel    96B  2 22 08:52 com.apple.launchd.M25OdKnv5A
drwxr-xr-x@ 4 xuyatao  wheel   128B  2 22 09:05 com.google.Keystone
-rw-r--r--  1 root     wheel   106B  2 22 12:41 com.sangfor.ca.sha
-rw-r--r--  1 root     wheel    40B  2 22 12:41 com.sangfor.ca.verification
-rwx------  1 root     wheel     0B  2 22 08:52 com.sangfor.lockcert
-rwx------  1 root     wheel     0B  2 22 08:52 com.sangfor.lockecagent
srwxrwxrwx  1 _mysql   wheel     0B  2 22 08:52 mysql.sock
-rw-------  1 _mysql   wheel     4B  2 22 08:52 mysql.sock.lock
srwxrwxrwx  1 _mysql   wheel     0B  2 22 08:52 mysqlx.sock
-rw-------  1 _mysql   wheel     5B  2 22 08:52 mysqlx.sock.lock
drwxr-xr-x  2 root     wheel    64B  2 22 08:52 powerlog
-rw-r--r--  1 root     wheel   888B  2 22 08:52 sangfor.ec.rundata
-rwxr-xr-x  1 root     wheel   1.1K  2 22 08:52 stop_easyconnect.sh
-rw-r--r--  1 root     wheel   188B  2 22 08:52 sunlogin_helper.log

 xuyatao@evan171206  /  docker run -d --rm -v /tmp:/tmp redis

cf2fac0ecd0df7ff06633ac92274b00edc0e9502e894621416f20a7f6c19db9c
 xuyatao@evan171206  /  docker exec -it cfc  bash
Error: No such container: cfc
 ✘ xuyatao@evan171206  /  ls
Applications System       Volumes      cores        dev          home         private      tmp          var
Library      Users        bin          data         etc          opt          sbin         usr
 xuyatao@evan171206  /  cd tmp
 xuyatao@evan171206  /tmp  ll
total 64
srwxr-xr-x  1 xuyatao  wheel     0B  2 22 10:08 Sublime Text.4cff18d2bab96a93366319a9e0fa060d.a18bce1dbfdc29215c8c91bb0c099887.sock
-rw-r--r--  1 root     wheel    10B  2 22 08:52 WillFinish.txt
drwx------  3 xuyatao  wheel    96B  2 22 08:52 com.apple.launchd.M25OdKnv5A
drwxr-xr-x@ 4 xuyatao  wheel   128B  2 22 09:05 com.google.Keystone
-rw-r--r--  1 root     wheel   106B  2 22 12:42 com.sangfor.ca.sha
-rw-r--r--  1 root     wheel    40B  2 22 12:42 com.sangfor.ca.verification
-rwx------  1 root     wheel     0B  2 22 08:52 com.sangfor.lockcert
-rwx------  1 root     wheel     0B  2 22 08:52 com.sangfor.lockecagent
srwxrwxrwx  1 _mysql   wheel     0B  2 22 08:52 mysql.sock
-rw-------  1 _mysql   wheel     4B  2 22 08:52 mysql.sock.lock
srwxrwxrwx  1 _mysql   wheel     0B  2 22 08:52 mysqlx.sock
-rw-------  1 _mysql   wheel     5B  2 22 08:52 mysqlx.sock.lock
drwxr-xr-x  2 root     wheel    64B  2 22 08:52 powerlog
-rw-r--r--  1 root     wheel   888B  2 22 08:52 sangfor.ec.rundata
-rwxr-xr-x  1 root     wheel   1.1K  2 22 08:52 stop_easyconnect.sh
-rw-r--r--  1 root     wheel   188B  2 22 08:52 sunlogin_helper.log

-v 参数挂载宿主机目录的这个功能,对于我们日常开发测试工作来说非常有用,我们可以在不变动本机环境的前提下,使用镜像安装任意的应用,然后直接以容器来运行我们本地的源码、脚本,非常方便。

2.3 如何实现网络互通

现在我们使用 docker cpdocker run -v 可以解决容器与外界的文件互通问题,但对于Nginx、Redis这些服务器来说,网络互通才是更要紧的问题。

首先需要给容器分配IP。有了IP之后才可以与外部世界的网络建立连接

Docker提供了三种网络模式,分别是nullhostbridge

2.3.1null

null是最简单的模式,也就是没有网络,但允许其他的网络插件来自定义网络连接。

2.3.2 host

host的意思是直接使用宿主机网络,相当于去掉了容器的网络隔离(其他隔离依然保留),所有的容器会共享宿主机的IP地址和网卡。这种模式没有中间层,自然通信效率高,但缺少了隔离,运行太多的容器也容易导致端口冲突。

host模式需要在 docker run 时使用 --net=host 参数,下面我就用这个参数启动Nginx:

css 复制代码
docker run -d --rm --net=host nginx:alpine

我们可以在本机和容器里分别执行 ip addr 命令,查看网卡信息:

bash 复制代码
ip addr                    # 本机查看网卡
docker exec xxx ip addr    # 容器查看网卡

2.3.3 bridge

第三种bridge,也就是桥接模式,它有点类似现实世界里的交换机、路由器,只不过是由软件虚拟出来的,容器和宿主机再通过虚拟网卡接入这个网桥(图中的docker0),那么它们之间也就可以正常的收发网络数据包了。不过和host模式相比,bridge模式多了虚拟网桥和网卡,通信效率会低一些。

和host模式一样,我们也可以用 --net=bridge 来启用桥接模式,但其实并没有这个必要,因为Docker默认的网络模式就是bridge,所以一般不需要显式指定。

以Nginx为例

sql 复制代码
xuyatao@evan171206  ~  docker run -d --rm nginx # 默认使用桥接模式
228d43d279f6cae1a0ffb24567d2d512413a40b370ee708cd53e180e3c2d6713

xuyatao@evan171206  ~  docker exec 228 ip addr
OCI runtime exec failed: exec failed: unable to start container process: exec: "ip": executable file not found in $PATH: unknown
 
✘ xuyatao@evan171206  ~  docker inspect 228 | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAddress": "172.17.0.2",


✘ xuyatao@evan171206  ~  ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: icmp_seq=0 ttl=64 time=0.106 ms
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.118 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.132 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.166 ms

2.4 网络端口映射

使用host模式或者bridge模式,我们的容器就有了IP地址,建立了与外部世界的网络连接,接下来要解决的就是网络服务的端口号问题。

一台主机上的端口号数量是有限的,而且多个服务之间还不能够冲突,但我们打包镜像应用的时候通常都使用的是默认端口,容器实际运行起来就很容易因为端口号被占用而无法启动。

解决这个问题的方法就是加入一个"中间层",由容器环境例如Docker来统一管理分配端口号,在本机端口和容器端口之间做一个"映射"操作,容器内部还是用自己的端口号,但外界看到的却是另外一个端口号,这样就很好地避免了冲突。

端口号映射需要使用bridge模式,并且在 docker run 启动容器时使用 -p 参数,形式和共享目录的 -v 参数很类似,用 : 分隔本机端口和容器端口

比如,如果要启动两个Nginx容器,分别跑在8080和8081端口上:

arduino 复制代码
docker run -d -p 8080:80 --rm nginx
docker run -d -p 8081:80 --rm nginx
sql 复制代码
xuyatao@evan171206  ~  docker ps | grep 'nginx'
dd23e9f30b44   nginx                  "/docker-entrypoint...."   45 seconds ago       Up 45 seconds       0.0.0.0:8080->80/tcp   tender_albattani
01354a4d68ed   nginx                  "/docker-entrypoint...."   About a minute ago   Up About a minute   0.0.0.0:8081->80/tcp   confident_lovelace

如果是在本机运行的 Docker,那么可以直接访问:[http://localhost:8080] ,如果是在虚拟机、云服务器上安装的 Docker,则需要将 localhost 换为虚拟机地址或者实际云服务器地址。

直接用浏览器访问的话,我们会看到默认的 Nginx 欢迎页面。

Docker 容器连接

相关推荐
保持学习ing16 分钟前
SpringBoot前后台交互 -- 登录功能实现(拦截器+异常捕获器)
java·spring boot·后端·ssm·交互·拦截器·异常捕获器
十年老菜鸟1 小时前
spring boot源码和lib分开打包
spring boot·后端·maven
白宇横流学长2 小时前
基于SpringBoot实现的课程答疑系统设计与实现【源码+文档】
java·spring boot·后端
加瓦点灯2 小时前
什么?工作五年还不了解SafePoint?
后端
他日若遂凌云志3 小时前
Lua 模块系统的前世今生:从 module () 到 local _M 的迭代
后端
David爱编程3 小时前
Docker 安全全揭秘:防逃逸、防漏洞、防越权,一篇学会容器防御!
后端·docker·容器
小码编匠3 小时前
WinForm 工业自动化上位机通用框架:注册登录及主界面切换实现
后端·c#·.net
weixin_483745623 小时前
Springboot项目的目录结构
java·后端
阿里云云原生3 小时前
2025年第二届“兴智杯”智能编码创新应用开发挑战赛正式启动
后端
保持学习ing4 小时前
SpringBoot 前后台交互 -- CRUD
java·spring boot·后端·ssm·项目实战·页面放行