0%

源起

服务器已经跑起来很久了,但是有需求要限制客户端使用我们服务,一同事说到了SSL双向认证的问题,之前没有玩过,所以查了一些资料。后来想了下,以前其实也碰到过,就是银行和支付宝的证书认证嘛,通过证书对帐号进行认证。还有一些登录的时候不填帐号,只填密码,其实就是在客户证书来当用户名嘛。这个在Nginx双向认证的时候就可以解决,Nginx收到证书信息,然后反向代理到后面服务的时候,可以在头信息里面加一点东西,就可以解决客户登录的时候的帐号填写问题了。当然还可以又填用户名,又使用证书,然后核对证书的用户名和用户填的用户名是否一致,不致的情况下,可以直接吊销证书等等,可以做的事情就很多了。简单记下之前配置的过程吧。

准备工作

可以编辑openssl.cnf,设置一部分默认值,比如国别、组织、机构啊什么的。CentOS在/etc/pki/tls目录下面,Gentoo在/etc/ssl目录下面。因为服务是CentOS,下面默认是在CentOS下操作。如果非root用户操作,做部分修改就行。

1
2
3
4
5
cd /etc/pki/CA
# 好像是证书db
touch index.txt
# 证书序号,每生成一个证书自增1
echo '00' > serial

生成CA密钥和证书

自已充当CA认证机构(一般为第三方权威机构),需要生成一个CA的密钥和证书,用于对其它用户的证书请求进行认证。

1
2
openssl genrsa -des3 -out ca-key.pem 4096
openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem

生成自签名服务器证书(非必需)

如果可以,最好去第三方权威机构申请证书(非WoSign),或用letsencrypt的证书。实在不行,就像我们这样,自已生成证书算球的,就可以走下面的流程了。

1
2
3
4
5
6
7
openssl genrsa -des3 -out server-key.pem 1024
#生成无密码的私钥,这样nginx加载的时候不会提示输入密码的地方卡住。或者直接上面那句不加-des3选项
openssl rsa -in server-key.pem -out server-key.key
#生成csr签名申请,用于CA对证书进行签名。重要:其中提示输入的CN common name必须填服务器的域名、泛域名或IP地址。
openssl req -new -key server-key.pem -out server-req.csr
#用CA对服务器证书进行签名
openssl ca -policy policy_anything -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -in server-req.csr -out server-cert.pem

生成客户证书

与生成服务器证书基本一致,对于CA而言,是一样的。申请过来,进行认证。

1
2
3
4
5
6
7
8
openssl genrsa -des3 -out client-key.pem 1024
#生成csr签名申请,用于CA对证书进行签名。
openssl req -new -key client-key.pem -out client-req.csr
#用CA对证书进行签名
openssl ca -policy policy_anything -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -in client-req.csr -out client-cert.pem
#客户端证书转换为PKCS #12格式,客户端浏览器导入即可
#openssl pkcs12 -export -out client.pfx -inkey client-key.pem -in client-cert.pem -certfile ca-cert.pem
openssl pkcs12 -export -clcerts -in client-cert.pem -inkey client-key.pem -out client.p12

Nginx配置

/etc/nginx/conf.d/default.conf
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
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name test.test;

ssl on;
# 可使用其它机构颁发的证书
ssl_certificate /etc/nginx/ssl/server-cert.pem;
# 服务器私钥
ssl_certificate_key /etc/nginx/ssl/server-key.key;

# 客户端证书认证
ssl_client_certificate /etc/nginx/ssl/ca-cert.pem;
# optional表示无证书可访问,用于无证书情况下的提示信息。
ssl_verify_client optional;

ssl_session_cache shared:SSL:5m;
ssl_session_timeout 1h;

ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;

client_max_body_size 10M;

# 客户端证书认证失败显示400信息
if ($ssl_client_verify != "SUCCESS") {
return 400;
}
......

参考

问题

现在项目中有个单据有三个表关系,主表、明细表、次明细表,大概关系如下面代码所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Main{
private String mtId;
private List<Sub> subs;
......
}

class Sub{
private String mtId;
private List<Bat> bats;
......
}

class Bat{
private String mtId;
......
}

之前有人最笨最笨的办法就是从数据库拉三次出来,然后再组成整个数据结构。其实本来MyBatis里面就有Collection,用起来很方便。然后我就用下面的Mapper.xml来改试了一下。

尝试

