docker

Docker

docker优点:

  • 启动速度很快

  • 运行需要的内存占用小

  • 可移植性很好:

    比如我们做了一个web app,按照以前的做法,自然是先写代码,再测试,然后在实际的环境中去运行。然后实际的环境与测试的环境并不一致,这就会出现很多问题。而docker不同,它把代码,运行的环境,依赖这些统统封装好了,打包为一个image,在实际的环境中只要安装docker,跑一下这个image就可以了,不会存在环境不同,缺少依赖等等问题。这就好像运行一个独立的虚拟机一样,但是比虚拟机更好。

docker vs virtual machine

docker的运行速度与大小比虚拟机都小很多,那么他们之间的差异是怎么形成的?

docker与virtual machine的对比:

  • virtualmachine:硬件+hypev技术+os操作系统(linux+software)

  • docker:硬件+(共享)系统linux内核+docker+software(不同os所需的lib等+software)

  1. 总结一下,虚拟机使用了hyperv技术,让不同的操作系统运行在虚拟机上。而docker则是共享正在运行的系统的linux内核,然后在docker上运行不同docker所需的软件,环境配置,库,依赖等。

  2. 所以虚拟机运行的是独立的操作系统,docker则仅仅是加载所需的库,依赖等,所以大小不同。至于启动速度也很好理解了,虚拟机需要启动一个独立的操作系统,需要的时间长;docker则不同,它依赖的就是系统本身的linux内核,等于说早就已经启动,只需要加载必要的software和依赖等就行了。

  3. 所以docker最好是运行在linux上,因为它要共享linux的内核,如果安装在windows上的话,那么docker就需要运行虚拟机,然后在虚拟机上运行docker,那么就体现不出docker的优势了。

如果虚拟机运行的是一个完整的操作系统,那么docker就像是运行一个软件,只不过还需要加载一个不同的依赖和环境而已。弄明白了docker的工作原理,只想说I love docker

image and docker container

image镜像是官方早就已经做好的,我们只需要下载下来,启动docker,然后运行这个image就行了。那么image运行的状态就叫docker container容器。

docker安装

树莓派上面叫docker.io:

1
sudo apt-get install docker.io

archlinux上面:

1
sudo pacman -S docker

查看版本:

1
sudo docker version

添加国内镜像地址

1
sudo vim /etc/docker/daemon.json
1
2
3
{
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}

启动docker服务

1
sudo systemctl start docker

搜索需要的image

1
2
3
sudo docker search image_name
eg:
sudo docker search mysql

下载image

1
2
3
sudo docker pull image_name
eg:
sudo docker pull mysql

或者

1
2
3
sudo docker run image_name
eg:
sudo docker run mysql

这种是直接运行一个container容器,如果还没有安装过,那么会自动下载

查看已下载的images

1
sudo docker images

查看镜像信息

1
2
3
4
5
6
7
sudo docker inspect images
eg:
sudo docker inspect mysql
#可查看数据存储目录,那么数据持久化就挂载到这个目录下
"Volumes": {
"/var/lib/mysql": {}
},

删除已下载images

1
sudo docker rmi <IMAGE ID>

运行container

两种方式:

  • attach:在终端中运行,可以看到container的运行信息

    1
    sudo docker run image_name:tag

    其中tag为lastest时可以省略,如果为其它tag则必须指定出来

  • detach:类似于放到后台运行

    1
    2
    3
    4
    sudo docker run -d image_name
    sudo docker run -d --name NAMES image_name # 指定运行container名字
    eg:
    sudo docker run -d --name my-nginx nginx

查看正在运行的container

1
sudo docker ps

停止运行的container

1
2
sudo docker stop <CONTAINER ID>/NAMES
sudo docker kill <CONTAINER ID>/NAMES

重启容器

1
sudo docker restart <CONTAINER ID>/NAMES

查看所有的container

1
sudo docker ps -a

删除已有container

1
sudo docker rm <CONTAINER ID>/NAMES

删除运行container中无用的数据(包括images)

1
sudo docker system prune

删除所有无用数据,包括停止的container

1
sudo docker system prune -a

删除所有docker数据

docker数据默认存储在/var/lib/docker

1
sudo rm -R /var/lib/docker

container运行条件

容器能够运行一定是启动的这个container上面有程序,服务正在运行。如果没有(比如运行一个linux系统),或者这个程序停止崩溃了,那么这个container就自动退出了。

1
docker run ubuntu

container会自动退出,因为没有进程,或者程序运行,仅仅是一个Ubuntu系统。

如果需要它启动呢?

1
docker run ubuntu sleep 100

进入容器运行命令

