Open GitEngHar opened 1 year ago
コードを作成 https://zenn.dev/engharu/articles/166c611d650cca
コードをCIしてみた https://zenn.dev/engharu/articles/5f450768c9152d
code
package main
import (
"fmt"
"os"
"strconv"
)
type HumanStatus struct {
Name string
BirthdayMonth int
BirthdayDay int
}
func personalColor(personalData HumanStatus) (personalColorData string) {
nameLength := len(personalData.Name)
redElement := int((nameLength * 255) / 10)
greenElement := int((personalData.BirthdayMonth * 255) / 12)
blueElement := int((personalData.BirthdayDay * 255) / 31)
personalColorData = fmt.Sprintf("%d,%d,%d", redElement, greenElement, blueElement)
return personalColorData
}
func main() {
month, _ := strconv.Atoi(os.Getenv("MONTH"))
day, _ := strconv.Atoi(os.Getenv("DAY"))
personalData := HumanStatus{os.Getenv("MyName"), month, day}
myLiteral := fmt.Sprintf("YourColorCode is %s", personalColor(personalData))
fmt.Printf(myLiteral)
}
compose
services:
app:
build: ./
tty: true
volumes:
- ./:/go/app
environment:
- MyName=satoshi
- MONTH=12
- DAY=10
Dockerfile
FROM golang:1.21.2-alpine3.18
COPY . /go/src/app
WORKDIR /go/src/app/
composeの立ち上げ
Goの実行をシェル化けして..
実行シェル
[node1] (local) root@192.168.0.28 ~/CiCdHandsOn/Go
$ cat runGoApp.sh
docker compose exec app go run ghTestActionApps.go
テストシェル
[node1] (local) root@192.168.0.28 ~/CiCdHandsOn/Go
$ cat testGoApp.sh
docker compose exec app go test -v
実行結果
[node1] (local) root@192.168.0.28 ~/CiCdHandsOn/Go
$ sh runGoApp.sh
YourColorCode is 178,255,82
[node1] (local) root@192.168.0.28 ~/CiCdHandsOn/Go
$ sh testGoApp.sh
=== RUN Test_personalColor
=== RUN Test_personalColor/test1
=== RUN Test_personalColor/test2
--- PASS: Test_personalColor (0.00s)
--- PASS: Test_personalColor/test1 (0.00s)
--- PASS: Test_personalColor/test2 (0.00s)
=== RUN Test_main
=== RUN Test_main/test1
YourColorCode is 178,255,82--- PASS: Test_main (0.00s)
--- PASS: Test_main/test1 (0.00s)
PASS
ok github.com/GitEngHar/CiCdHandsOn.git 0.003s
compose時の build imageを指定して、アプリを起動しない
Image名の指定
services:
app:
image: personalgo
container_name: personalcolor
build: ./
tty: true
volumes:
- ./:/go/app
environment:
- MyName=satoshi
- MONTH=12
- DAY=10
[node1] (local) root@192.168.0.28 ~/CiCdHandsOn/Go
$ docker compose build
[+] Building 0.2s (8/8) FINISHED
=> [app internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [app internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 107B 0.0s
=> [app internal] load metadata for docker.io/library/golang:1.21.2-alpine3.18 0.1s
=> [app internal] load build context 0.0s
=> => transferring context: 290B 0.0s
=> [app 1/3] FROM docker.io/library/golang:1.21.2-alpine3.18@sha256:a76f153cff6a59112777c071b0 0.0s
=> CACHED [app 2/3] COPY . /go/src/app 0.0s
=> CACHED [app 3/3] WORKDIR /go/src/app/ 0.0s
=> [app] exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:9f8436fc36955578bb3101a9413967073b9c3f146940c675f7db04a2045e0ad9 0.0s
=> => naming to docker.io/library/personalgo 0.0s
確認
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
personalgo latest 9f8436fc3695 3 minutes ago 222MB
image名を書き換える
[node1] (local) root@192.168.0.28 ~/CiCdHandsOn/Go
$ docker tag personalgo haruapp/gopersonalcolor:latest
確認
[node1] (local) root@192.168.0.28 ~/CiCdHandsOn/Go
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
haruapp/gopersonalcolor latest 9f8436fc3695 8 minutes ago 222MB
personalgo latest 9f8436fc3695 8 minutes ago 222MB
go-app latest e944bcc2d160 36 minutes ago 222MB
root-app latest 5a6c270e3231 About an hour ago 352MB
push ログ
[node1] (local) root@192.168.0.28 ~/CiCdHandsOn/Go
$ docker push haruapp/gopersonalcolor:latest
The push refers to repository [docker.io/haruapp/gopersonalcolor]
5f70bf18a086: Pushed
8ffadd81c74c: Pushed
cc2ac6e7152c: Mounted from library/golang
a9727a6bf15a: Mounted from library/golang
f6a51e30f545: Mounted from library/golang
cc2447e1835a: Mounted from library/golang
latest: digest: sha256:e1d88183407a448f61f2296b79a7d33bf99cee73add004abaf3129f83cafdd59 size: 1571
pushできた
あとはこれをパイプラインでやるのみ🔥
name: Docker
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: haruapp/gopersonalcolor
- name: Build
env:
tags: ${{ steps.meta.outputs.tags }}
run: docker compose -f ./Go/compose.yaml build && export IMAGENAME=`docker image ls | head -n 2 | tail -n 1 | awk '{print $1}'` && docker tag $IMAGENAME $tags && docker push $tags
# tag: ${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
使わなかったけども、以下コマンドで出力されているjsonからtag情報を取得できる
tag: ${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
name: Docker
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
prebuild:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21.2'
- name: Test
run: go test -v ./Go/...
build:
runs-on: ubuntu-latest
needs: prebuild
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: haruapp/gopersonalcolor
- name: Build
env:
tags: ${{ steps.meta.outputs.tags }}
run: docker compose -f ./Go/compose.yaml build && export IMAGENAME=`docker image ls | head -n 2 | tail -n 1 | awk '{print $1}'` && docker tag $IMAGENAME $tags && docker push $tags
# tag: ${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
goでtest成功後ビルドされるように変更。脆弱性診断も入れたいが、スコープ外にする needs を入れて依存関係を持たせた
参考
ブログ https://dev.classmethod.jp/articles/github-actions-aws-sts-credentials-iamrole/
ブログを参照して、譲渡Roleを作成し、以下ブログを参考にしてECRへのPushができた 次はデプロイをしていこう!
https://zenn.dev/aldagram_tech/articles/748bde18ae0b42
デプロイログ https://github.com/GitEngHar/CiCdHandsOn/actions/runs/6549007301/job/17784981738?pr=5
晴れてBG-deploy成功
DeployCode
AWSTemplateFormatVersion: "2010-09-09"
Description: CodeDeploy with ECS Blue/Green deploy
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Parameters for CodeDeploy
Parameters:
- ClusterName
- ServiceName
- ALBName
- ALBTargetGroupBlueName
- ALBTargetGroupGreenName
Parameters:
ClusterName:
Type: String
Default: FrontCluster
ServiceName:
Type: String
Default: FrontSvc
ALBName:
Type: String
Default: webAppFrontALB
ALBTargetGroupBlueName:
Type: String
Default: ALBTargetGroupBlue
ALBTargetGroupGreenName:
Type: String
Default: ALBTargetGroupGreen
Resources:
# ALB???
webAppALB:
Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
Properties:
Name: !Ref ALBName
Subnets:
- !ImportValue PublicSubnet1-ID
- !ImportValue PublicSubnet2-ID
SecurityGroups:
- !ImportValue SecurityGroupFrontALB-ID
Scheme: internet-facing
Type: application
IpAddressType: ipv4
# TargetGroup(1)???
ALBTargetGroupBlue:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
Properties:
Name: !Ref ALBTargetGroupBlueName
Protocol: HTTP
Port: 80
TargetType: ip
VpcId: !ImportValue Vpc-ID
HealthCheckIntervalSeconds: 5
HealthCheckPath: /
HealthCheckPort: '80'
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 2
HealthyThresholdCount: 2
Matcher:
HttpCode: '200'
UnhealthyThresholdCount: 4
# ???????
ALBListenerProdTraffic:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Properties:
LoadBalancerArn: !Ref webAppALB
Protocol: HTTP
Port: 80
DefaultActions:
- Type: forward
ForwardConfig:
TargetGroups:
- TargetGroupArn: !Ref ALBTargetGroupBlue
Weight: 1
# TargetGroup(2)???
ALBTargetGroupGreen:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
Properties:
Name: !Ref ALBTargetGroupGreenName
Protocol: HTTP
Port: 8080
TargetType: ip
VpcId: !ImportValue Vpc-ID
HealthCheckIntervalSeconds: 5
HealthCheckPath: /
HealthCheckPort: '80'
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 2
HealthyThresholdCount: 2
Matcher:
HttpCode: '200'
# Cluster??
ECSDemoCluster:
Type: 'AWS::ECS::Cluster'
Properties:
ClusterName: !Ref ClusterName
# Service???
ECSDemoService:
Type: 'AWS::ECS::Service'
Properties:
Cluster: !Ref ECSDemoCluster
ServiceName: !Ref ServiceName
TaskDefinition: !Ref BlueTaskDefinition
LoadBalancers:
- ContainerName: simpleWebapp
ContainerPort: 80
TargetGroupArn: !Ref ALBTargetGroupBlue
LaunchType: "FARGATE"
SchedulingStrategy: "REPLICA"
DeploymentController:
Type: CODE_DEPLOY
NetworkConfiguration:
AwsvpcConfiguration:
SecurityGroups:
- !ImportValue SecurityGroupFrontService-ID
Subnets:
- !ImportValue PublicSubnet1-ID
- !ImportValue PublicSubnet2-ID
AssignPublicIp: ENABLED
DesiredCount: 1
# ?????
BlueTaskDefinition:
Type: 'AWS::ECS::TaskDefinition'
DependsOn: ALBListenerProdTraffic
Properties:
NetworkMode: awsvpc
ContainerDefinitions:
- Image: '429535751272.dkr.ecr.ap-northeast-1.amazonaws.com/viewcertweb:f9b058ceae38161afc33a679aa39e037d0cad343'
Name: simpleWebapp
PortMappings:
- HostPort: 80
Protocol: tcp
ContainerPort: 80
Essential: true
RequiresCompatibilities:
- FARGATE
Cpu: '256'
Memory: '512'
Family: frontTask
ExecutionRoleArn: !ImportValue ECSTaskExecutionRole-Arn
EcsCodeDeploy:
Type: AWS::CodeDeploy::Application
Properties:
ApplicationName: !Sub AppECS-${ClusterName}-${ServiceName}
ComputePlatform: ECS
EcsDeploymentGroup:
Type: AWS::CodeDeploy::DeploymentGroup
DependsOn: ECSDemoService
Properties:
ApplicationName: !Ref EcsCodeDeploy
AutoRollbackConfiguration:
Enabled: True
Events:
- "DEPLOYMENT_FAILURE"
BlueGreenDeploymentConfiguration:
DeploymentReadyOption:
ActionOnTimeout: CONTINUE_DEPLOYMENT
WaitTimeInMinutes: 0
TerminateBlueInstancesOnDeploymentSuccess:
Action: TERMINATE
TerminationWaitTimeInMinutes: 5
DeploymentConfigName: "CodeDeployDefault.ECSLinear10PercentEvery1Minutes"
DeploymentGroupName: !Sub DgpECS-${ClusterName}-${ServiceName}
DeploymentStyle:
DeploymentOption: "WITH_TRAFFIC_CONTROL"
DeploymentType: "BLUE_GREEN"
ECSServices:
- ServiceName: !Ref ServiceName
ClusterName: !Ref ClusterName
LoadBalancerInfo:
TargetGroupPairInfoList:
- TargetGroups:
- Name:
!Ref ALBTargetGroupBlueName
- Name:
!Ref ALBTargetGroupGreenName
ProdTrafficRoute:
ListenerArns:
- !Ref ALBListenerProdTraffic
ServiceRoleArn:
!ImportValue CodeDeployServiceRole-Arn
Outputs:
appname:
Description: appname
Value: !Ref EcsCodeDeploy
FrontCode
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<body>
<div class="items">
<div class="head-base">
<div class="head">
<div class="itemtitle">資格情報管理システム</div>
</div>
<div class="head">
<div class="contentbutton">
<button id="getrequest" onclick="getCertData();" class="buttonitem">GET</button>
<button id="postrequest" onclick="postCertData();" class="buttonitem">POST</button>
</div>
</div>
</div>
<div class="inputBox">
<input id="dblink" type="text" placeholder="endpoint : http://xxx" class="inputcontent">
<input id="skillType" type="text" placeholder="CertTYpe" class="inputcontent">
<input id="certName" type="text" placeholder="CertName" class="inputcontent">
<textarea id="view" class="textareacontent" placeholder="Output" readonly></textarea>
</div>
<script language="javascript" type="text/javascript">
async function getCertData(){
var target = document.getElementById("view");
var dbgetLink = document.getElementById("dblink").value;
document.getElementById("dblink").value = ""
var viewText = ""
if(!inputCheck(dbgetLink,"http")){
target.innerHTML = "エンドポイントを正しくを入力してください";
return 0
}
let requestUrl = new URL("/getcert",dbgetLink)
var response = await fetch(requestUrl);
if(!response.ok){
viewText = "ResponseError";
}
const certRawJsonData = await response.json();
const certJsonToStringData = JSON.stringify(certRawJsonData)
var fixCertJsonTypeData = certJsonToStringData.replace(/'/g,"\"")
fixCertJsonTypeData = fixCertJsonTypeData.replace("\"{{","[{")
fixCertJsonTypeData = fixCertJsonTypeData.replace("}}\"","}]")
const certJsonData = JSON.parse(fixCertJsonTypeData,(key,value) => {
switch(key){
case "skillType":
viewText += `資格スキルの種類 : ${value} <br>`;
break;
case "certName":
viewText += `資格名 : ${value} <br><br>`
}
})
target.innerHTML = viewText;
console.log('response.json():', viewText);
}
async function postCertData(){
var target = document.getElementById("view");
var dbgetLink = document.getElementById("dblink").value;
var skillTypeText = document.getElementById("skillType").value
var certName = document.getElementById("certName").value
document.getElementById("skillType").value = ""
document.getElementById("certName").value = ""
document.getElementById("dblink").value = ""
if(!inputCheck(dbgetLink,"http")){
target.innerHTML = "エンドポイントを正しくを入力してください";
return 0
}
if(!inputCheck(skillTypeText,"none") || !inputCheck(certName,"none")){
target.innerHTML = "バックエンドへ送るテキストを入力してください";
return 0
}
var data = {
skilltype: skillTypeText,
certname: certName
}
let requestUrl = new URL("/regcert",dbgetLink)
target.innerHTML = "Success"
const response = await fetch(requestUrl, {
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
}
function inputCheck(checktr,type){
switch(type){
case "http":
return checktr.startsWith("http")
case "none":
var response = true
if(checktr.length == 0){
response = false
}
return response
}
}
</script>
<style type="text/css">
.items{
display:flex;
flex-flow: column;
padding: 10% 30% 30% 30%;
}
.head-base{
display:flex;
flex-flow: row;
width: 100%;
}
.head{
width: 50%;
}
.contentbutton{
margin-left: 50%;
cursor: auto;
}
.buttonitem{
cursor: pointer;
padding:0.5em;
background-color: white;
}
.buttonitem:hover{
background-color: aliceblue;
/*background-color: greenyellow;*/
}
.itemtitle{
margin-left: 0px;
font-size: 18px;
font-weight: bold;
font-family:'メイリオ', 'Meiryo', sans-serif;
}
.inputBox{
margin-left: 0;
margin-top: 5%;
}
.inputcontent{
padding: 1em;
margin:0.5em auto;
width: 100%;
}
.textareacontent{
width: 100%;
padding: 1em 1em 5em 1em;
margin:0.5em auto;
}
</style>
</div>
</body>
</html>
githubActionCode
name: Deploy app to Amazon ECS
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# permission can be added at job level or workflow level
permissions:
id-token: write
env:
ECR_REPONAME: viewcertweb
ECS_CLUSTERNAME: FrontCluster
ECS_SERVICENAME: FrontSvc
ECS_CONTAINERNAME: simpleWebapp
ROLE_NAME: GithubCICD
IMAGE_TAG: ${{ github.sha }}
jobs:
ecr-push:
name: Deploy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1-node16
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ env.ROLE_NAME }}
role-session-name: GitHubActions-${{ github.run_id }}
aws-region: ap-northeast-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
# DockerイメージをビルドしてECRにプッシュ
- name: Docker image build and push to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
#aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 429535751272.dkr.ecr.ap-northeast-1.amazonaws.com
run: |
echo $ECR_REGISTRY/${{ env.ECR_REPONAME }}
docker login -u AWS -p $(aws ecr get-login-password) https://${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-1.amazonaws.com
docker build -t $ECR_REGISTRY/${{ env.ECR_REPONAME }}:${{ env.IMAGE_TAG }} -f ./SimpleWebApp/Front/Dockerfile --build-arg AWS_ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID }} --build-arg IMAGE_TAG=${{ env.IMAGE_TAG }} .
docker push $ECR_REGISTRY/${{ env.ECR_REPONAME }}:${{ env.IMAGE_TAG }}
echo "image=$ECR_REGISTRY/${{ env.ECR_REPONAME }}:${{ env.IMAGE_TAG }}" >> $GITHUB_OUTPUT
- name: Download task definition
run: |
aws ecs describe-task-definition --task-definition frontTask --query taskDefinition > task-definition.json
# TaskDefinition の image を push した最新のものに書き換える
- name: Render TaskDefinition
id: render-container-api
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: ${{ env.ECS_CONTAINERNAME }}
image: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPONAME }}:${{ env.IMAGE_TAG }}
# デプロイする
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-container-api.outputs.task-definition }}
service: ${{ env.ECS_SERVICENAME }}
cluster: ${{ env.ECS_CLUSTERNAME }}
codedeploy-appspec: appspec.yaml
wait-for-service-stability: true
おまけでGoのコード
いろんなコードで テスト・ビルドしてみよう🎉 DockerImageをビルドしてHubにアップロードできるようになるといいなぁ