MyBatis官网已经讲了Collection的使用,很详细了。然后就自己试下看如何更爽的解决之前那个问题。MyBatis已经把配置荐mapUnderscoreToCamelCase设置成true了,这样就可以少写Entity的映射关系了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<select id="getWholeBill" resultMap="EntirBillResultMap">
SELECT
m.mt_id AS mt_id, s.mt_id AS s_mt_id, b.mt_id AS s_b_mt_it
FROM
main m
INNER JOIN
sub s ON (s.pid = m.id)
INNER JOIN
bat b ON (m.pid = s.id)
WHERE m.mt_id = #{id}
</select>

<resultMap id="EntirBillResultMap" type="Main" autoMapping="true">
<collection property="subs" columnPrefix="s_" ofType="Sub" autoMapping="true">
<collection property="bats" columnPrefix="b_" ofType="Bat" autoMapping="true">
</collection>
</collection>
</resultMap>

本来以为这样可以很好地解决我的问题,但是但是,测试的时候发现,主表和明细表好像没有group by的状态。

修改

碰到这个问题,其实查下源码可能就好解决了。但是其实我一直没有碰MyBatis的源码,所以可能查起来会很头痛。网上稍微搜了下,没有找到答案。应该是映射关系写得有问题吧。尝试了几次,觉得就是没有group by嘛,但是如果告诉MyBatis去group by哪个字段呢?好像平时写得最多的那个id标签没有用哦,太懒了,把这个都省掉。果然加上去就解决了,嗯嗯,很爽。

1
2
3
4
5
6
7
8
9
<resultMap id="EntirBillResultMap" type="Main" autoMapping="true">
<id column="mt_id" property="mtId"/>
<collection property="subs" columnPrefix="s_" ofType="Sub" autoMapping="true">
<id column="mt_id" property="mtId"/>
<collection property="bats" columnPrefix="b_" ofType="Bat" autoMapping="true">
<id column="mt_id" property="mtId"/>
</collection>
</collection>
</resultMap>

CI是个好东西

本身现在所在的团队使用Git的人不多,所以往GitLab上提交代码的时候往往有一些不合格的代码,后来发现GitLab CI可以完美地解决这个问题。

需求及实现

  1. service目录中不包含platformService目录。
  2. 所有项目中不能包含.properties文件,以.properties.sample文件提供给大家,然后.gitignore目录中也忽略.properties文件
  3. config目录中不包含index.js文件,以index.js.sample文件提供给大家
  4. 所有vue代码中不能包含调试用代码”debugger|console.log”
  5. Java代码能编译通过

只有通过以上5点的代码才能合并到主仓库,避免污染代码库。其实原因很简单,看下面就清楚。

.gitlab-ci.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
image: 10.17.xx.xx/maven:3-jdk-8-alpine

variables:
MAVEN_OPTS: "-Dmaven.repo.local=/root/.m2/repository -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
MAVEN_CLI_OPTS: "--batch-mode --errors --show-version -Dmaven.test.skip=true -s /root/.m2/settings.xml"

test:validate:
stage: test
script:
- test $(find service -type f -path "*/platformService/*" | wc -l) -eq 0
- test $(find service -name "*.properties" -path "*/properties/*" | wc -l) -eq 0
- test $(find web -name "index.js" -path "*/config/*" | wc -l) -eq 0
- test $(find web -name "*.vue" -type f -exec grep -E 'debugger|console\.log' {} \; | wc -l) -eq 0
tags:
- common

test:compile:
stage: test
before_script:
- wget http://10.17.xx.xx/download/settings.xml -O /root/.m2/settings.xml
script:
- mvn $MAVEN_CLI_OPTS -f service/spdHERPService/pom.xml clean compile
tags:
- common

Docker设置

其中Runner可能会用到Docker私服,现在我们是用Nexus搭建(IP:10.17.xx.xx)的,Docker需要登录才能使用。下面是Docker的配置,外面配置了两个代理,这样下载外面的镜像速度就很快了。

daemon.json
1
2
3
4
5
6
7
{
"registry-mirrors": ["https://registry.docker-cn.com", "https://docker.mirrors.ustc.edu.cn"],
"insecure-registries": ["10.17.xx.xx"],
"disable-legacy-registry": true,
"log-driver": "json-file",
"log-opts": {"max-size": "5m", "max-file": "3"}
}