类似ssh远程登录container,并指定使用bash来作为解释的shell

1
docker exec -it container_name /bin/bash

-i交互模式interactive

-t模拟tty登录

指定宿主机与容器的端口映射

-ppublish,发布的端口port。如果不指定则只能从容器内部访问,发布端口会将容器端口映射到主机端口,实现外部访问。

1
2
3
sudo docker run -d --name NAMES -p host_port:container_port image_name 
eg:
sudo docker run -d --name my-nginx -p 8080:80 nginx

指定了访问宿主机的8080端口时,自动映射到容器的80端口

容器的修改保存

如果不保存,那么对容器的修改不会持久化,也就是说修改不会保存到镜像中,只会保存在运行的container中。如果重新使用镜像生成容器,那么修改就没有了。

所以我们需要对修改进行提交,tag可以不写,默认为latest,如果写了,那么在创建容器时必须指定tag。

1
sudo docker commit -a "author" -m "message" <CONTAINER ID>/NAMES new_image_name:tag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
eg:
# 创建一个容器
sudo docker run -d --name my-nginx -p 80:80 nginx
# 进入容器
sudo docker exec -it my-nginx /bin/bash
# 新建目录
sudo mkdir test
# 退出容器
exit
# 提交修改,指定新的镜像名字和标签
sudo docker commit -a "lanzhan" -m "make a test directory" my-nginx my-new-nginx:v1.0
# 停止容器
sudo docker stop my-nginx
# 删除容器
sudo docker rm my-nginx
# 查看新建的镜像
sudo docker images
# 使用新建的镜像来创建新的容器
sudo docker run -d --name my-nginx my-new-nginx:v1.0
# 进入容器,查看新建的目录存在
sudo docker exec -it my-nginx /bin/bash
# 退出容器
exit

所以说,对容器修改的提交,就会创建一个新的镜像,所有的修改都会保存在新的镜像中。

文件、目录挂载

将宿主机中的文件挂载到容器中,挂载之后对宿主机中文件的修改,会反应到容器中,反之也会。共享数据实现了数据的持久化,同时方便本地的开发。

1
2
3
4
sudo docker run -d --name NAMES -p host_port:container_port -v host_file:container_file image_name
sudo docker run -d --name NAMES -p host_port:container_port -v host_dir:container_dir image_name
eg:
sudo docker run -d --name nginx -p 80:80 -v /home/narcissus/index.html:/usr/share/nginx/html/index.html my-new-nginx:v1.0

发现修改第二次修改之后容器中并没有发生改变,所有需要重启一下容器就正常了

1
sudo docker restart nginx

将容器中的文件复制到本地

1
2
3
sudo docker cp NAMES:/containcer_file /host_file
eg:
sudo docker cp nginx:/etc/fstab /home/narcissus/fstab.txt

将本地文件复制到容器中

1
2
3
sudo docker cp /host_file NAMES:/container_file
eg:
sudo docker cp /etc/fstab nginx:/etc/fstab

mysql容器的建立

  1. 设置root密码 -e MYSQL_ROOT_PASSWORD=my-secret-pw

  2. 数据同步备份 -v /my/own/datadir:/var/lib/mysql

  3. 获取远程权限(mysql8.0之后需要) alter user 'root'@'%' identified with mysql_native_password by '123456';

    查看权限 select host,user,authentication_string from user;

  4. 端口映射 -p host_port:container_port

1
sudo docker run -d --name mysql -v /home/narcissus/Data/dumpfile:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 mysql

然后就可以通过软件来连接docker中的mysql数据库了:

主机填写:localhost或者127.0.0.1

端口为映射的主机地址:3306

容器互联

--link 要被连接的容器名:在容器中的新别名

使用nginx去连接mysql数据库

1
sudo docker run -d --name nginx -p 80:80 --link mysql:mysql nginx

这样nginx就连接了mysql

自己构建镜像

前面已经写过如何保存修改镜像并生成新的镜像,指的是在container的基础上对镜像的修改。这里的构建镜像指的是如何基于原始镜像构建自己所需功能的基础镜像,也可以简单说是下载一些需要的软件,修改一些端口配置文件等等,来形成自己的个性化配置镜像。本来也可以使用container来一步一步的操作,但是dockerfile提供了自动化配置。

