Docker的使用

TMaize 于 2019-03-08 发布

Docker 是 Linux 虚拟化的一种封装,提供简单易用的容器使用接口。 它将应用程序与该程序的依赖,打包在一个文件里面。所以有了 Docker,就不用担心环境问题。

说到虚拟化,很容易想到虚拟器。Docker 和虚拟机有很大的区别,虚拟机一般是在系统上运行的另外一个系统,需要消耗大量的系统资源,而 Docker 可以理解为一个系统上的不同用户,他们都是运行的同一个系统上,性能消耗少,而且启动速度很快。

Linux 下安装

这里使用的 Centos7,Docker 要求 CentOS 系统的内核版本高于 3.10,如果你的内核版本过低需要升级内核才能支持 Docker

注意 OpenVZ 架构的 VPS 是不支持升级内核的,KVM 是支持的,或者使用云服务器。

more /etc/centos-release # 查看centos系统版本
uname -r # 查看内核版本

如果没有源的话导入下官方的源就行了,这里是官方的教程

# 卸载旧版本
yum -y remove docker docker-common docker-selinux docker-engine
yum update
# 安装
yum install docker

安装完毕输入docker version查看是否安装完毕,分为客户端和服务端。

在安装 docker 时,默认的安装位置是/var/lib/docker,后面下载的镜像也是在这个位置

如果提示 Cannot connect to the Docker daemon 表示服务端没启动,通过systemctl start docker命令启动 docker

Client:
 Version:         1.13.1
 API version:     1.26
 Package version: docker-1.13.1-91.git07f3374.el7.centos.x86_64
 Go version:      go1.10.3
 Git commit:      07f3374/1.13.1
 Built:           Wed Feb 13 17:10:12 2019
 OS/Arch:         linux/amd64

Server:
 Version:         1.13.1
 API version:     1.26 (minimum version 1.12)
 Package version: docker-1.13.1-91.git07f3374.el7.centos.x86_64
 Go version:      go1.10.3
 Git commit:      07f3374/1.13.1
 Built:           Wed Feb 13 17:10:12 2019
 OS/Arch:         linux/amd64
 Experimental:    false

Windows 下安装

有两个 Docker for Windows 和 Docker Toolbox,二者的工作原理都是在虚拟机的 Linux 环境下安装 Docker

Docker for Windows 使用的是 Windows 自带的 Hyper-v 虚拟技术,因此对系统的要求是 Win10 专业版

Docker Toolbox 则使用的是 Virtualbox,如果本机已经安装过 Virtualbox 在安装过程中可以选择不安装 Virtualbox,

通过 docker-machine 命令,可以创建一个 运行 docker 的运行环境,通过docker-machine env切换不同的 docker 环境。从这里也可以看到在 windows 上执行的 docker 命令其实是转发到 Linux 里面的(TCP)

注意 docker-machine 在创建虚拟机的过程中会去 Github 上下载最新的系统镜像,由于网路原因等待过程可能会很久,也可以自己手动去下载然后放到~/.docker/machine/cache/boot2docker.iso

# Docker安装目录下,在git-bash通过执行脚本创建
./start.sh

## 也可以手动创建运行环境
docker-machine create -d virtualbox default
docker-machine start default
docker-machine env default
docker-machine ls
docker-machine ssh default

创建完毕,docker info 若显示无法连接服务端,由于每次重启 IP 会变化,需要更新下 IP 到环境变量里面,再重启终端就好了

docker-machine restart default
docker-machine env default # 将default机器的连接IP写到环境变量
# 关闭终端重新进入刷新最新的环境变量,再次查看应该就成功了
docker info

Registry 仓库

docker 镜像托管和 git 仓库托管是很像的,对比 GitHub ,官方的镜像仓库是Docker Hub,拥有大量的高质量的官方镜像,当拉取镜像时,默认使用的也是改镜像。

由于国内网络环境下载镜像很慢,可以通过配置仓库的镜像地址加速下载

Linux 环境下更改配置文件/etc/docker/daemon.json

{
  "registry-mirrors": ["https://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn"]
}
systemctl restart docker
docker info # Registry Mirrors

Windows 的 Docker Toolbox 环境下

docker-machine ssh default
sudo vi /var/lib/boot2docker/profile # linux 环境,
docker-machine stop
docker-machine start

修改内容,在 EXTRA_ARGS 参数下追加--registry-mirror

EXTRA_ARGS='
--label provider=virtualbox
--registry-mirror https://hub-mirror.c.163.com
--registry-mirror https://docker.mirrors.ustc.edu.cn
'