待Docker配置好后就需要配置Runner了。首先获取Docker登录私服的登录信息,官网有两个方法。一个是
docker login registry.example.com --username my_username --password my_password,然后查看~/.docker/config.json;另外一个是echo -n "my_username:my_password" | base64。然后就是把登录信息加到Runner的配置文件中去。万事OK

config.toml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
concurrent = 10
check_interval = 0

[[runners]]
name = "122-runner"
url = "http://10.17.xx.xx/gitlab/"
token = "xxxtoken"
executor = "docker"
environment = ["DOCKER_AUTH_CONFIG={ \"auths\": { \"10.17.xx.xx\": { \"auth\": \"authtoken\" } } }"]
[runners.docker]
tls_verify = false
image = "alpine:latest"
privileged = false
disable_cache = false
volumes = ["/cache", "/root/.m2"]
shm_size = 0
[runners.cache]

需求

之前团队从 SVN 已经转到 Git 管理代码了,但是但是大家还是非常不熟悉怎么管理,应该也是我没有把大家带入正轨吧,关于 GitFlow 没有好好给大家讲清楚,木办法。用Gogs 管理感觉不是太好,代码多了,团队项目多之后管理不是太方便。于是把目标转向了 Gitlab。但是发现服务器直接装 Gitlab 很不爽,要装一大垞东东。那就转为 Docker 吧,经过一些尝试,一段时间的运行非常不错,加上 Gitlab Runner 测试提交文件是否有问题。也可以直接用 Runner 来打包发布,但是暂时没用它,现在还是用 Jenkins 来进行发布。也比较好用了,大家代码提交合并后,一键发布代码。 :)

其中外网 IP 的 1111 端口映射到服务器的 5622 端口,但 5622 端口已用 nginx 反射代理了 nexus,所以 docker gitlab 在主要要换一个端口,暂时用了 18080。

安装 Gitlab

把 Docker 安装好,然后设置好代理,要不然的话,下载镜像实在是太太太太慢了。我用了两个代理,一个是官方的天朝代理,一个是中科大的代理。

1
2
3
4
5
6
7
{
"registry-mirrors": ["https://registry.docker-cn.com", "https://docker.mirrors.ustc.edu.cn"],
"insecure-registries": ["x.x.x.x:8088"],
"disable-legacy-registry": true,
"log-driver": "json-file",
"log-opts": { "max-size": "5m", "max-file": "3" }
}

然后就是开始拖镜像,运行容器了。再稍微进行一点配置就 OK。

前期准备工作

  • Host 有 git 用户
  • 生成 Host git 用户与 docker git 用户通信用的 ssh 密钥与公钥, ssh-keygen。公钥保存在/opt/gitlab/authorized_keys_proxy,密钥保存在/opt/gitlab/id_rsa
  • 找一份 sshd 配置文件拷贝一份至/opt/gitlab/sshd_config,修改一处AuthorizedKeysFile,让 Host git 用户用密钥登录至 docker gitlab。参考处
1
2
3
...
AuthorizedKeysFile %h/.ssh/authorized_keys /gitlab-data/ssh/authorized_keys ****重要****
...
  • 查看 git 用户 user id 和 group id,生成一份passwdgroup文件给 docker gitlab 使用,文件见文末,从 gitlab 中提取的。我服务器上分别是 1006 和 1008。为了保证 Host 和 docker gitlab 中的数据文件不会因为权限问题而无法使用。主要是 gitlab 中的用户 SSH Key 文件与 Host git 用户共享。
  • 在 Host 写个 bash 脚本,用于转发 Host 22 端口接收到 git 用户时转到 docker gitlab 去。
1
2
3
su - git
mkdir -p /opt/gitlab/data/.ssh
ln -sf /opt/gitlab/data/.ssh /home/git/.ssh
  • gitlab 用户用 SSH Key 进行 git 操作时,先连接到 Host,读取/home/git/.ssh/authorized_keys信息并验证,其中authorized_keys第一行告诉用户强制运行一条命令/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell,正好用此命令转发 git 命令至 docker gitlab。这点与gogs中的配置类似。
/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell
1
2
3
#!/bin/sh

ssh -i /opt/gitlab/id_rsa -p 12222 -o StrictHostKeyChecking=no git@127.0.0.1 "SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@"

