从docker到k8s部署指南

主要介绍docker、docker-compose、dockerfile编写以及项目部署k8s。 部署项目:

  • traefik
  • react 前端
  • golang 微服务后端
  • consul
  • kafka
  • kafka-manager
  • zookeeper
  • redis
  • gogs

所有配置文件均可在github上下载

https://github.com/DougFlands/k8s-config

Docker

为什么使用docker

  • 环境统一
    • 每个人node版本不一样,导致前端项目依赖构建的时候可能出错
    • Golang 版本不一样,特性API可能变化导致非后端人员出错不好排查
  • 环境隔离,容器之间只能端口访问互相不影响
  • 比虚拟机开销低

本地搭建及K8S部署

docker 设置

先从官网下载docker安装,windows用户必须是旗舰版以上,家庭版不能用。

https://www.docker.com/

安装后启动,小图标右键 - 设置,要修改的内容如下 Resources

  • ADVANCED
    • 底下的Disk image location 修改路径,不然镜像打包时占用几十G,C盘可能不够用
    • 配置够的话可以增加cpu和内存占用
  • FILE SHARING
    • 勾选一个盘,docker容器与宿主机文件共享时才能使用 Docker Engine
  • registry-mirrors
    • 镜像下载,推荐使用阿里源,在阿里云控制台里,google 一下如何找到。
    • 其他的两个源也可以用。
    • 1
      2
      3
      "https://registry.docker-cn.com",
      "https://docker.mirrors.ustc.edu.cn",
      "https://xxx.mirror.aliyuncs.com"

file编写

部署docker后项目肯定要编写dockerfile,构建为镜像文件

React前端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FROM node:latest as builder

WORKDIR /usr/src/app
USER root

COPY . /usr/src/app
RUN npm config set registry https://registry.npm.taobao.org \
&& npm i \
&& npm run build


FROM nginx

WORKDIR /usr/share/nginx/html/
USER root

COPY --from=builder /usr/src/app/docker/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /usr/src/app/dist /usr/share/nginx/html/

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

文件如上,放在根目录或者项目某个文件夹内。
逐行解释,有些命令相同的不重复解释

  1. FROM node:latest as builder
    • 前端项目,构建时的环境为node,所以基础镜像为node
    • as builder,起一个名字,后面会用到
  2. WORKDIR /usr/src/app
    • 在docker环境内的工作目录
  3. USER root
    • 登录权限为root用户
  4. COPY . /usr/src/app
    • COPY 命令,第一个参数为宿主机的项目目录,第二个参数为复制到哪个目录下
    • 同样的还有ADD命令,不过官方推荐COPY,可以搜下不同
  5. RUN xxx
    • 运行命令行
    • 这里先设置npm源,然后安装依赖,打包
    • 换行的话最后加 \,下一行加&&,不换行直接加 && 即可
    • 如果构建错误,可以在这里用 ls 等命令查看输出日志
  6. FROM nginx
    • 多阶段构建,一个优化手段,可以搜一下
    • 上面的node镜像中依赖包很大,打包出来可能有1G+,但实际要用的只有dist目录
    • 这里前端项目容器需要单独能运行,所以需要nginx指向目录并开启端口,使其他服务能访问到
  7. COPY –from=builder
    • 第一行的as builder用处就来了,表示从镜像的某个目录下复制文件到此构建镜像的目录中
    • 这里的两行COPY表示将nginx设置和前端项目构建的dist复制到本镜像中
  8. EXPOSE 80
    • 镜像暴露端口,nginx用到
  9. CMD [“nginx”, “-g”, “daemon off;”]
    • 容器启动后要运行的命令
    • 同样可以使用 ENTRYPOINT 可以搜下区别

构建命令及说明
docker build --network host -t account ./ -f ./deploy/docker/account.dockerfile

  • –network host 表示使用宿主机的网络环境,可能用到代理什么的。
  • -t account ./ 打包出的镜像名称,以及构建时目录的上下文,这里的上下文指COPY的第一个参数运行上下文。
  • -f ./deploy/docker/account.dockerfile dockerfile配置文件存放目录。
  • 建议使用上面的命令行在项目根目录构建,参数顺序不能乱调,重要的是,运行上下文的问题。
  • 如果上下文在某个文件夹里,则docker image构建时,docker是访问不到文件夹外的文件的。
  • 比如如果运行命令在src目录里,则docker是的COPY . 上下文是在src下,package.json是无法访问的,就算使用RUN cd ..改变目录也不行。
  • 因为使用了多阶段构建,打包完后会有两个镜像,一个是account,一个是<none>
  • account是我们要的,<none>是构建第一阶段node环境出来的镜像。

