Server搭建过程-9.Docker个人心得

1.Docker的基本使用

参考链接:
1.40分钟的Docker实战攻略,一期视频精通Docker
2.Docker概念,工作流和实践 - 入门必懂
3.Docker — 从入门到实践

在Docker还没有出现的时期,使用裸机直接部署软件服务。
软件运行总要基于一些环境,而每个软件需要的环境配置可能各不相同。
先不说配环境有多痛苦,甚至有些软件需要同一个环境的不同版本,会引起冲突。
而且迁移方面,你的设备上也许能正常运行这个软件服务,别的设备上由于环境变化,软件服务就有可能运行不了了。

因此很自然的想出虚拟化的方案,先把软件服务需要的环境和软件一起打包,运行在一个独立的虚拟化环境,方便软件的部署和迁移。
但传统的虚拟化,是直接模拟出一套完整的硬件和一套完整的操作系统内核。
对于部署软件来说,很多时候都不需要这么完整的虚拟化,同时这种虚拟化的性能开销太大。

Linux内核支持操作系统层面的虚拟化技术。(这基于cgroup , namespace等特性)
在这基础上,docker做到了每个虚拟的容器,共用同一个系统内核(也就是宿主机的内核。)
同时docker也没有进行硬件的虚拟,而是直接使用宿主机的硬件。
运行的每个容器,就像是宿主机下一个一个的独立进程。
相较于传统的虚拟化大大降低了性能开销,同时这种虚拟化的结构更加短小精悍。

虽然说共用宿主机的系统内核,但你可能发现不同容器内的Linux发行版不同。
其实这只代表了用户空间,实际上还是同一个宿主机内核。

总结来讲,Docker有几个好处:
1.性能损耗低,资源利用率高,比传统虚拟化高效得多。(一般来说性能表现接近原生)
2.开箱即用,上手简单,镜像生态好(通过编写Dockerfile或者拿到别人预先构建好的镜像,可以快速部署直接可用的服务)
3.提供了一致的运行环境,打包迁移容器和镜像方便、快速。
4.每个容器之间独立隔离开来,既保证了容器内运行服务不受干扰,也一定程度上保障了安全。

Docker有两个概念,一个是镜像一个是容器。
镜像类似于模板,事先定义好了一套完整的应用程序运行环境,包括代码、库、依赖和配置。
而容器其实就是基于镜像所创建的一个具象的实例。像是根据图纸来构建出真正的房子。
可能听起来比较难理解,不过真正使用起来应该就马上懂了。

关于镜像和容器有几点需要注意的:
容器和镜像都是采用了分层存储的结构。容器实际上是在镜像的基础上,创建了一层可写层。
为了避免镜像间引用的依赖链过长和虚悬镜像。不要把生成的容器再次构建成镜像,接着循序反复。
建议直接使用Dockerfile,一次性安装完所有所需依赖,直接构建成一个镜像,减少中间层。
同时尽量保持可写层的无状态性,容器内使用数据卷或者绑定宿主目录。
对于分层存储,目前需要理解的就这么多。
更多深入内容等到初学之后,想自己编写Dockerfile时再查看。


1.docker pull

完整的命令示例:

1
docker pull docker.io/library/nginx:latest

几个参数介绍:
docker.io:默认对应Docker Hub官方仓库地址,也可以替换为其他仓库地址(例如国内的一些镜像源)。
library:默认对应官方镜像的命名空间,相当于作者名。
镜像冒号后的部分:用于指定镜像的标签(tag),常用来表示版本号(如nginx:1.25),默认值为latest。意为最新版本。

1
docker pull nginx

表示:在Docker官方仓库的官方命名空间内,下载Nginx的最新版本。

1
docker pull cloudreve/cloudreve

表示:在Docker官方仓库的Cloudreve作者名,下载Cloudreve的最新版本。

1
docker pull docker.xuanyuan.me/mysql

表示:在docker.xuanyuan.me这个非官方仓库的官方命名空间内,下载mysql的最新版本。
这也是一种临时换源的办法,把仓库地址指定为国内的镜像源。

