OCI DevOps ProfessionalのLeaning Pathを確認する 1

# はじめに
OCI DevOps ProfessionalのLeaning PathにあるLabを実際にやってみて、OCI DevOpsの設定や動作を確認します。

https://mylearn.oracle.com/ou/learning-path/become-an-oci-devops-professional-2023/121756

ここでは上記Learning Pathのうち、以下のコンテナの復習とOKEへのデプロイ、OCI DevOpsを用いたCI/CDの設定と実行までをやっていきます。

[01. Dockefileを使用したWeb Applicationの作成](#01-dockefileを使用したweb-applicationの作成)
[02. OCIRを使用したコンテナイメージの管理](#02-ocirを使用したコンテナイメージの管理)
[03. OCIR上のコンテナイメージをOKEから利用](#03-ocir上のコンテナイメージをokeから利用)
[04. OCI DevOps ProjectとCode Repositoryの作成](#04-oci-devops-projectとcode-repositoryの作成)
[05. アーティファクト・レジストリの作成とセットアップ](#05-アーティファクトレジストリの作成とセットアップ)
[06. CI/CD Pipelineの設定と実行](#06-cicd-pipelineの設定と実行)

# 01. Dockefileを使用したWeb Applicationの作成
LabではCloud Shellを使用する手順になっていますが、ローカルに構築したOracle Linuxの仮想マシンで実施します。

```
$ docker -v
Docker version 24.0.2, build cb74dfc
```

## コードのClone
使用するコードをGitHub Repositoryからクローンします。

```
$ git clone https://github.com/ou-developers/docker-helloworld-demo
Cloning into 'docker-helloworld-demo'...
remote: Enumerating objects: 20, done.
remote: Counting objects: 100% (20/20), done.
remote: Compressing objects: 100% (18/18), done.
remote: Total 20 (delta 4), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (20/20), 7.61 KiB | 7.61 MiB/s, done.
Resolving deltas: 100% (4/4), done.
```

```
$ cd docker-helloworld-demo/
$ ls -l
total 40
-rw-rw-r--. 1 opc opc  786 Jun 16 00:35 Dockerfile
-rw-rw-r--. 1 opc opc  824 Jun 16 00:35 HelloWorld-lb.yaml
-rw-rw-r--. 1 opc opc 1072 Jun 16 00:35 License.md
-rw-rw-r--. 1 opc opc  484 Jun 16 00:35 README.md
-rw-rw-r--. 1 opc opc  770 Jun 16 00:35 SECURITY.md
-rw-rw-r--. 1 opc opc 1691 Jun 16 00:35 build_spec.yaml
-rw-rw-r--. 1 opc opc 4425 Jun 16 00:35 index.html
-rw-rw-r--. 1 opc opc  272 Jun 16 00:35 nginx.conf
-rw-rw-r--. 1 opc opc  141 Jun 16 00:35 runner.sh
```

Dockefileの中身を確認しておきます。

```Dockerfile
############################################################
# Dockerfile to build Nginx Installed Containers
# Based on nginx image
############################################################


# Set the base image to nginx
FROM nginx

# File Author / Maintainer
MAINTAINER Mahendra Mehra

# Remove the default Nginx configuration file
RUN rm -v /etc/nginx/nginx.conf

# Copy a configuration file from the current directory
ADD nginx.conf /etc/nginx/

RUN mkdir /etc/nginx/logs

# Add a sample index file
ADD index.html /www/data/

# Create a runner script for the entrypoint
COPY runner.sh /runner.sh
RUN chmod +x /runner.sh

# Expose ports
EXPOSE 80

ENTRYPOINT ["/runner.sh"]

# Set the default command to execute
# when creating a new container
CMD ["nginx", "-g", "daemon off;"]
```

## ImageのBuild
DockerfileからDocker ImageをBuildします。

```
$ sudo docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
```

Tagとして`1.0`をつけてBuildします。

```
$ sudo docker build -t oci_sample_webapp_01:1.0 .
[+] Building 9.9s (12/12) FINISHED                                                                                                                                                                                                                           
 => [internal] load .dockerignore                                                                                                                                                                                                                       0.0s
 => => transferring context: 2B                                                                                                                                                                                                                         0.0s
 => [internal] load build definition from Dockerfile                                                                                                                                                                                                    0.0s
 => => transferring dockerfile: 884B                                                                                                                                                                                                                    0.0s
 => [internal] load metadata for docker.io/library/nginx:latest                                                                                                                                                                                         2.1s
 => [1/7] FROM docker.io/library/nginx@sha256:593dac25b7733ffb7afe1a72649a43e574778bf025ad60514ef40f6b5d606247                                                                                                                                          6.2s
 => => resolve docker.io/library/nginx@sha256:593dac25b7733ffb7afe1a72649a43e574778bf025ad60514ef40f6b5d606247                                                                                                                                          0.0s
 => => sha256:593dac25b7733ffb7afe1a72649a43e574778bf025ad60514ef40f6b5d606247 1.86kB / 1.86kB                                                                                                                                                          0.0s
 => => sha256:5b5fe70539cd6989aa19f25826309f9715a9489cf1c057982d6a84c1ad8975c7 29.12MB / 29.12MB                                                                                                                                                        1.0s
 => => sha256:441a1b46536703aec84bf2cabdd2524d4cc7dad9c6772491e01ff8ffe15b5507 41.46MB / 41.46MB                                                                                                                                                        0.8s
 => => sha256:3b9543f2b5005b74aae0ffaa779803f2b162ec3bd461c9e10a21671512718c87 627B / 627B                                                                                                                                                              0.2s
 => => sha256:d2b2f2980e9ccc570e5726b56b54580f23a018b7b7314c9eaff7e5e479c78657 1.78kB / 1.78kB                                                                                                                                                          0.0s
 => => sha256:eb4a57159180767450cb8426e6367f11b999653d8f185b5e3b78a9ca30c2c31d 8.15kB / 8.15kB                                                                                                                                                          0.0s
 => => sha256:ca89ed5461a9f2c681ebd39ed6583a7405d82f51b42177b7388c99eb6869e780 959B / 959B                                                                                                                                                              0.8s
 => => extracting sha256:5b5fe70539cd6989aa19f25826309f9715a9489cf1c057982d6a84c1ad8975c7                                                                                                                                                               1.3s
 => => sha256:b0e1283145af839c63923f671b4ab6c5d9e22826ab4372c6660db5a5556446c8 366B / 366B                                                                                                                                                              1.2s
 => => sha256:4b98867cde79486d55399cb95957331018b849a2bf04ee9a6dcae9b8bc482d76 1.21kB / 1.21kB                                                                                                                                                          1.2s
 => => sha256:4a85ce26214d83c77b5464631a67c71e1c2793b655261befe52ba0e20ffc3bd1 1.40kB / 1.40kB                                                                                                                                                          1.2s
 => => extracting sha256:441a1b46536703aec84bf2cabdd2524d4cc7dad9c6772491e01ff8ffe15b5507                                                                                                                                                               1.1s
 => => extracting sha256:3b9543f2b5005b74aae0ffaa779803f2b162ec3bd461c9e10a21671512718c87                                                                                                                                                               0.0s
 => => extracting sha256:ca89ed5461a9f2c681ebd39ed6583a7405d82f51b42177b7388c99eb6869e780                                                                                                                                                               0.0s
 => => extracting sha256:b0e1283145af839c63923f671b4ab6c5d9e22826ab4372c6660db5a5556446c8                                                                                                                                                               0.0s
 => => extracting sha256:4b98867cde79486d55399cb95957331018b849a2bf04ee9a6dcae9b8bc482d76                                                                                                                                                               0.0s
 => => extracting sha256:4a85ce26214d83c77b5464631a67c71e1c2793b655261befe52ba0e20ffc3bd1                                                                                                                                                               0.0s
 => [internal] load build context                                                                                                                                                                                                                       0.0s
 => => transferring context: 5.13kB                                                                                                                                                                                                                     0.0s
 => [2/7] RUN rm -v /etc/nginx/nginx.conf                                                                                                                                                                                                               0.2s
 => [3/7] ADD nginx.conf /etc/nginx/                                                                                                                                                                                                                    0.0s
 => [4/7] RUN mkdir /etc/nginx/logs                                                                                                                                                                                                                     0.2s
 => [5/7] ADD index.html /www/data/                                                                                                                                                                                                                     0.0s
 => [6/7] COPY runner.sh /runner.sh                                                                                                                                                                                                                     0.0s
 => [7/7] RUN chmod +x /runner.sh                                                                                                                                                                                                                       0.2s
 => exporting to image                                                                                                                                                                                                                                  0.9s
 => => exporting layers                                                                                                                                                                                                                                 0.9s
 => => writing image sha256:fefaa0f3727d5e02c83994cf443ab3b5c2f093028b189347a165044ec13e6f25                                                                                                                                                            0.0s
 => => naming to docker.io/library/oci_sample_webapp_01:1.0                                                                                                                                                                                             0.0s
```

```
$ sudo docker images
REPOSITORY             TAG       IMAGE ID       CREATED          SIZE
oci_sample_webapp_01   1.0       fefaa0f3727d   39 seconds ago   187MB
```

## 確認
BuildしたImageを起動（Run）します。

```
$ sudo docker run -d --name webapp-01 -p 80:80/tcp oci_sample_webapp_01:1.0
4d9119f9d61313fdf3d4ee95c05403f1e6ef3c0618b52581af3e04ba0c058724
$ sudo docker ps
CONTAINER ID   IMAGE                      COMMAND                   CREATED          STATUS          PORTS                               NAMES
4d9119f9d613   oci_sample_webapp_01:1.0   "/runner.sh nginx -g…"   11 seconds ago   Up 10 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp   webapp-01
```

アクセスできることを確認します。

```
$ curl -k http://localhost:80
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="A simple docker helloworld example.">
    <meta name="author" content="Mahendra Mehra">
    <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- Latest compiled and minified CSS -->
・・・
```

停止します。

```
$ sudo docker stop 4d9119f9d613
4d9119f9d613
$ sudo docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
```

```
$ sudo docker ps -a
CONTAINER ID   IMAGE                      COMMAND                   CREATED         STATUS                      PORTS     NAMES
4d9119f9d613   oci_sample_webapp_01:1.0   "/runner.sh nginx -g…"   2 minutes ago   Exited (0) 14 seconds ago             webapp-01
```

コンテナを削除します。

```
$ sudo docker rm webapp-01
webapp-01
$ sudo docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
```

# 02. OCIRを使用したコンテナイメージの管理
コンテナレジストリにOCIのマネージドサービスのOCIR（Oracle Cloud Infrastructure Registry）を使用して、コンテナイメージを管理します。
その際、`docker`コマンドを使用して、DockeHubと同様に操作できることを確認します。

## ポリシーの設定
ここでは権限の都合上、動的グループに対してポリシーを設定します。

```
Allow dynamic-group <動的グループ名> to manage repos in compartment <コンパートメント名>
```

## 認証トークンの作成
[ここ](https://qiita.com/dingtianhongjie/items/7d1270bfd327da800bbd#%E8%AA%8D%E8%A8%BC%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%81%AE%E4%BD%9C%E6%88%90)で作成したトークンを使用しますので、ここでは割愛します。

## リポジトリの作成
OCIのコンソールから以下の設定でリポジトリを作成します。

![スクリーンショット 2023-06-16 10.57.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/5a1b8dad-b5a4-7b76-e502-7f59aefa3aa3.png)

## OCIRへのログイン
`docker login`コマンドでOCIRにログインします。

コマンド引数の`lhr`はリージョンキーで、OCIRを作成したリージョンに合わせて変更します。（lhrはロンドンリージョン）
そのほかのリージョンキーは[こちら](https://docs.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm#top)で確認できます。

```
$ sudo docker login lhr.ocir.io
Username: <tenancy-namespace>/<username>
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
```

なお、テナンシーがOracle Identity Cloud Serviceでフェデレーションしている場合は、Usernameが`<tenancy-namespace>/oracleidentitycloudservice/<username>`になります。

## コンテナイメージへのタグ付けとPush
Buildしたコンテナイメージにタグ付けしてOCIRにPushします。

```
$ sudo docker tag oci_sample_webapp_01:1.0 lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01:latest
$ sudo docker images
REPOSITORY                                                           TAG       IMAGE ID       CREATED       SIZE
oci_sample_webapp_01                                                 1.0       fefaa0f3727d   4 hours ago   187MB
lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01   latest    fefaa0f3727d   4 hours ago   187MB
```

```
$ sudo docker push lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01:latest
The push refers to repository [lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01]
446dc8125d41: Pushed 
9790e90fe034: Pushed 
179ef86f9e79: Pushed 
074b93c4f63f: Pushed 
fc48ac889762: Pushed 
3c2e924b7ffb: Pushed 
9e96226c58e7: Pushed 
12a568acc014: Pushed 
7757099e19d2: Pushed 
bf8b62fb2f13: Pushed 
4ca29ffc4a01: Pushed 
a83110139647: Pushed 
ac4d164fef90: Pushed 
latest: digest: sha256:054b658ff90d0a528699a5922afd4e116366eb236d2a71a90cf97d3e4ded097a size: 3021
```

OCIコンソール上でイメージを確認できます。

![スクリーンショット 2023-06-16 13.31.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/cdf295a7-672f-1077-e11f-c05bf81f2b15.png)

## イメージのPull
今度はOCIRからコンテナイメージをPullします。

一旦、ローカルにあるコンテナイメージを削除します。

```
$ sudo docker images
REPOSITORY                                                           TAG       IMAGE ID       CREATED       SIZE
oci_sample_webapp_01                                                 1.0       fefaa0f3727d   4 hours ago   187MB
lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01   latest    fefaa0f3727d   4 hours ago   187MB
$ sudo docker rmi oci_sample_webapp_01:1.0
Untagged: oci_sample_webapp_01:1.0
$ sudo docker rmi lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01:latest
Untagged: lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01:latest
Untagged: lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01@sha256:054b658ff90d0a528699a5922afd4e116366eb236d2a71a90cf97d3e4ded097a
Deleted: sha256:fefaa0f3727d5e02c83994cf443ab3b5c2f093028b189347a165044ec13e6f25
$ sudo docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
```

OCIコンソールの右上のアクションから`プル・コマンドのコピー`を選択して、コマンドをコピーします。

![スクリーンショット 2023-06-16 13.55.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/998d2859-77fe-5f0c-218e-fedef714d36a.png)

（sudoを追記して）コマンドをペーストして実行します。

```
$ sudo docker pull lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01:latest
latest: Pulling from orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01
5b5fe70539cd: Already exists 
441a1b465367: Already exists 
3b9543f2b500: Already exists 
ca89ed5461a9: Already exists 
b0e1283145af: Already exists 
4b98867cde79: Already exists 
4a85ce26214d: Already exists 
e4efc6e2208a: Already exists 
74950baf2cdb: Already exists 
badb3a061df2: Already exists 
4ea2cca57f70: Already exists 
e11f5e15cb1e: Already exists 
922cb46c73c1: Already exists 
Digest: sha256:054b658ff90d0a528699a5922afd4e116366eb236d2a71a90cf97d3e4ded097a
Status: Downloaded newer image for lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01:latest
lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01:latest
```

確認します。

```
$ sudo docker images
REPOSITORY                                                           TAG       IMAGE ID       CREATED       SIZE
lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01   latest    fefaa0f3727d   4 hours ago   187MB
```

# 03. OCIR上のコンテナイメージをOKEから利用
LabではOKEのクラスタ構築からPod、LBのデプロイなどをやっていますが、ここでは構築済みのOKEクラスタにOCIRのコンテナイメージを使用してPodをデプロイする手順を確認します。

## Secretの作成
OCIRはプライベートリポジトリにしていますので、イメージをPullする際に認証が必要になります。認証に使用するSecretを作成します。

```
$ k create secret docker-registry ocir-secret-01 \
--docker-server="lhr.ocir.io" \ #OCIRのリージョンに合わせて変更
--docker-username="<tenancy-namespace>/<username>" \
--docker-password="<認証トークン>" \
--docker-email="<メールアドレス>" 
secret/ocir-secret-01 created
$ k get secret
NAME             TYPE                             DATA   AGE
ocir-secret-01   kubernetes.io/dockerconfigjson   1      14s
```

## Podのデプロイ
作成したSecretを`imagePullSecrets`に指定してPodをデプロイします。

```webapp.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: webapp
  name: webapp
spec:
  containers:
  - image: lhr.ocir.io/orasejapan/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01:latest
    name: webapp
    resources: {}
  imagePullSecrets:
  - name: ocir-secret-01
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
```

```
$ k apply -f webapp.yaml 
pod/webapp created
$ k get pod
NAME     READY   STATUS    RESTARTS   AGE
webapp   1/1     Running   0          70s
```

# 04. OCI DevOps ProjectとCode Repositoryの作成
OCI DevOpsはAWSだとCodeシリーズに相当するサービスです。
OCI DevOpsではProjectを作成して、そのProject配下にRepositoryなどを作成していきます。

## ポリシーの設定
Labではgroupや複数の動的グループを作成してそれぞれにポリシーを設定していますが、今回は権限の都合上、一つの動的グループに対して以下のポリシーを設定します。

```
Allow dynamic-group <動的グループ名> to manage devops-project in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage devops-repository in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage devops-connection in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage log-groups in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage ons-topics in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage repos in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage devops-family in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage vaults in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage keys in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage secret-family in compartment <コンパートメント名>
```

## ミラーリポジトリの作成
GitHubからCode Repositoryにミラーするミラーリポジトリを作成します。

### GitHubのアクセストークンの作成

GitHubにサインインして、以下のURLにアクセスします。

https://github.com/ou-developers/docker-helloworld-demo

Forkします。
![スクリーンショット 2023-06-19 10.50.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/b17be248-fc11-0b52-8677-779816993f45.png)

アカウントの`Settings`から
![スクリーンショット 2023-06-19 11.13.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/e3079000-72c4-4aa8-a2c4-c3bcbea87d79.png)

以下のように`Generate new token(classic)`を選択します。
![スクリーンショット 2023-06-19 11.15.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/b754cc36-977e-12ab-e069-bee0285d4c96.png)

ここでは以下のように設定します。
![スクリーンショット 2023-06-19 11.17.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/b07c5e44-a7fb-9816-a505-f4e032af75a8.png)

表示されたトークンをメモしておきます。

![スクリーンショット 2023-06-19 11.18.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/5c0c152d-7d9b-fdfb-54c7-40a7b2cc99ac.png)

### Vaultの作成

![スクリーンショット 2023-06-19 13.01.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/87cccee2-7438-31e5-d36c-87abda8de11f.png)

作成したVaultに以下の設定でキーを作成します。

![スクリーンショット 2023-06-19 13.08.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/21faad1d-8eb2-28a9-6cbd-67955e023594.png)

シークレットを以下の設定で作成します。
暗号化キーに上で作成したキーを選択し、シークレット・コンテンツにGitHubで作成したトークンを指定します。

![スクリーンショット 2023-06-19 13.11.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/8475c846-5e06-c8fc-adc5-443138ae114c.png)

## DevOps Projectの作成
### トピックの作成
DevOpsの通知に使用するトピックを作成します。

![スクリーンショット 2023-06-19 13.29.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/e97d67eb-6dec-a786-7dd3-fda1f0c995d8.png)

### Projectの作成
以下のように作成したトピックを指定して、Projectを作成します。

![スクリーンショット 2023-06-19 13.38.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/e034c601-af90-8cc7-364d-f099b9aeb707.png)

デフォルトではロギングが無効になっているので、有効にします。
以下の画面で`ログの有効化`をクリックします。

![スクリーンショット 2023-06-19 13.40.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/1bfd5cb6-a69e-4b54-c997-2a023955d243.png)

以下の画面で有効にします。

![スクリーンショット 2023-06-19 13.40.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/a0d10b67-24dc-a829-d667-b3ec29ab3d72.png)

![スクリーンショット 2023-06-19 13.41.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/a928fe30-d76c-cab3-e05b-50e83df13715.png)

### 外部接続の作成
作成したProjectの左メニューから`外部接続`を選択し、以下のように設定して外部接続を作成します。

![スクリーンショット 2023-06-19 13.57.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/0e95da22-c8fa-d340-a3f0-65ba402f0d90.png)

### ミラーリポジトリの作成
作成したProjectの左メニューから`コード・リポジトリ`を選択し、`リポジトリのミラー化`を選択します。

![スクリーンショット 2023-06-19 13.59.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/9df13bf9-da5c-b8dd-444e-2cfd66847447.png)

以下のように作成した外部接続とリポジトリをプルダウンメニューから選択します。
また、同期するスケジュールは1分間隔にしました。

![スクリーンショット 2023-06-19 14.02.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/663b7d51-8d26-4be2-85dc-5f9e3c9fcaac.png)

しばらくすると、以下のようにGitHubでForkしたリポジトリがミラーされていることがわかります。

![スクリーンショット 2023-06-19 14.09.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/5351b203-0e79-1218-f284-0f72ae28c10c.png)

GitHubで新しいファイル（ここでは`Mirror_test.txt`）を作成し、1分待つか`今すぐ同期化`をクリックすると、リポジトリにも反映されることを確認します。

![スクリーンショット 2023-06-19 14.20.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/269fa8c6-b684-0394-f300-81be59f5f0b5.png)

## コード・リポジトリの作成
今度はミラーではなく、オリジナルなリポジトリを作成します。
OCIコンソールのコード・リポジトリから`リポジトリの作成`をクリックします。

以下のように設定して、リポジトリを作成します。

![スクリーンショット 2023-06-19 14.22.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/3dd126f2-135d-303f-b7bc-dbaf3218176f.png)

作成したリポジトリの画面で`クローン`をクリックし、

![スクリーンショット 2023-06-19 14.23.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/712a8bb0-9f84-a90f-8e87-a55d04f487e8.png)

表示された画面のHTTPSでクローニングのURLをコピーします。

![スクリーンショット 2023-06-19 14.25.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/46fe7a80-3d4a-923c-deee-4594d3740484.png)

[01. Dockefileを使用したWeb Applicationの作成](#01-dockefileを使用したweb-applicationの作成)で使用した仮想マシンにクローンします。

```
$ git clone https://devops.scmservice.uk-london-1.oci.oraclecloud.com/namespaces/<テナンシー>/projects/LHR-DOP-LAB04-1-DP-01/repositories/LHR-DOP-LAB04-1-CR-01
Cloning into 'LHR-DOP-LAB04-1-CR-01'...
Username for 'https://devops.scmservice.uk-london-1.oci.oraclecloud.com': <tenancy-namespace>/<username>
Password for 'https://<tenancy-namespace>/<username>@devops.scmservice.uk-london-1.oci.oraclecloud.com': <認証トークン>
remote: Counting objects: 2, done
remote: Finding sources: 100% (2/2)
remote: Getting sizes: 100% (1/1)
remote: Total 2 (delta 0), reused 2 (delta 0)
Unpacking objects: 100% (2/2), 141 bytes | 141.00 KiB/s, done.
```

```
$ cd LHR-DOP-LAB04-1-CR-01/
$ ls -l
total 0
```

[01. Dockefileを使用したWeb Applicationの作成](#01-dockefileを使用したweb-applicationの作成)でクローンしたWebApplicationのディレクトリに移動して、各ファイルをコピーします。

```
$ cd ../../01/docker-helloworld-demo
$ cp * ../../04/LHR-DOP-LAB04-1-CR-01
$ cd ../../04/LHR-DOP-LAB04-1-CR-01
$ ls
Dockerfile  HelloWorld-lb.yaml  License.md  README.md  SECURITY.md  build_spec.yaml  index.html  nginx.conf  runner.sh
```

リポジトリにPushします。

```
$ git remote -v
origin  https://devops.scmservice.uk-london-1.oci.oraclecloud.com/namespaces/<テナンシー>/projects/LHR-DOP-LAB04-1-DP-01/repositories/LHR-DOP-LAB04-1-CR-01 (fetch)
origin  https://devops.scmservice.uk-london-1.oci.oraclecloud.com/namespaces/<テナンシー>/projects/LHR-DOP-LAB04-1-DP-01/repositories/LHR-DOP-LAB04-1-CR-01 (push)
$ git add .
$ git config --global user.email "<メールアドレス>"
$ git config --global user.name "<tenancy-namespace>/<username>"
$ git commit -m "first push"
[main 9863e6b] first push
 9 files changed, 311 insertions(+)
 create mode 100644 Dockerfile
 create mode 100644 HelloWorld-lb.yaml
 create mode 100644 License.md
 create mode 100644 README.md
 create mode 100644 SECURITY.md
 create mode 100644 build_spec.yaml
 create mode 100644 index.html
 create mode 100644 nginx.conf
 create mode 100644 runner.sh
$ git push -u -f origin main 
Username for 'https://devops.scmservice.uk-london-1.oci.oraclecloud.com': <tenancy-namespace>/<username>
Password for 'https://<tenancy-namespace>/<username>@devops.scmservice.uk-london-1.oci.oraclecloud.com': <認証トークン>
Enumerating objects: 12, done.
Counting objects: 100% (12/12), done.
Delta compression using up to 2 threads
Compressing objects: 100% (11/11), done.
Writing objects: 100% (11/11), 5.41 KiB | 5.41 MiB/s, done.
Total 11 (delta 0), reused 0 (delta 0), pack-reused 0
To https://devops.scmservice.uk-london-1.oci.oraclecloud.com/namespaces/<テナンシー>/projects/LHR-DOP-LAB04-1-DP-01/repositories/LHR-DOP-LAB04-1-CR-01
   2c84bca..9863e6b  main -> main
branch 'main' set up to track 'origin/main'.
```

コンソールでも確認します。

![スクリーンショット 2023-06-19 14.40.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/f6a1d94e-32b9-149c-a3d4-0795f912619a.png)

# 05. アーティファクト・レジストリの作成とセットアップ
アーティファクト・レジストリは、ソフトウェアパッケージやバイナリ、Kubernetesのマニフェストファイルなどを管理するリポジトリです。

## ポリシーの設定
以下のポリシーを設定しました。

```
Allow dynamic-group <動的グループ名> to manage artifact-repositories in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage generic-artifacts in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage devops-deploy-artifact in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage devops-deploy-environment in compartment <コンパートメント名>
```

## リポジトリの作成
以下の設定でリポジトリを作成します。

![スクリーンショット 2023-06-20 11.07.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/cfeade92-8947-7b3c-28bf-32680ff35d04.png)

## アーティファクトの追加
OCI DevOpsのProjectにアーティファクトを登録します。
ここでは、BuildPipeLine実行後のアーティファクトの保存先を設定します。

### コンテナイメージ
Projectの左メニューから`アーティファクト`を選択し、`アーティファクトの追加`をクリックします。
以下のように設定します。Tagには`${BUILDRUN_HASH}`を指定し、Build実行後に動的にアップデートされるようにします。

![スクリーンショット 2023-06-20 14.55.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/d9ec72df-4d40-9d18-7636-9c4eba1326e9.png)


### Kubernetesマニフェスト
同様に`アーティファクトの追加`から以下のように設定します。
`アーティファクト・レジストリ・リポジトリ`には先ほど作成した、アーティファクト・レジストリを選択します。また、バージョンはコンテナイメージのTagと同様に`${BUILDRUN_HASH}`を設定します。

![スクリーンショット 2023-06-20 11.17.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/e91d14a6-6ba1-c815-bcef-fb7e0a66ad27.png)

## 環境の作成
Projectの左メニューから`環境`を選択し、`環境の作成`をクリックします。
以下のように設定します。

![スクリーンショット 2023-06-20 11.19.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/51e15cb9-347a-2e5c-22df-0f739c1d15b5.png)

クラスタは事前に作成しておく必要があります。

![スクリーンショット 2023-06-20 11.19.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/26e19362-7ae2-d8ea-6cb3-98a12857bcc5.png)

# 06. CI/CD Pipelineの設定と実行
OCI DevOpsのBuild PipelineとDeployment Pipelineを設定して、コンテナイメージをビルドして、OKEにデプロイするまでのPipelineを実行します。

## ポリシーの設定
これまで設定済みのポリシーに加えて、以下を設定します。

```
Allow dynamic-group <動的グループ名> to manage cluster in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to manage all-resources in compartment <コンパートメント名>
Allow dynamic-group <動的グループ名> to read all-artifacts in compartment <コンパートメント名>
```

## マニフェストファイルの編集
ここでは、[「04. OCI DevOps ProjectとCode Repositoryの作成」で作成したコード・リポジトリ](https://qiita.com/dingtianhongjie/private/19d0ee610c2d697b2b31#%E3%82%B3%E3%83%BC%E3%83%89%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%AE%E4%BD%9C%E6%88%90)のコードを編集して使用します。

```
$ cd LHR-DOP-LAB04-1-CR-01
$ ls -l
total 40
-rw-rw-r--. 1 opc opc  786 Jun 19 05:32 Dockerfile
-rw-rw-r--. 1 opc opc  824 Jun 19 05:32 HelloWorld-lb.yaml
-rw-rw-r--. 1 opc opc 1072 Jun 19 05:32 License.md
-rw-rw-r--. 1 opc opc  484 Jun 19 05:32 README.md
-rw-rw-r--. 1 opc opc  770 Jun 19 05:32 SECURITY.md
-rw-rw-r--. 1 opc opc 1691 Jun 19 05:32 build_spec.yaml
-rw-rw-r--. 1 opc opc 4425 Jun 19 05:32 index.html
-rw-rw-r--. 1 opc opc  272 Jun 19 05:32 nginx.conf
-rw-rw-r--. 1 opc opc  141 Jun 19 05:32 runner.sh
```

`HelloWorld-lb.yaml`を以下のように編集します。
動作確認はPodをデプロイするだけにしますので、LoadBalancerはコメントアウトします。また、Secretはdefault namespaceに作成済みなので、namespaceもコメントアウトしておきます。

また、コンテナイメージのTagはBuild時にユニークなものとするため、`${BUILDRUN_HASH}`にします。

```HelloWorld-lb.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld-deployment-01
#  namespace: ns-01
spec:
  selector:
    matchLabels:
      app: helloworld
  replicas: 1
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - name: helloworld
    # enter the path to your image, be sure to include the correct region prefix    
        image: lhr.ocir.io/<テナンシーネームスペース>/lhr-dop-lab02-1-ocir-1/oci_sample_webapp_01:${BUILDRUN_HASH}
        ports:
        - containerPort: 80
      imagePullSecrets:
    # enter the name of the secret you created  
      - name: ocir-secret-01
---
#apiVersion: v1
#kind: Service
#metadata:
#  name: helloworld-service-<userid>
#  namespace: ns-<userid>
#spec:
#  type: LoadBalancer
#  ports:
#  - port: 80
#    protocol: TCP
#    targetPort: 80
#  selector:
#    app: helloworld
```

Commitして、コード・リポジトリにPushします。

```
$ git add .
$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   HelloWorld-lb.yaml

$ git commit -m "HelloWorld-lb.yaml modified for Lab06"
[main bafce83] HelloWorld-lb.yaml modified for Lab06
 1 file changed, 17 insertions(+), 17 deletions(-)
$ git push -u -f origin main
Username for 'https://devops.scmservice.uk-london-1.oci.oraclecloud.com': <tenancy-namespace>/<username>
Password for 'https://<tenancy-namespace>/<username>@devops.scmservice.uk-london-1.oci.oraclecloud.com': <認証トークン>
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 591 bytes | 591.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2)
To https://devops.scmservice.uk-london-1.oci.oraclecloud.com/namespaces/orasejapan/projects/LHR-DOP-LAB04-1-DP-01/repositories/LHR-DOP-LAB04-1-CR-01
   9863e6b..bafce83  main -> main
branch 'main' set up to track 'origin/main'.
```

コード・リポジトリでも反映されていることが確認できます。

![スクリーンショット 2023-06-20 16.08.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/cceea7cf-a45d-7d50-0054-55fe73d196ad.png)

## Build Pipelineの作成
CI/CDのCIの部分を設定します。

作成済みのProjectの左メニューから`ビルド・パイプライン`を選択し、`ビルド・パイプラインの作成`をクリックします。
以下のように設定して`作成`をクリックします。

![スクリーンショット 2023-06-20 16.31.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/00946e51-5852-0456-dcb1-78de9971c27d.png)

そうするとこの画面になります。

![スクリーンショット 2023-06-20 16.31.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/278270dc-34d7-4a77-06a3-02c697412f34.png)

`+`アイコンから`ステージの追加`をクリックします。

![スクリーンショット 2023-06-20 16.32.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/362f4849-8734-3548-d02d-6254fc7532f7.png)

この画面になります。ここで`マネージド・ビルド`を選択して、ビルドとテストを実行するステージを追加します。

![スクリーンショット 2023-06-20 16.33.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/928e6fb7-14ec-bdb3-c239-86c568faccb7.png)

ここではこのように設定して、`追加`をクリックします。

![スクリーンショット 2023-06-20 16.36.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/0f43d6c1-8f66-f4b2-3069-f75a1520aecc.png)

![スクリーンショット 2023-06-20 16.37.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/f9aa7d95-eb63-74fb-3682-d2a053a98dfb.png)

ステージが追加されました。
続いて、`+`アイコンをクリックして、

![スクリーンショット 2023-06-20 16.38.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/8ff9db07-ab4e-22da-f679-44397f28c999.png)

ビルドしたアーティファクトをリポジトリにPushするステージを追加します。

![スクリーンショット 2023-06-20 16.38.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/711fecc8-f888-1f32-8aab-456abe917505.png)

アーティファクトの保存先として、コンテナイメージをコンテナ・レジストリに、Kubernetesマニフェストをアーティファクト・レジストリに設定します。

![スクリーンショット 2023-06-20 16.40.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/255a5ca7-3bff-c570-bb3a-733242a01517.png)

`ビルド構成/結果アーティファクト名`は`build_spec.yaml`で指定した名称と合わせます。

![スクリーンショット 2023-06-20 16.47.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/e9036241-ae9e-a89b-05dc-e8ad57db61dd.png)



```build_spec.yaml
version: 0.1
component: build
timeoutInSeconds: 6000
runAs: root
shell: bash
env:
  # these are local variables to the build config
  variables:

  # the value of a vaultVariable is the secret-id (in OCI ID format) stored in the OCI Vault service
  # you can then access the value of that secret in your build_spec.yaml commands
  vaultVariables:
    
  # exportedVariables are made available to use as parameters in sucessor Build Pipeline stages
  # For this Build to run, the Build Pipeline needs to have a BUILDRUN_HASH parameter set
  exportedVariables:
    - BUILDRUN_HASH


#Its a native way to fetch artifacts from external or artifact repo or a file path to use before a stage.
# More about buildspec formats - https://docs.oracle.com/en-us/iaas/Content/devops/using/build_specs.htm

steps:
  - type: Command
    name: "Define unique image tag"
    timeoutInSeconds: 40
    command: |
      export BUILDRUN_HASH=`echo ${OCI_BUILD_RUN_ID} | rev | cut -c 1-7`
      echo "BUILDRUN_HASH: " $BUILDRUN_HASH
      
  - type: Command
    timeoutInSeconds: 1200
    name: "Build container image"
    command: |
      cd ${OCI_PRIMARY_SOURCE_DIR}
      docker build --pull --rm -t oke_app_base .      
     
outputArtifacts:
  - name: oke_app_base #これ
    type: DOCKER_IMAGE
    # this location tag doesn't effect the tag used to deliver the container image
    # to the Container Registry
    location: oke_app_base:latest

  - name: oke_deploy_manifest #これ
    type: BINARY
    # this location tag doesn't effect the tag used to deliver the container image
    # to the Container Registry
    location: ${OCI_PRIMARY_SOURCE_DIR}/HelloWorld-lb.yaml
```

このようになります。

![スクリーンショット 2023-06-20 16.54.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/6c08eefa-ac92-948d-d0b9-a9510eefc3d0.png)

## Deployment Pipelineの作成
CI/CDのCDの部分を設定します。

作成済みのProjectの左メニューから`デプロイメント・パイプライン`を選択し、`デプロイメント・パイプラインの作成`をクリックします。

以下のように設定してパイプラインを作成します。

![スクリーンショット 2023-06-20 16.57.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/4c64ac05-f727-393a-aa7c-16dd72c2d93b.png)

ビルド・パイプラインと同様に以下のような画面になりますので、`+`アイコンから`ステージの追加`を選択します。

![スクリーンショット 2023-06-20 16.58.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/d2d4f108-62fd-b5a7-3186-bd0805b884f7.png)

デプロイ先として`Kubenetesクラスタにマニフェストを適用`を選択します。

![スクリーンショット 2023-06-20 16.59.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/f28ed2bb-ca90-8874-0918-f6cef16cd617.png)

`環境`は作成済みの環境から選択し、アーティファクトも選択します。

![スクリーンショット 2023-06-20 17.01.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/460cab02-b1bb-3518-5cd1-cd1549b0ce1f.png)

このような画面になります。

![スクリーンショット 2023-06-20 17.02.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/ad26b35e-af83-977f-b096-541aab706658.png)

## Trigger Deployment Stageの作成
ビルド・パイプラインに戻って、デプロイメント・パイプラインをキックするトリガーを追加します。

![スクリーンショット 2023-06-20 17.04.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/96308c30-5040-13a0-016e-121a02f18ff3.png)

`デプロイメントのトリガー`を選択します。

![スクリーンショット 2023-06-20 17.04.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/ea16167d-8785-85eb-4657-a68af6bda914.png)

先ほど作成したデプロイメント・パイプラインを選択します。

![スクリーンショット 2023-06-20 17.07.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/a9c3cc2b-5fa8-2f13-03f3-148dc357e07d.png)

![スクリーンショット 2023-06-20 17.07.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/1fda2721-8cff-e1d7-f6db-95c4087bc3c0.png)

このようにビルド・パイプラインは3つのステージで構成されています。

![スクリーンショット 2023-06-20 17.08.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/d663fe32-e890-c8ec-ab70-425f2ae47b23.png)

## Pipelineの実行
設定したパイプラインを実行します。
ビルド・パイプラインの画面から`手動実行の開始`をクリックします。

![スクリーンショット 2023-06-20 17.10.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/fb28ffd8-3c38-b5c8-703f-6b69d00f3105.png)

![スクリーンショット 2023-06-20 17.11.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/c1dbb6e7-8fad-58a6-d07d-ec5257cdbf7c.png)

パイプラインを実行すると、以下のように実行中の様子がコンソールに表示されます。

![Build.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/3c530248-ff53-c446-e61b-68c7fc9ab10f.gif)

パイプラインが完了すると、以下のようになってログが確認できます。

![スクリーンショット 2023-06-21 9.49.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/94b58f1c-d130-238b-9ca5-c3ec6b3ccb29.png)

ビルド・パイプラインが完了すると、そのままデプロイメント・パイプラインが実行されます。
ちなみに、ビルド・パイプラインは1回で成功したのですが、デプロイメントは3回失敗して、4回目で成功しました。（ポリシーの設定が抜けていたり、namespaceの設定が間違っていたりしました）

![スクリーンショット 2023-06-21 10.26.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/9161f1ed-f3ad-c5ba-3cc7-9a9a315520e6.png)

失敗した場合はこの画面でログを確認して修正します。成功した場合のログももちろん確認できます。

![スクリーンショット 2023-06-21 10.26.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/a68a1312-55b7-a3c4-70fb-5b2fec7cbaad.png)

Podがデプロイされていることも確認できます。

```
$ k get pod
NAME                                        READY   STATUS    RESTARTS   AGE
helloworld-deployment-01-656885b4bb-b9lz8   1/1     Running   0          10m
```

## Buildイメージの確認
パイプライン実行でBuildしたアーティファクトを確認します。
ビルド・パイプラインは4回実行していますので、4つずつあります。タグやバージョンがBuild時のHASH値になっていることも確認できます。

![スクリーンショット 2023-06-21 10.31.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/7f8b931b-85b3-3225-4394-ac8c5bbbc0fa.png)

![スクリーンショット 2023-06-21 10.31.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/95b8d413-dbd1-6aa7-128c-d79075801633.png)