教程
https://yeasy.gitbook.io/docker_practice
zelda
https://www.youtube.com/watch?v=hWdmCB4R4sY
https://github.com/triwinds/ns-emu-tools/releases
https://pixeldrain.com/u/rKz4XwiR
https://www.gamer520.com/57619.html
https://5vz43f.sharepoint.com/sites/free16/Shared%20Documents/Forms/AllItems.aspx?ga=1&id=%2Fsites%2Ffree16%2FShared%20Documents%2F2023%E5%B9%B4%2D05%E6%9C%88%2DGAME%2FSED&viewid=bf5d72b0%2Df3a1%2D4644%2Dbc45%2D1d18a2b7ea24
http://pan.doullse.com/games/switch/%E5%A1%9E%E5%B0%94%E8%BE%BE%E7%8E%8B%E5%9B%BD%E4%B9%8B%E6%B3%AA/TOTK%20[0100F2C0115B6000][v0].xci
docker概念
docker基于go语言开发,基于linux内核的cgroup、namespace、union fs等技术
属于操作系统层面的虚拟化技术,对进程封装隔离
runc:linux命令行工具,根据OCI容器运行时规范,创建运行容器
containerd:守护进程,管理容器生命周期,在节点上执行容器和管理镜像的最小功能集
docker在文件系统、网络互联、进程隔离上简化了容器创建和维护
传统虚拟机技术:在硬件上运行完整的os
docker技术:进程运行在宿主的内核,容器没有自己的内核和硬件虚拟化
docker的优点
-
更高效的系统资源利用
-
更快速的启动时间
-
一致的运行环境
-
持续交付和部署
使用dockerfile进行镜像构建
-
更轻松的迁移
-
更轻松的维护和扩展
分层存储和镜像技术
docker组成
-
镜像(image)
-
操作系统分为内核和用户空间,linux内核启动后,挂载root文件系统,提供用户空间支持
-
docker镜像相当于特殊的root文件系统,包括
- 容器运行的程序、库、资源、配置
- 运行配置参数(匿名变量、环境变量、用户)
不包括
- 动态数据
-
分层存储
- 使用union fs技术,由多层文件系统组成
- 一层层构建,前一层是后一层的基础
- 每一层的改变只发生在该层构建结束前
- 构建完后,删除某一层文件,只是标记为删除,实际还在镜像中
- 便于复用、定制,在基础层上添加定制新的层
镜像:文件系统、静态、分层、定制化
-
-
容器(container)
- 镜像image和容器container,相当于类和实例
- 镜像静态,容器动态
- 容器本质上是进程,有独立的命名空间namespace(root文件系统、网络配置、进程空间、用户ID空间)
- 容器运行时,以镜像 为基础层,创建容器存储层,运行时读写
- 容器存储层生存周期和容器一样,数据卷(volume)生存周期独立于容器,数据存储在数据卷
容器:镜像实例、动态、独立命名空间、容器存储层、数据卷
-
仓库(repository)
-
docker registry:存储、发分镜像
-
docker regidtry包含多个仓库repository,每个仓库包含多个标签tag,每个标签对应一个镜像
-
一个仓库指明软件镜像,标签知名镜像的版本,格式为
<仓库名>:<标签>
例如
ubuntu:6.04
或者ubuntu:latest
-
仓库名以两段式路径形式,说明docker registry以多用户运行
-
docker registry公开服务有官方的Docker Hub、红帽的Quay.io,国内有Docker Hub的镜像服务,如阿里云加速器等
仓库:存储分发、命名格式(地址、仓库名、标签)、公开服务
-
使用镜像
获取镜像
从docker仓库获取镜像的命令
$ docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
- docker镜像仓库渎职:
<域名/ip>[:端口号]
,默认地址Docker Hub(docker.io)
- 仓库名:
<用户名>/<软件名>
,对于Docker Hub,默认用户名为library
,官方镜像
$ docker pull ubuntu:18.04
18.04: Pulling from library/ubuntu
92dc2a97ff99: Pull complete
be13a9d27eb8: Pull complete
c8299583700a: Pull complete
Digest: sha256:4bc3ae6596938cb0d9e5ac51a1152ec9dcac2a1c50829c74abd9c4361e321b26
Status: Downloaded newer image for ubuntu:18.04
docker.io/library/ubuntu:18.04
-
从
docker.io
的library/ubuntu
中获取标签为18.04
的镜像,使用docker pull
命令等价于
docker pull docker.io/library/ubuntu:18.04
-
从下载过程中可以看到,镜像多层存储,按层下载,结束后,给出sha256摘要
镜像加速器
网易云:https://hub-mirror.c.163.com
主流的Linux使用systemd
进行服务管理,使用其配置镜像加速器
-
先检查是否在
docker.service
文件中配置过镜像地址$ systemctl cat docker | grep '\-\-registry\-mirror'
-
如果该命令有输出,那么请执行
$ systemctl cat docker
查看ExecStart=
出现的位置,修改对应的文件内容去掉--registry-mirror
参数及其值,并按接下来的步骤进行配置。 -
如果以上命令没有任何输出,那么就可以在
/etc/docker/daemon.json
中写入如下内容(如果文件不存在请新建该文件):{ "registry-mirrors": [ "https://hub-mirror.c.163.com", "https://mirror.baidubce.com" ] }
-
-
重新启动服务
$ sudo systemctl daemon-reload $ sudo systemctl restart docker
-
检查加速器是否生效
执行
$ docker info
,如果从结果中看到了如下内容,说明配置成功。Registry Mirrors: https://hub-mirror.c.163.com/
运行镜像
以这个镜像为基础启动并运行一个容器。
$ docker run -it --rm ubuntu:18.04 bash
root@e7009c6ce357:/# cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.1 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.1 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
docker run
运行容器的命令-i
:交互式操作-t
:终端--rm
:容器推出后随之删除ubuntu:18.04
:指定镜像启动容器bash
:放在镜像名后的是命令,bash表明希望有交互式shell
-
进入容器后,可以使用shell执行命令
- 使用
exit
退出镜像
列出镜像
列出已经下载下来的镜像,可以使用 docker image ls
命令,列出所有的顶层镜像
列表包含了 仓库名
、标签
、镜像 ID
、创建时间
以及 所占用的空间
镜像ID对应唯一的镜像
镜像体积
- 镜像在本地展开的大小
- 不同镜像使用相同的基础镜像,拥有共同的层,相同的层只保存一份
通过 docker system df
命令来便捷的查看镜像、容器、数据卷所占用的空间
虚悬镜像
-
镜像没有仓库名、标签
-
由于新旧镜像名同名,旧镜像名被取消
$ docker image ls -f dangling=true REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> 00285df0df87 5 days ago 342 MB
-
使用
docker image prune
删除
中间层镜像
- 加速镜像构建,利用重复资源
docker image ls -a
显示无标签的中间层镜像,只存在一份
列出部分镜像
-
根据仓库名列出镜像
docker image ls ubuntu
-
根据仓库名和标签名列出镜像
docker image ls ubuntu:18.04
-
过滤器参数
--filter
$ docker image ls -f since=mongo:3.2 REPOSITORY TAG IMAGE ID CREATED SIZE redis latest 5f515359c7f8 5 days ago 183 MB nginx latest 05a60462f8ba 5 days ago 181 MB
以特定格式显示
$ docker image ls --format ": "
删除本地镜像
$ docker image rm [选项] <镜像1> [<镜像2> ...]
使用ID、镜像名、摘要删除镜像
<镜像>
可以是镜像短ID、镜像长ID、镜像名、镜像摘要
$ docker image rm 501#镜像短ID
$ docker image rm centos#镜像名
$ docker image ls --digests
$ docker image rm node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
Untagged 和 Deleted
- 镜像的唯一标识是ID和摘要,一个镜像可以有多个标签
- 先将满足要求的所有镜像标签都取消,因为如果仍有标签指向这个镜像,
Delete
行为就不会发生 - 当某个镜像的所有标签被取消后,触发删除行为
- 从上层向基础层方向删除,直到没有任何层依赖当前层,不会删除其他镜像共同 依赖的层
- 以该镜像为基础的容器,应该先删除
使用docker image ls
命令来配合
可以用来承接多个实体命令
使用docker image ls -q
来配合docker image rm
比如删除所有仓库名为redis的镜像
$ docker image rm $(docker image ls -q redis)
删除所有在mongo:3.2
之前的镜像
$ docker image rm $(docker image ls -q -f before=mongo:3.2)
利用commit理解镜像构成
使用Dockerfile定制镜像
Dockerfile指令详解
Dockerfile多阶段构建
构建多种系统架构支持的Docker镜像
其他制作镜像的方式
实现原理
操作容器
容器:独立的一个或一组应用
虚拟机:模拟运行的整套操作系统和上面运行的应用
启动
- 基于镜像新建容器(常用)
- 将终止状态(exited)的容器重新启动
新建并启动
$ docker run ubuntu:18.04 /bin/echo 'Hello world'
Hello world
输出“hello world”后,终止容器
$ docker run -t -i ubuntu:18.04 /bin/bash
root@af8bae53bdd3:/#
启动一个 bash 终端,允许用户进行交互
-t
让docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输出上-i
让容器的标准输入保持打开
使用docker run
,docker在后台的标准操作:
- 检查镜像是否存在,否则从registry下载
- 创建启动容器
- 分配文件系统,只读镜像层外挂载可读写层
- 从宿主主机的网桥接口桥接虚拟接口到容器中
- 从地址池中分配IP地址给容器
- 执行用户程序(比如指定的bash程序)
- 结束后容器终止
启动已终止的容器
使用docker container start
命令,启动已经终止(exited)的容器
在伪终端可以使用ps
或top
来查看进程信息
守护态运行
很多时候,docker在后台(daemon)运行,而不是直接将执行命令的结构输出在当前的宿主机下
-
不添加
-d
参数$ docker run ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done" hello world hello world hello world hello world
-
添加
-d
参数$ docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done" 77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a
容器在后台运行,但是不把结果(stdout)打印到宿主机上
使用
docker container logs [container ID or NAMES]
查看
终止
docker container stop
终止运行中的容器
docker container restart
命令会将一个运行态的容器终止,然后再重新启动它
进入容器
使用-d
命令后,容器启动会进入后台
某些时候,要进入容器操作,使用docker exec
或docker attach
,推荐前者
attach
命令
$ docker run -dit ubuntu
243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550
$ docker attach 243c
root@243c32535da7:/#
如果从stdin中exit,会导致容器的停止
exec
命令
-
docker exec
后边可以跟多个参数 -
只用
-i
参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回$ docker exec -i 69d1 bash ls bin boot dev ... $ docker exec -it 69d1 bash root@69d137adef7a:/#
-
从这个 stdin 中 exit,不会导致容器的停止,推荐大家使用
docker exec
导入和导出
导出容器
使用docker export
命令
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7691a814370e ubuntu:18.04 "/bin/bash" 36 hours ago Exited (0) 21 hours ago test
$ docker export 7691a814370e > ubuntu.tar
导入容器快照
-
使用
docker import
命令$ cat ubuntu.tar | docker import - test/ubuntu:v1.0 $ docker image ls REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB
-
也可以使用URL导入
$ docker import http://example.com/exampleimage.tgz example/imagerepo
注:用户既可以使用 *docker load*
来导入镜像存储文件到本地镜像库,也可以使用 *docker import*
来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。
删除
- 使用
docker container rm
来删除一个处于终止状态的容器 - 要删除一个运行中的容器,可以添加
-f
参数 $ docker container prune
清理掉所有处于终止状态的容器
访问仓库
注册服务器-仓库-标签
对于仓库地址 docker.io/ubuntu
来说,docker.io
是注册服务器地址,ubuntu
是仓库名
Docker Hub
docker search
查看官方仓库中的镜像
docker pull
下载镜像
推送镜像
用户在登录后,使用docker push
命令将镜像推送到docker hub
$ docker tag ubuntu:18.04 username/ubuntu:18.04
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 18.04 275d79972a86 6 days ago 94.6MB
username/ubuntu 18.04 275d79972a86 6 days ago 94.6MB
$ docker push username/ubuntu:18.04
$ docker search username
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
username/ubuntu
私有仓库
docker-registry是构建私有镜像仓库的工具
-
安装运行docker-registry
使用官方的
registry
镜像来启动私有仓库。默认情况下,仓库会被创建在容器的/var/lib/registry
目录下$ docker run -d -p 5000:5000 --restart=always --name registry registry
通过
-v
参数来将镜像文件存放在本地的指定路径$ docker run -d \ -p 5000:5000 \ -v /opt/data/registry:/var/lib/registry \ registry
registry本质上是一个镜像,用来创建仓库容器,镜像存放在仓库容器的特定目录下
-
在私有仓库上传、搜索、下载镜像
使用
docker tag
来标记一个镜像,然后推送它到仓库(先改名后推送)例如私有仓库地址为
127.0.0.1:5000
$ docker image ls REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE ubuntu latest ba5877dc9bec 6 weeks ago 192.7 MB
- 使用
docker tag
将ubuntu:latest
这个镜像标记为127.0.0.1:5000/ubuntu:latest
$ docker tag ubuntu:latest 127.0.0.1:5000/ubuntu:latest $ docker image ls REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE ubuntu latest ba5877dc9bec 6 weeks ago 192.7 MB 127.0.0.1:5000/ubuntu:latest latest ba5877dc9bec 6 weeks ago 192.7 MB
- 使用
docker push
上传标记的镜像
$ docker push 127.0.0.1:5000/ubuntu:latest The push refers to repository [127.0.0.1:5000/ubuntu] 373a30c24545: Pushed a9148f5200b0: Pushed cdd3de0940ab: Pushed fc56279bbb33: Pushed b38367233d37: Pushed 2aebd096e0e2: Pushed latest: digest: sha256:fe4277621f10b5026266932ddf760f5a756d2facd505a94d2da12f4f52f71f5a size: 1568
- 用
curl
查看仓库中的镜像
$ curl 127.0.0.1:5000/v2/_catalog {"repositories":["ubuntu"]}
- 先删除已有镜像,再尝试从私有仓库中下载这个镜像
$ docker image rm 127.0.0.1:5000/ubuntu:latest $ docker pull 127.0.0.1:5000/ubuntu:latest Pulling repository 127.0.0.1:5000/ubuntu:latest ba5877dc9bec: Download complete 511136ea3c5a: Download complete 9bad880da3d2: Download complete 25f11f5fb0cb: Download complete ebc34468f71d: Download complete 2318d26665ef: Download complete $ docker image ls REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 127.0.0.1:5000/ubuntu:latest latest ba5877dc9bec 6 weeks ago 192.7 MB
- 使用
-
配置非https仓库地址
-
让本网段的其他主机也能把镜像推送到私有仓库。把例如
192.168.199.100:5000
这样的内网地址作为私有仓库地址 -
Docker 默认不允许非
HTTPS
方式推送镜像 -
通过 Docker 的配置选项来取消这个限制
-
对于使用
systemd
的系统,请在/etc/docker/daemon.json
中写入如下内容{ "registry-mirrors": [ "https://hub-mirror.c.163.com", "https://mirror.baidubce.com" ], "insecure-registries": [ "192.168.199.100:5000" ] }
-
私有仓库高级配置
Nexus3
数据管理
数据卷
-
数据卷
是一个可供一个或多个容器使用的特殊目录 -
它绕过 UnionFS,可以提供很多有用的特性:
数据卷
可以在容器之间共享和重用- 对
数据卷
的修改会立马生效 - 对
数据卷
的更新,独立于镜像 数据卷
默认会一直存在,即使容器被删除
类似于 Linux 下对目录或文件进行 mount
镜像中的被指定为挂载点的目录中的文件会复制到数据卷中
-
创建一个数据卷
$ docker volume create my-vol
查看数据卷
$ docker volume ls DRIVER VOLUME NAME local my-vol
查看指定数据卷
$ docker volume inspect my-vol [ { "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/my-vol/_data", "Name": "my-vol", "Options": {}, "Scope": "local" } ]
-
启动一个挂载数据卷的容器
用
docker run
命令的时候,使用--mount
标记来将数据卷
挂载到容器里创建一个名为
web
的容器,并加载一个数据卷
到容器的/usr/share/nginx/html
目录$ docker run -d -P \ --name web \ # -v my-vol:/usr/share/nginx/html \ --mount source=my-vol,target=/usr/share/nginx/html \ nginx:alpine
-
查看数据卷的具体信息
查看
web
容器的信息$ docker inspect web
数据卷
信息在 “Mounts” Key 下面"Mounts": [ { "Type": "volume", "Name": "my-vol", "Source": "/var/lib/docker/volumes/my-vol/_data", "Destination": "/usr/share/nginx/html", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ],
-
删除数据卷
$ docker volume rm my-vol
无主的数据卷可能会占据很多空间,使用
$ docker volume prune
挂载主机目录
挂载一个主机目录作为数据卷
使用 --mount
标记可以指定挂载一个本地主机的目录到容器中去
$ docker run -d -P \
--name web \
# -v /src/webapp:/usr/share/nginx/html \
--mount type=bind,source=/src/webapp,target=/usr/share/nginx/html \
nginx:alpine
- 这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作
- 本地目录的路径必须是绝对路径
- 挂载主机目录的默认权限是
读写
查看数据卷的具体信息
在主机里使用以下命令可以查看 web
容器的信息
$ docker inspect web
挂载一个本地主机文件作为一个数据卷
$ docker run --rm -it \
# -v $HOME/.bash_history:/root/.bash_history \
--mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history \
ubuntu:18.04 \
bash
root@2affd44b4667:/# history
1 ls
2 diskutil list
使用网络
docker允许外部访问容器或者容器互联的方式来提供网络服务
外部访问容器
映射所有接口地址
-
容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过
-P
或-p
参数来指定端口映射 -
-P
标记时,Docker 会随机映射一个端口到内部容器开放的网络端口 -
使用
docker container ls
可以看到,本地主机的 32768 被映射到了容器的 80 端口。此时访问本机的 32768 端口即可访问容器内 NGINX 默认页面$ docker run -d -P nginx:alpine $ docker container ls -l CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fae320d08268 nginx:alpine "/docker-entrypoint.…" 24 seconds ago Up 20 seconds 0.0.0.0:32768->80/tcp bold_mcnulty
通过
docker logs
命令来查看访问记录$ docker logs fa 172.17.0.1 - - [25/Aug/2020:08:34:04 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0" "-"
-
-p
则可以指定要映射的端口ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort
映射到指定地址的指定端口
使用 ip:hostPort:containerPort
格式指定映射使用一个特定地址
$ docker run -d -p 127.0.0.1:80:80 nginx:alpine
映射到指定地址的任意端口
使用 ip::containerPort
绑定 localhost 的任意端口到容器的 80 端口,本地主机会自动分配一个端口
$ docker run -d -p 127.0.0.1::80 nginx:alpine
查看端口配置
使用 docker port
来查看当前映射的端口配置,也可以查看到绑定的地址
$ docker port fa 80
0.0.0.0:32768
-
容器有自己的内部网络和 ip 地址(使用
docker inspect
查看,Docker 还可以有一个可变的网络配置。) -
-p
标记可以多次使用来绑定多个端口
$ docker run -d \
-p 80:80 \
-p 443:443 \
nginx:alpine
容器互联
使用 --link
参数来使容器互联
新建网络
$ docker network create -d bridge my-net
-d
参数指定 Docker 网络类型
连接容器
运行一个容器并连接到新建的 my-net
网络
$ docker run -it --rm --name busybox1 --network my-net busybox sh
打开新的终端,再运行一个容器并加入到 my-net
网络
$ docker run -it --rm --name busybox2 --network my-net busybox sh
通过 ping
来证明 busybox1
容器和 busybox2
容器建立了互联关系
在 busybox1
容器输入以下命令
/ # ping busybox2
PING busybox2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.072 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.118 ms
配置DNS
如何自定义配置容器的主机名和 DNS 呢?秘诀就是 Docker 利用虚拟文件来挂载容器的 3 个相关配置文件
$ mount
/dev/disk/by-uuid/1fec...ebdf on /etc/hostname type ext4 ...
/dev/disk/by-uuid/1fec...ebdf on /etc/hosts type ext4 ...
tmpfs on /etc/resolv.conf type tmpfs ...
这种机制可以让宿主主机 DNS 信息发生更新后,所有 Docker 容器的 DNS 配置通过 /etc/resolv.conf
文件立刻得到更新
配置全部容器的 DNS ,也可以在 /etc/docker/daemon.json
文件中增加以下内容来设置
{
"dns" : [
"114.114.114.114",
"8.8.8.8"
]
}
一些tips
docker中的ubuntu安装软件:https://blog.csdn.net/cecurio/article/details/105578136
cp /etc/apt/sources.list /etc/apt/sources.list.bak
echo "deb http://mirrors.ustc.edu.cn/ubuntu/ xenial main restricted universe multiverse" >/etc/apt/sources.list
echo "deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-security main restricted universe multiverse" >>/etc/apt/sources.list
echo "deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse" >>/etc/apt/sources.list
echo "deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-proposed main restricted universe multiverse" >>/etc/apt/sources.list
echo "deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse" >>/etc/apt/sources.list
echo "deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial main restricted universe multiverse" >>/etc/apt/sources.list
echo "deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-security main restricted universe multiverse" >>/etc/apt/sources.list
echo "deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse" >>/etc/apt/sources.list
echo "deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-proposed main restricted universe multiverse" >> /etc/apt/sources.list
echo "deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse" >>/etc/apt/sources.list
apt-get update
apt-get install -y vim