运行 gitlab

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker pull gitlab/gitlab-ce:latest # 拉镜像文件
docker run -d -e -p 18080:5622 -p 12222:22 \
--name gitlab --restart always \ # restart always保证Docker服务启动时gitlab容器运行
-v /opt/gitlab/config:/etc/gitlab \ # 配置文件保存到Host,也可以用volume
-v /opt/gitlab/logs:/var/log/gitlab \ # 日志
-v /opt/gitlab/data:/var/opt/gitlab \ # 数据
-v /opt/gitlab/sshd_config:/etc/ssh/sshd_config:ro \ # 配置Host用密钥与docker gitlab通信
-v /opt/gitlab/authorized_keys_proxy:/gitlab-data/ssh/authorized_keys:ro \ # Host git用户密钥
-v /opt/gitlab/passwd:/etc/passwd:ro \ # git用户信息
-v /opt/gitlab/group:/etc/group:ro \ # git用户group信息
-v /usr/share/zoneinfo/Asia/Shanghai:/etc/timezone:ro \ # 时区
--ulimit sigpending=62793 --ulimit nproc=131072 \ # 优化参数
--ulimit nofile=60000 --ulimit core=0 \
gitlab/gitlab-ce:latest

配置 gitlab

docker ps查看 gitlab 容器运行成功后,停止容器运行,对 gitlab 进行配置。我的配置项如下。详细各配置项可见 gitlab.rb 的注释说明,或参照gitlab 官网说明

/opt/gitlab/config/gitlab.rb
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
external_url 'http://10.17.65.22:5622/gitlab'
gitlab_rails['gitlab_ssh_host'] = '10.17.65.22'
gitlab_rails['time_zone'] = 'Asia/Shanghai'
gitlab_rails['gitlab_email_enabled'] = true
gitlab_rails['gitlab_email_from'] = 'admin@guoliangwu.com'
gitlab_rails['gitlab_email_display_name'] = 'Admin'
gitlab_rails['gitlab_email_reply_to'] = 'no_reply@guoliangwu.com'
gitlab_rails['gitlab_username_changing_enabled'] = false
gitlab_rails['gitlab_default_theme'] = 4
gitlab_rails['gitlab_default_projects_features_snippets'] = false
gitlab_rails['gitlab_default_projects_features_builds'] = false
gitlab_rails['gitlab_default_projects_features_container_registry'] = false
gitlab_rails['incoming_email_enabled'] = false
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.guoliangwu.com"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "admin@guoliangwu.com"
gitlab_rails['smtp_password'] = "xxxx"
gitlab_rails['smtp_domain'] = "guoliangwu.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = true
gitlab_rails['registry_enabled'] = false
unicorn['worker_processes'] = 4
sidekiq['concurrency'] = 15
postgresql['shared_buffers'] = "2GB"
prometheus['enable'] = false

end

至此,Gitlab 应该能欢快地跑起来了。后面我在其它服务器配了 gitlab runner 来检查上传的代码是否有问题,不知道有没有时间深入了解 runner,先把.gitlab-ci.yml传上来,以备以后使用吧。PS:因为以后打包发布什么的都要用 maven,就用了 maven 镜像。

.gitlab.yml
1
2
3
4
5
6
7
8
9
10
image: maven:latest

test:
stage: test
script:
- test $(find shanghai/service -name "*.properties" -path "*/properties/*" | wc -l) -eq 0
- test $(find shanghai/web -name "index.js" -path "*/config/*" | wc -l) -eq 0
- test $(find shanghai/web -name "*.vue" -type f -exec grep -E 'debugger|console\.log' {} \; | wc -l) -eq 0
tags:
- common

passwd

/etc/gitlab/passwd
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
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
sshd:x:105:65534::/var/run/sshd:/usr/sbin/nologin
git:x:1006:1008::/var/opt/gitlab:/bin/sh ****注意****
gitlab-www:x:999:999::/var/opt/gitlab/nginx:/bin/false
gitlab-redis:x:997:997::/var/opt/gitlab/redis:/bin/false
gitlab-psql:x:996:996::/var/opt/gitlab/postgresql:/bin/sh
mattermost:x:994:994::/var/opt/gitlab/mattermost:/bin/sh
registry:x:993:993::/var/opt/gitlab/registry:/bin/sh
gitlab-prometheus:x:992:992::/var/opt/gitlab/prometheus:/bin/sh
gitlab-consul:x:991:991::/var/opt/gitlab/consul:/bin/sh