Golang后端

我自己的项目为Golang 微服务化的后端,所以这里的例子也以此构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FROM golang:1.13.5 as builder
ENV GO111MODULE=on
ENV GOPROXY=https://goproxy.cn

WORKDIR /go/src/admin
COPY . /go/src/admin
RUN mkdir dist \
&& cd service/account \
&& CGO_ENABLED=0 go build -a -installsuffix cgo \
&& cp -r /go/src/admin/service/account/account /go/src/admin/dist

FROM alpine:latest
COPY --from=builder /go/src/admin/dist /usr/bin/server
WORKDIR /usr/bin/server
RUN chmod -R 777 /usr/bin/server

ENTRYPOINT ["./account"]
#CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"
EXPOSE 80

逐行解释,部分命令跟前端重合,不重复讲

  1. ENV
    • 环境变量,go需要用到的
  2. RUN
    • mkdir dist 创建文件夹,将编译的程序放进去
    • CGO_ENABLED=0 go build -a -installsuffix cgo 关闭cgo,不然有报错,具体可以搜一下
  3. FROM alpine:latest
    • alpine镜像,一个小巧的linux系统
    • 里面没有bash,所以进入运行中的容器命令要用sh docker exec -it xx sh
  4. RUN chmod -R 777 /usr/bin/server
    • 设置目录权限
  5. #CMD exec /bin/sh -c “trap : TERM INT; (while true; do sleep 1000; done) & wait”
    • 这段注释了,有时候会用到
    • 镜像构建成功但镜像运行失败的话,这句某些情况可以阻止运行挂掉,可以进到运行的容器内运行命令

常用命令

  • 列出所有镜像 docker image list
  • 删除无用的镜像 docker rmi $(docker images -f dangling=true -q)
  • 进入容器 docker exec -it xx bash
  • 查看实例 docker ps -a
  • 停止 docker stop xx
  • 删除 docker rm xx
  • 运行 docker run -d --net=backend -p 宿主机端口:容器端口 -v 宿主机目录:容器目录 account

docker-compose

容器编排,不需要 docker run 然后加一堆参数
本地运行时可以使用这个,比K8S方便,就是如果服务器有k8s则要写两套配置
docker-compose.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
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
version: "3"
services:
mysql:
image: docker.io/mysql
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: root
volumes:
- "/data/mysql/data:/var/lib/mysql"
- "/data/mysql/conf:/etc/mysql/conf.d"
networks:
- backend

gogs:
image: docker.io/gogs/gogs
ports:
- 10022:10022
- 10080:3000
volumes:
- "/data/gogs/:/data"
depends_on:
- mysql
networks:
- backend

nginx:
image: docker.io/nginx
ports:
- 80:80
- 443:443
depends_on:
- gogs
volumes:
- "/data/nginx/nginx.conf:/etc/nginx/nginx.conf:ro"
- "/data/www:/www"
- "/data/nginx/conf.d:/etc/nginx/conf.d"
- "/data/nginx/keys:/etc/nginx/keys"
- "/data/nginx/err:/etc/nginx/err"
networks:
- backend

networks:
backend:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.18.0.0/24
gateway: 172.18.0.1

  • 跟docker启动参数差不多
  • depends_on
    • 前置运行的容器,等这个容器运行后再运行当前容器

常用命令

  • docker-compose down 删除所有容器
  • docker-compose stop 停止一个已运行的容器,但不删除他
  • docker-compose start 启动被停止的容器
  • docker-compose build 构建或重新构建服务
  • docker-compose up -d 会在后台启动容器并使它们运行
  • docker restart 76e79889f101 重启某个容器
  • docker-compose restart 重启所有容器

关于搭建mysql,gogs服务,可以看下我的一篇文章

https://flands.com/2018/11/20/30.docker

Kubernetes

搭建

