XSwitch通信百科

Docker 简介及常用命令

本文来自《Kamailio 实战》附录部分。

Docker是一种容器技术,现在,它已经成了 Linux 相关的软件开发和部署事实上的标准。Docker 是个开源项目,它彻底释放了计算虚拟化的威力,极大提高了应用的维护效率,降低了云计算应用开发的成本。通过使用 Docker,不仅可以让应用的部署、测试和分发都变得前所未有的高效和轻松,而且,当你在实验结束后,可以一键删除所有镜像和临时文件,而不会在你的操作系统上的各种目录中留下垃圾(当然,Docker 本身还是需要占用一些存储空间)。当然,如果你的硬盘空间足够大,是否占用存储空间也不算是主要矛盾,更重要的是,通过使用 Docker,可以把环境隔离开来,避免与其它环境产生冲突。比如笔者在开发过程中,经常用到 Kamailio 以及 FreeSWITCH 的不同版本,它们又分别依赖于很多不同的第三方软件库,不同的库又有很多不同的版本……,使用 Docker 容器就可以很好地避免它们之间的冲突。

Docker 简介

如果你还不熟悉 Docker,那至少应该听说过虚拟机。虚拟机是在真正的物理机上虚拟出来的“电脑”,有虚拟的 CPU、内存、硬盘、网卡等。而 Docker 技术在虚拟机的基础上更进一步,通过 Linux 内核和内核功能(例如 Cgroup 和 Namespace)来分隔进程,以便各进程相对独立运行,但还能共享宿主机的内核和网络资源等。

为了理解 Docker,我们先来理解一下虚拟机。大家都知道,计算机有 CPU、内存、硬盘、网卡等最基本的硬件,而为了有效的使用这些硬件,需要一个操作系统来管理和维护它们,典型的操作系统有 Windows、Linux、macOS 等(手机其实也是一台计算机,有 Android 和 iOS 等操作系统)。一般来说一台计算机上只能运行一个操作系统,为了能同时运行多个操作系统,人们发明了虚拟机。虚拟机就是使用软件模拟 CPU、内存、硬盘和网卡等,这样就是在操作系统中套操作系统。相对于虚拟机,运行原来的操作系统的主机就称为宿主机。常见的虚拟机软件有 VMWare 和 Virtual Box、以及 Xen、KVM 等。

但完全的虚拟各种硬件有些慢,有些重。在 Linux 内核中,有一个轻量级的东西叫 control groups(即 Cgroups),它可以做资源控制、进程控制和隔离。使用 Cgroups 做出来的虚拟化技术叫 LXC(Linux Container),由于 LXC 只是使用了资源隔离,而不需要像虚拟机那样将虚拟机里全部的 CPU 指令“翻译”成宿主机的指令,因而更轻量级。

但是 LXC 用起来还比较麻烦,因而出现了很多年其实没有流行起来,直到 Docker 出现。

Docker 其实并不是什么虚拟化技术,它只是提供了一组工具,可以方便的生成和管理镜像,启动虚拟化的容器等。所以,这些的虚拟化也不再叫虚拟机,而叫容器。就是说,在一个 Linux 操作系统上,可以跑很多不同的容器,不同的容器之间的资源(如 CPU、进程、内存、网络、硬盘空间等)都是隔离的,不同容器里的内容可以使用不同的内容,不同版本的应用序、或依赖库等,彼此独立运行,很方便,但它们实际上是共用内核,因而只适用于 Linux。也就是说,宿主机和服务器必须都是 Linux。

在 Linux 宿主上运行 Docker 开销很小,也理所当然。但人们还想在 macOS 上及 Windows 上运行 Docker。最早都是以虚拟机的方式实现的,如基于 Virtual Box 的实现。但后来,macOS 上有了性能更好的 Hypervisor.Framework(xhyve是它的具体实现),Windows 上也出现了 WSL2[^wsl2],支持更好的虚拟化。

[^wsl2]: 值得一提的是微软最初把开源和 Linux 视为“毒瘤、癌症”,但后来他们表示拥抱开源,现在有了 WSL 及 WSL2(Windows Subsystem for Linux)以及 WSA(Windows Subsystem for Android),相当于直接把 Linux 内核做进了 Windows 里,直接在 Windows 中就可以运行 Linux 和 Android 程序。

还有一个比较有用的工具叫 Docker Compose,它使用一组 YAML 格式的编排文件,可以更方便地管理很多容器。最初该工具是单独提供的,不过在最新的 Docker 版本里,已经把该工具整合进了 Docker。

Docker 安装

如果你使用 Ubuntu 或者 Debian Linux 操作系统,可以使用如下命令一键安装(通过--mirror参数使用阿里云的镜像速度会快一些):

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

或使用 DaoCloud 提供的一键安装方式:

curl -sSL https://get.daocloud.io/docker | sh

在其它 Linux 系统上(如 CentOS、RHEL 等)以及 macOS 和 Windows 上的安装方法也有所不同,篇幅原因我们就不一一介绍了,以下链接上列出了各操作系统上 Docker 的安装方法:

下面几个链接上也有中文的安装说明,供参考:

基本概念

这里我们简单介绍一下 Docker 相关的一些基本概念。

  • 镜像

即 Docker Image。里面是一些文件,相当于一个硬盘。镜像是分层的,便于传输和分享。如果日后镜像有更新,可以只下载更新过的层,而没动过的层不需要重新下载。

  • Tag

镜像有一个 Tag 属性,相当于给镜像打个记号。Tag 是可选的,它就是一个字符串,如果没有 Tag,默认为latest。Tag 通常用于标志镜像的版本。

  • 容器