group

/opt/gitlab/group
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:
tty:x:5:
disk:x:6:
lp:x:7:
mail:x:8:
news:x:9:
uucp:x:10:
man:x:12:
proxy:x:13:
kmem:x:15:
dialout:x:20:
fax:x:21:
voice:x:22:
cdrom:x:24:
floppy:x:25:
tape:x:26:
sudo:x:27:
audio:x:29:
dip:x:30:
www-data:x:33:
backup:x:34:
operator:x:37:
list:x:38:
irc:x:39:
src:x:40:
gnats:x:41:
shadow:x:42:
utmp:x:43:
video:x:44:
sasl:x:45:
plugdev:x:46:
staff:x:50:
games:x:60:
users:x:100:
nogroup:x:65534:
systemd-journal:x:101:
systemd-timesync:x:102:
systemd-network:x:103:
systemd-resolve:x:104:
systemd-bus-proxy:x:105:
ssh:x:106:
git:x:1008: ****注意****
gitlab-www:x:999:
gitlab-redis:x:997:
gitlab-psql:x:996:
mattermost:x:994:
registry:x:993:
gitlab-prometheus:x:99

问题

之前团队一直用的Subversion来管理代码的,开发的时候还OK,但是到了上线后,问题就开始出现,往往发布了一个版本后,代码又有一部分提交,同时可能引进一些新的Bug,再发包的时候,又有新的Bug,很不爽。本身SVN的分支管理不好用,我就强烈推荐Git,之前也一直在用,直接在服务器上开SSH端口就OK,但是权限管理又是一个问题啊,又找了一些Git权限管理的东东,但都太理想。后来想是不是有Github一样的东东,后来就发现了Gitlab,但是这货太耗资源,而且搭建起来也不容易,其他人去维护它就很困难。后来一顿放狗搜,找到了一个比较好的替代Gogs,上周末两天基本就在搞这货,使用起来也是很不错的。花时间的部分主要是与主机共享22端口的问题。

现在服务器那边的环境是,服务器位于防火墙后面,防火墙只开了有限供外网使用的端口,所以Gogs必须在某个共用端口的子目录下进行访问,比如domain.com/git/。防火墙8888端口映射到服务器4444端口,然后服务器4444端口再映射到Gogs Container的3000端口。

Docker Gogs安装

其实用Docker来安装Gogs很简单,直接参考官方的文档主OK了。

1
2
3
4
5
6
7
8
9
10
11
12
13
# Pull image from Docker Hub.
$ docker pull gogs/gogs

# Create local directory for volume.
$ mkdir -p /var/gogs

# Use `docker run` for the first time.
$ docker run --name=gogs -p 10022:22 -p 10080:3000 -v /var/gogs:/data gogs/gogs
# 下面是我的配置
$ docker run --name gogs -p 3000:3000 -p 10022:22 --link mysql:db -v /opt/my_gogs:/data -e "PUID=1002" -e "PGID=1002" -d gogs/gogs

# Use `docker start` if you have stopped it.
$ docker start gogs

其中我运行Gogs的时候加了一些东西。将前端端口映射到Host的3000端口,将Container的SSH端口22映射到Host的1022端口,然后将另一个mysql的Container链接到Gogs,这样可以直接访问MySQL,然后就是将Gogs的配置文件和其它工作文件链接到Host的gogs目录下,然后就是设置Gogs Containerm里面git用户的uid和gid。如果不设置uid和gid的话,container默认的是1000,一般情况下都不是我们想要的结果。一般是在Host新建一个git的用户,将container的uid和gid设置成Host上git用户对应的值,比如我的就是1002和1002。

Gogsl配置

Docker Gogs运行起来后,就可以直接浏览器连3000端口进行配置,配置界面很明了,一看就OK。但此时我用的MySQL也是用的Docker,因为之前用了--link mysql:db,表示我有一个container的名称为mysql,映射到gogs的container的名称为db,这样我可以直接用db:3306就可以连上了。在配置界面点确定前,需要在MySQL里新建一个仓,具体代码可以参考官方脚本

等界面配置好后就再对Gogs进行详细的修改,具体可以参考官方文档

app.ini
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
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
APP_NAME = Gogs
RUN_USER = git
RUN_MODE = prod

[database]
DB_TYPE = mysql
HOST = db:3306
NAME = gogs
USER = root
PASSWD = mysql_password
SSL_MODE = disable
PATH = data/gogs.db