大陆用户因为众所周知的原因,这里k8s很难启动,所以需要特殊的方式。
注意: 全程不要挂代理!全程不要挂代理!全程不要挂代理!否则k8s访问本地服务会出问题!
中途有错误可以在C:\ProgramData\DockerDesktop/service.txt里找到报错,如果出现访问网址404的错误,把代理关掉。

  1. 按照链接让k8s启动,能启动就可以了

    https://github.com/AliyunContainerService/k8s-for-docker-desktop

  2. 部署dashboard
    命令行可能出现403验证的问题,可以将文件下载下来执行。
    recommended 版本会变化,项目地址如下,在releases里查找对应docker的版本

https://github.com/kubernetes/dashboard

kubectl create -f .\recommendedv.yaml
部署token,这个是创建admin-user
dashboard-adminuser.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kube-system

kubectl create -f .\dashboard-adminuser.yaml

查看 Token 找到 admin-user-token
kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')
开启服务
kubectl proxy

dashboard有v2版本,查看地址有两种
v1
http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/
v2
http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

查看服务,可能用到
kubectl get pods,svc --all-namespaces -o wide

至此k8s应该可以访问了。

云上推荐使用提供的 容器服务,可以做到ci/cd那套玩法,镜像中心构建自己的镜像,配置好触发器即可。

部署服务

traefik

介绍及说明

https://docs.traefik.io/

一个均衡负载器,支持动态路由以及中间件,配合微服务非常好用。
这里有个问题,在本地可以使用 LoadBalancer 方式部署,将POD的80\443端口暴露给宿主机,然后所有流量通过traefik进行转发。但是在腾讯云上,NodePort暴露的端口只能在30000-32767之间,无法直接暴露80/443端口。而 LoadBalancer 由云厂商接管,使用这个需要付费。为了规避这个问题,要在宿主机上直接搭建一个 nginx,将所有的80/443端口流量转发到 traefik 的端口上。
本地和云上搭建配置文件有些不一样。
配置在github/traefik && github/traefik-tencent 里

配置文件讲解

  • crd,rbac 不多介绍,就是新增权限和 IngressRoute 类型路由
  • config 用默认的就行,具体可以看官网说明
  • route.yamlHost部分网址,改成你想要的,比如traefik.k8s.com
    • 配置文件包含了一份 consul 的配置,不需要可以删掉
    • IngressRoute: 路由配置,每次新增路由使用该配置进行新增/修改路由
    • IngressRoute: entryPoints 参数, web 表示80端口,websecure 表示用了SSL的443端口
    • Secret: 存储在k8s内的秘钥
    • Middleware: 中间件,路由时会经过此配置,对应IngressRoute中middlewares配置

本地搭建

  1. config,crd,deploy,rbac,route 执行命令 kubectl apply -f ./xxx.yaml 即可
  2. 有的教程写要带上 -n kube-system 部署到 kube-system 命名空间里,但是在腾讯云中部署时会出错,实测不带也可以
  3. dashboard 里可以看到服务启动了,然后直接输入 localhost:8080 可以访问 traefik dashboard
  4. 设置hosts 127.0.0.1 traefik.k8s.com,即可使用地址访问traefik dashboard,不再需要额外放开8080端口

云上搭建

云上有些不一样

  • 云上POD暴露端口由server控制,原理是云上的POD内部有端口映射工具。
  • 云上建议对域名访问加traefik的基础认证。
  • 不要使用腾讯云提供的面板搭建traefik,用文件里的,否则报错。搭建完后记得配置Service。

先创建认证,再进行部署

  • route.yamlSecret部分有个用户名密码字串,为访问域名时的基础。
  • 搜一下 在线生成 htpasswd ,用格式user:pwd生成md5加密字符串
  • 因为k8s存储的secret只能存储base64编码字符串或ssl,所以转出来的字符再用base64编码
  • 将字串放到 Secret 的users下面
  • 云上部署用 \traefik-tencent 文件替换掉 \traefik 内的文件,再执行 kubectl apply -f ./xxx.yaml

云上的traefik搭建就完成了,接下来需要宿主机的nginx转发端口