通过一个镜像可以启动一个容器,它是一个隔离的运行环境(可以认为是个轻量级的虚拟机),可以进入一个容器的内部执行命令。

Docker 容器在运行期间会保存一个临时的镜像,用于存储变动过的文件。容器重启临时镜像的内容还会保留,至到容器被彻底删除。

  • 网络

网络最典型的使用方法是使用 NAT 模式,在 Linux 宿主机上也可以使用host模式,但后者实际上是破坏了网络隔离。

  • Docker Hub

类似 Github 是众所周知的代码仓库聚集地,Docker hub 就是 Docker 镜象的聚集地。除 Docker Hub 外,其他云厂商也提供镜像服务,如附录 2 中的xswitch-free镜象就存储在腾讯云上。

常用命令

以下命令经常用到。

启动一个容器。如:

docker run hello-world

Hello from Docker!

在首次需要一个镜象时,Docker 会自行从 Docker Hub(或指定的其他镜象服务器)上下载。其中Hello from Docker是镜像启动后打印的内容,打印后即退出,容器也会退出。如果想让镜像启动后不退出,则可以运行一个永远不退出的程序,如以下命令运行alpine(它是一个小的 Linux 发行版,非常小)镜像的 Shell:

docker run -it alpine sh

其中,-it参数表示开启交互式终端。进入 Shell 环境后就可以在容器内部执行一些命令了。在宿主机上换一个终端容器,可以使用docker ps命令列出所有正在运行的镜象,如:

# docker ps
CONTAINER ID   IMAGE   COMMAND            CREATED  STATUS  PORTS     NAMES
873ea4a68dc0   alpine  "sh"    About a minute ago  Up

有了,Container ID,就可以在宿主机上可以进入这个容器:

docker exec -it 873ea4a68dc0 sh

当然,Container ID 是自动生成的,在启动 Docker 镜象时可以指定一个好记的名字,如

docker run --name alpine-test --rm alpine sh

其中,--name指定一个名字(alpine-test),--rm表示容器停止运行后,自动删除相关资源(如果在镜象运行期间创建了一个文件,则如果没有--rm的话,重启镜象后文件内容还在)。

有了名字,以后进入容器就可以把 Container ID 换成名字,如:

docker exec -it alpine-test sh

在容器中可以使用exit退出 Shell,如果是最初启动的那个 Shell 退出,那么容器就自动退出。

其他常用的参数是使用-p做 NAT 端口映射和-e设置环境变量等。

在宿主机上可以查看标准输出(STDOUT)的日志:

docker logs alpine-test

-f参数可以进入 Follow 模式,即跟踪日志输出永不退出:

docker logs -f alpine-test

停止容器:

docker stop alpine-test

彻底删除:

docker rm alpine-test

在 Docker 运行期间,会产生大量缓存,如自动下载的镜象,没有使用--rm自动删除容器产生的缓存等,时间长了会占用大量硬盘空间。可以使用如下命令删除(注意这个命令会删除东西,在使用前确保知道你自己在做什么,如果不确定的话最好多看看 Docker 手册或找个同事帮助你一块看着):

docker system prune -a

Docker Compose

此处以《Kamailio 实战》示例代码中的 Docker Compose 文件为例(docker/kam.yml):

version: "3.3" # 配置文件的版本号

services: # 服务,可以有多个
  kb-kam: # 定义一个Kamailio服务,名字任意
    container_name: kb-kam # 启动后,容器的名字,类似于`docker run --name 名字`
    image: kamailio/kamailio-ci:5.5.2-alpine # 使用的镜像,来自Docker Hub
    # restart: always         # 崩溃后是否自动重启,在生产环境中经常使用
    env_file: .env # 从该文件中自动导入一些环境变量
    stdin_open: true # 是否启用标准输入
    tty: true # 是否启用控制台,这个和stdin_open都开启可以在控制台输入
    # command: ["/bin/sh"]# 容器启动后执行的命令,如果没有则执行构建时Dockerfile中指定的命令
    privileged: true # 特权模式,如是否可以在容器中运行tcpdump抓包或gdb调试
    entrypoint: # 启动后自动执行的命令
      - /bin/sh
      # - /start-kam.sh
    volumes: # 挂载宿主机上的目录或文件,这些文件在容器停止后不会消失
      - ../etc:/usr/local/etc/kamailio:cached
      - ../etc:/etc/kamailio:cached
      - ../start-kam.sh:/start-kam.sh
    networks: # 指定网络,这里我们指定了一个外部网络,方便跟其他容器共享
      - kamailio-example
    ports: # 映射端口,这里引用了环境变量,在.env中设置
      - "${KAM_SIP_PORT}:${KAM_SIP_PORT}"
      - "${KAM_SIP_PORT}:${KAM_SIP_PORT}/udp"

networks:
  kamailio-example:
    external: true

其中,外部网络使用docker network create kamailio-example命令创建,这样,可以将 Kamailio 容器与 FreeSWITCH 容器启动到同一个网络上(使用相同的网络地址段)。

可以通过以下命令启动容器:

docker-compose -f docker/db.yml up      # 启动到前台,可以在当前Shell中看日志输入命令等
docker-compose -f docker/db.yml up -d   # 启动,进入后台模式,不占用当前Shell

上述命令中,如果把up换成down则可以停止服务。从上面可以看出,有了这个 YAML 编排文件,可以比原始的docker少输入很多命令行参数。另外也可以将多个服务写到同一个 YAML 文件中同时启停多个服务、管理他们的依赖关系等。

其他

本文只是简单介绍了 Docker 和 Docker Compose,可以带大家快速上手安装和使用 Docker,更多的 Docker 使用方法还需要读者自行去找相关资料学习。

Makefile极速入门