基于gogs及code-server建立项目基于浏览器IDE的在线开发
环境及工具
gogs
code-server:基于vscode的在线浏览器版
以下涉及的域名yourdomain.com
皆事先解析到某个服务器。
编辑gogs
增加webide入口
在templates\repo\header.tmpl
中添加,大概位于第84行1
2
3<a class="item" target="_blank" href="https://webide.yourdomain.com/?user={{.LoggedUser.Name}}&repo={{.Owner.Name}}/{{.Repository.Name}}">
<i class="octicon octicon-book"></i> WEB IDE
</a>
展示位置
重新编译gogs
1 | git clone https://github.com/gogs/gogs.git |
重复编译未知原因出现go-bindata
不存在的问题。
通过更改Dockerfile,在第3行下增加1
2RUN go get -u github.com/jteeuwen/go-bindata/... ; \
export PATH=$PATH:$GOPATH/bin
来解决。
使用docker运行命令进行构建1
docker build -t karoy/gogs:latest .
运行gogs
这里使用了traefik
作为前端访问端,更多的请参考本博客其他文章。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19mkdir -p /data/gogs/
docker volume create --driver local \
--opt device=/data/gogs/ \
--opt o=bind \
--opt type=none \
gogs_data
docker service create \
--name="gogs" \
--mount type=volume,source=gogs_data,destination=/data \
--network traefiknet \
--container-label traefik.backend="gogs" \
--container-label traefik.frontend.entryPoints="http,https" \
--container-label traefik.frontend.rule="Host: git.yourdomain.com" \
--container-label traefik.port='9000' \
--container-label traefik.protocol='http' \
--replicas 1 \
karoy/gogs:latesttraefiknet
为自定义的docker网络
实现及部署中转应用
实现应用核心代码
该应用主要在于自动创建docker容器及自动鉴权访问。使用php写的index.php1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
$action = $_GET['action']?:'default';
$id = substr(md5($_GET['user'].'-'.str_ireplace("/", "_", $_GET['repo'])), 5,10);
$workspace = "/data/workspace/".$id;
$workspace_cf = $workspace."/.qws";
switch($action){
case 'default':{
$domain = $id.'.webide.yourdomain.com';
ob_start();
system('curl -I -m 10 -o /dev/null -s -w %{http_code} '.$domain);
$code = ob_get_contents();
ob_end_clean();
echo '
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
<p style="margin:0 auto;width:500px;height:100%;text-align:center">
<a style="margin-top: 50%;display: block;" href="//'.$domain.'">等待自动跳转,若初次创建应用,请等待长一点。</a>
</p>
<script>
var t1 = "";
var f = function(){
var s=$.ajax({url:"/?action=getStatus&user='.$_GET['user'].'&repo='.$_GET['repo'].'",async:false});
console.log(s.responseText);
if(s.responseText!="fail"&&s.responseText!=""){
window.clearInterval(t1);
$.removeCookie("password");
$.cookie("password",s.responseText,{
expires:7,
path:"/",
domain:"webide.yourdomain.com",
secure:true
})
location.href="//'.$domain.'"
}
}
t1 = window.setInterval(f,1000);
</script>
';
if($code!=302){
ob_start();
mkdir($workspace,0777,true);
mkdir($workspace_cf,0777,true);
$create_service = 'docker service create \
--name '.$id.' \
--mount type=bind,source='.$workspace.',target=/root/project \
--network traefiknet \
--container-label traefik.backend="'.$id.'-ide" \
--container-label traefik.frontend.entryPoints="http,https" \
--container-label traefik.frontend.rule="Host: '.$domain.'" \
--container-label traefik.port="8443" \
--container-label traefik.protocol="http" \
--container-label traefik.frontend.redirect.entryPoint="https" \
--replicas 1 \
karoy/code-server:latest \
code-server \
--allow-http 2>&1';
file_put_contents("${workspace_cf}/create.sh",$create_service);
pclose(popen("/bin/sh -e ${workspace_cf}/create.sh &",'r'));
ob_end_clean();
}
break;
}
case "getStatus":{
ob_start();
$cmd = 'docker ps -a --format "table {{.ID}} {{.Names}}"|awk \'{print $2}\'|grep "'.$id.'"|awk \'{print $1}\'';
system($cmd);
$cantainerid = str_replace("\n","",ob_get_contents());
ob_end_clean();
if(file_exists("${workspace_cf}/${cantainerid}.ps")){
$password = file_get_contents("${workspace_cf}/${cantainerid}.ps");
if(strlen($password)<10){
unlink("${workspace_cf}/${cantainerid}.ps");
}else{
echo str_replace("\n","",$password);
}
}else{
sleep(2);
if(strlen($cantainerid)>10){
$cmd = "docker logs ${cantainerid} | grep Password | awk '{print $3}' 2>&1 | tee ${workspace_cf}/${cantainerid}.ps";
file_put_contents("${workspace_cf}/getpass.sh",$cmd);
pclose(popen("/bin/sh -e ${workspace_cf}/getpass.sh &",'r'));
}else{
echo "fail";
}
}
}
}
部署中转应用
将其部署到服务器,这里使用的自建的docker服务1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33mkdir -p /data/dev/webide
##自定义的seesion目录,你也可以不用,使用默认的,那么相关的都要调整。
mkdir -p /data/session
docker volume create --driver local \
--opt device=/data/session \
--opt o=bind \
--opt type=none \
apps_session
##用于存放工作空间数据
mkdir -p /data/workspace
docker service create \
--name app-webide \
--mount type=bind,source=/data/dev/webide,target=/var/www/html/src \
--mount type=bind,source=/data/workspace,target=/data/workspace \
--mount type=bind,source=/usr/bin/docker,target=/usr/bin/docker \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
--mount type=volume,source=apps_session,destination=/data/session \
--config source=webide.conf,target=/etc/apache2/sites-enabled/app.conf \
--network traefiknet \
--container-label traefik.backend="app-webide" \
--container-label traefik.frontend.entryPoints="http,https" \
--container-label traefik.frontend.rule="Host: webide.yourdomain.com" \
--container-label traefik.frontend.redirect.entryPoint="https" \
--container-label traefik.port='80' \
--container-label traefik.protocol='http' \
--replicas 1 \
karoy/php-7-apache-debian:latest
##注意/var/run/docker.sock权限,否则php执行命令将失败且无任何信息,主机可执行
chmod 777 /var/run/docker.sock
主要在于访问webide.yourdomain.com
能够访问到index.php
文件,你可以使用其他的方式部署文件
php-7-apache-debian示例
Dockerfile1
2
3
4FROM php:7.2-apache
RUN ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/rewrite.load
RUN ln -s /etc/apache2/mods-available/headers.load /etc/apache2/mods-enabled/headers.load
RUN cp -r -f /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone
构建命令1
docker build -t karoy/php-7-apache-debian:latest .
webide.conf内容示例
webide.conf内容,可以用作docker config
<VirtualHost *:80>
ServerName webide.yourdomain.com
ServerAdmin webmaster@yourdomain
DocumentRoot /var/www/html/src
DirectoryIndex index.html index.php
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
你也可以直接使用主机路径挂载1
2
3
4#将
--config source=webide.conf,target=/etc/apache2/sites-enabled/app.conf \
#替换为
--mount type=bind,source=/data/webide.conf,target=/etc/apache2/sites-enabled/app.conf \
构建code-server应用
Dockerfile
1 | FROM codercom/code-server |
build karoy/code-server:latest
1 | docker build -t karoy/code-server:latest . |
该镜像在中转应用创建项目空间时将使用到.
基本流程
从git.yourdomain.com
访问项目WEB IDE
链接,
结构为https://webide.yourdomain.com/?user=name&repo=org/repo
,
将重定向到webide.yourdomain.com
域,并执行index.php
中的逻辑:判断是否已存在对应应用,
有则跳转实际项目域名,没有则先尝试创建项目容器,再跳转到创建的项目对应的域名,
结构为*.webide.yourdomain.com
,如acbdfn5156.webide.yourdomain.com
,
因为必须自动取得验证信息并自动授权cookie,所以中转域名和实际项目域名在同一个父域下。
结束
该文章操作的并未处理外网访问的安全问题,建议在内网使用,因为当访问https://webide.yourdomain.com/?user=name&repo=org/repo
类型链接的时候,将自动提取验证信息授权,也意味着将自动进入项目空间。
但是如果使用项目空间对应的域名是需要输入一串密码字符串才能进去的如acbdfn5156.webide.yourdomain.com
(在未进去过的情况下,使用了cookie)。