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 的安装方法:
下面几个链接上也有中文的安装说明,供参考:
- https://www.runoob.com/docker/windows-docker-install.html
- https://www.runoob.com/docker/ubuntu-docker-install.html
- https://www.runoob.com/docker/macos-docker-install.html
基本概念
这里我们简单介绍一下 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 使用方法还需要读者自行去找相关资料学习。