有一个想法,就是既然k8s集群可以实现svc的方式访问外部服务(不管是ip:port或者域名二点方式),那可不可以实现一些特殊场景的需要呢,比如访问阿里云的oss服务。
参考链接:https://www.bbsmax.com/A/KE5QK60jzL/
-> /Users/wangkaixuan/k8s_deploy_oss ~#: tree
.
├── 0.oss_auth.lua
├── 1.nginx.conf
├── 2.nginx-conf-secrets.yaml
├── 3.nginx-lua-file-configmap.yaml
├── 4.oss-deployment.yaml
├── 5.oss-service.yaml
└── README.md
文件说明:
cat 0.oss_auth.lua
-- has been sorted in alphabetical order
local signed_subresources = {
'acl',
'append',
'bucketInfo',
'cname',
'commitTransition',
'comp',
'cors',
'delete',
'lifecycle',
'location',
'logging',
'mime',
'notification',
'objectInfo',
'objectMeta',
'partData',
'partInfo',
'partNumber',
'policy',
'position',
'referer',
'replication',
'replicationLocation',
'replicationProgress',
'requestPayment',
'response-cache-control',
'response-content-disposition',
'response-content-encoding',
'response-content-language',
'response-content-type',
'response-expires',
'restore',
'security-token',
'tagging',
'torrent',
'uploadId',
'uploads',
'versionId',
'versioning',
'versions',
'website'
}
function string.startswith(s, start)
return string.sub(s, 1, string.len(start)) == start
end
local function get_canon_sub_resource()
local args = ngx.req.get_uri_args()
-- lower keys
local keys = {}
for k, v in pairs(args) do
keys[k:lower()] = v
end
-- make resource string
local s = ''
local sep = '?'
for i, k in ipairs(signed_subresources) do
v = keys[k]
if v then
-- sub table
v = type(v) == 'table' and v[1] or v
s = s .. string.format("%s%s=%s", sep, k, v)
sep = '&'
end
end
return s
end
local function get_canon_resource()
resource = ''
object = ngx.unescape_uri(ngx.var.uri)
sub = get_canon_sub_resource()
return string.format("/%s%s%s", ngx.var.oss_bucket, object, sub)
end
local function get_canon_headers()
-- default: <lowerkey, value>
local headers = ngx.req.get_headers()
local keys = {}
for k, v in pairs(headers) do
if string.startswith(k, 'x-oss-') then
-- client must assemble the same header keys
if type(v) ~= 'string' then return nil end
table.insert(keys, k)
end
end
-- sorted in alphabetical order
table.sort(keys)
for i, key in ipairs(keys) do
keys[i] = key .. ':' .. headers[key] .. '\n'
end
return table.concat(keys)
end
local function calc_sign(key, method, md5, type_, date, oss_headers, resource)
-- string_to_sign:
-- method + '\n' + content_md5 + '\n' + content_type + '\n'
-- + date + '\n' + canonicalized_oss_headers + canonicalized_resource
local sign_str = string.format('%s\n%s\n%s\n%s\n%s%s',
method, md5, type_,
date, oss_headers, resource)
ngx.log(ngx.ERR, "SignStr:", sign_str, "\n")
local sign_result = ngx.encode_base64(ngx.hmac_sha1(key, sign_str))
return sign_result, sign_str
end
local function oss_auth()
-- ngx.log(ngx.INFO, 'auth')
--local method = ngx.var.request_method
local method = ngx.req.get_method()
local content_md5 = ngx.var.http_content_md5 or ''
local content_type = ngx.var.http_content_type or ''
-- get date
local date = ngx.var.http_x_oss_date or ngx.var.http_date or ''
if date == '' then
date = ngx.http_time(ngx.time())
-- ngx.log(ngx.INFO, 'Date:', date)
ngx.req.set_header('Date', date)
end
local resource = get_canon_resource()
local canon_headers = get_canon_headers()
local sign_result, sign_str = calc_sign(ngx.var.oss_auth_key, method, content_md5,
content_type, date, canon_headers, resource)
-- ngx.log(ngx.INFO, 'sign string:', sign_str)
-- ngx.log(ngx.INFO, 'sign string len:', string.len(sign_str))
local auth = string.format("OSS %s:%s", ngx.var.oss_auth_id, sign_result)
ngx.req.set_header('Authorization', auth)
ngx.exec("@oss")
end
-- main
res = oss_auth()
if res then
ngx.exit(res)
end
cat 1.nginx.conf
server {
listen 80;
location / {
set $oss_bucket "bucketname"; #oss的桶名
set $oss_auth_id "xxxxxxxxxxx"; #oss的access_key
set $oss_auth_key "xxxxxxxxxxxxxxxxxx"; #oss的secret_key
rewrite_by_lua_file /opt/oss_auth.lua; #oss_auth.lua的文件路径
}
location @oss {
proxy_pass http://my.oss-ap-hangzhou.aliyuncs.com; #真实的oss访问地址
}
}
cat 2.nginx-conf-secrets.yaml
apiVersion: v1
data:
#nginx.conf通过base64编码生成的字符串
oss-proxy-lua.conf: c2VydmVyIHsKICAgIGxpc3RdsvsdiA4MDsdvsdKICAgIGxvY2F0aW9uIC8gewogICAgICAgIHNldCAkb3NzX2J1Y2tldCAiZGlwYml0LWRlcGxveS1kZXYiOwogICAgICAgIHNldCAkb3NzX2F1dGhfaWQgIkxUQUlmN1NON0VPaVRzcHciOwogICAgICAgIHNldCAkb3NzX2F1dGhfa2V5ICI1ZUFDZ0wyQlVwb1p3SzBkRUQ3NkRNZkxyMHJFaUgiOwogICAgICAgIHJld3JpdGVfYnlfbHVhX2ZpbGUgL29wdC9vc3NfYXV0aC5sdWE7CiAgICB9CiAgICBsb2NhdGlvbiBAb3NzIHsKICAgICAgICBwcm94eV9wYXNzIGh0dHA6Ly9kaXBiaXQtZGVwbG95LWRldi5vc3MtYXAtc291dGhlYXN0LTEuYWxpeXVuY3MuY29tOwogICAgfQp9Cg==
kind: Secret
metadata:
name: nginx-conf-secret
namespace: default
type: Opaque #Opaque是base64 编码格式的 Secret,用来存储密码、密钥等
cat 3.nginx-lua-file-configmap.yaml
apiVersion: v1
data:
oss_auth.lua: |-
-- has been sorted in alphabetical order
local signed_subresources = {
'acl',
'append',
'bucketInfo',
'cname',
'commitTransition',
'comp',
'cors',
'delete',
'lifecycle',
'location',
'logging',
'mime',
'notification',
'objectInfo',
'objectMeta',
'partData',
'partInfo',
'partNumber',
'policy',
'position',
'referer',
'replication',
'replicationLocation',
'replicationProgress',
'requestPayment',
'response-cache-control',
'response-content-disposition',
'response-content-encoding',
'response-content-language',
'response-content-type',
'response-expires',
'restore',
'security-token',
'tagging',
'torrent',
'uploadId',
'uploads',
'versionId',
'versioning',
'versions',
'website'
}
function string.startswith(s, start)
return string.sub(s, 1, string.len(start)) == start
end
local function get_canon_sub_resource()
local args = ngx.req.get_uri_args()
-- lower keys
local keys = {}
for k, v in pairs(args) do
keys[k:lower()] = v
end
-- make resource string
local s = ''
local sep = '?'
for i, k in ipairs(signed_subresources) do
v = keys[k]
if v then
-- sub table
v = type(v) == 'table' and v[1] or v
s = s .. string.format("%s%s=%s", sep, k, v)
sep = '&'
end
end
return s
end
local function get_canon_resource()
resource = ''
object = ngx.unescape_uri(ngx.var.uri)
sub = get_canon_sub_resource()
return string.format("/%s%s%s", ngx.var.oss_bucket, object, sub)
end
local function get_canon_headers()
-- default: <lowerkey, value>
local headers = ngx.req.get_headers()
local keys = {}
for k, v in pairs(headers) do
if string.startswith(k, 'x-oss-') then
-- client must assemble the same header keys
if type(v) ~= 'string' then return nil end
table.insert(keys, k)
end
end
-- sorted in alphabetical order
table.sort(keys)
for i, key in ipairs(keys) do
keys[i] = key .. ':' .. headers[key] .. '\n'
end
return table.concat(keys)
end
local function calc_sign(key, method, md5, type_, date, oss_headers, resource)
-- string_to_sign:
-- method + '\n' + content_md5 + '\n' + content_type + '\n'
-- + date + '\n' + canonicalized_oss_headers + canonicalized_resource
local sign_str = string.format('%s\n%s\n%s\n%s\n%s%s',
method, md5, type_,
date, oss_headers, resource)
ngx.log(ngx.ERR, "SignStr:", sign_str, "\n")
local sign_result = ngx.encode_base64(ngx.hmac_sha1(key, sign_str))
return sign_result, sign_str
end
local function oss_auth()
-- ngx.log(ngx.INFO, 'auth')
--local method = ngx.var.request_method
local method = ngx.req.get_method()
local content_md5 = ngx.var.http_content_md5 or ''
local content_type = ngx.var.http_content_type or ''
-- get date
local date = ngx.var.http_x_oss_date or ngx.var.http_date or ''
if date == '' then
date = ngx.http_time(ngx.time())
-- ngx.log(ngx.INFO, 'Date:', date)
ngx.req.set_header('Date', date)
end
local resource = get_canon_resource()
local canon_headers = get_canon_headers()
local sign_result, sign_str = calc_sign(ngx.var.oss_auth_key, method, content_md5,
content_type, date, canon_headers, resource)
-- ngx.log(ngx.INFO, 'sign string:', sign_str)
-- ngx.log(ngx.INFO, 'sign string len:', string.len(sign_str))
local auth = string.format("OSS %s:%s", ngx.var.oss_auth_id, sign_result)
ngx.req.set_header('Authorization', auth)
ngx.exec("@oss")
end
-- main
res = oss_auth()
if res then
ngx.exit(res)
end
kind: ConfigMap
metadata:
name: nginx-lua-configmap
namespace: default
cat 4.oss-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "7"
labels:
app: aliyun-oss
name: aliyun-oss
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: aliyun-oss
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: aliyun-oss
spec:
containers:
- env:
- name: LUA_PATH
value: /opt;;
image: registry.ap-southeast-1.aliyuncs.com/mynamespace:openresty-1.13.6.2-0-centos-rpm
imagePullPolicy: IfNotPresent
name: aliyun-oss
ports:
- containerPort: 80
name: aliyun-oss
protocol: TCP
resources:
requests:
cpu: 250m
memory: 256Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /opt
name: nginx-lua-file #oss_auth.lua文件的挂载路径
- mountPath: /etc/nginx/conf.d
name: nginx-conf-file #nginx.conf的挂载路径
- mountPath: /etc/localtime
name: container-localtime #容器的市区
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: 41c11f57a260c99d30449e6d31b77e77 #拉取的密钥
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- configMap: #以confimap的方式挂载到容器中
defaultMode: 420
name: nginx-lua-configmap #oss_auth.lua存放在nginx-lua-configmap中
name: nginx-lua-file
- secret: #以secret的方式挂载到容器中
defaultMode: 420
secretName: nginx-conf-secret #nginx.conf存放在nginx-conf-secret中
name: nginx-conf-file
- hostPath: #以hostpath的方式挂载到容器中
path: /etc/localtime
type: ""
name: container-localtime
cat 5.oss-service.yaml
apiVersion: v1
kind: Service
metadata:
name: oss-svc
namespace: default
spec:
clusterIP: None
ports:
- name: oss-proxy
port: 80
protocol: TCP
targetPort: 80
selector:
app: aliyun-oss
kubectl apply -f 2.nginx-conf-secrets.yaml
kubectl apply -f 3.nginx-lua-file-configmap.yam
kubectl apply -f 4.oss-deployment.yaml
kubectl apply -f 5.oss-service.yaml