目前有两种部署Harbor的方法,其一是通过docker compose部署,其二是托管在Kubernetes平台之上。相比较而言,还是前一种方法更简洁易管理些,我们在这即使用方法一部署Harbor。然后重点介绍下怎样配置Kubernetes从Harbor拉取镜像。

实验环境说明

我们使用一套3节点部署的k8s环境,并且直接复用其中的节点3部署Harbor服务。

[k8s@worker-node3 ~]$ kubectl get nodes
NAME        STATUS   ROLES    AGE    VERSION
worker-node1   Ready    <none>   142d   v1.14.0
worker-node2   Ready    <none>   142d   v1.14.0
worker-node3   Ready    <none>   142d   v1.14.0

下载并部署docker-compose

复用worker-node3节点来部署该服务。

curl -L https://github.com/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` -o /opt/k8s/bin/docker-compose
chmod +x /opt/k8s/bin/docker-compose
export PATH=/opt/k8s/bin:$PATH

下载和导入harbor镜像

从 harbor 发布页面下载最新的 harbor 离线安装包

wget  -c https://storage.googleapis.com/harbor-releases/release-1.7.0/harbor-offline-installer-v1.7.4.tgz
tar -xzvf harbor-offline-installer-v1.7.4.tgz

如果上面链接无法下载,可以从这个网盘共享下载资源
链接: https://pan.baidu.com/s/1XS7oKEumMZqXqZ28UXfmWw 提取码: jje4

导入 docker images

cd harbor
docker load -i harbor.v1.7.4.tar.gz

创建 harbor nginx 服务器使用的 x509 证书

创建 harbor 证书签名请求:

cat > harbor-csr.json <<EOF
{
  "CN": "harbor",
  "hosts": [
    "127.0.0.1",
    "172.18.110.23",
    "harbor.corpcorp.com"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",
      "OU": "corpcorp"
    }
  ]
}
EOF
  • hosts 字段指定授权使用该证书的当前部署节点 IP,如果后续使用域名访问 harbor 则还需要添加域名;

生成 harbor 证书和私钥:

cfssl gencert -ca=/etc/kubernetes/cert/ca.pem -ca-key=/etc/kubernetes/cert/ca-key.pem -config=/etc/kubernetes/cert/ca-config.json -profile=kubernetes harbor-csr.json | cfssljson -bare harbor
mkdir -p /etc/harbor/ssl
mv harbor*.pem /etc/harbor/ssl

注意:上面是使用 CloudFlare 的 PKI 工具集 cfssl 创建证书,安装 cfssl 工具的方法如下。

sudo mkdir -p /opt/k8s/cert && sudo chown -R k8s /opt/k8s && cd /opt/k8s
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
mv cfssl_linux-amd64 /opt/k8s/bin/cfssl

wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
mv cfssljson_linux-amd64 /opt/k8s/bin/cfssljson

wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
mv cfssl-certinfo_linux-amd64 /opt/k8s/bin/cfssl-certinfo