编辑dockerfile文件,通过文件来构建镜像,dockerfile分为四个部分

  1. 基础镜像信息

    如果本地有image那么会使用本地的,如果没有会自动下载

    1
    2
    3
    from image_name:tag	# tag为latest时可以省略
    eg:
    from nginx
  2. 维护者信息

    1
    2
    3
    maintainer name
    eg:
    maintainer lanzhan
  3. 镜像操作指令

    run

    run后面的command为平常使用的shell命令

    1
    2
    3
    run command
    eg:
    run apt-get install sqlite3

    如果需要传参

    1
    2
    3
    run ['executable','param1','param2']
    eg:
    run ['./somefile.sh','66','/home/']

    每一个指令都会在docker上新建一层,形成当前最新的dockerimage,所以应该把指令写到一条中

    \的作用为换行

    1
    2
    3
    run apt-get install sqlite3 &&\
    ['./somefile.sh','66','/home/'] &&\
    echo "there command in a run instruct"

    这样只会新建一层镜像。


copyadd

用于复制本机文件到docker中,在docker构建镜像时,会将要复制的文件所在目录下的所有文件先tar打包,然后发送给docker引擎,所以dockerfile所在的目录应该不包含没用的数据,如果有需要复制的文件,那么就把文件复制到dockerfile所在的目录下,而不要放一些没用的数据。

1
2
3
4
copy 本机文件 容器中路径
eg:
copy index.html /var/lib/nginx/default
copy *.html /var/lib/nginx/default

copy和add功能类似,不同点:add会将tar类型的自动解压,同时可以访问网络,copy则不行。


cmd

只有最后一条起作用

与run类似,但是运行时间点不同,run是在docker build时运行(构建镜像),cmd则是在docker run时运行(启动容器)

作用是为container指定默认启动起来后运行的程序,不过如果自己指定了,那么会覆盖它。例如

1
2
sudo docker run -it --name mysql /bin/bash
# 这里就自己指定了/bin/bash

entrypoint

只有最后一条起作用

与run类似,不同点在于不会被docker run中指定的命令覆盖


label

为镜像添加标识

1
2
3
label key=value key=value
eg:
label version='V1.0'

env

为docker设置环境变量,后面可以使用$key来引用变量

1
2
env key value
env key=value key=value

arg

与env类似,不同点在于设置的变量仅对dockerfile有效(构建镜像时),对docker镜像没效


volume

设置默认挂载目录文件等,指定持久化目录

1
volume /location1 /location2

expose

指定要开放的端口,仅仅是告诉外部开放了那些端口可用,只用通过端口映射才真正可以从外部访问docker。

1
expose port1 port2 port3

workdir

指定的目录要提前创建好,在每次构建新的一层中它会一直存在。

1
workdir /location

user

为后续命令切换用户(和用户组)

1
2
user user_name
user user_name:user_group

healthcheck

指定某个程序来监控docker运行状态

1
healthcheck option cmd command

onbuild

当下次构建镜像时,如果以这个新构建的镜像为基础镜像时,会执行

1
onbuild command
  1. 容器启动执行指令

    cmdentrypoint

构建镜像

点表示在当前目录下,tag可以不写默认为lastest

1
2
3
docker build -t image_name:tag .
# 或者
docker build -f /path/to/dockerfile .

使用archlinux构建mariadb数据库

archlinux中mariadb代替了mysql

  • 新建dockerfile文件
1
2
3
4
5
mkdir /home/narcissus/Data/docker
mkdir arch-mysql
cd arch-mysql
touch dockerfile
cp /etc/pacman.d/mirrorlist . # 复制本机镜像地址,提升下载速度
  • dockerfile文件
1
2
3
4
5
6
7
8
9
10
11
12
from archlinux/base
maintainer lanzhan
add start.sh /home
add mirrorlist /etc/pacman.d
run pacman --noconfirm -Syy &&\
pacman --noconfirm -S mysql &&\
mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql &&\
chmod a+x /home/start.sh
label version=v1.0
volume /var/lib/mysql
expose 3306
entrypoint /home/start.sh

不使用cmd而使用entrypoint的原因是cmd命令会被启动容器时的命令覆盖

  • start.sh文件

    由于entrypoint只能执行一条指令,所以启动mysql的命令需要写到文件中。通过执行脚步来运行。

    本来打算使用systemd去启动,但是没有这个软件,而且安装了不知道怎么启用systemd,还有为了不让镜像太大

1
2
3
#!/bin/bash

cd '/usr' ; /usr/bin/mysqld_safe --datadir='/var/lib/mysql'
  • 运行dockerfile
1
sudo docker build -t mariadb .
  • 新建container
1
sudo docker run -d --name mariadb -p 3306:3306 mariadb
  • 进入容器修改密码,设置远程登录权限
1
sudo docker exec -it mariadb /bin/bash
1
2
3
4
5
mysql -uroot -p
use mysql;
set password for root@localhost = password('123456');
update user set host='%' where user='root';
flush privileges;