[repository]
ROOT = /data/git/gogs-repositories
FORCE_PRIVATE = true
DISABLE_HTTP_GIT = true

[repository.upload]
ENABLED = false

[server]
DOMAIN = yourdomain.com
HTTP_PORT = 3000
ROOT_URL = http://localhost:3000/git/
DISABLE_SSH = false
SSH_PORT = 22
START_SSH_SERVER = false
OFFLINE_MODE = false

[mailer]
ENABLED = true
DISABLE_HELO = true
HOST = smtp.xxx.com:587
SUBJECT = GIT
FROM = "GIT" <xxx@xxx.com>
USER = xxx@xxx.com
PASSWD = xxx
SKIP_VERIFY = false
USE_PLAIN_TEXT = false

[service]
REGISTER_EMAIL_CONFIRM = true
ENABLE_NOTIFY_MAIL = true
DISABLE_REGISTRATION = true
ENABLE_CAPTCHA = true
REQUIRE_SIGNIN_VIEW = true

[picture]
DISABLE_GRAVATAR = true
ENABLE_FEDERATED_AVATAR = true

[session]
PROVIDER = file

[log]
MODE = file
LEVEL = Info
ROOT_PATH = /app/gogs/log

[security]
INSTALL_LOCK = true
SECRET_KEY = qHtSV2KhCcMA0Xl

[other]
SHOW_FOOTER_BRANDING = false
SHOW_FOOTER_VERSION = false
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false

现在可以在浏览http://localhost:3000/git/来访问页面了。

Nginx反向代理

对于Nginx反向代理官方也给出了具体配置,还包括Apache和Lighttpd。其中Nginx中很重要的一点就是必须**要最后的反斜框[/]**。

1
2
3
4
5
6
location /git/ {
proxy_pass http://localhost:3000/;
}
location = /git {
proxy_pass http://localhost:3000/;
}

官方文档只提到了上面的一条,后面一条是根据我的实情情况加的。我发现登录的时候,点一次登录好长时间没有反应,过段时间后,返回的地址却是domain.com:4444/git/,很无语,不知道为啥跳到4444端口呢。后来发现登录跳转的路径是domain.com/git,没有最后的一个反斜框,Nginx无法知道怎么代理,这块搞了我快一两个小时,一直没有查出来原因。后来加了下面一条就解决了。

与Host共享22端口的问题

刚开始放狗搜了好久,都没有好的解决方案,有的提到sshd_config里面使用ForceCommand,看着比较靠谱,但好像也不成功。后来发现官方文档推荐一篇不错的文章。看守实操了一遍,大致原理是Host和Container用同一个uid的用户(假定为git),这时候连接到Host的git用户再转到container的10022端口,并运行特定的命令来进行git操作。

新建git用户

1
2
3
4
5
6
7
8
9
$ sudo useradd -s /bin/bash -d /opt/my_gogs -m git
$ id git
uid=1002(git) gid=1002(git) groups=1002(git)
$ su - git
$ cd ~
$ ssh-key -t rsa
$ cd ~/.ssh
$ cat id_rsa.pub >> authorized_keys
$ chmod 600 authorized_keys

这里我新建的git用户的uid和gid是1002,与之前运行gogs container需要对应。同时为了安全起见,将authorized_keys前加一些配置。

1
no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty rsa...

字面意思就很好理解了。因为之前Docker运行Gogs时已经设置-v /opt/my_gogs:/data -e "PUID=1002" -e "PGID=1002", 这样在Host和Container的git用户主目录是同一个,uid和gid也一样,这样Host的git用户直接ssh -p 10022 127.0.0.1是可以直接连接到Container的。

配置git用户登录问题

经过上面一步的设置,在外网就可以直接用git用户的密钥进行登录了,但是因为安全设置,没有tty等。然后就是设置像github那样,用户自己在web端导入自己的公钥,就可以用密钥拉取、推送代码。新建/app/gogs/gogs,脚本如下。

1
2
3
4
5
6
7
mkdir -p /app/gogs/
cat >/app/gogs/gogs <<'END'
#!/bin/sh
ssh -p 10022 -o StrictHostKeyChecking=no git@127.0.0.1 \
"SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@"
END
chmod 755 /app/gogs/gogs

对共享22端口不熟悉的话,可以认真看看这篇文件。SSH真是神器。 :) Happy coding…