chmod +x /opt/k8s/bin/*
export PATH=/opt/k8s/bin:$PATH

配置和安装harbor

备份一份harbor配置文件:

cp harbor.cfg harbor.cfg.origin

编辑并更新harbor.cfg:

diff harbor.cfg harbor.cfg.origin
8c8
< hostname = 172.18.110.23
---
> hostname = reg.mydomain.com
12c12
< ui_url_protocol = https
---
> ui_url_protocol = http
24,25c24,25
< ssl_cert = /etc/harbor/ssl/harbor.pem
< ssl_cert_key = /etc/harbor/ssl/harbor-key.pem
---
> ssl_cert = /data/cert/server.crt
> ssl_cert_key = /data/cert/server.key
58c58
< email_server = smtp.corpcorp.com
---
> email_server = smtp.mydomain.com
60,62c60,62
< email_username = admin@corpcorp.com
< email_password = data707
< email_from = harbor_admin <admin@corpcorp.com>
---
> email_username = sample_admin@mydomain.com
> email_password = abc
> email_from = admin <sample_admin@mydomain.com>
69c69
< harbor_admin_password = REPLACE-FOR-YOUR-PASSWORD
---
> harbor_admin_password = Harbor12345
  • harbor_admin_password,指定管理员账号的密码,或保留使用默认值
cd /home/k8s/harbor
mkdir /data
chmod 777 /var/run/docker.sock /data
./install.sh

检查服务组件运行状态

# docker-compose  ps
       Name                     Command                  State                                    Ports                              
-------------------------------------------------------------------------------------------------------------------------------------
harbor-adminserver   /harbor/start.sh                 Up (healthy)                                                                   
harbor-core          /harbor/start.sh                 Up (healthy)                                                                   
harbor-db            /entrypoint.sh postgres          Up (healthy)   5432/tcp                                                        
harbor-jobservice    /harbor/start.sh                 Up                                                                             
harbor-log           /bin/sh -c /usr/local/bin/ ...   Up (healthy)   127.0.0.1:1514->10514/tcp                                       
harbor-portal        nginx -g daemon off;             Up (healthy)   80/tcp                                                          
nginx                nginx -g daemon off;             Up (healthy)   0.0.0.0:443->443/tcp, 0.0.0.0:4443->4443/tcp, 0.0.0.0:80->80/tcp
redis                docker-entrypoint.sh redis ...   Up             6379/tcp                                                        
registry             /entrypoint.sh /etc/regist ...   Up (healthy)   5000/tcp                                                        
registryctl          /harbor/start.sh                 Up (healthy)

浏览器访问 https://172.18.110.23/

后续如果需要变更harbor.cfg配置文件,需要重新执行./prepare命令以将配置信息更新到 docker-compose.yml 文件。然后重启docker-compose服务。

停止 harbor的方法:docker-compose down -v

启动 harbor的方法:docker-compose up -d

Harbor管理平台

登录平台,创建必要的项目和用户账号,例如:
1)创建一个项目 dev-project
2)创建一个用户 dev-user
3)将用户 dev-user 设置为 dev-project 项目管理员

docker 客户端登陆harbor

将签署 harbor 证书的 CA 证书拷贝到/etc/docker/certs.d/172.18.110.23

mkdir -p /etc/docker/certs.d/172.18.110.23
cp /etc/kubernetes/cert/ca.pem /etc/docker/certs.d/172.18.110.23/ca.crt

登陆 harbor

# docker login 172.18.110.23
Username: dev-user
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
  • 认证信息会自动保存到 ~/.docker/config.json 文件。

制作容器镜像并推送到harbor中去

以下镜像是为研发测试设计的一个镜像版本,其中集成了openssh服务,操作系统账号是 root/hahaha2019 ,做了最小范围的系统配置调优。

centos6-bare-system:latest,290MB,在官网的centos:6镜像上,安装了ssh服务,增加中文支持,做了系统性能、时间和语言方面的配置

编写dockerfile文件
继续使用worker-node3节点作为制作容器镜像使用的环境。

mkdir -p  /opt/images/centos6-bare-system

镜像:centos6-bare-system:latest
基于官方镜像centos:6定制一个最小化的开发测试使用的容器镜像。

# cd /opt/images/centos6-bare-system
# ls
Dockerfile

Dockerfile文件内容如下:

FROM centos:6
MAINTAINER watermelonbig <watermelonbig@163.com>

RUN echo "root:hahaha2019" | chpasswd
RUN yum clean all; \
        rpm --rebuilddb; \
        yum install -y openssh-server openssh-clients

RUN echo 'net.ipv6.conf.all.disable_ipv6 = 1' >> /etc/sysctl.conf;\
        echo 'net.ipv6.conf.default.disable_ipv6 = 1' >> /etc/sysctl.conf;\
        echo 'net.ipv4.ip_local_port_range = 1024 65000' >> /etc/sysctl.conf;\
        echo 'vm.swappiness = 10' >> /etc/sysctl.conf;\
        echo '* soft nofile 65535' >> /etc/security/limits.conf;\
        echo '* hard nofile 65535' >> /etc/security/limits.conf;\
        echo "session   required        /lib64/security/pam_limits.so" >> /etc/pam.d/login;\
        sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config;\
        sed -i '/UseDNS yes/s/.*/UseDNS no/g' /etc/ssh/sshd_config;\
        cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN ssh-keygen -t rsa -P "" -f /etc/ssh/ssh_host_rsa_key;\
        ssh-keygen -t dsa -P ""  -f /etc/ssh/ssh_host_dsa_key

RUN chkconfig sshd on

CMD ["/usr/sbin/sshd","-D"]

EXPOSE 8080 8081 8082 22
  • 使用官方centos:6作为源镜像
  • 增加了openssh服务
  • 最小化的调优系统配置

执行创建镜像命令

cd  /opt/images/centos6-bare-system
docker build -t centos6-bare-system .

按harbor服务地址和项目名称给镜像打上标记:

docker tag centos6-bare-system:latest 172.18.110.23/dev-project/centos6-bare-system:v0

推送镜像至harbor:

docker login 172.18.110.23
docker push 172.18.110.23/dev-project/centos6-bare-system:v0

手动从harbor拉取镜像的方法:

docker login 172.18.110.23
docker pull 172.18.110.23/dev-project/centos6-bare-system:v0

在kubernetes中配置使用harbor提供的容器镜像服务

