- Istio 中 sidecar 自动注入过程
- Istio 中的 init 容器启动过程
- 启用了 Sidecar 自动注入的 Pod 的启动流程
下图中展示了 Istio 数据平面中的 Pod 启动完后的组件。
image.pngIstio 中的 sidecar 注入
Istio 中提供了以下两种 sidecar 注入方式:
- 使用
istioctl
手动注入。 - 基于 Kubernetes 的 突变 webhook 准入控制器(mutating webhook addmission controller 的自动 sidecar 注入方式。
不论是手动注入还是自动注入,sidecar 的注入过程都需要遵循如下步骤:
- Kubernetes 需要了解待注入的 sidecar 所连接的 Istio 集群及其配置;
- Kubernetes 需要了解待注入的 sidecar 容器本身的配置,如镜像地址、启动参数等;
- Kubernetes 根据 sidecar 注入模板和以上配置填充 sidecar 的配置参数,将以上配置注入到应用容器的一侧;
使用下面的命令可以手动注入 sidecar。
istioctl kube-inject -f ${YAML_FILE} | kuebectl apply -f -
该命令会使用 Istio 内置的 sidecar 配置来注入,下面使用 Istio详细配置请参考 Istio 官网 。
注入完成后您将看到 Istio 为原有 pod template 注入了 initContainer
及 sidecar proxy相关的配置。
Init 容器
Init 容器是一种专用容器,它在应用程序容器启动之前运行,用来包含一些应用镜像中不存在的实用工具或安装脚本。
一个 Pod 中可以指定多个 Init 容器,如果指定了多个,那么 Init 容器将会按顺序依次运行。只有当前面的 Init 容器必须运行成功后,才可以运行下一个 Init 容器。当所有的 Init 容器运行完成后,Kubernetes 才初始化 Pod 和运行应用容器。
Init 容器使用 Linux Namespace,所以相对应用程序容器来说具有不同的文件系统视图。因此,它们能够具有访问 Secret 的权限,而应用程序容器则不能。
在 Pod 启动过程中,Init 容器会按顺序在网络和数据卷初始化之后启动。每个容器必须在下一个容器启动之前成功退出。如果由于运行时或失败退出,将导致容器启动失败,它会根据 Pod 的 restartPolicy
指定的策略进行重试。然而,如果 Pod 的 restartPolicy
设置为 Always,Init 容器失败时会使用 RestartPolicy
策略。
在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready
状态。Init 容器的端口将不会在 Service中进行聚集。 正在初始化中的 Pod 处于 Pending
状态,但应该会将 Initializing
状态设置为 true。Init 容器运行完成以后就会自动终止。
深入研究 sidecar 的清单
我们首先看一下在之前部署的应用 pod 中,这两个容器的 YAML 清单(manifest)。
kubectl get po -l app=sleep -n apps -o yaml
我们来看一下 istio-init 和 istio-proxy 容器的片段。
istio-init 容器:
initContainers:
- name: istio-init
image: docker.io/istio/proxyv2:1.11.4
imagePullPolicy: IfNotPresent
args:
- istio-iptables
- -p
- "15001"
- -z
- "15006"
- -u
- "1337"
- -m
- REDIRECT
- -i
- '*'
- -x
- ""
- -b
- '*'
- -d
- 15090,15021,15020
env:
- name: ISTIO_META_DNS_AUTO_ALLOCATE
value: "true"
- name: ISTIO_META_DNS_CAPTURE
value: "true"
resources:
limits:
cpu: "2"
memory: 1Gi
requests:
cpu: 100m
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_ADMIN
- NET_RAW
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsGroup: 0
runAsNonRoot: false
runAsUser: 0
istio-proxy 容器:
containers:
- name: istio-proxy
image: docker.io/istio/proxyv2:1.11.4
imagePullPolicy: IfNotPresent
args:
- proxy
- sidecar
- --domain
- $(POD_NAMESPACE).svc.cluster.local
- --proxyLogLevel=warning
- --proxyComponentLogLevel=misc:error
- --log_output_level=default:info
- --concurrency
- "2"
ports:
- name: http-envoy-prom
containerPort: 15090
protocol: TCP
readinessProbe:
httpGet:
path: /healthz/ready
port: 15021
scheme: HTTP
failureThreshold: 30
initialDelaySeconds: 1
periodSeconds: 2
successThreshold: 1
timeoutSeconds: 3
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsGroup: 1337
runAsNonRoot: true
runAsUser: 1337
env:
- name: PROXY_CONFIG
value: |
{"proxyMetadata":{"ISTIO_META_DNS_AUTO_ALLOCATE":"true","ISTIO_META_DNS_CAPTURE":"true"}}
- name: ISTIO_META_DNS_AUTO_ALLOCATE
value: "true"
- name: ISTIO_META_DNS_CAPTURE
value: "true"
...
在这些片段中有一些有意思的事情:
-这两个容器都是由同一个镜像提供的,也就是 docker.io/istio/proxyv2:1.11。这意味着什么,它又是如何运行的呢?istio-iptables 和 proxy(在 args 部分下面)命令被封装到了镜像中的 pilot-agent 二进制文件中。所以,如果在 istio-proxy 容器中运行 pilot-agent 二进制文件的话
kubectl exec $(kubectl get po -l app=sleep -n apps -o jsonpath="{.items[0].metadata.name}") -n apps -c istio-proxy -- pilot-agent
输出如下所示:
Istio Pilot agent runs in the sidecar or gateway container and bootstraps Envoy.
Usage:
pilot-agent [command]
Available Commands:
completion generate the autocompletion script for the specified shell
help Help about any command
istio-clean-iptables Clean up iptables rules for Istio Sidecar
istio-iptables Set up iptables rules for Istio Sidecar
proxy XDS proxy agent
request Makes an HTTP request to the Envoy admin API
version Prints out build version information
wait Waits until the Envoy proxy is ready
为了尽可能减少攻击面,istio-init 容器中的 securityContext 小节(它是 PodSecurityContext 对象 的一部分)标记该容器将以 root 权限运行(runAsUser: 0),但是除了 NET_ADMIN 和 NET_RAW 能力之外,其他的 Linux 能力都被禁用了。这些能力为 istio-init init 容器提供了运行时的权限,以重写应用 pod 的 iptables。更多细节,请参阅 Istio 的文档。
allowPrivilegeEscalation: false
capabilities:
add:
- NET_ADMIN
- NET_RAW
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsGroup: 0
runAsNonRoot: false
runAsUser: 0
另一方面,istio-proxy 容器以 1337 用户在限制权限下运行。因为这是一个保留用户,所以应用工作负载的 UID(User ID)必须要与之不同,不能与 1337 冲突。1337 UID 是由 Istio 团队任意选择的,以便于将流量重定向到 istio-proxy 容器。我们可以看到在初始化 iptables 的时候,1337 也作为了 istio-iptables 的参数。由于这个容器会与应用工作负载一起运行,Istio 还确保它对根文件系统只有读的权限。
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsGroup: 1337
runAsNonRoot: true
runAsUser: 1337
istio-proxy 容器运行时会使用如下所示的就绪性(readiness)探针。在 Kubernetes 中,kubelet 使用这个探针确定 istio-proxy 是否已经准备好接收流量。只有当 istio-proxy 容器以及所有相关的应用容器均处于运行状态并且健康探针成功执行的情况下,kubelet 才会将 Pod 视为达到就绪状态。如果针对服务器路径“/healthz/ready”(在 pilot-agent 源码中定义的)的处理器返回成功的状态码,kubelet 就会认定容器处于健康状态。failureThreshold 配置指定了在将容器视为未就绪之前,这个就绪性探针允许连续失败的次数。
readinessProbe:
httpGet:
path: /healthz/ready
port: 15021
scheme: HTTP
initialDelaySeconds: 1
failureThreshold: 30
periodSeconds: 2
successThreshold: 1
timeoutSeconds: 3
Init 容器解析
Istio 在 pod 中注入的 Init 容器名为 istio-init
,我们在上面 Istio 注入完成后的 YAML 文件中看到了该容器的启动命令是:
istio-iptables -p 15001 -z 15006 -u 1337 -m REDIRECT -i '*' -x "" -b '*' -d 15090,15020
我们看到 istio-init
容器的入口是 /usr/local/bin/istio-iptables
命令行,该命令行工具的代码的位置在 Istio 源码仓库的 tools/istio-iptables 目录。
注意:在 Istio 1.1 版本时还是使用 isito-iptables.sh
命令行来操作 IPtables。
Init 容器启动入口
Init 容器的启动入口是 istio-iptables
命令行,该命令行工具的用法如下:
$ istio-iptables [flags]
-p: 指定重定向所有 TCP 流量的 sidecar 端口(默认为 $ENVOY_PORT = 15001)
-m: 指定入站连接重定向到 sidecar 的模式,“REDIRECT” 或 “TPROXY”(默认为 $ISTIO_INBOUND_INTERCEPTION_MODE)
-b: 逗号分隔的入站端口列表,其流量将重定向到 Envoy(可选)。使用通配符 “*” 表示重定向所有端口。为空时表示禁用所有入站重定向(默认为 $ISTIO_INBOUND_PORTS)
-d: 指定要从重定向到 sidecar 中排除的入站端口列表(可选),以逗号格式分隔。使用通配符“*” 表示重定向所有入站流量(默认为 $ISTIO_LOCAL_EXCLUDE_PORTS)
-o:逗号分隔的出站端口列表,不包括重定向到 Envoy 的端口。
-i: 指定重定向到 sidecar 的 IP 地址范围(可选),以逗号分隔的 CIDR 格式列表。使用通配符 “*” 表示重定向所有出站流量。空列表将禁用所有出站重定向(默认为 $ISTIO_SERVICE_CIDR)
-x: 指定将从重定向中排除的 IP 地址范围,以逗号分隔的 CIDR 格式列表。使用通配符 “*” 表示重定向所有出站流量(默认为 $ISTIO_SERVICE_EXCLUDE_CIDR)。
-k:逗号分隔的虚拟接口列表,其入站流量(来自虚拟机的)将被视为出站流量。
-g:指定不应用重定向的用户的 GID。(默认值与 -u param 相同)
-u:指定不应用重定向的用户的 UID。通常情况下,这是代理容器的 UID(默认值是 1337,即 istio-proxy 的 UID)。
-z: 所有进入 pod/VM 的 TCP 流量应被重定向到的端口(默认 $INBOUND_CAPTURE_PORT = 15006)。
以上传入的参数都会重新组装成 iptables
规则,关于该命令的详细用法请访问 tools/istio-iptables/pkg/cmd/root.go 。
该容器存在的意义就是让 sidecar 代理可以拦截所有的进出 pod 的流量,15090 端口(Mixer 使用)和 15092 端口(Ingress Gateway)除外的所有入站(inbound)流量重定向到 15006 端口(sidecar),再拦截应用容器的出站(outbound)流量经过 sidecar 处理(通过 15001 端口监听)后再出站。关于 Istio 中端口用途请参考 Istio 官方文档 。
命令解析
这条启动命令的作用是:
- 将应用容器的所有流量都转发到 sidecar 的 15006 端口。
- 使用
istio-proxy
用户身份运行, UID 为 1337,即 sidecar 所处的用户空间,这也是istio-proxy
容器默认使用的用户,见 YAML 配置中的runAsUser
字段。 - 使用默认的
REDIRECT
模式来重定向流量。 - 将所有出站流量都重定向到 sidecar 代理(通过 15001 端口)。
因为 Init 容器初始化完毕后就会自动终止,因为我们无法登陆到容器中查看 iptables 信息,但是 Init 容器初始化结果会保留到应用容器和 sidecar 容器中。
Pod 启动流程
启用了 Sidecar 自动注入的 Pod 启动流程如下:
- Init 容器先启动,向 Pod 中注入 iptables 规则,进行透明流量拦截。
- 随后,Kubernetes 会根据 Pod Spec 中容器的声明顺序依次启动容器,但这是非阻塞的,无法保证第一个容器启动完成后才启动下一个。
istio-proxy
容器启动时,pilot-agent
将作为 PID 1 号进程,它是 Linux 用户空间的第一个进程,负责拉起其他进程和处理僵尸进程。pilot-agent
将生成 Envoy bootstrap 配置并拉起envoy
进程;应用容器几乎跟istio-proxy
容器同时启动,为了防止 Pod 内的容器在还没启动好的情况而接收到外界流量,这时候就绪探针就派上用场了。Kubernetes 会在istio-proxy
容器的 15021 端口进行就绪检查,直到isito-proxy
启动完成后 kubelet 才会将流量路由到 Pod 内。 - 在 Pod 启动完成后,
pilot-agent
将变为守护进程监视系统其他进程,除此之外,该进程还为 Envoy 提供 Bootstrap 配置、证书、健康检查、配置热加载、身份支持及进程生命周期管理等。
Pod 内容器启动顺序问题
在 Pod 启动的过程中存在容器启动顺序问题,假设下面这种情况,应用容器先启动,请求其他服务,这时候 istio-proxy
容器还没启动完成,那么该请求将会失败,如果你的应用的健壮性不足,甚至可能导致应用容器崩溃,进而 Pod 重启。对于这种情况的解决方案是:
- 修改应用程序,增加超时重试。
- 增加应用容器中进程的启动延迟,比如增加
sleep
时间。 - 在应用容器中增加一个
postStart
配置,检测应用进程是否启动完成,只有当检测成功时,Kubernetes 才会将 Pod 的状态标记为Running
。
注入原理详解
概念
简单来说,Sidecar 注入会将额外容器的配置添加到 Pod 模板中。这里特指将Envoy容器注应用所在Pod中。
Istio 服务网格目前所需的容器有:
istio-init
用于设置 iptables 规则,以便将入站/出站流量通过 Sidecar 代理。
初始化容器与应用程序容器在以下方面有所不同:
- 它在启动应用容器之前运行,并一直运行直至完成。
- 如果有多个初始化容器,则每个容器都应在启动下一个容器之前成功完成。
因此,您可以看到,对于不需要成为实际应用容器一部分的设置或初始化作业来说,这种容器是多么的完美。在这种情况下,istio-init
就是这样做并设置了 iptables
规则。
istio-proxy
这个容器是真正的 Sidecar 代理(基于 Envoy)。
下面的内容描述了向 pod 中注入 Istio Sidecar 的两种方法:
- 使用
istioctl
手动注入 - 启用 pod 所属命名空间的 Istio Sidecar 注入器自动注入。
手动注入直接修改配置,如 deployment,并将代理配置注入其中。
当 pod 所属namespace
启用自动注入后,自动注入器会使用准入控制器在创建 Pod 时自动注入代理配置。
通过应用 istio-sidecar-injector
ConfigMap 中定义的模版进行注入。
自动注入
当你在一个namespace
中设置了 istio-injection=enabled
标签,且 injection webhook 被启用后,任何新的 pod 都有将在创建时自动添加 Sidecar. 请注意,区别于手动注入,自动注入发生在 pod 层面。你将看不到 deployment 本身有任何更改 。
kubectl label namespace default istio-inhection=enabled
kubectl get namespace -L istio-injection
NAME STATUS AGE ISTIO-INJECTION
default Active 1h enabled
istio-system Active 1h
kube-public Active 1h
kube-system Active 1h
注入发生在 pod 创建时。杀死正在运行的 pod 并验证新创建的 pod 是否注入 sidecar。原来的 pod 具有 READY 为 1/1 的容器,注入 sidecar 后的 pod 则具有 READY 为 2/2 的容器 。
自动注入原理
自动注入是利用了k8s Admission webhook 实现的。 Admission webhook 是一种用于接收准入请求并对其进行处理的 HTTP 回调机制, 它可以更改发送到 API 服务器的对象以执行自定义的设置默认值操作。 具体细节可以查阅 Admission webhook 文档。
istio 对应的istio-sidecar-injector webhook配置,默认会回调istio-sidecar-injector service的/inject
地址。
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
name: istio-sidecar-injector
webhooks:
- name: sidecar-injector.istio.io
clientConfig:
service:
name: istio-sidecar-injector
namespace: istio-system
path: "/inject"
caBundle: ${CA_BUNDLE}
rules:
- operations: [ "CREATE" ]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
namespaceSelector:
matchLabels:
istio-injection: enabled
回调API入口代码在 pkg/kube/inject/webhook.go
中
// 创建一个用于自动注入sidecar的新实例
func NewWebhook(p WebhookParameters) (*Webhook, error) {
// ...省略一万字...
wh := &Webhook{
Config: sidecarConfig,
sidecarTemplateVersion: sidecarTemplateVersionHash(sidecarConfig.Template),
meshConfig: p.Env.Mesh(),
configFile: p.ConfigFile,
valuesFile: p.ValuesFile,
valuesConfig: valuesConfig,
watcher: watcher,
healthCheckInterval: p.HealthCheckInterval,
healthCheckFile: p.HealthCheckFile,
env: p.Env,
revision: p.Revision,
}
//api server 回调函数,监听/inject回调
p.Mux.HandleFunc("/inject", wh.serveInject)
p.Mux.HandleFunc("/inject/", wh.serveInject)
// ...省略一万字...
return wh, nil
}
serveInject
逻辑
func (wh *Webhook) serveInject(w http.ResponseWriter, r *http.Request) {
// ...省略一万字...
var reviewResponse *v1beta1.AdmissionResponse
ar := v1beta1.AdmissionReview{}
if _, _, err := deserializer.Decode(body, nil, &ar); err != nil {
handleError(fmt.Sprintf("Could not decode body: %v", err))
reviewResponse = toAdmissionResponse(err)
} else {
//执行具体的inject逻辑
reviewResponse = wh.inject(&ar, path)
}
// 响应inject sidecar后的内容给k8s api server
response := v1beta1.AdmissionReview{}
if reviewResponse != nil {
response.Response = reviewResponse
if ar.Request != nil {
response.Response.UID = ar.Request.UID
}
}
// ...省略一万字...
}
// 注入逻辑实现
func (wh *Webhook) inject(ar *v1beta1.AdmissionReview, path string) *v1beta1.AdmissionResponse {
// ...省略一万字...
// injectRequired判断是否有设置自动注入
if !injectRequired(ignoredNamespaces, wh.Config, &pod.Spec, &pod.ObjectMeta) {
log.Infof("Skipping %s/%s due to policy check", pod.ObjectMeta.Namespace, podName)
totalSkippedInjections.Increment()
return &v1beta1.AdmissionResponse{
Allowed: true,
}
}
// ...省略一万字...
// 返回需要注入Pod的对象
spec, iStatus, err := InjectionData(wh.Config.Template, wh.valuesConfig, wh.sidecarTemplateVersion, typeMetadata, deployMeta, &pod.Spec, &pod.ObjectMeta, wh.meshConfig, path) // nolint: lll
if err != nil {
handleError(fmt.Sprintf("Injection data: err=%v spec=%vn", err, iStatus))
return toAdmissionResponse(err)
}
// 执行容器注入逻辑
patchBytes, err := createPatch(&pod, injectionStatus(&pod), wh.revision, annotations, spec, deployMeta.Name, wh.meshConfig)
if err != nil {
handleError(fmt.Sprintf("AdmissionResponse: err=%v spec=%vn", err, spec))
return toAdmissionResponse(err)
}
reviewResponse := v1beta1.AdmissionResponse{
Allowed: true,
Patch: patchBytes,
PatchType: func() *v1beta1.PatchType {
pt := v1beta1.PatchTypeJSONPatch
return &pt
}(),
}
return &reviewResponse
}
injectRequired
函数
func injectRequired(ignored []string, config *Config, podSpec *corev1.PodSpec, metadata *metav1.ObjectMeta) bool {
// HostNetwork模式直接跳过注入
if podSpec.HostNetwork {
return false
}
// k8s系统命名空间(kube-system/kube-public)跳过注入
for _, namespace := range ignored {
if metadata.Namespace == namespace {
return false
}
}
annos := metadata.GetAnnotations()
if annos == nil {
annos = map[string]string{}
}
var useDefault bool
var inject bool
// 优先判断是否申明了`sidecar.istio.io/inject` 注解,会覆盖命名配置
switch strings.ToLower(annos[annotation.SidecarInject.Name]) {
case "y", "yes", "true", "on":
inject = true
case "":
// 使用命名空间配置
useDefault = true
}
// 指定Pod不需要注入Sidecar的标签选择器
if useDefault {
for _, neverSelector := range config.NeverInjectSelector {
selector, err := metav1.LabelSelectorAsSelector(&neverSelector)
if err != nil {
} else if !selector.Empty() && selector.Matches(labels.Set(metadata.Labels))
// 设置不需要注入
inject = false
useDefault = false
break
}
}
}
// 总是将 sidecar 注入匹配标签选择器的 pod 中,而忽略全局策略
if useDefault {
for _, alwaysSelector := range config.AlwaysInjectSelector {
selector, err := metav1.LabelSelectorAsSelector(&alwaysSelector)
if err != nil {
log.Warnf("Invalid selector for AlwaysInjectSelector: %v (%v)", alwaysSelector, err)
} else if !selector.Empty() && selector.Matches(labels.Set(metadata.Labels)){ // 设置需要注入
inject = true
useDefault = false
break
}
}
}
// 如果都没有配置则使用默认注入策略
var required bool
switch config.Policy {
default: // InjectionPolicyOff
log.Errorf("Illegal value for autoInject:%s, must be one of [%s,%s]. Auto injection disabled!",
config.Policy, InjectionPolicyDisabled, InjectionPolicyEnabled)
required = false
case InjectionPolicyDisabled:
if useDefault {
required = false
} else {
required = inject
}
case InjectionPolicyEnabled:
if useDefault {
required = true
} else {
required = inject
}
}
return required
}
从上面我们可以看出,是否注入Sidecar的优先级为
Pod Annotations → NeverInjectSelector → AlwaysInjectSelector → Default Policy
createPath
函数
func createPatch(pod *corev1.Pod, prevStatus *SidecarInjectionStatus, revision string, annotations map[string]string,
sic *SidecarInjectionSpec, workloadName string, mesh *meshconfig.MeshConfig) ([]byte, error) {
var patch []rfc6902PatchOperation
// ...省略一万字...
// 注入初始化启动容器
patch = append(patch, addContainer(pod.Spec.InitContainers, sic.InitContainers, "/spec/initContainers")...)
// 注入Sidecar容器
patch = append(patch, addContainer(pod.Spec.Containers, sic.Containers, "/spec/containers")...)
// 注入挂载卷
patch = append(patch, addVolume(pod.Spec.Volumes, sic.Volumes, "/spec/volumes")...)
patch = append(patch, addImagePullSecrets(pod.Spec.ImagePullSecrets, sic.ImagePullSecrets, "/spec/imagePullSecrets")...)
// 注入新注解
patch = append(patch, updateAnnotation(pod.Annotations, annotations)...)
// ...省略一万字...
return json.Marshal(patch)
}
总结:可以看到,整个注入过程实际就是原本的Pod配置反解析成Pod对象,把需要注入的Yaml内容(如:Sidecar)反序列成对象然后append到对应Pod (如:Container)上,然后再把修改后的Pod重新解析成yaml 内容返回给k8s的api server,然后k8s 拿着修改后内容再将这两个容器调度到同一台机器进行部署,至此就完成了对应Sidecar的注入。
卸载 sidecar 自动注入器
kubectl delete mutatingwebhookconfiguration istio-sidecar-injector
kubectl -n istio-system delete service istio-sidecar-injector
kubectl -n istio-system delete deployment istio-sidecar-injector
kubectl -n istio-system delete serviceaccount istio-sidecar-injector-service-account
kubectl delete clusterrole istio-sidecar-injector-istio-system
kubectl delete clusterrolebinding istio-sidecar-injector-admin-role-binding-istio-system
上面的命令不会从 pod 中移除注入的 sidecar。需要进行滚动更新或者直接删除对应的pod,并强制 deployment 重新创建新pod。
手动注入 sidecar
手动注入 deployment ,需要使用 使用 istioctl kube-inject
使用手动注入前先关闭自动注入
kubectl label namespace default istio-injection=disabled
使用istioctl手动注入
istioctl kube-inject -f samples/sleep/sleep.yaml | kubectl apply -f -
我们可以查看对应的deployment
明细
describe deployment sleep
Name: sleep
Namespace: default
CreationTimestamp: Wed, 27 May 2020 10:45:23 +0800
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=sleep
Pod Template:
Labels: app=sleep
istio.io/rev=
security.istio.io/tlsMode=istio
Annotations: sidecar.istio.io/interceptionMode: REDIRECT
sidecar.istio.io/status:
{"version":"d36ff46d2def0caba37f639f09514b17c4e80078f749a46aae84439790d2b560","initContainers":["istio-init"],"containers":["istio-proxy"]...
traffic.sidecar.istio.io/excludeInboundPorts: 15020
traffic.sidecar.istio.io/includeOutboundIPRanges: *
Service Account: sleep
Init Containers:
istio-init:
Image: docker.io/istio/proxyv2:1.6.0
Port: <none>
Host Port: <none>
Args:
istio-iptables
-p
15001
-z
15006
-u
1337
-m
REDIRECT
-i
*
-x
-b
*
-d
15090,15021,15020
Containers:
sleep:
Image: governmentpaas/curl-ssl
Port: <none>
Host Port: <none>
Command:
/bin/sleep
3650d
Environment: <none>
Mounts:
/etc/sleep/tls from secret-volume (rw)
istio-proxy:
Image: docker.io/istio/proxyv2:1.6.0
Port: 15090/TCP
Host Port: 0/TCP
Args:
proxy
sidecar
--domain
$(POD_NAMESPACE).svc.cluster.local
--serviceCluster
sleep.$(POD_NAMESPACE)
--proxyLogLevel=warning
--proxyComponentLogLevel=misc:error
--trust-domain=cluster.local
--concurrency
2
可以看到,相比原始的deployment.yaml文件多出了两个容器,这两个容器的作用后面单独写一篇文章来分析:
-
Init Containers
下的istio-init
-
Containers
下的istio-proxy
上面两个容器的注入,默认情况下将使用集群内的配置,或者使用该配置的本地副本来完成注入。
kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yaml
kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.values}' > inject-values.yaml
kubectl -n istio-system get configmap istio -o=jsonpath='{.data.mesh}' > mesh-config.yaml
指定输入文件,运行 kube-inject
并部署
istioctl kube-inject \
--injectConfigFile inject-config.yaml \
--meshConfigFile mesh-config.yaml \
--valuesFile inject-values.yaml \
--filename samples/sleep/sleep.yaml \
| kubectl apply -f -
验证 sidecar 已经被注入到 READY 列下 2/2
的 sleep pod 中
kubectl get pod -l app=sleep
NAME READY STATUS RESTARTS AGE
sleep-64c6f57bc8-f5n4x 2/2 Running 0 24s
查看对应pod中的容器
kubectl get pods -o jsonpath="{.items[*].spec.containers[*].image}" | tr -s '[[:space:]]' '\n' | sort
docker.io/istio/proxyv2:1.6.0
governmentpaas/curl-ssl
手动注入的代码入口在 istioctl/cmd/kubeinject.go
手工注入跟自动注入还是有些差异的。手动注入是改变了Deployment
。我们可以看下它具体做了哪些动作:
Deployment
注入前配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: sleep
spec:
replicas: 1
selector:
matchLabels:
app: sleep
template:
metadata:
labels:
app: sleep
spec:
serviceAccountName: sleep
containers:
- name: sleep
image: governmentpaas/curl-ssl
command: ["/bin/sleep", "3650d"]
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /etc/sleep/tls
name: secret-volume
volumes:
- name: secret-volume
secret:
secretName: sleep-secret
optional: true
Deployment
注入后配置:
kubectl get deployment sleep -o yaml > sleep.yaml
less sleep.yaml
这里只保留跟注入容器有关的部分内容
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
generation: 1
name: sleep
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: sleep
template:
metadata:
annotations:
sidecar.istio.io/interceptionMode: REDIRECT
labels:
app: sleep
istio.io/rev: ""
security.istio.io/tlsMode: istio
spec:
containers:
- command:
- /bin/sleep
- 3650d
image: governmentpaas/curl-ssl
imagePullPolicy: IfNotPresent
name: sleep
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/sleep/tls
name: secret-volume
- args:
- proxy
- sidecar
- --domain
- $(POD_NAMESPACE).svc.cluster.local
- --serviceCluster
- sleep.$(POD_NAMESPACE)
- --proxyLogLevel=warning
- --proxyComponentLogLevel=misc:error
- --trust-domain=cluster.local
- --concurrency
- "2"
image: docker.io/istio/proxyv2:1.6.0
imagePullPolicy: Always
name: istio-proxy
ports:
- containerPort: 15090
name: http-envoy-prom
protocol: TCP
dnsPolicy: ClusterFirst
initContainers:
- args:
- istio-iptables
- -p
- "15001"
- -z
- "15006"
- -u
- "1337"
- -m
- REDIRECT
- -i
- '*'
- -x
- ""
- -b
- '*'
- -d
- 15090,15021,15020
env:
- name: DNS_AGENT
image: docker.io/istio/proxyv2:1.6.0
imagePullPolicy: Always
name: istio-init
restartPolicy: Always
可见新增了一个容器镜像
image: docker.io/istio/proxyv2:1.6.0
那么注入的内容模板从哪里获取,这里有两个选项。
- —injectConfigFile 指定对应的注入文件
- —injectConfigMapName 注入配置的 ConfigMap 名称
如果在操作时发现Sidecar没有注入成功可以根据注入的方式查看上面的注入流程来查找问题。
网友评论