Docker

时间:2020-05-16 16:24:49   收藏:0   阅读:63

Docker

介绍

DevOps = 文化 + 过程 + 工具

虚拟化:为了跨平台、资源(cpu、存储、带宽等)管理和隔离。

虚拟机:运行的程序通常会经过 Hypervisor 的监视来保证与硬件的兼容,在现实出于性能的考虑不会全部都经过 Hypervisor。

容器:

技术图片

开发环境搭建

安装虚拟机和Centos7

安装 vagrant
cd workspace
vagrant init centos/7
vagrant up --provider virtualbox
下载兼容的 virtualbox 版本
到 http://cloud.centos.org/centos/7/vagrant/x86_64/images/ 下载 centos7
vagrant box add centos/7 Documents/tools/CentOS-7-x86_64-Vagrant-2001_01.VirtualBox.box
vagrant up
vagrant ssh // 登陆
vagrant status // 查看虚拟机状态
vagrant halt // 停止虚拟机
vagrant destroy // 删除虚拟机

安装 Docker CE

https://docs.docker.com/install/linux/docker-ce/centos/

sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo // 这里可以更换其他源
sudo yum install docker-ce
// 阿里、网易、腾讯等都有
sudo mkdir /etc/docker
sudo tee /etc/docker/daemon.json <<-‘EOF‘
{
  "registry-mirrors": ["https://xxx.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl enable docker // 开机自启动
sudo systemctl start docker // 在我们通过软件包的形式安装 Docker Engine 时,安装包已经为我们在 Linux 系统中注册了一个 Docker 服务,所以不需要启动dockerd,直接启动即可。
sudo docker run -d --name=demo hello-world // -it 交互式运行,例如 docker run centos /bin/bash。name也是唯一的,可以当作containerid用,如docker start demo

如果run后不能退出,可以新开一个terminal,登陆虚拟机,把不能退出的 container stop 掉

便利性提升

// 运行 docker 命令时省去 sudo
sudo groupadd docker
sudo gpasswd -a vagrant docker
sudo service docker restart
exit
vagrant ssh
// 安装 vim
sudo yum install -y vim

理论和基本命令

底层基础

应用层基础

Docker Engine:本质是多个独立软件组成的软件包,核心包括:

docker版本:分为stable和edge。版本名由「年份.月份.bugfix」组成。目前 docker 主要基于 Linux kernel 3.10 以上版本,具体而言看书。

desktop版(更多介绍看书):

技术图片

容器生命周期

技术图片

容器的启动,本质上是PID 为 1 这个进程的启动,停止也一样。

容器在创建和启动的过程中,不需要进行任何的文件系统复制操作,也不需要为容器单独开辟大量的硬盘空间,只有在容器中发生对文件的修改时,修改才会体现到镜像沙盒环境上。(启动的时候只需要指向镜像文件,如果有修改才会复制镜像文件中)

常用命令

# 查看当前连接的 docker daemon 中存放和管理了哪些镜像。Docker 只显示了镜像 ID 的64个的前12个字符,大部分情况下,它们已经能够让我们在单一主机中识别出不同的镜像了。
docker images 
# 部分字段说明:镜像的命名由username(识别上传镜像的不同用户)、repository(镜像的表意描述) 和 tag(版本等细节)组成。
docker pull ${repository/repository:tag}
docker search ${keyword}
# 更详细的镜像信息
docker inspect ${repository/repository:tag} 或者 ${containerId}
# 删除镜像,共享的底层不会删
docker rmi ${repository/repository:tag}
docker rmi `docker images | awk ‘/^<none>/ { print $3 }‘`
docker create --name ${custom_name} ${repository/repository:tag} # 处于 Created 状态
docker start ${custom_name}
# 可以将上面两步合并,但默认前台运行,所以习惯加 -d
docker run -d
# 罗列当前的容器,-a把不在running的也列出
docker ps -a
docker stop ${custom_name} # 维持的文件系统沙盒环境还是存在的,内部被修改的内容也都会保留
# 完全删除。当我们短时间内不需要使用容器时,最佳的做法是删除它而不是仅仅停止它。对于想保留数据,用数据卷更好。
docker rm ${custom_name}
# 在正在运行的容器中运行指定命令
docker exec ${custom_name} more /etc/hostname
# 进入容器的 bash,没有才用 sh。其中 -i ( --interactive ) 表示保持我们的输入流,只有使用它才能保证控制台程序能够正确识别我们的命令。而 -t ( --tty ) 表示启用一个伪终端,形成我们与 bash 的交互,如果没有它,我们无法看到 bash 内部的执行结果。
docker exec -it ${custom_name} bash
# 查看日志
docker logs ${custom_name}
k8s.gcr.io/defaultbackend-amd64:1.5
k8s.gct.io/defaultbackend-amd64

Docker 网络

容器的网络模型由下面三个重要概念组成。

容器网络模型为容器引擎提供了一套标准的网络对接范式,而在 Docker 中,实现这套范式的是 Docker 所封装的 libnetwork 模块,该模块也提供统一接口。目前官方提供五种网络驱动:Bridge Driver、Host Driver、Overlay Driver、MacLan Driver、None Driver。

docker run -d --name mysql -e MYSQL_RANDOM_ROOT_PASSWORD=yes mysql
# 容器连接,这样容器间的网络已经打通。只需要将容器的网络命名填入到连接地址中,就可以访问需要连接的容器。例如代码为 String url = "jdbc:mysql://mysql:3306/webapp",Docker 会将其指向 MySQL 容器的 IP 地址
docker run -d --name webapp --link mysql webapp:latest
# 只有容器自身允许的端口,才能被其他容器所访问。docker ps 中的 PORTS 就是暴露的端口。运行时可以通过 --expose 指定。
# 也可以用别名连接,database 作为别名,代码中写了 database,就会指向 mysql
docker run -d --name webapp --link mysql:database webapp:latest

当多个主机在同一子网里时,才能互相看到并进行网络数据交换。上面的操作之所以能够连通,是因为启动 Docker 服务时,它会为我们创建一个默认的 bridge 网络,而我们创建的容器在不专门指定网络的情况下都会连接到这个网络上。网络可以通过 docker inspect container 查看。

# 创建网络,-d指定网络驱动类型,individual是网络名称
docker network create -d bridge individual
docker network ls
# 在运行时指定网络。不同网络的容器无法连通。
docker run -d --name webapp --link mysql --network individual webapp:latest
# 指定端口映射 -p <ip>:<host-port>:<container-port>,ip默认时监听所有网卡,即0.0.0.0
docker run -d --name nginx -p 80:80 -p 443:443 nginx:1.12

技术图片

管理和存储数据

容器的文件系统弊端:伴随容器生命周期,没有持久化;外部很难绕开容器访问数据。

解决方法:将宿主操作系统中,文件系统里的文件或目录挂载到容器中,实现容器内外共享这个文件。

挂载方式:

挂载文件到容器Bind Mount:

# -v <host-path>:<container-path>,必须使用绝对路径。指定目录进行挂载,也能够指定具体的文件来挂载。当挂载了目录的容器启动后,我们可以看到我们在宿主操作系统中的文件已经出现在容器中了。这里:ro是只读的意思
docker run -d --name nginx -v /webapp/html:/usr/share/nginx/html:ro nginx:1.12

这种方式在权限允许的情况下能够挂载任何目录或文件,所以要注意外部目录的选择。在保证安全的前提下,这种方式适合:

挂载临时文件目录

--tmpfs,只需指定容器内目录。

方式场景:

挂载数据卷

目录存放在 Docker 内部,接受 Docker 的管理。同样是 -v,但不指定宿主目录。可以通过-v <name>:<container-path>指定卷的名字。如果数据卷不存在,Docker 会为我们自动创建和分配宿主操作系统的目录,而如果同名数据卷已经存在,则会直接引用。适合场景:

# 让多个容器挂载同一个数据卷
docker run -d --name webapp -v html:/webapp/html webapp:latest
docker run -d --name nginx -v html:/usr/share/nginx/html:ro nginx:1.12

# 直接创建、查看、删除
docker volume create appdata
docker volume ls
docker volume rm appdata
docker rm $(docker container ls -f "status=exited" -q) // 删除掉所有已经退出的container
# 删除容器同时删除关联的卷
docker rm -v webapp
# 删除没有被引用的卷
docker volume prune

数据卷容器:一个没有具体指定的应用,甚至不需要运行的容器,我们使用它的目的,是为了定义一个或多个数据卷并持有它们的引用。主要是更轻松的实现容器的迁移,因为不在需要指定挂载的目录。

# 由于不需要容器本身运行,因而我们找个简单的系统镜像都可以完成创建。
docker create --name appdata -v /webapp/storage ubuntu
# 数据卷容器就可以算是容器间的文件系统桥梁。我们可以像加入网络一样引用数据卷容器,只需要在创建新容器时使用专门的 --volumes-from 选项即可。
docker run -d --name webapp --volumes-from appdata webapp:latest

备份和迁移数据卷:数据备份、迁移、恢复的过程可以理解为对数据进行打包,移动到其他位置,在需要的地方解压的过程。

# 先建立一个临时的容器,将用于备份的目录和要备份的数据卷都挂载到这个容器上。--rm 容器在停止后自动删除。后续就是主程序启动命令。
docker run --rm --volumes-from appdata -v /backup:/backup ubuntu tar cvf /backup/backup.tar /webapp/storage
# 恢复数据卷中的数据
docker run --rm --volumes-from appdata -v /backup:/backup ubuntu tar xvf /backup/backup.tar -C /webapp/storage --strip

另一种挂载选项

docker run -d --name webapp webapp:latest --mount ‘type=volume,src=appdata,dst=/webapp/storage,volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>‘ webapp:latest

保存和共享镜像

保存:Docker 镜像的本质是多个基于 UnionFS 的镜像层依次挂载的结果,而容器的文件系统则是在以只读方式挂载镜像后增加的一个可读可写的沙盒环境(沙盒的修改不是真的对镜像的修改,而是创建新的文件盖住镜像里的文件)。Docker 提供了讲这个沙盒环境持久化为镜像层的方法。

# 会先暂停容器,后面一个是镜像 tag 命名。但或许先创建一个新 dockerfile,从原 image 上进行修改再 commit 更好。直接 commit 的话,不知道这个 commit 在原 image 的基础上发生了什么。
docker commit -m "Configured" webapp webapp:2.0
# 镜像命名。对镜像进行命名后,虽然能够在镜像列表里同时看到新老两个镜像,实质是它们其实引用着相同的镜像层。
docker tag 0bc42f7ff218 webapp:1.0

迁移:

docker save -o ./webapp-1.0.tar webapp:1.0
docker load -i webapp-1.0.tar
# (可忽略)将镜像输出。默认定义下,docker save 命令会将镜像内容放入输出流中,这就需要我们使用管道进行接收 ( 也就是命令中的 > 符号 )
docker save webapp:1.0 > webapp-1.0.tar
docker load < webapp-1.0.tar
# commit 和 save 的结合
docker export -o ./webapp.tar webapp
docker import ./webapp.tar webapp:1.0

Dockerfile

例子

# Dockerfile
FROM python:2.7
LABEL maintainer="ly<xxxx@gmail.com>"
RUN pip install flask
COPY app.py /app/ # 先把源码上传到相应目录。不要漏掉第二个“/”,否则app.py就重命名为app
WORKDIR /app
EXPOSE 5000
CMD ["python", "app.py"]

# Dockerfile
FROM ubuntu
RUN apt-get update && apt-get install -y stress
ENTRYPOINT ["/usr/bin/stress"]
CMD []
// 上面这个 build 好后,docker run it xxx/xx -args,这样 args 就可以传到 CMD 中

构建

# build指定的是目录,从这个目录下寻找名为 Dockerfile 的文件
docker build ./webapp
# -t 命名,-f 指定 dockerfile 文件。--no-cache禁用缓存。
docker build -t webapp:latest -f ./webapp/a.Dockerfile --no-cache ./webapp

技术图片

Docker仓库

Alpine:Linux 是一个相当精简的操作系统,对软件镜像进行改造,并基于其构建新的镜像,则还是用完整的 Linux 版本。

对容器进行配置:在镜像的详情里,一般会描述启动镜像的参数,例如mysql,通过 docker run --name mysql -e MYSQL_DATABASE=webapp -e MYSQL_USER=www -e MYSQL_PASSWORD=my-secret-pw -d mysql:5.7 就能完成启动、数据库建立、用户创建等工作。

Docker Compose(本地测试)

在单机上实现多个容器的统一部署。compose 不属于 Engine,需要另外下载,其实就是一个 python 程序。desktop 默认已经集成。

使用

先编写 compose.yml

docker-compose up -d
docker-compose down
# 指定 compose 文件
docker-compose -f ./compose/docker-compose.yml -p myapp up -d
# 查看日志。这里 nginx 是 yml 中定义的服务名,并非 container name,所以用 docker logs nginx 可能看不到。
docker-compose logs nginx
docker-compose create/start/stop

整体服务例子

目录设计

└─ project
   ├─ app #用于存放程序工程,即代码、编译结果以及相关的库、工具等;
   ├─ compose
   │  └─ docker-compose.yml
   ├─ mysql
   │  └─ my.cnf
   ├─ redis
   │  └─ redis.conf
   └─ tomcat
      ├─ server.xml
      └─ web.xml

准备配置文件

这有三种方法:

编写 docker-compose.yml

version: "3" # compose的版本

services:

  redis:
    image: redis:3.2
    volumes:
      - ../redis/redis.conf:/etc/redis/redis.conf:ro
      - ../redis/data:/data
    command:
      - redis-server
      - /etc/redis/redis.conf
    ports:
     - "6379:6379"

  mysql:
    image: mysql:5.7
    volumes:
      - ../mysql/my.cnf:/etc/mysql/my.cnf:ro
      - ../mysql/data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: my-secret-pw
    ports:
      - "3306:3306"

  tomcat:
    image: tomcat:8.5
    volumes:
      - ../app:/usr/local/tomcat/webapps/ROOT
    ports:
      - "80:8080" # 把 Tomcat 默认的 8080 端口映射到了宿主机的 80 端口上,这样便于我们直接通过地址访问网站,不需要经常人工补充端口号了

volumes:
  mysql-data: # 仅需要提供数据卷的名称
    external: true # 如果要引入 compose 外的数据卷,从 Docker Engine 中已有的数据卷里寻找并直接采用

但上述对线上部署都不适用。

微服务例子(等到k8s再解决)

需要在本地搭建起自己所开发服务的运行环境,再与其他开发者搭建的环境互联即可。通过 Overlay Network 实现跨物理主机的限制。

参考:

开发者必备的 Docker 实践指南

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!