2.docker images(等同于docker image ls

列出本地所存的镜像。

1
2
3
REPOSITORY                             TAG       IMAGE ID       CREATED       SIZE
docker.m.daocloud.io/node latest 0c7eb8906f06 7 days ago 1.14GB
nginx latest ad5708199ec7 3 weeks ago 192MB

3.docker tag

上面docker images中,经过临时代理下载的镜像,REPOSITORY名变得比官方仓库的长的多。
想要重命名镜像,可以使用docker tag命令。

1
docker tag docker.m.daocloud.io/node node

和上面类似的,如果镜像名后不写tag,默认为latest

需要注意tag命令只是新增了一个标签指向这个镜像。
现在其实node:latestdocker.m.daocloud.io/node:latest这两个tag都指向了同一镜像。(即IMAGE ID0c7eb8906f06的镜像)需要的话,可以使用命令把原来的长tag删除。

1
2
docker rmi docker.m.daocloud.io/node
Untagged: docker.m.daocloud.io/node:latest

实际上执行命令后,Docker首先会把这个tag删除。如果还有其他tag指向同一个镜像ID,那镜像文件还会保留。
当这个镜像的所有tag都没了,且没有容器依赖它,这个镜像ID就会真正从磁盘上清出。

4.docker run

常规命令

1
docker run --network host --name atest -itd nginx

docker start/stop/restart

docker ps

ps中,容器的启动命令是在哪里编写的,Dockerfile吗?
ps中,容器的启动命令中的当前终端(比如sh、bash)如何进入。

docker run中指定 -arg代理变量是如何在容器内生效的?指定在某个终端(比如sh和bash)生效吗?

docker log 中记录的容器日志是从哪来的,终端输出的信息吗?

docker ps -a

docker rename

docker exec

我们都知道,使用

1
docker exec -it node bash

的话,打开top监视,会发现创建了一个新的bash终端。
这也正是linux的特性,用户可以同时打开并独立操作多个终端,而互不影响。

docker copy

docker build

docker push

docker rm/rmi


先说说使用docker镜像搭建tailscalederp中转服务器。

参考链接:
1.https://icloudnative.io/posts/custom-derp-servers/#使用纯-ip
2.https://github.com/yangchuansheng/ip_derper
3.https://blog.csdn.net/god_sword_/article/details/129353427
4.https://neucrack.com/p/286
5.令人头疼的 docker 代理问题,我整理了解决方法和验证方案

docker需要换源和代理的,修改这里:

1
vim /etc/docker/daemon.json

格式类似这样:

1
2
3
4
5
6
7
8
9
10
{
"registry-mirrors": [
"https://docker.xuanyuan.me"
],
"proxies": {
"http-proxy": "http://127.0.0.1:7890",
"https-proxy": "http://127.0.0.1:7890",
"no-proxy": "localhost,127.0.0.0/8"
}
}

如果只是调整json文件而没有更改systemd的配置文件,只需要重启docker就行了。

1
sudo systemctl restart docker

否则,则输入这两条命令让配置生效。

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

若要检查生效情况,可以执行docker info
在信息中有http proxy等字眼就是成功了。

配置后拉取镜像可能一开始会EOF报错,多拉几次就好了。
docker pull和 push都可以用这个。

当然一般来说设置了代理就不设置国内镜像源,设置了国内镜像源就不设置代理。
因为两个都设置的情况下,就是在国内镜像源内寻找镜像,接着通过代理拉取到本地,纯粹没必要这样做。
一般情况下我不设置国内镜像源,因为国内镜像源并不稳定,哪天没了还得再找。
以下都以代理配置为主。
设置了可用的国内镜像源,等效于daemon.json+宿主机终端代理设置。

docker build又分两种情况,第一种是docker deamon pull外部镜像,比如用FROM go等别的镜像,
第二种情况是构建的临时容器内 如wget下载github文件等可能。
检查daemon.json是否对两种有用,可以用curl然后docker查看容器日志的形式。

另外检查daemon.json是否能直接使得容器内挂在代理上,额是不是受了itd后台运行的影响,不开后台d 用原本的终端程序进去看看有没有代理。

另外有一种情况,服务器开启了公网ipv6的地址(比如腾讯云的服务器),而一般的代理工具只代理V4。
docker build拉取镜像的时候报错信息里面有IPV6的地址,应该就是daemon优先使用了v6地址。

因此daemon这样设置:

1
2
3
4
5
6
7
8
9
10
11
{
"registry-mirrors": [
"https://docker.xuanyuan.me"
],
"proxies": {
"http-proxy": "http://127.0.0.1:7890",
"https-proxy": "http://127.0.0.1:7890",
"no-proxy": "localhost,127.0.0.0/8"
},
"ipv6": false
}

docker build的时候会终端
不要pull再build

daemon不会影响buld

config.json对应传进去容器内

network了就不用再config,network能直接终端,不需要再config设置。和run后打开的容器不一样。

docker build 时候From镜像的部分,也就是这部分:

1
2
3
4
5
6
7
=> [internal] load build definition from Dockerfile                                                               
=> => transferring dockerfile: 130B
=> [internal] load metadata for docker.io/library/golang:latest
=> [internal] load .dockerignore
=> => transferring context: 2B
=> [1/3] FROM docker.io/library/golang:latest@sha256:5502b0e56fca23feba76dbc5387ba59c593c02ccc2f0f7355871ea9a085
=> => resolve docker.io/library/golang:latest@sha256:5502b0e56fca23feba76dbc5387ba59c593c02ccc2f0f7355871ea9a085

如果国内环境,什么也不动的情况下,大概率是在load metadata这一行就会卡住。

Build分两部分:
FROM 镜像,load metadata这些部分应该算是dockerd的,因为这部分只需要在配置好daemon.json的前提下
(记得docker info检查一下json配置文件生效没)
将宿主机的终端设置上代理变量,就可以正常通过FROM这一关。

1
2
3
4
5
6
7
8
export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
export HTTP_PROXY=http://127.0.0.1:7890
export HTTPS_PROXY=http://127.0.0.1:7890
export ALL_PROXY=socks5h://127.0.0.1:7890
export all_proxy=socks5h://127.0.0.1:7890
export NO_PROXY=localhost,127.0.0.0/8,::1
export no_proxy=localhost,127.0.0.0/8,::1

当然你如果不是裸核运行,有自动化的工具已经帮你配好变量的话就不用弄这一步了。

另一部分就是build后开启的临时容器,这其中会执行Dockerfile中RUN等各种参数。
docker run 添加--network host参数已经起不了作用了,并不能使得临时容器内成功使用到宿主机的代理。

要么在docker run中使用--build-arg参数,要么在~/.docker/config.json配置文件下设置代理。
都能把代理变量传入临时容器中。

至于代理变量的值是多少,要看你docker run添不添加--network host参数
加了的话就127.0.0.1,不加就是docker的虚拟网址。

参考:

1
2
3
4
5
6
7
docker build \
--build-arg http_proxy=http://127.0.0.1:7890 \
--build-arg https_proxy=http://127.0.0.1:7890 \
--build-arg HTTP_PROXY=http://127.0.0.1:7890 \
--build-arg HTTPS_PROXY=http://127.0.0.1:7890 \
--network host \
-t name .

一般来说上面虽然export了很多变量在终端,但是docker容器内部一般来说用不到socks和noproxy吧?
根据自己情况来调整最好。




我选择cd到opt目录下编译文件。

1
cd /opt

先把相关文件git下来

1
git clone https://github.com/yangchuansheng/ip_derper

里面有参考链接1对应的生成证书sh脚本和Dockerfile。

cd到git下载的文件夹内,即和build_cert.shDockerfile的同级目录下:

1
cd ip_derper

接着git clone tailscale上的官方仓库

1
git clone https://github.com/tailscale/tailscale

因为Dockerfile里面写的是ADD变量,要把宿主机上的tailscale对应文件复制进docker容器编译。所以先要把文件下到宿主机。

更改cert.go文件以支持纯IP

1
vim tailscale/cmd/derper/cert.go

找到这三行:(大概在第131行)

1
2
3
if hi.ServerName != m.hostname && !m.noHostname {
return nil, fmt.Errorf("cert mismatch with hostname: %q", hi.ServerName)
}

删除或者注释掉。

回到Dockerfile目录下

1
cd /opt/ip_derper

使用docker构建镜像

1
docker build --network host -t derp .

之前在daemon.json设置了换源和代理,build的时候其实会开一个临时容器,build的时候设置网络模式为host,直接用宿主机的7890代理端口,加快编译。
(到时候记得测试一下docker exec进容器终端能不能用代理)

~./docker/config.json和/etc/docker/daemon.json这两个设置代理的区别。

1
2
docker run --network host --name derp -itd derp
docker exec -it derp bash

https://www.cnblogs.com/johnnyzen/p/18746970