除了下载外,你也可以推送镜像到仓库,以阿里云的容器镜像服务为例

docker tag nginx registry.cn-hangzhou.aliyuncs.com/xxx/xxx:v1.0
docker login --username=177****@qq.com registry.cn-hangzhou.aliyuncs.com
docker push registry.cn-hangzhou.aliyuncs.com/xxx/xxx:v1.0

image 相关命令

Docker 把应用程序及其依赖打包在 image 文件里面。

image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。

# 列出本地镜像
docker image ls

# 删除镜像,若改镜像已生成容器实例,需要把所有实例删除才可以删除镜像
docker image rm [imageName]
docker rmi

# 从远端下载镜像,比如从官方库下载hello-world项目docker image pull [域名/IP][:端口/][用户名/][项目名][:标签]
# 除了项目名外都有默认值,下面的两条命令是等价的
docker image pull docker.io/library/hello-world:latest
docker image pull hello-world

container 相关命令

image 文件生成的容器实例,本身也是一个文件,称为容器文件。

# 列出本机正在运行的容器,可以得到容器 ID 进行后继操作
docker container ls
# 列出本机所有容器,包括终止运行的容器
docker container ls --all
# 生成一个正在运行的容器实例,本地没有会自动执行 docker image pull
# 运行同一个镜像每次都会生成一个新的容器
# 如 docker container run hello-world,该应用运行完后会自动退出。有些容器不会自动终止,因为提供的是服务

# --name 自定义容器名字,否则是一个随机串
# --rm 容器停止后自动删除
# -d 开启 Daemon 模式,后台运行
# -p 端口映射,主机端口:容器端口
# -v 路径映射,两边必须都是绝对路径,模式 rw,ro 主机路径:容器路径[:模式],$PWD表示当前外部主机的目录
docker container run [imageName]
# 用来启动已经生成、已经停止运行的容器文件
docker container start/stop/restart [containerID]
# 删除容器实例,若在运行无法删除, 需要先stop
docker container rm [containerID]
# 强制终止容器运行,相当于向容器里面的主进程发出 SIGKILL 信号
docker container kill [containerID]

# 终止容器运行,相当于向容器里面的主进程发出 SIGTERM 信号,然后过一段时间再发出 SIGKILL 信号
# 应用程序收到 SIGTERM 信号以后,可以自行进行收尾清理工作。
docker container stop [containerID]

如果需要查看容器创建时的启动命令,可以使用 inspect,不过内容太多,可以使用runlike

docker container inspect 3186de74c6be

pip3 install runlike
runlike 3186de74c6be

runlike

其他命令

使用docker stats命令可以方便的看出正在运行的容器的 CPU 占用等信息

docker-stats

使用docker search [imageName]用于从镜像仓库搜索可用的镜像

使用docker cp用于容器与主机之间的数据拷贝; 如docker cp /www/site01 96f7f14e99ab:/www/将 site01 目录的内容拷贝到 ID 为 96f7f14e99ab 的容器中

使用docker search [imageName]从镜像仓库搜索镜像

使用docker exec [OPTIONS] CONTAINER COMMAND [ARG...]可以运行容器中的脚本,前提是容器正在运行; 它有 3 个参数可选-d :分离模式: 在后台运行,-i :即使没有附加也保持STDIN 打开,-t :分配一个伪终端;

比如docker exec -it 8b57a62e9e1f bash实际上是/bin/bash可以进入容器中中断,并可以进行交互。

默认一些常用软件是没有的,想要安装额外的软件可以:

apt-get update
apt-get install vi
apt-get install telnet
# ifconfig
apt-get install net-tools

比如docker exec 8b57a62e9e1f env可以查看容器的环境变量

使用docker inspect [conatiner]查看改容器的更多详细信息,比如 IPAddress 字段表示容器的 IP 地址

[root@localhost ~]# docker exec app01-mysql env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=6faa0da5db0c
MYSQL_ROOT_PASSWORD=123456
GOSU_VERSION=1.7
MYSQL_MAJOR=8.0
MYSQL_VERSION=8.0.15-1debian9
HOME=/root

安装 tomcat

image-tomcat

# 下载镜像到本地
docker pull tomcat

# 后台运行
# 自定义名字
# 端口映射
# 路径映射
docker container run -d --name app01 -p 80:8080 -v /home/app01:/usr/local/tomcat/webapps/app01 tomcat

# 上传代码到/home/app01

# 重启应用
docker container restart app01

# 访问http://127.0.0.1:80/index.jsp

安装 mysql