Nginx 搭建

  1. PCRE pcre-devel 安装 PCRE(Perl Compatible Regular Expressions) 是一个Perl库,包括 perl 兼容的正则表达式库。nginx 的 http 模块使用 pcre 来解析正则表达式,所以需要在 linux 上安装 pcre 库,pcre-devel 是使用 pcre 开发的一个二次开发库。nginx也需要此库。
    yum install -y pcre pcre-devel

  2. zlib 安装 zlib 库提供了很多种压缩和解压缩的方式, nginx 使用 zlib 对 http 包的内容进行 gzip ,所以需要在 Centos 上安装 zlib 库。
    yum install -y zlib zlib-devel

  3. OpenSSL 安装 OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及 SSL 协议,并提供丰富的应用程序供测试或其它目的使用。
    nginx 不仅支持 http 协议,还支持 https(即在ssl协议上传输http),所以需要在 Centos 安装 OpenSSL 库。
    yum install -y openssl openssl-devel

在官网下载安装包

https://nginx.org/en/download.html
wget -c https://nginx.org/download/nginx-1.16.1.tar.gz
tar zxvf nginx-1.16.1.tar.gz
cd nginx-1.16.1
./configure --with-http_gzip_static_module --with-http_ssl_module --with-http_v2_module
make && make install
启动 /usr/local/nginx/sbin/nginx

设置开机自启
vim /etc/rc.local 最下面加入一行 /usr/local/nginx/sbin/nginx 就可以了

配置转发到 traefik ,直接贴出配置文件供参考。
我这里用了ssl,不需要的可以自行修改。域名的SSL在nginx上设置,traefik 配置无效。
http://127.0.0.1:30443 端口为 traefik 的端口

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
server {  
listen 80;
server_name xxx.cn;
return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl http2;
server_name xxx.cn;
ssl_certificate /data/nginx/keys/xxx.cn.crt;
ssl_certificate_key /data/nginx/keys/xxx.cn.key;
ssl_session_timeout 10m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:d!MD5:!RC4:!DHE;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
location / {
proxy_pass http://127.0.0.1:30443;
proxy_redirect default;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}
access_log /usr/local/nginx/logs/traefik.log;
}

这样就搭建好了,可以访问网站试下能不能进入到 traefik dashboard 里

consul & zookeeper

单机版本,配置查看github项目即可
consul 云上无需部署 services.yaml

gogs

需要修改镜像地址,腾讯云的docker hub 找不到 gogs 镜像,所以自己编译,并镜像公开了,如果不是腾讯云部署可以改为docker hub地址
app.ini 存放到 /data/gogs/gogs/conf/
具体配置查看 gogs 文档

https://gogs.io/

redis

redis.conf 修改 requirepass 值为访问密码 配置的主要点在于映射 /etc/redis 目录,目录下有 redis.conf ,所以将 redis.conf 存放到 /data/redis/conf/ 内 运行命令 redis-server, 运行参数 /etc/redis/redis.conf

redis.conf 官网下载的,去掉了注释,实际上要用的就前两行,其他的都是默认值

1
2
requirepass xxx
appendonly yes

kafka

因为走内网,可以不提供公网服务,于是也没加验证。
deployment 中有几个值讲一下:

  • KAFKA_ADVERTISED_HOST_NAME: kafka 服务ip的值,由于不能填写127.0.0.1或localhost所以等创建好server后再修改配置
  • KAFKA_ZOOKEEPER_CONNECT: zookeeper 的ip,所以要等zookeeper搭建后再创建kafka
  • KAFKA_ADVERTISED_LISTENERS: 外网服务ip,不需要外网服务可以不写 测试一下
    1
    2
    3
    kubectl exec -it kafka-6b6c6d4d4d-tv2qq /bin/bash
    kafka-console-producer.sh --broker-list 172.16.255.xx:9092 --topic oss
    kafka-console-consumer.sh --bootstrap-server 172.16.255.xx:9092 --topic oss

kafka-manager

修改 deployment 中 ZK_HOSTS 值为 zookeeper 的ip地址 面板上新建clusters时,连接为zk地址,zk-ip:port

K8S 常用命令

资源类型: secret, pod 等等

  • 创建某个资源 kubectl apply -f ./xxx.yaml
  • 获取某个资源 kubectl get xxx
  • 删除某个资源 kubectl detele 资源类型 xxx
  • 按文件删除某个资源 kubectl detele -f ./xxx.yaml