Docker初体验

  • bash 前台运行
  • 容器持久运行的条件
  • nginx 后台运行
  • 后台运行的条件
  • Docker数据存储的哲学
  • Docker使用的哲学

前台运行容器

拉取 centos 镜像

1
2
3
4
5
6
root@ubuntu:~# docker pull centos
Using default tag: latest
latest: Pulling from library/centos
93857f76ae30: Pull complete
Digest: sha256:4eda692c08e0a065ae91d74e82fff4af3da307b4341ad61fa61771cc4659af60
Status: Downloaded newer image for centos:latest

查看镜像列表

1
2
3
4
5
root@ubuntu:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest a8493f5f50ff 33 hours ago 192MB
nginx latest 5766334bdaa0 37 hours ago 183MB
hello-world latest 48b5124b2768 2 months ago 1.84kB

以前台运行方式运行容器

1
2
root@ubuntu:~# docker run --name "mycentos" -it centos /bin/bash
[root@e4b1ccd2600b /]#
  • run: 运行一个容器
  • —name “mycentos”: 给容器命名
  • -i: 打开容器的标准输入
  • -t: 分配一个伪终端 tty
  • centos: 镜像名
  • 运行容器后执行的命令

语法:

1
2
3
4
5
root@ubuntu:~# docker run --help

Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

Run a command in a new container

操作容器

1
yum install dstat htop lsof curl wget lrzsz

退出终端/退出容器

1
2
3
[root@e4b1ccd2600b /]# 
[root@e4b1ccd2600b /]# exit
root@ubuntu:~#

随着终端的退出, 容器也随之退出


容器持久运行的条件

阻塞!!!

Docker 容器持久运行的基本条件是-进程的阻塞!

Docker 的哲学中, 主张一个容器只启动”一个进程”, 这里的一个进程可以理解为一个程序 或是一个主进程, 比如一个 Apache, 一个 Apache 可以启动一个主进程和若干个子进程.

如果想让 Docker 持久化运行, 那么在容器启动时, 必须有一个进程阻塞住终端. Docker 判断容器内服务是否正常的方式很简单, 就是判断最后阻塞的进程是否挂掉, 如果最后阻塞的进程挂掉, 那么容器退出.

Docker 认为, 容器即服务, 容器即程序, 一个容器就是一个程序, 如果一个容器内启动了多个服务, 比如 A, B 和 C服务, C 作为最后一个程序阻塞住容器使其运行起来, 此时, 如果容器内部的 A 或 B 服务挂掉, 容器仍然认为自己是正常的, 因为 Docker 只关心最后阻塞的进程是否退出. 反之, A 和 B 服务正常, 如果 C 服务挂掉, 那么则整个容器退出!


后台运行容器

获取 nginx 镜像

https://hub.docker.com

右边是拉取 nginx latest 版本镜像的命令

左边 repo info 标签内是 nginx 简短说明/完整说明

完整说明中包括:

  • 版本的介绍
  • 软件的介绍
  • 使用方式的介绍

左边 tag 是所有版本的列表

https://store.docker.com

下载最新稳定发行版的 nginx 镜像

1
2
3
4
5
6
7
8
9
10
root@ubuntu:~# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
6d827a3ef358: Pull complete
f8f2e0556751: Pull complete
5c9972dca3fd: Pull complete
451b9524cb06: Pull complete
Digest: sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582
Status: Downloaded newer image for nginx:latest
root@ubuntu:~#

运行 nginx 镜像

1
2
3
4
5
6
7
8
9
10
# 首先创建一个 web 目录
root@ubuntu:~# mkdir web
root@ubuntu:~# echo "Hello Docker" > web/index.html
# 运行 nginx 镜像
root@ubuntu:~# docker run --name "my-first-nginx" -v /root/web:/usr/share/nginx/html:ro -d nginx
11610baab1ce0b684367a44313fc0401489b115399f0a4469bec15bd4b8f6769
root@ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
11610baab1ce nginx "nginx -g 'daemon ..." 6 seconds ago Up 6 seconds 80/tcp, 443/tcp my-first-nginx
root@ubuntu:~#
  • -v: 挂载卷 宿主机目录:容器内目录:挂载方式 其中挂载方式支持以只读ro或读写rw方式挂载, 默认为rw
  • -d: 以守护进程daemon方式放入到后台运行