我们在k8s中创建容器时,需要告知Pods使用有效的凭证访问正确的harbor服务地址来获取容器镜像。
有两种配置方法,一种方法是在每次需要创建pod时都显示地使用ImagePullSecrets定义获取镜像使用的配置和认证信息;另一种方法是,通过service account为k8s绑定一个全局性的镜像服务配置信息,后续在创建pod时会被自动地在资源定义中附加上访问容器镜像资源服务的所需的配置和认证信息。

以上两种方法没有优劣之分,各自有适用的场景。前者胜在灵活性,而后者则降低了一点使用上的复杂性。

1)在Pod中指定ImagePullSecrets的方法

Kubernetes支持在一个pod中使用ImagePullSecrets指定访问registry服务的密钥。
首先,我们需要创建出这个secret密钥。

有两种方法:

  • 通过命令行方式创建密钥;
  • 通过已有的Docker认证信息创建密钥;

方法一:通过命令行方式创建密钥

cat <<EOF > ./kustomization.yaml
secretGenerator:
- name: myregistrykey
  type: docker-registry
  literals:
  - docker-server=DOCKER_REGISTRY_SERVER
  - docker-username=DOCKER_USER
  - docker-password=DOCKER_PASSWORD
  - docker-email=DOCKER_EMAIL
EOF
kubectl apply -k .
  • 注意使用实际的harbor服务地址、用户账号信息填充上面的yaml文件
  • 密钥的type类型是docker-registry
  • kubectl create secret docker-registry 方法创建出的密钥只能用于访问一个固定的私有镜像仓库服务
  • 由于Pods只能引用自己所在namespace中的secrets密钥,所以当同时管理与使用多个namespace空间时,需要为每一个命名空间都执行一遍上面的创建密钥程序

方法二:通过已有的Docker认证信息创建密钥

如果你已经使用docker login登录过私有镜像仓库服务了,那么可以直接把这份已有的认证信息拷贝到Kubernetes中使用。

# cd ~/.docker    
# cat config.json
{
    "auths": {
        "172.18.110.23": {
            "auth": "anNiZGV2ZWcvfGVyOkpzYjEyMaQ1"
        }
    },
    "HttpHeaders": {
        "User-Agent": "Docker-Client/18.09.2 (linux)"
    }
}
# cat config.json |base64 -w 0
wraJImF1dGhzIjogeraJCSIxOTIuMTY4LjEzMC4yMyI6IHsKCQkJImF1dGgiOiAiYW5OaVpHVjJaV3h2Y0dWeU9rcHpZakV5TXpRMSIKCQl9Cgl9LAoJIkh0dHBIZWFkZXJzIjogewoJCSJVc2VyLUFnZW50IjogIkRvY2tlci1DbGllbnQvMTguMDkuMiAobGludXgpIgoJfQp9
  • 找到保存了登录私有仓库认证信息的文件,然后使用base64编码生成一份不带换行的字符串数据

定义一个secret.yaml文件:

apiVersion: v1
kind: Secret
metadata:
  name: myregistrykey
  namespace: development
data:
  .dockerconfigjson: wraJImF1dGhzIjogeraJCSIxOTIuMTY4LjEzMC4yMyI6IHsKCQkJImF1dGgiOiAiYW5OaVpHVjJaV3h2Y0dWeU9rcHpZakV5TXpRMSIKCQl9Cgl9LAoJIkh0dHBIZWFkZXJzIjogewoJCSJVc2VyLUFnZW50IjogIkRvY2tlci1DbGllbnQvMTguMDkuMiAobGludXgpIgoJfQp9
type: kubernetes.io/dockerconfigjson
  • data item需要设置为".dockerconfigjson"
  • data[".dockerconfigjson"]的值需要是以base64编码且不带换行的认证信息字符串
  • type 需要设置为 kubernetes.io/dockerconfigjson

创建密钥:

kubectl create -f secret.yaml

在部署应用的时候,我们需要为Pod指定下载镜像所需的secret密钥

创建一个测试目的的pod:

[k8s@worker-node1 ~]$ cat testserver1-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-baresystem
  namespace: development
spec:
  containers:
    - name: test-baresystem
      image: 172.18.110.23/dev-project/centos6-bare-system:v0
      ports:
      - containerPort: 8080
        hostPort: 30001
      - containerPort: 8081
        hostPort: 30002
      - containerPort: 8082
        hostPort: 30003
      - containerPort: 22
        hostPort: 30004
  imagePullSecrets:
    - name: myregistrykey

执行创建pod的命令:

[k8s@worker-node1 ~]$ kubectl create -f testserver1-pod.yamlpod/test-baresystem created

查看结果:

[k8s@worker-node1 ~]$ kubectl get pods -o wide
NAME                               READY   STATUS    RESTARTS   AGE    IP                NODE        NOMINATED NODE   READINESS GATES
test-baresystem                    1/1     Running   0          115m   192.168.169.144   worker-node2   <none>           <none>

通过kubectl exec登录pod容器,检查是否符合我们的设计要求:

kubectl exec -it test-baresystem -- /bin/bash

由于我们为这个pod都配置了4个hostPort端口,直接访问物理服务器的地址和这些端口,就可以ssh管理到业务pod容器和访问容器内的应用服务。根据上文设计,远程ssh登录以下地址和端口,可以登录上我们创建的pods容器:worker-node2:30004

注意:如果你的系统环境中,需要访问多个私有的容器镜像仓库,你可以为每个私有仓库创建一个密钥,然后在pod定义文件中同时引用它们。Kubelet将会把所有的imagePullSecrets合并为一个虚拟的.docker/config.json后使用。

2)把 ImagePullSecrets 添加到一个 service account的使用方法

在上一步骤中,我们需要在每个pod的定义文件中显示指定访问私有镜像仓库服务的密钥信息。
我们还有另外一个选择,就是把imagePullSecrets的配置信息附加到命名空间中的default 账号(serviceaccount)上去。

[k8s@worker-node1 ~]$ kubectl get sa
NAME      SECRETS   AGE
default   1         38d

[k8s@worker-node1 ~]$ kubectl describe sa default
Name:                default
Namespace:           development
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   default-token-5mfln
Tokens:              default-token-5mfln
Events:              <none>

为指定的sa账号添加以下registry认证配置信息:

[k8s@worker-node1 ~]$ kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'serviceaccount/default patched

查看serviceaccount账号default (development命名空间下的):

[k8s@worker-node1 ~]$ kubectl get serviceaccounts default -o yaml
apiVersion: v1
imagePullSecrets:
- name: myregistrykey
kind: ServiceAccount
metadata:
  creationTimestamp: "2019-03-31T13:04:59Z"
  name: default
  namespace: development
  resourceVersion: "21269444"
  selfLink: /api/v1/namespaces/development/serviceaccounts/default
  uid: 967e535f-53b5-11e9-85a8-14187748b31c
secrets:
- name: default-token-5mfln

此时,在development命名空间下创建的任何pods容器,都会自动在pod定义中附加上下面这样的密钥认证信息了:

spec:
  imagePullSecrets:
  - name:myregistrykey

我们拿重新上文创建的pod做下验证测试
先删除之前创建的pod:

[k8s@worker-node1 ~]$ kubectl delete pod test-baresystem
pod "test-baresystem" deleted

复制一份yaml定义文件并删除imagePullScrets的内容:

apiVersion: v1
kind: Pod
metadata:
  name: test-baresystem
  namespace: development
spec:
  containers:
    - name: test-baresystem
      image: 172.18.110.23/dev-project/centos6-bare-system:v0
      ports:
      - containerPort: 8080
        hostPort: 30001
      - containerPort: 8081
        hostPort: 30002
      - containerPort: 8082
        hostPort: 30003
      - containerPort: 22
        hostPort: 30004

创建pod:

[k8s@worker-node1 ~]$ kubectl create -f testserver1-pod-by-sa.yaml
pod/test-baresystem created
[k8s@worker-node1 ~]$ kubectl get pods
NAME                               READY   STATUS    RESTARTS   AGE
test-baresystem                    1/1     Running   0          2s

查看运行中的pod test-baresystem的定义(只截取了部分):

[k8s@worker-node1 ~]$ kubectl get pod test-baresystem -o yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2019-05-09T08:21:16Z"
  name: test-baresystem
  namespace: development
  resourceVersion: "21273557"
  selfLink: /api/v1/namespaces/development/pods/test-baresystem
  uid: 6a23ac7c-7233-11e9-9524-14187749bf6b
spec:
  containers:
  - image: 172.18.110.23/dev-project/centos6-bare-system:v0
    imagePullPolicy: IfNotPresent
    name: test-baresystem
    ports:
    - containerPort: 8080
      hostPort: 30001
      protocol: TCP
    - containerPort: 8081
      hostPort: 30002
      protocol: TCP
    - containerPort: 8082
      hostPort: 30003
      protocol: TCP
    - containerPort: 22
      hostPort: 30004
      protocol: TCP
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-5mfln
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  imagePullSecrets:
  - name: myregistrykey
..............
  • 可以看到创建出的pod中已经被自动补充上了访问私有镜像仓库服务所需要的密钥认证信息了

参考资料:
https://github.com/opsnull/follow-me-install-kubernetes-cluster/blob/master/11.部署Harbor-Registry.mdhttps://www.kubernetes.org.cn/1738.html
https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod
https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account