-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Docker 简介
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。
由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。
传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。
安装
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加软件源信息
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3: 更新并安装 Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce
# Step 4: 开启Docker服务
sudo service docker start
镜像
镜像是 Docker 中最核心的概念,一个容器可以理解为镜像的实例化
Dockerfile 指令集
COPY 复制文件
COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。
<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
COPY package.json /usr/src/app/
ADD 更高级的复制文件
ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。
- <源路径> 可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去
- 如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。
因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD。
CMD 容器启动命令
容器就是进程 ,既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器 主进程 的启动命令的。
CMD 有两种格式:
- shell 格式:CMD <命令>
- exec 格式:CMD ["可执行文件", "参数1", "参数2"...]
如果使用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行。比如:
CMD echo $HOME
在实际执行中,会将其变更为:
CMD [ "sh", "-c", "echo $HOME" ]
使用 nginx 以后台守护进程形式启动 nginx 服务
CMD service nginx start
在实际执行中,会将其变更为:
CMD sh -c service nginx start
这样将导致sh执行完后sh主进程退出,导致容器退出
正确的做法是直接执行 nginx 可执行文件,并且要求以前台形式运行。比如:
CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT 入口点
ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。
当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ] <CMD>
ENV 设置环境变量
格式有两种:
- ENV
- ENV = =...
定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。比如在官方 node 镜像 Dockerfile 中,就有类似这样的代码:
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
&& rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
VOLUME 定义匿名卷
格式为:
- VOLUME ["<路径1>", "<路径2>"...]
- VOLUME <路径>
VOLUME /data
这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。
docker run -d -v mydata:/data xxxx
EXPOSE 声明端口
- 格式为 EXPOSE <端口1> [<端口2>...]
EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
WORKDIR 指定工作目录
- 格式为 WORKDIR <工作目录路径>。
使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
USER 指定当前用户
- 格式:USER <用户名>
USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份。
当然,和 WORKDIR 一样,USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。
RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]
如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 gosu。
# 建立 redis 用户,并使用 gosu 换另一个用户执行命令
RUN groupadd -r redis && useradd -r -g redis redis
# 下载 gosu
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \
&& chmod +x /usr/local/bin/gosu \
&& gosu nobody true
# 设置 CMD,并以另外的用户执行
CMD [ "exec", "gosu", "redis", "redis-server" ]
容器
容器可以理解为独立运行的一个或者一组应用,它完全隔离和宿主主机的环境。
在运行时可以指定新的命令来替代镜像设置中的这个默认命令
sudo docker run -it unbuntu cat /etc/os-release
新建并启动容器
sudo docker run --name web nginx
后台新建并启动容器
在使用 -d 参数时,容器启动后会进入后台。
sudo docker run --name web -d nginx
注: 容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关。
交互式运行容器
sudo docker run --name centos -it centos bash
其中,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。
停止容器
sudo docker container web stop
启动容器
sudo docker container web start
要获取容器的输出信息,可以通过 docker container logs 命令。
进入容器
docker exec -it web bash
删除容器
sudo docker container rm web
删除所有停止状态的容器
sudo docker container prune
仓库
仓库(Repository)是集中存放镜像的地方。
Docker Hub
前 Docker 官方维护了一个公共仓库 Docker Hub,其中已经包括了数量超过 15,000 的镜像。大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现
登录 Docker Hub
可以通过执行 docker login 命令交互式的输入用户名及密码来完成在命令行界面登录 Docker Hub。
你可以通过 docker logout 退出登录。
拉取镜像
你可以通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它下载到本地。
推送镜像
用户也可以在登录后通过 docker push 命令来将自己的镜像推送到 Docker Hub。
$ docker tag ubuntu:17.10 username/ubuntu:17.10
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 17.10 275d79972a86 6 days ago 94.6MB
username/ubuntu 17.10 275d79972a86 6 days ago 94.6MB
$ docker push username/ubuntu:17.10
$ docker search username
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
username/ubuntu
网络
Docker允许外部网络访问到容器内部。同样也可以使用容器互联的方式实现各个容器之间的访问。
外部网络访问容器
Docker容器中运行的应用,如果想要通过外部访问这个应用,可以使用-P 或者 -p 将容器应用开放的端口映射到主机的端口上面。外部就可以通过访问宿主的ip加上映射的端口来访问容器内的应用了。
当使用 -P 标记时,Docker 会随机映射一个的端口到内部容器开放的网络端口。
[vagrant@localhost ~]$ sudo docker run --name nginx1 -d -P nginx
d4d7abd0a5b10e3ae6f3783fccadd3122960bb9190c29753ee894dc92bb94f3b
[vagrant@localhost ~]$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d4d7abd0a5b1 nginx "nginx -g 'daemon of…" 48 seconds ago Up 47 seconds 0.0.0.0:32769->80/tcp nginx1-p 则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有 ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort。
# 制定映射到主机的端口为80,绑定主机上所有的IP
sudo docker run --rm -p 80:80 nginx
# 制定映射到主机的端口为80,绑定主机上指定IP 127.0.0.1
sudo docker run --rm -p 127.0.0.1:80:80 nginx
# 制定映射到主机的随机闲置端口,绑定主机上指定IP 127.0.0.1
sudo docker run --rm -p 127.0.0.1::80 nginx
-p 标记可以多次使用来绑定多个端口
$ docker run -d \
-p 5000:5000 \
-p 3000:80 \
training/webapp \
python app.py
容器互联
创建一个新的 Docker 网络
[vagrant@localhost ~]$ sudo docker network create -d bridge my_network
89ba79b8b48d0cab00545154cd87f5502ea5c8157d0a3df18f76de95880979ee
查看所有的容器网络
[vagrant@localhost ~]$ sudo docker network ls
NETWORK ID NAME DRIVER SCOPE
a51998aeaa00 bridge bridge local
d8463e2bd0fe host host local
89ba79b8b48d my_network bridge local
84d05dcc187f none null local
-d 参数指定 Docker 网络类型,有 bridge overlay。其中 overlay 网络类型用于 Swarm mode,在本小节中你可以忽略它。
运行容器 并且 指定容器网络
下面的操作证明同属于my_network网络 容器centos1 和容器centos1 是容器互联的状态
[vagrant@localhost ~]$ sudo docker run -it --rm --name centos1 --network my_network centos bash
[root@a6c7797239a0 /]# ping centos2
PING centos2 (172.19.0.2) 56(84) bytes of data.
64 bytes from a6c7797239a0 (172.19.0.3): icmp_seq=1 ttl=64 time=0.054 ms
64 bytes from a6c7797239a0 (172.19.0.3): icmp_seq=2 ttl=64 time=0.049 ms
64 bytes from a6c7797239a0 (172.19.0.3): icmp_seq=3 ttl=64 time=0.031 ms
[vagrant@localhost ~]$ sudo docker run -it --rm --name centos2 --network my_network centos bash
[root@a6c7797239a0 /]# ping centos1
PING centos1 (172.19.0.2) 56(84) bytes of data.
64 bytes from a6c7797239a0 (172.19.0.2): icmp_seq=1 ttl=64 time=0.054 ms
64 bytes from a6c7797239a0 (172.19.0.2): icmp_seq=2 ttl=64 time=0.049 ms
64 bytes from a6c7797239a0 (172.19.0.2): icmp_seq=3 ttl=64 time=0.031 ms
查看端口映射情况
sudo docker port container_name
sudo docker inspect container_name
加入和移除网络
加入网络
# 移除容器的网络
docker network disconnect web web
# 加入容器到网络
docker network connect web web
存储
Docker 容器管理数据主要有两种方式
- 数据卷 volumes
- 挂载主机目录
数据卷volumes
数据卷有很多特性
- 数据卷可以在多个容器之间共享
- 数据卷修改会立马生效
- 数据卷是独立存在的,不会因为容器删除而被删除
- 数据卷的更新不会影响镜像
数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的 数据卷
创建数据卷
[vagrant@localhost ~]$ sudo docker volume create my_volume
my_volume
查看所有数据卷
[vagrant@localhost ~]$ sudo docker volume ls
DRIVER VOLUME NAME
local my_volume
查看特定的数据卷的详细信息
[vagrant@localhost ~]$ sudo docker volume inspect my_volume
[
{
"CreatedAt": "2018-02-08T16:52:56Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my_volume/_data",
"Name": "my_volume",
"Options": {},
"Scope": "local"
}
]
挂在数据卷到容器
[vagrant@localhost ~]$ sudo docker run --name web1 -v my_volume:/usr/share/nginx/html -d nginx
e5f017eb4055f9d4d5ae0989c3e2893820cfe9292c0d38ac411f7611a1229cda
[vagrant@localhost ~]$ sudo docker run --name web2 -v my_volume:/usr/share/nginx/html -d nginx
4943f3b685e7ccef352e2127590c0ca82204ab687dd2379a1e6548dcbd700066
上述实例的两个容器同时挂载了my_volume数据卷,所以他们的数据是共享的
[vagrant@localhost ~]$ sudo docker exec -it web1 bash
root@e5f017eb4055:/# cd /usr/share/nginx/html/
root@e5f017eb4055:/usr/share/nginx/html# echo "test_share_volume" >> test.html
root@e5f017eb4055:/usr/share/nginx/html# cat test.html
test_share_volume
[vagrant@localhost ~]$ sudo docker exec -it web2 bash
root@e5f017eb4055:/# cd /usr/share/nginx/html/
root@e5f017eb4055:/usr/share/nginx/html# echo "test_share_volume" >> test.html
root@e5f017eb4055:/usr/share/nginx/html# cat test.html
test_share_volume
随机存储位置 -v
docker run -v /mnt --name db centos
也可以使用inspect查看当前容器的数据卷
[vagrant@localhost ~]$ sudo docker inspect web1
"Mounts": [
{
"Type": "volume",
"Name": "my_volume",
"Source": "/var/lib/docker/volumes/my_volume/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
]
删除数据卷
[vagrant@localhost ~]$ sudo docker volume rm my_volume
Error response from daemon: unable to remove volume: remove my_volume: volume is in use - [e5f017eb4055f9d4d5ae0989c3e2893820cfe9292c0d38ac411f7611a1229cda, 4943f3b685e7ccef352e2127590c0ca82204ab687dd2379a1e6548dcbd700066]
当数据卷在使用中的时候删除会出现警告
数据卷是用来做数据持久化的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令。
无主的数据卷可能会占据很多空间,要清理请使用以下命令
$ docker volume prune
挂在主机目录
挂在主机上的本地目录到指定的容器
[vagrant@localhost ~]$ sudo docker run --name volume_local -v $HOME/volume_local:/usr/share/nginx/html -p 80:80 -d nginx
挂在当前的加目录的文件夹到nginx的目录,可以在本地修改代码同步到容器
数据容器 --volumes-from
sudo docker run --name web --volume-from <container> centos
docker-compose
安装
sudo curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
启动项目 后台
docker-compose up -d -p project_name
实战 lnmp
https://github.com/DanceSmile/docker_php_run_context
swarm 集群
结语
Docker 简化了部署环境,平衡了不同平台之间的差异。