一、前置学习
1 虚拟机 vs 容器
容器少了hypervisor层(把物理资源虚拟化的层)和操作系统,更小巧。而每个虚拟机都是一个完整的操作系统,要给其分配资源。
2 容器能做什么
简化配置(常用)、代码流水线管理、提高开发效率、隔离应用、整合服务器、调试能力、多租户、快速部署。
3 热门的容器
docker、k8s(容器编排工具,应用场景:比如有上千个容器)
二、ubuntu 20.04 配置docker
官网地址:
先用sudo apt-get update更新包索引,再允许apt使用https上的仓库:
添加docker的gpg密钥:
添加仓库:
再次更新索引并下载:
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
查看docker安装的版本:
测试一下:
至此,docker安装完成。之后,是关于docker其他内容的学习。
三、docker基础指令
方括号表可选。
0.文件传输
# 主机拷贝文件到docker
docker cp 源文件 docker_id:要保存的文件路径
# docker拷贝文件到主机
docker cp docker_id:源文件 要保存的文件路径
1.保存修改
# 改动不会默认保存,如果你需要保存修改,保存后会产生一个新image而不是新docker
docker commit -m"备注" docker_id [new_image_name]
然后可以通过docker ps -a查看新生成的image
2.删除
docker rmi image_id # 删除images
docker rm docker_id # 删除docker
3.展示镜像或容器
docker ps # 正在运行的docker
docker ps -a # 所有docker
docker images # 所有镜像
4.docker的启动和停止
docker run image_name # 启动
docker stop docker_id|docker_name # 停止
5.进入已启动的docker
docker exec -it docker_id|docker_name sh
四、Dockfile入门
首先要确定docker、容器和镜像的关系。docker是容器的一种,因为广泛使用,有些地方直接把docker和容器混用。容器由镜像实例化而来,也可以说,容器是进程。镜像是只读的,可以理解为静态文件,相对而言容器是动态的。
通过编写dockerfile创建docker镜像。
至此,可以开始学习dockerfile的编写,先看语法。
1 dockerfile语法
dockerfile的语法很简单,也不区分大小写,但还是约定为大写,常用的如下:
FROM 基于哪个镜像来实现
MAINTAINER 镜像的创建者
ENV 声明环境变量
RUN 执行的命令
ADD 添加宿主机文件到容器里,有需要解压的文件会自动解压
COPY 添加宿主机文件到容器中
WORKDIR 工作目录
EXPOSE 声明容器打算使用的端口,但并不会因为有这条命令就自动做端口映射。实际作用是帮助使用者配置映射,或者是当运行时使用随机端口映射,即docker run -p时,会自动映射到EXPOSE端口。
CMD 容器启动后所执行的程序,如果执行docker run后面跟启动命令会被覆盖掉
ENTRYPOINT 与CMD功能相同,区别在于:没有指定ENTRYPOINT时,按CMD运行;如果指定了ENTRYPOINT,CMD指定的内容会变成ENTRYPOINT的参数。
VOLUME 将宿主机的目录挂载到容器中
2 第一个dockfile
找一个你想放docker文件的地方,新建一个文件,通常直接命名为Dockerfile(用其他也可以但是不推荐),内容为:
FROM alpine:latest
MAINTAINER Winszheng
CMD echo 'hello docker'
其中,alpine是一个专门为docker做的linux镜像。写好文件后,在文件所在目录打开终端,使用命令生成镜像:
docker build -t hello_docker .
其中-t表示tag为hello_docker,‘.’表示使用当前目录的所有文件来生成这个镜像。运行完毕之后查看是否产生了这个镜像:
可见产生成功,试着运行一下:
可见正常运行。至此,我们用dockerfile产生了第一个镜像并成功运行。
3 第二个dockerfile
创建一个文件夹,新建一个Dockfile文件和index.html文件,这个dockerfile会安装nginx并把本机中的index.html复制到容器当中: Dockfile:
from ubuntu
maintainer Winszheng
run sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
run apt-get update
run apt-get install -y nginx
copy index.html /var/www/html
ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]
EXPOSE 80
这里值得注意的是,当docker镜像启动后,我们往往希望docker自动运行特定的程序,此时需要用CMD或者ENTRYPOINT显式指定具体命令。
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>我的测试站点</title>
</head>
<body>
<p>这是我的页面</p>
</body>
</html>
然后生成镜像:
docker build -t winszheng/docker_nginx .
尝试运行镜像:
其中-d表示后台运行镜像并返回容器id;-p指定要映射的ip和端口,可以接收的映射格式有:
host_port:docker_port
ip:host_pot:docker_port
ip::docker_port
打开相应页面,可见第二个镜像也成功运行:
3 镜像分层
dockerfile中的每一行都会产生一个新层。镜像是静态的,所以镜像中的层只读,但是当镜像运行时,会产生容器层,容器层可读可写——因此,镜像静态只读,但是运行成容器之后,却又能修改能容并保存成新的镜像。
分层的其他好处:不同docker之间可能会有共享的内容,比如几个docker之间有几层是共享的,那么分层就能减小存储压力。
五、关于Volume
1 把容器的指定目录挂载到本地
运行一个nginx程序,通过-v挂载一个卷:
sudo docker run -d --name nginx -v /usr/share/nginx/html docker_nginx
# -d: 后台运行并返回容器id
# /usr/share/nginx/html: 容器内的路径
# --name: 自定义docker名
可见挂载成功:
那么如何查看挂载信息呢? 执行:
docker inspect nginx # inspect是查看的意思
结果会出现docker的所有信息:
然而我们并不需要读懂所有内容,暂时只需关心“Mounts”部分:
可见,我们把主机的source路径挂载到了docker的destination路径,接下来为了方便操作,使用sudo su切换到root用户,然后查看挂载文件夹,可见docker的文件index.html已经共享到本地:
为了验证本地和docker之间能否方便地共享文件,在本地文件夹随便创建一个文件:
然后到docker找到这个文件,可见共享成功:
2 把本地的指定目录挂载到容器的指定目录
其中,$PWD是一个始终指向当前目录的环境变量,这条指令在启动docker的同时把当前目录下的html文件夹挂载到docker的/usr/share/nginx/html,此时查看访问效果:
显然本地文件夹确实挂载到docker了!不用在docker里修改东西却还能用docker的环境,舒服!
3 数据卷容器
即,从容器挂载到容器。
详细点说,就是从另一个容器中挂载容器中已经创建好的数据卷。
如果我们存在一些需要持续更新,并需要在容器中共享的数据卷,那么最好创建数据卷容器。目前来看,docker需要持久化的数据都应该放在数据卷上。
本质上,数据卷容器也就是个普通的容器,只是专门用来提供数据卷供其他容器挂载罢了。
3.1 创建数据卷容器
在你喜欢的目录下创建data文件夹,并打开终端,输入命令:
sudo docker create -v $PWD/data:/var/mydata --name db ubuntu
以上意思是把$PWD/data挂载到docker的/var/mydata,基础镜像选ubuntu,docker名字为db。
3.2 挂载数据卷容器
sudo docker run -it --volumes-from db ubuntu sh
--volumes-from: 表示从容器db挂载
结果:
可见docker中确实产生了/var/mydata,但是是空的,所以我随便创建了一个文件并退出。然后直接查看本地,发现文件共享成功:
至此,完成了把本地文件夹挂载到数据卷容器,再在其他容器中成功挂载数据卷容器。也即,以后用docker完成容器见数据共享也可以在本地操作了。
六、docker部署mysql
拉取官方最新镜像:
sudo docker pull mysql/mysql-server:latest
创建mysql文件夹用于挂载数据库文件,生成mysql容器:
进入mysql容器并连接:
可见能够连接。
七、golang容器化
使用docker部署一个golang应用。照例随便选一个你喜欢的目录,创建两个文件:main.go和Dockerfile,其中main.go表你的golang程序,是什么都可以,现在先用个简单的。
Dockerfile的内容:
FROM golang:alpine
ENV GO111MODULE=auto \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
WORKDIR /build
COPY . .
RUN go build -o main .
WORKDIR /main
RUN cp /build/main .
EXPOSE 9090
CMD ["/main/main"]
main.go:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", hello)
server := &http.Server{
Addr: ":9090",
}
fmt.Println("server startup...")
server.ListenAndServe()
}
func hello(w http.ResponseWriter, _ *http.Request) {
w.Write([]byte("hello docker!"))
}
然后sudo docker build . -t go_demo构建镜像并运行:
可见部署成功:
八、杂七杂八的问题
1 docker build的速度很慢
那就是遇到喜闻乐见的外国源问题,如果你的系统是ubuntu,那么:
cd /etc/docker
sudo gedit daemon.json
在打开文件中写入:
{
"registry-mirrors":["https://almtd3fa.mirror.aliyuncs.com"]
}
然后重启docker即可:
service docker restart