正式全职做开发已经半年了,两台服务器作开发,Oracle/MySQL/Nexus/Jenkins/Redis/ActiveMQ,一大堆的东东,还好有一些Linux的经验(上次CentOS被人挖矿,下次再说如何抓别人的),搭建这些东西都是比较容易的,当然不包括调优。就算自己搭建这些东西不难,但是真的比较费时间。后来有一个想法就是用Docker,今天搞了大半天终于搞定了。记下来,便于以后自己翻查吧。

后台架构

前台用的是Vue来开发,后台Nginx处理静态文件,反向代理请求到Tomcat,Tomcat再到Redis/ActiveMQ/MySQL。

所以后台需要Nginx, Tomcat, Redis, MySQL这些软件。根据调用关系,先把底层的服务开启。Docker安装就看官方文档吧。

Redis服务

首先将Redis的镜像(image)给拉到本地,然后开动。

1
2
3
4
5
6
7
docker pull redis
docker run --name redis -p 6379:6379 --privileged -v `pwd`/redis:/data -v `pwd`/redis/redis.conf:/etc/redis/redis.conf -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -d redis redis-server /etc/redis/redis.conf
-----
docker exec -it redis /bin/bash
echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf
echo "net.core.somaxconn = 65536" >> /etc/sysctl.conf
sysctl -p

docker run里面的参数含义分别是:

  • name:给container取名字,方便以后操作,要不得填container ID,很不友好。
  • p:官方解释Publish a container's port(s) to the host,我的理解就是相当于一个端口映射吧。将主机的端口映射到容器的端口。
  • privileged:官方解释Give extended privileges to this container,主要是要修改一些container参数,要不会提示一些Warning。
  • v:Bind mount a volume,将host文件或目录挂载到container,这样可以直接在host修改一些配置文件或container产生的一些文件存在host。
  • d:Run container in background and print container ID,后台运行。

然后redis需要远程访问,就要修改redis.conf,把bind给注释掉,protedted-mod改为no

MySQL服务

1
docker run --name mysql -e MYSQL_ROOT_PASSWORD=your-mysql-root-password -p 3306:3306 -v `pwd`/mysql:/var/lib/mysql -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -d mysql/mysql-server

注意将其中的your-mysql-root-password改为自己的密码。

这时MySQL的root不能远程访问,放狗搜的话一大堆。

1
2
docker exec -it mysql /bin/bash
mysql -u root -p
1
2
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'your-mysql-root-password' WITH GRANT OPTION;
FLUSH RIVILEGES

Tomcat服务

1
docker run --name tomcat -p 8080:8080 -v `pwd`/tomcat/webapps:/usr/local/tomcat/webapps -v `pwd`/tomcat/conf/tomcat-users.xml:/usr/local/tomcat/conf/tomcat-users.xml -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone --link redis:redis --link mysql:mysql -d andreptb/tomcat

--link主要是一个container连接到另一个contaner,好像是在container的/etc/hosts加上到另一个容器的地上,然后environment里加一些环境变量。这个只是我的猜测,等过段里子好好看看Docker的文件后再深入了解下。

这样在tomcat的应用中可以把jdbc.url里的host直接填mysql,redis的host填redis。比如jdbc.url=jdbc:mysql://mysql:3306/test

Nginx服务

