sync.Cond的例子
sync.Cond类似于pthread中的条件变量,但等待的为goroutine,而不是线程。比较难理解的为Wait函数,在调用该函数时必须L为Lock状态,调用Wait函数后,goroutine会自动解锁,并等待条件的到来,等条件到来后会重新加锁。
代码量并不多,下面是去掉注释后的代码。
1 | package sync |
具体的使用例子如下:
1 | package main |
sync.Cond类似于pthread中的条件变量,但等待的为goroutine,而不是线程。比较难理解的为Wait函数,在调用该函数时必须L为Lock状态,调用Wait函数后,goroutine会自动解锁,并等待条件的到来,等条件到来后会重新加锁。
代码量并不多,下面是去掉注释后的代码。
1 | package sync |
具体的使用例子如下:
1 | package main |
自从阮一峰的博客中增加了每周分享栏目,自己每周五都是主动的浏览一下阮老师的每周分享,一来阮老师的涉猎非常广泛,可以提高自己的视野;二来,阮老师的文章都特别容易懂,给人一种一直想看下去的冲动。
我个人平常也会看很多的技术类文章,也会遇到各种工具或者特别不错的文章,也有分享的冲动,也想搞一些分享的文章,当然我没有阮老师勤奋和涉猎广泛,但也期望能够对他人有所帮助,哪怕文中的一条分享能够让读者觉得有价值,那么也是值的做的一件事情。
具体的分享版块可能不会特别固定,分享的间隔也不会特别勤快,很难做到阮老师的一周一次的频次。
Golang的内存模型,建议Golang开发者读一遍。
知名博客酷壳的作者陈皓的技术专栏,花钱购买一下专栏还是非常值得的,尤其是最近写的程序员练级攻略系列,能提供大量有价值的学习资料及方向指导,非常赞。
http://
对kubernetes的资源管理讲解的非常到位和深入,文章略长,需要花点时间才能读完,值的一看。
网络方面的经典著作,每个工程师必读,虽然是写给工程师看的,但很多的学术著作中引用到了该书中内容。
对于go的内部实现原理讲解的挺到位,对于理解go的原理挺有帮助。涉及少量汇编,我不太懂汇编,涉及汇编的地方直接跳过了。
RedHat官方的Linux文档,我个人还没怎么读过。
货真价实的互联网CEO的视频分享,谈创业、谈感悟,目前已经有蔚来汽车、VIPKID、每日优鲜、快手、Keep、知乎的CEO的分享。
新浪微博开源的nginx module,用于动态更改upstream server。
软件版本在取名上会比较混乱,有的使用1.0.1,有的使用1.0等,SemVer用于规范软件版本的命名。
吴军老师的最新图书,内容整理自吴军的专栏《硅谷来信》,每篇文章一个主题,值的一读。
又听到了一首关于济南的歌曲,曾在济南生活多年,必须要分享一下。
Github上的开源项目,将命令行工具单独保存为SVG动画。
提供了ssh的审计和回放,基于SSH的RBAC管理,同时还有一个带管理功能的ui界面,目的是用于取代系统自带的sshd。
统计代码行数的工具,下面是kubernetes项目的v1.11.2版本的代码行数统计,go的代码行数已经超过了100万行。
我个人不是vim工具党,刚毕业那会曾经一度热衷于将vim打造成为一个开发C++的IDE,但经过复杂的配置后仍然难以达到CLion这种IDE的水平。最近偶然看到SpaceVim,心中为之一振,这就是我想要的vim,虽达不到IDEA的高度,但已经可以跟vscode的易用度差不多了。
SpaceVim的强大之处在于Space键的使用,默认情况下按下空格键会给出快捷键的提示,类似于桌面系统中的菜单功能。
macvlan的原理是在宿主机物理网卡上虚拟出多个子接口,每个子接口有独立的mac地址,通过不同的MAC地址在数据链路层(Data Link Layer)进行网络数据转发的。达到的效果类似,一块物理网卡上有多个IP地址,多个IP地址有自己的mac地址。
它是比较新的网络虚拟化技术,需要较新的内核支持(Linux kernel v3.9–3.19 and 4.0+)。
macvlan设备跟物理设备之间并不直接互通。
macvlan不以交换机端口来划分vlan,一个交换机端口可接收来自多个mac地址的数据。
一个交换机端口要处理多个vlan的数据,需要开启trunk模式。
以下四种模式为每个macvlan设备单独配置,而不是一个物理设备就只有一个配置。
所有发送出去的报文都经过交换机,交换机再发送到对应的目标地址。默认模式。物理网卡接收到macvlan设备的数据后,总是将数据发送出去,即使是发往本设备上其他macvlan设备的数据包。这样在交换机设备上可以看到所有网络的流量。如果是本机的macvlan设备流量仍然是发往本机的macvlan设备,可能会被交换机的生成树协议阻止。需要交换机开启hairpin模式或者reflective relay模式,该模式在目前的交换机上未广泛支持,vepa模式的应用较少。
linux的网桥支持hairpin模式。
最常用,同一个物理设备上的不同macvlan设备间的通讯可以直接转发,不再需要经过外部的交换机。转发非常快速,macvlan设备相对固定,不需要生成树协议。
本质上是VEPA模式,但同一个物理设备上的macvlan设备之间无法直接通讯,不常用。
后来增加的模式,比较少用
vepa和passthru都会将不同macvlan接口之间的数据发送到交换机,然后发回,对性能的影响比较明显。
物理网卡收到包后,根据包的mac地址来判断这个包交给哪个虚拟接口。
以下实验为在virturalbox虚拟机下
1 | # 创建两个network namespace net1和net2 |
创建macvlan接口的格式为:ip link add link <PARENT> <NAME> type macvlan
,
1 | # 将mac1放入到net1 namespace中 |
1 | [root@localhost vagrant]# docker network create -d macvlan --subnet=10.0.2.100/24 --gateway=10.0.2.2 -o parent=enp0s3 mcv |
在编写shell的时候,经常会在目录之间进行切换,如果使用cd命令经常会切换错误,pushd和popd使用栈的方式来管理目录。
用于显示当前目录栈中的所有记录。
将目录加入到栈顶部,并将当前目录切换到该目录。若不加任何参数,该命令用于将栈顶的两个目录进行对调。
删除目录栈中的目录。若不加任何参数,则会首先删除目录栈顶的目录,并将当前目录切换到栈顶下面的目录。
命令格式:pushd [-N | +N] [-n]
+N
将第N个目录删除(从左边数起,数字从0开始)-N
将第N个目录删除(从右边数起,数字从0开始)-n
将目录出栈时,不切换目录1 | [root@localhost tmp]# mkdir /tmp/dir{1,2,3,4} |
docker可以通过命令docker image inspect ${image}
来查看image的详细信息,其中包含了所使用的底层文件系统及各层的信息。
docker container的存储结构分为了只读层、init层和可读写层。
只读层跟docker image的层次结构恰好对应,主要包含操作系统的文件、配置、目录等信息,不包含操作系统镜像。
init层在只读层和读写层中间,用来专门存放/etc/hosts /etc/resolv.conf等信息,这些文件往往需要在启动的时候写入一些指定值,但不期望docker commit
命令对其进行提交。
可读写层为容器在运行中可以进行写入的层。
采用了两层结构,lowerdir为镜像层,只读。upperdir为容器层。
每层都会在/var/run/docker/overlay创建一个文件夹,文件夹中为实际层的内容,文件采用硬链接的方式链接到真实层中的文件,每一层都包含该层该拥有的所有文件,而该文件的真实存储可能是采用硬链接的方式链接到上层中的真实文件,因此比较耗费inode。
创建一个容器时,会新增两个目录,一个为读写层,一个为初始层。初始层中保存了容器初始化时的环境信息,如hostname、hosts文件等。读写层用于记录容器的所有改动。
为了规避overlay消耗inode节点过多的问题,overlay2采用在每层中增加lower文件的方式来记录所有底层的信息,类似于链表的形式。
docker pull ubuntu
1 | [root@localhost runc]# docker pull ubuntu |
会在/var/run/docker/overlay2目录下创建如下文件:
1 | [root@localhost overlay2]# tree -L 2 |
l目录下为超链接,缩短后的目录,为了避免mount时超出页大小限制。
每一层中的diff文件夹包含实际内容。
每一层中都有一个link文件,内容为l目录中的超链接,超链接实际指向当前层目录中的diff文件夹。
除去最底层的目录外,其余每一层中包含一个lower文件,包含了该层的所有更底层名称和顺序,可以根据该文件构建出整个镜像的层次结构。
work目录用于OverlayFS内部使用。
最底层只有link文件,无lower文件,因此664ae13f1c21402385076025d68476eb8d1cc4be6c6a218b24bd55217ac62672为最底层。
以上五层为lower,只读。
当使用docker run -it ubuntu:latest /bin/bash
启动一个容器后,在overlay2目录下会多出两个文件夹。
1 | [root@localhost overlay2]# tree -L 1 0326c1da0af912a6ea5efda77b65b04e796993e0f111ed8f262c55b2716f1c08-init 0326c1da0af912a6ea5efda77b65b04e796993e0f111ed8f262c55b2716f1c08 l |
0326c1da0af912a6ea5efda77b65b04e796993e0f111ed8f262c55b2716f1c08-init
用于存放容器初始化时的信息,通过下面查看更直观。
1 | [root@localhost overlay2]# tree 0326c1da0af912a6ea5efda77b65b04e796993e0f111ed8f262c55b2716f1c08-init |
0326c1da0af912a6ea5efda77b65b04e796993e0f111ed8f262c55b2716f1c08
的直接底层为init层,更详细的目录结构如下。
1 | [root@localhost overlay2]# tree -L 2 0326c1da0af912a6ea5efda77b65b04e796993e0f111ed8f262c55b2716f1c08 |
merged文件夹中内容较多,为overlay2的直接挂载点,对容器的修改会反应到该目录中。例如在容器中增加/root/hello.txt文件,在merged目录下会增加root/hello.txt文件。
1 | [root@localhost overlay2]# mount | grep overlay2 |
本文绝大多数题目来源于网络,部分题目为原创。
1 | type student struct { |
每次遍历的时候stu变量为值拷贝,stu变量的地址未改变,即&stu未改变,遍历结束后stu指向stus中的最后一个元素。
使用reflect.TypeOf(str)
打印出的类型为main.student,如果使用stu.Age += 10
这样的语法是不会修改stus中的值的。
可修改为如下形式:
1 | for i, _ := range stus { |
1 | type foo struct { |
意在考察range遍历的时候是值拷贝,以及slice的内部数据结构,slice的数据结构如下:
1 | struct Slice |
执行append函数后会返回一个新的Slice对象,新的Slice对象跟旧Slice对象共用相同的数据存储,但是len的值并不相同。
该题目中,可以通过下面的方式来修改值:
1 | // range方式 |
1 | func find(s []foo, name string) []*foo { |
仍旧是考察range是值拷贝的用法,此处使用for i 循环即可
1 | func find(s []foo, name string) []*foo { |
1 | package main |
slice的函数传递为值拷贝方式,在函数m中对下标为0的元素的修改会直接修改原slice中的值,因为slice中的指针指向的地址是相同的。
append之后的slice虽然可能是在原数组上增加了元素,但原slice中的len字段并没有变化。
make([]int, 3, 6)虽然指定了slice的cap,但对于append没有影响,还是会在slice中最后一个元素的下一个位置增加新元素。
数组由于是值拷贝,对新数组的修改不会影响到原数组。
输出内容如下:
1 | [-1 2 3] |
该题目为我自己想出来的,非来自于互联网,意在考察对slice和append函数的理解。
1 | func f() { |
输出结果如下,在执行第二个append后,第一个append在内存中增加的元素4会被5覆盖掉。执行结果可以通过fmt.Println(s1, cap(s1), &s1[0])
的形式将第一个元素的内存地址打印出来查看。
1 | [0 0 4] |
1 | package main |
不会有任何输出
1 | type People struct{} |
输出
1 | showA |
有点出乎意料,可以举个反例,如果ShowA()方法会调用到Teacher类型的ShowB()方法,假设People和Teacher并不在同一个包中时,编译一定会出现错误。
Go中没有继承机制,只有组合机制。
1 | func main() { |
会间歇性触发异常,select会随机选择。
1 | package main |
不能编译过去,提示Stduent does not implement People (Speak method has pointer receiver)
,将Speak定义更改为func (stu Stduent) Speak(think string) (talk string)
即可编译通过。
main的调用方式更改为如下也可以编译通过var peo People = new(Stduent)
。
func (stu *Stduent) Speak(think string) (talk string)
是*Student
类型的方法,不是Stduent
类型的方法。
1 | package main |
一直输出9999.涉及到goroutine的切换时机,仅系统调用或者有函数调用的情况下才会切换goroutine,for循环情况下一直没有系统调用或函数切换发生,需要等到for循环结束后才会启动新的goroutine。
1 | package main |
打印BBBBBBB
。
1 | func main() { |
1 | type People struct { |
打印结果为people: {123}
1 | func funcMui(x,y int)(sum int,error){ |
函数返回值命名 在函数有多个返回值时,只要有一个返回值有指定命名,其他的也必须有命名。 如果返回值有有多个返回值必须加上括号; 如果只有一个返回值并且有命名也需要加上括号; 此处函数第一个返回值有sum名称,第二个为命名,所以错误。
1 | package main |
输出结果为: 4 1 3 2
return语句不是一个原子指令,分为两个阶段,执行return后面的表达式和返回表达式的结果。defer函数在返回表达式之前执行。
DeferFunc1在第一步执行表达式后t=1,执行defer后t=4,返回值为4
DeferFunc2在第一步执行表达式后t=1,执行defer后t=4,返回值为第一步表达式的结果1
DeferFunc3在第一步表达式为t=2,执行defer后t=3,返回值为t=3
DeferFunc4在第一步执行表达式后t=2,返回值为t=2
1 | package main |
结构体比较 进行结构体比较时候,只有相同类型的结构体才可以比较,结构体是否相同不但与属性类型个数有关,还与属性顺序相关。
还有一点需要注意的是结构体是相同的,但是结构体属性中有不可以比较的类型,如map,slice。 如果该结构属性都是可以比较的,那么就可以使用“==”进行比较操作。
1 | package main |
输出“non-empty interface”
使用两个 goroutine 交替打印序列,一个 goroutine 打印数字, 另外一个 goroutine 打印字母, 最终效果为: 12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728
1 | package main |
假设T类型的方法上接收器既有T类型的,又有T指针类型的,那么就不可以在不能寻址的T值上调用T接收器的方法。
请看代码,试问能正常编译通过吗?
1 | import ( |
能正常编译通过,并输出”poniter”
请接着看以下的代码,试问能编译通过?
1 | import ( |
不能编译通过。
“cannot call pointer method on Lili literal”
“cannot take the address of Lili literal”
其实在第一个代码示例中,main主函数中的“li”是一个变量,li的虽然是类型Lili,但是li是可以寻址的,&li的类型是Lili,因此可以调用Lili的方法。
1 | package main |
考察golang的runtime机制,goroutine的切换时机只有在有系统调用或者函数调用时才会发生,本例子中的for循环结束之前不会发生goroutine的切换,所以最终输出结果为5.
1 | package main |
编译不通过,仅*Student
实现了People接口,更改为var peo People = &Student{}
即可编译通过。
1 | package main |
编译失败,常量cl通常在预处理阶段会直接展开,无法取其地址。
1 | import "sync" |
Mutex对象不能被值拷贝,后续传递需要使用指针的形式
1 | func main() { |
本题意在考察string和slice的数据结构,string的数据结构如下:
case1的内存结构变化情况如下:
case2由于s1默认长度为0,直接使用s1[0]复制会出现panic错误。
通过使用Google的登陆二步验证(即Google Authenticator服务),我们在登陆时需要输入额外由手机客户端生成的一次性密码。大大提高登陆的安全性。
实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。
google实现了基于时间的TOTP算法(Time-based One-time Password),客户端实现包括了android和ios。
算法为公开算法,google没有提供服务端的实现,各个语言都有单独的实现。自己系统使用可以直接使用网上的代码。
linux下有libpam-google-authenticator模块,可以使用yum或者源码编译安装,github上有源码,编译出来的为so文件,可以加到sshd的配置文件中,用于给sshd提供二次认证机制。
客户端和服务端存在时间差的问题,google authenticator的超时时间为30s,服务端可以使用两个30s的时间来验证,包括当前和上一个30s。
曾经使用多说和网易云评论作为博客的评论系统,不幸都相继倒闭后,博客就一直没有评论系统。虽博客的访问量可以忽略不计,但本着折腾和好奇的原则,还是折腾一下gitment。
最新版本的next主题已经默认支持gitment,需要将next主题升级到最新版本。
我的hexo-theme-next使用单独的git项目进行管理,git地址为:https://github.com/kuring/hexo-theme-next。接下来需要将fork出来的git项目跟next的git项目进行同步。
在本地创建名字为upstream的remote,指向地址为:git remote add upstream https://github.com/theme-next/hexo-theme-next.git
拉取next项目到本地分支,本地的分支,执行git fetch upstream
将upsteam/master分支合并到master分支上
1 | git checkout master |
前往:https://github.com/settings/profile
Developer settings -> Register a new application
在界面中输入如下内容:
获取到Client ID和Client Secret.
创建新的github项目:https://github.com/kuring/gitment-comments
next主题的配置文件为theme/next/_config.yml,修改其中的gitment设置如下,
1 | # Gitment |
执行hexo clean && hexo g && hexo s
重新生成页面并在本地运行,可以看到gitment组件已经可以显示了,但是提示Error: Comments Not Initialized
错误,点击login,然后允许认证,即可消除该错误。
在界面上添加评论后,可以在github repo的issuse中看到,整个搭建完毕。
linux在内核中对数据包过滤和修改的模块为netfilter,netfilter模块本身并不对数据包进行过滤,只是允许将过滤数据包或修改数据包的函数hook到内核网络协议栈的适当位置。
iptables是用户态的工具,用于向netfilter中添加规则从而实现报文的过滤和修改等功能,工作在ip层。ebtables工作在数据链路层,用于处理以太网帧。
图中绿色代表iptables的表,可以看到有部分位于了数据链路层,之所以产生这种奇怪的架构,原因是bridge_nf模块,因为bridge工作在数据链路层,不一定会经过网络层,但仍然需要iptables的功能。详细信息可以在ebtables/iptables interaction on a Linux-based bridge中了解。
概念:tables -> chains -> rules
每个表都由一组内置的链,还可以添加用户自定义链,只是用户自定义链没有钩子可以触发,需要从其他链通过-j
即JUMP进行触发。
从chain的角度考虑数据包的流向:
当一个网络包进入一台机器的时候,首先拿下 MAC 头看看,是不是我的。如果是,则拿下 IP 头来。得到目标 IP 之后呢,就开始进行路由判断。在路由判断之前,这个节点我们称为 PREROUTING。如果发现 IP 是我的,包就应该是我的,就发给上面的传输层,这个节点叫作 INPUT。如果发现 IP 不是我的,就需要转发出去,这个节点称为 FORWARD。如果是我的,上层处理完毕完毕后,一般会返回一个处理结果,这个处理结果会发出去,这个节点称为 OUTPUT,无论是 FORWARD 还是 OUTPUT,都是路由判断之后发生的,最后一个节点是 POSTROUTING。
有了chain的概念后,为了便于chain中rule的管理,又引入了table的概念,用于存放相同功能的rule,不同功能的rule放到不同的table中。
包括:filter nat mangle raw
默认表,管理本机数据包的进出,用于实现包的过滤,对应内核模块iptables_filter
input:想要进入linux主机的包
output:linux主机要发送的包
forward:传递包到后端计算机,与nat table关联较多
管理后端主机进出,与linux主机没有关系,与linux后的主机有关
prerouting:进行路由判断之前的规则(dnat/redirect)
postrouting:路由判断之后执行的规则(snat/masquerade)
output:与发出去的包有关
较少使用,用于拆解报文,修改数据包,并重新封装。
raw表的主要作用是允许我们给某些特定的数据包打上标记。
包含了匹配条件和处理动作。
匹配条件包括:source ip、destination ip、source port、destination port
处理动作包括:
这三者之间的关系还是相当的绕。
chain中存放了rule,某些chain中注定不包含某些rule。例如prerouting链中的rule仅存在于nat raw mangle三张表中。
prerouting链中的规则存在的表:raw mangle nat
input链中的规则存在的表:mangle filter nat
forward链中的规则存在的表:mangle filter
output链中的规则存在的表:raw mangle filter nat
postrouting链中的规则存在的表:mangle nat
raw表中的规则可以被链使用:prerouting output
表的名字为小写,链的名字为大写
iptables 查询默认的表为filter,默认会列出表中所有链的规则
iptables -t filter -L
:从表的角度查询规则,用于查看filter表中的所有规则
iptables -L INPUT
: 从链的角度查询规则,用于查看INPUT链中的所有规则
iptables -vL INPUT
: 从链的角度查询规则,用于查看INPUT链中的所有规则,可查看更详细信息,包含了规则的匹配信息
iptables -nvL
:以精确数字显示
--dport
: 目的端口--sport
: 源端口--match-set
:匹配ipsetiptables -F INPUT
:清空filter表中的INPUT链中的所有规则。
iptables -D 链名 规则编号
,其中规则编号可以通过--line-number
查看到。iptables -F 链名 -t 表名
iptables -X 链名 -t 表名
开启trace功能
1 | # centos7 系统下有效,centos6下内核模块为ipt_LOG |
要开启icmp协议的追踪,执行如下的命令
1 | iptables -t raw -A OUTPUT -p icmp -m comment --comment "TRACE" -j TRACE |
可以通过如下的命令看到插入的iptabels规则:
1 | iptables -t raw -nvL --line-number |
追踪日志最终会在/var/log/message或者/var/log/kern下看到:
1 | Feb 6 11:22:04 c43k09006.cloud.k09.am17 kernel: TRACE: raw:PREROUTING:policy:3 IN=docker0 OUT= PHYSIN=bond0.9 MAC=02:42:30:fb:43:94:5c:c9:99:de:c4:8b:08:00 SRC=10.45.8.10 DST=10.45.4.99 LEN=84 TOS=0x00 PREC=0x00 TTL=62 ID=25550 DF PROTO=ICMP TYPE=0 CODE=0 ID=24191 SEQ=2 |
格式这块的含义如下:
“TRACE: tablename:chainname:type:rulenum “ where type can be “rule” for plain rule, “return” for implicit rule at the end of a user defined chain and “policy” for the policy of the built in chains.
环境清理,删除刚刚创建的规则即可,其中1为规则的编号:
1 | # 可以通过此来查询之前创建的规则编号 |
1 | # 清空filter表中的input链规则 |
接下在上面实验的基础上测试删除规则
1 | # 删除刚刚创建的规则2 |
在上面实验的基础上修改规则
1 | # 将规则动作从REJECT更改为REJECT |
防火墙的所有修改都是临时的,重启系统后会失效。iptables会读取/etc/sysconfig/iptables中的规则。
1 | # iptables-save命令仅会打印当前的规则,需要使用重定向当前规则到文件中 |
1 | # 可一次性插入两条规则 |
使用-p来指定协议类型,支持tcp udp icmp等,不指定时默认匹配所有协议
-i
来指定从某个网卡进入的流量,仅使用于PREROUTING INPUT FORWARD三条链。
-o
来指定从某个网络流出的流量,仅适用于FORWARD OUTPUT POSTROUTING三条链。
使用了扩展模块tcp udp,默认可以省略
--dport
来匹配报文的目的端口,使用时必须指定协议,即-p
选项。--sport
来匹配报文的源端口,使用时必须指定协议,即-p
选项。
端口可以指定范围,例如22:25表示22-25之间的所有端口,22,25表示22和25端口,还可以配合起来使用,比如22,80:88表示22和80-88之间的端口。
1 | # 可以指定目的端口的范围 |
iprange扩展模块可以指定一段连续的ip地址范围。
--src-range
和--dst-range
用来指定源地址和目的范围。
1 | [root@localhost vagrant]# iptables -t filter -I INPUT -m iprange --src-range 192.168.33.1-192.168.33.10 -j DROP |
匹配报文中包含的字符串
1 | # 匹配报文中包含XXOO的报文 |
time扩展用来根据时间段进行匹配
connlimit用来对ip的并发连接数进行限制
limit模块限制单位时间内进出包的数量
tcp扩展中可以使用--tcp-flags
可根据tcp flag进行匹配
state扩展可根据tcp的连接状态进行匹配
1 | # 创建自定义链 IN_WEB |
为了其他主机可访问docker registry,必须采用https协议。
1 | registry_data_dir=~/docker_registry/data |
停止registry镜像并删除的命令为:
1 | docker stop registry && docker rm -v registry |
下载最新的centos7镜像
1 | docker pull centos:7.3.1611 |
将centos7镜像增加tag
1 | docker tag centos:7.3.1611 127.0.0.1:15000/centos:7.3 |
docker push命令仅支持https协议,签名已经启动了自签名的https协议的registry,为了能够让docker能够信任registry,需要在/etc/docker/certs.d/目录下增加相应的crt文件,增加后的目录结构为/etc/docker/certs.d/103-17-184-lg-201-k08.yidian.com:5000/103-17-184-lg-201-k08.yidian.com.crt,添加完成后需要重启docker服务。
将image push到registry
1 | docker push 103-17-184-lg-201-k08.yidian.com:5000/centos:7.3 |
可以直接通过curl命令来访问api:curl --cacert 103-17-184-lg-201-k08.yidian.com.crt -v https://103-17-184-lg-201-k08.yidian.com:5000/v2