# 使用命令搜寻镜像
docker search mysql
docker pull mysql
# MYSQL_DATABASE=testdb 同时创建一个testdb的数据库
docker run --name app02 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=testdb -d mysql --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

设置外部访问权限

docker exec -it app02 /bin/bash
mysql -u root -p
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;

容器之间通信

启动 docker 时,docker 进程会创建一个名为 docker0 的虚拟网桥,用于宿主机与容器之间的通信。当启动一个 docker 容器时,docker 容器将会附加到虚拟网桥上,容器内的报文通过 docker0 向外转发。

docker inspect app01
docker inspect app01
# 宿主机通过ifconfig查看网络

多个容器之间通过 IP 通信,缺点是容器重启后自己的 IP 会改变,link 机智很好的解决了这个问题,且可以安全的传递一些连接信息给其它的容器

删除之前创建的容器
docker container stop app01 app02
docker container rm app01 app02
# 创建mysql容器命名为app02
docker run --name app02 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=testdb -d mysql --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

# 创建tomcat容器命名为app01,并链接到app02
docker container run -d --name app01 -p 80:8080 --link app02:app01db -v /home/app01:/usr/local/tomcat/webapps/app01 tomcat

# 进入tomcat容器
docker exec -it app01 bash

# 查看系统变量,可以看到mysql容器内的一些变量
env

# 测试连通
# 64 bytes from app01db (172.17.0.2): icmp_seq=1 ttl=64 time=0.949 ms
ping app01db
telnet app01db 3306

Java 代码测试数据库

String JDBC_DRIVER = "com.mysql.jdbc.Driver";
String DB_URL = "jdbc:mysql://app01db:3306/testdb";
String USER = "root";
String PASS = "123456";

如果报错Unable to load authentication plugin 'caching_sha2_password'表示 mysql 版本太高,jdbc 太低,问题不大,百度即可解决。但是说明已经可以访问到数据库了。

Docker Compose 工具

综上,容器之间通信,命令行提供容器之间的连接信息不便于管理。

Docker 公司推出了 Docker Compose,可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的 compose 配置文件。默认是 docker-compose.xml,或者使用-f 命令指定配置文件

安装

sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

docker-compose --version

新建 docker-compose.yml 文件,写入下面的内容

app02:
  image: mysql:5.7
  environment:
    - MYSQL_ROOT_PASSWORD=123456
    - MYSQL_DATABASE=testdb
app01:
  image: tomcat
  links:
    - app02:app01db
  ports:
    - 80:8080
  volumes:
    - /home/app01:/usr/local/tomcat/webapps/app01

docker-compose up启动,或者docker-compose up -d在后台运行

docker-compose ps产看正在运行的容器

    Name                 Command             State          Ports
-------------------------------------------------------------------------
root_app01_1   catalina.sh run               Up      0.0.0.0:80->8080/tcp
root_app02_1   docker-entrypoint.sh mysqld   Up      3306/tcp, 33060/tcp

基本的使用就是这样…

自定义镜像

有两种方式创建属于自己的镜像

  1. 根据 container 生成镜像

    进入到一个运行的容器中,比如执行一些操作如安装软件,然后再提交改变生成镜像

    docker container exec -it 1a548378cf20 sh
    touch demo.txt
    exit
    docker container stop 1a548378cf20
    # docker container diff 1a548378cf20
    docker commit -m "install app" 1a548378cf20 customer-image:v1.0.1
    docker image ls
    # docker push customer-image:v1.0.1
    # docker run -p 10086:80 -d customer-image:v1.0.1
    
  2. 通过 Dockerfile 来生成自定义镜像

    注意,每次的 RUN/COPY 等操作都会生成一层,为了镜像的大小可以吧多个 RUN 的命令合并成一个

    FROM openjdk:8
    # RUN mkdir -p /work
    COPY ./demo.jar /work/demo.jar
    WORKDIR /work
    CMD [ "java", "-jar", "demo.jar" ]
    EXPOSE 80
    

    生成镜像,注意所处的目录不要有多余的文件,因为会把当前目录临时拷贝到容器内

    docker image build -t spring:v1.0.0 .
    

注意

在 Docker Toolbox 环境下,容器绑定宿主机端口后并不能通过 127.0.0.1,因为在 Docker Toolbox 环境下宿主机是虚拟机, 应使用docker-machine ip default去访问

参考

Docker 官方文档

阮一峰 - Docker 入门教程

阮一峰 - Docker 微服务教程

菜鸟教程 - Docker 命令大全

Docker 从入门到实践

减小 Docker 镜像体积