1
docker run --name nginx -p 80:80 -v `pwd`/nginx/html:/usr/share/nginx/html -v `pwd`/nginx/nginx.conf:/etc/nginx/nginx.conf -v `pwd`/nginx/conf.d:/etc/nginx/conf.d -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone --link tomcat:tomcat -d nginx
/etc/nginx/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
#...
http {
upstream backend {
server tomcat:8080;
}

fastcgi_intercept_errors on;
proxy_intercept_errors on;

include /etc/nginx/conf.d/*.conf;
#...
}
/etc/nginx/conf.d/default.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server {
#...
location /service/ {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Remote-Addr $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location ^~ /pic/ {
root /opt/web;
index index.html;

location ~* \.(jpe?g|png|bmp|pdf)$ {
try_files $uri $uri/ /pic/default.png;
}
}
#...
}

嗯,好久好久没有写东西了。下次再讲讲jenkins吧,这个东东解放了好多时间,现在项目中我只是让它自己打包,打包出错时发送邮件。部署的话还没有去实现,主要是现阶段的代码问题太多了。现在还在思考一个问题,是否可以将Docker用于生产环境,然后学习下自己制作Dockerfile,这样我就可以把上面所有的服务打成一个Dockerfile,给别人一个Dockerfile就可以直接用来开发,完全不用自己再去搭环境了。

MyBatis开始写基础的SQL蛮麻烦的,放Google搜了搜,发现有个不错的插件MyBatis-Plugin,可以自动生成这些CRUD的基础语句,OK就用它了。至于如何激活,呵呵。。

MyBatis-Generator配置

直接在resources用插件新建文件mybatis-generator-config.xml,然后编辑其中注释中带!!!!的地方,主要有以下几个地方。

mybatis-generator-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
<!-- !!!! Driver Class Path !!!! -->

<!-- !!!! Database Configurations !!!! -->

<!-- !!!! Model Configurations !!!! -->

<!-- !!!! Mapper XML Configurations !!!! -->

<!-- !!!! Mapper Interface Configurations !!!! -->

<!-- !!!! Table Configurations !!!! -->

其中Driver Class Path当时用最新的6.0.4,无法自己生成,放狗搜了后,改成低版本就OK了。

然后另一个问题就是表与Domain类名字、域名字不相同的情况,直接在Table Configurations里面改就好。

mybatis-generator-config.xml
1
2
3
4
<table tableName="t_teacher" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
enableUpdateByExample="false" domainObjectName="Teacher">
<columnRenamingRule searchString="t_" replaceString=""/>
</table>

重点就是tableName,domainObjectName, columnRenameingRule,从字面意思就好理解。

MyBatis Mapper日志中显示SQL语句

在日志方面选择过好几次log4j, log4j2,后来改成了logback,显示SQL语句只需要在配置文件中增加一条。

logback.xml
1
<logger name="com.liangwu.courses.domain.mapper" level="DEBUG"></logger>

插入新数据后,获取ID

一般主键ID都是自动生成的,所以插入新数据的时候ID都是数据库自动生成的,如何在插入数据后Domain获得这个ID呢,最开始自己脑子发热,插入后查询。后来放狗搜,在配置文件里面改改就OK。

mybatis-generator-config.xml
1
2
3
4
<insert id="insert" parameterType="com.liangwu.courses.domain.Teacher" useGeneratedKeys="true" keyColumn="t_id" keyProperty="id" >
insert into t_teacher (t_name, t_gender)
values (#{name,jdbcType=VARCHAR}, #{gender,jdbcType=BIT})
</insert>

在insert配置的时候加useGeneratedKeys="true" keyColumn="t_id" keyProperty="id"三项,insert后就可以直接getId()了。

之前家里用的是DB120-WG,双USB口,16M Flash,64M RAM。用起来爽爽的,刷OpenWrt,接移动硬盘,transmission和Aria2离线下载,设置Shadowsocks,自动配置翻墙。可惜有次吵架,被老婆一脚给踩坏了,唉。后来通过闲鱼重新购入一枚Netgear WNDR3400,这家伙体质没有DB120-WG来得好,Flash、RAM小,无线的驱动不理想,不过还好基本功能能满足,可以刷OpenWrt和dd-Wrt。

Read more »

Spring MyBatis小试

之前了解过Hibernate,感觉不写SQL语句好爽啊。后来写了一个小程序用来计算东西,用Hibernate,但是War包实在是太大了,想要减小下体积,于是试了试MyBatis,感觉不错。写下来方便以后使用。主要是参考了MyBatis官方的文章MyBatisMyBatis-Spring

搭建环境

IDE用的Intellij IDEA,数据库用的H2 Database,包管理Gradle,日志Log4j,然后MyBatis,数据用的是Oracle数据库自带的测试数据UTLSAMPLE.sql。其中build.gradle如下。

Read more »

Eclipse出现问题

在前两天BuildShip升级后就出现问题,导致Eclipse中使用Gradle不方便,算了直接试试神器Intellij Idea吧。之前各版本信息:Eclipse(Version: Neon.2 Release (4.6.2)),BuildShip(Version: 2.0.0.v20170111-2019),Gradle(Version: 3.3)

转向Idea

好嘛,直接导入build.gradle就好了。如果之前不是用Gradle创建的工程,直接在项目目录下加入build.gradle重新导入就可以正常工作。之前在Eclipse中的build.gradle,供以后参考 :)

Read more »