此方式没有对外映射端口, 所以只能在容器内部访问

在容器内部访问页面

1
2
3
4
5
6
7
8
9
10
11
12
13
# 进入容器
root@ubuntu:~# docker exec -it 11610 /bin/bash
# 查看命令是否安装
root@11610baab1ce:/# curl
bash: curl: command not found
# 查看发行版
root@11610baab1ce:/# cat /etc/issue
Debian GNU/Linux 8 \n \l
# 安装命令
root@11610baab1ce:/# apt install curl
# 测试页面
root@11610baab1ce:/# curl http://127.0.0.1
Hello Docker

运行暴露端口的 nginx 镜像

1
2
3
4
5
6
7
8
9
root@ubuntu:~# docker run --name "my-second-nginx" -v /root/web:/usr/share/nginx/html:ro -d -p 8800:80 nginx
d0a4936e940e5d3b9e2d96be3b08133ddb7f5d9f9a28a81e3b99dff03fb1d747
root@ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d0a4936e940e nginx "nginx -g 'daemon ..." 3 seconds ago Up 2 seconds 443/tcp, 0.0.0.0:8800->80/tcp my-second-nginx
11610baab1ce nginx "nginx -g 'daemon ..." 12 minutes ago Up 12 minutes 80/tcp, 443/tcp my-first-nginx
root@ubuntu:~#
root@ubuntu:~# curl http://127.0.0.1:8800
Hello Docker
  • -p: 对外映射端口 宿主机端口:容器内端口

运行暴露多个端口的 nginx 镜像

1
2
3
4
5
6
7
8
root@ubuntu:~# docker run --name "my-third-nginx" -v /root/web:/usr/share/nginx/html:ro -d -p 8080:80 -p 443:443 nginx
b9704931408c6d054bc9c510878e10467a71d412bef065afa7414403982001b6
root@ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b9704931408c nginx "nginx -g 'daemon ..." 6 seconds ago Up 6 seconds 0.0.0.0:443->443/tcp, 0.0.0.0:8080->80/tcp my-third-nginx
d0a4936e940e nginx "nginx -g 'daemon ..." 3 minutes ago Up 3 minutes 443/tcp, 0.0.0.0:8800->80/tcp my-second-nginx
11610baab1ce nginx "nginx -g 'daemon ..." 15 minutes ago Up 15 minutes 80/tcp, 443/tcp my-first-nginx
root@ubuntu:~#

Docker 数据存储的哲学

Docker 主张运行无状态的容器, 什么样的容器是无状态的容器呢?

打个比方: 以上面的 nginx 实例为例

  • 以如上方式-v挂载的方式即为有状态
  • 以 dockerfile 的形式将数据目录预先拷贝到镜像的方式即为无状态

也可以简单的理解为:

  • 镜像内包含数据且数据可随时随着镜像销毁的容器为无状态容器
  • 镜像内只包含运行环境, 数据需要靠挂载目录的, 或是容器内数据不可随意销毁的容器为有状态容器

这两种用法各有利弊, 直至现在也争论不休, 没有最好的使用方式, 只有最合适的使用方式(注意: 在集群中, 最好使用无状态的方式. 但是最新版本的 k8s 已经支持有状态容器集群)

Docker 使用的哲学

由于 Docker 容器的生命周期完全取决于最后阻塞容器入口的进程的存活状态, 所以在使用容器的时候, 一定要多加注意阻塞入口的进程和主进程的关系. 比如 node.js 容器, 在里面使用pm2 管理的 node 项目, 最后阻塞入口的进程是 pm2, 那么里面 node 项目的管理就脱离了容器的控制, 而由 pm2 去接管, 在node 进程出问题时也是由pm2去控制重启的. 在实际使用情况中, 一定要注意这样的使用方式会不会对后续的维护造成不便. 我推荐使用原生的命令启动主进程, 不要在主进程外面再套一层程序管理层.